Commit ffaee08c authored by Brenden Blanco's avatar Brenden Blanco

Merge pull request #83 from iovisor/yhs_dev

Yhs dev
parents 0fa99f4d 00eb0883
import atexit
import subprocess
from pyroute2 import IPRoute, NetNS, IPDB, NSPopen
class Simulation(object):
......@@ -14,15 +14,22 @@ class Simulation(object):
self.processes = []
self.released = False
# 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"):
# helper function to add additional ifc to namespace
# if called directly outside Simulation class, "ifc_base_name" should be
# different from "name", the "ifc_base_name" and "name" are the same for
# the first ifc created by namespace
def _ns_add_ifc(self, name, ns_ifc, ifc_base_name, in_ifc=None, out_ifc=None,
ipaddr=None, macaddr=None, fn=None, cmd=None, action="ok",
disable_ipv6=False):
if name in self.ipdbs:
ns_ipdb = self.ipdbs[name]
else:
ns_ipdb = IPDB(nl=NetNS(name))
if in_ifc:
in_ifname = in_ifc.ifname
else:
out_ifc = self.ipdb.create(ifname="%sa" % name, kind="veth",
peer="%sb" % name).commit()
out_ifc = self.ipdb.create(ifname="%sa" % ifc_base_name, kind="veth",
peer="%sb" % ifc_base_name).commit()
in_ifc = self.ipdb.interfaces[out_ifc.peer]
in_ifname = in_ifc.ifname
with in_ifc as v:
......@@ -31,21 +38,36 @@ class Simulation(object):
in_ifc = ns_ipdb.interfaces[in_ifname]
if out_ifc: out_ifc.up().commit()
with in_ifc as v:
v.ifname = "eth0"
v.ifname = ns_ifc
if ipaddr: v.add_ip("%s" % ipaddr)
if macaddr: v.address = macaddr
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:
self.ipdb.nl.tc("add", "ingress", out_ifc["index"], "ffff:")
self.ipdb.nl.tc("add-filter", "bpf", out_ifc["index"], ":1",
fd=fn.fd, name=fn.name, parent="ffff:",
action=action, classid=1)
self.ipdbs[ns_ipdb.nl.netns] = ns_ipdb
self.namespaces.append(ns_ipdb.nl)
if cmd:
self.processes.append(NSPopen(ns_ipdb.nl.netns, cmd))
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", name, 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):
if self.released: return
self.released = True
......
../../examples/simulation.py
\ No newline at end of file
......@@ -101,13 +101,12 @@ int pem(struct __sk_buff *skb) {
}
}
return 0;
return 1;
}
static int br_common(struct __sk_buff *skb, int which_br) __attribute__((always_inline));
static int br_common(struct __sk_buff *skb, int which_br) {
u8 *cursor = 0;
bpf_metadata_t meta = {};
u16 proto;
u16 arpop;
eth_addr_t dmac;
......@@ -118,20 +117,16 @@ static int br_common(struct __sk_buff *skb, int which_br) {
bpf_dest_t *dest_p;
u32 index, *rtrif_p;
if (skb->tc_index == 0) {
skb->tc_index = 1;
skb->cb[0] = skb->cb[1] = 0;
meta.prog_id = meta.rx_port_id = 0;
} else {
meta.prog_id = skb->cb[0];
meta.rx_port_id = skb->cb[1];
}
struct ethernet_t *ethernet = cursor_advance(cursor, sizeof(*ethernet));
/* handle ethernet packet header */
{
dmac.addr = ethernet->dst;
if (meta.prog_id != 0) {
/* send to the router */
/* skb->tc_index may be preserved accross router namespace if router simply rewrite packet
* and send it back.
*/
if (skb->tc_index == 1) {
/* packet from pem, send to the router, set tc_index to 2 */
skb->tc_index = 2;
if (dmac.addr == 0xffffffffffffULL) {
index = 0;
if (which_br == 1)
......@@ -149,9 +144,11 @@ static int br_common(struct __sk_buff *skb, int which_br) {
if (rtrif_p)
bpf_clone_redirect(skb, *rtrif_p, 0);
}
return 0;
return 1;
}
/* set the tc_index to 1 so pem knows it is from internal */
skb->tc_index = 1;
switch (ethernet->type) {
case ETH_P_IP: goto ip;
case ETH_P_ARP: goto arp;
......@@ -217,7 +214,7 @@ xmit:
}
EOP:
return 0;
return 1;
}
int br1(struct __sk_buff *skb) {
......
......@@ -61,68 +61,31 @@
# 8:
# 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 bpf import BPF
from pyroute2 import IPRoute
from socket import socket, AF_INET, SOCK_DGRAM
from pyroute2 import IPRoute, NetNS, IPDB, NSPopen
import sys
from time import sleep
from unittest import main, TestCase
import subprocess
import struct
from simulation import Simulation
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(["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, "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(["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, "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, "ip", "link", "set", "eth1", "up"])
def set_default_const(self):
self.ns1 = "ns1"
self.ns1_eth_in = "v1"
self.ns1_eth_out = "v2"
self.ns2 = "ns2"
self.ns2_eth_in = "v3"
self.ns2_eth_out = "v4"
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.vm2_ip = "200.1.1.1"
self.vm1_rtr_ip = "100.1.1.254"
self.vm2_rtr_ip = "200.1.1.254"
self.vm1_rtr_mask = "100.1.1.0"
self.vm2_rtr_mask = "200.1.1.0"
self.vm1_rtr_mask = "100.1.1.0/24"
self.vm2_rtr_mask = "200.1.1.0/24"
def get_table(self, b):
self.jump = b.get_table("jump")
......@@ -141,29 +104,15 @@ class TestBPFSocket(TestCase):
self.br2_rtr = b.get_table("br2_rtr")
def connect_ports(self, prog_id_pem, prog_id_br, curr_pem_pid, curr_br_pid,
ip, br_dest_map, br_mac_map,
ns_eth_out, vm_mac, vm_ip):
br_dest_map, br_mac_map, ifindex, vm_mac, vm_ip):
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)
ifindex = ip.link_lookup(ifname=ns_eth_out)[0]
self.pem_port[c_uint(curr_pem_pid)] = c_uint(ifindex)
self.pem_ifindex[c_uint(ifindex)] = c_uint(curr_pem_pid)
mac_addr = br_mac_map.Key(int(EUI(vm_mac.decode())))
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):
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
prog_id_pem = 1
prog_id_br1 = 2
......@@ -173,89 +122,97 @@ class TestBPFSocket(TestCase):
curr_pem_pid = 0
curr_br1_pid = 0
curr_br2_pid = 0
self.get_table(b)
# configure jump table
self.jump[c_uint(prog_id_pem)] = c_uint(pem_fn.fd)
self.jump[c_uint(prog_id_br1)] = c_uint(br1_fn.fd)
self.jump[c_uint(prog_id_br2)] = c_uint(br2_fn.fd)
self.jump[c_uint(prog_id_pem)] = c_uint(self.pem_fn.fd)
self.jump[c_uint(prog_id_br1)] = c_uint(self.br1_fn.fd)
self.jump[c_uint(prog_id_br2)] = c_uint(self.br2_fn.fd)
# connect pem and br1
curr_pem_pid = curr_pem_pid + 1
curr_br1_pid = curr_br1_pid + 1
self.connect_ports(prog_id_pem, prog_id_br1, curr_pem_pid, curr_br1_pid,
ip, self.br1_dest, self.br1_mac,
self.ns1_eth_out, self.vm1_mac, self.vm1_ip)
self.br1_dest, self.br1_mac,
self.ns1_eth_out.index, self.vm1_mac, self.vm1_ip)
# connect pem and br2
curr_pem_pid = curr_pem_pid + 1
curr_br2_pid = curr_br2_pid + 1
self.connect_ports(prog_id_pem, prog_id_br2, curr_pem_pid, curr_br2_pid,
ip, self.br2_dest, self.br2_mac,
self.ns2_eth_out, self.vm2_mac, self.vm2_ip)
self.br2_dest, self.br2_mac,
self.ns2_eth_out.index, self.vm2_mac, self.vm2_ip)
# connect <br1, rtr> and <br2, rtr>
ifindex = ip.link_lookup(ifname=self.nsrtr_eth0_out)[0]
self.br1_rtr[c_uint(0)] = c_uint(ifindex)
ifindex = ip.link_lookup(ifname=self.nsrtr_eth1_out)[0]
self.br2_rtr[c_uint(0)] = c_uint(ifindex)
self.br1_rtr[c_uint(0)] = c_uint(self.nsrtr_eth0_out.index)
self.br2_rtr[c_uint(0)] = c_uint(self.nsrtr_eth1_out.index)
# 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 setUp(self):
def test_brb(self):
try:
b = BPF(src_file=arg1, debug=0)
self.pem_fn = b.load_func("pem", BPF.SCHED_CLS)
self.br1_fn = b.load_func("br1", BPF.SCHED_CLS)
self.br2_fn = b.load_func("br2", BPF.SCHED_CLS)
self.get_table(b)
# set up the environment
# set up the topology
self.set_default_const()
self.setup_vm_ns(self.ns1, self.ns1_eth_in, self.ns1_eth_out)
self.setup_vm_ns(self.ns2, self.ns2_eth_in, self.ns2_eth_out)
self.config_vm_ns(self.ns1, self.vm1_ip, self.vm2_rtr_mask, self.vm1_rtr_ip)
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
self.vm1_mac = subprocess.check_output(["ip", "netns", "exec", self.ns1, "cat", "/sys/class/net/eth0/address"])
self.vm1_mac = self.vm1_mac.strip()
self.vm2_mac = subprocess.check_output(["ip", "netns", "exec", self.ns2, "cat", "/sys/class/net/eth0/address"])
self.vm2_mac = self.vm2_mac.strip()
# load the program and configure maps
(ns1_ipdb, self.ns1_eth_out, _) = sim._create_ns(self.ns1, ipaddr=self.vm1_ip+'/24',
fn=self.pem_fn, action='drop',
disable_ipv6=True)
(ns2_ipdb, self.ns2_eth_out, _) = sim._create_ns(self.ns2, ipaddr=self.vm2_ip+'/24',
fn=self.pem_fn, action='drop',
disable_ipv6=True)
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
(_, self.nsrtr_eth0_out, _) = 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, _) = sim._ns_add_ifc(self.ns_router, "eth1", "ns_router2",
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()
def test_brb(self):
# our bridge is not smart enough, so send arping for router learning to prevent router
# from sending out arp request
subprocess.call(["ip", "netns", "exec", self.ns1, "arping", "-w", "1", "-c", "1", "-I", "eth0",
self.vm1_rtr_ip])
subprocess.call(["ip", "netns", "exec", self.ns2, "arping", "-w", "1", "-c", "1", "-I", "eth0",
self.vm2_rtr_ip])
nsp = NSPopen(ns1_ipdb.nl.netns,
["arping", "-w", "1", "-c", "1", "-I", "eth0", self.vm1_rtr_ip])
nsp.wait(); nsp.release()
nsp = NSPopen(ns2_ipdb.nl.netns,
["arping", "-w", "1", "-c", "1", "-I", "eth0", self.vm2_rtr_ip])
nsp.wait(); nsp.release()
# ping
subprocess.call(["ip", "netns", "exec", self.ns1, "ping", self.vm2_ip, "-c", "2"])
# minimum one arp reply, 5 icmp reply
self.assertGreater(self.pem_stats[c_uint(0)].value, 5)
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
# total 8 packets should be counted
self.assertEqual(self.pem_stats[c_uint(0)].value, 8)
# 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", "-xSC"])
sleep(1)
subprocess.call(["ip", "netns", "exec", self.ns1, "iperf", "-c", self.vm2_ip, "-t", "1", "-xSC"])
subprocess.call(["ip", "netns", "exec", self.ns2, "killall", "iperf"])
nsp = NSPopen(ns1_ipdb.nl.netns, ["iperf", "-c", self.vm2_ip, "-t", "1", "-xSC"])
nsp.wait(); nsp.release()
nsp_server.kill(); nsp_server.wait(); nsp_server.release()
# netperf, run server on the background
subprocess.Popen(["ip", "netns", "exec", self.ns2, "netserver"])
nsp_server = NSPopen(ns2_ipdb.nl.netns, ["netserver", "-D"])
sleep(1)
subprocess.call(["ip", "netns", "exec", self.ns1, "netperf", "-l", "1", "-H", self.vm2_ip, "--", "-m", "65160"])
subprocess.call(["ip", "netns", "exec", self.ns1, "netperf", "-l", "1", "-H", self.vm2_ip, "-t", "TCP_RR"])
subprocess.call(["ip", "netns", "exec", self.ns2, "killall", "netserver"])
# cleanup, tear down the veths and namespaces
subprocess.call(["ip", "netns", "del", self.ns1])
subprocess.call(["ip", "netns", "del", self.ns2])
subprocess.call(["ip", "netns", "del", self.ns_router])
nsp = NSPopen(ns1_ipdb.nl.netns, ["netperf", "-l", "1", "-H", self.vm2_ip, "--", "-m", "65160"])
nsp.wait(); nsp.release()
nsp = NSPopen(ns1_ipdb.nl.netns, ["netperf", "-l", "1", "-H", self.vm2_ip, "-t", "TCP_RR"])
nsp.wait(); nsp.release()
nsp_server.kill(); nsp_server.wait(); nsp_server.release()
finally:
sim.release()
ipdb.release()
if __name__ == "__main__":
......
......@@ -24,5 +24,5 @@ int pem(struct __sk_buff *skb) {
bpf_clone_redirect(skb, *ifindex_p, 0);
}
return 0;
return 1;
}
......@@ -54,72 +54,41 @@
# 9:
# 9: OK
from ctypes import c_ubyte, c_ushort, c_uint, c_ulonglong, Structure
from netaddr import IPAddress
from ctypes import c_uint
from bpf import BPF
from pyroute2 import IPRoute
from socket import socket, AF_INET, SOCK_DGRAM
from pyroute2 import IPRoute, NetNS, IPDB, NSPopen
import sys
from time import sleep
from unittest import main, TestCase
import subprocess
from simulation import Simulation
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(["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, "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(["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, "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, "ip", "link", "set", "eth1", "up"])
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(["ip", "link", "set", br, "up"])
def setup_br(self, br, veth_rt_2_br, veth_pem_2_br, veth_br_2_pem):
# 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_br_2_pem + ".disable_ipv6=1"])
def br_add_pem_link(self, br, veth_pem_2_br, veth_br_2_pem):
subprocess.call(["ip", "link", "add", veth_pem_2_br, "type", "veth", "peer", "name", veth_br_2_pem])
subprocess.call(["ip", "link", "set", "dev", veth_pem_2_br, "master", br])
subprocess.call(["ip", "link", "set", veth_pem_2_br, "up"])
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):
self.ns1 = "ns1"
self.ns1_eth_in = "v1"
self.ns1_eth_out = "v2"
self.ns2 = "ns2"
self.ns2_eth_in = "v3"
self.ns2_eth_out = "v4"
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.veth_pem_2_br1 = "v20"
self.veth_br1_2_pem = "v21"
......@@ -131,88 +100,91 @@ class TestBPFSocket(TestCase):
self.vm2_ip = "200.1.1.1"
self.vm1_rtr_ip = "100.1.1.254"
self.vm2_rtr_ip = "200.1.1.254"
self.vm1_rtr_mask = "100.1.1.0"
self.vm2_rtr_mask = "200.1.1.0"
self.vm1_rtr_mask = "100.1.1.0/24"
self.vm2_rtr_mask = "200.1.1.0/24"
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,
def attach_filter(self, ifname, fd, name):
ifindex = ipdb.interfaces[ifname].index
ipr.tc("add", "ingress", ifindex, "ffff:")
ipr.tc("add-filter", "bpf", ifindex, ":1", fd=fd, name=name,
parent="ffff:", action="drop", classid=1)
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
# slave link in the bridge interface
ns1_ifindex = ip.link_lookup(ifname=self.ns1_eth_out)[0]
ns2_ifindex = ip.link_lookup(ifname=self.ns2_eth_out)[0]
br1_ifindex = ip.link_lookup(ifname=self.veth_br1_2_pem)[0]
br2_ifindex = ip.link_lookup(ifname=self.veth_br2_2_pem)[0]
ns1_ifindex = self.ns1_eth_out.index
ns2_ifindex = self.ns2_eth_out.index
br1_ifindex = ipdb.interfaces[self.veth_br1_2_pem].index
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(br1_ifindex)] = c_uint(ns1_ifindex)
self.pem_dest[c_uint(ns2_ifindex)] = c_uint(br2_ifindex)
self.pem_dest[c_uint(br2_ifindex)] = c_uint(ns2_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.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)
self.attach_filter(self.veth_br1_2_pem, self.pem_fn.fd, self.pem_fn.name)
self.attach_filter(self.veth_br2_2_pem, self.pem_fn.fd, self.pem_fn.name)
def setUp(self):
def test_brb2(self):
try:
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 environment
# set up the topology
self.set_default_const()
self.setup_vm_ns(self.ns1, self.ns1_eth_in, self.ns1_eth_out)
self.setup_vm_ns(self.ns2, self.ns2_eth_in, self.ns2_eth_out)
self.config_vm_ns(self.ns1, self.vm1_ip, self.vm2_rtr_mask, self.vm1_rtr_ip)
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)
# 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)
self.br_add_pem_link(self.br1, self.veth_pem_2_br1, self.veth_br1_2_pem)
self.setup_br(self.br2, self.nsrtr_eth1_out)
self.br_add_pem_link(self.br2, self.veth_pem_2_br2, self.veth_br2_2_pem)
(ns1_ipdb, self.ns1_eth_out, _) = sim._create_ns(self.ns1, ipaddr=self.vm1_ip+'/24',
fn=self.pem_fn, action='drop',
disable_ipv6=True)
(ns2_ipdb, self.ns2_eth_out, _) = sim._create_ns(self.ns2, ipaddr=self.vm2_ip+'/24',
fn=self.pem_fn, action='drop',
disable_ipv6=True)
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.nsrtr_eth0_out, _) = sim._create_ns(self.ns_router, ipaddr=self.vm1_rtr_ip+'/24',
disable_ipv6=True)
(rt_ipdb, self.nsrtr_eth1_out, _) = sim._ns_add_ifc(self.ns_router, "eth1", "ns_router2",
ipaddr=self.vm2_rtr_ip+'/24',
disable_ipv6=True)
# enable ip forwarding in router ns
nsp = NSPopen(rt_ipdb.nl.netns, ["sysctl", "-w", "net.ipv4.ip_forward=1"])
nsp.wait(); nsp.release()
# 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
self.config_maps()
def test_brb2(self):
# ping
subprocess.call(["ip", "netns", "exec", self.ns1, "ping", self.vm2_ip, "-c", "2"])
# minimum one arp request/reply, 5 icmp request/reply
self.assertGreater(self.pem_stats[c_uint(0)].value, 11)
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
self.assertEqual(self.pem_stats[c_uint(0)].value, 12)
# 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", "-xSC"])
sleep(1)
subprocess.call(["ip", "netns", "exec", self.ns1, "iperf", "-c", self.vm2_ip, "-t", "1", "-xSC"])
subprocess.call(["ip", "netns", "exec", self.ns2, "killall", "iperf"])
nsp = NSPopen(ns1_ipdb.nl.netns, ["iperf", "-c", self.vm2_ip, "-t", "1", "-xSC"])
nsp.wait(); nsp.release()
nsp_server.kill(); nsp_server.wait(); nsp_server.release()
# netperf, run server on the background
subprocess.Popen(["ip", "netns", "exec", self.ns2, "netserver"])
nsp_server = NSPopen(ns2_ipdb.nl.netns, ["netserver", "-D"])
sleep(1)
subprocess.call(["ip", "netns", "exec", self.ns1, "netperf", "-l", "1", "-H", self.vm2_ip, "--", "-m", "65160"])
subprocess.call(["ip", "netns", "exec", self.ns1, "netperf", "-l", "1", "-H", self.vm2_ip, "-t", "TCP_RR"])
subprocess.call(["ip", "netns", "exec", self.ns2, "killall", "netserver"])
# cleanup, tear down the veths and namespaces
subprocess.call(["ip", "link", "del", self.veth_br1_2_pem])
subprocess.call(["ip", "link", "del", self.veth_br2_2_pem])
subprocess.call(["ip", "link", "del", self.br1])
subprocess.call(["ip", "link", "del", self.br2])
subprocess.call(["ip", "netns", "del", self.ns1])
subprocess.call(["ip", "netns", "del", self.ns2])
subprocess.call(["ip", "netns", "del", self.ns_router])
nsp = NSPopen(ns1_ipdb.nl.netns, ["netperf", "-l", "1", "-H", self.vm2_ip, "--", "-m", "65160"])
nsp.wait(); nsp.release()
nsp = NSPopen(ns1_ipdb.nl.netns, ["netperf", "-l", "1", "-H", self.vm2_ip, "-t", "TCP_RR"])
nsp.wait(); nsp.release()
nsp_server.kill(); nsp_server.wait(); nsp_server.release()
finally:
if self.br1 in ipdb.interfaces: ipdb.interfaces[self.br1].remove().commit()
if self.br2 in ipdb.interfaces: ipdb.interfaces[self.br2].remove().commit()
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__":
......
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