Message ID | 20190724170018.96659-6-sdf@google.com |
---|---|
State | Changes Requested |
Delegated to: | BPF Maintainers |
Headers | show |
Series | bpf/flow_dissector: support input flags | expand |
On Wed, Jul 24, 2019 at 10:11 AM Stanislav Fomichev <sdf@google.com> wrote: > > bpf_flow.c: exit early unless FLOW_DISSECTOR_F_PARSE_1ST_FRAG is passed > in flags. Also, set ip_proto earlier, this makes sure we have correct > value with fragmented packets. > > Add selftest cases to test ipv4/ipv6 fragments and skip eth_get_headlen > tests that don't have FLOW_DISSECTOR_F_PARSE_1ST_FRAG flag. > > eth_get_headlen calls flow dissector with > FLOW_DISSECTOR_F_PARSE_1ST_FRAG flag so we can't run tests that > have different set of input flags against it. > > Cc: Willem de Bruijn <willemb@google.com> > Cc: Petar Penkov <ppenkov@google.com> > Signed-off-by: Stanislav Fomichev <sdf@google.com> > --- > .../selftests/bpf/prog_tests/flow_dissector.c | 129 ++++++++++++++++++ > tools/testing/selftests/bpf/progs/bpf_flow.c | 28 +++- > 2 files changed, 151 insertions(+), 6 deletions(-) > > diff --git a/tools/testing/selftests/bpf/prog_tests/flow_dissector.c b/tools/testing/selftests/bpf/prog_tests/flow_dissector.c > index c938283ac232..966cb3b06870 100644 > --- a/tools/testing/selftests/bpf/prog_tests/flow_dissector.c > +++ b/tools/testing/selftests/bpf/prog_tests/flow_dissector.c > @@ -5,6 +5,10 @@ > #include <linux/if_tun.h> > #include <sys/uio.h> > > +#ifndef IP_MF > +#define IP_MF 0x2000 > +#endif > + > #define CHECK_FLOW_KEYS(desc, got, expected) \ > CHECK_ATTR(memcmp(&got, &expected, sizeof(got)) != 0, \ > desc, \ > @@ -49,6 +53,18 @@ struct ipv6_pkt { > struct tcphdr tcp; > } __packed; > > +struct ipv6_frag_pkt { > + struct ethhdr eth; > + struct ipv6hdr iph; > + struct frag_hdr { > + __u8 nexthdr; > + __u8 reserved; > + __be16 frag_off; > + __be32 identification; > + } ipf; > + struct tcphdr tcp; > +} __packed; > + > struct dvlan_ipv6_pkt { > struct ethhdr eth; > __u16 vlan_tci; > @@ -65,9 +81,11 @@ struct test { > struct ipv4_pkt ipv4; > struct svlan_ipv4_pkt svlan_ipv4; > struct ipv6_pkt ipv6; > + struct ipv6_frag_pkt ipv6_frag; > struct dvlan_ipv6_pkt dvlan_ipv6; > } pkt; > struct bpf_flow_keys keys; > + __u32 flags; > }; > > #define VLAN_HLEN 4 > @@ -143,6 +161,102 @@ struct test tests[] = { > .n_proto = __bpf_constant_htons(ETH_P_IPV6), > }, > }, > + { > + .name = "ipv4-frag", > + .pkt.ipv4 = { > + .eth.h_proto = __bpf_constant_htons(ETH_P_IP), > + .iph.ihl = 5, > + .iph.protocol = IPPROTO_TCP, > + .iph.tot_len = __bpf_constant_htons(MAGIC_BYTES), > + .iph.frag_off = __bpf_constant_htons(IP_MF), > + .tcp.doff = 5, > + .tcp.source = 80, > + .tcp.dest = 8080, > + }, > + .keys = { > + .flags = FLOW_DISSECTOR_F_PARSE_1ST_FRAG, > + .nhoff = ETH_HLEN, > + .thoff = ETH_HLEN + sizeof(struct iphdr), > + .addr_proto = ETH_P_IP, > + .ip_proto = IPPROTO_TCP, > + .n_proto = __bpf_constant_htons(ETH_P_IP), > + .is_frag = true, > + .is_first_frag = true, > + .sport = 80, > + .dport = 8080, > + }, > + .flags = FLOW_DISSECTOR_F_PARSE_1ST_FRAG, > + }, > + { > + .name = "ipv4-no-frag", > + .pkt.ipv4 = { > + .eth.h_proto = __bpf_constant_htons(ETH_P_IP), > + .iph.ihl = 5, > + .iph.protocol = IPPROTO_TCP, > + .iph.tot_len = __bpf_constant_htons(MAGIC_BYTES), > + .iph.frag_off = __bpf_constant_htons(IP_MF), > + .tcp.doff = 5, > + .tcp.source = 80, > + .tcp.dest = 8080, > + }, > + .keys = { > + .nhoff = ETH_HLEN, > + .thoff = ETH_HLEN + sizeof(struct iphdr), > + .addr_proto = ETH_P_IP, > + .ip_proto = IPPROTO_TCP, > + .n_proto = __bpf_constant_htons(ETH_P_IP), > + .is_frag = true, > + .is_first_frag = true, > + }, > + }, > + { > + .name = "ipv6-frag", > + .pkt.ipv6_frag = { > + .eth.h_proto = __bpf_constant_htons(ETH_P_IPV6), > + .iph.nexthdr = IPPROTO_FRAGMENT, > + .iph.payload_len = __bpf_constant_htons(MAGIC_BYTES), > + .ipf.nexthdr = IPPROTO_TCP, > + .tcp.doff = 5, > + .tcp.source = 80, > + .tcp.dest = 8080, > + }, > + .keys = { > + .flags = FLOW_DISSECTOR_F_PARSE_1ST_FRAG, > + .nhoff = ETH_HLEN, > + .thoff = ETH_HLEN + sizeof(struct ipv6hdr) + > + sizeof(struct frag_hdr), > + .addr_proto = ETH_P_IPV6, > + .ip_proto = IPPROTO_TCP, > + .n_proto = __bpf_constant_htons(ETH_P_IPV6), > + .is_frag = true, > + .is_first_frag = true, > + .sport = 80, > + .dport = 8080, > + }, > + .flags = FLOW_DISSECTOR_F_PARSE_1ST_FRAG, > + }, > + { > + .name = "ipv6-no-frag", > + .pkt.ipv6_frag = { > + .eth.h_proto = __bpf_constant_htons(ETH_P_IPV6), > + .iph.nexthdr = IPPROTO_FRAGMENT, > + .iph.payload_len = __bpf_constant_htons(MAGIC_BYTES), > + .ipf.nexthdr = IPPROTO_TCP, > + .tcp.doff = 5, > + .tcp.source = 80, > + .tcp.dest = 8080, > + }, > + .keys = { > + .nhoff = ETH_HLEN, > + .thoff = ETH_HLEN + sizeof(struct ipv6hdr) + > + sizeof(struct frag_hdr), > + .addr_proto = ETH_P_IPV6, > + .ip_proto = IPPROTO_TCP, > + .n_proto = __bpf_constant_htons(ETH_P_IPV6), > + .is_frag = true, > + .is_first_frag = true, > + }, > + }, > }; > > static int create_tap(const char *ifname) > @@ -225,6 +339,13 @@ void test_flow_dissector(void) > .data_size_in = sizeof(tests[i].pkt), > .data_out = &flow_keys, > }; > + static struct bpf_flow_keys ctx = {}; > + > + if (tests[i].flags) { > + tattr.ctx_in = &ctx; > + tattr.ctx_size_in = sizeof(ctx); > + ctx.flags = tests[i].flags; > + } > > err = bpf_prog_test_run_xattr(&tattr); > CHECK_ATTR(tattr.data_size_out != sizeof(flow_keys) || > @@ -255,6 +376,14 @@ void test_flow_dissector(void) > struct bpf_prog_test_run_attr tattr = {}; > __u32 key = 0; > > + /* Don't run tests that are not marked as > + * FLOW_DISSECTOR_F_PARSE_1ST_FRAG; eth_get_headlen > + * sets this flag. > + */ > + > + if (tests[i].flags != FLOW_DISSECTOR_F_PARSE_1ST_FRAG) > + continue; Maybe test flags & FLOW_DISSECTOR_F_PARSE_1ST_FRAG == 0 instead? It is not necessary now, but might be useful in the future. Thanks, Song
On 07/24, Song Liu wrote: > On Wed, Jul 24, 2019 at 10:11 AM Stanislav Fomichev <sdf@google.com> wrote: > > > > bpf_flow.c: exit early unless FLOW_DISSECTOR_F_PARSE_1ST_FRAG is passed > > in flags. Also, set ip_proto earlier, this makes sure we have correct > > value with fragmented packets. > > > > Add selftest cases to test ipv4/ipv6 fragments and skip eth_get_headlen > > tests that don't have FLOW_DISSECTOR_F_PARSE_1ST_FRAG flag. > > > > eth_get_headlen calls flow dissector with > > FLOW_DISSECTOR_F_PARSE_1ST_FRAG flag so we can't run tests that > > have different set of input flags against it. > > > > Cc: Willem de Bruijn <willemb@google.com> > > Cc: Petar Penkov <ppenkov@google.com> > > Signed-off-by: Stanislav Fomichev <sdf@google.com> > > --- > > .../selftests/bpf/prog_tests/flow_dissector.c | 129 ++++++++++++++++++ > > tools/testing/selftests/bpf/progs/bpf_flow.c | 28 +++- > > 2 files changed, 151 insertions(+), 6 deletions(-) > > > > diff --git a/tools/testing/selftests/bpf/prog_tests/flow_dissector.c b/tools/testing/selftests/bpf/prog_tests/flow_dissector.c > > index c938283ac232..966cb3b06870 100644 > > --- a/tools/testing/selftests/bpf/prog_tests/flow_dissector.c > > +++ b/tools/testing/selftests/bpf/prog_tests/flow_dissector.c > > @@ -5,6 +5,10 @@ > > #include <linux/if_tun.h> > > #include <sys/uio.h> > > > > +#ifndef IP_MF > > +#define IP_MF 0x2000 > > +#endif > > + > > #define CHECK_FLOW_KEYS(desc, got, expected) \ > > CHECK_ATTR(memcmp(&got, &expected, sizeof(got)) != 0, \ > > desc, \ > > @@ -49,6 +53,18 @@ struct ipv6_pkt { > > struct tcphdr tcp; > > } __packed; > > > > +struct ipv6_frag_pkt { > > + struct ethhdr eth; > > + struct ipv6hdr iph; > > + struct frag_hdr { > > + __u8 nexthdr; > > + __u8 reserved; > > + __be16 frag_off; > > + __be32 identification; > > + } ipf; > > + struct tcphdr tcp; > > +} __packed; > > + > > struct dvlan_ipv6_pkt { > > struct ethhdr eth; > > __u16 vlan_tci; > > @@ -65,9 +81,11 @@ struct test { > > struct ipv4_pkt ipv4; > > struct svlan_ipv4_pkt svlan_ipv4; > > struct ipv6_pkt ipv6; > > + struct ipv6_frag_pkt ipv6_frag; > > struct dvlan_ipv6_pkt dvlan_ipv6; > > } pkt; > > struct bpf_flow_keys keys; > > + __u32 flags; > > }; > > > > #define VLAN_HLEN 4 > > @@ -143,6 +161,102 @@ struct test tests[] = { > > .n_proto = __bpf_constant_htons(ETH_P_IPV6), > > }, > > }, > > + { > > + .name = "ipv4-frag", > > + .pkt.ipv4 = { > > + .eth.h_proto = __bpf_constant_htons(ETH_P_IP), > > + .iph.ihl = 5, > > + .iph.protocol = IPPROTO_TCP, > > + .iph.tot_len = __bpf_constant_htons(MAGIC_BYTES), > > + .iph.frag_off = __bpf_constant_htons(IP_MF), > > + .tcp.doff = 5, > > + .tcp.source = 80, > > + .tcp.dest = 8080, > > + }, > > + .keys = { > > + .flags = FLOW_DISSECTOR_F_PARSE_1ST_FRAG, > > + .nhoff = ETH_HLEN, > > + .thoff = ETH_HLEN + sizeof(struct iphdr), > > + .addr_proto = ETH_P_IP, > > + .ip_proto = IPPROTO_TCP, > > + .n_proto = __bpf_constant_htons(ETH_P_IP), > > + .is_frag = true, > > + .is_first_frag = true, > > + .sport = 80, > > + .dport = 8080, > > + }, > > + .flags = FLOW_DISSECTOR_F_PARSE_1ST_FRAG, > > + }, > > + { > > + .name = "ipv4-no-frag", > > + .pkt.ipv4 = { > > + .eth.h_proto = __bpf_constant_htons(ETH_P_IP), > > + .iph.ihl = 5, > > + .iph.protocol = IPPROTO_TCP, > > + .iph.tot_len = __bpf_constant_htons(MAGIC_BYTES), > > + .iph.frag_off = __bpf_constant_htons(IP_MF), > > + .tcp.doff = 5, > > + .tcp.source = 80, > > + .tcp.dest = 8080, > > + }, > > + .keys = { > > + .nhoff = ETH_HLEN, > > + .thoff = ETH_HLEN + sizeof(struct iphdr), > > + .addr_proto = ETH_P_IP, > > + .ip_proto = IPPROTO_TCP, > > + .n_proto = __bpf_constant_htons(ETH_P_IP), > > + .is_frag = true, > > + .is_first_frag = true, > > + }, > > + }, > > + { > > + .name = "ipv6-frag", > > + .pkt.ipv6_frag = { > > + .eth.h_proto = __bpf_constant_htons(ETH_P_IPV6), > > + .iph.nexthdr = IPPROTO_FRAGMENT, > > + .iph.payload_len = __bpf_constant_htons(MAGIC_BYTES), > > + .ipf.nexthdr = IPPROTO_TCP, > > + .tcp.doff = 5, > > + .tcp.source = 80, > > + .tcp.dest = 8080, > > + }, > > + .keys = { > > + .flags = FLOW_DISSECTOR_F_PARSE_1ST_FRAG, > > + .nhoff = ETH_HLEN, > > + .thoff = ETH_HLEN + sizeof(struct ipv6hdr) + > > + sizeof(struct frag_hdr), > > + .addr_proto = ETH_P_IPV6, > > + .ip_proto = IPPROTO_TCP, > > + .n_proto = __bpf_constant_htons(ETH_P_IPV6), > > + .is_frag = true, > > + .is_first_frag = true, > > + .sport = 80, > > + .dport = 8080, > > + }, > > + .flags = FLOW_DISSECTOR_F_PARSE_1ST_FRAG, > > + }, > > + { > > + .name = "ipv6-no-frag", > > + .pkt.ipv6_frag = { > > + .eth.h_proto = __bpf_constant_htons(ETH_P_IPV6), > > + .iph.nexthdr = IPPROTO_FRAGMENT, > > + .iph.payload_len = __bpf_constant_htons(MAGIC_BYTES), > > + .ipf.nexthdr = IPPROTO_TCP, > > + .tcp.doff = 5, > > + .tcp.source = 80, > > + .tcp.dest = 8080, > > + }, > > + .keys = { > > + .nhoff = ETH_HLEN, > > + .thoff = ETH_HLEN + sizeof(struct ipv6hdr) + > > + sizeof(struct frag_hdr), > > + .addr_proto = ETH_P_IPV6, > > + .ip_proto = IPPROTO_TCP, > > + .n_proto = __bpf_constant_htons(ETH_P_IPV6), > > + .is_frag = true, > > + .is_first_frag = true, > > + }, > > + }, > > }; > > > > static int create_tap(const char *ifname) > > @@ -225,6 +339,13 @@ void test_flow_dissector(void) > > .data_size_in = sizeof(tests[i].pkt), > > .data_out = &flow_keys, > > }; > > + static struct bpf_flow_keys ctx = {}; > > + > > + if (tests[i].flags) { > > + tattr.ctx_in = &ctx; > > + tattr.ctx_size_in = sizeof(ctx); > > + ctx.flags = tests[i].flags; > > + } > > > > err = bpf_prog_test_run_xattr(&tattr); > > CHECK_ATTR(tattr.data_size_out != sizeof(flow_keys) || > > @@ -255,6 +376,14 @@ void test_flow_dissector(void) > > struct bpf_prog_test_run_attr tattr = {}; > > __u32 key = 0; > > > > + /* Don't run tests that are not marked as > > + * FLOW_DISSECTOR_F_PARSE_1ST_FRAG; eth_get_headlen > > + * sets this flag. > > + */ > > + > > + if (tests[i].flags != FLOW_DISSECTOR_F_PARSE_1ST_FRAG) > > + continue; > > Maybe test flags & FLOW_DISSECTOR_F_PARSE_1ST_FRAG == 0 instead? > It is not necessary now, but might be useful in the future. I'm not sure about this one. We want flags here to match flags from eth_get_headlen: const unsigned int flags = FLOW_DISSECTOR_F_PARSE_1ST_FRAG; ... if (!skb_flow_dissect_flow_keys_basic(..., flags)) Otherwise the test might break unexpectedly. So I'd rather manually adjust a test here if eth_get_headlen flags change. Maybe I should clarify the comment to signify that dependency? Because currently it might be read as if we only care about FLOW_DISSECTOR_F_PARSE_1ST_FRAG, but we really care about all flags in eth_get_headlen; it just happens that it only has one right now.
On Wed, Jul 24, 2019 at 4:52 PM Stanislav Fomichev <sdf@fomichev.me> wrote: > > On 07/24, Song Liu wrote: > > On Wed, Jul 24, 2019 at 10:11 AM Stanislav Fomichev <sdf@google.com> wrote: > > > > > > bpf_flow.c: exit early unless FLOW_DISSECTOR_F_PARSE_1ST_FRAG is passed > > > in flags. Also, set ip_proto earlier, this makes sure we have correct > > > value with fragmented packets. > > > > > > Add selftest cases to test ipv4/ipv6 fragments and skip eth_get_headlen > > > tests that don't have FLOW_DISSECTOR_F_PARSE_1ST_FRAG flag. > > > > > > eth_get_headlen calls flow dissector with > > > FLOW_DISSECTOR_F_PARSE_1ST_FRAG flag so we can't run tests that > > > have different set of input flags against it. > > > > > > Cc: Willem de Bruijn <willemb@google.com> > > > Cc: Petar Penkov <ppenkov@google.com> > > > Signed-off-by: Stanislav Fomichev <sdf@google.com> > > > --- > > > .../selftests/bpf/prog_tests/flow_dissector.c | 129 ++++++++++++++++++ > > > tools/testing/selftests/bpf/progs/bpf_flow.c | 28 +++- > > > 2 files changed, 151 insertions(+), 6 deletions(-) > > > > > > diff --git a/tools/testing/selftests/bpf/prog_tests/flow_dissector.c b/tools/testing/selftests/bpf/prog_tests/flow_dissector.c > > > index c938283ac232..966cb3b06870 100644 > > > --- a/tools/testing/selftests/bpf/prog_tests/flow_dissector.c > > > +++ b/tools/testing/selftests/bpf/prog_tests/flow_dissector.c > > > @@ -5,6 +5,10 @@ > > > #include <linux/if_tun.h> > > > #include <sys/uio.h> > > > > > > +#ifndef IP_MF > > > +#define IP_MF 0x2000 > > > +#endif > > > + > > > #define CHECK_FLOW_KEYS(desc, got, expected) \ > > > CHECK_ATTR(memcmp(&got, &expected, sizeof(got)) != 0, \ > > > desc, \ > > > @@ -49,6 +53,18 @@ struct ipv6_pkt { > > > struct tcphdr tcp; > > > } __packed; > > > > > > +struct ipv6_frag_pkt { > > > + struct ethhdr eth; > > > + struct ipv6hdr iph; > > > + struct frag_hdr { > > > + __u8 nexthdr; > > > + __u8 reserved; > > > + __be16 frag_off; > > > + __be32 identification; > > > + } ipf; > > > + struct tcphdr tcp; > > > +} __packed; > > > + > > > struct dvlan_ipv6_pkt { > > > struct ethhdr eth; > > > __u16 vlan_tci; > > > @@ -65,9 +81,11 @@ struct test { > > > struct ipv4_pkt ipv4; > > > struct svlan_ipv4_pkt svlan_ipv4; > > > struct ipv6_pkt ipv6; > > > + struct ipv6_frag_pkt ipv6_frag; > > > struct dvlan_ipv6_pkt dvlan_ipv6; > > > } pkt; > > > struct bpf_flow_keys keys; > > > + __u32 flags; > > > }; > > > > > > #define VLAN_HLEN 4 > > > @@ -143,6 +161,102 @@ struct test tests[] = { > > > .n_proto = __bpf_constant_htons(ETH_P_IPV6), > > > }, > > > }, > > > + { > > > + .name = "ipv4-frag", > > > + .pkt.ipv4 = { > > > + .eth.h_proto = __bpf_constant_htons(ETH_P_IP), > > > + .iph.ihl = 5, > > > + .iph.protocol = IPPROTO_TCP, > > > + .iph.tot_len = __bpf_constant_htons(MAGIC_BYTES), > > > + .iph.frag_off = __bpf_constant_htons(IP_MF), > > > + .tcp.doff = 5, > > > + .tcp.source = 80, > > > + .tcp.dest = 8080, > > > + }, > > > + .keys = { > > > + .flags = FLOW_DISSECTOR_F_PARSE_1ST_FRAG, > > > + .nhoff = ETH_HLEN, > > > + .thoff = ETH_HLEN + sizeof(struct iphdr), > > > + .addr_proto = ETH_P_IP, > > > + .ip_proto = IPPROTO_TCP, > > > + .n_proto = __bpf_constant_htons(ETH_P_IP), > > > + .is_frag = true, > > > + .is_first_frag = true, > > > + .sport = 80, > > > + .dport = 8080, > > > + }, > > > + .flags = FLOW_DISSECTOR_F_PARSE_1ST_FRAG, > > > + }, > > > + { > > > + .name = "ipv4-no-frag", > > > + .pkt.ipv4 = { > > > + .eth.h_proto = __bpf_constant_htons(ETH_P_IP), > > > + .iph.ihl = 5, > > > + .iph.protocol = IPPROTO_TCP, > > > + .iph.tot_len = __bpf_constant_htons(MAGIC_BYTES), > > > + .iph.frag_off = __bpf_constant_htons(IP_MF), > > > + .tcp.doff = 5, > > > + .tcp.source = 80, > > > + .tcp.dest = 8080, > > > + }, > > > + .keys = { > > > + .nhoff = ETH_HLEN, > > > + .thoff = ETH_HLEN + sizeof(struct iphdr), > > > + .addr_proto = ETH_P_IP, > > > + .ip_proto = IPPROTO_TCP, > > > + .n_proto = __bpf_constant_htons(ETH_P_IP), > > > + .is_frag = true, > > > + .is_first_frag = true, > > > + }, > > > + }, > > > + { > > > + .name = "ipv6-frag", > > > + .pkt.ipv6_frag = { > > > + .eth.h_proto = __bpf_constant_htons(ETH_P_IPV6), > > > + .iph.nexthdr = IPPROTO_FRAGMENT, > > > + .iph.payload_len = __bpf_constant_htons(MAGIC_BYTES), > > > + .ipf.nexthdr = IPPROTO_TCP, > > > + .tcp.doff = 5, > > > + .tcp.source = 80, > > > + .tcp.dest = 8080, > > > + }, > > > + .keys = { > > > + .flags = FLOW_DISSECTOR_F_PARSE_1ST_FRAG, > > > + .nhoff = ETH_HLEN, > > > + .thoff = ETH_HLEN + sizeof(struct ipv6hdr) + > > > + sizeof(struct frag_hdr), > > > + .addr_proto = ETH_P_IPV6, > > > + .ip_proto = IPPROTO_TCP, > > > + .n_proto = __bpf_constant_htons(ETH_P_IPV6), > > > + .is_frag = true, > > > + .is_first_frag = true, > > > + .sport = 80, > > > + .dport = 8080, > > > + }, > > > + .flags = FLOW_DISSECTOR_F_PARSE_1ST_FRAG, > > > + }, > > > + { > > > + .name = "ipv6-no-frag", > > > + .pkt.ipv6_frag = { > > > + .eth.h_proto = __bpf_constant_htons(ETH_P_IPV6), > > > + .iph.nexthdr = IPPROTO_FRAGMENT, > > > + .iph.payload_len = __bpf_constant_htons(MAGIC_BYTES), > > > + .ipf.nexthdr = IPPROTO_TCP, > > > + .tcp.doff = 5, > > > + .tcp.source = 80, > > > + .tcp.dest = 8080, > > > + }, > > > + .keys = { > > > + .nhoff = ETH_HLEN, > > > + .thoff = ETH_HLEN + sizeof(struct ipv6hdr) + > > > + sizeof(struct frag_hdr), > > > + .addr_proto = ETH_P_IPV6, > > > + .ip_proto = IPPROTO_TCP, > > > + .n_proto = __bpf_constant_htons(ETH_P_IPV6), > > > + .is_frag = true, > > > + .is_first_frag = true, > > > + }, > > > + }, > > > }; > > > > > > static int create_tap(const char *ifname) > > > @@ -225,6 +339,13 @@ void test_flow_dissector(void) > > > .data_size_in = sizeof(tests[i].pkt), > > > .data_out = &flow_keys, > > > }; > > > + static struct bpf_flow_keys ctx = {}; > > > + > > > + if (tests[i].flags) { > > > + tattr.ctx_in = &ctx; > > > + tattr.ctx_size_in = sizeof(ctx); > > > + ctx.flags = tests[i].flags; > > > + } > > > > > > err = bpf_prog_test_run_xattr(&tattr); > > > CHECK_ATTR(tattr.data_size_out != sizeof(flow_keys) || > > > @@ -255,6 +376,14 @@ void test_flow_dissector(void) > > > struct bpf_prog_test_run_attr tattr = {}; > > > __u32 key = 0; > > > > > > + /* Don't run tests that are not marked as > > > + * FLOW_DISSECTOR_F_PARSE_1ST_FRAG; eth_get_headlen > > > + * sets this flag. > > > + */ > > > + > > > + if (tests[i].flags != FLOW_DISSECTOR_F_PARSE_1ST_FRAG) > > > + continue; > > > > Maybe test flags & FLOW_DISSECTOR_F_PARSE_1ST_FRAG == 0 instead? > > It is not necessary now, but might be useful in the future. > I'm not sure about this one. We want flags here to match flags > from eth_get_headlen: > > const unsigned int flags = FLOW_DISSECTOR_F_PARSE_1ST_FRAG; > ... > if (!skb_flow_dissect_flow_keys_basic(..., flags)) > > Otherwise the test might break unexpectedly. So I'd rather manually > adjust a test here if eth_get_headlen flags change. Could we have flags == FLOW_DISSECTOR_F_PARSE_1ST_FRAG | some_other_flag in the future? This flag is not equal to FLOW_DISSECTOR_F_PARSE_1ST_FRAG. > > Maybe I should clarify the comment to signify that dependency? Because > currently it might be read as if we only care about > FLOW_DISSECTOR_F_PARSE_1ST_FRAG, but we really care about all flags > in eth_get_headlen; it just happens that it only has one right now. Some clarification will be great. Thanks, Song
diff --git a/tools/testing/selftests/bpf/prog_tests/flow_dissector.c b/tools/testing/selftests/bpf/prog_tests/flow_dissector.c index c938283ac232..966cb3b06870 100644 --- a/tools/testing/selftests/bpf/prog_tests/flow_dissector.c +++ b/tools/testing/selftests/bpf/prog_tests/flow_dissector.c @@ -5,6 +5,10 @@ #include <linux/if_tun.h> #include <sys/uio.h> +#ifndef IP_MF +#define IP_MF 0x2000 +#endif + #define CHECK_FLOW_KEYS(desc, got, expected) \ CHECK_ATTR(memcmp(&got, &expected, sizeof(got)) != 0, \ desc, \ @@ -49,6 +53,18 @@ struct ipv6_pkt { struct tcphdr tcp; } __packed; +struct ipv6_frag_pkt { + struct ethhdr eth; + struct ipv6hdr iph; + struct frag_hdr { + __u8 nexthdr; + __u8 reserved; + __be16 frag_off; + __be32 identification; + } ipf; + struct tcphdr tcp; +} __packed; + struct dvlan_ipv6_pkt { struct ethhdr eth; __u16 vlan_tci; @@ -65,9 +81,11 @@ struct test { struct ipv4_pkt ipv4; struct svlan_ipv4_pkt svlan_ipv4; struct ipv6_pkt ipv6; + struct ipv6_frag_pkt ipv6_frag; struct dvlan_ipv6_pkt dvlan_ipv6; } pkt; struct bpf_flow_keys keys; + __u32 flags; }; #define VLAN_HLEN 4 @@ -143,6 +161,102 @@ struct test tests[] = { .n_proto = __bpf_constant_htons(ETH_P_IPV6), }, }, + { + .name = "ipv4-frag", + .pkt.ipv4 = { + .eth.h_proto = __bpf_constant_htons(ETH_P_IP), + .iph.ihl = 5, + .iph.protocol = IPPROTO_TCP, + .iph.tot_len = __bpf_constant_htons(MAGIC_BYTES), + .iph.frag_off = __bpf_constant_htons(IP_MF), + .tcp.doff = 5, + .tcp.source = 80, + .tcp.dest = 8080, + }, + .keys = { + .flags = FLOW_DISSECTOR_F_PARSE_1ST_FRAG, + .nhoff = ETH_HLEN, + .thoff = ETH_HLEN + sizeof(struct iphdr), + .addr_proto = ETH_P_IP, + .ip_proto = IPPROTO_TCP, + .n_proto = __bpf_constant_htons(ETH_P_IP), + .is_frag = true, + .is_first_frag = true, + .sport = 80, + .dport = 8080, + }, + .flags = FLOW_DISSECTOR_F_PARSE_1ST_FRAG, + }, + { + .name = "ipv4-no-frag", + .pkt.ipv4 = { + .eth.h_proto = __bpf_constant_htons(ETH_P_IP), + .iph.ihl = 5, + .iph.protocol = IPPROTO_TCP, + .iph.tot_len = __bpf_constant_htons(MAGIC_BYTES), + .iph.frag_off = __bpf_constant_htons(IP_MF), + .tcp.doff = 5, + .tcp.source = 80, + .tcp.dest = 8080, + }, + .keys = { + .nhoff = ETH_HLEN, + .thoff = ETH_HLEN + sizeof(struct iphdr), + .addr_proto = ETH_P_IP, + .ip_proto = IPPROTO_TCP, + .n_proto = __bpf_constant_htons(ETH_P_IP), + .is_frag = true, + .is_first_frag = true, + }, + }, + { + .name = "ipv6-frag", + .pkt.ipv6_frag = { + .eth.h_proto = __bpf_constant_htons(ETH_P_IPV6), + .iph.nexthdr = IPPROTO_FRAGMENT, + .iph.payload_len = __bpf_constant_htons(MAGIC_BYTES), + .ipf.nexthdr = IPPROTO_TCP, + .tcp.doff = 5, + .tcp.source = 80, + .tcp.dest = 8080, + }, + .keys = { + .flags = FLOW_DISSECTOR_F_PARSE_1ST_FRAG, + .nhoff = ETH_HLEN, + .thoff = ETH_HLEN + sizeof(struct ipv6hdr) + + sizeof(struct frag_hdr), + .addr_proto = ETH_P_IPV6, + .ip_proto = IPPROTO_TCP, + .n_proto = __bpf_constant_htons(ETH_P_IPV6), + .is_frag = true, + .is_first_frag = true, + .sport = 80, + .dport = 8080, + }, + .flags = FLOW_DISSECTOR_F_PARSE_1ST_FRAG, + }, + { + .name = "ipv6-no-frag", + .pkt.ipv6_frag = { + .eth.h_proto = __bpf_constant_htons(ETH_P_IPV6), + .iph.nexthdr = IPPROTO_FRAGMENT, + .iph.payload_len = __bpf_constant_htons(MAGIC_BYTES), + .ipf.nexthdr = IPPROTO_TCP, + .tcp.doff = 5, + .tcp.source = 80, + .tcp.dest = 8080, + }, + .keys = { + .nhoff = ETH_HLEN, + .thoff = ETH_HLEN + sizeof(struct ipv6hdr) + + sizeof(struct frag_hdr), + .addr_proto = ETH_P_IPV6, + .ip_proto = IPPROTO_TCP, + .n_proto = __bpf_constant_htons(ETH_P_IPV6), + .is_frag = true, + .is_first_frag = true, + }, + }, }; static int create_tap(const char *ifname) @@ -225,6 +339,13 @@ void test_flow_dissector(void) .data_size_in = sizeof(tests[i].pkt), .data_out = &flow_keys, }; + static struct bpf_flow_keys ctx = {}; + + if (tests[i].flags) { + tattr.ctx_in = &ctx; + tattr.ctx_size_in = sizeof(ctx); + ctx.flags = tests[i].flags; + } err = bpf_prog_test_run_xattr(&tattr); CHECK_ATTR(tattr.data_size_out != sizeof(flow_keys) || @@ -255,6 +376,14 @@ void test_flow_dissector(void) struct bpf_prog_test_run_attr tattr = {}; __u32 key = 0; + /* Don't run tests that are not marked as + * FLOW_DISSECTOR_F_PARSE_1ST_FRAG; eth_get_headlen + * sets this flag. + */ + + if (tests[i].flags != FLOW_DISSECTOR_F_PARSE_1ST_FRAG) + continue; + err = tx_tap(tap_fd, &tests[i].pkt, sizeof(tests[i].pkt)); CHECK(err < 0, "tx_tap", "err %d errno %d\n", err, errno); diff --git a/tools/testing/selftests/bpf/progs/bpf_flow.c b/tools/testing/selftests/bpf/progs/bpf_flow.c index 5ae485a6af3f..0eabe5e57944 100644 --- a/tools/testing/selftests/bpf/progs/bpf_flow.c +++ b/tools/testing/selftests/bpf/progs/bpf_flow.c @@ -153,7 +153,6 @@ static __always_inline int parse_ip_proto(struct __sk_buff *skb, __u8 proto) struct tcphdr *tcp, _tcp; struct udphdr *udp, _udp; - keys->ip_proto = proto; switch (proto) { case IPPROTO_ICMP: icmp = bpf_flow_dissect_get_header(skb, sizeof(*icmp), &_icmp); @@ -231,7 +230,6 @@ static __always_inline int parse_ipv6_proto(struct __sk_buff *skb, __u8 nexthdr) { struct bpf_flow_keys *keys = skb->flow_keys; - keys->ip_proto = nexthdr; switch (nexthdr) { case IPPROTO_HOPOPTS: case IPPROTO_DSTOPTS: @@ -266,6 +264,7 @@ PROG(IP)(struct __sk_buff *skb) keys->addr_proto = ETH_P_IP; keys->ipv4_src = iph->saddr; keys->ipv4_dst = iph->daddr; + keys->ip_proto = iph->protocol; keys->thoff += iph->ihl << 2; if (data + keys->thoff > data_end) @@ -273,13 +272,19 @@ PROG(IP)(struct __sk_buff *skb) if (iph->frag_off & bpf_htons(IP_MF | IP_OFFSET)) { keys->is_frag = true; - if (iph->frag_off & bpf_htons(IP_OFFSET)) + if (iph->frag_off & bpf_htons(IP_OFFSET)) { /* From second fragment on, packets do not have headers * we can parse. */ done = true; - else + } else { keys->is_first_frag = true; + /* No need to parse fragmented packet unless + * explicitly asked for. + */ + if (!(keys->flags & FLOW_DISSECTOR_F_PARSE_1ST_FRAG)) + done = true; + } } if (done) @@ -301,6 +306,7 @@ PROG(IPV6)(struct __sk_buff *skb) memcpy(&keys->ipv6_src, &ip6h->saddr, 2*sizeof(ip6h->saddr)); keys->thoff += sizeof(struct ipv6hdr); + keys->ip_proto = ip6h->nexthdr; return parse_ipv6_proto(skb, ip6h->nexthdr); } @@ -317,7 +323,8 @@ PROG(IPV6OP)(struct __sk_buff *skb) /* hlen is in 8-octets and does not include the first 8 bytes * of the header */ - skb->flow_keys->thoff += (1 + ip6h->hdrlen) << 3; + keys->thoff += (1 + ip6h->hdrlen) << 3; + keys->ip_proto = ip6h->nexthdr; return parse_ipv6_proto(skb, ip6h->nexthdr); } @@ -333,9 +340,18 @@ PROG(IPV6FR)(struct __sk_buff *skb) keys->thoff += sizeof(*fragh); keys->is_frag = true; - if (!(fragh->frag_off & bpf_htons(IP6_OFFSET))) + keys->ip_proto = fragh->nexthdr; + + if (!(fragh->frag_off & bpf_htons(IP6_OFFSET))) { keys->is_first_frag = true; + /* No need to parse fragmented packet unless + * explicitly asked for. + */ + if (!(keys->flags & FLOW_DISSECTOR_F_PARSE_1ST_FRAG)) + return export_flow_keys(keys, BPF_OK); + } + return parse_ipv6_proto(skb, fragh->nexthdr); }
bpf_flow.c: exit early unless FLOW_DISSECTOR_F_PARSE_1ST_FRAG is passed in flags. Also, set ip_proto earlier, this makes sure we have correct value with fragmented packets. Add selftest cases to test ipv4/ipv6 fragments and skip eth_get_headlen tests that don't have FLOW_DISSECTOR_F_PARSE_1ST_FRAG flag. eth_get_headlen calls flow dissector with FLOW_DISSECTOR_F_PARSE_1ST_FRAG flag so we can't run tests that have different set of input flags against it. Cc: Willem de Bruijn <willemb@google.com> Cc: Petar Penkov <ppenkov@google.com> Signed-off-by: Stanislav Fomichev <sdf@google.com> --- .../selftests/bpf/prog_tests/flow_dissector.c | 129 ++++++++++++++++++ tools/testing/selftests/bpf/progs/bpf_flow.c | 28 +++- 2 files changed, 151 insertions(+), 6 deletions(-)