xref: /openbmc/linux/samples/bpf/parse_varlen.c (revision 239480ab)
1 /* Copyright (c) 2016 Facebook
2  *
3  * This program is free software; you can redistribute it and/or
4  * modify it under the terms of version 2 of the GNU General Public
5  * License as published by the Free Software Foundation.
6  */
7 #define KBUILD_MODNAME "foo"
8 #include <linux/if_ether.h>
9 #include <linux/ip.h>
10 #include <linux/ipv6.h>
11 #include <linux/in.h>
12 #include <linux/tcp.h>
13 #include <linux/udp.h>
14 #include <uapi/linux/bpf.h>
15 #include <net/ip.h>
16 #include "bpf_helpers.h"
17 
18 #define DEFAULT_PKTGEN_UDP_PORT 9
19 #define DEBUG 0
20 
21 static int tcp(void *data, uint64_t tp_off, void *data_end)
22 {
23 	struct tcphdr *tcp = data + tp_off;
24 
25 	if (tcp + 1 > data_end)
26 		return 0;
27 	if (tcp->dest == htons(80) || tcp->source == htons(80))
28 		return TC_ACT_SHOT;
29 	return 0;
30 }
31 
32 static int udp(void *data, uint64_t tp_off, void *data_end)
33 {
34 	struct udphdr *udp = data + tp_off;
35 
36 	if (udp + 1 > data_end)
37 		return 0;
38 	if (udp->dest == htons(DEFAULT_PKTGEN_UDP_PORT) ||
39 	    udp->source == htons(DEFAULT_PKTGEN_UDP_PORT)) {
40 		if (DEBUG) {
41 			char fmt[] = "udp port 9 indeed\n";
42 
43 			bpf_trace_printk(fmt, sizeof(fmt));
44 		}
45 		return TC_ACT_SHOT;
46 	}
47 	return 0;
48 }
49 
50 static int parse_ipv4(void *data, uint64_t nh_off, void *data_end)
51 {
52 	struct iphdr *iph;
53 	uint64_t ihl_len;
54 
55 	iph = data + nh_off;
56 	if (iph + 1 > data_end)
57 		return 0;
58 
59 	if (ip_is_fragment(iph))
60 		return 0;
61 	ihl_len = iph->ihl * 4;
62 
63 	if (iph->protocol == IPPROTO_IPIP) {
64 		iph = data + nh_off + ihl_len;
65 		if (iph + 1 > data_end)
66 			return 0;
67 		ihl_len += iph->ihl * 4;
68 	}
69 
70 	if (iph->protocol == IPPROTO_TCP)
71 		return tcp(data, nh_off + ihl_len, data_end);
72 	else if (iph->protocol == IPPROTO_UDP)
73 		return udp(data, nh_off + ihl_len, data_end);
74 	return 0;
75 }
76 
77 static int parse_ipv6(void *data, uint64_t nh_off, void *data_end)
78 {
79 	struct ipv6hdr *ip6h;
80 	struct iphdr *iph;
81 	uint64_t ihl_len = sizeof(struct ipv6hdr);
82 	uint64_t nexthdr;
83 
84 	ip6h = data + nh_off;
85 	if (ip6h + 1 > data_end)
86 		return 0;
87 
88 	nexthdr = ip6h->nexthdr;
89 
90 	if (nexthdr == IPPROTO_IPIP) {
91 		iph = data + nh_off + ihl_len;
92 		if (iph + 1 > data_end)
93 			return 0;
94 		ihl_len += iph->ihl * 4;
95 		nexthdr = iph->protocol;
96 	} else if (nexthdr == IPPROTO_IPV6) {
97 		ip6h = data + nh_off + ihl_len;
98 		if (ip6h + 1 > data_end)
99 			return 0;
100 		ihl_len += sizeof(struct ipv6hdr);
101 		nexthdr = ip6h->nexthdr;
102 	}
103 
104 	if (nexthdr == IPPROTO_TCP)
105 		return tcp(data, nh_off + ihl_len, data_end);
106 	else if (nexthdr == IPPROTO_UDP)
107 		return udp(data, nh_off + ihl_len, data_end);
108 	return 0;
109 }
110 
111 struct vlan_hdr {
112 	uint16_t h_vlan_TCI;
113 	uint16_t h_vlan_encapsulated_proto;
114 };
115 
116 SEC("varlen")
117 int handle_ingress(struct __sk_buff *skb)
118 {
119 	void *data = (void *)(long)skb->data;
120 	struct ethhdr *eth = data;
121 	void *data_end = (void *)(long)skb->data_end;
122 	uint64_t h_proto, nh_off;
123 
124 	nh_off = sizeof(*eth);
125 	if (data + nh_off > data_end)
126 		return 0;
127 
128 	h_proto = eth->h_proto;
129 
130 	if (h_proto == ETH_P_8021Q || h_proto == ETH_P_8021AD) {
131 		struct vlan_hdr *vhdr;
132 
133 		vhdr = data + nh_off;
134 		nh_off += sizeof(struct vlan_hdr);
135 		if (data + nh_off > data_end)
136 			return 0;
137 		h_proto = vhdr->h_vlan_encapsulated_proto;
138 	}
139 	if (h_proto == ETH_P_8021Q || h_proto == ETH_P_8021AD) {
140 		struct vlan_hdr *vhdr;
141 
142 		vhdr = data + nh_off;
143 		nh_off += sizeof(struct vlan_hdr);
144 		if (data + nh_off > data_end)
145 			return 0;
146 		h_proto = vhdr->h_vlan_encapsulated_proto;
147 	}
148 	if (h_proto == htons(ETH_P_IP))
149 		return parse_ipv4(data, nh_off, data_end);
150 	else if (h_proto == htons(ETH_P_IPV6))
151 		return parse_ipv6(data, nh_off, data_end);
152 	return 0;
153 }
154 char _license[] SEC("license") = "GPL";
155