xref: /openbmc/linux/samples/bpf/sockex2_kern.c (revision 4b4193256c8d3bc3a5397b5cd9494c2ad386317d)
1fbe33108SAlexei Starovoitov #include <uapi/linux/bpf.h>
2fbe33108SAlexei Starovoitov #include <uapi/linux/in.h>
3fbe33108SAlexei Starovoitov #include <uapi/linux/if.h>
4fbe33108SAlexei Starovoitov #include <uapi/linux/if_ether.h>
5fbe33108SAlexei Starovoitov #include <uapi/linux/ip.h>
6fbe33108SAlexei Starovoitov #include <uapi/linux/ipv6.h>
7fbe33108SAlexei Starovoitov #include <uapi/linux/if_tunnel.h>
8*03421a92SYonghong Song #include <bpf/bpf_helpers.h>
9*03421a92SYonghong Song #include "bpf_legacy.h"
10fbe33108SAlexei Starovoitov #define IP_MF		0x2000
11fbe33108SAlexei Starovoitov #define IP_OFFSET	0x1FFF
12fbe33108SAlexei Starovoitov 
13fbe33108SAlexei Starovoitov struct vlan_hdr {
14fbe33108SAlexei Starovoitov 	__be16 h_vlan_TCI;
15fbe33108SAlexei Starovoitov 	__be16 h_vlan_encapsulated_proto;
16fbe33108SAlexei Starovoitov };
17fbe33108SAlexei Starovoitov 
1832c00979SPrashant Bhole struct flow_key_record {
19fbe33108SAlexei Starovoitov 	__be32 src;
20fbe33108SAlexei Starovoitov 	__be32 dst;
21fbe33108SAlexei Starovoitov 	union {
22fbe33108SAlexei Starovoitov 		__be32 ports;
23fbe33108SAlexei Starovoitov 		__be16 port16[2];
24fbe33108SAlexei Starovoitov 	};
25fbe33108SAlexei Starovoitov 	__u16 thoff;
26fbe33108SAlexei Starovoitov 	__u8 ip_proto;
27fbe33108SAlexei Starovoitov };
28fbe33108SAlexei Starovoitov 
proto_ports_offset(__u64 proto)29fbe33108SAlexei Starovoitov static inline int proto_ports_offset(__u64 proto)
30fbe33108SAlexei Starovoitov {
31fbe33108SAlexei Starovoitov 	switch (proto) {
32fbe33108SAlexei Starovoitov 	case IPPROTO_TCP:
33fbe33108SAlexei Starovoitov 	case IPPROTO_UDP:
34fbe33108SAlexei Starovoitov 	case IPPROTO_DCCP:
35fbe33108SAlexei Starovoitov 	case IPPROTO_ESP:
36fbe33108SAlexei Starovoitov 	case IPPROTO_SCTP:
37fbe33108SAlexei Starovoitov 	case IPPROTO_UDPLITE:
38fbe33108SAlexei Starovoitov 		return 0;
39fbe33108SAlexei Starovoitov 	case IPPROTO_AH:
40fbe33108SAlexei Starovoitov 		return 4;
41fbe33108SAlexei Starovoitov 	default:
42fbe33108SAlexei Starovoitov 		return 0;
43fbe33108SAlexei Starovoitov 	}
44fbe33108SAlexei Starovoitov }
45fbe33108SAlexei Starovoitov 
ip_is_fragment(struct __sk_buff * ctx,__u64 nhoff)46614cd3bdSAlexei Starovoitov static inline int ip_is_fragment(struct __sk_buff *ctx, __u64 nhoff)
47fbe33108SAlexei Starovoitov {
48fbe33108SAlexei Starovoitov 	return load_half(ctx, nhoff + offsetof(struct iphdr, frag_off))
49fbe33108SAlexei Starovoitov 		& (IP_MF | IP_OFFSET);
50fbe33108SAlexei Starovoitov }
51fbe33108SAlexei Starovoitov 
ipv6_addr_hash(struct __sk_buff * ctx,__u64 off)52614cd3bdSAlexei Starovoitov static inline __u32 ipv6_addr_hash(struct __sk_buff *ctx, __u64 off)
53fbe33108SAlexei Starovoitov {
54fbe33108SAlexei Starovoitov 	__u64 w0 = load_word(ctx, off);
55fbe33108SAlexei Starovoitov 	__u64 w1 = load_word(ctx, off + 4);
56fbe33108SAlexei Starovoitov 	__u64 w2 = load_word(ctx, off + 8);
57fbe33108SAlexei Starovoitov 	__u64 w3 = load_word(ctx, off + 12);
58fbe33108SAlexei Starovoitov 
59fbe33108SAlexei Starovoitov 	return (__u32)(w0 ^ w1 ^ w2 ^ w3);
60fbe33108SAlexei Starovoitov }
61fbe33108SAlexei Starovoitov 
parse_ip(struct __sk_buff * skb,__u64 nhoff,__u64 * ip_proto,struct flow_key_record * flow)62614cd3bdSAlexei Starovoitov static inline __u64 parse_ip(struct __sk_buff *skb, __u64 nhoff, __u64 *ip_proto,
6332c00979SPrashant Bhole 			     struct flow_key_record *flow)
64fbe33108SAlexei Starovoitov {
65fbe33108SAlexei Starovoitov 	__u64 verlen;
66fbe33108SAlexei Starovoitov 
67fbe33108SAlexei Starovoitov 	if (unlikely(ip_is_fragment(skb, nhoff)))
68fbe33108SAlexei Starovoitov 		*ip_proto = 0;
69fbe33108SAlexei Starovoitov 	else
70fbe33108SAlexei Starovoitov 		*ip_proto = load_byte(skb, nhoff + offsetof(struct iphdr, protocol));
71fbe33108SAlexei Starovoitov 
72fbe33108SAlexei Starovoitov 	if (*ip_proto != IPPROTO_GRE) {
73fbe33108SAlexei Starovoitov 		flow->src = load_word(skb, nhoff + offsetof(struct iphdr, saddr));
74fbe33108SAlexei Starovoitov 		flow->dst = load_word(skb, nhoff + offsetof(struct iphdr, daddr));
75fbe33108SAlexei Starovoitov 	}
76fbe33108SAlexei Starovoitov 
77fbe33108SAlexei Starovoitov 	verlen = load_byte(skb, nhoff + 0/*offsetof(struct iphdr, ihl)*/);
78fbe33108SAlexei Starovoitov 	if (likely(verlen == 0x45))
79fbe33108SAlexei Starovoitov 		nhoff += 20;
80fbe33108SAlexei Starovoitov 	else
81fbe33108SAlexei Starovoitov 		nhoff += (verlen & 0xF) << 2;
82fbe33108SAlexei Starovoitov 
83fbe33108SAlexei Starovoitov 	return nhoff;
84fbe33108SAlexei Starovoitov }
85fbe33108SAlexei Starovoitov 
parse_ipv6(struct __sk_buff * skb,__u64 nhoff,__u64 * ip_proto,struct flow_key_record * flow)86614cd3bdSAlexei Starovoitov static inline __u64 parse_ipv6(struct __sk_buff *skb, __u64 nhoff, __u64 *ip_proto,
8732c00979SPrashant Bhole 			       struct flow_key_record *flow)
88fbe33108SAlexei Starovoitov {
89fbe33108SAlexei Starovoitov 	*ip_proto = load_byte(skb,
90fbe33108SAlexei Starovoitov 			      nhoff + offsetof(struct ipv6hdr, nexthdr));
91fbe33108SAlexei Starovoitov 	flow->src = ipv6_addr_hash(skb,
92fbe33108SAlexei Starovoitov 				   nhoff + offsetof(struct ipv6hdr, saddr));
93fbe33108SAlexei Starovoitov 	flow->dst = ipv6_addr_hash(skb,
94fbe33108SAlexei Starovoitov 				   nhoff + offsetof(struct ipv6hdr, daddr));
95fbe33108SAlexei Starovoitov 	nhoff += sizeof(struct ipv6hdr);
96fbe33108SAlexei Starovoitov 
97fbe33108SAlexei Starovoitov 	return nhoff;
98fbe33108SAlexei Starovoitov }
99fbe33108SAlexei Starovoitov 
flow_dissector(struct __sk_buff * skb,struct flow_key_record * flow)10032c00979SPrashant Bhole static inline bool flow_dissector(struct __sk_buff *skb,
10132c00979SPrashant Bhole 				  struct flow_key_record *flow)
102fbe33108SAlexei Starovoitov {
103fbe33108SAlexei Starovoitov 	__u64 nhoff = ETH_HLEN;
104fbe33108SAlexei Starovoitov 	__u64 ip_proto;
105fbe33108SAlexei Starovoitov 	__u64 proto = load_half(skb, 12);
106fbe33108SAlexei Starovoitov 	int poff;
107fbe33108SAlexei Starovoitov 
108fbe33108SAlexei Starovoitov 	if (proto == ETH_P_8021AD) {
109fbe33108SAlexei Starovoitov 		proto = load_half(skb, nhoff + offsetof(struct vlan_hdr,
110fbe33108SAlexei Starovoitov 							h_vlan_encapsulated_proto));
111fbe33108SAlexei Starovoitov 		nhoff += sizeof(struct vlan_hdr);
112fbe33108SAlexei Starovoitov 	}
113fbe33108SAlexei Starovoitov 
114fbe33108SAlexei Starovoitov 	if (proto == ETH_P_8021Q) {
115fbe33108SAlexei Starovoitov 		proto = load_half(skb, nhoff + offsetof(struct vlan_hdr,
116fbe33108SAlexei Starovoitov 							h_vlan_encapsulated_proto));
117fbe33108SAlexei Starovoitov 		nhoff += sizeof(struct vlan_hdr);
118fbe33108SAlexei Starovoitov 	}
119fbe33108SAlexei Starovoitov 
120fbe33108SAlexei Starovoitov 	if (likely(proto == ETH_P_IP))
121fbe33108SAlexei Starovoitov 		nhoff = parse_ip(skb, nhoff, &ip_proto, flow);
122fbe33108SAlexei Starovoitov 	else if (proto == ETH_P_IPV6)
123fbe33108SAlexei Starovoitov 		nhoff = parse_ipv6(skb, nhoff, &ip_proto, flow);
124fbe33108SAlexei Starovoitov 	else
125fbe33108SAlexei Starovoitov 		return false;
126fbe33108SAlexei Starovoitov 
127fbe33108SAlexei Starovoitov 	switch (ip_proto) {
128fbe33108SAlexei Starovoitov 	case IPPROTO_GRE: {
129fbe33108SAlexei Starovoitov 		struct gre_hdr {
130fbe33108SAlexei Starovoitov 			__be16 flags;
131fbe33108SAlexei Starovoitov 			__be16 proto;
132fbe33108SAlexei Starovoitov 		};
133fbe33108SAlexei Starovoitov 
134fbe33108SAlexei Starovoitov 		__u64 gre_flags = load_half(skb,
135fbe33108SAlexei Starovoitov 					    nhoff + offsetof(struct gre_hdr, flags));
136fbe33108SAlexei Starovoitov 		__u64 gre_proto = load_half(skb,
137fbe33108SAlexei Starovoitov 					    nhoff + offsetof(struct gre_hdr, proto));
138fbe33108SAlexei Starovoitov 
139fbe33108SAlexei Starovoitov 		if (gre_flags & (GRE_VERSION|GRE_ROUTING))
140fbe33108SAlexei Starovoitov 			break;
141fbe33108SAlexei Starovoitov 
142fbe33108SAlexei Starovoitov 		proto = gre_proto;
143fbe33108SAlexei Starovoitov 		nhoff += 4;
144fbe33108SAlexei Starovoitov 		if (gre_flags & GRE_CSUM)
145fbe33108SAlexei Starovoitov 			nhoff += 4;
146fbe33108SAlexei Starovoitov 		if (gre_flags & GRE_KEY)
147fbe33108SAlexei Starovoitov 			nhoff += 4;
148fbe33108SAlexei Starovoitov 		if (gre_flags & GRE_SEQ)
149fbe33108SAlexei Starovoitov 			nhoff += 4;
150fbe33108SAlexei Starovoitov 
151fbe33108SAlexei Starovoitov 		if (proto == ETH_P_8021Q) {
152fbe33108SAlexei Starovoitov 			proto = load_half(skb,
153fbe33108SAlexei Starovoitov 					  nhoff + offsetof(struct vlan_hdr,
154fbe33108SAlexei Starovoitov 							   h_vlan_encapsulated_proto));
155fbe33108SAlexei Starovoitov 			nhoff += sizeof(struct vlan_hdr);
156fbe33108SAlexei Starovoitov 		}
157fbe33108SAlexei Starovoitov 
158fbe33108SAlexei Starovoitov 		if (proto == ETH_P_IP)
159fbe33108SAlexei Starovoitov 			nhoff = parse_ip(skb, nhoff, &ip_proto, flow);
160fbe33108SAlexei Starovoitov 		else if (proto == ETH_P_IPV6)
161fbe33108SAlexei Starovoitov 			nhoff = parse_ipv6(skb, nhoff, &ip_proto, flow);
162fbe33108SAlexei Starovoitov 		else
163fbe33108SAlexei Starovoitov 			return false;
164fbe33108SAlexei Starovoitov 		break;
165fbe33108SAlexei Starovoitov 	}
166fbe33108SAlexei Starovoitov 	case IPPROTO_IPIP:
167fbe33108SAlexei Starovoitov 		nhoff = parse_ip(skb, nhoff, &ip_proto, flow);
168fbe33108SAlexei Starovoitov 		break;
169fbe33108SAlexei Starovoitov 	case IPPROTO_IPV6:
170fbe33108SAlexei Starovoitov 		nhoff = parse_ipv6(skb, nhoff, &ip_proto, flow);
171fbe33108SAlexei Starovoitov 		break;
172fbe33108SAlexei Starovoitov 	default:
173fbe33108SAlexei Starovoitov 		break;
174fbe33108SAlexei Starovoitov 	}
175fbe33108SAlexei Starovoitov 
176fbe33108SAlexei Starovoitov 	flow->ip_proto = ip_proto;
177fbe33108SAlexei Starovoitov 	poff = proto_ports_offset(ip_proto);
178fbe33108SAlexei Starovoitov 	if (poff >= 0) {
179fbe33108SAlexei Starovoitov 		nhoff += poff;
180fbe33108SAlexei Starovoitov 		flow->ports = load_word(skb, nhoff);
181fbe33108SAlexei Starovoitov 	}
182fbe33108SAlexei Starovoitov 
183fbe33108SAlexei Starovoitov 	flow->thoff = (__u16) nhoff;
184fbe33108SAlexei Starovoitov 
185fbe33108SAlexei Starovoitov 	return true;
186fbe33108SAlexei Starovoitov }
187fbe33108SAlexei Starovoitov 
188614cd3bdSAlexei Starovoitov struct pair {
189614cd3bdSAlexei Starovoitov 	long packets;
190614cd3bdSAlexei Starovoitov 	long bytes;
191614cd3bdSAlexei Starovoitov };
192614cd3bdSAlexei Starovoitov 
193451d1dc8SDaniel T. Lee struct {
194451d1dc8SDaniel T. Lee 	__uint(type, BPF_MAP_TYPE_HASH);
195451d1dc8SDaniel T. Lee 	__type(key, __be32);
196451d1dc8SDaniel T. Lee 	__type(value, struct pair);
197451d1dc8SDaniel T. Lee 	__uint(max_entries, 1024);
198451d1dc8SDaniel T. Lee } hash_map SEC(".maps");
199fbe33108SAlexei Starovoitov 
200fbe33108SAlexei Starovoitov SEC("socket2")
bpf_prog2(struct __sk_buff * skb)201614cd3bdSAlexei Starovoitov int bpf_prog2(struct __sk_buff *skb)
202fbe33108SAlexei Starovoitov {
20332c00979SPrashant Bhole 	struct flow_key_record flow = {};
204614cd3bdSAlexei Starovoitov 	struct pair *value;
205fbe33108SAlexei Starovoitov 	u32 key;
206fbe33108SAlexei Starovoitov 
207fbe33108SAlexei Starovoitov 	if (!flow_dissector(skb, &flow))
208fbe33108SAlexei Starovoitov 		return 0;
209fbe33108SAlexei Starovoitov 
210fbe33108SAlexei Starovoitov 	key = flow.dst;
211fbe33108SAlexei Starovoitov 	value = bpf_map_lookup_elem(&hash_map, &key);
212fbe33108SAlexei Starovoitov 	if (value) {
213614cd3bdSAlexei Starovoitov 		__sync_fetch_and_add(&value->packets, 1);
214614cd3bdSAlexei Starovoitov 		__sync_fetch_and_add(&value->bytes, skb->len);
215fbe33108SAlexei Starovoitov 	} else {
216614cd3bdSAlexei Starovoitov 		struct pair val = {1, skb->len};
217fbe33108SAlexei Starovoitov 
218fbe33108SAlexei Starovoitov 		bpf_map_update_elem(&hash_map, &key, &val, BPF_ANY);
219fbe33108SAlexei Starovoitov 	}
220fbe33108SAlexei Starovoitov 	return 0;
221fbe33108SAlexei Starovoitov }
222fbe33108SAlexei Starovoitov 
223fbe33108SAlexei Starovoitov char _license[] SEC("license") = "GPL";
224