summaryrefslogtreecommitdiffstats
path: root/sys/netinet/sctp_asconf.c
diff options
context:
space:
mode:
authorrrs <rrs@FreeBSD.org>2007-09-08 17:48:46 +0000
committerrrs <rrs@FreeBSD.org>2007-09-08 17:48:46 +0000
commite1de0a1edadaa9d67f677c0aa3a582c9bcc34b9c (patch)
treec5cd7d7c9d05970381c8f80e243b1da248135d4c /sys/netinet/sctp_asconf.c
parent4dd82bd675126ae3087b47d4425b57c8c44aa790 (diff)
downloadFreeBSD-src-e1de0a1edadaa9d67f677c0aa3a582c9bcc34b9c.zip
FreeBSD-src-e1de0a1edadaa9d67f677c0aa3a582c9bcc34b9c.tar.gz
- send call has a reference to uio->uio_resid in
the recent send code, but uio may be NULL on sendfile calls. Change to use sndlen variable. - EMSGSIZE is not being returned in non-blocking mode and needs a small tweak to look if the msg would ever fit when returning EWOULDBLOCK. - FWD-TSN has a bug in stream processing which could cause a panic. This is a follow on to the codenomicon fix. - PDAPI level 1 and 2 do not work unless the reader gets his returned buffer full. Fix so we can break out when at level 1 or 2. - Fix fast-handoff features to copy across properly on accepted sockets - Fix sctp_peeloff() system call when no true system call exists to screen arguments for errors. In cases where a real system call exists the system call itself does this. - Fix raddr leak in recent add-ip code change for bundled asconfs (even when non-bundled asconfs are received) - Make sure ipi_addr lock is held when walking global addr list. Need to change this lock type to a rwlock(). - Add don't wake flag on both input and output when the socket is closing. - When deleting an address verify the interface is correct before allowing the delete to process. This protects panda and unnumbered. - Clean up old sysctl stuff and get rid of the old Open/Net BSD structures. - Add a function to watch the ranges in the sysctl sets. - When appending in the reassembly queue, validate that the assoc has not gone to about to be freed. If so (in the middle) abort out. Note this especially effects MAC I think due to the lock/unlock they do (or with LOCK testing in place). - Netstat patch to get rid of warnings. - Make sure that no data gets queued to inactive/unconfirmed destinations. This especially effect CMT but also makes a impact on regular SCTP as well. - During init collision when we detect seq number out of sync we need to treat it like Case C and discard the cookie (no invarient needed here). - Atomic access to the random store. - When we declare a vtag good, we need to shove it into the time wait hash to prevent further use. When the tag is put into the assoc hash, we need to remove it from the twait hash (where it will surely be). This prevents duplicate tag assignments. - Move decr-ref count to better protect sysctl out of data. - ltrace error corrections in sctp6_usrreq.c - Add hook for interface up/down to be sent to us. - Make sysctl() exported structures independent of processor architecture. - Fix route and src addr cache clearing for delete address case. - Make sure address marked SCTP_DEL_IP_ADDRESS is never selected as src addr. - in icmp handling fixed so we actually look at the icmp codes to figure out what to do. - Modified mobility code. Reception of DELETE IP ADDRESS for a primary destination and SET PRIMARY for a new primary destination is used for retransmission trigger to the new primary destination. Also, in this case, destination of chunks in send_queue are changed to the new primary destination. - Fix so that we disallow sending by mbuf to ever have EEOR mode set upon it. Approved by: re@freebsd.org (B Mah)
Diffstat (limited to 'sys/netinet/sctp_asconf.c')
-rw-r--r--sys/netinet/sctp_asconf.c178
1 files changed, 167 insertions, 11 deletions
diff --git a/sys/netinet/sctp_asconf.c b/sys/netinet/sctp_asconf.c
index fb51dd4..e75f5de 100644
--- a/sys/netinet/sctp_asconf.c
+++ b/sys/netinet/sctp_asconf.c
@@ -40,6 +40,7 @@ __FBSDID("$FreeBSD$");
#include <netinet/sctputil.h>
#include <netinet/sctp_output.h>
#include <netinet/sctp_asconf.h>
+#include <netinet/sctp_timer.h>
/*
* debug flags:
@@ -550,6 +551,42 @@ sctp_process_asconf_set_primary(struct mbuf *m,
if (response_required) {
m_reply = sctp_asconf_success_response(aph->correlation_id);
}
+ /*
+ * Mobility adaptation. Ideally, when the reception of SET
+ * PRIMARY with DELETE IP ADDRESS of the previous primary
+ * destination, unacknowledged DATA are retransmitted
+ * immediately to the new primary destination for seamless
+ * handover. If the destination is UNCONFIRMED and marked
+ * to REQ_PRIM, The retransmission occur when reception of
+ * the HEARTBEAT-ACK. (See sctp_handle_heartbeat_ack in
+ * sctp_input.c) Also, when change of the primary
+ * destination, it is better that all subsequent new DATA
+ * containing already queued DATA are transmitted to the new
+ * primary destination. (by micchie)
+ */
+ if ((sctp_is_mobility_feature_on(stcb->sctp_ep,
+ SCTP_MOBILITY_BASE) ||
+ sctp_is_mobility_feature_on(stcb->sctp_ep,
+ SCTP_MOBILITY_FASTHANDOFF)) &&
+ sctp_is_mobility_feature_on(stcb->sctp_ep,
+ SCTP_MOBILITY_PRIM_DELETED) &&
+ (stcb->asoc.primary_destination->dest_state &
+ SCTP_ADDR_UNCONFIRMED) == 0) {
+
+ sctp_timer_stop(SCTP_TIMER_TYPE_PRIM_DELETED, stcb->sctp_ep, stcb, NULL, SCTP_FROM_SCTP_TIMER + SCTP_LOC_7);
+ if (sctp_is_mobility_feature_on(stcb->sctp_ep,
+ SCTP_MOBILITY_FASTHANDOFF)) {
+ sctp_assoc_immediate_retrans(stcb,
+ stcb->asoc.primary_destination);
+ }
+ if (sctp_is_mobility_feature_on(stcb->sctp_ep,
+ SCTP_MOBILITY_BASE)) {
+ sctp_move_chunks_from_deleted_prim(stcb,
+ stcb->asoc.primary_destination);
+ }
+ sctp_delete_prim_timer(stcb->sctp_ep, stcb,
+ stcb->asoc.deleted_primary);
+ }
} else {
/* couldn't set the requested primary address! */
SCTPDBG(SCTP_DEBUG_ASCONF1,
@@ -940,26 +977,138 @@ sctp_asconf_nets_cleanup(struct sctp_tcb *stcb, struct sctp_ifn *ifn)
}
}
+void
+sctp_move_chunks_from_deleted_prim(struct sctp_tcb *stcb, struct sctp_nets *dst)
+{
+ struct sctp_association *asoc;
+ struct sctp_stream_out *outs;
+ struct sctp_tmit_chunk *chk;
+ struct sctp_stream_queue_pending *sp;
+
+ if (dst->dest_state & SCTP_ADDR_UNCONFIRMED) {
+ SCTPDBG(SCTP_DEBUG_ASCONF1, "move_chunks_from_deleted_prim: specified destination is UNCONFIRMED\n");
+ return;
+ }
+ if (stcb->asoc.deleted_primary == NULL) {
+ SCTPDBG(SCTP_DEBUG_ASCONF1, "move_chunks_from_deleted_prim: Funny, old primary is not stored\n");
+ return;
+ }
+ asoc = &stcb->asoc;
+
+ /*
+ * now through all the streams checking for chunks sent to our bad
+ * network.
+ */
+ TAILQ_FOREACH(outs, &asoc->out_wheel, next_spoke) {
+ /* now clean up any chunks here */
+ TAILQ_FOREACH(sp, &outs->outqueue, next) {
+ if (sp->net == asoc->deleted_primary) {
+ sctp_free_remote_addr(sp->net);
+ sp->net = dst;
+ atomic_add_int(&dst->ref_count, 1);
+ }
+ }
+ }
+ /* Now check the pending queue */
+ TAILQ_FOREACH(chk, &asoc->send_queue, sctp_next) {
+ if (chk->whoTo == asoc->deleted_primary) {
+ sctp_free_remote_addr(chk->whoTo);
+ chk->whoTo = dst;
+ atomic_add_int(&dst->ref_count, 1);
+ }
+ }
+
+}
+
+extern int cur_oerr;
+
+void
+sctp_assoc_immediate_retrans(struct sctp_tcb *stcb, struct sctp_nets *dstnet)
+{
+ int error;
+ struct sctp_tmit_chunk *chk;
+
+ //for debug
+ if (dstnet->dest_state & SCTP_ADDR_UNCONFIRMED) {
+ SCTPDBG(SCTP_DEBUG_ASCONF1, "assoc_immediate_retrans: specified destination is UNCONFIRMED\n");
+ return;
+ }
+ if (stcb->asoc.deleted_primary == NULL) {
+ SCTPDBG(SCTP_DEBUG_ASCONF1, "assoc_immediate_retrans: Funny, old primary is not stored\n");
+ return;
+ }
+ if (!TAILQ_EMPTY(&stcb->asoc.sent_queue)) {
+ SCTPDBG(SCTP_DEBUG_ASCONF1, "Deleted primary is ");
+ SCTPDBG_ADDR(SCTP_DEBUG_ASCONF1, &stcb->asoc.deleted_primary->ro._l_addr.sa);
+ SCTPDBG(SCTP_DEBUG_ASCONF1, "Current Primary is ");
+ SCTPDBG_ADDR(SCTP_DEBUG_ASCONF1, &stcb->asoc.primary_destination->ro._l_addr.sa);
+ SCTPDBG(SCTP_DEBUG_ASCONF1, "Stopping send timer and calling t3rxt_timer\n");
+ sctp_timer_stop(SCTP_TIMER_TYPE_SEND, stcb->sctp_ep, stcb,
+ stcb->asoc.deleted_primary,
+ SCTP_FROM_SCTP_TIMER + SCTP_LOC_8);
+ stcb->asoc.num_send_timers_up--;
+ if (stcb->asoc.num_send_timers_up < 0) {
+ stcb->asoc.num_send_timers_up = 0;
+ }
+ SCTP_TCB_LOCK_ASSERT(stcb);
+ cur_oerr = stcb->asoc.overall_error_count;
+ error = sctp_t3rxt_timer(stcb->sctp_ep, stcb,
+ stcb->asoc.deleted_primary);
+ if (error) {
+ SCTPDBG(SCTP_DEBUG_ASCONF1, "t3rxt_timer error\n");
+ SCTP_INP_DECR_REF(stcb->sctp_ep);
+ return;
+ }
+ SCTP_TCB_LOCK_ASSERT(stcb);
+#ifdef SCTP_AUDITING_ENABLED
+ sctp_auditing(4, stcb->sctp_ep, stcb->asoc.deleted_primary);
+#endif
+ /* Debug code */
+ SCTPDBG(SCTP_DEBUG_ASCONF1, "assoc_immediate_retrans: calling chunk_output, retran_cnt is %d\n", stcb->asoc.sent_queue_retran_cnt);
+ TAILQ_FOREACH(chk, &stcb->asoc.sent_queue, sctp_next) {
+ SCTPDBG(SCTP_DEBUG_ASCONF1, "assoc_immediate_retrans: chk->whoTo is ");
+ SCTPDBG_ADDR(SCTP_DEBUG_ASCONF1, &chk->whoTo->ro._l_addr.sa);
+ SCTPDBG(SCTP_DEBUG_ASCONF1, "state is %d\n", chk->sent);
+ }
+ /* end Debug code */
+ sctp_chunk_output(stcb->sctp_ep, stcb, SCTP_OUTPUT_FROM_T3, SCTP_SO_NOT_LOCKED);
+ if ((stcb->asoc.num_send_timers_up == 0) &&
+ (stcb->asoc.sent_queue_cnt > 0)) {
+ struct sctp_tmit_chunk *chk;
+
+ chk = TAILQ_FIRST(&stcb->asoc.sent_queue);
+ sctp_timer_start(SCTP_TIMER_TYPE_SEND, stcb->sctp_ep,
+ stcb, chk->whoTo);
+ }
+ }
+ return;
+}
+
static int
sctp_asconf_queue_mgmt(struct sctp_tcb *, struct sctp_ifa *, uint16_t);
-static void
+void
sctp_net_immediate_retrans(struct sctp_tcb *stcb, struct sctp_nets *net)
{
struct sctp_tmit_chunk *chk;
+ int cnt = 0; /* debug */
- SCTPDBG(SCTP_DEBUG_ASCONF2, "net_immediate_retrans()\n");
- SCTPDBG(SCTP_DEBUG_ASCONF2, "RTO is %d\n", net->RTO);
+ SCTPDBG(SCTP_DEBUG_ASCONF1, "net_immediate_retrans:\n");
+ SCTPDBG(SCTP_DEBUG_ASCONF1, "RTO is %d\n", net->RTO);
sctp_timer_stop(SCTP_TIMER_TYPE_SEND, stcb->sctp_ep, stcb, net,
SCTP_FROM_SCTP_TIMER + SCTP_LOC_5);
stcb->asoc.cc_functions.sctp_set_initial_cc_param(stcb, net);
net->error_count = 0;
TAILQ_FOREACH(chk, &stcb->asoc.sent_queue, sctp_next) {
if (chk->whoTo == net) {
- chk->sent = SCTP_DATAGRAM_RESEND;
- sctp_ucount_incr(stcb->asoc.sent_queue_retran_cnt);
+ if (chk->sent < SCTP_DATAGRAM_RESEND) {
+ chk->sent = SCTP_DATAGRAM_RESEND;
+ sctp_ucount_incr(stcb->asoc.sent_queue_retran_cnt);
+ cnt++;
+ }
}
}
+ SCTPDBG(SCTP_DEBUG_ASCONF1, "%d chunks are marked to RESEND, retran_cnt is %d\n", cnt, stcb->asoc.sent_queue_retran_cnt);
}
static void
@@ -1365,7 +1514,7 @@ sctp_asconf_queue_add_sa(struct sctp_tcb *stcb, struct sockaddr *sa,
vrf_id = SCTP_DEFAULT_VRFID;
}
- ifa = sctp_find_ifa_by_addr(sa, vrf_id, 0);
+ ifa = sctp_find_ifa_by_addr(sa, vrf_id, SCTP_ADDR_NOT_LOCKED);
if (ifa == NULL) {
/* Invalid address */
return (-1);
@@ -2061,8 +2210,7 @@ sctp_asconf_iterator_stcb(struct sctp_inpcb *inp, struct sctp_tcb *stcb,
sctp_rtentry_t *rt;
/* delete this address if cached */
- if (net->ro._s_addr &&
- (net->ro._s_addr->ifa == ifa)) {
+ if (net->ro._s_addr == ifa) {
sctp_free_ifa(net->ro._s_addr);
net->ro._s_addr = NULL;
net->src_addr_selected = 0;
@@ -2223,8 +2371,10 @@ sctp_find_valid_localaddr(struct sctp_tcb *stcb)
struct sctp_ifn *sctp_ifn;
struct sctp_ifa *sctp_ifa;
+ SCTP_IPI_ADDR_LOCK();
vrf = sctp_find_vrf(stcb->asoc.vrf_id);
if (vrf == NULL) {
+ SCTP_IPI_ADDR_UNLOCK();
return (NULL);
}
LIST_FOREACH(sctp_ifn, &vrf->ifnlist, next_ifn) {
@@ -2250,6 +2400,7 @@ sctp_find_valid_localaddr(struct sctp_tcb *stcb)
if (sctp_is_addr_restricted(stcb, sctp_ifa))
continue;
/* found a valid local v4 address to use */
+ SCTP_IPI_ADDR_UNLOCK();
return (&sctp_ifa->address.sa);
} else if (sctp_ifa->address.sa.sa_family == AF_INET6 &&
stcb->asoc.ipv6_addr_legal) {
@@ -2271,11 +2422,13 @@ sctp_find_valid_localaddr(struct sctp_tcb *stcb)
continue;
/* found a valid local v6 address to use */
+ SCTP_IPI_ADDR_UNLOCK();
return (&sctp_ifa->address.sa);
}
}
}
/* no valid addresses found */
+ SCTP_IPI_ADDR_UNLOCK();
return (NULL);
}
@@ -2566,7 +2719,7 @@ sctp_process_initack_addresses(struct sctp_tcb *stcb, struct mbuf *m,
vrf_id = SCTP_DEFAULT_VRFID;
}
- sctp_ifa = sctp_find_ifa_by_addr(sa, vrf_id, 0);
+ sctp_ifa = sctp_find_ifa_by_addr(sa, vrf_id, SCTP_ADDR_NOT_LOCKED);
if (sctp_ifa == NULL) {
/* address doesn't exist anymore */
int status;
@@ -2773,8 +2926,10 @@ sctp_check_address_list_all(struct sctp_tcb *stcb, struct mbuf *m, int offset,
} else {
return;
}
+ SCTP_IPI_ADDR_LOCK();
vrf = sctp_find_vrf(vrf_id);
if (vrf == NULL) {
+ SCTP_IPI_ADDR_UNLOCK();
return;
}
/* go through all our known interfaces */
@@ -2798,6 +2953,7 @@ sctp_check_address_list_all(struct sctp_tcb *stcb, struct mbuf *m, int offset,
}
} /* end foreach ifa */
} /* end foreach ifn */
+ SCTP_IPI_ADDR_UNLOCK();
}
/*
@@ -2850,10 +3006,10 @@ sctp_addr_mgmt_ep_sa(struct sctp_inpcb *inp, struct sockaddr *sa,
ifa = sctp_ifap;
} else if (type == SCTP_ADD_IP_ADDRESS) {
/* For an add the address MUST be on the system */
- ifa = sctp_find_ifa_by_addr(sa, vrf_id, 0);
+ ifa = sctp_find_ifa_by_addr(sa, vrf_id, SCTP_ADDR_NOT_LOCKED);
} else if (type == SCTP_DEL_IP_ADDRESS) {
/* For a delete we need to find it in the inp */
- ifa = sctp_find_ifa_in_ep(inp, sa, 0);
+ ifa = sctp_find_ifa_in_ep(inp, sa, SCTP_ADDR_NOT_LOCKED);
} else {
ifa = NULL;
}
OpenPOWER on IntegriCloud