backing up
[vsorcdistro/.git] / ryu / build / lib.linux-armv7l-2.7 / ryu / services / protocols / vrrp / sample_router.py
1 # Copyright (C) 2013 Nippon Telegraph and Telephone Corporation.
2 # Copyright (C) 2013 Isaku Yamahata <yamahata at private email ne jp>
3 #
4 # Licensed under the Apache License, Version 2.0 (the "License");
5 # you may not use this file except in compliance with the License.
6 # You may obtain a copy of the License at
7 #
8 #    http://www.apache.org/licenses/LICENSE-2.0
9 #
10 # Unless required by applicable law or agreed to in writing, software
11 # distributed under the License is distributed on an "AS IS" BASIS,
12 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
13 # implied.
14 # See the License for the specific language governing permissions and
15 # limitations under the License.
16
17 """
18 router implementation base class
19 a template for router implementation that support VRRP
20 Those routers needs to be created by someone else.
21 sample_manager.routerManager is an example.
22 Usage example:
23 PYTHONPATH=. ./bin/ryu-manager --verbose \
24              ryu.services.protocols.vrrp.manager \
25              ryu.services.protocols.vrrp.dumper \
26              ryu.services.protocols.vrrp.sample_manager
27 """
28
29 import contextlib
30 import greenlet
31 import socket
32
33 from ryu.base import app_manager
34 from ryu.controller import handler
35 from ryu.controller import ofp_event
36 from ryu.lib import hub
37 from ryu.lib import mac as mac_lib
38 from ryu.lib.packet import arp
39 from ryu.lib.packet import ethernet
40 from ryu.lib.packet import packet
41 from ryu.lib.packet import vlan
42 from ryu.lib.packet import vrrp
43 from ryu.ofproto import ether
44 from ryu.ofproto import ofproto_v1_2
45 from ryu.services.protocols.vrrp import api as vrrp_api
46 from ryu.services.protocols.vrrp import event as vrrp_event
47 from ryu.services.protocols.vrrp import utils
48
49
50 class RouterBase(app_manager.RyuApp):
51     def _router_name(self, config, interface):
52         ip_version = 'ipv6' if config.is_ipv6 else 'ipv4'
53         return '%s-%s-%d-%s' % (self.__class__.__name__,
54                                 str(interface), config.vrid, ip_version)
55
56     def __init__(self, *args, **kwargs):
57         super(RouterBase, self).__init__(*args, **kwargs)
58         self.instance_name = kwargs['name']
59         self.monitor_name = kwargs['monitor_name']
60         self.config = kwargs['config']
61         self.interface = kwargs['interface']
62         self.name = self._router_name(self.config, self.interface)
63
64     def _transmit(self, data):
65         vrrp_api.vrrp_transmit(self, self.monitor_name, data)
66
67     def _initialized(self):
68         self.logger.debug('initialized')
69
70     def _initialized_to_master(self):
71         self.logger.debug('initialized to master')
72         # RFC3768 6.4.1
73         # o  Broadcast a gratuitous ARP request containing the virtual
74         # router MAC address for each IP address associated with the
75         # virtual router.
76         #
77         # or
78         #
79         # RFC 5795 6.4.1
80         # (115)+ If the protected IPvX address is an IPv4 address, then:
81         #   (120) * Broadcast a gratuitous ARP request containing the
82         #   virtual router MAC address for each IP address associated
83         #   with the virtual router.
84         # (125) + else // IPv6
85         #   (130) * For each IPv6 address associated with the virtual
86         #   router, send an unsolicited ND Neighbor Advertisement with
87         #   the Router Flag (R) set, the Solicited Flag (S) unset, the
88         #   Override flag (O) set, the target address set to the IPv6
89         #   address of the virtual router, and the target link-layer
90         #   address set to the virtual router MAC address.
91
92     def _become_master(self):
93         self.logger.debug('become master')
94         # RFC3768 6.4.2
95         # o  Broadcast a gratuitous ARP request containing the virtual
96         #    router MAC address for each IP address associated with the
97         #    virtual router
98         #
99         # or
100         #
101         # RFC 5795 6.4.2
102         # (375)+ If the protected IPvX address is an IPv4 address, then:
103         #   (380)* Broadcast a gratuitous ARP request on that interface
104         #   containing the virtual router MAC address for each IPv4
105         #   address associated with the virtual router.
106         # (385) + else // ipv6
107         #   (390) * Compute and join the Solicited-Node multicast
108         #   address [RFC4291] for the IPv6 address(es) associated with
109         #   the virtual router.
110         #   (395) * For each IPv6 address associated with the virtual
111         #   router, send an unsolicited ND Neighbor Advertisement with
112         #   the Router Flag (R) set, the Solicited Flag (S) unset, the
113         #   Override flag (O) set, the target address set to the IPv6
114         #   address of the virtual router, and the target link-layer
115         #   address set to the virtual router MAC address.
116
117     def _become_backup(self):
118         self.logger.debug('become backup')
119         # RFC 3768 6.4.2 Backup
120         # -  MUST NOT respond to ARP requests for the IP address(s)
121         #    associated with the virtual router.
122         # -  MUST discard packets with a destination link layer MAC address
123         #    equal to the virtual router MAC address.
124         # -  MUST NOT accept packets addressed to the IP address(es)
125         #    associated with the virtual router.
126         #
127         # or
128         #
129         # RFC 5798 6.4.2 Backup
130         # (305) - If the protected IPvX address is an IPv4 address, then:
131         #   (310) + MUST NOT respond to ARP requests for the IPv4
132         #   address(es) associated with the virtual router.
133         # (315) - else // protected addr is IPv6
134         #   (320) + MUST NOT respond to ND Neighbor Solicitation messages
135         #   for the IPv6 address(es) associated with the virtual router.
136         #   (325) + MUST NOT send ND Router Advertisement messages for the
137         #   virtual router.
138         # (330) -endif // was protected addr IPv4?
139         # (335) - MUST discard packets with a destination link-layer MAC
140         # address equal to the virtual router MAC address.
141         # (340) - MUST NOT accept packets addressed to the IPvX address(es)
142         # associated with the virtual router.
143
144     def _shutdowned(self):
145         self.logger.debug('shutdowned')
146
147     @handler.set_ev_handler(vrrp_event.EventVRRPStateChanged)
148     def vrrp_state_changed_handler(self, ev):
149         old_state = ev.old_state
150         new_state = ev.new_state
151         self.logger.debug('sample router %s -> %s', old_state, new_state)
152         if new_state == vrrp_event.VRRP_STATE_MASTER:
153             if old_state == vrrp_event.VRRP_STATE_INITIALIZE:
154                 self._initialized_to_master()
155             elif old_state == vrrp_event.VRRP_STATE_BACKUP:
156                 self._become_master()
157
158             # RFC 3768 6.4.3
159             # -  MUST respond to ARP requests for the IP address(es) associated
160             #    with the virtual router.
161             # -  MUST forward packets with a destination link layer MAC address
162             #    equal to the virtual router MAC address.
163             # -  MUST NOT accept packets addressed to the IP address(es)
164             #    associated with the virtual router if it is not the IP address
165             #    owner.
166             # -  MUST accept packets addressed to the IP address(es) associated
167             #    with the virtual router if it is the IP address owner.
168             #
169             # or
170             #
171             # RFC5798 6.4.3
172             # (605) - If the protected IPvX address is an IPv4 address, then:
173             #   (610) + MUST respond to ARP requests for the IPv4 address(es)
174             #   associated with the virtual router.
175             # (615) - else // ipv6
176             #   (620) + MUST be a member of the Solicited-Node multicast
177             #   address for the IPv6 address(es) associated with the virtual
178             #   router.
179             #   (625) + MUST respond to ND Neighbor Solicitation message for
180             #   the IPv6 address(es) associated with the virtual router.
181             #   (630) ++ MUST send ND Router Advertisements for the virtual
182             #   router.
183             #   (635) ++ If Accept_Mode is False:  MUST NOT drop IPv6 Neighbor
184             #   Solicitations and Neighbor Advertisements.
185             # (640) +-endif // ipv4?
186             # (645) - MUST forward packets with a destination link-layer MAC
187             # address equal to the virtual router MAC address.
188             # (650) - MUST accept packets addressed to the IPvX address(es)
189             # associated with the virtual router if it is the IPvX address
190             # owner or if Accept_Mode is True.  Otherwise, MUST NOT accept
191             # these packets.
192
193         elif new_state == vrrp_event.VRRP_STATE_BACKUP:
194             self._become_backup()
195         elif new_state == vrrp_event.VRRP_STATE_INITIALIZE:
196             if old_state is None:
197                 self._initialized()
198             else:
199                 self._shutdowned()
200         else:
201             raise ValueError('invalid vrrp state %s' % new_state)
202
203
204 class RouterIPV4(RouterBase):
205     def _garp_packet(self, ip_address):
206         # prepare garp packet
207         src_mac = vrrp.vrrp_ipv4_src_mac_address(self.config.vrid)
208         e = ethernet.ethernet(mac_lib.BROADCAST_STR, src_mac,
209                               ether.ETH_TYPE_ARP)
210         a = arp.arp_ip(arp.ARP_REQUEST, src_mac, ip_address,
211                        mac_lib.DONTCARE_STR, ip_address)
212
213         p = packet.Packet()
214         p.add_protocol(e)
215         utils.may_add_vlan(p, self.interface.vlan_id)
216         p.add_protocol(a)
217         p.serialize()
218         return p
219
220     def __init__(self, *args, **kwargs):
221         super(RouterIPV4, self).__init__(*args, **kwargs)
222         assert not self.config.is_ipv6
223
224         self.garp_packets = [self._garp_packet(ip_address)
225                              for ip_address in self.config.ip_addresses]
226
227     def _send_garp(self):
228         self.logger.debug('_send_garp')
229         for garp_packet in self.garp_packets:
230             self._transmit(garp_packet.data)
231
232     def _arp_reply_packet(self, arp_req_sha, arp_req_spa, arp_req_tpa):
233         if not (arp_req_tpa in self.config.ip_addresses or
234                 arp_req_tpa == self.config.primary_ip_address):
235             return None
236
237         src_mac = vrrp.vrrp_ipv4_src_mac_address(self.config.vrid)
238         e = ethernet.ethernet(arp_req_sha, src_mac, ether.ETH_TYPE_ARP)
239         a = arp.arp_ip(arp.ARP_REPLY, src_mac, arp_req_tpa,
240                        arp_req_sha, arp_req_spa)
241
242         p = packet.Packet()
243         p.add_protocol(e)
244         utils.may_add_vlan(p, self.interface.vlan_id)
245         p.add_protocol(a)
246         p.serialize()
247         self._transmit(p.data)
248
249     def _arp_process(self, data):
250         dst_mac = vrrp.vrrp_ipv4_src_mac_address(self.config.vrid)
251         arp_sha = None
252         arp_spa = None
253         arp_tpa = None
254
255         p = packet.Packet(data)
256         for proto in p.protocols:
257             if isinstance(proto, ethernet.ethernet):
258                 if proto.dst not in (mac_lib.BROADCAST_STR, dst_mac):
259                     return None
260                 ethertype = proto.ethertype
261                 if not ((self.interface.vlan_id is None and
262                          ethertype == ether.ETH_TYPE_ARP) or
263                         (self.interface.vlan_id is not None and
264                          ethertype == ether.ETH_TYPE_8021Q)):
265                     return None
266             elif isinstance(proto, vlan.vlan):
267                 if (proto.vid != self.interface.vlan_id or
268                         proto.ethertype != ether.ETH_TYPE_ARP):
269                     return None
270             elif isinstance(proto, arp.arp):
271                 if (proto.hwtype != arp.ARP_HW_TYPE_ETHERNET or
272                     proto.proto != ether.ETH_TYPE_IP or
273                     proto.hlen != 6 or proto.plen != 4 or
274                     proto.opcode != arp.ARP_REQUEST or
275                         proto.dst_mac != dst_mac):
276                     return None
277                 arp_sha = proto.src_mac
278                 arp_spa = proto.src_ip
279                 arp_tpa = proto.dst_ip
280                 break
281
282         if arp_sha is None or arp_spa is None or arp_tpa is None:
283             self.logger.debug('malformed arp request? arp_sha %s arp_spa %s',
284                               arp_sha, arp_spa)
285             return None
286
287         self._arp_reply_packet(arp_sha, arp_spa, arp_tpa)
288
289
290 class RouterIPV4Linux(RouterIPV4):
291     def __init__(self, *args, **kwargs):
292         super(RouterIPV4Linux, self).__init__(*args, **kwargs)
293         assert isinstance(self.interface,
294                           vrrp_event.VRRPInterfaceNetworkDevice)
295         self.__is_master = False
296         self._arp_thread = None
297
298     def start(self):
299         self._disable_router()
300         super(RouterIPV4Linux, self).start()
301
302     def _initialized_to_master(self):
303         self.logger.debug('initialized to master')
304         self._master()
305
306     def _become_master(self):
307         self.logger.debug('become master')
308         self._master()
309
310     def _master(self):
311         self.__is_master = True
312         self._enable_router()
313         self._send_garp()
314
315     def _become_backup(self):
316         self.logger.debug('become backup')
317         self.__is_master = False
318         self._disable_router()
319
320     def _shutdowned(self):
321         # When VRRP functionality is disabled, what to do?
322         #  should we also exit? or continue to route packets?
323         self._disable_router()
324
325     def _arp_loop_socket(self, packet_socket):
326         while True:
327             try:
328                 buf = packet_socket.recv(1500)
329             except socket.timeout:
330                 continue
331
332             self._arp_process(buf)
333
334     def _arp_loop(self):
335         try:
336             with contextlib.closing(
337                 socket.socket(
338                     socket.AF_PACKET, socket.SOCK_RAW,
339                     socket.htons(ether.ETH_TYPE_ARP))) as packet_socket:
340                 packet_socket.bind((self.interface.device_name,
341                                     socket.htons(ether.ETH_TYPE_ARP),
342                                     socket.PACKET_BROADCAST,
343                                     arp.ARP_HW_TYPE_ETHERNET,
344                                     mac_lib.BROADCAST))
345                 self._arp_loop_socket(packet_socket)
346         except greenlet.GreenletExit:
347             # suppress thread.kill exception
348             pass
349
350     def _enable_router(self):
351         if self._arp_thread is None:
352             self._arp_thread = hub.spawn(self._arp_loop)
353         # TODO: implement real routing logic
354         self.logger.debug('TODO:_enable_router')
355
356     def _disable_router(self):
357         if self._arp_thread is not None:
358             self._arp_thread.kill()
359             hub.joinall([self._arp_thread])
360             self._arp_thread = None
361         # TODO: implement real routing logic
362         self.logger.debug('TODO:_disable_router')
363
364
365 class RouterIPV4OpenFlow(RouterIPV4):
366     OFP_VERSIONS = [ofproto_v1_2.OFP_VERSION]
367
368     # it must be that
369     # _DROP_PRIORITY < monitor.VRRPInterfaceMonitorOpenFlow._PRIORITY or
370     # _DROP_TABLE > monitor.VRRPInterfaceMonitorOpenFlow._TABLE
371     # to gurantee that VRRP packets are send to controller
372     _DROP_TABLE = 0
373     _DROP_PRIORITY = 0x8000 / 2
374
375     # it must be that
376     # _ARP_PRIORITY < _DROP_PRIORITY or
377     # _ARP_TABLE > _DROP_TABLE
378     # to gurantee that responding arp can be disabled
379     _ARP_TABLE = 0
380     _ARP_PRIORITY = _DROP_PRIORITY // 2
381
382     # it must be that
383     # _ROUTEING_TABLE < _ARP_TABLE or
384     # _ROUTING_TABLE > _ARP_TABLE
385     # to gurantee that routing can be disabled
386     _ROUTING_TABLE = 0
387     _ROUTING_PRIORITY = _ARP_PRIORITY // 2
388
389     def __init__(self, *args, **kwargs):
390         super(RouterIPV4OpenFlow, self).__init__(*args, **kwargs)
391         assert isinstance(self.interface, vrrp_event.VRRPInterfaceOpenFlow)
392
393     def _get_dp(self):
394         return utils.get_dp(self, self.interface.dpid)
395
396     def start(self):
397         dp = self._get_dp()
398         assert dp
399         self._uninstall_route_rule(dp)
400         self._uninstall_arp_rule(dp)
401         self._uninstall_drop_rule(dp)
402         self._install_drop_rule(dp)
403         self._install_arp_rule(dp)
404         self._install_route_rule(dp)
405         super(RouterIPV4OpenFlow, self).start()
406
407     def _initialized_to_master(self):
408         self.logger.debug('initialized to master')
409         self._master()
410
411     def _become_master(self):
412         self.logger.debug('become master')
413         self._master()
414
415     def _master(self):
416         dp = self._get_dp()
417         if dp is None:
418             return
419
420         self._uninstall_drop_rule(dp)
421         self._send_garp(dp)
422
423     def _become_backup(self):
424         self.logger.debug('become backup')
425         dp = self._get_dp()
426         if dp is None:
427             return
428
429         self._install_drop_rule(dp)
430
431     def _shutdowned(self):
432         dp = self._get_dp()
433         if dp is None:
434             return
435
436         # When VRRP functionality is disabled, what to do?
437         #  should we also exit? or continue to route packets?
438         self._uninstall_route_rule(dp)
439         self._uninstall_arp_rule(dp)
440         self._uninstall_drop_rule(dp)
441
442     @handler.set_ev_cls(ofp_event.EventOFPPacketIn, handler.MAIN_DISPATCHER)
443     def packet_in_handler(self, ev):
444         msg = ev.msg
445         datapath = msg.datapath
446         ofproto = datapath.ofproto
447
448         # TODO: subscribe only the datapath that we route
449         dpid = datapath.dpid
450         if dpid != self.interface.dpid:
451             return
452
453         for field in msg.match.fields:
454             header = field.header
455             if header == ofproto.OXM_OF_IN_PORT:
456                 if field.value != self.interface.port_no:
457                     return
458                 break
459
460         self._arp_process(msg.data)
461
462     def _drop_match(self, dp):
463         kwargs = {}
464         kwargs['in_port'] = self.interface.port_no
465         kwargs['eth_dst'] = vrrp.vrrp_ipv4_src_mac_address(self.config.vrid)
466         if self.interface.vlan_id is not None:
467             kwargs['vlan_vid'] = self.interface.vlan_id
468         return dp.ofproto_parser.OFPMatch(**kwargs)
469
470     def _install_drop_rule(self, dp):
471         match = self._drop_match(dp)
472         utils.dp_flow_mod(dp, self._DROP_TABLE, dp.ofproto.OFPFC_ADD,
473                           self._DROP_PRIORITY, match, [])
474
475     def _uninstall_drop_rule(self, dp):
476         match = self._drop_match(dp)
477         utils.dp_flow_mod(dp, self._DROP_TABLE, dp.ofproto.OFPFC_DELETE_STRICT,
478                           self._DROP_PRIORITY, match, [])
479
480     def _arp_match(self, dp):
481         kwargs = {}
482         kwargs['in_port'] = self.interface.port_no
483         kwargs['eth_dst'] = mac_lib.BROADCAST_STR
484         kwargs['eth_type'] = ether.ETH_TYPE_ARP
485         if self.interface.vlan_id is not None:
486             kwargs['vlan_vid'] = self.interface.vlan_id
487         kwargs['arp_op'] = arp.ARP_REQUEST
488         kwargs['arp_tpa'] = vrrp.vrrp_ipv4_src_mac_address(self.config.vrid)
489         return dp.ofproto_parser.OFPMatch(**kwargs)
490
491     def _install_arp_rule(self, dp):
492         ofproto = dp.ofproto
493         ofproto_parser = dp.ofproto_parser
494
495         match = self._arp_match(dp)
496         actions = [ofproto_parser.OFPActionOutput(ofproto.OFPP_CONTROLLER,
497                                                   ofproto.OFPCML_NO_BUFFER)]
498         instructions = [ofproto_parser.OFPInstructionActions(
499             ofproto.OFPIT_APPLY_ACTIONS, actions)]
500         utils.dp_flow_mod(dp, self._ARP_TABLE, dp.fproto.OFPFC_ADD,
501                           self._ARP_PRIORITY, match, instructions)
502
503     def _uninstall_arp_rule(self, dp):
504         match = self._arp_match(dp)
505         utils.dp_flow_mod(dp, self._ARP_TABLE, dp.fproto.OFPFC_DELETE_STRICT,
506                           self._ARP_PRIORITY, match, [])
507
508     def _install_route_rule(self, dp):
509         # TODO: implement real routing logic
510         self.logger.debug('TODO:_install_router_rule')
511
512     def _uninstall_route_rule(self, dp):
513         # TODO: implement real routing logic
514         self.logger.debug('TODO:_uninstall_router_rule')
515
516
517 class RouterIPV6(RouterBase):
518     def __init__(self, *args, **kwargs):
519         super(RouterIPV6, self).__init__(*args, **kwargs)
520         assert self.config.is_ipv6
521
522
523 class RouterIPV6Linux(RouterIPV6):
524     def __init__(self, *args, **kwargs):
525         super(RouterIPV6Linux, self).__init__(*args, **kwargs)
526         assert isinstance(self.interface,
527                           vrrp_event.VRRPInterfaceNetworkDevice)
528
529     # TODO: reader's home work
530     pass
531
532
533 class RouterIPV6OpenFlow(RouterIPV6):
534     def __init__(self, *args, **kwargs):
535         super(RouterIPV6OpenFlow, self).__init__(*args, **kwargs)
536         assert isinstance(self.interface, vrrp_event.VRRPInterfaceOpenFlow)
537
538     # TODO: reader's home work
539     pass