# 網路模擬-anti TCP FIN Port Scan ###### tags: `P4`、`Mininet` ------- Question === - H1 will use TCP FIN to do the port scan. S1(p4 switch) will detect it and block the packet sent from H1. - Detect method: when the number of FIN packets - the number of FIN packets > 3, then S1 will block the connection. Environment === ![](https://i.imgur.com/ju8Pley.png) >TCP threehandshake about fin port scan. ![](https://i.imgur.com/O6UEGZD.png) Codes === > basic.p4 ↓ ``` #include <core.p4> #include <v1model.p4> typedef bit<48> macAddr_t; typedef bit<9> egressSpec_t; header arp_t { bit<16> htype; bit<16> ptype; bit<8> hlen; bit<8> plen; bit<16> opcode; bit<48> hwSrcAddr; bit<32> protoSrcAddr; bit<48> hwDstAddr; bit<32> protoDstAddr; } header ethernet_t { bit<48> dstAddr; bit<48> srcAddr; bit<16> etherType; } header ipv4_t { bit<4> version; bit<4> ihl; bit<8> diffserv; bit<16> totalLen; bit<16> identification; bit<3> flags; bit<13> fragOffset; bit<8> ttl; bit<8> protocol; bit<16> hdrChecksum; bit<32> srcAddr; bit<32> dstAddr; } struct metadata { } struct headers { @name(".arp") arp_t arp; @name(".ethernet") ethernet_t ethernet; @name(".ipv4") ipv4_t ipv4; } parser ParserImpl(packet_in packet, out headers hdr, inout metadata meta, inout standard_metadata_t standard_metadata) { @name(".parse_arp") state parse_arp { packet.extract(hdr.arp); transition accept; } @name(".parse_ethernet") state parse_ethernet { packet.extract(hdr.ethernet); transition select(hdr.ethernet.etherType) { 16w0x800: parse_ipv4; 16w0x806: parse_arp; default: accept; } } @name(".parse_ipv4") state parse_ipv4 { packet.extract(hdr.ipv4); transition accept; } @name(".start") state start { transition parse_ethernet; } } control egress(inout headers hdr, inout metadata meta, inout standard_metadata_t standard_metadata) { action tocpu() { clone(CloneType.E2E,100); } table send2cpu { actions = { tocpu; NoAction; } key = { hdr.ipv4.srcAddr: lpm; } size = 512; const default_action = NoAction; } apply { if(hdr.ipv4.isValid() && standard_metadata.instance_type == 0){ send2cpu.apply(); } } } control ingress(inout headers hdr, inout metadata meta, inout standard_metadata_t standard_metadata) { action forward(macAddr_t dstAddr, egressSpec_t port) { //set the src mac address as the previous dst, this is not correct right? hdr.ethernet.srcAddr = hdr.ethernet.dstAddr; //set the destination mac address that we got from the match in the table hdr.ethernet.dstAddr = dstAddr; //set the output port that we also get from the table standard_metadata.egress_spec = port; //decrease ttl by 1 hdr.ipv4.ttl = hdr.ipv4.ttl - 1; } action _drop() { mark_to_drop(standard_metadata); } table ipv4_lpm { actions = { forward; _drop; } key = { hdr.ipv4.dstAddr: exact; } size = 512; const default_action = _drop(); } table block_pkt { actions = { NoAction; _drop; } key = { hdr.ipv4.srcAddr: exact; } size = 512; const default_action = NoAction(); } apply { ipv4_lpm.apply(); block_pkt.apply(); } } control DeparserImpl(packet_out packet, in headers hdr) { apply { packet.emit(hdr.ethernet); packet.emit(hdr.arp); packet.emit(hdr.ipv4); } } control verifyChecksum(inout headers hdr, inout metadata meta) { apply { verify_checksum(true, { hdr.ipv4.version, hdr.ipv4.ihl, hdr.ipv4.diffserv, hdr.ipv4.totalLen, hdr.ipv4.identification, hdr.ipv4.flags, hdr.ipv4.fragOffset, hdr.ipv4.ttl, hdr.ipv4.protocol, hdr.ipv4.srcAddr, hdr.ipv4.dstAddr }, hdr.ipv4.hdrChecksum, HashAlgorithm.csum16); } } control computeChecksum(inout headers hdr, inout metadata meta) { apply { update_checksum(true, { hdr.ipv4.version, hdr.ipv4.ihl, hdr.ipv4.diffserv, hdr.ipv4.totalLen, hdr.ipv4.identification, hdr.ipv4.flags, hdr.ipv4.fragOffset, hdr.ipv4.ttl, hdr.ipv4.protocol, hdr.ipv4.srcAddr, hdr.ipv4.dstAddr }, hdr.ipv4.hdrChecksum, HashAlgorithm.csum16); } } V1Switch(ParserImpl(), verifyChecksum(), ingress(), egress(), computeChecksum(), DeparserImpl()) main; ``` > p4app.json ↓ ``` { "program": "basic.p4", "switch": "simple_switch", "compiler": "p4c", "options": "--target bmv2 --arch v1model --std p4-16", "switch_cli": "simple_switch_CLI", "cli": true, "pcap_dump": true, "enable_log": true, "topo_module": { "file_path": "", "module_name": "p4utils.mininetlib.apptopo", "object_name": "AppTopoStrategies" }, "controller_module": null, "topodb_module": { "file_path": "", "module_name": "p4utils.utils.topology", "object_name": "Topology" }, "mininet_module": { "file_path": "", "module_name": "p4utils.mininetlib.p4net", "object_name": "P4Mininet" }, "topology": { "assignment_strategy": "manual", "auto_arp_tables": "true", "auto_gw_arp": "true", "links": [["h1", "s1"], ["h2", "s1"],["h3", "s1"]], "hosts": { "h1": { "ip": "10.0.1.1/24", "gw": "10.0.1.254" }, "h2": { "ip": "10.0.2.1/24", "gw": "10.0.2.254" }, "h3": { "ip": "10.0.3.1/24", "gw": "10.0.3.254" } }, "switches": { "s1": { "cli_input": "cmd.txt", "program": "basic.p4", "cpu_port": true } } } } ``` > cmd.txt ↓ ``` table_add ipv4_lpm forward 10.0.1.1 => 00:00:0a:00:01:01 1 table_add ipv4_lpm forward 10.0.2.1 => 00:00:0a:00:02:01 2 table_add ipv4_lpm forward 10.0.3.1 => 00:00:0a:00:03:01 3 #table_add block_pkt _drop 10.0.1.1 => table_add send2cpu tocpu 10.0.1.0/24 => table_add send2cpu tocpu 10.0.2.0/24 => table_add send2cpu tocpu 10.0.3.0/24 => mirroring_add 100 4 ``` > controller.py ``` import nnpy import struct from p4utils.utils.topology import Topology from p4utils.utils.sswitch_API import SimpleSwitchAPI from scapy.all import Ether, sniff, Packet, TCP from collections import Counter count1= Counter() count2= Counter() val1=0 val2=0 blockip=[] class myController(object): def __init__(self): self.topo = Topology(db="topology.db") self.controllers = {} self.connect_to_switches() def connect_to_switches(self): for p4switch in self.topo.get_p4switches(): thrift_port = self.topo.get_thrift_port(p4switch) #print "p4switch:", p4switch, "thrift_port:", thrift_port self.controllers[p4switch] = SimpleSwitchAPI(thrift_port) def recv_msg_cpu(self, pkt): print "interface:", pkt.sniffed_on print "summary:", pkt.summary() global val1,val2 if TCP in pkt and pkt[TCP].flags==2: src = pkt.sprintf('{IP:%IP.src%}') dst = pkt.sprintf('{IP:%IP.dst%}') count1[(src, dst)] += 1 val1=count1[(src, dst)] print "count1[",src,",",dst,"]=",count1[(src, dst)] if TCP in pkt and pkt[TCP].flags==18: src = pkt.sprintf('{IP:%IP.src%}') dst = pkt.sprintf('{IP:%IP.dst%}') count2[(dst, src)] += 1 val2=count2[(dst, src)] print "count2[",dst,",",src,"]=",count2[(dst, src)] print "val1:", val1, " val2:", val2 if (val1-val2>=3) and (TCP in pkt) and pkt[TCP].flags==2: src = pkt.sprintf('{IP:%IP.src%}') if src not in blockip: self.controllers["s1"].table_add("block_pkt", "_drop", [str(src)], []) blockip.append(src) def run_cpu_port_loop(self): cpu_interfaces = [str(self.topo.get_cpu_port_intf(sw_name).replace("eth0", "eth1")) for sw_name in self.controllers] sniff(iface=cpu_interfaces, prn=self.recv_msg_cpu) if __name__ == "__main__": controller = myController() controller.run_cpu_port_loop() ``` Test === > anti syn port scan, if malicious packet transmission over than 3, controller will set a rule about "drop packet" and back to p4-switch. ![](https://i.imgur.com/DyvD7qi.png) >h1 send a packet to h3, and h1's ip is block. ![](https://i.imgur.com/sqOP06u.png) Reference === [anti port scan](http://csie.nqu.edu.tw/smallko/sdn/anti_tcp_syn_port_scan.htm) --- ###### 2020-06-29 ###### written by **yi-hao tu**