packet01-parsingでは以下を行う。
- 指定したICMPパケットを許可し、それ以外のICMPパケットはドロップする。
- ICMPパケットでないものは許可。
Assignment1
parse_ethhdr()
のbouds checkのバグフィックスをする。
テスト環境構築
sudo ../testenv/testenv.sh setup --name veth-packet01
コード修正
--- a/packet01-parsing/xdp_prog_kern.c +++ b/packet01-parsing/xdp_prog_kern.c @@ -36,7 +36,7 @@ static __always_inline int parse_ethhdr(struct hdr_cursor *nh, /* Byte-count bounds check; check if current pointer + size of header * is after data_end. */ - if (nh->pos + 1 > data_end) + if (nh->pos + hdrsize > data_end) return -1; nh->pos += hdrsize;
Assignment2
parse_ip6hdr()
を実装して、IPv6パケットをパースする。
テスト環境構築
sudo ../testenv/testenv.sh setup --name veth-packet01
コード修正
--- a/packet01-parsing/xdp_prog_kern.c +++ b/packet01-parsing/xdp_prog_kern.c @@ -46,11 +46,18 @@ static __always_inline int parse_ethhdr(struct hdr_cursor *nh, } /* Assignment 2: Implement and use this */ -/*static __always_inline int parse_ip6hdr(struct hdr_cursor *nh, +static __always_inline int parse_ip6hdr(struct hdr_cursor *nh, void *data_end, struct ipv6hdr **ip6hdr) { -}*/ + struct ipv6hdr *ip6h = nh->pos; + + if (ip6h + 1 > data_end) + return -1; + nh->pos = ip6h + 1; + *ip6hdr = ip6h; + return ip6h->nexthdr; +} /* Assignment 3: Implement and use this */ /*static __always_inline int parse_icmp6hdr(struct hdr_cursor *nh, @@ -65,6 +72,7 @@ int xdp_parser_func(struct xdp_md *ctx) void *data_end = (void *)(long)ctx->data_end; void *data = (void *)(long)ctx->data; struct ethhdr *eth; + struct ipv6hdr *ipv6; /* Default action XDP_PASS, imply everything we couldn't parse, or that * we don't want to deal with, we just pass up the stack and let the @@ -86,6 +94,9 @@ int xdp_parser_func(struct xdp_md *ctx) nh_type = parse_ethhdr(&nh, data_end, ð); if (nh_type != bpf_htons(ETH_P_IPV6)) goto out; + nh_type = parse_ip6hdr(&nh, data_end, &ipv6); + if (nh_type != IPPROTO_ICMPV6) + goto out; /* Assignment additions go below here */
Assignment3
parse_icmp6hdr()
を実装して、seqenceが奇数のICMPパケットのみを許可する。
コード修正
--- a/packet01-parsing/xdp_prog_kern.c +++ b/packet01-parsing/xdp_prog_kern.c @@ -60,11 +60,18 @@ static __always_inline int parse_ip6hdr(struct hdr_cursor *nh, } /* Assignment 3: Implement and use this */ -/*static __always_inline int parse_icmp6hdr(struct hdr_cursor *nh, +static __always_inline int parse_icmp6hdr(struct hdr_cursor *nh, void *data_end, struct icmp6hdr **icmp6hdr) { -}*/ + struct icmp6hdr *icmp6h = nh->pos; + + if (icmp6h + 1 > data_end) + return -1; + nh->pos = icmp6h + 1; + *icmp6hdr = icmp6h; + return bpf_ntohs(icmp6h->icmp6_sequence); +} SEC("xdp") int xdp_parser_func(struct xdp_md *ctx) @@ -73,6 +80,8 @@ int xdp_parser_func(struct xdp_md *ctx) void *data = (void *)(long)ctx->data; struct ethhdr *eth; struct ipv6hdr *ipv6; + struct icmp6hdr *icmp6; + int icmp_seq; /* Default action XDP_PASS, imply everything we couldn't parse, or that * we don't want to deal with, we just pass up the stack and let the @@ -97,6 +106,9 @@ int xdp_parser_func(struct xdp_md *ctx) nh_type = parse_ip6hdr(&nh, data_end, &ipv6); if (nh_type != IPPROTO_ICMPV6) goto out; + icmp_seq = parse_icmp6hdr(&nh, data_end, &icmp6); + if (icmp_seq % 2 == 1) + goto out; /* Assignment additions go below here */
TEST
$ make $ sudo ./xdp-loader unload veth-packet01 --all $ sudo ./xdp-loader load --prog-name xdp_parser_func --mode skb veth-packet01 xdp_prog_kern.o
$ ping -6 fc00:dead:cafe:1::2 PING fc00:dead:cafe:1::2(fc00:dead:cafe:1::2) 56 data bytes 64 bytes from fc00:dead:cafe:1::2: icmp_seq=1 ttl=64 time=0.057 ms 64 bytes from fc00:dead:cafe:1::2: icmp_seq=3 ttl=64 time=0.082 ms 64 bytes from fc00:dead:cafe:1::2: icmp_seq=5 ttl=64 time=0.075 ms ^C --- fc00:dead:cafe:1::2 ping statistics --- 5 packets transmitted, 3 received, 40% packet loss, time 4084ms rtt min/avg/max/mdev = 0.057/0.071/0.082/0.010 ms
奇数シーケンスのパケットのみpingが通ってる。
Assignment4
VLANをサポートさせる。 また、通常のタグパケットだけでなく、ダブルタグVLANも考慮する。
テスト環境構築
以下コマンドでvlan環境を構築する。
sudo ../testenv/testenv.sh setup --name veth-packet01 --vlan
下図のような環境が出来上がる。
試しにhost namespaceからveth0.2のアドレスに向けてpingを打ってみる。
$ ping -6 fc00:dead:cafe:2001::2 -I veth-packet01.2 -c 2 PING fc00:dead:cafe:2001::2(fc00:dead:cafe:2001::2) from fc00:dead:cafe:2001::1 veth-packet01.2: 56 data bytes 64 bytes from fc00:dead:cafe:2001::2: icmp_seq=1 ttl=64 time=0.097 ms 64 bytes from fc00:dead:cafe:2001::2: icmp_seq=2 ttl=64 time=0.099 ms --- fc00:dead:cafe:2001::2 ping statistics --- 2 packets transmitted, 2 received, 0% packet loss, time 1008ms rtt min/avg/max/mdev = 0.097/0.098/0.099/0.001 ms
veth-packet01.2で取得したキャプチャ
veth-packet01で取得したキャプチャ
veth-packet01.2(VLANインターフェース)でvlan tagの付与・取り外しがちゃんと行われている。
コード修正
--- a/packet01-parsing/xdp_prog_kern.c +++ b/packet01-parsing/xdp_prog_kern.c @@ -8,6 +8,7 @@ #include <linux/icmpv6.h> #include <bpf/bpf_helpers.h> #include <bpf/bpf_endian.h> +#include <pcap/vlan.h> /* Defines xdp_stats_map from packet04 */ #include "../common/xdp_stats_kern_user.h" #include "../common/xdp_stats_kern.h" @@ -17,6 +18,10 @@ struct hdr_cursor { void *pos; }; +static __always_inline int proto_is_vlan(__u16 h_proto) +{ + return !!(h_proto == bpf_htons(ETH_P_8021Q) || h_proto == bpf_htons(ETH_P_8021AD)); +} /* Packet parsing helpers. * * Each helper parses a packet header, including doing bounds checking, and @@ -31,18 +36,38 @@ static __always_inline int parse_ethhdr(struct hdr_cursor *nh, struct ethhdr **ethhdr) { struct ethhdr *eth = nh->pos; - int hdrsize = sizeof(*eth); + __u16 *proto = NULL; + struct vlan_tag *vtag; /* Byte-count bounds check; check if current pointer + size of header * is after data_end. */ - if (nh->pos + hdrsize > data_end) + if ((nh->pos + sizeof(eth->h_dest)) > data_end) return -1; - - nh->pos += hdrsize; + nh->pos += sizeof(eth->h_dest); + if ((nh->pos + sizeof(eth->h_source)) > data_end) + return -1; + nh->pos += sizeof(eth->h_source); + + int i = 0; + #pragma unroll + for (i = 0; i < 2; i++) { + if ((nh->pos + sizeof(vtag->vlan_tpid)) > data_end) + return -1; + if (!proto_is_vlan(*(__u16 *)nh->pos)) + break; + if ((nh->pos + sizeof(struct vlan_tag)) > data_end) + return -1; + nh->pos += sizeof(struct vlan_tag); + } + + if ((nh->pos + sizeof(eth->h_proto)) > data_end) + return -1; + proto = (__u16 *)nh->pos; + nh->pos += sizeof(eth->h_proto); *ethhdr = eth; - return eth->h_proto; /* network-byte-order */ + return *proto; /* network-byte-order */ } /* Assignment 2: Implement and use this */
TEST
$ make $ sudo ./xdp-loader unload veth-packet01 --all $ sudo ./xdp-loader load --prog-name xdp_parser_func --mode skb veth-packet01 xdp_prog_kern.o
$ ping -6 fc00:dead:cafe:2001::2 PING fc00:dead:cafe:2001::2(fc00:dead:cafe:2001::2) 56 data bytes 64 bytes from fc00:dead:cafe:2001::2: icmp_seq=1 ttl=64 time=0.081 ms 64 bytes from fc00:dead:cafe:2001::2: icmp_seq=3 ttl=64 time=0.092 ms 64 bytes from fc00:dead:cafe:2001::2: icmp_seq=5 ttl=64 time=0.088 ms 64 bytes from fc00:dead:cafe:2001::2: icmp_seq=7 ttl=64 time=0.091 ms 64 bytes from fc00:dead:cafe:2001::2: icmp_seq=9 ttl=64 time=0.087 ms ^C --- fc00:dead:cafe:2001::2 ping statistics --- 9 packets transmitted, 5 received, 44.4444% packet loss, time 8180ms rtt min/avg/max/mdev = 0.081/0.087/0.092/0.003 ms
Assignment5
IPv4をサポートさせる。
テスト環境構築
以下コマンドでipv4環境を構築する。
sudo ../testenv/testenv.sh setup --name veth-packet01 --vlan --legacy-ip
確認した所、vlan interfaceにIPアドレスがアサインされてないようなので、アサインする。
$ sudo ip addr add 192.168.101.1/24 dev veth-packet01.1 $ sudo ip addr add 192.168.102.1/24 dev veth-packet01.2 $ sudo ip netns exec veth-packet01 ip addr add 192.168.101.2/24 dev veth0.1 $ sudo ip netns exec veth-packet01 ip addr add 192.168.102.2/24 dev veth0.2
試しにhost namespaceからveth0.2のアドレスに向けてpingを打ってみる。
$ ping 192.168.102.2 -c 2 PING 192.168.102.2 (192.168.102.2) 56(84) bytes of data. 64 bytes from 192.168.102.2: icmp_seq=1 ttl=64 time=0.240 ms 64 bytes from 192.168.102.2: icmp_seq=2 ttl=64 time=0.091 ms --- 192.168.102.2 ping statistics --- 2 packets transmitted, 2 received, 0% packet loss, time 1001ms rtt min/avg/max/mdev = 0.091/0.165/0.240/0.074 ms
veth-packet01.2で取得したキャプチャ
veth-packet01で取得したキャプチャ
コード修正
--- a/packet01-parsing/xdp_prog_kern.c +++ b/packet01-parsing/xdp_prog_kern.c @@ -4,7 +4,9 @@ #include <linux/in.h> #include <linux/if_ether.h> #include <linux/if_packet.h> +#include <linux/ip.h> #include <linux/ipv6.h> +#include <linux/icmp.h> #include <linux/icmpv6.h> #include <bpf/bpf_helpers.h> #include <bpf/bpf_endian.h> @@ -70,6 +72,22 @@ static __always_inline int parse_ethhdr(struct hdr_cursor *nh, return *proto; /* network-byte-order */ } +static __always_inline int parse_iphdr(struct hdr_cursor *nh, + void *data_end, + struct iphdr **iphdr) +{ + struct iphdr *iph = nh->pos; + + if (iph + 1 > data_end) + return -1; + int hdrsize = iph->ihl * 4; + if (nh->pos + hdrsize > data_end) + return -1; + nh->pos += hdrsize; + *iphdr = iph; + return iph->protocol; +} + /* Assignment 2: Implement and use this */ static __always_inline int parse_ip6hdr(struct hdr_cursor *nh, void *data_end, @@ -84,6 +102,19 @@ static __always_inline int parse_ip6hdr(struct hdr_cursor *nh, return ip6h->nexthdr; } +static __always_inline int parse_icmphdr(struct hdr_cursor *nh, + void *data_end, + struct icmphdr **icmphdr) +{ + struct icmphdr *icmph = nh->pos; + + if (icmph + 1 > data_end) + return -1; + nh->pos = icmph + 1; + *icmphdr = icmph; + return bpf_ntohs(icmph->un.echo.sequence); +} + /* Assignment 3: Implement and use this */ static __always_inline int parse_icmp6hdr(struct hdr_cursor *nh, void *data_end, @@ -104,7 +135,9 @@ int xdp_parser_func(struct xdp_md *ctx) void *data_end = (void *)(long)ctx->data_end; void *data = (void *)(long)ctx->data; struct ethhdr *eth; + struct iphdr *ip; struct ipv6hdr *ipv6; + struct icmphdr *icmp; struct icmp6hdr *icmp6; int icmp_seq; @@ -126,12 +159,19 @@ int xdp_parser_func(struct xdp_md *ctx) * header type in the packet correct?), and bounds checking. */ nh_type = parse_ethhdr(&nh, data_end, ð); - if (nh_type != bpf_htons(ETH_P_IPV6)) - goto out; - nh_type = parse_ip6hdr(&nh, data_end, &ipv6); - if (nh_type != IPPROTO_ICMPV6) + if (nh_type == bpf_htons(ETH_P_IP)) { + nh_type = parse_iphdr(&nh, data_end, &ip); + if (nh_type != IPPROTO_ICMP) + goto out; + icmp_seq = parse_icmphdr(&nh, data_end, &icmp); + } else if (nh_type == bpf_htons(ETH_P_IPV6)) { + nh_type = parse_ip6hdr(&nh, data_end, &ipv6); + if (nh_type != IPPROTO_ICMPV6) + goto out; + icmp_seq = parse_icmp6hdr(&nh, data_end, &icmp6); + } else { goto out; - icmp_seq = parse_icmp6hdr(&nh, data_end, &icmp6); + } if (icmp_seq % 2 == 1) goto out;
TEST
$ make $ sudo ./xdp-loader unload veth-packet01 --all $ sudo ./xdp-loader load --prog-name xdp_parser_func --mode skb veth-packet01 xdp_prog_kern.o
$ ping 192.168.102.2 PING 192.168.102.2 (192.168.102.2) 56(84) bytes of data. 64 bytes from 192.168.102.2: icmp_seq=1 ttl=64 time=0.204 ms 64 bytes from 192.168.102.2: icmp_seq=3 ttl=64 time=0.104 ms 64 bytes from 192.168.102.2: icmp_seq=5 ttl=64 time=0.091 ms 64 bytes from 192.168.102.2: icmp_seq=7 ttl=64 time=0.087 ms 64 bytes from 192.168.102.2: icmp_seq=9 ttl=64 time=0.083 ms 64 bytes from 192.168.102.2: icmp_seq=11 ttl=64 time=0.086 ms --- 192.168.102.2 ping statistics --- 11 packets transmitted, 6 received, 45.4545% packet loss, time 10223ms rtt min/avg/max/mdev = 0.083/0.109/0.204/0.042 ms
$ ping -6 fc00:dead:cafe:2001::2 PING fc00:dead:cafe:2001::2(fc00:dead:cafe:2001::2) 56 data bytes 64 bytes from fc00:dead:cafe:2001::2: icmp_seq=1 ttl=64 time=0.074 ms 64 bytes from fc00:dead:cafe:2001::2: icmp_seq=3 ttl=64 time=0.095 ms 64 bytes from fc00:dead:cafe:2001::2: icmp_seq=5 ttl=64 time=0.088 ms 64 bytes from fc00:dead:cafe:2001::2: icmp_seq=7 ttl=64 time=0.088 ms 64 bytes from fc00:dead:cafe:2001::2: icmp_seq=9 ttl=64 time=0.090 ms --- fc00:dead:cafe:2001::2 ping statistics --- 10 packets transmitted, 5 received, 50% packet loss, time 9201ms rtt min/avg/max/mdev = 0.074/0.087/0.095/0.007 ms
補足
testenv.sh
コピペ用コマンドメモ
sudo ../testenv/testenv.sh setup --name veth-packet01 sudo ../testenv/testenv.sh setup --name veth-packet01 --vlan sudo ../testenv/testenv.sh setup --name veth-packet01 --vlan --legacy-ip sudo ../testenv/testenv.sh enter --name veth-packet01 sudo ../testenv/testenv.sh teardown
xdp-loader
コピペ用コマンドメモ
sudo ./xdp-loader load --prog-name xdp_parser_func --mode skb veth-packet01 xdp_prog_kern.o sudo ./xdp-loader unload veth-packet01 --all sudo ./xdp-loader status