summaryrefslogtreecommitdiffstats
path: root/sys/netinet/sctp_asconf.c
diff options
context:
space:
mode:
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