import nnpy import struct from p4utils.utils.topology import Topology from p4utils.utils.sswitch_API import SimpleSwitchAPI from scapy.all import sniff, get_if_list, Ether, get_if_hwaddr, sendp from scapy.all import IP, Raw, IPv6, TCP, TCP_client from scapy.all import Ether, sniff, Packet, BitField import sys import re import logging import argparse import ipaddress import subprocess logging.basicConfig() log = logging.getLogger("main") class L2Controller(object): def __init__(self, sw_name): # Command line mapping self.modes = ['base'] # Network / egress self.v6_routes = {} self.v6_routes['base'] = [] self.v6_routes['base'].append({ "net": "2001:db8:61::/64", "port": "1"}) self.v6_routes['base'].append({ "net": "2001:db8:62::/64", "port": "2"}) self.v4_routes = {} self.v4_routes['base'] = [] self.v4_routes['base'].append({ "net": "10.0.41.0/24", "port": "3"}) self.v4_routes['base'].append({ "net": "10.0.42.0/24", "port": "4"}) self.init_boilerplate(sw_name) def init_boilerplate(self, sw_name): self.topo = Topology(db="topology.db") self.sw_name = sw_name self.thrift_port = self.topo.get_thrift_port(sw_name) self.cpu_port = self.topo.get_cpu_port_index(self.sw_name) self.controller = SimpleSwitchAPI(self.thrift_port) self.intf = str(self.topo.get_cpu_port_intf(self.sw_name).replace("eth0", "eth1")) self.controller.reset_state() if self.cpu_port: self.controller.mirroring_add(100, self.cpu_port) def config(self): self.fill_tables() self.config_hosts() def fill_tables(self): self.controller.table_clear("v6_routing") for v6route in self.v6_routes[self.mode]: self.controller.table_add("v6_routing", "set_egress_port", [v6route['net']], [v6route['port']]) self.controller.table_clear("v4_routing") for v4route in self.v4_routes[self.mode]: self.controller.table_add("v4_routing", "set_egress_port", [v4route['net']], [v4route['port']]) def config_hosts(self): """ Assumptions: - all routes are networks (no /128 v6 or /32 v4 - hosts get the first ip address in the network """ for v6route in self.v6_routes[self.mode]: host = "h{}".format(v6route['port']) dev = "{}-eth0".format(host) net = ipaddress.ip_network(v6route['net']) ipaddr = "{}/{}".format(net[1],net.prefix_length) subprocess.run(["mx", host, "ip", "addr", "flush", "dev", dev]) subprocess.run(["mx", host, "sysctl", "net.ipv6.conf.lo.disable_ipv6=0"]) subprocess.run(["mx", host, "sysctl", "net.ipv6.conf.{}.disable_ipv6=0".format(dev)]) subprocess.run(["mx", host, "ip", "addr", "add", ipaddr, "dev", dev]) # mx h$i "ip -6 route add default via 2001:db8:6::42" #for v4route in self.v4_routes[self.mode]: def debug_print_pkg(self, pkg, msg="INCOMING"): log.info("{}: {}".format(msg, self.debug_format_pkg(pkg))) def debug_format_pkg(self, pkg): packet = Ether(str(pkg)) if packet.type == 0x800: ip = pkg.getlayer(IP) elif packet.type == 0x86dd: ip = pkg.getlayer(IPv6) # tcp = pkg.getlayer(TCP) # raw = pkg.getlayer(Raw) # return "{}:{} => {}:{}: flags={} seq={} ack={} raw={}".format( # ip.src, tcp.sport, # ip.dst, tcp.dport, # tcp.flags, # tcp.seq, # tcp.ack, # raw) def recv_msg_cpu(self, pkg): packet = Ether(str(pkg)) if packet.type == 0x800: pass elif packet.type == 0x86dd: pass else: print("Broken pkg: {}".format(pkg)) return def run_cpu_port_loop(self): sniff(iface=self.intf, prn=self.recv_msg_cpu) def commandline(self): parser = argparse.ArgumentParser(description='controller++') parser.add_argument('--mode', help='Select mode / settings to use', choices=self.modes) args = parser.parse_args() self.mode = args.mode if __name__ == "__main__": import sys import os if "DEBUG" in os.environ: log.setLevel(logging.DEBUG) else: log.setLevel(logging.INFO) log.info("Booting...") log.debug("Debug enabled.") sw_name = "s1" controller = L2Controller(sw_name) controller.commandline() controller.config() controller.run_cpu_port_loop()