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 bridge is implemented with limited functionality in bpf program. 16*387f9dfdSAndroid Build Coastguard Worker# 17*387f9dfdSAndroid Build Coastguard Worker# vm1 and vm2 are in different subnet. For vm1 to communicate to vm2, 18*387f9dfdSAndroid Build Coastguard Worker# the packet will have to travel from vm1 to pem, bridge1, router, bridge2, pem, and 19*387f9dfdSAndroid Build Coastguard Worker# then come to vm2. 20*387f9dfdSAndroid Build Coastguard Worker# 21*387f9dfdSAndroid Build Coastguard Worker# When this test is run with verbose mode (ctest -R <test_name> -V), 22*387f9dfdSAndroid Build Coastguard Worker# the following printout is observed on my local box: 23*387f9dfdSAndroid Build Coastguard Worker# 24*387f9dfdSAndroid Build Coastguard Worker# ...... 25*387f9dfdSAndroid Build Coastguard Worker# 8: ARPING 100.1.1.254 from 100.1.1.1 eth0 26*387f9dfdSAndroid Build Coastguard Worker# 8: Unicast reply from 100.1.1.254 [76:62:B5:5C:8C:6F] 0.533ms 27*387f9dfdSAndroid Build Coastguard Worker# 8: Sent 1 probes (1 broadcast(s)) 28*387f9dfdSAndroid Build Coastguard Worker# 8: Received 1 response(s) 29*387f9dfdSAndroid Build Coastguard Worker# 8: ARPING 200.1.1.254 from 200.1.1.1 eth0 30*387f9dfdSAndroid Build Coastguard Worker# 8: Unicast reply from 200.1.1.254 [F2:F0:B4:ED:7B:1B] 0.524ms 31*387f9dfdSAndroid Build Coastguard Worker# 8: Sent 1 probes (1 broadcast(s)) 32*387f9dfdSAndroid Build Coastguard Worker# 8: Received 1 response(s) 33*387f9dfdSAndroid Build Coastguard Worker# 8: PING 200.1.1.1 (200.1.1.1) 56(84) bytes of data. 34*387f9dfdSAndroid Build Coastguard Worker# 8: 64 bytes from 200.1.1.1: icmp_req=1 ttl=63 time=0.074 ms 35*387f9dfdSAndroid Build Coastguard Worker# 8: 64 bytes from 200.1.1.1: icmp_req=2 ttl=63 time=0.061 ms 36*387f9dfdSAndroid Build Coastguard Worker# 8: 37*387f9dfdSAndroid Build Coastguard Worker# 8: --- 200.1.1.1 ping statistics --- 38*387f9dfdSAndroid Build Coastguard Worker# 8: 2 packets transmitted, 2 received, 0% packet loss, time 999ms 39*387f9dfdSAndroid Build Coastguard Worker# 8: rtt min/avg/max/mdev = 0.061/0.067/0.074/0.010 ms 40*387f9dfdSAndroid Build Coastguard Worker# 8: [ ID] Interval Transfer Bandwidth 41*387f9dfdSAndroid Build Coastguard Worker# 8: [ 5] 0.0- 1.0 sec 4.00 GBytes 34.3 Gbits/sec 42*387f9dfdSAndroid Build Coastguard Worker# 8: Starting netserver with host 'IN(6)ADDR_ANY' port '12865' and family AF_UNSPEC 43*387f9dfdSAndroid Build Coastguard Worker# 8: 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 44*387f9dfdSAndroid Build Coastguard Worker# 8: Recv Send Send 45*387f9dfdSAndroid Build Coastguard Worker# 8: Socket Socket Message Elapsed 46*387f9dfdSAndroid Build Coastguard Worker# 8: Size Size Size Time Throughput 47*387f9dfdSAndroid Build Coastguard Worker# 8: bytes bytes bytes secs. 10^6bits/sec 48*387f9dfdSAndroid Build Coastguard Worker# 8: 49*387f9dfdSAndroid Build Coastguard Worker# 8: 87380 16384 65160 1.00 41991.68 50*387f9dfdSAndroid Build Coastguard Worker# 8: 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 51*387f9dfdSAndroid Build Coastguard Worker# 8: Local /Remote 52*387f9dfdSAndroid Build Coastguard Worker# 8: Socket Size Request Resp. Elapsed Trans. 53*387f9dfdSAndroid Build Coastguard Worker# 8: Send Recv Size Size Time Rate 54*387f9dfdSAndroid Build Coastguard Worker# 8: bytes Bytes bytes bytes secs. per sec 55*387f9dfdSAndroid Build Coastguard Worker# 8: 56*387f9dfdSAndroid Build Coastguard Worker# 8: 16384 87380 1 1 1.00 48645.53 57*387f9dfdSAndroid Build Coastguard Worker# 8: 16384 87380 58*387f9dfdSAndroid Build Coastguard Worker# 8: . 59*387f9dfdSAndroid Build Coastguard Worker# 8: ---------------------------------------------------------------------- 60*387f9dfdSAndroid Build Coastguard Worker# 8: Ran 1 test in 11.296s 61*387f9dfdSAndroid Build Coastguard Worker# 8: 62*387f9dfdSAndroid Build Coastguard Worker# 8: OK 63*387f9dfdSAndroid Build Coastguard Worker 64*387f9dfdSAndroid Build Coastguard Workerfrom ctypes import c_uint 65*387f9dfdSAndroid Build Coastguard Workerfrom netaddr import IPAddress, EUI 66*387f9dfdSAndroid Build Coastguard Workerfrom bcc import BPF 67*387f9dfdSAndroid Build Coastguard Workerfrom pyroute2 import IPRoute, NetNS, IPDB, NSPopen 68*387f9dfdSAndroid Build Coastguard Workerfrom utils import NSPopenWithCheck, skipUnlessHasBinaries 69*387f9dfdSAndroid Build Coastguard Workerimport sys 70*387f9dfdSAndroid Build Coastguard Workerfrom time import sleep 71*387f9dfdSAndroid Build Coastguard Workerfrom unittest import main, TestCase 72*387f9dfdSAndroid Build Coastguard Workerfrom simulation import Simulation 73*387f9dfdSAndroid Build Coastguard Worker 74*387f9dfdSAndroid Build Coastguard Workerarg1 = sys.argv.pop(1) 75*387f9dfdSAndroid Build Coastguard Workeripr = IPRoute() 76*387f9dfdSAndroid Build Coastguard Workeripdb = IPDB(nl=ipr) 77*387f9dfdSAndroid Build Coastguard Workersim = Simulation(ipdb) 78*387f9dfdSAndroid Build Coastguard Worker 79*387f9dfdSAndroid Build Coastguard Workerclass TestBPFSocket(TestCase): 80*387f9dfdSAndroid Build Coastguard Worker def set_default_const(self): 81*387f9dfdSAndroid Build Coastguard Worker self.ns1 = "ns1" 82*387f9dfdSAndroid Build Coastguard Worker self.ns2 = "ns2" 83*387f9dfdSAndroid Build Coastguard Worker self.ns_router = "ns_router" 84*387f9dfdSAndroid Build Coastguard Worker self.vm1_ip = "100.1.1.1" 85*387f9dfdSAndroid Build Coastguard Worker self.vm2_ip = "200.1.1.1" 86*387f9dfdSAndroid Build Coastguard Worker self.vm1_rtr_ip = "100.1.1.254" 87*387f9dfdSAndroid Build Coastguard Worker self.vm2_rtr_ip = "200.1.1.254" 88*387f9dfdSAndroid Build Coastguard Worker self.vm1_rtr_mask = "100.1.1.0/24" 89*387f9dfdSAndroid Build Coastguard Worker self.vm2_rtr_mask = "200.1.1.0/24" 90*387f9dfdSAndroid Build Coastguard Worker 91*387f9dfdSAndroid Build Coastguard Worker def get_table(self, b): 92*387f9dfdSAndroid Build Coastguard Worker self.jump = b.get_table(b"jump") 93*387f9dfdSAndroid Build Coastguard Worker 94*387f9dfdSAndroid Build Coastguard Worker self.pem_dest = b.get_table(b"pem_dest") 95*387f9dfdSAndroid Build Coastguard Worker self.pem_port = b.get_table(b"pem_port") 96*387f9dfdSAndroid Build Coastguard Worker self.pem_ifindex = b.get_table(b"pem_ifindex") 97*387f9dfdSAndroid Build Coastguard Worker self.pem_stats = b.get_table(b"pem_stats") 98*387f9dfdSAndroid Build Coastguard Worker 99*387f9dfdSAndroid Build Coastguard Worker self.br1_dest = b.get_table(b"br1_dest") 100*387f9dfdSAndroid Build Coastguard Worker self.br1_mac = b.get_table(b"br1_mac") 101*387f9dfdSAndroid Build Coastguard Worker self.br1_rtr = b.get_table(b"br1_rtr") 102*387f9dfdSAndroid Build Coastguard Worker 103*387f9dfdSAndroid Build Coastguard Worker self.br2_dest = b.get_table(b"br2_dest") 104*387f9dfdSAndroid Build Coastguard Worker self.br2_mac = b.get_table(b"br2_mac") 105*387f9dfdSAndroid Build Coastguard Worker self.br2_rtr = b.get_table(b"br2_rtr") 106*387f9dfdSAndroid Build Coastguard Worker 107*387f9dfdSAndroid Build Coastguard Worker def connect_ports(self, prog_id_pem, prog_id_br, curr_pem_pid, curr_br_pid, 108*387f9dfdSAndroid Build Coastguard Worker br_dest_map, br_mac_map, ifindex, vm_mac, vm_ip): 109*387f9dfdSAndroid Build Coastguard Worker self.pem_dest[c_uint(curr_pem_pid)] = self.pem_dest.Leaf(prog_id_br, curr_br_pid) 110*387f9dfdSAndroid Build Coastguard Worker br_dest_map[c_uint(curr_br_pid)] = br_dest_map.Leaf(prog_id_pem, curr_pem_pid) 111*387f9dfdSAndroid Build Coastguard Worker self.pem_port[c_uint(curr_pem_pid)] = c_uint(ifindex) 112*387f9dfdSAndroid Build Coastguard Worker self.pem_ifindex[c_uint(ifindex)] = c_uint(curr_pem_pid) 113*387f9dfdSAndroid Build Coastguard Worker mac_addr = br_mac_map.Key(int(EUI(vm_mac))) 114*387f9dfdSAndroid Build Coastguard Worker br_mac_map[mac_addr] = c_uint(curr_br_pid) 115*387f9dfdSAndroid Build Coastguard Worker 116*387f9dfdSAndroid Build Coastguard Worker def config_maps(self): 117*387f9dfdSAndroid Build Coastguard Worker # program id 118*387f9dfdSAndroid Build Coastguard Worker prog_id_pem = 1 119*387f9dfdSAndroid Build Coastguard Worker prog_id_br1 = 2 120*387f9dfdSAndroid Build Coastguard Worker prog_id_br2 = 3 121*387f9dfdSAndroid Build Coastguard Worker 122*387f9dfdSAndroid Build Coastguard Worker # initial port id and table pointers 123*387f9dfdSAndroid Build Coastguard Worker curr_pem_pid = 0 124*387f9dfdSAndroid Build Coastguard Worker curr_br1_pid = 0 125*387f9dfdSAndroid Build Coastguard Worker curr_br2_pid = 0 126*387f9dfdSAndroid Build Coastguard Worker 127*387f9dfdSAndroid Build Coastguard Worker # configure jump table 128*387f9dfdSAndroid Build Coastguard Worker self.jump[c_uint(prog_id_pem)] = c_uint(self.pem_fn.fd) 129*387f9dfdSAndroid Build Coastguard Worker self.jump[c_uint(prog_id_br1)] = c_uint(self.br1_fn.fd) 130*387f9dfdSAndroid Build Coastguard Worker self.jump[c_uint(prog_id_br2)] = c_uint(self.br2_fn.fd) 131*387f9dfdSAndroid Build Coastguard Worker 132*387f9dfdSAndroid Build Coastguard Worker # connect pem and br1 133*387f9dfdSAndroid Build Coastguard Worker curr_pem_pid = curr_pem_pid + 1 134*387f9dfdSAndroid Build Coastguard Worker curr_br1_pid = curr_br1_pid + 1 135*387f9dfdSAndroid Build Coastguard Worker self.connect_ports(prog_id_pem, prog_id_br1, curr_pem_pid, curr_br1_pid, 136*387f9dfdSAndroid Build Coastguard Worker self.br1_dest, self.br1_mac, 137*387f9dfdSAndroid Build Coastguard Worker self.ns1_eth_out.index, self.vm1_mac, self.vm1_ip) 138*387f9dfdSAndroid Build Coastguard Worker 139*387f9dfdSAndroid Build Coastguard Worker # connect pem and br2 140*387f9dfdSAndroid Build Coastguard Worker curr_pem_pid = curr_pem_pid + 1 141*387f9dfdSAndroid Build Coastguard Worker curr_br2_pid = curr_br2_pid + 1 142*387f9dfdSAndroid Build Coastguard Worker self.connect_ports(prog_id_pem, prog_id_br2, curr_pem_pid, curr_br2_pid, 143*387f9dfdSAndroid Build Coastguard Worker self.br2_dest, self.br2_mac, 144*387f9dfdSAndroid Build Coastguard Worker self.ns2_eth_out.index, self.vm2_mac, self.vm2_ip) 145*387f9dfdSAndroid Build Coastguard Worker 146*387f9dfdSAndroid Build Coastguard Worker # connect <br1, rtr> and <br2, rtr> 147*387f9dfdSAndroid Build Coastguard Worker self.br1_rtr[c_uint(0)] = c_uint(self.nsrtr_eth0_out.index) 148*387f9dfdSAndroid Build Coastguard Worker self.br2_rtr[c_uint(0)] = c_uint(self.nsrtr_eth1_out.index) 149*387f9dfdSAndroid Build Coastguard Worker 150*387f9dfdSAndroid Build Coastguard Worker @skipUnlessHasBinaries( 151*387f9dfdSAndroid Build Coastguard Worker ["arping", "iperf", "netperf", "netserver", "ping"], 152*387f9dfdSAndroid Build Coastguard Worker "iperf and netperf packages must be installed.") 153*387f9dfdSAndroid Build Coastguard Worker def test_brb(self): 154*387f9dfdSAndroid Build Coastguard Worker try: 155*387f9dfdSAndroid Build Coastguard Worker b = BPF(src_file=arg1.encode(), debug=0) 156*387f9dfdSAndroid Build Coastguard Worker self.pem_fn = b.load_func(b"pem", BPF.SCHED_CLS) 157*387f9dfdSAndroid Build Coastguard Worker self.br1_fn = b.load_func(b"br1", BPF.SCHED_CLS) 158*387f9dfdSAndroid Build Coastguard Worker self.br2_fn = b.load_func(b"br2", BPF.SCHED_CLS) 159*387f9dfdSAndroid Build Coastguard Worker self.get_table(b) 160*387f9dfdSAndroid Build Coastguard Worker 161*387f9dfdSAndroid Build Coastguard Worker # set up the topology 162*387f9dfdSAndroid Build Coastguard Worker self.set_default_const() 163*387f9dfdSAndroid Build Coastguard Worker (ns1_ipdb, self.ns1_eth_out, _) = sim._create_ns(self.ns1, ipaddr=self.vm1_ip+'/24', 164*387f9dfdSAndroid Build Coastguard Worker fn=self.pem_fn, action='drop', 165*387f9dfdSAndroid Build Coastguard Worker disable_ipv6=True) 166*387f9dfdSAndroid Build Coastguard Worker (ns2_ipdb, self.ns2_eth_out, _) = sim._create_ns(self.ns2, ipaddr=self.vm2_ip+'/24', 167*387f9dfdSAndroid Build Coastguard Worker fn=self.pem_fn, action='drop', 168*387f9dfdSAndroid Build Coastguard Worker disable_ipv6=True) 169*387f9dfdSAndroid Build Coastguard Worker ns1_ipdb.routes.add({'dst': self.vm2_rtr_mask, 'gateway': self.vm1_rtr_ip}).commit() 170*387f9dfdSAndroid Build Coastguard Worker ns2_ipdb.routes.add({'dst': self.vm1_rtr_mask, 'gateway': self.vm2_rtr_ip}).commit() 171*387f9dfdSAndroid Build Coastguard Worker self.vm1_mac = ns1_ipdb.interfaces['eth0'].address 172*387f9dfdSAndroid Build Coastguard Worker self.vm2_mac = ns2_ipdb.interfaces['eth0'].address 173*387f9dfdSAndroid Build Coastguard Worker 174*387f9dfdSAndroid Build Coastguard Worker (_, self.nsrtr_eth0_out, _) = sim._create_ns(self.ns_router, ipaddr=self.vm1_rtr_ip+'/24', 175*387f9dfdSAndroid Build Coastguard Worker fn=self.br1_fn, action='drop', 176*387f9dfdSAndroid Build Coastguard Worker disable_ipv6=True) 177*387f9dfdSAndroid Build Coastguard Worker (rt_ipdb, self.nsrtr_eth1_out, _) = sim._ns_add_ifc(self.ns_router, "eth1", "ns_router2", 178*387f9dfdSAndroid Build Coastguard Worker ipaddr=self.vm2_rtr_ip+'/24', 179*387f9dfdSAndroid Build Coastguard Worker fn=self.br2_fn, action='drop', 180*387f9dfdSAndroid Build Coastguard Worker disable_ipv6=True) 181*387f9dfdSAndroid Build Coastguard Worker nsp = NSPopen(rt_ipdb.nl.netns, ["sysctl", "-w", "net.ipv4.ip_forward=1"]) 182*387f9dfdSAndroid Build Coastguard Worker nsp.wait(); nsp.release() 183*387f9dfdSAndroid Build Coastguard Worker 184*387f9dfdSAndroid Build Coastguard Worker # configure maps 185*387f9dfdSAndroid Build Coastguard Worker self.config_maps() 186*387f9dfdSAndroid Build Coastguard Worker 187*387f9dfdSAndroid Build Coastguard Worker # our bridge is not smart enough, so send arping for router learning to prevent router 188*387f9dfdSAndroid Build Coastguard Worker # from sending out arp request 189*387f9dfdSAndroid Build Coastguard Worker nsp = NSPopen(ns1_ipdb.nl.netns, 190*387f9dfdSAndroid Build Coastguard Worker ["arping", "-w", "1", "-c", "1", "-I", "eth0", self.vm1_rtr_ip]) 191*387f9dfdSAndroid Build Coastguard Worker nsp.wait(); nsp.release() 192*387f9dfdSAndroid Build Coastguard Worker nsp = NSPopen(ns2_ipdb.nl.netns, 193*387f9dfdSAndroid Build Coastguard Worker ["arping", "-w", "1", "-c", "1", "-I", "eth0", self.vm2_rtr_ip]) 194*387f9dfdSAndroid Build Coastguard Worker nsp.wait(); nsp.release() 195*387f9dfdSAndroid Build Coastguard Worker 196*387f9dfdSAndroid Build Coastguard Worker # ping 197*387f9dfdSAndroid Build Coastguard Worker nsp = NSPopen(ns1_ipdb.nl.netns, ["ping", self.vm2_ip, "-c", "2"]) 198*387f9dfdSAndroid Build Coastguard Worker nsp.wait(); nsp.release() 199*387f9dfdSAndroid Build Coastguard Worker # pem_stats only counts pem->bridge traffic, each VM has 4: arping/arp request/2 icmp request 200*387f9dfdSAndroid Build Coastguard Worker # total 8 packets should be counted 201*387f9dfdSAndroid Build Coastguard Worker self.assertEqual(self.pem_stats[c_uint(0)].value, 8) 202*387f9dfdSAndroid Build Coastguard Worker 203*387f9dfdSAndroid Build Coastguard Worker nsp_server = NSPopenWithCheck(ns2_ipdb.nl.netns, ["iperf", "-s", "-xSC"]) 204*387f9dfdSAndroid Build Coastguard Worker sleep(1) 205*387f9dfdSAndroid Build Coastguard Worker nsp = NSPopen(ns1_ipdb.nl.netns, ["iperf", "-c", self.vm2_ip, "-t", "1", "-xSC"]) 206*387f9dfdSAndroid Build Coastguard Worker nsp.wait(); nsp.release() 207*387f9dfdSAndroid Build Coastguard Worker nsp_server.kill(); nsp_server.wait(); nsp_server.release() 208*387f9dfdSAndroid Build Coastguard Worker 209*387f9dfdSAndroid Build Coastguard Worker nsp_server = NSPopenWithCheck(ns2_ipdb.nl.netns, ["netserver", "-D"]) 210*387f9dfdSAndroid Build Coastguard Worker sleep(1) 211*387f9dfdSAndroid Build Coastguard Worker nsp = NSPopenWithCheck(ns1_ipdb.nl.netns, ["netperf", "-l", "1", "-H", self.vm2_ip, "--", "-m", "65160"]) 212*387f9dfdSAndroid Build Coastguard Worker nsp.wait(); nsp.release() 213*387f9dfdSAndroid Build Coastguard Worker nsp = NSPopen(ns1_ipdb.nl.netns, ["netperf", "-l", "1", "-H", self.vm2_ip, "-t", "TCP_RR"]) 214*387f9dfdSAndroid Build Coastguard Worker nsp.wait(); nsp.release() 215*387f9dfdSAndroid Build Coastguard Worker nsp_server.kill(); nsp_server.wait(); nsp_server.release() 216*387f9dfdSAndroid Build Coastguard Worker 217*387f9dfdSAndroid Build Coastguard Worker finally: 218*387f9dfdSAndroid Build Coastguard Worker sim.release() 219*387f9dfdSAndroid Build Coastguard Worker ipdb.release() 220*387f9dfdSAndroid Build Coastguard Worker 221*387f9dfdSAndroid Build Coastguard Worker 222*387f9dfdSAndroid Build Coastguard Workerif __name__ == "__main__": 223*387f9dfdSAndroid Build Coastguard Worker main() 224