• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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