136 lines
5.5 KiB
Python
136 lines
5.5 KiB
Python
# Copyright (c) 2017 OpenStack Foundation.
|
|
# All Rights Reserved.
|
|
#
|
|
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
|
# not use this file except in compliance with the License. You may obtain
|
|
# a copy of the License at
|
|
#
|
|
# http://www.apache.org/licenses/LICENSE-2.0
|
|
#
|
|
# Unless required by applicable law or agreed to in writing, software
|
|
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
|
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
|
# License for the specific language governing permissions and limitations
|
|
# under the License.
|
|
from oslo_log import log
|
|
from ryu.ofproto import nicira_ext
|
|
|
|
from dragonflow.controller.common import constants as const
|
|
from dragonflow.controller import df_base_app
|
|
from dragonflow.db.models import constants as model_constants
|
|
from dragonflow.db.models import switch
|
|
|
|
|
|
LOG = log.getLogger(__name__)
|
|
|
|
|
|
class ClassifierApp(df_base_app.DFlowApp):
|
|
|
|
def __init__(self, *args, **kwargs):
|
|
super(ClassifierApp, self).__init__(*args, **kwargs)
|
|
self._ofport_unique_key_map = {}
|
|
|
|
def switch_features_handler(self, ev):
|
|
self._ofport_unique_key_map.clear()
|
|
self.add_flow_go_to_table(
|
|
table=const.INGRESS_CLASSIFICATION_DISPATCH_TABLE,
|
|
priority=const.PRIORITY_DEFAULT,
|
|
goto_table_id=self.dfdp.apps['portsec'].entrypoints.default,
|
|
)
|
|
|
|
@df_base_app.register_event(
|
|
switch.SwitchPort, model_constants.EVENT_CREATED)
|
|
@df_base_app.register_event(
|
|
switch.SwitchPort, model_constants.EVENT_UPDATED)
|
|
def _switch_port_created(self, switch_port, orig_switch_port=None):
|
|
port_num = switch_port.port_num
|
|
lport_ref = switch_port.lport
|
|
if not lport_ref:
|
|
return # Not relevant
|
|
if orig_switch_port and orig_switch_port.port_num != port_num:
|
|
self._switch_port_deleted(switch_port)
|
|
if not port_num or port_num == -1:
|
|
return # Not ready yet, or error
|
|
lport = self.nb_api.get(lport_ref)
|
|
self._ofport_unique_key_map[switch_port.id] = (
|
|
port_num, lport.unique_key)
|
|
LOG.info("Add local ovs port %(switch_port)s, logical port "
|
|
"%(lport)s for classification",
|
|
{'switch_port': port_num, 'lport': lport})
|
|
self._make_ingress_classification_flow(lport, port_num)
|
|
self._make_ingress_dispatch_flow(lport, port_num)
|
|
|
|
def _make_ingress_dispatch_flow(self, lport,
|
|
port_num):
|
|
port_key = lport.unique_key
|
|
match = self.parser.OFPMatch(reg7=port_key)
|
|
LOG.debug("match reg7=%(reg7)s for ingress dispatch of %(lport)s",
|
|
{'reg7': port_key, 'lport': lport})
|
|
actions = [self.parser.OFPActionOutput(port_num,
|
|
self.ofproto.OFPCML_NO_BUFFER)]
|
|
action_inst = self.parser.OFPInstructionActions(
|
|
self.ofproto.OFPIT_APPLY_ACTIONS, actions)
|
|
inst = [action_inst]
|
|
self.mod_flow(
|
|
inst=inst,
|
|
table_id=const.INGRESS_DISPATCH_TABLE,
|
|
priority=const.PRIORITY_MEDIUM,
|
|
match=match)
|
|
|
|
def _make_ingress_classification_flow(self, lport, port_num):
|
|
match = self.parser.OFPMatch(in_port=port_num)
|
|
network_id = lport.lswitch.unique_key
|
|
LOG.debug("match in_port=%(in_port)s for ingress classification "
|
|
"of %(lport)s in network %(network)s",
|
|
{'in_port': port_num, 'lport': lport, 'network': network_id})
|
|
# Reset in_port to 0 to avoid drop by output command.
|
|
actions = [
|
|
self.parser.OFPActionSetField(reg6=lport.unique_key),
|
|
self.parser.OFPActionSetField(metadata=network_id),
|
|
self.parser.NXActionRegLoad(
|
|
dst='in_port',
|
|
value=0,
|
|
ofs_nbits=nicira_ext.ofs_nbits(0, 31),
|
|
),
|
|
self.parser.NXActionResubmit(),
|
|
]
|
|
self.mod_flow(
|
|
table_id=const.INGRESS_CLASSIFICATION_DISPATCH_TABLE,
|
|
priority=const.PRIORITY_MEDIUM,
|
|
match=match,
|
|
actions=actions,
|
|
)
|
|
|
|
@df_base_app.register_event(
|
|
switch.SwitchPort, model_constants.EVENT_DELETED)
|
|
def _switch_port_deleted(self, switch_port):
|
|
try:
|
|
port_num, port_key = self._ofport_unique_key_map.pop(
|
|
switch_port.id)
|
|
except KeyError:
|
|
# Port not present in lookup, was either not added, or removed
|
|
# by a previous update. In both cases irrelevant.
|
|
return
|
|
self._del_ingress_dispatch_flow(port_key)
|
|
self._del_ingress_classification_flow(port_num)
|
|
|
|
def _del_ingress_dispatch_flow(self, port_key):
|
|
LOG.debug("delete ingress dispatch flow for port_key=%(port_key)s",
|
|
{'port_key': port_key})
|
|
match = self.parser.OFPMatch(reg7=port_key)
|
|
self.mod_flow(
|
|
table_id=const.INGRESS_DISPATCH_TABLE,
|
|
command=self.ofproto.OFPFC_DELETE,
|
|
priority=const.PRIORITY_MEDIUM,
|
|
match=match)
|
|
|
|
def _del_ingress_classification_flow(self, port_num):
|
|
LOG.debug("delete in_port=%(in_port)s ingress classification",
|
|
{'in_port': port_num})
|
|
match = self.parser.OFPMatch(in_port=port_num)
|
|
self.mod_flow(
|
|
table_id=const.INGRESS_CLASSIFICATION_DISPATCH_TABLE,
|
|
command=self.ofproto.OFPFC_DELETE,
|
|
priority=const.PRIORITY_MEDIUM,
|
|
match=match)
|