97 lines
3.7 KiB
Python
Executable File
97 lines
3.7 KiB
Python
Executable File
#!/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()
|