xref: /openbmc/linux/samples/bpf/parse_varlen.c (revision 8dd06ef34b6e2f41b29fbf5fc1663780f2524285)
165d472fbSAlexei Starovoitov /* Copyright (c) 2016 Facebook
265d472fbSAlexei Starovoitov  *
365d472fbSAlexei Starovoitov  * This program is free software; you can redistribute it and/or
465d472fbSAlexei Starovoitov  * modify it under the terms of version 2 of the GNU General Public
565d472fbSAlexei Starovoitov  * License as published by the Free Software Foundation.
665d472fbSAlexei Starovoitov  */
796a8eb1eSDaniel Borkmann #define KBUILD_MODNAME "foo"
865d472fbSAlexei Starovoitov #include <linux/if_ether.h>
94d5d33a0STaeung Song #include <linux/if_vlan.h>
1065d472fbSAlexei Starovoitov #include <linux/ip.h>
1165d472fbSAlexei Starovoitov #include <linux/ipv6.h>
1265d472fbSAlexei Starovoitov #include <linux/in.h>
1365d472fbSAlexei Starovoitov #include <linux/tcp.h>
1465d472fbSAlexei Starovoitov #include <linux/udp.h>
1565d472fbSAlexei Starovoitov #include <uapi/linux/bpf.h>
1665d472fbSAlexei Starovoitov #include <net/ip.h>
17*7cf245a3SToke Høiland-Jørgensen #include <bpf/bpf_helpers.h>
1865d472fbSAlexei Starovoitov 
1965d472fbSAlexei Starovoitov #define DEFAULT_PKTGEN_UDP_PORT 9
2065d472fbSAlexei Starovoitov #define DEBUG 0
2165d472fbSAlexei Starovoitov 
tcp(void * data,uint64_t tp_off,void * data_end)2265d472fbSAlexei Starovoitov static int tcp(void *data, uint64_t tp_off, void *data_end)
2365d472fbSAlexei Starovoitov {
2465d472fbSAlexei Starovoitov 	struct tcphdr *tcp = data + tp_off;
2565d472fbSAlexei Starovoitov 
2665d472fbSAlexei Starovoitov 	if (tcp + 1 > data_end)
2765d472fbSAlexei Starovoitov 		return 0;
2865d472fbSAlexei Starovoitov 	if (tcp->dest == htons(80) || tcp->source == htons(80))
2965d472fbSAlexei Starovoitov 		return TC_ACT_SHOT;
3065d472fbSAlexei Starovoitov 	return 0;
3165d472fbSAlexei Starovoitov }
3265d472fbSAlexei Starovoitov 
udp(void * data,uint64_t tp_off,void * data_end)3365d472fbSAlexei Starovoitov static int udp(void *data, uint64_t tp_off, void *data_end)
3465d472fbSAlexei Starovoitov {
3565d472fbSAlexei Starovoitov 	struct udphdr *udp = data + tp_off;
3665d472fbSAlexei Starovoitov 
3765d472fbSAlexei Starovoitov 	if (udp + 1 > data_end)
3865d472fbSAlexei Starovoitov 		return 0;
3965d472fbSAlexei Starovoitov 	if (udp->dest == htons(DEFAULT_PKTGEN_UDP_PORT) ||
4065d472fbSAlexei Starovoitov 	    udp->source == htons(DEFAULT_PKTGEN_UDP_PORT)) {
4165d472fbSAlexei Starovoitov 		if (DEBUG) {
4265d472fbSAlexei Starovoitov 			char fmt[] = "udp port 9 indeed\n";
4365d472fbSAlexei Starovoitov 
4465d472fbSAlexei Starovoitov 			bpf_trace_printk(fmt, sizeof(fmt));
4565d472fbSAlexei Starovoitov 		}
4665d472fbSAlexei Starovoitov 		return TC_ACT_SHOT;
4765d472fbSAlexei Starovoitov 	}
4865d472fbSAlexei Starovoitov 	return 0;
4965d472fbSAlexei Starovoitov }
5065d472fbSAlexei Starovoitov 
parse_ipv4(void * data,uint64_t nh_off,void * data_end)5165d472fbSAlexei Starovoitov static int parse_ipv4(void *data, uint64_t nh_off, void *data_end)
5265d472fbSAlexei Starovoitov {
5365d472fbSAlexei Starovoitov 	struct iphdr *iph;
5465d472fbSAlexei Starovoitov 	uint64_t ihl_len;
5565d472fbSAlexei Starovoitov 
5665d472fbSAlexei Starovoitov 	iph = data + nh_off;
5765d472fbSAlexei Starovoitov 	if (iph + 1 > data_end)
5865d472fbSAlexei Starovoitov 		return 0;
5965d472fbSAlexei Starovoitov 
6065d472fbSAlexei Starovoitov 	if (ip_is_fragment(iph))
6165d472fbSAlexei Starovoitov 		return 0;
6265d472fbSAlexei Starovoitov 	ihl_len = iph->ihl * 4;
6365d472fbSAlexei Starovoitov 
6465d472fbSAlexei Starovoitov 	if (iph->protocol == IPPROTO_IPIP) {
6565d472fbSAlexei Starovoitov 		iph = data + nh_off + ihl_len;
6665d472fbSAlexei Starovoitov 		if (iph + 1 > data_end)
6765d472fbSAlexei Starovoitov 			return 0;
6865d472fbSAlexei Starovoitov 		ihl_len += iph->ihl * 4;
6965d472fbSAlexei Starovoitov 	}
7065d472fbSAlexei Starovoitov 
7165d472fbSAlexei Starovoitov 	if (iph->protocol == IPPROTO_TCP)
7265d472fbSAlexei Starovoitov 		return tcp(data, nh_off + ihl_len, data_end);
7365d472fbSAlexei Starovoitov 	else if (iph->protocol == IPPROTO_UDP)
7465d472fbSAlexei Starovoitov 		return udp(data, nh_off + ihl_len, data_end);
7565d472fbSAlexei Starovoitov 	return 0;
7665d472fbSAlexei Starovoitov }
7765d472fbSAlexei Starovoitov 
parse_ipv6(void * data,uint64_t nh_off,void * data_end)7865d472fbSAlexei Starovoitov static int parse_ipv6(void *data, uint64_t nh_off, void *data_end)
7965d472fbSAlexei Starovoitov {
8065d472fbSAlexei Starovoitov 	struct ipv6hdr *ip6h;
8165d472fbSAlexei Starovoitov 	struct iphdr *iph;
8265d472fbSAlexei Starovoitov 	uint64_t ihl_len = sizeof(struct ipv6hdr);
8365d472fbSAlexei Starovoitov 	uint64_t nexthdr;
8465d472fbSAlexei Starovoitov 
8565d472fbSAlexei Starovoitov 	ip6h = data + nh_off;
8665d472fbSAlexei Starovoitov 	if (ip6h + 1 > data_end)
8765d472fbSAlexei Starovoitov 		return 0;
8865d472fbSAlexei Starovoitov 
8965d472fbSAlexei Starovoitov 	nexthdr = ip6h->nexthdr;
9065d472fbSAlexei Starovoitov 
9165d472fbSAlexei Starovoitov 	if (nexthdr == IPPROTO_IPIP) {
9265d472fbSAlexei Starovoitov 		iph = data + nh_off + ihl_len;
9365d472fbSAlexei Starovoitov 		if (iph + 1 > data_end)
9465d472fbSAlexei Starovoitov 			return 0;
9565d472fbSAlexei Starovoitov 		ihl_len += iph->ihl * 4;
9665d472fbSAlexei Starovoitov 		nexthdr = iph->protocol;
9765d472fbSAlexei Starovoitov 	} else if (nexthdr == IPPROTO_IPV6) {
9865d472fbSAlexei Starovoitov 		ip6h = data + nh_off + ihl_len;
9965d472fbSAlexei Starovoitov 		if (ip6h + 1 > data_end)
10065d472fbSAlexei Starovoitov 			return 0;
10165d472fbSAlexei Starovoitov 		ihl_len += sizeof(struct ipv6hdr);
10265d472fbSAlexei Starovoitov 		nexthdr = ip6h->nexthdr;
10365d472fbSAlexei Starovoitov 	}
10465d472fbSAlexei Starovoitov 
10565d472fbSAlexei Starovoitov 	if (nexthdr == IPPROTO_TCP)
10665d472fbSAlexei Starovoitov 		return tcp(data, nh_off + ihl_len, data_end);
10765d472fbSAlexei Starovoitov 	else if (nexthdr == IPPROTO_UDP)
10865d472fbSAlexei Starovoitov 		return udp(data, nh_off + ihl_len, data_end);
10965d472fbSAlexei Starovoitov 	return 0;
11065d472fbSAlexei Starovoitov }
11165d472fbSAlexei Starovoitov 
11265d472fbSAlexei Starovoitov SEC("varlen")
handle_ingress(struct __sk_buff * skb)11365d472fbSAlexei Starovoitov int handle_ingress(struct __sk_buff *skb)
11465d472fbSAlexei Starovoitov {
11565d472fbSAlexei Starovoitov 	void *data = (void *)(long)skb->data;
11665d472fbSAlexei Starovoitov 	struct ethhdr *eth = data;
11765d472fbSAlexei Starovoitov 	void *data_end = (void *)(long)skb->data_end;
11865d472fbSAlexei Starovoitov 	uint64_t h_proto, nh_off;
11965d472fbSAlexei Starovoitov 
12065d472fbSAlexei Starovoitov 	nh_off = sizeof(*eth);
12165d472fbSAlexei Starovoitov 	if (data + nh_off > data_end)
12265d472fbSAlexei Starovoitov 		return 0;
12365d472fbSAlexei Starovoitov 
12465d472fbSAlexei Starovoitov 	h_proto = eth->h_proto;
12565d472fbSAlexei Starovoitov 
12665d472fbSAlexei Starovoitov 	if (h_proto == ETH_P_8021Q || h_proto == ETH_P_8021AD) {
12765d472fbSAlexei Starovoitov 		struct vlan_hdr *vhdr;
12865d472fbSAlexei Starovoitov 
12965d472fbSAlexei Starovoitov 		vhdr = data + nh_off;
13065d472fbSAlexei Starovoitov 		nh_off += sizeof(struct vlan_hdr);
13165d472fbSAlexei Starovoitov 		if (data + nh_off > data_end)
13265d472fbSAlexei Starovoitov 			return 0;
13365d472fbSAlexei Starovoitov 		h_proto = vhdr->h_vlan_encapsulated_proto;
13465d472fbSAlexei Starovoitov 	}
13565d472fbSAlexei Starovoitov 	if (h_proto == ETH_P_8021Q || h_proto == ETH_P_8021AD) {
13665d472fbSAlexei Starovoitov 		struct vlan_hdr *vhdr;
13765d472fbSAlexei Starovoitov 
13865d472fbSAlexei Starovoitov 		vhdr = data + nh_off;
13965d472fbSAlexei Starovoitov 		nh_off += sizeof(struct vlan_hdr);
14065d472fbSAlexei Starovoitov 		if (data + nh_off > data_end)
14165d472fbSAlexei Starovoitov 			return 0;
14265d472fbSAlexei Starovoitov 		h_proto = vhdr->h_vlan_encapsulated_proto;
14365d472fbSAlexei Starovoitov 	}
14465d472fbSAlexei Starovoitov 	if (h_proto == htons(ETH_P_IP))
14565d472fbSAlexei Starovoitov 		return parse_ipv4(data, nh_off, data_end);
14665d472fbSAlexei Starovoitov 	else if (h_proto == htons(ETH_P_IPV6))
14765d472fbSAlexei Starovoitov 		return parse_ipv6(data, nh_off, data_end);
14865d472fbSAlexei Starovoitov 	return 0;
14965d472fbSAlexei Starovoitov }
15065d472fbSAlexei Starovoitov char _license[] SEC("license") = "GPL";
151