Skip to content

Commit

Permalink
pf/ipfw/netinet[6]: IP forwarding rework, fixes IPv4 in pf(4)
Browse files Browse the repository at this point in the history
Based on feedback by countless users, this removes the if_output
calls in the pf code that escape pfil processing in IPv4 by going
the long way.  This is only partially applicable to FreeBSD and
we are working on fixing this in a full fashion in 12-CURRENT.

It also disables ip_tryforward() as that does not seem trivial
to convert as it is missing 12-CURRENT's nhop4/nhop6.  Maybe we
will see that in 11.1.

Many thanks to Andrey V. Elsukov (ae@) for giving this direction
and review.

Also see: https://reviews.freebsd.org/D8877
  • Loading branch information
fichtner committed Jan 10, 2017
1 parent d15d07f commit e92bed1
Show file tree
Hide file tree
Showing 13 changed files with 288 additions and 237 deletions.
21 changes: 10 additions & 11 deletions sys/netinet/ip_fastfwd.c
Original file line number Diff line number Diff line change
Expand Up @@ -162,7 +162,6 @@ ip_tryforward(struct mbuf *m)
uint16_t ip_len, ip_off;
int error = 0;
int mtu;
struct m_tag *fwd_tag = NULL;

/*
* Are we active and forwarding packets?
Expand Down Expand Up @@ -340,9 +339,7 @@ ip_tryforward(struct mbuf *m)
/*
* Destination address changed?
*/
if (m->m_flags & M_IP_NEXTHOP)
fwd_tag = m_tag_find(m, PACKET_TAG_IPFORWARD, NULL);
if (odest.s_addr != dest.s_addr || fwd_tag != NULL) {
if (odest.s_addr != dest.s_addr || IP_HAS_NEXTHOP(m)) {
/*
* Is it now for a local address on this host?
*/
Expand All @@ -356,16 +353,18 @@ ip_tryforward(struct mbuf *m)
RTFREE(ro.ro_rt);
return m;
}
RTFREE(ro.ro_rt);
if (IP_HAS_NEXTHOP(m)) {
struct sockaddr_in tmp;
if (ip_get_fwdtag(m, &tmp, NULL)) {
return NULL;
}
dest.s_addr = tmp.sin_addr.s_addr;
ip_flush_fwdtag(m);
}
/*
* Redo route lookup with new destination address
*/
if (fwd_tag) {
dest.s_addr = ((struct sockaddr_in *)
(fwd_tag + 1))->sin_addr.s_addr;
m_tag_delete(m, fwd_tag);
m->m_flags &= ~M_IP_NEXTHOP;
}
RTFREE(ro.ro_rt);
if ((dst = ip_findroute(&ro, dest, m)) == NULL)
return NULL; /* icmp unreach already sent */
ifp = ro.ro_rt->rt_ifp;
Expand Down
28 changes: 8 additions & 20 deletions sys/netinet/ip_input.c
Original file line number Diff line number Diff line change
Expand Up @@ -560,22 +560,12 @@ ip_input(struct mbuf *m)
m_adj(m, ip_len - m->m_pkthdr.len);
}

/* Try to forward the packet, but if we fail continue */
#ifdef IPSEC
/* For now we do not handle IPSEC in tryforward. */
if (!key_havesp(IPSEC_DIR_INBOUND) && !key_havesp(IPSEC_DIR_OUTBOUND) &&
(V_ipforwarding == 1))
if (ip_tryforward(m) == NULL)
return;
/*
* Bypass packet filtering for packets previously handled by IPsec.
*/
if (ip_ipsec_filtertunnel(m))
goto passin;
#else
if (V_ipforwarding == 1)
if (ip_tryforward(m) == NULL)
return;
#endif /* IPSEC */

/*
Expand Down Expand Up @@ -605,16 +595,14 @@ ip_input(struct mbuf *m)
goto ours;
}
reinjected:
if (m->m_flags & M_IP_NEXTHOP) {
if (m_tag_find(m, PACKET_TAG_IPFORWARD, NULL) != NULL) {
/*
* Directly ship the packet on. This allows
* forwarding packets originally destined to us
* to some other directly connected host.
*/
ip_forward(m, 1);
return;
}
if (IP_HAS_NEXTHOP(m)) {
/*
* Directly ship the packet on. This allows
* forwarding packets originally destined to us
* to some other directly connected host.
*/
ip_forward(m, 1);
return;
}
passin:

