struct ifaddr * ifa_ifwithroute(int flags, struct sockaddr *dst, struct sockaddr *gateway, u_int rtableid) { struct ifaddr *ifa; #ifdef IPSEC /* * If the destination is a PF_KEY address, we'll look * for the existence of a encap interface number or address * in the options list of the gateway. By default, we'll return * enc0. */ if (dst && (dst->sa_family == PF_KEY)) return (encap_findgwifa(gateway)); #endif if ((flags & RTF_GATEWAY) == 0) { /* * If we are adding a route to an interface, * and the interface is a pt to pt link * we should search for the destination * as our clue to the interface. Otherwise * we can use the local address. */ ifa = NULL; if (flags & RTF_HOST) ifa = ifa_ifwithdstaddr(dst, rtableid); if (ifa == NULL) ifa = ifa_ifwithaddr(gateway, rtableid); } else { /* * If we are adding a route to a remote net * or host, the gateway may still be on the * other end of a pt to pt link. */ ifa = ifa_ifwithdstaddr(gateway, rtableid); } if (ifa == NULL) ifa = ifa_ifwithnet(gateway, rtableid); if (ifa == NULL) { struct rtentry *rt = rtalloc1(gateway, 0, rtable_l2(rtableid)); if (rt == NULL) return (NULL); rt->rt_refcnt--; /* The gateway must be local if the same address family. */ if ((rt->rt_flags & RTF_GATEWAY) && rt_key(rt)->sa_family == dst->sa_family) return (0); if ((ifa = rt->rt_ifa) == NULL) return (NULL); } if (ifa->ifa_addr->sa_family != dst->sa_family) { struct ifaddr *oifa = ifa; ifa = ifaof_ifpforaddr(dst, ifa->ifa_ifp); if (ifa == NULL) ifa = oifa; } return (ifa); }
STATUS ifUnnumberedSet ( char *pIfName, /* Name of interface to configure */ char *pDstIp, /* Destination address of the point to point link */ char *pBorrowedIp, /* The borrowed IP address/router ID */ char *pDstMac /* Destination MAC address */ ) { int s, ret; struct ifnet *ifp; struct sockaddr_in sock; if(!(ifp = ifunit (pIfName))) return (ERROR); /* * Make sure that the interface with the "real" * pBorrowedIp is already brought up. */ bzero ((char *)&sock, sizeof (struct sockaddr_in)); sock.sin_family = AF_INET; sock.sin_len = sizeof (struct sockaddr_in); sock.sin_addr.s_addr = inet_addr (pBorrowedIp); if (!ifa_ifwithaddr ((struct sockaddr *)&sock)) return (EINVAL); /* We manually configure the interface to be IFF_UNNUMBERED */ s = splnet (); ifp->if_flags &= ~IFF_BROADCAST; ifp->if_flags |= IFF_UNNUMBERED; /* It's defined as IFF_POINTOPOINT */ splx (s); ret = ifAddrAdd (pIfName, pBorrowedIp, pDstIp, 0); if (ret != OK) return (ret); /* * For ethernet devices, we need to complete the route entry * just created by the above operation. The ATF_INCOMPLETE flag * will change the routing entry to contain the MAC address * of the destination's ethernet device. */ if (pDstMac != (char *)NULL) ret = arpAdd (pDstIp, pDstMac, ATF_PERM | ATF_INCOMPLETE); return (ret); }
int rt_getifa(struct rt_addrinfo *info, u_int rtid) { struct ifaddr *ifa; int error = 0; /* * ifp may be specified by sockaddr_dl when protocol address * is ambiguous */ if (info->rti_ifp == NULL && info->rti_info[RTAX_IFP] != NULL && info->rti_info[RTAX_IFP]->sa_family == AF_LINK && (ifa = ifa_ifwithnet((struct sockaddr *)info->rti_info[RTAX_IFP], rtid)) != NULL) info->rti_ifp = ifa->ifa_ifp; if (info->rti_ifa == NULL && info->rti_info[RTAX_IFA] != NULL) info->rti_ifa = ifa_ifwithaddr(info->rti_info[RTAX_IFA], rtid); if (info->rti_ifa == NULL) { struct sockaddr *sa; if ((sa = info->rti_info[RTAX_IFA]) == NULL) if ((sa = info->rti_info[RTAX_GATEWAY]) == NULL) sa = info->rti_info[RTAX_DST]; if (sa != NULL && info->rti_ifp != NULL) info->rti_ifa = ifaof_ifpforaddr(sa, info->rti_ifp); else if (info->rti_info[RTAX_DST] != NULL && info->rti_info[RTAX_GATEWAY] != NULL) info->rti_ifa = ifa_ifwithroute(info->rti_flags, info->rti_info[RTAX_DST], info->rti_info[RTAX_GATEWAY], rtid); else if (sa != NULL) info->rti_ifa = ifa_ifwithroute(info->rti_flags, sa, sa, rtid); } if ((ifa = info->rti_ifa) != NULL) { if (info->rti_ifp == NULL) info->rti_ifp = ifa->ifa_ifp; } else error = ENETUNREACH; return (error); }
static int in_pcbbind_addr(struct inpcb *inp, struct sockaddr_in *sin) { if (sin->sin_family != AF_INET) return (EAFNOSUPPORT); if (IN_MULTICAST(sin->sin_addr.s_addr)) { /* Always succeed; port reuse handled in in_pcbbind_port(). */ } else if (!in_nullhost(sin->sin_addr)) { struct in_ifaddr *ia = NULL; INADDR_TO_IA(sin->sin_addr, ia); /* check for broadcast addresses */ if (ia == NULL) ia = ifatoia(ifa_ifwithaddr(sintosa(sin))); if (ia == NULL) return (EADDRNOTAVAIL); } inp->inp_laddr = sin->sin_addr; return (0); }
static int map_fix_local_rlocs(struct locator_chain * lcptr) { int error = EINVAL; struct sockaddr_in *rloc_inet = NULL; struct sockaddr_in6 *rloc_inet6 = NULL; struct in_ifaddr *ia = NULL; struct in6_ifaddr *ia6 = NULL; while ( lcptr ) { /* Scan the chain checking if the RLOC is the address * of a local interface. */ switch (lcptr->rloc.rloc_addr->ss_family) { case AF_INET: rloc_inet = (struct sockaddr_in *) lcptr->rloc.rloc_addr; INADDR_TO_IFADDR(rloc_inet->sin_addr, ia); /* * If the address matches, set RLOCF_LIF * flag and MTU. */ if ((ia != NULL) && (IA_SIN(ia)->sin_addr.s_addr == rloc_inet->sin_addr.s_addr)) { lcptr->rloc.rloc_metrix.rlocmtx.flags |= RLOCF_LIF; lcptr->rloc.rloc_metrix.rlocmtx.mtu = (ia->ia_ifp)->if_mtu; error = 0; }; break; case AF_INET6: rloc_inet6 = (struct sockaddr_in6 *) lcptr->rloc.rloc_addr; ia6 = (struct in6_ifaddr *)ifa_ifwithaddr((struct sockaddr *)(rloc_inet6)); /* * If the address matches, set RLOCF_LIF * flag and MTU. */ if ((ia6 != NULL) && (IN6_ARE_ADDR_EQUAL(&ia6->ia_addr.sin6_addr, &rloc_inet6->sin6_addr))) { lcptr->rloc.rloc_metrix.rlocmtx.flags |= RLOCF_LIF; lcptr->rloc.rloc_metrix.rlocmtx.mtu = (ia6->ia_ifp)->if_mtu; error = 0; }; break; }; lcptr = lcptr->next; }; #ifdef LISP_DEBUG if (error) { DEBUGLISP("[MAP_FIX_LOCAL_RLOC] No local IF RLOCs Provided for local mapping! \n"); }; #endif /* LISP_DEBUG */ return (error); } /* map_fix_local_rloc() */
int in_pcbbind(struct inpcb *inp, struct mbuf *nam) { register struct socket *so = inp->inp_socket; unsigned short *lastport; struct sockaddr_in *sin; u_short lport = 0; int wild = 0, reuseport = (so->so_options & SO_REUSEPORT); int error; if (in_ifaddr == 0) return (EADDRNOTAVAIL); if (inp->inp_lport || inp->inp_laddr.s_addr != INADDR_ANY) return (EINVAL); if ((so->so_options & (SO_REUSEADDR|SO_REUSEPORT)) == 0 && ((so->so_proto->pr_flags & PR_CONNREQUIRED) == 0 || (so->so_options & SO_ACCEPTCONN) == 0)) wild = 1; if (nam) { sin = mtod(nam, struct sockaddr_in *); if (nam->m_len != sizeof (*sin)) return (EINVAL); #ifdef notdef /* * We should check the family, but old programs * incorrectly fail to initialize it. */ if (sin->sin_family != AF_INET) return (EAFNOSUPPORT); #endif lport = sin->sin_port; if (IN_MULTICAST(ntohl(sin->sin_addr.s_addr))) { /* * Treat SO_REUSEADDR as SO_REUSEPORT for multicast; * allow complete duplication of binding if * SO_REUSEPORT is set, or if SO_REUSEADDR is set * and a multicast address is bound on both * new and duplicated sockets. */ if (so->so_options & SO_REUSEADDR) reuseport = SO_REUSEADDR|SO_REUSEPORT; } else if (sin->sin_addr.s_addr != INADDR_ANY) { sin->sin_port = 0; /* yech... */ if (ifa_ifwithaddr((struct sockaddr *)sin) == 0) return (EADDRNOTAVAIL); } if (lport) { struct inpcb *t; /* GROSS */ if (ntohs(lport) < IPPORT_RESERVED && (error = suser(p->p_ucred, &p->p_acflag))) return (EACCES); if (so->so_uid) { t = in_pcblookup(inp->inp_pcbinfo, zeroin_addr, 0, sin->sin_addr, lport, INPLOOKUP_WILDCARD); if (t && (so->so_uid != t->inp_socket->so_uid)) return (EADDRINUSE); } t = in_pcblookup(inp->inp_pcbinfo, zeroin_addr, 0, sin->sin_addr, lport, wild); if (t && (reuseport & t->inp_socket->so_options) == 0) return (EADDRINUSE); } inp->inp_laddr = sin->sin_addr; } if (lport == 0) { unsigned short first, last; int count; inp->inp_flags |= INP_ANONPORT; if (inp->inp_flags & INP_HIGHPORT) { first = ipport_hifirstauto; /* sysctl */ last = ipport_hilastauto; lastport = &inp->inp_pcbinfo->lasthi; } else if (inp->inp_flags & INP_LOWPORT) { if ((error = suser(p->p_ucred, &p->p_acflag))) return (EACCES); first = ipport_lowfirstauto; /* 1023 */ last = ipport_lowlastauto; /* 600 */ lastport = &inp->inp_pcbinfo->lastlow; } else { first = ipport_firstauto; /* sysctl */ last = ipport_lastauto; lastport = &inp->inp_pcbinfo->lastport; } /* * Simple check to ensure all ports are not used up causing * a deadlock here. * * We split the two cases (up and down) so that the direction * is not being tested on each round of the loop. */ if (first > last) { /* * counting down */ count = first - last; do { if (count-- <= 0) /* completely used? */ return (EADDRNOTAVAIL); --*lastport; if (*lastport > first || *lastport < last) *lastport = first; lport = htons(*lastport); } while (in_pcblookup(inp->inp_pcbinfo, zeroin_addr, 0, inp->inp_laddr, lport, wild)); } else { /* * counting up */ count = last - first; do { if (count-- <= 0) /* completely used? */ return (EADDRNOTAVAIL); ++*lastport; if (*lastport < first || *lastport > last) *lastport = first; lport = htons(*lastport); } while (in_pcblookup(inp->inp_pcbinfo, zeroin_addr, 0, inp->inp_laddr, lport, wild)); } } inp->inp_lport = lport; in_pcbrehash(inp); return (0); }
/* * Force a routing table entry to the specified * destination to go through the given gateway. * Normally called as a result of a routing redirect * message from the network layer. * * N.B.: must be called at splsoftnet */ void rtredirect(struct sockaddr *dst, struct sockaddr *gateway, struct sockaddr *netmask, int flags, struct sockaddr *src, struct rtentry **rtp, u_int rdomain) { struct rtentry *rt; int error = 0; u_int32_t *stat = NULL; struct rt_addrinfo info; struct ifaddr *ifa; struct ifnet *ifp = NULL; splsoftassert(IPL_SOFTNET); /* verify the gateway is directly reachable */ if ((ifa = ifa_ifwithnet(gateway, rdomain)) == NULL) { error = ENETUNREACH; goto out; } ifp = ifa->ifa_ifp; rt = rtalloc1(dst, 0, rdomain); /* * If the redirect isn't from our current router for this dst, * it's either old or wrong. If it redirects us to ourselves, * we have a routing loop, perhaps as a result of an interface * going down recently. */ #define equal(a1, a2) \ ((a1)->sa_len == (a2)->sa_len && \ bcmp((caddr_t)(a1), (caddr_t)(a2), (a1)->sa_len) == 0) if (!(flags & RTF_DONE) && rt && (!equal(src, rt->rt_gateway) || rt->rt_ifa != ifa)) error = EINVAL; else if (ifa_ifwithaddr(gateway, rdomain) != NULL) error = EHOSTUNREACH; if (error) goto done; /* * Create a new entry if we just got back a wildcard entry * or the lookup failed. This is necessary for hosts * which use routing redirects generated by smart gateways * to dynamically build the routing tables. */ if ((rt == NULL) || (rt_mask(rt) && rt_mask(rt)->sa_len < 2)) goto create; /* * Don't listen to the redirect if it's * for a route to an interface. */ if (rt->rt_flags & RTF_GATEWAY) { if (((rt->rt_flags & RTF_HOST) == 0) && (flags & RTF_HOST)) { /* * Changing from route to net => route to host. * Create new route, rather than smashing route to net. */ create: if (rt) rtfree(rt); flags |= RTF_GATEWAY | RTF_DYNAMIC; bzero(&info, sizeof(info)); info.rti_info[RTAX_DST] = dst; info.rti_info[RTAX_GATEWAY] = gateway; info.rti_info[RTAX_NETMASK] = netmask; info.rti_ifa = ifa; info.rti_flags = flags; rt = NULL; error = rtrequest1(RTM_ADD, &info, RTP_DEFAULT, &rt, rdomain); if (rt != NULL) flags = rt->rt_flags; stat = &rtstat.rts_dynamic; } else { /* * Smash the current notion of the gateway to * this destination. Should check about netmask!!! */ rt->rt_flags |= RTF_MODIFIED; flags |= RTF_MODIFIED; stat = &rtstat.rts_newgateway; rt_setgate(rt, rt_key(rt), gateway, rdomain); } } else error = EHOSTUNREACH; done: if (rt) { if (rtp && !error) *rtp = rt; else rtfree(rt); } out: if (error) rtstat.rts_badredirect++; else if (stat != NULL) (*stat)++; bzero((caddr_t)&info, sizeof(info)); info.rti_info[RTAX_DST] = dst; info.rti_info[RTAX_GATEWAY] = gateway; info.rti_info[RTAX_NETMASK] = netmask; info.rti_info[RTAX_AUTHOR] = src; rt_missmsg(RTM_REDIRECT, &info, flags, ifp, error, rdomain); }
/* * Do option processing on a datagram, possibly discarding it if bad options * are encountered, or forwarding it if source-routed. * * The pass argument is used when operating in the IPSTEALTH mode to tell * what options to process: [LS]SRR (pass 0) or the others (pass 1). The * reason for as many as two passes is that when doing IPSTEALTH, non-routing * options should be processed only if the packet is for us. * * Returns 1 if packet has been forwarded/freed, 0 if the packet should be * processed further. */ int ip_dooptions(struct mbuf *m, int pass) { struct ip *ip = mtod(m, struct ip *); u_char *cp; struct in_ifaddr *ia; int opt, optlen, cnt, off, code, type = ICMP_PARAMPROB, forward = 0; struct in_addr *sin, dst; uint32_t ntime; struct sockaddr_in ipaddr = { sizeof(ipaddr), AF_INET }; /* Ignore or reject packets with IP options. */ if (ip_doopts == 0) return 0; else if (ip_doopts == 2) { type = ICMP_UNREACH; code = ICMP_UNREACH_FILTER_PROHIB; goto bad; } dst = ip->ip_dst; cp = (u_char *)(ip + 1); cnt = (ip->ip_hl << 2) - sizeof (struct ip); for (; cnt > 0; cnt -= optlen, cp += optlen) { opt = cp[IPOPT_OPTVAL]; if (opt == IPOPT_EOL) break; if (opt == IPOPT_NOP) optlen = 1; else { if (cnt < IPOPT_OLEN + sizeof(*cp)) { code = &cp[IPOPT_OLEN] - (u_char *)ip; goto bad; } optlen = cp[IPOPT_OLEN]; if (optlen < IPOPT_OLEN + sizeof(*cp) || optlen > cnt) { code = &cp[IPOPT_OLEN] - (u_char *)ip; goto bad; } } switch (opt) { default: break; /* * Source routing with record. Find interface with current * destination address. If none on this machine then drop if * strictly routed, or do nothing if loosely routed. Record * interface address and bring up next address component. If * strictly routed make sure next address is on directly * accessible net. */ case IPOPT_LSRR: case IPOPT_SSRR: #ifdef IPSTEALTH if (V_ipstealth && pass > 0) break; #endif if (optlen < IPOPT_OFFSET + sizeof(*cp)) { code = &cp[IPOPT_OLEN] - (u_char *)ip; goto bad; } if ((off = cp[IPOPT_OFFSET]) < IPOPT_MINOFF) { code = &cp[IPOPT_OFFSET] - (u_char *)ip; goto bad; } ipaddr.sin_addr = ip->ip_dst; if (ifa_ifwithaddr_check((struct sockaddr *)&ipaddr) == 0) { if (opt == IPOPT_SSRR) { type = ICMP_UNREACH; code = ICMP_UNREACH_SRCFAIL; goto bad; } if (!ip_dosourceroute) goto nosourcerouting; /* * Loose routing, and not at next destination * yet; nothing to do except forward. */ break; } off--; /* 0 origin */ if (off > optlen - (int)sizeof(struct in_addr)) { /* * End of source route. Should be for us. */ if (!ip_acceptsourceroute) goto nosourcerouting; save_rte(m, cp, ip->ip_src); break; } #ifdef IPSTEALTH if (V_ipstealth) goto dropit; #endif if (!ip_dosourceroute) { if (V_ipforwarding) { char buf[16]; /* aaa.bbb.ccc.ddd\0 */ /* * Acting as a router, so generate * ICMP */ nosourcerouting: strcpy(buf, inet_ntoa(ip->ip_dst)); log(LOG_WARNING, "attempted source route from %s to %s\n", inet_ntoa(ip->ip_src), buf); type = ICMP_UNREACH; code = ICMP_UNREACH_SRCFAIL; goto bad; } else { /* * Not acting as a router, so * silently drop. */ #ifdef IPSTEALTH dropit: #endif IPSTAT_INC(ips_cantforward); m_freem(m); return (1); } } /* * locate outgoing interface */ (void)memcpy(&ipaddr.sin_addr, cp + off, sizeof(ipaddr.sin_addr)); if (opt == IPOPT_SSRR) { #define INA struct in_ifaddr * #define SA struct sockaddr * if ((ia = (INA)ifa_ifwithdstaddr((SA)&ipaddr)) == NULL) ia = (INA)ifa_ifwithnet((SA)&ipaddr, 0); } else /* XXX MRT 0 for routing */ ia = ip_rtaddr(ipaddr.sin_addr, M_GETFIB(m)); if (ia == NULL) { type = ICMP_UNREACH; code = ICMP_UNREACH_SRCFAIL; goto bad; } ip->ip_dst = ipaddr.sin_addr; (void)memcpy(cp + off, &(IA_SIN(ia)->sin_addr), sizeof(struct in_addr)); ifa_free(&ia->ia_ifa); cp[IPOPT_OFFSET] += sizeof(struct in_addr); /* * Let ip_intr's mcast routing check handle mcast pkts */ forward = !IN_MULTICAST(ntohl(ip->ip_dst.s_addr)); break; case IPOPT_RR: #ifdef IPSTEALTH if (V_ipstealth && pass == 0) break; #endif if (optlen < IPOPT_OFFSET + sizeof(*cp)) { code = &cp[IPOPT_OFFSET] - (u_char *)ip; goto bad; } if ((off = cp[IPOPT_OFFSET]) < IPOPT_MINOFF) { code = &cp[IPOPT_OFFSET] - (u_char *)ip; goto bad; } /* * If no space remains, ignore. */ off--; /* 0 origin */ if (off > optlen - (int)sizeof(struct in_addr)) break; (void)memcpy(&ipaddr.sin_addr, &ip->ip_dst, sizeof(ipaddr.sin_addr)); /* * Locate outgoing interface; if we're the * destination, use the incoming interface (should be * same). */ if ((ia = (INA)ifa_ifwithaddr((SA)&ipaddr)) == NULL && (ia = ip_rtaddr(ipaddr.sin_addr, M_GETFIB(m))) == NULL) { type = ICMP_UNREACH; code = ICMP_UNREACH_HOST; goto bad; } (void)memcpy(cp + off, &(IA_SIN(ia)->sin_addr), sizeof(struct in_addr)); ifa_free(&ia->ia_ifa); cp[IPOPT_OFFSET] += sizeof(struct in_addr); break; case IPOPT_TS: #ifdef IPSTEALTH if (V_ipstealth && pass == 0) break; #endif code = cp - (u_char *)ip; if (optlen < 4 || optlen > 40) { code = &cp[IPOPT_OLEN] - (u_char *)ip; goto bad; } if ((off = cp[IPOPT_OFFSET]) < 5) { code = &cp[IPOPT_OLEN] - (u_char *)ip; goto bad; } if (off > optlen - (int)sizeof(int32_t)) { cp[IPOPT_OFFSET + 1] += (1 << 4); if ((cp[IPOPT_OFFSET + 1] & 0xf0) == 0) { code = &cp[IPOPT_OFFSET] - (u_char *)ip; goto bad; } break; } off--; /* 0 origin */ sin = (struct in_addr *)(cp + off); switch (cp[IPOPT_OFFSET + 1] & 0x0f) { case IPOPT_TS_TSONLY: break; case IPOPT_TS_TSANDADDR: if (off + sizeof(uint32_t) + sizeof(struct in_addr) > optlen) { code = &cp[IPOPT_OFFSET] - (u_char *)ip; goto bad; } ipaddr.sin_addr = dst; ia = (INA)ifaof_ifpforaddr((SA)&ipaddr, m->m_pkthdr.rcvif); if (ia == NULL) continue; (void)memcpy(sin, &IA_SIN(ia)->sin_addr, sizeof(struct in_addr)); ifa_free(&ia->ia_ifa); cp[IPOPT_OFFSET] += sizeof(struct in_addr); off += sizeof(struct in_addr); break; case IPOPT_TS_PRESPEC: if (off + sizeof(uint32_t) + sizeof(struct in_addr) > optlen) { code = &cp[IPOPT_OFFSET] - (u_char *)ip; goto bad; } (void)memcpy(&ipaddr.sin_addr, sin, sizeof(struct in_addr)); if (ifa_ifwithaddr_check((SA)&ipaddr) == 0) continue; cp[IPOPT_OFFSET] += sizeof(struct in_addr); off += sizeof(struct in_addr); break; default: code = &cp[IPOPT_OFFSET + 1] - (u_char *)ip; goto bad; } ntime = iptime(); (void)memcpy(cp + off, &ntime, sizeof(uint32_t)); cp[IPOPT_OFFSET] += sizeof(uint32_t); } } if (forward && V_ipforwarding) { ip_forward(m, 1); return (1); } return (0); bad: icmp_error(m, type, code, 0, 0); IPSTAT_INC(ips_badoptions); return (1); }
int in6_pcbbind( struct inpcb *inp, struct sockaddr *nam, struct proc *p) { struct socket *so = inp->inp_socket; struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)NULL; struct inpcbinfo *pcbinfo = inp->inp_pcbinfo; u_short lport = 0; int wild = 0, reuseport = (so->so_options & SO_REUSEPORT); if (!in6_ifaddrs) /* XXX broken! */ return (EADDRNOTAVAIL); if (inp->inp_lport || !IN6_IS_ADDR_UNSPECIFIED(&inp->in6p_laddr)) return(EINVAL); if ((so->so_options & (SO_REUSEADDR|SO_REUSEPORT)) == 0) wild = 1; socket_unlock(so, 0); /* keep reference */ lck_rw_lock_exclusive(pcbinfo->mtx); if (nam) { sin6 = (struct sockaddr_in6 *)nam; if (nam->sa_len != sizeof(*sin6)) { lck_rw_done(pcbinfo->mtx); socket_lock(so, 0); return(EINVAL); } /* * family check. */ if (nam->sa_family != AF_INET6) { lck_rw_done(pcbinfo->mtx); socket_lock(so, 0); return(EAFNOSUPPORT); } /* KAME hack: embed scopeid */ if (in6_embedscope(&sin6->sin6_addr, sin6, inp, NULL) != 0) { lck_rw_done(pcbinfo->mtx); socket_lock(so, 0); return EINVAL; } /* this must be cleared for ifa_ifwithaddr() */ sin6->sin6_scope_id = 0; lport = sin6->sin6_port; if (IN6_IS_ADDR_MULTICAST(&sin6->sin6_addr)) { /* * Treat SO_REUSEADDR as SO_REUSEPORT for multicast; * allow compepte duplication of binding if * SO_REUSEPORT is set, or if SO_REUSEADDR is set * and a multicast address is bound on both * new and duplicated sockets. */ if (so->so_options & SO_REUSEADDR) reuseport = SO_REUSEADDR|SO_REUSEPORT; } else if (!IN6_IS_ADDR_UNSPECIFIED(&sin6->sin6_addr)) { struct ifaddr *ia = NULL; sin6->sin6_port = 0; /* yech... */ if ((ia = ifa_ifwithaddr((struct sockaddr *)sin6)) == 0) { lck_rw_done(pcbinfo->mtx); socket_lock(so, 0); return(EADDRNOTAVAIL); } /* * XXX: bind to an anycast address might accidentally * cause sending a packet with anycast source address. * We should allow to bind to a deprecated address, since * the application dare to use it. */ if (ia && ((struct in6_ifaddr *)ia)->ia6_flags & (IN6_IFF_ANYCAST|IN6_IFF_NOTREADY|IN6_IFF_DETACHED)) { ifafree(ia); lck_rw_done(pcbinfo->mtx); socket_lock(so, 0); return(EADDRNOTAVAIL); } ifafree(ia); ia = NULL; } if (lport) { struct inpcb *t; /* GROSS */ if (ntohs(lport) < IPV6PORT_RESERVED && p && ((so->so_state & SS_PRIV) == 0)) { lck_rw_done(pcbinfo->mtx); socket_lock(so, 0); return(EACCES); } if (so->so_uid && !IN6_IS_ADDR_MULTICAST(&sin6->sin6_addr)) { t = in6_pcblookup_local_and_cleanup(pcbinfo, &sin6->sin6_addr, lport, INPLOOKUP_WILDCARD); if (t && (!IN6_IS_ADDR_UNSPECIFIED(&sin6->sin6_addr) || !IN6_IS_ADDR_UNSPECIFIED(&t->in6p_laddr) || (t->inp_socket->so_options & SO_REUSEPORT) == 0) && (so->so_uid != t->inp_socket->so_uid) && ((t->inp_socket->so_flags & SOF_REUSESHAREUID) == 0)) { lck_rw_done(pcbinfo->mtx); socket_lock(so, 0); return (EADDRINUSE); } if ((inp->inp_flags & IN6P_IPV6_V6ONLY) == 0 && IN6_IS_ADDR_UNSPECIFIED(&sin6->sin6_addr)) { struct sockaddr_in sin; in6_sin6_2_sin(&sin, sin6); t = in_pcblookup_local_and_cleanup(pcbinfo, sin.sin_addr, lport, INPLOOKUP_WILDCARD); if (t && (t->inp_socket->so_options & SO_REUSEPORT) == 0 && (so->so_uid != t->inp_socket->so_uid) && (ntohl(t->inp_laddr.s_addr) != INADDR_ANY || INP_SOCKAF(so) == INP_SOCKAF(t->inp_socket))) { lck_rw_done(pcbinfo->mtx); socket_lock(so, 0); return (EADDRINUSE); } } } t = in6_pcblookup_local_and_cleanup(pcbinfo, &sin6->sin6_addr, lport, wild); if (t && (reuseport & t->inp_socket->so_options) == 0) { lck_rw_done(pcbinfo->mtx); socket_lock(so, 0); return(EADDRINUSE); } if ((inp->inp_flags & IN6P_IPV6_V6ONLY) == 0 && IN6_IS_ADDR_UNSPECIFIED(&sin6->sin6_addr)) { struct sockaddr_in sin; in6_sin6_2_sin(&sin, sin6); t = in_pcblookup_local_and_cleanup(pcbinfo, sin.sin_addr, lport, wild); if (t && (reuseport & t->inp_socket->so_options) == 0 && (ntohl(t->inp_laddr.s_addr) != INADDR_ANY || INP_SOCKAF(so) == INP_SOCKAF(t->inp_socket))) { lck_rw_done(pcbinfo->mtx); socket_lock(so, 0); return (EADDRINUSE); } } } inp->in6p_laddr = sin6->sin6_addr; } socket_lock(so, 0); if (lport == 0) { int e; if ((e = in6_pcbsetport(&inp->in6p_laddr, inp, p, 1)) != 0) { lck_rw_done(pcbinfo->mtx); return(e); } } else { inp->inp_lport = lport; if (in_pcbinshash(inp, 1) != 0) { inp->in6p_laddr = in6addr_any; inp->inp_lport = 0; lck_rw_done(pcbinfo->mtx); return (EAGAIN); } } lck_rw_done(pcbinfo->mtx); sflt_notify(so, sock_evt_bound, NULL); return(0); }
int Lpx_PCB_bind( register struct lpxpcb *lpxp, struct sockaddr *nam, /* local address */ struct proc *td ) { register struct sockaddr_lpx *slpx = NULL; u_short lport = 0; struct lpx_addr laddr; DEBUG_CALL(4, ("Lpx_PCB_bind: Entered.\n")); if (lpxp->lpxp_lport || !lpx_nullhost(lpxp->lpxp_laddr)) { DEBUG_CALL(0,("!!!Lpx_PCB_bind: already have local port or address info\n")); return (EINVAL); } if (nam) { slpx = (struct sockaddr_lpx *)nam; if (!lpx_nullhost(slpx->sipx_addr)) { int tport = slpx->slpx_port; slpx->slpx_port = 0; /* yech... */ if (ifa_ifwithaddr((struct sockaddr *)slpx) == 0) { DEBUG_CALL(0,("!!Lpx_PCB_bind: Address not available\n")); return (EADDRNOTAVAIL); } slpx->slpx_port = tport; } lport = slpx->slpx_port; if (lport) { #if 0 u_short aport = ntohs(lport); int error; #endif /* if 0 */ #if 0 if (aport < LPXPORT_RESERVED && td != NULL && (error = suser(td->p_ucred, &td->p_acflag)) != 0) { return (error); } #endif /* if 0 */ #if 0 if (Lpx_PCB_lookup(&zerolpx_addr, lport, 0)) { DEBUG_CALL(0,("!!Lpx_PCB_bind: local port number in use\n")); return (EADDRINUSE); } #else /* if 0 */ DEBUG_CALL(4, ("Lpx_PCB_bind: 1.\n")); laddr.x_port = lport; if (Lpx_PCB_lookup(&slpx->sipx_addr, &laddr, 0)) { DEBUG_CALL(0,("!!Lpx_PCB_bind: local port number in use\n")); return (EADDRINUSE); } #endif /* if 0 else */ } lpxp->lpxp_laddr = slpx->sipx_addr; } /* if (nam) */ if (lport == 0) { DEBUG_CALL(4, ("Lpx_PCB_bind: 2.\n")); #if 1 do { lpxpcb.lpxp_lport++; if ((lpxpcb.lpxp_lport < LPXPORT_RESERVED) || (lpxpcb.lpxp_lport >= LPXPORT_WELLKNOWN)) lpxpcb.lpxp_lport = LPXPORT_RESERVED; lport = htons(lpxpcb.lpxp_lport); laddr.x_port = lport; } while (Lpx_PCB_lookup(&zerolpx_addr, &laddr, 0)); #else /* if 0 */ do { lpxpcb.lpxp_lport++; if ((lpxpcb.lpxp_lport < LPXPORT_RESERVED) || (lpxpcb.lpxp_lport >= LPXPORT_WELLKNOWN)) lpxpcb.lpxp_lport = LPXPORT_RESERVED; lport = htons(lpxpcb.lpxp_lport); } while (Lpx_PCB_lookup(&slpx->sipx_addr, lport, 0)); #endif /* if 0 else */ } /* if (lport == 0) */ lpxp->lpxp_lport = lport; #if 1 ((struct sockaddr_lpx *)nam)->slpx_port = lport; #endif /* if 1 */ return (0); }
/* * Connect from a socket to a specified address. * Both address and port must be specified in argument slpx. * If don't have a local address for this socket yet, * then pick one. */ int Lpx_PCB_connect( struct lpxpcb *lpxp, struct sockaddr *nam, struct proc *td ) { struct lpx_ifaddr *ia = NULL; register struct sockaddr_lpx *slpx = (struct sockaddr_lpx *)nam; register struct lpx_addr *dst; register struct route *ro; struct ifnet *ifp; struct lpx_addr laddr; DEBUG_CALL(4, ("Lpx_PCB_connect\n")); if (nam == NULL) { return(EINVAL); } DEBUG_CALL(2, ("slpx->sipx_addr.x_net = %x\n", slpx->sipx_addr.x_net)); if (slpx->slpx_family != AF_LPX) return (EAFNOSUPPORT); if (slpx->slpx_port == 0 || lpx_nullhost(slpx->sipx_addr)) return (EADDRNOTAVAIL); /* * If we haven't bound which network number to use as ours, * we will use the number of the outgoing interface. * This depends on having done a routing lookup, which * we will probably have to do anyway, so we might * as well do it now. On the other hand if we are * sending to multiple destinations we may have already * done the lookup, so see if we can use the route * from before. In any case, we only * chose a port number once, even if sending to multiple * destinations. */ ro = &lpxp->lpxp_route; dst = &satolpx_addr(ro->ro_dst); if (lpxp->lpxp_socket->so_options & SO_DONTROUTE) goto flush; if (!lpx_neteq(lpxp->lpxp_lastdst, slpx->sipx_addr)) goto flush; if (!lpx_hosteq(lpxp->lpxp_lastdst, slpx->sipx_addr)) { if (ro->ro_rt != NULL && !(ro->ro_rt->rt_flags & RTF_HOST)) { /* can patch route to avoid rtalloc */ *dst = slpx->sipx_addr; } else { flush: if (ro->ro_rt != NULL) RTFREE(ro->ro_rt); ro->ro_rt = NULL; } }/* else cached route is ok; do nothing */ lpxp->lpxp_lastdst = slpx->sipx_addr; if ((lpxp->lpxp_socket->so_options & SO_DONTROUTE) == 0 && /*XXX*/ (ro->ro_rt == NULL || ro->ro_rt->rt_ifp == NULL)) { /* No route yet, so try to acquire one */ ro->ro_dst.sa_family = AF_LPX; ro->ro_dst.sa_len = sizeof(ro->ro_dst); *dst = slpx->sipx_addr; dst->x_port = 0; rtalloc(ro); DEBUG_CALL(4, ("Lpx_PCB_connect RO 1\n")); } if (lpx_neteqnn(lpxp->lpxp_laddr.x_net, lpx_zeronet)) { DEBUG_CALL(4, ("Lpx_PCB_connect RO 2\n")); /* * If route is known or can be allocated now, * our src addr is taken from the i/f, else punt. */ /* * If we found a route, use the address * corresponding to the outgoing interface */ ia = NULL; if (ro->ro_rt != NULL && (ifp = ro->ro_rt->rt_ifp) != NULL) for (ia = lpx_ifaddr; ia != NULL; ia = ia->ia_next) if (ia->ia_ifp == ifp) break; if (ia == NULL) { // u_short fport = slpx->sipx_addr.x_port; //slpx->sipx_addr.x_port = 0; ia = (struct lpx_ifaddr *) ifa_ifwithaddr((struct sockaddr *)&lpxp->lpxp_laddr); //slpx->sipx_addr.x_port = fport; // BUG BUG BUG!!! if (ia == NULL) ia = Lpx_CTL_iaonnetof(&slpx->sipx_addr); if (ia == NULL) ia = lpx_ifaddr; if (ia == NULL) return (EADDRNOTAVAIL); } lpxp->lpxp_laddr.x_net = satolpx_addr(ia->ia_addr).x_net; } if (lpx_nullhost(lpxp->lpxp_laddr)) { DEBUG_CALL(4, ("Lpx_PCB_connect RO 3\n")); /* * If route is known or can be allocated now, * our src addr is taken from the i/f, else punt. */ /* * If we found a route, use the address * corresponding to the outgoing interface */ ia = NULL; if (ro->ro_rt != NULL && (ifp = ro->ro_rt->rt_ifp) != NULL) for (ia = lpx_ifaddr; ia != NULL; ia = ia->ia_next) if (ia->ia_ifp == ifp) break; if (ia == NULL) { u_short fport = slpx->sipx_addr.x_port; slpx->sipx_addr.x_port = 0; ia = (struct lpx_ifaddr *) ifa_ifwithdstaddr((struct sockaddr *)slpx); slpx->sipx_addr.x_port = fport; // BUG BUG BUG!!! if (ia == NULL) ia = Lpx_CTL_iaonnetof(&slpx->sipx_addr); if (ia == NULL) ia = lpx_ifaddr; if (ia == NULL) return (EADDRNOTAVAIL); } lpxp->lpxp_laddr.x_host = satolpx_addr(ia->ia_addr).x_host; } DEBUG_CALL(4, ("Lpx_PCB_connect: 4.\n")); laddr.x_port = lpxp->lpxp_lport; if (Lpx_PCB_lookup(&slpx->sipx_addr, &laddr, 0)) return (EADDRINUSE); if (lpxp->lpxp_lport == 0) Lpx_PCB_bind(lpxp, (struct sockaddr *)NULL, td); /* XXX just leave it zero if we can't find a route */ lpxp->lpxp_faddr = slpx->sipx_addr; /* Includes lpxp->lpxp_fport = slpx->slpx_port; */ return (0); }
/* * Bind address from sin6 to in6p. */ static int in6_pcbbind_addr(struct in6pcb *in6p, struct sockaddr_in6 *sin6, struct lwp *l) { int error; /* * We should check the family, but old programs * incorrectly fail to intialize it. */ if (sin6->sin6_family != AF_INET6) return (EAFNOSUPPORT); #ifndef INET if (IN6_IS_ADDR_V4MAPPED(&sin6->sin6_addr)) return (EADDRNOTAVAIL); #endif if ((error = sa6_embedscope(sin6, ip6_use_defzone)) != 0) return (error); if (IN6_IS_ADDR_V4MAPPED(&sin6->sin6_addr)) { if ((in6p->in6p_flags & IN6P_IPV6_V6ONLY) != 0) return (EINVAL); if (sin6->sin6_addr.s6_addr32[3]) { struct sockaddr_in sin; memset(&sin, 0, sizeof(sin)); sin.sin_len = sizeof(sin); sin.sin_family = AF_INET; bcopy(&sin6->sin6_addr.s6_addr32[3], &sin.sin_addr, sizeof(sin.sin_addr)); if (ifa_ifwithaddr((struct sockaddr *)&sin) == 0) return EADDRNOTAVAIL; } } else if (!IN6_IS_ADDR_UNSPECIFIED(&sin6->sin6_addr)) { struct ifaddr *ia = NULL; if ((in6p->in6p_flags & IN6P_FAITH) == 0 && (ia = ifa_ifwithaddr((struct sockaddr *)sin6)) == 0) return (EADDRNOTAVAIL); /* * bind to an anycast address might accidentally * cause sending a packet with an anycast source * address, so we forbid it. * * We should allow to bind to a deprecated address, * since the application dare to use it. * But, can we assume that they are careful enough * to check if the address is deprecated or not? * Maybe, as a safeguard, we should have a setsockopt * flag to control the bind(2) behavior against * deprecated addresses (default: forbid bind(2)). */ if (ia && ((struct in6_ifaddr *)ia)->ia6_flags & (IN6_IFF_ANYCAST|IN6_IFF_NOTREADY|IN6_IFF_DETACHED)) return (EADDRNOTAVAIL); } in6p->in6p_laddr = sin6->sin6_addr; return (0); }
int in6_pcbbind(void *v, struct mbuf *nam, struct lwp *l) { struct in6pcb *in6p = v; struct socket *so = in6p->in6p_socket; struct inpcbtable *table = in6p->in6p_table; struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)NULL; u_int16_t lport = 0; int wild = 0, reuseport = (so->so_options & SO_REUSEPORT); if (in6p->in6p_af != AF_INET6) return (EINVAL); if (in6p->in6p_lport || !IN6_IS_ADDR_UNSPECIFIED(&in6p->in6p_laddr)) return (EINVAL); if ((so->so_options & (SO_REUSEADDR|SO_REUSEPORT)) == 0 && ((so->so_proto->pr_flags & PR_CONNREQUIRED) == 0 || (so->so_options & SO_ACCEPTCONN) == 0)) wild = 1; if (nam) { int error; sin6 = mtod(nam, struct sockaddr_in6 *); if (nam->m_len != sizeof(*sin6)) return (EINVAL); /* * We should check the family, but old programs * incorrectly fail to intialize it. */ if (sin6->sin6_family != AF_INET6) return (EAFNOSUPPORT); #ifndef INET if (IN6_IS_ADDR_V4MAPPED(&sin6->sin6_addr)) return (EADDRNOTAVAIL); #endif if ((error = sa6_embedscope(sin6, ip6_use_defzone)) != 0) return (error); lport = sin6->sin6_port; if (IN6_IS_ADDR_MULTICAST(&sin6->sin6_addr)) { /* * Treat SO_REUSEADDR as SO_REUSEPORT for multicast; * allow compepte duplication of binding if * SO_REUSEPORT is set, or if SO_REUSEADDR is set * and a multicast address is bound on both * new and duplicated sockets. */ if (so->so_options & SO_REUSEADDR) reuseport = SO_REUSEADDR|SO_REUSEPORT; } else if (IN6_IS_ADDR_V4MAPPED(&sin6->sin6_addr)) { if ((in6p->in6p_flags & IN6P_IPV6_V6ONLY) != 0) return (EINVAL); if (sin6->sin6_addr.s6_addr32[3]) { struct sockaddr_in sin; bzero(&sin, sizeof(sin)); sin.sin_len = sizeof(sin); sin.sin_family = AF_INET; bcopy(&sin6->sin6_addr.s6_addr32[3], &sin.sin_addr, sizeof(sin.sin_addr)); if (ifa_ifwithaddr((struct sockaddr *)&sin) == 0) return EADDRNOTAVAIL; } } else if (!IN6_IS_ADDR_UNSPECIFIED(&sin6->sin6_addr)) { struct ifaddr *ia = NULL; sin6->sin6_port = 0; /* yech... */ if ((in6p->in6p_flags & IN6P_FAITH) == 0 && (ia = ifa_ifwithaddr((struct sockaddr *)sin6)) == 0) return (EADDRNOTAVAIL); /* * bind to an anycast address might accidentally * cause sending a packet with an anycast source * address, so we forbid it. * * We should allow to bind to a deprecated address, * since the application dare to use it. * But, can we assume that they are careful enough * to check if the address is deprecated or not? * Maybe, as a safeguard, we should have a setsockopt * flag to control the bind(2) behavior against * deprecated addresses (default: forbid bind(2)). */ if (ia && ((struct in6_ifaddr *)ia)->ia6_flags & (IN6_IFF_ANYCAST|IN6_IFF_NOTREADY|IN6_IFF_DETACHED)) return (EADDRNOTAVAIL); } if (lport) { #ifndef IPNOPRIVPORTS int priv; /* * NOTE: all operating systems use suser() for * privilege check! do not rewrite it into SS_PRIV. */ priv = (l && !kauth_authorize_generic(l->l_cred, KAUTH_GENERIC_ISSUSER, NULL)) ? 1 : 0; /* GROSS */ if (ntohs(lport) < IPV6PORT_RESERVED && !priv) return (EACCES); #endif if (IN6_IS_ADDR_V4MAPPED(&sin6->sin6_addr)) { #ifdef INET struct inpcb *t; t = in_pcblookup_port(table, *(struct in_addr *)&sin6->sin6_addr.s6_addr32[3], lport, wild); if (t && (reuseport & t->inp_socket->so_options) == 0) return (EADDRINUSE); #else return (EADDRNOTAVAIL); #endif } { struct in6pcb *t; t = in6_pcblookup_port(table, &sin6->sin6_addr, lport, wild); if (t && (reuseport & t->in6p_socket->so_options) == 0) return (EADDRINUSE); } } in6p->in6p_laddr = sin6->sin6_addr; } if (lport == 0) { int e; e = in6_pcbsetport(&in6p->in6p_laddr, in6p, l); if (e != 0) return (e); } else { in6p->in6p_lport = lport; in6_pcbstate(in6p, IN6P_BOUND); } LIST_REMOVE(&in6p->in6p_head, inph_lhash); LIST_INSERT_HEAD(IN6PCBHASH_PORT(table, in6p->in6p_lport), &in6p->in6p_head, inph_lhash); #if 0 in6p->in6p_flowinfo = 0; /* XXX */ #endif return (0); }
/* * Bind an address (or at least a port) to an PF_INET6 socket. */ int in6_pcbbind(struct inpcb *inp, struct mbuf *nam, struct proc *p) { struct socket *so = inp->inp_socket; struct inpcbtable *head = inp->inp_table; struct sockaddr_in6 *sin6; u_short lport = 0; int wild = INPLOOKUP_IPV6, reuseport = (so->so_options & SO_REUSEPORT); int error; /* * REMINDER: Once up to speed, flow label processing should go here, * too. (Same with in6_pcbconnect.) */ if (in6_ifaddr == 0) return EADDRNOTAVAIL; if (inp->inp_lport != 0 || !IN6_IS_ADDR_UNSPECIFIED(&inp->inp_laddr6)) return EINVAL; /* If already bound, EINVAL! */ if ((so->so_options & (SO_REUSEADDR | SO_REUSEPORT)) == 0 && ((so->so_proto->pr_flags & PR_CONNREQUIRED) == 0 || (so->so_options & SO_ACCEPTCONN) == 0)) wild |= INPLOOKUP_WILDCARD; /* * If I did get a sockaddr passed in... */ if (nam) { sin6 = mtod(nam, struct sockaddr_in6 *); if (nam->m_len != sizeof (*sin6)) return EINVAL; /* * Unlike v4, I have no qualms about EAFNOSUPPORT if the * wretched family is not filled in! */ if (sin6->sin6_family != AF_INET6) return EAFNOSUPPORT; /* KAME hack: embed scopeid */ if (in6_embedscope(&sin6->sin6_addr, sin6, inp, NULL) != 0) return EINVAL; /* this must be cleared for ifa_ifwithaddr() */ sin6->sin6_scope_id = 0; lport = sin6->sin6_port; /* reject IPv4 mapped address, we have no support for it */ if (IN6_IS_ADDR_V4MAPPED(&sin6->sin6_addr)) return EADDRNOTAVAIL; if (IN6_IS_ADDR_MULTICAST(&sin6->sin6_addr)) { /* * Treat SO_REUSEADDR as SO_REUSEPORT for multicast; * allow complete duplication of binding if * SO_REUSEPORT is set, or if SO_REUSEADDR is set * and a multicast address is bound on both * new and duplicated sockets. */ if (so->so_options & SO_REUSEADDR) reuseport = SO_REUSEADDR | SO_REUSEPORT; } else if (!IN6_IS_ADDR_UNSPECIFIED(&sin6->sin6_addr)) { struct ifaddr *ia = NULL; sin6->sin6_port = 0; /* * Yechhhh, because of upcoming * call to ifa_ifwithaddr(), which * does bcmp's over the PORTS as * well. (What about flow?) */ sin6->sin6_flowinfo = 0; if (!(so->so_options & SO_BINDANY) && ((ia = ifa_ifwithaddr((struct sockaddr *)sin6)) == NULL)) return EADDRNOTAVAIL; /* * bind to an anycast address might accidentally * cause sending a packet with an anycast source * address, so we forbid it. * * We should allow to bind to a deprecated address, * since the application dare to use it. * But, can we assume that they are careful enough * to check if the address is deprecated or not? * Maybe, as a safeguard, we should have a setsockopt * flag to control the bind(2) behavior against * deprecated addresses (default: forbid bind(2)). */ if (ia && ((struct in6_ifaddr *)ia)->ia6_flags & (IN6_IFF_ANYCAST|IN6_IFF_NOTREADY|IN6_IFF_DETACHED)) return (EADDRNOTAVAIL); } if (lport) { struct inpcb *t; /* * Question: Do we wish to continue the Berkeley * tradition of ports < IPPORT_RESERVED be only for * root? * Answer: For now yes, but IMHO, it should be REMOVED! * OUCH: One other thing, is there no better way of * finding a process for a socket instead of using * curproc? (Marked with BSD's {in,}famous XXX ? */ if (ntohs(lport) < IPPORT_RESERVED && (error = suser(p, 0))) return error; t = in_pcblookup(head, (struct in_addr *)&zeroin6_addr, 0, (struct in_addr *)&sin6->sin6_addr, lport, wild); if (t && (reuseport & t->inp_socket->so_options) == 0) return EADDRINUSE; } inp->inp_laddr6 = sin6->sin6_addr; } if (lport == 0) { error = in6_pcbsetport(&inp->inp_laddr6, inp, p); if (error != 0) return error; } else { inp->inp_lport = lport; in_pcbrehash(inp); } return 0; }
int in_pcbbind(struct inpcb *inp, struct mbuf *name) { struct sockaddr_in *sin; struct socket *so; struct inpcb *t; short *lastport; unsigned short lport; int wild, reuseport, i; /* Setup locals */ so = inp->inp_socket; lastport = &inp->inp_pcbinfo->lastport; reuseport = (so->so_options & SO_REUSEPORT); lport = 0; wild = 0; i = in_pcbhash(inp); if (in_ifaddr == NULL) return EADDRNOTAVAIL; /* If local port or any inet address */ if ( (inp->inp_lport != 0) || (inp->inp_laddr.s_addr != INADDR_ANY) ) return EINVAL; if ( ((so->so_options & (SO_REUSEADDR | SO_REUSEPORT)) == 0) && ( ((so->so_proto->pr_flags & PR_CONNREQUIRED) == 0) || ((so->so_options & SO_ACCEPTCONN) == 0) ) ) wild = INPLOOKUP_WILDCARD; /* If name non-null */ if (name != NULL) { /* Get name */ sin = mtod(name, struct sockaddr_in *); if ( name->m_len != sizeof(struct sockaddr_in) ) return EINVAL; #ifdef NET_DIAGNOSE /* Check family */ if (sin->sin_family != AF_INET) return EAFNOSUPPORT; #endif /* NET_DIAGNOSE */ /* Get port */ lport = sin->sin_port; /* If multicast address */ if ( IN_MULTICAST( ntohl(sin->sin_addr.s_addr) ) ) { if (so->so_options & SO_REUSEADDR) reuseport = SO_REUSEADDR | SO_REUSEPORT; } /* End if multicast address */ /* Else if not any address */ else if (sin->sin_addr.s_addr != INADDR_ANY) { sin->sin_port = 0; if (ifa_ifwithaddr((struct sockaddr *) sin) == NULL) return EADDRNOTAVAIL; } /* End else if not any address */ /* If local port */ if (lport) { /* Lookup address */ t = in_pcblookup(inp->inp_pcbinfo, zeroin_addr, 0, sin->sin_addr, lport, wild); if ( (t != NULL) && ((reuseport & t->inp_socket->so_options) == 0) ) return EADDRINUSE; } /* End if local port */ /* Store local address */ inp->inp_laddr = sin->sin_addr; } /* End if name non-null */
int ifRouteDelete ( char *ifName, /* name of the interface */ int unit /* unit number for this interface */ ) { FAST struct ifnet *ifp; FAST struct ifaddr *ifa; FAST struct in_ifaddr *ia = 0; int deleted; struct sockaddr_in inetAddr; if (ifName == NULL) return (ERROR); deleted = 0; #ifdef VIRTUAL_STACK for (ifp = _ifnet; ifp; ifp = ifp->if_next) #else for (ifp = ifnet; ifp; ifp = ifp->if_next) #endif /* VIRTUAL_STACK */ { if (ifp->if_unit != unit || strcmp(ifName, ifp->if_name)) continue; for (ifa = ifp->if_addrlist; ifa; ifa = ifa->ifa_next) { /* skip if address family does not belong to AF_INET */ if (ifa->ifa_addr->sa_family != AF_INET) continue; /* * Every element in the if_addrlist (type struct ifaddr) with * family AF_INET is actually the first part of a larger * in_ifaddr structure which includes the subnet mask. Access * that structure to use the mask value. */ ia = (struct in_ifaddr *) ifa; /* * The following is a sanity test. */ #ifdef ROUTER_STACK /* UNNUMBERED_SUPPORT */ /* * This test is to prevent an ipDetach on an unnumbered interface * from removing the routes generated by the real interface if that * interface is still up. See ifUnnumberedSet for the definition * of "real interface." */ if (ifa == ifa_ifwithaddr (ifa->ifa_addr)) #endif /* ROUTER_STACK */ { if (mRouteEntryDelete (((struct sockaddr_in*)ifa->ifa_addr)-> sin_addr.s_addr, 0, 0xffffffff, 0, RTF_HOST, M2_ipRouteProto_local) == OK) ++deleted; inetAddr.sin_addr.s_addr = htonl (ia->ia_subnetmask) & ((struct sockaddr_in *)ifa->ifa_addr)->sin_addr.s_addr; if (mRouteEntryDelete (((struct sockaddr_in*)ifa->ifa_addr)-> sin_addr.s_addr, 0, htonl(ia->ia_subnetmask), 0, 0, M2_ipRouteProto_local) == OK) ++deleted; } if (ifp->if_flags & (IFF_POINTOPOINT|IFF_UNNUMBERED)) { if (mRouteEntryDelete (((struct sockaddr_in*)ifa->ifa_dstaddr)-> sin_addr.s_addr, ((struct sockaddr_in*)ifa->ifa_addr)-> sin_addr.s_addr, 0x0, 0, RTF_HOST, M2_ipRouteProto_local) == OK) ++deleted; #ifdef ROUTER_STACK /* UNNUMBERED_SUPPORT */ else { _arpCmd (SIOCDARP, &(((struct sockaddr_in *) ifa->ifa_dstaddr)->sin_addr), NULL, NULL); deleted++; } #endif /* ROUTER_STACK */ } } } return (deleted); }
/* * Ethernet output routine. * Encapsulate a packet of type family for the local net. * Assumes that ifp is actually pointer to arpcom structure. */ int ether_output(struct ifnet *ifp0, struct mbuf *m0, struct sockaddr *dst, struct rtentry *rt0) { u_int16_t etype; int s, len, error = 0, hdrcmplt = 0; u_char edst[ETHER_ADDR_LEN], esrc[ETHER_ADDR_LEN]; struct mbuf *m = m0; struct rtentry *rt; struct mbuf *mcopy = (struct mbuf *)0; struct ether_header *eh; struct arpcom *ac = (struct arpcom *)ifp0; short mflags; struct ifnet *ifp = ifp0; #ifdef DIAGNOSTIC if (ifp->if_rdomain != rtable_l2(m->m_pkthdr.rdomain)) { printf("%s: trying to send packet on wrong domain. " "if %d vs. mbuf %d, AF %d\n", ifp->if_xname, ifp->if_rdomain, rtable_l2(m->m_pkthdr.rdomain), dst->sa_family); } #endif #if NTRUNK > 0 /* restrict transmission on trunk members to bpf only */ if (ifp->if_type == IFT_IEEE8023ADLAG && (m_tag_find(m, PACKET_TAG_DLT, NULL) == NULL)) senderr(EBUSY); #endif #if NCARP > 0 if (ifp->if_type == IFT_CARP) { struct ifaddr *ifa; /* loop back if this is going to the carp interface */ if (dst != NULL && LINK_STATE_IS_UP(ifp0->if_link_state) && (ifa = ifa_ifwithaddr(dst, ifp->if_rdomain)) != NULL && ifa->ifa_ifp == ifp0) return (looutput(ifp0, m, dst, rt0)); ifp = ifp->if_carpdev; ac = (struct arpcom *)ifp; if ((ifp0->if_flags & (IFF_UP|IFF_RUNNING)) != (IFF_UP|IFF_RUNNING)) senderr(ENETDOWN); } #endif /* NCARP > 0 */ if ((ifp->if_flags & (IFF_UP|IFF_RUNNING)) != (IFF_UP|IFF_RUNNING)) senderr(ENETDOWN); if ((rt = rt0) != NULL) { if ((rt->rt_flags & RTF_UP) == 0) { if ((rt0 = rt = rtalloc1(dst, RT_REPORT, m->m_pkthdr.rdomain)) != NULL) rt->rt_refcnt--; else senderr(EHOSTUNREACH); } if (rt->rt_flags & RTF_GATEWAY) { if (rt->rt_gwroute == 0) goto lookup; if (((rt = rt->rt_gwroute)->rt_flags & RTF_UP) == 0) { rtfree(rt); rt = rt0; lookup: rt->rt_gwroute = rtalloc1(rt->rt_gateway, RT_REPORT, ifp->if_rdomain); if ((rt = rt->rt_gwroute) == NULL) senderr(EHOSTUNREACH); } } if (rt->rt_flags & RTF_REJECT) if (rt->rt_rmx.rmx_expire == 0 || time_second < rt->rt_rmx.rmx_expire) senderr(rt == rt0 ? EHOSTDOWN : EHOSTUNREACH); } switch (dst->sa_family) { #ifdef INET case AF_INET: if (!arpresolve(ac, rt, m, dst, edst)) return (0); /* if not yet resolved */ /* If broadcasting on a simplex interface, loopback a copy */ if ((m->m_flags & M_BCAST) && (ifp->if_flags & IFF_SIMPLEX) && !m->m_pkthdr.pf.routed) mcopy = m_copy(m, 0, (int)M_COPYALL); etype = htons(ETHERTYPE_IP); break; #endif #ifdef INET6 case AF_INET6: if (!nd6_storelladdr(ifp, rt, m, dst, (u_char *)edst)) return (0); /* it must be impossible, but... */ etype = htons(ETHERTYPE_IPV6); break; #endif #ifdef MPLS case AF_MPLS: if (rt) dst = rt_key(rt); else senderr(EHOSTUNREACH); if (!ISSET(ifp->if_xflags, IFXF_MPLS)) senderr(ENETUNREACH); switch (dst->sa_family) { case AF_LINK: if (((struct sockaddr_dl *)dst)->sdl_alen < sizeof(edst)) senderr(EHOSTUNREACH); bcopy(LLADDR(((struct sockaddr_dl *)dst)), edst, sizeof(edst)); break; case AF_INET: if (!arpresolve(ac, rt, m, dst, edst)) return (0); /* if not yet resolved */ break; default: senderr(EHOSTUNREACH); } /* XXX handling for simplex devices in case of M/BCAST ?? */ if (m->m_flags & (M_BCAST | M_MCAST)) etype = htons(ETHERTYPE_MPLS_MCAST); else etype = htons(ETHERTYPE_MPLS); break; #endif /* MPLS */ case pseudo_AF_HDRCMPLT: hdrcmplt = 1; eh = (struct ether_header *)dst->sa_data; bcopy((caddr_t)eh->ether_shost, (caddr_t)esrc, sizeof(esrc)); /* FALLTHROUGH */ case AF_UNSPEC: eh = (struct ether_header *)dst->sa_data; bcopy((caddr_t)eh->ether_dhost, (caddr_t)edst, sizeof(edst)); /* AF_UNSPEC doesn't swap the byte order of the ether_type. */ etype = eh->ether_type; break; default: printf("%s: can't handle af%d\n", ifp->if_xname, dst->sa_family); senderr(EAFNOSUPPORT); } /* XXX Should we feed-back an unencrypted IPsec packet ? */ if (mcopy) (void) looutput(ifp, mcopy, dst, rt); /* * Add local net header. If no space in first mbuf, * allocate another. */ M_PREPEND(m, ETHER_HDR_LEN, M_DONTWAIT); if (m == 0) senderr(ENOBUFS); eh = mtod(m, struct ether_header *); eh->ether_type = etype; memcpy(eh->ether_dhost, edst, sizeof(edst)); if (hdrcmplt) memcpy(eh->ether_shost, esrc, sizeof(eh->ether_shost)); else memcpy(eh->ether_shost, ac->ac_enaddr, sizeof(eh->ether_shost)); #if NCARP > 0 if (ifp0 != ifp && ifp0->if_type == IFT_CARP) carp_rewrite_lladdr(ifp0, eh->ether_shost); #endif #if NBRIDGE > 0 /* * Interfaces that are bridgeports need special handling for output. */ if (ifp->if_bridgeport) { struct m_tag *mtag; /* * Check if this packet has already been sent out through * this bridgeport, in which case we simply send it out * without further bridge processing. */ for (mtag = m_tag_find(m, PACKET_TAG_BRIDGE, NULL); mtag; mtag = m_tag_find(m, PACKET_TAG_BRIDGE, mtag)) { #ifdef DEBUG /* Check that the information is there */ if (mtag->m_tag_len != sizeof(caddr_t)) { error = EINVAL; goto bad; } #endif if (!bcmp(&ifp->if_bridgeport, mtag + 1, sizeof(caddr_t))) break; } if (mtag == NULL) { /* Attach a tag so we can detect loops */ mtag = m_tag_get(PACKET_TAG_BRIDGE, sizeof(caddr_t), M_NOWAIT); if (mtag == NULL) { error = ENOBUFS; goto bad; } bcopy(&ifp->if_bridgeport, mtag + 1, sizeof(caddr_t)); m_tag_prepend(m, mtag); error = bridge_output(ifp, m, NULL, NULL); return (error); } } #endif mflags = m->m_flags; len = m->m_pkthdr.len; s = splnet(); /* * Queue message on interface, and start output if interface * not yet active. */ IFQ_ENQUEUE(&ifp->if_snd, m, NULL, error); if (error) { /* mbuf is already freed */ splx(s); return (error); } ifp->if_obytes += len; #if NCARP > 0 if (ifp != ifp0) ifp0->if_obytes += len; #endif /* NCARP > 0 */ if (mflags & M_MCAST) ifp->if_omcasts++; if_start(ifp); splx(s); return (error); bad: if (m) m_freem(m); return (error); }
/* * Return an IPv6 address, which is the most appropriate for a given * destination and user specified options. * If necessary, this function lookups the routing table and returns * an entry to the caller for later use. */ int in6_selectsrc(struct in6_addr **in6src, struct sockaddr_in6 *dstsock, struct ip6_pktopts *opts, struct ip6_moptions *mopts, struct route_in6 *ro, struct in6_addr *laddr, u_int rtableid) { struct ifnet *ifp = NULL; struct in6_addr *dst; struct in6_ifaddr *ia6 = NULL; struct in6_pktinfo *pi = NULL; int error; dst = &dstsock->sin6_addr; /* * If the source address is explicitly specified by the caller, * check if the requested source address is indeed a unicast address * assigned to the node, and can be used as the packet's source * address. If everything is okay, use the address as source. */ if (opts && (pi = opts->ip6po_pktinfo) && !IN6_IS_ADDR_UNSPECIFIED(&pi->ipi6_addr)) { struct sockaddr_in6 sa6; /* get the outgoing interface */ error = in6_selectif(dstsock, opts, mopts, ro, &ifp, rtableid); if (error) return (error); bzero(&sa6, sizeof(sa6)); sa6.sin6_family = AF_INET6; sa6.sin6_len = sizeof(sa6); sa6.sin6_addr = pi->ipi6_addr; if (ifp && IN6_IS_SCOPE_EMBED(&sa6.sin6_addr)) sa6.sin6_addr.s6_addr16[1] = htons(ifp->if_index); if_put(ifp); /* put reference from in6_selectif */ ia6 = ifatoia6(ifa_ifwithaddr(sin6tosa(&sa6), rtableid)); if (ia6 == NULL || (ia6->ia6_flags & (IN6_IFF_ANYCAST | IN6_IFF_NOTREADY))) return (EADDRNOTAVAIL); pi->ipi6_addr = sa6.sin6_addr; /* XXX: this overrides pi */ *in6src = &pi->ipi6_addr; return (0); } /* * If the source address is not specified but the socket(if any) * is already bound, use the bound address. */ if (laddr && !IN6_IS_ADDR_UNSPECIFIED(laddr)) { *in6src = laddr; return (0); } /* * If the caller doesn't specify the source address but * the outgoing interface, use an address associated with * the interface. */ if (pi && pi->ipi6_ifindex) { ifp = if_get(pi->ipi6_ifindex); if (ifp == NULL) return (ENXIO); /* XXX: better error? */ ia6 = in6_ifawithscope(ifp, dst, rtableid); if_put(ifp); if (ia6 == NULL) return (EADDRNOTAVAIL); *in6src = &ia6->ia_addr.sin6_addr; return (0); } /* * If the destination address is a link-local unicast address or * a link/interface-local multicast address, and if the outgoing * interface is specified by the sin6_scope_id filed, use an address * associated with the interface. * XXX: We're now trying to define more specific semantics of * sin6_scope_id field, so this part will be rewritten in * the near future. */ if ((IN6_IS_ADDR_LINKLOCAL(dst) || IN6_IS_ADDR_MC_LINKLOCAL(dst) || IN6_IS_ADDR_MC_INTFACELOCAL(dst)) && dstsock->sin6_scope_id) { ifp = if_get(dstsock->sin6_scope_id); if (ifp == NULL) return (ENXIO); /* XXX: better error? */ ia6 = in6_ifawithscope(ifp, dst, rtableid); if_put(ifp); if (ia6 == NULL) return (EADDRNOTAVAIL); *in6src = &ia6->ia_addr.sin6_addr; return (0); } /* * If the destination address is a multicast address and * the outgoing interface for the address is specified * by the caller, use an address associated with the interface. * Even if the outgoing interface is not specified, we also * choose a loopback interface as the outgoing interface. */ if (IN6_IS_ADDR_MULTICAST(dst)) { ifp = mopts ? if_get(mopts->im6o_ifidx) : NULL; if (!ifp && dstsock->sin6_scope_id) ifp = if_get(htons(dstsock->sin6_scope_id)); if (ifp) { ia6 = in6_ifawithscope(ifp, dst, rtableid); if_put(ifp); if (ia6 == NULL) return (EADDRNOTAVAIL); *in6src = &ia6->ia_addr.sin6_addr; return (0); } } /* * If route is known or can be allocated now, * our src addr is taken from the i/f, else punt. */ if (ro) { if (!rtisvalid(ro->ro_rt) || (ro->ro_tableid != rtableid) || !IN6_ARE_ADDR_EQUAL(&ro->ro_dst.sin6_addr, dst)) { rtfree(ro->ro_rt); ro->ro_rt = NULL; } if (ro->ro_rt == NULL) { struct sockaddr_in6 *sa6; /* No route yet, so try to acquire one */ bzero(&ro->ro_dst, sizeof(struct sockaddr_in6)); ro->ro_tableid = rtableid; sa6 = &ro->ro_dst; sa6->sin6_family = AF_INET6; sa6->sin6_len = sizeof(struct sockaddr_in6); sa6->sin6_addr = *dst; sa6->sin6_scope_id = dstsock->sin6_scope_id; ro->ro_rt = rtalloc(sin6tosa(&ro->ro_dst), RT_RESOLVE, ro->ro_tableid); } /* * in_pcbconnect() checks out IFF_LOOPBACK to skip using * the address. But we don't know why it does so. * It is necessary to ensure the scope even for lo0 * so doesn't check out IFF_LOOPBACK. */ if (ro->ro_rt) { ifp = if_get(ro->ro_rt->rt_ifidx); if (ifp != NULL) { ia6 = in6_ifawithscope(ifp, dst, rtableid); if_put(ifp); } if (ia6 == NULL) /* xxx scope error ?*/ ia6 = ifatoia6(ro->ro_rt->rt_ifa); } if (ia6 == NULL) return (EHOSTUNREACH); /* no route */ *in6src = &ia6->ia_addr.sin6_addr; return (0); } return (EADDRNOTAVAIL); }
struct in6_addr * in6_selectsrc(struct sockaddr_in6 *dstsock, struct ip6_pktopts *opts, struct inpcb *inp, struct route_in6 *ro, struct ifnet **ifpp, struct in6_addr *src_storage, unsigned int ifscope, int *errorp) { struct in6_addr dst; struct ifnet *ifp = NULL; struct in6_ifaddr *ia = NULL, *ia_best = NULL; struct in6_pktinfo *pi = NULL; int dst_scope = -1, best_scope = -1, best_matchlen = -1; struct in6_addrpolicy *dst_policy = NULL, *best_policy = NULL; u_int32_t odstzone; int prefer_tempaddr; struct ip6_moptions *mopts; struct timeval timenow; unsigned int nocell; boolean_t islocal = FALSE; getmicrotime(&timenow); dst = dstsock->sin6_addr; /* make a copy for local operation */ *errorp = 0; if (ifpp != NULL) *ifpp = NULL; if (inp != NULL) { mopts = inp->in6p_moptions; nocell = (inp->inp_flags & INP_NO_IFT_CELLULAR) ? 1 : 0; } else { mopts = NULL; nocell = 0; } /* * If the source address is explicitly specified by the caller, * check if the requested source address is indeed a unicast address * assigned to the node, and can be used as the packet's source * address. If everything is okay, use the address as source. */ if (opts && (pi = opts->ip6po_pktinfo) && !IN6_IS_ADDR_UNSPECIFIED(&pi->ipi6_addr)) { struct sockaddr_in6 srcsock; struct in6_ifaddr *ia6; /* get the outgoing interface */ if ((*errorp = in6_selectif(dstsock, opts, mopts, ro, ifscope, nocell, &ifp)) != 0) { return (NULL); } /* * determine the appropriate zone id of the source based on * the zone of the destination and the outgoing interface. * If the specified address is ambiguous wrt the scope zone, * the interface must be specified; otherwise, ifa_ifwithaddr() * will fail matching the address. */ bzero(&srcsock, sizeof(srcsock)); srcsock.sin6_family = AF_INET6; srcsock.sin6_len = sizeof(srcsock); srcsock.sin6_addr = pi->ipi6_addr; if (ifp) { *errorp = in6_setscope(&srcsock.sin6_addr, ifp, NULL); if (*errorp != 0) { ifnet_release(ifp); return (NULL); } } ia6 = (struct in6_ifaddr *)ifa_ifwithaddr((struct sockaddr *)(&srcsock)); if (ia6 == NULL) { *errorp = EADDRNOTAVAIL; if (ifp != NULL) ifnet_release(ifp); return (NULL); } IFA_LOCK_SPIN(&ia6->ia_ifa); if ((ia6->ia6_flags & (IN6_IFF_ANYCAST | IN6_IFF_NOTREADY)) || (nocell && (ia6->ia_ifa.ifa_ifp->if_type == IFT_CELLULAR))) { IFA_UNLOCK(&ia6->ia_ifa); IFA_REMREF(&ia6->ia_ifa); *errorp = EADDRNOTAVAIL; if (ifp != NULL) ifnet_release(ifp); return (NULL); } *src_storage = satosin6(&ia6->ia_addr)->sin6_addr; IFA_UNLOCK(&ia6->ia_ifa); IFA_REMREF(&ia6->ia_ifa); if (ifpp != NULL) { /* if ifp is non-NULL, refcnt held in in6_selectif() */ *ifpp = ifp; } else if (ifp != NULL) { ifnet_release(ifp); } return (src_storage); } /* * Otherwise, if the socket has already bound the source, just use it. */ if (inp != NULL && !IN6_IS_ADDR_UNSPECIFIED(&inp->in6p_laddr)) return (&inp->in6p_laddr); /* * If the address is not specified, choose the best one based on * the outgoing interface and the destination address. */ /* get the outgoing interface */ if ((*errorp = in6_selectif(dstsock, opts, mopts, ro, ifscope, nocell, &ifp)) != 0) return (NULL); #ifdef DIAGNOSTIC if (ifp == NULL) /* this should not happen */ panic("in6_selectsrc: NULL ifp"); #endif *errorp = in6_setscope(&dst, ifp, &odstzone); if (*errorp != 0) { if (ifp != NULL) ifnet_release(ifp); return (NULL); } lck_rw_lock_shared(&in6_ifaddr_rwlock); for (ia = in6_ifaddrs; ia; ia = ia->ia_next) { int new_scope = -1, new_matchlen = -1; struct in6_addrpolicy *new_policy = NULL; u_int32_t srczone, osrczone, dstzone; struct in6_addr src; struct ifnet *ifp1 = ia->ia_ifp; IFA_LOCK(&ia->ia_ifa); /* * We'll never take an address that breaks the scope zone * of the destination. We also skip an address if its zone * does not contain the outgoing interface. * XXX: we should probably use sin6_scope_id here. */ if (in6_setscope(&dst, ifp1, &dstzone) || odstzone != dstzone) goto next; src = ia->ia_addr.sin6_addr; if (in6_setscope(&src, ifp, &osrczone) || in6_setscope(&src, ifp1, &srczone) || osrczone != srczone) goto next; /* avoid unusable addresses */ if ((ia->ia6_flags & (IN6_IFF_NOTREADY | IN6_IFF_ANYCAST | IN6_IFF_DETACHED))) goto next; if (!ip6_use_deprecated && IFA6_IS_DEPRECATED(ia)) goto next; /* Rule 1: Prefer same address */ if (IN6_ARE_ADDR_EQUAL(&dst, &ia->ia_addr.sin6_addr)) BREAK(1); /* there should be no better candidate */ if (ia_best == NULL) REPLACE(0); /* Rule 2: Prefer appropriate scope */ if (dst_scope < 0) dst_scope = in6_addrscope(&dst); new_scope = in6_addrscope(&ia->ia_addr.sin6_addr); if (IN6_ARE_SCOPE_CMP(best_scope, new_scope) < 0) { if (IN6_ARE_SCOPE_CMP(best_scope, dst_scope) < 0) REPLACE(2); NEXTSRC(2); } else if (IN6_ARE_SCOPE_CMP(new_scope, best_scope) < 0) { if (IN6_ARE_SCOPE_CMP(new_scope, dst_scope) < 0) NEXTSRC(2); REPLACE(2); } /* * Rule 3: Avoid deprecated addresses. Note that the case of * !ip6_use_deprecated is already rejected above. */ if (!IFA6_IS_DEPRECATED(ia_best) && IFA6_IS_DEPRECATED(ia)) NEXTSRC(3); if (IFA6_IS_DEPRECATED(ia_best) && !IFA6_IS_DEPRECATED(ia)) REPLACE(3); /* Rule 4: Prefer home addresses */ /* * XXX: This is a TODO. We should probably merge the MIP6 * case above. */ /* Rule 5: Prefer outgoing interface */ if (ia_best->ia_ifp == ifp && ia->ia_ifp != ifp) NEXTSRC(5); if (ia_best->ia_ifp != ifp && ia->ia_ifp == ifp) REPLACE(5); /* * Rule 6: Prefer matching label * Note that best_policy should be non-NULL here. */ if (dst_policy == NULL) dst_policy = in6_addrsel_lookup_policy(dstsock); if (dst_policy->label != ADDR_LABEL_NOTAPP) { new_policy = in6_addrsel_lookup_policy(&ia->ia_addr); if (dst_policy->label == best_policy->label && dst_policy->label != new_policy->label) NEXTSRC(6); if (dst_policy->label != best_policy->label && dst_policy->label == new_policy->label) REPLACE(6); } /* * Rule 7: Prefer public addresses. * We allow users to reverse the logic by configuring * a sysctl variable, so that privacy conscious users can * always prefer temporary addresses. * Don't use temporary addresses for local destinations or * for multicast addresses unless we were passed in an option. */ if (IN6_IS_ADDR_MULTICAST(&dst) || in6_matchlen(&ia_best->ia_addr.sin6_addr, &dst) >= in6_mask2len(&ia_best->ia_prefixmask.sin6_addr, NULL)) islocal = TRUE; if (opts == NULL || opts->ip6po_prefer_tempaddr == IP6PO_TEMPADDR_SYSTEM) { prefer_tempaddr = islocal ? 0 : ip6_prefer_tempaddr; } else if (opts->ip6po_prefer_tempaddr == IP6PO_TEMPADDR_NOTPREFER) { prefer_tempaddr = 0; } else prefer_tempaddr = 1; if (!(ia_best->ia6_flags & IN6_IFF_TEMPORARY) && (ia->ia6_flags & IN6_IFF_TEMPORARY)) { if (prefer_tempaddr) REPLACE(7); else NEXTSRC(7); } if ((ia_best->ia6_flags & IN6_IFF_TEMPORARY) && !(ia->ia6_flags & IN6_IFF_TEMPORARY)) { if (prefer_tempaddr) NEXTSRC(7); else REPLACE(7); } /* * Rule 8: prefer addresses on alive interfaces. * This is a KAME specific rule. */ if ((ia_best->ia_ifp->if_flags & IFF_UP) && !(ia->ia_ifp->if_flags & IFF_UP)) NEXTSRC(8); if (!(ia_best->ia_ifp->if_flags & IFF_UP) && (ia->ia_ifp->if_flags & IFF_UP)) REPLACE(8); /* * Rule 14: Use longest matching prefix. * Note: in the address selection draft, this rule is * documented as "Rule 8". However, since it is also * documented that this rule can be overridden, we assign * a large number so that it is easy to assign smaller numbers * to more preferred rules. */ new_matchlen = in6_matchlen(&ia->ia_addr.sin6_addr, &dst); if (best_matchlen < new_matchlen) REPLACE(14); if (new_matchlen < best_matchlen) NEXTSRC(14); /* Rule 15 is reserved. */ /* * Last resort: just keep the current candidate. * Or, do we need more rules? */ IFA_UNLOCK(&ia->ia_ifa); continue; replace: best_scope = (new_scope >= 0 ? new_scope : in6_addrscope(&ia->ia_addr.sin6_addr)); best_policy = (new_policy ? new_policy : in6_addrsel_lookup_policy(&ia->ia_addr)); best_matchlen = (new_matchlen >= 0 ? new_matchlen : in6_matchlen(&ia->ia_addr.sin6_addr, &dst)); IFA_ADDREF_LOCKED(&ia->ia_ifa); /* for ia_best */ IFA_UNLOCK(&ia->ia_ifa); if (ia_best != NULL) IFA_REMREF(&ia_best->ia_ifa); ia_best = ia; continue; next: IFA_UNLOCK(&ia->ia_ifa); continue; out: IFA_ADDREF_LOCKED(&ia->ia_ifa); /* for ia_best */ IFA_UNLOCK(&ia->ia_ifa); if (ia_best != NULL) IFA_REMREF(&ia_best->ia_ifa); ia_best = ia; break; } lck_rw_done(&in6_ifaddr_rwlock); if (nocell && ia_best != NULL && (ia_best->ia_ifa.ifa_ifp->if_type == IFT_CELLULAR)) { IFA_REMREF(&ia_best->ia_ifa); ia_best = NULL; } if ( (ia = ia_best) == NULL) { *errorp = EADDRNOTAVAIL; if (ifp != NULL) ifnet_release(ifp); return (NULL); } IFA_LOCK_SPIN(&ia->ia_ifa); *src_storage = satosin6(&ia->ia_addr)->sin6_addr; IFA_UNLOCK(&ia->ia_ifa); IFA_REMREF(&ia->ia_ifa); if (ifpp != NULL) { /* if ifp is non-NULL, refcnt held in in6_selectif() */ *ifpp = ifp; } else if (ifp != NULL) { ifnet_release(ifp); } return (src_storage); }
struct in6_addr * in6_selectsrc(struct sockaddr_in6 *dstsock, struct ip6_pktopts *opts, struct ip6_moptions *mopts, struct route *ro, struct ifnet **ifpp, struct in6_addr *laddr, int *errorp) { struct in6_addr *dst; struct ifnet *ifp = NULL; struct in6_ifaddr *ia = NULL, *ia_best = NULL; struct in6_pktinfo *pi = NULL; int dst_scope = -1, best_scope = -1, best_matchlen = -1; struct in6_addrpolicy *dst_policy = NULL, *best_policy = NULL; #ifdef MIP6 struct hif_softc *sc; #ifdef MIP6_ALLOW_COA_FALLBACK struct mip6_bu *mbu_dst; u_int8_t coafallback = 0; #endif #endif dst = &dstsock->sin6_addr; *errorp = 0; if (ifpp) *ifpp = NULL; /* * If the source address is explicitly specified by the caller, * check if the requested source address is indeed a unicast address * assigned to the node, and can be used as the packet's source * address. If everything is okay, use the address as source. */ if (opts && (pi = opts->ip6po_pktinfo) && !IN6_IS_ADDR_UNSPECIFIED(&pi->ipi6_addr)) { struct sockaddr_in6 srcsock; struct in6_ifaddr *ia6; /* get the outgoing interface */ if ((*errorp = in6_selectif(dstsock, opts, mopts, ro, &ifp)) != 0) { return(NULL); } /* * determine the appropriate zone id of the source based on * the zone of the destination and the outgoing interface. */ bzero(&srcsock, sizeof(srcsock)); srcsock.sin6_family = AF_INET6; srcsock.sin6_len = sizeof(srcsock); srcsock.sin6_addr = pi->ipi6_addr; if (ifp) { int64_t zone; zone = in6_addr2zoneid(ifp, &pi->ipi6_addr); if (zone < 0) { /* XXX: this should not happen */ *errorp = EINVAL; return(NULL); } srcsock.sin6_scope_id = zone; } if ((*errorp = in6_embedscope(&srcsock.sin6_addr, &srcsock)) != 0) { return(NULL); } #ifndef SCOPEDROUTING srcsock.sin6_scope_id = 0; /* XXX: ifa_ifwithaddr expects 0 */ #endif ia6 = (struct in6_ifaddr *)ifa_ifwithaddr((struct sockaddr *)(&srcsock)); if (ia6 == NULL || (ia6->ia6_flags & (IN6_IFF_ANYCAST | IN6_IFF_NOTREADY))) { *errorp = EADDRNOTAVAIL; return(NULL); } pi->ipi6_addr = srcsock.sin6_addr; /* XXX: this overrides pi */ if (*ifpp) *ifpp = ifp; return(&pi->ipi6_addr); } /* * Otherwise, if the socket has already bound the source, just use it. */ if (laddr && !IN6_IS_ADDR_UNSPECIFIED(laddr)) return(laddr); /* * If the address is not specified, choose the best one based on * the outgoing interface and the destination address. */ /* get the outgoing interface */ if ((*errorp = in6_selectif(dstsock, opts, mopts, ro, &ifp)) != 0) return(NULL); #ifdef MIP6 #ifdef MIP6_ALLOW_COA_FALLBACK for (sc = TAILQ_FIRST(&hif_softc_list); sc; sc = TAILQ_NEXT(sc, hif_entry)) { mbu_dst = mip6_bu_list_find_withpaddr(&sc->hif_bu_list, dst); if (mbu_dst != NULL) coafallback = mbu_dst->mbu_coafallback; } #endif /* MIP6_ALLOW_COA_FALLBACK */ #endif /* MIP6 */ #ifdef DIAGNOSTIC if (ifp == NULL) /* this should not happen */ panic("in6_selectsrc: NULL ifp"); #endif for (ia = in6_ifaddr; ia; ia = ia->ia_next) { int new_scope = -1, new_matchlen = -1; struct in6_addrpolicy *new_policy = NULL; int64_t srczone, dstzone; struct ifnet *ifp1 = ia->ia_ifp; /* * We'll never take an address that breaks the scope zone * of the destination. We also skip an address if its zone * does not contain the outgoing interface. * XXX: we should probably use sin6_scope_id here. */ if ((dstzone = in6_addr2zoneid(ifp1, dst)) < 0 || dstzone != in6_addr2zoneid(ifp, dst)) { continue; } if ((srczone = in6_addr2zoneid(ifp1, &ia->ia_addr.sin6_addr)) < 0 || srczone != in6_addr2zoneid(ifp, &ia->ia_addr.sin6_addr)) { continue; } /* avoid unusable addresses */ if ((ia->ia6_flags & (IN6_IFF_NOTREADY | IN6_IFF_ANYCAST | IN6_IFF_DETACHED))) { continue; } if (!ip6_use_deprecated && IFA6_IS_DEPRECATED(ia)) continue; /* Rule 1: Prefer same address */ if (IN6_ARE_ADDR_EQUAL(dst, &ia->ia_addr.sin6_addr)) { ia_best = ia; BREAK(1); /* there should be no better candidate */ } if (ia_best == NULL) REPLACE(0); /* Rule 2: Prefer appropriate scope */ if (dst_scope < 0) dst_scope = in6_addrscope(dst); new_scope = in6_addrscope(&ia->ia_addr.sin6_addr); if (IN6_ARE_SCOPE_CMP(best_scope, new_scope) < 0) { if (IN6_ARE_SCOPE_CMP(best_scope, dst_scope) < 0) REPLACE(2); NEXT(2); } else if (IN6_ARE_SCOPE_CMP(new_scope, best_scope) < 0) { if (IN6_ARE_SCOPE_CMP(new_scope, dst_scope) < 0) NEXT(2); REPLACE(2); } /* * Rule 3: Avoid deprecated addresses. Note that the case of * !ip6_use_deprecated is already rejected above. */ if (!IFA6_IS_DEPRECATED(ia_best) && IFA6_IS_DEPRECATED(ia)) NEXT(3); if (IFA6_IS_DEPRECATED(ia_best) && !IFA6_IS_DEPRECATED(ia)) REPLACE(3); /* Rule 4: Prefer home addresses */ /* * XXX: This is a TODO. We should probably merge the MIP6 * case above. */ #ifdef MIP6 /* * If SA is simultaneously a home address and care-of address * and SB is not, then prefer SA. Similarly, if SB is * simultaneously a home address and care-of address and SA is * not, then prefer SB. */ { struct mip6_bu *mbu_ia_best = NULL, *mbu_ia = NULL; if (ia_best->ia6_flags & IN6_IFF_HOME) { /* * find a binding update entry for ia_best. */ for (sc = TAILQ_FIRST(&hif_softc_list); sc; sc = TAILQ_NEXT(sc, hif_entry)) { mbu_ia_best = mip6_bu_list_find_home_registration( &sc->hif_bu_list, &ia->ia_addr.sin6_addr); if (mbu_ia_best) break; } } if (ia->ia6_flags & IN6_IFF_HOME) { /* * find a binding update entry for ia. */ for (sc = TAILQ_FIRST(&hif_softc_list); sc; sc = TAILQ_NEXT(sc, hif_entry)) { mbu_ia = mip6_bu_list_find_home_registration( &sc->hif_bu_list, &ia->ia_addr.sin6_addr); if (mbu_ia) break; } } /* * if the binding update entry for a certain address * exists and its registration status is * MIP6_BU_REG_STATE_NOTREG, the address is a home * address and a care of addres simultaneously. */ if ((mbu_ia_best && (mbu_ia_best->mbu_reg_state == MIP6_BU_REG_STATE_NOTREG)) && !(mbu_ia && (mbu_ia->mbu_reg_state == MIP6_BU_REG_STATE_NOTREG))) { NEXT(4); } if (!(mbu_ia_best && (mbu_ia_best->mbu_reg_state == MIP6_BU_REG_STATE_NOTREG)) && (mbu_ia && (mbu_ia->mbu_reg_state == MIP6_BU_REG_STATE_NOTREG))) { REPLACE(4); } } #ifdef MIP6_ALLOW_COA_FALLBACK if (coafallback) { /* * if the peer doesn't recognize a home * address destination option, we will use a * CoA as a source address instead of a home * address we have registered before. Though * this behavior may arouse a mip6 beleiver's * anger, is very useful in the current * transition period that many hosts don't * recognize a home address destination * option... */ if ((ia_best->ia6_flags & IN6_IFF_HOME) == 0 && (ia->ia6_flags & IN6_IFF_HOME) != 0) { /* XXX will break stat! */ NEXT(0); } if ((ia_best->ia6_flags & IN6_IFF_HOME) != 0 && (ia->ia6_flags & IN6_IFF_HOME) == 0) { /* XXX will break stat! */ REPLACE(0); } } else #endif { /* * If SA is just a home address and SB is just * a care-of address, then prefer * SA. Similarly, if SB is just a home address * and SA is just a care-of address, then * prefer SB. */ if ((ia_best->ia6_flags & IN6_IFF_HOME) != 0 && (ia->ia6_flags & IN6_IFF_HOME) == 0) { NEXT(4); } if ((ia_best->ia6_flags & IN6_IFF_HOME) == 0 && (ia->ia6_flags & IN6_IFF_HOME) != 0) { REPLACE(4); } } #endif /* MIP6 */ /* Rule 5: Prefer outgoing interface */ if (ia_best->ia_ifp == ifp && ia->ia_ifp != ifp) NEXT(5); if (ia_best->ia_ifp != ifp && ia->ia_ifp == ifp) REPLACE(5); /* * Rule 6: Prefer matching label * Note that best_policy should be non-NULL here. */ if (dst_policy == NULL) dst_policy = lookup_addrsel_policy(dstsock); if (dst_policy->label != ADDR_LABEL_NOTAPP) { new_policy = lookup_addrsel_policy(&ia->ia_addr); if (dst_policy->label == best_policy->label && dst_policy->label != new_policy->label) NEXT(6); if (dst_policy->label != best_policy->label && dst_policy->label == new_policy->label) REPLACE(6); } /* * Rule 7: Prefer public addresses. * We allow users to reverse the logic by configuring * a sysctl variable, so that privacy conscious users can * always prefer temporary addresses. */ if (!(ia_best->ia6_flags & IN6_IFF_TEMPORARY) && (ia->ia6_flags & IN6_IFF_TEMPORARY)) { if (ip6_prefer_tempaddr) REPLACE(7); else NEXT(7); } if ((ia_best->ia6_flags & IN6_IFF_TEMPORARY) && !(ia->ia6_flags & IN6_IFF_TEMPORARY)) { if (ip6_prefer_tempaddr) NEXT(7); else REPLACE(7); } /* * Rule 8: prefer addresses on alive interfaces. * This is a KAME specific rule. */ if ((ia_best->ia_ifp->if_flags & IFF_UP) && !(ia->ia_ifp->if_flags & IFF_UP)) NEXT(8); if (!(ia_best->ia_ifp->if_flags & IFF_UP) && (ia->ia_ifp->if_flags & IFF_UP)) REPLACE(8); /* * Rule 9: prefer addresses on "preferred" interfaces. * This is a KAME specific rule. */ #define NDI_BEST (nd_ifinfo[ia_best->ia_ifp->if_index]) #define NDI_NEW (nd_ifinfo[ia->ia_ifp->if_index]) if ((NDI_BEST.flags & ND6_IFF_PREFER_SOURCE) && !(NDI_NEW.flags & ND6_IFF_PREFER_SOURCE)) NEXT(9); if (!(NDI_BEST.flags & ND6_IFF_PREFER_SOURCE) && (NDI_NEW.flags & ND6_IFF_PREFER_SOURCE)) REPLACE(9); #undef NDI_BEST #undef NDI_NEW /* * Rule 14: Use longest matching prefix. * Note: in the address selection draft, this rule is * documented as "Rule 8". However, since it is also * documented that this rule can be overridden, we assign * a large number so that it is easy to assign smaller numbers * to more preferred rules. */ new_matchlen = in6_matchlen(&ia->ia_addr.sin6_addr, dst); if (best_matchlen < new_matchlen) REPLACE(14); if (new_matchlen < best_matchlen) NEXT(14); /* Rule 15 is reserved. */ /* * Last resort: just keep the current candidate. * Or, do we need more rules? */ continue; replace: ia_best = ia; best_scope = (new_scope >= 0 ? new_scope : in6_addrscope(&ia_best->ia_addr.sin6_addr)); best_policy = (new_policy ? new_policy : lookup_addrsel_policy(&ia_best->ia_addr)); best_matchlen = (new_matchlen >= 0 ? new_matchlen : in6_matchlen(&ia_best->ia_addr.sin6_addr, dst)); next: continue; out: break; } if ((ia = ia_best) == NULL) { *errorp = EADDRNOTAVAIL; return(NULL); } if (ifpp) *ifpp = ifp; return(&ia->ia_addr.sin6_addr); }