summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorwatanabe.fumitaka <watanabe.fumitaka@nttcom.co.jp>2013-09-03 17:06:55 +0900
committerFUJITA Tomonori <fujita.tomonori@lab.ntt.co.jp>2013-09-03 17:23:22 +0900
commite777a16f842c0cf554dbe6e410adbd02044e6b7d (patch)
treecb8789b62301fc3540e2b0281087784dccd3de75
parent05cdadc90054f3453c47038ea9279b884d125d1c (diff)
rest_firewall: blocked packet logging
Signed-off-by: WATANABE Fumitaka <watanabe.fumitaka@nttcom.co.jp> Signed-off-by: FUJITA Tomonori <fujita.tomonori@lab.ntt.co.jp>
-rw-r--r--ryu/app/rest_firewall.py176
1 files changed, 158 insertions, 18 deletions
diff --git a/ryu/app/rest_firewall.py b/ryu/app/rest_firewall.py
index c547cb0e..b8919e98 100644
--- a/ryu/app/rest_firewall.py
+++ b/ryu/app/rest_firewall.py
@@ -31,6 +31,7 @@ from ryu.lib import mac
from ryu.lib import dpid as dpid_lib
from ryu.lib import ofctl_v1_0
from ryu.lib import ofctl_v1_2
+from ryu.lib.packet import packet
from ryu.ofproto import ether
from ryu.ofproto import inet
from ryu.ofproto import ofproto_v1_0
@@ -38,9 +39,6 @@ from ryu.ofproto import ofproto_v1_2
from ryu.ofproto import ofproto_v1_2_parser
-LOG = logging.getLogger('ryu.app.firewall')
-
-
#=============================
# REST API
#=============================
@@ -62,6 +60,18 @@ LOG = logging.getLogger('ryu.app.firewall')
# PUT /firewall/module/disable/{switch-id}
#
#
+## about Firewall logs
+#
+# get log status of all firewall switches
+# GET /firewall/log/status
+#
+# set log enable the firewall switches
+# PUT /firewall/log/enable/{switch-id}
+#
+# set log disable the firewall switches
+# PUT /firewall/log/disable/{switch-id}
+#
+#
## about Firewall rules
#
# get rules of the firewall switches
@@ -131,6 +141,7 @@ REST_SWITCHID = 'switch_id'
REST_VLANID = 'vlan_id'
REST_RULE_ID = 'rule_id'
REST_STATUS = 'status'
+REST_LOG_STATUS = 'log_status'
REST_STATUS_ENABLE = 'enable'
REST_STATUS_DISABLE = 'disable'
REST_COOKIE = 'cookie'
@@ -154,10 +165,13 @@ REST_TP_DST = 'tp_dst'
REST_ACTION = 'actions'
REST_ACTION_ALLOW = 'ALLOW'
REST_ACTION_DENY = 'DENY'
+REST_ACTION_PACKETIN = 'PACKETIN'
STATUS_FLOW_PRIORITY = ofproto_v1_2_parser.UINT16_MAX
ARP_FLOW_PRIORITY = ofproto_v1_2_parser.UINT16_MAX - 1
+LOG_FLOW_PRIORITY = 0
+ACL_FLOW_PRIORITY_MIN = LOG_FLOW_PRIORITY + 1
ACL_FLOW_PRIORITY_MAX = ofproto_v1_2_parser.UINT16_MAX - 2
VLANID_NONE = 0
@@ -176,6 +190,10 @@ class RestFirewallAPI(app_manager.RyuApp):
def __init__(self, *args, **kwargs):
super(RestFirewallAPI, self).__init__(*args, **kwargs)
+
+ # logger configure
+ FirewallController.set_logger(self.logger)
+
self.dpset = kwargs['dpset']
wsgi = kwargs['wsgi']
self.waiters = {}
@@ -189,6 +207,7 @@ class RestFirewallAPI(app_manager.RyuApp):
requirements = {'switchid': SWITCHID_PATTERN,
'vlanid': VLANID_PATTERN}
+ # for firewall status
uri = path + '/module/status'
mapper.connect('firewall', uri,
controller=FirewallController, action='get_status',
@@ -206,6 +225,24 @@ class RestFirewallAPI(app_manager.RyuApp):
conditions=dict(method=['PUT']),
requirements=requirements)
+ # for firewall logs
+ uri = path + '/log/status'
+ mapper.connect('firewall', uri,
+ controller=FirewallController, action='get_log_status',
+ conditions=dict(method=['GET']))
+
+ uri = path + '/log/enable/{switchid}'
+ mapper.connect('firewall', uri,
+ controller=FirewallController, action='set_log_enable',
+ conditions=dict(method=['PUT']),
+ requirements=requirements)
+
+ uri = path + '/log/disable/{switchid}'
+ mapper.connect('firewall', uri,
+ controller=FirewallController, action='set_log_disable',
+ conditions=dict(method=['PUT']),
+ requirements=requirements)
+
# for no VLAN data
uri = path + '/rules/{switchid}'
mapper.connect('firewall', uri,
@@ -273,6 +310,10 @@ class RestFirewallAPI(app_manager.RyuApp):
def stats_reply_handler_v1_2(self, ev):
self.stats_reply_handler(ev)
+ @set_ev_cls(ofp_event.EventOFPPacketIn, MAIN_DISPATCHER)
+ def packet_in_handler(self, ev):
+ FirewallController.packet_in_handler(ev.msg)
+
class FirewallOfsList(dict):
def __init__(self):
@@ -303,34 +344,46 @@ class FirewallOfsList(dict):
class FirewallController(ControllerBase):
_OFS_LIST = FirewallOfsList()
+ _LOGGER = None
def __init__(self, req, link, data, **config):
super(FirewallController, self).__init__(req, link, data, **config)
self.dpset = data['dpset']
self.waiters = data['waiters']
+ @classmethod
+ def set_logger(cls, logger):
+ cls._LOGGER = logger
+ cls._LOGGER.propagate = False
+ hdlr = logging.StreamHandler()
+ fmt_str = '[FW][%(levelname)s] %(message)s'
+ hdlr.setFormatter(logging.Formatter(fmt_str))
+ cls._LOGGER.addHandler(hdlr)
+
@staticmethod
def regist_ofs(dp):
+ dpid_str = dpid_lib.dpid_to_str(dp.id)
try:
f_ofs = Firewall(dp)
except OFPUnknownVersion, message:
- mes = 'dpid=%s : %s' % (dpid_lib.dpid_to_str(dp.id), message)
- LOG.info(mes)
+ FirewallController._LOGGER.info('dpid=%s: %s',
+ dpid_str, message)
return
FirewallController._OFS_LIST.setdefault(dp.id, f_ofs)
f_ofs.set_disable_flow()
f_ofs.set_arp_flow()
- LOG.info('dpid=%s : Join as firewall switch.' %
- dpid_lib.dpid_to_str(dp.id))
+ f_ofs.set_log_enable()
+ FirewallController._LOGGER.info('dpid=%s: Join as firewall.',
+ dpid_str)
@staticmethod
def unregist_ofs(dp):
if dp.id in FirewallController._OFS_LIST:
del FirewallController._OFS_LIST[dp.id]
- LOG.info('dpid=%s : Leave firewall switch.' %
- dpid_lib.dpid_to_str(dp.id))
+ FirewallController._LOGGER.info('dpid=%s: Leave firewall.',
+ dpid_lib.dpid_to_str(dp.id))
# GET /firewall/module/status
def get_status(self, req, **_kwargs):
@@ -377,6 +430,34 @@ class FirewallController(ControllerBase):
body = json.dumps(msgs)
return Response(content_type='application/json', body=body)
+ # GET /firewall/log/status
+ def get_log_status(self, dummy, **_kwargs):
+ return self._access_module(REST_ALL, 'get_log_status',
+ waiters=self.waiters)
+
+ # PUT /firewall/log/enable/{switchid}
+ def set_log_enable(self, dummy, switchid, **_kwargs):
+ return self._access_module(switchid, 'set_log_enable')
+
+ # PUT /firewall/log/disable/{switchid}
+ def set_log_disable(self, dummy, switchid, **_kwargs):
+ return self._access_module(switchid, 'set_log_disable')
+
+ def _access_module(self, switchid, func, waiters=None):
+ try:
+ dps = self._OFS_LIST.get_ofs(switchid)
+ except ValueError, message:
+ return Response(status=400, body=str(message))
+
+ msgs = {}
+ for f_ofs in dps.values():
+ function = getattr(f_ofs, func)
+ msg = function(waiters) if waiters else function()
+ msgs.update(msg)
+
+ body = json.dumps(msgs)
+ return Response(content_type='application/json', body=body)
+
# GET /firewall/rules/{switchid}
def get_rules(self, req, switchid, **_kwargs):
return self._get_rules(switchid)
@@ -420,7 +501,7 @@ class FirewallController(ControllerBase):
try:
rule = eval(req.body)
except SyntaxError:
- LOG.debug('invalid syntax %s', req.body)
+ FirewallController._LOGGER.debug('invalid syntax %s', req.body)
return Response(status=400)
try:
@@ -444,7 +525,7 @@ class FirewallController(ControllerBase):
try:
ruleid = eval(req.body)
except SyntaxError:
- LOG.debug('invalid syntax %s', req.body)
+ FirewallController._LOGGER.debug('invalid syntax %s', req.body)
return Response(status=400)
try:
@@ -475,6 +556,13 @@ class FirewallController(ControllerBase):
raise ValueError(msg)
return vlan_id
+ @staticmethod
+ def packet_in_handler(msg):
+ pkt = packet.Packet(msg.data)
+ dpid_str = dpid_lib.dpid_to_str(msg.datapath.id)
+ FirewallController._LOGGER.info('dpid=%s: Blocked packet = %s',
+ dpid_str, pkt)
+
class Firewall(object):
@@ -568,6 +656,49 @@ class Firewall(object):
dpid_lib.dpid_to_str(self.dp.id))
return {switch_id: msg}
+ def get_log_status(self, waiters):
+ msgs = self.ofctl.get_flow_stats(self.dp, waiters)
+
+ status = REST_STATUS_DISABLE
+ if str(self.dp.id) in msgs:
+ flow_stats = msgs[str(self.dp.id)]
+ for flow_stat in flow_stats:
+ if flow_stat['priority'] == LOG_FLOW_PRIORITY:
+ if flow_stat['actions']:
+ status = REST_STATUS_ENABLE
+
+ msg = {REST_LOG_STATUS: status}
+ switch_id = '%s: %s' % (REST_SWITCHID,
+ dpid_lib.dpid_to_str(self.dp.id))
+ return {switch_id: msg}
+
+ def set_log_disable(self):
+ return self._set_log_status(False)
+
+ def set_log_enable(self):
+ return self._set_log_status(True)
+
+ def _set_log_status(self, is_enable):
+ if is_enable:
+ actions = Action.to_openflow(self.dp,
+ {REST_ACTION: REST_ACTION_PACKETIN})
+ details = 'Log collection started.'
+ else:
+ actions = []
+ details = 'Log collection stopped.'
+
+ flow = self._to_of_flow(cookie=0, priority=LOG_FLOW_PRIORITY,
+ match={}, actions=actions)
+
+ cmd = self.dp.ofproto.OFPFC_ADD
+ self.ofctl.mod_flow_entry(self.dp, flow, cmd)
+
+ msg = {'result': 'success',
+ 'details': details}
+ switch_id = '%s: %s' % (REST_SWITCHID,
+ dpid_lib.dpid_to_str(self.dp.id))
+ return {switch_id: msg}
+
def set_arp_flow(self):
cookie = 0
priority = ARP_FLOW_PRIORITY
@@ -591,11 +722,13 @@ class Firewall(object):
return {switch_id: msgs}
def _set_rule(self, cookie, rest, vlan_id):
- priority = int(rest.get(REST_PRIORITY, 0))
+ priority = int(rest.get(REST_PRIORITY, ACL_FLOW_PRIORITY_MIN))
+
+ if (priority < ACL_FLOW_PRIORITY_MIN
+ or ACL_FLOW_PRIORITY_MAX < priority):
+ raise ValueError('Invalid priority value. Set [%d-%d]'
+ % (ACL_FLOW_PRIORITY_MIN, ACL_FLOW_PRIORITY_MAX))
- if priority < 0 or ACL_FLOW_PRIORITY_MAX < priority:
- raise ValueError('Invalid priority value. Set [0-%d]'
- % ACL_FLOW_PRIORITY_MAX)
if vlan_id:
rest[REST_DL_VLAN] = vlan_id
@@ -627,8 +760,10 @@ class Firewall(object):
if str(self.dp.id) in msgs:
flow_stats = msgs[str(self.dp.id)]
for flow_stat in flow_stats:
- if (flow_stat[REST_PRIORITY] != STATUS_FLOW_PRIORITY
- and flow_stat[REST_PRIORITY] != ARP_FLOW_PRIORITY):
+ priority = flow_stat[REST_PRIORITY]
+ if (priority != STATUS_FLOW_PRIORITY
+ and priority != ARP_FLOW_PRIORITY
+ and priority != LOG_FLOW_PRIORITY):
vid = flow_stat[REST_MATCH].get(REST_DL_VLAN, VLANID_NONE)
if vlan_id == REST_ALL or vlan_id == vid:
rule = self._to_rest_rule(flow_stat)
@@ -669,7 +804,8 @@ class Firewall(object):
dl_vlan = flow_stat[REST_MATCH].get(REST_DL_VLAN, VLANID_NONE)
if (priority != STATUS_FLOW_PRIORITY
- and priority != ARP_FLOW_PRIORITY):
+ and priority != ARP_FLOW_PRIORITY
+ and priority != LOG_FLOW_PRIORITY):
if ((rule_id == REST_ALL or rule_id == ruleid) and
(vlan_id == dl_vlan or vlan_id == REST_ALL)):
match = Match.to_del_openflow(flow_stat[REST_MATCH])
@@ -837,6 +973,10 @@ class Action(object):
'port': out_port}]
elif value == REST_ACTION_DENY:
action = []
+ elif value == REST_ACTION_PACKETIN:
+ out_port = dp.ofproto.OFPP_CONTROLLER
+ action = [{'type': 'OUTPUT',
+ 'port': out_port}]
else:
raise ValueError('Invalid action type.')