/* * Generic for IPv4 and IPv6. * * Used by callers that need to cache e.g., the datapath * Returns the generation number in the last argument. */ dce_t * dce_lookup_pkt(mblk_t *mp, ip_xmit_attr_t *ixa, uint_t *generationp) { if (ixa->ixa_flags & IXAF_IS_IPV4) { /* * If we have a source route we need to look for the final * destination in the source route option. */ ipaddr_t final_dst; ipha_t *ipha = (ipha_t *)mp->b_rptr; final_dst = ip_get_dst(ipha); return (dce_lookup_v4(final_dst, ixa->ixa_ipst, generationp)); } else { uint_t ifindex; /* * If we have a routing header we need to look for the final * destination in the routing extension header. */ in6_addr_t final_dst; ip6_t *ip6h = (ip6_t *)mp->b_rptr; final_dst = ip_get_dst_v6(ip6h, mp, NULL); ifindex = 0; if (IN6_IS_ADDR_LINKSCOPE(&final_dst) && ixa->ixa_nce != NULL) { ifindex = ixa->ixa_nce->nce_common->ncec_ill-> ill_phyint->phyint_ifindex; } return (dce_lookup_v6(&final_dst, ifindex, ixa->ixa_ipst, generationp)); } }
/* * Atomically looks for a non-default DCE, and if not found tries to create one. * If there is no memory it returns NULL. * When an entry is created we increase the generation number on * the default DCE so that conn_ip_output will detect there is a new DCE. * ifindex should only be used with link-local addresses. */ dce_t * dce_lookup_and_add_v6(const in6_addr_t *dst, uint_t ifindex, ip_stack_t *ipst) { uint_t hash; dcb_t *dcb; dce_t *dce; /* We should not create entries for link-locals w/o an ifindex */ ASSERT(!(IN6_IS_ADDR_LINKSCOPE(dst)) || ifindex != 0); hash = IRE_ADDR_HASH_V6(*dst, ipst->ips_dce_hashsize); dcb = &ipst->ips_dce_hash_v6[hash]; rw_enter(&dcb->dcb_lock, RW_WRITER); for (dce = dcb->dcb_dce; dce != NULL; dce = dce->dce_next) { if (IN6_ARE_ADDR_EQUAL(&dce->dce_v6addr, dst) && dce->dce_ifindex == ifindex) { mutex_enter(&dce->dce_lock); if (!DCE_IS_CONDEMNED(dce)) { dce_refhold(dce); mutex_exit(&dce->dce_lock); rw_exit(&dcb->dcb_lock); return (dce); } mutex_exit(&dce->dce_lock); } } dce = kmem_cache_alloc(dce_cache, KM_NOSLEEP); if (dce == NULL) { rw_exit(&dcb->dcb_lock); return (NULL); } bzero(dce, sizeof (dce_t)); dce->dce_ipst = ipst; /* No netstack_hold */ dce->dce_v6addr = *dst; dce->dce_ifindex = ifindex; dce->dce_generation = DCE_GENERATION_INITIAL; dce->dce_ipversion = IPV6_VERSION; dce->dce_last_change_time = TICK_TO_SEC(ddi_get_lbolt64()); dce_refhold(dce); /* For the hash list */ /* Link into list */ if (dcb->dcb_dce != NULL) dcb->dcb_dce->dce_ptpn = &dce->dce_next; dce->dce_next = dcb->dcb_dce; dce->dce_ptpn = &dcb->dcb_dce; dcb->dcb_dce = dce; dce->dce_bucket = dcb; atomic_add_32(&dcb->dcb_cnt, 1); dce_refhold(dce); /* For the caller */ rw_exit(&dcb->dcb_lock); /* Initialize dce_ident to be different than for the last packet */ dce->dce_ident = ipst->ips_dce_default->dce_ident + 1; dce_increment_generation(ipst->ips_dce_default); return (dce); }
void sctp_update_dce(sctp_t *sctp) { sctp_faddr_t *fp; sctp_stack_t *sctps = sctp->sctp_sctps; iulp_t uinfo; ip_stack_t *ipst = sctps->sctps_netstack->netstack_ip; uint_t ifindex; for (fp = sctp->sctp_faddrs; fp != NULL; fp = fp->next) { bzero(&uinfo, sizeof (uinfo)); /* * Only record the PMTU for this faddr if we actually have * done discovery. This prevents initialized default from * clobbering any real info that IP may have. */ if (fp->pmtu_discovered) { if (fp->isv4) { uinfo.iulp_mtu = fp->sfa_pmss + sctp->sctp_hdr_len; } else { uinfo.iulp_mtu = fp->sfa_pmss + sctp->sctp_hdr6_len; } } if (sctps->sctps_rtt_updates != 0 && fp->rtt_updates >= sctps->sctps_rtt_updates) { /* * dce_update_uinfo() merges these values with the * old values. */ uinfo.iulp_rtt = TICK_TO_MSEC(fp->srtt); uinfo.iulp_rtt_sd = TICK_TO_MSEC(fp->rttvar); fp->rtt_updates = 0; } ifindex = 0; if (IN6_IS_ADDR_LINKSCOPE(&fp->faddr)) { /* * If we are going to create a DCE we'd better have * an ifindex */ if (fp->ixa->ixa_nce != NULL) { ifindex = fp->ixa->ixa_nce->nce_common-> ncec_ill->ill_phyint->phyint_ifindex; } else { continue; } } (void) dce_update_uinfo(&fp->faddr, ifindex, &uinfo, ipst); } }
/* * 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); }
/* * OOTB version of the above. * If iserror == 0, sends an abort. If iserror != 0, sends an error. */ void sctp_ootb_send_abort(uint32_t vtag, uint16_t serror, char *details, size_t len, const mblk_t *inmp, int iserror, boolean_t tbit, ip_recv_attr_t *ira, ip_stack_t *ipst) { uint32_t ip_hdr_len; size_t ahlen; ipha_t *ipha = NULL; ip6_t *ip6h = NULL; sctp_hdr_t *insctph; int i; uint16_t port; ssize_t alen; int isv4; mblk_t *mp; netstack_t *ns = ipst->ips_netstack; sctp_stack_t *sctps = ns->netstack_sctp; ip_xmit_attr_t ixas; bzero(&ixas, sizeof (ixas)); isv4 = (IPH_HDR_VERSION(inmp->b_rptr) == IPV4_VERSION); ip_hdr_len = ira->ira_ip_hdr_length; ahlen = ip_hdr_len + sizeof (sctp_hdr_t); /* * If this is a labeled system, then check to see if we're allowed to * send a response to this particular sender. If not, then just drop. */ if (is_system_labeled() && !tsol_can_reply_error(inmp, ira)) return; mp = allocb(ahlen + sctps->sctps_wroff_xtra, BPRI_MED); if (mp == NULL) { return; } mp->b_rptr += sctps->sctps_wroff_xtra; mp->b_wptr = mp->b_rptr + ahlen; bcopy(inmp->b_rptr, mp->b_rptr, ahlen); /* * We follow the logic in tcp_xmit_early_reset() in that we skip * reversing source route (i.e. replace all IP options with EOL). */ if (isv4) { ipaddr_t v4addr; ipha = (ipha_t *)mp->b_rptr; for (i = IP_SIMPLE_HDR_LENGTH; i < (int)ip_hdr_len; i++) mp->b_rptr[i] = IPOPT_EOL; /* Swap addresses */ ipha->ipha_length = htons(ahlen); v4addr = ipha->ipha_src; ipha->ipha_src = ipha->ipha_dst; ipha->ipha_dst = v4addr; ipha->ipha_ident = 0; ipha->ipha_ttl = (uchar_t)sctps->sctps_ipv4_ttl; ixas.ixa_flags = IXAF_BASIC_SIMPLE_V4; } else { in6_addr_t v6addr; ip6h = (ip6_t *)mp->b_rptr; /* Remove any extension headers assuming partial overlay */ if (ip_hdr_len > IPV6_HDR_LEN) { uint8_t *to; to = mp->b_rptr + ip_hdr_len - IPV6_HDR_LEN; ovbcopy(ip6h, to, IPV6_HDR_LEN); mp->b_rptr += ip_hdr_len - IPV6_HDR_LEN; ip_hdr_len = IPV6_HDR_LEN; ip6h = (ip6_t *)mp->b_rptr; ip6h->ip6_nxt = IPPROTO_SCTP; ahlen = ip_hdr_len + sizeof (sctp_hdr_t); } ip6h->ip6_plen = htons(ahlen - IPV6_HDR_LEN); v6addr = ip6h->ip6_src; ip6h->ip6_src = ip6h->ip6_dst; ip6h->ip6_dst = v6addr; ip6h->ip6_hops = (uchar_t)sctps->sctps_ipv6_hoplimit; ixas.ixa_flags = IXAF_BASIC_SIMPLE_V6; if (IN6_IS_ADDR_LINKSCOPE(&ip6h->ip6_dst)) { ixas.ixa_flags |= IXAF_SCOPEID_SET; ixas.ixa_scopeid = ira->ira_ruifindex; } } insctph = (sctp_hdr_t *)(mp->b_rptr + ip_hdr_len); /* Swap ports. Verification tag is reused. */ port = insctph->sh_sport; insctph->sh_sport = insctph->sh_dport; insctph->sh_dport = port; insctph->sh_verf = vtag; /* Link in the abort chunk */ if ((alen = sctp_link_abort(mp, serror, details, len, iserror, tbit)) < 0) { freemsg(mp); return; } ixas.ixa_pktlen = ahlen + alen; ixas.ixa_ip_hdr_length = ip_hdr_len; if (isv4) { ipha->ipha_length = htons(ixas.ixa_pktlen); } else { ip6h->ip6_plen = htons(ixas.ixa_pktlen - IPV6_HDR_LEN); } ixas.ixa_protocol = IPPROTO_SCTP; ixas.ixa_zoneid = ira->ira_zoneid; ixas.ixa_ipst = ipst; ixas.ixa_ifindex = 0; SCTPS_BUMP_MIB(sctps, sctpAborted); if (is_system_labeled()) { ASSERT(ira->ira_tsl != NULL); ixas.ixa_tsl = ira->ira_tsl; /* A multi-level responder */ } if (ira->ira_flags & IRAF_IPSEC_SECURE) { /* * Apply IPsec based on how IPsec was applied to * the packet that was out of the blue. */ if (!ipsec_in_to_out(ira, &ixas, mp, ipha, ip6h)) { BUMP_MIB(&ipst->ips_ip_mib, ipIfStatsOutDiscards); /* Note: mp already consumed and ip_drop_packet done */ return; } } else { /* * This is in clear. The abort message we are building * here should go out in clear, independent of our policy. */ ixas.ixa_flags |= IXAF_NO_IPSEC; } (void) ip_output_simple(mp, &ixas); ixa_cleanup(&ixas); }