summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorIsaku Yamahata <yamahata@valinux.co.jp>2012-02-10 11:43:23 +0900
committerFUJITA Tomonori <fujita.tomonori@lab.ntt.co.jp>2012-02-10 16:45:30 +0900
commit6d4206d494455405671168f5b0a1e7e90c88439a (patch)
treebf5097c85401fa2f6ec8d9b8abd0cdd25ab9c8b8
parentb4465af91a0bcfa12203b2b297a30fbf65cb08da (diff)
app/simple_isolation: various improvement
- add drop action when external network id - improve handling port status change Signed-off-by: Isaku Yamahata <yamahata@valinux.co.jp> Signed-off-by: FUJITA Tomonori <fujita.tomonori@lab.ntt.co.jp>
-rw-r--r--ryu/app/simple_isolation.py135
1 files changed, 120 insertions, 15 deletions
diff --git a/ryu/app/simple_isolation.py b/ryu/app/simple_isolation.py
index 9e1248b2..f5475292 100644
--- a/ryu/app/simple_isolation.py
+++ b/ryu/app/simple_isolation.py
@@ -1,5 +1,5 @@
# Copyright (C) 2011 Nippon Telegraph and Telephone Corporation.
-# Copyright (C) 2011 Isaku Yamahata <yamahata at valinux co jp>
+# Copyright (C) 2011, 2012 Isaku Yamahata <yamahata at valinux co jp>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
@@ -26,6 +26,8 @@ from ryu.controller.handler import MAIN_DISPATCHER
from ryu.controller.handler import CONFIG_DISPATCHER
from ryu.controller.handler import set_ev_cls
from ryu.lib.mac import haddr_to_str
+from ryu.lib import mac
+
LOG = logging.getLogger('ryu.app.simple_isolation')
@@ -33,6 +35,7 @@ LOG = logging.getLogger('ryu.app.simple_isolation')
class SimpleIsolation(object):
def __init__(self, *_args, **kwargs):
self.nw = kwargs['network']
+ self.dpset = kwargs['dpset']
self.mac2port = mac_to_port.MacToPortTable()
self.mac2net = mac_to_network.MacToNetwork(self.nw)
@@ -106,6 +109,13 @@ class SimpleIsolation(object):
else:
self._flood_to_nw_id(msg, src, dst, dst_nw_id)
+ def _modflow_and_drop_packet(self, msg, src, dst):
+ self._modflow_and_send_packet(msg, src, dst, [])
+
+ def _drop_packet(self, msg):
+ datapath = msg.datapath
+ datapath.send_packet_out(msg.buffer_id, msg.in_port, [])
+
@set_ev_cls(ofp_event.EventOFPPacketIn, MAIN_DISPATCHER)
def packet_in_handler(self, ev):
# LOG.debug('packet in ev %s msg %s', ev, ev.msg)
@@ -135,6 +145,7 @@ class SimpleIsolation(object):
#
# should we install drop action pro-actively for future?
#
+ self._drop_packet(msg)
return
old_port = self.mac2port.port_add(datapath.id, msg.in_port, src)
@@ -159,8 +170,7 @@ class SimpleIsolation(object):
dst_nw_id = self.mac2net.get_network(dst, NW_ID_UNKNOWN)
# we handle multicast packet as same as broadcast
- first_oct = struct.unpack_from('B', dst)[0]
- broadcast = (dst == '\xff' * 6) or (first_oct & 0x01)
+ broadcast = (dst == mac.BROADCAST) or mac.is_multicast(dst)
out_port = self.mac2port.port_get(datapath.id, dst)
#
@@ -178,20 +188,29 @@ class SimpleIsolation(object):
# Can the following logic be refined/shortened?
#
+ # When NW_ID_UNKNOWN is found, registering ports might be delayed.
+ # So just drop only this packet and not install flow entry.
+ # It is expected that when next packet arrives, the port is registers
+ # with some network id
+
if port_nw_id != NW_ID_EXTERNAL and port_nw_id != NW_ID_UNKNOWN:
if broadcast:
# flood to all ports of external or src_nw_id
self._flood_to_nw_id(msg, src, dst, src_nw_id)
- elif src_nw_id != NW_ID_EXTERNAL and src_nw_id != NW_ID_UNKNOWN:
+ elif src_nw_id == NW_ID_EXTERNAL:
+ self._modflow_and_drop_packet(msg, src, dst)
+ return
+ elif src_nw_id == NW_ID_UNKNOWN:
+ self._drop_packet(msg)
+ return
+ else:
+ # src_nw_id != NW_ID_EXTERNAL and src_nw_id != NW_ID_UNKNOWN:
+ #
# try learned mac check if the port is net_id
# or
# flood to all ports of external or src_nw_id
self._learned_mac_or_flood_to_nw_id(msg, src, dst,
src_nw_id, out_port)
- else:
- # NW_ID_EXTERNAL or NW_ID_UNKNOWN
- # drop packets
- return
elif port_nw_id == NW_ID_EXTERNAL:
if src_nw_id != NW_ID_EXTERNAL and src_nw_id != NW_ID_UNKNOWN:
@@ -211,6 +230,7 @@ class SimpleIsolation(object):
else:
# should not occur?
LOG.debug("should this case happen?")
+ self._drop_packet(msg)
elif dst_nw_id == NW_ID_EXTERNAL:
# try learned mac
# or
@@ -219,24 +239,109 @@ class SimpleIsolation(object):
src_nw_id, out_port)
else:
assert dst_nw_id == NW_ID_UNKNOWN
-
+ LOG.debug("Unknown dst_nw_id")
+ self._drop_packet(msg)
elif src_nw_id == NW_ID_EXTERNAL:
- # drop packet
- pass
+ self._modflow_and_drop_packet(msg, src, dst)
else:
# should not occur?
- # drop packets
assert src_nw_id == NW_ID_UNKNOWN
+ self._drop_packet(msg)
else:
- # drop packets?
+ # drop packets
assert port_nw_id == NW_ID_UNKNOWN
+ self._drop_packet(msg)
+ # LOG.debug("Unknown port_nw_id")
- @set_ev_cls(ofp_event.EventOFPPortStatus, MAIN_DISPATCHER)
- def port_status_handler(self, ev):
+ def _port_add(self, ev):
+ #
+ # delete flows entries that matches with
+ # dl_dst == broadcast/multicast
+ # and dl_src = network id if network id of this port is known
+ # to send broadcast packet to this newly added port.
+ #
+ # Openflow v1.0 doesn't support masked match of dl_dst,
+ # so delete all flow entries. It's inefficient, though.
+ #
msg = ev.msg
datapath = msg.datapath
+
datapath.send_delete_all_flows()
datapath.send_barrier()
+ self.nw.port_added(datapath, msg.desc.port_no)
+
+ def _port_del(self, ev):
+ # free mac addresses associated to this VM port,
+ # and delete related flow entries for later reuse of mac address
+
+ dps_needs_barrier = set()
+
+ msg = ev.msg
+ datapath = msg.datapath
+ datapath_id = datapath.id
+ port_no = msg.desc.port_no
+
+ wildcards = datapath.ofproto.OFPFW_ALL
+ wildcards &= ~datapath.ofproto.OFPFW_IN_PORT
+ match = datapath.ofproto_parser.OFPMatch(wildcards, port_no, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0)
+ datapath.send_flow_del(match=match, cookie=0)
+
+ match = datapath.ofproto_parser.OFPMatch(
+ datapath.ofproto.OFPFW_ALL, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0)
+ datapath.send_flow_del(match=match, cookie=0, out_port=port_no)
+ dps_needs_barrier.add(datapath)
+
+ try:
+ port_nw_id = self.nw.get_network(datapath_id, port_no)
+ except PortUnknown:
+ # race condition between rest api delete port
+ # and openflow port deletion ofp_event
+ pass
+ else:
+ if port_nw_id in (NW_ID_UNKNOWN, NW_ID_EXTERNAL):
+ datapath.send_barrier()
+ return
+
+ for mac_ in self.mac2port.mac_list(datapath_id, port_no):
+ for dp in self.dpset.get_all():
+ if self.mac2port.port_get(dp.id, mac_) is None:
+ continue
+
+ wildcards = dp.ofproto.OFPFW_ALL
+ wildcards &= ~dp.ofproto.OFPFW_DL_SRC
+ match = dp.ofproto_parser.OFPMatch(
+ wildcards, 0, mac_, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0)
+ dp.send_flow_del(match=match, cookie=0)
+
+ wildcards = dp.ofproto.OFPFW_ALL
+ wildcards &= ~dp.ofproto.OFPFW_DL_DST
+ match = dp.ofproto_parser.OFPMatch(
+ wildcards, 0, 0, mac_, 0, 0, 0, 0, 0, 0, 0, 0, 0)
+ dp.send_flow_del(match=match, cookie=0)
+ dps_needs_barrier.add(dp)
+
+ self.mac2port.mac_del(dp.id, mac_)
+
+ self.mac2net.del_mac(mac_)
+
+ self.nw.port_deleted(datapath.id, port_no)
+
+ for dp in dps_needs_barrier:
+ dp.send_barrier()
+
+ @set_ev_cls(ofp_event.EventOFPPortStatus, MAIN_DISPATCHER)
+ def port_status_handler(self, ev):
+ msg = ev.msg
+ reason = msg.reason
+ ofproto = msg.datapath.ofproto
+
+ if reason == ofproto.OFPPR_ADD:
+ self._port_add(ev)
+ elif reason == ofproto.OFPPR_DELETE:
+ self._port_del(ev)
+ else:
+ assert reason == ofproto.OFPPR_MODIFY
@set_ev_cls(ofp_event.EventOFPBarrierReply, MAIN_DISPATCHER)
def barrier_replay_handler(self, ev):