/* * Add a list of addresses to a sctp_t. */ int sctp_bind_add(sctp_t *sctp, const void *addrs, uint32_t addrcnt, boolean_t caller_hold_lock, in_port_t port) { int err = 0; boolean_t do_asconf = B_FALSE; if (!caller_hold_lock) RUN_SCTP(sctp); if (sctp->sctp_state > SCTPS_ESTABLISHED) { if (!caller_hold_lock) WAKE_SCTP(sctp); return (EINVAL); } if (sctp->sctp_state > SCTPS_LISTEN) { /* * Let's do some checking here rather than undoing the * add later (for these reasons). */ if (!sctp_addip_enabled || !sctp->sctp_understands_asconf || !sctp->sctp_understands_addip) { if (!caller_hold_lock) WAKE_SCTP(sctp); return (EINVAL); } do_asconf = B_TRUE; } /* * On a clustered node, for an inaddr_any bind, we will pass the list * of all the addresses in the global list, minus any address on the * loopback interface, and expect the clustering susbsystem to give us * the correct list for the 'port'. For explicit binds we give the * list of addresses and the clustering module validates it for the * 'port'. * * On a non-clustered node, cl_sctp_check_addrs will be NULL and * we proceed as usual. */ if (cl_sctp_check_addrs != NULL) { uchar_t *addrlist = NULL; size_t size = 0; int unspec = 0; boolean_t do_listen; uchar_t *llist = NULL; size_t lsize = 0; /* * If we are adding addresses after listening, but before * an association is established, we need to update the * clustering module with this info. */ do_listen = !do_asconf && sctp->sctp_state > SCTPS_BOUND && cl_sctp_listen != NULL; err = sctp_get_addrlist(sctp, addrs, &addrcnt, &addrlist, &unspec, &size); if (err != 0) { ASSERT(addrlist == NULL); ASSERT(addrcnt == 0); ASSERT(size == 0); if (!caller_hold_lock) WAKE_SCTP(sctp); SCTP_KSTAT(sctp_cl_check_addrs); return (err); } ASSERT(addrlist != NULL); (*cl_sctp_check_addrs)(sctp->sctp_family, port, &addrlist, size, &addrcnt, unspec == 1); if (addrcnt == 0) { /* We free the list */ kmem_free(addrlist, size); if (!caller_hold_lock) WAKE_SCTP(sctp); return (EINVAL); } if (do_listen) { lsize = sizeof (in6_addr_t) * addrcnt; llist = kmem_alloc(lsize, KM_SLEEP); } err = sctp_valid_addr_list(sctp, addrlist, addrcnt, llist, lsize); if (err == 0 && do_listen) { (*cl_sctp_listen)(sctp->sctp_family, llist, addrcnt, sctp->sctp_lport); /* list will be freed by the clustering module */ } else if (err != 0 && llist != NULL) { kmem_free(llist, lsize); } /* free the list we allocated */ kmem_free(addrlist, size); } else { err = sctp_valid_addr_list(sctp, addrs, addrcnt, NULL, 0); } if (err != 0) { if (!caller_hold_lock) WAKE_SCTP(sctp); return (err); } /* Need to send ASCONF messages */ if (do_asconf) { err = sctp_add_ip(sctp, addrs, addrcnt); if (err != 0) { sctp_del_saddr_list(sctp, addrs, addrcnt, B_FALSE); if (!caller_hold_lock) WAKE_SCTP(sctp); return (err); } } if (!caller_hold_lock) WAKE_SCTP(sctp); if (do_asconf) sctp_process_sendq(sctp); return (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); }