DEFINE_APITEST(peeloff, goodcase) { int lfd, cfd, pfd; struct sockaddr_in addr; socklen_t addr_len; sctp_assoc_t assoc_id; if ((lfd = socket(AF_INET, SOCK_STREAM, IPPROTO_SCTP)) < 0) { RETURN_FAILED_WITH_ERRNO; } if (sctp_bind(lfd, INADDR_LOOPBACK, 0) < 0) { close(lfd); RETURN_FAILED_WITH_ERRNO; } if (listen(lfd, 1) < 0) { close(lfd); RETURN_FAILED_WITH_ERRNO; } if ((cfd = socket(AF_INET, SOCK_SEQPACKET, IPPROTO_SCTP)) < 0) { close(lfd); RETURN_FAILED_WITH_ERRNO; } if (sctp_bind(cfd, INADDR_LOOPBACK, 0) < 0) { close(lfd); close(cfd); RETURN_FAILED_WITH_ERRNO; } addr_len = (socklen_t)sizeof(struct sockaddr_in); if (getsockname (lfd, (struct sockaddr *) &addr, &addr_len) < 0) { close(lfd); close(cfd); RETURN_FAILED_WITH_ERRNO; } if (sctp_connectx(cfd, (struct sockaddr *) &addr, 1, &assoc_id) < 0) { close(lfd); close(cfd); RETURN_FAILED_WITH_ERRNO; } sctp_delay(SCTP_SLEEP_MS); if ((pfd = sctp_peeloff(cfd, assoc_id)) < 0) { close(lfd); close(cfd); RETURN_FAILED_WITH_ERRNO; } if (close(pfd) < 0) { close(cfd); close(lfd); RETURN_FAILED_WITH_ERRNO; } if (close(cfd) < 0) { close(lfd); RETURN_FAILED_WITH_ERRNO; } if (close(lfd) < 0) { RETURN_FAILED_WITH_ERRNO; } RETURN_PASSED; }
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); }
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); }
/* * 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); } }