SIGCOMM 2019 Tutorial Edits (#272)
* Updated the utils/run_exercise.py to allow exercises to customize host configuration from the topology.json file. Now hosts and `ping` each other in the basic exercise. Other Linux utilities should work as well (e.g. iperf). ``` mininet> h1 ping h2 PING 10.0.2.2 (10.0.2.2) 56(84) bytes of data. 64 bytes from 10.0.2.2: icmp_seq=1 ttl=62 time=3.11 ms 64 bytes from 10.0.2.2: icmp_seq=2 ttl=62 time=2.34 ms 64 bytes from 10.0.2.2: icmp_seq=3 ttl=62 time=2.15 ms ^C --- 10.0.2.2 ping statistics --- 3 packets transmitted, 3 received, 0% packet loss, time 2003ms rtt min/avg/max/mdev = 2.153/2.540/3.118/0.416 ms mininet> pingall *** Ping: testing ping reachability h1 -> h2 h3 h2 -> h1 h3 h3 -> h1 h2 *** Results: 0% dropped (6/6 received) ``` Only updated basic exercise, still need to update other exercises. Also, updated the root-bootstrap.sh because I was running into issues with latest version of vagrant. * Accidentially added the solution to the basic exercise in the previous commit. Undoing that here ... * Updated the topology.json file and table entries for the basic_tunnel exercise. * Updated P4Runtime exercise with new topology and table entries. * Fixed MAC addresses in P4Runtime exercise. It is working now. * Fixed MAC addresses in P4Runtime exercise starter code * Updated ECN exercise to use new topology.json file. Updated the table entries / MAC addresses as well. * Updated the topology.json file and table entries for the MRI exercise. * Updated source_routing exercise with new topology file and verified correct functionality. * Updated load_balance exercise with new topology. * Moved basic exercise triangle topology into a separate folder * Added new topology for the basic exercise: a single pod of a fat-tree. * Updated Makefiles and run_exercise.py to allow exercises to configure each switch with a different P4 program. This is mainly for the firewall exercise. * Updated Makefiles of project to work with new utils/Makefile * Updated load_balance and p4runtime exercise Makefiles * Initial commit of the firewall exercise, which is a simple stateful firewall that uses a bloom filter. Need to update README files * Initial commit of the path_monitor exercise. It is working but still need to update the README and figure out what we want the tutorial attendees to implement. * Updated README file in firewall exercise. Also removed the bits from the starter code that we want the tutorial attendees to implement * Renamed path_monitor exercise to link_monitor * Updated the README in the link_monitor exercise and removed the bits from the starter code that we want the tutorial attendees to implement. * Updated README for the firewall exercise * Adding pod-topo.png image to basic exercise * Added firewall-topo.png image to firewall exercise * Added link-monitor-topo.png to link_monitor exercise * Updated README files to point to topology images * Updated top-level README to point to new exercises. * Fixed link for VM dependencies script in README * Updated bmv2/pi/p4c commits * Updated README files for exercises to fix some typos and added a note about the V1Model architecture. * Added a note about food for thought in the link_monitor README * Updated the firewall.p4 program to use two register arrays rather than a single one. This is to make the design more portable to high line rate devices which can only support a single access to each register array. * Minor fix to firewall exercise to get rid of compiler warning. * Updated comment in firewall exercise. * Minor (typo) fixes in the firewall ReadMe * More info in firewall exercise ReadMe step 2 * Updated firewall.p4 to reuse direction variable * More testing steps, small fixes in firewall exercise Readme * Added food for thought to firewall Readme * Cosmetic fixes to firewall ReadMe * Made a few updates to the basic exercise README and added more details to the link_monitor exercise README. Also added a command to install grip when provisioning the VM. This could be useful for rendering the markdown README files offline. * Updated top level README so it can be merged into the master branch. * Moved cmd to install grip from root-bootstrap to user-bootstrap
This commit is contained in:
@@ -2,18 +2,26 @@ BUILD_DIR = build
|
||||
PCAP_DIR = pcaps
|
||||
LOG_DIR = logs
|
||||
|
||||
TOPO = topology.json
|
||||
P4C = p4c-bm2-ss
|
||||
P4C_ARGS += --p4runtime-files $(BUILD_DIR)/$(basename $@).p4.p4info.txt
|
||||
|
||||
RUN_SCRIPT = ../../utils/run_exercise.py
|
||||
|
||||
source := $(wildcard *.p4)
|
||||
outfile := $(source:.p4=.json)
|
||||
ifndef TOPO
|
||||
TOPO = topology.json
|
||||
endif
|
||||
|
||||
compiled_json := $(BUILD_DIR)/$(outfile)
|
||||
source = $(wildcard *.p4)
|
||||
compiled_json := $(source:.p4=.json)
|
||||
|
||||
ifndef DEFAULT_PROG
|
||||
DEFAULT_PROG = $(wildcard *.p4)
|
||||
endif
|
||||
DEFAULT_JSON = $(BUILD_DIR)/$(DEFAULT_PROG:.p4=.json)
|
||||
|
||||
# Define NO_P4 to start BMv2 without a program
|
||||
ifndef NO_P4
|
||||
run_args += -j $(compiled_json)
|
||||
run_args += -j $(DEFAULT_JSON)
|
||||
endif
|
||||
|
||||
# Set BMV2_SWITCH_EXE to override the BMv2 target
|
||||
@@ -31,8 +39,8 @@ stop:
|
||||
|
||||
build: dirs $(compiled_json)
|
||||
|
||||
$(BUILD_DIR)/%.json: %.p4
|
||||
$(P4C) --p4v 16 $(P4C_ARGS) -o $@ $<
|
||||
%.json: %.p4
|
||||
$(P4C) --p4v 16 $(P4C_ARGS) -o $(BUILD_DIR)/$@ $<
|
||||
|
||||
dirs:
|
||||
mkdir -p $(BUILD_DIR) $(PCAP_DIR) $(LOG_DIR)
|
||||
|
||||
@@ -48,7 +48,7 @@ class P4RuntimeSwitch(P4Switch):
|
||||
if json_path is not None:
|
||||
# make sure that the provided JSON file exists
|
||||
if not os.path.isfile(json_path):
|
||||
error("Invalid JSON file.\n")
|
||||
error("Invalid JSON file: {}\n".format(json_path))
|
||||
exit(1)
|
||||
self.json_path = json_path
|
||||
else:
|
||||
|
||||
@@ -66,65 +66,57 @@ def configureP4Switch(**switch_args):
|
||||
|
||||
class ExerciseTopo(Topo):
|
||||
""" The mininet topology class for the P4 tutorial exercises.
|
||||
A custom class is used because the exercises make a few topology
|
||||
assumptions, mostly about the IP and MAC addresses.
|
||||
"""
|
||||
def __init__(self, hosts, switches, links, log_dir, **opts):
|
||||
def __init__(self, hosts, switches, links, log_dir, bmv2_exe, pcap_dir, **opts):
|
||||
Topo.__init__(self, **opts)
|
||||
host_links = []
|
||||
switch_links = []
|
||||
self.sw_port_mapping = {}
|
||||
|
||||
# assumes host always comes first for host<-->switch links
|
||||
for link in links:
|
||||
if link['node1'][0] == 'h':
|
||||
host_links.append(link)
|
||||
else:
|
||||
switch_links.append(link)
|
||||
|
||||
link_sort_key = lambda x: x['node1'] + x['node2']
|
||||
# Links must be added in a sorted order so bmv2 port numbers are predictable
|
||||
host_links.sort(key=link_sort_key)
|
||||
switch_links.sort(key=link_sort_key)
|
||||
|
||||
for sw in switches:
|
||||
self.addSwitch(sw, log_file="%s/%s.log" %(log_dir, sw))
|
||||
for sw, params in switches.iteritems():
|
||||
if "program" in params:
|
||||
switchClass = configureP4Switch(
|
||||
sw_path=bmv2_exe,
|
||||
json_path=params["program"],
|
||||
log_console=True,
|
||||
pcap_dump=pcap_dir)
|
||||
else:
|
||||
# add default switch
|
||||
switchClass = None
|
||||
self.addSwitch(sw, log_file="%s/%s.log" %(log_dir, sw), cls=switchClass)
|
||||
|
||||
for link in host_links:
|
||||
host_name = link['node1']
|
||||
host_sw = link['node2']
|
||||
host_num = int(host_name[1:])
|
||||
sw_num = int(host_sw[1:])
|
||||
host_ip = "10.0.%d.%d" % (sw_num, host_num)
|
||||
host_mac = '00:00:00:00:%02x:%02x' % (sw_num, host_num)
|
||||
# Each host IP should be /24, so all exercise traffic will use the
|
||||
# default gateway (the switch) without sending ARP requests.
|
||||
self.addHost(host_name, ip=host_ip+'/24', mac=host_mac)
|
||||
self.addLink(host_name, host_sw,
|
||||
sw_name, sw_port = self.parse_switch_node(link['node2'])
|
||||
host_ip = hosts[host_name]['ip']
|
||||
host_mac = hosts[host_name]['mac']
|
||||
self.addHost(host_name, ip=host_ip, mac=host_mac)
|
||||
self.addLink(host_name, sw_name,
|
||||
delay=link['latency'], bw=link['bandwidth'],
|
||||
addr1=host_mac, addr2=host_mac)
|
||||
self.addSwitchPort(host_sw, host_name)
|
||||
port2=sw_port)
|
||||
|
||||
for link in switch_links:
|
||||
self.addLink(link['node1'], link['node2'],
|
||||
sw1_name, sw1_port = self.parse_switch_node(link['node1'])
|
||||
sw2_name, sw2_port = self.parse_switch_node(link['node2'])
|
||||
self.addLink(sw1_name, sw2_name,
|
||||
port1=sw1_port, port2=sw2_port,
|
||||
delay=link['latency'], bw=link['bandwidth'])
|
||||
self.addSwitchPort(link['node1'], link['node2'])
|
||||
self.addSwitchPort(link['node2'], link['node1'])
|
||||
|
||||
self.printPortMapping()
|
||||
|
||||
def addSwitchPort(self, sw, node2):
|
||||
if sw not in self.sw_port_mapping:
|
||||
self.sw_port_mapping[sw] = []
|
||||
portno = len(self.sw_port_mapping[sw])+1
|
||||
self.sw_port_mapping[sw].append((portno, node2))
|
||||
|
||||
def printPortMapping(self):
|
||||
print "Switch port mapping:"
|
||||
for sw in sorted(self.sw_port_mapping.keys()):
|
||||
print "%s: " % sw,
|
||||
for portno, node2 in self.sw_port_mapping[sw]:
|
||||
print "%d:%s\t" % (portno, node2),
|
||||
print
|
||||
def parse_switch_node(self, node):
|
||||
assert(len(node.split('-')) == 2)
|
||||
sw_name, sw_port = node.split('-')
|
||||
try:
|
||||
sw_port = int(sw_port[1])
|
||||
except:
|
||||
raise Exception('Invalid switch node in topology file: {}'.format(node))
|
||||
return sw_name, sw_port
|
||||
|
||||
|
||||
class ExerciseRunner:
|
||||
@@ -134,8 +126,8 @@ class ExerciseRunner:
|
||||
pcap_dir : string // directory for mininet switch pcap files
|
||||
quiet : bool // determines if we print logger messages
|
||||
|
||||
hosts : list<string> // list of mininet host names
|
||||
switches : dict<string, dict> // mininet host names and their associated properties
|
||||
hosts : dict<string, dict> // mininet host names and their associated properties
|
||||
switches : dict<string, dict> // mininet switch names and their associated properties
|
||||
links : list<dict> // list of mininet link properties
|
||||
|
||||
switch_json : string // json of the compiled p4 example
|
||||
@@ -149,7 +141,7 @@ class ExerciseRunner:
|
||||
if not self.quiet:
|
||||
print(' '.join(items))
|
||||
|
||||
def formatLatency(self, l):
|
||||
def format_latency(self, l):
|
||||
""" Helper method for parsing link latencies from the topology json. """
|
||||
if isinstance(l, (str, unicode)):
|
||||
return l
|
||||
@@ -232,7 +224,7 @@ class ExerciseRunner:
|
||||
'bandwidth':None
|
||||
}
|
||||
if len(link) > 2:
|
||||
link_dict['latency'] = self.formatLatency(link[2])
|
||||
link_dict['latency'] = self.format_latency(link[2])
|
||||
if len(link) > 3:
|
||||
link_dict['bandwidth'] = link[3]
|
||||
|
||||
@@ -251,18 +243,18 @@ class ExerciseRunner:
|
||||
"""
|
||||
self.logger("Building mininet topology.")
|
||||
|
||||
self.topo = ExerciseTopo(self.hosts, self.switches.keys(), self.links, self.log_dir)
|
||||
defaultSwitchClass = configureP4Switch(
|
||||
sw_path=self.bmv2_exe,
|
||||
json_path=self.switch_json,
|
||||
log_console=True,
|
||||
pcap_dump=self.pcap_dir)
|
||||
|
||||
switchClass = configureP4Switch(
|
||||
sw_path=self.bmv2_exe,
|
||||
json_path=self.switch_json,
|
||||
log_console=True,
|
||||
pcap_dump=self.pcap_dir)
|
||||
self.topo = ExerciseTopo(self.hosts, self.switches, self.links, self.log_dir, self.bmv2_exe, self.pcap_dir)
|
||||
|
||||
self.net = Mininet(topo = self.topo,
|
||||
link = TCLink,
|
||||
host = P4Host,
|
||||
switch = switchClass,
|
||||
switch = defaultSwitchClass,
|
||||
controller = None)
|
||||
|
||||
def program_switch_p4runtime(self, sw_name, sw_dict):
|
||||
@@ -312,30 +304,13 @@ class ExerciseRunner:
|
||||
self.program_switch_p4runtime(sw_name, sw_dict)
|
||||
|
||||
def program_hosts(self):
|
||||
""" Adds static ARP entries and default routes to each mininet host.
|
||||
|
||||
Assumes:
|
||||
- A mininet instance is stored as self.net and self.net.start() has
|
||||
been called.
|
||||
""" Execute any commands provided in the topology.json file on each Mininet host
|
||||
"""
|
||||
for host_name in self.topo.hosts():
|
||||
for host_name, host_info in self.hosts.items():
|
||||
h = self.net.get(host_name)
|
||||
h_iface = h.intfs.values()[0]
|
||||
link = h_iface.link
|
||||
|
||||
sw_iface = link.intf1 if link.intf1 != h_iface else link.intf2
|
||||
# phony IP to lie to the host about
|
||||
host_id = int(host_name[1:])
|
||||
sw_ip = '10.0.%d.254' % host_id
|
||||
|
||||
# Ensure each host's interface name is unique, or else
|
||||
# mininet cannot shutdown gracefully
|
||||
h.defaultIntf().rename('%s-eth0' % host_name)
|
||||
# static arp entries and default routes
|
||||
h.cmd('arp -i %s -s %s %s' % (h_iface.name, sw_ip, sw_iface.mac))
|
||||
h.cmd('ethtool --offload %s rx off tx off' % h_iface.name)
|
||||
h.cmd('ip route add %s dev %s' % (sw_ip, h_iface.name))
|
||||
h.setDefaultRoute("via %s" % sw_ip)
|
||||
if "commands" in host_info:
|
||||
for cmd in host_info["commands"]:
|
||||
h.cmd(cmd)
|
||||
|
||||
|
||||
def do_net_cli(self):
|
||||
|
||||
Reference in New Issue
Block a user