1#!/usr/bin/python 2# Copyright (c) PLUMgrid, Inc. 3# Licensed under the Apache License, Version 2.0 (the "License") 4 5from bcc import BPF 6from builtins import input 7from pyroute2 import IPRoute, NetNS, IPDB, NSPopen 8from random import shuffle 9from time import sleep 10from simulation import Simulation 11import sys 12 13ipr = IPRoute() 14ipdb = IPDB(nl=ipr) 15 16num_clients = 3 17num_vlans = 16 18 19# load the bpf program 20b = BPF(src_file="vlan_learning.c", debug=0) 21phys_fn = b.load_func("handle_phys2virt", BPF.SCHED_CLS) 22virt_fn = b.load_func("handle_virt2phys", BPF.SCHED_CLS) 23 24ingress = b.get_table("ingress") 25egress = b.get_table("egress") 26 27class VlanSimulation(Simulation): 28 def __init__(self, ipdb): 29 super(VlanSimulation, self).__init__(ipdb) 30 31 def start(self): 32 # start identical workers each in a namespace 33 for i in range(0, num_clients): 34 httpmod = ("SimpleHTTPServer" if sys.version_info[0] < 3 35 else "http.server") 36 cmd = ["python", "-m", httpmod, "80"] 37 self._create_ns("worker%d" % i, cmd=cmd, fn=virt_fn, action="drop", 38 ipaddr="172.16.1.5/24") 39 40 # simulate a physical eth vlan trunk 41 with self.ipdb.create(ifname="eth0a", kind="veth", peer="eth0b") as v: 42 v.up() 43 self.ipdb.interfaces.eth0b.up().commit() 44 45 # eth0a will be hooked to clients with vlan interfaces 46 # add the bpf program to eth0b for demuxing phys2virt packets 47 v = self.ipdb.interfaces["eth0b"] 48 ipr.tc("add", "ingress", v["index"], "ffff:") 49 ipr.tc("add-filter", "bpf", v["index"], ":1", fd=phys_fn.fd, 50 name=phys_fn.name, parent="ffff:", action="drop", classid=1) 51 52 # allocate vlans randomly 53 available_vlans = [i for i in range(2, 2 + num_vlans)] 54 shuffle(available_vlans) 55 available_ips = [[i for i in range(100, 105)] for i in range(0, num_clients)] 56 57 # these are simulations of physical clients 58 for i in range(0, num_clients): 59 macaddr = ("02:00:00:%.2x:%.2x:%.2x" % 60 ((i >> 16) & 0xff, (i >> 8) & 0xff, i & 0xff)) 61 62 # assign this client to the given worker 63 idx = self.ipdb.interfaces["worker%da" % i]["index"] 64 mac = int(macaddr.replace(":", ""), 16) 65 ingress[ingress.Key(mac)] = ingress.Leaf(idx, 0, 0, 0, 0) 66 67 # test traffic with curl loop 68 cmd = ["bash", "-c", 69 "for i in {1..8}; do curl 172.16.1.5 -o /dev/null; sleep 1; done"] 70 client_ifc = self.ipdb.create(ifname="eth0a.%d" % i, kind="vlan", 71 link=self.ipdb.interfaces["eth0a"], 72 vlan_id=available_vlans.pop(0)).commit() 73 (out_ifc, in_ifc) = self._create_ns("client%d" % i, in_ifc=client_ifc, 74 ipaddr="172.16.1.100/24", 75 macaddr=macaddr, cmd=cmd)[1:3] 76 77try: 78 sim = VlanSimulation(ipdb) 79 sim.start() 80 sleep(10) 81 input("Press enter to exit: ") 82 83 stats_collect = {} 84 for key, leaf in ingress.items(): 85 stats_collect[key.value] = [leaf.tx_pkts, leaf.tx_bytes, 0, 0] 86 for key, leaf in egress.items(): 87 x = stats_collect.get(key.value, [0, 0, 0, 0]) 88 x[2] = leaf.tx_pkts 89 x[3] = leaf.tx_bytes 90 for k, v in stats_collect.items(): 91 print("mac %.12x rx pkts = %u, rx bytes = %u" % (k, v[0], v[1])) 92 print(" tx pkts = %u, tx bytes = %u" % (v[2], v[3])) 93finally: 94 if "eth0a" in ipdb.interfaces: ipdb.interfaces.eth0a.remove().commit() 95 if "sim" in locals(): sim.release() 96 ipdb.release() 97