78 lines
2.7 KiB
Python
78 lines
2.7 KiB
Python
# Copyright 2013-present Barefoot Networks, Inc.
|
|
#
|
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
# you may not use this file except in compliance with the License.
|
|
# You may obtain a copy of the License at
|
|
#
|
|
# http://www.apache.org/licenses/LICENSE-2.0
|
|
#
|
|
# Unless required by applicable law or agreed to in writing, software
|
|
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
# See the License for the specific language governing permissions and
|
|
# limitations under the License.
|
|
|
|
from scapy.all import *
|
|
import subprocess
|
|
import os
|
|
|
|
CLI_PATH = None
|
|
|
|
EXTERN_IP = "192.168.0.1"
|
|
|
|
current_nat_port = 1025
|
|
nat_mappings = {}
|
|
|
|
def send_to_CLI(cmd):
|
|
this_dir = os.path.dirname(os.path.realpath(__file__))
|
|
p = Popen(os.path.join(this_dir, 'sswitch_CLI.sh'), stdout=PIPE, stdin=PIPE)
|
|
output = p.communicate(input=cmd)[0]
|
|
# print output
|
|
|
|
# This is a very basic implementation of a full-cone NAT for TCP traffic
|
|
# We do not maintain a state machine for each connection, so we are not able to
|
|
# cleanup the port mappings, but this is sufficient for demonstration purposes
|
|
def process_cpu_pkt(p):
|
|
global current_nat_port
|
|
global EXTERN_IP
|
|
|
|
p_str = str(p)
|
|
# 0-7 : preamble
|
|
# 8 : device
|
|
# 9 : reason
|
|
# 10 : iface
|
|
# 11- : data packet (TCP)
|
|
if p_str[:8] != '\x00' * 8 or p_str[8] != '\x00' or p_str[9] != '\xab':
|
|
return
|
|
ip_hdr = None
|
|
tcp_hdr = None
|
|
try:
|
|
p2 = Ether(p_str[11:])
|
|
ip_hdr = p2['IP']
|
|
tcp_hdr = p2['TCP']
|
|
except:
|
|
return
|
|
print "Packet received"
|
|
print p2.summary()
|
|
if (ip_hdr.src, tcp_hdr.sport) not in nat_mappings:
|
|
ext_port = current_nat_port
|
|
current_nat_port += 1
|
|
print "Allocating external port", ext_port
|
|
nat_mappings[(ip_hdr.src, tcp_hdr.sport)] = ext_port
|
|
# internal to external rule for this mapping
|
|
send_to_CLI("table_add nat nat_hit_int_to_ext 0 1 1 %s&&&255.255.255.255 0.0.0.0&&&0.0.0.0 %d&&&0xffff 0&&&0 => %s %d 1" %\
|
|
(ip_hdr.src, tcp_hdr.sport, EXTERN_IP, ext_port))
|
|
# external to internal rule for this mapping
|
|
send_to_CLI("table_add nat nat_hit_ext_to_int 1 1 1 0.0.0.0&&&0.0.0.0 %s&&&255.255.255.255 0&&&0 %d&&&0xffff => %s %d 1" %\
|
|
(EXTERN_IP, ext_port, ip_hdr.src, tcp_hdr.sport))
|
|
# a little bit hacky, this essentially ensures that the packet we re-inject
|
|
# in the CPU iface will not be processed again by this method
|
|
new_p = p_str[:9] + '\xac' + p_str[10:]
|
|
sendp(new_p, iface="cpu-veth-0", verbose=0)
|
|
|
|
def main():
|
|
sniff(iface="cpu-veth-0", prn=lambda x: process_cpu_pkt(x))
|
|
|
|
if __name__ == '__main__':
|
|
main()
|