diff options
Diffstat (limited to 'sys/netinet/sctp_asconf.c')
-rw-r--r-- | sys/netinet/sctp_asconf.c | 760 |
1 files changed, 354 insertions, 406 deletions
diff --git a/sys/netinet/sctp_asconf.c b/sys/netinet/sctp_asconf.c index 8abab9d..d4f088b 100644 --- a/sys/netinet/sctp_asconf.c +++ b/sys/netinet/sctp_asconf.c @@ -34,6 +34,7 @@ __FBSDID("$FreeBSD$"); #include <netinet/sctp_os.h> #include <netinet/sctp_var.h> +#include <netinet/sctp_sysctl.h> #include <netinet/sctp_pcb.h> #include <netinet/sctp_header.h> #include <netinet/sctputil.h> @@ -47,8 +48,6 @@ __FBSDID("$FreeBSD$"); * SCTP_DEBUG_ASCONF2: detailed info */ #ifdef SCTP_DEBUG -extern uint32_t sctp_debug_on; - #endif /* SCTP_DEBUG */ @@ -316,7 +315,6 @@ sctp_process_asconf_add_ip(struct mbuf *m, struct sctp_asconf_paramhdr *aph, } sctp_timer_stop(SCTP_TIMER_TYPE_HEARTBEAT, stcb->sctp_ep, stcb, NULL, SCTP_FROM_SCTP_ASCONF + SCTP_LOC_1); sctp_timer_start(SCTP_TIMER_TYPE_HEARTBEAT, stcb->sctp_ep, stcb, NULL); - } return m_reply; @@ -990,12 +988,12 @@ sctp_asconf_cleanup(struct sctp_tcb *stcb, struct sctp_nets *net) /* * process an ADD/DELETE IP ack from peer. - * addr corresponding ifaddr to the address being added/deleted. + * addr corresponding sctp_ifa to the address being added/deleted. * type: SCTP_ADD_IP_ADDRESS or SCTP_DEL_IP_ADDRESS. * flag: 1=success, 0=failure. */ static void -sctp_asconf_addr_mgmt_ack(struct sctp_tcb *stcb, struct ifaddr *addr, +sctp_asconf_addr_mgmt_ack(struct sctp_tcb *stcb, struct sctp_ifa *addr, uint16_t type, uint32_t flag) { /* @@ -1023,7 +1021,7 @@ sctp_asconf_addr_mgmt_ack(struct sctp_tcb *stcb, struct ifaddr *addr, * for add. If a duplicate operation is found, ignore the new one. */ static uint32_t -sctp_asconf_queue_add(struct sctp_tcb *stcb, struct ifaddr *ifa, uint16_t type) +sctp_asconf_queue_add(struct sctp_tcb *stcb, struct sctp_ifa *ifa, uint16_t type) { struct sctp_asconf_addr *aa, *aa_next; struct sockaddr *sa; @@ -1037,7 +1035,7 @@ sctp_asconf_queue_add(struct sctp_tcb *stcb, struct ifaddr *ifa, uint16_t type) aa = aa_next) { aa_next = TAILQ_NEXT(aa, next); /* address match? */ - if (sctp_asconf_addr_match(aa, ifa->ifa_addr) == 0) + if (sctp_asconf_addr_match(aa, &ifa->address.sa) == 0) continue; /* is the request already in queue (sent or not) */ if (aa->ap.aph.ph.param_type == type) { @@ -1077,11 +1075,11 @@ sctp_asconf_queue_add(struct sctp_tcb *stcb, struct ifaddr *ifa, uint16_t type) aa->ap.aph.ph.param_type = type; aa->ifa = ifa; /* correlation_id filled in during send routine later... */ - if (ifa->ifa_addr->sa_family == AF_INET6) { + if (ifa->address.sa.sa_family == AF_INET6) { /* IPv6 address */ struct sockaddr_in6 *sin6; - sin6 = (struct sockaddr_in6 *)ifa->ifa_addr; + sin6 = (struct sockaddr_in6 *)&ifa->address.sa; sa = (struct sockaddr *)sin6; aa->ap.addrp.ph.param_type = SCTP_IPV6_ADDRESS; aa->ap.addrp.ph.param_length = (sizeof(struct sctp_ipv6addr_param)); @@ -1090,9 +1088,9 @@ sctp_asconf_queue_add(struct sctp_tcb *stcb, struct ifaddr *ifa, uint16_t type) sizeof(struct sctp_ipv6addr_param); memcpy(&aa->ap.addrp.addr, &sin6->sin6_addr, sizeof(struct in6_addr)); - } else if (ifa->ifa_addr->sa_family == AF_INET) { + } else if (ifa->address.sa.sa_family == AF_INET) { /* IPv4 address */ - struct sockaddr_in *sin = (struct sockaddr_in *)ifa->ifa_addr; + struct sockaddr_in *sin = (struct sockaddr_in *)&ifa->address.sa; sa = (struct sockaddr *)sin; aa->ap.addrp.ph.param_type = SCTP_IPV4_ADDRESS; @@ -1153,6 +1151,7 @@ sctp_asconf_queue_add_sa(struct sctp_tcb *stcb, struct sockaddr *sa, uint16_t type) { struct sctp_asconf_addr *aa, *aa_next; + uint32_t vrf_id; /* see if peer supports ASCONF */ if (stcb->asoc.peer_supports_asconf == 0) { @@ -1208,8 +1207,9 @@ sctp_asconf_queue_add_sa(struct sctp_tcb *stcb, struct sockaddr *sa, } /* fill in asconf address parameter fields */ /* top level elements are "networked" during send */ + vrf_id = SCTP_DEFAULT_VRFID; aa->ap.aph.ph.param_type = type; - aa->ifa = sctp_find_ifa_by_addr(sa); + aa->ifa = sctp_find_ifa_by_addr(sa, vrf_id, 0); /* correlation_id filled in during send routine later... */ if (sa->sa_family == AF_INET6) { /* IPv6 address */ @@ -1590,35 +1590,6 @@ sctp_handle_asconf_ack(struct mbuf *m, int offset, } } -/* is this an interface that we care about at all? */ -static uint32_t -sctp_is_desired_interface_type(struct ifaddr *ifa) -{ - int result; - - /* check the interface type to see if it's one we care about */ - switch (ifa->ifa_ifp->if_type) { - case IFT_ETHER: - case IFT_ISO88023: - case IFT_ISO88025: - case IFT_STARLAN: - case IFT_P10: - case IFT_P80: - case IFT_HY: - case IFT_FDDI: - case IFT_PPP: - case IFT_XETHER: - case IFT_SLIP: - case IFT_GIF: - result = 1; - break; - default: - result = 0; - } - - return (result); -} - static uint32_t sctp_is_scopeid_in_nets(struct sctp_tcb *stcb, struct sockaddr *sa) { @@ -1656,7 +1627,7 @@ sctp_is_scopeid_in_nets(struct sctp_tcb *stcb, struct sockaddr *sa) */ static void sctp_addr_mgmt_assoc(struct sctp_inpcb *inp, struct sctp_tcb *stcb, - struct ifaddr *ifa, uint16_t type) + struct sctp_ifa *ifa, uint16_t type) { int status; @@ -1672,23 +1643,17 @@ sctp_addr_mgmt_assoc(struct sctp_inpcb *inp, struct sctp_tcb *stcb, */ /* first, make sure it's a good address family */ - if (ifa->ifa_addr->sa_family != AF_INET6 && - ifa->ifa_addr->sa_family != AF_INET) { + if (ifa->address.sa.sa_family != AF_INET6 && + ifa->address.sa.sa_family != AF_INET) { return; } /* make sure we're "allowed" to add this type of addr */ - if (ifa->ifa_addr->sa_family == AF_INET6) { - struct in6_ifaddr *ifa6; - + if (ifa->address.sa.sa_family == AF_INET6) { /* invalid if we're not a v6 endpoint */ if ((inp->sctp_flags & SCTP_PCB_FLAGS_BOUND_V6) == 0) return; /* is the v6 addr really valid ? */ - ifa6 = (struct in6_ifaddr *)ifa; - if (IFA6_IS_DEPRECATED(ifa6) || - (ifa6->ia6_flags & - (IN6_IFF_DETACHED | IN6_IFF_ANYCAST | IN6_IFF_NOTREADY))) { - /* can't use an invalid address */ + if (ifa->localifa_flags & SCTP_ADDR_IFA_UNUSEABLE) { return; } } @@ -1700,27 +1665,16 @@ sctp_addr_mgmt_assoc(struct sctp_inpcb *inp, struct sctp_tcb *stcb, * Recall that this routine is only called for the subset bound * w/ASCONF allowed case. */ - - /* - * do a scope_id check against any link local addresses in the - * destination nets list to see if we should put this local address - * on the pending list or not eg. don't put on the list if we have a - * link local destination with the same scope_id - */ - if (type == SCTP_ADD_IP_ADDRESS) { - if (sctp_is_scopeid_in_nets(stcb, ifa->ifa_addr) == 0) { - sctp_add_local_addr_assoc(stcb, ifa); - } - } + sctp_add_local_addr_assoc(stcb, ifa, 1); /* * check address scope if address is out of scope, don't queue * anything... note: this would leave the address on both inp and * asoc lists */ - if (ifa->ifa_addr->sa_family == AF_INET6) { + if (ifa->address.sa.sa_family == AF_INET6) { struct sockaddr_in6 *sin6; - sin6 = (struct sockaddr_in6 *)ifa->ifa_addr; + sin6 = (struct sockaddr_in6 *)&ifa->address.sin6; if (IN6_IS_ADDR_UNSPECIFIED(&sin6->sin6_addr)) { /* we skip unspecifed addresses */ return; @@ -1730,7 +1684,7 @@ sctp_addr_mgmt_assoc(struct sctp_inpcb *inp, struct sctp_tcb *stcb, return; } /* is it the right link local scope? */ - if (sctp_is_scopeid_in_nets(stcb, ifa->ifa_addr) == 0) { + if (sctp_is_scopeid_in_nets(stcb, &ifa->address.sa) == 0) { return; } } @@ -1738,7 +1692,7 @@ sctp_addr_mgmt_assoc(struct sctp_inpcb *inp, struct sctp_tcb *stcb, IN6_IS_ADDR_SITELOCAL(&sin6->sin6_addr)) { return; } - } else if (ifa->ifa_addr->sa_family == AF_INET) { + } else if (ifa->address.sa.sa_family == AF_INET) { struct sockaddr_in *sin; struct in6pcb *inp6; @@ -1748,7 +1702,7 @@ sctp_addr_mgmt_assoc(struct sctp_inpcb *inp, struct sctp_tcb *stcb, SCTP_IPV6_V6ONLY(inp6)) return; - sin = (struct sockaddr_in *)ifa->ifa_addr; + sin = (struct sockaddr_in *)&ifa->address.sa; if (sin->sin_addr.s_addr == 0) { /* we skip unspecifed addresses */ return; @@ -1759,16 +1713,10 @@ sctp_addr_mgmt_assoc(struct sctp_inpcb *inp, struct sctp_tcb *stcb, } } else { /* else, not AF_INET or AF_INET6, so skip */ -#ifdef SCTP_DEBUG - if (sctp_debug_on & SCTP_DEBUG_ASCONF1) { - printf("addr_mgmt_assoc: not AF_INET or AF_INET6\n"); - } -#endif /* SCTP_DEBUG */ return; } /* queue an asconf for this address add/delete */ - if (sctp_is_feature_on(inp, SCTP_PCB_FLAGS_DO_ASCONF)) { /* does the peer do asconf? */ if (stcb->asoc.peer_supports_asconf) { @@ -1786,247 +1734,266 @@ sctp_addr_mgmt_assoc(struct sctp_inpcb *inp, struct sctp_tcb *stcb, stcb, stcb->asoc.primary_destination); } } - } else { - /* this is the boundall, no ASCONF case */ -#if 0 - /* Peter: Fixe me? why the if 0? */ - /* - * assume kernel will delete this very shortly; add done - * above - */ - if (type == SCTP_DEL_IP_ADDRESS) { - /* if deleting, add this addr to the do not use list */ - sctp_add_local_addr_assoc(stcb, ifa); - } -#endif } } -static void -sctp_addr_mgmt_ep(struct sctp_inpcb *inp, struct ifaddr *ifa, uint16_t type) -{ - struct sctp_tcb *stcb; - - - SCTP_INP_WLOCK(inp); - /* make sure we're "allowed" to add this type of addr */ - if (ifa->ifa_addr->sa_family == AF_INET6) { - struct in6_ifaddr *ifa6; - /* invalid if we're not a v6 endpoint */ - if ((inp->sctp_flags & SCTP_PCB_FLAGS_BOUND_V6) == 0) { - SCTP_INP_WUNLOCK(inp); - return; - } - /* is the v6 addr really valid ? */ - ifa6 = (struct in6_ifaddr *)ifa; - if (IFA6_IS_DEPRECATED(ifa6) || - (ifa6->ia6_flags & - (IN6_IFF_DETACHED | IN6_IFF_ANYCAST | IN6_IFF_NOTREADY))) { - /* can't use an invalid address */ - SCTP_INP_WUNLOCK(inp); - return; - } - } else if (ifa->ifa_addr->sa_family == AF_INET) { - /* invalid if we are a v6 only endpoint */ - struct in6pcb *inp6; - - inp6 = (struct in6pcb *)&inp->ip_inp.inp; - - if ((inp->sctp_flags & SCTP_PCB_FLAGS_BOUND_V6) && - SCTP_IPV6_V6ONLY(inp6)) { - SCTP_INP_WUNLOCK(inp); - return; - } - } else { - /* invalid address family */ - SCTP_INP_WUNLOCK(inp); - return; - } - /* is this endpoint subset bound ? */ - if ((inp->sctp_flags & SCTP_PCB_FLAGS_BOUNDALL) == 0) { - /* subset bound endpoint */ - if (sctp_is_feature_off(inp, SCTP_PCB_FLAGS_DO_ASCONF)) { - /* - * subset bound, but ASCONFs not allowed... if - * adding, nothing to do, since not allowed if - * deleting, remove address from endpoint peer will - * have to "timeout" this addr - */ - if (type == SCTP_DEL_IP_ADDRESS) { - sctp_del_local_addr_ep(inp, ifa); +int +sctp_iterator_ep(struct sctp_inpcb *inp, void *ptr, uint32_t val) +{ + struct sctp_asconf_iterator *asc; + struct sctp_ifa *ifa; + struct sctp_laddr *l; + int type; + int cnt_invalid = 0; + + asc = (struct sctp_asconf_iterator *)ptr; + LIST_FOREACH(l, &asc->list_of_work, sctp_nxt_addr) { + ifa = l->ifa; + type = l->action; + if (ifa->address.sa.sa_family == AF_INET6) { + /* invalid if we're not a v6 endpoint */ + if ((inp->sctp_flags & SCTP_PCB_FLAGS_BOUND_V6) == 0) { + cnt_invalid++; + if (asc->cnt == cnt_invalid) + return (1); + else + continue; } - /* no asconfs to queue for this inp... */ - SCTP_INP_WUNLOCK(inp); - return; - } else { - /* - * subset bound, ASCONFs allowed... if adding, add - * address to endpoint list if deleting, remove - * address from endpoint - */ - if (type == SCTP_ADD_IP_ADDRESS) { - sctp_add_local_addr_ep(inp, ifa); - } else { - sctp_del_local_addr_ep(inp, ifa); + } else if (ifa->address.sa.sa_family == AF_INET) { + /* invalid if we are a v6 only endpoint */ + struct in6pcb *inp6; + + inp6 = (struct in6pcb *)&inp->ip_inp.inp; + if ((inp->sctp_flags & SCTP_PCB_FLAGS_BOUND_V6) && + SCTP_IPV6_V6ONLY(inp6)) { + cnt_invalid++; + if (asc->cnt == cnt_invalid) + return (1); + else + continue; } - /* drop through and notify all asocs */ + } else { + /* invalid address family */ + cnt_invalid++; + if (asc->cnt == cnt_invalid) + return (1); + else + continue; } } - /* process for all associations for this endpoint */ - LIST_FOREACH(stcb, &inp->sctp_asoc_list, sctp_tcblist) { - SCTP_TCB_LOCK(stcb); - sctp_addr_mgmt_assoc(inp, stcb, ifa, type); - SCTP_TCB_UNLOCK(stcb); - } - SCTP_INP_WUNLOCK(inp); + return (0); } -/* - * restrict the use of this address - */ -static void -sctp_addr_mgmt_restrict_ep(struct sctp_inpcb *inp, struct ifaddr *ifa) +int +sctp_iterator_ep_end(struct sctp_inpcb *inp, void *ptr, uint32_t val) { - struct sctp_tcb *stcb; - - /* is this endpoint bound to all? */ - if ((inp->sctp_flags & SCTP_PCB_FLAGS_BOUNDALL) == 0) { - /* - * Nothing to do for subset bound case. Allow sctp_bindx() - * to manage the address lists - */ - return; - } - SCTP_INP_RLOCK(inp); - /* process for all associations for this endpoint */ - LIST_FOREACH(stcb, &inp->sctp_asoc_list, sctp_tcblist) { - /* put this address on the "pending/do not use yet" list */ - SCTP_TCB_LOCK(stcb); - sctp_add_local_addr_assoc(stcb, ifa); - SCTP_TCB_UNLOCK(stcb); + struct sctp_ifa *ifa; + struct sctp_asconf_iterator *asc; + struct sctp_laddr *laddr, *nladdr, *l; + + /* Only for specific case not bound all */ + asc = (struct sctp_asconf_iterator *)ptr; + LIST_FOREACH(l, &asc->list_of_work, sctp_nxt_addr) { + ifa = l->ifa; + if (l->action == SCTP_ADD_IP_ADDRESS) { + LIST_FOREACH(laddr, &inp->sctp_addr_list, sctp_nxt_addr) { + if (laddr->ifa == ifa) { + laddr->action = 0; + break; + } + } + } else if (l->action == SCTP_DEL_IP_ADDRESS) { + laddr = LIST_FIRST(&inp->sctp_addr_list); + while (laddr) { + nladdr = LIST_NEXT(laddr, sctp_nxt_addr); + /* remove only after all guys are done */ + if (laddr->ifa == ifa) { + sctp_del_local_addr_ep(inp, ifa); + } + laddr = nladdr; + } + } } - SCTP_INP_RUNLOCK(inp); + return (0); } -/* - * this is only called for kernel initiated address changes eg. it will check - * the PCB_FLAGS_AUTO_ASCONF flag - */ -static void -sctp_addr_mgmt(struct ifaddr *ifa, uint16_t type) +void +sctp_iterator_stcb(struct sctp_inpcb *inp, struct sctp_tcb *stcb, void *ptr, + uint32_t val) { - struct sockaddr *sa; - struct sctp_inpcb *inp; - - /* make sure we care about this interface... */ - if (!sctp_is_desired_interface_type(ifa)) { - return; - } - sa = ifa->ifa_addr; - if (sa->sa_family != AF_INET && sa->sa_family != AF_INET6) - return; - -#ifdef SCTP_DEBUG - if (sctp_debug_on & SCTP_DEBUG_ASCONF1) { - if (type == SCTP_ADD_IP_ADDRESS) - printf("sctp_addr_mgmt: kernel adds "); - else - printf("sctp_addr_mgmt: kernel deletes "); - sctp_print_address(sa); - } -#endif /* SCTP_DEBUG */ + struct sctp_asconf_iterator *asc; + struct sctp_ifa *ifa; + struct sctp_laddr *l; + int cnt_invalid = 0; + int type, status; + + asc = (struct sctp_asconf_iterator *)ptr; + LIST_FOREACH(l, &asc->list_of_work, sctp_nxt_addr) { + ifa = l->ifa; + type = l->action; + /* Same checks again for assoc */ + if (ifa->address.sa.sa_family == AF_INET6) { + /* invalid if we're not a v6 endpoint */ + struct sockaddr_in6 *sin6; + + if ((inp->sctp_flags & SCTP_PCB_FLAGS_BOUND_V6) == 0) { + cnt_invalid++; + if (asc->cnt == cnt_invalid) + return; + else + continue; + } + sin6 = (struct sockaddr_in6 *)&ifa->address.sin6; + if (IN6_IS_ADDR_UNSPECIFIED(&sin6->sin6_addr)) { + /* we skip unspecifed addresses */ + continue; + } + if (IN6_IS_ADDR_LINKLOCAL(&sin6->sin6_addr)) { + if (stcb->asoc.local_scope == 0) { + continue; + } + /* is it the right link local scope? */ + if (sctp_is_scopeid_in_nets(stcb, &ifa->address.sa) == 0) { + continue; + } + } + } else if (ifa->address.sa.sa_family == AF_INET) { + /* invalid if we are a v6 only endpoint */ + struct in6pcb *inp6; + struct sockaddr_in *sin; + + inp6 = (struct in6pcb *)&inp->ip_inp.inp; + /* invalid if we are a v6 only endpoint */ + if ((inp->sctp_flags & SCTP_PCB_FLAGS_BOUND_V6) && + SCTP_IPV6_V6ONLY(inp6)) + continue; - /* go through all our PCB's */ - LIST_FOREACH(inp, &sctppcbinfo.listhead, sctp_list) { - if (sctp_is_feature_on(inp, SCTP_PCB_FLAGS_AUTO_ASCONF)) { - sctp_addr_mgmt_ep(inp, ifa, type); + sin = (struct sockaddr_in *)&ifa->address.sa; + if (sin->sin_addr.s_addr == 0) { + /* we skip unspecifed addresses */ + continue; + } + if (stcb->asoc.ipv4_local_scope == 0 && + IN4_ISPRIVATE_ADDRESS(&sin->sin_addr)) { + continue;; + } + if ((inp->sctp_flags & SCTP_PCB_FLAGS_BOUND_V6) && + SCTP_IPV6_V6ONLY(inp6)) { + cnt_invalid++; + if (asc->cnt == cnt_invalid) + return; + else + continue; + } } else { - /* this address is going away anyways... */ - if (type == SCTP_DEL_IP_ADDRESS) + /* invalid address family */ + cnt_invalid++; + if (asc->cnt == cnt_invalid) return; - /* (temporarily) restrict this address */ - sctp_addr_mgmt_restrict_ep(inp, ifa); + else + continue; } - /* else, not allowing automatic asconf's, so ignore */ - } -} - -/* - * add/delete IP address requests from kernel (via routing change) assumed - * that the address is non-broadcast, non-multicast all addresses are passed - * from any type of interface-- need to filter duplicate addresses may get - * requested - */ - -void -sctp_add_ip_address(struct ifaddr *ifa) -{ - sctp_addr_mgmt(ifa, SCTP_ADD_IP_ADDRESS); -} - -void -sctp_delete_ip_address(struct ifaddr *ifa) -{ - struct sctp_inpcb *inp; - - /* process the delete */ - sctp_addr_mgmt(ifa, SCTP_DEL_IP_ADDRESS); - /* - * need to remove this ifaddr from any cached routes and also any - * from any assoc "restricted/pending" lists - */ - /* make sure we care about this interface... */ - if (!sctp_is_desired_interface_type(ifa)) { - return; - } - /* go through all our PCB's */ - SCTP_INP_INFO_RLOCK(); - LIST_FOREACH(inp, &sctppcbinfo.listhead, sctp_list) { - struct sctp_tcb *stcb; - struct sctp_laddr *laddr, *laddr_next; - - /* process for all associations for this endpoint */ - SCTP_INP_RLOCK(inp); - LIST_FOREACH(stcb, &inp->sctp_asoc_list, sctp_tcblist) { + /* put this address on the "pending/do not use yet" list */ + if (type == SCTP_ADD_IP_ADDRESS) { + sctp_add_local_addr_assoc(stcb, ifa, 1); + } else if (type == SCTP_DEL_IP_ADDRESS) { struct sctp_nets *net; - /* process through the nets list */ TAILQ_FOREACH(net, &stcb->asoc.nets, sctp_next) { struct rtentry *rt; /* delete this address if cached */ - rt = net->ro.ro_rt; - if (rt != NULL && rt->rt_ifa == ifa) { - /* RTFREE(rt); */ - net->ro.ro_rt = NULL; + if (net->ro._s_addr && + (net->ro._s_addr->ifa == ifa)) { + sctp_free_ifa(net->ro._s_addr); + net->ro._s_addr = NULL; + net->src_addr_selected = 0; + rt = net->ro.ro_rt; + if (rt) { + RTFREE(rt); + net->ro.ro_rt = NULL; + } + /* + * Now we deleted our src address, + * should we not also now reset the + * cwnd/rto to start as if its a new + * address? + */ + sctp_set_initial_cc_param(stcb, net); + net->RTO = stcb->asoc.initial_rto; + } - } /* for each net */ - /* process through the asoc "pending" list */ - laddr = LIST_FIRST(&stcb->asoc.sctp_local_addr_list); - while (laddr != NULL) { - laddr_next = LIST_NEXT(laddr, sctp_nxt_addr); - /* remove if in use */ - if (laddr->ifa == ifa) { - sctp_remove_laddr(laddr); + } + } else if (type == SCTP_SET_PRIM_ADDR) { + if ((stcb->sctp_ep->sctp_flags & SCTP_PCB_FLAGS_BOUNDALL) == 0) { + /* + * must validate the ifa in question is in + * the ep + */ + if (sctp_is_addr_in_ep(stcb->sctp_ep, ifa) == 0) { + continue; + } + } else { + /* Need to check scopes for this guy */ + if (sctp_is_address_in_scope(ifa, + stcb->asoc.ipv4_addr_legal, + stcb->asoc.ipv6_addr_legal, + stcb->asoc.loopback_scope, + stcb->asoc.ipv4_local_scope, + stcb->asoc.local_scope, + stcb->asoc.site_scope, 0) == 0) { + continue; + } + } + + } + /* queue an asconf for this address add/delete */ + if (sctp_is_feature_on(inp, SCTP_PCB_FLAGS_DO_ASCONF)) { + /* does the peer do asconf? */ + if (stcb->asoc.peer_supports_asconf) { + /* queue an asconf for this addr */ + + status = sctp_asconf_queue_add(stcb, ifa, type); + /* + * if queued ok, and in correct state, set + * the ASCONF timer if in non-open state, we + * will set this timer when the state does + * go open and do all the asconf's + */ + if (status == 0 && + SCTP_GET_STATE(&stcb->asoc) == SCTP_STATE_OPEN) { + sctp_timer_start(SCTP_TIMER_TYPE_ASCONF, inp, + stcb, stcb->asoc.primary_destination); } - laddr = laddr_next; - } /* while */ - } /* for each stcb */ - /* process through the inp bound addr list */ - laddr = LIST_FIRST(&inp->sctp_addr_list); - while (laddr != NULL) { - laddr_next = LIST_NEXT(laddr, sctp_nxt_addr); - /* remove if in use */ - if (laddr->ifa == ifa) { - sctp_remove_laddr(laddr); } - laddr = laddr_next; } - SCTP_INP_RUNLOCK(inp); } - SCTP_INP_INFO_RUNLOCK(); +} + +void +sctp_iterator_end(void *ptr, uint32_t val) +{ + struct sctp_asconf_iterator *asc; + struct sctp_ifa *ifa; + struct sctp_laddr *l, *l_next; + + asc = (struct sctp_asconf_iterator *)ptr; + l = LIST_FIRST(&asc->list_of_work); + while (l != NULL) { + l_next = LIST_NEXT(l, sctp_nxt_addr); + ifa = l->ifa; + if (l->action == SCTP_ADD_IP_ADDRESS) { + /* Clear the defer use flag */ + ifa->localifa_flags &= ~SCTP_ADDR_DEFER_USE; + } + sctp_free_ifa(ifa); + SCTP_ZONE_FREE(sctppcbinfo.ipi_zone_laddr, l); + SCTP_DECR_LADDR_COUNT(); + l = l_next; + } + SCTP_FREE(asc); } /* @@ -2067,14 +2034,10 @@ sctp_set_primary_ip_address_sa(struct sctp_tcb *stcb, struct sockaddr *sa) } void -sctp_set_primary_ip_address(struct ifaddr *ifa) +sctp_set_primary_ip_address(struct sctp_ifa *ifa) { struct sctp_inpcb *inp; - /* make sure we care about this interface... */ - if (!sctp_is_desired_interface_type(ifa)) { - return; - } /* go through all our PCB's */ LIST_FOREACH(inp, &sctppcbinfo.listhead, sctp_list) { struct sctp_tcb *stcb; @@ -2095,7 +2058,7 @@ sctp_set_primary_ip_address(struct ifaddr *ifa) if (sctp_debug_on & SCTP_DEBUG_ASCONF1) { printf("set_primary_ip_address: queued on stcb=%p, ", stcb); - sctp_print_address(ifa->ifa_addr); + sctp_print_address(&ifa->address.sa); } #endif /* SCTP_DEBUG */ } @@ -2106,21 +2069,23 @@ sctp_set_primary_ip_address(struct ifaddr *ifa) static struct sockaddr * sctp_find_valid_localaddr(struct sctp_tcb *stcb) { - struct ifnet *ifn; - struct ifaddr *ifa; - - - TAILQ_FOREACH(ifn, &ifnet, if_list) { - if (stcb->asoc.loopback_scope == 0 && ifn->if_type == IFT_LOOP) { + struct sctp_vrf *vrf = NULL; + struct sctp_ifn *sctp_ifn; + struct sctp_ifa *sctp_ifa; + + vrf = sctp_find_vrf(stcb->asoc.vrf_id); + LIST_FOREACH(sctp_ifn, &vrf->ifnlist, next_ifn) { + if (stcb->asoc.loopback_scope == 0 && + SCTP_IFN_IS_IFT_LOOP(sctp_ifn)) { /* Skip if loopback_scope not set */ continue; } - TAILQ_FOREACH(ifa, &ifn->if_addrlist, ifa_list) { - if (ifa->ifa_addr->sa_family == AF_INET && + LIST_FOREACH(sctp_ifa, &sctp_ifn->ifalist, next_ifa) { + if (sctp_ifa->address.sa.sa_family == AF_INET && stcb->asoc.ipv4_addr_legal) { struct sockaddr_in *sin; - sin = (struct sockaddr_in *)ifa->ifa_addr; + sin = (struct sockaddr_in *)&sctp_ifa->address.sa; if (sin->sin_addr.s_addr == 0) { /* skip unspecifed addresses */ continue; @@ -2129,23 +2094,18 @@ sctp_find_valid_localaddr(struct sctp_tcb *stcb) IN4_ISPRIVATE_ADDRESS(&sin->sin_addr)) continue; - if (sctp_is_addr_restricted(stcb, - ifa->ifa_addr)) + if (sctp_is_addr_restricted(stcb, sctp_ifa)) continue; /* found a valid local v4 address to use */ - return (ifa->ifa_addr); - } else if (ifa->ifa_addr->sa_family == AF_INET6 && + return (&sctp_ifa->address.sa); + } else if (sctp_ifa->address.sa.sa_family == AF_INET6 && stcb->asoc.ipv6_addr_legal) { struct sockaddr_in6 *sin6; - struct in6_ifaddr *ifa6; - ifa6 = (struct in6_ifaddr *)ifa; - if (IFA6_IS_DEPRECATED(ifa6) || - (ifa6->ia6_flags & (IN6_IFF_DETACHED | - IN6_IFF_ANYCAST | IN6_IFF_NOTREADY))) + if (sctp_ifa->localifa_flags & SCTP_ADDR_IFA_UNUSEABLE) { continue; - - sin6 = (struct sockaddr_in6 *)ifa->ifa_addr; + } + sin6 = (struct sockaddr_in6 *)&sctp_ifa->address.sa; if (IN6_IS_ADDR_UNSPECIFIED(&sin6->sin6_addr)) { /* we skip unspecifed addresses */ continue; @@ -2158,7 +2118,7 @@ sctp_find_valid_localaddr(struct sctp_tcb *stcb) continue; /* found a valid local v6 address to use */ - return (ifa->ifa_addr); + return (&sctp_ifa->address.sa); } } } @@ -2175,15 +2135,15 @@ sctp_find_valid_localaddr_ep(struct sctp_tcb *stcb) if (laddr->ifa == NULL) { continue; } - if (laddr->ifa->ifa_addr == NULL) { + if (laddr->ifa == NULL) { continue; } /* is the address restricted ? */ - if (sctp_is_addr_restricted(stcb, laddr->ifa->ifa_addr)) + if (sctp_is_addr_restricted(stcb, laddr->ifa)) continue; /* found a valid local address to use */ - return (laddr->ifa->ifa_addr); + return (&laddr->ifa->address.sa); } /* no valid addresses found */ return (NULL); @@ -2384,11 +2344,12 @@ sctp_process_initack_addresses(struct sctp_tcb *stcb, struct mbuf *m, { struct sctp_paramhdr tmp_param, *ph; uint16_t plen, ptype; + struct sctp_ifa *sctp_ifa; struct sctp_ipv6addr_param addr_store; struct sockaddr_in6 sin6; struct sockaddr_in sin; struct sockaddr *sa; - struct ifaddr *ifa; + uint32_t vrf_id; #ifdef SCTP_DEBUG if (sctp_debug_on & SCTP_DEBUG_ASCONF2) { @@ -2438,7 +2399,9 @@ sctp_process_initack_addresses(struct sctp_tcb *stcb, struct mbuf *m, struct sctp_ipv4addr_param *a4p; /* get the entire IPv4 address param */ - a4p = (struct sctp_ipv4addr_param *)sctp_m_getptr(m, offset, sizeof(struct sctp_ipv4addr_param), (uint8_t *) & addr_store); + a4p = (struct sctp_ipv4addr_param *)sctp_m_getptr(m, offset, + sizeof(struct sctp_ipv4addr_param), + (uint8_t *) & addr_store); if (plen != sizeof(struct sctp_ipv4addr_param) || a4p == NULL) { return; @@ -2450,8 +2413,9 @@ sctp_process_initack_addresses(struct sctp_tcb *stcb, struct mbuf *m, } /* see if this address really (still) exists */ - ifa = sctp_find_ifa_by_addr(sa); - if (ifa == NULL) { + vrf_id = SCTP_DEFAULT_VRFID; + sctp_ifa = sctp_find_ifa_by_addr(sa, vrf_id, 0); + if (sctp_ifa == NULL) { /* address doesn't exist anymore */ int status; @@ -2474,25 +2438,7 @@ sctp_process_initack_addresses(struct sctp_tcb *stcb, struct mbuf *m, stcb->asoc.primary_destination); } } - } else { - /* address still exists */ - /* - * if subset bound, ep addr's managed by default if - * not doing ASCONF, add the address to the assoc - */ - if ((stcb->sctp_ep->sctp_flags & - SCTP_PCB_FLAGS_BOUNDALL) == 0 && - (sctp_is_feature_off(stcb->sctp_ep, - SCTP_PCB_FLAGS_DO_ASCONF))) { -#ifdef SCTP_DEBUG - if (sctp_debug_on & SCTP_DEBUG_ASCONF2) { - printf("process_initack_addrs: adding local addr to asoc\n"); - } -#endif /* SCTP_DEBUG */ - sctp_add_local_addr_assoc(stcb, ifa); - } } - next_addr: /* * Sanity check: Make sure the length isn't 0, otherwise @@ -2530,8 +2476,9 @@ sctp_addr_in_initack(struct sctp_tcb *stcb, struct mbuf *m, uint32_t offset, struct sctp_ipv4addr_param *a4p; #ifdef INET6 - struct sockaddr_in6 *sin6, sin6_tmp; + struct sockaddr_in6 *sin6; struct sctp_ipv6addr_param *a6p; + struct sockaddr_in6 sin6_tmp; #endif /* INET6 */ @@ -2642,7 +2589,7 @@ sctp_check_address_list_ep(struct sctp_tcb *stcb, struct mbuf *m, int offset, #endif /* SCTP_DEBUG */ continue; } - if (laddr->ifa->ifa_addr == NULL) { + if (laddr->ifa == NULL) { #ifdef SCTP_DEBUG if (sctp_debug_on & SCTP_DEBUG_ASCONF1) { printf("check_addr_list_ep: laddr->ifa->ifa_addr is NULL"); @@ -2651,12 +2598,12 @@ sctp_check_address_list_ep(struct sctp_tcb *stcb, struct mbuf *m, int offset, continue; } /* do i have it implicitly? */ - if (sctp_cmpaddr(laddr->ifa->ifa_addr, init_addr)) { + if (sctp_cmpaddr(&laddr->ifa->address.sa, init_addr)) { continue; } /* check to see if in the init-ack */ if (!sctp_addr_in_initack(stcb, m, offset, length, - laddr->ifa->ifa_addr)) { + &laddr->ifa->address.sa)) { /* try to add it */ sctp_addr_mgmt_assoc(stcb->sctp_ep, stcb, laddr->ifa, SCTP_ADD_IP_ADDRESS); @@ -2674,27 +2621,34 @@ sctp_check_address_list_all(struct sctp_tcb *stcb, struct mbuf *m, int offset, uint16_t local_scope, uint16_t site_scope, uint16_t ipv4_scope, uint16_t loopback_scope) { - struct ifnet *ifn; - struct ifaddr *ifa; - + struct sctp_vrf *vrf = NULL; + struct sctp_ifn *sctp_ifn; + struct sctp_ifa *sctp_ifa; + uint32_t vrf_id; + + vrf_id = SCTP_DEFAULT_VRFID; + vrf = sctp_find_vrf(vrf_id); + if (vrf == NULL) { + return; + } /* go through all our known interfaces */ - TAILQ_FOREACH(ifn, &ifnet, if_list) { - if (loopback_scope == 0 && ifn->if_type == IFT_LOOP) { + LIST_FOREACH(sctp_ifn, &vrf->ifnlist, next_ifn) { + if (loopback_scope == 0 && SCTP_IFN_IS_IFT_LOOP(sctp_ifn)) { /* skip loopback interface */ continue; } /* go through each interface address */ - TAILQ_FOREACH(ifa, &ifn->if_addrlist, ifa_list) { + LIST_FOREACH(sctp_ifa, &sctp_ifn->ifalist, next_ifa) { /* do i have it implicitly? */ - if (sctp_cmpaddr(ifa->ifa_addr, init_addr)) { + if (sctp_cmpaddr(&sctp_ifa->address.sa, init_addr)) { continue; } /* check to see if in the init-ack */ if (!sctp_addr_in_initack(stcb, m, offset, length, - ifa->ifa_addr)) { + &sctp_ifa->address.sa)) { /* try to add it */ sctp_addr_mgmt_assoc(stcb->sctp_ep, stcb, - ifa, SCTP_ADD_IP_ADDRESS); + sctp_ifa, SCTP_ADD_IP_ADDRESS); } } /* end foreach ifa */ } /* end foreach ifn */ @@ -2737,71 +2691,65 @@ sctp_check_address_list(struct sctp_tcb *stcb, struct mbuf *m, int offset, * sctp_bindx() support */ uint32_t -sctp_addr_mgmt_ep_sa(struct sctp_inpcb *inp, struct sockaddr *sa, uint16_t type) +sctp_addr_mgmt_ep_sa(struct sctp_inpcb *inp, struct sockaddr *sa, uint32_t type, uint32_t vrf_id) { - struct ifaddr *ifa; + struct sctp_ifa *ifa; - - if (sa->sa_len == 0) + if (sa->sa_len == 0) { return (EINVAL); - - ifa = sctp_find_ifa_by_addr(sa); + } + 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); + } 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); + } else { + ifa = NULL; + } if (ifa != NULL) { -#ifdef INET6 - if (ifa->ifa_addr->sa_family == AF_INET6) { - struct in6_ifaddr *ifa6; - - ifa6 = (struct in6_ifaddr *)ifa; - if (IFA6_IS_DEPRECATED(ifa6) || - (ifa6->ia6_flags & (IN6_IFF_DETACHED | - IN6_IFF_ANYCAST | IN6_IFF_NOTREADY))) { - /* Can't bind a non-existent addr. */ - return (EINVAL); + /* add this address */ + struct sctp_asconf_iterator *asc; + struct sctp_laddr *wi; + + SCTP_MALLOC(asc, struct sctp_asconf_iterator *, + sizeof(struct sctp_asconf_iterator), "SCTP_ASCONF_ITERATOR"); + if (asc == NULL) { + return (ENOMEM); + } + wi = SCTP_ZONE_GET(sctppcbinfo.ipi_zone_laddr, struct sctp_laddr); + if (wi == NULL) { + SCTP_FREE(asc); + return (ENOMEM); + } + if (type == SCTP_ADD_IP_ADDRESS) { + sctp_add_local_addr_ep(inp, ifa, type); + } else if (type == SCTP_DEL_IP_ADDRESS) { + struct sctp_laddr *laddr; + + LIST_FOREACH(laddr, &inp->sctp_addr_list, sctp_nxt_addr) { + if (ifa == laddr->ifa) { + /* Mark in the delete */ + laddr->action = type; + } } } -#endif /* INET6 */ - /* add this address */ - sctp_addr_mgmt_ep(inp, ifa, type); + LIST_INIT(&asc->list_of_work); + asc->cnt = 1; + SCTP_INCR_LADDR_COUNT(); + wi->ifa = ifa; + wi->action = type; + atomic_add_int(&ifa->refcount, 1); + LIST_INSERT_HEAD(&asc->list_of_work, wi, sctp_nxt_addr); + sctp_initiate_iterator(sctp_iterator_ep, + sctp_iterator_stcb, + sctp_iterator_ep_end, + SCTP_PCB_ANY_FLAGS, + SCTP_PCB_ANY_FEATURES, SCTP_ASOC_ANY_STATE, (void *)asc, 0, + sctp_iterator_end, inp, 0); } else { /* invalid address! */ return (EADDRNOTAVAIL); } return (0); } - -void -sctp_addr_change(struct ifaddr *ifa, int cmd) -{ - struct sctp_laddr *wi; - - wi = SCTP_ZONE_GET(sctppcbinfo.ipi_zone_laddr, struct sctp_laddr); - if (wi == NULL) { - /* - * Gak, what can we do? We have lost an address change can - * you say HOSED? - */ -#ifdef SCTP_DEBUG - if (sctp_debug_on & SCTP_DEBUG_PCB1) { - printf("Lost and address change ???\n"); - } -#endif /* SCTP_DEBUG */ - return; - } - SCTP_INCR_LADDR_COUNT(); - bzero(wi, sizeof(*wi)); - wi->ifa = ifa; - IFAREF(ifa); - - wi->action = cmd; - SCTP_IPI_ADDR_LOCK(); - /* - * Should this really be a tailq? As it is we will process the - * newest first :-0 - */ - LIST_INSERT_HEAD(&sctppcbinfo.addr_wq, wi, sctp_nxt_addr); - sctp_timer_start(SCTP_TIMER_TYPE_ADDR_WQ, - (struct sctp_inpcb *)NULL, - (struct sctp_tcb *)NULL, - (struct sctp_nets *)NULL); - SCTP_IPI_ADDR_UNLOCK(); -} |