text: #!/usr/bin/python
  #
  # xdp_macswap_count.py Swap Source and Destination MAC addresses on
  #                      incoming packets and transmit packets back on
  #                      same interface in XDP layer and count for which
  #                      protocol type
  #
  # Copyright (c) 2016 PLUMgrid
  # Copyright (c) 2016 Jan Ruth
  # Copyright (c) 2018 Andy Gospodarek
  # Licensed under the Apache License, Version 2.0 (the "License")
  
  from bcc import BPF
  import pyroute2
  import time
  import sys
  
  flags = 0
  def usage():
      print("Usage: {0} [-S] <ifdev>".format(sys.argv[0]))
      print("       -S: use skb mode\n")
      print("e.g.: {0} eth0\n".format(sys.argv[0]))
      exit(1)
  
  if len(sys.argv) < 2 or len(sys.argv) > 3:
      usage()
  
  if len(sys.argv) == 2:
      device = sys.argv[1]
  
  if len(sys.argv) == 3:
      if "-S" in sys.argv:
          # XDP_FLAGS_SKB_MODE
          flags |= 2 << 0
  
      if "-S" == sys.argv[1]:
          device = sys.argv[2]
      else:
          device = sys.argv[1]
  
  mode = BPF.XDP
  #mode = BPF.SCHED_CLS
  
  if mode == BPF.XDP:
      ret = "XDP_TX"
      ctxtype = "xdp_md"
  else:
      ret = "TC_ACT_SHOT"
      ctxtype = "__sk_buff"
  
  # load BPF program
  b = BPF(text = """
  #include <uapi/linux/bpf.h>
  #include <linux/in.h>
  #include <linux/if_ether.h>
  #include <linux/if_packet.h>
  #include <linux/if_vlan.h>
  #include <linux/ip.h>
  #include <linux/ipv6.h>
  
  
  BPF_PERCPU_ARRAY(dropcnt, long, 256);
  
  static inline int parse_ipv4(void *data, u64 nh_off, void *data_end) {
      struct iphdr *iph = data + nh_off;
  
      if ((void*)&iph[1] > data_end)
          return 0;
      return iph->protocol;
  }
  
  static inline int parse_ipv6(void *data, u64 nh_off, void *data_end) {
      struct ipv6hdr *ip6h = data + nh_off;
  
      if ((void*)&ip6h[1] > data_end)
          return 0;
      return ip6h->nexthdr;
  }
  
  static void swap_src_dst_mac(void *data)
  {
      unsigned short *p = data;
      unsigned short dst[3];
  
      dst[0] = p[0];
      dst[1] = p[1];
      dst[2] = p[2];
      p[0] = p[3];
      p[1] = p[4];
      p[2] = p[5];
      p[3] = dst[0];
      p[4] = dst[1];
      p[5] = dst[2];
  }
  
  int xdp_prog1(struct CTXTYPE *ctx) {
  
      void* data_end = (void*)(long)ctx->data_end;
      void* data = (void*)(long)ctx->data;
  
      struct ethhdr *eth = data;
  
      // drop packets
      int rc = RETURNCODE; // let pass XDP_PASS or redirect to tx via XDP_TX
      long *value;
      uint16_t h_proto;
      uint64_t nh_off = 0;
      uint32_t index;
  
      nh_off = sizeof(*eth);
  
      if (data + nh_off  > data_end)
          return rc;
  
      h_proto = eth->h_proto;
  
      if (h_proto == htons(ETH_P_8021Q) || h_proto == htons(ETH_P_8021AD)) {
          struct vlan_hdr *vhdr;
  
          vhdr = data + nh_off;
          nh_off += sizeof(struct vlan_hdr);
          if (data + nh_off > data_end)
              return rc;
              h_proto = vhdr->h_vlan_encapsulated_proto;
      }
      if (h_proto == htons(ETH_P_8021Q) || h_proto == htons(ETH_P_8021AD)) {
          struct vlan_hdr *vhdr;
  
          vhdr = data + nh_off;
          nh_off += sizeof(struct vlan_hdr);
          if (data + nh_off > data_end)
              return rc;
              h_proto = vhdr->h_vlan_encapsulated_proto;
      }
  
      if (h_proto == htons(ETH_P_IP))
          index = parse_ipv4(data, nh_off, data_end);
      else if (h_proto == htons(ETH_P_IPV6))
         index = parse_ipv6(data, nh_off, data_end);
      else
          index = 0;
  
      if (index == IPPROTO_UDP) {
          swap_src_dst_mac(data);
          rc = XDP_TX;
      }
  
      value = dropcnt.lookup(&index);
      if (value)
          *value += 1;
  
      return rc;
  }
  """, cflags=["-w", "-DRETURNCODE=%s" % ret, "-DCTXTYPE=%s" % ctxtype])
  
  fn = b.load_func("xdp_prog1", mode)
  
  if mode == BPF.XDP:
      b.attach_xdp(device, fn, flags)
  else:
      ip = pyroute2.IPRoute()
      ipdb = pyroute2.IPDB(nl=ip)
      idx = ipdb.interfaces[device].index
      ip.tc("add", "clsact", idx)
      ip.tc("add-filter", "bpf", idx, ":1", fd=fn.fd, name=fn.name,
            parent="ffff:fff2", classid=1, direct_action=True)
  
  dropcnt = b.get_table("dropcnt")
  prev = [0] * 256
  print("Printing drops per IP protocol-number, hit CTRL+C to stop")
  while 1:
      try:
          for k in dropcnt.keys():
              val = dropcnt.sum(k).value
              i = k.value
              if val:
                  delta = val - prev[i]
                  prev[i] = val
                  print("{}: {} pkt/s".format(i, delta))
          time.sleep(1)
      except KeyboardInterrupt:
          print("Removing filter from device")
          break;
  
  if mode == BPF.XDP:
      b.remove_xdp(device, flags)
  else:
      ip.tc("del", "clsact", idx)
      ipdb.release()
  