pf/ipfw/netinet[6]: IP forwarding rework, fixes IPv4 in pf(4)

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.

In our 11.1 iteration ip_tryforward() is easy to port and while
we are at it we shall also tackle IPv6.  :)

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

Also see: https://reviews.freebsd.org/D8877

pf: add ipv6 shared forwarding
This commit is contained in:
Franco Fichtner 2016-12-18 11:08:50 +01:00
parent 05dbf872ab
commit b349ba1521
15 changed files with 569 additions and 162 deletions

View file

@ -969,6 +969,9 @@ RACCT_DEFAULT_TO_DISABLED opt_global.h
# Resource Limits
RCTL opt_global.h
# OPNsense additions
PF_SHARE_FORWARD opt_pf.h
# Random number generator(s)
# Which CSPRNG hash we get.
# If Yarrow is not chosen, Fortuna is selected.

View file

@ -154,7 +154,6 @@ ip_tryforward(struct mbuf *m)
struct in_addr odest, dest;
uint16_t ip_len, ip_off;
int error = 0;
struct m_tag *fwd_tag = NULL;
/*
* Are we active and forwarding packets?
@ -317,9 +316,7 @@ passin:
/*
* 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?
*/
@ -334,11 +331,13 @@ forwardlocal:
/*
* 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;
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);
}
if (ip_findroute(&nh, dest, m) != 0)
return (NULL); /* icmp unreach already sent */

View file

@ -622,16 +622,14 @@ tooshort:
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:

View file

@ -102,20 +102,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 */
@ -179,13 +179,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 */
}
@ -403,6 +405,7 @@ again:
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.
@ -571,7 +574,8 @@ sendit:
/* 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;
@ -588,6 +592,9 @@ sendit:
rte = NULL;
gw = dst;
ip = mtod(m, struct ip *);
if (same != ifp) {
goto haveroute;
}
goto again;
}
@ -1390,3 +1397,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);
}
}

View file

@ -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)

View file

@ -575,6 +575,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;
@ -582,8 +583,8 @@ tcp_input(struct mbuf **mp, int *offp, int proto)
int thflags;
int rstreason = 0; /* For badport_bandlim accounting purposes */
uint8_t iptos;
struct m_tag *fwd_tag = NULL;
#ifdef INET6
struct sockaddr_in6 next_hop6;
struct ip6_hdr *ip6 = NULL;
int isipv6;
#else
@ -777,22 +778,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) {
@ -802,10 +787,11 @@ findpcb:
}
#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?
@ -820,11 +806,13 @@ findpcb:
* 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,
@ -836,10 +824,10 @@ findpcb:
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?
@ -854,11 +842,13 @@ findpcb:
* 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,

View file

