634 |
635 | This program is free software: you can redistribute it and/or modify
636 | it under the terms of the GNU Affero General Public License as published
637 | by the Free Software Foundation, either version 3 of the License, or
638 | (at your option) any later version.
639 |
640 | This program is distributed in the hope that it will be useful,
641 | but WITHOUT ANY WARRANTY; without even the implied warranty of
642 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
643 | GNU Affero General Public License for more details.
644 |
645 | You should have received a copy of the GNU Affero General Public License
646 | along with this program. If not, see .
647 |
648 | Also add information on how to contact you by electronic and paper mail.
649 |
650 | If your software can interact with users remotely through a computer
651 | network, you should also make sure that it provides a way for users to
652 | get its source. For example, if your program is a web application, its
653 | interface could display a "Source" link that leads users to an archive
654 | of the code. There are many ways you could offer source, and different
655 | solutions will be better for different programs; see section 13 for the
656 | specific requirements.
657 |
658 | You should also get your employer (if you work as a programmer) or school,
659 | if any, to sign a "copyright disclaimer" for the program, if necessary.
660 | For more information on this, and how to apply and follow the GNU AGPL, see
661 | .
662 |
--------------------------------------------------------------------------------
/Makefile:
--------------------------------------------------------------------------------
1 | #application
2 |
3 | export POX_PATH := /opt/ik2220/pox
4 | export APP_PATH := ik2220
5 | app:
6 | #mkdir $$POX_PATH/ext/$$APP_PATH
7 | #python -m compileall ./sdn/task1/
8 | cp sdn/task1/* $$POX_PATH/ext/
9 | python -m compileall $$POX_PATH/ext/
10 | sudo python $$POX_PATH/pox.py l2_firewall &
11 | #sudo python $$POX_PATH/pox.py $$APP_PATH.l2_firewall
12 |
13 | clean:
14 | sudo pkill -9 -f pox.py
15 |
16 | #topology
17 |
18 | topo:
19 | sudo python phase1.py
20 |
21 | clean:
22 | sudo mn -c
23 |
24 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | [](https://travis-ci.org/shrin18/SDN_Project)
2 |
3 | ## SDN_Project
4 |
5 |
6 |
7 | This is a project based on the above topology which incorporates the use of Load Balanacer, Firewalls, Intrusion detecion system and the NAPT. The topology of this project is divided into three zones namely: Private, Public and DmZ. Stress Tests for the two firewalls are run by sending packets from one zone to the other. There are two sets of servers that are running a simpleHTTP and DNS server as configured in the dns.py scripts. Tests are also run for testing these servers using dig and curl commands respectively.
8 |
9 | Mininet Tutorial
10 | https://kth.instructure.com/courses/7689/pages/ik2220-tutorial-video-mininet+pox?module_item_id=134779e
11 |
12 | Visualiser
http://mininet.spear.narmox.com
13 | *Just paste your answer for links and dump in the given link and render graph for the topology for mininet*
14 |
15 | Repo for VND (Visual Network Descriptor)
https://github.com/ramonfontes/vnd
16 |
17 |
18 | Commands for Starting topology, firewall, click scripts independently
19 | 1. Topology
20 |
sudo python filename.py
21 | 2. Firewall
22 |
./pox.py log.level --DEBUG forwarding.l2_firewall
23 | 3. Click
24 |
sudo /usr/local/bin/click filename.click
25 |
--------------------------------------------------------------------------------
/_config.yml:
--------------------------------------------------------------------------------
1 | theme: jekyll-theme-cayman
--------------------------------------------------------------------------------
/click/Makefile:
--------------------------------------------------------------------------------
1 | export POX_PATH := /opt/ik2220/pox
2 | export APP_PATH := ik2220
3 | export PATHRR := $(CURDIR)
4 | app:
5 | sudo cp sdn/* $$POX_PATH/ext/
6 | sudo cp nfv/* $$POX_PATH/ext/
7 | sudo python -m compileall $$POX_PATH/ext/
8 | sudo python $$POX_PATH/pox.py l2_firewall --pathExec=$$PATHRR &
9 |
10 | clean:
11 | -sudo pkill -2 -f click
12 | -sudo pkill -9 -f pox
13 |
14 |
--------------------------------------------------------------------------------
/click/ids.click:
--------------------------------------------------------------------------------
1 | //Intrusion detection system
2 | // Benign -> HTTP POST, PUT
3 | // Malicious -> SQL injection (cat /etc/passwd, cat /var/log/, INSERT, UPDATE, DELETE),
4 | // HTTP GET, HEAD, OPTIONS, TRACE, DELETE, CONNECT
5 | /*
6 | +------ v ------+ +---------------+ +---------------+
7 | | | | | | |
8 | | SW2 |------------> IDS +----Benign--> LB2 |
9 | | | fromSwitch | | | |
10 | +---------------+ +-------+-------+ +---------------+
11 | |
12 | Malicious
13 | |
14 | +------ v ------+
15 | | |
16 | | INSP |
17 | | |
18 | +---------------+
19 | */
20 |
21 | ctr_pkt_in, ctr_pkt_malicious, ctr_pkt_benign :: AverageCounter;
22 |
23 | fromSwitch :: FromDevice(id8-eth1, METHOD LINUX, SNIFFER false);
24 | fromLoadbal :: FromDevice(id8-eth2, METHOD LINUX, SNIFFER false);
25 |
26 | toSwitch :: Queue -> ToDevice(id8-eth1); // connect sw2
27 | toInsp :: Queue -> ctr_pkt_malicious -> ToDevice(id8-eth3); // connect insp and count number of malicious packets
28 | toLoadbal :: Queue -> ctr_pkt_benign -> ToDevice(id8-eth2); // connect lb2 and count number of benign packets
29 |
30 | //Define patterns to look out for
31 | filter :: Classifier(
32 | 66/474554, // HTTP GET
33 | 66/48454144, // HTTP head
34 | 66/5452414345, // HTTP TRACE
35 | 66/4f5054494f4e53, //HTTP OPTIONS
36 | 66/44454c455445, //HTTP DELETE
37 | 66/434f4e4e454354, //HTTP CONNECT
38 | 209/636174202f6574632f706173737764, // "cat /etc/passwd"
39 | 209/636174202f7661722f6c6f672f, // "cat /var/log/"
40 | 208/494E53455254, //"INSERT"
41 | 208/555044415445, //"UPDATE"
42 | 208/44454C455445, //"DELETE"
43 | -);
44 |
45 |
46 | fromSwitch -> ctr_pkt_in -> filter; // Count incoming pkts before sending to filter
47 |
48 | filter[0],filter[1],filter[2],filter[3],filter[4],filter[5],filter[6],filter[7],filter[8],filter[9],filter[10] -> toInsp; // Send malicious pkts to Inspector
49 |
50 | filter[11] -> toLoadbal; //send all rest of the pkts(-) to loadbalancer2
51 |
52 | fromLoadbal -> toSwitch; // On return path ids should be transparant to all packets. Send all pkts from loadbalancer2 to sw2
53 |
54 |
55 | // report
56 | DriverManager(wait , print > $rPath/../results/ids.counter " #../results/ids.report
57 | =================== IDS Report ===================
58 | Input Packet Rate (pps): $(ctr_pkt_in.rate)
59 | Output Packet Rate(pps): $(ctr_pkt_benign.rate)
60 | Total # of input packets: $(ctr_pkt_in.count)
61 | Total # of output packets: $(ctr_pkt_benign.count)
62 | Total # of dropped packets: $(ctr_pkt_malicious.count)
63 | ==================================================
64 | " , stop);
65 |
--------------------------------------------------------------------------------
/click/lb.click:
--------------------------------------------------------------------------------
1 | // define counter
2 | counter_from_DmZ, counter_from_PrZ, counter_to_DmZ, counter_to_PrZ :: AverageCounter;
3 | arp_req1, arp_res1, icmp1, ip1 :: Counter;
4 | arp_req2, arp_res2, icmp2, ip2 :: Counter;
5 | drop1, drop2, drop3, drop4 :: Counter;
6 |
7 | AddressInfo(
8 | DmZ 100.0.0.1 00:00:00:01:00:01,
9 | PrZ 10.0.0.1 00:00:00:00:10:01,
10 | );
11 |
12 |
13 | // DmZ to PrZ
14 | from_DmZ :: FromDevice(n9-eth1, METHOD LINUX, SNIFFER false);
15 | to_PrZ :: Queue -> counter_to_PrZ -> ToDevice(n9-eth2);
16 |
17 |
18 | // PrZ to DmZ
19 | from_PrZ :: FromDevice(n9-eth2, METHOD LINUX, SNIFFER false);
20 | to_DmZ :: Queue -> counter_to_DmZ -> ToDevice(n9-eth1);
21 |
22 |
23 | // Packet classifier
24 | PrZ_classifier, DmZ_classifier :: Classifier(
25 | 12/0806 20/0001, //[0]ARP request
26 | 12/0806 20/0002, //[1]ARP reply
27 | 12/0800, //[2]IP
28 | -); //[3]rest
29 |
30 |
31 | // ARP querier
32 | DmZ_arpq :: ARPQuerier(DmZ);
33 | PrZ_arpq :: ARPQuerier(PrZ);
34 |
35 |
36 | // IP packet
37 | ip_to_DmZ :: GetIPAddress(16) -> CheckIPHeader -> [0]DmZ_arpq -> IPPrint("Ip packet to DmZ") -> to_DmZ;
38 | ip_to_PrZ :: GetIPAddress(16) -> CheckIPHeader -> [0]PrZ_arpq -> IPPrint("Ip packet to PrZ") -> to_PrZ;
39 |
40 |
41 | // TCP & UDP rewrite
42 | ip_rewrite :: IPRewriter(pattern 100.0.0.1 20000-65535 - - 0 1,drop);
43 | ip_rewrite[0] -> ip_to_DmZ;
44 | ip_rewrite[1] -> ip_to_PrZ;
45 |
46 |
47 | // ICMP echo rewrite
48 | icmp_rewrite :: ICMPPingRewriter(pattern 100.0.0.1 20000-65535 - - 0 1,drop);
49 | icmp_rewrite[0] -> ip_to_DmZ;
50 | icmp_rewrite[1] -> ip_to_PrZ;
51 |
52 |
53 | // packet from DmZ
54 | from_DmZ -> counter_from_DmZ -> DmZ_classifier;
55 | DmZ_classifier[0] -> arp_req1 -> ARPResponder(DmZ) -> to_DmZ; //ARP request
56 | DmZ_classifier[1] -> arp_res1 -> [1]DmZ_arpq; //ARP reply
57 | DmZ_classifier[2] -> ip1 -> Strip(14) -> CheckIPHeader -> IPPrint("IP packet from DmZ") -> DmZ_IP_classifier :: IPClassifier(icmp type echo-reply, udp or tcp, -); //IP packet
58 | DmZ_IP_classifier[0] -> icmp2 -> [1]icmp_rewrite;
59 | DmZ_IP_classifier[1] -> [1]ip_rewrite;
60 | DmZ_IP_classifier[2] -> drop3 -> Discard;
61 | DmZ_classifier[3] -> drop1 -> Discard; //Drop rest packet
62 |
63 |
64 | // packet from PrZ
65 | from_PrZ -> counter_from_PrZ -> PrZ_classifier;
66 | PrZ_classifier[0] -> arp_req2 -> ARPResponder(PrZ)-> to_PrZ; //ARP request
67 | PrZ_classifier[1] -> arp_res2 -> [1]PrZ_arpq; //ARP reply
68 | PrZ_classifier[2] -> ip2 -> Strip(14)-> CheckIPHeader -> IPPrint("IP packet from PrZ") -> PrZ_IP_classifier :: IPClassifier(icmp type echo, udp or tcp, -); //IP packet
69 | PrZ_IP_classifier[0] -> icmp1 -> [0]icmp_rewrite;
70 | PrZ_IP_classifier[1] -> [0]ip_rewrite;
71 | PrZ_IP_classifier[2] -> drop4 -> Discard;
72 | PrZ_classifier[3] -> drop2 -> Discard; //Drop rest packet
73 |
74 |
75 | // report
76 | DriverManager(wait , print > $rPath/../results/napt.counter "
77 | =================== NAPT Report ===================
78 | Input Packet Rate (pps): $(add $(counter_from_DmZ.rate) $(counter_from_PrZ.rate))
79 | Output Packet Rate(pps): $(add $(counter_to_DmZ.rate) $(counter_to_PrZ.rate))
80 |
81 | Total # of input packets: $(add $(counter_from_DmZ.count) $(counter_from_PrZ.count))
82 | Total # of output packets: $(add $(counter_to_DmZ.count) $(counter_to_PrZ.count))
83 |
84 | Total # of ARP requests packets: $(add $(arp_req1.count) $(arp_req2.count))
85 | Total # of ARP respondes packets: $(add $(arp_res1.count) $(arp_res2.count))
86 |
87 | Total # of service requests packets: $(add $(ip1.count) $(ip2.count))
88 | Total # of ICMP packets: $(add $(icmp1.count) $(icmp2.count))
89 | Total # of dropped packets: $(add $(drop1.count) $(drop2.count) $(drop3.count) $(drop4.count))
90 | ==================================================
91 | " , stop);
92 |
93 |
94 | shrinish@click:/home/tejankar/ik2220-assign-phase2-team5/application/nfv$
95 | shrinish@click:/home/tejankar/ik2220-assign-phase2-team5/application/nfv$
96 | shrinish@click:/home/tejankar/ik2220-assign-phase2-team5/application/nfv$
97 | shrinish@click:/home/tejankar/ik2220-assign-phase2-team5/application/nfv$
98 | shrinish@click:/home/tejankar/ik2220-assign-phase2-team5/application/nfv$
99 | shrinish@click:/home/tejankar/ik2220-assign-phase2-team5/application/nfv$ cat lb_update.click
100 | //LOAD BALANCER
101 |
102 | //define average counters
103 | IF1_in, IF1_out, IF2_in, IF2_out :: AverageCounter;
104 |
105 | //define counters for different packets
106 | arp_req1, arp_rep1, ip1 :: Counter;
107 | arp_req2, arp_rep2, ip2 :: Counter;
108 | icmp, drop_IF1, drop_IF2, drop_IP :: Counter;
109 |
110 | // Traffic from server to client
111 | init_serv :: FromDevice($LB-$port1, METHOD LINUX, SNIFFER false);
112 | end_cli :: Queue -> IF1_out -> ToDevice($LB-$port2, METHOD LINUX);
113 |
114 | // Traffic from client to server
115 | init_cli :: FromDevice($LB-$port2, METHOD LINUX, SNIFFER false);
116 | end_serv :: Queue -> IF2_out -> ToDevice($LB-$port1, METHOD LINUX);
117 |
118 |
119 | // Packet classification
120 | cli_pkt, serv_pkt :: Classifier(
121 | 12/0806 20/0001, //[0]ARP request
122 | 12/0806 20/0002, //[1]ARP reply
123 | 12/0800, //[2]IP
124 | -); //[3]others
125 |
126 |
127 | // ARP query definition
128 | serv_arpq :: ARPQuerier($VIP/32, $LB-$port1);
129 | cli_arpq :: ARPQuerier($VIP/32, $LB-$port2);
130 |
131 |
132 | // IP packet
133 | // GetIPAddress(16) OFFSET is usually 16, to fetch the destination address from an IP packet.
134 |
135 | ip_to_cli :: GetIPAddress(16) -> CheckIPHeader -> [0]cli_arpq -> IPPrint("IP packet destined to client") -> end_cli;
136 | ip_to_serv :: GetIPAddress(16) -> CheckIPHeader -> [0]serv_arpq -> IPPrint("IP packet destined to server") -> end_serv;
137 |
138 |
139 | // Load balancing through Round Rrobin and IP Rewrite elements
140 | ip_map :: RoundRobinIPMapper(
141 | $VIP - $DIP_1 $prt_n 0 1,
142 | $VIP - $DIP_2 $prt_n 0 1,
143 | $VIP - $DIP_3 $prt_n 0 1);
144 | ip_assign :: IPRewriter(ip_map, pattern $VIP 20000-65535 - - 1 0);
145 | ip_assign[0] -> ip_to_serv;
146 | ip_assign[1] -> ip_to_cli;
147 |
148 |
149 | // packet coming from server
150 | init_serv -> IF2_in -> serv_pkt;
151 | serv_pkt[0] -> arp_req2 -> ARPResponder($VIP $LB-$port1) -> end_serv;
152 | serv_pkt[1] -> arp_rep2 -> [1]serv_arpq;
153 | serv_pkt[2] -> ip2 -> Strip(14) -> CheckIPHeader -> IPPrint("IP packet coming from server") -> [1]ip_assign; //IP packet and Strip(14) to get rid of the Ethernet header
154 | serv_pkt[3] -> drop_IF2 -> Discard; //Drop other packets
155 |
156 | // packet coming from client
157 | init_cli -> IF1_in -> cli_pkt;
158 | cli_pkt[0] -> arp_req1 -> ARPResponder($VIP $LB-$port2) -> end_cli;
159 | cli_pkt[1] -> arp_rep1 -> [1]cli_arpq;
160 | cli_pkt[2] -> ip1 -> Strip(14) -> CheckIPHeader -> IPPrint("IP packet coming from client") -> cli_IP_pkt :: IPClassifier(icmp, dst $prt port $prt_n, -); //IP packet
161 | cli_IP_pkt[0] -> icmp -> icmppr :: ICMPPingResponder() -> ip_to_cli; //ICMP
162 | cli_IP_pkt[1] -> [0]ip_assign;
163 | cli_IP_pkt[2] -> drop_IP -> Discard; //drop other IP packets
164 | cli_pkt[3] -> drop_IF1 -> Discard; //Drop other packet
165 |
166 |
167 |
168 | //Report generation
169 |
170 | DriverManager(wait , print > $rPath/../results/$LB.counter "
171 | =================== LB Report ===================================
172 | Input Packet Rate (pps): $(add $(IF2_in.rate) $(IF1_in.rate))
173 | Output Packet Rate(pps): $(add $(IF2_out.rate) $(IF1_out.rate))
174 | Total # of input packets: $(add $(IF2_in.count) $(IF1_in.count))
175 | Total # of output packets: $(add $(IF2_out.count) $(IF2_out.count))
176 | Total # of ARP requests packets: $(add $(arp_req1.count) $(arp_req2.count))
177 | Total # of ARP respondes packets: $(add $(arp_rep1.count) $(arp_rep2.count))
178 | Total # of service requests packets: $(add $(ip1.count) $(ip2.count))
179 | Total # of ICMP packets: $(icmp.count)
180 | Total # of dropped packets: $(add $(drop_IF1.count) $(drop_IF2.count) $(drop_IP.count))
181 | ====================================================================
182 | " , stop);
183 |
--------------------------------------------------------------------------------
/click/lb1.click:
--------------------------------------------------------------------------------
1 | //LOAD BALANCER
2 |
3 | //define average counters
4 | IF1_in, IF1_out, IF2_in, IF2_out :: AverageCounter;
5 |
6 | //define counters for different packets
7 | arp_req1, arp_rep1, ip1 :: Counter;
8 | arp_req2, arp_rep2, ip2 :: Counter;
9 | icmp, drop_IF1, drop_IF2, drop_IP :: Counter;
10 |
11 | // Traffic from server to client
12 | init_serv :: FromDevice(lb7-eth1, METHOD LINUX, SNIFFER false);
13 | end_cli :: Queue -> IF1_out -> ToDevice(lb7-eth2, METHOD LINUX);
14 |
15 | // Traffic from client to server
16 | init_cli :: FromDevice(lb7-eth2, METHOD LINUX, SNIFFER false);
17 | end_serv :: Queue -> IF2_out -> ToDevice(lb7-eth1, METHOD LINUX);
18 |
19 |
20 | // Packet classification
21 | cli_pkt, serv_pkt :: Classifier(
22 | 12/0806 20/0001, //[0]ARP request
23 | 12/0806 20/0002, //[1]ARP reply
24 | 12/0800, //[2]IP
25 | -); //[3]others
26 |
27 |
28 | // ARP query definition
29 | serv_arpq :: ARPQuerier(100.0.0.45, lb7-eth1);
30 | cli_arpq :: ARPQuerier(100.0.0.45, lb7-eth2);
31 |
32 |
33 | // IP packet
34 | // GetIPAddress(16) OFFSET is usually 16, to fetch the destination address from an IP packet.
35 |
36 | ip_to_cli :: GetIPAddress(16) -> CheckIPHeader -> [0]cli_arpq -> IPPrint("IP packet destined to client") -> end_cli;
37 | ip_to_serv :: GetIPAddress(16) -> CheckIPHeader -> [0]serv_arpq -> IPPrint("IP packet destined to server") -> end_serv;
38 |
39 |
40 | // Load balancing through Round Rrobin and IP Rewrite elements
41 | ip_map :: RoundRobinIPMapper(
42 | 100.0.0.45 - 100.0.0.40 80 0 1,
43 | 100.0.0.45 - 100.0.0.41 80 0 1,
44 | 100.0.0.45 - 100.0.0.42 80 0 1);
45 | ip_assign :: IPRewriter(ip_map, pattern 100.0.0.45 20000-65535 - - 1 0);
46 | ip_assign[0] -> ip_to_serv;
47 | ip_assign[1] -> ip_to_cli;
48 |
49 |
50 | // packet coming from server
51 | init_serv -> IF2_in -> serv_pkt;
52 | serv_pkt[0] -> arp_req2 -> ARPResponder(100.0.0.45 lb7-eth1) -> end_serv;
53 | serv_pkt[1] -> arp_rep2 -> [1]serv_arpq;
54 | serv_pkt[2] -> ip2 -> Strip(14) -> CheckIPHeader -> IPPrint("IP packet coming from server") -> [1]ip_assign; //IP packet and Strip(14) to get rid of the Ethernet header
55 | serv_pkt[3] -> drop_IF2 -> Discard; //Drop other packets
56 |
57 | // packet coming from client
58 | init_cli -> IF1_in -> cli_pkt;
59 | cli_pkt[0] -> arp_req1 -> ARPResponder(100.0.0.45 lb7-eth2) -> end_cli;
60 | cli_pkt[1] -> arp_rep1 -> [1]cli_arpq;
61 | cli_pkt[2] -> ip1 -> Strip(14) -> CheckIPHeader -> IPPrint("IP packet coming from client") -> cli_IP_pkt :: IPClassifier(icmp, dst tcp port 80, -); //IP packet
62 | cli_IP_pkt[0] -> icmp -> icmppr :: ICMPPingResponder() -> ip_to_cli; //ICMP
63 | cli_IP_pkt[1] -> [0]ip_assign; //UDP
64 | cli_IP_pkt[2] -> drop_IP -> Discard; //drop other IP packets
65 | cli_pkt[3] -> drop_IF1 -> Discard; //Drop other packet
66 |
67 |
68 |
69 | //Report generation
70 |
71 | DriverManager(wait , print > lb2.report "
72 | =================== LB2 Report ===================================
73 | Input Packet Rate (pps): $(add $(IF2_in.rate) $(IF1_in.rate))
74 | Output Packet Rate(pps): $(add $(IF2_out.rate) $(IF1_out.rate))
75 | Total # of input packets: $(add $(IF2_in.count) $(IF1_in.count))
76 | Total # of output packets: $(add $(IF2_out.count) $(IF2_out.count))
77 | Total # of ARP requests packets: $(add $(arp_req1.count) $(arp_req2.count))
78 | Total # of ARP respondes packets: $(add $(arp_rep1.count) $(arp_rep2.count))
79 | Total # of service requests packets: $(add $(ip1.count) $(ip2.count))
80 | Total # of ICMP packets: $(icmp.count)
81 | Total # of dropped packets: $(add $(drop_IF1.count) $(drop_IF2.count) $(drop_IP.count))
82 | ====================================================================
83 | " , stop);
84 |
85 | shrinish@click:/opt/ik2220/click$
86 | shrinish@click:/opt/ik2220/click$
87 | shrinish@click:/opt/ik2220/click$
88 | shrinish@click:/opt/ik2220/click$
89 | shrinish@click:/opt/ik2220/click$ cat LB1.click
90 | IF1_in, IF1_out, IF2_in, IF2_out :: AverageCounter;
91 |
92 | //define counters for different packets
93 | arp_req1, arp_rep1, ip1 :: Counter;
94 | arp_req2, arp_rep2, ip2 :: Counter;
95 | icmp, drop_IF1, drop_IF2, drop_IP :: Counter;
96 |
97 | // Traffic from server to client
98 | init_serv :: FromDevice(lb6-eth2, METHOD LINUX, SNIFFER false);
99 | end_cli :: Queue -> IF2_out -> ToDevice(lb6-eth1, METHOD LINUX);
100 |
101 | // Traffic from client to server
102 | init_cli :: FromDevice(lb6-eth1, METHOD LINUX, SNIFFER false);
103 | end_serv :: Queue -> IF1_out -> ToDevice(lb6-eth2, METHOD LINUX);
104 |
105 |
106 | // Packet classification
107 | cli_pkt, serv_pkt :: Classifier(
108 | 12/0806 20/0001, //[0]ARP request
109 | 12/0806 20/0002, //[1]ARP reply
110 | 12/0800, //[2]IP
111 | -); //[3]others
112 |
113 |
114 | // ARP query definition
115 | serv_arpq :: ARPQuerier(100.0.0.25, lb6-eth2);
116 | cli_arpq :: ARPQuerier(100.0.0.25, lb6-eth1);
117 |
118 |
119 | // IP packet
120 | // GetIPAddress(16) OFFSET is usually 16, to fetch the destination address from an IP packet.
121 |
122 | ip_to_cli :: GetIPAddress(16) -> CheckIPHeader -> [0]cli_arpq -> IPPrint("IP packet destined to client") -> end_cli;
123 | ip_to_serv :: GetIPAddress(16) -> CheckIPHeader -> [0]serv_arpq -> IPPrint("IP packet destined to server") -> end_serv;
124 |
125 |
126 | // Load balancing through Round Rrobin and IP Rewrite elements
127 | ip_map :: RoundRobinIPMapper(
128 | 100.0.0.25 - 100.0.0.20 53 0 1,
129 | 100.0.0.25 - 100.0.0.21 53 0 1,
130 | 100.0.0.25 - 100.0.0.22 53 0 1);
131 | ip_assign :: IPRewriter(ip_map, pattern 100.0.0.25 20000-65535 - - 1 0);
132 | ip_assign[0] -> ip_to_serv;
133 | ip_assign[1] -> ip_to_cli;
134 |
135 |
136 | // packet coming from server
137 | init_serv -> IF1_in -> serv_pkt;
138 | serv_pkt[0] -> arp_req2 -> ARPResponder(100.0.0.25 lb6-eth2) -> end_serv;
139 | serv_pkt[1] -> arp_rep2 -> [1]serv_arpq;
140 | serv_pkt[2] -> ip2 -> Strip(14) -> CheckIPHeader -> IPPrint("IP packet coming from server") -> [1]ip_assign; //IP packet and Strip(14) to get rid of the Ethernet header
141 | serv_pkt[3] -> drop_IF1 -> Discard; //Drop other packets
142 |
143 | // packet coming from client
144 | init_cli -> IF2_in -> cli_pkt;
145 | cli_pkt[0] -> arp_req1 -> ARPResponder(100.0.0.25 lb6-eth1) -> end_cli;
146 | cli_pkt[1] -> arp_rep1 -> [1]cli_arpq;
147 | cli_pkt[2] -> ip1 -> Strip(14) -> CheckIPHeader -> IPPrint("IP packet coming from client") -> cli_IP_pkt :: IPClassifier(icmp, dst udp port 53, -); //IP packet
148 | cli_IP_pkt[0] -> icmp -> icmppr :: ICMPPingResponder() -> ip_to_cli; //ICMP
149 | cli_IP_pkt[1] -> [0]ip_assign; //UDP
150 | cli_IP_pkt[2] -> drop_IP -> Discard; //drop other IP packets
151 | cli_pkt[3] -> drop_IF2 -> Discard; //Drop other packet
152 |
153 |
154 |
155 | //Report generation
156 |
157 | DriverManager(wait , print > ../results/lb1.report "
158 | =================== LB1 Report ===================================
159 | Input Packet Rate (pps): $(add $(IF2_in.rate) $(IF1_in.rate))
160 | Output Packet Rate(pps): $(add $(IF2_out.rate) $(IF1_out.rate))
161 |
162 | Total # of input packets: $(add $(IF2_in.count) $(IF1_in.count))
163 | Total # of output packets: $(add $(IF2_out.count) $(IF2_out.count))
164 |
165 | Total # of ARP requests packets: $(add $(arp_req1.count) $(arp_req2.count))
166 | Total # of ARP respondes packets: $(add $(arp_rep1.count) $(arp_rep2.count))
167 |
168 | Total # of service requests packets: $(add $(ip1.count) $(ip2.count))
169 | Total # of ICMP packets: $(icmp.count)
170 | Total # of dropped packets: $(add $(drop_IF1.count) $(drop_IF2.count) $(drop_IP.count))
171 | ====================================================================
172 | " , stop);
173 |
174 |
--------------------------------------------------------------------------------
/click/lb2.click:
--------------------------------------------------------------------------------
1 | //LOAD BALANCER
2 |
3 | //define average counters
4 | IF1_in, IF1_out, IF2_in, IF2_out :: AverageCounter;
5 |
6 | //define counters for different packets
7 | arp_req1, arp_rep1, ip1 :: Counter;
8 | arp_req2, arp_rep2, ip2 :: Counter;
9 | icmp, drop_IF1, drop_IF2, drop_IP :: Counter;
10 |
11 | // Traffic from server to client
12 | init_serv :: FromDevice(lb7-eth1, METHOD LINUX, SNIFFER false);
13 | end_cli :: Queue -> IF1_out -> ToDevice(lb7-eth2, METHOD LINUX);
14 |
15 | // Traffic from client to server
16 | init_cli :: FromDevice(lb7-eth2, METHOD LINUX, SNIFFER false);
17 | end_serv :: Queue -> IF2_out -> ToDevice(lb7-eth1, METHOD LINUX);
18 |
19 |
20 | // Packet classification
21 | cli_pkt, serv_pkt :: Classifier(
22 | 12/0806 20/0001, //[0]ARP request
23 | 12/0806 20/0002, //[1]ARP reply
24 | 12/0800, //[2]IP
25 | -); //[3]others
26 |
27 |
28 | // ARP query definition
29 | serv_arpq :: ARPQuerier(100.0.0.45, lb7-eth1);
30 | cli_arpq :: ARPQuerier(100.0.0.45, lb7-eth2);
31 |
32 |
33 | // IP packet
34 | // GetIPAddress(16) OFFSET is usually 16, to fetch the destination address from an IP packet.
35 |
36 | ip_to_cli :: GetIPAddress(16) -> CheckIPHeader -> [0]cli_arpq -> IPPrint("IP packet destined to client") -> end_cli;
37 | ip_to_serv :: GetIPAddress(16) -> CheckIPHeader -> [0]serv_arpq -> IPPrint("IP packet destined to server") -> end_serv;
38 |
39 |
40 | // Load balancing through Round Rrobin and IP Rewrite elements
41 | ip_map :: RoundRobinIPMapper(
42 | 100.0.0.45 - 100.0.0.40 80 0 1,
43 | 100.0.0.45 - 100.0.0.41 80 0 1,
44 | 100.0.0.45 - 100.0.0.42 80 0 1);
45 | ip_assign :: IPRewriter(ip_map, pattern 100.0.0.45 20000-65535 - - 1 0);
46 | ip_assign[0] -> ip_to_serv;
47 | ip_assign[1] -> ip_to_cli;
48 |
49 |
50 | // packet coming from server
51 | init_serv -> IF2_in -> serv_pkt;
52 | serv_pkt[0] -> arp_req2 -> ARPResponder(100.0.0.45 lb7-eth1) -> end_serv;
53 | serv_pkt[1] -> arp_rep2 -> [1]serv_arpq;
54 | serv_pkt[2] -> ip2 -> Strip(14) -> CheckIPHeader -> IPPrint("IP packet coming from server") -> [1]ip_assign; //IP packet and Strip(14) to get rid of the Ethernet header
55 | serv_pkt[3] -> drop_IF2 -> Discard; //Drop other packets
56 |
57 | // packet coming from client
58 | init_cli -> IF1_in -> cli_pkt;
59 | cli_pkt[0] -> arp_req1 -> ARPResponder(100.0.0.45 lb7-eth2) -> end_cli;
60 | cli_pkt[1] -> arp_rep1 -> [1]cli_arpq;
61 | cli_pkt[2] -> ip1 -> Strip(14) -> CheckIPHeader -> IPPrint("IP packet coming from client") -> cli_IP_pkt :: IPClassifier(icmp, dst tcp port 80, -); //IP packet
62 | cli_IP_pkt[0] -> icmp -> icmppr :: ICMPPingResponder() -> ip_to_cli; //ICMP
63 | cli_IP_pkt[1] -> [0]ip_assign; //UDP
64 | cli_IP_pkt[2] -> drop_IP -> Discard; //drop other IP packets
65 | cli_pkt[3] -> drop_IF1 -> Discard; //Drop other packet
66 |
67 |
68 |
69 | //Report generation
70 |
71 | DriverManager(wait , print > lb2.report "
72 | =================== LB2 Report ===================================
73 | Input Packet Rate (pps): $(add $(IF2_in.rate) $(IF1_in.rate))
74 | Output Packet Rate(pps): $(add $(IF2_out.rate) $(IF1_out.rate))
75 | Total # of input packets: $(add $(IF2_in.count) $(IF1_in.count))
76 | Total # of output packets: $(add $(IF2_out.count) $(IF2_out.count))
77 | Total # of ARP requests packets: $(add $(arp_req1.count) $(arp_req2.count))
78 | Total # of ARP respondes packets: $(add $(arp_rep1.count) $(arp_rep2.count))
79 | Total # of service requests packets: $(add $(ip1.count) $(ip2.count))
80 | Total # of ICMP packets: $(icmp.count)
81 | Total # of dropped packets: $(add $(drop_IF1.count) $(drop_IF2.count) $(drop_IP.count))
82 | ====================================================================
83 | " , stop);
84 |
85 |
--------------------------------------------------------------------------------
/click/n9.click:
--------------------------------------------------------------------------------
1 | // define counter
2 | counter_from_DmZ, counter_from_PrZ, counter_to_DmZ, counter_to_PrZ :: AverageCounter;
3 | arp_req1, arp_res1, icmp1, ip1 :: Counter;
4 | arp_req2, arp_res2, icmp2, ip2 :: Counter;
5 | drop1, drop2, drop3, drop4 :: Counter;
6 |
7 | AddressInfo(
8 | DmZ 100.0.0.1 00:00:00:01:00:01,
9 | PrZ 10.0.0.1 00:00:00:00:10:01,
10 | );
11 |
12 |
13 | // DmZ to PrZ
14 | from_DmZ :: FromDevice(n9-eth1, METHOD LINUX, SNIFFER false);
15 | to_PrZ :: Queue -> counter_to_PrZ -> ToDevice(n9-eth2);
16 |
17 |
18 | // PrZ to DmZ
19 | from_PrZ :: FromDevice(n9-eth2, METHOD LINUX, SNIFFER false);
20 | to_DmZ :: Queue -> counter_to_DmZ -> ToDevice(n9-eth1);
21 |
22 |
23 | // Packet classifier
24 | PrZ_classifier, DmZ_classifier :: Classifier(
25 | 12/0806 20/0001, //[0]ARP request
26 | 12/0806 20/0002, //[1]ARP reply
27 | 12/0800, //[2]IP
28 | -); //[3]rest
29 |
30 |
31 | // ARP querier
32 | DmZ_arpq :: ARPQuerier(DmZ);
33 | PrZ_arpq :: ARPQuerier(PrZ);
34 |
35 |
36 | // IP packet
37 | ip_to_DmZ :: GetIPAddress(16) -> CheckIPHeader -> [0]DmZ_arpq -> IPPrint("Ip packet to DmZ") -> to_DmZ;
38 | ip_to_PrZ :: GetIPAddress(16) -> CheckIPHeader -> [0]PrZ_arpq -> IPPrint("Ip packet to PrZ") -> to_PrZ;
39 |
40 |
41 | // TCP & UDP rewrite
42 | ip_rewrite :: IPRewriter(pattern 100.0.0.1 20000-65535 - - 0 1,drop);
43 | ip_rewrite[0] -> ip_to_DmZ;
44 | ip_rewrite[1] -> ip_to_PrZ;
45 |
46 |
47 | // ICMP echo rewrite
48 | icmp_rewrite :: ICMPPingRewriter(pattern 100.0.0.1 20000-65535 - - 0 1,drop);
49 | icmp_rewrite[0] -> ip_to_DmZ;
50 | icmp_rewrite[1] -> ip_to_PrZ;
51 |
52 |
53 | // packet from DmZ
54 | from_DmZ -> counter_from_DmZ -> DmZ_classifier;
55 | DmZ_classifier[0] -> arp_req1 -> ARPResponder(DmZ) -> to_DmZ; //ARP request
56 | DmZ_classifier[1] -> arp_res1 -> [1]DmZ_arpq; //ARP reply
57 | DmZ_classifier[2] -> ip1 -> Strip(14) -> CheckIPHeader -> IPPrint("IP packet from DmZ") -> DmZ_IP_classifier :: IPClassifier(icmp type echo-reply, udp or tcp, -); //IP packet
58 | DmZ_IP_classifier[0] -> icmp2 -> [1]icmp_rewrite;
59 | DmZ_IP_classifier[1] -> [1]ip_rewrite;
60 | DmZ_IP_classifier[2] -> drop3 -> Discard;
61 | DmZ_classifier[3] -> drop1 -> Discard; //Drop rest packet
62 |
63 |
64 | // packet from PrZ
65 | from_PrZ -> counter_from_PrZ -> PrZ_classifier;
66 | PrZ_classifier[0] -> arp_req2 -> ARPResponder(PrZ)-> to_PrZ; //ARP request
67 | PrZ_classifier[1] -> arp_res2 -> [1]PrZ_arpq; //ARP reply
68 | PrZ_classifier[2] -> ip2 -> Strip(14)-> CheckIPHeader -> IPPrint("IP packet from PrZ") -> PrZ_IP_classifier :: IPClassifier(icmp type echo, udp or tcp, -); //IP packet
69 | PrZ_IP_classifier[0] -> icmp1 -> [0]icmp_rewrite;
70 | PrZ_IP_classifier[1] -> [0]ip_rewrite;
71 | PrZ_IP_classifier[2] -> drop4 -> Discard;
72 | PrZ_classifier[3] -> drop2 -> Discard; //Drop rest packet
73 |
74 |
75 | // report
76 | DriverManager(wait , print > n9.report "
77 | =================== NAPT Report ===================
78 | Input Packet Rate (pps): $(add $(counter_from_DmZ.rate) $(counter_from_PrZ.rate))
79 | Output Packet Rate(pps): $(add $(counter_to_DmZ.rate) $(counter_to_PrZ.rate))
80 |
81 | Total # of input packets: $(add $(counter_from_DmZ.count) $(counter_from_PrZ.count))
82 | Total # of output packets: $(add $(counter_to_DmZ.count) $(counter_to_PrZ.count))
83 |
84 | Total # of ARP requests packets: $(add $(arp_req1.count) $(arp_req2.count))
85 | Total # of ARP respondes packets: $(add $(arp_res1.count) $(arp_res2.count))
86 |
87 | Total # of service requests packets: $(add $(ip1.count) $(ip2.count))
88 | Total # of ICMP packets: $(add $(icmp1.count) $(icmp2.count))
89 | Total # of dropped packets: $(add $(drop1.count) $(drop2.count) $(drop3.count) $(drop4.count))
90 | ==================================================
91 | " , stop);
92 |
93 |
--------------------------------------------------------------------------------
/firewall/Makefile:
--------------------------------------------------------------------------------
1 | export POX_PATH := /opt/ik2220/pox
2 | export APP_PATH := ik2220
3 | export PATHRR := $(CURDIR)
4 | app:
5 | sudo cp sdn/* $$POX_PATH/ext/
6 | sudo cp nfv/* $$POX_PATH/ext/
7 | sudo python -m compileall $$POX_PATH/ext/
8 | sudo python $$POX_PATH/pox.py l2_firewall --pathExec=$$PATHRR &
9 |
10 | clean:
11 | -sudo pkill -2 -f click
12 | -sudo pkill -9 -f pox
13 |
--------------------------------------------------------------------------------
/firewall/firewall_parent_proactive.py:
--------------------------------------------------------------------------------
1 | # Copyright 2011-2012 James McCauley
2 | #
3 | # Licensed under the Apache License, Version 2.0 (the "License");
4 | # you may not use this file except in compliance with the License.
5 | # You may obtain a copy of the License at:
6 | #
7 | # http://www.apache.org/licenses/LICENSE-2.0
8 | #
9 | # Unless required by applicable law or agreed to in writing, software
10 | # distributed under the License is distributed on an "AS IS" BASIS,
11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | # See the License for the specific language governing permissions and
13 | # limitations under the License.
14 |
15 | """
16 | An L2 learning switch.
17 | It is derived from one written live for an SDN crash course.
18 | It is somwhat similar to NOX's pyswitch in that it installs
19 | exact-match rules for each flow.
20 | """
21 |
22 | from pox.core import core
23 | import pox.openflow.libopenflow_01 as of
24 | from pox.lib.util import dpid_to_str
25 | from pox.lib.util import str_to_bool
26 | from pox.lib.addresses import IPAddr, EthAddr
27 | import time
28 | import pox.lib.packet as pkt
29 | from pox.lib.addresses import EthAddr
30 |
31 | log = core.getLogger()
32 |
33 | # We don't want to flood immediately when a switch connects.
34 | # Can be overriden on commandline.
35 | _flood_delay = 0
36 |
37 | class FirewallSwitch (object):
38 | def __init__ (self, connection, transparent):
39 | # Switch we'll be adding L2 learning switch capabilities to
40 | self.connection = connection
41 | self.transparent = transparent
42 |
43 | # Our table
44 | self.macToPort = {}
45 |
46 | # Our firewall table
47 | self.firewall = {}
48 |
49 | # Add a Couple of Rules
50 | #self.AddRule('00-00-00-00-00-01',EthAddr('00:00:00:00:00:01'))
51 | #self.AddRule('00-00-00-00-00-01',EthAddr('00:00:00:00:00:02'))
52 | self.BasicRule(connection)
53 | connection.addListeners(self)
54 |
55 | # We just use this to know when to log a helpful message
56 | self.hold_down_expired = _flood_delay == 0
57 |
58 | #log.debug("Initializing LearningSwitch, transparent=%s",
59 | # str(self.transparent))
60 |
61 |
62 | # function that allows adding firewall rules into the firewall table
63 | def AddRule (self, dpidstr, src=0,value=True):
64 | self.firewall[(dpidstr,src)]=value
65 | log.debug("Adding firewall rule in %s: %s", dpidstr, src)
66 |
67 | @classmethod
68 | def getConnection(self):
69 | return self.connection
70 | def BasicRule (self, connection):
71 | fm = of.ofp_flow_mod()
72 | fm.match.in_port = 1
73 | fm.priority = 18001
74 | fm.match.dl_type = 0x0806
75 | fm.actions.append(of.ofp_action_output( port = 2 ) )
76 | connection.send( fm )
77 | # Allow arp based on dl_type for in_port 2 to output 1
78 | fm = of.ofp_flow_mod()
79 | fm.match.in_port = 2
80 | fm.priority = 18001
81 | fm.match.dl_type = 0x0806
82 | fm.actions.append(of.ofp_action_output( port = 1 ) )
83 | connection.send( fm )
84 | # Default drop
85 | fm = of.ofp_flow_mod()
86 | fm.priority = 1001
87 | connection.send( fm )
88 |
89 | # function that allows deleting firewall rules from the firewall table
90 | # Not used DeleteRule
91 | def DeleteRule (self, dpidstr, src=0):
92 | try:
93 | del self.firewall[(dpidstr,src)]
94 | log.debug("Deleting firewall rule in %s: %s",
95 | dpidstr, src)
96 | except KeyError:
97 | log.error("Cannot find in %s: %s",
98 | dpidstr, src)
99 |
100 |
101 | # check if packet is compliant to rules before proceeding
102 | def CheckRule (self, dpidstr, src=0):
103 | try:
104 | entry = self.firewall[(dpidstr, src)]
105 | if (entry == True):
106 | log.debug("Rule (%s) found in %s: FORWARD",
107 | src, dpidstr)
108 | else:
109 | log.debug("Rule (%s) found in %s: DROP",
110 | src, dpidstr)
111 | return entry
112 | except KeyError:
113 | log.debug("Rule (%s) NOT found in %s: DROP",
114 | src, dpidstr)
115 | return False
116 |
117 | def _handle_PacketIn (self, event):
118 | """
119 | Handle packet in messages from the switch to implement above algorithm.
120 | """
121 |
122 | packet = event.parsed
123 |
124 | def flood (message = None):
125 | """ Floods the packet """
126 | msg = of.ofp_packet_out()
127 | if time.time() - self.connection.connect_time >= _flood_delay:
128 | # Only flood if we've been connected for a little while...
129 |
130 | if self.hold_down_expired is False:
131 | # Oh yes it is!
132 | self.hold_down_expired = True
133 | log.info("%s: Flood hold-down expired -- flooding",
134 | dpid_to_str(event.dpid))
135 |
136 | if message is not None: log.debug(message)
137 | #log.debug("%i: flood %s -> %s", event.dpid,packet.src,packet.dst)
138 | # OFPP_FLOOD is optional; on some switches you may need to change
139 | # this to OFPP_ALL.
140 | msg.actions.append(of.ofp_action_output(port = of.OFPP_FLOOD))
141 | else:
142 | pass
143 | #log.info("Holding down flood for %s", dpid_to_str(event.dpid))
144 | msg.data = event.ofp
145 | msg.in_port = event.port
146 | self.connection.send(msg)
147 |
148 | def drop (duration = None):
149 | """
150 | Drops this packet and optionally installs a flow to continue
151 | dropping similar ones for a while
152 | """
153 | if duration is not None:
154 | if not isinstance(duration, tuple):
155 | duration = (duration,duration)
156 | msg = of.ofp_flow_mod()
157 | msg.match = of.ofp_match.from_packet(packet)
158 | msg.idle_timeout = duration[0]
159 | msg.hard_timeout = duration[1]
160 | msg.buffer_id = event.ofp.buffer_id
161 | self.connection.send(msg)
162 | elif event.ofp.buffer_id is not None:
163 | msg = of.ofp_packet_out()
164 | msg.buffer_id = event.ofp.buffer_id
165 | msg.in_port = event.port
166 | self.connection.send(msg)
167 |
168 | self.macToPort[packet.src] = event.port # 1
169 |
170 | # Get the DPID of the Switch Connection
171 | dpidstr = dpid_to_str(event.connection.dpid)
172 |
173 | packet = event.parsed
174 | if packet.type == packet.ARP_TYPE:
175 | log.info("Caught Arp packet")
176 |
177 | # Check the Firewall Rules
178 | if self.CheckRule(dpidstr, packet.src) == False:
179 | drop()
180 | return
181 |
182 | if not self.transparent: # 2
183 | if packet.type == packet.LLDP_TYPE or packet.dst.isBridgeFiltered():
184 | drop() # 2a
185 | return
186 |
187 | if packet.dst.is_multicast:
188 | flood() # 3a
189 | else:
190 | if packet.dst not in self.macToPort: # 4
191 | flood("Port for %s unknown -- flooding" % (packet.dst,)) # 4a
192 | else:
193 | port = self.macToPort[packet.dst]
194 | if port == event.port: # 5
195 | # 5a
196 | log.warning("Same port for packet from %s -> %s on %s.%s. Drop."
197 | % (packet.src, packet.dst, dpid_to_str(event.dpid), port))
198 | drop(10)
199 | return
200 | # 6
201 | log.debug("installing flow for %s.%i -> %s.%i" %
202 | (packet.src, event.port, packet.dst, port))
203 | msg = of.ofp_flow_mod()
204 | msg.match = of.ofp_match.from_packet(packet, event.port)
205 | msg.idle_timeout = 10
206 | msg.hard_timeout = 30
207 | msg.actions.append(of.ofp_action_output(port = port))
208 | msg.data = event.ofp # 6a
209 | self.connection.send(msg)
210 |
211 |
212 | class firewall (object):
213 | """
214 | Waits for OpenFlow switches to connect and makes them learning switches.
215 | """
216 | def __init__ (self, transparent):
217 | core.openflow.addListeners(self)
218 | self.transparent = transparent
219 |
220 | def _handle_ConnectionUp (self, event):
221 | log.debug("Connection %s" % (event.connection,))
222 | #FirewallSwitch(event.connection, self.transparent)
223 | log.debug("###################DPID#############:%s ", dpid_to_str(event.connection.dpid))
224 | # Allow arp based on dl_type for in_port 1 to output 2
225 | #if dpid_to_str(event.connection.dpid) == "00-00-00-00-00-01":
226 | # return
227 | if dpid_to_str(event.connection.dpid) == "00-00-00-00-00-0a":
228 | fw1 = FW1(event.connection, self.transparent)
229 | fw1.AddRule(event.connection)
230 | elif dpid_to_str(event.connection.dpid) == "00-00-00-00-00-0b":
231 | fw2 = FW2(event.connection, self.transparent)
232 | fw2.AddRule(event.connection)
233 | else:
234 | return
235 |
236 | class FW1 (FirewallSwitch):
237 | @staticmethod
238 | def AddRule(connection):
239 | #connection = connection()
240 | # ICMP Echo Request in_port 2 out_port 1
241 | fm = of.ofp_flow_mod()
242 | fm.match.in_port = 2
243 | fm.priority = 33001
244 | fm.match.dl_type = 0x0800
245 | fm.match.nw_proto=pkt.ipv4.ICMP_PROTOCOL
246 | fm.match.tp_src = pkt.ICMP.TYPE_ECHO_REQUEST
247 | fm.actions.append(of.ofp_action_output( port = 1 ) )
248 | connection.send( fm )
249 | # ICMP Echo Reply in_port 1 out_port 2
250 | fm = of.ofp_flow_mod()
251 | fm.match.in_port = 1
252 | fm.priority = 33001
253 | fm.match.dl_type = 0x0800
254 | fm.match.nw_proto=pkt.ipv4.ICMP_PROTOCOL
255 | fm.match.tp_src = pkt.ICMP.TYPE_ECHO_REPLY
256 | fm.actions.append(of.ofp_action_output( port = 2 ) )
257 | connection.send( fm )
258 |
259 | class FW2 (FirewallSwitch):
260 | @staticmethod
261 | def AddRule(connection):
262 | # ALLOW default in_port 2 out_port 1
263 | fm = of.ofp_flow_mod()
264 | fm.match.in_port = 2
265 | fm.priority = 33001
266 | #fm.match.dl_type = 0x0800
267 | #fm.match.nw_proto=pkt.ipv4.ICMP_PROTOCOL
268 | #fm.match.tp_src = pkt.ICMP.TYPE_ECHO_REQUEST
269 | fm.actions.append(of.ofp_action_output( port = 1 ) )
270 | connection.send( fm )
271 | # Allow Echo Reply in_port 1 out_port 2
272 | fm = of.ofp_flow_mod()
273 | fm.match.in_port = 1
274 | fm.priority = 33001
275 | fm.match.dl_type = 0x0800
276 | fm.match.nw_proto=pkt.ipv4.ICMP_PROTOCOL
277 | fm.match.tp_src = pkt.ICMP.TYPE_ECHO_REPLY
278 | fm.actions.append(of.ofp_action_output( port = 2 ) )
279 | connection.send( fm )
280 |
281 | def launch (transparent=False, hold_down=_flood_delay):
282 | """
283 | Starts an firewall switch.
284 | """
285 | try:
286 | global _flood_delay
287 | _flood_delay = int(str(hold_down), 10)
288 | assert _flood_delay >= 0
289 | except:
290 | raise RuntimeError("Expected hold-down to be a number")
291 |
292 | core.registerNew(firewall, str_to_bool(transparent))
293 |
--------------------------------------------------------------------------------
/firewall/l2_firewall.py:
--------------------------------------------------------------------------------
1 | # Copyright 2011-2012 James McCauley
2 | #
3 | # Licensed under the Apache License, Version 2.0 (the "License");
4 | # you may not use this file except in compliance with the License.
5 | # You may obtain a copy of the License at:
6 | #
7 | # http://www.apache.org/licenses/LICENSE-2.0
8 | #
9 | # Unless required by applicable law or agreed to in writing, software
10 | # distributed under the License is distributed on an "AS IS" BASIS,
11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | # See the License for the specific language governing permissions and
13 | # limitations under the License.
14 |
15 | """
16 | An L2 learning switch.
17 | It is derived from one written live for an SDN crash course.
18 | It is somwhat similar to NOX's pyswitch in that it installs
19 | exact-match rules for each flow.
20 | """
21 |
22 | from pox.core import core
23 | import pox.openflow.libopenflow_01 as of
24 | from pox.lib.util import dpid_to_str
25 | from pox.lib.util import str_to_bool
26 | from pox.lib.addresses import IPAddr, EthAddr
27 | import time
28 | import pox.lib.packet as pkt
29 | from pox.lib.addresses import EthAddr
30 | from pox.forwarding.l2_learning import LearningSwitch
31 | import os
32 |
33 | log = core.getLogger()
34 | rPath = ""
35 | # We don't want to flood immediately when a switch connects.
36 | # Can be overriden on commandline.
37 | _flood_delay = 0
38 |
39 | class FirewallSwitch (object):
40 | def __init__ (self, connection, transparent):
41 | # Switch we'll be adding L2 learning switch capabilities to
42 | self.connection = connection
43 | self.transparent = transparent
44 |
45 | # Our table
46 | self.macToPort = {}
47 |
48 | # Our firewall table
49 | self.firewall = {}
50 |
51 | # Add a Couple of Rules
52 | #self.AddRule('00-00-00-00-00-01',EthAddr('00:00:00:00:00:01'))
53 | #self.AddRule('00-00-00-00-00-01',EthAddr('00:00:00:00:00:02'))
54 | self.BasicRule(connection)
55 | connection.addListeners(self)
56 |
57 | # We just use this to know when to log a helpful message
58 | self.hold_down_expired = _flood_delay == 0
59 |
60 | #log.debug("Initializing LearningSwitch, transparent=%s",
61 | # str(self.transparent))
62 |
63 |
64 | # function that allows adding firewall rules into the firewall table
65 | def AddRule (self, dpidstr, src=0,value=True):
66 | self.firewall[(dpidstr,src)]=value
67 | log.debug("Adding firewall rule in %s: %s", dpidstr, src)
68 |
69 | @classmethod
70 | def getConnection(self):
71 | return self.connection
72 | def BasicRule (self, connection):
73 | fm = of.ofp_flow_mod()
74 | fm.match.in_port = 1
75 | fm.priority = 18001
76 | fm.match.dl_type = 0x0806
77 | fm.actions.append(of.ofp_action_output( port = 2 ) )
78 | connection.send( fm )
79 | # Allow arp based on dl_type for in_port 2 to output 1
80 | fm = of.ofp_flow_mod()
81 | fm.match.in_port = 2
82 | fm.priority = 18001
83 | fm.match.dl_type = 0x0806
84 | fm.actions.append(of.ofp_action_output( port = 1 ) )
85 | connection.send( fm )
86 | # Default drop
87 | fm = of.ofp_flow_mod()
88 | fm.priority = 1001
89 | connection.send( fm )
90 |
91 | # function that allows deleting firewall rules from the firewall table
92 | # Not used DeleteRule
93 | def DeleteRule (self, dpidstr, src=0):
94 | try:
95 | del self.firewall[(dpidstr,src)]
96 | log.debug("Deleting firewall rule in %s: %s",
97 | dpidstr, src)
98 | except KeyError:
99 | log.error("Cannot find in %s: %s",
100 | dpidstr, src)
101 |
102 |
103 | # check if packet is compliant to rules before proceeding
104 | def CheckRule (self, dpidstr, src=0):
105 | try:
106 | entry = self.firewall[(dpidstr, src)]
107 | if (entry == True):
108 | log.debug("Rule (%s) found in %s: FORWARD",
109 | src, dpidstr)
110 | else:
111 | log.debug("Rule (%s) found in %s: DROP",
112 | src, dpidstr)
113 | return entry
114 | except KeyError:
115 | log.debug("Rule (%s) NOT found in %s: DROP",
116 | src, dpidstr)
117 | return False
118 |
119 | def _handle_PacketIn (self, event):
120 | """
121 | Handle packet in messages from the switch to implement above algorithm.
122 | """
123 |
124 | packet = event.parsed
125 |
126 | def flood (message = None):
127 | """ Floods the packet """
128 | msg = of.ofp_packet_out()
129 | if time.time() - self.connection.connect_time >= _flood_delay:
130 | # Only flood if we've been connected for a little while...
131 |
132 | if self.hold_down_expired is False:
133 | # Oh yes it is!
134 | self.hold_down_expired = True
135 | log.info("%s: Flood hold-down expired -- flooding",
136 | dpid_to_str(event.dpid))
137 |
138 | if message is not None: log.debug(message)
139 | #log.debug("%i: flood %s -> %s", event.dpid,packet.src,packet.dst)
140 | # OFPP_FLOOD is optional; on some switches you may need to change
141 | # this to OFPP_ALL.
142 | msg.actions.append(of.ofp_action_output(port = of.OFPP_FLOOD))
143 | else:
144 | pass
145 | #log.info("Holding down flood for %s", dpid_to_str(event.dpid))
146 | msg.data = event.ofp
147 | msg.in_port = event.port
148 | self.connection.send(msg)
149 |
150 | def drop (duration = None):
151 | """
152 | Drops this packet and optionally installs a flow to continue
153 | dropping similar ones for a while
154 | """
155 | if duration is not None:
156 | if not isinstance(duration, tuple):
157 | duration = (duration,duration)
158 | msg = of.ofp_flow_mod()
159 | msg.match = of.ofp_match.from_packet(packet)
160 | msg.idle_timeout = duration[0]
161 | msg.hard_timeout = duration[1]
162 | msg.buffer_id = event.ofp.buffer_id
163 | self.connection.send(msg)
164 | elif event.ofp.buffer_id is not None:
165 | msg = of.ofp_packet_out()
166 | msg.buffer_id = event.ofp.buffer_id
167 | msg.in_port = event.port
168 | self.connection.send(msg)
169 |
170 | self.macToPort[packet.src] = event.port # 1
171 |
172 | # Get the DPID of the Switch Connection
173 | dpidstr = dpid_to_str(event.connection.dpid)
174 |
175 | packet = event.parsed
176 | if packet.type == packet.ARP_TYPE:
177 | log.info("Caught Arp packet")
178 |
179 | # Check the Firewall Rules
180 | if self.CheckRule(dpidstr, packet.src) == False:
181 | drop()
182 | return
183 |
184 | if not self.transparent: # 2
185 | if packet.type == packet.LLDP_TYPE or packet.dst.isBridgeFiltered():
186 | drop() # 2a
187 | return
188 |
189 | if packet.dst.is_multicast:
190 | flood() # 3a
191 | else:
192 | if packet.dst not in self.macToPort: # 4
193 | flood("Port for %s unknown -- flooding" % (packet.dst,)) # 4a
194 | else:
195 | port = self.macToPort[packet.dst]
196 | if port == event.port: # 5
197 | # 5a
198 | log.warning("Same port for packet from %s -> %s on %s.%s. Drop."
199 | % (packet.src, packet.dst, dpid_to_str(event.dpid), port))
200 | drop(10)
201 | return
202 | # 6
203 | log.debug("installing flow for %s.%i -> %s.%i" %
204 | (packet.src, event.port, packet.dst, port))
205 | msg = of.ofp_flow_mod()
206 | msg.match = of.ofp_match.from_packet(packet, event.port)
207 | msg.idle_timeout = 10
208 | msg.hard_timeout = 30
209 | msg.actions.append(of.ofp_action_output(port = port))
210 | msg.data = event.ofp # 6a
211 | self.connection.send(msg)
212 |
213 |
214 | class firewall (object):
215 | """
216 | Waits for OpenFlow switches to connect and makes them learning switches.
217 | """
218 | def __init__ (self, transparent):
219 | core.openflow.addListeners(self)
220 | self.transparent = transparent
221 |
222 | def _handle_ConnectionUp (self, event):
223 | global rPath
224 | log.debug("Connection %s" % (event.connection,))
225 | log.debug("report rPath %s ", rPath)
226 | #FirewallSwitch(event.connection, self.transparent)
227 | log.debug("###################DPID#############:%s ", dpid_to_str(event.connection.dpid))
228 | # Allow arp based on dl_type for in_port 1 to output 2
229 | #if dpid_to_str(event.connection.dpid) == "00-00-00-00-00-01":
230 | # return
231 | if dpid_to_str(event.connection.dpid) == "00-00-00-00-00-0a":
232 | fw1 = FW1(event.connection, self.transparent)
233 | fw1.AddRule(event.connection)
234 | elif dpid_to_str(event.connection.dpid) == "00-00-00-00-00-0b":
235 | fw2 = FW2(event.connection, self.transparent)
236 | fw2.AddRule(event.connection)
237 | if dpid_to_str(event.connection.dpid) == "00-00-00-00-00-08":
238 | os.system('sudo /usr/local/bin/click /opt/ik2220/pox/ext/ids.click rPath='+rPath+' &')
239 | elif dpid_to_str(event.connection.dpid) == "00-00-00-00-00-06":
240 | os.system('sudo /usr/local/bin/click /opt/ik2220/pox/ext/lb_update.click LB=lb6 VIP=100.0.0.25 prt_n=53 prt=udp port1=eth2 port2=eth1 DIP_1=100.0.0.20 DIP_2=100.0.0.21 DIP_3=100.0.0.22 rPath='+rPath+' &')
241 | elif dpid_to_str(event.connection.dpid) == "00-00-00-00-00-07":
242 | os.system('sudo /usr/local/bin/click /opt/ik2220/pox/ext/lb_update.click LB=lb7 VIP=100.0.0.45 prt_n=80 prt=tcp port1=eth1 port2=eth2 DIP_1=100.0.0.40 DIP_2=100.0.0.41 DIP_3=100.0.0.42 rPath='+rPath+' &')
243 | elif dpid_to_str(event.connection.dpid) == "00-00-00-00-00-09":
244 | #os.system('sudo /usr/local/bin/click /opt/ik2220/click/n9.click &')
245 | os.system('sudo /usr/local/bin/click /opt/ik2220/pox/ext/napt.click rPath='+rPath+' &')
246 | else:
247 | LearningSwitch(event.connection, False)
248 |
249 | class FW1 (FirewallSwitch):
250 | @staticmethod
251 | def AddRule(connection):
252 | #connection = connection()
253 | # ICMP Echo Request in_port 2 out_port 1
254 | fm = of.ofp_flow_mod()
255 | fm.match.in_port = 2
256 | fm.priority = 33001
257 | fm.match.dl_type = 0x0800
258 | fm.match.nw_proto=pkt.ipv4.ICMP_PROTOCOL
259 | fm.match.tp_src = pkt.ICMP.TYPE_ECHO_REQUEST
260 | fm.actions.append(of.ofp_action_output( port = 1 ) )
261 | connection.send( fm )
262 | # ICMP Echo Reply in_port 1 out_port 2
263 | fm = of.ofp_flow_mod()
264 | fm.match.in_port = 1
265 | fm.priority = 33001
266 | fm.match.dl_type = 0x0800
267 | fm.match.nw_proto=pkt.ipv4.ICMP_PROTOCOL
268 | fm.match.tp_src = pkt.ICMP.TYPE_ECHO_REPLY
269 | fm.actions.append(of.ofp_action_output( port = 2 ) )
270 | connection.send( fm )
271 | #Allow icmp echo request for SLBs in_port 1
272 | fm = init_fm( 1, 2)
273 | fm.match.tp_src = pkt.ICMP.TYPE_ECHO_REQUEST
274 | fm.match.nw_dst='100.0.0.25'
275 | connection.send(fm)
276 | fm = init_fm( 1, 2)
277 | fm.match.tp_src = pkt.ICMP.TYPE_ECHO_REQUEST
278 | fm.match.nw_dst='100.0.0.45'
279 | connection.send(fm)
280 | #Allow ICMP reply from SLBs in_port 2 out_port 1
281 | fm = init_fm( 2, 1)
282 | fm.match.tp_src = pkt.ICMP.TYPE_ECHO_REPLY
283 | connection.send(fm)
284 | #Allow DNS Request
285 | fm = of.ofp_flow_mod()
286 | fm.match.in_port = 1
287 | fm.priority = 33001
288 | fm.match.dl_type = 0x0800
289 | fm.match.nw_proto=pkt.ipv4.UDP_PROTOCOL
290 | fm.match.tp_dst = 53
291 | fm.actions.append(of.ofp_action_output( port = 2 ) )
292 | connection.send( fm )
293 | #Allow DNS reply
294 | fm = of.ofp_flow_mod()
295 | fm.match.in_port = 2
296 | fm.priority = 33001
297 | fm.match.dl_type = 0x0800
298 | fm.match.nw_proto=pkt.ipv4.UDP_PROTOCOL
299 | fm.match.tp_src = 53
300 | fm.actions.append(of.ofp_action_output( port = 1 ) )
301 | connection.send( fm )
302 | #Allow HTTP Request
303 | fm = of.ofp_flow_mod()
304 | fm.match.in_port = 1
305 | fm.priority = 33001
306 | fm.match.dl_type = 0x0800
307 | fm.match.nw_proto=pkt.ipv4.TCP_PROTOCOL
308 | fm.match.tp_dst = 80
309 | fm.actions.append(of.ofp_action_output( port = 2 ) )
310 | connection.send( fm )
311 | #Allow HTTP response
312 | fm = of.ofp_flow_mod()
313 | fm.match.in_port = 2
314 | fm.priority = 33001
315 | fm.match.dl_type = 0x0800
316 | fm.match.nw_proto=pkt.ipv4.TCP_PROTOCOL
317 | fm.match.tp_src = 80
318 | fm.actions.append(of.ofp_action_output( port = 1 ) )
319 | connection.send( fm )
320 |
321 | class FW2 (FirewallSwitch):
322 | @staticmethod
323 | def AddRule(connection):
324 | # ALLOW default in_port 2 out_port 1
325 | fm = of.ofp_flow_mod()
326 | fm.match.in_port = 2
327 | fm.priority = 33001
328 | #fm.match.dl_type = 0x0800
329 | #fm.match.nw_proto=pkt.ipv4.ICMP_PROTOCOL
330 | #fm.match.tp_src = pkt.ICMP.TYPE_ECHO_REQUEST
331 | fm.actions.append(of.ofp_action_output( port = 1 ) )
332 | connection.send( fm )
333 | # Allow Echo Reply in_port 1 out_port 2
334 | fm = of.ofp_flow_mod()
335 | fm.match.in_port = 1
336 | fm.priority = 33001
337 | fm.match.dl_type = 0x0800
338 | fm.match.nw_proto=pkt.ipv4.ICMP_PROTOCOL
339 | fm.match.tp_src = pkt.ICMP.TYPE_ECHO_REPLY
340 | fm.actions.append(of.ofp_action_output( port = 2 ) )
341 | connection.send( fm )
342 | #Allow incoming DNS Response
343 | fm = of.ofp_flow_mod()
344 | fm.match.in_port = 1
345 | fm.priority = 55001
346 | fm.match.dl_type = 0x0800
347 | fm.match.nw_proto=pkt.ipv4.UDP_PROTOCOL
348 | fm.match.tp_src = 53
349 | fm.actions.append(of.ofp_action_output( port = 2 ) )
350 | connection.send( fm )
351 | #Allow HTTP incoming response
352 | fm = of.ofp_flow_mod()
353 | fm.match.in_port = 1
354 | fm.priority = 55001
355 | fm.match.dl_type = 0x0800
356 | fm.match.nw_proto=pkt.ipv4.TCP_PROTOCOL
357 | fm.match.tp_src = 80
358 | fm.actions.append(of.ofp_action_output( port = 2 ) )
359 | connection.send( fm )
360 | #Block ICMP request for dmz
361 | #fm = of.ofp_flow_mod()
362 | #fm.match.in_port = 2
363 | #fm.priority = 55001
364 | #fm.match.dl_type = 0x0800
365 | #fm.match.nw_proto=pkt.ipv4.ICMP_PROTOCOL
366 | #fm.match.tp_src = pkt.ICMP.TYPE_ECHO_REQUEST
367 | #fm.match.nw_dst='100.0.0.20'
368 | #connection.send( fm )
369 | #fm = of.ofp_flow_mod()
370 | #fm.match.in_port = 2
371 | #fm.priority = 55001
372 | #fm.match.dl_type = 0x0800
373 | #fm.match.nw_proto=pkt.ipv4.ICMP_PROTOCOL
374 | #fm.match.tp_src = pkt.ICMP.TYPE_ECHO_REQUEST
375 | #fm.match.nw_dst='100.0.0.40'
376 | #connection.send( fm )
377 | connection.send( block_icmp_ip('100.0.0.20') )
378 | connection.send( block_icmp_ip('100.0.0.21') )
379 | connection.send( block_icmp_ip('100.0.0.22') )
380 | connection.send( block_icmp_ip('100.0.0.40') )
381 | connection.send( block_icmp_ip('100.0.0.41') )
382 | connection.send( block_icmp_ip('100.0.0.42') )
383 |
384 | def block_icmp_ip(ip):
385 | fm = of.ofp_flow_mod()
386 | fm.match.in_port = 2
387 | fm.priority = 55001
388 | fm.match.dl_type = 0x0800
389 | fm.match.nw_proto=pkt.ipv4.ICMP_PROTOCOL
390 | fm.match.tp_src = pkt.ICMP.TYPE_ECHO_REQUEST
391 | fm.match.nw_dst=ip
392 | return fm
393 |
394 | def init_fm(in_port, out_port):
395 | fm = of.ofp_flow_mod()
396 | fm.match.in_port = in_port
397 | fm.priority = 33001
398 | fm.match.dl_type = 0x0800
399 | fm.match.nw_proto=pkt.ipv4.ICMP_PROTOCOL
400 | #fm.match.tp_src = tp_src
401 | fm.actions.append(of.ofp_action_output( port = out_port ) )
402 | return fm
403 |
404 | def launch (pathExec, transparent=False, hold_down=_flood_delay):
405 | """
406 | Starts an firewall switch.
407 | """
408 | try:
409 | global _flood_delay
410 | global rPath
411 | print("Pathexec:"+pathExec)
412 | rPath = pathExec
413 | _flood_delay = int(str(hold_down), 10)
414 | assert _flood_delay >= 0
415 | except:
416 | raise RuntimeError("Expected hold-down to be a number")
417 |
418 | core.registerNew(firewall, str_to_bool(transparent))
419 |
--------------------------------------------------------------------------------
/firewall/l2_firewall_example.py:
--------------------------------------------------------------------------------
1 | # Copyright 2011-2012 James McCauley
2 | #
3 | # Licensed under the Apache License, Version 2.0 (the "License");
4 | # you may not use this file except in compliance with the License.
5 | # You may obtain a copy of the License at:
6 | #
7 | # http://www.apache.org/licenses/LICENSE-2.0
8 | #
9 | # Unless required by applicable law or agreed to in writing, software
10 | # distributed under the License is distributed on an "AS IS" BASIS,
11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | # See the License for the specific language governing permissions and
13 | # limitations under the License.
14 |
15 | """
16 | An L2 learning switch.
17 |
18 | It is derived from one written live for an SDN crash course.
19 | It is somwhat similar to NOX's pyswitch in that it installs
20 | exact-match rules for each flow.
21 | """
22 |
23 | from pox.core import core
24 | import pox.openflow.libopenflow_01 as of
25 | from pox.lib.util import dpid_to_str
26 | from pox.lib.util import str_to_bool
27 | import time
28 |
29 | from pox.lib.addresses import EthAddr
30 |
31 | log = core.getLogger()
32 |
33 | # We don't want to flood immediately when a switch connects.
34 | # Can be overriden on commandline.
35 | _flood_delay = 0
36 |
37 | class LearningSwitch (object):
38 | """
39 | The learning switch "brain" associated with a single OpenFlow switch.
40 |
41 | When we see a packet, we'd like to output it on a port which will
42 | eventually lead to the destination. To accomplish this, we build a
43 | table that maps addresses to ports.
44 |
45 | We populate the table by observing traffic. When we see a packet
46 | from some source coming from some port, we know that source is out
47 | that port.
48 |
49 | When we want to forward traffic, we look up the desintation in our
50 | table. If we don't know the port, we simply send the message out
51 | all ports except the one it came in on. (In the presence of loops,
52 | this is bad!).
53 |
54 | In short, our algorithm looks like this:
55 |
56 | For each packet from the switch:
57 | 1) Use source address and switch port to update address/port table
58 | 2) Is transparent = False and either Ethertype is LLDP or the packet's
59 | destination address is a Bridge Filtered address?
60 | Yes:
61 | 2a) Drop packet -- don't forward link-local traffic (LLDP, 802.1x)
62 | DONE
63 | 3) Is destination multicast?
64 | Yes:
65 | 3a) Flood the packet
66 | DONE
67 | 4) Port for destination address in our address/port table?
68 | No:
69 | 4a) Flood the packet
70 | DONE
71 | 5) Is output port the same as input port?
72 | Yes:
73 | 5a) Drop packet and similar ones for a while
74 | 6) Install flow table entry in the switch so that this
75 | flow goes out the appopriate port
76 | 6a) Send the packet out appropriate port
77 | """
78 | def __init__ (self, connection, transparent):
79 | # Switch we'll be adding L2 learning switch capabilities to
80 | self.connection = connection
81 | self.transparent = transparent
82 |
83 | # Our table
84 | self.macToPort = {}
85 |
86 | # Our firewall table
87 | self.firewall = {}
88 |
89 | # Add a Couple of Rules
90 | self.AddRule('00-00-00-00-00-01',EthAddr('00:00:00:00:00:01'))
91 | self.AddRule('00-00-00-00-00-01',EthAddr('00:00:00:00:00:02'))
92 |
93 | # We want to hear PacketIn messages, so we listen
94 | # to the connection
95 | connection.addListeners(self)
96 |
97 | # We just use this to know when to log a helpful message
98 | self.hold_down_expired = _flood_delay == 0
99 |
100 | #log.debug("Initializing LearningSwitch, transparent=%s",
101 | # str(self.transparent))
102 |
103 |
104 | # function that allows adding firewall rules into the firewall table
105 | def AddRule (self, dpidstr, src=0,value=True):
106 | self.firewall[(dpidstr,src)]=value
107 | log.debug("Adding firewall rule in %s: %s", dpidstr, src)
108 |
109 | # function that allows deleting firewall rules from the firewall table
110 | def DeleteRule (self, dpidstr, src=0):
111 | try:
112 | del self.firewall[(dpidstr,src)]
113 | log.debug("Deleting firewall rule in %s: %s",
114 | dpidstr, src)
115 | except KeyError:
116 | log.error("Cannot find in %s: %s",
117 | dpidstr, src)
118 |
119 |
120 | # check if packet is compliant to rules before proceeding
121 | def CheckRule (self, dpidstr, src=0):
122 | try:
123 | entry = self.firewall[(dpidstr, src)]
124 | if (entry == True):
125 | log.debug("Rule (%s) found in %s: FORWARD",
126 | src, dpidstr)
127 | else:
128 | log.debug("Rule (%s) found in %s: DROP",
129 | src, dpidstr)
130 | return entry
131 | except KeyError:
132 | log.debug("Rule (%s) NOT found in %s: DROP",
133 | src, dpidstr)
134 | return False
135 |
136 | def _handle_PacketIn (self, event):
137 | """
138 | Handle packet in messages from the switch to implement above algorithm.
139 | """
140 |
141 | packet = event.parsed
142 |
143 | def flood (message = None):
144 | """ Floods the packet """
145 | msg = of.ofp_packet_out()
146 | if time.time() - self.connection.connect_time >= _flood_delay:
147 | # Only flood if we've been connected for a little while...
148 |
149 | if self.hold_down_expired is False:
150 | # Oh yes it is!
151 | self.hold_down_expired = True
152 | log.info("%s: Flood hold-down expired -- flooding",
153 | dpid_to_str(event.dpid))
154 |
155 | if message is not None: log.debug(message)
156 | #log.debug("%i: flood %s -> %s", event.dpid,packet.src,packet.dst)
157 | # OFPP_FLOOD is optional; on some switches you may need to change
158 | # this to OFPP_ALL.
159 | msg.actions.append(of.ofp_action_output(port = of.OFPP_FLOOD))
160 | else:
161 | pass
162 | #log.info("Holding down flood for %s", dpid_to_str(event.dpid))
163 | msg.data = event.ofp
164 | msg.in_port = event.port
165 | self.connection.send(msg)
166 |
167 | def drop (duration = None):
168 | """
169 | Drops this packet and optionally installs a flow to continue
170 | dropping similar ones for a while
171 | """
172 | if duration is not None:
173 | if not isinstance(duration, tuple):
174 | duration = (duration,duration)
175 | msg = of.ofp_flow_mod()
176 | msg.match = of.ofp_match.from_packet(packet)
177 | msg.idle_timeout = duration[0]
178 | msg.hard_timeout = duration[1]
179 | msg.buffer_id = event.ofp.buffer_id
180 | self.connection.send(msg)
181 | elif event.ofp.buffer_id is not None:
182 | msg = of.ofp_packet_out()
183 | msg.buffer_id = event.ofp.buffer_id
184 | msg.in_port = event.port
185 | self.connection.send(msg)
186 |
187 | self.macToPort[packet.src] = event.port # 1
188 |
189 | # Get the DPID of the Switch Connection
190 | dpidstr = dpid_to_str(event.connection.dpid)
191 |
192 | # Check the Firewall Rules
193 | if self.CheckRule(dpidstr, packet.src) == False:
194 | drop()
195 | return
196 |
197 | if not self.transparent: # 2
198 | if packet.type == packet.LLDP_TYPE or packet.dst.isBridgeFiltered():
199 | drop() # 2a
200 | return
201 |
202 | if packet.dst.is_multicast:
203 | flood() # 3a
204 | else:
205 | if packet.dst not in self.macToPort: # 4
206 | flood("Port for %s unknown -- flooding" % (packet.dst,)) # 4a
207 | else:
208 | port = self.macToPort[packet.dst]
209 | if port == event.port: # 5
210 | # 5a
211 | log.warning("Same port for packet from %s -> %s on %s.%s. Drop."
212 | % (packet.src, packet.dst, dpid_to_str(event.dpid), port))
213 | drop(10)
214 | return
215 | # 6
216 | log.debug("installing flow for %s.%i -> %s.%i" %
217 | (packet.src, event.port, packet.dst, port))
218 | msg = of.ofp_flow_mod()
219 | msg.match = of.ofp_match.from_packet(packet, event.port)
220 | msg.idle_timeout = 10
221 | msg.hard_timeout = 30
222 | msg.actions.append(of.ofp_action_output(port = port))
223 | msg.data = event.ofp # 6a
224 | self.connection.send(msg)
225 |
226 |
227 | class l2_learning (object):
228 | """
229 | Waits for OpenFlow switches to connect and makes them learning switches.
230 | """
231 | def __init__ (self, transparent):
232 | core.openflow.addListeners(self)
233 | self.transparent = transparent
234 |
235 | def _handle_ConnectionUp (self, event):
236 | log.debug("Connection %s" % (event.connection,))
237 | LearningSwitch(event.connection, self.transparent)
238 |
239 |
240 | def launch (transparent=False, hold_down=_flood_delay):
241 | """
242 | Starts an L2 learning switch.
243 | """
244 | try:
245 | global _flood_delay
246 | _flood_delay = int(str(hold_down), 10)
247 | assert _flood_delay >= 0
248 | except:
249 | raise RuntimeError("Expected hold-down to be a number")
250 |
251 | core.registerNew(l2_learning, str_to_bool(transparent))
252 |
--------------------------------------------------------------------------------
/firewall/l2_learning.py:
--------------------------------------------------------------------------------
1 | # Copyright 2011-2012 James McCauley
2 | #
3 | # Licensed under the Apache License, Version 2.0 (the "License");
4 | # you may not use this file except in compliance with the License.
5 | # You may obtain a copy of the License at:
6 | #
7 | # http://www.apache.org/licenses/LICENSE-2.0
8 | #
9 | # Unless required by applicable law or agreed to in writing, software
10 | # distributed under the License is distributed on an "AS IS" BASIS,
11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | # See the License for the specific language governing permissions and
13 | # limitations under the License.
14 |
15 | """
16 | An L2 learning switch.
17 |
18 | It is derived from one written live for an SDN crash course.
19 | It is somwhat similar to NOX's pyswitch in that it installs
20 | exact-match rules for each flow.
21 | """
22 |
23 | from pox.core import core
24 | import pox.openflow.libopenflow_01 as of
25 | from pox.lib.util import dpid_to_str
26 | from pox.lib.util import str_to_bool
27 | import time
28 |
29 | log = core.getLogger()
30 |
31 | # We don't want to flood immediately when a switch connects.
32 | # Can be overriden on commandline.
33 | _flood_delay = 0
34 |
35 | class LearningSwitch (object):
36 | """
37 | The learning switch "brain" associated with a single OpenFlow switch.
38 |
39 | When we see a packet, we'd like to output it on a port which will
40 | eventually lead to the destination. To accomplish this, we build a
41 | table that maps addresses to ports.
42 |
43 | We populate the table by observing traffic. When we see a packet
44 | from some source coming from some port, we know that source is out
45 | that port.
46 |
47 | When we want to forward traffic, we look up the desintation in our
48 | table. If we don't know the port, we simply send the message out
49 | all ports except the one it came in on. (In the presence of loops,
50 | this is bad!).
51 |
52 | In short, our algorithm looks like this:
53 |
54 | For each packet from the switch:
55 | 1) Use source address and switch port to update address/port table
56 | 2) Is transparent = False and either Ethertype is LLDP or the packet's
57 | destination address is a Bridge Filtered address?
58 | Yes:
59 | 2a) Drop packet -- don't forward link-local traffic (LLDP, 802.1x)
60 | DONE
61 | 3) Is destination multicast?
62 | Yes:
63 | 3a) Flood the packet
64 | DONE
65 | 4) Port for destination address in our address/port table?
66 | No:
67 | 4a) Flood the packet
68 | DONE
69 | 5) Is output port the same as input port?
70 | Yes:
71 | 5a) Drop packet and similar ones for a while
72 | 6) Install flow table entry in the switch so that this
73 | flow goes out the appopriate port
74 | 6a) Send the packet out appropriate port
75 | """
76 | def __init__ (self, connection, transparent):
77 | # Switch we'll be adding L2 learning switch capabilities to
78 | self.connection = connection
79 | self.transparent = transparent
80 |
81 | # Our table
82 | self.macToPort = {}
83 |
84 | # We want to hear PacketIn messages, so we listen
85 | # to the connection
86 | connection.addListeners(self)
87 |
88 | # We just use this to know when to log a helpful message
89 | self.hold_down_expired = _flood_delay == 0
90 |
91 | #log.debug("Initializing LearningSwitch, transparent=%s",
92 | # str(self.transparent))
93 |
94 | def _handle_PacketIn (self, event):
95 | """
96 | Handle packet in messages from the switch to implement above algorithm.
97 | """
98 |
99 | packet = event.parsed
100 |
101 | def flood (message = None):
102 | """ Floods the packet """
103 | msg = of.ofp_packet_out()
104 | if time.time() - self.connection.connect_time >= _flood_delay:
105 | # Only flood if we've been connected for a little while...
106 |
107 | if self.hold_down_expired is False:
108 | # Oh yes it is!
109 | self.hold_down_expired = True
110 | log.info("%s: Flood hold-down expired -- flooding",
111 | dpid_to_str(event.dpid))
112 |
113 | if message is not None: log.debug(message)
114 | #log.debug("%i: flood %s -> %s", event.dpid,packet.src,packet.dst)
115 | # OFPP_FLOOD is optional; on some switches you may need to change
116 | # this to OFPP_ALL.
117 | msg.actions.append(of.ofp_action_output(port = of.OFPP_FLOOD))
118 | else:
119 | pass
120 | #log.info("Holding down flood for %s", dpid_to_str(event.dpid))
121 | msg.data = event.ofp
122 | msg.in_port = event.port
123 | self.connection.send(msg)
124 |
125 | def drop (duration = None):
126 | """
127 | Drops this packet and optionally installs a flow to continue
128 | dropping similar ones for a while
129 | """
130 | if duration is not None:
131 | if not isinstance(duration, tuple):
132 | duration = (duration,duration)
133 | msg = of.ofp_flow_mod()
134 | msg.match = of.ofp_match.from_packet(packet)
135 | msg.idle_timeout = duration[0]
136 | msg.hard_timeout = duration[1]
137 | msg.buffer_id = event.ofp.buffer_id
138 | self.connection.send(msg)
139 | elif event.ofp.buffer_id is not None:
140 | msg = of.ofp_packet_out()
141 | msg.buffer_id = event.ofp.buffer_id
142 | msg.in_port = event.port
143 | self.connection.send(msg)
144 |
145 | self.macToPort[packet.src] = event.port # 1
146 |
147 | if not self.transparent: # 2
148 | if packet.type == packet.LLDP_TYPE or packet.dst.isBridgeFiltered():
149 | drop() # 2a
150 | return
151 |
152 | if packet.dst.is_multicast:
153 | flood() # 3a
154 | else:
155 | if packet.dst not in self.macToPort: # 4
156 | flood("Port for %s unknown -- flooding" % (packet.dst,)) # 4a
157 | else:
158 | port = self.macToPort[packet.dst]
159 | if port == event.port: # 5
160 | # 5a
161 | log.warning("Same port for packet from %s -> %s on %s.%s. Drop."
162 | % (packet.src, packet.dst, dpid_to_str(event.dpid), port))
163 | drop(10)
164 | return
165 | # 6
166 | log.debug("installing flow for %s.%i -> %s.%i" %
167 | (packet.src, event.port, packet.dst, port))
168 | msg = of.ofp_flow_mod()
169 | msg.match = of.ofp_match.from_packet(packet, event.port)
170 | msg.idle_timeout = 10
171 | msg.hard_timeout = 30
172 | msg.actions.append(of.ofp_action_output(port = port))
173 | msg.data = event.ofp # 6a
174 | self.connection.send(msg)
175 |
176 |
177 | class l2_learning (object):
178 | """
179 | Waits for OpenFlow switches to connect and makes them learning switches.
180 | """
181 | def __init__ (self, transparent):
182 | core.openflow.addListeners(self)
183 | self.transparent = transparent
184 |
185 | def _handle_ConnectionUp (self, event):
186 | log.debug("Connection %s" % (event.connection,))
187 | LearningSwitch(event.connection, self.transparent)
188 | if dpid_to_str(event.connection.dpid) == "00-00-00-00-00-01":
189 | return
190 |
191 |
192 | def launch (transparent=False, hold_down=_flood_delay):
193 | """
194 | Starts an L2 learning switch.
195 | """
196 | try:
197 | global _flood_delay
198 | _flood_delay = int(str(hold_down), 10)
199 | assert _flood_delay >= 0
200 | except:
201 | raise RuntimeError("Expected hold-down to be a number")
202 |
203 | core.registerNew(l2_learning, str_to_bool(transparent))
204 |
--------------------------------------------------------------------------------
/ik2220-assign-phase2-team5.tar.gz:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shrin18/SDNProject/824ac53287c27f18df2e4022693fd0ca8dd5fab7/ik2220-assign-phase2-team5.tar.gz
--------------------------------------------------------------------------------
/shrinish.txt:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/src/dns_server.py:
--------------------------------------------------------------------------------
1 | #DNS Server
2 |
3 | import socket, glob, json
4 |
5 | port = 53
6 | ip = '127.0.0.1'
7 |
8 | sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
9 | sock.bind((ip, port))
10 |
11 |
12 | def load_zones():
13 |
14 | jsonzone = {}
15 | zonefiles = glob.glob('zones/*.zone')
16 |
17 | for zone in zonefiles:
18 | with open(zone) as zonedata:
19 | data = json.load(zonedata)
20 | zonename = data["$origin"]
21 | jsonzone[zonename] = data
22 | return jsonzone
23 |
24 | zonedata = load_zones()
25 |
26 | def getflags(flags):
27 |
28 | byte1 = bytes(flags[:1])
29 | byte2 = bytes(flags[1:2])
30 |
31 | rflags = ''
32 |
33 | QR = '1'
34 |
35 | OPCODE = ''
36 | for bit in range(1,5):
37 | OPCODE += str(ord(byte1)&(1<