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