int sctp_listen(sctp_t *sctp) { sctp_tf_t *tf; RUN_SCTP(sctp); /* * TCP handles listen() increasing the backlog, need to check * if it should be handled here too */ if (sctp->sctp_state > SCTPS_BOUND) { WAKE_SCTP(sctp); return (EINVAL); } /* Do an anonymous bind for unbound socket doing listen(). */ if (sctp->sctp_nsaddrs == 0) { struct sockaddr_storage ss; int ret; bzero(&ss, sizeof (ss)); ss.ss_family = sctp->sctp_family; WAKE_SCTP(sctp); if ((ret = sctp_bind(sctp, (struct sockaddr *)&ss, sizeof (ss))) != 0) return (ret); RUN_SCTP(sctp) } sctp->sctp_state = SCTPS_LISTEN; (void) random_get_pseudo_bytes(sctp->sctp_secret, SCTP_SECRET_LEN); sctp->sctp_last_secret_update = lbolt64; bzero(sctp->sctp_old_secret, SCTP_SECRET_LEN); tf = &sctp_listen_fanout[SCTP_LISTEN_HASH(ntohs(sctp->sctp_lport))]; sctp_listen_hash_insert(tf, sctp); WAKE_SCTP(sctp); return (0); }
/* * Remove one or more addresses bound to the sctp_t. */ int sctp_bind_del(sctp_t *sctp, const void *addrs, uint32_t addrcnt, boolean_t caller_hold_lock) { int error = 0; boolean_t do_asconf = B_FALSE; uchar_t *ulist = NULL; size_t usize = 0; if (!caller_hold_lock) RUN_SCTP(sctp); if (sctp->sctp_state > SCTPS_ESTABLISHED) { if (!caller_hold_lock) WAKE_SCTP(sctp); return (EINVAL); } /* * Fail the remove if we are beyond listen, but can't send this * to the peer. */ if (sctp->sctp_state > SCTPS_LISTEN) { 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; } /* Can't delete the last address nor all of the addresses */ if (sctp->sctp_nsaddrs == 1 || addrcnt >= sctp->sctp_nsaddrs) { if (!caller_hold_lock) WAKE_SCTP(sctp); return (EINVAL); } if (cl_sctp_unlisten != NULL && !do_asconf && sctp->sctp_state > SCTPS_BOUND) { usize = sizeof (in6_addr_t) * addrcnt; ulist = kmem_alloc(usize, KM_SLEEP); } error = sctp_del_ip(sctp, addrs, addrcnt, ulist, usize); if (error != 0) { if (ulist != NULL) kmem_free(ulist, usize); if (!caller_hold_lock) WAKE_SCTP(sctp); return (error); } /* ulist will be non-NULL only if cl_sctp_unlisten is non-NULL */ if (ulist != NULL) { ASSERT(cl_sctp_unlisten != NULL); (*cl_sctp_unlisten)(sctp->sctp_family, ulist, addrcnt, sctp->sctp_lport); /* ulist will be freed by the clustering module */ } if (!caller_hold_lock) WAKE_SCTP(sctp); if (do_asconf) sctp_process_sendq(sctp); return (error); }
/* * 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); }
/* * 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); }
/* * Exported routine for extracting active SCTP associations. * Like TCP, we terminate the walk if the callback returns non-zero. */ int cl_sctp_walk_list(int (*cl_callback)(cl_sctp_info_t *, void *), void *arg, boolean_t cansleep) { sctp_t *sctp; sctp_t *sctp_prev; cl_sctp_info_t cl_sctpi; uchar_t *slist; uchar_t *flist; sctp = gsctp; sctp_prev = NULL; mutex_enter(&sctp_g_lock); while (sctp != NULL) { size_t ssize; size_t fsize; mutex_enter(&sctp->sctp_reflock); if (sctp->sctp_condemned || sctp->sctp_state <= SCTPS_LISTEN) { mutex_exit(&sctp->sctp_reflock); sctp = list_next(&sctp_g_list, sctp); continue; } sctp->sctp_refcnt++; mutex_exit(&sctp->sctp_reflock); mutex_exit(&sctp_g_lock); if (sctp_prev != NULL) SCTP_REFRELE(sctp_prev); RUN_SCTP(sctp); ssize = sizeof (in6_addr_t) * sctp->sctp_nsaddrs; fsize = sizeof (in6_addr_t) * sctp->sctp_nfaddrs; slist = kmem_alloc(ssize, cansleep ? KM_SLEEP : KM_NOSLEEP); flist = kmem_alloc(fsize, cansleep ? KM_SLEEP : KM_NOSLEEP); if (slist == NULL || flist == NULL) { WAKE_SCTP(sctp); if (slist != NULL) kmem_free(slist, ssize); if (flist != NULL) kmem_free(flist, fsize); SCTP_REFRELE(sctp); return (1); } cl_sctpi.cl_sctpi_version = CL_SCTPI_V1; sctp_get_saddr_list(sctp, slist, ssize); sctp_get_faddr_list(sctp, flist, fsize); cl_sctpi.cl_sctpi_nladdr = sctp->sctp_nsaddrs; cl_sctpi.cl_sctpi_nfaddr = sctp->sctp_nfaddrs; cl_sctpi.cl_sctpi_family = sctp->sctp_family; cl_sctpi.cl_sctpi_ipversion = sctp->sctp_ipversion; cl_sctpi.cl_sctpi_state = sctp->sctp_state; cl_sctpi.cl_sctpi_lport = sctp->sctp_lport; cl_sctpi.cl_sctpi_fport = sctp->sctp_fport; cl_sctpi.cl_sctpi_handle = (cl_sctp_handle_t)sctp; WAKE_SCTP(sctp); cl_sctpi.cl_sctpi_laddrp = slist; cl_sctpi.cl_sctpi_faddrp = flist; if ((*cl_callback)(&cl_sctpi, arg) != 0) { kmem_free(slist, ssize); kmem_free(flist, fsize); SCTP_REFRELE(sctp); return (1); } /* list will be freed by cl_callback */ sctp_prev = sctp; mutex_enter(&sctp_g_lock); sctp = list_next(&sctp_g_list, sctp); } mutex_exit(&sctp_g_lock); if (sctp_prev != NULL) SCTP_REFRELE(sctp_prev); return (0); }
/* * Walk the SCTP global list and refrele the ire for this ipif * This is called when an address goes down, so that we release any reference * to the ire associated with this address. Additionally, for any SCTP if * this was the only/last address in its source list, we don't kill the * assoc., if there is no address added subsequently, or if this does not * come up, then the assoc. will die a natural death (i.e. timeout). */ void sctp_ire_cache_flush(ipif_t *ipif) { sctp_t *sctp; sctp_t *sctp_prev = NULL; sctp_faddr_t *fp; conn_t *connp; ire_t *ire; sctp = gsctp; mutex_enter(&sctp_g_lock); while (sctp != NULL) { mutex_enter(&sctp->sctp_reflock); if (sctp->sctp_condemned) { mutex_exit(&sctp->sctp_reflock); sctp = list_next(&sctp_g_list, sctp); continue; } sctp->sctp_refcnt++; mutex_exit(&sctp->sctp_reflock); mutex_exit(&sctp_g_lock); if (sctp_prev != NULL) SCTP_REFRELE(sctp_prev); RUN_SCTP(sctp); connp = sctp->sctp_connp; mutex_enter(&connp->conn_lock); ire = connp->conn_ire_cache; if (ire != NULL && ire->ire_ipif == ipif) { connp->conn_ire_cache = NULL; mutex_exit(&connp->conn_lock); IRE_REFRELE_NOTR(ire); } else { mutex_exit(&connp->conn_lock); } /* check for ires cached in faddr */ for (fp = sctp->sctp_faddrs; fp != NULL; fp = fp->next) { /* * If this ipif is being used as the source address * we need to update it as well, else we will end * up using the dead source address. */ ire = fp->ire; if (ire != NULL && ire->ire_ipif == ipif) { fp->ire = NULL; IRE_REFRELE_NOTR(ire); } /* * This may result in setting the fp as unreachable, * i.e. if all the source addresses are down. In * that case the assoc. would timeout. */ if (IN6_ARE_ADDR_EQUAL(&ipif->ipif_v6lcl_addr, &fp->saddr)) { sctp_set_saddr(sctp, fp); if (fp == sctp->sctp_current && fp->state != SCTP_FADDRS_UNREACH) { sctp_set_faddr_current(sctp, fp); } } } WAKE_SCTP(sctp); sctp_prev = sctp; mutex_enter(&sctp_g_lock); sctp = list_next(&sctp_g_list, sctp); } mutex_exit(&sctp_g_lock); if (sctp_prev != NULL) SCTP_REFRELE(sctp_prev); }
/* * 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); }
int sctp_listen(sctp_t *sctp) { sctp_tf_t *tf; sctp_stack_t *sctps = sctp->sctp_sctps; conn_t *connp = sctp->sctp_connp; RUN_SCTP(sctp); /* * TCP handles listen() increasing the backlog, need to check * if it should be handled here too */ if (sctp->sctp_state > SCTPS_BOUND || (sctp->sctp_connp->conn_state_flags & CONN_CLOSING)) { WAKE_SCTP(sctp); return (EINVAL); } /* Do an anonymous bind for unbound socket doing listen(). */ if (sctp->sctp_nsaddrs == 0) { struct sockaddr_storage ss; int ret; bzero(&ss, sizeof (ss)); ss.ss_family = connp->conn_family; WAKE_SCTP(sctp); if ((ret = sctp_bind(sctp, (struct sockaddr *)&ss, sizeof (ss))) != 0) return (ret); RUN_SCTP(sctp) } /* Cache things in the ixa without any refhold */ ASSERT(!(connp->conn_ixa->ixa_free_flags & IXA_FREE_CRED)); connp->conn_ixa->ixa_cred = connp->conn_cred; connp->conn_ixa->ixa_cpid = connp->conn_cpid; if (is_system_labeled()) connp->conn_ixa->ixa_tsl = crgetlabel(connp->conn_cred); sctp->sctp_state = SCTPS_LISTEN; (void) random_get_pseudo_bytes(sctp->sctp_secret, SCTP_SECRET_LEN); sctp->sctp_last_secret_update = ddi_get_lbolt64(); bzero(sctp->sctp_old_secret, SCTP_SECRET_LEN); /* * If there is an association limit, allocate and initialize * the counter struct. Note that since listen can be called * multiple times, the struct may have been allready allocated. */ if (!list_is_empty(&sctps->sctps_listener_conf) && sctp->sctp_listen_cnt == NULL) { sctp_listen_cnt_t *slc; uint32_t ratio; ratio = sctp_find_listener_conf(sctps, ntohs(connp->conn_lport)); if (ratio != 0) { uint32_t mem_ratio, tot_buf; slc = kmem_alloc(sizeof (sctp_listen_cnt_t), KM_SLEEP); /* * Calculate the connection limit based on * the configured ratio and maxusers. Maxusers * are calculated based on memory size, * ~ 1 user per MB. Note that the conn_rcvbuf * and conn_sndbuf may change after a * connection is accepted. So what we have * is only an approximation. */ if ((tot_buf = connp->conn_rcvbuf + connp->conn_sndbuf) < MB) { mem_ratio = MB / tot_buf; slc->slc_max = maxusers / ratio * mem_ratio; } else { mem_ratio = tot_buf / MB; slc->slc_max = maxusers / ratio / mem_ratio; } /* At least we should allow some associations! */ if (slc->slc_max < sctp_min_assoc_listener) slc->slc_max = sctp_min_assoc_listener; slc->slc_cnt = 1; slc->slc_drop = 0; sctp->sctp_listen_cnt = slc; } } tf = &sctps->sctps_listen_fanout[SCTP_LISTEN_HASH( ntohs(connp->conn_lport))]; sctp_listen_hash_insert(tf, sctp); WAKE_SCTP(sctp); return (0); }
/* * Common accept code. Called by sctp_conn_request. * cr_pkt is the INIT / INIT ACK packet. */ static int sctp_accept_comm(sctp_t *listener, sctp_t *acceptor, mblk_t *cr_pkt, uint_t ip_hdr_len, sctp_init_chunk_t *iack) { sctp_hdr_t *sctph; sctp_chunk_hdr_t *ich; sctp_init_chunk_t *init; int err; uint_t sctp_options; conn_t *aconnp; conn_t *lconnp; sctp_stack_t *sctps = listener->sctp_sctps; sctph = (sctp_hdr_t *)(cr_pkt->b_rptr + ip_hdr_len); ASSERT(OK_32PTR(sctph)); aconnp = acceptor->sctp_connp; lconnp = listener->sctp_connp; aconnp->conn_lport = lconnp->conn_lport; aconnp->conn_fport = sctph->sh_sport; ich = (sctp_chunk_hdr_t *)(iack + 1); init = (sctp_init_chunk_t *)(ich + 1); /* acceptor isn't in any fanouts yet, so don't need to hold locks */ ASSERT(acceptor->sctp_faddrs == NULL); err = sctp_get_addrparams(acceptor, listener, cr_pkt, ich, &sctp_options); if (err != 0) return (err); if ((err = sctp_set_hdraddrs(acceptor)) != 0) return (err); if ((err = sctp_build_hdrs(acceptor, KM_NOSLEEP)) != 0) return (err); if ((sctp_options & SCTP_PRSCTP_OPTION) && listener->sctp_prsctp_aware && sctps->sctps_prsctp_enabled) { acceptor->sctp_prsctp_aware = B_TRUE; } else { acceptor->sctp_prsctp_aware = B_FALSE; } /* Get initial TSNs */ acceptor->sctp_ltsn = ntohl(iack->sic_inittsn); acceptor->sctp_recovery_tsn = acceptor->sctp_lastack_rxd = acceptor->sctp_ltsn - 1; acceptor->sctp_adv_pap = acceptor->sctp_lastack_rxd; /* Serial numbers are initialized to the same value as the TSNs */ acceptor->sctp_lcsn = acceptor->sctp_ltsn; if (!sctp_initialize_params(acceptor, init, iack)) return (ENOMEM); /* * Copy sctp_secret from the listener in case we need to validate * a possibly delayed cookie. */ bcopy(listener->sctp_secret, acceptor->sctp_secret, SCTP_SECRET_LEN); bcopy(listener->sctp_old_secret, acceptor->sctp_old_secret, SCTP_SECRET_LEN); acceptor->sctp_last_secret_update = ddi_get_lbolt64(); /* * After acceptor is inserted in the hash list, it can be found. * So we need to lock it here. */ RUN_SCTP(acceptor); sctp_conn_hash_insert(&sctps->sctps_conn_fanout[ SCTP_CONN_HASH(sctps, aconnp->conn_ports)], acceptor, 0); sctp_bind_hash_insert(&sctps->sctps_bind_fanout[ SCTP_BIND_HASH(ntohs(aconnp->conn_lport))], acceptor, 0); /* * No need to check for multicast destination since ip will only pass * up multicasts to those that have expressed interest * TODO: what about rejecting broadcasts? * Also check that source is not a multicast or broadcast address. */ /* XXXSCTP */ acceptor->sctp_state = SCTPS_ESTABLISHED; acceptor->sctp_assoc_start_time = (uint32_t)ddi_get_lbolt(); /* * listener->sctp_rwnd should be the default window size or a * window size changed via SO_RCVBUF option. */ acceptor->sctp_rwnd = listener->sctp_rwnd; acceptor->sctp_irwnd = acceptor->sctp_rwnd; acceptor->sctp_pd_point = acceptor->sctp_rwnd; acceptor->sctp_upcalls = listener->sctp_upcalls; return (0); }
/* * Connect to a peer - this function inserts the sctp in the * bind and conn fanouts, sends the INIT, and replies to the client * with an OK ack. */ int sctp_connect(sctp_t *sctp, const struct sockaddr *dst, uint32_t addrlen, cred_t *cr, pid_t pid) { sin_t *sin; sin6_t *sin6; in6_addr_t dstaddr; in_port_t dstport; mblk_t *initmp; sctp_tf_t *tbf; sctp_t *lsctp; char buf[INET6_ADDRSTRLEN]; int sleep = sctp->sctp_cansleep ? KM_SLEEP : KM_NOSLEEP; int err; sctp_faddr_t *cur_fp; sctp_stack_t *sctps = sctp->sctp_sctps; conn_t *connp = sctp->sctp_connp; uint_t scope_id = 0; ip_xmit_attr_t *ixa; /* * Determine packet type based on type of address passed in * the request should contain an IPv4 or IPv6 address. * Make sure that address family matches the type of * family of the address passed down. */ if (addrlen < sizeof (sin_t)) { return (EINVAL); } switch (dst->sa_family) { case AF_INET: sin = (sin_t *)dst; /* Check for attempt to connect to non-unicast */ if (CLASSD(sin->sin_addr.s_addr) || (sin->sin_addr.s_addr == INADDR_BROADCAST)) { ip0dbg(("sctp_connect: non-unicast\n")); return (EINVAL); } if (connp->conn_ipv6_v6only) return (EAFNOSUPPORT); /* convert to v6 mapped */ /* Check for attempt to connect to INADDR_ANY */ if (sin->sin_addr.s_addr == INADDR_ANY) { struct in_addr v4_addr; /* * SunOS 4.x and 4.3 BSD allow an application * to connect a TCP socket to INADDR_ANY. * When they do this, the kernel picks the * address of one interface and uses it * instead. The kernel usually ends up * picking the address of the loopback * interface. This is an undocumented feature. * However, we provide the same thing here * in case any TCP apps that use this feature * are being ported to SCTP... */ v4_addr.s_addr = htonl(INADDR_LOOPBACK); IN6_INADDR_TO_V4MAPPED(&v4_addr, &dstaddr); } else { IN6_INADDR_TO_V4MAPPED(&sin->sin_addr, &dstaddr); } dstport = sin->sin_port; break; case AF_INET6: sin6 = (sin6_t *)dst; /* Check for attempt to connect to non-unicast. */ if ((addrlen < sizeof (sin6_t)) || IN6_IS_ADDR_MULTICAST(&sin6->sin6_addr)) { ip0dbg(("sctp_connect: non-unicast\n")); return (EINVAL); } if (connp->conn_ipv6_v6only && IN6_IS_ADDR_V4MAPPED(&sin6->sin6_addr)) { return (EAFNOSUPPORT); } /* check for attempt to connect to unspec */ if (IN6_IS_ADDR_UNSPECIFIED(&sin6->sin6_addr)) { dstaddr = ipv6_loopback; } else { dstaddr = sin6->sin6_addr; if (IN6_IS_ADDR_LINKLOCAL(&dstaddr)) { sctp->sctp_linklocal = 1; scope_id = sin6->sin6_scope_id; } } dstport = sin6->sin6_port; connp->conn_flowinfo = sin6->sin6_flowinfo; break; default: dprint(1, ("sctp_connect: unknown family %d\n", dst->sa_family)); return (EAFNOSUPPORT); } (void) inet_ntop(AF_INET6, &dstaddr, buf, sizeof (buf)); dprint(1, ("sctp_connect: attempting connect to %s...\n", buf)); RUN_SCTP(sctp); if (connp->conn_family != dst->sa_family || (connp->conn_state_flags & CONN_CLOSING)) { WAKE_SCTP(sctp); return (EINVAL); } /* We update our cred/cpid based on the caller of connect */ if (connp->conn_cred != cr) { crhold(cr); crfree(connp->conn_cred); connp->conn_cred = cr; } connp->conn_cpid = pid; /* Cache things in conn_ixa without any refhold */ ixa = connp->conn_ixa; ixa->ixa_cred = cr; ixa->ixa_cpid = pid; if (is_system_labeled()) { /* We need to restart with a label based on the cred */ ip_xmit_attr_restore_tsl(ixa, ixa->ixa_cred); } switch (sctp->sctp_state) { case SCTPS_IDLE: { struct sockaddr_storage ss; /* * We support a quick connect capability here, allowing * clients to transition directly from IDLE to COOKIE_WAIT. * sctp_bindi will pick an unused port, insert the connection * in the bind hash and transition to BOUND state. SCTP * picks and uses what it considers the optimal local address * set (just like specifiying INADDR_ANY to bind()). */ dprint(1, ("sctp_connect: idle, attempting bind...\n")); ASSERT(sctp->sctp_nsaddrs == 0); bzero(&ss, sizeof (ss)); ss.ss_family = connp->conn_family; WAKE_SCTP(sctp); if ((err = sctp_bind(sctp, (struct sockaddr *)&ss, sizeof (ss))) != 0) { return (err); } RUN_SCTP(sctp); /* FALLTHRU */ } case SCTPS_BOUND: ASSERT(sctp->sctp_nsaddrs > 0); /* do the connect */ /* XXX check for attempt to connect to self */ connp->conn_fport = dstport; ASSERT(sctp->sctp_iphc); ASSERT(sctp->sctp_iphc6); /* * Don't allow this connection to completely duplicate * an existing connection. * * Ensure that the duplicate check and insertion is atomic. */ sctp_conn_hash_remove(sctp); tbf = &sctps->sctps_conn_fanout[SCTP_CONN_HASH(sctps, connp->conn_ports)]; mutex_enter(&tbf->tf_lock); lsctp = sctp_lookup(sctp, &dstaddr, tbf, &connp->conn_ports, SCTPS_COOKIE_WAIT); if (lsctp != NULL) { /* found a duplicate connection */ mutex_exit(&tbf->tf_lock); SCTP_REFRELE(lsctp); WAKE_SCTP(sctp); return (EADDRINUSE); } /* * OK; set up the peer addr (this may grow after we get * the INIT ACK from the peer with additional addresses). */ if ((err = sctp_add_faddr(sctp, &dstaddr, sleep, B_FALSE)) != 0) { mutex_exit(&tbf->tf_lock); WAKE_SCTP(sctp); return (err); } cur_fp = sctp->sctp_faddrs; ASSERT(cur_fp->ixa != NULL); /* No valid src addr, return. */ if (cur_fp->state == SCTP_FADDRS_UNREACH) { mutex_exit(&tbf->tf_lock); WAKE_SCTP(sctp); return (EADDRNOTAVAIL); } sctp->sctp_primary = cur_fp; sctp->sctp_current = cur_fp; sctp->sctp_mss = cur_fp->sfa_pmss; sctp_conn_hash_insert(tbf, sctp, 1); mutex_exit(&tbf->tf_lock); ixa = cur_fp->ixa; ASSERT(ixa->ixa_cred != NULL); if (scope_id != 0) { ixa->ixa_flags |= IXAF_SCOPEID_SET; ixa->ixa_scopeid = scope_id; } else { ixa->ixa_flags &= ~IXAF_SCOPEID_SET; } /* initialize composite headers */ if ((err = sctp_set_hdraddrs(sctp)) != 0) { sctp_conn_hash_remove(sctp); WAKE_SCTP(sctp); return (err); } if ((err = sctp_build_hdrs(sctp, KM_SLEEP)) != 0) { sctp_conn_hash_remove(sctp); WAKE_SCTP(sctp); return (err); } /* * Turn off the don't fragment bit on the (only) faddr, * so that if one of the messages exchanged during the * initialization sequence exceeds the path mtu, it * at least has a chance to get there. SCTP does no * fragmentation of initialization messages. The DF bit * will be turned on again in sctp_send_cookie_echo() * (but the cookie echo will still be sent with the df bit * off). */ cur_fp->df = B_FALSE; /* Mark this address as alive */ cur_fp->state = SCTP_FADDRS_ALIVE; /* Send the INIT to the peer */ SCTP_FADDR_TIMER_RESTART(sctp, cur_fp, cur_fp->rto); sctp->sctp_state = SCTPS_COOKIE_WAIT; /* * sctp_init_mp() could result in modifying the source * address list, so take the hash lock. */ mutex_enter(&tbf->tf_lock); initmp = sctp_init_mp(sctp, cur_fp); if (initmp == NULL) { mutex_exit(&tbf->tf_lock); /* * It may happen that all the source addresses * (loopback/link local) are removed. In that case, * faile the connect. */ if (sctp->sctp_nsaddrs == 0) { sctp_conn_hash_remove(sctp); SCTP_FADDR_TIMER_STOP(cur_fp); WAKE_SCTP(sctp); return (EADDRNOTAVAIL); } /* Otherwise, let the retransmission timer retry */ WAKE_SCTP(sctp); goto notify_ulp; } mutex_exit(&tbf->tf_lock); /* * On a clustered note send this notification to the clustering * subsystem. */ if (cl_sctp_connect != NULL) { uchar_t *slist; uchar_t *flist; size_t ssize; size_t fsize; fsize = sizeof (in6_addr_t) * sctp->sctp_nfaddrs; ssize = sizeof (in6_addr_t) * sctp->sctp_nsaddrs; slist = kmem_alloc(ssize, KM_SLEEP); flist = kmem_alloc(fsize, KM_SLEEP); /* The clustering module frees the lists */ sctp_get_saddr_list(sctp, slist, ssize); sctp_get_faddr_list(sctp, flist, fsize); (*cl_sctp_connect)(connp->conn_family, slist, sctp->sctp_nsaddrs, connp->conn_lport, flist, sctp->sctp_nfaddrs, connp->conn_fport, B_TRUE, (cl_sctp_handle_t)sctp); } ASSERT(ixa->ixa_cred != NULL); ASSERT(ixa->ixa_ire != NULL); (void) conn_ip_output(initmp, ixa); BUMP_LOCAL(sctp->sctp_opkts); WAKE_SCTP(sctp); notify_ulp: sctp_set_ulp_prop(sctp); return (0); default: ip0dbg(("sctp_connect: invalid state. %d\n", sctp->sctp_state)); WAKE_SCTP(sctp); return (EINVAL); } }