xref: /aosp_15_r20/external/bcc/tests/python/test_brb2.py (revision 387f9dfdfa2baef462e92476d413c7bc2470293e)
1*387f9dfdSAndroid Build Coastguard Worker#!/usr/bin/env python3
2*387f9dfdSAndroid Build Coastguard Worker# Copyright (c) PLUMgrid, Inc.
3*387f9dfdSAndroid Build Coastguard Worker# Licensed under the Apache License, Version 2.0 (the "License")
4*387f9dfdSAndroid Build Coastguard Worker
5*387f9dfdSAndroid Build Coastguard Worker# This program implements a topology likes below:
6*387f9dfdSAndroid Build Coastguard Worker#   pem: physical endpoint manager, implemented as a bpf program
7*387f9dfdSAndroid Build Coastguard Worker#
8*387f9dfdSAndroid Build Coastguard Worker#     vm1 <--------+  +----> bridge1 <----+
9*387f9dfdSAndroid Build Coastguard Worker#                  V  V                   V
10*387f9dfdSAndroid Build Coastguard Worker#                  pem                  router
11*387f9dfdSAndroid Build Coastguard Worker#                  ^  ^                   ^
12*387f9dfdSAndroid Build Coastguard Worker#     vm2 <--------+  +----> bridge2 <----+
13*387f9dfdSAndroid Build Coastguard Worker#
14*387f9dfdSAndroid Build Coastguard Worker# The vm1, vm2 and router are implemented as namespaces.
15*387f9dfdSAndroid Build Coastguard Worker# The linux bridge device is used to provice bridge functionality.
16*387f9dfdSAndroid Build Coastguard Worker# pem bpf will be attached to related network devices for vm1, vm1, bridge1 and bridge2.
17*387f9dfdSAndroid Build Coastguard Worker#
18*387f9dfdSAndroid Build Coastguard Worker# vm1 and vm2 are in different subnet. For vm1 to communicate to vm2,
19*387f9dfdSAndroid Build Coastguard Worker# the packet will have to travel from vm1 to pem, bridge1, router, bridge2, pem, and
20*387f9dfdSAndroid Build Coastguard Worker# then come to vm2.
21*387f9dfdSAndroid Build Coastguard Worker#
22*387f9dfdSAndroid Build Coastguard Worker# When this test is run with verbose mode (ctest -R <test_name> -V),
23*387f9dfdSAndroid Build Coastguard Worker# the following printout is observed on my local box:
24*387f9dfdSAndroid Build Coastguard Worker#
25*387f9dfdSAndroid Build Coastguard Worker# ......
26*387f9dfdSAndroid Build Coastguard Worker# 9: PING 200.1.1.1 (200.1.1.1) 56(84) bytes of data.
27*387f9dfdSAndroid Build Coastguard Worker# 9: 64 bytes from 200.1.1.1: icmp_req=1 ttl=63 time=0.090 ms
28*387f9dfdSAndroid Build Coastguard Worker# 9: 64 bytes from 200.1.1.1: icmp_req=2 ttl=63 time=0.032 ms
29*387f9dfdSAndroid Build Coastguard Worker# 9:
30*387f9dfdSAndroid Build Coastguard Worker# 9: --- 200.1.1.1 ping statistics ---
31*387f9dfdSAndroid Build Coastguard Worker# 9: 2 packets transmitted, 2 received, 0% packet loss, time 999ms
32*387f9dfdSAndroid Build Coastguard Worker# 9: rtt min/avg/max/mdev = 0.032/0.061/0.090/0.029 ms
33*387f9dfdSAndroid Build Coastguard Worker# 9: [ ID] Interval       Transfer     Bandwidth
34*387f9dfdSAndroid Build Coastguard Worker# 9: [  5]  0.0- 1.0 sec  3.80 GBytes  32.6 Gbits/sec
35*387f9dfdSAndroid Build Coastguard Worker# 9: Starting netserver with host 'IN(6)ADDR_ANY' port '12865' and family AF_UNSPEC
36*387f9dfdSAndroid Build Coastguard Worker# 9: MIGRATED TCP STREAM TEST from 0.0.0.0 (0.0.0.0) port 0 AF_INET to 200.1.1.1 (200.1.1.1) port 0 AF_INET : demo
37*387f9dfdSAndroid Build Coastguard Worker# 9: Recv   Send    Send
38*387f9dfdSAndroid Build Coastguard Worker# 9: Socket Socket  Message  Elapsed
39*387f9dfdSAndroid Build Coastguard Worker# 9: Size   Size    Size     Time     Throughput
40*387f9dfdSAndroid Build Coastguard Worker# 9: bytes  bytes   bytes    secs.    10^6bits/sec
41*387f9dfdSAndroid Build Coastguard Worker# 9:
42*387f9dfdSAndroid Build Coastguard Worker# 9:  87380  16384  65160    1.00     39940.46
43*387f9dfdSAndroid Build Coastguard Worker# 9: MIGRATED TCP REQUEST/RESPONSE TEST from 0.0.0.0 (0.0.0.0) port 0 AF_INET to 200.1.1.1 (200.1.1.1) port 0 AF_INET : demo : first burst 0
44*387f9dfdSAndroid Build Coastguard Worker# 9: Local /Remote
45*387f9dfdSAndroid Build Coastguard Worker# 9: Socket Size   Request  Resp.   Elapsed  Trans.
46*387f9dfdSAndroid Build Coastguard Worker# 9: Send   Recv   Size     Size    Time     Rate
47*387f9dfdSAndroid Build Coastguard Worker# 9: bytes  Bytes  bytes    bytes   secs.    per sec
48*387f9dfdSAndroid Build Coastguard Worker# 9:
49*387f9dfdSAndroid Build Coastguard Worker# 9: 16384  87380  1        1       1.00     46387.80
50*387f9dfdSAndroid Build Coastguard Worker# 9: 16384  87380
51*387f9dfdSAndroid Build Coastguard Worker# 9: .
52*387f9dfdSAndroid Build Coastguard Worker# 9: ----------------------------------------------------------------------
53*387f9dfdSAndroid Build Coastguard Worker# 9: Ran 1 test in 7.495s
54*387f9dfdSAndroid Build Coastguard Worker# 9:
55*387f9dfdSAndroid Build Coastguard Worker# 9: OK
56*387f9dfdSAndroid Build Coastguard Worker
57*387f9dfdSAndroid Build Coastguard Workerfrom ctypes import c_uint
58*387f9dfdSAndroid Build Coastguard Workerfrom bcc import BPF
59*387f9dfdSAndroid Build Coastguard Workerfrom pyroute2 import IPRoute, NetNS, IPDB, NSPopen
60*387f9dfdSAndroid Build Coastguard Workerfrom utils import NSPopenWithCheck, mayFail
61*387f9dfdSAndroid Build Coastguard Workerimport sys
62*387f9dfdSAndroid Build Coastguard Workerfrom time import sleep
63*387f9dfdSAndroid Build Coastguard Workerfrom unittest import main, TestCase
64*387f9dfdSAndroid Build Coastguard Workerimport subprocess
65*387f9dfdSAndroid Build Coastguard Workerfrom simulation import Simulation
66*387f9dfdSAndroid Build Coastguard Worker
67*387f9dfdSAndroid Build Coastguard Workerarg1 = sys.argv.pop(1)
68*387f9dfdSAndroid Build Coastguard Workeripr = IPRoute()
69*387f9dfdSAndroid Build Coastguard Workeripdb = IPDB(nl=ipr)
70*387f9dfdSAndroid Build Coastguard Workersim = Simulation(ipdb)
71*387f9dfdSAndroid Build Coastguard Worker
72*387f9dfdSAndroid Build Coastguard Workerallocated_interfaces = set(ipdb.interfaces.keys())
73*387f9dfdSAndroid Build Coastguard Worker
74*387f9dfdSAndroid Build Coastguard Workerdef get_next_iface(prefix):
75*387f9dfdSAndroid Build Coastguard Worker    i = 0
76*387f9dfdSAndroid Build Coastguard Worker    while True:
77*387f9dfdSAndroid Build Coastguard Worker        iface = "{0}{1}".format(prefix, i)
78*387f9dfdSAndroid Build Coastguard Worker        if iface not in allocated_interfaces:
79*387f9dfdSAndroid Build Coastguard Worker            allocated_interfaces.add(iface)
80*387f9dfdSAndroid Build Coastguard Worker            return iface
81*387f9dfdSAndroid Build Coastguard Worker        i += 1
82*387f9dfdSAndroid Build Coastguard Worker
83*387f9dfdSAndroid Build Coastguard Workerclass TestBPFSocket(TestCase):
84*387f9dfdSAndroid Build Coastguard Worker    def setup_br(self, br, veth_rt_2_br, veth_pem_2_br, veth_br_2_pem):
85*387f9dfdSAndroid Build Coastguard Worker        # create veth which connecting pem and br
86*387f9dfdSAndroid Build Coastguard Worker        with ipdb.create(ifname=veth_pem_2_br, kind="veth", peer=veth_br_2_pem) as v:
87*387f9dfdSAndroid Build Coastguard Worker            v.up()
88*387f9dfdSAndroid Build Coastguard Worker        ipdb.interfaces[veth_br_2_pem].up().commit()
89*387f9dfdSAndroid Build Coastguard Worker        subprocess.call(["sysctl", "-q", "-w", "net.ipv6.conf." + veth_pem_2_br + ".disable_ipv6=1"])
90*387f9dfdSAndroid Build Coastguard Worker        subprocess.call(["sysctl", "-q", "-w", "net.ipv6.conf." + veth_br_2_pem + ".disable_ipv6=1"])
91*387f9dfdSAndroid Build Coastguard Worker
92*387f9dfdSAndroid Build Coastguard Worker        # set up the bridge and add router interface as one of its slaves
93*387f9dfdSAndroid Build Coastguard Worker        with ipdb.create(ifname=br, kind="bridge") as br1:
94*387f9dfdSAndroid Build Coastguard Worker            br1.add_port(ipdb.interfaces[veth_pem_2_br])
95*387f9dfdSAndroid Build Coastguard Worker            br1.add_port(ipdb.interfaces[veth_rt_2_br])
96*387f9dfdSAndroid Build Coastguard Worker            br1.up()
97*387f9dfdSAndroid Build Coastguard Worker        subprocess.call(["sysctl", "-q", "-w", "net.ipv6.conf." + br + ".disable_ipv6=1"])
98*387f9dfdSAndroid Build Coastguard Worker
99*387f9dfdSAndroid Build Coastguard Worker    def set_default_const(self):
100*387f9dfdSAndroid Build Coastguard Worker        self.ns1            = "ns1"
101*387f9dfdSAndroid Build Coastguard Worker        self.ns2            = "ns2"
102*387f9dfdSAndroid Build Coastguard Worker        self.ns_router      = "ns_router"
103*387f9dfdSAndroid Build Coastguard Worker        self.br1            = get_next_iface("br")
104*387f9dfdSAndroid Build Coastguard Worker        self.veth_pem_2_br1 = "v20"
105*387f9dfdSAndroid Build Coastguard Worker        self.veth_br1_2_pem = "v21"
106*387f9dfdSAndroid Build Coastguard Worker        self.br2            = get_next_iface("br")
107*387f9dfdSAndroid Build Coastguard Worker        self.veth_pem_2_br2 = "v22"
108*387f9dfdSAndroid Build Coastguard Worker        self.veth_br2_2_pem = "v23"
109*387f9dfdSAndroid Build Coastguard Worker
110*387f9dfdSAndroid Build Coastguard Worker        self.vm1_ip         = "100.1.1.1"
111*387f9dfdSAndroid Build Coastguard Worker        self.vm2_ip         = "200.1.1.1"
112*387f9dfdSAndroid Build Coastguard Worker        self.vm1_rtr_ip     = "100.1.1.254"
113*387f9dfdSAndroid Build Coastguard Worker        self.vm2_rtr_ip     = "200.1.1.254"
114*387f9dfdSAndroid Build Coastguard Worker        self.vm1_rtr_mask   = "100.1.1.0/24"
115*387f9dfdSAndroid Build Coastguard Worker        self.vm2_rtr_mask   = "200.1.1.0/24"
116*387f9dfdSAndroid Build Coastguard Worker
117*387f9dfdSAndroid Build Coastguard Worker    def attach_filter(self, ifname, fd, name):
118*387f9dfdSAndroid Build Coastguard Worker        ifindex = ipdb.interfaces[ifname].index
119*387f9dfdSAndroid Build Coastguard Worker        ipr.tc("add", "ingress", ifindex, "ffff:")
120*387f9dfdSAndroid Build Coastguard Worker        ipr.tc("add-filter", "bpf", ifindex, ":1", fd=fd, name=name,
121*387f9dfdSAndroid Build Coastguard Worker              parent="ffff:", action="drop", classid=1)
122*387f9dfdSAndroid Build Coastguard Worker
123*387f9dfdSAndroid Build Coastguard Worker    def config_maps(self):
124*387f9dfdSAndroid Build Coastguard Worker        # pem just relays packets between VM and its corresponding
125*387f9dfdSAndroid Build Coastguard Worker        # slave link in the bridge interface
126*387f9dfdSAndroid Build Coastguard Worker        ns1_ifindex = self.ns1_eth_out.index
127*387f9dfdSAndroid Build Coastguard Worker        ns2_ifindex = self.ns2_eth_out.index
128*387f9dfdSAndroid Build Coastguard Worker        br1_ifindex = ipdb.interfaces[self.veth_br1_2_pem].index
129*387f9dfdSAndroid Build Coastguard Worker        br2_ifindex = ipdb.interfaces[self.veth_br2_2_pem].index
130*387f9dfdSAndroid Build Coastguard Worker        self.pem_dest[c_uint(ns1_ifindex)] = c_uint(br1_ifindex)
131*387f9dfdSAndroid Build Coastguard Worker        self.pem_dest[c_uint(br1_ifindex)] = c_uint(ns1_ifindex)
132*387f9dfdSAndroid Build Coastguard Worker        self.pem_dest[c_uint(ns2_ifindex)] = c_uint(br2_ifindex)
133*387f9dfdSAndroid Build Coastguard Worker        self.pem_dest[c_uint(br2_ifindex)] = c_uint(ns2_ifindex)
134*387f9dfdSAndroid Build Coastguard Worker
135*387f9dfdSAndroid Build Coastguard Worker        # tc filter setup with bpf programs attached
136*387f9dfdSAndroid Build Coastguard Worker        self.attach_filter(self.veth_br1_2_pem, self.pem_fn.fd, self.pem_fn.name)
137*387f9dfdSAndroid Build Coastguard Worker        self.attach_filter(self.veth_br2_2_pem, self.pem_fn.fd, self.pem_fn.name)
138*387f9dfdSAndroid Build Coastguard Worker
139*387f9dfdSAndroid Build Coastguard Worker    @mayFail("This fails on github actions environment, and needs to be fixed")
140*387f9dfdSAndroid Build Coastguard Worker    def test_brb2(self):
141*387f9dfdSAndroid Build Coastguard Worker        try:
142*387f9dfdSAndroid Build Coastguard Worker            b = BPF(src_file=arg1.encode(), debug=0)
143*387f9dfdSAndroid Build Coastguard Worker            self.pem_fn = b.load_func(b"pem", BPF.SCHED_CLS)
144*387f9dfdSAndroid Build Coastguard Worker            self.pem_dest= b.get_table(b"pem_dest")
145*387f9dfdSAndroid Build Coastguard Worker            self.pem_stats = b.get_table(b"pem_stats")
146*387f9dfdSAndroid Build Coastguard Worker
147*387f9dfdSAndroid Build Coastguard Worker            # set up the topology
148*387f9dfdSAndroid Build Coastguard Worker            self.set_default_const()
149*387f9dfdSAndroid Build Coastguard Worker            (ns1_ipdb, self.ns1_eth_out, _) = sim._create_ns(self.ns1, ipaddr=self.vm1_ip+'/24',
150*387f9dfdSAndroid Build Coastguard Worker                                                             fn=self.pem_fn, action='drop',
151*387f9dfdSAndroid Build Coastguard Worker                                                             disable_ipv6=True)
152*387f9dfdSAndroid Build Coastguard Worker            (ns2_ipdb, self.ns2_eth_out, _) = sim._create_ns(self.ns2, ipaddr=self.vm2_ip+'/24',
153*387f9dfdSAndroid Build Coastguard Worker                                                             fn=self.pem_fn, action='drop',
154*387f9dfdSAndroid Build Coastguard Worker                                                             disable_ipv6=True)
155*387f9dfdSAndroid Build Coastguard Worker            ns1_ipdb.routes.add({'dst': self.vm2_rtr_mask, 'gateway': self.vm1_rtr_ip}).commit()
156*387f9dfdSAndroid Build Coastguard Worker            ns2_ipdb.routes.add({'dst': self.vm1_rtr_mask, 'gateway': self.vm2_rtr_ip}).commit()
157*387f9dfdSAndroid Build Coastguard Worker
158*387f9dfdSAndroid Build Coastguard Worker            (_, self.nsrtr_eth0_out, _) = sim._create_ns(self.ns_router, ipaddr=self.vm1_rtr_ip+'/24',
159*387f9dfdSAndroid Build Coastguard Worker                                                         disable_ipv6=True)
160*387f9dfdSAndroid Build Coastguard Worker            (rt_ipdb, self.nsrtr_eth1_out, _) = sim._ns_add_ifc(self.ns_router, "eth1", "ns_router2",
161*387f9dfdSAndroid Build Coastguard Worker                                                                ipaddr=self.vm2_rtr_ip+'/24',
162*387f9dfdSAndroid Build Coastguard Worker                                                                disable_ipv6=True)
163*387f9dfdSAndroid Build Coastguard Worker            # enable ip forwarding in router ns
164*387f9dfdSAndroid Build Coastguard Worker            nsp = NSPopen(rt_ipdb.nl.netns, ["sysctl", "-w", "net.ipv4.ip_forward=1"])
165*387f9dfdSAndroid Build Coastguard Worker            nsp.wait(); nsp.release()
166*387f9dfdSAndroid Build Coastguard Worker
167*387f9dfdSAndroid Build Coastguard Worker            # for each VM connecting to pem, there will be a corresponding veth connecting to the bridge
168*387f9dfdSAndroid Build Coastguard Worker            self.setup_br(self.br1, self.nsrtr_eth0_out.ifname, self.veth_pem_2_br1, self.veth_br1_2_pem)
169*387f9dfdSAndroid Build Coastguard Worker            self.setup_br(self.br2, self.nsrtr_eth1_out.ifname, self.veth_pem_2_br2, self.veth_br2_2_pem)
170*387f9dfdSAndroid Build Coastguard Worker
171*387f9dfdSAndroid Build Coastguard Worker            # load the program and configure maps
172*387f9dfdSAndroid Build Coastguard Worker            self.config_maps()
173*387f9dfdSAndroid Build Coastguard Worker
174*387f9dfdSAndroid Build Coastguard Worker            # ping
175*387f9dfdSAndroid Build Coastguard Worker            nsp = NSPopen(ns1_ipdb.nl.netns, ["ping", self.vm2_ip, "-c", "2"]); nsp.wait(); nsp.release()
176*387f9dfdSAndroid Build Coastguard Worker            # one arp request/reply, 2 icmp request/reply per VM, total 6 packets per VM, 12 packets total
177*387f9dfdSAndroid Build Coastguard Worker            self.assertEqual(self.pem_stats[c_uint(0)].value, 12)
178*387f9dfdSAndroid Build Coastguard Worker
179*387f9dfdSAndroid Build Coastguard Worker            nsp_server = NSPopenWithCheck(ns2_ipdb.nl.netns, ["iperf", "-s", "-xSC"])
180*387f9dfdSAndroid Build Coastguard Worker            sleep(1)
181*387f9dfdSAndroid Build Coastguard Worker            nsp = NSPopen(ns1_ipdb.nl.netns, ["iperf", "-c", self.vm2_ip, "-t", "1", "-xSC"])
182*387f9dfdSAndroid Build Coastguard Worker            nsp.wait(); nsp.release()
183*387f9dfdSAndroid Build Coastguard Worker            nsp_server.kill(); nsp_server.wait(); nsp_server.release()
184*387f9dfdSAndroid Build Coastguard Worker
185*387f9dfdSAndroid Build Coastguard Worker            nsp_server = NSPopenWithCheck(ns2_ipdb.nl.netns, ["netserver", "-D"])
186*387f9dfdSAndroid Build Coastguard Worker            sleep(1)
187*387f9dfdSAndroid Build Coastguard Worker            nsp = NSPopenWithCheck(ns1_ipdb.nl.netns, ["netperf", "-l", "1", "-H", self.vm2_ip, "--", "-m", "65160"])
188*387f9dfdSAndroid Build Coastguard Worker            nsp.wait(); nsp.release()
189*387f9dfdSAndroid Build Coastguard Worker            nsp = NSPopen(ns1_ipdb.nl.netns, ["netperf", "-l", "1", "-H", self.vm2_ip, "-t", "TCP_RR"])
190*387f9dfdSAndroid Build Coastguard Worker            nsp.wait(); nsp.release()
191*387f9dfdSAndroid Build Coastguard Worker            nsp_server.kill(); nsp_server.wait(); nsp_server.release()
192*387f9dfdSAndroid Build Coastguard Worker
193*387f9dfdSAndroid Build Coastguard Worker        finally:
194*387f9dfdSAndroid Build Coastguard Worker            if self.br1 in ipdb.interfaces: ipdb.interfaces[self.br1].remove().commit()
195*387f9dfdSAndroid Build Coastguard Worker            if self.br2 in ipdb.interfaces: ipdb.interfaces[self.br2].remove().commit()
196*387f9dfdSAndroid Build Coastguard Worker            if self.veth_pem_2_br1 in ipdb.interfaces: ipdb.interfaces[self.veth_pem_2_br1].remove().commit()
197*387f9dfdSAndroid Build Coastguard Worker            if self.veth_pem_2_br2 in ipdb.interfaces: ipdb.interfaces[self.veth_pem_2_br2].remove().commit()
198*387f9dfdSAndroid Build Coastguard Worker            sim.release()
199*387f9dfdSAndroid Build Coastguard Worker            ipdb.release()
200*387f9dfdSAndroid Build Coastguard Worker
201*387f9dfdSAndroid Build Coastguard Worker
202*387f9dfdSAndroid Build Coastguard Workerif __name__ == "__main__":
203*387f9dfdSAndroid Build Coastguard Worker    main()
204