github.com/dylandreimerink/gobpfld@v0.6.1-0.20220205171531-e79c330ad608/cmd/examples/bpf_to_bpf/src/xdp.c (about)

     1  #include <stddef.h>
     2  #include <linux/bpf.h>
     3  #include <linux/in.h>
     4  #include <linux/if_ether.h>
     5  #include <linux/if_packet.h>
     6  #include <linux/ip.h>
     7  #include <linux/ipv6.h>
     8  #include <linux/tcp.h>
     9  #include <linux/udp.h>
    10  #include "bpf_endian.h"
    11  
    12  #define SEC(NAME) __attribute__((section(NAME), used))
    13  
    14  #ifndef __noinline
    15  #define __noinline __attribute__((noinline))
    16  #endif
    17  
    18  /*
    19   * bpf_map_lookup_elem
    20   *
    21   * 	Perform a lookup in *map* for an entry associated to *key*.
    22   *
    23   * Returns
    24   * 	Map value associated to *key*, or **NULL** if no entry was
    25   * 	found.
    26   */
    27  static void *(*bpf_map_lookup_elem)(void *map, const void *key) = (void *) 1;
    28  
    29  /*
    30   * bpf_map_update_elem
    31   *
    32   * 	Add or update the value of the entry associated to *key* in
    33   * 	*map* with *value*. *flags* is one of:
    34   *
    35   * 	**BPF_NOEXIST**
    36   * 		The entry for *key* must not exist in the map.
    37   * 	**BPF_EXIST**
    38   * 		The entry for *key* must already exist in the map.
    39   * 	**BPF_ANY**
    40   * 		No condition on the existence of the entry for *key*.
    41   *
    42   * 	Flag value **BPF_NOEXIST** cannot be used for maps of types
    43   * 	**BPF_MAP_TYPE_ARRAY** or **BPF_MAP_TYPE_PERCPU_ARRAY**  (all
    44   * 	elements always exist), the helper would return an error.
    45   *
    46   * Returns
    47   * 	0 on success, or a negative error in case of failure.
    48   */
    49  static long (*bpf_map_update_elem)(void *map, const void *key, const void *value, __u64 flags) = (void *) 2;
    50  
    51  
    52  struct bpf_map_def {
    53  	unsigned int type;
    54  	unsigned int key_size;
    55  	unsigned int value_size;
    56  	unsigned int max_entries;
    57  	unsigned int map_flags;
    58  };
    59  
    60  struct traffic_stats {
    61  	__u64 pkts;
    62  	__u64 bytes;
    63  };
    64  
    65  // Stats on packets keyed by protocol number
    66  struct bpf_map_def SEC("maps") ip_proto_stats = {
    67  	.type        = BPF_MAP_TYPE_LRU_PERCPU_HASH,
    68  	.key_size    = sizeof(__u8),
    69  	.value_size  = sizeof(struct traffic_stats),
    70  	.max_entries = 16, // stats on top 16 protocol seem more than enough
    71  	.map_flags 	 = BPF_F_NO_COMMON_LRU,
    72  };
    73  
    74  // Stats on udp packets keyed by dest port
    75  struct bpf_map_def SEC("maps") udp_stats = {
    76  	.type        = BPF_MAP_TYPE_LRU_PERCPU_HASH,
    77  	.key_size    = sizeof(__u16),
    78  	.value_size  = sizeof(struct traffic_stats),
    79  	.max_entries = 128, // top 128 udp ports seems good enough
    80  	.map_flags   = BPF_F_NO_COMMON_LRU,
    81  };
    82  
    83  // Stats on tcp packets keyed by dest port
    84  struct bpf_map_def SEC("maps") tcp_stats = {
    85  	.type        = BPF_MAP_TYPE_LRU_PERCPU_HASH,
    86  	.key_size    = sizeof(__u16),
    87  	.value_size  = sizeof(struct traffic_stats),
    88  	.max_entries = 128, // top 128 tcp ports seems good enough
    89  	.map_flags   = BPF_F_NO_COMMON_LRU,
    90  };
    91  
    92  struct vlan_hdr {
    93  	__be16 h_vlan_TCI;
    94  	__be16 h_vlan_encapsulated_proto;
    95  };
    96  
    97  static __noinline void inc_ip_proto(
    98  	__u8 proto,
    99  	__u64 framesize
   100  ) {
   101  	struct traffic_stats* stats_ptr = bpf_map_lookup_elem(&ip_proto_stats, &proto);
   102  	if( stats_ptr == NULL ) {
   103  		// Make a new stats object
   104  		struct traffic_stats stats = {
   105              .pkts = 1,
   106              .bytes = framesize,
   107          };
   108  		bpf_map_update_elem(&ip_proto_stats, &proto, &stats, BPF_ANY);
   109  	} else {
   110  		stats_ptr->pkts++;
   111          stats_ptr->bytes += framesize;
   112  	}
   113  }
   114  
   115  static __noinline void inc_tcp(
   116  	struct tcphdr* tcphdr,
   117  	__u64 framesize
   118  ){
   119  	__le16 le_dest = bpf_ntohs(tcphdr->dest);
   120  	// Get existing stats
   121  	struct traffic_stats* stats_ptr = bpf_map_lookup_elem(&tcp_stats, &le_dest);
   122  	if( stats_ptr == NULL ) {
   123  		// Make a new stats object
   124  		struct traffic_stats stats = {
   125              .pkts = 1,
   126              .bytes = framesize,
   127          };
   128  		bpf_map_update_elem(&tcp_stats, &le_dest, &stats, BPF_ANY);
   129  	} else {
   130          stats_ptr->pkts++;
   131          stats_ptr->bytes += framesize;
   132  	}
   133  }
   134  
   135  static __noinline void inc_udp(
   136  	struct udphdr* udphdr,
   137  	__u64 framesize
   138  ){
   139  	__le16 le_dest = bpf_ntohs(udphdr->dest);
   140  	// Get existing stats
   141  	struct traffic_stats* stats_ptr = bpf_map_lookup_elem(&udp_stats, &le_dest);
   142  	if( stats_ptr == NULL ) {
   143  		// Make a new stats object
   144  		struct traffic_stats stats = {
   145              .pkts = 1,
   146              .bytes = framesize,
   147          };
   148  
   149  		bpf_map_update_elem(&udp_stats, &le_dest, &stats, BPF_ANY);
   150  	} else {
   151  		stats_ptr->pkts++;
   152          stats_ptr->bytes += framesize;
   153  	}
   154  }
   155  
   156  static __noinline void handle_ipv4(void* data, void* data_end, __u64 nh_off) {
   157  	struct iphdr* iph = data + nh_off;
   158  	nh_off += sizeof(struct iphdr);
   159  	__u64 framesize = data_end - data;
   160  
   161  	// Drop packets which don't have enough data to fit the IPv4 header
   162  	if( data + nh_off > data_end ){
   163  		return;
   164  	}
   165  
   166  	__u8 ipproto = iph->protocol;
   167  
   168      inc_ip_proto(ipproto, framesize);
   169  
   170  	if( ipproto == IPPROTO_UDP ){
   171          struct udphdr* udphdr = data + nh_off;
   172          nh_off += sizeof(struct udphdr);
   173          
   174          // If there is not enough data to parse a UDP header, drop the packet
   175          if( data + nh_off > data_end ) {
   176              return;
   177          }
   178  
   179          inc_udp(udphdr, framesize);
   180      }
   181  
   182      if( ipproto == IPPROTO_TCP ){
   183          struct tcphdr* tcphdr = data + nh_off;
   184          nh_off += sizeof(struct tcphdr);
   185  
   186          
   187          // If there is not enough data to parse a UDP header, drop the packet
   188          if( data + nh_off > data_end ) {
   189              return;
   190          }
   191  
   192          inc_tcp(tcphdr, framesize);
   193      }
   194  }
   195  
   196  static __noinline void handle_ipv6(void* data, void* data_end, __u64 nh_off) {
   197  	struct ipv6hdr* ip6h = data + nh_off;
   198  	nh_off += sizeof(struct ipv6hdr);
   199  	__u64 framesize = data_end - data;
   200  
   201  	// Drop packets which don't have enough data to fit the IPv4 header
   202  	if( data + nh_off > data_end ){
   203  		return;
   204  	}
   205  
   206  	__u8 ipproto = ip6h->nexthdr;
   207  
   208  	inc_ip_proto(ipproto, framesize);
   209  
   210      if( ipproto == IPPROTO_UDP ){
   211          struct udphdr* udphdr = data + nh_off;
   212          nh_off += sizeof(struct udphdr);
   213          
   214          // If there is not enough data to parse a UDP header, drop the packet
   215          if( data + nh_off > data_end ) {
   216              return;
   217          }
   218  
   219          inc_udp(udphdr, framesize);
   220      }
   221  
   222      if( ipproto == IPPROTO_TCP ){
   223          struct tcphdr* tcphdr = data + nh_off;
   224          nh_off += sizeof(struct tcphdr);
   225          
   226          // If there is not enough data to parse a TCP header, drop the packet
   227          if( data + nh_off > data_end ) {
   228              return;
   229          }
   230  
   231          inc_tcp(tcphdr, framesize);
   232      }
   233  }
   234  
   235  SEC("xdp/proto_stats")
   236  int firewall_prog(struct xdp_md *ctx)
   237  {
   238  	void* data_end = (void *)(long)ctx->data_end;
   239  	void* data = (void *)(long)ctx->data;
   240  
   241  	// Offset to the next header
   242  	__u64 nh_off = sizeof(struct ethhdr);
   243  
   244  	// If we don't even have enough data to a ethernet frame header, drop the message
   245  	if( data + nh_off > data_end ){
   246  		return XDP_DROP;
   247  	}
   248  
   249  	struct ethhdr* eth = data;
   250  	__be16 h_proto = eth->h_proto;
   251  
   252  	// If the ethernet packet contains a IEEE 802.1Q or 802.1AD VLAN header
   253  	if( h_proto == bpf_htons(ETH_P_8021Q) || h_proto == bpf_htons(ETH_P_8021AD) ){
   254  		struct vlan_hdr* vhdr = data + nh_off;
   255  		nh_off += sizeof(struct vlan_hdr);
   256  		
   257  		// Drop packets which don't have enough data to fit the VLAN header
   258  		if( data + nh_off > data_end ){
   259  			return XDP_DROP;
   260  		}
   261  
   262  		h_proto = vhdr->h_vlan_encapsulated_proto;
   263  	}
   264  
   265  	if( h_proto == bpf_htons(ETH_P_IP) ){
   266  		handle_ipv4(data, data_end, nh_off);
   267  	} else if( h_proto == bpf_htons(ETH_P_IPV6) ){
   268  		handle_ipv6(data, data_end, nh_off);
   269  	}
   270  	
   271  	return XDP_PASS;
   272  }
   273  
   274  char _license[] SEC("license") = "GPL";