1 # Copyright (C) 2013 Nippon Telegraph and Telephone Corporation.
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
7 # http://www.apache.org/licenses/LICENSE-2.0
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
13 # See the License for the specific language governing permissions and
14 # limitations under the License.
24 from ryu.app.wsgi import ControllerBase
25 from ryu.app.wsgi import Response
26 from ryu.app.wsgi import WSGIApplication
27 from ryu.base import app_manager
28 from ryu.controller import dpset
29 from ryu.controller import ofp_event
30 from ryu.controller.handler import set_ev_cls
31 from ryu.controller.handler import MAIN_DISPATCHER
32 from ryu.exception import OFPUnknownVersion
33 from ryu.exception import RyuException
34 from ryu.lib import dpid as dpid_lib
35 from ryu.lib import hub
36 from ryu.lib import mac as mac_lib
37 from ryu.lib import addrconv
38 from ryu.lib.packet import arp
39 from ryu.lib.packet import ethernet
40 from ryu.lib.packet import icmp
41 from ryu.lib.packet import ipv4
42 from ryu.lib.packet import packet
43 from ryu.lib.packet import packet_base
44 from ryu.lib.packet import tcp
45 from ryu.lib.packet import udp
46 from ryu.lib.packet import vlan
47 from ryu.ofproto import ether
48 from ryu.ofproto import inet
49 from ryu.ofproto import ofproto_v1_0
50 from ryu.ofproto import ofproto_v1_2
51 from ryu.ofproto import ofproto_v1_3
54 # =============================
56 # =============================
58 # Note: specify switch and vlan group, as follows.
59 # {switch_id} : 'all' or switchID
60 # {vlan_id} : 'all' or vlanID
63 # 1. get address data and routing data.
65 # * get data of no vlan
66 # GET /router/{switch_id}
68 # * get data of specific vlan group
69 # GET /router/{switch_id}/{vlan_id}
72 # 2. set address data or routing data.
74 # * set data of no vlan
75 # POST /router/{switch_id}
77 # * set data of specific vlan group
78 # POST /router/{switch_id}/{vlan_id}
80 # case1: set address data.
81 # parameter = {"address": "A.B.C.D/M"}
82 # case2-1: set static route.
83 # parameter = {"destination": "A.B.C.D/M", "gateway": "E.F.G.H"}
84 # case2-2: set default route.
85 # parameter = {"gateway": "E.F.G.H"}
88 # 3. delete address data or routing data.
90 # * delete data of no vlan
91 # DELETE /router/{switch_id}
93 # * delete data of specific vlan group
94 # DELETE /router/{switch_id}/{vlan_id}
96 # case1: delete address data.
97 # parameter = {"address_id": "<int>"} or {"address_id": "all"}
98 # case2: delete routing data.
99 # parameter = {"route_id": "<int>"} or {"route_id": "all"}
105 UINT32_MAX = 0xffffffff
106 UINT64_MAX = 0xffffffffffffffff
108 ETHERNET = ethernet.ethernet.__name__
109 VLAN = vlan.vlan.__name__
110 IPV4 = ipv4.ipv4.__name__
111 ARP = arp.arp.__name__
112 ICMP = icmp.icmp.__name__
113 TCP = tcp.tcp.__name__
114 UDP = udp.udp.__name__
116 MAX_SUSPENDPACKETS = 50 # Threshold of the packet suspends thread count.
118 ARP_REPLY_TIMER = 2 # sec
119 OFP_REPLY_TIMER = 1.0 # sec
120 CHK_ROUTING_TBL_INTERVAL = 1800 # sec
122 SWITCHID_PATTERN = dpid_lib.DPID_PATTERN + r'|all'
123 VLANID_PATTERN = r'[0-9]{1,4}|all'
129 COOKIE_DEFAULT_ID = 0
130 COOKIE_SHIFT_VLANID = 32
131 COOKIE_SHIFT_ROUTEID = 16
133 DEFAULT_ROUTE = '0.0.0.0/0'
134 IDLE_TIMEOUT = 1800 # sec
137 REST_COMMAND_RESULT = 'command_result'
138 REST_RESULT = 'result'
139 REST_DETAILS = 'details'
143 REST_SWITCHID = 'switch_id'
144 REST_VLANID = 'vlan_id'
145 REST_NW = 'internal_network'
146 REST_ADDRESSID = 'address_id'
147 REST_ADDRESS = 'address'
148 REST_ROUTEID = 'route_id'
150 REST_DESTINATION = 'destination'
151 REST_GATEWAY = 'gateway'
153 PRIORITY_VLAN_SHIFT = 1000
154 PRIORITY_NETMASK_SHIFT = 32
157 PRIORITY_ARP_HANDLING = 1
158 PRIORITY_DEFAULT_ROUTING = 1
159 PRIORITY_MAC_LEARNING = 2
160 PRIORITY_STATIC_ROUTING = 2
161 PRIORITY_IMPLICIT_ROUTING = 3
162 PRIORITY_L2_SWITCHING = 4
163 PRIORITY_IP_HANDLING = 5
165 PRIORITY_TYPE_ROUTE = 'priority_route'
168 def get_priority(priority_type, vid=0, route=None):
170 priority = priority_type
172 if priority_type == PRIORITY_TYPE_ROUTE:
173 assert route is not None
175 priority_type = PRIORITY_STATIC_ROUTING
176 priority = priority_type + route.netmask
177 log_msg = 'static routing'
179 priority_type = PRIORITY_DEFAULT_ROUTING
180 priority = priority_type
181 log_msg = 'default routing'
183 if vid or priority_type == PRIORITY_IP_HANDLING:
184 priority += PRIORITY_VLAN_SHIFT
186 if priority_type > PRIORITY_STATIC_ROUTING:
187 priority += PRIORITY_NETMASK_SHIFT
192 return priority, log_msg
195 def get_priority_type(priority, vid):
197 priority -= PRIORITY_VLAN_SHIFT
201 class NotFoundError(RyuException):
202 message = 'Router SW is not connected. : switch_id=%(switch_id)s'
205 class CommandFailure(RyuException):
209 class RestRouterAPI(app_manager.RyuApp):
211 OFP_VERSIONS = [ofproto_v1_0.OFP_VERSION,
212 ofproto_v1_2.OFP_VERSION,
213 ofproto_v1_3.OFP_VERSION]
215 _CONTEXTS = {'dpset': dpset.DPSet,
216 'wsgi': WSGIApplication}
218 def __init__(self, *args, **kwargs):
219 super(RestRouterAPI, self).__init__(*args, **kwargs)
222 RouterController.set_logger(self.logger)
224 wsgi = kwargs['wsgi']
226 self.data = {'waiters': self.waiters}
229 wsgi.registory['RouterController'] = self.data
230 requirements = {'switch_id': SWITCHID_PATTERN,
231 'vlan_id': VLANID_PATTERN}
234 path = '/router/{switch_id}'
235 mapper.connect('router', path, controller=RouterController,
236 requirements=requirements,
238 conditions=dict(method=['GET']))
239 mapper.connect('router', path, controller=RouterController,
240 requirements=requirements,
242 conditions=dict(method=['POST']))
243 mapper.connect('router', path, controller=RouterController,
244 requirements=requirements,
245 action='delete_data',
246 conditions=dict(method=['DELETE']))
248 path = '/router/{switch_id}/{vlan_id}'
249 mapper.connect('router', path, controller=RouterController,
250 requirements=requirements,
251 action='get_vlan_data',
252 conditions=dict(method=['GET']))
253 mapper.connect('router', path, controller=RouterController,
254 requirements=requirements,
255 action='set_vlan_data',
256 conditions=dict(method=['POST']))
257 mapper.connect('router', path, controller=RouterController,
258 requirements=requirements,
259 action='delete_vlan_data',
260 conditions=dict(method=['DELETE']))
262 @set_ev_cls(dpset.EventDP, dpset.DPSET_EV_DISPATCHER)
263 def datapath_handler(self, ev):
265 RouterController.register_router(ev.dp)
267 RouterController.unregister_router(ev.dp)
269 @set_ev_cls(ofp_event.EventOFPPacketIn, MAIN_DISPATCHER)
270 def packet_in_handler(self, ev):
271 RouterController.packet_in_handler(ev.msg)
273 def _stats_reply_handler(self, ev):
277 if (dp.id not in self.waiters
278 or msg.xid not in self.waiters[dp.id]):
280 event, msgs = self.waiters[dp.id][msg.xid]
283 if ofproto_v1_3.OFP_VERSION == dp.ofproto.OFP_VERSION:
284 more = dp.ofproto.OFPMPF_REPLY_MORE
286 more = dp.ofproto.OFPSF_REPLY_MORE
289 del self.waiters[dp.id][msg.xid]
292 # for OpenFlow version1.0
293 @set_ev_cls(ofp_event.EventOFPFlowStatsReply, MAIN_DISPATCHER)
294 def stats_reply_handler_v1_0(self, ev):
295 self._stats_reply_handler(ev)
297 # for OpenFlow version1.2/1.3
298 @set_ev_cls(ofp_event.EventOFPStatsReply, MAIN_DISPATCHER)
299 def stats_reply_handler_v1_2(self, ev):
300 self._stats_reply_handler(ev)
302 # TODO: Update routing table when port status is changed.
305 # REST command template
306 def rest_command(func):
307 def _rest_command(*args, **kwargs):
309 msg = func(*args, **kwargs)
310 return Response(content_type='application/json',
311 body=json.dumps(msg))
313 except SyntaxError as e:
316 except (ValueError, NameError) as e:
320 except NotFoundError as msg:
324 msg = {REST_RESULT: REST_NG,
325 REST_DETAILS: details}
326 return Response(status=status, body=json.dumps(msg))
331 class RouterController(ControllerBase):
336 def __init__(self, req, link, data, **config):
337 super(RouterController, self).__init__(req, link, data, **config)
338 self.waiters = data['waiters']
341 def set_logger(cls, logger):
343 cls._LOGGER.propagate = False
344 hdlr = logging.StreamHandler()
345 fmt_str = '[RT][%(levelname)s] switch_id=%(sw_id)s: %(message)s'
346 hdlr.setFormatter(logging.Formatter(fmt_str))
347 cls._LOGGER.addHandler(hdlr)
350 def register_router(cls, dp):
351 dpid = {'sw_id': dpid_lib.dpid_to_str(dp.id)}
353 router = Router(dp, cls._LOGGER)
354 except OFPUnknownVersion as message:
355 cls._LOGGER.error(str(message), extra=dpid)
357 cls._ROUTER_LIST.setdefault(dp.id, router)
358 cls._LOGGER.info('Join as router.', extra=dpid)
361 def unregister_router(cls, dp):
362 if dp.id in cls._ROUTER_LIST:
363 cls._ROUTER_LIST[dp.id].delete()
364 del cls._ROUTER_LIST[dp.id]
366 dpid = {'sw_id': dpid_lib.dpid_to_str(dp.id)}
367 cls._LOGGER.info('Leave router.', extra=dpid)
370 def packet_in_handler(cls, msg):
371 dp_id = msg.datapath.id
372 if dp_id in cls._ROUTER_LIST:
373 router = cls._ROUTER_LIST[dp_id]
374 router.packet_in_handler(msg)
376 # GET /router/{switch_id}
378 def get_data(self, req, switch_id, **_kwargs):
379 return self._access_router(switch_id, VLANID_NONE,
382 # GET /router/{switch_id}/{vlan_id}
384 def get_vlan_data(self, req, switch_id, vlan_id, **_kwargs):
385 return self._access_router(switch_id, vlan_id,
388 # POST /router/{switch_id}
390 def set_data(self, req, switch_id, **_kwargs):
391 return self._access_router(switch_id, VLANID_NONE,
394 # POST /router/{switch_id}/{vlan_id}
396 def set_vlan_data(self, req, switch_id, vlan_id, **_kwargs):
397 return self._access_router(switch_id, vlan_id,
400 # DELETE /router/{switch_id}
402 def delete_data(self, req, switch_id, **_kwargs):
403 return self._access_router(switch_id, VLANID_NONE,
406 # DELETE /router/{switch_id}/{vlan_id}
408 def delete_vlan_data(self, req, switch_id, vlan_id, **_kwargs):
409 return self._access_router(switch_id, vlan_id,
412 def _access_router(self, switch_id, vlan_id, func, req):
414 routers = self._get_router(switch_id)
416 param = req.json if req.body else {}
418 raise SyntaxError('invalid syntax %s', req.body)
419 for router in routers.values():
420 function = getattr(router, func)
421 data = function(vlan_id, param, self.waiters)
422 rest_message.append(data)
426 def _get_router(self, switch_id):
429 if switch_id == REST_ALL:
430 routers = self._ROUTER_LIST
432 sw_id = dpid_lib.str_to_dpid(switch_id)
433 if sw_id in self._ROUTER_LIST:
434 routers = {sw_id: self._ROUTER_LIST[sw_id]}
439 raise NotFoundError(switch_id=switch_id)
443 def __init__(self, dp, logger):
444 super(Router, self).__init__()
446 self.dpid_str = dpid_lib.dpid_to_str(dp.id)
447 self.sw_id = {'sw_id': self.dpid_str}
450 self.port_data = PortData(dp.ports)
452 ofctl = OfCtl.factory(dp, logger)
453 cookie = COOKIE_DEFAULT_ID
455 # Set SW config: TTL error packet in (for OFPv1.2/1.3)
456 ofctl.set_sw_config_for_ttl()
458 # Set flow: ARP handling (packet in)
459 priority = get_priority(PRIORITY_ARP_HANDLING)
460 ofctl.set_packetin_flow(cookie, priority, dl_type=ether.ETH_TYPE_ARP)
461 self.logger.info('Set ARP handling (packet in) flow [cookie=0x%x]',
462 cookie, extra=self.sw_id)
464 # Set flow: L2 switching (normal)
465 priority = get_priority(PRIORITY_NORMAL)
466 ofctl.set_normal_flow(cookie, priority)
467 self.logger.info('Set L2 switching (normal) flow [cookie=0x%x]',
468 cookie, extra=self.sw_id)
470 # Set VlanRouter for vid=None.
471 vlan_router = VlanRouter(VLANID_NONE, dp, self.port_data, logger)
472 self[VLANID_NONE] = vlan_router
474 # Start cyclic routing table check.
475 self.thread = hub.spawn(self._cyclic_update_routing_tbl)
476 self.logger.info('Start cyclic routing table update.',
480 hub.kill(self.thread)
482 self.logger.info('Stop cyclic routing table update.',
485 def _get_vlan_router(self, vlan_id):
488 if vlan_id == REST_ALL:
489 vlan_routers = list(self.values())
491 vlan_id = int(vlan_id)
492 if (vlan_id != VLANID_NONE and
493 (vlan_id < VLANID_MIN or VLANID_MAX < vlan_id)):
494 msg = 'Invalid {vlan_id} value. Set [%d-%d]'
495 raise ValueError(msg % (VLANID_MIN, VLANID_MAX))
496 elif vlan_id in self:
497 vlan_routers = [self[vlan_id]]
501 def _add_vlan_router(self, vlan_id):
502 vlan_id = int(vlan_id)
503 if vlan_id not in self:
504 vlan_router = VlanRouter(vlan_id, self.dp, self.port_data,
506 self[vlan_id] = vlan_router
509 def _del_vlan_router(self, vlan_id, waiters):
510 # Remove unnecessary VlanRouter.
511 if vlan_id == VLANID_NONE:
514 vlan_router = self[vlan_id]
515 if (len(vlan_router.address_data) == 0
516 and len(vlan_router.routing_tbl) == 0):
517 vlan_router.delete(waiters)
520 def get_data(self, vlan_id, dummy1, dummy2):
521 vlan_routers = self._get_vlan_router(vlan_id)
523 msgs = [vlan_router.get_data() for vlan_router in vlan_routers]
525 msgs = [{REST_VLANID: vlan_id}]
527 return {REST_SWITCHID: self.dpid_str,
530 def set_data(self, vlan_id, param, waiters):
531 vlan_routers = self._get_vlan_router(vlan_id)
533 vlan_routers = [self._add_vlan_router(vlan_id)]
536 for vlan_router in vlan_routers:
538 msg = vlan_router.set_data(param)
540 if msg[REST_RESULT] == REST_NG:
541 # Data setting is failure.
542 self._del_vlan_router(vlan_router.vlan_id, waiters)
543 except ValueError as err_msg:
544 # Data setting is failure.
545 self._del_vlan_router(vlan_router.vlan_id, waiters)
548 return {REST_SWITCHID: self.dpid_str,
549 REST_COMMAND_RESULT: msgs}
551 def delete_data(self, vlan_id, param, waiters):
553 vlan_routers = self._get_vlan_router(vlan_id)
555 for vlan_router in vlan_routers:
556 msg = vlan_router.delete_data(param, waiters)
559 # Check unnecessary VlanRouter.
560 self._del_vlan_router(vlan_router.vlan_id, waiters)
562 msgs = [{REST_RESULT: REST_NG,
563 REST_DETAILS: 'Data is nothing.'}]
565 return {REST_SWITCHID: self.dpid_str,
566 REST_COMMAND_RESULT: msgs}
568 def packet_in_handler(self, msg):
569 pkt = packet.Packet(msg.data)
570 # TODO: Packet library convert to string
571 # self.logger.debug('Packet in = %s', str(pkt), self.sw_id)
572 header_list = dict((p.protocol_name, p)
573 for p in pkt.protocols
574 if isinstance(p, packet_base.PacketBase))
577 vlan_id = VLANID_NONE
578 if VLAN in header_list:
579 vlan_id = header_list[VLAN].vid
583 self[vlan_id].packet_in_handler(msg, header_list)
585 self.logger.debug('Drop unknown vlan packet. [vlan_id=%d]',
586 vlan_id, extra=self.sw_id)
588 def _cyclic_update_routing_tbl(self):
590 # send ARP to all gateways.
591 for vlan_router in self.values():
592 vlan_router.send_arp_all_gw()
595 hub.sleep(CHK_ROUTING_TBL_INTERVAL)
598 class VlanRouter(object):
599 def __init__(self, vlan_id, dp, port_data, logger):
600 super(VlanRouter, self).__init__()
601 self.vlan_id = vlan_id
603 self.sw_id = {'sw_id': dpid_lib.dpid_to_str(dp.id)}
606 self.port_data = port_data
607 self.address_data = AddressData()
608 self.routing_tbl = RoutingTable()
609 self.packet_buffer = SuspendPacketList(self.send_icmp_unreach_error)
610 self.ofctl = OfCtl.factory(dp, logger)
612 # Set flow: default route (drop)
613 self._set_defaultroute_drop()
615 def delete(self, waiters):
617 msgs = self.ofctl.get_all_flow(waiters)
619 for stats in msg.body:
620 vlan_id = VlanRouter._cookie_to_id(REST_VLANID, stats.cookie)
621 if vlan_id == self.vlan_id:
622 self.ofctl.delete_flow(stats)
624 assert len(self.packet_buffer) == 0
627 def _cookie_to_id(id_type, cookie):
628 if id_type == REST_VLANID:
629 rest_id = cookie >> COOKIE_SHIFT_VLANID
630 elif id_type == REST_ADDRESSID:
631 rest_id = cookie & UINT32_MAX
633 assert id_type == REST_ROUTEID
634 rest_id = (cookie & UINT32_MAX) >> COOKIE_SHIFT_ROUTEID
638 def _id_to_cookie(self, id_type, rest_id):
639 vid = self.vlan_id << COOKIE_SHIFT_VLANID
641 if id_type == REST_VLANID:
642 cookie = rest_id << COOKIE_SHIFT_VLANID
643 elif id_type == REST_ADDRESSID:
644 cookie = vid + rest_id
646 assert id_type == REST_ROUTEID
647 cookie = vid + (rest_id << COOKIE_SHIFT_ROUTEID)
651 def _get_priority(self, priority_type, route=None):
652 return get_priority(priority_type, vid=self.vlan_id, route=route)
654 def _response(self, msg):
655 if msg and self.vlan_id:
656 msg.setdefault(REST_VLANID, self.vlan_id)
660 address_data = self._get_address_data()
661 routing_data = self._get_routing_data()
664 if address_data[REST_ADDRESS]:
665 data.update(address_data)
666 if routing_data[REST_ROUTE]:
667 data.update(routing_data)
669 return self._response(data)
671 def _get_address_data(self):
673 for value in self.address_data.values():
674 default_gw = ip_addr_ntoa(value.default_gw)
675 address = '%s/%d' % (default_gw, value.netmask)
676 data = {REST_ADDRESSID: value.address_id,
677 REST_ADDRESS: address}
678 address_data.append(data)
679 return {REST_ADDRESS: address_data}
681 def _get_routing_data(self):
683 for key, value in self.routing_tbl.items():
684 if value.gateway_mac is not None:
685 gateway = ip_addr_ntoa(value.gateway_ip)
686 data = {REST_ROUTEID: value.route_id,
687 REST_DESTINATION: key,
688 REST_GATEWAY: gateway}
689 routing_data.append(data)
690 return {REST_ROUTE: routing_data}
692 def set_data(self, data):
697 if REST_ADDRESS in data:
698 address = data[REST_ADDRESS]
699 address_id = self._set_address_data(address)
700 details = 'Add address [address_id=%d]' % address_id
702 elif REST_GATEWAY in data:
703 gateway = data[REST_GATEWAY]
704 if REST_DESTINATION in data:
705 destination = data[REST_DESTINATION]
707 destination = DEFAULT_ROUTE
708 route_id = self._set_routing_data(destination, gateway)
709 details = 'Add route [route_id=%d]' % route_id
711 except CommandFailure as err_msg:
712 msg = {REST_RESULT: REST_NG, REST_DETAILS: str(err_msg)}
713 return self._response(msg)
715 if details is not None:
716 msg = {REST_RESULT: REST_OK, REST_DETAILS: details}
717 return self._response(msg)
719 raise ValueError('Invalid parameter.')
721 def _set_address_data(self, address):
722 address = self.address_data.add(address)
724 cookie = self._id_to_cookie(REST_ADDRESSID, address.address_id)
726 # Set flow: host MAC learning (packet in)
727 priority = self._get_priority(PRIORITY_MAC_LEARNING)
728 self.ofctl.set_packetin_flow(cookie, priority,
729 dl_type=ether.ETH_TYPE_IP,
730 dl_vlan=self.vlan_id,
731 dst_ip=address.nw_addr,
732 dst_mask=address.netmask)
733 log_msg = 'Set host MAC learning (packet in) flow [cookie=0x%x]'
734 self.logger.info(log_msg, cookie, extra=self.sw_id)
736 # set Flow: IP handling(PacketIn)
737 priority = self._get_priority(PRIORITY_IP_HANDLING)
738 self.ofctl.set_packetin_flow(cookie, priority,
739 dl_type=ether.ETH_TYPE_IP,
740 dl_vlan=self.vlan_id,
741 dst_ip=address.default_gw)
742 self.logger.info('Set IP handling (packet in) flow [cookie=0x%x]',
743 cookie, extra=self.sw_id)
745 # Set flow: L2 switching (normal)
746 outport = self.ofctl.dp.ofproto.OFPP_NORMAL
747 priority = self._get_priority(PRIORITY_L2_SWITCHING)
748 self.ofctl.set_routing_flow(
749 cookie, priority, outport, dl_vlan=self.vlan_id,
750 nw_src=address.nw_addr, src_mask=address.netmask,
751 nw_dst=address.nw_addr, dst_mask=address.netmask)
752 self.logger.info('Set L2 switching (normal) flow [cookie=0x%x]',
753 cookie, extra=self.sw_id)
756 self.send_arp_request(address.default_gw, address.default_gw)
758 return address.address_id
760 def _set_routing_data(self, destination, gateway):
761 err_msg = 'Invalid [%s] value.' % REST_GATEWAY
762 dst_ip = ip_addr_aton(gateway, err_msg=err_msg)
763 address = self.address_data.get_data(ip=dst_ip)
765 msg = 'Gateway=%s\'s address is not registered.' % gateway
766 raise CommandFailure(msg=msg)
767 elif dst_ip == address.default_gw:
768 msg = 'Gateway=%s is used as default gateway of address_id=%d'\
769 % (gateway, address.address_id)
770 raise CommandFailure(msg=msg)
772 src_ip = address.default_gw
773 route = self.routing_tbl.add(destination, gateway)
774 self._set_route_packetin(route)
775 self.send_arp_request(src_ip, dst_ip)
776 return route.route_id
778 def _set_defaultroute_drop(self):
779 cookie = self._id_to_cookie(REST_VLANID, self.vlan_id)
780 priority = self._get_priority(PRIORITY_DEFAULT_ROUTING)
781 outport = None # for drop
782 self.ofctl.set_routing_flow(cookie, priority, outport,
783 dl_vlan=self.vlan_id)
784 self.logger.info('Set default route (drop) flow [cookie=0x%x]',
785 cookie, extra=self.sw_id)
787 def _set_route_packetin(self, route):
788 cookie = self._id_to_cookie(REST_ROUTEID, route.route_id)
789 priority, log_msg = self._get_priority(PRIORITY_TYPE_ROUTE,
791 self.ofctl.set_packetin_flow(cookie, priority,
792 dl_type=ether.ETH_TYPE_IP,
793 dl_vlan=self.vlan_id,
795 dst_mask=route.netmask)
796 self.logger.info('Set %s (packet in) flow [cookie=0x%x]', log_msg,
797 cookie, extra=self.sw_id)
799 def delete_data(self, data, waiters):
800 if REST_ROUTEID in data:
801 route_id = data[REST_ROUTEID]
802 msg = self._delete_routing_data(route_id, waiters)
803 elif REST_ADDRESSID in data:
804 address_id = data[REST_ADDRESSID]
805 msg = self._delete_address_data(address_id, waiters)
807 raise ValueError('Invalid parameter.')
809 return self._response(msg)
811 def _delete_address_data(self, address_id, waiters):
812 if address_id != REST_ALL:
814 address_id = int(address_id)
815 except ValueError as e:
816 err_msg = 'Invalid [%s] value. %s'
817 raise ValueError(err_msg % (REST_ADDRESSID, e.message))
819 skip_ids = self._chk_addr_relation_route(address_id)
823 msgs = self.ofctl.get_all_flow(waiters)
826 for stats in msg.body:
827 vlan_id = VlanRouter._cookie_to_id(REST_VLANID, stats.cookie)
828 if vlan_id != self.vlan_id:
830 addr_id = VlanRouter._cookie_to_id(REST_ADDRESSID,
832 if addr_id in skip_ids:
834 elif address_id == REST_ALL:
835 if addr_id <= COOKIE_DEFAULT_ID or max_id < addr_id:
837 elif address_id != addr_id:
839 delete_list.append(stats)
842 for flow_stats in delete_list:
844 self.ofctl.delete_flow(flow_stats)
845 address_id = VlanRouter._cookie_to_id(REST_ADDRESSID,
848 del_address = self.address_data.get_data(addr_id=address_id)
849 if del_address is not None:
850 # Clean up suspend packet threads.
851 self.packet_buffer.delete(del_addr=del_address)
854 self.address_data.delete(address_id)
855 if address_id not in delete_ids:
856 delete_ids.append(address_id)
860 delete_ids = ','.join(str(addr_id) for addr_id in delete_ids)
861 details = 'Delete address [address_id=%s]' % delete_ids
862 msg = {REST_RESULT: REST_OK, REST_DETAILS: details}
865 skip_ids = ','.join(str(addr_id) for addr_id in skip_ids)
866 details = 'Skip delete (related route exist) [address_id=%s]'\
869 msg[REST_DETAILS] += ', %s' % details
871 msg = {REST_RESULT: REST_NG, REST_DETAILS: details}
875 def _delete_routing_data(self, route_id, waiters):
876 if route_id != REST_ALL:
878 route_id = int(route_id)
879 except ValueError as e:
880 err_msg = 'Invalid [%s] value. %s'
881 raise ValueError(err_msg % (REST_ROUTEID, e.message))
884 msgs = self.ofctl.get_all_flow(waiters)
888 for stats in msg.body:
889 vlan_id = VlanRouter._cookie_to_id(REST_VLANID, stats.cookie)
890 if vlan_id != self.vlan_id:
892 rt_id = VlanRouter._cookie_to_id(REST_ROUTEID, stats.cookie)
893 if route_id == REST_ALL:
894 if rt_id == COOKIE_DEFAULT_ID:
896 elif route_id != rt_id:
898 delete_list.append(stats)
902 for flow_stats in delete_list:
903 self.ofctl.delete_flow(flow_stats)
904 route_id = VlanRouter._cookie_to_id(REST_ROUTEID,
906 self.routing_tbl.delete(route_id)
907 if route_id not in delete_ids:
908 delete_ids.append(route_id)
910 # case: Default route deleted. -> set flow (drop)
911 route_type = get_priority_type(flow_stats.priority,
913 if route_type == PRIORITY_DEFAULT_ROUTING:
914 self._set_defaultroute_drop()
918 delete_ids = ','.join(str(route_id) for route_id in delete_ids)
919 details = 'Delete route [route_id=%s]' % delete_ids
920 msg = {REST_RESULT: REST_OK, REST_DETAILS: details}
924 def _chk_addr_relation_route(self, address_id):
925 # Check exist of related routing data.
927 gateways = self.routing_tbl.get_gateways()
928 for gateway in gateways:
929 address = self.address_data.get_data(ip=gateway)
930 if address is not None:
931 if (address_id == REST_ALL
932 and address.address_id not in relate_list):
933 relate_list.append(address.address_id)
934 elif address.address_id == address_id:
935 relate_list = [address_id]
939 def packet_in_handler(self, msg, header_list):
940 # Check invalid TTL (for OpenFlow V1.2/1.3)
941 ofproto = self.dp.ofproto
942 if ofproto.OFP_VERSION == ofproto_v1_2.OFP_VERSION or \
943 ofproto.OFP_VERSION == ofproto_v1_3.OFP_VERSION:
944 if msg.reason == ofproto.OFPR_INVALID_TTL:
945 self._packetin_invalid_ttl(msg, header_list)
948 # Analyze event type.
949 if ARP in header_list:
950 self._packetin_arp(msg, header_list)
953 if IPV4 in header_list:
954 rt_ports = self.address_data.get_default_gw()
955 if header_list[IPV4].dst in rt_ports:
956 # Packet to router's port.
957 if ICMP in header_list:
958 if header_list[ICMP].type == icmp.ICMP_ECHO_REQUEST:
959 self._packetin_icmp_req(msg, header_list)
961 elif TCP in header_list or UDP in header_list:
962 self._packetin_tcp_udp(msg, header_list)
965 # Packet to internal host or gateway router.
966 self._packetin_to_node(msg, header_list)
969 def _packetin_arp(self, msg, header_list):
970 src_addr = self.address_data.get_data(ip=header_list[ARP].src_ip)
974 # case: Receive ARP from the gateway
975 # Update routing table.
976 # case: Receive ARP from an internal host
978 gw_flg = self._update_routing_tbl(msg, header_list)
980 self._learning_host_mac(msg, header_list)
982 # ARP packet handling.
983 in_port = self.ofctl.get_packetin_inport(msg)
984 src_ip = header_list[ARP].src_ip
985 dst_ip = header_list[ARP].dst_ip
986 srcip = ip_addr_ntoa(src_ip)
987 dstip = ip_addr_ntoa(dst_ip)
988 rt_ports = self.address_data.get_default_gw()
991 # GARP -> packet forward (normal)
992 output = self.ofctl.dp.ofproto.OFPP_NORMAL
993 self.ofctl.send_packet_out(in_port, output, msg.data)
995 self.logger.info('Receive GARP from [%s].', srcip,
997 self.logger.info('Send GARP (normal).', extra=self.sw_id)
999 elif dst_ip not in rt_ports:
1000 dst_addr = self.address_data.get_data(ip=dst_ip)
1001 if (dst_addr is not None and
1002 src_addr.address_id == dst_addr.address_id):
1003 # ARP from internal host -> packet forward (normal)
1004 output = self.ofctl.dp.ofproto.OFPP_NORMAL
1005 self.ofctl.send_packet_out(in_port, output, msg.data)
1007 self.logger.info('Receive ARP from an internal host [%s].',
1008 srcip, extra=self.sw_id)
1009 self.logger.info('Send ARP (normal)', extra=self.sw_id)
1011 if header_list[ARP].opcode == arp.ARP_REQUEST:
1012 # ARP request to router port -> send ARP reply
1013 src_mac = self.port_data[in_port].mac
1014 dst_mac = header_list[ARP].src_mac
1015 arp_target_mac = dst_mac
1017 in_port = self.ofctl.dp.ofproto.OFPP_CONTROLLER
1019 self.ofctl.send_arp(arp.ARP_REPLY, self.vlan_id,
1020 src_mac, dst_mac, dst_ip, src_ip,
1021 arp_target_mac, in_port, output)
1023 log_msg = 'Receive ARP request from [%s] to router port [%s].'
1024 self.logger.info(log_msg, srcip, dstip, extra=self.sw_id)
1025 self.logger.info('Send ARP reply to [%s]', srcip,
1028 elif header_list[ARP].opcode == arp.ARP_REPLY:
1029 # ARP reply to router port -> suspend packets forward
1030 log_msg = 'Receive ARP reply from [%s] to router port [%s].'
1031 self.logger.info(log_msg, srcip, dstip, extra=self.sw_id)
1033 packet_list = self.packet_buffer.get_data(src_ip)
1035 # stop ARP reply wait thread.
1036 for suspend_packet in packet_list:
1037 self.packet_buffer.delete(pkt=suspend_packet)
1039 # send suspend packet.
1040 output = self.ofctl.dp.ofproto.OFPP_TABLE
1041 for suspend_packet in packet_list:
1042 self.ofctl.send_packet_out(suspend_packet.in_port,
1044 suspend_packet.data)
1045 self.logger.info('Send suspend packet to [%s].',
1046 srcip, extra=self.sw_id)
1048 def _packetin_icmp_req(self, msg, header_list):
1049 # Send ICMP echo reply.
1050 in_port = self.ofctl.get_packetin_inport(msg)
1051 self.ofctl.send_icmp(in_port, header_list, self.vlan_id,
1052 icmp.ICMP_ECHO_REPLY,
1053 icmp.ICMP_ECHO_REPLY_CODE,
1054 icmp_data=header_list[ICMP].data)
1056 srcip = ip_addr_ntoa(header_list[IPV4].src)
1057 dstip = ip_addr_ntoa(header_list[IPV4].dst)
1058 log_msg = 'Receive ICMP echo request from [%s] to router port [%s].'
1059 self.logger.info(log_msg, srcip, dstip, extra=self.sw_id)
1060 self.logger.info('Send ICMP echo reply to [%s].', srcip,
1063 def _packetin_tcp_udp(self, msg, header_list):
1064 # Send ICMP port unreach error.
1065 in_port = self.ofctl.get_packetin_inport(msg)
1066 self.ofctl.send_icmp(in_port, header_list, self.vlan_id,
1067 icmp.ICMP_DEST_UNREACH,
1068 icmp.ICMP_PORT_UNREACH_CODE,
1071 srcip = ip_addr_ntoa(header_list[IPV4].src)
1072 dstip = ip_addr_ntoa(header_list[IPV4].dst)
1073 self.logger.info('Receive TCP/UDP from [%s] to router port [%s].',
1074 srcip, dstip, extra=self.sw_id)
1075 self.logger.info('Send ICMP destination unreachable to [%s].', srcip,
1078 def _packetin_to_node(self, msg, header_list):
1079 if len(self.packet_buffer) >= MAX_SUSPENDPACKETS:
1080 self.logger.info('Packet is dropped, MAX_SUSPENDPACKETS exceeded.',
1084 # Send ARP request to get node MAC address.
1085 in_port = self.ofctl.get_packetin_inport(msg)
1087 dst_ip = header_list[IPV4].dst
1088 srcip = ip_addr_ntoa(header_list[IPV4].src)
1089 dstip = ip_addr_ntoa(dst_ip)
1091 address = self.address_data.get_data(ip=dst_ip)
1092 if address is not None:
1093 log_msg = 'Receive IP packet from [%s] to an internal host [%s].'
1094 self.logger.info(log_msg, srcip, dstip, extra=self.sw_id)
1095 src_ip = address.default_gw
1097 route = self.routing_tbl.get_data(dst_ip=dst_ip)
1098 if route is not None:
1099 log_msg = 'Receive IP packet from [%s] to [%s].'
1100 self.logger.info(log_msg, srcip, dstip, extra=self.sw_id)
1101 gw_address = self.address_data.get_data(ip=route.gateway_ip)
1102 if gw_address is not None:
1103 src_ip = gw_address.default_gw
1104 dst_ip = route.gateway_ip
1106 if src_ip is not None:
1107 self.packet_buffer.add(in_port, header_list, msg.data)
1108 self.send_arp_request(src_ip, dst_ip, in_port=in_port)
1109 self.logger.info('Send ARP request (flood)', extra=self.sw_id)
1111 def _packetin_invalid_ttl(self, msg, header_list):
1112 # Send ICMP TTL error.
1113 srcip = ip_addr_ntoa(header_list[IPV4].src)
1114 self.logger.info('Receive invalid ttl packet from [%s].', srcip,
1117 in_port = self.ofctl.get_packetin_inport(msg)
1118 src_ip = self._get_send_port_ip(header_list)
1119 if src_ip is not None:
1120 self.ofctl.send_icmp(in_port, header_list, self.vlan_id,
1121 icmp.ICMP_TIME_EXCEEDED,
1122 icmp.ICMP_TTL_EXPIRED_CODE,
1123 msg_data=msg.data, src_ip=src_ip)
1124 self.logger.info('Send ICMP time exceeded to [%s].', srcip,
1127 def send_arp_all_gw(self):
1128 gateways = self.routing_tbl.get_gateways()
1129 for gateway in gateways:
1130 address = self.address_data.get_data(ip=gateway)
1131 self.send_arp_request(address.default_gw, gateway)
1133 def send_arp_request(self, src_ip, dst_ip, in_port=None):
1134 # Send ARP request from all ports.
1135 for send_port in self.port_data.values():
1136 if in_port is None or in_port != send_port.port_no:
1137 src_mac = send_port.mac
1138 dst_mac = mac_lib.BROADCAST_STR
1139 arp_target_mac = mac_lib.DONTCARE_STR
1140 inport = self.ofctl.dp.ofproto.OFPP_CONTROLLER
1141 output = send_port.port_no
1142 self.ofctl.send_arp(arp.ARP_REQUEST, self.vlan_id,
1143 src_mac, dst_mac, src_ip, dst_ip,
1144 arp_target_mac, inport, output)
1146 def send_icmp_unreach_error(self, packet_buffer):
1147 # Send ICMP host unreach error.
1148 self.logger.info('ARP reply wait timer was timed out.',
1150 src_ip = self._get_send_port_ip(packet_buffer.header_list)
1151 if src_ip is not None:
1152 self.ofctl.send_icmp(packet_buffer.in_port,
1153 packet_buffer.header_list,
1155 icmp.ICMP_DEST_UNREACH,
1156 icmp.ICMP_HOST_UNREACH_CODE,
1157 msg_data=packet_buffer.data,
1160 dstip = ip_addr_ntoa(packet_buffer.dst_ip)
1161 self.logger.info('Send ICMP destination unreachable to [%s].',
1162 dstip, extra=self.sw_id)
1164 def _update_routing_tbl(self, msg, header_list):
1165 # Set flow: routing to gateway.
1166 out_port = self.ofctl.get_packetin_inport(msg)
1167 src_mac = header_list[ARP].src_mac
1168 dst_mac = self.port_data[out_port].mac
1169 src_ip = header_list[ARP].src_ip
1172 for key, value in self.routing_tbl.items():
1173 if value.gateway_ip == src_ip:
1175 if value.gateway_mac == src_mac:
1177 self.routing_tbl[key].gateway_mac = src_mac
1179 cookie = self._id_to_cookie(REST_ROUTEID, value.route_id)
1180 priority, log_msg = self._get_priority(PRIORITY_TYPE_ROUTE,
1182 self.ofctl.set_routing_flow(cookie, priority, out_port,
1183 dl_vlan=self.vlan_id,
1186 nw_dst=value.dst_ip,
1187 dst_mask=value.netmask,
1189 self.logger.info('Set %s flow [cookie=0x%x]', log_msg, cookie,
1193 def _learning_host_mac(self, msg, header_list):
1194 # Set flow: routing to internal Host.
1195 out_port = self.ofctl.get_packetin_inport(msg)
1196 src_mac = header_list[ARP].src_mac
1197 dst_mac = self.port_data[out_port].mac
1198 src_ip = header_list[ARP].src_ip
1200 gateways = self.routing_tbl.get_gateways()
1201 if src_ip not in gateways:
1202 address = self.address_data.get_data(ip=src_ip)
1203 if address is not None:
1204 cookie = self._id_to_cookie(REST_ADDRESSID, address.address_id)
1205 priority = self._get_priority(PRIORITY_IMPLICIT_ROUTING)
1206 self.ofctl.set_routing_flow(cookie, priority,
1207 out_port, dl_vlan=self.vlan_id,
1208 src_mac=dst_mac, dst_mac=src_mac,
1210 idle_timeout=IDLE_TIMEOUT,
1212 self.logger.info('Set implicit routing flow [cookie=0x%x]',
1213 cookie, extra=self.sw_id)
1215 def _get_send_port_ip(self, header_list):
1217 src_mac = header_list[ETHERNET].src
1218 if IPV4 in header_list:
1219 src_ip = header_list[IPV4].src
1221 src_ip = header_list[ARP].src_ip
1223 self.logger.debug('Receive unsupported packet.', extra=self.sw_id)
1226 address = self.address_data.get_data(ip=src_ip)
1227 if address is not None:
1228 return address.default_gw
1230 route = self.routing_tbl.get_data(gw_mac=src_mac)
1231 if route is not None:
1232 address = self.address_data.get_data(ip=route.gateway_ip)
1233 if address is not None:
1234 return address.default_gw
1236 self.logger.debug('Receive packet from unknown IP[%s].',
1237 ip_addr_ntoa(src_ip), extra=self.sw_id)
1241 class PortData(dict):
1242 def __init__(self, ports):
1243 super(PortData, self).__init__()
1244 for port in ports.values():
1245 data = Port(port.port_no, port.hw_addr)
1246 self[port.port_no] = data
1250 def __init__(self, port_no, hw_addr):
1251 super(Port, self).__init__()
1252 self.port_no = port_no
1256 class AddressData(dict):
1258 super(AddressData, self).__init__()
1261 def add(self, address):
1262 err_msg = 'Invalid [%s] value.' % REST_ADDRESS
1263 nw_addr, mask, default_gw = nw_addr_aton(address, err_msg=err_msg)
1266 for other in self.values():
1267 other_mask = mask_ntob(other.netmask)
1268 add_mask = mask_ntob(mask, err_msg=err_msg)
1269 if (other.nw_addr == ipv4_apply_mask(default_gw, other.netmask) or
1270 nw_addr == ipv4_apply_mask(other.default_gw, mask,
1272 msg = 'Address overlaps [address_id=%d]' % other.address_id
1273 raise CommandFailure(msg=msg)
1275 address = Address(self.address_id, nw_addr, mask, default_gw)
1276 ip_str = ip_addr_ntoa(nw_addr)
1277 key = '%s/%d' % (ip_str, mask)
1280 self.address_id += 1
1281 self.address_id &= UINT32_MAX
1282 if self.address_id == COOKIE_DEFAULT_ID:
1287 def delete(self, address_id):
1288 for key, value in self.items():
1289 if value.address_id == address_id:
1293 def get_default_gw(self):
1294 return [address.default_gw for address in self.values()]
1296 def get_data(self, addr_id=None, ip=None):
1297 for address in self.values():
1298 if addr_id is not None:
1299 if addr_id == address.address_id:
1302 assert ip is not None
1303 if ipv4_apply_mask(ip, address.netmask) == address.nw_addr:
1308 class Address(object):
1309 def __init__(self, address_id, nw_addr, netmask, default_gw):
1310 super(Address, self).__init__()
1311 self.address_id = address_id
1312 self.nw_addr = nw_addr
1313 self.netmask = netmask
1314 self.default_gw = default_gw
1316 def __contains__(self, ip):
1317 return bool(ipv4_apply_mask(ip, self.netmask) == self.nw_addr)
1320 class RoutingTable(dict):
1322 super(RoutingTable, self).__init__()
1325 def add(self, dst_nw_addr, gateway_ip):
1326 err_msg = 'Invalid [%s] value.'
1328 if dst_nw_addr == DEFAULT_ROUTE:
1332 dst_ip, netmask, dummy = nw_addr_aton(
1333 dst_nw_addr, err_msg=err_msg % REST_DESTINATION)
1335 gateway_ip = ip_addr_aton(gateway_ip, err_msg=err_msg % REST_GATEWAY)
1338 overlap_route = None
1339 if dst_nw_addr == DEFAULT_ROUTE:
1340 if DEFAULT_ROUTE in self:
1341 overlap_route = self[DEFAULT_ROUTE].route_id
1342 elif dst_nw_addr in self:
1343 overlap_route = self[dst_nw_addr].route_id
1345 if overlap_route is not None:
1346 msg = 'Destination overlaps [route_id=%d]' % overlap_route
1347 raise CommandFailure(msg=msg)
1349 routing_data = Route(self.route_id, dst_ip, netmask, gateway_ip)
1350 ip_str = ip_addr_ntoa(dst_ip)
1351 key = '%s/%d' % (ip_str, netmask)
1352 self[key] = routing_data
1355 self.route_id &= UINT32_MAX
1356 if self.route_id == COOKIE_DEFAULT_ID:
1361 def delete(self, route_id):
1362 for key, value in self.items():
1363 if value.route_id == route_id:
1367 def get_gateways(self):
1368 return [routing_data.gateway_ip for routing_data in self.values()]
1370 def get_data(self, gw_mac=None, dst_ip=None):
1371 if gw_mac is not None:
1372 for route in self.values():
1373 if gw_mac == route.gateway_mac:
1377 elif dst_ip is not None:
1380 for route in self.values():
1381 if ipv4_apply_mask(dst_ip, route.netmask) == route.dst_ip:
1383 if mask < route.netmask:
1385 mask = route.netmask
1387 if get_route is None:
1388 get_route = self.get(DEFAULT_ROUTE, None)
1394 class Route(object):
1395 def __init__(self, route_id, dst_ip, netmask, gateway_ip):
1396 super(Route, self).__init__()
1397 self.route_id = route_id
1398 self.dst_ip = dst_ip
1399 self.netmask = netmask
1400 self.gateway_ip = gateway_ip
1401 self.gateway_mac = None
1404 class SuspendPacketList(list):
1405 def __init__(self, timeout_function):
1406 super(SuspendPacketList, self).__init__()
1407 self.timeout_function = timeout_function
1409 def add(self, in_port, header_list, data):
1410 suspend_pkt = SuspendPacket(in_port, header_list, data,
1411 self.wait_arp_reply_timer)
1412 self.append(suspend_pkt)
1414 def delete(self, pkt=None, del_addr=None):
1418 assert del_addr is not None
1419 del_list = [pkt for pkt in self if pkt.dst_ip in del_addr]
1421 for pkt in del_list:
1423 hub.kill(pkt.wait_thread)
1424 pkt.wait_thread.wait()
1426 def get_data(self, dst_ip):
1427 return [pkt for pkt in self if pkt.dst_ip == dst_ip]
1429 def wait_arp_reply_timer(self, suspend_pkt):
1430 hub.sleep(ARP_REPLY_TIMER)
1431 if suspend_pkt in self:
1432 self.timeout_function(suspend_pkt)
1433 self.delete(pkt=suspend_pkt)
1436 class SuspendPacket(object):
1437 def __init__(self, in_port, header_list, data, timer):
1438 super(SuspendPacket, self).__init__()
1439 self.in_port = in_port
1440 self.dst_ip = header_list[IPV4].dst
1441 self.header_list = header_list
1443 # Start ARP reply wait timer.
1444 self.wait_thread = hub.spawn(timer, self)
1447 class OfCtl(object):
1451 def register_of_version(version):
1452 def _register_of_version(cls):
1453 OfCtl._OF_VERSIONS.setdefault(version, cls)
1455 return _register_of_version
1458 def factory(dp, logger):
1459 of_version = dp.ofproto.OFP_VERSION
1460 if of_version in OfCtl._OF_VERSIONS:
1461 ofctl = OfCtl._OF_VERSIONS[of_version](dp, logger)
1463 raise OFPUnknownVersion(version=of_version)
1467 def __init__(self, dp, logger):
1468 super(OfCtl, self).__init__()
1470 self.sw_id = {'sw_id': dpid_lib.dpid_to_str(dp.id)}
1471 self.logger = logger
1473 def set_sw_config_for_ttl(self):
1474 # OpenFlow v1_2/1_3.
1477 def set_flow(self, cookie, priority, dl_type=0, dl_dst=0, dl_vlan=0,
1478 nw_src=0, src_mask=32, nw_dst=0, dst_mask=32,
1479 nw_proto=0, idle_timeout=0, actions=None):
1481 raise NotImplementedError()
1483 def send_arp(self, arp_opcode, vlan_id, src_mac, dst_mac,
1484 src_ip, dst_ip, arp_target_mac, in_port, output):
1485 # Generate ARP packet
1486 if vlan_id != VLANID_NONE:
1487 ether_proto = ether.ETH_TYPE_8021Q
1490 vlan_ether = ether.ETH_TYPE_ARP
1491 v = vlan.vlan(pcp, cfi, vlan_id, vlan_ether)
1493 ether_proto = ether.ETH_TYPE_ARP
1495 arp_proto = ether.ETH_TYPE_IP
1499 pkt = packet.Packet()
1500 e = ethernet.ethernet(dst_mac, src_mac, ether_proto)
1501 a = arp.arp(hwtype, arp_proto, hlen, plen, arp_opcode,
1502 src_mac, src_ip, arp_target_mac, dst_ip)
1504 if vlan_id != VLANID_NONE:
1510 self.send_packet_out(in_port, output, pkt.data, data_str=str(pkt))
1512 def send_icmp(self, in_port, protocol_list, vlan_id, icmp_type,
1513 icmp_code, icmp_data=None, msg_data=None, src_ip=None):
1514 # Generate ICMP reply packet
1516 offset = ethernet.ethernet._MIN_LEN
1518 if vlan_id != VLANID_NONE:
1519 ether_proto = ether.ETH_TYPE_8021Q
1522 vlan_ether = ether.ETH_TYPE_IP
1523 v = vlan.vlan(pcp, cfi, vlan_id, vlan_ether)
1524 offset += vlan.vlan._MIN_LEN
1526 ether_proto = ether.ETH_TYPE_IP
1528 eth = protocol_list[ETHERNET]
1529 e = ethernet.ethernet(eth.src, eth.dst, ether_proto)
1531 ip = protocol_list[IPV4]
1533 if icmp_data is None and msg_data is not None:
1534 # RFC 4884 says that we should send "at least 128 octets"
1535 # if we are using the ICMP Extension Structure.
1536 # We're not using the extension structure, but let's send
1537 # up to 128 bytes of the original msg_data.
1539 # RFC 4884 also states that the length field is interpreted in
1540 # 32 bit units, so the length calculated in bytes needs to first
1541 # be divided by 4, then increased by 1 if the modulus is non-zero.
1543 # Finally, RFC 4884 says, if we're specifying the length, we MUST
1544 # zero pad to the next 32 bit boundary.
1545 end_of_data = offset + len(ip) + 128
1546 ip_datagram = bytearray()
1547 ip_datagram += msg_data[offset:end_of_data]
1548 data_len = int(len(ip_datagram) / 4)
1549 length_modulus = int(len(ip_datagram) % 4)
1552 ip_datagram += bytearray([0] * (4 - length_modulus))
1553 if icmp_type == icmp.ICMP_DEST_UNREACH:
1554 icmp_data = icmp.dest_unreach(data_len=data_len,
1556 elif icmp_type == icmp.ICMP_TIME_EXCEEDED:
1557 icmp_data = icmp.TimeExceeded(data_len=data_len,
1560 ic = icmp.icmp(icmp_type, icmp_code, csum, data=icmp_data)
1564 ip_total_length = ip.header_length * 4 + ic._MIN_LEN
1565 if ic.data is not None:
1566 ip_total_length += ic.data._MIN_LEN
1567 if ic.data.data is not None:
1568 ip_total_length += + len(ic.data.data)
1569 i = ipv4.ipv4(ip.version, ip.header_length, ip.tos,
1570 ip_total_length, ip.identification, ip.flags,
1571 ip.offset, DEFAULT_TTL, inet.IPPROTO_ICMP, csum,
1574 pkt = packet.Packet()
1576 if vlan_id != VLANID_NONE:
1579 pkt.add_protocol(ic)
1583 self.send_packet_out(in_port, self.dp.ofproto.OFPP_IN_PORT,
1584 pkt.data, data_str=str(pkt))
1586 def send_packet_out(self, in_port, output, data, data_str=None):
1587 actions = [self.dp.ofproto_parser.OFPActionOutput(output, 0)]
1588 self.dp.send_packet_out(buffer_id=UINT32_MAX, in_port=in_port,
1589 actions=actions, data=data)
1590 # TODO: Packet library convert to string
1591 # if data_str is None:
1592 # data_str = str(packet.Packet(data))
1593 # self.logger.debug('Packet out = %s', data_str, extra=self.sw_id)
1595 def set_normal_flow(self, cookie, priority):
1596 out_port = self.dp.ofproto.OFPP_NORMAL
1597 actions = [self.dp.ofproto_parser.OFPActionOutput(out_port, 0)]
1598 self.set_flow(cookie, priority, actions=actions)
1600 def set_packetin_flow(self, cookie, priority, dl_type=0, dl_dst=0,
1601 dl_vlan=0, dst_ip=0, dst_mask=32, nw_proto=0):
1602 miss_send_len = UINT16_MAX
1603 actions = [self.dp.ofproto_parser.OFPActionOutput(
1604 self.dp.ofproto.OFPP_CONTROLLER, miss_send_len)]
1605 self.set_flow(cookie, priority, dl_type=dl_type, dl_dst=dl_dst,
1606 dl_vlan=dl_vlan, nw_dst=dst_ip, dst_mask=dst_mask,
1607 nw_proto=nw_proto, actions=actions)
1609 def send_stats_request(self, stats, waiters):
1610 self.dp.set_xid(stats)
1611 waiters_per_dp = waiters.setdefault(self.dp.id, {})
1614 waiters_per_dp[stats.xid] = (event, msgs)
1615 self.dp.send_msg(stats)
1618 event.wait(timeout=OFP_REPLY_TIMER)
1620 del waiters_per_dp[stats.xid]
1625 @OfCtl.register_of_version(ofproto_v1_0.OFP_VERSION)
1626 class OfCtl_v1_0(OfCtl):
1628 def __init__(self, dp, logger):
1629 super(OfCtl_v1_0, self).__init__(dp, logger)
1631 def get_packetin_inport(self, msg):
1634 def get_all_flow(self, waiters):
1635 ofp = self.dp.ofproto
1636 ofp_parser = self.dp.ofproto_parser
1638 match = ofp_parser.OFPMatch(ofp.OFPFW_ALL, 0, 0, 0,
1639 0, 0, 0, 0, 0, 0, 0, 0, 0)
1640 stats = ofp_parser.OFPFlowStatsRequest(self.dp, 0, match,
1641 0xff, ofp.OFPP_NONE)
1642 return self.send_stats_request(stats, waiters)
1644 def set_flow(self, cookie, priority, dl_type=0, dl_dst=0, dl_vlan=0,
1645 nw_src=0, src_mask=32, nw_dst=0, dst_mask=32,
1646 nw_proto=0, idle_timeout=0, actions=None):
1647 ofp = self.dp.ofproto
1648 ofp_parser = self.dp.ofproto_parser
1652 wildcards = ofp.OFPFW_ALL
1654 wildcards &= ~ofp.OFPFW_DL_TYPE
1656 wildcards &= ~ofp.OFPFW_DL_DST
1658 wildcards &= ~ofp.OFPFW_DL_VLAN
1660 v = (32 - src_mask) << ofp.OFPFW_NW_SRC_SHIFT | \
1661 ~ofp.OFPFW_NW_SRC_MASK
1663 nw_src = ipv4_text_to_int(nw_src)
1665 v = (32 - dst_mask) << ofp.OFPFW_NW_DST_SHIFT | \
1666 ~ofp.OFPFW_NW_DST_MASK
1668 nw_dst = ipv4_text_to_int(nw_dst)
1670 wildcards &= ~ofp.OFPFW_NW_PROTO
1672 match = ofp_parser.OFPMatch(wildcards, 0, 0, dl_dst, dl_vlan, 0,
1673 dl_type, 0, nw_proto,
1674 nw_src, nw_dst, 0, 0)
1675 actions = actions or []
1677 m = ofp_parser.OFPFlowMod(self.dp, match, cookie, cmd,
1678 idle_timeout=idle_timeout,
1679 priority=priority, actions=actions)
1682 def set_routing_flow(self, cookie, priority, outport, dl_vlan=0,
1683 nw_src=0, src_mask=32, nw_dst=0, dst_mask=32,
1684 src_mac=0, dst_mac=0, idle_timeout=0, **dummy):
1685 ofp_parser = self.dp.ofproto_parser
1687 dl_type = ether.ETH_TYPE_IP
1689 # Decrement TTL value is not supported at OpenFlow V1.0
1692 actions.append(ofp_parser.OFPActionSetDlSrc(
1693 mac_lib.haddr_to_bin(src_mac)))
1695 actions.append(ofp_parser.OFPActionSetDlDst(
1696 mac_lib.haddr_to_bin(dst_mac)))
1697 if outport is not None:
1698 actions.append(ofp_parser.OFPActionOutput(outport))
1700 self.set_flow(cookie, priority, dl_type=dl_type, dl_vlan=dl_vlan,
1701 nw_src=nw_src, src_mask=src_mask,
1702 nw_dst=nw_dst, dst_mask=dst_mask,
1703 idle_timeout=idle_timeout, actions=actions)
1705 def delete_flow(self, flow_stats):
1706 match = flow_stats.match
1707 cookie = flow_stats.cookie
1708 cmd = self.dp.ofproto.OFPFC_DELETE_STRICT
1709 priority = flow_stats.priority
1712 flow_mod = self.dp.ofproto_parser.OFPFlowMod(
1713 self.dp, match, cookie, cmd, priority=priority, actions=actions)
1714 self.dp.send_msg(flow_mod)
1715 self.logger.info('Delete flow [cookie=0x%x]', cookie, extra=self.sw_id)
1718 class OfCtl_after_v1_2(OfCtl):
1720 def __init__(self, dp, logger):
1721 super(OfCtl_after_v1_2, self).__init__(dp, logger)
1723 def set_sw_config_for_ttl(self):
1726 def get_packetin_inport(self, msg):
1727 in_port = self.dp.ofproto.OFPP_ANY
1728 for match_field in msg.match.fields:
1729 if match_field.header == self.dp.ofproto.OXM_OF_IN_PORT:
1730 in_port = match_field.value
1734 def get_all_flow(self, waiters):
1737 def set_flow(self, cookie, priority, dl_type=0, dl_dst=0, dl_vlan=0,
1738 nw_src=0, src_mask=32, nw_dst=0, dst_mask=32,
1739 nw_proto=0, idle_timeout=0, actions=None):
1740 ofp = self.dp.ofproto
1741 ofp_parser = self.dp.ofproto_parser
1745 match = ofp_parser.OFPMatch()
1747 match.set_dl_type(dl_type)
1749 match.set_dl_dst(dl_dst)
1751 match.set_vlan_vid(dl_vlan)
1753 match.set_ipv4_src_masked(ipv4_text_to_int(nw_src),
1754 mask_ntob(src_mask))
1756 match.set_ipv4_dst_masked(ipv4_text_to_int(nw_dst),
1757 mask_ntob(dst_mask))
1759 if dl_type == ether.ETH_TYPE_IP:
1760 match.set_ip_proto(nw_proto)
1761 elif dl_type == ether.ETH_TYPE_ARP:
1762 match.set_arp_opcode(nw_proto)
1765 actions = actions or []
1766 inst = [ofp_parser.OFPInstructionActions(ofp.OFPIT_APPLY_ACTIONS,
1769 m = ofp_parser.OFPFlowMod(self.dp, cookie, 0, 0, cmd, idle_timeout,
1770 0, priority, UINT32_MAX, ofp.OFPP_ANY,
1771 ofp.OFPG_ANY, 0, match, inst)
1774 def set_routing_flow(self, cookie, priority, outport, dl_vlan=0,
1775 nw_src=0, src_mask=32, nw_dst=0, dst_mask=32,
1776 src_mac=0, dst_mac=0, idle_timeout=0, dec_ttl=False):
1777 ofp = self.dp.ofproto
1778 ofp_parser = self.dp.ofproto_parser
1780 dl_type = ether.ETH_TYPE_IP
1784 actions.append(ofp_parser.OFPActionDecNwTtl())
1786 actions.append(ofp_parser.OFPActionSetField(eth_src=src_mac))
1788 actions.append(ofp_parser.OFPActionSetField(eth_dst=dst_mac))
1789 if outport is not None:
1790 actions.append(ofp_parser.OFPActionOutput(outport, 0))
1792 self.set_flow(cookie, priority, dl_type=dl_type, dl_vlan=dl_vlan,
1793 nw_src=nw_src, src_mask=src_mask,
1794 nw_dst=nw_dst, dst_mask=dst_mask,
1795 idle_timeout=idle_timeout, actions=actions)
1797 def delete_flow(self, flow_stats):
1798 ofp = self.dp.ofproto
1799 ofp_parser = self.dp.ofproto_parser
1801 cmd = ofp.OFPFC_DELETE
1802 cookie = flow_stats.cookie
1803 cookie_mask = UINT64_MAX
1804 match = ofp_parser.OFPMatch()
1807 flow_mod = ofp_parser.OFPFlowMod(self.dp, cookie, cookie_mask, 0, cmd,
1808 0, 0, 0, UINT32_MAX, ofp.OFPP_ANY,
1809 ofp.OFPG_ANY, 0, match, inst)
1810 self.dp.send_msg(flow_mod)
1811 self.logger.info('Delete flow [cookie=0x%x]', cookie, extra=self.sw_id)
1814 @OfCtl.register_of_version(ofproto_v1_2.OFP_VERSION)
1815 class OfCtl_v1_2(OfCtl_after_v1_2):
1817 def __init__(self, dp, logger):
1818 super(OfCtl_v1_2, self).__init__(dp, logger)
1820 def set_sw_config_for_ttl(self):
1821 flags = self.dp.ofproto.OFPC_INVALID_TTL_TO_CONTROLLER
1822 miss_send_len = UINT16_MAX
1823 m = self.dp.ofproto_parser.OFPSetConfig(self.dp, flags,
1826 self.logger.info('Set SW config for TTL error packet in.',
1829 def get_all_flow(self, waiters):
1830 ofp = self.dp.ofproto
1831 ofp_parser = self.dp.ofproto_parser
1833 match = ofp_parser.OFPMatch()
1834 stats = ofp_parser.OFPFlowStatsRequest(self.dp, 0, ofp.OFPP_ANY,
1835 ofp.OFPG_ANY, 0, 0, match)
1836 return self.send_stats_request(stats, waiters)
1839 @OfCtl.register_of_version(ofproto_v1_3.OFP_VERSION)
1840 class OfCtl_v1_3(OfCtl_after_v1_2):
1842 def __init__(self, dp, logger):
1843 super(OfCtl_v1_3, self).__init__(dp, logger)
1845 def set_sw_config_for_ttl(self):
1846 packet_in_mask = (1 << self.dp.ofproto.OFPR_ACTION |
1847 1 << self.dp.ofproto.OFPR_INVALID_TTL)
1848 port_status_mask = (1 << self.dp.ofproto.OFPPR_ADD |
1849 1 << self.dp.ofproto.OFPPR_DELETE |
1850 1 << self.dp.ofproto.OFPPR_MODIFY)
1851 flow_removed_mask = (1 << self.dp.ofproto.OFPRR_IDLE_TIMEOUT |
1852 1 << self.dp.ofproto.OFPRR_HARD_TIMEOUT |
1853 1 << self.dp.ofproto.OFPRR_DELETE)
1854 m = self.dp.ofproto_parser.OFPSetAsync(
1855 self.dp, [packet_in_mask, 0], [port_status_mask, 0],
1856 [flow_removed_mask, 0])
1858 self.logger.info('Set SW config for TTL error packet in.',
1861 def get_all_flow(self, waiters):
1862 ofp = self.dp.ofproto
1863 ofp_parser = self.dp.ofproto_parser
1865 match = ofp_parser.OFPMatch()
1866 stats = ofp_parser.OFPFlowStatsRequest(self.dp, 0, 0, ofp.OFPP_ANY,
1867 ofp.OFPG_ANY, 0, 0, match)
1868 return self.send_stats_request(stats, waiters)
1871 def ip_addr_aton(ip_str, err_msg=None):
1873 return addrconv.ipv4.bin_to_text(socket.inet_aton(ip_str))
1874 except (struct.error, socket.error) as e:
1875 if err_msg is not None:
1876 e.message = '%s %s' % (err_msg, e.message)
1877 raise ValueError(e.message)
1880 def ip_addr_ntoa(ip):
1881 return socket.inet_ntoa(addrconv.ipv4.text_to_bin(ip))
1884 def mask_ntob(mask, err_msg=None):
1886 return (UINT32_MAX << (32 - mask)) & UINT32_MAX
1888 msg = 'illegal netmask'
1889 if err_msg is not None:
1890 msg = '%s %s' % (err_msg, msg)
1891 raise ValueError(msg)
1894 def ipv4_apply_mask(address, prefix_len, err_msg=None):
1897 assert isinstance(address, str)
1898 address_int = ipv4_text_to_int(address)
1899 return ipv4_int_to_text(address_int & mask_ntob(prefix_len, err_msg))
1902 def ipv4_int_to_text(ip_int):
1903 assert isinstance(ip_int, numbers.Integral)
1904 return addrconv.ipv4.bin_to_text(struct.pack('!I', ip_int))
1907 def ipv4_text_to_int(ip_text):
1910 assert isinstance(ip_text, str)
1911 return struct.unpack('!I', addrconv.ipv4.text_to_bin(ip_text))[0]
1914 def nw_addr_aton(nw_addr, err_msg=None):
1915 ip_mask = nw_addr.split('/')
1916 default_route = ip_addr_aton(ip_mask[0], err_msg=err_msg)
1918 if len(ip_mask) == 2:
1920 netmask = int(ip_mask[1])
1921 except ValueError as e:
1922 if err_msg is not None:
1923 e.message = '%s %s' % (err_msg, e.message)
1924 raise ValueError(e.message)
1926 msg = 'illegal netmask'
1927 if err_msg is not None:
1928 msg = '%s %s' % (err_msg, msg)
1929 raise ValueError(msg)
1930 nw_addr = ipv4_apply_mask(default_route, netmask, err_msg)
1931 return nw_addr, netmask, default_route