int atmresolve(struct rtentry *rt0, struct mbuf *m, const struct sockaddr *dst, struct atm_pseudohdr *desten /* OUT */) { const struct sockaddr_dl *sdl; struct rtentry *rt = rt0; if (m->m_flags & (M_BCAST|M_MCAST)) { log(LOG_INFO, "atmresolve: BCAST/MCAST packet detected/dumped\n"); goto bad; } if (rt == NULL) { rt = RTALLOC1(dst, 0); if (rt == NULL) goto bad; /* failed */ if ((rt->rt_flags & RTF_GATEWAY) != 0 || /* XXX: are we using LLINFO? */ rt->rt_gateway->sa_family != AF_LINK) { rtfree(rt); goto bad; } } /* * note that rt_gateway is a sockaddr_dl which contains the * atm_pseudohdr data structure for this route. we currently * don't need any rt_llinfo info (but will if we want to support * ATM ARP [c.f. if_ether.c]). */ sdl = satocsdl(rt->rt_gateway); /* * Check the address family and length is valid, the address * is resolved; otherwise, try to resolve. */ if (sdl->sdl_family == AF_LINK && sdl->sdl_alen == sizeof(*desten)) { memcpy(desten, CLLADDR(sdl), sdl->sdl_alen); if (rt != rt0) rtfree(rt); return (1); /* ok, go for it! */ } if (rt != rt0) rtfree(rt); /* * we got an entry, but it doesn't have valid link address * info in it (it is prob. the interface route, which has * sdl_alen == 0). dump packet. (fall through to "bad"). */ bad: m_freem(m); return (0); }
static void at_pcbdetach( struct socket *so, struct ddpcb *ddp) { soisdisconnected( so ); so->so_pcb = 0; sofree( so ); /* remove ddp from ddp_ports list */ if ( ddp->ddp_lsat.sat_port != ATADDR_ANYPORT && ddp_ports[ ddp->ddp_lsat.sat_port - 1 ] != NULL ) { if ( ddp->ddp_pprev != NULL ) { ddp->ddp_pprev->ddp_pnext = ddp->ddp_pnext; } else { ddp_ports[ ddp->ddp_lsat.sat_port - 1 ] = ddp->ddp_pnext; } if ( ddp->ddp_pnext != NULL ) { ddp->ddp_pnext->ddp_pprev = ddp->ddp_pprev; } } if ( ddp->ddp_route.ro_rt ) { rtfree( ddp->ddp_route.ro_rt ); } if ( ddp->ddp_prev ) { ddp->ddp_prev->ddp_next = ddp->ddp_next; } else { ddpcb = ddp->ddp_next; } if ( ddp->ddp_next ) { ddp->ddp_next->ddp_prev = ddp->ddp_prev; } FREE(ddp, M_PCB); }
/* * Delete a route and generate a message */ int rtdeletemsg(struct rtentry *rt, u_int tableid) { int error; struct rt_addrinfo info; struct ifnet *ifp; /* * Request the new route so that the entry is not actually * deleted. That will allow the information being reported to * be accurate (and consistent with route_output()). */ bzero((caddr_t)&info, sizeof(info)); info.rti_info[RTAX_DST] = rt_key(rt); info.rti_info[RTAX_NETMASK] = rt_mask(rt); info.rti_info[RTAX_GATEWAY] = rt->rt_gateway; info.rti_flags = rt->rt_flags; ifp = rt->rt_ifp; error = rtrequest1(RTM_DELETE, &info, rt->rt_priority, &rt, tableid); rt_missmsg(RTM_DELETE, &info, info.rti_flags, ifp, error, tableid); /* Adjust the refcount */ if (error == 0 && rt->rt_refcnt <= 0) { rt->rt_refcnt++; rtfree(rt); } return (error); }
/*------------------------------------------------------------------------ * tcpwinit - initialize window data for a new connection *------------------------------------------------------------------------ */ int tcpwinit(struct tcb *ptcb, struct tcb *newptcb, struct ep *pep) { struct ip *pip = (struct ip *)pep->ep_data; struct tcp *ptcp = (struct tcp *)pip->ip_data; struct route *prt; unsigned mss; Bool local; newptcb->tcb_swindow = ptcp->tcp_window; newptcb->tcb_lwseq = ptcp->tcp_seq; newptcb->tcb_lwack = newptcb->tcb_iss; /* set in tcpsync() */ prt = (struct route *)rtget(pip->ip_src, RTF_REMOTE); local = prt && prt->rt_metric == 0; newptcb->tcb_pni = &nif[prt->rt_ifnum]; rtfree(prt); if (local) mss = newptcb->tcb_pni->ni_mtu-IPMHLEN-TCPMHLEN; else mss = 536; /* RFC 1122 */ if (ptcb->tcb_smss) { newptcb->tcb_smss = min(ptcb->tcb_smss, mss); ptcb->tcb_smss = 0; /* reset server smss */ } else newptcb->tcb_smss = mss; newptcb->tcb_rmss = mss; /* receive mss */ newptcb->tcb_cwnd = newptcb->tcb_smss; /* 1 segment */ newptcb->tcb_ssthresh = 65535; /* IP max window */ newptcb->tcb_rnext = ptcp->tcp_seq; newptcb->tcb_cwin = newptcb->tcb_rnext + newptcb->tcb_rbsize; }
/* * Check for alternatives when higher level complains * about service problems. For now, invalidate cached * routing information. If the route was created dynamically * (by a redirect), time to try a default gateway again. */ void in_losing(struct inpcb *inp) { register struct rtentry *rt; struct rt_addrinfo info; if ((rt = inp->inp_route.ro_rt)) { inp->inp_route.ro_rt = 0; bzero((caddr_t)&info, sizeof(info)); info.rti_info[RTAX_DST] = (struct sockaddr *)&inp->inp_route.ro_dst; info.rti_info[RTAX_GATEWAY] = rt->rt_gateway; info.rti_info[RTAX_NETMASK] = rt_mask(rt); rt_missmsg(RTM_LOSING, &info, rt->rt_flags, 0); if (rt->rt_flags & RTF_DYNAMIC) (void) rtrequest(RTM_DELETE, rt_key(rt), rt->rt_gateway, rt_mask(rt), rt->rt_flags, (struct rtentry **)0); else /* * A new route can be allocated * the next time output is attempted. */ rtfree(rt); } }
/* Gateway selection by Hash-Threshold (RFC 2992) */ struct rtentry * rn_mpath_select(struct rtentry *rt, uint32_t *srcaddrp) { struct radix_node *rn; int hash, npaths, threshold; rn = (struct radix_node *)rt; npaths = rn_mpath_active_count(rn); hash = rn_mpath_hash(rt_key(rt), srcaddrp) & 0xffff; threshold = 1 + (0xffff / npaths); while (hash > threshold && rn) { /* stay within the multipath routes */ rn = rn_mpath_next(rn, RMP_MODE_ACTIVE); hash -= threshold; } /* if gw selection fails, use the first match (default) */ if (rn != NULL) { rtfree(rt); rt = (struct rtentry *)rn; rt->rt_refcnt++; } return (rt); }
/* * allocate a route, potentially using multipath to select the peer. */ void rtalloc_mpath(struct route *ro, u_int32_t *srcaddrp) { #if defined(INET) || defined(INET6) struct radix_node *rn; int hash, npaths, threshold; #endif /* * return a cached entry if it is still valid, otherwise we increase * the risk of disrupting local flows. */ if (ro->ro_rt && ro->ro_rt->rt_ifp && (ro->ro_rt->rt_flags & RTF_UP)) return; ro->ro_rt = rtalloc1(&ro->ro_dst, RT_REPORT, ro->ro_tableid); /* if the route does not exist or it is not multipath, don't care */ if (!ro->ro_rt || !(ro->ro_rt->rt_flags & RTF_MPATH)) return; /* check if multipath routing is enabled for the specified protocol */ if (!(0 #ifdef INET || (ipmultipath && ro->ro_dst.sa_family == AF_INET) #endif #ifdef INET6 || (ip6_multipath && ro->ro_dst.sa_family == AF_INET6) #endif )) return; #if defined(INET) || defined(INET6) /* gw selection by Hash-Threshold (RFC 2992) */ rn = (struct radix_node *)ro->ro_rt; npaths = rn_mpath_count(rn); hash = rn_mpath_hash(ro, srcaddrp) & 0xffff; threshold = 1 + (0xffff / npaths); while (hash > threshold && rn) { /* stay within the multipath routes */ if (rn_mpath_next(rn, 0) == NULL) break; rn = rn->rn_dupedkey; hash -= threshold; } /* XXX try filling rt_gwroute and avoid unreachable gw */ /* if gw selection fails, use the first match (default) */ if (!rn) return; rtfree(ro->ro_rt); ro->ro_rt = (struct rtentry *)rn; ro->ro_rt->rt_refcnt++; #endif }
struct rtentry * in6_selectroute(struct sockaddr_in6 *dstsock, struct ip6_pktopts *opts, struct route_in6 *ro, unsigned int rtableid) { struct in6_addr *dst; dst = &dstsock->sin6_addr; /* * Use a cached route if it exists and is valid, else try to allocate * a new one. Note that we should check the address family of the * cached destination, in case of sharing the cache with IPv4. */ if (ro) { if (!rtisvalid(ro->ro_rt) || sin6tosa(&ro->ro_dst)->sa_family != AF_INET6 || !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 = *dstsock; sa6->sin6_scope_id = 0; ro->ro_tableid = rtableid; ro->ro_rt = rtalloc_mpath(sin6tosa(&ro->ro_dst), NULL, ro->ro_tableid); } /* * Check if the outgoing interface conflicts with * the interface specified by ipi6_ifindex (if specified). * Note that loopback interface is always okay. * (this may happen when we are sending a packet to one of * our own addresses.) */ if (opts && opts->ip6po_pktinfo && opts->ip6po_pktinfo->ipi6_ifindex) { if (ro->ro_rt != NULL && !ISSET(ro->ro_rt->rt_flags, RTF_LOCAL) && ro->ro_rt->rt_ifidx != opts->ip6po_pktinfo->ipi6_ifindex) { return (NULL); } } return (ro->ro_rt); } return (NULL); }
void Lpx_PCB_detach(struct lpxpcb *lpxp ) { struct socket *so = lpxp->lpxp_socket; so->so_pcb = 0; sofree(so); if (lpxp->lpxp_route.ro_rt != NULL) rtfree(lpxp->lpxp_route.ro_rt); remque(lpxp); FREE(lpxp, M_PCB); }
/* * After a routing change, flush old routing * and allocate a (hopefully) better one. */ lpx_rtchange( struct lpxpcb *lpxp ) { if (lpxp->lpxp_route.ro_rt != NULL) { rtfree(lpxp->lpxp_route.ro_rt); lpxp->lpxp_route.ro_rt = NULL; /* * A new route can be allocated the next time * output is attempted. */ } /* SHOULD NOTIFY HIGHER-LEVEL PROTOCOLS */ }
/* * After a routing change, flush old routing * and allocate a (hopefully) better one. */ static void in_rtchange(struct inpcb *inp, int errnum) { if (inp->inp_route.ro_rt) { rtfree(inp->inp_route.ro_rt); inp->inp_route.ro_rt = 0; /* * A new route can be allocated the next time * output is attempted. */ } }
void in6_pcbdetach( struct inpcb *inp) { struct socket *so = inp->inp_socket; struct inpcbinfo *ipi = inp->inp_pcbinfo; #if IPSEC if (inp->in6p_sp != NULL) { ipsec6_delete_pcbpolicy(inp); } #endif /* IPSEC */ if (in_pcb_checkstate(inp, WNT_STOPUSING, 1) != WNT_STOPUSING) printf("in6_pcbdetach so=%p can't be marked dead ok\n", so); inp->inp_state = INPCB_STATE_DEAD; if ((so->so_flags & SOF_PCBCLEARING) == 0) { struct ip_moptions *imo; struct ip6_moptions *im6o; inp->inp_vflag = 0; so->so_flags |= SOF_PCBCLEARING; inp->inp_gencnt = ++ipi->ipi_gencnt; if (inp->in6p_options) m_freem(inp->in6p_options); ip6_freepcbopts(inp->in6p_outputopts); if (inp->in6p_route.ro_rt) { rtfree(inp->in6p_route.ro_rt); inp->in6p_route.ro_rt = NULL; } /* Check and free IPv4 related resources in case of mapped addr */ if (inp->inp_options) (void)m_free(inp->inp_options); im6o = inp->in6p_moptions; inp->in6p_moptions = NULL; if (im6o != NULL) IM6O_REMREF(im6o); imo = inp->inp_moptions; inp->inp_moptions = NULL; if (imo != NULL) IMO_REMREF(imo); } }
void rtalloc_mpath(struct route *ro, int hash) { struct radix_node *rn0, *rn; int n; /* * XXX we don't attempt to lookup cached route again; what should * be done for sendto(3) case? */ if (ro->ro_rt && ro->ro_rt->rt_ifp && (ro->ro_rt->rt_flags & RTF_UP)) return; /* XXX */ #ifdef __FreeBSD__ ro->ro_rt = rtalloc1(&ro->ro_dst, 1, 0UL); #else ro->ro_rt = rtalloc1(&ro->ro_dst, 1); #endif /* if the route does not exist or it is not multipath, don't care */ if (!ro->ro_rt || !rn_mpath_next((struct radix_node *)ro->ro_rt)) return; /* beyond here, we use rn as the master copy */ rn0 = rn = (struct radix_node *)ro->ro_rt; n = rn_mpath_count(rn0); /* gw selection by Modulo-N Hash (RFC2991) XXX need improvement? */ hash += hashjitter; hash %= n; while (hash-- > 0 && rn) { /* stay within the multipath routes */ if (rn->rn_dupedkey && rn->rn_mask != rn->rn_dupedkey->rn_mask) break; rn = rn->rn_dupedkey; } /* XXX try filling rt_gwroute and avoid unreachable gw */ /* if gw selection fails, use the first match (default) */ if (!rn) return; rtfree(ro->ro_rt); ro->ro_rt = (struct rtentry *)rn; ro->ro_rt->rt_refcnt++; }
void in_pcbdetach(struct inpcb *inp) { struct socket *so = inp->inp_socket; struct inpcbinfo *ipi = inp->inp_pcbinfo; int s; inp->inp_gencnt = ++ipi->ipi_gencnt; so->so_pcb = 0; sofree(so); if (inp->inp_options) (void)m_free(inp->inp_options); if (inp->inp_route.ro_rt) rtfree(inp->inp_route.ro_rt); ip_freemoptions(inp->inp_moptions); s = splnet(); LIST_REMOVE(inp, inp_hash); LIST_REMOVE(inp, inp_list); splx(s); FREE(inp, M_PCB); }
/* * Apply routing function on the affected upstream and downstream prefixes, * i.e. either set or clear RTF_PROXY on the cloning prefix route; all route * entries that were cloned off these prefixes will be blown away. Caller * must have acquried proxy6_lock and must not be holding nd6_mutex. */ static void nd6_prproxy_prelist_setroute(boolean_t enable, struct nd6_prproxy_prelist_head *up_head, struct nd6_prproxy_prelist_head *down_head) { struct nd6_prproxy_prelist *up, *down, *ndprl_tmp; struct nd_prefix *pr; lck_mtx_assert(&proxy6_lock, LCK_MTX_ASSERT_OWNED); lck_mtx_assert(nd6_mutex, LCK_MTX_ASSERT_NOTOWNED); SLIST_FOREACH_SAFE(up, up_head, ndprl_le, ndprl_tmp) { struct rtentry *rt; boolean_t prproxy, set_allmulti = FALSE; int allmulti_sw; struct ifnet *ifp = NULL; SLIST_REMOVE(up_head, up, nd6_prproxy_prelist, ndprl_le); pr = up->ndprl_pr; VERIFY(up->ndprl_up == NULL); NDPR_LOCK(pr); ifp = pr->ndpr_ifp; prproxy = (pr->ndpr_stateflags & NDPRF_PRPROXY); VERIFY(!prproxy || ((pr->ndpr_stateflags & NDPRF_ONLINK) && !(pr->ndpr_stateflags & NDPRF_IFSCOPE))); nd6_prproxy_sols_reap(pr); VERIFY(pr->ndpr_prproxy_sols_cnt == 0); VERIFY(RB_EMPTY(&pr->ndpr_prproxy_sols)); if (enable && pr->ndpr_allmulti_cnt == 0) { nd6_prproxy++; pr->ndpr_allmulti_cnt++; set_allmulti = TRUE; allmulti_sw = TRUE; } else if (!enable && pr->ndpr_allmulti_cnt > 0) { nd6_prproxy--; pr->ndpr_allmulti_cnt--; set_allmulti = TRUE; allmulti_sw = FALSE; } if ((rt = pr->ndpr_rt) != NULL) { if ((enable && prproxy) || (!enable && !prproxy)) RT_ADDREF(rt); else rt = NULL; NDPR_UNLOCK(pr); } else { NDPR_UNLOCK(pr); } /* Call the following ioctl after releasing NDPR lock */ if (set_allmulti && ifp != NULL) if_allmulti(ifp, allmulti_sw); NDPR_REMREF(pr); if (rt != NULL) { rt_set_proxy(rt, enable); rtfree(rt); } nd6_ndprl_free(up); } SLIST_FOREACH_SAFE(down, down_head, ndprl_le, ndprl_tmp) { struct nd_prefix *pr_up; struct rtentry *rt; boolean_t prproxy, set_allmulti = FALSE; int allmulti_sw; struct ifnet *ifp = NULL; SLIST_REMOVE(down_head, down, nd6_prproxy_prelist, ndprl_le); pr = down->ndprl_pr; pr_up = down->ndprl_up; VERIFY(pr_up != NULL); NDPR_LOCK(pr_up); ifp = pr->ndpr_ifp; prproxy = (pr_up->ndpr_stateflags & NDPRF_PRPROXY); VERIFY(!prproxy || ((pr_up->ndpr_stateflags & NDPRF_ONLINK) && !(pr_up->ndpr_stateflags & NDPRF_IFSCOPE))); NDPR_UNLOCK(pr_up); NDPR_LOCK(pr); if (enable && pr->ndpr_allmulti_cnt == 0) { pr->ndpr_allmulti_cnt++; set_allmulti = TRUE; allmulti_sw = TRUE; } else if (!enable && pr->ndpr_allmulti_cnt > 0) { pr->ndpr_allmulti_cnt--; set_allmulti = TRUE; allmulti_sw = FALSE; } if ((rt = pr->ndpr_rt) != NULL) { if ((enable && prproxy) || (!enable && !prproxy)) RT_ADDREF(rt); else rt = NULL; NDPR_UNLOCK(pr); } else { NDPR_UNLOCK(pr); } if (set_allmulti && ifp != NULL) if_allmulti(ifp, allmulti_sw); NDPR_REMREF(pr); NDPR_REMREF(pr_up); if (rt != NULL) { rt_set_proxy(rt, enable); rtfree(rt); } nd6_ndprl_free(down); } }
/* * 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); }
/* * 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; u_char edst[ETHER_ADDR_LEN]; u_char *esrc; struct mbuf *m = m0; struct rtentry *rt; struct mbuf *mcopy = NULL; 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.ph_rtableid)) { 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.ph_rtableid), 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) { 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.ph_rtableid)) != NULL) rt->rt_refcnt--; else senderr(EHOSTUNREACH); } if (rt->rt_flags & RTF_GATEWAY) { if (rt->rt_gwroute == NULL) 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); } esrc = ac->ac_enaddr; 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); memcpy(edst, LLADDR((struct sockaddr_dl *)dst), 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: eh = (struct ether_header *)dst->sa_data; esrc = eh->ether_shost; /* FALLTHROUGH */ case AF_UNSPEC: eh = (struct ether_header *)dst->sa_data; memcpy(edst, eh->ether_dhost, 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); #if NCARP > 0 if (ifp0 != ifp && ifp0->if_type == IFT_CARP) esrc = carp_get_srclladdr(ifp0, esrc); #endif if (ether_addheader(&m, ifp, etype, esrc, edst) == -1) senderr(ENOBUFS); #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 (!memcmp(&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; } memcpy(mtag + 1, &ifp->if_bridgeport, 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); }
static int wdbUdpSockRcvfrom ( WDB_COMM_ID commId, caddr_t addr, uint_t len, struct sockaddr_in *pSockAddr, struct timeval *tv ) { uint_t nBytes; int addrLen = sizeof (struct sockaddr_in); struct fd_set readFds; static struct route theRoute; struct sockaddr_in sockAddr; /* wait for data with a timeout */ FD_ZERO (&readFds); FD_SET (wdbUdpSock, &readFds); if (select (wdbUdpSock + 1, &readFds, NULL, NULL, tv) < 0) { printErr ("wdbUdpSockLib: select failed!\n"); return (0); } if (!FD_ISSET (wdbUdpSock, &readFds)) { return (0); /* select timed out */ } /* read the data */ nBytes = recvfrom (wdbUdpSock, addr, len, 0, (struct sockaddr *)pSockAddr, &addrLen); if (nBytes < 4) return (0); /* * The following is a fix for SPR #4645 (the agent does * not report the correct MTU to the host). We lower * the value of the global variable wdbCommMtu if a packet * arrives via an interface with a smaller MTU. * * XXX This fix has some problems: * 1) it uses rtalloc, which is not an approved interface. * Note: rtalloc doesn't seem to like having the port number * in the socket address. * 2) it assumes all agent packets arrive via the same netif. * 3) it doesn't take into account intermediate MTUs in a WAN. * The right fix for (3) would be to use an MTU-discovey * algorithm (e.g. send larger and larger IP datagrams with the * "don't fragment" flag until an ICMP error is returned). */ if (theRoute.ro_rt == NULL) { sockAddr = *pSockAddr; sockAddr.sin_port = 0; theRoute.ro_dst = * (struct sockaddr *) &sockAddr; rtalloc (&theRoute); if (theRoute.ro_rt != NULL) { if (wdbCommMtu > theRoute.ro_rt->rt_ifp->if_mtu) wdbCommMtu = theRoute.ro_rt->rt_ifp->if_mtu; rtfree (theRoute.ro_rt); } } return (nBytes); }
/* * prepend shim and deliver */ static int mpls_output(struct ifnet *ifp, struct mbuf *m, const struct sockaddr *dst, const struct rtentry *rt) { union mpls_shim mh, *pms; struct rtentry *rt1; int err; uint psize = sizeof(struct sockaddr_mpls); KASSERT(KERNEL_LOCKED_P()); if ((ifp->if_flags & (IFF_UP|IFF_RUNNING)) != (IFF_UP|IFF_RUNNING)) { m_freem(m); return ENETDOWN; } if (rt_gettag(rt) == NULL || rt_gettag(rt)->sa_family != AF_MPLS) { m_freem(m); return EINVAL; } bpf_mtap_af(ifp, dst->sa_family, m); memset(&mh, 0, sizeof(mh)); mh.s_addr = MPLS_GETSADDR(rt); mh.shim.bos = 1; mh.shim.exp = 0; mh.shim.ttl = mpls_defttl; pms = &((struct sockaddr_mpls*)rt_gettag(rt))->smpls_addr; while (psize <= rt_gettag(rt)->sa_len - sizeof(mh)) { pms++; if (mh.shim.label != MPLS_LABEL_IMPLNULL && ((m = mpls_prepend_shim(m, &mh)) == NULL)) return ENOBUFS; memset(&mh, 0, sizeof(mh)); mh.s_addr = ntohl(pms->s_addr); mh.shim.bos = mh.shim.exp = 0; mh.shim.ttl = mpls_defttl; psize += sizeof(mh); } switch(dst->sa_family) { #ifdef INET case AF_INET: m = mpls_label_inet(m, &mh, psize - sizeof(struct sockaddr_mpls)); break; #endif #ifdef INET6 case AF_INET6: m = mpls_label_inet6(m, &mh, psize - sizeof(struct sockaddr_mpls)); break; #endif default: m = mpls_prepend_shim(m, &mh); break; } if (m == NULL) { IF_DROP(&ifp->if_snd); ifp->if_oerrors++; return ENOBUFS; } ifp->if_opackets++; ifp->if_obytes += m->m_pkthdr.len; if ((rt1=rtalloc1(rt->rt_gateway, 1)) == NULL) { m_freem(m); return EHOSTUNREACH; } err = mpls_send_frame(m, rt1->rt_ifp, rt); rtfree(rt1); return err; }
/* * MPLS Label Switch Engine */ static int mpls_lse(struct mbuf *m) { struct sockaddr_mpls dst; union mpls_shim tshim, *htag; struct rtentry *rt = NULL; int error = ENOBUFS; uint psize = sizeof(struct sockaddr_mpls); bool push_back_alert = false; if (m->m_len < sizeof(union mpls_shim) && (m = m_pullup(m, sizeof(union mpls_shim))) == NULL) goto done; dst.smpls_len = sizeof(struct sockaddr_mpls); dst.smpls_family = AF_MPLS; dst.smpls_addr.s_addr = ntohl(mtod(m, union mpls_shim *)->s_addr); /* Check if we're accepting MPLS Frames */ error = EINVAL; if (!mpls_frame_accept) goto done; /* TTL decrement */ if ((m = mpls_ttl_dec(m)) == NULL) goto done; /* RFC 4182 */ if (mpls_rfc4182 != 0) while((dst.smpls_addr.shim.label == MPLS_LABEL_IPV4NULL || dst.smpls_addr.shim.label == MPLS_LABEL_IPV6NULL) && __predict_false(dst.smpls_addr.shim.bos == 0)) TRIM_LABEL; /* RFC 3032 Section 2.1 Page 4 */ if (__predict_false(dst.smpls_addr.shim.label == MPLS_LABEL_RTALERT) && dst.smpls_addr.shim.bos == 0) { TRIM_LABEL; push_back_alert = true; } if (dst.smpls_addr.shim.label <= MPLS_LABEL_RESMAX) { /* Don't swap reserved labels */ switch (dst.smpls_addr.shim.label) { #ifdef INET case MPLS_LABEL_IPV4NULL: /* Pop shim and push mbuf to IP stack */ if (dst.smpls_addr.shim.bos) error = mpls_unlabel_inet(m); break; #endif #ifdef INET6 case MPLS_LABEL_IPV6NULL: /* Pop shim and push mbuf to IPv6 stack */ if (dst.smpls_addr.shim.bos) error = mpls_unlabel_inet6(m); break; #endif case MPLS_LABEL_RTALERT: /* Yeah, I'm all alerted */ case MPLS_LABEL_IMPLNULL: /* This is logical only */ default: /* Rest are not allowed */ break; } goto done; } /* Check if we should do MPLS forwarding */ error = EHOSTUNREACH; if (!mpls_forwarding) goto done; /* Get a route to dst */ dst.smpls_addr.shim.ttl = dst.smpls_addr.shim.bos = dst.smpls_addr.shim.exp = 0; dst.smpls_addr.s_addr = htonl(dst.smpls_addr.s_addr); if ((rt = rtalloc1((const struct sockaddr*)&dst, 1)) == NULL) goto done; /* MPLS packet with no MPLS tagged route ? */ if ((rt->rt_flags & RTF_GATEWAY) == 0 || rt_gettag(rt) == NULL || rt_gettag(rt)->sa_family != AF_MPLS) goto done; tshim.s_addr = MPLS_GETSADDR(rt); /* Swap labels */ if ((m->m_len < sizeof(union mpls_shim)) && (m = m_pullup(m, sizeof(union mpls_shim))) == 0) { error = ENOBUFS; goto done; } /* Replace only the label */ htag = mtod(m, union mpls_shim *); htag->s_addr = ntohl(htag->s_addr); htag->shim.label = tshim.shim.label; htag->s_addr = htonl(htag->s_addr); /* check if there is anything more to prepend */ htag = &((struct sockaddr_mpls*)rt_gettag(rt))->smpls_addr; while (psize <= rt_gettag(rt)->sa_len - sizeof(tshim)) { htag++; memset(&tshim, 0, sizeof(tshim)); tshim.s_addr = ntohl(htag->s_addr); tshim.shim.bos = tshim.shim.exp = 0; tshim.shim.ttl = mpls_defttl; if (tshim.shim.label != MPLS_LABEL_IMPLNULL && ((m = mpls_prepend_shim(m, &tshim)) == NULL)) return ENOBUFS; psize += sizeof(tshim); } if (__predict_false(push_back_alert == true)) { /* re-add the router alert label */ memset(&tshim, 0, sizeof(tshim)); tshim.s_addr = MPLS_LABEL_RTALERT; tshim.shim.bos = tshim.shim.exp = 0; tshim.shim.ttl = mpls_defttl; if ((m = mpls_prepend_shim(m, &tshim)) == NULL) return ENOBUFS; } if ((rt->rt_flags & RTF_GATEWAY) == 0) { error = EHOSTUNREACH; goto done; } rt->rt_use++; error = mpls_send_frame(m, rt->rt_ifp, rt); done: if (error != 0 && m != NULL) m_freem(m); if (rt != NULL) rtfree(rt); return error; }
struct mbuf * ip6_forward(struct mbuf *m, struct route_in6 *ip6forward_rt, int srcrt) { struct ip6_hdr *ip6 = mtod(m, struct ip6_hdr *); struct sockaddr_in6 *dst; struct rtentry *rt; int error, type = 0, code = 0; boolean_t proxy = FALSE; struct mbuf *mcopy = NULL; struct ifnet *ifp, *origifp; /* maybe unnecessary */ u_int32_t inzone, outzone; struct in6_addr src_in6, dst_in6; #if IPSEC struct secpolicy *sp = NULL; #endif struct timeval timenow; unsigned int ifscope = IFSCOPE_NONE; #if PF struct pf_mtag *pf_mtag; #endif /* PF */ getmicrotime(&timenow); #if PF pf_mtag = pf_find_mtag(m); if (pf_mtag != NULL && pf_mtag->pftag_rtableid != IFSCOPE_NONE) ifscope = pf_mtag->pftag_rtableid; /* * If the caller provides a route which is on a different interface * than the one specified for scoped forwarding, discard the route * and do a lookup below. */ if (ifscope != IFSCOPE_NONE && (rt = ip6forward_rt->ro_rt) != NULL) { RT_LOCK(rt); if (rt->rt_ifp->if_index != ifscope) { RT_UNLOCK(rt); rtfree(rt); rt = ip6forward_rt->ro_rt = NULL; } else { RT_UNLOCK(rt); } } #endif /* PF */ #if IPSEC /* * Check AH/ESP integrity. */ /* * Don't increment ip6s_cantforward because this is the check * before forwarding packet actually. */ if (ipsec_bypass == 0) { if (ipsec6_in_reject(m, NULL)) { IPSEC_STAT_INCREMENT(ipsec6stat.in_polvio); m_freem(m); return (NULL); } } #endif /*IPSEC*/ /* * Do not forward packets to multicast destination (should be handled * by ip6_mforward(). * Do not forward packets with unspecified source. It was discussed * in July 2000, on ipngwg mailing list. */ if ((m->m_flags & (M_BCAST|M_MCAST)) != 0 || IN6_IS_ADDR_MULTICAST(&ip6->ip6_dst) || IN6_IS_ADDR_UNSPECIFIED(&ip6->ip6_src)) { ip6stat.ip6s_cantforward++; /* XXX in6_ifstat_inc(rt->rt_ifp, ifs6_in_discard) */ if (ip6_log_time + ip6_log_interval < timenow.tv_sec) { ip6_log_time = timenow.tv_sec; log(LOG_DEBUG, "cannot forward " "from %s to %s nxt %d received on %s\n", ip6_sprintf(&ip6->ip6_src), ip6_sprintf(&ip6->ip6_dst), ip6->ip6_nxt, if_name(m->m_pkthdr.rcvif)); } m_freem(m); return (NULL); } if (ip6->ip6_hlim <= IPV6_HLIMDEC) { /* XXX in6_ifstat_inc(rt->rt_ifp, ifs6_in_discard) */ icmp6_error(m, ICMP6_TIME_EXCEEDED, ICMP6_TIME_EXCEED_TRANSIT, 0); return (NULL); } /* * See if the destination is a proxied address, and if so pretend * that it's for us. This is mostly to handle NUD probes against * the proxied addresses. We filter for ICMPv6 here and will let * icmp6_input handle the rest. */ if (!srcrt && nd6_prproxy) { VERIFY(!IN6_IS_ADDR_MULTICAST(&ip6->ip6_dst)); proxy = nd6_prproxy_isours(m, ip6, ip6forward_rt, ifscope); /* * Don't update hop limit while proxying; RFC 4389 4.1. * Also skip IPsec forwarding path processing as this * packet is not to be forwarded. */ if (proxy) goto skip_ipsec; } ip6->ip6_hlim -= IPV6_HLIMDEC; /* * Save at most ICMPV6_PLD_MAXLEN (= the min IPv6 MTU - * size of IPv6 + ICMPv6 headers) bytes of the packet in case * we need to generate an ICMP6 message to the src. * Thanks to M_EXT, in most cases copy will not occur. * * It is important to save it before IPsec processing as IPsec * processing may modify the mbuf. */ mcopy = m_copy(m, 0, imin(m->m_pkthdr.len, ICMPV6_PLD_MAXLEN)); #if IPSEC if (ipsec_bypass != 0) goto skip_ipsec; /* get a security policy for this packet */ sp = ipsec6_getpolicybyaddr(m, IPSEC_DIR_OUTBOUND, IP_FORWARDING, &error); if (sp == NULL) { IPSEC_STAT_INCREMENT(ipsec6stat.out_inval); ip6stat.ip6s_cantforward++; if (mcopy) { #if 0 /* XXX: what icmp ? */ #else m_freem(mcopy); #endif } m_freem(m); return (NULL); } error = 0; /* check policy */ switch (sp->policy) { case IPSEC_POLICY_DISCARD: case IPSEC_POLICY_GENERATE: /* * This packet is just discarded. */ IPSEC_STAT_INCREMENT(ipsec6stat.out_polvio); ip6stat.ip6s_cantforward++; key_freesp(sp, KEY_SADB_UNLOCKED); if (mcopy) { #if 0 /* XXX: what icmp ? */ #else m_freem(mcopy); #endif } m_freem(m); return (NULL); case IPSEC_POLICY_BYPASS: case IPSEC_POLICY_NONE: /* no need to do IPsec. */ key_freesp(sp, KEY_SADB_UNLOCKED); goto skip_ipsec; case IPSEC_POLICY_IPSEC: if (sp->req == NULL) { /* XXX should be panic ? */ printf("ip6_forward: No IPsec request specified.\n"); ip6stat.ip6s_cantforward++; key_freesp(sp, KEY_SADB_UNLOCKED); if (mcopy) { #if 0 /* XXX: what icmp ? */ #else m_freem(mcopy); #endif } m_freem(m); return (NULL); } /* do IPsec */ break; case IPSEC_POLICY_ENTRUST: default: /* should be panic ?? */ printf("ip6_forward: Invalid policy found. %d\n", sp->policy); key_freesp(sp, KEY_SADB_UNLOCKED); goto skip_ipsec; } { struct ipsec_output_state state; /* * All the extension headers will become inaccessible * (since they can be encrypted). * Don't panic, we need no more updates to extension headers * on inner IPv6 packet (since they are now encapsulated). * * IPv6 [ESP|AH] IPv6 [extension headers] payload */ bzero(&state, sizeof(state)); state.m = m; state.dst = NULL; /* update at ipsec6_output_tunnel() */ error = ipsec6_output_tunnel(&state, sp, 0); key_freesp(sp, KEY_SADB_UNLOCKED); if (state.tunneled == 4) return (NULL); /* packet is gone - sent over IPv4 */ m = state.m; if (state.ro.ro_rt) { rtfree(state.ro.ro_rt); state.ro.ro_rt = NULL; } if (error) { /* mbuf is already reclaimed in ipsec6_output_tunnel. */ switch (error) { case EHOSTUNREACH: case ENETUNREACH: case EMSGSIZE: case ENOBUFS: case ENOMEM: break; default: printf("ip6_output (ipsec): error code %d\n", error); /* fall through */ case ENOENT: /* don't show these error codes to the user */ break; } ip6stat.ip6s_cantforward++; if (mcopy) { #if 0 /* XXX: what icmp ? */ #else m_freem(mcopy); #endif } m_freem(m); return (NULL); } } skip_ipsec: #endif /* IPSEC */ dst = (struct sockaddr_in6 *)&ip6forward_rt->ro_dst; if ((rt = ip6forward_rt->ro_rt) != NULL) { RT_LOCK(rt); /* Take an extra ref for ourselves */ RT_ADDREF_LOCKED(rt); } if (!srcrt) { /* * ip6forward_rt->ro_dst.sin6_addr is equal to ip6->ip6_dst */ if (rt == NULL || !(rt->rt_flags & RTF_UP) || rt->generation_id != route_generation) { if (rt != NULL) { /* Release extra ref */ RT_REMREF_LOCKED(rt); RT_UNLOCK(rt); rtfree(rt); ip6forward_rt->ro_rt = NULL; } /* this probably fails but give it a try again */ rtalloc_scoped_ign((struct route *)ip6forward_rt, RTF_PRCLONING, ifscope); if ((rt = ip6forward_rt->ro_rt) != NULL) { RT_LOCK(rt); /* Take an extra ref for ourselves */ RT_ADDREF_LOCKED(rt); } } if (rt == NULL) { ip6stat.ip6s_noroute++; in6_ifstat_inc(m->m_pkthdr.rcvif, ifs6_in_noroute); if (mcopy) icmp6_error(mcopy, ICMP6_DST_UNREACH, ICMP6_DST_UNREACH_NOROUTE, 0); m_freem(m); return (NULL); } RT_LOCK_ASSERT_HELD(rt); } else if (rt == NULL || !(rt->rt_flags & RTF_UP) || !IN6_ARE_ADDR_EQUAL(&ip6->ip6_dst, &dst->sin6_addr) || rt->generation_id != route_generation) { if (rt != NULL) { /* Release extra ref */ RT_REMREF_LOCKED(rt); RT_UNLOCK(rt); rtfree(rt); ip6forward_rt->ro_rt = NULL; } bzero(dst, sizeof(*dst)); dst->sin6_len = sizeof(struct sockaddr_in6); dst->sin6_family = AF_INET6; dst->sin6_addr = ip6->ip6_dst; rtalloc_scoped_ign((struct route *)ip6forward_rt, RTF_PRCLONING, ifscope); if ((rt = ip6forward_rt->ro_rt) == NULL) { ip6stat.ip6s_noroute++; in6_ifstat_inc(m->m_pkthdr.rcvif, ifs6_in_noroute); if (mcopy) icmp6_error(mcopy, ICMP6_DST_UNREACH, ICMP6_DST_UNREACH_NOROUTE, 0); m_freem(m); return (NULL); } RT_LOCK(rt); /* Take an extra ref for ourselves */ RT_ADDREF_LOCKED(rt); } /* * Source scope check: if a packet can't be delivered to its * destination for the reason that the destination is beyond the scope * of the source address, discard the packet and return an icmp6 * destination unreachable error with Code 2 (beyond scope of source * address) unless we are proxying (source address is link local * for NUDs.) We use a local copy of ip6_src, since in6_setscope() * will possibly modify its first argument. * [draft-ietf-ipngwg-icmp-v3-04.txt, Section 3.1] */ src_in6 = ip6->ip6_src; if (in6_setscope(&src_in6, rt->rt_ifp, &outzone)) { /* XXX: this should not happen */ ip6stat.ip6s_cantforward++; ip6stat.ip6s_badscope++; m_freem(m); return (NULL); } if (in6_setscope(&src_in6, m->m_pkthdr.rcvif, &inzone)) { ip6stat.ip6s_cantforward++; ip6stat.ip6s_badscope++; m_freem(m); return (NULL); } if (inzone != outzone && !proxy) { ip6stat.ip6s_cantforward++; ip6stat.ip6s_badscope++; in6_ifstat_inc(rt->rt_ifp, ifs6_in_discard); if (ip6_log_time + ip6_log_interval < timenow.tv_sec) { ip6_log_time = timenow.tv_sec; log(LOG_DEBUG, "cannot forward " "src %s, dst %s, nxt %d, rcvif %s, outif %s\n", ip6_sprintf(&ip6->ip6_src), ip6_sprintf(&ip6->ip6_dst), ip6->ip6_nxt, if_name(m->m_pkthdr.rcvif), if_name(rt->rt_ifp)); } /* Release extra ref */ RT_REMREF_LOCKED(rt); RT_UNLOCK(rt); if (mcopy) { icmp6_error(mcopy, ICMP6_DST_UNREACH, ICMP6_DST_UNREACH_BEYONDSCOPE, 0); } m_freem(m); return (NULL); } /* * Destination scope check: if a packet is going to break the scope * zone of packet's destination address, discard it. This case should * usually be prevented by appropriately-configured routing table, but * we need an explicit check because we may mistakenly forward the * packet to a different zone by (e.g.) a default route. */ dst_in6 = ip6->ip6_dst; if (in6_setscope(&dst_in6, m->m_pkthdr.rcvif, &inzone) != 0 || in6_setscope(&dst_in6, rt->rt_ifp, &outzone) != 0 || inzone != outzone) { ip6stat.ip6s_cantforward++; ip6stat.ip6s_badscope++; m_freem(m); return (NULL); } if (m->m_pkthdr.len > rt->rt_ifp->if_mtu) { in6_ifstat_inc(rt->rt_ifp, ifs6_in_toobig); if (mcopy) { uint32_t mtu; #if IPSEC struct secpolicy *sp2; int ipsecerror; size_t ipsechdrsiz; #endif mtu = rt->rt_ifp->if_mtu; #if IPSEC /* * When we do IPsec tunnel ingress, we need to play * with the link value (decrement IPsec header size * from mtu value). The code is much simpler than v4 * case, as we have the outgoing interface for * encapsulated packet as "rt->rt_ifp". */ sp2 = ipsec6_getpolicybyaddr(mcopy, IPSEC_DIR_OUTBOUND, IP_FORWARDING, &ipsecerror); if (sp2) { ipsechdrsiz = ipsec6_hdrsiz(mcopy, IPSEC_DIR_OUTBOUND, NULL); if (ipsechdrsiz < mtu) mtu -= ipsechdrsiz; key_freesp(sp2, KEY_SADB_UNLOCKED); } /* * if mtu becomes less than minimum MTU, * tell minimum MTU (and I'll need to fragment it). */ if (mtu < IPV6_MMTU) mtu = IPV6_MMTU; #endif /* Release extra ref */ RT_REMREF_LOCKED(rt); RT_UNLOCK(rt); icmp6_error(mcopy, ICMP6_PACKET_TOO_BIG, 0, mtu); } else { /* Release extra ref */ RT_REMREF_LOCKED(rt); RT_UNLOCK(rt); } m_freem(m); return (NULL); } if (rt->rt_flags & RTF_GATEWAY) dst = (struct sockaddr_in6 *)(void *)rt->rt_gateway; /* * If we are to forward the packet using the same interface * as one we got the packet from, perhaps we should send a redirect * to sender to shortcut a hop. * Only send redirect if source is sending directly to us, * and if packet was not source routed (or has any options). * Also, don't send redirect if forwarding using a route * modified by a redirect. */ if (!proxy && ip6_sendredirects && rt->rt_ifp == m->m_pkthdr.rcvif && !srcrt && (rt->rt_flags & (RTF_DYNAMIC|RTF_MODIFIED)) == 0) { if ((rt->rt_ifp->if_flags & IFF_POINTOPOINT) != 0) { /* * If the incoming interface is equal to the outgoing * one, and the link attached to the interface is * point-to-point, then it will be highly probable * that a routing loop occurs. Thus, we immediately * drop the packet and send an ICMPv6 error message. * * type/code is based on suggestion by Rich Draves. * not sure if it is the best pick. */ RT_REMREF_LOCKED(rt); /* Release extra ref */ RT_UNLOCK(rt); icmp6_error(mcopy, ICMP6_DST_UNREACH, ICMP6_DST_UNREACH_ADDR, 0); m_freem(m); return (NULL); } type = ND_REDIRECT; } #if IPFW2 /* * Check with the firewall... */ if (ip6_fw_enable && ip6_fw_chk_ptr) { u_short port = 0; ifp = rt->rt_ifp; /* Drop the lock but retain the extra ref */ RT_UNLOCK(rt); /* If ipfw says divert, we have to just drop packet */ if (ip6_fw_chk_ptr(&ip6, ifp, &port, &m)) { m_freem(m); goto freecopy; } if (!m) { goto freecopy; } /* We still have the extra ref on rt */ RT_LOCK(rt); } #endif /* * Fake scoped addresses. Note that even link-local source or * destinaion can appear, if the originating node just sends the * packet to us (without address resolution for the destination). * Since both icmp6_error and icmp6_redirect_output fill the embedded * link identifiers, we can do this stuff after making a copy for * returning an error. */ if ((rt->rt_ifp->if_flags & IFF_LOOPBACK) != 0) { /* * See corresponding comments in ip6_output. * XXX: but is it possible that ip6_forward() sends a packet * to a loopback interface? I don't think so, and thus * I bark here. ([email protected]) * XXX: it is common to route invalid packets to loopback. * also, the codepath will be visited on use of ::1 in * rthdr. (itojun) */ #if 1 if (0) #else if ((rt->rt_flags & (RTF_BLACKHOLE|RTF_REJECT)) == 0) #endif { printf("ip6_forward: outgoing interface is loopback. " "src %s, dst %s, nxt %d, rcvif %s, outif %s\n", ip6_sprintf(&ip6->ip6_src), ip6_sprintf(&ip6->ip6_dst), ip6->ip6_nxt, if_name(m->m_pkthdr.rcvif), if_name(rt->rt_ifp)); } /* we can just use rcvif in forwarding. */ origifp = m->m_pkthdr.rcvif; } else origifp = rt->rt_ifp; /* * clear embedded scope identifiers if necessary. * in6_clearscope will touch the addresses only when necessary. */ in6_clearscope(&ip6->ip6_src); in6_clearscope(&ip6->ip6_dst); ifp = rt->rt_ifp; /* Drop the lock but retain the extra ref */ RT_UNLOCK(rt); /* * If this is to be processed locally, let ip6_input have it. */ if (proxy) { VERIFY(m->m_pkthdr.aux_flags & MAUXF_PROXY_DST); /* Release extra ref */ RT_REMREF(rt); if (mcopy != NULL) m_freem(mcopy); return (m); } #if PF /* Invoke outbound packet filter */ error = pf_af_hook(ifp, NULL, &m, AF_INET6, FALSE, NULL); if (error != 0 || m == NULL) { if (m != NULL) { panic("%s: unexpected packet %p\n", __func__, m); /* NOTREACHED */ } /* Already freed by callee */ goto senderr; } ip6 = mtod(m, struct ip6_hdr *); #endif /* PF */ error = nd6_output(ifp, origifp, m, dst, rt, NULL); if (error) { in6_ifstat_inc(ifp, ifs6_out_discard); ip6stat.ip6s_cantforward++; } else { ip6stat.ip6s_forward++; in6_ifstat_inc(ifp, ifs6_out_forward); if (type) ip6stat.ip6s_redirectsent++; else { if (mcopy) { goto freecopy; } } } #if PF senderr: #endif /* PF */ if (mcopy == NULL) { /* Release extra ref */ RT_REMREF(rt); return (NULL); } switch (error) { case 0: #if 1 if (type == ND_REDIRECT) { icmp6_redirect_output(mcopy, rt); /* Release extra ref */ RT_REMREF(rt); return (NULL); } #endif goto freecopy; case EMSGSIZE: /* xxx MTU is constant in PPP? */ goto freecopy; case ENOBUFS: /* Tell source to slow down like source quench in IP? */ goto freecopy; case ENETUNREACH: /* shouldn't happen, checked above */ case EHOSTUNREACH: case ENETDOWN: case EHOSTDOWN: default: type = ICMP6_DST_UNREACH; code = ICMP6_DST_UNREACH_ADDR; break; } icmp6_error(mcopy, type, code, 0); /* Release extra ref */ RT_REMREF(rt); return (NULL); freecopy: m_freem(mcopy); /* Release extra ref */ RT_REMREF(rt); return (NULL); }
/* * Ethernet output routine. * Encapsulate a packet of type family for the local net. * Assumes that ifp is actually pointer to ethercom structure. */ int ssh_interceptor_ether_output(struct ifnet *ifp, struct mbuf *m0, struct sockaddr *dst, struct rtentry *rt0) { u_int16_t etype = 0; int s, error = 0, hdrcmplt = 0; u_char esrc[6], edst[6]; struct mbuf *m = m0; struct rtentry *rt; struct mbuf *mcopy = (struct mbuf *)0; struct ether_header *eh, ehd; #ifdef INET struct arphdr *ah; #endif /* INET */ #ifdef NETATALK struct at_ifaddr *aa; #endif /* NETATALK */ if ((ifp->if_flags & (IFF_UP|IFF_RUNNING)) != (IFF_UP|IFF_RUNNING)) senderr(ENETDOWN); ifp->if_lastchange = time; if ((rt = rt0) != NULL) { if ((rt->rt_flags & RTF_UP) == 0) { if ((rt0 = rt = rtalloc1(dst, 1)) != NULL) { rt->rt_refcnt--; if (rt->rt_ifp != ifp) return (*rt->rt_ifp->if_output) (ifp, m0, dst, rt); } else senderr(EHOSTUNREACH); } if ((rt->rt_flags & RTF_GATEWAY) && dst->sa_family != AF_NS) { 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, 1); if ((rt = rt->rt_gwroute) == 0) senderr(EHOSTUNREACH); /* the "G" test below also prevents rt == rt0 */ if ((rt->rt_flags & RTF_GATEWAY) || (rt->rt_ifp != ifp)) { rt->rt_refcnt--; rt0->rt_gwroute = 0; senderr(EHOSTUNREACH); } } } if (rt->rt_flags & RTF_REJECT) if (rt->rt_rmx.rmx_expire == 0 || time.tv_sec < rt->rt_rmx.rmx_expire) senderr(rt == rt0 ? EHOSTDOWN : EHOSTUNREACH); } switch (dst->sa_family) { #ifdef INET case AF_INET: if (m->m_flags & M_BCAST) bcopy((caddr_t)etherbroadcastaddr, (caddr_t)edst, sizeof(edst)); else if (m->m_flags & M_MCAST) { ETHER_MAP_IP_MULTICAST(&SIN(dst)->sin_addr, (caddr_t)edst) } else if (!arpresolve(ifp, 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)) mcopy = m_copy(m, 0, (int)M_COPYALL); etype = htons(ETHERTYPE_IP); break; case AF_ARP: ah = mtod(m, struct arphdr *); if (m->m_flags & M_BCAST) bcopy((caddr_t)etherbroadcastaddr, (caddr_t)edst, sizeof(edst)); else bcopy((caddr_t)ar_tha(ah), (caddr_t)edst, sizeof(edst)); ah->ar_hrd = htons(ARPHRD_ETHER); switch(ntohs(ah->ar_op)) { case ARPOP_REVREQUEST: case ARPOP_REVREPLY: etype = htons(ETHERTYPE_REVARP); break; case ARPOP_REQUEST: case ARPOP_REPLY: default: etype = htons(ETHERTYPE_ARP); } break; #endif #ifdef INET6 case AF_INET6: #ifdef OLDIP6OUTPUT if (!nd6_resolve(ifp, rt, m, dst, (u_char *)edst)) return(0); /* if not yet resolves */ #else if (!nd6_storelladdr(ifp, rt, m, dst, (u_char *)edst)){ /* this must be impossible, so we bark */ printf("nd6_storelladdr failed\n"); return(0); } #endif /* OLDIP6OUTPUT */ etype = htons(ETHERTYPE_IPV6); break; #endif #ifdef NETATALK case AF_APPLETALK: if (!aarpresolve(ifp, m, (struct sockaddr_at *)dst, edst)) { #ifdef NETATALKDEBUG printf("aarpresolv failed\n"); #endif /* NETATALKDEBUG */ return (0); } /* * ifaddr is the first thing in at_ifaddr */ aa = (struct at_ifaddr *) at_ifawithnet( (struct sockaddr_at *)dst, ifp); if (aa == NULL) goto bad; /* * In the phase 2 case, we need to prepend an mbuf for the * llc header. Since we must preserve the value of m, * which is passed to us by value, we m_copy() the first * mbuf, and use it for our llc header. */ if (aa->aa_flags & AFA_PHASE2) { struct llc llc; M_PREPEND(m, sizeof(struct llc), M_DONTWAIT); llc.llc_dsap = llc.llc_ssap = LLC_SNAP_LSAP; llc.llc_control = LLC_UI; bcopy(at_org_code, llc.llc_snap_org_code, sizeof(llc.llc_snap_org_code)); llc.llc_snap_ether_type = htons(ETHERTYPE_ATALK); bcopy(&llc, mtod(m, caddr_t), sizeof(struct llc)); } else { etype = htons(ETHERTYPE_ATALK); } break; #endif /* NETATALK */ #ifdef NS case AF_NS: etype = htons(ETHERTYPE_NS); bcopy((caddr_t)&(((struct sockaddr_ns *)dst)->sns_addr.x_host), (caddr_t)edst, sizeof (edst)); if (!bcmp((caddr_t)edst, (caddr_t)&ns_thishost, sizeof(edst))) return (looutput(ifp, m, dst, rt)); /* If broadcasting on a simplex interface, loopback a copy */ if ((m->m_flags & M_BCAST) && (ifp->if_flags & IFF_SIMPLEX)) mcopy = m_copy(m, 0, (int)M_COPYALL); break; #endif #ifdef IPX case AF_IPX: etype = htons(ETHERTYPE_IPX); bcopy((caddr_t)&(((struct sockaddr_ipx *)dst)->sipx_addr.x_host), (caddr_t)edst, sizeof (edst)); /* If broadcasting on a simplex interface, loopback a copy */ if ((m->m_flags & M_BCAST) && (ifp->if_flags & IFF_SIMPLEX)) mcopy = m_copy(m, 0, (int)M_COPYALL); break; #endif #ifdef ISO case AF_ISO: { int snpalen; struct llc *l; struct sockaddr_dl *sdl; if (rt && (sdl = (struct sockaddr_dl *)rt->rt_gateway) && sdl->sdl_family == AF_LINK && sdl->sdl_alen > 0) { bcopy(LLADDR(sdl), (caddr_t)edst, sizeof(edst)); } else { error = iso_snparesolve(ifp, (struct sockaddr_iso *)dst, (char *)edst, &snpalen); if (error) goto bad; /* Not Resolved */ } /* If broadcasting on a simplex interface, loopback a copy */ if (*edst & 1) m->m_flags |= (M_BCAST|M_MCAST); if ((m->m_flags & M_BCAST) && (ifp->if_flags & IFF_SIMPLEX) && (mcopy = m_copy(m, 0, (int)M_COPYALL))) { M_PREPEND(mcopy, sizeof (*eh), M_DONTWAIT); if (mcopy) { eh = mtod(mcopy, struct ether_header *); bcopy((caddr_t)edst, (caddr_t)eh->ether_dhost, sizeof (edst)); bcopy(LLADDR(ifp->if_sadl), (caddr_t)eh->ether_shost, sizeof (edst)); } } M_PREPEND(m, 3, M_DONTWAIT); if (m == NULL) return (0); l = mtod(m, struct llc *); l->llc_dsap = l->llc_ssap = LLC_ISO_LSAP; l->llc_control = LLC_UI; #ifdef ARGO_DEBUG if (argo_debug[D_ETHER]) { int i; printf("unoutput: sending pkt to: "); for (i=0; i<6; i++) printf("%x ", edst[i] & 0xff); printf("\n"); } #endif } break; #endif /* ISO */ #ifdef LLC /* case AF_NSAP: */ case AF_CCITT: { struct sockaddr_dl *sdl = (struct sockaddr_dl *) rt -> rt_gateway; if (sdl && sdl->sdl_family == AF_LINK && sdl->sdl_alen > 0) { bcopy(LLADDR(sdl), (char *)edst, sizeof(edst)); } else goto bad; /* Not a link interface ? Funny ... */ if ((ifp->if_flags & IFF_SIMPLEX) && (*edst & 1) && (mcopy = m_copy(m, 0, (int)M_COPYALL))) { M_PREPEND(mcopy, sizeof (*eh), M_DONTWAIT); if (mcopy) { eh = mtod(mcopy, struct ether_header *); bcopy((caddr_t)edst, (caddr_t)eh->ether_dhost, sizeof (edst)); bcopy(LLADDR(ifp->if_sadl), (caddr_t)eh->ether_shost, sizeof (edst)); } } #ifdef LLC_DEBUG { int i; struct llc *l = mtod(m, struct llc *); printf("ether_output: sending LLC2 pkt to: "); for (i=0; i<6; i++) printf("%x ", edst[i] & 0xff); printf(" len 0x%x dsap 0x%x ssap 0x%x control 0x%x\n", m->m_pkthdr.len, l->llc_dsap & 0xff, l->llc_ssap &0xff, l->llc_control & 0xff); } #endif /* LLC_DEBUG */ } break;
int rtrequest1(int req, struct rt_addrinfo *info, struct rtentry **ret_nrt, u_int tableid) { int s = splsoftnet(); int error = 0; struct rtentry *rt, *crt; struct radix_node *rn; struct radix_node_head *rnh; struct ifaddr *ifa; struct sockaddr *ndst; struct sockaddr_rtlabel *sa_rl; #define senderr(x) { error = x ; goto bad; } if ((rnh = rt_gettable(info->rti_info[RTAX_DST]->sa_family, tableid)) == NULL) senderr(EAFNOSUPPORT); if (info->rti_flags & RTF_HOST) info->rti_info[RTAX_NETMASK] = NULL; switch (req) { case RTM_DELETE: if ((rn = rnh->rnh_lookup(info->rti_info[RTAX_DST], info->rti_info[RTAX_NETMASK], rnh)) == NULL) senderr(ESRCH); rt = (struct rtentry *)rn; #ifndef SMALL_KERNEL /* * if we got multipath routes, we require users to specify * a matching RTAX_GATEWAY. */ if (rn_mpath_capable(rnh)) { rt = rt_mpath_matchgate(rt, info->rti_info[RTAX_GATEWAY]); rn = (struct radix_node *)rt; if (!rt) senderr(ESRCH); } #endif if ((rn = rnh->rnh_deladdr(info->rti_info[RTAX_DST], info->rti_info[RTAX_NETMASK], rnh, rn)) == NULL) senderr(ESRCH); rt = (struct rtentry *)rn; /* clean up any cloned children */ if ((rt->rt_flags & RTF_CLONING) != 0) rtflushclone(rnh, rt); if (rn->rn_flags & (RNF_ACTIVE | RNF_ROOT)) panic ("rtrequest delete"); if (rt->rt_gwroute) { rt = rt->rt_gwroute; RTFREE(rt); (rt = (struct rtentry *)rn)->rt_gwroute = NULL; } if (rt->rt_parent) { rt->rt_parent->rt_refcnt--; rt->rt_parent = NULL; } #ifndef SMALL_KERNEL if (rn_mpath_capable(rnh)) { if ((rn = rnh->rnh_lookup(info->rti_info[RTAX_DST], info->rti_info[RTAX_NETMASK], rnh)) != NULL && rn_mpath_next(rn) == NULL) ((struct rtentry *)rn)->rt_flags &= ~RTF_MPATH; } #endif rt->rt_flags &= ~RTF_UP; if ((ifa = rt->rt_ifa) && ifa->ifa_rtrequest) ifa->ifa_rtrequest(RTM_DELETE, rt, info); rttrash++; if (ret_nrt) *ret_nrt = rt; else if (rt->rt_refcnt <= 0) { rt->rt_refcnt++; rtfree(rt); } break; case RTM_RESOLVE: if (ret_nrt == NULL || (rt = *ret_nrt) == NULL) senderr(EINVAL); if ((rt->rt_flags & RTF_CLONING) == 0) senderr(EINVAL); ifa = rt->rt_ifa; info->rti_flags = rt->rt_flags & ~(RTF_CLONING | RTF_STATIC); info->rti_flags |= RTF_CLONED; info->rti_info[RTAX_GATEWAY] = rt->rt_gateway; if ((info->rti_info[RTAX_NETMASK] = rt->rt_genmask) == NULL) info->rti_flags |= RTF_HOST; goto makeroute; case RTM_ADD: if (info->rti_ifa == 0 && (error = rt_getifa(info))) senderr(error); ifa = info->rti_ifa; makeroute: rt = pool_get(&rtentry_pool, PR_NOWAIT); if (rt == NULL) senderr(ENOBUFS); Bzero(rt, sizeof(*rt)); rt->rt_flags = RTF_UP | info->rti_flags; LIST_INIT(&rt->rt_timer); if (rt_setgate(rt, info->rti_info[RTAX_DST], info->rti_info[RTAX_GATEWAY], tableid)) { pool_put(&rtentry_pool, rt); senderr(ENOBUFS); } ndst = rt_key(rt); if (info->rti_info[RTAX_NETMASK] != NULL) { rt_maskedcopy(info->rti_info[RTAX_DST], ndst, info->rti_info[RTAX_NETMASK]); } else Bcopy(info->rti_info[RTAX_DST], ndst, info->rti_info[RTAX_DST]->sa_len); #ifndef SMALL_KERNEL /* do not permit exactly the same dst/mask/gw pair */ if (rn_mpath_capable(rnh) && rt_mpath_conflict(rnh, rt, info->rti_info[RTAX_NETMASK], info->rti_flags & RTF_MPATH)) { if (rt->rt_gwroute) rtfree(rt->rt_gwroute); Free(rt_key(rt)); pool_put(&rtentry_pool, rt); senderr(EEXIST); } #endif if (info->rti_info[RTAX_LABEL] != NULL) { sa_rl = (struct sockaddr_rtlabel *) info->rti_info[RTAX_LABEL]; rt->rt_labelid = rtlabel_name2id(sa_rl->sr_label); } ifa->ifa_refcnt++; rt->rt_ifa = ifa; rt->rt_ifp = ifa->ifa_ifp; if (req == RTM_RESOLVE) { /* * Copy both metrics and a back pointer to the cloned * route's parent. */ rt->rt_rmx = (*ret_nrt)->rt_rmx; /* copy metrics */ rt->rt_parent = *ret_nrt; /* Back ptr. to parent. */ rt->rt_parent->rt_refcnt++; } rn = rnh->rnh_addaddr((caddr_t)ndst, (caddr_t)info->rti_info[RTAX_NETMASK], rnh, rt->rt_nodes); if (rn == NULL && (crt = rtalloc1(ndst, 0, tableid)) != NULL) { /* overwrite cloned route */ if ((crt->rt_flags & RTF_CLONED) != 0) { rtdeletemsg(crt, tableid); rn = rnh->rnh_addaddr((caddr_t)ndst, (caddr_t)info->rti_info[RTAX_NETMASK], rnh, rt->rt_nodes); } RTFREE(crt); } if (rn == 0) { IFAFREE(ifa); if ((rt->rt_flags & RTF_CLONED) != 0 && rt->rt_parent) rtfree(rt->rt_parent); if (rt->rt_gwroute) rtfree(rt->rt_gwroute); Free(rt_key(rt)); pool_put(&rtentry_pool, rt); senderr(EEXIST); } #ifndef SMALL_KERNEL if (rn_mpath_capable(rnh) && (rn = rnh->rnh_lookup(info->rti_info[RTAX_DST], info->rti_info[RTAX_NETMASK], rnh)) != NULL) { if (rn_mpath_next(rn) == NULL) ((struct rtentry *)rn)->rt_flags &= ~RTF_MPATH; else ((struct rtentry *)rn)->rt_flags |= RTF_MPATH; } #endif if (ifa->ifa_rtrequest) ifa->ifa_rtrequest(req, rt, info); if (ret_nrt) { *ret_nrt = rt; rt->rt_refcnt++; } if ((rt->rt_flags & RTF_CLONING) != 0) { /* clean up any cloned children */ rtflushclone(rnh, rt); } if_group_routechange(info->rti_info[RTAX_DST], info->rti_info[RTAX_NETMASK]); break; } bad: splx(s); 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); }
static int in6_selectif(struct sockaddr_in6 *dstsock, struct ip6_pktopts *opts, struct ip6_moptions *mopts, struct route_in6 *ro, unsigned int ifscope, unsigned int nocell, struct ifnet **retifp) { int error; struct route_in6 sro; struct rtentry *rt = NULL; if (ro == NULL) { bzero(&sro, sizeof(sro)); ro = &sro; } if ((error = selectroute(NULL, dstsock, opts, mopts, ro, retifp, &rt, 0, 1, ifscope, nocell)) != 0) { if (ro == &sro && rt && rt == sro.ro_rt) rtfree(rt); return (error); } /* * do not use a rejected or black hole route. * XXX: this check should be done in the L2 output routine. * However, if we skipped this check here, we'd see the following * scenario: * - install a rejected route for a scoped address prefix * (like fe80::/10) * - send a packet to a destination that matches the scoped prefix, * with ambiguity about the scope zone. * - pick the outgoing interface from the route, and disambiguate the * scope zone with the interface. * - ip6_output() would try to get another route with the "new" * destination, which may be valid. * - we'd see no error on output. * Although this may not be very harmful, it should still be confusing. * We thus reject the case here. */ if (rt && (rt->rt_flags & (RTF_REJECT | RTF_BLACKHOLE))) { int flags = (rt->rt_flags & RTF_HOST ? EHOSTUNREACH : ENETUNREACH); if (ro == &sro && rt && rt == sro.ro_rt) rtfree(rt); return (flags); } /* * Adjust the "outgoing" interface. If we're going to loop the packet * back to ourselves, the ifp would be the loopback interface. * However, we'd rather know the interface associated to the * destination address (which should probably be one of our own * addresses.) */ if (rt && rt->rt_ifa && rt->rt_ifa->ifa_ifp) { if (*retifp != NULL) ifnet_release(*retifp); *retifp = rt->rt_ifa->ifa_ifp; ifnet_reference(*retifp); } if (ro == &sro && rt && rt == sro.ro_rt) rtfree(rt); return (0); }
int rtrequest1(int req, struct rt_addrinfo *info, u_int8_t prio, struct rtentry **ret_nrt, u_int tableid) { int s = splsoftnet(); int error = 0; struct rtentry *rt, *crt; struct radix_node *rn; struct radix_node_head *rnh; struct ifaddr *ifa; struct sockaddr *ndst; struct sockaddr_rtlabel *sa_rl, sa_rl2; #ifdef MPLS struct sockaddr_mpls *sa_mpls; #endif #define senderr(x) { error = x ; goto bad; } if ((rnh = rt_gettable(info->rti_info[RTAX_DST]->sa_family, tableid)) == NULL) senderr(EAFNOSUPPORT); if (info->rti_flags & RTF_HOST) info->rti_info[RTAX_NETMASK] = NULL; switch (req) { case RTM_DELETE: if ((rn = rnh->rnh_lookup(info->rti_info[RTAX_DST], info->rti_info[RTAX_NETMASK], rnh)) == NULL) senderr(ESRCH); rt = (struct rtentry *)rn; #ifndef SMALL_KERNEL /* * if we got multipath routes, we require users to specify * a matching RTAX_GATEWAY. */ if (rn_mpath_capable(rnh)) { rt = rt_mpath_matchgate(rt, info->rti_info[RTAX_GATEWAY], prio); rn = (struct radix_node *)rt; if (!rt) senderr(ESRCH); } #endif if ((rn = rnh->rnh_deladdr(info->rti_info[RTAX_DST], info->rti_info[RTAX_NETMASK], rnh, rn)) == NULL) senderr(ESRCH); rt = (struct rtentry *)rn; /* clean up any cloned children */ if ((rt->rt_flags & RTF_CLONING) != 0) rtflushclone(rnh, rt); if (rn->rn_flags & (RNF_ACTIVE | RNF_ROOT)) panic ("rtrequest delete"); if (rt->rt_gwroute) { rt = rt->rt_gwroute; RTFREE(rt); (rt = (struct rtentry *)rn)->rt_gwroute = NULL; } if (rt->rt_parent) { rt->rt_parent->rt_refcnt--; rt->rt_parent = NULL; } #ifndef SMALL_KERNEL if (rn_mpath_capable(rnh)) { if ((rn = rnh->rnh_lookup(info->rti_info[RTAX_DST], info->rti_info[RTAX_NETMASK], rnh)) != NULL && rn_mpath_next(rn, 0) == NULL) ((struct rtentry *)rn)->rt_flags &= ~RTF_MPATH; } #endif rt->rt_flags &= ~RTF_UP; if ((ifa = rt->rt_ifa) && ifa->ifa_rtrequest) ifa->ifa_rtrequest(RTM_DELETE, rt, info); rttrash++; if (ret_nrt) *ret_nrt = rt; else if (rt->rt_refcnt <= 0) { rt->rt_refcnt++; rtfree(rt); } break; case RTM_RESOLVE: if (ret_nrt == NULL || (rt = *ret_nrt) == NULL) senderr(EINVAL); if ((rt->rt_flags & RTF_CLONING) == 0) senderr(EINVAL); ifa = rt->rt_ifa; info->rti_flags = rt->rt_flags & ~(RTF_CLONING | RTF_STATIC); info->rti_flags |= RTF_CLONED; info->rti_info[RTAX_GATEWAY] = rt->rt_gateway; if ((info->rti_info[RTAX_NETMASK] = rt->rt_genmask) == NULL) info->rti_flags |= RTF_HOST; info->rti_info[RTAX_LABEL] = rtlabel_id2sa(rt->rt_labelid, &sa_rl2); goto makeroute; case RTM_ADD: if (info->rti_ifa == 0 && (error = rt_getifa(info, tableid))) senderr(error); ifa = info->rti_ifa; makeroute: rt = pool_get(&rtentry_pool, PR_NOWAIT | PR_ZERO); if (rt == NULL) senderr(ENOBUFS); rt->rt_flags = info->rti_flags; if (prio == 0) prio = ifa->ifa_ifp->if_priority + RTP_STATIC; rt->rt_priority = prio; /* init routing priority */ if ((LINK_STATE_IS_UP(ifa->ifa_ifp->if_link_state) || ifa->ifa_ifp->if_link_state == LINK_STATE_UNKNOWN) && ifa->ifa_ifp->if_flags & IFF_UP) rt->rt_flags |= RTF_UP; else { rt->rt_flags &= ~RTF_UP; rt->rt_priority |= RTP_DOWN; } LIST_INIT(&rt->rt_timer); if (rt_setgate(rt, info->rti_info[RTAX_DST], info->rti_info[RTAX_GATEWAY], tableid)) { pool_put(&rtentry_pool, rt); senderr(ENOBUFS); } ndst = rt_key(rt); if (info->rti_info[RTAX_NETMASK] != NULL) { rt_maskedcopy(info->rti_info[RTAX_DST], ndst, info->rti_info[RTAX_NETMASK]); } else Bcopy(info->rti_info[RTAX_DST], ndst, info->rti_info[RTAX_DST]->sa_len); #ifndef SMALL_KERNEL /* do not permit exactly the same dst/mask/gw pair */ if (rn_mpath_capable(rnh) && rt_mpath_conflict(rnh, rt, info->rti_info[RTAX_NETMASK], info->rti_flags & RTF_MPATH)) { if (rt->rt_gwroute) rtfree(rt->rt_gwroute); Free(rt_key(rt)); pool_put(&rtentry_pool, rt); senderr(EEXIST); } #endif if (info->rti_info[RTAX_LABEL] != NULL) { sa_rl = (struct sockaddr_rtlabel *) info->rti_info[RTAX_LABEL]; rt->rt_labelid = rtlabel_name2id(sa_rl->sr_label); } #ifdef MPLS /* We have to allocate additional space for MPLS infos */ if (info->rti_info[RTAX_SRC] != NULL || info->rti_info[RTAX_DST]->sa_family == AF_MPLS) { struct rt_mpls *rt_mpls; sa_mpls = (struct sockaddr_mpls *) info->rti_info[RTAX_SRC]; rt->rt_llinfo = (caddr_t)malloc(sizeof(struct rt_mpls), M_TEMP, M_NOWAIT|M_ZERO); if (rt->rt_llinfo == NULL) { if (rt->rt_gwroute) rtfree(rt->rt_gwroute); Free(rt_key(rt)); pool_put(&rtentry_pool, rt); senderr(ENOMEM); } rt_mpls = (struct rt_mpls *)rt->rt_llinfo; if (sa_mpls != NULL) rt_mpls->mpls_label = sa_mpls->smpls_label; rt_mpls->mpls_operation = info->rti_mpls; /* XXX: set experimental bits */ rt->rt_flags |= RTF_MPLS; } #endif ifa->ifa_refcnt++; rt->rt_ifa = ifa; rt->rt_ifp = ifa->ifa_ifp; if (req == RTM_RESOLVE) { /* * Copy both metrics and a back pointer to the cloned * route's parent. */ rt->rt_rmx = (*ret_nrt)->rt_rmx; /* copy metrics */ rt->rt_priority = (*ret_nrt)->rt_priority; rt->rt_parent = *ret_nrt; /* Back ptr. to parent. */ rt->rt_parent->rt_refcnt++; } rn = rnh->rnh_addaddr((caddr_t)ndst, (caddr_t)info->rti_info[RTAX_NETMASK], rnh, rt->rt_nodes, rt->rt_priority); if (rn == NULL && (crt = rtalloc1(ndst, 0, tableid)) != NULL) { /* overwrite cloned route */ if ((crt->rt_flags & RTF_CLONED) != 0) { rtdeletemsg(crt, tableid); rn = rnh->rnh_addaddr((caddr_t)ndst, (caddr_t)info->rti_info[RTAX_NETMASK], rnh, rt->rt_nodes, rt->rt_priority); } RTFREE(crt); } if (rn == 0) { IFAFREE(ifa); if ((rt->rt_flags & RTF_CLONED) != 0 && rt->rt_parent) rtfree(rt->rt_parent); if (rt->rt_gwroute) rtfree(rt->rt_gwroute); Free(rt_key(rt)); pool_put(&rtentry_pool, rt); senderr(EEXIST); } #ifndef SMALL_KERNEL if (rn_mpath_capable(rnh) && (rn = rnh->rnh_lookup(info->rti_info[RTAX_DST], info->rti_info[RTAX_NETMASK], rnh)) != NULL && (rn = rn_mpath_prio(rn, prio)) != NULL) { if (rn_mpath_next(rn, 0) == NULL) ((struct rtentry *)rn)->rt_flags &= ~RTF_MPATH; else ((struct rtentry *)rn)->rt_flags |= RTF_MPATH; } #endif if (ifa->ifa_rtrequest) ifa->ifa_rtrequest(req, rt, info); if (ret_nrt) { *ret_nrt = rt; rt->rt_refcnt++; } if ((rt->rt_flags & RTF_CLONING) != 0) { /* clean up any cloned children */ rtflushclone(rnh, rt); } if_group_routechange(info->rti_info[RTAX_DST], info->rti_info[RTAX_NETMASK]); break; } bad: splx(s); return (error); }
/* * Given a source IPv6 address (and route, if available), determine the best * interface to send the packet from. Checking for (and updating) the * ROF_SRCIF_SELECTED flag in the pcb-supplied route placeholder is done * without any locks, based on the assumption that in the event this is * called from ip6_output(), the output operation is single-threaded per-pcb, * i.e. for any given pcb there can only be one thread performing output at * the IPv6 layer. * * This routine is analogous to in_selectsrcif() for IPv4. * * clone - meaningful only for bsdi and freebsd */ static int selectroute(struct sockaddr_in6 *srcsock, struct sockaddr_in6 *dstsock, struct ip6_pktopts *opts, struct ip6_moptions *mopts, struct route_in6 *ro, struct ifnet **retifp, struct rtentry **retrt, int clone, int norouteok, unsigned int ifscope, unsigned int nocell) { int error = 0; struct ifnet *ifp = NULL; struct route_in6 *route = NULL; struct sockaddr_in6 *sin6_next; struct in6_pktinfo *pi = NULL; struct in6_addr *dst = &dstsock->sin6_addr; struct ifaddr *ifa = NULL; char s_src[MAX_IPv6_STR_LEN], s_dst[MAX_IPv6_STR_LEN]; boolean_t select_srcif; #if 0 char ip6buf[INET6_ADDRSTRLEN]; if (dstsock->sin6_addr.s6_addr32[0] == 0 && dstsock->sin6_addr.s6_addr32[1] == 0 && !IN6_IS_ADDR_LOOPBACK(&dstsock->sin6_addr)) { printf("in6_selectroute: strange destination %s\n", ip6_sprintf(ip6buf, &dstsock->sin6_addr)); } else { printf("in6_selectroute: destination = %s%%%d\n", ip6_sprintf(ip6buf, &dstsock->sin6_addr), dstsock->sin6_scope_id); /* for debug */ } #endif if (retifp != NULL) *retifp = NULL; if (retrt != NULL) *retrt = NULL; if (ip6_select_srcif_debug) { struct in6_addr src; src = (srcsock != NULL) ? srcsock->sin6_addr : in6addr_any; (void) inet_ntop(AF_INET6, &src, s_src, sizeof (s_src)); (void) inet_ntop(AF_INET6, dst, s_dst, sizeof (s_dst)); } /* * If the destination address is UNSPECIFIED addr, bail out. */ if (IN6_IS_ADDR_UNSPECIFIED(dst)) { error = EHOSTUNREACH; goto done; } /* * Perform source interface selection only if Scoped Routing * is enabled and a source address that isn't unspecified. */ select_srcif = (ip6_doscopedroute && srcsock != NULL && !IN6_IS_ADDR_UNSPECIFIED(&srcsock->sin6_addr)); /* * If Scoped Routing is disabled, ignore the given ifscope. * Otherwise even if source selection won't be performed, * we still obey IPV6_BOUND_IF. */ if (!ip6_doscopedroute && ifscope != IFSCOPE_NONE) ifscope = IFSCOPE_NONE; /* If the caller specified the outgoing interface explicitly, use it */ if (opts != NULL && (pi = opts->ip6po_pktinfo) != NULL && pi->ipi6_ifindex != 0) { /* * If IPV6_PKTINFO takes precedence over IPV6_BOUND_IF. */ ifscope = pi->ipi6_ifindex; ifnet_head_lock_shared(); /* ifp may be NULL if detached or out of range */ ifp = (ifscope <= if_index) ? ifindex2ifnet[ifscope] : NULL; ifnet_head_done(); if (norouteok || retrt == NULL || IN6_IS_ADDR_MULTICAST(dst)) { /* * We do not have to check or get the route for * multicast. If the caller didn't ask/care for * the route and we have no interface to use, * it's an error. */ if (ifp == NULL) error = EHOSTUNREACH; goto done; } else { goto getsrcif; } } /* * If the destination address is a multicast address and the outgoing * interface for the address is specified by the caller, use it. */ if (IN6_IS_ADDR_MULTICAST(dst) && mopts != NULL) { IM6O_LOCK(mopts); if ((ifp = mopts->im6o_multicast_ifp) != NULL) { IM6O_UNLOCK(mopts); goto done; /* we do not need a route for multicast. */ } IM6O_UNLOCK(mopts); } getsrcif: /* * If the outgoing interface was not set via IPV6_BOUND_IF or * IPV6_PKTINFO, use the scope ID in the destination address. */ if (ip6_doscopedroute && ifscope == IFSCOPE_NONE) ifscope = dstsock->sin6_scope_id; /* * Perform source interface selection; the source IPv6 address * must belong to one of the addresses of the interface used * by the route. For performance reasons, do this only if * there is no route, or if the routing table has changed, * or if we haven't done source interface selection on this * route (for this PCB instance) before. */ if (!select_srcif || (ro != NULL && ro->ro_rt != NULL && (ro->ro_rt->rt_flags & RTF_UP) && ro->ro_rt->generation_id == route_generation && (ro->ro_flags & ROF_SRCIF_SELECTED))) { if (ro != NULL && ro->ro_rt != NULL) { ifa = ro->ro_rt->rt_ifa; IFA_ADDREF(ifa); } goto getroute; } /* * Given the source IPv6 address, find a suitable source interface * to use for transmission; if a scope ID has been specified, * optimize the search by looking at the addresses only for that * interface. This is still suboptimal, however, as we need to * traverse the per-interface list. */ if (ifscope != IFSCOPE_NONE || (ro != NULL && ro->ro_rt != NULL)) { unsigned int scope = ifscope; struct ifnet *rt_ifp; rt_ifp = (ro->ro_rt != NULL) ? ro->ro_rt->rt_ifp : NULL; /* * If no scope is specified and the route is stale (pointing * to a defunct interface) use the current primary interface; * this happens when switching between interfaces configured * with the same IPv6 address. Otherwise pick up the scope * information from the route; the ULP may have looked up a * correct route and we just need to verify it here and mark * it with the ROF_SRCIF_SELECTED flag below. */ if (scope == IFSCOPE_NONE) { scope = rt_ifp->if_index; if (scope != get_primary_ifscope(AF_INET6) && ro->ro_rt->generation_id != route_generation) scope = get_primary_ifscope(AF_INET6); } ifa = (struct ifaddr *) ifa_foraddr6_scoped(&srcsock->sin6_addr, scope); if (ip6_select_srcif_debug && ifa != NULL) { if (ro->ro_rt != NULL) { printf("%s->%s ifscope %d->%d ifa_if %s " "ro_if %s\n", s_src, s_dst, ifscope, scope, if_name(ifa->ifa_ifp), if_name(rt_ifp)); } else { printf("%s->%s ifscope %d->%d ifa_if %s\n", s_src, s_dst, ifscope, scope, if_name(ifa->ifa_ifp)); } } } /* * Slow path; search for an interface having the corresponding source * IPv6 address if the scope was not specified by the caller, and: * * 1) There currently isn't any route, or, * 2) The interface used by the route does not own that source * IPv6 address; in this case, the route will get blown away * and we'll do a more specific scoped search using the newly * found interface. */ if (ifa == NULL && ifscope == IFSCOPE_NONE) { ifa = (struct ifaddr *)ifa_foraddr6(&srcsock->sin6_addr); if (ip6_select_srcif_debug && ifa != NULL) { printf("%s->%s ifscope %d ifa_if %s\n", s_src, s_dst, ifscope, if_name(ifa->ifa_ifp)); } } getroute: if (ifa != NULL) ifscope = ifa->ifa_ifp->if_index; /* * If the next hop address for the packet is specified by the caller, * use it as the gateway. */ if (opts != NULL && opts->ip6po_nexthop != NULL) { struct route_in6 *ron; sin6_next = satosin6(opts->ip6po_nexthop); /* at this moment, we only support AF_INET6 next hops */ if (sin6_next->sin6_family != AF_INET6) { error = EAFNOSUPPORT; /* or should we proceed? */ goto done; } /* * If the next hop is an IPv6 address, then the node identified * by that address must be a neighbor of the sending host. */ ron = &opts->ip6po_nextroute; if (ron->ro_rt != NULL) RT_LOCK(ron->ro_rt); if ((ron->ro_rt != NULL && ((ron->ro_rt->rt_flags & (RTF_UP | RTF_LLINFO)) != (RTF_UP | RTF_LLINFO) || ron->ro_rt->generation_id != route_generation || (select_srcif && (ifa == NULL || ifa->ifa_ifp != ron->ro_rt->rt_ifp)))) || !IN6_ARE_ADDR_EQUAL(&satosin6(&ron->ro_dst)->sin6_addr, &sin6_next->sin6_addr)) { if (ron->ro_rt != NULL) { RT_UNLOCK(ron->ro_rt); rtfree(ron->ro_rt); ron->ro_rt = NULL; } *satosin6(&ron->ro_dst) = *sin6_next; } if (ron->ro_rt == NULL) { rtalloc_scoped((struct route *)ron, ifscope); if (ron->ro_rt != NULL) RT_LOCK(ron->ro_rt); if (ron->ro_rt == NULL || !(ron->ro_rt->rt_flags & RTF_LLINFO) || !IN6_ARE_ADDR_EQUAL(&satosin6(rt_key(ron->ro_rt))-> sin6_addr, &sin6_next->sin6_addr)) { if (ron->ro_rt != NULL) { RT_UNLOCK(ron->ro_rt); rtfree(ron->ro_rt); ron->ro_rt = NULL; } error = EHOSTUNREACH; goto done; } } route = ron; ifp = ron->ro_rt->rt_ifp; /* * When cloning is required, try to allocate a route to the * destination so that the caller can store path MTU * information. */ if (!clone) { if (select_srcif) { /* Keep the route locked */ goto validateroute; } RT_UNLOCK(ron->ro_rt); goto done; } RT_UNLOCK(ron->ro_rt); } /* * Use a cached route if it exists and is valid, else try to allocate * a new one. Note that we should check the address family of the * cached destination, in case of sharing the cache with IPv4. */ if (ro == NULL) goto done; if (ro->ro_rt != NULL) RT_LOCK(ro->ro_rt); if (ro->ro_rt != NULL && (!(ro->ro_rt->rt_flags & RTF_UP) || satosin6(&ro->ro_dst)->sin6_family != AF_INET6 || ro->ro_rt->generation_id != route_generation || !IN6_ARE_ADDR_EQUAL(&satosin6(&ro->ro_dst)->sin6_addr, dst) || (select_srcif && (ifa == NULL || ifa->ifa_ifp != ro->ro_rt->rt_ifp)))) { RT_UNLOCK(ro->ro_rt); rtfree(ro->ro_rt); ro->ro_rt = NULL; } if (ro->ro_rt == NULL) { struct sockaddr_in6 *sa6; if (ro->ro_rt != NULL) RT_UNLOCK(ro->ro_rt); /* No route yet, so try to acquire one */ bzero(&ro->ro_dst, sizeof(struct sockaddr_in6)); sa6 = (struct sockaddr_in6 *)&ro->ro_dst; sa6->sin6_family = AF_INET6; sa6->sin6_len = sizeof(struct sockaddr_in6); sa6->sin6_addr = *dst; if (IN6_IS_ADDR_MULTICAST(dst)) { ro->ro_rt = rtalloc1_scoped( &((struct route *)ro)->ro_dst, 0, 0, ifscope); } else { rtalloc_scoped((struct route *)ro, ifscope); } if (ro->ro_rt != NULL) RT_LOCK(ro->ro_rt); } /* * Do not care about the result if we have the nexthop * explicitly specified (in case we're asked to clone.) */ if (opts != NULL && opts->ip6po_nexthop != NULL) { if (ro->ro_rt != NULL) RT_UNLOCK(ro->ro_rt); goto done; } if (ro->ro_rt != NULL) { RT_LOCK_ASSERT_HELD(ro->ro_rt); ifp = ro->ro_rt->rt_ifp; } else { error = EHOSTUNREACH; } route = ro; validateroute: if (select_srcif) { boolean_t has_route = (route != NULL && route->ro_rt != NULL); if (has_route) RT_LOCK_ASSERT_HELD(route->ro_rt); /* * If there is a non-loopback route with the wrong interface, * or if there is no interface configured with such an address, * blow it away. Except for local/loopback, we look for one * with a matching interface scope/index. */ if (has_route && (ifa == NULL || (ifa->ifa_ifp != ifp && ifp != lo_ifp) || !(route->ro_rt->rt_flags & RTF_UP))) { if (ip6_select_srcif_debug) { if (ifa != NULL) { printf("%s->%s ifscope %d ro_if %s " "!= ifa_if %s (cached route " "cleared)\n", s_src, s_dst, ifscope, if_name(ifp), if_name(ifa->ifa_ifp)); } else { printf("%s->%s ifscope %d ro_if %s " "(no ifa_if found)\n", s_src, s_dst, ifscope, if_name(ifp)); } } RT_UNLOCK(route->ro_rt); rtfree(route->ro_rt); route->ro_rt = NULL; route->ro_flags &= ~ROF_SRCIF_SELECTED; error = EHOSTUNREACH; /* Undo the settings done above */ route = NULL; ifp = NULL; } else if (has_route) { route->ro_flags |= ROF_SRCIF_SELECTED; route->ro_rt->generation_id = route_generation; RT_UNLOCK(route->ro_rt); } } else { if (ro->ro_rt != NULL) RT_UNLOCK(ro->ro_rt); if (ifp != NULL && opts != NULL && opts->ip6po_pktinfo != NULL && opts->ip6po_pktinfo->ipi6_ifindex != 0) { /* * Check if the outgoing interface conflicts with the * interface specified by ipi6_ifindex (if specified). * Note that loopback interface is always okay. * (this may happen when we are sending a packet to * one of our own addresses.) */ if (!(ifp->if_flags & IFF_LOOPBACK) && ifp->if_index != opts->ip6po_pktinfo->ipi6_ifindex) { error = EHOSTUNREACH; goto done; } } } done: if (nocell && error == 0) { if ((ifp != NULL && ifp->if_type == IFT_CELLULAR) || (route != NULL && route->ro_rt != NULL && route->ro_rt->rt_ifp->if_type == IFT_CELLULAR)) { if (route != NULL && route->ro_rt != NULL) { rtfree(route->ro_rt); route->ro_rt = NULL; route->ro_flags &= ~ROF_SRCIF_SELECTED; route = NULL; } ifp = NULL; error = EHOSTUNREACH; } } if (ifp == NULL && (route == NULL || route->ro_rt == NULL)) { /* * This can happen if the caller did not pass a cached route * nor any other hints. We treat this case an error. */ error = EHOSTUNREACH; } if (error == EHOSTUNREACH) ip6stat.ip6s_noroute++; if (error == 0) { if (retifp != NULL) { if (ifp != NULL) ifnet_reference(ifp); /* for caller */ *retifp = ifp; } if (retrt != NULL && route != NULL) *retrt = route->ro_rt; /* ro_rt may be NULL */ } else if (select_srcif && ip6_select_srcif_debug) { printf("%s->%s ifscope %d ifa_if %s ro_if %s (error=%d)\n", s_src, s_dst, ifscope, (ifa != NULL) ? if_name(ifa->ifa_ifp) : "NONE", (ifp != NULL) ? if_name(ifp) : "NONE", error); } if (ifa != NULL) IFA_REMREF(ifa); return (error); }
int ipx_outputfl(struct mbuf *m0, struct route *ro, int flags) { struct ipx *ipx = mtod(m0, struct ipx *); struct ifnet *ifp = NULL; int error = 0; struct sockaddr_ipx *dst; struct route ipxroute; /* * Route packet. */ if (ro == NULL) { ro = &ipxroute; bzero((caddr_t)ro, sizeof(*ro)); } dst = (struct sockaddr_ipx *)&ro->ro_dst; if (ro->ro_rt == NULL) { dst->sipx_family = AF_IPX; dst->sipx_len = sizeof(*dst); dst->sipx_addr = ipx->ipx_dna; dst->sipx_addr.x_port = 0; /* * If routing to interface only, * short circuit routing lookup. */ if (flags & IPX_ROUTETOIF) { struct ipx_ifaddr *ia = ipx_iaonnetof(&ipx->ipx_dna); if (ia == NULL) { ipxstat.ipxs_noroute++; error = ENETUNREACH; goto bad; } ifp = ia->ia_ifp; goto gotif; } rtalloc(ro); } else if ((ro->ro_rt->rt_flags & RTF_UP) == 0) { /* * The old route has gone away; try for a new one. */ rtfree(ro->ro_rt); ro->ro_rt = NULL; rtalloc(ro); } if (ro->ro_rt == NULL || (ifp = ro->ro_rt->rt_ifp) == NULL) { ipxstat.ipxs_noroute++; error = ENETUNREACH; goto bad; } ro->ro_rt->rt_use++; if (ro->ro_rt->rt_flags & (RTF_GATEWAY|RTF_HOST)) dst = (struct sockaddr_ipx *)ro->ro_rt->rt_gateway; gotif: /* * Look for multicast addresses and * and verify user is allowed to send * such a packet. */ if (dst->sipx_addr.x_host.c_host[0]&1) { if ((ifp->if_flags & (IFF_BROADCAST | IFF_LOOPBACK)) == 0) { error = EADDRNOTAVAIL; goto bad; } if ((flags & IPX_ALLOWBROADCAST) == 0) { error = EACCES; goto bad; } m0->m_flags |= M_BCAST; } if (htons(ipx->ipx_len) <= ifp->if_mtu) { ipxstat.ipxs_localout++; if (ipx_copy_output) { ipx_watch_output(m0, ifp); } error = ifp->if_output(ifp, m0, (struct sockaddr *)dst, ro->ro_rt); goto done; } else { ipxstat.ipxs_mtutoosmall++; error = EMSGSIZE; } bad: if (ipx_copy_output) { ipx_watch_output(m0, ifp); } m_freem(m0); done: if (ro == &ipxroute && (flags & IPX_ROUTETOIF) == 0 && ro->ro_rt != NULL) { RTFREE(ro->ro_rt); ro->ro_rt = NULL; } return (error); }
int ns_output(struct mbuf *m0, ...) { struct route *ro; int flags; struct idp *idp = mtod(m0, struct idp *); struct ifnet *ifp = 0; int error = 0; struct route idproute; struct sockaddr_ns *dst; va_list ap; va_start(ap, m0); ro = va_arg(ap, struct route *); flags = va_arg(ap, int); va_end(ap); if (ns_hold_output) { if (ns_lastout) { (void)m_free(ns_lastout); } ns_lastout = m_copy(m0, 0, (int)M_COPYALL); } /* * Route packet. */ if (ro == 0) { ro = &idproute; bzero((caddr_t)ro, sizeof (*ro)); } dst = satosns(&ro->ro_dst); if (ro->ro_rt == 0) { dst->sns_family = AF_NS; dst->sns_len = sizeof (*dst); dst->sns_addr = idp->idp_dna; dst->sns_addr.x_port = 0; /* * If routing to interface only, * short circuit routing lookup. */ if (flags & NS_ROUTETOIF) { struct ns_ifaddr *ia = ns_iaonnetof(&idp->idp_dna); if (ia == 0) { error = ENETUNREACH; goto bad; } ifp = ia->ia_ifp; goto gotif; } rtalloc(ro); } else if ((ro->ro_rt->rt_flags & RTF_UP) == 0) { /* * The old route has gone away; try for a new one. */ rtfree(ro->ro_rt); ro->ro_rt = NULL; rtalloc(ro); } if (ro->ro_rt == 0 || (ifp = ro->ro_rt->rt_ifp) == 0) { error = ENETUNREACH; goto bad; } ro->ro_rt->rt_use++; if (ro->ro_rt->rt_flags & (RTF_GATEWAY|RTF_HOST)) dst = satosns(ro->ro_rt->rt_gateway); gotif: /* * Look for multicast addresses and * and verify user is allowed to send * such a packet. */ if (dst->sns_addr.x_host.c_host[0]&1) { if ((ifp->if_flags & IFF_BROADCAST) == 0) { error = EADDRNOTAVAIL; goto bad; } if ((flags & NS_ALLOWBROADCAST) == 0) { error = EACCES; goto bad; } } if (htons(idp->idp_len) <= ifp->if_mtu) { ns_output_cnt++; if (ns_copy_output) { ns_watch_output(m0, ifp); } error = (*ifp->if_output)(ifp, m0, snstosa(dst), ro->ro_rt); goto done; } else error = EMSGSIZE; bad: if (ns_copy_output) { ns_watch_output(m0, ifp); } m_freem(m0); done: if (ro == &idproute && (flags & NS_ROUTETOIF) == 0 && ro->ro_rt) { RTFREE(ro->ro_rt); ro->ro_rt = 0; } return (error); }
/* * Input a Neighbor Solicitation Message. * * Based on RFC 2461 * Based on RFC 2462 (duplicate address detection) */ void nd6_ns_input(struct mbuf *m, int off, int icmp6len) { struct ifnet *ifp = m->m_pkthdr.rcvif; struct ip6_hdr *ip6 = mtod(m, struct ip6_hdr *); struct nd_neighbor_solicit *nd_ns; struct in6_addr saddr6 = ip6->ip6_src; struct in6_addr daddr6 = ip6->ip6_dst; struct in6_addr taddr6; struct in6_addr myaddr6; char *lladdr = NULL; struct ifaddr *ifa; int lladdrlen = 0; int anycast = 0, proxy = 0, tentative = 0; int router = ip6_forwarding; int tlladdr; union nd_opts ndopts; const struct sockaddr_dl *proxydl = NULL; IP6_EXTHDR_GET(nd_ns, struct nd_neighbor_solicit *, m, off, icmp6len); if (nd_ns == NULL) { ICMP6_STATINC(ICMP6_STAT_TOOSHORT); return; } ip6 = mtod(m, struct ip6_hdr *); /* adjust pointer for safety */ taddr6 = nd_ns->nd_ns_target; if (in6_setscope(&taddr6, ifp, NULL) != 0) goto bad; if (ip6->ip6_hlim != 255) { nd6log((LOG_ERR, "nd6_ns_input: invalid hlim (%d) from %s to %s on %s\n", ip6->ip6_hlim, ip6_sprintf(&ip6->ip6_src), ip6_sprintf(&ip6->ip6_dst), if_name(ifp))); goto bad; } if (IN6_IS_ADDR_UNSPECIFIED(&saddr6)) { /* dst has to be a solicited node multicast address. */ /* don't check ifindex portion */ if (daddr6.s6_addr16[0] == IPV6_ADDR_INT16_MLL && daddr6.s6_addr32[1] == 0 && daddr6.s6_addr32[2] == IPV6_ADDR_INT32_ONE && daddr6.s6_addr8[12] == 0xff) { ; /* good */ } else { nd6log((LOG_INFO, "nd6_ns_input: bad DAD packet " "(wrong ip6 dst)\n")); goto bad; } } else { struct sockaddr_in6 ssin6; /* * Make sure the source address is from a neighbor's address. */ sockaddr_in6_init(&ssin6, &saddr6, 0, 0, 0); if (nd6_is_addr_neighbor(&ssin6, ifp) == 0) { nd6log((LOG_INFO, "nd6_ns_input: " "NS packet from non-neighbor\n")); goto bad; } } if (IN6_IS_ADDR_MULTICAST(&taddr6)) { nd6log((LOG_INFO, "nd6_ns_input: bad NS target (multicast)\n")); goto bad; } icmp6len -= sizeof(*nd_ns); nd6_option_init(nd_ns + 1, icmp6len, &ndopts); if (nd6_options(&ndopts) < 0) { nd6log((LOG_INFO, "nd6_ns_input: invalid ND option, ignored\n")); /* nd6_options have incremented stats */ goto freeit; } if (ndopts.nd_opts_src_lladdr) { lladdr = (char *)(ndopts.nd_opts_src_lladdr + 1); lladdrlen = ndopts.nd_opts_src_lladdr->nd_opt_len << 3; } if (IN6_IS_ADDR_UNSPECIFIED(&ip6->ip6_src) && lladdr) { nd6log((LOG_INFO, "nd6_ns_input: bad DAD packet " "(link-layer address option)\n")); goto bad; } /* * Attaching target link-layer address to the NA? * (RFC 2461 7.2.4) * * NS IP dst is multicast MUST add * Otherwise MAY be omitted * * In this implementation, we omit the target link-layer address * in the "MAY" case. */ #if 0 /* too much! */ ifa = (struct ifaddr *)in6ifa_ifpwithaddr(ifp, &daddr6); if (ifa && (((struct in6_ifaddr *)ifa)->ia6_flags & IN6_IFF_ANYCAST)) tlladdr = 0; else #endif if (!IN6_IS_ADDR_MULTICAST(&daddr6)) tlladdr = 0; else tlladdr = 1; /* * Target address (taddr6) must be either: * (1) Valid unicast/anycast address for my receiving interface, * (2) Unicast address for which I'm offering proxy service, or * (3) "tentative" address on which DAD is being performed. */ /* (1) and (3) check. */ #if NCARP > 0 if (ifp->if_carp && ifp->if_type != IFT_CARP) ifa = carp_iamatch6(ifp->if_carp, &taddr6); else ifa = NULL; if (!ifa) ifa = (struct ifaddr *)in6ifa_ifpwithaddr(ifp, &taddr6); #else ifa = (struct ifaddr *)in6ifa_ifpwithaddr(ifp, &taddr6); #endif /* (2) check. */ if (ifa == NULL) { struct rtentry *rt; struct sockaddr_in6 tsin6; sockaddr_in6_init(&tsin6, &taddr6, 0, 0, 0); rt = rtalloc1((struct sockaddr *)&tsin6, 0); if (rt && (rt->rt_flags & RTF_ANNOUNCE) != 0 && rt->rt_gateway->sa_family == AF_LINK) { /* * proxy NDP for single entry */ ifa = (struct ifaddr *)in6ifa_ifpforlinklocal(ifp, IN6_IFF_NOTREADY|IN6_IFF_ANYCAST); if (ifa) { proxy = 1; proxydl = satocsdl(rt->rt_gateway); router = 0; /* XXX */ } } if (rt) rtfree(rt); } if (ifa == NULL) { /* * We've got an NS packet, and we don't have that address * assigned for us. We MUST silently ignore it. * See RFC2461 7.2.3. */ goto freeit; } myaddr6 = *IFA_IN6(ifa); anycast = ((struct in6_ifaddr *)ifa)->ia6_flags & IN6_IFF_ANYCAST; tentative = ((struct in6_ifaddr *)ifa)->ia6_flags & IN6_IFF_TENTATIVE; if (((struct in6_ifaddr *)ifa)->ia6_flags & IN6_IFF_DUPLICATED) goto freeit; if (lladdr && ((ifp->if_addrlen + 2 + 7) & ~7) != lladdrlen) { nd6log((LOG_INFO, "nd6_ns_input: lladdrlen mismatch for %s " "(if %d, NS packet %d)\n", ip6_sprintf(&taddr6), ifp->if_addrlen, lladdrlen - 2)); goto bad; } if (IN6_ARE_ADDR_EQUAL(&myaddr6, &saddr6)) { nd6log((LOG_INFO, "nd6_ns_input: duplicate IP6 address %s\n", ip6_sprintf(&saddr6))); goto freeit; } /* * We have neighbor solicitation packet, with target address equals to * one of my tentative address. * * src addr how to process? * --- --- * multicast of course, invalid (rejected in ip6_input) * unicast somebody is doing address resolution -> ignore * unspec dup address detection * * The processing is defined in RFC 2462. */ if (tentative) { /* * If source address is unspecified address, it is for * duplicate address detection. * * If not, the packet is for addess resolution; * silently ignore it. */ if (IN6_IS_ADDR_UNSPECIFIED(&saddr6)) nd6_dad_ns_input(ifa); goto freeit; } /* * If the source address is unspecified address, entries must not * be created or updated. * It looks that sender is performing DAD. Output NA toward * all-node multicast address, to tell the sender that I'm using * the address. * S bit ("solicited") must be zero. */ if (IN6_IS_ADDR_UNSPECIFIED(&saddr6)) { struct in6_addr in6_all; in6_all = in6addr_linklocal_allnodes; if (in6_setscope(&in6_all, ifp, NULL) != 0) goto bad; nd6_na_output(ifp, &in6_all, &taddr6, ((anycast || proxy || !tlladdr) ? 0 : ND_NA_FLAG_OVERRIDE) | (ip6_forwarding ? ND_NA_FLAG_ROUTER : 0), tlladdr, (const struct sockaddr *)proxydl); goto freeit; } nd6_cache_lladdr(ifp, &saddr6, lladdr, lladdrlen, ND_NEIGHBOR_SOLICIT, 0); nd6_na_output(ifp, &saddr6, &taddr6, ((anycast || proxy || !tlladdr) ? 0 : ND_NA_FLAG_OVERRIDE) | (router ? ND_NA_FLAG_ROUTER : 0) | ND_NA_FLAG_SOLICITED, tlladdr, (const struct sockaddr *)proxydl); freeit: m_freem(m); return; bad: nd6log((LOG_ERR, "nd6_ns_input: src=%s\n", ip6_sprintf(&saddr6))); nd6log((LOG_ERR, "nd6_ns_input: dst=%s\n", ip6_sprintf(&daddr6))); nd6log((LOG_ERR, "nd6_ns_input: tgt=%s\n", ip6_sprintf(&taddr6))); ICMP6_STATINC(ICMP6_STAT_BADNS); m_freem(m); }