[controller] insert 6->4 session

This commit is contained in:
Nico Schottelius 2019-05-06 12:16:22 +02:00
parent 09a15b6c25
commit b0b9cd7461
3 changed files with 118 additions and 7 deletions

View file

@ -1432,7 +1432,6 @@ Please make sure that it is installed and available in your $PATH:
steps steps
- Longer than /96: suffix support - Longer than /96: suffix support
** Motivation ** Motivation
TBD TBD
** Translation mechanisms ** Translation mechanisms
@ -1856,10 +1855,23 @@ libnanomsg-dev libjudy-dev
***** TODO Case IPv4 initiator ***** TODO Case IPv4 initiator
- Needs upper level protol - Needs upper level protol
**** TODO General network matching **** TODO General network matching
***** TODO Create table ***** DONE Create table(s)
***** TODO Fill it up from the controller ***** DONE Fill it up from the controller: general network
**** TODO tcp session ***** TODO Create controller session handler
****** Controller Logic
- controller selects "outgoing" IPv4 address range => base for sessions
- IPv4 addresses can be "random" (in our test case), but need
to be unique
- switch does not need to know about the "range", only about
sessions
- on session create, controller selects "random" ip (ring?)
- on session create, controller selects "random port" (next in range?)
- on session create controller adds choice into 2 tables:
incoming, outgoing
***** DONE Feed back to controller: implemented in switch
***** TODO Create session in the controller
**** TODO tcp session
**** TODO udp session **** TODO udp session
**** TODO tcp session **** TODO tcp session
** TODO Comparison with existing tools (Performance, Features) ** TODO Comparison with existing tools (Performance, Features)

View file

