183 lines
5.6 KiB
Python
183 lines
5.6 KiB
Python
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 subprocess
|
|
|
|
# Broken in python2
|
|
#import ipaddress
|
|
|
|
|
|
logging.basicConfig()
|
|
log = logging.getLogger("main")
|
|
|
|
class L2Controller(object):
|
|
def __init__(self, sw_name):
|
|
# Command line mapping
|
|
self.modes = ['base']
|
|
|
|
# Network / egress
|
|
self.v6_mask = "64"
|
|
|
|
self.v6_routes = {}
|
|
self.v6_routes[None] = []
|
|
self.v6_routes['base'] = []
|
|
self.v6_routes['base'].append({ "net": "2001:db8:61::", "port": "1"})
|
|
self.v6_routes['base'].append({ "net": "2001:db8:62::", "port": "2"})
|
|
|
|
self.v4_mask = "24"
|
|
self.v4_routes = {}
|
|
self.v4_routes[None] = []
|
|
self.v4_routes['base'] = []
|
|
self.v4_routes['base'].append({ "net": "10.0.41.", "port": "3"})
|
|
self.v4_routes['base'].append({ "net": "10.0.42.", "port": "4"})
|
|
|
|
self.init_boilerplate(sw_name)
|
|
|
|
@staticmethod
|
|
def prefix_to_net(net, mask):
|
|
""" work around ipaddress bug """
|
|
net = "{}0/{}".format(net, mask)
|
|
log.debug("net: {}".format(net))
|
|
return net
|
|
|
|
@staticmethod
|
|
def prefix_to_host(net, mask):
|
|
""" work around ipaddress bug """
|
|
host = "{}1/{}".format(net, mask)
|
|
log.debug("host: {}".format(host))
|
|
return "{}1/{}".format(net, mask)
|
|
|
|
@staticmethod
|
|
def add_host_ips(host, net, ipaddr, dev):
|
|
subprocess.call(["mx", host, "ip", "addr", "flush", "dev", dev])
|
|
subprocess.call(["mx", host, "sysctl", "net.ipv6.conf.lo.disable_ipv6=0"])
|
|
subprocess.call(["mx", host, "sysctl", "net.ipv6.conf.{}.disable_ipv6=0".format(dev)])
|
|
subprocess.call(["mx", host, "ip", "addr", "add", ipaddr, "dev", dev])
|
|
|
|
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]:
|
|
net = self.prefix_to_net(v6route['net'], self.v6_mask)
|
|
self.controller.table_add("v6_routing", "set_egress_port", [net], [v6route['port']])
|
|
|
|
self.controller.table_clear("v4_routing")
|
|
for v4route in self.v4_routes[self.mode]:
|
|
net = self.prefix_to_net(v4route['net'], self.v4_mask)
|
|
self.controller.table_add("v4_routing", "set_egress_port", [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)
|
|
net = self.prefix_to_net(v6route['net'], self.v6_mask)
|
|
ipaddr = self.prefix_to_host(v6route['net'], self.v6_mask)
|
|
|
|
self.add_host_ips(host, net, ipaddr, 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)
|
|
parser.add_argument('--debug', help='Enable debug logging', action='store_true')
|
|
|
|
self.args = parser.parse_args()
|
|
self.mode = self.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()
|
|
if controller.args.debug:
|
|
log.setLevel(logging.DEBUG)
|
|
controller.config()
|
|
controller.run_cpu_port_loop()
|