/* * Returns 0 if the check failed and the restart should be refused, * 1 if the check succeeded. */ int sctp_secure_restart_check(mblk_t *pkt, sctp_chunk_hdr_t *ich, uint32_t ports, int sleep, sctp_stack_t *sctps, ip_recv_attr_t *ira) { sctp_faddr_t *fp, *fphead = NULL; sctp_parm_hdr_t *ph; ssize_t remaining; int isv4; ipha_t *iph; ip6_t *ip6h; in6_addr_t hdraddr[1]; int retval = 0; sctp_tf_t *tf; sctp_t *sctp; int compres; sctp_init_chunk_t *init; int nadded = 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, hdraddr); } else { ip6h = (ip6_t *)pkt->b_rptr; hdraddr[0] = ip6h->ip6_src; } /* 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)) { /* no parameters; restart OK */ return (1); } init = (sctp_init_chunk_t *)(ich + 1); ph = (sctp_parm_hdr_t *)(init + 1); while (ph != NULL) { sctp_faddr_t *fpa = NULL; /* params will have already been byteordered when validating */ if (ph->sph_type == htons(PARM_ADDR4)) { if (remaining >= PARM_ADDR4_LEN) { in6_addr_t addr; IN6_INADDR_TO_V4MAPPED((struct in_addr *) (ph + 1), &addr); fpa = kmem_cache_alloc(sctp_kmem_faddr_cache, sleep); if (fpa == NULL) { goto done; } bzero(fpa, sizeof (*fpa)); fpa->faddr = addr; fpa->next = NULL; } } else if (ph->sph_type == htons(PARM_ADDR6)) { if (remaining >= PARM_ADDR6_LEN) { fpa = kmem_cache_alloc(sctp_kmem_faddr_cache, sleep); if (fpa == NULL) { goto done; } bzero(fpa, sizeof (*fpa)); bcopy(ph + 1, &fpa->faddr, sizeof (fpa->faddr)); fpa->next = NULL; } } /* link in the new addr, if it was an addr param */ if (fpa != NULL) { if (fphead == NULL) { fphead = fpa; } else { fpa->next = fphead; fphead = fpa; } } ph = sctp_next_parm(ph, &remaining); } if (fphead == NULL) { /* no addr parameters; restart OK */ return (1); } /* * got at least one; make sure the header's addr is * in the list */ fp = sctp_lookup_faddr_nosctp(fphead, hdraddr); if (fp == NULL) { /* not included; add it now */ fp = kmem_cache_alloc(sctp_kmem_faddr_cache, sleep); if (fp == NULL) { goto done; } bzero(fp, sizeof (*fp)); fp->faddr = *hdraddr; fp->next = fphead; fphead = fp; } /* * Now, we can finally do the check: For each sctp instance * on the hash line for ports, compare its faddr set against * the new one. If the new one is a strict subset of any * existing sctp's faddrs, the restart is OK. However, if there * is an overlap, this could be an attack, so return failure. * If all sctp's faddrs are disjoint, this is a legitimate new * association. */ tf = &(sctps->sctps_conn_fanout[SCTP_CONN_HASH(sctps, ports)]); mutex_enter(&tf->tf_lock); for (sctp = tf->tf_sctp; sctp; sctp = sctp->sctp_conn_hash_next) { if (ports != sctp->sctp_connp->conn_ports) { continue; } compres = sctp_compare_faddrsets(fphead, sctp->sctp_faddrs); if (compres <= SCTP_ADDR_SUBSET) { retval = 1; mutex_exit(&tf->tf_lock); goto done; } if (compres == SCTP_ADDR_OVERLAP) { dprint(1, ("new assoc from %x:%x:%x:%x overlaps with %p\n", SCTP_PRINTADDR(*hdraddr), (void *)sctp)); /* * While we still hold the lock, we need to * figure out which addresses have been * added so we can include them in the abort * we will send back. Since these faddrs will * never be used, we overload the rto field * here, setting it to 0 if the address was * not added, 1 if it was added. */ for (fp = fphead; fp; fp = fp->next) { if (sctp_lookup_faddr(sctp, &fp->faddr)) { fp->rto = 0; } else { fp->rto = 1; nadded++; } } mutex_exit(&tf->tf_lock); goto done; } } mutex_exit(&tf->tf_lock); /* All faddrs are disjoint; legit new association */ retval = 1; done: /* If are attempted adds, send back an abort listing the addrs */ if (nadded > 0) { void *dtail; size_t dlen; dtail = kmem_alloc(PARM_ADDR6_LEN * nadded, KM_NOSLEEP); if (dtail == NULL) { goto cleanup; } ph = dtail; dlen = 0; for (fp = fphead; fp; fp = fp->next) { if (fp->rto == 0) { continue; } if (IN6_IS_ADDR_V4MAPPED(&fp->faddr)) { ipaddr_t addr4; ph->sph_type = htons(PARM_ADDR4); ph->sph_len = htons(PARM_ADDR4_LEN); IN6_V4MAPPED_TO_IPADDR(&fp->faddr, addr4); ph++; bcopy(&addr4, ph, sizeof (addr4)); ph = (sctp_parm_hdr_t *) ((char *)ph + sizeof (addr4)); dlen += PARM_ADDR4_LEN; } else { ph->sph_type = htons(PARM_ADDR6); ph->sph_len = htons(PARM_ADDR6_LEN); ph++; bcopy(&fp->faddr, ph, sizeof (fp->faddr)); ph = (sctp_parm_hdr_t *) ((char *)ph + sizeof (fp->faddr)); dlen += PARM_ADDR6_LEN; } } /* Send off the abort */ sctp_send_abort(sctp, sctp_init2vtag(ich), SCTP_ERR_RESTART_NEW_ADDRS, dtail, dlen, pkt, 0, B_TRUE, ira); kmem_free(dtail, PARM_ADDR6_LEN * nadded); } cleanup: /* Clean up */ if (fphead) { sctp_faddr_t *fpn; for (fp = fphead; fp; fp = fpn) { fpn = fp->next; if (fp->ixa != NULL) { ixa_refrele(fp->ixa); fp->ixa = NULL; } kmem_cache_free(sctp_kmem_faddr_cache, fp); } } return (retval); }
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); }
/* * 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); }
/*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); } }
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); }
static mblk_t * sctp_check_addip_addr(sctp_parm_hdr_t *ph, sctp_parm_hdr_t *oph, int *cont, uint32_t cid, in6_addr_t *raddr) { uint16_t atype; uint16_t alen; mblk_t *mp; in6_addr_t addr; ipaddr_t *addr4; atype = ntohs(ph->sph_type); alen = ntohs(ph->sph_len); if (atype != PARM_ADDR4 && atype != PARM_ADDR6) { mp = sctp_asconf_adderr(SCTP_ERR_BAD_MANDPARM, oph, cid); if (mp == NULL) { *cont = -1; } return (mp); } if ((atype == PARM_ADDR4 && alen < PARM_ADDR4_LEN) || (atype == PARM_ADDR6 && alen < PARM_ADDR6_LEN)) { mp = sctp_asconf_adderr(SCTP_ERR_BAD_MANDPARM, oph, cid); if (mp == NULL) { *cont = -1; } return (mp); } /* Address parameter is present; extract and screen it */ if (atype == PARM_ADDR4) { addr4 = (ipaddr_t *)(ph + 1); IN6_IPADDR_TO_V4MAPPED(*addr4, &addr); /* screen XXX loopback to scoping */ if (*addr4 == 0 || *addr4 == INADDR_BROADCAST || *addr4 == htonl(INADDR_LOOPBACK) || CLASSD(*addr4)) { dprint(1, ("addip: addr not unicast: %x:%x:%x:%x\n", SCTP_PRINTADDR(addr))); mp = sctp_asconf_adderr(SCTP_ERR_BAD_MANDPARM, oph, cid); if (mp == NULL) { *cont = -1; } return (mp); } /* * XXX also need to check for subnet * broadcasts. This should probably * wait until we have full access * to the ILL tables. */ } else { bcopy(ph + 1, &addr, sizeof (addr)); /* screen XXX loopback to scoping */ if (IN6_IS_ADDR_LINKLOCAL(&addr) || IN6_IS_ADDR_MULTICAST(&addr) || IN6_IS_ADDR_LOOPBACK(&addr)) { dprint(1, ("addip: addr not unicast: %x:%x:%x:%x\n", SCTP_PRINTADDR(addr))); mp = sctp_asconf_adderr(SCTP_ERR_BAD_MANDPARM, oph, cid); if (mp == NULL) { *cont = -1; } return (mp); } } /* OK */ *raddr = addr; return (NULL); }
/* ARGSUSED */ void dhcp_rebind(iu_tq_t *tqp, void *arg) { dhcp_lease_t *dlp = arg; dhcp_smach_t *dsmp = dlp->dl_smach; int nlifs; dhcp_lif_t *lif; boolean_t some_valid; uint32_t expiremax; DHCPSTATE oldstate; dhcpmsg(MSG_VERBOSE, "dhcp_rebind: T2 timer expired on %s", dsmp->dsm_name); dlp->dl_t2.dt_id = -1; if ((oldstate = dsmp->dsm_state) == REBINDING) { dhcpmsg(MSG_DEBUG, "dhcp_renew: already rebinding"); release_lease(dlp); return; } /* * Sanity check: don't send packets if we've already expired on all of * the addresses. We compute the maximum expiration time here, because * it won't matter for v4 (there's only one lease) and for v6 we need * to know when the last lease ages away. */ some_valid = B_FALSE; expiremax = monosec(); lif = dlp->dl_lifs; for (nlifs = dlp->dl_nlifs; nlifs > 0; nlifs--, lif = lif->lif_next) { uint32_t expire; expire = dsmp->dsm_curstart_monosec + lif->lif_expire.dt_start; if (expire > expiremax) { expiremax = expire; some_valid = B_TRUE; } } if (!some_valid) { dhcpmsg(MSG_DEBUG, "dhcp_rebind: all leases expired on %s", dsmp->dsm_name); release_lease(dlp); return; } /* * This is our first venture into the REBINDING state, so reset the * server address. We know the renew timer has already been cancelled * (or we wouldn't be here). */ if (dsmp->dsm_isv6) { dsmp->dsm_server = ipv6_all_dhcp_relay_and_servers; } else { IN6_IPADDR_TO_V4MAPPED(htonl(INADDR_BROADCAST), &dsmp->dsm_server); } /* {Bound,Renew}->rebind transitions cannot fail */ (void) set_smach_state(dsmp, REBINDING); /* * If there isn't an async event pending, or if we can cancel the one * that's there, then try to rebind by sending an extension request. * If that fails, we'll clean up when the lease expires. */ if (!async_cancel(dsmp) || !async_start(dsmp, DHCP_EXTEND, B_FALSE) || !dhcp_extending(dsmp)) { if (monosec() + RETRY_DELAY < expiremax) { /* * Try again in RETRY_DELAY seconds; user command * should be gone. */ init_timer(&dlp->dl_t2, RETRY_DELAY); (void) set_smach_state(dsmp, oldstate); if (!schedule_lease_timer(dlp, &dlp->dl_t2, dhcp_rebind)) { dhcpmsg(MSG_INFO, "dhcp_rebind: unable to " "reschedule rebind around user command on " "%s; lease may expire", dsmp->dsm_name); } } else { dhcpmsg(MSG_WARNING, "dhcp_rebind: user busy on %s; " "will expire", dsmp->dsm_name); } } release_lease(dlp); }
/* ARGSUSED */ void ip_fanout_sctp(mblk_t *mp, ill_t *recv_ill, ipha_t *ipha, uint32_t ports, uint_t flags, boolean_t mctl_present, boolean_t ip_policy, uint_t ipif_seqid, zoneid_t zoneid) { sctp_t *sctp; boolean_t isv4; conn_t *connp; mblk_t *first_mp; ip6_t *ip6h; in6_addr_t map_src, map_dst; in6_addr_t *src, *dst; first_mp = mp; if (mctl_present) { mp = first_mp->b_cont; ASSERT(mp != NULL); } /* Assume IP provides aligned packets - otherwise toss */ if (!OK_32PTR(mp->b_rptr)) { BUMP_MIB(&ip_mib, ipInDiscards); freemsg(first_mp); return; } if (IPH_HDR_VERSION(ipha) == IPV6_VERSION) { ip6h = (ip6_t *)ipha; src = &ip6h->ip6_src; dst = &ip6h->ip6_dst; isv4 = B_FALSE; } else { ip6h = NULL; IN6_IPADDR_TO_V4MAPPED(ipha->ipha_src, &map_src); IN6_IPADDR_TO_V4MAPPED(ipha->ipha_dst, &map_dst); src = &map_src; dst = &map_dst; isv4 = B_TRUE; } if ((connp = sctp_fanout(src, dst, ports, ipif_seqid, zoneid, mp)) == NULL) { ip_fanout_sctp_raw(first_mp, recv_ill, ipha, isv4, ports, mctl_present, flags, ip_policy, ipif_seqid, zoneid); return; } sctp = CONN2SCTP(connp); /* Found a client; up it goes */ BUMP_MIB(&ip_mib, ipInDelivers); /* * We check some fields in conn_t without holding a lock. * This should be fine. */ if (CONN_INBOUND_POLICY_PRESENT(connp) || mctl_present) { first_mp = ipsec_check_inbound_policy(first_mp, connp, ipha, NULL, mctl_present); if (first_mp == NULL) { SCTP_REFRELE(sctp); return; } } /* Initiate IPPF processing for fastpath */ if (IPP_ENABLED(IPP_LOCAL_IN)) { ip_process(IPP_LOCAL_IN, &mp, recv_ill->ill_phyint->phyint_ifindex); if (mp == NULL) { SCTP_REFRELE(sctp); if (mctl_present) freeb(first_mp); return; } else if (mctl_present) { /* * ip_process might return a new mp. */ ASSERT(first_mp != mp); first_mp->b_cont = mp; } else { first_mp = mp; } } if (connp->conn_recvif || connp->conn_recvslla || connp->conn_ipv6_recvpktinfo) { int in_flags = 0; if (connp->conn_recvif || connp->conn_ipv6_recvpktinfo) { in_flags = IPF_RECVIF; } if (connp->conn_recvslla) { in_flags |= IPF_RECVSLLA; } if (isv4) { mp = ip_add_info(mp, recv_ill, in_flags); } else { mp = ip_add_info_v6(mp, recv_ill, &ip6h->ip6_dst); } if (mp == NULL) { SCTP_REFRELE(sctp); if (mctl_present) freeb(first_mp); return; } else if (mctl_present) { /* * ip_add_info might return a new mp. */ ASSERT(first_mp != mp); first_mp->b_cont = mp; } else { first_mp = mp; } } mutex_enter(&sctp->sctp_lock); if (sctp->sctp_running) { if (mctl_present) mp->b_prev = first_mp; if (!sctp_add_recvq(sctp, mp, B_FALSE)) { BUMP_MIB(&ip_mib, ipInDiscards); freemsg(first_mp); } mutex_exit(&sctp->sctp_lock); } else { sctp->sctp_running = B_TRUE; mutex_exit(&sctp->sctp_lock); mutex_enter(&sctp->sctp_recvq_lock); if (sctp->sctp_recvq != NULL) { if (mctl_present) mp->b_prev = first_mp; if (!sctp_add_recvq(sctp, mp, B_TRUE)) { BUMP_MIB(&ip_mib, ipInDiscards); freemsg(first_mp); } mutex_exit(&sctp->sctp_recvq_lock); WAKE_SCTP(sctp); } else { mutex_exit(&sctp->sctp_recvq_lock); sctp_input_data(sctp, mp, (mctl_present ? first_mp : NULL)); WAKE_SCTP(sctp); sctp_process_sendq(sctp); } } SCTP_REFRELE(sctp); }
/* * inet_pton: This function takes string format IPv4 or IPv6 address and * converts it to binary form. The format of this function corresponds to * inet_pton() in the socket library. * * Return values: * 0 invalid IPv4 or IPv6 address * 1 successful conversion * -1 af is not AF_INET or AF_INET6 */ int __inet_pton(int af, char *inp, void *outp, int compat) { int i; long byte; char *end; switch (af) { case AF_INET: if (str2inet_addr(inp, (ipaddr_t *)outp) != 0) { if (!compat) *(uint32_t *)outp = htonl(*(uint32_t *)outp); return (1); } else { return (0); } case AF_INET6: { union v6buf_u { uint16_t v6words_u[8]; in6_addr_t v6addr_u; } v6buf, *v6outp; uint16_t *dbl_col = NULL; char lastbyte = NULL; v6outp = (union v6buf_u *)outp; if (strchr_w(inp, '.') != NULL) { /* v4 mapped or v4 compatable */ if (strncmp(inp, "::ffff:", 7) == 0) { ipaddr_t ipv4_all_zeroes = 0; /* mapped - first init prefix and then fill */ IN6_IPADDR_TO_V4MAPPED(ipv4_all_zeroes, &v6outp->v6addr_u); return (str2inet_addr(inp + 7, &(v6outp->v6addr_u.s6_addr32[3]))); } else if (strncmp(inp, "::", 2) == 0) { /* v4 compatable - prefix all zeroes */ bzero(&v6outp->v6addr_u, sizeof (in6_addr_t)); return (str2inet_addr(inp + 2, &(v6outp->v6addr_u.s6_addr32[3]))); } return (0); } for (i = 0; i < 8; i++) { int error; /* * if ddi_strtol() fails it could be because * the string is "::". That is valid and * checked for below so just set the value to * 0 and continue. */ if ((error = ddi_strtol(inp, &end, 16, &byte)) != 0) { if (error == ERANGE) return (0); byte = 0; } if (byte < 0 || byte > 0x0ffff) { return (0); } if (compat) { v6buf.v6words_u[i] = (uint16_t)byte; } else { v6buf.v6words_u[i] = htons((uint16_t)byte); } if (*end == NULL || i == 7) { inp = end; break; } if (inp == end) { /* not a number must be */ if (*inp == ':' && ((i == 0 && *(inp + 1) == ':') || lastbyte == ':')) { if (dbl_col) { return (0); } if (byte != 0) i++; dbl_col = &v6buf.v6words_u[i]; if (i == 0) inp++; } else if (*inp == NULL || *inp == ' ' || *inp == '\t') { break; } else { return (0); } } else { inp = end; } if (*inp != ':') { return (0); } inp++; if (*inp == NULL || *inp == ' ' || *inp == '\t') { break; } lastbyte = *inp; } if (*inp != NULL && *inp != ' ' && *inp != '\t') { return (0); } /* * v6words now contains the bytes we could translate * dbl_col points to the word (should be 0) where * a double colon was found */ if (i == 7) { v6outp->v6addr_u = v6buf.v6addr_u; } else { int rem; int word; int next; if (dbl_col == NULL) { return (0); } bzero(&v6outp->v6addr_u, sizeof (in6_addr_t)); rem = dbl_col - &v6buf.v6words_u[0]; for (next = 0; next < rem; next++) { v6outp->v6words_u[next] = v6buf.v6words_u[next]; } next++; /* skip dbl_col 0 */ rem = i - rem; word = 8 - rem; while (rem > 0) { v6outp->v6words_u[word] = v6buf.v6words_u[next]; word++; rem--; next++; } } return (1); /* Success */ } } /* switch */ return (-1); /* return -1 for default case */ }
/* Process the COOKIE packet, mp, directed at the listener 'sctp' */ sctp_t * sctp_conn_request(sctp_t *sctp, mblk_t *mp, uint_t ifindex, uint_t ip_hdr_len, sctp_init_chunk_t *iack, ip_recv_attr_t *ira) { sctp_t *eager; ip6_t *ip6h; int err; conn_t *connp, *econnp; sctp_stack_t *sctps; struct sock_proto_props sopp; cred_t *cr; pid_t cpid; in6_addr_t faddr, laddr; ip_xmit_attr_t *ixa; /* * No need to check for duplicate as this is the listener * and we are holding the lock. This means that no new * connection can be created out of it. And since the * fanout already done cannot find a match, it means that * there is no duplicate. */ ASSERT(OK_32PTR(mp->b_rptr)); if ((eager = sctp_create_eager(sctp)) == NULL) { return (NULL); } connp = sctp->sctp_connp; sctps = sctp->sctp_sctps; econnp = eager->sctp_connp; if (connp->conn_policy != NULL) { /* Inherit the policy from the listener; use actions from ira */ if (!ip_ipsec_policy_inherit(econnp, connp, ira)) { sctp_close_eager(eager); BUMP_MIB(&sctps->sctps_mib, sctpListenDrop); return (NULL); } } ip6h = (ip6_t *)mp->b_rptr; if (ira->ira_flags & IXAF_IS_IPV4) { ipha_t *ipha; ipha = (ipha_t *)ip6h; IN6_IPADDR_TO_V4MAPPED(ipha->ipha_dst, &laddr); IN6_IPADDR_TO_V4MAPPED(ipha->ipha_src, &faddr); } else { laddr = ip6h->ip6_dst; faddr = ip6h->ip6_src; } if (ira->ira_flags & IRAF_IPSEC_SECURE) { /* * XXX need to fix the cached policy issue here. * We temporarily set the conn_laddr/conn_faddr here so * that IPsec can use it for the latched policy * selector. This is obvioursly wrong as SCTP can * use different addresses... */ econnp->conn_laddr_v6 = laddr; econnp->conn_faddr_v6 = faddr; econnp->conn_saddr_v6 = laddr; } if (ipsec_conn_cache_policy(econnp, (ira->ira_flags & IRAF_IS_IPV4) != 0) != 0) { sctp_close_eager(eager); BUMP_MIB(&sctps->sctps_mib, sctpListenDrop); return (NULL); } /* Save for getpeerucred */ cr = ira->ira_cred; cpid = ira->ira_cpid; if (is_system_labeled()) { ip_xmit_attr_t *ixa = econnp->conn_ixa; ASSERT(ira->ira_tsl != NULL); /* Discard any old label */ if (ixa->ixa_free_flags & IXA_FREE_TSL) { ASSERT(ixa->ixa_tsl != NULL); label_rele(ixa->ixa_tsl); ixa->ixa_free_flags &= ~IXA_FREE_TSL; ixa->ixa_tsl = NULL; } if ((connp->conn_mlp_type != mlptSingle || connp->conn_mac_mode != CONN_MAC_DEFAULT) && ira->ira_tsl != NULL) { /* * If this is an MLP connection or a MAC-Exempt * connection with an unlabeled node, packets are to be * exchanged using the security label of the received * Cookie packet instead of the server application's * label. * tsol_check_dest called from ip_set_destination * might later update TSF_UNLABELED by replacing * ixa_tsl with a new label. */ label_hold(ira->ira_tsl); ip_xmit_attr_replace_tsl(ixa, ira->ira_tsl); } else { ixa->ixa_tsl = crgetlabel(econnp->conn_cred); } } err = sctp_accept_comm(sctp, eager, mp, ip_hdr_len, iack); if (err != 0) { sctp_close_eager(eager); BUMP_MIB(&sctps->sctps_mib, sctpListenDrop); return (NULL); } ASSERT(eager->sctp_current->ixa != NULL); ixa = eager->sctp_current->ixa; if (!(ira->ira_flags & IXAF_IS_IPV4)) { ASSERT(!(ixa->ixa_flags & IXAF_IS_IPV4)); if (IN6_IS_ADDR_LINKLOCAL(&ip6h->ip6_src) || IN6_IS_ADDR_LINKLOCAL(&ip6h->ip6_dst)) { eager->sctp_linklocal = 1; ixa->ixa_flags |= IXAF_SCOPEID_SET; ixa->ixa_scopeid = ifindex; econnp->conn_incoming_ifindex = ifindex; } } /* * On a clustered note send this notification to the clustering * subsystem. */ if (cl_sctp_connect != NULL) { uchar_t *slist; uchar_t *flist; size_t fsize; size_t ssize; fsize = sizeof (in6_addr_t) * eager->sctp_nfaddrs; ssize = sizeof (in6_addr_t) * eager->sctp_nsaddrs; slist = kmem_alloc(ssize, KM_NOSLEEP); flist = kmem_alloc(fsize, KM_NOSLEEP); if (slist == NULL || flist == NULL) { if (slist != NULL) kmem_free(slist, ssize); if (flist != NULL) kmem_free(flist, fsize); sctp_close_eager(eager); BUMP_MIB(&sctps->sctps_mib, sctpListenDrop); SCTP_KSTAT(sctps, sctp_cl_connect); return (NULL); } /* The clustering module frees these list */ sctp_get_saddr_list(eager, slist, ssize); sctp_get_faddr_list(eager, flist, fsize); (*cl_sctp_connect)(econnp->conn_family, slist, eager->sctp_nsaddrs, econnp->conn_lport, flist, eager->sctp_nfaddrs, econnp->conn_fport, B_FALSE, (cl_sctp_handle_t)eager); } /* Connection established, so send up the conn_ind */ if ((eager->sctp_ulpd = sctp->sctp_ulp_newconn(sctp->sctp_ulpd, (sock_lower_handle_t)eager, NULL, cr, cpid, &eager->sctp_upcalls)) == NULL) { sctp_close_eager(eager); BUMP_MIB(&sctps->sctps_mib, sctpListenDrop); return (NULL); } ASSERT(SCTP_IS_DETACHED(eager)); eager->sctp_detached = B_FALSE; bzero(&sopp, sizeof (sopp)); sopp.sopp_flags = SOCKOPT_MAXBLK|SOCKOPT_WROFF; sopp.sopp_maxblk = strmsgsz; if (econnp->conn_family == AF_INET) { sopp.sopp_wroff = sctps->sctps_wroff_xtra + sizeof (sctp_data_hdr_t) + sctp->sctp_hdr_len; } else { sopp.sopp_wroff = sctps->sctps_wroff_xtra + sizeof (sctp_data_hdr_t) + sctp->sctp_hdr6_len; } eager->sctp_ulp_prop(eager->sctp_ulpd, &sopp); return (eager); }