Expand Down
113 changes: 103 additions & 10 deletions sys/netinet/ip_output.c
Original file line number Diff line number Diff line change
Expand Up @@ -105,20 +105,20 @@ extern int in_mcast_loop;
extern struct protosw inetsw[];

static inline int
ip_output_pfil(struct mbuf **mp, struct ifnet *ifp, struct inpcb *inp,
ip_output_pfil(struct mbuf **mp, struct ifnet **ifp, struct inpcb *inp,
struct sockaddr_in *dst, int *fibnum, int *error)
{
struct m_tag *fwd_tag = NULL;
struct mbuf *m;
struct in_addr odst;
struct ip *ip;
u_short ifidx;

m = *mp;
ip = mtod(m, struct ip *);

/* Run through list of hooks for output packets. */
odst.s_addr = ip->ip_dst.s_addr;
*error = pfil_run_hooks(&V_inet_pfil_hook, mp, ifp, PFIL_OUT, inp);
*error = pfil_run_hooks(&V_inet_pfil_hook, mp, *ifp, PFIL_OUT, inp);
m = *mp;
if ((*error) != 0 || m == NULL)
return 1; /* Finished */
Expand Down Expand Up @@ -182,13 +182,15 @@ ip_output_pfil(struct mbuf **mp, struct ifnet *ifp, struct inpcb *inp,
return 1; /* Finished */
}
/* Or forward to some other address? */
if ((m->m_flags & M_IP_NEXTHOP) &&
((fwd_tag = m_tag_find(m, PACKET_TAG_IPFORWARD, NULL)) != NULL)) {
bcopy((fwd_tag+1), dst, sizeof(struct sockaddr_in));
if (IP_HAS_NEXTHOP(m) && !ip_get_fwdtag(m, dst, &ifidx)) {
if (ifidx != 0) {
struct ifnet *nifp = ifnet_byindex(ifidx);
if (nifp != NULL) {
*ifp = nifp;
}
}
m->m_flags |= M_SKIP_FIREWALL;
m->m_flags &= ~M_IP_NEXTHOP;
m_tag_delete(m, fwd_tag);

ip_flush_fwdtag(m);
return -1; /* Reloop for CHANGE of dst */
}

Expand Down Expand Up @@ -407,6 +409,7 @@ ip_output(struct mbuf *m, struct mbuf *opt, struct route *ro, int flags,
isbroadcast = in_broadcast(gw->sin_addr, ifp);
}

haveroute:
/*
* Calculate MTU. If we have a route that is up, use that,
* otherwise use the interface's MTU.
Expand Down Expand Up @@ -577,7 +580,8 @@ ip_output(struct mbuf *m, struct mbuf *opt, struct route *ro, int flags,

/* Jump over all PFIL processing if hooks are not active. */
if (PFIL_HOOKED(&V_inet_pfil_hook)) {
switch (ip_output_pfil(&m, ifp, inp, dst, &fibnum, &error)) {
struct ifnet *same = ifp;
switch (ip_output_pfil(&m, &ifp, inp, dst, &fibnum, &error)) {
case 1: /* Finished */
goto done;

Expand All @@ -594,6 +598,9 @@ ip_output(struct mbuf *m, struct mbuf *opt, struct route *ro, int flags,
rte = NULL;
gw = dst;
ip = mtod(m, struct ip *);
if (same != ifp) {
goto haveroute;
}
goto again;

}
Expand Down Expand Up @@ -1416,3 +1423,89 @@ ip_mloopback(struct ifnet *ifp, const struct mbuf *m, int hlen)
if_simloop(ifp, copym, AF_INET, 0);
}
}

struct ip_fwdtag {
struct sockaddr_in dst;
u_short if_index;
};

int
ip_set_fwdtag(struct mbuf *m, struct sockaddr_in *dst, u_short ifidx)
{
struct ip_fwdtag *fwd_info;
struct m_tag *fwd_tag;

KASSERT(dst != NULL, ("%s: !dst", __func__));
KASSERT(dst->sin_family == AF_INET, ("%s: !AF_INET", __func__));

fwd_tag = m_tag_find(m, PACKET_TAG_IPFORWARD, NULL);
if (fwd_tag != NULL) {
KASSERT(((struct ip_fwdtag *)(fwd_tag+1))->dst.sin_family ==
AF_INET, ("%s: !AF_INET", __func__));

m_tag_unlink(m, fwd_tag);
} else {
fwd_tag = m_tag_get(PACKET_TAG_IPFORWARD, sizeof(*fwd_info),
M_NOWAIT);
if (fwd_tag == NULL) {
return (ENOBUFS);
}
}

fwd_info = (struct ip_fwdtag *)(fwd_tag+1);

bcopy(dst, &fwd_info->dst, sizeof(fwd_info->dst));
fwd_info->if_index = ifidx;
m->m_flags |= M_IP_NEXTHOP;

if (in_localip(fwd_info->dst.sin_addr))
m->m_flags |= M_FASTFWD_OURS;
else
m->m_flags &= ~M_FASTFWD_OURS;

m_tag_prepend(m, fwd_tag);

return (0);
}

int
ip_get_fwdtag(struct mbuf *m, struct sockaddr_in *dst, u_short *ifidx)
{
struct ip_fwdtag *fwd_info;
struct m_tag *fwd_tag;

fwd_tag = m_tag_find(m, PACKET_TAG_IPFORWARD, NULL);
if (fwd_tag == NULL) {
return (ENOENT);
}

fwd_info = (struct ip_fwdtag *)(fwd_tag+1);

KASSERT(((struct sockaddr *)&fwd_info->dst)->sa_family == AF_INET,
("%s: !AF_INET", __func__));

if (dst != NULL) {
bcopy(&fwd_info->dst, dst, sizeof(*dst));
}

if (ifidx != NULL) {
*ifidx = fwd_info->if_index;
}

return (0);
}

void
ip_flush_fwdtag(struct mbuf *m)
{
struct m_tag *fwd_tag;

fwd_tag = m_tag_find(m, PACKET_TAG_IPFORWARD, NULL);
if (fwd_tag != NULL) {
KASSERT(((struct sockaddr *)(fwd_tag+1))->sa_family ==
AF_INET, ("%s: !AF_INET", __func__));

m->m_flags &= ~(M_IP_NEXTHOP | M_FASTFWD_OURS);
m_tag_delete(m, fwd_tag);
}
}
5 changes: 5 additions & 0 deletions sys/netinet/ip_var.h
Original file line number Diff line number Diff line change
Expand Up @@ -236,6 +236,11 @@ extern int (*ip_rsvp_vif)(struct socket *, struct sockopt *);
extern void (*ip_rsvp_force_done)(struct socket *);
extern int (*rsvp_input_p)(struct mbuf **, int *, int);

#define IP_HAS_NEXTHOP(m) ((m)->m_flags & M_IP_NEXTHOP)
int ip_set_fwdtag(struct mbuf *, struct sockaddr_in *, u_short);
int ip_get_fwdtag(struct mbuf *, struct sockaddr_in *, u_short *);
void ip_flush_fwdtag(struct mbuf *);

VNET_DECLARE(struct pfil_head, inet_pfil_hook); /* packet filter hooks */
#define V_inet_pfil_hook VNET(inet_pfil_hook)

Expand Down
48 changes: 19 additions & 29 deletions sys/netinet/tcp_input.c
Original file line number Diff line number Diff line change
Expand Up @@ -592,6 +592,7 @@ tcp_input(struct mbuf **mp, int *offp, int proto)
int off0;
int optlen = 0;
#ifdef INET
struct sockaddr_in next_hop;
int len;
#endif
int tlen = 0, off;
Expand All @@ -602,8 +603,8 @@ tcp_input(struct mbuf **mp, int *offp, int proto)
uint8_t sig_checked = 0;
#endif
uint8_t iptos = 0;
struct m_tag *fwd_tag = NULL;
#ifdef INET6
struct sockaddr_in6 next_hop6;
struct ip6_hdr *ip6 = NULL;
int isipv6;
#else
Expand Down Expand Up @@ -804,22 +805,6 @@ tcp_input(struct mbuf **mp, int *offp, int proto)
} else
ti_locked = TI_UNLOCKED;

/*
* Grab info from PACKET_TAG_IPFORWARD tag prepended to the chain.
*/
if (
#ifdef INET6
(isipv6 && (m->m_flags & M_IP6_NEXTHOP))
#ifdef INET
|| (!isipv6 && (m->m_flags & M_IP_NEXTHOP))
#endif
#endif
#if defined(INET) && !defined(INET6)
(m->m_flags & M_IP_NEXTHOP)
#endif
)
fwd_tag = m_tag_find(m, PACKET_TAG_IPFORWARD, NULL);

findpcb:
#ifdef INVARIANTS
if (ti_locked == TI_RLOCKED) {
Expand All @@ -829,10 +814,11 @@ tcp_input(struct mbuf **mp, int *offp, int proto)
}
#endif
#ifdef INET6
if (isipv6 && fwd_tag != NULL) {
struct sockaddr_in6 *next_hop6;

next_hop6 = (struct sockaddr_in6 *)(fwd_tag + 1);
/*
* Grab info from IP forward tag prepended to the chain.
*/
if (isipv6 && IP6_HAS_NEXTHOP(m) &&
!ip6_get_fwdtag(m, &next_hop6, NULL)) {
/*
* Transparently forwarded. Pretend to be the destination.
* Already got one like this?
Expand All @@ -847,11 +833,13 @@ tcp_input(struct mbuf **mp, int *offp, int proto)
* any hardware-generated hash is ignored.
*/
inp = in6_pcblookup(&V_tcbinfo, &ip6->ip6_src,
th->th_sport, &next_hop6->sin6_addr,
next_hop6->sin6_port ? ntohs(next_hop6->sin6_port) :
th->th_sport, &next_hop6.sin6_addr,
next_hop6.sin6_port ? ntohs(next_hop6.sin6_port) :
th->th_dport, INPLOOKUP_WILDCARD |
INPLOOKUP_WLOCKPCB, m->m_pkthdr.rcvif);
}
/* Remove the tag from the packet. We don't need it anymore. */
ip6_flush_fwdtag(m);
} else if (isipv6) {
inp = in6_pcblookup_mbuf(&V_tcbinfo, &ip6->ip6_src,
th->th_sport, &ip6->ip6_dst, th->th_dport,
Expand All @@ -863,10 +851,10 @@ tcp_input(struct mbuf **mp, int *offp, int proto)
else
#endif
#ifdef INET
if (fwd_tag != NULL) {
struct sockaddr_in *next_hop;

next_hop = (struct sockaddr_in *)(fwd_tag+1);
/*
* Grab info from IP forward tag prepended to the chain.
*/
if (IP_HAS_NEXTHOP(m) && !ip_get_fwdtag(m, &next_hop, NULL)) {
/*
* Transparently forwarded. Pretend to be the destination.
* already got one like this?
Expand All @@ -881,11 +869,13 @@ tcp_input(struct mbuf **mp, int *offp, int proto)
* any hardware-generated hash is ignored.
*/
inp = in_pcblookup(&V_tcbinfo, ip->ip_src,
th->th_sport, next_hop->sin_addr,
next_hop->sin_port ? ntohs(next_hop->sin_port) :
th->th_sport, next_hop.sin_addr,
next_hop.sin_port ? ntohs(next_hop.sin_port) :
th->th_dport, INPLOOKUP_WILDCARD |
INPLOOKUP_WLOCKPCB, m->m_pkthdr.rcvif);
}
/* Remove the tag from the packet. We don't need it anymore. */
ip_flush_fwdtag(m);
} else
inp = in_pcblookup_mbuf(&V_tcbinfo, ip->ip_src,
th->th_sport, ip->ip_dst, th->th_dport,
Expand Down
Loading

0 comments on commit e92bed1

Please sign in to comment.