void sctp_zap_addrs(sctp_t *sctp) { sctp_zap_faddrs(sctp, 0); sctp_free_saddrs(sctp); }
/* * Bind the sctp_t to a sockaddr, which includes an address and other * information, such as port or flowinfo. */ int sctp_bind(sctp_t *sctp, struct sockaddr *sa, socklen_t len) { int user_specified; boolean_t bind_to_req_port_only; in_port_t requested_port; in_port_t allocated_port; int err = 0; ASSERT(sctp != NULL); ASSERT(sa); RUN_SCTP(sctp); if (sctp->sctp_state > SCTPS_BOUND) { err = EINVAL; goto done; } switch (sa->sa_family) { case AF_INET: if (len < sizeof (struct sockaddr_in) || sctp->sctp_family == AF_INET6) { err = EINVAL; goto done; } requested_port = ntohs(((struct sockaddr_in *)sa)->sin_port); break; case AF_INET6: if (len < sizeof (struct sockaddr_in6) || sctp->sctp_family == AF_INET) { err = EINVAL; goto done; } requested_port = ntohs(((struct sockaddr_in6 *)sa)->sin6_port); /* Set the flowinfo. */ sctp->sctp_ip6h->ip6_vcf = (IPV6_DEFAULT_VERS_AND_FLOW & IPV6_VERS_AND_FLOW_MASK) | (((struct sockaddr_in6 *)sa)->sin6_flowinfo & ~IPV6_VERS_AND_FLOW_MASK); break; default: err = EAFNOSUPPORT; goto done; } bind_to_req_port_only = requested_port == 0 ? B_FALSE : B_TRUE; err = sctp_select_port(sctp, &requested_port, &user_specified); if (err != 0) goto done; if ((err = sctp_bind_add(sctp, sa, 1, B_TRUE, user_specified == 1 ? htons(requested_port) : 0)) != 0) { goto done; } err = sctp_bindi(sctp, requested_port, bind_to_req_port_only, user_specified, &allocated_port); if (err != 0) { sctp_free_saddrs(sctp); } else { ASSERT(sctp->sctp_state == SCTPS_BOUND); } done: WAKE_SCTP(sctp); return (err); }
/* * 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); }
/* * Bind the sctp_t to a sockaddr, which includes an address and other * information, such as port or flowinfo. */ int sctp_bind(sctp_t *sctp, struct sockaddr *sa, socklen_t len) { int user_specified; boolean_t bind_to_req_port_only; in_port_t requested_port; in_port_t allocated_port; int err = 0; conn_t *connp = sctp->sctp_connp; uint_t scope_id; sin_t *sin; sin6_t *sin6; ASSERT(sctp != NULL); RUN_SCTP(sctp); if ((sctp->sctp_state >= SCTPS_BOUND) || (sctp->sctp_connp->conn_state_flags & CONN_CLOSING) || (sa == NULL || len == 0)) { /* * Multiple binds not allowed for any SCTP socket * Also binding with null address is not supported. */ err = EINVAL; goto done; } switch (sa->sa_family) { case AF_INET: sin = (sin_t *)sa; if (len < sizeof (struct sockaddr_in) || connp->conn_family == AF_INET6) { err = EINVAL; goto done; } requested_port = ntohs(sin->sin_port); break; case AF_INET6: sin6 = (sin6_t *)sa; if (len < sizeof (struct sockaddr_in6) || connp->conn_family == AF_INET) { err = EINVAL; goto done; } requested_port = ntohs(sin6->sin6_port); /* Set the flowinfo. */ connp->conn_flowinfo = sin6->sin6_flowinfo & ~IPV6_VERS_AND_FLOW_MASK; scope_id = sin6->sin6_scope_id; if (scope_id != 0 && IN6_IS_ADDR_LINKSCOPE(&sin6->sin6_addr)) { connp->conn_ixa->ixa_flags |= IXAF_SCOPEID_SET; connp->conn_ixa->ixa_scopeid = scope_id; connp->conn_incoming_ifindex = scope_id; } else { connp->conn_ixa->ixa_flags &= ~IXAF_SCOPEID_SET; connp->conn_incoming_ifindex = connp->conn_bound_if; } break; default: err = EAFNOSUPPORT; goto done; } bind_to_req_port_only = requested_port == 0 ? B_FALSE : B_TRUE; err = sctp_select_port(sctp, &requested_port, &user_specified); if (err != 0) goto done; if ((err = sctp_bind_add(sctp, sa, 1, B_TRUE, user_specified == 1 ? htons(requested_port) : 0)) != 0) { goto done; } err = sctp_bindi(sctp, requested_port, bind_to_req_port_only, user_specified, &allocated_port); if (err != 0) { sctp_free_saddrs(sctp); } else { ASSERT(sctp->sctp_state == SCTPS_BOUND); } done: WAKE_SCTP(sctp); return (err); }