1 // Copyright (c) PLUMgrid, Inc.
2 // Licensed under the Apache License, Version 2.0 (the "License")
3 #include <bcc/proto.h>
4
5 struct ipkey {
6 u32 inner_sip;
7 u32 inner_dip;
8 u32 outer_sip;
9 u32 outer_dip;
10 u32 vni;
11 };
12 struct counters {
13 u64 tx_pkts;
14 u64 rx_pkts;
15 u64 tx_bytes;
16 u64 rx_bytes;
17 };
18
19 BPF_HASH(stats, struct ipkey, struct counters, 1024);
20 BPF_PROG_ARRAY(parser, 10);
21
22 enum cb_index {
23 CB_FLAGS = 0,
24 CB_SIP,
25 CB_DIP,
26 CB_VNI,
27 CB_OFFSET,
28 };
29
30 // helper func to swap two memory locations
31 static inline
swap32(u32 * a,u32 * b)32 void swap32(u32 *a, u32 *b) {
33 u32 t = *a;
34 *a = *b;
35 *b = t;
36 }
37
38 // helper to swap the fields in an ipkey to give consistent ordering
39 static inline
swap_ipkey(struct ipkey * key)40 void swap_ipkey(struct ipkey *key) {
41 swap32(&key->outer_sip, &key->outer_dip);
42 swap32(&key->inner_sip, &key->inner_dip);
43 }
44
45 #define IS_INGRESS 0x1
46 // initial handler for each packet on an ingress tc filter
handle_ingress(struct __sk_buff * skb)47 int handle_ingress(struct __sk_buff *skb) {
48 skb->cb[CB_FLAGS] = IS_INGRESS;
49 parser.call(skb, 1); // jump to generic packet parser
50 return 1;
51 }
52
53 // initial handler for each packet on an egress tc filter
handle_egress(struct __sk_buff * skb)54 int handle_egress(struct __sk_buff *skb) {
55 skb->cb[CB_FLAGS] = 0;
56 parser.call(skb, 1); // jump to generic packet parser
57 return 1;
58 }
59
60 // parse the outer vxlan frame
handle_outer(struct __sk_buff * skb)61 int handle_outer(struct __sk_buff *skb) {
62 u8 *cursor = 0;
63
64 struct ethernet_t *ethernet = cursor_advance(cursor, sizeof(*ethernet));
65
66 // filter bcast/mcast from the stats
67 if (ethernet->dst & (1ull << 40))
68 goto finish;
69
70 switch (ethernet->type) {
71 case 0x0800: goto ip;
72 default: goto finish;
73 }
74
75 ip: ;
76 struct ip_t *ip = cursor_advance(cursor, sizeof(*ip));
77 skb->cb[CB_SIP] = ip->src;
78 skb->cb[CB_DIP] = ip->dst;
79
80 switch (ip->nextp) {
81 case 17: goto udp;
82 default: goto finish;
83 }
84
85 udp: ;
86 struct udp_t *udp = cursor_advance(cursor, sizeof(*udp));
87 switch (udp->dport) {
88 case 4789: goto vxlan;
89 default: goto finish;
90 }
91
92 vxlan: ;
93 struct vxlan_t *vxlan = cursor_advance(cursor, sizeof(*vxlan));
94 skb->cb[CB_VNI] = vxlan->key;
95 skb->cb[CB_OFFSET] = (u64)vxlan + sizeof(*vxlan);
96 parser.call(skb, 2);
97
98 finish:
99 return 1;
100 }
101
102 // Parse the inner frame, whatever it may be. If it is ipv4, add the inner
103 // source/dest ip to the key, for finer grained stats
handle_inner(struct __sk_buff * skb)104 int handle_inner(struct __sk_buff *skb) {
105 int is_ingress = skb->cb[CB_FLAGS] & IS_INGRESS;
106 struct ipkey key = {
107 .vni=skb->cb[CB_VNI],
108 .outer_sip = skb->cb[CB_SIP],
109 .outer_dip = skb->cb[CB_DIP]
110 };
111 u8 *cursor = (u8 *)(u64)skb->cb[CB_OFFSET];
112
113 struct ethernet_t *ethernet = cursor_advance(cursor, sizeof(*ethernet));
114 switch (ethernet->type) {
115 case 0x0800: goto ip;
116 default: goto finish;
117 }
118 ip: ;
119 struct ip_t *ip = cursor_advance(cursor, sizeof(*ip));
120 key.inner_sip = ip->src;
121 key.inner_dip = ip->dst;
122
123 finish:
124 // consistent ordering
125 if (key.outer_dip < key.outer_sip)
126 swap_ipkey(&key);
127 struct counters zleaf = {0};
128 struct counters *leaf = stats.lookup_or_try_init(&key, &zleaf);
129 if (leaf) {
130 if (is_ingress) {
131 lock_xadd(&leaf->rx_pkts, 1);
132 lock_xadd(&leaf->rx_bytes, skb->len);
133 } else {
134 lock_xadd(&leaf->tx_pkts, 1);
135 lock_xadd(&leaf->tx_bytes, skb->len);
136 }
137 }
138 return 1;
139 }
140