xref: /aosp_15_r20/external/bcc/examples/networking/vlan_learning/vlan_learning.py (revision 387f9dfdfa2baef462e92476d413c7bc2470293e)
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