Back to home page

OSCL-LXR

 
 

    


0001 /* Copyright (c) 2015 PLUMgrid, http://plumgrid.com
0002  *
0003  * This program is free software; you can redistribute it and/or
0004  * modify it under the terms of version 2 of the GNU General Public
0005  * License as published by the Free Software Foundation.
0006  */
0007 #include <uapi/linux/bpf.h>
0008 #include <uapi/linux/in.h>
0009 #include <uapi/linux/if.h>
0010 #include <uapi/linux/if_ether.h>
0011 #include <uapi/linux/ip.h>
0012 #include <uapi/linux/ipv6.h>
0013 #include <uapi/linux/if_tunnel.h>
0014 #include <uapi/linux/mpls.h>
0015 #include <bpf/bpf_helpers.h>
0016 #include "bpf_legacy.h"
0017 #define IP_MF       0x2000
0018 #define IP_OFFSET   0x1FFF
0019 
0020 #define PROG(F) SEC("socket/"__stringify(F)) int bpf_func_##F
0021 
0022 struct {
0023     __uint(type, BPF_MAP_TYPE_PROG_ARRAY);
0024     __uint(key_size, sizeof(u32));
0025     __uint(value_size, sizeof(u32));
0026     __uint(max_entries, 8);
0027 } jmp_table SEC(".maps");
0028 
0029 #define PARSE_VLAN 1
0030 #define PARSE_MPLS 2
0031 #define PARSE_IP 3
0032 #define PARSE_IPV6 4
0033 
0034 /* Protocol dispatch routine. It tail-calls next BPF program depending
0035  * on eth proto. Note, we could have used ...
0036  *
0037  *   bpf_tail_call(skb, &jmp_table, proto);
0038  *
0039  * ... but it would need large prog_array and cannot be optimised given
0040  * the map key is not static.
0041  */
0042 static inline void parse_eth_proto(struct __sk_buff *skb, u32 proto)
0043 {
0044     switch (proto) {
0045     case ETH_P_8021Q:
0046     case ETH_P_8021AD:
0047         bpf_tail_call(skb, &jmp_table, PARSE_VLAN);
0048         break;
0049     case ETH_P_MPLS_UC:
0050     case ETH_P_MPLS_MC:
0051         bpf_tail_call(skb, &jmp_table, PARSE_MPLS);
0052         break;
0053     case ETH_P_IP:
0054         bpf_tail_call(skb, &jmp_table, PARSE_IP);
0055         break;
0056     case ETH_P_IPV6:
0057         bpf_tail_call(skb, &jmp_table, PARSE_IPV6);
0058         break;
0059     }
0060 }
0061 
0062 struct vlan_hdr {
0063     __be16 h_vlan_TCI;
0064     __be16 h_vlan_encapsulated_proto;
0065 };
0066 
0067 struct flow_key_record {
0068     __be32 src;
0069     __be32 dst;
0070     union {
0071         __be32 ports;
0072         __be16 port16[2];
0073     };
0074     __u32 ip_proto;
0075 };
0076 
0077 static inline int ip_is_fragment(struct __sk_buff *ctx, __u64 nhoff)
0078 {
0079     return load_half(ctx, nhoff + offsetof(struct iphdr, frag_off))
0080         & (IP_MF | IP_OFFSET);
0081 }
0082 
0083 static inline __u32 ipv6_addr_hash(struct __sk_buff *ctx, __u64 off)
0084 {
0085     __u64 w0 = load_word(ctx, off);
0086     __u64 w1 = load_word(ctx, off + 4);
0087     __u64 w2 = load_word(ctx, off + 8);
0088     __u64 w3 = load_word(ctx, off + 12);
0089 
0090     return (__u32)(w0 ^ w1 ^ w2 ^ w3);
0091 }
0092 
0093 struct globals {
0094     struct flow_key_record flow;
0095 };
0096 
0097 struct {
0098     __uint(type, BPF_MAP_TYPE_ARRAY);
0099     __type(key, __u32);
0100     __type(value, struct globals);
0101     __uint(max_entries, 32);
0102 } percpu_map SEC(".maps");
0103 
0104 /* user poor man's per_cpu until native support is ready */
0105 static struct globals *this_cpu_globals(void)
0106 {
0107     u32 key = bpf_get_smp_processor_id();
0108 
0109     return bpf_map_lookup_elem(&percpu_map, &key);
0110 }
0111 
0112 /* some simple stats for user space consumption */
0113 struct pair {
0114     __u64 packets;
0115     __u64 bytes;
0116 };
0117 
0118 struct {
0119     __uint(type, BPF_MAP_TYPE_HASH);
0120     __type(key, struct flow_key_record);
0121     __type(value, struct pair);
0122     __uint(max_entries, 1024);
0123 } hash_map SEC(".maps");
0124 
0125 static void update_stats(struct __sk_buff *skb, struct globals *g)
0126 {
0127     struct flow_key_record key = g->flow;
0128     struct pair *value;
0129 
0130     value = bpf_map_lookup_elem(&hash_map, &key);
0131     if (value) {
0132         __sync_fetch_and_add(&value->packets, 1);
0133         __sync_fetch_and_add(&value->bytes, skb->len);
0134     } else {
0135         struct pair val = {1, skb->len};
0136 
0137         bpf_map_update_elem(&hash_map, &key, &val, BPF_ANY);
0138     }
0139 }
0140 
0141 static __always_inline void parse_ip_proto(struct __sk_buff *skb,
0142                        struct globals *g, __u32 ip_proto)
0143 {
0144     __u32 nhoff = skb->cb[0];
0145     int poff;
0146 
0147     switch (ip_proto) {
0148     case IPPROTO_GRE: {
0149         struct gre_hdr {
0150             __be16 flags;
0151             __be16 proto;
0152         };
0153 
0154         __u32 gre_flags = load_half(skb,
0155                         nhoff + offsetof(struct gre_hdr, flags));
0156         __u32 gre_proto = load_half(skb,
0157                         nhoff + offsetof(struct gre_hdr, proto));
0158 
0159         if (gre_flags & (GRE_VERSION|GRE_ROUTING))
0160             break;
0161 
0162         nhoff += 4;
0163         if (gre_flags & GRE_CSUM)
0164             nhoff += 4;
0165         if (gre_flags & GRE_KEY)
0166             nhoff += 4;
0167         if (gre_flags & GRE_SEQ)
0168             nhoff += 4;
0169 
0170         skb->cb[0] = nhoff;
0171         parse_eth_proto(skb, gre_proto);
0172         break;
0173     }
0174     case IPPROTO_IPIP:
0175         parse_eth_proto(skb, ETH_P_IP);
0176         break;
0177     case IPPROTO_IPV6:
0178         parse_eth_proto(skb, ETH_P_IPV6);
0179         break;
0180     case IPPROTO_TCP:
0181     case IPPROTO_UDP:
0182         g->flow.ports = load_word(skb, nhoff);
0183     case IPPROTO_ICMP:
0184         g->flow.ip_proto = ip_proto;
0185         update_stats(skb, g);
0186         break;
0187     default:
0188         break;
0189     }
0190 }
0191 
0192 PROG(PARSE_IP)(struct __sk_buff *skb)
0193 {
0194     struct globals *g = this_cpu_globals();
0195     __u32 nhoff, verlen, ip_proto;
0196 
0197     if (!g)
0198         return 0;
0199 
0200     nhoff = skb->cb[0];
0201 
0202     if (unlikely(ip_is_fragment(skb, nhoff)))
0203         return 0;
0204 
0205     ip_proto = load_byte(skb, nhoff + offsetof(struct iphdr, protocol));
0206 
0207     if (ip_proto != IPPROTO_GRE) {
0208         g->flow.src = load_word(skb, nhoff + offsetof(struct iphdr, saddr));
0209         g->flow.dst = load_word(skb, nhoff + offsetof(struct iphdr, daddr));
0210     }
0211 
0212     verlen = load_byte(skb, nhoff + 0/*offsetof(struct iphdr, ihl)*/);
0213     nhoff += (verlen & 0xF) << 2;
0214 
0215     skb->cb[0] = nhoff;
0216     parse_ip_proto(skb, g, ip_proto);
0217     return 0;
0218 }
0219 
0220 PROG(PARSE_IPV6)(struct __sk_buff *skb)
0221 {
0222     struct globals *g = this_cpu_globals();
0223     __u32 nhoff, ip_proto;
0224 
0225     if (!g)
0226         return 0;
0227 
0228     nhoff = skb->cb[0];
0229 
0230     ip_proto = load_byte(skb,
0231                  nhoff + offsetof(struct ipv6hdr, nexthdr));
0232     g->flow.src = ipv6_addr_hash(skb,
0233                      nhoff + offsetof(struct ipv6hdr, saddr));
0234     g->flow.dst = ipv6_addr_hash(skb,
0235                      nhoff + offsetof(struct ipv6hdr, daddr));
0236     nhoff += sizeof(struct ipv6hdr);
0237 
0238     skb->cb[0] = nhoff;
0239     parse_ip_proto(skb, g, ip_proto);
0240     return 0;
0241 }
0242 
0243 PROG(PARSE_VLAN)(struct __sk_buff *skb)
0244 {
0245     __u32 nhoff, proto;
0246 
0247     nhoff = skb->cb[0];
0248 
0249     proto = load_half(skb, nhoff + offsetof(struct vlan_hdr,
0250                         h_vlan_encapsulated_proto));
0251     nhoff += sizeof(struct vlan_hdr);
0252     skb->cb[0] = nhoff;
0253 
0254     parse_eth_proto(skb, proto);
0255 
0256     return 0;
0257 }
0258 
0259 PROG(PARSE_MPLS)(struct __sk_buff *skb)
0260 {
0261     __u32 nhoff, label;
0262 
0263     nhoff = skb->cb[0];
0264 
0265     label = load_word(skb, nhoff);
0266     nhoff += sizeof(struct mpls_label);
0267     skb->cb[0] = nhoff;
0268 
0269     if (label & MPLS_LS_S_MASK) {
0270         __u8 verlen = load_byte(skb, nhoff);
0271         if ((verlen & 0xF0) == 4)
0272             parse_eth_proto(skb, ETH_P_IP);
0273         else
0274             parse_eth_proto(skb, ETH_P_IPV6);
0275     } else {
0276         parse_eth_proto(skb, ETH_P_MPLS_UC);
0277     }
0278 
0279     return 0;
0280 }
0281 
0282 SEC("socket/0")
0283 int main_prog(struct __sk_buff *skb)
0284 {
0285     __u32 nhoff = ETH_HLEN;
0286     __u32 proto = load_half(skb, 12);
0287 
0288     skb->cb[0] = nhoff;
0289     parse_eth_proto(skb, proto);
0290     return 0;
0291 }
0292 
0293 char _license[] SEC("license") = "GPL";