0001
0002
0003
0004
0005
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
0035
0036
0037
0038
0039
0040
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
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
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);
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";