@ -386,7 +386,7 @@ udp_input(struct mbuf **mp, int *offp, int proto)
struct ip save_ip;
struct sockaddr_in udp_in;
struct mbuf *m;
struct m_tag *fwd_tag;
struct sockaddr_in next_hop;
int cscov_partial, iphlen;
m = *mp;
@ -624,14 +624,9 @@ udp_input(struct mbuf **mp, int *offp, int proto)
*/
/*
* Grab info from PACKET_TAG_IPFORWARD tag prepended to the chain.
* Grab info from IP forward tag prepended to the chain.
*/
if ((m->m_flags & M_IP_NEXTHOP) &&
(fwd_tag = m_tag_find(m, PACKET_TAG_IPFORWARD, NULL)) != NULL) {
struct sockaddr_in *next_hop;
next_hop = (struct sockaddr_in *)(fwd_tag + 1);
if (IP_HAS_NEXTHOP(m) && !ip_get_fwdtag(m, &next_hop, NULL)) {
/*
* Transparently forwarded. Pretend to be the destination.
* Already got one like this?
@ -645,14 +640,13 @@ udp_input(struct mbuf **mp, int *offp, int proto)
* any hardware-generated hash is ignored.
*/
inp = in_pcblookup(pcbinfo, ip->ip_src,
uh->uh_sport, next_hop->sin_addr,
next_hop->sin_port ? htons(next_hop->sin_port) :
uh->uh_sport, next_hop.sin_addr,
next_hop.sin_port ? htons(next_hop.sin_port) :
uh->uh_dport, INPLOOKUP_WILDCARD |
INPLOOKUP_RLOCKPCB, ifp);
}
/* Remove the tag from the packet. We don't need it anymore. */
m_tag_delete(m, fwd_tag);
m->m_flags &= ~M_IP_NEXTHOP;
ip_flush_fwdtag(m);
} else
inp = in_pcblookup_mbuf(pcbinfo, ip->ip_src, uh->uh_sport,
ip->ip_dst, uh->uh_dport, INPLOOKUP_WILDCARD |

View file

@ -87,7 +87,6 @@ ip6_tryforward(struct mbuf *m)
{
struct sockaddr_in6 dst;
struct nhop6_basic nh;
struct m_tag *fwd_tag;
struct ip6_hdr *ip6;
struct ifnet *rcvif;
uint32_t plen;
@ -173,14 +172,16 @@ ip6_tryforward(struct mbuf *m)
return (m);
ip6 = mtod(m, struct ip6_hdr *);
if ((m->m_flags & M_IP6_NEXTHOP) &&
(fwd_tag = m_tag_find(m, PACKET_TAG_IPFORWARD, NULL)) != NULL) {
if (IP6_HAS_NEXTHOP(m)) {
/*
* Now we will find route to forwarded by pfil destination.
*/
bcopy((fwd_tag + 1), &dst, sizeof(dst));
m->m_flags &= ~M_IP6_NEXTHOP;
m_tag_delete(m, fwd_tag);
struct sockaddr_in6 tmp;
if (ip6_get_fwdtag(m, &tmp, NULL)) {
return (NULL);
}
dst.sin6_addr = tmp.sin6_addr;
ip6_flush_fwdtag(m);
} else {
/* Update dst since pfil could change it */
dst.sin6_addr = ip6->ip6_dst;
@ -232,17 +233,15 @@ passin:
* Again. A packet filter could change the destination address.
*/
ip6 = mtod(m, struct ip6_hdr *);
if (m->m_flags & M_IP6_NEXTHOP)
fwd_tag = m_tag_find(m, PACKET_TAG_IPFORWARD, NULL);
else
fwd_tag = NULL;
if (fwd_tag != NULL ||
if (IP6_HAS_NEXTHOP(m) ||
!IN6_ARE_ADDR_EQUAL(&dst.sin6_addr, &ip6->ip6_dst)) {
if (fwd_tag != NULL) {
bcopy((fwd_tag + 1), &dst, sizeof(dst));
m->m_flags &= ~M_IP6_NEXTHOP;
m_tag_delete(m, fwd_tag);
if (IP6_HAS_NEXTHOP(m)) {
struct sockaddr_in6 tmp;
if (ip6_get_fwdtag(m, &tmp, NULL)) {
return (NULL);
}
dst.sin6_addr = tmp.sin6_addr;
ip6_flush_fwdtag(m);
} else
dst.sin6_addr = ip6->ip6_dst;
/*

View file

@ -98,7 +98,6 @@ ip6_forward(struct mbuf *m, int srcrt)
#ifdef SCTP
int sw_csum;
#endif
struct m_tag *fwd_tag;
char ip6bufs[INET6_ADDRSTRLEN], ip6bufd[INET6_ADDRSTRLEN];
/*
@ -359,13 +358,17 @@ again2:
goto out;
}
/* Or forward to some other address? */
if ((m->m_flags & M_IP6_NEXTHOP) &&
(fwd_tag = m_tag_find(m, PACKET_TAG_IPFORWARD, NULL)) != NULL) {
if (IP6_HAS_NEXTHOP(m)) {
dst = (struct sockaddr_in6 *)&rin6.ro_dst;
bcopy((fwd_tag+1), dst, sizeof(struct sockaddr_in6));
if (ip6_get_fwdtag(m, dst, NULL) != 0) {
if (mcopy) {
m_freem(m);
goto freecopy;
}
goto bad;
}
m->m_flags |= M_SKIP_FIREWALL;
m->m_flags &= ~M_IP6_NEXTHOP;
m_tag_delete(m, fwd_tag);
ip6_flush_fwdtag(m);
RTFREE(rt);
goto again2;
}

View file

@ -785,8 +785,7 @@ ip6_input(struct mbuf *m)
goto hbhcheck;
}
reinjected:
if ((m->m_flags & M_IP6_NEXTHOP) &&
m_tag_find(m, PACKET_TAG_IPFORWARD, NULL) != NULL) {
if (IP6_HAS_NEXTHOP(m)) {
/*
* Directly ship the packet on. This allows forwarding
* packets originally destined to us to some other directly

View file

@ -319,8 +319,8 @@ ip6_output(struct mbuf *m0, struct ip6_pktopts *opt,
int hdrsplit = 0;
int sw_csum, tso;
int needfiblookup;
int has_fwd_tag = 0;
uint32_t fibnum;
struct m_tag *fwd_tag = NULL;
uint32_t id;
if (inp != NULL) {
@ -541,7 +541,7 @@ again:
ro->ro_dst.sin6_family = AF_INET6;
RT_VALIDATE((struct route *)ro, &inp->inp_rt_cookie, fibnum);
}
if (ro->ro_rt && fwd_tag == NULL && (ro->ro_rt->rt_flags & RTF_UP) &&
if (ro->ro_rt && !has_fwd_tag && (ro->ro_rt->rt_flags & RTF_UP) &&
ro->ro_dst.sin6_family == AF_INET6 &&
IN6_ARE_ADDR_EQUAL(&ro->ro_dst.sin6_addr, &ip6->ip6_dst)) {
rt = ro->ro_rt;
@ -550,7 +550,7 @@ again:
if (ro->ro_lle)
LLE_FREE(ro->ro_lle); /* zeros ro_lle */
ro->ro_lle = NULL;
if (fwd_tag == NULL) {
if (!has_fwd_tag) {
bzero(&dst_sa, sizeof(dst_sa));
dst_sa.sin6_family = AF_INET6;
dst_sa.sin6_len = sizeof(dst_sa);
@ -854,13 +854,11 @@ again:
goto done;
}
/* Or forward to some other address? */
if ((m->m_flags & M_IP6_NEXTHOP) &&
(fwd_tag = m_tag_find(m, PACKET_TAG_IPFORWARD, NULL)) != NULL) {
if (IP6_HAS_NEXTHOP(m) && !ip6_get_fwdtag(m, &dst_sa, NULL)) {
dst = (struct sockaddr_in6 *)&ro->ro_dst;
bcopy((fwd_tag+1), &dst_sa, sizeof(struct sockaddr_in6));
m->m_flags |= M_SKIP_FIREWALL;
m->m_flags &= ~M_IP6_NEXTHOP;
m_tag_delete(m, fwd_tag);
ip6_flush_fwdtag(m);
has_fwd_tag = 1;
goto again;
}
@ -3062,3 +3060,102 @@ ip6_optlen(struct inpcb *in6p)
return len;
#undef elen
}
struct ip6_fwdtag {
struct sockaddr_in6 dst;
u_short if_index;
};
int
ip6_set_fwdtag(struct mbuf *m, struct sockaddr_in6 *dst, u_short ifidx)
{
struct ip6_fwdtag *fwd_info;
struct m_tag *fwd_tag;
int error;
KASSERT(dst != NULL, ("%s: !dst", __func__));
KASSERT(dst->sin6_family == AF_INET6, ("%s: !AF_INET6", __func__));
fwd_tag = m_tag_find(m, PACKET_TAG_IPFORWARD, NULL);
if (fwd_tag != NULL) {
KASSERT(((struct ip6_fwdtag *)(fwd_tag+1))->dst.sin6_family ==
AF_INET6, ("%s: !AF_INET6", __func__));
m_tag_unlink(m, fwd_tag);
} else {
fwd_tag = m_tag_get(PACKET_TAG_IPFORWARD, sizeof(*dst),
M_NOWAIT);
if (fwd_tag == NULL) {
return (ENOBUFS);
}
}
fwd_info = (struct ip6_fwdtag *)(fwd_tag+1);
bcopy(dst, &fwd_info->dst, sizeof(fwd_info->dst));
/*
* If nh6 address is link-local we should convert
* it to kernel internal form before doing any
* comparisons.
*/
error = sa6_embedscope(&fwd_info->dst, V_ip6_use_defzone);
if (error != 0) {
m_tag_free(fwd_tag);
return (error);
}
if (in6_localip(&fwd_info->dst.sin6_addr))
m->m_flags |= M_FASTFWD_OURS;
else
m->m_flags &= ~M_FASTFWD_OURS;
fwd_info->if_index = ifidx;
m->m_flags |= M_IP6_NEXTHOP;
m_tag_prepend(m, fwd_tag);
return (0);
}
int
ip6_get_fwdtag(struct mbuf *m, struct sockaddr_in6 *dst, u_short *ifidx)
{
struct ip6_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 ip6_fwdtag *)(fwd_tag+1);
KASSERT(((struct sockaddr *)&fwd_info->dst)->sa_family == AF_INET6,
("%s: !AF_INET6", __func__));
if (dst != NULL) {
bcopy((fwd_tag+1), dst, sizeof(*dst));
}
if (ifidx != NULL) {
*ifidx = fwd_info->if_index;
}
return (0);
}
void
ip6_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_INET6, ("%s: !AF_INET6", __func__));
m->m_flags &= ~(M_IP6_NEXTHOP | M_FASTFWD_OURS);
m_tag_delete(m, fwd_tag);
}
}

View file

@ -397,6 +397,11 @@ int ip6_deletefraghdr(struct mbuf *, int, int);
int ip6_fragment(struct ifnet *, struct mbuf *, int, u_char, int,
uint32_t);
#define IP6_HAS_NEXTHOP(m) ((m)->m_flags & M_IP6_NEXTHOP)
int ip6_set_fwdtag(struct mbuf *, struct sockaddr_in6 *, u_short);
int ip6_get_fwdtag(struct mbuf *, struct sockaddr_in6 *, u_short *);
void ip6_flush_fwdtag(struct mbuf *);
int route6_input(struct mbuf **, int *, int);
void frag6_init(void);

View file

@ -203,7 +203,7 @@ udp6_input(struct mbuf **mp, int *offp, int proto)
int cscov_partial;
int plen, ulen;
struct sockaddr_in6 fromsa;
struct m_tag *fwd_tag;
struct sockaddr_in6 next_hop6;
uint16_t uh_sum;
uint8_t nxt;
@ -411,14 +411,9 @@ udp6_input(struct mbuf **mp, int *offp, int proto)
*/
/*
* Grab info from PACKET_TAG_IPFORWARD tag prepended to the chain.
* Grab info from IP forward tag prepended to the chain.
*/
if ((m->m_flags & M_IP6_NEXTHOP) &&
(fwd_tag = m_tag_find(m, PACKET_TAG_IPFORWARD, NULL)) != NULL) {
struct sockaddr_in6 *next_hop6;
next_hop6 = (struct sockaddr_in6 *)(fwd_tag + 1);
if (IP6_HAS_NEXTHOP(m) && !ip6_get_fwdtag(m, &next_hop6, NULL)) {
/*
* Transparently forwarded. Pretend to be the destination.
* Already got one like this?
@ -433,14 +428,13 @@ udp6_input(struct mbuf **mp, int *offp, int proto)
* any hardware-generated hash is ignored.
*/
inp = in6_pcblookup(pcbinfo, &ip6->ip6_src,
uh->uh_sport, &next_hop6->sin6_addr,
next_hop6->sin6_port ? htons(next_hop6->sin6_port) :
uh->uh_sport, &next_hop6.sin6_addr,
next_hop6.sin6_port ? htons(next_hop6.sin6_port) :
uh->uh_dport, INPLOOKUP_WILDCARD |
INPLOOKUP_RLOCKPCB, m->m_pkthdr.rcvif);
}
/* Remove the tag from the packet. We don't need it anymore. */
m_tag_delete(m, fwd_tag);
m->m_flags &= ~M_IP6_NEXTHOP;
ip6_flush_fwdtag(m);
} else
inp = in6_pcblookup_mbuf(pcbinfo, &ip6->ip6_src,
uh->uh_sport, &ip6->ip6_dst, uh->uh_dport,

View file

@ -165,66 +165,25 @@ again:
ret = EACCES;
#else
{
struct m_tag *fwd_tag;
size_t len;
KASSERT(args.next_hop == NULL || args.next_hop6 == NULL,
("%s: both next_hop=%p and next_hop6=%p not NULL", __func__,
args.next_hop, args.next_hop6));
#ifdef INET6
if (args.next_hop6 != NULL)
len = sizeof(struct sockaddr_in6);
#endif
#ifdef INET
if (args.next_hop != NULL)
len = sizeof(struct sockaddr_in);
#endif
/* Incoming packets should not be tagged so we do not
* m_tag_find. Outgoing packets may be tagged, so we
* reuse the tag if present.
*/
fwd_tag = (dir == DIR_IN) ? NULL :
m_tag_find(*m0, PACKET_TAG_IPFORWARD, NULL);
if (fwd_tag != NULL) {
m_tag_unlink(*m0, fwd_tag);
} else {
fwd_tag = m_tag_get(PACKET_TAG_IPFORWARD, len,
M_NOWAIT);
if (fwd_tag == NULL) {
ret = EACCES;
break; /* i.e. drop */
}
}
#ifdef INET6
if (args.next_hop6 != NULL) {
struct sockaddr_in6 *sa6;
sa6 = (struct sockaddr_in6 *)(fwd_tag + 1);
bcopy(args.next_hop6, sa6, len);
/*
* If nh6 address is link-local we should convert
* it to kernel internal form before doing any
* comparisons.
*/
if (sa6_embedscope(sa6, V_ip6_use_defzone) != 0) {
if (ip6_set_fwdtag(*m0, args.next_hop6, 0)) {
ret = EACCES;
break;
}
if (in6_localip(&sa6->sin6_addr))
(*m0)->m_flags |= M_FASTFWD_OURS;
(*m0)->m_flags |= M_IP6_NEXTHOP;
}
#endif
#ifdef INET
if (args.next_hop != NULL) {
bcopy(args.next_hop, (fwd_tag+1), len);
if (in_localip(args.next_hop->sin_addr))
(*m0)->m_flags |= M_FASTFWD_OURS;
(*m0)->m_flags |= M_IP_NEXTHOP;
if (ip_set_fwdtag(*m0, args.next_hop, 0)) {
ret = EACCES;
break;
}
}
#endif
m_tag_prepend(*m0, fwd_tag);
}
#endif /* INET || INET6 */
break;

View file

@ -290,6 +290,9 @@ static void pf_mtag_free(struct m_tag *);
static void pf_route(struct mbuf **, struct pf_rule *, int,
struct ifnet *, struct pf_state *,
struct pf_pdesc *);
static void pf_route_shared(struct mbuf **, struct pf_rule *, int,
struct ifnet *, struct pf_state *,
struct pf_pdesc *);
#endif /* INET */
#ifdef INET6
static void pf_change_a6(struct pf_addr *, u_int16_t *,
@ -297,6 +300,9 @@ static void pf_change_a6(struct pf_addr *, u_int16_t *,
static void pf_route6(struct mbuf **, struct pf_rule *, int,
struct ifnet *, struct pf_state *,
struct pf_pdesc *);
static void pf_route6_shared(struct mbuf **, struct pf_rule *,
int, struct ifnet *, struct pf_state *,
struct pf_pdesc *);
#endif /* INET6 */
int in4_cksum(struct mbuf *m, u_int8_t nxt, int off, int len);
@ -368,6 +374,25 @@ SYSCTL_ULONG(_net_pf, OID_AUTO, states_hashsize, CTLFLAG_RDTUN,
SYSCTL_ULONG(_net_pf, OID_AUTO, source_nodes_hashsize, CTLFLAG_RDTUN,
&pf_srchashsize, 0, "Size of pf(4) source nodes hashtable");
#ifdef PF_SHARE_FORWARD
static VNET_DEFINE(int, pf_share_forward) = 1;
static VNET_DEFINE(int, pf_share_forward6) = 1;
#else
static VNET_DEFINE(int, pf_share_forward) = 0;
static VNET_DEFINE(int, pf_share_forward6) = 0;
#endif
#define V_pf_share_forward VNET(pf_share_forward)
#define V_pf_share_forward6 VNET(pf_share_forward6)
SYSCTL_INT(_net_pf, OID_AUTO, share_forward,
CTLFLAG_VNET | CTLFLAG_RW, &VNET_NAME(pf_share_forward), 0,
"If set pf(4) will share IPv4 forwarding decisions with ipfw(4).");
SYSCTL_INT(_net_pf, OID_AUTO, share_forward6,
CTLFLAG_VNET | CTLFLAG_RW, &VNET_NAME(pf_share_forward6), 0,
"If set pf(4) will share IPv6 forwarding decisions with ipfw(4).");
VNET_DEFINE(void *, pf_swi_cookie);
VNET_DEFINE(uint32_t, pf_hashseed);
@ -5568,6 +5593,113 @@ done:
*m = NULL;
return;
bad_locked:
if (s)
PF_STATE_UNLOCK(s);
bad:
m_freem(m0);
goto done;
}
static void
pf_route_shared(struct mbuf **m, struct pf_rule *r, int dir, struct ifnet *ifp,
struct pf_state *s, struct pf_pdesc *pd)
{
struct mbuf *m0;
struct sockaddr_in dst;
struct ip *ip;
struct pf_addr naddr;
struct pf_src_node *sn = NULL;
int error = 0;
KASSERT(m && *m && r && ifp, ("%s: invalid parameters", __func__));
KASSERT(dir == PF_IN || dir == PF_OUT, ("%s: invalid direction",
__func__));
if ((pd->pf_mtag == NULL &&
((pd->pf_mtag = pf_get_mtag(*m)) == NULL)) ||
pd->pf_mtag->routed++ > 3) {
m0 = *m;
*m = NULL;
goto bad_locked;
}
if (r->rt == PF_DUPTO) {
if ((m0 = m_dup(*m, M_NOWAIT)) == NULL) {
if (s)
PF_STATE_UNLOCK(s);
return;
}
} else {
if ((r->rt == PF_REPLYTO) == (r->direction == dir)) {
if (s)
PF_STATE_UNLOCK(s);
return;
}
m0 = *m;
}
/* retain old behaviour by avoiding a rewrite */
if (IP_HAS_NEXTHOP(m0)) {
if (s)
PF_STATE_UNLOCK(s);
return;
}
ip = mtod(m0, struct ip *);
bzero(&dst, sizeof(dst));
dst.sin_family = AF_INET;
dst.sin_len = sizeof(dst);
dst.sin_addr = ip->ip_dst;
if (r->rt == PF_FASTROUTE) {
struct nhop4_basic nh4;
if (s)
PF_STATE_UNLOCK(s);
if (fib4_lookup_nh_basic(M_GETFIB(m0), ip->ip_dst, 0,
m0->m_pkthdr.flowid, &nh4) != 0) {
KMOD_IPSTAT_INC(ips_noroute);
error = EHOSTUNREACH;
goto bad;
}
ifp = nh4.nh_ifp;
dst.sin_addr = nh4.nh_addr;
} else {
if (TAILQ_EMPTY(&r->rpool.list)) {
DPFPRINTF(PF_DEBUG_URGENT,
("%s: TAILQ_EMPTY(&r->rpool.list)\n", __func__));
goto bad_locked;
}
if (s == NULL) {
pf_map_addr(AF_INET, r, (struct pf_addr *)&ip->ip_src,
&naddr, NULL, &sn);
if (!PF_AZERO(&naddr, AF_INET))
dst.sin_addr.s_addr = naddr.v4.s_addr;
ifp = r->rpool.cur->kif ?
r->rpool.cur->kif->pfik_ifp : NULL;
} else {
if (!PF_AZERO(&s->rt_addr, AF_INET))
dst.sin_addr.s_addr =
s->rt_addr.v4.s_addr;
ifp = s->rt_kif ? s->rt_kif->pfik_ifp : NULL;
PF_STATE_UNLOCK(s);
}
}
if (ifp == NULL)
goto bad;
if (ip_set_fwdtag(m0, &dst, ifp->if_index))
goto bad;
done:
if (r->rt == PF_DUPTO && IP_HAS_NEXTHOP(m0))
ip_forward(m0, 1);
return;
bad_locked:
if (s)
PF_STATE_UNLOCK(s);
@ -5703,6 +5835,109 @@ done:
*m = NULL;
return;
bad_locked:
if (s)
PF_STATE_UNLOCK(s);
bad:
m_freem(m0);
goto done;
}
static void
pf_route6_shared(struct mbuf **m, struct pf_rule *r, int dir,
struct ifnet *ifp, struct pf_state *s, struct pf_pdesc *pd)
{
struct mbuf *m0;
struct sockaddr_in6 dst;
struct ip6_hdr *ip6;
struct pf_addr naddr;
struct pf_src_node *sn = NULL;
KASSERT(m && *m && r && ifp, ("%s: invalid parameters", __func__));
KASSERT(dir == PF_IN || dir == PF_OUT, ("%s: invalid direction",
__func__));
if ((pd->pf_mtag == NULL &&
((pd->pf_mtag = pf_get_mtag(*m)) == NULL)) ||
pd->pf_mtag->routed++ > 3) {
m0 = *m;
*m = NULL;
goto bad_locked;
}
if (r->rt == PF_DUPTO) {
if ((m0 = m_dup(*m, M_NOWAIT)) == NULL) {
if (s)
PF_STATE_UNLOCK(s);
return;
}
} else {
if ((r->rt == PF_REPLYTO) == (r->direction == dir)) {
if (s)
PF_STATE_UNLOCK(s);
return;
}
m0 = *m;
}
/* retain old behaviour by avoiding a rewrite */
if (IP6_HAS_NEXTHOP(m0)) {
if (s)
PF_STATE_UNLOCK(s);
return;
}
ip6 = mtod(m0, struct ip6_hdr *);
bzero(&dst, sizeof(dst));
dst.sin6_family = AF_INET6;
dst.sin6_len = sizeof(dst);
dst.sin6_addr = ip6->ip6_dst;
/* Cheat. XXX why only in the v6 case??? */
if (r->rt == PF_FASTROUTE) {
if (s)
PF_STATE_UNLOCK(s);
m0->m_flags |= M_SKIP_FIREWALL;
ip6_output(m0, NULL, NULL, 0, NULL, NULL, NULL);
*m = NULL;
return;
}
if (TAILQ_EMPTY(&r->rpool.list)) {
DPFPRINTF(PF_DEBUG_URGENT,
("%s: TAILQ_EMPTY(&r->rpool.list)\n", __func__));
goto bad_locked;
}
if (s == NULL) {
pf_map_addr(AF_INET6, r, (struct pf_addr *)&ip6->ip6_src,
&naddr, NULL, &sn);
if (!PF_AZERO(&naddr, AF_INET6))
PF_ACPY((struct pf_addr *)&dst.sin6_addr,
&naddr, AF_INET6);
ifp = r->rpool.cur->kif ? r->rpool.cur->kif->pfik_ifp : NULL;
} else {
if (!PF_AZERO(&s->rt_addr, AF_INET6))
PF_ACPY((struct pf_addr *)&dst.sin6_addr,
&s->rt_addr, AF_INET6);
ifp = s->rt_kif ? s->rt_kif->pfik_ifp : NULL;
}
if (s)
PF_STATE_UNLOCK(s);
if (ifp == NULL)
goto bad;
if (ip6_set_fwdtag(m0, &dst, ifp->if_index))
goto bad;
done:
if (r->rt == PF_DUPTO && IP6_HAS_NEXTHOP(m0))
ip6_forward(m0, 1);
return;
bad_locked:
if (s)
PF_STATE_UNLOCK(s);
@ -5861,12 +6096,25 @@ pf_test(int dir, struct ifnet *ifp, struct mbuf **m0, struct inpcb *inp)
struct pf_ruleset *ruleset = NULL;
struct pf_pdesc pd;
int off, dirndx, pqid = 0;
int share_forward = V_pf_share_forward;
u_short ifidx;
M_ASSERTPKTHDR(m);
if (!V_pf_status.running)
return (PF_PASS);
/* restore the correct forwarding interface */
if (share_forward && dir == PF_OUT && IP_HAS_NEXTHOP(m) &&
!ip_get_fwdtag(m, NULL, &ifidx)) {
if (ifidx != 0) {
struct ifnet *nifp = ifnet_byindex(ifidx);
if (nifp != NULL) {
ifp = nifp;
}
}
}
memset(&pd, 0, sizeof(pd));
kif = (struct pfi_kif *)ifp->if_pf_kif;
@ -6222,7 +6470,11 @@ done:
default:
/* pf_route() returns unlocked. */
if (r->rt) {
pf_route(m0, r, dir, kif->pfik_ifp, s, &pd);
if (!share_forward)
pf_route(m0, r, dir, kif->pfik_ifp, s, &pd);
else
pf_route_shared(m0, r, dir, kif->pfik_ifp, s,
&pd);
return (action);
}
break;
@ -6249,6 +6501,8 @@ pf_test6(int dir, struct ifnet *ifp, struct mbuf **m0, struct inpcb *inp)
struct pf_pdesc pd;
int off, terminal = 0, dirndx, rh_cnt = 0, pqid = 0;
int fwdir = dir;
int share_forward = V_pf_share_forward6;
u_short ifidx;
M_ASSERTPKTHDR(m);
@ -6273,6 +6527,17 @@ pf_test6(int dir, struct ifnet *ifp, struct mbuf **m0, struct inpcb *inp)
if (!V_pf_status.running)
return (PF_PASS);
/* restore the correct forwarding interface */
if (share_forward && dir == PF_OUT && IP6_HAS_NEXTHOP(m) &&
!ip6_get_fwdtag(m, NULL, &ifidx)) {
if (ifidx != 0) {
struct ifnet *nifp = ifnet_byindex(ifidx);
if (nifp != NULL) {
ifp = nifp;
}
}
}
memset(&pd, 0, sizeof(pd));
pd.pf_mtag = pf_find_mtag(m);
@ -6637,7 +6902,11 @@ done:
default:
/* pf_route6() returns unlocked. */
if (r->rt) {
pf_route6(m0, r, dir, kif->pfik_ifp, s, &pd);
if (!share_forward)
pf_route6(m0, r, dir, kif->pfik_ifp, s, &pd);
else
pf_route6_shared(m0, r, dir, kif->pfik_ifp, s,
&pd);
return (action);
}
break;