In order to do *only one* route lookup without using a global variable
(yes, I'm looking at you netinet6) and without doing too much spaghetti,
here's a refactoring of ip_input().
It basically merges in_ouraddr() into ip_input(), but some operations
are shuffled, when possible, to reduces differences with the IPv6
version.
It also increments rt_use counters for the local routes, (say yeah!).
Comment, bikeshed, ok?
Index: netinet/ip_input.c
===================================================================
RCS file: /home/ncvs/src/sys/netinet/ip_input.c,v
retrieving revision 1.240
diff -u -p -r1.240 ip_input.c
--- netinet/ip_input.c 4 Nov 2014 15:24:40 -0000 1.240
+++ netinet/ip_input.c 5 Nov 2014 13:52:25 -0000
@@ -123,7 +123,6 @@ struct ipstat ipstat;
void ip_ours(struct mbuf *);
int ip_dooptions(struct mbuf *, struct ifnet *);
-int in_ouraddr(struct mbuf *, struct ifnet *, struct in_addr);
void ip_forward(struct mbuf *, struct ifnet *, int);
/*
@@ -222,6 +221,8 @@ ipintr(void)
void
ipv4_input(struct mbuf *m)
{
+ struct sockaddr_in sin;
+ struct rtentry *rt;
struct ifnet *ifp;
struct ip *ip;
int hlen, len;
@@ -341,11 +342,25 @@ ipv4_input(struct mbuf *m)
return;
}
- if (in_ouraddr(m, ifp, ip->ip_dst)) {
+ if (ip->ip_dst.s_addr == INADDR_BROADCAST ||
+ ip->ip_dst.s_addr == INADDR_ANY) {
+ ip_ours(m);
+ return;
+ }
+
+#if NPF > 0
+ /* No need for a route lookup if we already know it's for us. */
+ if (m->m_pkthdr.pf.flags & PF_TAG_DIVERTED ||
+ (m->m_pkthdr.pf.statekey && m->m_pkthdr.pf.statekey->inp)) {
ip_ours(m);
return;
}
+ /* If we have linked state keys it is certainly forwarded. */
+ if (m->m_pkthdr.pf.statekey && m->m_pkthdr.pf.statekey->reverse)
+ goto forward;
+#endif
+
if (IN_MULTICAST(ip->ip_dst.s_addr)) {
struct in_multi *inm;
#ifdef MROUTING
@@ -401,12 +416,79 @@ ipv4_input(struct mbuf *m)
return;
}
- if (ip->ip_dst.s_addr == INADDR_BROADCAST ||
- ip->ip_dst.s_addr == INADDR_ANY) {
- ip_ours(m);
- return;
+ memset(&sin, 0, sizeof(sin));
+ sin.sin_len = sizeof(sin);
+ sin.sin_family = AF_INET;
+ sin.sin_addr = ip->ip_dst;
+
+ rt = rtalloc(sintosa(&sin), 0, m->m_pkthdr.ph_rtableid);
+ if (rt != NULL) {
+ /*
+ * We consider a packet as local if its destination
+ * matches a local route and the associated interface
+ * is up.
+ */
+ if (ISSET(rt->rt_flags, RTF_LOCAL)) {
+ /*
+ * XXX Since local routes' rt_ifp points to lo0
+ * we cannot rely on the rtentry flags.
+ */
+ if (ISSET(rt->rt_ifa->ifa_ifp->if_flags, IFF_UP)) {
+ rt->rt_use++;
+ rtfree(rt);
+ ip_ours(m);
+ return;
+ }
+ }
+
+ /*
+ * This matches a broadcast address on one of our interfaces.
+ * If directedbcast is enabled we only consider it local if it
+ * is received on the interface with that address.
+ */
+ if (ISSET(rt->rt_flags, RTF_BROADCAST) &&
+ ISSET(rt->rt_flags, RTF_UP) &&
+ (!ip_directedbcast || rt->rt_ifp == ifp)) {
+ rt->rt_use++;
+ rtfree(rt);
+
+ /* Make sure M_BCAST is set */
+ m->m_flags |= M_BCAST;
+ ip_ours(m);
+ return;
+ }
+
+ rtfree(rt);
+ }
+
+ /*
+ * No local address or broadcast address found, so check for
+ * ancient classful broadcast addresses.
+ * It must have been broadcast on the link layer, and for an
+ * address on the interface it was received on.
+ */
+ if (ISSET(m->m_flags, M_BCAST) &&
+ IN_CLASSFULBROADCAST(ip->ip_dst.s_addr, ip->ip_dst.s_addr)) {
+ struct ifaddr *ifa;
+
+ /*
+ * The check in the loop assumes you only rx a packet
+ * on an UP interface, and that M_BCAST will only be
+ * set on a BROADCAST interface.
+ */
+ TAILQ_FOREACH(ifa, &ifp->if_addrlist, ifa_list) {
+ if (ifa->ifa_addr->sa_family != AF_INET)
+ continue;
+
+ if (IN_CLASSFULBROADCAST(ip->ip_dst.s_addr,
+ ifatoia(ifa)->ia_addr.sin_addr.s_addr)) {
+ ip_ours(m);
+ return;
+ }
+ }
}
+forward:
#if NCARP > 0
if (ifp->if_type == IFT_CARP && ip->ip_p == IPPROTO_ICMP &&
carp_lsdrop(m, AF_INET, &ip->ip_src.s_addr, &ip->ip_dst.s_addr))
@@ -636,89 +718,6 @@ found:
return;
bad:
m_freem(m);
-}
-
-int
-in_ouraddr(struct mbuf *m, struct ifnet *ifp, struct in_addr ina)
-{
- struct in_ifaddr *ia = NULL;
- struct rtentry *rt;
- struct sockaddr_in sin;
-#if NPF > 0
- struct pf_state_key *key;
-
- if (m->m_pkthdr.pf.flags & PF_TAG_DIVERTED)
- return (1);
-
- key = m->m_pkthdr.pf.statekey;
- if (key != NULL) {
- if (key->inp != NULL)
- return (1);
-
- /* If we have linked state keys it is certainly forwarded. */
- if (key->reverse != NULL)
- return (0);
- }
-#endif
-
- memset(&sin, 0, sizeof(sin));
- sin.sin_len = sizeof(sin);
- sin.sin_family = AF_INET;
- sin.sin_addr = ina;
- rt = rtalloc(sintosa(&sin), 0, m->m_pkthdr.ph_rtableid);
- if (rt != NULL) {
- if (rt->rt_flags & (RTF_LOCAL|RTF_BROADCAST))
- ia = ifatoia(rt->rt_ifa);
- rtfree(rt);
- }
-
- if (ia == NULL) {
- struct ifaddr *ifa;
-
- /*
- * No local address or broadcast address found, so check for
- * ancient classful broadcast addresses.
- * It must have been broadcast on the link layer, and for an
- * address on the interface it was received on.
- */
- if (!ISSET(m->m_flags, M_BCAST) ||
- !IN_CLASSFULBROADCAST(ina.s_addr, ina.s_addr))
- return (0);
-
- if (ifp->if_rdomain != rtable_l2(m->m_pkthdr.ph_rtableid))
- return (0);
- /*
- * The check in the loop assumes you only rx a packet on an UP
- * interface, and that M_BCAST will only be set on a BROADCAST
- * interface.
- */
- TAILQ_FOREACH(ifa, &ifp->if_addrlist, ifa_list) {
- if (ifa->ifa_addr->sa_family != AF_INET)
- continue;
-
- if (IN_CLASSFULBROADCAST(ina.s_addr,
- ifatoia(ifa)->ia_addr.sin_addr.s_addr))
- return (1);
- }
-
- return (0);
- }
-
- if (ina.s_addr != ia->ia_addr.sin_addr.s_addr) {
- /*
- * This matches a broadcast address on one of our interfaces.
- * If directedbcast is enabled we only consider it local if it
- * is received on the interface with that address.
- */
- if (ip_directedbcast && ia->ia_ifp != ifp)
- return (0);
-
- /* Make sure M_BCAST is set */
- if (m)
- m->m_flags |= M_BCAST;
- }
-
- return (ISSET(ia->ia_ifp->if_flags, IFF_UP));
}
struct in_ifaddr *