brintos

brintos / linux-shallow public Read only

0
0
Text · 6.5 KiB · 822c132 Raw
305 lines · c
1/* Copyright (c) 2015 PLUMgrid, http://plumgrid.com2 *3 * This program is free software; you can redistribute it and/or4 * modify it under the terms of version 2 of the GNU General Public5 * License as published by the Free Software Foundation.6 */7#include <uapi/linux/bpf.h>8#include <uapi/linux/in.h>9#include <uapi/linux/if.h>10#include <uapi/linux/if_ether.h>11#include <uapi/linux/ip.h>12#include <uapi/linux/ipv6.h>13#include <uapi/linux/if_tunnel.h>14#include <uapi/linux/mpls.h>15#include <bpf/bpf_helpers.h>16#include "bpf_legacy.h"17#define IP_MF		0x200018#define IP_OFFSET	0x1FFF19 20#define PARSE_VLAN 121#define PARSE_MPLS 222#define PARSE_IP 323#define PARSE_IPV6 424 25struct vlan_hdr {26	__be16 h_vlan_TCI;27	__be16 h_vlan_encapsulated_proto;28};29 30struct flow_key_record {31	__be32 src;32	__be32 dst;33	union {34		__be32 ports;35		__be16 port16[2];36	};37	__u32 ip_proto;38};39 40static inline void parse_eth_proto(struct __sk_buff *skb, u32 proto);41 42static inline int ip_is_fragment(struct __sk_buff *ctx, __u64 nhoff)43{44	return load_half(ctx, nhoff + offsetof(struct iphdr, frag_off))45		& (IP_MF | IP_OFFSET);46}47 48static inline __u32 ipv6_addr_hash(struct __sk_buff *ctx, __u64 off)49{50	__u64 w0 = load_word(ctx, off);51	__u64 w1 = load_word(ctx, off + 4);52	__u64 w2 = load_word(ctx, off + 8);53	__u64 w3 = load_word(ctx, off + 12);54 55	return (__u32)(w0 ^ w1 ^ w2 ^ w3);56}57 58struct globals {59	struct flow_key_record flow;60};61 62struct {63	__uint(type, BPF_MAP_TYPE_ARRAY);64	__type(key, __u32);65	__type(value, struct globals);66	__uint(max_entries, 32);67} percpu_map SEC(".maps");68 69/* user poor man's per_cpu until native support is ready */70static struct globals *this_cpu_globals(void)71{72	u32 key = bpf_get_smp_processor_id();73 74	return bpf_map_lookup_elem(&percpu_map, &key);75}76 77/* some simple stats for user space consumption */78struct pair {79	__u64 packets;80	__u64 bytes;81};82 83struct {84	__uint(type, BPF_MAP_TYPE_HASH);85	__type(key, struct flow_key_record);86	__type(value, struct pair);87	__uint(max_entries, 1024);88} hash_map SEC(".maps");89 90static void update_stats(struct __sk_buff *skb, struct globals *g)91{92	struct flow_key_record key = g->flow;93	struct pair *value;94 95	value = bpf_map_lookup_elem(&hash_map, &key);96	if (value) {97		__sync_fetch_and_add(&value->packets, 1);98		__sync_fetch_and_add(&value->bytes, skb->len);99	} else {100		struct pair val = {1, skb->len};101 102		bpf_map_update_elem(&hash_map, &key, &val, BPF_ANY);103	}104}105 106static __always_inline void parse_ip_proto(struct __sk_buff *skb,107					   struct globals *g, __u32 ip_proto)108{109	__u32 nhoff = skb->cb[0];110	int poff;111 112	switch (ip_proto) {113	case IPPROTO_GRE: {114		struct gre_hdr {115			__be16 flags;116			__be16 proto;117		};118 119		__u32 gre_flags = load_half(skb,120					    nhoff + offsetof(struct gre_hdr, flags));121		__u32 gre_proto = load_half(skb,122					    nhoff + offsetof(struct gre_hdr, proto));123 124		if (gre_flags & (GRE_VERSION|GRE_ROUTING))125			break;126 127		nhoff += 4;128		if (gre_flags & GRE_CSUM)129			nhoff += 4;130		if (gre_flags & GRE_KEY)131			nhoff += 4;132		if (gre_flags & GRE_SEQ)133			nhoff += 4;134 135		skb->cb[0] = nhoff;136		parse_eth_proto(skb, gre_proto);137		break;138	}139	case IPPROTO_IPIP:140		parse_eth_proto(skb, ETH_P_IP);141		break;142	case IPPROTO_IPV6:143		parse_eth_proto(skb, ETH_P_IPV6);144		break;145	case IPPROTO_TCP:146	case IPPROTO_UDP:147		g->flow.ports = load_word(skb, nhoff);148	case IPPROTO_ICMP:149		g->flow.ip_proto = ip_proto;150		update_stats(skb, g);151		break;152	default:153		break;154	}155}156 157SEC("socket")158int bpf_func_ip(struct __sk_buff *skb)159{160	struct globals *g = this_cpu_globals();161	__u32 nhoff, verlen, ip_proto;162 163	if (!g)164		return 0;165 166	nhoff = skb->cb[0];167 168	if (unlikely(ip_is_fragment(skb, nhoff)))169		return 0;170 171	ip_proto = load_byte(skb, nhoff + offsetof(struct iphdr, protocol));172 173	if (ip_proto != IPPROTO_GRE) {174		g->flow.src = load_word(skb, nhoff + offsetof(struct iphdr, saddr));175		g->flow.dst = load_word(skb, nhoff + offsetof(struct iphdr, daddr));176	}177 178	verlen = load_byte(skb, nhoff + 0/*offsetof(struct iphdr, ihl)*/);179	nhoff += (verlen & 0xF) << 2;180 181	skb->cb[0] = nhoff;182	parse_ip_proto(skb, g, ip_proto);183	return 0;184}185 186SEC("socket")187int bpf_func_ipv6(struct __sk_buff *skb)188{189	struct globals *g = this_cpu_globals();190	__u32 nhoff, ip_proto;191 192	if (!g)193		return 0;194 195	nhoff = skb->cb[0];196 197	ip_proto = load_byte(skb,198			     nhoff + offsetof(struct ipv6hdr, nexthdr));199	g->flow.src = ipv6_addr_hash(skb,200				     nhoff + offsetof(struct ipv6hdr, saddr));201	g->flow.dst = ipv6_addr_hash(skb,202				     nhoff + offsetof(struct ipv6hdr, daddr));203	nhoff += sizeof(struct ipv6hdr);204 205	skb->cb[0] = nhoff;206	parse_ip_proto(skb, g, ip_proto);207	return 0;208}209 210SEC("socket")211int bpf_func_vlan(struct __sk_buff *skb)212{213	__u32 nhoff, proto;214 215	nhoff = skb->cb[0];216 217	proto = load_half(skb, nhoff + offsetof(struct vlan_hdr,218						h_vlan_encapsulated_proto));219	nhoff += sizeof(struct vlan_hdr);220	skb->cb[0] = nhoff;221 222	parse_eth_proto(skb, proto);223 224	return 0;225}226 227SEC("socket")228int bpf_func_mpls(struct __sk_buff *skb)229{230	__u32 nhoff, label;231 232	nhoff = skb->cb[0];233 234	label = load_word(skb, nhoff);235	nhoff += sizeof(struct mpls_label);236	skb->cb[0] = nhoff;237 238	if (label & MPLS_LS_S_MASK) {239		__u8 verlen = load_byte(skb, nhoff);240		if ((verlen & 0xF0) == 4)241			parse_eth_proto(skb, ETH_P_IP);242		else243			parse_eth_proto(skb, ETH_P_IPV6);244	} else {245		parse_eth_proto(skb, ETH_P_MPLS_UC);246	}247 248	return 0;249}250 251struct {252	__uint(type, BPF_MAP_TYPE_PROG_ARRAY);253	__uint(key_size, sizeof(u32));254	__uint(max_entries, 8);255	__array(values, u32 (void *));256} prog_array_init SEC(".maps") = {257	.values = {258		[PARSE_VLAN] = (void *)&bpf_func_vlan,259		[PARSE_IP]   = (void *)&bpf_func_ip,260		[PARSE_IPV6] = (void *)&bpf_func_ipv6,261		[PARSE_MPLS] = (void *)&bpf_func_mpls,262	},263};264 265/* Protocol dispatch routine. It tail-calls next BPF program depending266 * on eth proto. Note, we could have used ...267 *268 *   bpf_tail_call(skb, &prog_array_init, proto);269 *270 * ... but it would need large prog_array and cannot be optimised given271 * the map key is not static.272 */273static inline void parse_eth_proto(struct __sk_buff *skb, u32 proto)274{275	switch (proto) {276	case ETH_P_8021Q:277	case ETH_P_8021AD:278		bpf_tail_call(skb, &prog_array_init, PARSE_VLAN);279		break;280	case ETH_P_MPLS_UC:281	case ETH_P_MPLS_MC:282		bpf_tail_call(skb, &prog_array_init, PARSE_MPLS);283		break;284	case ETH_P_IP:285		bpf_tail_call(skb, &prog_array_init, PARSE_IP);286		break;287	case ETH_P_IPV6:288		bpf_tail_call(skb, &prog_array_init, PARSE_IPV6);289		break;290	}291}292 293SEC("socket")294int main_prog(struct __sk_buff *skb)295{296	__u32 nhoff = ETH_HLEN;297	__u32 proto = load_half(skb, 12);298 299	skb->cb[0] = nhoff;300	parse_eth_proto(skb, proto);301	return 0;302}303 304char _license[] SEC("license") = "GPL";305