backing up
[vsorcdistro/.git] / ryu / build / lib.linux-armv7l-2.7 / ryu / app / sdnhub_apps / stateless_lb.py
1 # Copyright (C) 2014 SDN Hub
2 #
3 # Licensed under the GNU GENERAL PUBLIC LICENSE, Version 3.
4 # You may not use this file except in compliance with this License.
5 # You may obtain a copy of the License at
6 #
7 #    http://www.gnu.org/licenses/gpl-3.0.txt
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
12 # implied.
13
14 import logging
15 import json
16 import random
17
18 from ryu.lib import mac as mac_lib
19 from ryu.lib import ip as ip_lib
20 from ryu.base import app_manager
21 from ryu.controller import ofp_event
22 from ryu.controller.handler import MAIN_DISPATCHER, CONFIG_DISPATCHER
23 from ryu.controller.handler import set_ev_cls
24
25 from ryu.lib.packet import packet
26 from ryu.lib.packet import ethernet
27 from ryu.lib.packet import ipv4
28 from ryu.lib.packet import tcp
29 from ryu.lib.packet import arp
30 from ryu.ofproto import ether, inet
31 from ryu.ofproto import ofproto_v1_0, ofproto_v1_3
32 from ryu.lib import dpid as dpid_lib
33 import learning_switch
34
35 UINT32_MAX = 0xffffffff
36
37 ################ Main ###################
38
39 # The stateless server load balancer picks a different server for each
40 # request. For making the assignment, it only uses the servers it
41 # already knows the location of. The clients or the gateway sents along
42 # a request for the Virtual IP of the load-balancer. The first switch
43 # intercepting the request will rewrite the headers to match the actual
44 # server picked. So all other switches will only have to do simple
45 # L2 forwarding. It is possible to avoid IP header writing if alias IP
46 # is set on the servers. The call skip_ip_header_rewriting() will handle
47 # the appropriate flag setting.
48
49 class StatelessLB(app_manager.RyuApp):
50
51     def __init__(self, *args, **kwargs):
52         super(StatelessLB, self).__init__(*args, **kwargs)
53         self.rewrite_ip_header = True
54         self.server_index = 0
55         self.servers = []
56
57         self.virtual_ip = None
58         #self.virtual_ip = "10.0.0.5"
59         self.virtual_mac = "A6:63:DD:D7:C0:C8" # Pick something dummy and
60
61         #self.servers.append({'ip':"10.0.0.2", 'mac':"00:00:00:00:00:02"})
62         #self.servers.append({'ip':"10.0.0.3", 'mac':"00:00:00:00:00:03"})
63         #self.servers.append({'ip':"10.0.0.4", 'mac':"00:00:00:00:00:04"})
64
65         #self.learning_switch = kwargs['learning_switch']
66         #self.learning_switch.add_exemption({'dl_type': ether.ETH_TYPE_LLDP})
67         #self.learning_switch.add_exemption({'dl_dst': self.virtual_mac})
68
69     def set_learning_switch(self, learning_switch):
70         self.learning_switch = learning_switch
71         self.learning_switch.clear_exemption()
72         self.learning_switch.add_exemption({'dl_dst': self.virtual_mac})
73
74     # Users can skip doing header rewriting by setting the virtual IP 
75     # as an alias IP on all the servers. This works well in single subnet
76     def set_rewrite_ip_flag(self, rewrite_ip):
77         if rewrite_ip == 1:
78             self.rewrite_ip_header = True
79         else:
80             self.rewrite_ip_header = False
81
82     def set_virtual_ip(self, virtual_ip=None):
83         self.virtual_ip = virtual_ip
84
85     def set_server_pool(self, servers=None):
86         self.servers = servers
87
88     def formulate_arp_reply(self, dst_mac, dst_ip):
89         if self.virtual_ip == None:
90             return
91
92         src_mac = self.virtual_mac
93         src_ip = self.virtual_ip
94         arp_opcode = arp.ARP_REPLY
95         arp_target_mac = dst_mac
96
97         ether_proto = ether.ETH_TYPE_ARP
98         hwtype = 1
99         arp_proto = ether.ETH_TYPE_IP
100         hlen = 6
101         plen = 4
102
103         pkt = packet.Packet()
104         e = ethernet.ethernet(dst_mac, src_mac, ether_proto)
105         a = arp.arp(hwtype, arp_proto, hlen, plen, arp_opcode,
106                     src_mac, src_ip, arp_target_mac, dst_ip)
107         pkt.add_protocol(e)
108         pkt.add_protocol(a)
109         pkt.serialize()
110
111         return pkt
112
113
114     @set_ev_cls(ofp_event.EventOFPPacketIn, MAIN_DISPATCHER)
115     def packet_in_handler(self, ev):
116         if self.virtual_ip == None or self.servers == None:
117             return
118
119         msg = ev.msg
120         datapath = msg.datapath
121         ofp = datapath.ofproto
122         ofp_parser = datapath.ofproto_parser
123         in_port = msg.match['in_port']
124         dpid = datapath.id
125
126         pkt = packet.Packet(msg.data)
127         eth = pkt.get_protocols(ethernet.ethernet)[0]
128
129         if eth.ethertype == ether.ETH_TYPE_ARP:
130             arp_hdr = pkt.get_protocols(arp.arp)[0]
131
132             if arp_hdr.dst_ip == self.virtual_ip and arp_hdr.opcode == arp.ARP_REQUEST:
133
134                 reply_pkt = self.formulate_arp_reply(arp_hdr.src_mac,
135                         arp_hdr.src_ip)
136
137                 actions = [ofp_parser.OFPActionOutput(in_port)]
138                 out = ofp_parser.OFPPacketOut(datapath=datapath, 
139                            in_port=ofp.OFPP_ANY, data=reply_pkt.data,
140                            actions=actions, buffer_id = UINT32_MAX)
141                 datapath.send_msg(out)
142
143             return
144
145         # Only handle IPv4 traffic going forward
146         elif eth.ethertype != ether.ETH_TYPE_IP:
147             return
148
149         iphdr = pkt.get_protocols(ipv4.ipv4)[0]
150
151         # Only handle traffic destined to virtual IP
152         if (iphdr.dst != self.virtual_ip):
153             return 
154
155         # Only handle TCP traffic
156         if iphdr.proto != inet.IPPROTO_TCP:
157             return 
158
159         tcphdr = pkt.get_protocols(tcp.tcp)[0]
160
161         valid_servers = []
162         for server in self.servers:
163             outport = self.learning_switch.get_attachment_port(dpid, server['mac'])
164             if outport != None:
165                 server['outport'] = outport
166                 valid_servers.append(server)
167
168         total_servers = len(valid_servers)
169
170         # If we there are no servers with location known, then skip
171         if total_servers == 0:
172             return
173
174         # Round robin selection of servers
175         index = self.server_index % total_servers
176         selected_server_ip = valid_servers[index]['ip']
177         selected_server_mac = valid_servers[index]['mac']
178         selected_server_outport = valid_servers[index]['outport']
179         self.server_index += 1
180         print "Selected server", selected_server_ip
181
182         ########### Setup route to server
183         match = ofp_parser.OFPMatch(in_port=in_port,
184                 eth_type=eth.ethertype,  eth_src=eth.src,    eth_dst=eth.dst, 
185                 ip_proto=iphdr.proto,    ipv4_src=iphdr.src, ipv4_dst=iphdr.dst,
186                 tcp_src=tcphdr.src_port, tcp_dst=tcphdr.dst_port)
187
188         if self.rewrite_ip_header:
189             actions = [ofp_parser.OFPActionSetField(eth_dst=selected_server_mac),
190                        ofp_parser.OFPActionSetField(ipv4_dst=selected_server_ip),
191                        ofp_parser.OFPActionOutput(selected_server_outport) ]
192         else:
193             actions = [ofp_parser.OFPActionSetField(eth_dst=selected_server_mac),
194                        ofp_parser.OFPActionOutput(selected_server_outport) ]
195
196         inst = [ofp_parser.OFPInstructionActions(ofp.OFPIT_APPLY_ACTIONS, actions)]
197
198         cookie = random.randint(0, 0xffffffffffffffff)
199
200         mod = ofp_parser.OFPFlowMod(datapath=datapath, match=match, idle_timeout=10,
201                 instructions=inst, buffer_id = msg.buffer_id, cookie=cookie)
202         datapath.send_msg(mod)
203
204         ########### Setup reverse route from server
205         match = ofp_parser.OFPMatch(in_port=selected_server_outport,
206                 eth_type=eth.ethertype,  eth_src=selected_server_mac, eth_dst=eth.src, 
207                 ip_proto=iphdr.proto,    ipv4_src=selected_server_ip, ipv4_dst=iphdr.src,
208                 tcp_src=tcphdr.dst_port, tcp_dst=tcphdr.src_port)
209
210         if self.rewrite_ip_header:
211             actions = ([ofp_parser.OFPActionSetField(eth_src=self.virtual_mac),
212                        ofp_parser.OFPActionSetField(ipv4_src=self.virtual_ip),
213                        ofp_parser.OFPActionOutput(in_port) ])
214         else:
215             actions = ([ofp_parser.OFPActionSetField(eth_src=self.virtual_mac),
216                        ofp_parser.OFPActionOutput(in_port) ])
217
218         inst = [ofp_parser.OFPInstructionActions(ofp.OFPIT_APPLY_ACTIONS, actions)]
219
220         cookie = random.randint(0, 0xffffffffffffffff)
221
222         mod = ofp_parser.OFPFlowMod(datapath=datapath, match=match, idle_timeout=10,
223                 instructions=inst, cookie=cookie)
224         datapath.send_msg(mod)
225