master-thesis/p4app/controller.py

275 lines
8.9 KiB
Python
Raw Normal View History

from __future__ import unicode_literals
2019-02-21 22:38:09 +00:00
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
2019-02-23 14:13:47 +00:00
import subprocess
# Broken in python2
import ipaddress
2019-02-21 22:38:09 +00:00
logging.basicConfig()
log = logging.getLogger("main")
class CpuHeader(Packet):
name = 'CpuPacket'
fields_desc = [BitField('ingress_port', 0, 16)]
2019-02-21 22:38:09 +00:00
class L2Controller(object):
def __init__(self, sw_name):
2019-02-23 13:36:19 +00:00
# Command line mapping
2019-02-23 17:58:04 +00:00
self.modes = ['base', 'router']
self.info={}
self.info['ndp_multicast'] = ipaddress.ip_network(u"ff02::1:ff00:0/104")
2019-02-23 17:58:04 +00:00
self.address_suffix = 42
# 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"})
2019-02-23 17:58:04 +00:00
self.v6_routes['router'] = self.v6_routes['base']
2019-02-23 13:36:19 +00:00
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"})
2019-02-23 17:58:04 +00:00
self.v4_routes['router'] = self.v4_routes['base']
self.v6_addresses = {}
self.v6_addresses[None] = []
self.v6_addresses['base'] = []
self.v6_addresses['router'] = [ { "addr": "{}{}".format(n['net'], self.address_suffix), "port": n['port'] } for n in self.v6_routes['router'] ]
self.v4_addresses = {}
self.v4_addresses[None] = []
self.v4_addresses['base'] = []
self.v4_addresses['router'] = [ { "addr": "{}{}".format(n['net'], self.address_suffix), "port": n['port'] } for n in self.v4_routes['router'] ]
2019-02-23 13:36:19 +00:00
self.init_boilerplate(sw_name)
@staticmethod
def prefix_to_net(net, mask):
""" work around ipaddress bug """
2019-02-23 17:25:53 +00:00
net = "{}0/{}".format(net, mask)
log.debug("net: {}".format(net))
return net
@staticmethod
def prefix_to_host(net, mask):
""" work around ipaddress bug """
2019-02-23 17:25:53 +00:00
host = "{}1/{}".format(net, mask)
log.debug("host: {}".format(host))
return "{}1/{}".format(net, mask)
2019-02-23 17:17:00 +00:00
@staticmethod
def add_host_ips(host, net, ipaddr, dev):
subprocess.call(["mx", host, "ip", "addr", "flush", "dev", dev])
for v6dev in [ "lo", "default", "all", dev ]:
subprocess.call(["mx", host, "sysctl", "net.ipv6.conf.{}.disable_ipv6=0".format(v6dev)])
# Set down & up to regain link local address
subprocess.call(["mx", host, "ip", "link", "set", dev, "down"])
subprocess.call(["mx", host, "ip", "link", "set", dev, "up"])
# Now add global address
subprocess.call(["mx", host, "ip", "addr", "add", ipaddr, "dev", dev])
def init_ndp(self):
""" initialise neighbor discovery protocol"""
# https://en.wikipedia.org/wiki/Solicited-node_multicast_address
ndp_prefix = "ff02::1:ff00:0/104"
all_ports = range(1,5)
# create multicast nodes
for rid in range(1,5):
ports = [ x for x in all_ports if not x == rid ]
n_handle = self.controller.mc_node_create(rid, ports)
log.debug("Creating MC node rid={} ports={} handle={}".format(rid, ports, n_handle))
g_handle = self.controller.mc_mgrp_create(rid)
log.debug("Creating MC group mgrp={} handle={} && associating afterwards".format(rid, g_handle))
self.controller.mc_node_associate(g_handle, n_handle)
self.controller.table_clear("ndp")
for port in all_ports:
self.controller.table_add("ndp", "multicast_pkg", [ndp_prefix, str(port)], [str(port)])
2019-02-28 09:56:22 +00:00
# Special rule for switch entries
self.controller.table_add("ndp_answer", "icmp6_neighbor_solicitation", ["ff02::1:ff00:42", "135"], ["2001:db8:61::42"])
2019-02-21 22:38:09 +00:00
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)
2019-03-03 21:37:58 +00:00
# self.init_ndp()
def config(self):
self.fill_tables()
self.config_hosts()
2019-02-23 17:58:04 +00:00
2019-02-21 22:38:09 +00:00
def fill_tables(self):
self.controller.table_clear("v6_networks")
for v6route in self.v6_routes[self.mode]:
net = self.prefix_to_net(v6route['net'], self.v6_mask)
self.controller.table_add("v6_networks", "set_egress_port", [net], [v6route['port']])
net = str(self.info['ndp_multicast'])
self.controller.table_add("v6_networks", "controller_reply", [net])
self.controller.table_clear("v4_routing")
for v4route in self.v4_routes[self.mode]:
net = self.prefix_to_net(v4route['net'], self.v4_mask)
2019-02-23 17:28:47 +00:00
self.controller.table_add("v4_routing", "set_egress_port", [net], [v4route['port']])
2019-02-23 17:58:04 +00:00
self.controller.table_clear("v6_addresses")
for v6addr in self.v6_addresses[self.mode]:
2019-03-03 21:37:58 +00:00
self.controller.table_add("v6_addresses", "controller_reply", [v6addr['addr']])
2019-02-23 17:58:04 +00:00
def gen_ndp_multicast_addr(self, addr):
""" append the 24 bit of the address to the multicast address"""
return ipaddress.ip_address(int(self.info['ndp_multicast']) + (int(addr) & 0xffffff))
2019-02-23 17:58:04 +00:00
def config_hosts(self):
""" Assumptions:
- all routes are networks (no /128 v6 or /32 v4
- hosts get the first ip address in the network
"""
2019-02-23 14:13:47 +00:00
for v6route in self.v6_routes[self.mode]:
2019-02-23 14:13:47 +00:00
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)
2019-02-23 14:13:47 +00:00
# mx h$i "ip -6 route add default via 2001:db8:6::42"
#for v4route in self.v4_routes[self.mode]:
# Todo: add v4 routes / ip addresses
# Fake mac addresses - this gets REALLY messy now
# for host in range(4):
# macrange = [1, 2, 3, 4, 42]
# for mac in macrange:
# subprocess.call(["mx", host, "ip", "addr", "add", ipaddr, "dev", dev])
2019-02-21 22:38:09 +00:00
def debug_print_pkg(self, pkg, msg="INCOMING"):
2019-02-24 14:58:15 +00:00
#log.debug("{}: {}".format(msg, self.debug_format_pkg(pkg)))
log.debug("{}: {}".format(msg, pkg.__repr__()))
2019-02-21 22:38:09 +00:00
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))
2019-02-24 14:54:32 +00:00
self.debug_print_pkg(pkg)
if packet.type == 0x0800:
2019-02-21 22:38:09 +00:00
pass
elif packet.type == 0x86dd:
pass
elif packet.type == 0x4242:
print("Special handling needed")
pass
elif packet.type == 0x2323:
print("Debug pkg")
pass
2019-02-21 22:38:09 +00:00
else:
2019-03-04 13:24:02 +00:00
print("Broken pkg: {}".format(pkg.__repr__()))
2019-02-21 22:38:09 +00:00
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)
2019-02-23 17:25:53 +00:00
parser.add_argument('--debug', help='Enable debug logging', action='store_true')
2019-02-23 17:26:34 +00:00
self.args = parser.parse_args()
2019-02-23 17:27:09 +00:00
self.mode = self.args.mode
2019-02-21 22:38:09 +00:00
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()
2019-02-23 17:25:53 +00:00
if controller.args.debug:
log.setLevel(logging.DEBUG)
controller.config()
controller.run_cpu_port_loop()