diff options
| author | Bjoern A. Zeeb <bz@FreeBSD.org> | 2016-01-22 15:11:40 +0000 |
|---|---|---|
| committer | Bjoern A. Zeeb <bz@FreeBSD.org> | 2016-01-22 15:11:40 +0000 |
| commit | 009e81b16465ea457c0e63fd49fe77f47cc27a5a (patch) | |
| tree | 432b4196a8e5ea631b52a6a944d842f91fd08c7c /sys/net | |
| parent | 1f12da0e82e1f93bb8b608307d38bc8e5d177169 (diff) | |
| parent | 54902c0a6a9376d3618f14103db2d767006a293a (diff) | |
Notes
Diffstat (limited to 'sys/net')
| -rw-r--r-- | sys/net/bpf.c | 24 | ||||
| -rw-r--r-- | sys/net/flowtable.c | 10 | ||||
| -rw-r--r-- | sys/net/if.c | 41 | ||||
| -rw-r--r-- | sys/net/if_arcsubr.c | 5 | ||||
| -rw-r--r-- | sys/net/if_ethersubr.c | 291 | ||||
| -rw-r--r-- | sys/net/if_fddisubr.c | 5 | ||||
| -rw-r--r-- | sys/net/if_fwsubr.c | 9 | ||||
| -rw-r--r-- | sys/net/if_gif.c | 2 | ||||
| -rw-r--r-- | sys/net/if_gre.c | 22 | ||||
| -rw-r--r-- | sys/net/if_iso88025subr.c | 8 | ||||
| -rw-r--r-- | sys/net/if_llatbl.c | 102 | ||||
| -rw-r--r-- | sys/net/if_llatbl.h | 20 | ||||
| -rw-r--r-- | sys/net/if_loop.c | 8 | ||||
| -rw-r--r-- | sys/net/if_var.h | 44 | ||||
| -rw-r--r-- | sys/net/radix_mpath.c | 64 | ||||
| -rw-r--r-- | sys/net/radix_mpath.h | 1 | ||||
| -rw-r--r-- | sys/net/route.c | 293 | ||||
| -rw-r--r-- | sys/net/route.h | 64 | ||||
| -rw-r--r-- | sys/net/rtsock.c | 27 |
19 files changed, 695 insertions, 345 deletions
diff --git a/sys/net/bpf.c b/sys/net/bpf.c index 37edbe8275375..661a1cfbd1fba 100644 --- a/sys/net/bpf.c +++ b/sys/net/bpf.c @@ -69,6 +69,7 @@ __FBSDID("$FreeBSD$"); #include <net/if.h> #include <net/if_var.h> +#include <net/if_dl.h> #include <net/bpf.h> #include <net/bpf_buffer.h> #ifdef BPF_JITTER @@ -76,6 +77,7 @@ __FBSDID("$FreeBSD$"); #endif #include <net/bpf_zerocopy.h> #include <net/bpfdesc.h> +#include <net/route.h> #include <net/vnet.h> #include <netinet/in.h> @@ -164,7 +166,7 @@ static void bpf_detachd(struct bpf_d *); static void bpf_detachd_locked(struct bpf_d *); static void bpf_freed(struct bpf_d *); static int bpf_movein(struct uio *, int, struct ifnet *, struct mbuf **, - struct sockaddr *, int *, struct bpf_insn *); + struct sockaddr *, int *, struct bpf_d *); static int bpf_setif(struct bpf_d *, struct ifreq *); static void bpf_timed_out(void *); static __inline void @@ -454,7 +456,7 @@ bpf_ioctl_setzbuf(struct thread *td, struct bpf_d *d, struct bpf_zbuf *bz) */ static int bpf_movein(struct uio *uio, int linktype, struct ifnet *ifp, struct mbuf **mp, - struct sockaddr *sockp, int *hdrlen, struct bpf_insn *wfilter) + struct sockaddr *sockp, int *hdrlen, struct bpf_d *d) { const struct ieee80211_bpf_params *p; struct ether_header *eh; @@ -549,7 +551,7 @@ bpf_movein(struct uio *uio, int linktype, struct ifnet *ifp, struct mbuf **mp, if (error) goto bad; - slen = bpf_filter(wfilter, mtod(m, u_char *), len, len); + slen = bpf_filter(d->bd_wfilter, mtod(m, u_char *), len, len); if (slen == 0) { error = EPERM; goto bad; @@ -566,6 +568,10 @@ bpf_movein(struct uio *uio, int linktype, struct ifnet *ifp, struct mbuf **mp, else m->m_flags |= M_MCAST; } + if (d->bd_hdrcmplt == 0) { + memcpy(eh->ether_shost, IF_LLADDR(ifp), + sizeof(eh->ether_shost)); + } break; } @@ -1088,6 +1094,7 @@ bpfwrite(struct cdev *dev, struct uio *uio, int ioflag) struct ifnet *ifp; struct mbuf *m, *mc; struct sockaddr dst; + struct route ro; int error, hlen; error = devfs_get_cdevpriv((void **)&d); @@ -1119,7 +1126,7 @@ bpfwrite(struct cdev *dev, struct uio *uio, int ioflag) hlen = 0; /* XXX: bpf_movein() can sleep */ error = bpf_movein(uio, (int)d->bd_bif->bif_dlt, ifp, - &m, &dst, &hlen, d->bd_wfilter); + &m, &dst, &hlen, d); if (error) { d->bd_wdcount++; return (error); @@ -1151,7 +1158,14 @@ bpfwrite(struct cdev *dev, struct uio *uio, int ioflag) BPFD_UNLOCK(d); #endif - error = (*ifp->if_output)(ifp, m, &dst, NULL); + bzero(&ro, sizeof(ro)); + if (hlen != 0) { + ro.ro_prepend = (u_char *)&dst.sa_data; + ro.ro_plen = hlen; + ro.ro_flags = RT_HAS_HEADER; + } + + error = (*ifp->if_output)(ifp, m, &dst, &ro); if (error) d->bd_wdcount++; diff --git a/sys/net/flowtable.c b/sys/net/flowtable.c index a16584356ee41..f45a6872a815d 100644 --- a/sys/net/flowtable.c +++ b/sys/net/flowtable.c @@ -665,6 +665,7 @@ int flowtable_lookup(sa_family_t sa, struct mbuf *m, struct route *ro) { struct flentry *fle; + struct llentry *lle; if (V_flowtable_enable == 0) return (ENXIO); @@ -693,8 +694,15 @@ flowtable_lookup(sa_family_t sa, struct mbuf *m, struct route *ro) } ro->ro_rt = fle->f_rt; - ro->ro_lle = fle->f_lle; ro->ro_flags |= RT_NORTREF; + lle = fle->f_lle; + if (lle != NULL && (lle->la_flags & LLE_VALID)) { + ro->ro_prepend = lle->r_linkdata; + ro->ro_plen = lle->r_hdrlen; + ro->ro_flags |= RT_MAY_LOOP; + if (lle->la_flags & LLE_IFADDR) + ro->ro_flags |= RT_L2_ME; + } return (0); } diff --git a/sys/net/if.c b/sys/net/if.c index c18b1cc0250f7..45ed30ccba083 100644 --- a/sys/net/if.c +++ b/sys/net/if.c @@ -161,6 +161,7 @@ static int ifconf(u_long, caddr_t); static void if_freemulti(struct ifmultiaddr *); static void if_grow(void); static void if_input_default(struct ifnet *, struct mbuf *); +static int if_requestencap_default(struct ifnet *, struct if_encap_req *); static void if_route(struct ifnet *, int flag, int fam); static int if_setflag(struct ifnet *, int, int, int *, int); static int if_transmit(struct ifnet *ifp, struct mbuf *m); @@ -693,6 +694,9 @@ if_attach_internal(struct ifnet *ifp, int vmove, struct if_clone *ifc) if (ifp->if_input == NULL) ifp->if_input = if_input_default; + if (ifp->if_requestencap == NULL) + ifp->if_requestencap = if_requestencap_default; + if (!vmove) { #ifdef MAC mac_ifnet_create(ifp); @@ -3423,6 +3427,43 @@ if_setlladdr(struct ifnet *ifp, const u_char *lladdr, int len) } /* + * Compat function for handling basic encapsulation requests. + * Not converted stacks (FDDI, IB, ..) supports traditional + * output model: ARP (and other similar L2 protocols) are handled + * inside output routine, arpresolve/nd6_resolve() returns MAC + * address instead of full prepend. + * + * This function creates calculated header==MAC for IPv4/IPv6 and + * returns EAFNOSUPPORT (which is then handled in ARP code) for other + * address families. + */ +static int +if_requestencap_default(struct ifnet *ifp, struct if_encap_req *req) +{ + + if (req->rtype != IFENCAP_LL) + return (EOPNOTSUPP); + + if (req->bufsize < req->lladdr_len) + return (ENOMEM); + + switch (req->family) { + case AF_INET: + case AF_INET6: + break; + default: + return (EAFNOSUPPORT); + } + + /* Copy lladdr to storage as is */ + memmove(req->buf, req->lladdr, req->lladdr_len); + req->bufsize = req->lladdr_len; + req->lladdr_off = 0; + + return (0); +} + +/* * The name argument must be a pointer to storage which will last as * long as the interface does. For physical devices, the result of * device_get_name(dev) is a good choice and for pseudo-devices a diff --git a/sys/net/if_arcsubr.c b/sys/net/if_arcsubr.c index 4944e970e1ccb..16adba4e6cbff 100644 --- a/sys/net/if_arcsubr.c +++ b/sys/net/if_arcsubr.c @@ -113,9 +113,8 @@ arc_output(struct ifnet *ifp, struct mbuf *m, const struct sockaddr *dst, error = 0; #if defined(INET) || defined(INET6) - if (ro != NULL && ro->ro_rt != NULL && - (ro->ro_rt->rt_flags & RTF_GATEWAY) != 0) - is_gw = 1; + if (ro != NULL) + is_gw = (ro->ro_flags & RT_HAS_GW) != 0; #endif switch (dst->sa_family) { diff --git a/sys/net/if_ethersubr.c b/sys/net/if_ethersubr.c index 71e28ea00166f..2d652ad8fc319 100644 --- a/sys/net/if_ethersubr.c +++ b/sys/net/if_ethersubr.c @@ -113,6 +113,7 @@ static int ether_resolvemulti(struct ifnet *, struct sockaddr **, #ifdef VIMAGE static void ether_reassign(struct ifnet *, struct vnet *, char *); #endif +static int ether_requestencap(struct ifnet *, struct if_encap_req *); #define ETHER_IS_BROADCAST(addr) \ (bcmp(etherbroadcastaddr, (addr), ETHER_ADDR_LEN) == 0) @@ -136,6 +137,136 @@ update_mbuf_csumflags(struct mbuf *src, struct mbuf *dst) } /* + * Handle link-layer encapsulation requests. + */ +static int +ether_requestencap(struct ifnet *ifp, struct if_encap_req *req) +{ + struct ether_header *eh; + struct arphdr *ah; + uint16_t etype; + const u_char *lladdr; + + if (req->rtype != IFENCAP_LL) + return (EOPNOTSUPP); + + if (req->bufsize < ETHER_HDR_LEN) + return (ENOMEM); + + eh = (struct ether_header *)req->buf; + lladdr = req->lladdr; + req->lladdr_off = 0; + + switch (req->family) { + case AF_INET: + etype = htons(ETHERTYPE_IP); + break; + case AF_INET6: + etype = htons(ETHERTYPE_IPV6); + break; + case AF_ARP: + ah = (struct arphdr *)req->hdata; + 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; + } + + if (req->flags & IFENCAP_FLAG_BROADCAST) + lladdr = ifp->if_broadcastaddr; + break; + default: + return (EAFNOSUPPORT); + } + + memcpy(&eh->ether_type, &etype, sizeof(eh->ether_type)); + memcpy(eh->ether_dhost, lladdr, ETHER_ADDR_LEN); + memcpy(eh->ether_shost, IF_LLADDR(ifp), ETHER_ADDR_LEN); + req->bufsize = sizeof(struct ether_header); + + return (0); +} + + +static int +ether_resolve_addr(struct ifnet *ifp, struct mbuf *m, + const struct sockaddr *dst, struct route *ro, u_char *phdr, + uint32_t *pflags) +{ + struct ether_header *eh; + uint32_t lleflags = 0; + int error = 0; +#if defined(INET) || defined(INET6) + uint16_t etype; +#endif + + eh = (struct ether_header *)phdr; + + switch (dst->sa_family) { +#ifdef INET + case AF_INET: + if ((m->m_flags & (M_BCAST | M_MCAST)) == 0) + error = arpresolve(ifp, 0, m, dst, phdr, &lleflags); + else { + if (m->m_flags & M_BCAST) + memcpy(eh->ether_dhost, ifp->if_broadcastaddr, + ETHER_ADDR_LEN); + else { + const struct in_addr *a; + a = &(((const struct sockaddr_in *)dst)->sin_addr); + ETHER_MAP_IP_MULTICAST(a, eh->ether_dhost); + } + etype = htons(ETHERTYPE_IP); + memcpy(&eh->ether_type, &etype, sizeof(etype)); + memcpy(eh->ether_shost, IF_LLADDR(ifp), ETHER_ADDR_LEN); + } + break; +#endif +#ifdef INET6 + case AF_INET6: + if ((m->m_flags & M_MCAST) == 0) + error = nd6_resolve(ifp, 0, m, dst, phdr, &lleflags); + else { + const struct in6_addr *a6; + a6 = &(((const struct sockaddr_in6 *)dst)->sin6_addr); + ETHER_MAP_IPV6_MULTICAST(a6, eh->ether_dhost); + etype = htons(ETHERTYPE_IPV6); + memcpy(&eh->ether_type, &etype, sizeof(etype)); + memcpy(eh->ether_shost, IF_LLADDR(ifp), ETHER_ADDR_LEN); + } + break; +#endif + default: + if_printf(ifp, "can't handle af%d\n", dst->sa_family); + if (m != NULL) + m_freem(m); + return (EAFNOSUPPORT); + } + + if (error == EHOSTDOWN) { + if (ro != NULL && (ro->ro_flags & RT_HAS_GW) != 0) + error = EHOSTUNREACH; + } + + if (error != 0) + return (error); + + *pflags = RT_MAY_LOOP; + if (lleflags & LLE_IFADDR) + *pflags |= RT_L2_ME; + + return (0); +} + +/* * Ethernet output routine. * Encapsulate a packet of type family for the local net. * Use trailer local net encapsulation if enough data in first @@ -145,27 +276,20 @@ int ether_output(struct ifnet *ifp, struct mbuf *m, const struct sockaddr *dst, struct route *ro) { - short type; - int error = 0, hdrcmplt = 0; - u_char edst[ETHER_ADDR_LEN]; - struct llentry *lle = NULL; - struct rtentry *rt0 = NULL; + int error = 0; + char linkhdr[ETHER_HDR_LEN], *phdr; struct ether_header *eh; struct pf_mtag *t; int loop_copy = 1; int hlen; /* link layer header length */ - int is_gw = 0; - uint32_t pflags = 0; + uint32_t pflags; + phdr = NULL; + pflags = 0; if (ro != NULL) { - if (!(m->m_flags & (M_BCAST | M_MCAST))) { - lle = ro->ro_lle; - if (lle != NULL) - pflags = lle->la_flags; - } - rt0 = ro->ro_rt; - if (rt0 != NULL && (rt0->rt_flags & RTF_GATEWAY) != 0) - is_gw = 1; + phdr = ro->ro_prepend; + hlen = ro->ro_plen; + pflags = ro->ro_flags; } #ifdef MAC error = mac_ifnet_check_transmit(ifp, m); @@ -180,94 +304,35 @@ ether_output(struct ifnet *ifp, struct mbuf *m, (ifp->if_drv_flags & IFF_DRV_RUNNING))) senderr(ENETDOWN); - hlen = ETHER_HDR_LEN; - switch (dst->sa_family) { -#ifdef INET - case AF_INET: - if (lle != NULL && (pflags & LLE_VALID) != 0) - memcpy(edst, &lle->ll_addr.mac16, sizeof(edst)); - else - error = arpresolve(ifp, is_gw, m, dst, edst, &pflags); - if (error) - return (error == EWOULDBLOCK ? 0 : error); - type = htons(ETHERTYPE_IP); - break; - case AF_ARP: - { - struct arphdr *ah; - ah = mtod(m, struct arphdr *); - ah->ar_hrd = htons(ARPHRD_ETHER); - - loop_copy = 0; /* if this is for us, don't do it */ - - switch(ntohs(ah->ar_op)) { - case ARPOP_REVREQUEST: - case ARPOP_REVREPLY: - type = htons(ETHERTYPE_REVARP); - break; - case ARPOP_REQUEST: - case ARPOP_REPLY: - default: - type = htons(ETHERTYPE_ARP); - break; - } - - if (m->m_flags & M_BCAST) - bcopy(ifp->if_broadcastaddr, edst, ETHER_ADDR_LEN); - else - bcopy(ar_tha(ah), edst, ETHER_ADDR_LEN); - - } - break; -#endif -#ifdef INET6 - case AF_INET6: - if (lle != NULL && (pflags & LLE_VALID)) - memcpy(edst, &lle->ll_addr.mac16, sizeof(edst)); - else - error = nd6_resolve(ifp, is_gw, m, dst, (u_char *)edst, - &pflags); - if (error) + if (phdr == NULL) { + /* No prepend data supplied. Try to calculate ourselves. */ + phdr = linkhdr; + hlen = ETHER_HDR_LEN; + error = ether_resolve_addr(ifp, m, dst, ro, phdr, &pflags); + if (error != 0) return (error == EWOULDBLOCK ? 0 : error); - type = htons(ETHERTYPE_IPV6); - break; -#endif - case pseudo_AF_HDRCMPLT: - { - const struct ether_header *eh; - - hdrcmplt = 1; - /* FALLTHROUGH */ - - case AF_UNSPEC: - loop_copy = 0; /* if this is for us, don't do it */ - eh = (const struct ether_header *)dst->sa_data; - (void)memcpy(edst, eh->ether_dhost, sizeof (edst)); - type = eh->ether_type; - break; - } - default: - if_printf(ifp, "can't handle af%d\n", dst->sa_family); - senderr(EAFNOSUPPORT); } - if ((pflags & LLE_IFADDR) != 0) { + if ((pflags & RT_L2_ME) != 0) { update_mbuf_csumflags(m, m); return (if_simloop(ifp, m, dst->sa_family, 0)); } + loop_copy = pflags & RT_MAY_LOOP; /* * Add local net header. If no space in first mbuf, * allocate another. + * + * Note that we do prepend regardless of RT_HAS_HEADER flag. + * This is done because BPF code shifts m_data pointer + * to the end of ethernet header prior to calling if_output(). */ - M_PREPEND(m, ETHER_HDR_LEN, M_NOWAIT); + M_PREPEND(m, hlen, M_NOWAIT); if (m == NULL) senderr(ENOBUFS); - eh = mtod(m, struct ether_header *); - if (hdrcmplt == 0) { - memcpy(&eh->ether_type, &type, sizeof(eh->ether_type)); - memcpy(eh->ether_dhost, edst, sizeof (edst)); - memcpy(eh->ether_shost, IF_LLADDR(ifp),sizeof(eh->ether_shost)); + if ((pflags & RT_HAS_HEADER) == 0) { + eh = mtod(m, struct ether_header *); + memcpy(eh, phdr, hlen); } /* @@ -279,34 +344,27 @@ ether_output(struct ifnet *ifp, struct mbuf *m, * on the wire). However, we don't do that here for security * reasons and compatibility with the original behavior. */ - if ((ifp->if_flags & IFF_SIMPLEX) && loop_copy && + if ((m->m_flags & M_BCAST) && loop_copy && (ifp->if_flags & IFF_SIMPLEX) && ((t = pf_find_mtag(m)) == NULL || !t->routed)) { - if (m->m_flags & M_BCAST) { - struct mbuf *n; + struct mbuf *n; - /* - * Because if_simloop() modifies the packet, we need a - * writable copy through m_dup() instead of a readonly - * one as m_copy[m] would give us. The alternative would - * be to modify if_simloop() to handle the readonly mbuf, - * but performancewise it is mostly equivalent (trading - * extra data copying vs. extra locking). - * - * XXX This is a local workaround. A number of less - * often used kernel parts suffer from the same bug. - * See PR kern/105943 for a proposed general solution. - */ - if ((n = m_dup(m, M_NOWAIT)) != NULL) { - update_mbuf_csumflags(m, n); - (void)if_simloop(ifp, n, dst->sa_family, hlen); - } else - if_inc_counter(ifp, IFCOUNTER_IQDROPS, 1); - } else if (bcmp(eh->ether_dhost, eh->ether_shost, - ETHER_ADDR_LEN) == 0) { - update_mbuf_csumflags(m, m); - (void) if_simloop(ifp, m, dst->sa_family, hlen); - return (0); /* XXX */ - } + /* + * Because if_simloop() modifies the packet, we need a + * writable copy through m_dup() instead of a readonly + * one as m_copy[m] would give us. The alternative would + * be to modify if_simloop() to handle the readonly mbuf, + * but performancewise it is mostly equivalent (trading + * extra data copying vs. extra locking). + * + * XXX This is a local workaround. A number of less + * often used kernel parts suffer from the same bug. + * See PR kern/105943 for a proposed general solution. + */ + if ((n = m_dup(m, M_NOWAIT)) != NULL) { + update_mbuf_csumflags(m, n); + (void)if_simloop(ifp, n, dst->sa_family, hlen); + } else + if_inc_counter(ifp, IFCOUNTER_IQDROPS, 1); } /* @@ -798,6 +856,7 @@ ether_ifattach(struct ifnet *ifp, const u_int8_t *lla) ifp->if_output = ether_output; ifp->if_input = ether_input; ifp->if_resolvemulti = ether_resolvemulti; + ifp->if_requestencap = ether_requestencap; #ifdef VIMAGE ifp->if_reassign = ether_reassign; #endif diff --git a/sys/net/if_fddisubr.c b/sys/net/if_fddisubr.c index 81b65a6346664..84ee669ae254b 100644 --- a/sys/net/if_fddisubr.c +++ b/sys/net/if_fddisubr.c @@ -119,9 +119,8 @@ fddi_output(struct ifnet *ifp, struct mbuf *m, const struct sockaddr *dst, getmicrotime(&ifp->if_lastchange); #if defined(INET) || defined(INET6) - if (ro != NULL && ro->ro_rt != NULL && - (ro->ro_rt->rt_flags & RTF_GATEWAY) != 0) - is_gw = 1; + if (ro != NULL) + is_gw = (ro->ro_flags & RT_HAS_GW) != 0; #endif switch (dst->sa_family) { diff --git a/sys/net/if_fwsubr.c b/sys/net/if_fwsubr.c index 626b1cb856fb5..a070f61760235 100644 --- a/sys/net/if_fwsubr.c +++ b/sys/net/if_fwsubr.c @@ -106,9 +106,8 @@ firewire_output(struct ifnet *ifp, struct mbuf *m, const struct sockaddr *dst, } #if defined(INET) || defined(INET6) - if (ro != NULL && ro->ro_rt != NULL && - (ro->ro_rt->rt_flags & RTF_GATEWAY) != 0) - is_gw = 1; + if (ro != NULL) + is_gw = (ro->ro_flags & RT_HAS_GW) != 0; #endif /* * For unicast, we make a tag to store the lladdr of the @@ -145,10 +144,6 @@ firewire_output(struct ifnet *ifp, struct mbuf *m, const struct sockaddr *dst, * doesn't fit into the arp model. */ if (unicast) { - is_gw = 0; - if (ro != NULL && ro->ro_rt != NULL && - (ro->ro_rt->rt_flags & RTF_GATEWAY) != 0) - is_gw = 1; error = arpresolve(ifp, is_gw, m, dst, (u_char *) destfw, NULL); if (error) return (error == EWOULDBLOCK ? 0 : error); diff --git a/sys/net/if_gif.c b/sys/net/if_gif.c index 33d583c9686a3..1607af9c8e65d 100644 --- a/sys/net/if_gif.c +++ b/sys/net/if_gif.c @@ -529,7 +529,6 @@ gif_input(struct mbuf *m, struct ifnet *ifp, int proto, uint8_t ecn) struct gif_softc *sc; struct ether_header *eh; struct ifnet *oldifp; - uint32_t gif_options; int isr, n, af; if (ifp == NULL) { @@ -538,7 +537,6 @@ gif_input(struct mbuf *m, struct ifnet *ifp, int proto, uint8_t ecn) return; } sc = ifp->if_softc; - gif_options = sc->gif_options; m->m_pkthdr.rcvif = ifp; m_clrprotoflags(m); switch (proto) { diff --git a/sys/net/if_gre.c b/sys/net/if_gre.c index 24ed971135c2b..9d4a9763ca7f1 100644 --- a/sys/net/if_gre.c +++ b/sys/net/if_gre.c @@ -682,7 +682,10 @@ gre_input(struct mbuf **mp, int *offp, int proto) struct grehdr *gh; struct ifnet *ifp; struct mbuf *m; - uint32_t *opts, key; + uint32_t *opts; +#ifdef notyet + uint32_t key; +#endif uint16_t flags; int hlen, isr, af; @@ -715,17 +718,28 @@ gre_input(struct mbuf **mp, int *offp, int proto) opts++; } if (flags & GRE_FLAGS_KP) { +#ifdef notyet + /* + * XXX: The current implementation uses the key only for outgoing + * packets. But we can check the key value here, or even in the + * encapcheck function. + */ key = ntohl(*opts); +#endif hlen += sizeof(uint32_t); opts++; + } +#ifdef notyet } else key = 0; - /* + if (sc->gre_key != 0 && (key != sc->gre_key || key != 0)) goto drop; - */ +#endif if (flags & GRE_FLAGS_SP) { - /* seq = ntohl(*opts); */ +#ifdef notyet + seq = ntohl(*opts); +#endif hlen += sizeof(uint32_t); } switch (ntohs(gh->gre_proto)) { diff --git a/sys/net/if_iso88025subr.c b/sys/net/if_iso88025subr.c index 7192998ac05e4..466784fb075fd 100644 --- a/sys/net/if_iso88025subr.c +++ b/sys/net/if_iso88025subr.c @@ -214,12 +214,8 @@ iso88025_output(struct ifnet *ifp, struct mbuf *m, const struct sockaddr *dst, struct rtentry *rt0 = NULL; int is_gw = 0; - if (ro != NULL) { - rt0 = ro->ro_rt; - if (rt0 != NULL && (rt0->rt_flags & RTF_GATEWAY) != 0) - is_gw = 1; - } - + if (ro != NULL) + is_gw = (ro->ro_flags & RT_HAS_GW) != 0; #ifdef MAC error = mac_ifnet_check_transmit(ifp, m); if (error) diff --git a/sys/net/if_llatbl.c b/sys/net/if_llatbl.c index b0450b36000b9..beef6b481adba 100644 --- a/sys/net/if_llatbl.c +++ b/sys/net/if_llatbl.c @@ -278,10 +278,12 @@ lltable_drop_entry_queue(struct llentry *lle) void lltable_set_entry_addr(struct ifnet *ifp, struct llentry *lle, - const char *lladdr) + const char *linkhdr, size_t linkhdrsize, int lladdr_off) { - bcopy(lladdr, &lle->ll_addr, ifp->if_addrlen); + memcpy(lle->r_linkdata, linkhdr, linkhdrsize); + lle->r_hdrlen = linkhdrsize; + lle->ll_addr = &lle->r_linkdata[lladdr_off]; lle->la_flags |= LLE_VALID; lle->r_flags |= RLLE_VALID; } @@ -296,7 +298,7 @@ lltable_set_entry_addr(struct ifnet *ifp, struct llentry *lle, */ int lltable_try_set_entry_addr(struct ifnet *ifp, struct llentry *lle, - const char *lladdr) + const char *linkhdr, size_t linkhdrsize, int lladdr_off) { /* Perform real LLE update */ @@ -318,7 +320,7 @@ lltable_try_set_entry_addr(struct ifnet *ifp, struct llentry *lle, } /* Update data */ - lltable_set_entry_addr(ifp, lle, lladdr); + lltable_set_entry_addr(ifp, lle, linkhdr, linkhdrsize, lladdr_off); IF_AFDATA_WUNLOCK(ifp); @@ -327,6 +329,84 @@ lltable_try_set_entry_addr(struct ifnet *ifp, struct llentry *lle, return (1); } + /* + * Helper function used to pre-compute full/partial link-layer + * header data suitable for feeding into if_output(). + */ +int +lltable_calc_llheader(struct ifnet *ifp, int family, char *lladdr, + char *buf, size_t *bufsize, int *lladdr_off) +{ + struct if_encap_req ereq; + int error; + + bzero(buf, *bufsize); + bzero(&ereq, sizeof(ereq)); + ereq.buf = buf; + ereq.bufsize = *bufsize; + ereq.rtype = IFENCAP_LL; + ereq.family = family; + ereq.lladdr = lladdr; + ereq.lladdr_len = ifp->if_addrlen; + error = ifp->if_requestencap(ifp, &ereq); + if (error == 0) { + *bufsize = ereq.bufsize; + *lladdr_off = ereq.lladdr_off; + } + + return (error); +} + +/* + * Update link-layer header for given @lle after + * interface lladdr was changed. + */ +static int +llentry_update_ifaddr(struct lltable *llt, struct llentry *lle, void *farg) +{ + struct ifnet *ifp; + u_char linkhdr[LLE_MAX_LINKHDR]; + size_t linkhdrsize; + u_char *lladdr; + int lladdr_off; + + ifp = (struct ifnet *)farg; + + lladdr = lle->ll_addr; + + LLE_WLOCK(lle); + if ((lle->la_flags & LLE_VALID) == 0) { + LLE_WUNLOCK(lle); + return (0); + } + + if ((lle->la_flags & LLE_IFADDR) != 0) + lladdr = IF_LLADDR(ifp); + + linkhdrsize = sizeof(linkhdr); + lltable_calc_llheader(ifp, llt->llt_af, lladdr, linkhdr, &linkhdrsize, + &lladdr_off); + memcpy(lle->r_linkdata, linkhdr, linkhdrsize); + LLE_WUNLOCK(lle); + + return (0); +} + +/* + * Update all calculated headers for given @llt + */ +void +lltable_update_ifaddr(struct lltable *llt) +{ + + if (llt->llt_ifp->if_flags & IFF_LOOPBACK) + return; + + IF_AFDATA_WLOCK(llt->llt_ifp); + lltable_foreach_lle(llt, llentry_update_ifaddr, llt->llt_ifp); + IF_AFDATA_WUNLOCK(llt->llt_ifp); +} + /* * * Performes generic cleanup routines and frees lle. @@ -642,6 +722,9 @@ lla_rt_output(struct rt_msghdr *rtm, struct rt_addrinfo *info) struct ifnet *ifp; struct lltable *llt; struct llentry *lle, *lle_tmp; + uint8_t linkhdr[LLE_MAX_LINKHDR]; + size_t linkhdrsize; + int lladdr_off; u_int laflags = 0; int error; @@ -677,11 +760,14 @@ lla_rt_output(struct rt_msghdr *rtm, struct rt_addrinfo *info) if (lle == NULL) return (ENOMEM); - bcopy(LLADDR(dl), &lle->ll_addr, ifp->if_addrlen); + linkhdrsize = sizeof(linkhdr); + if (lltable_calc_llheader(ifp, dst->sa_family, LLADDR(dl), + linkhdr, &linkhdrsize, &lladdr_off) != 0) + return (EINVAL); + lltable_set_entry_addr(ifp, lle, linkhdr, linkhdrsize, + lladdr_off); if ((rtm->rtm_flags & RTF_ANNOUNCE)) lle->la_flags |= LLE_PUB; - lle->la_flags |= LLE_VALID; - lle->r_flags |= RLLE_VALID; lle->la_expire = rtm->rtm_rmx.rmx_expire; laflags = lle->la_flags; @@ -767,7 +853,7 @@ llatbl_lle_show(struct llentry_sa *la) db_printf(" ln_router=%u\n", lle->ln_router); db_printf(" ln_ntick=%ju\n", (uintmax_t)lle->ln_ntick); db_printf(" lle_refcnt=%d\n", lle->lle_refcnt); - bcopy(&lle->ll_addr.mac16, octet, sizeof(octet)); + bcopy(lle->ll_addr, octet, sizeof(octet)); db_printf(" ll_addr=%02x:%02x:%02x:%02x:%02x:%02x\n", octet[0], octet[1], octet[2], octet[3], octet[4], octet[5]); db_printf(" lle_timer=%p\n", &lle->lle_timer); diff --git a/sys/net/if_llatbl.h b/sys/net/if_llatbl.h index 8a300c31ed167..b6111c65cc9f8 100644 --- a/sys/net/if_llatbl.h +++ b/sys/net/if_llatbl.h @@ -48,6 +48,7 @@ extern struct rwlock lltable_rwlock; #define LLTABLE_WUNLOCK() rw_wunlock(&lltable_rwlock) #define LLTABLE_LOCK_ASSERT() rw_assert(&lltable_rwlock, RA_LOCKED) +#define LLE_MAX_LINKHDR 24 /* Full IB header */ /* * Code referencing llentry must at least hold * a shared lock @@ -58,14 +59,11 @@ struct llentry { struct in_addr addr4; struct in6_addr addr6; } r_l3addr; - union { - uint64_t mac_aligned; - uint16_t mac16[3]; - uint8_t mac8[20]; /* IB needs 20 bytes. */ - } ll_addr; + char r_linkdata[LLE_MAX_LINKHDR]; /* L2 data */ + uint8_t r_hdrlen; /* length for LL header */ + uint8_t spare0[3]; uint16_t r_flags; /* LLE runtime flags */ uint16_t r_skip_req; /* feedback from fast path */ - uint64_t spare1; struct lltable *lle_tbl; struct llentries *lle_head; @@ -82,6 +80,7 @@ struct llentry { time_t lle_remtime; /* Real time remaining */ time_t lle_hittime; /* Time when r_skip_req was unset */ int lle_refcnt; + char *ll_addr; /* link-layer address */ LIST_ENTRY(llentry) lle_chain; /* chain of deleted items */ struct callout lle_timer; @@ -198,6 +197,8 @@ MALLOC_DECLARE(M_LLTABLE); /* LLE request flags */ #define LLE_EXCLUSIVE 0x2000 /* return lle xlocked */ #define LLE_UNLOCKED 0x4000 /* return lle unlocked */ +#define LLE_ADDRONLY 0x4000 /* return lladdr instead of full header */ +#define LLE_CREATE 0x8000 /* hint to avoid lle lookup */ /* LLE flags used by fastpath code */ #define RLLE_VALID 0x0001 /* entry is valid */ @@ -223,10 +224,13 @@ struct llentry *llentry_alloc(struct ifnet *, struct lltable *, /* helper functions */ size_t lltable_drop_entry_queue(struct llentry *); void lltable_set_entry_addr(struct ifnet *ifp, struct llentry *lle, - const char *lladdr); + const char *linkhdr, size_t linkhdrsize, int lladdr_off); int lltable_try_set_entry_addr(struct ifnet *ifp, struct llentry *lle, - const char *lladdr); + const char *linkhdr, size_t linkhdrsize, int lladdr_off); +int lltable_calc_llheader(struct ifnet *ifp, int family, char *lladdr, + char *buf, size_t *bufsize, int *lladdr_off); +void lltable_update_ifaddr(struct lltable *llt); struct llentry *lltable_alloc_entry(struct lltable *llt, u_int flags, const struct sockaddr *l4addr); void lltable_free_entry(struct lltable *llt, struct llentry *lle); diff --git a/sys/net/if_loop.c b/sys/net/if_loop.c index f4ac8b4e729ae..1291f7b44d919 100644 --- a/sys/net/if_loop.c +++ b/sys/net/if_loop.c @@ -202,15 +202,12 @@ looutput(struct ifnet *ifp, struct mbuf *m, const struct sockaddr *dst, struct route *ro) { u_int32_t af; - struct rtentry *rt = NULL; #ifdef MAC int error; #endif M_ASSERTPKTHDR(m); /* check if we have the packet header */ - if (ro != NULL) - rt = ro->ro_rt; #ifdef MAC error = mac_ifnet_check_transmit(ifp, m); if (error) { @@ -219,10 +216,9 @@ looutput(struct ifnet *ifp, struct mbuf *m, const struct sockaddr *dst, } #endif - if (rt && rt->rt_flags & (RTF_REJECT|RTF_BLACKHOLE)) { + if (ro != NULL && ro->ro_flags & (RT_REJECT|RT_BLACKHOLE)) { m_freem(m); - return (rt->rt_flags & RTF_BLACKHOLE ? 0 : - rt->rt_flags & RTF_HOST ? EHOSTUNREACH : ENETUNREACH); + return (ro->ro_flags & RT_BLACKHOLE ? 0 : EHOSTUNREACH); } if_inc_counter(ifp, IFCOUNTER_OPACKETS, 1); diff --git a/sys/net/if_var.h b/sys/net/if_var.h index 5911cecb706ba..54de56727f914 100644 --- a/sys/net/if_var.h +++ b/sys/net/if_var.h @@ -134,6 +134,48 @@ struct ifnet_hw_tsomax { u_int tsomaxsegsize; /* TSO maximum segment size in bytes */ }; +/* Interface encap request types */ +typedef enum { + IFENCAP_LL = 1 /* pre-calculate link-layer header */ +} ife_type; + +/* + * The structure below allows to request various pre-calculated L2/L3 headers + * for different media. Requests varies by type (rtype field). + * + * IFENCAP_LL type: pre-calculates link header based on address family + * and destination lladdr. + * + * Input data fields: + * buf: pointer to destination buffer + * bufsize: buffer size + * flags: IFENCAP_FLAG_BROADCAST if destination is broadcast + * family: address family defined by AF_ constant. + * lladdr: pointer to link-layer address + * lladdr_len: length of link-layer address + * hdata: pointer to L3 header (optional, used for ARP requests). + * Output data fields: + * buf: encap data is stored here + * bufsize: resulting encap length is stored here + * lladdr_off: offset of link-layer address from encap hdr start + * hdata: L3 header may be altered if necessary + */ + +struct if_encap_req { + u_char *buf; /* Destination buffer (w) */ + size_t bufsize; /* size of provided buffer (r) */ + ife_type rtype; /* request type (r) */ + uint32_t flags; /* Request flags (r) */ + int family; /* Address family AF_* (r) */ + int lladdr_off; /* offset from header start (w) */ + int lladdr_len; /* lladdr length (r) */ + char *lladdr; /* link-level address pointer (r) */ + char *hdata; /* Upper layer header data (rw) */ +}; + +#define IFENCAP_FLAG_BROADCAST 0x02 /* Destination is broadcast */ + + /* * Structure defining a network interface. * @@ -235,6 +277,8 @@ struct ifnet { void (*if_reassign) /* reassign to vnet routine */ (struct ifnet *, struct vnet *, char *); if_get_counter_t if_get_counter; /* get counter values */ + int (*if_requestencap) /* make link header from request */ + (struct ifnet *, struct if_encap_req *); /* Statistics. */ counter_u64_t if_counters[IFCOUNTERS]; diff --git a/sys/net/radix_mpath.c b/sys/net/radix_mpath.c index 82c0adda0d766..5f40745f49040 100644 --- a/sys/net/radix_mpath.c +++ b/sys/net/radix_mpath.c @@ -197,14 +197,50 @@ rt_mpath_conflict(struct radix_node_head *rnh, struct rtentry *rt, return (0); } -void -rtalloc_mpath_fib(struct route *ro, uint32_t hash, u_int fibnum) +static struct rtentry * +rt_mpath_selectrte(struct rtentry *rte, uint32_t hash) { struct radix_node *rn0, *rn; - u_int32_t n; + uint32_t total_weight; struct rtentry *rt; int64_t weight; + /* beyond here, we use rn as the master copy */ + rn0 = rn = (struct radix_node *)rte; + rt = rte; + + /* gw selection by Modulo-N Hash (RFC2991) XXX need improvement? */ + total_weight = rn_mpath_count(rn0); + hash += hashjitter; + hash %= total_weight; + for (weight = abs((int32_t)hash); + rt != NULL && weight >= rt->rt_weight; + weight -= rt->rt_weight) { + + /* stay within the multipath routes */ + if (rn->rn_dupedkey && rn->rn_mask != rn->rn_dupedkey->rn_mask) + break; + rn = rn->rn_dupedkey; + rt = (struct rtentry *)rn; + } + + return (rt); +} + +struct rtentry * +rt_mpath_select(struct rtentry *rte, uint32_t hash) +{ + if (rn_mpath_next((struct radix_node *)rte) == NULL) + return (rte); + + return (rt_mpath_selectrte(rte, hash)); +} + +void +rtalloc_mpath_fib(struct route *ro, uint32_t hash, u_int fibnum) +{ + struct rtentry *rt; + /* * XXX we don't attempt to lookup cached route again; what should * be done for sendto(3) case? @@ -222,34 +258,18 @@ rtalloc_mpath_fib(struct route *ro, uint32_t hash, u_int fibnum) 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; - for (weight = abs((int32_t)hash), rt = ro->ro_rt; - weight >= rt->rt_weight && rn; - weight -= rt->rt_weight) { - - /* stay within the multipath routes */ - if (rn->rn_dupedkey && rn->rn_mask != rn->rn_dupedkey->rn_mask) - break; - rn = rn->rn_dupedkey; - rt = (struct rtentry *)rn; - } + rt = rt_mpath_selectrte(ro->ro_rt, hash); /* XXX try filling rt_gwroute and avoid unreachable gw */ /* gw selection has failed - there must be only zero weight routes */ - if (!rn) { + if (!rt) { RT_UNLOCK(ro->ro_rt); ro->ro_rt = NULL; return; } if (ro->ro_rt != rt) { RTFREE_LOCKED(ro->ro_rt); - ro->ro_rt = (struct rtentry *)rn; + ro->ro_rt = rt; RT_LOCK(ro->ro_rt); RT_ADDREF(ro->ro_rt); diff --git a/sys/net/radix_mpath.h b/sys/net/radix_mpath.h index bcb210e37cffd..fc6f7775a5ffc 100644 --- a/sys/net/radix_mpath.h +++ b/sys/net/radix_mpath.h @@ -52,6 +52,7 @@ int rt_mpath_conflict(struct radix_node_head *, struct rtentry *, struct sockaddr *); void rtalloc_mpath_fib(struct route *, u_int32_t, u_int); #define rtalloc_mpath(_route, _hash) rtalloc_mpath_fib((_route), (_hash), 0) +struct rtentry *rt_mpath_select(struct rtentry *, uint32_t); struct radix_node *rn_mpath_lookup(void *, void *, struct radix_node_head *); int rt_mpath_deldup(struct rtentry *, struct rtentry *); diff --git a/sys/net/route.c b/sys/net/route.c index 86f99ee93df96..001a15f6c8e54 100644 --- a/sys/net/route.c +++ b/sys/net/route.c @@ -147,6 +147,8 @@ static void rt_notifydelete(struct rtentry *rt, struct rt_addrinfo *info); static struct radix_node *rt_mpath_unlink(struct radix_node_head *rnh, struct rt_addrinfo *info, struct rtentry *rto, int *perror); #endif +static int rt_exportinfo(struct rtentry *rt, struct rt_addrinfo *info, + int flags); struct if_mtuinfo { @@ -341,35 +343,6 @@ sys_setfib(struct thread *td, struct setfib_args *uap) * Packet routing routines. */ void -rtalloc(struct route *ro) -{ - - rtalloc_ign_fib(ro, 0UL, RT_DEFAULT_FIB); -} - -void -rtalloc_fib(struct route *ro, u_int fibnum) -{ - rtalloc_ign_fib(ro, 0UL, fibnum); -} - -void -rtalloc_ign(struct route *ro, u_long ignore) -{ - struct rtentry *rt; - - if ((rt = ro->ro_rt) != NULL) { - if (rt->rt_ifp != NULL && rt->rt_flags & RTF_UP) - return; - RTFREE(rt); - ro->ro_rt = NULL; - } - ro->ro_rt = rtalloc1_fib(&ro->ro_dst, 1, ignore, RT_DEFAULT_FIB); - if (ro->ro_rt) - RT_UNLOCK(ro->ro_rt); -} - -void rtalloc_ign_fib(struct route *ro, u_long ignore, u_int fibnum) { struct rtentry *rt; @@ -407,7 +380,6 @@ rtalloc1_fib(struct sockaddr *dst, int report, u_long ignflags, struct rtentry *newrt; struct rt_addrinfo info; int err = 0, msgtype = RTM_MISS; - int needlock; KASSERT((fibnum < rt_numfibs), ("rtalloc1_fib: bad fibnum")); rnh = rt_tables_get_rnh(fibnum, dst->sa_family); @@ -418,23 +390,16 @@ rtalloc1_fib(struct sockaddr *dst, int report, u_long ignflags, /* * Look up the address in the table for that Address Family */ - needlock = !(ignflags & RTF_RNH_LOCKED); - if (needlock) - RADIX_NODE_HEAD_RLOCK(rnh); -#ifdef INVARIANTS - else - RADIX_NODE_HEAD_LOCK_ASSERT(rnh); -#endif + RADIX_NODE_HEAD_RLOCK(rnh); rn = rnh->rnh_matchaddr(dst, rnh); if (rn && ((rn->rn_flags & RNF_ROOT) == 0)) { newrt = RNTORT(rn); RT_LOCK(newrt); RT_ADDREF(newrt); - if (needlock) - RADIX_NODE_HEAD_RUNLOCK(rnh); - goto done; + RADIX_NODE_HEAD_RUNLOCK(rnh); + return (newrt); - } else if (needlock) + } else RADIX_NODE_HEAD_RUNLOCK(rnh); /* @@ -454,10 +419,7 @@ miss: bzero(&info, sizeof(info)); info.rti_info[RTAX_DST] = dst; rt_missmsg_fib(msgtype, &info, 0, err, fibnum); - } -done: - if (newrt) - RT_LOCK_ASSERT(newrt); + } return (newrt); } @@ -548,17 +510,6 @@ done: * message from the network layer. */ void -rtredirect(struct sockaddr *dst, - struct sockaddr *gateway, - struct sockaddr *netmask, - int flags, - struct sockaddr *src) -{ - - rtredirect_fib(dst, gateway, netmask, flags, src, RT_DEFAULT_FIB); -} - -void rtredirect_fib(struct sockaddr *dst, struct sockaddr *gateway, struct sockaddr *netmask, @@ -566,7 +517,7 @@ rtredirect_fib(struct sockaddr *dst, struct sockaddr *src, u_int fibnum) { - struct rtentry *rt, *rt0 = NULL; + struct rtentry *rt; int error = 0; short *stat = NULL; struct rt_addrinfo info; @@ -625,8 +576,8 @@ rtredirect_fib(struct sockaddr *dst, * Create new route, rather than smashing route to net. */ create: - rt0 = rt; - rt = NULL; + if (rt != NULL) + RTFREE_LOCKED(rt); flags |= RTF_DYNAMIC; bzero((caddr_t)&info, sizeof(info)); @@ -635,21 +586,14 @@ rtredirect_fib(struct sockaddr *dst, info.rti_info[RTAX_NETMASK] = netmask; info.rti_ifa = ifa; info.rti_flags = flags; - if (rt0 != NULL) - RT_UNLOCK(rt0); /* drop lock to avoid LOR with RNH */ error = rtrequest1_fib(RTM_ADD, &info, &rt, fibnum); if (rt != NULL) { RT_LOCK(rt); - if (rt0 != NULL) - EVENTHANDLER_INVOKE(route_redirect_event, rt0, rt, dst); flags = rt->rt_flags; } - if (rt0 != NULL) - RTFREE(rt0); stat = &V_rtstat.rts_dynamic; } else { - struct rtentry *gwrt; /* * Smash the current notion of the gateway to @@ -667,11 +611,7 @@ rtredirect_fib(struct sockaddr *dst, RADIX_NODE_HEAD_LOCK(rnh); RT_LOCK(rt); rt_setgate(rt, rt_key(rt), gateway); - gwrt = rtalloc1(gateway, 1, RTF_RNH_LOCKED); RADIX_NODE_HEAD_UNLOCK(rnh); - EVENTHANDLER_INVOKE(route_redirect_event, rt, gwrt, dst); - if (gwrt) - RTFREE_LOCKED(gwrt); } } else error = EHOSTUNREACH; @@ -693,13 +633,6 @@ out: ifa_free(ifa); } -int -rtioctl(u_long req, caddr_t data) -{ - - return (rtioctl_fib(req, data, RT_DEFAULT_FIB)); -} - /* * Routing table ioctl interface. */ @@ -752,7 +685,7 @@ ifa_ifwithroute(int flags, const struct sockaddr *dst, struct sockaddr *gateway, if (ifa == NULL) ifa = ifa_ifwithnet(gateway, 0, fibnum); if (ifa == NULL) { - struct rtentry *rt = rtalloc1_fib(gateway, 0, RTF_RNH_LOCKED, fibnum); + struct rtentry *rt = rtalloc1_fib(gateway, 0, 0, fibnum); if (rt == NULL) return (NULL); /* @@ -796,19 +729,6 @@ ifa_ifwithroute(int flags, const struct sockaddr *dst, struct sockaddr *gateway, * all the bits of info needed */ int -rtrequest(int req, - struct sockaddr *dst, - struct sockaddr *gateway, - struct sockaddr *netmask, - int flags, - struct rtentry **ret_nrt) -{ - - return (rtrequest_fib(req, dst, gateway, netmask, flags, ret_nrt, - RT_DEFAULT_FIB)); -} - -int rtrequest_fib(int req, struct sockaddr *dst, struct sockaddr *gateway, @@ -832,6 +752,147 @@ rtrequest_fib(int req, /* + * Copy most of @rt data into @info. + * + * If @flags contains NHR_COPY, copies dst,netmask and gw to the + * pointers specified by @info structure. Assume such pointers + * are zeroed sockaddr-like structures with sa_len field initialized + * to reflect size of the provided buffer. if no NHR_COPY is specified, + * point dst,netmask and gw @info fields to appropriate @rt values. + * + * if @flags contains NHR_REF, do refcouting on rt_ifp. + * + * Returns 0 on success. + */ +int +rt_exportinfo(struct rtentry *rt, struct rt_addrinfo *info, int flags) +{ + struct rt_metrics *rmx; + struct sockaddr *src, *dst; + int sa_len; + + if (flags & NHR_COPY) { + /* Copy destination if dst is non-zero */ + src = rt_key(rt); + dst = info->rti_info[RTAX_DST]; + sa_len = src->sa_len; + if (dst != NULL) { + if (src->sa_len > dst->sa_len) + return (ENOMEM); + memcpy(dst, src, src->sa_len); + info->rti_addrs |= RTA_DST; + } + + /* Copy mask if set && dst is non-zero */ + src = rt_mask(rt); + dst = info->rti_info[RTAX_NETMASK]; + if (src != NULL && dst != NULL) { + + /* + * Radix stores different value in sa_len, + * assume rt_mask() to have the same length + * as rt_key() + */ + if (sa_len > dst->sa_len) + return (ENOMEM); + memcpy(dst, src, src->sa_len); + info->rti_addrs |= RTA_NETMASK; + } + + /* Copy gateway is set && dst is non-zero */ + src = rt->rt_gateway; + dst = info->rti_info[RTAX_GATEWAY]; + if ((rt->rt_flags & RTF_GATEWAY) && src != NULL && dst != NULL){ + if (src->sa_len > dst->sa_len) + return (ENOMEM); + memcpy(dst, src, src->sa_len); + info->rti_addrs |= RTA_GATEWAY; + } + } else { + info->rti_info[RTAX_DST] = rt_key(rt); + info->rti_addrs |= RTA_DST; + if (rt_mask(rt) != NULL) { + info->rti_info[RTAX_NETMASK] = rt_mask(rt); + info->rti_addrs |= RTA_NETMASK; + } + if (rt->rt_flags & RTF_GATEWAY) { + info->rti_info[RTAX_GATEWAY] = rt->rt_gateway; + info->rti_addrs |= RTA_GATEWAY; + } + } + + rmx = info->rti_rmx; + if (rmx != NULL) { + info->rti_mflags |= RTV_MTU; + rmx->rmx_mtu = rt->rt_mtu; + } + + info->rti_flags = rt->rt_flags; + info->rti_ifp = rt->rt_ifp; + info->rti_ifa = rt->rt_ifa; + + if (flags & NHR_REF) { + /* Do 'traditional' refcouting */ + if_ref(info->rti_ifp); + } + + return (0); +} + +/* + * Lookups up route entry for @dst in RIB database for fib @fibnum. + * Exports entry data to @info using rt_exportinfo(). + * + * if @flags contains NHR_REF, refcouting is performed on rt_ifp. + * All references can be released later by calling rib_free_info() + * + * Returns 0 on success. + * Returns ENOENT for lookup failure, ENOMEM for export failure. + */ +int +rib_lookup_info(uint32_t fibnum, const struct sockaddr *dst, uint32_t flags, + uint32_t flowid, struct rt_addrinfo *info) +{ + struct radix_node_head *rh; + struct radix_node *rn; + struct rtentry *rt; + int error; + + KASSERT((fibnum < rt_numfibs), ("rib_lookup_rte: bad fibnum")); + rh = rt_tables_get_rnh(fibnum, dst->sa_family); + if (rh == NULL) + return (ENOENT); + + RADIX_NODE_HEAD_RLOCK(rh); + rn = rh->rnh_matchaddr(__DECONST(void *, dst), rh); + if (rn != NULL && ((rn->rn_flags & RNF_ROOT) == 0)) { + rt = RNTORT(rn); + /* Ensure route & ifp is UP */ + if (RT_LINK_IS_UP(rt->rt_ifp)) { + flags = (flags & NHR_REF) | NHR_COPY; + error = rt_exportinfo(rt, info, flags); + RADIX_NODE_HEAD_RUNLOCK(rh); + + return (error); + } + } + RADIX_NODE_HEAD_RUNLOCK(rh); + + return (ENOENT); +} + +/* + * Releases all references acquired by rib_lookup_info() when + * called with NHR_REF flags. + */ +void +rib_free_info(struct rt_addrinfo *info) +{ + + if_rele(info->rti_ifp); +} + +/* * Iterates over all existing fibs in system calling * @setwa_f function prior to traversing each fib. * Calls @wa_f function for each element in current fib. @@ -1432,7 +1493,7 @@ int rtrequest1_fib(int req, struct rt_addrinfo *info, struct rtentry **ret_nrt, u_int fibnum) { - int error = 0, needlock = 0; + int error = 0; struct rtentry *rt, *rt_old; #ifdef FLOWTABLE struct rtentry *rt0; @@ -1442,9 +1503,9 @@ rtrequest1_fib(int req, struct rt_addrinfo *info, struct rtentry **ret_nrt, struct ifaddr *ifa; struct sockaddr *ndst; struct sockaddr_storage mdst; -#define senderr(x) { error = x ; goto bad; } KASSERT((fibnum < rt_numfibs), ("rtrequest1_fib: bad fibnum")); + KASSERT((flags & RTF_RNH_LOCKED) == 0, ("rtrequest1_fib: locked")); switch (dst->sa_family) { case AF_INET6: case AF_INET: @@ -1461,12 +1522,7 @@ rtrequest1_fib(int req, struct rt_addrinfo *info, struct rtentry **ret_nrt, rnh = rt_tables_get_rnh(fibnum, dst->sa_family); if (rnh == NULL) return (EAFNOSUPPORT); - needlock = ((flags & RTF_RNH_LOCKED) == 0); - flags &= ~RTF_RNH_LOCKED; - if (needlock) - RADIX_NODE_HEAD_LOCK(rnh); - else - RADIX_NODE_HEAD_LOCK_ASSERT(rnh); + /* * If we are adding a host route then we don't want to put * a netmask in the tree, nor do we want to clone it. @@ -1481,9 +1537,11 @@ rtrequest1_fib(int req, struct rt_addrinfo *info, struct rtentry **ret_nrt, dst = (struct sockaddr *)&mdst; } + RADIX_NODE_HEAD_LOCK(rnh); rt = rt_unlinkrte(rnh, info, &error); + RADIX_NODE_HEAD_UNLOCK(rnh); if (error != 0) - goto bad; + return (error); rt_notifydelete(rt, info); @@ -1506,33 +1564,32 @@ rtrequest1_fib(int req, struct rt_addrinfo *info, struct rtentry **ret_nrt, break; case RTM_ADD: if ((flags & RTF_GATEWAY) && !gateway) - senderr(EINVAL); + return (EINVAL); if (dst && gateway && (dst->sa_family != gateway->sa_family) && (gateway->sa_family != AF_UNSPEC) && (gateway->sa_family != AF_LINK)) - senderr(EINVAL); + return (EINVAL); if (info->rti_ifa == NULL) { error = rt_getifa_fib(info, fibnum); if (error) - senderr(error); + return (error); } else ifa_ref(info->rti_ifa); ifa = info->rti_ifa; rt = uma_zalloc(V_rtzone, M_NOWAIT); if (rt == NULL) { ifa_free(ifa); - senderr(ENOBUFS); + return (ENOBUFS); } rt->rt_flags = RTF_UP | flags; rt->rt_fibnum = fibnum; /* * Add the gateway. Possibly re-malloc-ing the storage for it. */ - RT_LOCK(rt); if ((error = rt_setgate(rt, dst, gateway)) != 0) { ifa_free(ifa); uma_zfree(V_rtzone, rt); - senderr(error); + return (error); } /* @@ -1559,14 +1616,18 @@ rtrequest1_fib(int req, struct rt_addrinfo *info, struct rtentry **ret_nrt, rt_setmetrics(info, rt); + RADIX_NODE_HEAD_LOCK(rnh); + RT_LOCK(rt); #ifdef RADIX_MPATH /* do not permit exactly the same dst/mask/gw pair */ if (rn_mpath_capable(rnh) && rt_mpath_conflict(rnh, rt, netmask)) { + RADIX_NODE_HEAD_UNLOCK(rnh); + ifa_free(rt->rt_ifa); R_Free(rt_key(rt)); uma_zfree(V_rtzone, rt); - senderr(EEXIST); + return (EEXIST); } #endif @@ -1595,6 +1656,7 @@ rtrequest1_fib(int req, struct rt_addrinfo *info, struct rtentry **ret_nrt, rn = rnh->rnh_addaddr(ndst, netmask, rnh, rt->rt_nodes); } + RADIX_NODE_HEAD_UNLOCK(rnh); if (rt_old != NULL) RT_UNLOCK(rt_old); @@ -1611,7 +1673,7 @@ rtrequest1_fib(int req, struct rt_addrinfo *info, struct rtentry **ret_nrt, if (rt0 != NULL) RTFREE(rt0); #endif - senderr(EEXIST); + return (EEXIST); } #ifdef FLOWTABLE else if (rt0 != NULL) { @@ -1643,16 +1705,15 @@ rtrequest1_fib(int req, struct rt_addrinfo *info, struct rtentry **ret_nrt, RT_UNLOCK(rt); break; case RTM_CHANGE: + RADIX_NODE_HEAD_LOCK(rnh); error = rtrequest1_fib_change(rnh, info, ret_nrt, fibnum); + RADIX_NODE_HEAD_UNLOCK(rnh); break; default: error = EOPNOTSUPP; } -bad: - if (needlock) - RADIX_NODE_HEAD_UNLOCK(rnh); + return (error); -#undef senderr } #undef dst @@ -1802,15 +1863,7 @@ rt_setgate(struct rtentry *rt, struct sockaddr *dst, struct sockaddr *gate) { /* XXX dst may be overwritten, can we move this to below */ int dlen = SA_SIZE(dst), glen = SA_SIZE(gate); -#ifdef INVARIANTS - struct radix_node_head *rnh; - rnh = rt_tables_get_rnh(rt->rt_fibnum, dst->sa_family); -#endif - - RT_LOCK_ASSERT(rt); - RADIX_NODE_HEAD_LOCK_ASSERT(rnh); - /* * Prepare to store the gateway in rt->rt_gateway. * Both dst and gateway are stored one after the other in the same diff --git a/sys/net/route.h b/sys/net/route.h index 66f4d775bb84f..ed21a299b7e97 100644 --- a/sys/net/route.h +++ b/sys/net/route.h @@ -44,21 +44,33 @@ */ /* - * A route consists of a destination address, a reference - * to a routing entry, and a reference to an llentry. - * These are often held by protocols in their control - * blocks, e.g. inpcb. + * Struct route consiste of a destination address, + * a route entry pointer, link-layer prepend data pointer along + * with its length. */ struct route { struct rtentry *ro_rt; - struct llentry *ro_lle; - struct in_ifaddr *ro_ia; - int ro_flags; + char *ro_prepend; + uint16_t ro_plen; + uint16_t ro_flags; + uint16_t ro_mtu; /* saved ro_rt mtu */ + uint16_t spare; struct sockaddr ro_dst; }; +#define RT_L2_ME_BIT 2 /* dst L2 addr is our address */ +#define RT_MAY_LOOP_BIT 3 /* dst may require loop copy */ +#define RT_HAS_HEADER_BIT 4 /* mbuf already have its header prepended */ + #define RT_CACHING_CONTEXT 0x1 /* XXX: not used anywhere */ #define RT_NORTREF 0x2 /* doesn't hold reference on ro_rt */ +#define RT_L2_ME (1 << RT_L2_ME_BIT) /* 0x0004 */ +#define RT_MAY_LOOP (1 << RT_MAY_LOOP_BIT) /* 0x0008 */ +#define RT_HAS_HEADER (1 << RT_HAS_HEADER_BIT) /* 0x0010 */ + +#define RT_REJECT 0x0020 /* Destination is reject */ +#define RT_BLACKHOLE 0x0040 /* Destination is blackhole */ +#define RT_HAS_GW 0x0080 /* Destination has GW */ struct rt_metrics { u_long rmx_locks; /* Kernel must leave these values alone */ @@ -163,7 +175,7 @@ struct rtentry { /* 0x8000000 and up unassigned */ #define RTF_STICKY 0x10000000 /* always route dst->src */ -#define RTF_RNH_LOCKED 0x40000000 /* radix node head is locked */ +#define RTF_RNH_LOCKED 0x40000000 /* unused */ #define RTF_GWFLAG_COMPAT 0x80000000 /* a compatibility bit for interacting with existing routing apps */ @@ -189,6 +201,9 @@ struct rtentry { #define NHR_IFAIF 0x01 /* Return ifa_ifp interface */ #define NHR_REF 0x02 /* For future use */ +/* Control plane route request flags */ +#define NHR_COPY 0x100 /* Copy rte data */ + /* rte<>nhop translation */ static inline uint16_t fib_rte_to_nh_flags(int rt_flags) @@ -204,6 +219,21 @@ fib_rte_to_nh_flags(int rt_flags) return (res); } +#ifdef _KERNEL +/* rte<>ro_flags translation */ +static inline void +rt_update_ro_flags(struct route *ro) +{ + int rt_flags = ro->ro_rt->rt_flags; + + ro->ro_flags &= ~ (RT_REJECT|RT_BLACKHOLE|RT_HAS_GW); + + ro->ro_flags |= (rt_flags & RTF_REJECT) ? RT_REJECT : 0; + ro->ro_flags |= (rt_flags & RTF_BLACKHOLE) ? RT_BLACKHOLE : 0; + ro->ro_flags |= (rt_flags & RTF_GATEWAY) ? RT_HAS_GW : 0; +} +#endif + /* * Routing statistics. */ @@ -406,8 +436,6 @@ int rtsock_routemsg(int, struct ifnet *ifp, int, struct rtentry *, int); /* * Note the following locking behavior: * - * rtalloc_ign() and rtalloc() return ro->ro_rt unlocked - * * rtalloc1() returns a locked rtentry * * rtfree() and RTFREE_LOCKED() require a locked rtentry @@ -415,9 +443,7 @@ int rtsock_routemsg(int, struct ifnet *ifp, int, struct rtentry *, int); * RTFREE() uses an unlocked entry. */ -int rt_expunge(struct radix_node_head *, struct rtentry *); void rtfree(struct rtentry *); -int rt_check(struct rtentry **, struct rtentry **, struct sockaddr *); void rt_updatemtu(struct ifnet *); typedef int rt_walktree_f_t(struct rtentry *, void *); @@ -428,15 +454,8 @@ void rt_flushifroutes(struct ifnet *ifp); /* XXX MRT COMPAT VERSIONS THAT SET UNIVERSE to 0 */ /* Thes are used by old code not yet converted to use multiple FIBS */ -void rtalloc_ign(struct route *ro, u_long ignflags); -void rtalloc(struct route *ro); /* XXX deprecated, use rtalloc_ign(ro, 0) */ struct rtentry *rtalloc1(struct sockaddr *, int, u_long); int rtinit(struct ifaddr *, int, int); -int rtioctl(u_long, caddr_t); -void rtredirect(struct sockaddr *, struct sockaddr *, - struct sockaddr *, int, struct sockaddr *); -int rtrequest(int, struct sockaddr *, - struct sockaddr *, struct sockaddr *, int, struct rtentry **); /* XXX MRT NEW VERSIONS THAT USE FIBs * For now the protocol indepedent versions are the same as the AF_INET ones @@ -444,7 +463,6 @@ int rtrequest(int, struct sockaddr *, */ int rt_getifa_fib(struct rt_addrinfo *, u_int fibnum); void rtalloc_ign_fib(struct route *ro, u_long ignflags, u_int fibnum); -void rtalloc_fib(struct route *ro, u_int fibnum); struct rtentry *rtalloc1_fib(struct sockaddr *, int, u_long, u_int); int rtioctl_fib(u_long, caddr_t, u_int); void rtredirect_fib(struct sockaddr *, struct sockaddr *, @@ -452,10 +470,10 @@ void rtredirect_fib(struct sockaddr *, struct sockaddr *, int rtrequest_fib(int, struct sockaddr *, struct sockaddr *, struct sockaddr *, int, struct rtentry **, u_int); int rtrequest1_fib(int, struct rt_addrinfo *, struct rtentry **, u_int); +int rib_lookup_info(uint32_t, const struct sockaddr *, uint32_t, uint32_t, + struct rt_addrinfo *); +void rib_free_info(struct rt_addrinfo *info); -#include <sys/eventhandler.h> -typedef void (*rtevent_redirect_fn)(void *, struct rtentry *, struct rtentry *, struct sockaddr *); -EVENTHANDLER_DECLARE(route_redirect_event, rtevent_redirect_fn); #endif #endif diff --git a/sys/net/rtsock.c b/sys/net/rtsock.c index 6fcbbc6ec431f..a5ca9faf40236 100644 --- a/sys/net/rtsock.c +++ b/sys/net/rtsock.c @@ -614,11 +614,16 @@ route_output(struct mbuf *m, struct socket *so, ...) */ if (info.rti_info[RTAX_GATEWAY] != NULL && info.rti_info[RTAX_GATEWAY]->sa_family != AF_LINK) { - struct route gw_ro; + struct rt_addrinfo ginfo; + struct sockaddr *gdst; + + bzero(&ginfo, sizeof(ginfo)); + bzero(&ss, sizeof(ss)); + ss.ss_len = sizeof(ss); + + ginfo.rti_info[RTAX_GATEWAY] = (struct sockaddr *)&ss; + gdst = info.rti_info[RTAX_GATEWAY]; - bzero(&gw_ro, sizeof(gw_ro)); - gw_ro.ro_dst = *info.rti_info[RTAX_GATEWAY]; - rtalloc_ign_fib(&gw_ro, 0, fibnum); /* * A host route through the loopback interface is * installed for each interface adddress. In pre 8.0 @@ -629,14 +634,14 @@ route_output(struct mbuf *m, struct socket *so, ...) * AF_LINK sa_family type of the rt_gateway, and the * rt_ifp has the IFF_LOOPBACK flag set. */ - if (gw_ro.ro_rt != NULL && - gw_ro.ro_rt->rt_gateway->sa_family == AF_LINK && - gw_ro.ro_rt->rt_ifp->if_flags & IFF_LOOPBACK) { - info.rti_flags &= ~RTF_GATEWAY; - info.rti_flags |= RTF_GWFLAG_COMPAT; + if (rib_lookup_info(fibnum, gdst, NHR_REF, 0, &ginfo) == 0) { + if (ss.ss_family == AF_LINK && + ginfo.rti_ifp->if_flags & IFF_LOOPBACK) { + info.rti_flags &= ~RTF_GATEWAY; + info.rti_flags |= RTF_GWFLAG_COMPAT; + } + rib_free_info(&ginfo); } - if (gw_ro.ro_rt != NULL) - RTFREE(gw_ro.ro_rt); } switch (rtm->rtm_type) { |
