[controller] insert 6->4 session
This commit is contained in:
parent
09a15b6c25
commit
b0b9cd7461
3 changed files with 118 additions and 7 deletions
20
doc/plan.org
20
doc/plan.org
|
@ -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)
|
||||||
|
|
|
@ -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]))
|
||||||
|
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue