pf: must restore forwarding interface with PF_OUT

The old code did this while violating the pfil chain, but now we
need to look up the interface correctly.  Maybe this check should
be bubbled up in the future, but for now we should keep this with
pf(4) until we upstream these changes.

(cherry picked from commit 3f4c4011db)
(cherry picked from commit d189241c36)
(cherry picked from commit 9dbd3471ce)
(cherry picked from commit 8743ecdee9)
This commit is contained in:
Franco Fichtner 2017-02-03 10:37:46 +01:00
parent 28f90e8e8c
commit 8fb06e15ab
2 changed files with 221 additions and 5 deletions

View file

@ -982,6 +982,9 @@ PAX_ASLR_COMPAT_DELTA_STACK_DEF_LEN opt_pax.h
PAX_ASLR_COMPAT_DELTA_EXEC_DEF_LEN opt_pax.h
PAX_ASLR_COMPAT_DELTA_VDSO_DEF_LEN opt_pax.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

@ -288,6 +288,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 *,
@ -366,6 +369,17 @@ 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;
#else
static VNET_DEFINE(int, pf_share_forward) = 0;
#endif
#define V_pf_share_forward VNET(pf_share_forward)
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).");
VNET_DEFINE(void *, pf_swi_cookie);
VNET_DEFINE(uint32_t, pf_hashseed);
@ -5393,17 +5407,19 @@ pf_routable(struct pf_addr *addr, sa_family_t af, struct pfi_kif *kif,
#ifdef INET
static void
pf_route(struct mbuf **m, struct pf_rule *r, int dir, struct ifnet *ifp,
pf_route(struct mbuf **m, struct pf_rule *r, int dir, struct ifnet *oifp,
struct pf_state *s, struct pf_pdesc *pd)
{
struct mbuf *m0;
struct mbuf *m0, *m1;
struct sockaddr_in dst;
struct ip *ip;
struct ifnet *ifp = NULL;
struct pf_addr naddr;
struct pf_src_node *sn = NULL;
int error = 0;
uint16_t ip_len, ip_off;
KASSERT(m && *m && r && ifp, ("%s: invalid parameters", __func__));
KASSERT(m && *m && r && oifp, ("%s: invalid parameters", __func__));
KASSERT(dir == PF_IN || dir == PF_OUT, ("%s: invalid direction",
__func__));
@ -5476,9 +5492,189 @@ pf_route(struct mbuf **m, struct pf_rule *r, int dir, struct ifnet *ifp,
if (ifp == NULL)
goto bad;
if (ip_set_fwdtag(m0, &dst, ifp->if_index))
if (oifp != ifp) {
if (pf_test(PF_OUT, ifp, &m0, NULL) != PF_PASS)
goto bad;
else if (m0 == NULL)
goto done;
if (m0->m_len < sizeof(struct ip)) {
DPFPRINTF(PF_DEBUG_URGENT,
("%s: m0->m_len < sizeof(struct ip)\n", __func__));
goto bad;
}
ip = mtod(m0, struct ip *);
}
if (ifp->if_flags & IFF_LOOPBACK)
m0->m_flags |= M_SKIP_FIREWALL;
ip_len = ntohs(ip->ip_len);
ip_off = ntohs(ip->ip_off);
/* Copied from FreeBSD 10.0-CURRENT ip_output. */
m0->m_pkthdr.csum_flags |= CSUM_IP;
if (m0->m_pkthdr.csum_flags & CSUM_DELAY_DATA & ~ifp->if_hwassist) {
in_delayed_cksum(m0);
m0->m_pkthdr.csum_flags &= ~CSUM_DELAY_DATA;
}
#ifdef SCTP
if (m0->m_pkthdr.csum_flags & CSUM_SCTP & ~ifp->if_hwassist) {
sctp_delayed_cksum(m, (uint32_t)(ip->ip_hl << 2));
m0->m_pkthdr.csum_flags &= ~CSUM_SCTP;
}
#endif
/*
* If small enough for interface, or the interface will take
* care of the fragmentation for us, we can just send directly.
*/
if (ip_len <= ifp->if_mtu ||
(m0->m_pkthdr.csum_flags & ifp->if_hwassist & CSUM_TSO) != 0) {
ip->ip_sum = 0;
if (m0->m_pkthdr.csum_flags & CSUM_IP & ~ifp->if_hwassist) {
ip->ip_sum = in_cksum(m0, ip->ip_hl << 2);
m0->m_pkthdr.csum_flags &= ~CSUM_IP;
}
m_clrprotoflags(m0); /* Avoid confusing lower layers. */
error = (*ifp->if_output)(ifp, m0, sintosa(&dst), NULL);
goto done;
}
/* Balk when DF bit is set or the interface didn't support TSO. */
if ((ip_off & IP_DF) || (m0->m_pkthdr.csum_flags & CSUM_TSO)) {
error = EMSGSIZE;
KMOD_IPSTAT_INC(ips_cantfrag);
if (r->rt != PF_DUPTO) {
icmp_error(m0, ICMP_UNREACH, ICMP_UNREACH_NEEDFRAG, 0,
ifp->if_mtu);
goto done;
} else
goto bad;
}
error = ip_fragment(ip, &m0, ifp->if_mtu, ifp->if_hwassist);
if (error)
goto bad;
for (; m0; m0 = m1) {
m1 = m0->m_nextpkt;
m0->m_nextpkt = NULL;
if (error == 0) {
m_clrprotoflags(m0);
error = (*ifp->if_output)(ifp, m0, sintosa(&dst), NULL);
} else
m_freem(m0);
}
if (error == 0)
KMOD_IPSTAT_INC(ips_fragmented);
done:
if (r->rt != PF_DUPTO)
*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))
@ -5778,12 +5974,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(*m0) &&
!ip_get_fwdtag(*m0, 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;
@ -6139,7 +6348,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;