sctp_t * sctp_conn_match(in6_addr_t *faddr, in6_addr_t *laddr, uint32_t ports, uint_t ipif_seqid, zoneid_t zoneid) { sctp_tf_t *tf; sctp_t *sctp; sctp_faddr_t *fp; tf = &(sctp_conn_fanout[SCTP_CONN_HASH(ports)]); mutex_enter(&tf->tf_lock); for (sctp = tf->tf_sctp; sctp; sctp = sctp->sctp_conn_hash_next) { if (ports != sctp->sctp_ports || !IPCL_ZONE_MATCH(sctp->sctp_connp, zoneid)) { continue; } /* check for faddr match */ for (fp = sctp->sctp_faddrs; fp; fp = fp->next) { if (IN6_ARE_ADDR_EQUAL(faddr, &fp->faddr)) { break; } } if (!fp) { /* no faddr match; keep looking */ continue; } /* check for laddr match */ if (ipif_seqid == 0) { if (sctp_saddr_lookup(sctp, laddr, 0) != NULL) { SCTP_REFHOLD(sctp); goto done; } } else { if (sctp_ipif_lookup(sctp, ipif_seqid) != NULL) { SCTP_REFHOLD(sctp); goto done; } /* no match; continue to the next in the chain */ } } done: mutex_exit(&tf->tf_lock); return (sctp); }
static sctp_t * listen_match(in6_addr_t *laddr, uint32_t ports, uint_t ipif_seqid, zoneid_t zoneid) { sctp_t *sctp; sctp_tf_t *tf; uint16_t lport; lport = ((uint16_t *)&ports)[1]; tf = &(sctp_listen_fanout[SCTP_LISTEN_HASH(ntohs(lport))]); mutex_enter(&tf->tf_lock); for (sctp = tf->tf_sctp; sctp; sctp = sctp->sctp_listen_hash_next) { if (lport != sctp->sctp_lport || !IPCL_ZONE_MATCH(sctp->sctp_connp, zoneid)) { continue; } if (ipif_seqid == 0) { if (sctp_saddr_lookup(sctp, laddr, 0) != NULL) { SCTP_REFHOLD(sctp); goto done; } } else { if (sctp_ipif_lookup(sctp, ipif_seqid) != NULL) { SCTP_REFHOLD(sctp); goto done; } } /* no match; continue to the next in the chain */ } done: mutex_exit(&tf->tf_lock); return (sctp); }
/* * Call this function to get information about a peer addr fp. * * Uses ip_attr_connect to avoid explicit use of ire and source address * selection. */ void sctp_get_dest(sctp_t *sctp, sctp_faddr_t *fp) { in6_addr_t laddr; in6_addr_t nexthop; sctp_saddr_ipif_t *sp; int hdrlen; sctp_stack_t *sctps = sctp->sctp_sctps; conn_t *connp = sctp->sctp_connp; iulp_t uinfo; uint_t pmtu; int error; uint32_t flags = IPDF_VERIFY_DST | IPDF_IPSEC | IPDF_SELECT_SRC | IPDF_UNIQUE_DCE; /* * Tell sctp_make_mp it needs to call us again should we not * complete and set the saddr. */ fp->saddr = ipv6_all_zeros; /* * If this addr is not reachable, mark it as unconfirmed for now, the * state will be changed back to unreachable later in this function * if it is still the case. */ if (fp->state == SCTP_FADDRS_UNREACH) { fp->state = SCTP_FADDRS_UNCONFIRMED; } /* * Socket is connected - enable PMTU discovery. */ if (!sctps->sctps_ignore_path_mtu) fp->ixa->ixa_flags |= IXAF_PMTU_DISCOVERY; ip_attr_nexthop(&connp->conn_xmit_ipp, fp->ixa, &fp->faddr, &nexthop); laddr = fp->saddr; error = ip_attr_connect(connp, fp->ixa, &laddr, &fp->faddr, &nexthop, connp->conn_fport, &laddr, &uinfo, flags); if (error != 0) { dprint(3, ("sctp_get_dest: no ire for %x:%x:%x:%x\n", SCTP_PRINTADDR(fp->faddr))); /* * It is tempting to just leave the src addr * unspecified and let IP figure it out, but we * *cannot* do this, since IP may choose a src addr * that is not part of this association... unless * this sctp has bound to all addrs. So if the dest * lookup fails, try to find one in our src addr * list, unless the sctp has bound to all addrs, in * which case we change the src addr to unspec. * * Note that if this is a v6 endpoint but it does * not have any v4 address at this point (e.g. may * have been deleted), sctp_get_valid_addr() will * return mapped INADDR_ANY. In this case, this * address should be marked not reachable so that * it won't be used to send data. */ sctp_set_saddr(sctp, fp); if (fp->state == SCTP_FADDRS_UNREACH) return; goto check_current; } ASSERT(fp->ixa->ixa_ire != NULL); ASSERT(!(fp->ixa->ixa_ire->ire_flags & (RTF_REJECT|RTF_BLACKHOLE))); if (!sctp->sctp_loopback) sctp->sctp_loopback = uinfo.iulp_loopback; /* Make sure the laddr is part of this association */ if ((sp = sctp_saddr_lookup(sctp, &laddr, 0)) != NULL && !sp->saddr_ipif_dontsrc) { if (sp->saddr_ipif_unconfirmed == 1) sp->saddr_ipif_unconfirmed = 0; /* We did IPsec policy lookup for laddr already */ fp->saddr = laddr; } else { dprint(2, ("sctp_get_dest: src addr is not part of assoc " "%x:%x:%x:%x\n", SCTP_PRINTADDR(laddr))); /* * Set the src to the first saddr and hope for the best. * Note that this case should very seldomly * happen. One scenario this can happen is an app * explicitly bind() to an address. But that address is * not the preferred source address to send to the peer. */ sctp_set_saddr(sctp, fp); if (fp->state == SCTP_FADDRS_UNREACH) { return; } } /* * Pull out RTO information for this faddr and use it if we don't * have any yet. */ if (fp->srtt == -1 && uinfo.iulp_rtt != 0) { /* The cached value is in ms. */ fp->srtt = MSEC_TO_TICK(uinfo.iulp_rtt); fp->rttvar = MSEC_TO_TICK(uinfo.iulp_rtt_sd); fp->rto = 3 * fp->srtt; /* Bound the RTO by configured min and max values */ if (fp->rto < sctp->sctp_rto_min) { fp->rto = sctp->sctp_rto_min; } if (fp->rto > sctp->sctp_rto_max) { fp->rto = sctp->sctp_rto_max; } SCTP_MAX_RTO(sctp, fp); } pmtu = uinfo.iulp_mtu; /* * Record the MTU for this faddr. If the MTU for this faddr has * changed, check if the assc MTU will also change. */ if (fp->isv4) { hdrlen = sctp->sctp_hdr_len; } else { hdrlen = sctp->sctp_hdr6_len; } if ((fp->sfa_pmss + hdrlen) != pmtu) { /* Make sure that sfa_pmss is a multiple of SCTP_ALIGN. */ fp->sfa_pmss = (pmtu - hdrlen) & ~(SCTP_ALIGN - 1); if (fp->cwnd < (fp->sfa_pmss * 2)) { SET_CWND(fp, fp->sfa_pmss, sctps->sctps_slow_start_initial); } } check_current: if (fp == sctp->sctp_current) sctp_set_faddr_current(sctp, fp); }
/* * Sets the address parameters given in the INIT chunk into sctp's * faddrs; if psctp is non-NULL, copies psctp's saddrs. If there are * no address parameters in the INIT chunk, a single faddr is created * from the ip hdr at the beginning of pkt. * If there already are existing addresses hanging from sctp, merge * them in, if the old info contains addresses which are not present * in this new info, get rid of them, and clean the pointers if there's * messages which have this as their target address. * * We also re-adjust the source address list here since the list may * contain more than what is actually part of the association. If * we get here from sctp_send_cookie_echo(), we are on the active * side and psctp will be NULL and ich will be the INIT-ACK chunk. * If we get here from sctp_accept_comm(), ich will be the INIT chunk * and psctp will the listening endpoint. * * INIT processing: When processing the INIT we inherit the src address * list from the listener. For a loopback or linklocal association, we * delete the list and just take the address from the IP header (since * that's how we created the INIT-ACK). Additionally, for loopback we * ignore the address params in the INIT. For determining which address * types were sent in the INIT-ACK we follow the same logic as in * creating the INIT-ACK. We delete addresses of the type that are not * supported by the peer. * * INIT-ACK processing: When processing the INIT-ACK since we had not * included addr params for loopback or linklocal addresses when creating * the INIT, we just use the address from the IP header. Further, for * loopback we ignore the addr param list. We mark addresses of the * type not supported by the peer as unconfirmed. * * In case of INIT processing we look for supported address types in the * supported address param, if present. In both cases the address type in * the IP header is supported as well as types for addresses in the param * list, if any. * * Once we have the supported address types sctp_check_saddr() runs through * the source address list and deletes or marks as unconfirmed address of * types not supported by the peer. * * Returns 0 on success, sys errno on failure */ int sctp_get_addrparams(sctp_t *sctp, sctp_t *psctp, mblk_t *pkt, sctp_chunk_hdr_t *ich, uint_t *sctp_options) { sctp_init_chunk_t *init; ipha_t *iph; ip6_t *ip6h; in6_addr_t hdrsaddr[1]; in6_addr_t hdrdaddr[1]; sctp_parm_hdr_t *ph; ssize_t remaining; int isv4; int err; sctp_faddr_t *fp; int supp_af = 0; boolean_t check_saddr = B_TRUE; in6_addr_t curaddr; sctp_stack_t *sctps = sctp->sctp_sctps; conn_t *connp = sctp->sctp_connp; if (sctp_options != NULL) *sctp_options = 0; /* extract the address from the IP header */ isv4 = (IPH_HDR_VERSION(pkt->b_rptr) == IPV4_VERSION); if (isv4) { iph = (ipha_t *)pkt->b_rptr; IN6_IPADDR_TO_V4MAPPED(iph->ipha_src, hdrsaddr); IN6_IPADDR_TO_V4MAPPED(iph->ipha_dst, hdrdaddr); supp_af |= PARM_SUPP_V4; } else { ip6h = (ip6_t *)pkt->b_rptr; hdrsaddr[0] = ip6h->ip6_src; hdrdaddr[0] = ip6h->ip6_dst; supp_af |= PARM_SUPP_V6; } /* * Unfortunately, we can't delay this because adding an faddr * looks for the presence of the source address (from the ire * for the faddr) in the source address list. We could have * delayed this if, say, this was a loopback/linklocal connection. * Now, we just end up nuking this list and taking the addr from * the IP header for loopback/linklocal. */ if (psctp != NULL && psctp->sctp_nsaddrs > 0) { ASSERT(sctp->sctp_nsaddrs == 0); err = sctp_dup_saddrs(psctp, sctp, KM_NOSLEEP); if (err != 0) return (err); } /* * We will add the faddr before parsing the address list as this * might be a loopback connection and we would not have to * go through the list. * * Make sure the header's addr is in the list */ fp = sctp_lookup_faddr(sctp, hdrsaddr); if (fp == NULL) { /* not included; add it now */ err = sctp_add_faddr(sctp, hdrsaddr, KM_NOSLEEP, B_TRUE); if (err != 0) return (err); /* sctp_faddrs will be the hdr addr */ fp = sctp->sctp_faddrs; } /* make the header addr the primary */ if (cl_sctp_assoc_change != NULL && psctp == NULL) curaddr = sctp->sctp_current->faddr; sctp->sctp_primary = fp; sctp->sctp_current = fp; sctp->sctp_mss = fp->sfa_pmss; /* For loopback connections & linklocal get address from the header */ if (sctp->sctp_loopback || sctp->sctp_linklocal) { if (sctp->sctp_nsaddrs != 0) sctp_free_saddrs(sctp); if ((err = sctp_saddr_add_addr(sctp, hdrdaddr, 0)) != 0) return (err); /* For loopback ignore address list */ if (sctp->sctp_loopback) return (0); check_saddr = B_FALSE; } /* Walk the params in the INIT [ACK], pulling out addr params */ remaining = ntohs(ich->sch_len) - sizeof (*ich) - sizeof (sctp_init_chunk_t); if (remaining < sizeof (*ph)) { if (check_saddr) { sctp_check_saddr(sctp, supp_af, psctp == NULL ? B_FALSE : B_TRUE, hdrdaddr); } ASSERT(sctp_saddr_lookup(sctp, hdrdaddr, 0) != NULL); return (0); } init = (sctp_init_chunk_t *)(ich + 1); ph = (sctp_parm_hdr_t *)(init + 1); /* params will have already been byteordered when validating */ while (ph != NULL) { if (ph->sph_type == htons(PARM_SUPP_ADDRS)) { int plen; uint16_t *p; uint16_t addrtype; ASSERT(psctp != NULL); plen = ntohs(ph->sph_len); p = (uint16_t *)(ph + 1); while (plen > 0) { addrtype = ntohs(*p); switch (addrtype) { case PARM_ADDR6: supp_af |= PARM_SUPP_V6; break; case PARM_ADDR4: supp_af |= PARM_SUPP_V4; break; default: break; } p++; plen -= sizeof (*p); } } else if (ph->sph_type == htons(PARM_ADDR4)) { if (remaining >= PARM_ADDR4_LEN) { in6_addr_t addr; ipaddr_t ta; supp_af |= PARM_SUPP_V4; /* * Screen out broad/multicasts & loopback. * If the endpoint only accepts v6 address, * go to the next one. * * Subnet broadcast check is done in * sctp_add_faddr(). If the address is * a broadcast address, it won't be added. */ bcopy(ph + 1, &ta, sizeof (ta)); if (ta == 0 || ta == INADDR_BROADCAST || ta == htonl(INADDR_LOOPBACK) || CLASSD(ta) || connp->conn_ipv6_v6only) { goto next; } IN6_INADDR_TO_V4MAPPED((struct in_addr *) (ph + 1), &addr); /* Check for duplicate. */ if (sctp_lookup_faddr(sctp, &addr) != NULL) goto next; /* OK, add it to the faddr set */ err = sctp_add_faddr(sctp, &addr, KM_NOSLEEP, B_FALSE); /* Something is wrong... Try the next one. */ if (err != 0) goto next; } } else if (ph->sph_type == htons(PARM_ADDR6) && connp->conn_family == AF_INET6) { /* An v4 socket should not take v6 addresses. */ if (remaining >= PARM_ADDR6_LEN) { in6_addr_t *addr6; supp_af |= PARM_SUPP_V6; addr6 = (in6_addr_t *)(ph + 1); /* * Screen out link locals, mcast, loopback * and bogus v6 address. */ if (IN6_IS_ADDR_LINKLOCAL(addr6) || IN6_IS_ADDR_MULTICAST(addr6) || IN6_IS_ADDR_LOOPBACK(addr6) || IN6_IS_ADDR_V4MAPPED(addr6)) { goto next; } /* Check for duplicate. */ if (sctp_lookup_faddr(sctp, addr6) != NULL) goto next; err = sctp_add_faddr(sctp, (in6_addr_t *)(ph + 1), KM_NOSLEEP, B_FALSE); /* Something is wrong... Try the next one. */ if (err != 0) goto next; } } else if (ph->sph_type == htons(PARM_FORWARD_TSN)) { if (sctp_options != NULL) *sctp_options |= SCTP_PRSCTP_OPTION; } /* else; skip */ next: ph = sctp_next_parm(ph, &remaining); } if (check_saddr) { sctp_check_saddr(sctp, supp_af, psctp == NULL ? B_FALSE : B_TRUE, hdrdaddr); } ASSERT(sctp_saddr_lookup(sctp, hdrdaddr, 0) != NULL); /* * We have the right address list now, update clustering's * knowledge because when we sent the INIT we had just added * the address the INIT was sent to. */ if (psctp == NULL && cl_sctp_assoc_change != NULL) { uchar_t *alist; size_t asize; uchar_t *dlist; size_t dsize; asize = sizeof (in6_addr_t) * sctp->sctp_nfaddrs; alist = kmem_alloc(asize, KM_NOSLEEP); if (alist == NULL) { SCTP_KSTAT(sctps, sctp_cl_assoc_change); return (ENOMEM); } /* * Just include the address the INIT was sent to in the * delete list and send the entire faddr list. We could * do it differently (i.e include all the addresses in the * add list even if it contains the original address OR * remove the original address from the add list etc.), but * this seems reasonable enough. */ dsize = sizeof (in6_addr_t); dlist = kmem_alloc(dsize, KM_NOSLEEP); if (dlist == NULL) { kmem_free(alist, asize); SCTP_KSTAT(sctps, sctp_cl_assoc_change); return (ENOMEM); } bcopy(&curaddr, dlist, sizeof (curaddr)); sctp_get_faddr_list(sctp, alist, asize); (*cl_sctp_assoc_change)(connp->conn_family, alist, asize, sctp->sctp_nfaddrs, dlist, dsize, 1, SCTP_CL_PADDR, (cl_sctp_handle_t)sctp); /* alist and dlist will be freed by the clustering module */ } return (0); }
int sctp_set_peerprim(sctp_t *sctp, const void *inp) { const struct sctp_setprim *prim = inp; const struct sockaddr_storage *ss; struct sockaddr_in *sin; struct sockaddr_in6 *sin6; in6_addr_t addr; mblk_t *mp; sctp_saddr_ipif_t *sp; sctp_addip4_t *ad4; sctp_addip6_t *ad6; sctp_asconf_t asc[1]; int error = 0; uint_t ifindex = 0; /* Does the peer understand ASCONF and Add-IP? */ if (!sctp->sctp_understands_asconf || !sctp->sctp_understands_addip) { return (EOPNOTSUPP); } /* Don't do anything if we are not connected */ if (sctp->sctp_state != SCTPS_ESTABLISHED) return (EINVAL); ss = &prim->ssp_addr; sin = NULL; sin6 = NULL; if (ss->ss_family == AF_INET) { sin = (struct sockaddr_in *)ss; IN6_IPADDR_TO_V4MAPPED(sin->sin_addr.s_addr, &addr); } else if (ss->ss_family == AF_INET6) { sin6 = (struct sockaddr_in6 *)ss; addr = sin6->sin6_addr; ifindex = sin6->sin6_scope_id; } else { return (EAFNOSUPPORT); } sp = sctp_saddr_lookup(sctp, &addr, ifindex); if (sp == NULL) return (EADDRNOTAVAIL); sctp_asconf_init(asc); if (sin) { mp = allocb(sizeof (*ad4), BPRI_MED); if (mp == NULL) { error = ENOMEM; goto fail; } mp->b_wptr += sizeof (*ad4); ad4 = (sctp_addip4_t *)mp->b_rptr; ad4->sad4_addip_ph.sph_type = htons(PARM_SET_PRIMARY); ad4->sad4_addip_ph.sph_len = htons(sizeof (sctp_parm_hdr_t) + PARM_ADDR4_LEN + sizeof (ad4->asconf_req_cid)); ad4->sad4_addr4_ph.sph_type = htons(PARM_ADDR4); ad4->sad4_addr4_ph.sph_len = htons(PARM_ADDR4_LEN); ad4->sad4_addr = sin->sin_addr.s_addr; } else { mp = allocb(sizeof (*ad6), BPRI_MED); if (mp == NULL) { error = ENOMEM; goto fail; } mp->b_wptr += sizeof (*ad6); ad6 = (sctp_addip6_t *)mp->b_rptr; ad6->sad6_addip_ph.sph_type = htons(PARM_SET_PRIMARY); ad6->sad6_addip_ph.sph_len = htons(sizeof (sctp_parm_hdr_t) + PARM_ADDR6_LEN + sizeof (ad6->asconf_req_cid)); ad6->sad6_addr6_ph.sph_type = htons(PARM_ADDR6); ad6->sad6_addr6_ph.sph_len = htons(PARM_ADDR6_LEN); ad6->sad6_addr = sin6->sin6_addr; } error = sctp_asconf_add(asc, mp); if (error != 0) { goto fail; } error = sctp_asconf_send(sctp, asc, sctp->sctp_current, NULL); if (error == 0) { return (0); } fail: sctp_asconf_destroy(asc); return (error); }
int sctp_del_ip(sctp_t *sctp, const void *addrs, uint32_t cnt, uchar_t *ulist, size_t usize) { struct sockaddr_in *sin4; struct sockaddr_in6 *sin6; mblk_t *mp; int error = 0; int i; int addrcnt = 0; sctp_addip4_t *ad4; sctp_addip6_t *ad6; sctp_asconf_t asc[1]; sctp_saddr_ipif_t *nsp; uint16_t type = htons(PARM_DEL_IP); boolean_t v4mapped = B_FALSE; in6_addr_t addr; boolean_t asconf = B_TRUE; uint_t ifindex; sctp_cl_ainfo_t *ainfo = NULL; uchar_t *p = ulist; boolean_t check_lport = B_FALSE; sctp_stack_t *sctps = sctp->sctp_sctps; conn_t *connp = sctp->sctp_connp; /* Does the peer understand ASCONF and Add-IP? */ if (sctp->sctp_state <= SCTPS_LISTEN || !sctps->sctps_addip_enabled || !sctp->sctp_understands_asconf || !sctp->sctp_understands_addip) { asconf = B_FALSE; } if (sctp->sctp_state > SCTPS_BOUND) check_lport = B_TRUE; if (asconf) { /* * On a clustered node, we need to pass this list when * we get an ASCONF-ACK. We only pre-allocate memory for the * list, but fill in the addresses when it is processed * successfully after we get an ASCONF-ACK. */ if (cl_sctp_assoc_change != NULL) { ainfo = kmem_alloc(sizeof (*ainfo), KM_SLEEP); ainfo->sctp_cl_dsize = sizeof (in6_addr_t) * cnt; ainfo->sctp_cl_dlist = kmem_alloc(ainfo->sctp_cl_dsize, KM_SLEEP); } sctp_asconf_init(asc); } /* * Screen addresses: * If adding: * o Must not already be a part of the association * o Must be AF_INET or AF_INET6 * o XXX Must be valid source address for this node * o Must be unicast * o XXX Must fit scoping rules * If deleting: * o Must be part of the association */ for (i = 0; i < cnt; i++) { ifindex = 0; switch (connp->conn_family) { case AF_INET: sin4 = (struct sockaddr_in *)addrs + i; if (check_lport && sin4->sin_port != connp->conn_lport) { error = EINVAL; goto fail; } v4mapped = B_TRUE; IN6_IPADDR_TO_V4MAPPED(sin4->sin_addr.s_addr, &addr); break; case AF_INET6: sin6 = (struct sockaddr_in6 *)addrs + i; if (check_lport && sin6->sin6_port != connp->conn_lport) { error = EINVAL; goto fail; } addr = sin6->sin6_addr; ifindex = sin6->sin6_scope_id; break; } nsp = sctp_saddr_lookup(sctp, &addr, ifindex); if (nsp == NULL) { error = EADDRNOTAVAIL; goto fail; } /* Collect the list of addresses, if required */ if (usize >= sizeof (addr)) { bcopy(&addr, p, sizeof (addr)); p += sizeof (addr); usize -= sizeof (addr); } if (!asconf) continue; nsp->saddr_ipif_delete_pending = 1; nsp->saddr_ipif_dontsrc = 1; addrcnt++; if (v4mapped) { mp = allocb(sizeof (*ad4), BPRI_MED); if (mp == NULL) { error = ENOMEM; goto fail; } mp->b_wptr += sizeof (*ad4); ad4 = (sctp_addip4_t *)mp->b_rptr; ad4->sad4_addip_ph.sph_type = type; ad4->sad4_addip_ph.sph_len = htons(sizeof (sctp_parm_hdr_t) + PARM_ADDR4_LEN + sizeof (ad4->asconf_req_cid)); ad4->sad4_addr4_ph.sph_type = htons(PARM_ADDR4); ad4->sad4_addr4_ph.sph_len = htons(PARM_ADDR4_LEN); ad4->sad4_addr = sin4->sin_addr.s_addr; } else { mp = allocb(sizeof (*ad6), BPRI_MED); if (mp == NULL) { error = ENOMEM; goto fail; } mp->b_wptr += sizeof (*ad6); ad6 = (sctp_addip6_t *)mp->b_rptr; ad6->sad6_addip_ph.sph_type = type; ad6->sad6_addip_ph.sph_len = htons(sizeof (sctp_parm_hdr_t) + PARM_ADDR6_LEN + sizeof (ad6->asconf_req_cid)); ad6->sad6_addr6_ph.sph_type = htons(PARM_ADDR6); ad6->sad6_addr6_ph.sph_len = htons(PARM_ADDR6_LEN); ad6->sad6_addr = addr; } error = sctp_asconf_add(asc, mp); if (error != 0) goto fail; } if (!asconf) { sctp_del_saddr_list(sctp, addrs, cnt, B_FALSE); return (0); } error = sctp_asconf_send(sctp, asc, sctp->sctp_current, ainfo); if (error != 0) goto fail; sctp_redo_faddr_srcs(sctp); return (0); fail: if (ainfo != NULL) { kmem_free(ainfo->sctp_cl_dlist, ainfo->sctp_cl_dsize); ainfo->sctp_cl_dsize = 0; kmem_free(ainfo, sizeof (*ainfo)); } if (!asconf) return (error); for (i = 0; i < addrcnt; i++) { ifindex = 0; switch (connp->conn_family) { case AF_INET: sin4 = (struct sockaddr_in *)addrs + i; IN6_INADDR_TO_V4MAPPED(&(sin4->sin_addr), &addr); break; case AF_INET6: sin6 = (struct sockaddr_in6 *)addrs + i; addr = sin6->sin6_addr; ifindex = sin6->sin6_scope_id; break; } nsp = sctp_saddr_lookup(sctp, &addr, ifindex); ASSERT(nsp != NULL); nsp->saddr_ipif_delete_pending = 0; nsp->saddr_ipif_dontsrc = 0; } sctp_asconf_destroy(asc); return (error); }
/*ARGSUSED*/ static void sctp_addip_ack(sctp_t *sctp, sctp_parm_hdr_t *ph, sctp_parm_hdr_t *oph, sctp_faddr_t *fp, in6_addr_t *laddr) { in6_addr_t addr; sctp_saddr_ipif_t *sp; ipaddr_t *addr4; boolean_t backout = B_FALSE; uint16_t type; uint32_t *cid; /* could be an ASSERT */ if (laddr != NULL) IN6_IPADDR_TO_V4MAPPED(0, laddr); /* If the peer doesn't understand Add-IP, remember it */ if (ph != NULL && ph->sph_type == htons(PARM_UNRECOGNIZED)) { sctp->sctp_understands_addip = B_FALSE; backout = B_TRUE; } /* * If OK, continue with the add / delete action, otherwise * back out the action. */ if (ph != NULL && ph->sph_type != htons(PARM_SUCCESS)) { backout = B_TRUE; sctp_error_event(sctp, (sctp_chunk_hdr_t *)ph); } type = ntohs(oph->sph_type); cid = (uint32_t *)(oph + 1); oph = (sctp_parm_hdr_t *)(cid + 1); if (oph->sph_type == htons(PARM_ADDR4)) { addr4 = (ipaddr_t *)(oph + 1); IN6_IPADDR_TO_V4MAPPED(*addr4, &addr); } else { bcopy(oph + 1, &addr, sizeof (addr)); } /* Signifies that the address was sucessfully processed */ if (!backout && laddr != NULL) *laddr = addr; sp = sctp_saddr_lookup(sctp, &addr, 0); ASSERT(sp != NULL); if (type == PARM_ADD_IP) { if (backout) { sctp_del_saddr(sctp, sp); } else { sp->saddr_ipif_dontsrc = 0; } } else if (type == PARM_DEL_IP) { if (backout) { sp->saddr_ipif_delete_pending = 0; sp->saddr_ipif_dontsrc = 0; } else { sctp_del_saddr(sctp, sp); } } else { /* Must be either PARM_ADD_IP or PARM_DEL_IP */ ASSERT(0); } }