diff options
author | melifaro <melifaro@FreeBSD.org> | 2015-09-16 14:26:28 +0000 |
---|---|---|
committer | melifaro <melifaro@FreeBSD.org> | 2015-09-16 14:26:28 +0000 |
commit | 493325342d2aeb9d06e09827e294b9407ec60e9b (patch) | |
tree | e04e3fcd4841d6aa1ce593fcc38aa9bb51b4cd33 /sys/net/if_arcsubr.c | |
parent | 1391356f66bee9ea2da2af8675144717f9efcfb3 (diff) | |
download | FreeBSD-src-493325342d2aeb9d06e09827e294b9407ec60e9b.zip FreeBSD-src-493325342d2aeb9d06e09827e294b9407ec60e9b.tar.gz |
Simplify the way of attaching IPv6 link-layer header.
Problem description:
How do we currently perform layer 2 resolution and header imposition:
For IPv4 we have the following chain:
ip_output() -> (ether|atm|whatever)_output() -> arpresolve()
Lookup is done in proper place (link-layer output routine) and it is possible
to provide cached lle data.
For IPv6 situation is more complex:
ip6_output() -> nd6_output() -> nd6_output_ifp() -> (whatever)_output() ->
nd6_storelladdr()
We have ip6_ouput() which calls nd6_output() instead of link output routine.
nd6_output() does the following:
* checks if lle exists, creates it if needed (similar to arpresolve())
* performes lle state transitions (similar to arpresolve())
* calls nd6_output_ifp() which pushes packets to link output routine along
with running SeND/MAC hooks regardless of lle state
(e.g. works as run-hooks placeholder).
After that, iface output routine like ether_output() calls nd6_storelladdr()
which performs lle lookup once again.
As a result, we perform lookup twice for each outgoing packet for most types
of interfaces. We also need to maintain runtime-checked table of 'nd6-free'
interfaces (see nd6_need_cache()).
Fix this behavior by eliminating first ND lookup. To be more specific:
* make all nd6_output() consumers use nd6_output_ifp() instead
* rename nd6_output[_slow]() to nd6_resolve_[slow]()
* convert nd6_resolve() and nd6_resolve_slow() to arpresolve() semantics,
e.g. copy L2 address to buffer instead of pushing packet towards lower
layers
* Make all nd6_storelladdr() users use nd6_resolve()
* eliminate nd6_storelladdr()
The resulting callchain is the following:
ip6_output() -> nd6_output_ifp() -> (whatever)_output() -> nd6_resolve()
Error handling:
Currently sending packet to non-existing la results in ip6_<output|forward>
-> nd6_output() -> nd6_output _lle() which returns 0.
In new scenario packet is propagated to <ether|whatever>_output() ->
nd6_resolve() which will return EWOULDBLOCK, and that result
will be converted to 0.
(And EWOULDBLOCK is actually used by IB/TOE code).
Sponsored by: Yandex LLC
Differential Revision: https://reviews.freebsd.org/D1469
Diffstat (limited to 'sys/net/if_arcsubr.c')
-rw-r--r-- | sys/net/if_arcsubr.c | 22 |
1 files changed, 12 insertions, 10 deletions
diff --git a/sys/net/if_arcsubr.c b/sys/net/if_arcsubr.c index d6724cf..d59c4a0 100644 --- a/sys/net/if_arcsubr.c +++ b/sys/net/if_arcsubr.c @@ -103,8 +103,8 @@ arc_output(struct ifnet *ifp, struct mbuf *m, const struct sockaddr *dst, u_int8_t atype, adst; int loop_copy = 0; int isphds; -#ifdef INET - int is_gw; +#if defined(INET) || defined(INET6) + int is_gw = 0; #endif if (!((ifp->if_flags & IFF_UP) && @@ -112,6 +112,11 @@ arc_output(struct ifnet *ifp, struct mbuf *m, const struct sockaddr *dst, return(ENETDOWN); /* m, m1 aren't initialized yet */ 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; +#endif switch (dst->sa_family) { #ifdef INET @@ -125,10 +130,6 @@ arc_output(struct ifnet *ifp, struct mbuf *m, const struct sockaddr *dst, else if (ifp->if_flags & IFF_NOARP) adst = ntohl(SIN(dst)->sin_addr.s_addr) & 0xFF; else { - 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, &adst, NULL); if (error) return (error == EWOULDBLOCK ? 0 : error); @@ -169,10 +170,11 @@ arc_output(struct ifnet *ifp, struct mbuf *m, const struct sockaddr *dst, case AF_INET6: if ((m->m_flags & M_MCAST) != 0) adst = arcbroadcastaddr; /* ARCnet broadcast address */ - else - error = nd6_storelladdr(ifp, m, dst, (u_char *)&adst, NULL); - if (error) - return (error); + else { + error = nd6_resolve(ifp, is_gw, m, dst, &adst, NULL); + if (error != 0) + return (error == EWOULDBLOCK ? 0 : error); + } atype = ARCTYPE_INET6; break; #endif |