Commit 485af951 authored by Yonghong Song's avatar Yonghong Song

rewrite test_brb/test_brb2 python binding with pyroute2

Signed-off-by: default avatarYonghong Song <yhs@plumgrid.com>
parent 3a898644
import atexit import subprocess
from pyroute2 import IPRoute, NetNS, IPDB, NSPopen from pyroute2 import IPRoute, NetNS, IPDB, NSPopen
class Simulation(object): class Simulation(object):
...@@ -14,15 +14,18 @@ class Simulation(object): ...@@ -14,15 +14,18 @@ class Simulation(object):
self.processes = [] self.processes = []
self.released = False self.released = False
# helper function to create a namespace and a veth connecting it # helper function to add additional ifc to namespace
def _create_ns(self, name, in_ifc=None, out_ifc=None, ipaddr=None, def _ns_add_ifc(self, name, ns_ifc, in_ifc=None, out_ifc=None, ipaddr=None,
macaddr=None, fn=None, cmd=None, action="ok"): macaddr=None, fn=None, cmd=None, action="ok", disable_ipv6=False):
ns_ipdb = IPDB(nl=NetNS(name)) if name in self.ipdbs:
ns_ipdb = self.ipdbs[name]
else:
ns_ipdb = IPDB(nl=NetNS(name))
if in_ifc: if in_ifc:
in_ifname = in_ifc.ifname in_ifname = in_ifc.ifname
else: else:
out_ifc = self.ipdb.create(ifname="%sa" % name, kind="veth", out_ifc = self.ipdb.create(ifname="%s%sa" % (name, ns_ifc), kind="veth",
peer="%sb" % name).commit() peer="%s%sb" % (name, ns_ifc)).commit()
in_ifc = self.ipdb.interfaces[out_ifc.peer] in_ifc = self.ipdb.interfaces[out_ifc.peer]
in_ifname = in_ifc.ifname in_ifname = in_ifc.ifname
with in_ifc as v: with in_ifc as v:
...@@ -31,21 +34,33 @@ class Simulation(object): ...@@ -31,21 +34,33 @@ class Simulation(object):
in_ifc = ns_ipdb.interfaces[in_ifname] in_ifc = ns_ipdb.interfaces[in_ifname]
if out_ifc: out_ifc.up().commit() if out_ifc: out_ifc.up().commit()
with in_ifc as v: with in_ifc as v:
v.ifname = "eth0" v.ifname = ns_ifc
if ipaddr: v.add_ip("%s" % ipaddr) if ipaddr: v.add_ip("%s" % ipaddr)
if macaddr: v.address = macaddr if macaddr: v.address = macaddr
v.up() v.up()
# if required, disable ipv6 before attaching the filter
if disable_ipv6:
subprocess.call(["sysctl", "-q", "-w", "net.ipv6.conf." + out_ifc.ifname+ ".disable_ipv6=1"])
nsp = NSPopen(ns_ipdb.nl.netns, ["sysctl", "-q", "-w", "net.ipv6.conf." + ns_ifc + ".disable_ipv6=1"])
nsp.wait(); nsp.release()
if fn and out_ifc: if fn and out_ifc:
self.ipdb.nl.tc("add", "ingress", out_ifc["index"], "ffff:") self.ipdb.nl.tc("add", "ingress", out_ifc["index"], "ffff:")
self.ipdb.nl.tc("add-filter", "bpf", out_ifc["index"], ":1", self.ipdb.nl.tc("add-filter", "bpf", out_ifc["index"], ":1",
fd=fn.fd, name=fn.name, parent="ffff:", fd=fn.fd, name=fn.name, parent="ffff:",
action=action, classid=1) action=action, classid=1)
self.ipdbs[ns_ipdb.nl.netns] = ns_ipdb
self.namespaces.append(ns_ipdb.nl)
if cmd: if cmd:
self.processes.append(NSPopen(ns_ipdb.nl.netns, cmd)) self.processes.append(NSPopen(ns_ipdb.nl.netns, cmd))
return (ns_ipdb, out_ifc, in_ifc) return (ns_ipdb, out_ifc, in_ifc)
# helper function to create a namespace and a veth connecting it
def _create_ns(self, name, in_ifc=None, out_ifc=None, ipaddr=None,
macaddr=None, fn=None, cmd=None, action="ok", disable_ipv6=False):
(ns_ipdb, out_ifc, in_ifc) = self._ns_add_ifc(name, "eth0", in_ifc, out_ifc, ipaddr,
macaddr, fn, cmd, action, disable_ipv6)
self.ipdbs[ns_ipdb.nl.netns] = ns_ipdb
self.namespaces.append(ns_ipdb.nl)
return (ns_ipdb, out_ifc, in_ifc)
def release(self): def release(self):
if self.released: return if self.released: return
self.released = True self.released = True
......
...@@ -91,7 +91,7 @@ class VlanSimulation(Simulation): ...@@ -91,7 +91,7 @@ class VlanSimulation(Simulation):
((i >> 16) & 0xff, (i >> 8) & 0xff, i & 0xff)) ((i >> 16) & 0xff, (i >> 8) & 0xff, i & 0xff))
# assign this client to the given worker # assign this client to the given worker
idx = self.ipdb.interfaces["worker%da" % i]["index"] idx = self.ipdb.interfaces["worker%deth0a" % i]["index"]
mac = int(macaddr.replace(":", ""), 16) mac = int(macaddr.replace(":", ""), 16)
ingress[ingress.Key(mac)] = ingress.Leaf(idx, 0, 0) ingress[ingress.Key(mac)] = ingress.Leaf(idx, 0, 0)
......
../../examples/simulation.py
\ No newline at end of file
...@@ -61,74 +61,31 @@ ...@@ -61,74 +61,31 @@
# 8: # 8:
# 8: OK # 8: OK
from ctypes import c_ubyte, c_ushort, c_uint, c_ulonglong, Structure from ctypes import c_uint
from netaddr import IPAddress, EUI from netaddr import IPAddress, EUI
from bpf import BPF from bpf import BPF
from pyroute2 import IPRoute from pyroute2 import IPRoute, NetNS, IPDB, NSPopen
import sys import sys
from time import sleep from time import sleep
from unittest import main, TestCase from unittest import main, TestCase
import subprocess from simulation import Simulation
import struct
arg1 = sys.argv.pop(1) arg1 = sys.argv.pop(1)
ipr = IPRoute()
ipdb = IPDB(nl=ipr)
sim = Simulation(ipdb)
class TestBPFSocket(TestCase): class TestBPFSocket(TestCase):
def setup_vm_ns(self, ns, veth_in, veth_out):
subprocess.call(["ip", "link", "add", veth_in, "type", "veth", "peer", "name", veth_out])
subprocess.call(["ip", "netns", "add", ns])
subprocess.call(["ip", "link", "set", veth_in, "netns", ns])
subprocess.call(["ip", "netns", "exec", ns, "ip", "link", "set", veth_in, "name", "eth0"])
subprocess.call(["sysctl", "-q", "-w", "net.ipv6.conf." + veth_out+ ".disable_ipv6=1"])
subprocess.call(["ip", "link", "set", veth_out, "up"])
def config_vm_ns(self, ns, ip_addr, net_mask, ip_gw):
subprocess.call(["ip", "netns", "exec", ns, "ip", "addr", "add", ip_addr + "/24", "dev", "eth0"])
subprocess.call(["ip", "netns", "exec", ns, "sysctl", "-q", "-w", "net.ipv6.conf.eth0.disable_ipv6=1"])
subprocess.call(["ip", "netns", "exec", ns, "ip", "link", "set", "eth0", "up"])
subprocess.call(["ip", "netns", "exec", ns, "ip", "route", "add", net_mask + "/24", "via", ip_gw])
def setup_router_ns(self, ns, veth1_in, veth1_out, veth2_in, veth2_out):
subprocess.call(["ip", "netns", "add", ns])
subprocess.call(["ip", "link", "add", veth1_in, "type", "veth", "peer", "name", veth1_out])
subprocess.call(["ip", "link", "set", veth1_in, "netns", ns])
subprocess.call(["ip", "netns", "exec", ns, "ip", "link", "set", veth1_in, "name", "eth0"])
subprocess.call(["ip", "link", "add", veth2_in, "type", "veth", "peer", "name", veth2_out])
subprocess.call(["ip", "link", "set", veth2_in, "netns", ns])
subprocess.call(["ip", "netns", "exec", ns, "ip", "link", "set", veth2_in, "name", "eth1"])
subprocess.call(["sysctl", "-q", "-w", "net.ipv6.conf." + veth1_out+ ".disable_ipv6=1"])
subprocess.call(["sysctl", "-q", "-w", "net.ipv6.conf." + veth2_out+ ".disable_ipv6=1"])
subprocess.call(["ip", "link", "set", veth1_out, "up"])
subprocess.call(["ip", "link", "set", veth2_out, "up"])
def config_router_ns(self, ns, ip_eth0, ip_eth1):
subprocess.call(["ip", "netns", "exec", ns, "ip", "addr", "add", ip_eth0 + "/24", "dev", "eth0"])
subprocess.call(["ip", "netns", "exec", ns, "sysctl", "-q", "-w", "net.ipv6.conf.eth0.disable_ipv6=1"])
subprocess.call(["ip", "netns", "exec", ns, "ip", "link", "set", "eth0", "up"])
subprocess.call(["ip", "netns", "exec", ns, "ip", "addr", "add", ip_eth1 + "/24", "dev", "eth1"])
subprocess.call(["ip", "netns", "exec", ns, "sysctl", "-q", "-w", "net.ipv6.conf.eth1.disable_ipv6=1"])
subprocess.call(["ip", "netns", "exec", ns, "ip", "link", "set", "eth1", "up"])
subprocess.call(["ip", "netns", "exec", ns, "sysctl", "-w", "net.ipv4.ip_forward=1"])
def set_default_const(self): def set_default_const(self):
self.ns1 = "ns1" self.ns1 = "ns1"
self.ns1_eth_in = "v1"
self.ns1_eth_out = "v2"
self.ns2 = "ns2" self.ns2 = "ns2"
self.ns2_eth_in = "v3"
self.ns2_eth_out = "v4"
self.ns_router = "ns_router" self.ns_router = "ns_router"
self.nsrtr_eth0_in = "v10"
self.nsrtr_eth0_out = "v11"
self.nsrtr_eth1_in = "v12"
self.nsrtr_eth1_out = "v13"
self.vm1_ip = "100.1.1.1" self.vm1_ip = "100.1.1.1"
self.vm2_ip = "200.1.1.1" self.vm2_ip = "200.1.1.1"
self.vm1_rtr_ip = "100.1.1.254" self.vm1_rtr_ip = "100.1.1.254"
self.vm2_rtr_ip = "200.1.1.254" self.vm2_rtr_ip = "200.1.1.254"
self.vm1_rtr_mask = "100.1.1.0" self.vm1_rtr_mask = "100.1.1.0/24"
self.vm2_rtr_mask = "200.1.1.0" self.vm2_rtr_mask = "200.1.1.0/24"
def get_table(self, b): def get_table(self, b):
self.jump = b.get_table("jump") self.jump = b.get_table("jump")
...@@ -147,29 +104,15 @@ class TestBPFSocket(TestCase): ...@@ -147,29 +104,15 @@ class TestBPFSocket(TestCase):
self.br2_rtr = b.get_table("br2_rtr") self.br2_rtr = b.get_table("br2_rtr")
def connect_ports(self, prog_id_pem, prog_id_br, curr_pem_pid, curr_br_pid, def connect_ports(self, prog_id_pem, prog_id_br, curr_pem_pid, curr_br_pid,
ip, br_dest_map, br_mac_map, br_dest_map, br_mac_map, ifindex, vm_mac, vm_ip):
ns_eth_out, vm_mac, vm_ip):
self.pem_dest[c_uint(curr_pem_pid)] = self.pem_dest.Leaf(prog_id_br, curr_br_pid) self.pem_dest[c_uint(curr_pem_pid)] = self.pem_dest.Leaf(prog_id_br, curr_br_pid)
br_dest_map[c_uint(curr_br_pid)] = br_dest_map.Leaf(prog_id_pem, curr_pem_pid) br_dest_map[c_uint(curr_br_pid)] = br_dest_map.Leaf(prog_id_pem, curr_pem_pid)
ifindex = ip.link_lookup(ifname=ns_eth_out)[0]
self.pem_port[c_uint(curr_pem_pid)] = c_uint(ifindex) self.pem_port[c_uint(curr_pem_pid)] = c_uint(ifindex)
self.pem_ifindex[c_uint(ifindex)] = c_uint(curr_pem_pid) self.pem_ifindex[c_uint(ifindex)] = c_uint(curr_pem_pid)
mac_addr = br_mac_map.Key(int(EUI(vm_mac.decode()))) mac_addr = br_mac_map.Key(int(EUI(vm_mac.decode())))
br_mac_map[mac_addr] = c_uint(curr_br_pid) br_mac_map[mac_addr] = c_uint(curr_br_pid)
def attach_filter(self, ip, ifname, fd, name):
ifindex = ip.link_lookup(ifname=ifname)[0]
ip.tc("add", "ingress", ifindex, "ffff:")
ip.tc("add-filter", "bpf", ifindex, ":1", fd=fd, name=name,
parent="ffff:", action="drop", classid=1)
def config_maps(self): def config_maps(self):
b = BPF(src_file=arg1, debug=0)
pem_fn = b.load_func("pem", BPF.SCHED_CLS)
br1_fn = b.load_func("br1", BPF.SCHED_CLS)
br2_fn = b.load_func("br2", BPF.SCHED_CLS)
ip = IPRoute()
# program id # program id
prog_id_pem = 1 prog_id_pem = 1
prog_id_br1 = 2 prog_id_br1 = 2
...@@ -179,91 +122,91 @@ class TestBPFSocket(TestCase): ...@@ -179,91 +122,91 @@ class TestBPFSocket(TestCase):
curr_pem_pid = 0 curr_pem_pid = 0
curr_br1_pid = 0 curr_br1_pid = 0
curr_br2_pid = 0 curr_br2_pid = 0
self.get_table(b)
# configure jump table # configure jump table
self.jump[c_uint(prog_id_pem)] = c_uint(pem_fn.fd) self.jump[c_uint(prog_id_pem)] = c_uint(self.pem_fn.fd)
self.jump[c_uint(prog_id_br1)] = c_uint(br1_fn.fd) self.jump[c_uint(prog_id_br1)] = c_uint(self.br1_fn.fd)
self.jump[c_uint(prog_id_br2)] = c_uint(br2_fn.fd) self.jump[c_uint(prog_id_br2)] = c_uint(self.br2_fn.fd)
# connect pem and br1 # connect pem and br1
curr_pem_pid = curr_pem_pid + 1 curr_pem_pid = curr_pem_pid + 1
curr_br1_pid = curr_br1_pid + 1 curr_br1_pid = curr_br1_pid + 1
self.connect_ports(prog_id_pem, prog_id_br1, curr_pem_pid, curr_br1_pid, self.connect_ports(prog_id_pem, prog_id_br1, curr_pem_pid, curr_br1_pid,
ip, self.br1_dest, self.br1_mac, self.br1_dest, self.br1_mac,
self.ns1_eth_out, self.vm1_mac, self.vm1_ip) self.ns1_eth_out.index, self.vm1_mac, self.vm1_ip)
# connect pem and br2 # connect pem and br2
curr_pem_pid = curr_pem_pid + 1 curr_pem_pid = curr_pem_pid + 1
curr_br2_pid = curr_br2_pid + 1 curr_br2_pid = curr_br2_pid + 1
self.connect_ports(prog_id_pem, prog_id_br2, curr_pem_pid, curr_br2_pid, self.connect_ports(prog_id_pem, prog_id_br2, curr_pem_pid, curr_br2_pid,
ip, self.br2_dest, self.br2_mac, self.br2_dest, self.br2_mac,
self.ns2_eth_out, self.vm2_mac, self.vm2_ip) self.ns2_eth_out.index, self.vm2_mac, self.vm2_ip)
# connect <br1, rtr> and <br2, rtr> # connect <br1, rtr> and <br2, rtr>
ifindex = ip.link_lookup(ifname=self.nsrtr_eth0_out)[0] self.br1_rtr[c_uint(0)] = c_uint(self.nsrtr_eth0_out.index)
self.br1_rtr[c_uint(0)] = c_uint(ifindex) self.br2_rtr[c_uint(0)] = c_uint(self.nsrtr_eth1_out.index)
ifindex = ip.link_lookup(ifname=self.nsrtr_eth1_out)[0]
self.br2_rtr[c_uint(0)] = c_uint(ifindex)
# tc filter setup with bpf programs attached
self.attach_filter(ip, self.ns1_eth_out, pem_fn.fd, pem_fn.name)
self.attach_filter(ip, self.ns2_eth_out, pem_fn.fd, pem_fn.name)
self.attach_filter(ip, self.nsrtr_eth0_out, br1_fn.fd, br1_fn.name)
self.attach_filter(ip, self.nsrtr_eth1_out, br2_fn.fd, br2_fn.name)
def test_brb(self): def test_brb(self):
try: try:
# set up the environment b = BPF(src_file=arg1, debug=0)
self.set_default_const() self.pem_fn = b.load_func("pem", BPF.SCHED_CLS)
self.setup_vm_ns(self.ns1, self.ns1_eth_in, self.ns1_eth_out) self.br1_fn = b.load_func("br1", BPF.SCHED_CLS)
self.setup_vm_ns(self.ns2, self.ns2_eth_in, self.ns2_eth_out) self.br2_fn = b.load_func("br2", BPF.SCHED_CLS)
self.config_vm_ns(self.ns1, self.vm1_ip, self.vm2_rtr_mask, self.vm1_rtr_ip) self.get_table(b)
self.config_vm_ns(self.ns2, self.vm2_ip, self.vm1_rtr_mask, self.vm2_rtr_ip)
self.setup_router_ns(self.ns_router, self.nsrtr_eth0_in, self.nsrtr_eth0_out,
self.nsrtr_eth1_in, self.nsrtr_eth1_out)
self.config_router_ns(self.ns_router, self.vm1_rtr_ip, self.vm2_rtr_ip)
# get vm mac address # set up the topology
self.vm1_mac = subprocess.check_output(["ip", "netns", "exec", self.ns1, "cat", "/sys/class/net/eth0/address"]) self.set_default_const()
self.vm1_mac = self.vm1_mac.strip() (ns1_ipdb, self.ns1_eth_out, unused) = sim._create_ns(self.ns1, ipaddr=self.vm1_ip+'/24', fn=self.pem_fn, action='drop',
self.vm2_mac = subprocess.check_output(["ip", "netns", "exec", self.ns2, "cat", "/sys/class/net/eth0/address"]) disable_ipv6=True)
self.vm2_mac = self.vm2_mac.strip() (ns2_ipdb, self.ns2_eth_out, unused) = sim._create_ns(self.ns2, ipaddr=self.vm2_ip+'/24', fn=self.pem_fn, action='drop',
disable_ipv6=True)
# load the program and configure maps ns1_ipdb.routes.add({'dst': self.vm2_rtr_mask, 'gateway': self.vm1_rtr_ip}).commit()
ns2_ipdb.routes.add({'dst': self.vm1_rtr_mask, 'gateway': self.vm2_rtr_ip}).commit()
self.vm1_mac = ns1_ipdb.interfaces['eth0'].address
self.vm2_mac = ns2_ipdb.interfaces['eth0'].address
(rt_ipdb, self.nsrtr_eth0_out, unused) = sim._create_ns(self.ns_router, ipaddr=self.vm1_rtr_ip+'/24', fn=self.br1_fn,
action='drop', disable_ipv6=True)
(rt_ipdb, self.nsrtr_eth1_out, unused) = sim._ns_add_ifc(self.ns_router, "eth1", ipaddr=self.vm2_rtr_ip+'/24',
fn=self.br2_fn, action='drop', disable_ipv6=True)
nsp = NSPopen(rt_ipdb.nl.netns, ["sysctl", "-w", "net.ipv4.ip_forward=1"]); nsp.wait(); nsp.release()
# configure maps
self.config_maps() self.config_maps()
# our bridge is not smart enough, so send arping for router learning to prevent router # our bridge is not smart enough, so send arping for router learning to prevent router
# from sending out arp request # from sending out arp request
subprocess.call(["ip", "netns", "exec", self.ns1, "arping", "-w", "1", "-c", "1", "-I", "eth0", nsp = NSPopen(ns1_ipdb.nl.netns, ["arping", "-w", "1", "-c", "1", "-I", "eth0", self.vm1_rtr_ip]); nsp.wait(); nsp.release()
self.vm1_rtr_ip]) nsp = NSPopen(ns2_ipdb.nl.netns, ["arping", "-w", "1", "-c", "1", "-I", "eth0", self.vm2_rtr_ip]); nsp.wait(); nsp.release()
subprocess.call(["ip", "netns", "exec", self.ns2, "arping", "-w", "1", "-c", "1", "-I", "eth0",
self.vm2_rtr_ip])
# ping # ping
subprocess.call(["ip", "netns", "exec", self.ns1, "ping", self.vm2_ip, "-c", "2"]) nsp = NSPopen(ns1_ipdb.nl.netns, ["ping", self.vm2_ip, "-c", "2"]); nsp.wait(); nsp.release()
# pem_stats only counts pem->bridge traffic, each VM has 4: arping/arp request/2 icmp request # pem_stats only counts pem->bridge traffic, each VM has 4: arping/arp request/2 icmp request
# total 8 packets should be counted # total 8 packets should be counted
self.assertEqual(self.pem_stats[c_uint(0)].value, 8) self.assertEqual(self.pem_stats[c_uint(0)].value, 8)
# iperf, run server on the background # iperf, run server on the background
subprocess.Popen(["ip", "netns", "exec", self.ns2, "iperf", "-s", "-xSCD"]) nsp_server = NSPopen(ns2_ipdb.nl.netns, ["iperf", "-s", "-xSCD"])
sleep(1) sleep(1)
subprocess.call(["ip", "netns", "exec", self.ns1, "iperf", "-c", self.vm2_ip, "-t", "1", "-xSC"]) nsp = NSPopen(ns1_ipdb.nl.netns, ["iperf", "-c", self.vm2_ip, "-t", "1", "-xSC"]); nsp.wait(); nsp.release()
subprocess.call(["ip", "netns", "exec", self.ns2, "killall", "iperf"]) nsp_server.kill(); nsp_server.wait(); nsp.release()
# netperf, run server on the background # netperf, run server on the background
subprocess.Popen(["ip", "netns", "exec", self.ns2, "netserver"]) nsp_server = NSPopen(ns2_ipdb.nl.netns, ["netserver"])
sleep(1) sleep(1)
subprocess.call(["ip", "netns", "exec", self.ns1, "netperf", "-l", "1", "-H", self.vm2_ip, "--", "-m", "65160"]) nsp = NSPopen(ns1_ipdb.nl.netns, ["netperf", "-l", "1", "-H", self.vm2_ip, "--", "-m", "65160"]); nsp.wait(); nsp.release()
subprocess.call(["ip", "netns", "exec", self.ns1, "netperf", "-l", "1", "-H", self.vm2_ip, "-t", "TCP_RR"]) nsp = NSPopen(ns1_ipdb.nl.netns, ["netperf", "-l", "1", "-H", self.vm2_ip, "-t", "TCP_RR"]); nsp.wait(); nsp.release()
subprocess.call(["ip", "netns", "exec", self.ns2, "killall", "netserver"]) nsp_server.kill(); nsp_server.wait(); nsp.release()
finally: finally:
# cleanup, tear down the veths and namespaces # this is a little bit hacker, but we want to be sure to remove all created interfaces
ns_list = subprocess.check_output(["ip", "netns", "list"]).split() # ns1_eth_out, ns2_eth_out, nsrtr_eth0_out, nsrtr_eth1_out
if self.ns1 in ns_list: subprocess.call(["ip", "netns", "del", self.ns1]) if "ns1eth0a" in ipdb.interfaces: ipdb.interfaces.ns1eth0a.remove().commit()
if self.ns2 in ns_list: subprocess.call(["ip", "netns", "del", self.ns2]) if "ns2eth0a" in ipdb.interfaces: ipdb.interfaces.ns2eth0a.remove().commit()
if self.ns_router in ns_list: subprocess.call(["ip", "netns", "del", self.ns_router]) if "ns_routereth0a" in ipdb.interfaces: ipdb.interfaces.ns_routereth0a.remove().commit()
if "ns_routereth1a" in ipdb.interfaces: ipdb.interfaces.ns_routereth1a.remove().commit()
sim.release()
ipdb.release()
if __name__ == "__main__": if __name__ == "__main__":
......
...@@ -54,81 +54,41 @@ ...@@ -54,81 +54,41 @@
# 9: # 9:
# 9: OK # 9: OK
from ctypes import c_ubyte, c_ushort, c_uint, c_ulonglong, Structure from ctypes import c_uint
from netaddr import IPAddress
from bpf import BPF from bpf import BPF
from pyroute2 import IPRoute from pyroute2 import IPRoute, NetNS, IPDB, NSPopen
import sys import sys
from time import sleep from time import sleep
from unittest import main, TestCase from unittest import main, TestCase
import subprocess import subprocess
from simulation import Simulation
arg1 = sys.argv.pop(1) arg1 = sys.argv.pop(1)
ipr = IPRoute()
ipdb = IPDB(nl=ipr)
sim = Simulation(ipdb)
class TestBPFSocket(TestCase):
def setup_vm_ns(self, ns, veth_in, veth_out):
subprocess.call(["ip", "link", "add", veth_in, "type", "veth", "peer", "name", veth_out])
subprocess.call(["ip", "netns", "add", ns])
subprocess.call(["ip", "link", "set", veth_in, "netns", ns])
subprocess.call(["ip", "netns", "exec", ns, "ip", "link", "set", veth_in, "name", "eth0"])
subprocess.call(["sysctl", "-q", "-w", "net.ipv6.conf." + veth_out+ ".disable_ipv6=1"])
subprocess.call(["ip", "link", "set", veth_out, "up"])
def config_vm_ns(self, ns, ip_addr, net_mask, ip_gw):
subprocess.call(["ip", "netns", "exec", ns, "ip", "addr", "add", ip_addr + "/24", "dev", "eth0"])
subprocess.call(["ip", "netns", "exec", ns, "sysctl", "-q", "-w", "net.ipv6.conf.eth0.disable_ipv6=1"])
subprocess.call(["ip", "netns", "exec", ns, "ip", "link", "set", "eth0", "up"])
subprocess.call(["ip", "netns", "exec", ns, "route", "add", "-net", net_mask + "/24", "gw", ip_gw])
def setup_router_ns(self, ns, veth1_in, veth1_out, veth2_in, veth2_out):
subprocess.call(["ip", "netns", "add", ns])
subprocess.call(["ip", "link", "add", veth1_in, "type", "veth", "peer", "name", veth1_out])
subprocess.call(["ip", "link", "set", veth1_in, "netns", ns])
subprocess.call(["ip", "netns", "exec", ns, "ip", "link", "set", veth1_in, "name", "eth0"])
subprocess.call(["ip", "link", "add", veth2_in, "type", "veth", "peer", "name", veth2_out])
subprocess.call(["ip", "link", "set", veth2_in, "netns", ns])
subprocess.call(["ip", "netns", "exec", ns, "ip", "link", "set", veth2_in, "name", "eth1"])
subprocess.call(["sysctl", "-q", "-w", "net.ipv6.conf." + veth1_out+ ".disable_ipv6=1"])
subprocess.call(["sysctl", "-q", "-w", "net.ipv6.conf." + veth2_out+ ".disable_ipv6=1"])
subprocess.call(["ip", "link", "set", veth1_out, "up"])
subprocess.call(["ip", "link", "set", veth2_out, "up"])
def config_router_ns(self, ns, ip_eth0, ip_eth1):
subprocess.call(["ip", "netns", "exec", ns, "ip", "addr", "add", ip_eth0 + "/24", "dev", "eth0"])
subprocess.call(["ip", "netns", "exec", ns, "sysctl", "-q", "-w", "net.ipv6.conf.eth0.disable_ipv6=1"])
subprocess.call(["ip", "netns", "exec", ns, "ip", "link", "set", "eth0", "up"])
subprocess.call(["ip", "netns", "exec", ns, "ip", "addr", "add", ip_eth1 + "/24", "dev", "eth1"])
subprocess.call(["ip", "netns", "exec", ns, "sysctl", "-q", "-w", "net.ipv6.conf.eth1.disable_ipv6=1"])
subprocess.call(["ip", "netns", "exec", ns, "ip", "link", "set", "eth1", "up"])
subprocess.call(["ip", "netns", "exec", ns, "sysctl", "-w", "net.ipv4.ip_forward=1"])
def setup_br(self, br, veth_rt_2_br):
# set up the bridge and add router interface as one of its slaves
subprocess.call(["ip", "link", "add", "name", br, "type", "bridge"])
subprocess.call(["ip", "link", "set", "dev", veth_rt_2_br, "master", br])
subprocess.call(["sysctl", "-q", "-w", "net.ipv6.conf." + br + ".disable_ipv6=1"])
subprocess.call(["ip", "link", "set", br, "up"])
def br_add_pem_link(self, br, veth_pem_2_br, veth_br_2_pem): class TestBPFSocket(TestCase):
subprocess.call(["ip", "link", "add", veth_pem_2_br, "type", "veth", "peer", "name", veth_br_2_pem]) def setup_br(self, br, veth_rt_2_br, veth_pem_2_br, veth_br_2_pem):
subprocess.call(["ip", "link", "set", "dev", veth_pem_2_br, "master", br]) # create veth which connecting pem and br
with ipdb.create(ifname=veth_pem_2_br, kind="veth", peer=veth_br_2_pem) as v:
v.up()
ipdb.interfaces[veth_br_2_pem].up().commit()
subprocess.call(["sysctl", "-q", "-w", "net.ipv6.conf." + veth_pem_2_br + ".disable_ipv6=1"]) subprocess.call(["sysctl", "-q", "-w", "net.ipv6.conf." + veth_pem_2_br + ".disable_ipv6=1"])
subprocess.call(["ip", "link", "set", veth_pem_2_br, "up"])
subprocess.call(["sysctl", "-q", "-w", "net.ipv6.conf." + veth_br_2_pem + ".disable_ipv6=1"]) subprocess.call(["sysctl", "-q", "-w", "net.ipv6.conf." + veth_br_2_pem + ".disable_ipv6=1"])
subprocess.call(["ip", "link", "set", veth_br_2_pem, "up"])
# set up the bridge and add router interface as one of its slaves
with ipdb.create(ifname=br, kind="bridge") as br1:
br1.add_port(ipdb.interfaces[veth_pem_2_br])
br1.add_port(ipdb.interfaces[veth_rt_2_br])
br1.up()
subprocess.call(["sysctl", "-q", "-w", "net.ipv6.conf." + br + ".disable_ipv6=1"])
def set_default_const(self): def set_default_const(self):
self.ns1 = "ns1" self.ns1 = "ns1"
self.ns1_eth_in = "v1"
self.ns1_eth_out = "v2"
self.ns2 = "ns2" self.ns2 = "ns2"
self.ns2_eth_in = "v3"
self.ns2_eth_out = "v4"
self.ns_router = "ns_router" self.ns_router = "ns_router"
self.nsrtr_eth0_in = "v10"
self.nsrtr_eth0_out = "v11"
self.nsrtr_eth1_in = "v12"
self.nsrtr_eth1_out = "v13"
self.br1 = "br1" self.br1 = "br1"
self.veth_pem_2_br1 = "v20" self.veth_pem_2_br1 = "v20"
self.veth_br1_2_pem = "v21" self.veth_br1_2_pem = "v21"
...@@ -140,90 +100,93 @@ class TestBPFSocket(TestCase): ...@@ -140,90 +100,93 @@ class TestBPFSocket(TestCase):
self.vm2_ip = "200.1.1.1" self.vm2_ip = "200.1.1.1"
self.vm1_rtr_ip = "100.1.1.254" self.vm1_rtr_ip = "100.1.1.254"
self.vm2_rtr_ip = "200.1.1.254" self.vm2_rtr_ip = "200.1.1.254"
self.vm1_rtr_mask = "100.1.1.0" self.vm1_rtr_mask = "100.1.1.0/24"
self.vm2_rtr_mask = "200.1.1.0" self.vm2_rtr_mask = "200.1.1.0/24"
def attach_filter(self, ip, ifname, fd, name): def attach_filter(self, ifname, fd, name):
ifindex = ip.link_lookup(ifname=ifname)[0] ifindex = ipdb.interfaces[ifname].index
ip.tc("add", "ingress", ifindex, "ffff:") ipr.tc("add", "ingress", ifindex, "ffff:")
ip.tc("add-filter", "bpf", ifindex, ":1", fd=fd, name=name, ipr.tc("add-filter", "bpf", ifindex, ":1", fd=fd, name=name,
parent="ffff:", action="drop", classid=1) parent="ffff:", action="drop", classid=1)
def config_maps(self): def config_maps(self):
b = BPF(src_file=arg1, debug=0)
pem_fn = b.load_func("pem", BPF.SCHED_CLS)
self.pem_dest= b.get_table("pem_dest")
self.pem_stats = b.get_table("pem_stats")
ip = IPRoute()
# pem just relays packets between VM and its corresponding # pem just relays packets between VM and its corresponding
# slave link in the bridge interface # slave link in the bridge interface
ns1_ifindex = ip.link_lookup(ifname=self.ns1_eth_out)[0] ns1_ifindex = self.ns1_eth_out.index
ns2_ifindex = ip.link_lookup(ifname=self.ns2_eth_out)[0] ns2_ifindex = self.ns2_eth_out.index
br1_ifindex = ip.link_lookup(ifname=self.veth_br1_2_pem)[0] br1_ifindex = ipdb.interfaces[self.veth_br1_2_pem].index
br2_ifindex = ip.link_lookup(ifname=self.veth_br2_2_pem)[0] br2_ifindex = ipdb.interfaces[self.veth_br2_2_pem].index
self.pem_dest[c_uint(ns1_ifindex)] = c_uint(br1_ifindex) self.pem_dest[c_uint(ns1_ifindex)] = c_uint(br1_ifindex)
self.pem_dest[c_uint(br1_ifindex)] = c_uint(ns1_ifindex) self.pem_dest[c_uint(br1_ifindex)] = c_uint(ns1_ifindex)
self.pem_dest[c_uint(ns2_ifindex)] = c_uint(br2_ifindex) self.pem_dest[c_uint(ns2_ifindex)] = c_uint(br2_ifindex)
self.pem_dest[c_uint(br2_ifindex)] = c_uint(ns2_ifindex) self.pem_dest[c_uint(br2_ifindex)] = c_uint(ns2_ifindex)
# tc filter setup with bpf programs attached # tc filter setup with bpf programs attached
self.attach_filter(ip, self.ns1_eth_out, pem_fn.fd, pem_fn.name) self.attach_filter(self.veth_br1_2_pem, self.pem_fn.fd, self.pem_fn.name)
self.attach_filter(ip, self.ns2_eth_out, pem_fn.fd, pem_fn.name) self.attach_filter(self.veth_br2_2_pem, self.pem_fn.fd, self.pem_fn.name)
self.attach_filter(ip, self.veth_br1_2_pem, pem_fn.fd, pem_fn.name)
self.attach_filter(ip, self.veth_br2_2_pem, pem_fn.fd, pem_fn.name)
def test_brb2(self): def test_brb2(self):
try: try:
# set up the environment b = BPF(src_file=arg1, debug=0)
self.pem_fn = b.load_func("pem", BPF.SCHED_CLS)
self.pem_dest= b.get_table("pem_dest")
self.pem_stats = b.get_table("pem_stats")
# set up the topology
self.set_default_const() self.set_default_const()
self.setup_vm_ns(self.ns1, self.ns1_eth_in, self.ns1_eth_out) (ns1_ipdb, self.ns1_eth_out, unused) = sim._create_ns(self.ns1, ipaddr=self.vm1_ip+'/24', fn=self.pem_fn,
self.setup_vm_ns(self.ns2, self.ns2_eth_in, self.ns2_eth_out) action='drop', disable_ipv6=True)
self.config_vm_ns(self.ns1, self.vm1_ip, self.vm2_rtr_mask, self.vm1_rtr_ip) (ns2_ipdb, self.ns2_eth_out, unused) = sim._create_ns(self.ns2, ipaddr=self.vm2_ip+'/24', fn=self.pem_fn,
self.config_vm_ns(self.ns2, self.vm2_ip, self.vm1_rtr_mask, self.vm2_rtr_ip) action='drop', disable_ipv6=True)
self.setup_router_ns(self.ns_router, self.nsrtr_eth0_in, self.nsrtr_eth0_out, ns1_ipdb.routes.add({'dst': self.vm2_rtr_mask, 'gateway': self.vm1_rtr_ip}).commit()
self.nsrtr_eth1_in, self.nsrtr_eth1_out) ns2_ipdb.routes.add({'dst': self.vm1_rtr_mask, 'gateway': self.vm2_rtr_ip}).commit()
self.config_router_ns(self.ns_router, self.vm1_rtr_ip, self.vm2_rtr_ip)
(rt_ipdb, self.nsrtr_eth0_out, unused) = sim._create_ns(self.ns_router, ipaddr=self.vm1_rtr_ip+'/24',
# for each VM connecting to pem, there will be a corresponding veth disable_ipv6=True)
# connecting to the bridge (rt_ipdb, self.nsrtr_eth1_out, unused) = sim._ns_add_ifc(self.ns_router, "eth1", ipaddr=self.vm2_rtr_ip+'/24',
self.setup_br(self.br1, self.nsrtr_eth0_out) disable_ipv6=True)
self.br_add_pem_link(self.br1, self.veth_pem_2_br1, self.veth_br1_2_pem) # enable ip forwarding in router ns
self.setup_br(self.br2, self.nsrtr_eth1_out) nsp = NSPopen(rt_ipdb.nl.netns, ["sysctl", "-w", "net.ipv4.ip_forward=1"]); nsp.wait(); nsp.release()
self.br_add_pem_link(self.br2, self.veth_pem_2_br2, self.veth_br2_2_pem)
# for each VM connecting to pem, there will be a corresponding veth connecting to the bridge
self.setup_br(self.br1, self.nsrtr_eth0_out.ifname, self.veth_pem_2_br1, self.veth_br1_2_pem)
self.setup_br(self.br2, self.nsrtr_eth1_out.ifname, self.veth_pem_2_br2, self.veth_br2_2_pem)
# load the program and configure maps # load the program and configure maps
self.config_maps() self.config_maps()
# ping # ping
subprocess.call(["ip", "netns", "exec", self.ns1, "ping", self.vm2_ip, "-c", "2"]) nsp = NSPopen(ns1_ipdb.nl.netns, ["ping", self.vm2_ip, "-c", "2"]); nsp.wait(); nsp.release()
# one arp request/reply, 2 icmp request/reply per VM, total 6 packets per VM, 12 packets total # one arp request/reply, 2 icmp request/reply per VM, total 6 packets per VM, 12 packets total
self.assertEqual(self.pem_stats[c_uint(0)].value, 12) self.assertEqual(self.pem_stats[c_uint(0)].value, 12)
# iperf, run server on the background # iperf, run server on the background
subprocess.Popen(["ip", "netns", "exec", self.ns2, "iperf", "-s", "-xSCD"]) nsp_server = NSPopen(ns2_ipdb.nl.netns, ["iperf", "-s", "-xSCD"])
sleep(1) sleep(1)
subprocess.call(["ip", "netns", "exec", self.ns1, "iperf", "-c", self.vm2_ip, "-t", "1", "-xSC"]) nsp = NSPopen(ns1_ipdb.nl.netns, ["iperf", "-c", self.vm2_ip, "-t", "1", "-xSC"]); nsp.wait(); nsp.release()
subprocess.call(["ip", "netns", "exec", self.ns2, "killall", "iperf"]) nsp_server.kill(); nsp_server.wait(); nsp.release()
# netperf, run server on the background # netperf, run server on the background
subprocess.Popen(["ip", "netns", "exec", self.ns2, "netserver"]) nsp_server = NSPopen(ns2_ipdb.nl.netns, ["netserver"])
sleep(1) sleep(1)
subprocess.call(["ip", "netns", "exec", self.ns1, "netperf", "-l", "1", "-H", self.vm2_ip, "--", "-m", "65160"]) nsp = NSPopen(ns1_ipdb.nl.netns, ["netperf", "-l", "1", "-H", self.vm2_ip, "--", "-m", "65160"]); nsp.wait(); nsp.release()
subprocess.call(["ip", "netns", "exec", self.ns1, "netperf", "-l", "1", "-H", self.vm2_ip, "-t", "TCP_RR"]) nsp = NSPopen(ns1_ipdb.nl.netns, ["netperf", "-l", "1", "-H", self.vm2_ip, "-t", "TCP_RR"]); nsp.wait(); nsp.release()
subprocess.call(["ip", "netns", "exec", self.ns2, "killall", "netserver"]) nsp_server.kill(); nsp_server.wait(); nsp.release()
finally: finally:
# cleanup, tear down the veths and namespaces # this is a little bit hacker, but we want to be sure to remove all created interfaces
net_list = subprocess.check_output(["ls", "/sys/class/net"]).split() # ns1_eth_out, ns2_eth_out, nsrtr_eth0_out, nsrtr_eth1_out
ns_list = subprocess.check_output(["ip", "netns", "list"]).split() if "ns1eth0a" in ipdb.interfaces: ipdb.interfaces.ns1eth0a.remove().commit()
if self.veth_br1_2_pem in net_list: subprocess.call(["ip", "link", "del", self.veth_br1_2_pem]) if "ns2eth0a" in ipdb.interfaces: ipdb.interfaces.ns2eth0a.remove().commit()
if self.veth_br2_2_pem in net_list: subprocess.call(["ip", "link", "del", self.veth_br2_2_pem]) if "ns_routereth0a" in ipdb.interfaces: ipdb.interfaces.ns_routereth0a.remove().commit()
if self.br1 in net_list: subprocess.call(["ip", "link", "del", self.br1]) if "ns_routereth1a" in ipdb.interfaces: ipdb.interfaces.ns_routereth1a.remove().commit()
if self.br2 in net_list: subprocess.call(["ip", "link", "del", self.br2])
if self.ns1 in ns_list: subprocess.call(["ip", "netns", "del", self.ns1]) if self.br1 in ipdb.interfaces: ipdb.interfaces[self.br1].remove().commit()
if self.ns2 in ns_list: subprocess.call(["ip", "netns", "del", self.ns2]) if self.br2 in ipdb.interfaces: ipdb.interfaces[self.br2].remove().commit()
if self.ns_router in ns_list: subprocess.call(["ip", "netns", "del", self.ns_router]) if self.veth_pem_2_br1 in ipdb.interfaces: ipdb.interfaces[self.veth_pem_2_br1].remove().commit()
if self.veth_pem_2_br2 in ipdb.interfaces: ipdb.interfaces[self.veth_pem_2_br2].remove().commit()
sim.release()
ipdb.release()
if __name__ == "__main__": if __name__ == "__main__":
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment