diff options
Diffstat (limited to 'sys/netinet/sctp_pcb.c')
-rw-r--r-- | sys/netinet/sctp_pcb.c | 257 |
1 files changed, 237 insertions, 20 deletions
diff --git a/sys/netinet/sctp_pcb.c b/sys/netinet/sctp_pcb.c index 30fc1cb..e7830fc 100644 --- a/sys/netinet/sctp_pcb.c +++ b/sys/netinet/sctp_pcb.c @@ -296,6 +296,110 @@ sctp_delete_ifn(struct sctp_ifn *sctp_ifnp, int hold_addr_lock) sctp_free_ifn(sctp_ifnp); } +void +sctp_mark_ifa_addr_down(uint32_t vrf_id, struct sockaddr *addr, const char *if_name, uint32_t ifn_index) +{ + struct sctp_vrf *vrf; + struct sctp_ifa *sctp_ifap = NULL; + + SCTP_IPI_ADDR_LOCK(); + vrf = sctp_find_vrf(vrf_id); + if (vrf == NULL) { + SCTPDBG(SCTP_DEBUG_PCB1, "Can't find vrf_id:%d\n", vrf_id); + goto out; + + } + sctp_ifap = sctp_find_ifa_by_addr(addr, vrf->vrf_id, SCTP_ADDR_LOCKED); + if (sctp_ifap == NULL) { + SCTPDBG(SCTP_DEBUG_PCB1, "Can't find sctp_ifap for address\n"); + goto out; + } + if (sctp_ifap->ifn_p == NULL) { + SCTPDBG(SCTP_DEBUG_PCB1, "IFA has no IFN - can't mark unuseable\n"); + goto out; + } + if (if_name) { + int len1, len2; + + len1 = strlen(if_name); + len2 = strlen(sctp_ifap->ifn_p->ifn_name); + if (len1 != len2) { + SCTPDBG(SCTP_DEBUG_PCB1, "IFN of ifa names different lenght %d vs %d - ignored\n", + len1, len2); + goto out; + } + if (strncmp(if_name, sctp_ifap->ifn_p->ifn_name, len1) != 0) { + SCTPDBG(SCTP_DEBUG_PCB1, "IFN %s of IFA not the same as %s\n", + sctp_ifap->ifn_p->ifn_name, + if_name); + goto out; + } + } else { + if (sctp_ifap->ifn_p->ifn_index != ifn_index) { + SCTPDBG(SCTP_DEBUG_PCB1, "IFA owned by ifn_index:%d down command for ifn_index:%d - ignored\n", + sctp_ifap->ifn_p->ifn_index, ifn_index); + goto out; + } + } + + sctp_ifap->localifa_flags &= (~SCTP_ADDR_VALID); + sctp_ifap->localifa_flags |= SCTP_ADDR_IFA_UNUSEABLE; +out: + SCTP_IPI_ADDR_UNLOCK(); +} + +void +sctp_mark_ifa_addr_up(uint32_t vrf_id, struct sockaddr *addr, const char *if_name, uint32_t ifn_index) +{ + struct sctp_vrf *vrf; + struct sctp_ifa *sctp_ifap = NULL; + + SCTP_IPI_ADDR_LOCK(); + vrf = sctp_find_vrf(vrf_id); + if (vrf == NULL) { + SCTPDBG(SCTP_DEBUG_PCB1, "Can't find vrf_id:%d\n", vrf_id); + goto out; + + } + sctp_ifap = sctp_find_ifa_by_addr(addr, vrf->vrf_id, SCTP_ADDR_LOCKED); + if (sctp_ifap == NULL) { + SCTPDBG(SCTP_DEBUG_PCB1, "Can't find sctp_ifap for address\n"); + goto out; + } + if (sctp_ifap->ifn_p == NULL) { + SCTPDBG(SCTP_DEBUG_PCB1, "IFA has no IFN - can't mark unuseable\n"); + goto out; + } + if (if_name) { + int len1, len2; + + len1 = strlen(if_name); + len2 = strlen(sctp_ifap->ifn_p->ifn_name); + if (len1 != len2) { + SCTPDBG(SCTP_DEBUG_PCB1, "IFN of ifa names different lenght %d vs %d - ignored\n", + len1, len2); + goto out; + } + if (strncmp(if_name, sctp_ifap->ifn_p->ifn_name, len1) != 0) { + SCTPDBG(SCTP_DEBUG_PCB1, "IFN %s of IFA not the same as %s\n", + sctp_ifap->ifn_p->ifn_name, + if_name); + goto out; + } + } else { + if (sctp_ifap->ifn_p->ifn_index != ifn_index) { + SCTPDBG(SCTP_DEBUG_PCB1, "IFA owned by ifn_index:%d down command for ifn_index:%d - ignored\n", + sctp_ifap->ifn_p->ifn_index, ifn_index); + goto out; + } + } + + sctp_ifap->localifa_flags &= (~SCTP_ADDR_IFA_UNUSEABLE); + sctp_ifap->localifa_flags |= SCTP_ADDR_VALID; +out: + SCTP_IPI_ADDR_UNLOCK(); +} + struct sctp_ifa * sctp_add_addr_to_vrf(uint32_t vrf_id, void *ifn, uint32_t ifn_index, @@ -361,7 +465,7 @@ sctp_add_addr_to_vrf(uint32_t vrf_id, void *ifn, uint32_t ifn_index, atomic_add_int(&sctppcbinfo.ipi_count_ifns, 1); new_ifn_af = 1; } - sctp_ifap = sctp_find_ifa_by_addr(addr, vrf->vrf_id, 1); + sctp_ifap = sctp_find_ifa_by_addr(addr, vrf->vrf_id, SCTP_ADDR_LOCKED); if (sctp_ifap) { /* Hmm, it already exists? */ if ((sctp_ifap->ifn_p) && @@ -384,15 +488,19 @@ sctp_add_addr_to_vrf(uint32_t vrf_id, void *ifn, uint32_t ifn_index, } else { if (sctp_ifap->ifn_p) { /* - * The first IFN gets the address, - * duplicates are ignored. + * The last IFN gets the address, old ones + * are deleted. */ if (new_ifn_af) { /* * Remove the created one that we * don't want */ - sctp_delete_ifn(sctp_ifnp, 1); + sctp_free_ifn(sctp_ifap->ifn_p); + if (sctp_ifap->ifn_p->refcount == 1) + sctp_delete_ifn(sctp_ifap->ifn_p, 1); + sctp_ifap->ifn_p = sctp_ifnp; + atomic_add_int(&sctp_ifap->ifn_p->refcount, 1); } goto exit_stage_left; } else { @@ -488,7 +596,7 @@ sctp_add_addr_to_vrf(uint32_t vrf_id, void *ifn, uint32_t ifn_index, */ SCTPDBG(SCTP_DEBUG_PCB1, "Lost and address change ???\n"); /* Opps, must decrement the count */ - sctp_del_addr_from_vrf(vrf_id, addr, ifn_index); + sctp_del_addr_from_vrf(vrf_id, addr, ifn_index, if_name); return (NULL); } SCTP_INCR_LADDR_COUNT(); @@ -516,7 +624,7 @@ sctp_add_addr_to_vrf(uint32_t vrf_id, void *ifn, uint32_t ifn_index, void sctp_del_addr_from_vrf(uint32_t vrf_id, struct sockaddr *addr, - uint32_t ifn_index) + uint32_t ifn_index, const char *if_name) { struct sctp_vrf *vrf; struct sctp_ifa *sctp_ifap = NULL; @@ -528,8 +636,51 @@ sctp_del_addr_from_vrf(uint32_t vrf_id, struct sockaddr *addr, SCTP_PRINTF("Can't find vrf_id:%d\n", vrf_id); goto out_now; } - sctp_ifap = sctp_find_ifa_by_addr(addr, vrf->vrf_id, 1); + sctp_ifap = sctp_find_ifa_by_addr(addr, vrf->vrf_id, SCTP_ADDR_LOCKED); if (sctp_ifap) { + /* Validate the delete */ + if (sctp_ifap->ifn_p) { + int valid = 0; + + /*- + * The name has priority over the ifn_index + * if its given. We do this especially for + * panda who might recycle indexes fast. + */ + if (if_name) { + int len1, len2; + + len1 = min(SCTP_IFNAMSIZ, strlen(if_name)); + len2 = min(SCTP_IFNAMSIZ, strlen(sctp_ifap->ifn_p->ifn_name)); + if (len1 && len2 && (len1 == len2)) { + /* we can compare them */ + if (strncmp(if_name, sctp_ifap->ifn_p->ifn_name, len1) == 0) { + /* + * They match its a correct + * delete + */ + valid = 1; + } + } + } + if (!valid) { + /* last ditch check ifn_index */ + if (ifn_index == sctp_ifap->ifn_p->ifn_index) { + valid = 1; + } + } + if (!valid) { +#ifdef SCTP_DEBUG + SCTPDBG(SCTP_DEBUG_PCB1, "Deleting address:"); + SCTPDBG_ADDR(SCTP_DEBUG_PCB1, addr); + SCTPDBG(SCTP_DEBUG_PCB1, "ifn:%d ifname:%s does not match addresses\n", + ifn_index, ((if_name == NULL) ? "NULL" : if_name)); + SCTPDBG(SCTP_DEBUG_PCB1, "ifn:%d ifname:%s - ignoring delete\n", + sctp_ifap->ifn_p->ifn_index, sctp_ifap->ifn_p->ifn_name); +#endif + return; + } + } sctp_ifap->localifa_flags &= SCTP_ADDR_VALID; sctp_ifap->localifa_flags |= SCTP_BEING_DELETED; vrf->total_ifa_count--; @@ -1857,8 +2008,8 @@ sctp_inpcb_alloc(struct socket *so, uint32_t vrf_id) * unsupported socket type (RAW, etc)- in case we missed it * in protosw */ - SCTP_ZONE_FREE(sctppcbinfo.ipi_zone_ep, inp); SCTP_LTRACE_ERR_RET(inp, NULL, NULL, SCTP_FROM_SCTP_PCB, EOPNOTSUPP); + SCTP_ZONE_FREE(sctppcbinfo.ipi_zone_ep, inp); return (EOPNOTSUPP); } if (sctp_default_frag_interleave == SCTP_FRAG_LEVEL_1) { @@ -1875,8 +2026,8 @@ sctp_inpcb_alloc(struct socket *so, uint32_t vrf_id) &inp->sctp_hashmark); if (inp->sctp_tcbhash == NULL) { SCTP_PRINTF("Out of SCTP-INPCB->hashinit - no resources\n"); - SCTP_ZONE_FREE(sctppcbinfo.ipi_zone_ep, inp); SCTP_LTRACE_ERR_RET(inp, NULL, NULL, SCTP_FROM_SCTP_PCB, ENOBUFS); + SCTP_ZONE_FREE(sctppcbinfo.ipi_zone_ep, inp); return (ENOBUFS); } inp->def_vrf_id = vrf_id; @@ -2086,6 +2237,7 @@ sctp_move_pcb_and_assoc(struct sctp_inpcb *old_inp, struct sctp_inpcb *new_inp, stcb->asoc.shut_guard_timer.ep = (void *)new_inp; stcb->asoc.autoclose_timer.ep = (void *)new_inp; stcb->asoc.delayed_event_timer.ep = (void *)new_inp; + stcb->asoc.delete_prim_timer.ep = (void *)new_inp; /* now what about the nets? */ TAILQ_FOREACH(net, &stcb->asoc.nets, sctp_next) { net->pmtu_timer.ep = (void *)new_inp; @@ -2410,8 +2562,10 @@ sctp_inpcb_bind(struct socket *so, struct sockaddr *addr, */ if (sctp_mobility_base == 0) { sctp_mobility_feature_off(inp, SCTP_MOBILITY_BASE); + sctp_mobility_feature_off(inp, SCTP_MOBILITY_PRIM_DELETED); } else { sctp_mobility_feature_on(inp, SCTP_MOBILITY_BASE); + sctp_mobility_feature_off(inp, SCTP_MOBILITY_PRIM_DELETED); } /* * set the automatic mobility_fasthandoff from kernel flag @@ -2419,10 +2573,10 @@ sctp_inpcb_bind(struct socket *so, struct sockaddr *addr, */ if (sctp_mobility_fasthandoff == 0) { sctp_mobility_feature_off(inp, SCTP_MOBILITY_FASTHANDOFF); - sctp_mobility_feature_off(inp, SCTP_MOBILITY_DO_FASTHANDOFF); + sctp_mobility_feature_off(inp, SCTP_MOBILITY_PRIM_DELETED); } else { sctp_mobility_feature_on(inp, SCTP_MOBILITY_FASTHANDOFF); - sctp_mobility_feature_off(inp, SCTP_MOBILITY_DO_FASTHANDOFF); + sctp_mobility_feature_off(inp, SCTP_MOBILITY_PRIM_DELETED); } } else { /* @@ -2466,7 +2620,7 @@ sctp_inpcb_bind(struct socket *so, struct sockaddr *addr, * (Panda). */ ifa = sctp_find_ifa_by_addr((struct sockaddr *)&store_sa, - vrf_id, 0); + vrf_id, SCTP_ADDR_NOT_LOCKED); } if (ifa == NULL) { /* Can't find an interface with that address */ @@ -2625,6 +2779,10 @@ sctp_inpcb_free(struct sctp_inpcb *inp, int immediate, int from) * via the sockets layer. */ inp->sctp_flags &= ~SCTP_PCB_FLAGS_CLOSE_IP; + /* socket is gone, so no more wakeups allowed */ + inp->sctp_flags |= SCTP_PCB_FLAGS_DONT_WAKE; + inp->sctp_flags &= ~SCTP_PCB_FLAGS_WAKEINPUT; + inp->sctp_flags &= ~SCTP_PCB_FLAGS_WAKEOUTPUT; } sctp_timer_stop(SCTP_TIMER_TYPE_NEWCOOKIE, inp, NULL, NULL, SCTP_FROM_SCTP_PCB + SCTP_LOC_1); @@ -3039,7 +3197,7 @@ sctp_is_address_on_local_host(struct sockaddr *addr, uint32_t vrf_id) { struct sctp_ifa *sctp_ifa; - sctp_ifa = sctp_find_ifa_by_addr(addr, vrf_id, 0); + sctp_ifa = sctp_find_ifa_by_addr(addr, vrf_id, SCTP_ADDR_NOT_LOCKED); if (sctp_ifa) { return (1); } else { @@ -3541,6 +3699,8 @@ sctp_aloc_assoc(struct sctp_inpcb *inp, struct sockaddr *firstaddr, sctppcbinfo.hashasocmark)]; /* put it in the bucket in the vtag hash of assoc's for the system */ LIST_INSERT_HEAD(head, stcb, sctp_asocs); + sctp_delete_from_timewait(stcb->asoc.my_vtag); + SCTP_INP_INFO_WUNLOCK(); if ((err = sctp_add_remote_addr(stcb, firstaddr, SCTP_DO_SETSCOPE, SCTP_ALLOC_ASOC))) { @@ -3570,6 +3730,7 @@ sctp_aloc_assoc(struct sctp_inpcb *inp, struct sockaddr *firstaddr, SCTP_OS_TIMER_INIT(&asoc->shut_guard_timer.timer); SCTP_OS_TIMER_INIT(&asoc->autoclose_timer.timer); SCTP_OS_TIMER_INIT(&asoc->delayed_event_timer.timer); + SCTP_OS_TIMER_INIT(&asoc->delete_prim_timer.timer); LIST_INSERT_HEAD(&inp->sctp_asoc_list, stcb, sctp_tcblist); /* now file the port under the hash as well */ @@ -3597,6 +3758,26 @@ sctp_remove_net(struct sctp_tcb *stcb, struct sctp_nets *net) struct sctp_nets *lnet; lnet = TAILQ_FIRST(&asoc->nets); + /* + * Mobility adaptation Ideally, if deleted destination is + * the primary, it becomes a fast retransmission trigger by + * the subsequent SET PRIMARY. (by micchie) + */ + if (sctp_is_mobility_feature_on(stcb->sctp_ep, + SCTP_MOBILITY_FASTHANDOFF)) { + SCTPDBG(SCTP_DEBUG_ASCONF1, "remove_net: primary dst is deleting\n"); + if (asoc->deleted_primary != NULL) { + SCTPDBG(SCTP_DEBUG_ASCONF1, "remove_net: deleted primary may be already stored\n"); + goto leave; + } + asoc->deleted_primary = net; + atomic_add_int(&net->ref_count, 1); + sctp_mobility_feature_on(stcb->sctp_ep, + SCTP_MOBILITY_PRIM_DELETED); + sctp_timer_start(SCTP_TIMER_TYPE_PRIM_DELETED, + stcb->sctp_ep, stcb, NULL); + } +leave: /* Try to find a confirmed primary */ asoc->primary_destination = sctp_find_alternate_net(stcb, lnet, 0); } @@ -3652,9 +3833,33 @@ sctp_del_remote_addr(struct sctp_tcb *stcb, struct sockaddr *remaddr) return (-2); } +void +sctp_delete_from_timewait(uint32_t tag) +{ + struct sctpvtaghead *chain; + struct sctp_tagblock *twait_block; + int found = 0; + int i; + + chain = &sctppcbinfo.vtag_timewait[(tag % SCTP_STACK_VTAG_HASH_SIZE)]; + if (!SCTP_LIST_EMPTY(chain)) { + LIST_FOREACH(twait_block, chain, sctp_nxt_tagblock) { + for (i = 0; i < SCTP_NUMBER_IN_VTAG_BLOCK; i++) { + if (twait_block->vtag_block[i].v_tag == tag) { + twait_block->vtag_block[i].tv_sec_at_expire = 0; + twait_block->vtag_block[i].v_tag = 0; + found = 1; + break; + } + } + if (found) + break; + } + } +} void -sctp_add_vtag_to_timewait(struct sctp_inpcb *inp, uint32_t tag, uint32_t time) +sctp_add_vtag_to_timewait(uint32_t tag, uint32_t time) { struct sctpvtaghead *chain; struct sctp_tagblock *twait_block; @@ -3843,6 +4048,9 @@ sctp_free_assoc(struct sctp_inpcb *inp, struct sctp_tcb *stcb, int from_inpcbfre asoc->shut_guard_timer.self = NULL; (void)SCTP_OS_TIMER_STOP(&asoc->delayed_event_timer.timer); asoc->delayed_event_timer.self = NULL; + /* Mobility adaptation */ + (void)SCTP_OS_TIMER_STOP(&asoc->delete_prim_timer.timer); + asoc->delete_prim_timer.self = NULL; TAILQ_FOREACH(net, &asoc->nets, sctp_next) { (void)SCTP_OS_TIMER_STOP(&net->fr_timer.timer); net->fr_timer.self = NULL; @@ -3999,7 +4207,7 @@ sctp_free_assoc(struct sctp_inpcb *inp, struct sctp_tcb *stcb, int from_inpcbfre } /* pull from vtag hash */ LIST_REMOVE(stcb, sctp_asocs); - sctp_add_vtag_to_timewait(inp, asoc->my_vtag, SCTP_TIME_WAIT); + sctp_add_vtag_to_timewait(asoc->my_vtag, SCTP_TIME_WAIT); /* * Now restop the timers to be sure - this is paranoia at is finest! @@ -4246,11 +4454,9 @@ sctp_free_assoc(struct sctp_inpcb *inp, struct sctp_tcb *stcb, int from_inpcbfre SCTP_FREE(aparam, SCTP_M_ASC_ADDR); } while (!TAILQ_EMPTY(&asoc->asconf_ack_sent)) { + /* sa_ignore FREED_MEMORY */ aack = TAILQ_FIRST(&asoc->asconf_ack_sent); TAILQ_REMOVE(&asoc->asconf_ack_sent, aack, next); - if (aack->last_sent_to != NULL) { - sctp_free_remote_addr(aack->last_sent_to); - } if (aack->data != NULL) { sctp_m_freem(aack->data); } @@ -4696,6 +4902,7 @@ sctp_pcb_init() * the sctp_init() funciton. */ int i; + struct timeval tv; if (sctp_pcb_initialized != 0) { /* error I was called twice */ @@ -4704,7 +4911,9 @@ sctp_pcb_init() sctp_pcb_initialized = 1; bzero(&sctpstat, sizeof(struct sctpstat)); - (void)SCTP_GETTIME_TIMEVAL(&sctpstat.sctps_discontinuitytime); + (void)SCTP_GETTIME_TIMEVAL(&tv); + sctpstat.sctps_discontinuitytime.tv_sec = (uint32_t) tv.tv_sec; + sctpstat.sctps_discontinuitytime.tv_usec = (uint32_t) tv.tv_usec; /* init the empty list of (All) Endpoints */ LIST_INIT(&sctppcbinfo.listhead); @@ -5542,7 +5751,15 @@ check_time_wait: } } } - /* Not found, ok to use the tag */ + /*- + * Not found, ok to use the tag, add it to the time wait hash + * as well this will prevent two sucessive cookies from getting + * the same tag or two inits sent quickly on multi-processors. + * We only keep the tag for the life of a cookie and when we + * add this tag to the assoc hash we need to purge it from + * the t-wait hash. + */ + sctp_add_vtag_to_timewait(tag, TICKS_TO_SEC(inp->sctp_ep.def_cookie_life)); SCTP_INP_INFO_WUNLOCK(); return (1); } |