xref: /aosp_15_r20/external/bcc/examples/networking/distributed_bridge/tunnel_mesh.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 sys import argv
6from bcc import BPF
7from builtins import input
8from ctypes import c_int, c_uint
9from http.server import HTTPServer, SimpleHTTPRequestHandler
10import json
11from netaddr import EUI, IPAddress
12from pyroute2 import IPRoute, NetNS, IPDB, NSPopen
13from socket import htons, AF_INET
14from threading import Thread
15from subprocess import call, Popen, PIPE
16
17num_hosts = int(argv[1])
18host_id = int(argv[2])
19dhcp = int(argv[3])
20gretap = int(argv[4])
21
22b = BPF(src_file="tunnel_mesh.c")
23ingress_fn = b.load_func("handle_ingress", BPF.SCHED_CLS)
24egress_fn = b.load_func("handle_egress", BPF.SCHED_CLS)
25tunkey2if = b.get_table("tunkey2if")
26if2tunkey = b.get_table("if2tunkey")
27conf = b.get_table("conf")
28
29ipr = IPRoute()
30ipdb = IPDB(nl=ipr)
31
32ifc = ipdb.interfaces.eth0
33
34# ifcs to cleanup at the end
35ifc_gc = []
36
37# dhcp server and client processes
38d_serv = []
39d_client = []
40
41def run():
42    if gretap:
43        with ipdb.create(ifname="gretap1", kind="gretap", gre_ikey=0, gre_okey=0,
44                         gre_local='172.16.1.%d' % (100 + host_id),
45                         gre_ttl=16, gre_collect_metadata=1) as vx:
46            vx.up()
47            ifc_gc.append(vx.ifname)
48    else:
49        with ipdb.create(ifname="vxlan0", kind="vxlan", vxlan_id=0,
50                         vxlan_link=ifc, vxlan_port=4789,
51                         vxlan_collect_metadata=True,
52                         vxlan_learning=False) as vx:
53            vx.up()
54            ifc_gc.append(vx.ifname)
55
56    conf[c_int(1)] = c_int(vx.index)
57
58    ipr.tc("add", "ingress", vx.index, "ffff:")
59    ipr.tc("add-filter", "bpf", vx.index, ":1", fd=ingress_fn.fd,
60           name=ingress_fn.name, parent="ffff:", action="drop", classid=1)
61
62    for j in range(0, 2):
63        vni = 10000 + j
64        with ipdb.create(ifname="br%d" % j, kind="bridge") as br:
65            for i in range(0, num_hosts):
66                if i != host_id:
67                    v = ipdb.create(ifname="dummy%d%d" % (j , i), kind="dummy").up().commit()
68                    ipaddr = "172.16.1.%d" % (100 + i)
69                    tunkey2if_key = tunkey2if.Key(vni)
70                    tunkey2if_key.remote_ipv4 = IPAddress(ipaddr)
71                    tunkey2if_leaf = tunkey2if.Leaf(v.index)
72                    tunkey2if[tunkey2if_key] = tunkey2if_leaf
73
74                    if2tunkey_key = if2tunkey.Key(v.index)
75                    if2tunkey_leaf = if2tunkey.Leaf(vni)
76                    if2tunkey_leaf.remote_ipv4 = IPAddress(ipaddr)
77                    if2tunkey[if2tunkey_key] = if2tunkey_leaf
78
79                    ipr.tc("add", "sfq", v.index, "1:")
80                    ipr.tc("add-filter", "bpf", v.index, ":1", fd=egress_fn.fd,
81                       name=egress_fn.name, parent="1:", action="drop", classid=1)
82                    br.add_port(v)
83                    br.up()
84                    ifc_gc.append(v.ifname)
85            if dhcp == 0:
86                ipaddr = "99.1.%d.%d/24" % (j, host_id + 1)
87                br.add_ip(ipaddr)
88            ifc_gc.append(br.ifname)
89
90    # dhcp server only runs on host 0
91    if dhcp == 1 and host_id == 0:
92        for j in range(0, 2):
93            v1 = "dhcp%d_v1" % j
94            v2 = "dhcp%d_v2" % j
95            br = ipdb.interfaces["br%d" % j]
96            with ipdb.create(ifname=v1, kind="veth", peer=v2) as v:
97                    v.up()
98            br.add_port(ipdb.interfaces[v1]).commit()
99            dhcp_v2 = ipdb.interfaces[v2]
100            dhcp_v2.add_ip("99.1.%d.1/24" % j).up().commit()
101
102            call(["/bin/rm", "-f", "/tmp/dnsmasq.%d.leases" % j])
103            cmd = ["dnsmasq", "-d", "--bind-interfaces", "--strict-order",
104                   "--conf-file=",
105                   "--dhcp-range", "99.1.%d.2,99.1.%d.254,255.255.255.0,12h" % (j, j),
106                   "--dhcp-no-override", "--except-interface=lo",
107                   "--interface=dhcp%d_v2" % j,
108                   "--dhcp-authoritative",
109                   "--dhcp-leasefile=/tmp/dnsmasq.%d.leases" % j]
110            d_serv.append(Popen(cmd, stdout=PIPE, stderr=PIPE))
111
112    # dhcp client to assign ip address for each bridge
113    if dhcp == 1:
114        for j in range(0, 2):
115            call(["/bin/rm", "-rf", "/tmp/dhcp_%d_%d" % (host_id, j)])
116            call(["mkdir", "/tmp/dhcp_%d_%d" % (host_id, j)])
117            call(["touch", "/tmp/dhcp_%d_%d/dhclient.conf" % (host_id, j)])
118            call(["touch", "/tmp/dhcp_%d_%d/dhclient.lease" % (host_id, j)])
119            cmd = ["dhclient", "-d", "br%d" % j,
120                   "-cf", "/tmp/dhcp_%d_%d/dhclient.conf" % (host_id, j),
121                   "-lf", "/tmp/dhcp_%d_%d/dhclient.lease" % (host_id, j)]
122            d_client.append(Popen(cmd, stdout=PIPE, stderr=PIPE))
123
124            # make sure we get address for eth0
125            retry = -1
126            while retry < 0:
127                check = Popen(["ip", "addr", "show", "br%d" % j], stdout=PIPE, stderr=PIPE)
128                out = check.stdout.read()
129                checkip = b"99.1.%d" % j
130                retry = out.find(checkip)
131
132try:
133    run()
134    input("")
135finally:
136    for v in ifc_gc: call(["ip", "link", "del", v])
137    ipdb.release()
138    for p in d_client: p.kill()
139    for p in d_serv: p.kill()
140