mirror of
https://github.com/freebsd/freebsd-src.git
synced 2024-11-26 20:12:44 +00:00
pf: handle IPv6 fragmentation for route-to
If a fragmented IPv6 packet hits a route-to rule we have to first prevent the pf_test(PF_OUT) check in pf_route6() from refragmenting (and calling ip6_output()/ip6_forward()). We then have to refragment in pf_route6() and transmit the packets on the route-to interface. Split pf_refragment6() into two parts, the first to perform the refragmentation, the second to call ip6_output()/ip6_forward() and call the former from pf_route6(). Add a test case for route-to-ing fragmented IPv6 packets to verify this works as expected. Sponsored by: Rubicon Communications, LLC ("Netgate") Differential Revision: https://reviews.freebsd.org/D47684
This commit is contained in:
parent
4cc5d081d8
commit
56b7685ae3
@ -70,6 +70,8 @@
|
||||
|
||||
#ifdef _KERNEL
|
||||
|
||||
#define PF_PFIL_NOREFRAGMENT 0x80000000
|
||||
|
||||
#if defined(__arm__)
|
||||
#define PF_WANT_32_TO_64_COUNTER
|
||||
#endif
|
||||
@ -2372,7 +2374,8 @@ void pf_poolmask(struct pf_addr *, struct pf_addr*,
|
||||
struct pf_addr *, struct pf_addr *, sa_family_t);
|
||||
void pf_addr_inc(struct pf_addr *, sa_family_t);
|
||||
int pf_max_frag_size(struct mbuf *);
|
||||
int pf_refragment6(struct ifnet *, struct mbuf **, struct m_tag *, bool);
|
||||
int pf_refragment6(struct ifnet *, struct mbuf **, struct m_tag *,
|
||||
struct ifnet *, bool);
|
||||
#endif /* INET6 */
|
||||
|
||||
int pf_multihome_scan_init(int, int, struct pf_pdesc *);
|
||||
|
@ -7927,6 +7927,7 @@ pf_route6(struct mbuf **m, struct pf_krule *r, struct ifnet *oifp,
|
||||
struct pf_kstate *s, struct pf_pdesc *pd, struct inpcb *inp)
|
||||
{
|
||||
struct mbuf *m0, *md;
|
||||
struct m_tag *mtag;
|
||||
struct sockaddr_in6 dst;
|
||||
struct ip6_hdr *ip6;
|
||||
struct pfi_kkif *nkif = NULL;
|
||||
@ -8053,8 +8054,8 @@ pf_route6(struct mbuf **m, struct pf_krule *r, struct ifnet *oifp,
|
||||
}
|
||||
|
||||
if (pd->dir == PF_IN) {
|
||||
if (pf_test(AF_INET6, PF_OUT, PFIL_FWD, ifp, &m0, inp,
|
||||
&pd->act) != PF_PASS) {
|
||||
if (pf_test(AF_INET6, PF_OUT, PFIL_FWD | PF_PFIL_NOREFRAGMENT,
|
||||
ifp, &m0, inp, &pd->act) != PF_PASS) {
|
||||
SDT_PROBE1(pf, ip6, route_to, drop, __LINE__);
|
||||
goto bad;
|
||||
} else if (m0 == NULL) {
|
||||
@ -8087,6 +8088,14 @@ pf_route6(struct mbuf **m, struct pf_krule *r, struct ifnet *oifp,
|
||||
*/
|
||||
if (IN6_IS_SCOPE_EMBED(&dst.sin6_addr))
|
||||
dst.sin6_addr.s6_addr16[1] = htons(ifp->if_index);
|
||||
mtag = m_tag_find(m0, PACKET_TAG_PF_REASSEMBLED, NULL);
|
||||
if (mtag != NULL) {
|
||||
int ret;
|
||||
ret = pf_refragment6(ifp, &m0, mtag, ifp, true);
|
||||
SDT_PROBE2(pf, ip6, route_to, output, ifp, ret);
|
||||
goto done;
|
||||
}
|
||||
|
||||
if ((u_long)m0->m_pkthdr.len <= ifp->if_mtu) {
|
||||
md = m0;
|
||||
pf_dummynet_route(pd, s, r, ifp, sintosa(&dst), &md);
|
||||
@ -9474,14 +9483,15 @@ eat_pkt:
|
||||
if (s)
|
||||
PF_STATE_UNLOCK(s);
|
||||
|
||||
out:
|
||||
#ifdef INET6
|
||||
/* If reassembled packet passed, create new fragments. */
|
||||
if (af == AF_INET6 && action == PF_PASS && *m0 && dir == PF_OUT &&
|
||||
(! (pflags & PF_PFIL_NOREFRAGMENT)) &&
|
||||
(mtag = m_tag_find(pd.m, PACKET_TAG_PF_REASSEMBLED, NULL)) != NULL)
|
||||
action = pf_refragment6(ifp, m0, mtag, pflags & PFIL_FWD);
|
||||
action = pf_refragment6(ifp, m0, mtag, NULL, pflags & PFIL_FWD);
|
||||
#endif
|
||||
|
||||
out:
|
||||
pf_sctp_multihome_delayed(&pd, kif, s, action);
|
||||
|
||||
return (action);
|
||||
|
@ -42,6 +42,7 @@
|
||||
#include <sys/socket.h>
|
||||
|
||||
#include <net/if.h>
|
||||
#include <net/if_var.h>
|
||||
#include <net/vnet.h>
|
||||
#include <net/pfvar.h>
|
||||
#include <net/if_pflog.h>
|
||||
@ -49,6 +50,8 @@
|
||||
#include <netinet/in.h>
|
||||
#include <netinet/ip.h>
|
||||
#include <netinet/ip_var.h>
|
||||
#include <netinet6/in6_var.h>
|
||||
#include <netinet6/nd6.h>
|
||||
#include <netinet6/ip6_var.h>
|
||||
#include <netinet6/scope6_var.h>
|
||||
#include <netinet/tcp.h>
|
||||
@ -958,7 +961,7 @@ pf_max_frag_size(struct mbuf *m)
|
||||
|
||||
int
|
||||
pf_refragment6(struct ifnet *ifp, struct mbuf **m0, struct m_tag *mtag,
|
||||
bool forward)
|
||||
struct ifnet *rt, bool forward)
|
||||
{
|
||||
struct mbuf *m = *m0, *t;
|
||||
struct ip6_hdr *hdr;
|
||||
@ -1029,16 +1032,27 @@ pf_refragment6(struct ifnet *ifp, struct mbuf **m0, struct m_tag *mtag,
|
||||
m->m_flags |= M_SKIP_FIREWALL;
|
||||
memset(&pd, 0, sizeof(pd));
|
||||
pd.pf_mtag = pf_find_mtag(m);
|
||||
if (error == 0)
|
||||
if (forward) {
|
||||
MPASS(m->m_pkthdr.rcvif != NULL);
|
||||
ip6_forward(m, 0);
|
||||
} else {
|
||||
(void)ip6_output(m, NULL, NULL, 0, NULL, NULL,
|
||||
NULL);
|
||||
}
|
||||
else
|
||||
if (error != 0) {
|
||||
m_freem(m);
|
||||
continue;
|
||||
}
|
||||
if (rt != NULL) {
|
||||
struct sockaddr_in6 dst;
|
||||
hdr = mtod(m, struct ip6_hdr *);
|
||||
|
||||
bzero(&dst, sizeof(dst));
|
||||
dst.sin6_family = AF_INET6;
|
||||
dst.sin6_len = sizeof(dst);
|
||||
dst.sin6_addr = hdr->ip6_dst;
|
||||
|
||||
nd6_output_ifp(rt, rt, m, &dst, NULL);
|
||||
} else if (forward) {
|
||||
MPASS(m->m_pkthdr.rcvif != NULL);
|
||||
ip6_forward(m, 0);
|
||||
} else {
|
||||
(void)ip6_output(m, NULL, NULL, 0, NULL, NULL,
|
||||
NULL);
|
||||
}
|
||||
}
|
||||
|
||||
return (action);
|
||||
|
@ -155,6 +155,75 @@ v6_cleanup()
|
||||
pft_cleanup
|
||||
}
|
||||
|
||||
atf_test_case "v6_route_to" "cleanup"
|
||||
v6_route_to_head()
|
||||
{
|
||||
atf_set descr 'Test IPv6 reassembly combined with route-to'
|
||||
atf_set require.user root
|
||||
}
|
||||
|
||||
v6_route_to_body()
|
||||
{
|
||||
pft_init
|
||||
}
|
||||
|
||||
v6_route_to_cleanup()
|
||||
{
|
||||
pft_cleanup
|
||||
|
||||
epair_send=$(vnet_mkepair)
|
||||
epair_link=$(vnet_mkepair)
|
||||
|
||||
vnet_mkjail alcatraz ${epair_send}b ${epair_link}a
|
||||
vnet_mkjail singsing ${epair_link}b
|
||||
|
||||
ifconfig ${epair_send}a inet6 2001:db8:42::1/64 no_dad up
|
||||
|
||||
jexec alcatraz ifconfig ${epair_send}b inet6 2001:db8:42::2/64 no_dad up
|
||||
jexec alcatraz ifconfig ${epair_link}a inet6 2001:db8:43::2/64 no_dad up
|
||||
jexec alcatraz sysctl net.inet6.ip6.forwarding=1
|
||||
|
||||
jexec singsing ifconfig ${epair_link}b inet6 2001:db8:43::3/64 no_dad up
|
||||
jexec singsing route add -6 2001:db8:42::/64 2001:db8:43::2
|
||||
route add -6 2001:db8:43::/64 2001:db8:42::2
|
||||
|
||||
jexec alcatraz ifconfig ${epair_send}b inet6 -ifdisabled
|
||||
jexec alcatraz ifconfig ${epair_link}a inet6 -ifdisabled
|
||||
jexec singsing ifconfig ${epair_link}b inet6 -ifdisabled
|
||||
ifconfig ${epair_send}a inet6 -ifdisabled
|
||||
|
||||
jexec alcatraz pfctl -e
|
||||
pft_set_rules alcatraz \
|
||||
"set reassemble yes" \
|
||||
"pass" \
|
||||
"pass in route-to (${epair_link}a 2001:db8:43::3) inet6 proto icmp6 from any to 2001:db8:43::3 keep state"
|
||||
|
||||
# Forwarding test
|
||||
atf_check -s exit:0 -o ignore \
|
||||
ping -6 -c 1 2001:db8:43::3
|
||||
|
||||
atf_check -s exit:0 -o ignore \
|
||||
ping -6 -c 1 -s 4500 2001:db8:43::3
|
||||
|
||||
atf_check -s exit:0 -o ignore\
|
||||
ping -6 -c 1 -b 70000 -s 65000 2001:db8:43::3
|
||||
|
||||
# Now test this without fragmentation
|
||||
pft_set_rules alcatraz \
|
||||
"set reassemble no" \
|
||||
"pass" \
|
||||
"pass in route-to (${epair_link}a 2001:db8:43::3) inet6 proto icmp6 from any to 2001:db8:43::3 keep state"
|
||||
|
||||
atf_check -s exit:0 -o ignore \
|
||||
ping -6 -c 1 2001:db8:43::3
|
||||
|
||||
atf_check -s exit:0 -o ignore \
|
||||
ping -6 -c 1 -s 4500 2001:db8:43::3
|
||||
|
||||
atf_check -s exit:0 -o ignore\
|
||||
ping -6 -c 1 -b 70000 -s 65000 2001:db8:43::3
|
||||
}
|
||||
|
||||
atf_test_case "mtu_diff" "cleanup"
|
||||
mtu_diff_head()
|
||||
{
|
||||
@ -544,6 +613,7 @@ atf_init_test_cases()
|
||||
{
|
||||
atf_add_test_case "too_many_fragments"
|
||||
atf_add_test_case "v6"
|
||||
atf_add_test_case "v6_route_to"
|
||||
atf_add_test_case "mtu_diff"
|
||||
atf_add_test_case "overreplace"
|
||||
atf_add_test_case "overindex"
|
||||
|
Loading…
Reference in New Issue
Block a user