@ -26,7 +26,8 @@ cpu_fields = {
0: 'UNSET', 0: 'UNSET',
1: 'ICMP6_NS', 1: 'ICMP6_NS',
2: 'ICMP6_GENERAL', 2: 'ICMP6_GENERAL',
3: 'DEBUG' 3: 'DEBUG',
8: 'NAT64_TCP_SESSION'
} }
table_id_fields = { table_id_fields = {
@ -73,7 +74,7 @@ class CpuHeader(Packet):
class L2Controller(object): class L2Controller(object):
def __init__(self, sw_name): def __init__(self, sw_name):
# Command line mapping # Command line mapping
self.modes = ['base', 'router', "range_router" ] self.modes = ['base', 'router', "range_router", "session_router" ]
# Reverse maps the cpu header # Reverse maps the cpu header
self.task = dict(reversed(item) for item in cpu_fields.items()) self.task = dict(reversed(item) for item in cpu_fields.items())
@ -112,11 +113,13 @@ class L2Controller(object):
# /96 after the /40 pool we use above # /96 after the /40 pool we use above
self.info['nat64_prefix_dynamic'] = ipaddress.ip_network("2001:db8:100::/96") self.info['nat64_prefix_dynamic'] = ipaddress.ip_network("2001:db8:100::/96")
self.info['nat64_tcp_session'] = {}
self.v6_routes = {} self.v6_routes = {}
self.v6_routes[None] = [] self.v6_routes[None] = []
self.v6_routes['base'] = [] self.v6_routes['base'] = []
self.ports = [] self.ports = []
for port in range(1,3): for port in range(1,3):
@ -130,8 +133,10 @@ class L2Controller(object):
self.v6_routes['router'] = self.v6_routes['base'] self.v6_routes['router'] = self.v6_routes['base']
# only 1 route to avoid table duplicate/conflict # only 1 route to avoid table duplicate/conflict
self.v6_routes['range_router'] = self.v6_routes['base'][0:1] self.v6_routes['range_router'] = self.v6_routes['base'][0:1]
self.v6_routes['session_router'] = self.v6_routes['range_router']
self.v4_routes = {} self.v4_routes = {}
self.v4_routes[None] = [] self.v4_routes[None] = []
@ -147,6 +152,7 @@ class L2Controller(object):
self.v4_routes['router'] = self.v4_routes['base'] self.v4_routes['router'] = self.v4_routes['base']
self.v4_routes['range_router'] = self.v4_routes['base'] self.v4_routes['range_router'] = self.v4_routes['base']
self.v4_routes['session_router'] = self.v4_routes['base']
self.v6_addresses = {} self.v6_addresses = {}
self.v6_addresses[None] = [] self.v6_addresses[None] = []
@ -159,9 +165,12 @@ class L2Controller(object):
self.v4_addresses[mode] = [ net['net'][self.info['switch_suffix']] for net in self.v4_routes[mode] ] self.v4_addresses[mode] = [ net['net'][self.info['switch_suffix']] for net in self.v4_routes[mode] ]
self.nat64_map = {} self.nat64_map = {}
self.nat64_session_net = {}
# init default # init default
for mode in self.modes: for mode in self.modes:
self.nat64_map[mode] = [] self.nat64_map[mode] = []
self.nat64_session_net[mode] = []
# specific settings - mapping 256 IPv6 IPs max statically (based on /24) # specific settings - mapping 256 IPv6 IPs max statically (based on /24)
for mode in ["range_router"]: for mode in ["range_router"]:
@ -183,6 +192,16 @@ class L2Controller(object):
"v4_dst": v4_dst "v4_dst": v4_dst
}) })
# allow session translation
# TODO: maybe support multiple networks
for mode in ["session_router"]:
self.nat64_session_net[mode] = [
{ 'v4_net': self.info['v4_nat64_map'].next(),
'v6_net': self.info['nat64_prefix_dynamic'],
'v4_idx': 1
}
]
self.init_boilerplate(sw_name) self.init_boilerplate(sw_name)
self.init_other_port_multicast_groups() self.init_other_port_multicast_groups()
@ -313,6 +332,23 @@ class L2Controller(object):
for nat64map in self.nat64_map[self.mode]: for nat64map in self.nat64_map[self.mode]:
self.static_nat64_mapping(**nat64map) self.static_nat64_mapping(**nat64map)
# NAT64 session based
self.controller.table_clear("nat64_session")
# These will be only populated dynamically
self.controller.table_clear("nat64_tcp_session")
self.controller.table_clear("nat46_tcp_session")
for net in self.nat64_session_net[mode]:
# Only for matching / if selecting
self.controller.table_add("nat64_session", "NoAction",
[
net['v6_net']
]
)
def static_nat64_mapping(self, v6_src, v6_dst, v4_src, v4_dst): def static_nat64_mapping(self, v6_src, v6_dst, v4_src, v4_dst):
""" """
Currently using destination only matching due to non priority Currently using destination only matching due to non priority
@ -475,6 +511,67 @@ class L2Controller(object):
self.send_pkg(answer) self.send_pkg(answer)
def nat64_tcp_session_entry(self, ipv6_src_addr, ipv6_dst_addr,
tcp_src_port, tcp_dst_port):
return "{}:{} - {}:{}".format(ipv6_src_addr, tcp_src_port,
ipv6_dst_addr, tcp_dst_port)
def nat64_create_tcp_session(self, pkg):
"""
session:
hdr.ipv6.src_addr: exact;
hdr.ipv6.dst_addr: exact;
hdr.tcp.src_port: exact;
hdr.tcp.dst_port: exact;
"""
id = self.nat64_tcp_session_entry(pkg[IPv6].src,
pkg[TCP].src_port,
pkg[IPv6].dst,
pkg[TCP].dst_port)
# Has already been added? then it's a race condition and
# it needs to go back to the switch
# Not in the table? create an entry!
if not id in self.info['nat64_tcp_session']:
# FIXME: Change randomly later, supporting 1:N mappings
# Keep the same for the moment
tcp_src_port = pkg[TCP].src_port
tcp_dst_port = pkg[TCP].dst_port
# FIXME: range, reuse, etc.
ipv4_src_addr = self.nat64_session_net[self.mode]
self.nat64_session_net[self.mode] += 1
self.controller.table_add("nat64_tcp_session",
"nat64_tcp_session_translate",
[
pkg[IPv6].src,
pkg[TCP].src_port,
pkg[IPv6].dst,
pkg[TCP].dst_port
],
[
ipv4_src_addr,
tcp_src_port,
ipv4_dst_addr,
tcp_dst_port
]
)
self.send_pkg(pkg)
def send_pkg(self, pkg): def send_pkg(self, pkg):
self.debug_print_pkg(pkg, "OUTGOING") self.debug_print_pkg(pkg, "OUTGOING")
sendp(pkg, iface=self.intf, verbose=False) sendp(pkg, iface=self.intf, verbose=False)
@ -494,6 +591,7 @@ class L2Controller(object):
ether_orig = Ether(src=packet.src, dst=packet.dst, type=cpu_header.type) ether_orig = Ether(src=packet.src, dst=packet.dst, type=cpu_header.type)
# Note to myself: this is actually broken for ARP
if cpu_header.type == 0x0800: if cpu_header.type == 0x0800:
orig_packet = ether_orig / IP(cpu_header.load) orig_packet = ether_orig / IP(cpu_header.load)
elif cpu_header.type == 0x86dd: elif cpu_header.type == 0x86dd:
@ -520,6 +618,8 @@ class L2Controller(object):
elif cpu_header.task == self.task['ICMP6_GENERAL']: elif cpu_header.task == self.task['ICMP6_GENERAL']:
if ICMPv6EchoRequest in orig_packet: if ICMPv6EchoRequest in orig_packet:
self.handle_icmp6_echo_request(orig_packet) self.handle_icmp6_echo_request(orig_packet)
elif cpu_header.task == self.task['NAT64_TCP_SESSION']:
self.nat64_create_tcp_session(orig_packet)
else: else:
log.info("unhandled reassambled={} from table {}".format(orig_packet.__repr__(), table_id_fields[cpu_header.table_id])) log.info("unhandled reassambled={} from table {}".format(orig_packet.__repr__(), table_id_fields[cpu_header.table_id]))

View file

@ -298,7 +298,6 @@ Echo or Echo Reply Message
NoAction; NoAction;
} }
size = NAT64_TABLE_SIZE; size = NAT64_TABLE_SIZE;
//default_action = controller_debug_table_id(TABLE_NAT64_TCP);
default_action = nat64_tcp_session_create; default_action = nat64_tcp_session_create;
} }