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