#!/usr/bin/python # Copyright (c) PLUMgrid, Inc. # Licensed under the Apache License, Version 2.0 (the "License") from bcc import BPF from builtins import input from pyroute2 import IPRoute, NetNS, IPDB, NSPopen from random import shuffle from time import sleep from simulation import Simulation import sys ipr = IPRoute() ipdb = IPDB(nl=ipr) num_clients = 3 num_vlans = 16 # load the bpf program b = BPF(src_file="vlan_learning.c", debug=0) phys_fn = b.load_func("handle_phys2virt", BPF.SCHED_CLS) virt_fn = b.load_func("handle_virt2phys", BPF.SCHED_CLS) ingress = b.get_table("ingress") egress = b.get_table("egress") class VlanSimulation(Simulation): def __init__(self, ipdb): super(VlanSimulation, self).__init__(ipdb) def start(self): # start identical workers each in a namespace for i in range(0, num_clients): httpmod = ("SimpleHTTPServer" if sys.version_info[0] < 3 else "http.server") cmd = ["python", "-m", httpmod, "80"] self._create_ns("worker%d" % i, cmd=cmd, fn=virt_fn, action="drop", ipaddr="172.16.1.5/24") # simulate a physical eth vlan trunk with self.ipdb.create(ifname="eth0a", kind="veth", peer="eth0b") as v: v.up() self.ipdb.interfaces.eth0b.up().commit() # eth0a will be hooked to clients with vlan interfaces # add the bpf program to eth0b for demuxing phys2virt packets v = self.ipdb.interfaces["eth0b"] ipr.tc("add", "ingress", v["index"], "ffff:") ipr.tc("add-filter", "bpf", v["index"], ":1", fd=phys_fn.fd, name=phys_fn.name, parent="ffff:", action="drop", classid=1) # allocate vlans randomly available_vlans = [i for i in range(2, 2 + num_vlans)] shuffle(available_vlans) available_ips = [[i for i in range(100, 105)] for i in range(0, num_clients)] # these are simulations of physical clients for i in range(0, num_clients): macaddr = ("02:00:00:%.2x:%.2x:%.2x" % ((i >> 16) & 0xff, (i >> 8) & 0xff, i & 0xff)) # assign this client to the given worker idx = self.ipdb.interfaces["worker%da" % i]["index"] mac = int(macaddr.replace(":", ""), 16) ingress[ingress.Key(mac)] = ingress.Leaf(idx, 0, 0, 0, 0) # test traffic with curl loop cmd = ["bash", "-c", "for i in {1..8}; do curl 172.16.1.5 -o /dev/null; sleep 1; done"] client_ifc = self.ipdb.create(ifname="eth0a.%d" % i, kind="vlan", link=self.ipdb.interfaces["eth0a"], vlan_id=available_vlans.pop(0)).commit() (out_ifc, in_ifc) = self._create_ns("client%d" % i, in_ifc=client_ifc, ipaddr="172.16.1.100/24", macaddr=macaddr, cmd=cmd)[1:3] try: sim = VlanSimulation(ipdb) sim.start() sleep(10) input("Press enter to exit: ") stats_collect = {} for key, leaf in ingress.items(): stats_collect[key.value] = [leaf.tx_pkts, leaf.tx_bytes, 0, 0] for key, leaf in egress.items(): x = stats_collect.get(key.value, [0, 0, 0, 0]) x[2] = leaf.tx_pkts x[3] = leaf.tx_bytes for k, v in stats_collect.items(): print("mac %.12x rx pkts = %u, rx bytes = %u" % (k, v[0], v[1])) print(" tx pkts = %u, tx bytes = %u" % (v[2], v[3])) finally: if "eth0a" in ipdb.interfaces: ipdb.interfaces.eth0a.remove().commit() if "sim" in locals(): sim.release() ipdb.release()