"Fossies" - the Fresh Open Source Software Archive

Member "neutron-14.0.3/neutron/plugins/ml2/drivers/openvswitch/agent/openflow/native/br_int.py" (22 Oct 2019, 14504 Bytes) of package /linux/misc/openstack/neutron-14.0.3.tar.gz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) Python source code syntax highlighting (style: standard) with prefixed line numbers. Alternatively you can here view or download the uninterpreted source code file. For more information about "br_int.py" see the Fossies "Dox" file reference documentation and the latest Fossies "Diffs" side-by-side code changes report: 14.0.2_vs_14.0.3.

    1 # Copyright (C) 2014,2015 VA Linux Systems Japan K.K.
    2 # Copyright (C) 2014,2015 YAMAMOTO Takashi <yamamoto at valinux co jp>
    3 # All Rights Reserved.
    4 #
    5 #    Licensed under the Apache License, Version 2.0 (the "License"); you may
    6 #    not use this file except in compliance with the License. You may obtain
    7 #    a copy of the License at
    8 #
    9 #         http://www.apache.org/licenses/LICENSE-2.0
   10 #
   11 #    Unless required by applicable law or agreed to in writing, software
   12 #    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
   13 #    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
   14 #    License for the specific language governing permissions and limitations
   15 #    under the License.
   16 
   17 """
   18 * references
   19 ** OVS agent https://wiki.openstack.org/wiki/Ovs-flow-logic
   20 """
   21 
   22 import netaddr
   23 
   24 from neutron_lib import constants as p_const
   25 from os_ken.lib.packet import ether_types
   26 from os_ken.lib.packet import icmpv6
   27 from os_ken.lib.packet import in_proto
   28 from oslo_log import log as logging
   29 
   30 from neutron.plugins.ml2.drivers.openvswitch.agent.common import constants
   31 from neutron.plugins.ml2.drivers.openvswitch.agent.openflow.native \
   32     import ovs_bridge
   33 
   34 
   35 LOG = logging.getLogger(__name__)
   36 
   37 
   38 class OVSIntegrationBridge(ovs_bridge.OVSAgentBridge):
   39     """openvswitch agent br-int specific logic."""
   40 
   41     of_tables = constants.INT_BR_ALL_TABLES
   42 
   43     def setup_default_table(self):
   44         self.setup_canary_table()
   45         self.install_goto(dest_table_id=constants.TRANSIENT_TABLE)
   46         self.install_normal(table_id=constants.TRANSIENT_TABLE, priority=3)
   47         self.install_drop(table_id=constants.ARP_SPOOF_TABLE)
   48         self.install_drop(table_id=constants.LOCAL_SWITCHING,
   49                           priority=constants.OPENFLOW_MAX_PRIORITY,
   50                           vlan_vid=constants.DEAD_VLAN_TAG)
   51 
   52     def setup_canary_table(self):
   53         self.install_drop(constants.CANARY_TABLE)
   54 
   55     def check_canary_table(self):
   56         try:
   57             flows = self.dump_flows(constants.CANARY_TABLE)
   58         except RuntimeError:
   59             LOG.exception("Failed to communicate with the switch")
   60             return constants.OVS_DEAD
   61         return constants.OVS_NORMAL if flows else constants.OVS_RESTARTED
   62 
   63     @staticmethod
   64     def _local_vlan_match(_ofp, ofpp, port, vlan_vid):
   65         return ofpp.OFPMatch(in_port=port, vlan_vid=vlan_vid)
   66 
   67     def provision_local_vlan(self, port, lvid, segmentation_id):
   68         (_dp, ofp, ofpp) = self._get_dp()
   69         if segmentation_id is None:
   70             vlan_vid = ofp.OFPVID_NONE
   71             actions = [ofpp.OFPActionPushVlan()]
   72         else:
   73             vlan_vid = segmentation_id | ofp.OFPVID_PRESENT
   74             actions = []
   75         match = self._local_vlan_match(ofp, ofpp, port, vlan_vid)
   76         actions += [
   77             ofpp.OFPActionSetField(vlan_vid=lvid | ofp.OFPVID_PRESENT),
   78         ]
   79         instructions = [
   80             ofpp.OFPInstructionActions(ofp.OFPIT_APPLY_ACTIONS, actions),
   81             ofpp.OFPInstructionGotoTable(table_id=constants.TRANSIENT_TABLE),
   82         ]
   83         self.install_instructions(
   84             instructions=instructions,
   85             priority=3,
   86             match=match,
   87         )
   88 
   89     def reclaim_local_vlan(self, port, segmentation_id):
   90         (_dp, ofp, ofpp) = self._get_dp()
   91         if segmentation_id is None:
   92             vlan_vid = ofp.OFPVID_NONE
   93         else:
   94             vlan_vid = segmentation_id | ofp.OFPVID_PRESENT
   95         match = self._local_vlan_match(ofp, ofpp, port, vlan_vid)
   96         self.uninstall_flows(match=match)
   97 
   98     @staticmethod
   99     def _arp_dvr_dst_mac_match(ofp, ofpp, vlan, dvr_mac):
  100         # If eth_dst is equal to the dvr mac of this host, then
  101         # flag it as matched.
  102         return ofpp.OFPMatch(vlan_vid=vlan | ofp.OFPVID_PRESENT,
  103                              eth_dst=dvr_mac)
  104 
  105     @staticmethod
  106     def _dvr_dst_mac_table_id(network_type):
  107         if network_type == p_const.TYPE_VLAN:
  108             return constants.ARP_DVR_MAC_TO_DST_MAC_VLAN
  109         else:
  110             return constants.ARP_DVR_MAC_TO_DST_MAC
  111 
  112     def install_dvr_dst_mac_for_arp(self, network_type,
  113                                     vlan_tag, gateway_mac, dvr_mac, rtr_port):
  114         table_id = self._dvr_dst_mac_table_id(network_type)
  115         # Match the destination MAC with the DVR MAC
  116         (_dp, ofp, ofpp) = self._get_dp()
  117         match = self._arp_dvr_dst_mac_match(ofp, ofpp, vlan_tag, dvr_mac)
  118         # Incoming packet will come with destination MAC of DVR host MAC from
  119         # the ARP Responder. The Source MAC in this case will have the source
  120         # MAC of the port MAC that responded from the ARP responder.
  121         # So just remove the DVR host MAC from the 'eth_dst' and replace it
  122         # with the gateway-mac. The packet should end up in the right the table
  123         # for the packet to reach the router interface.
  124         actions = [
  125             ofpp.OFPActionSetField(eth_dst=gateway_mac),
  126             ofpp.OFPActionPopVlan(),
  127             ofpp.OFPActionOutput(rtr_port, 0)
  128         ]
  129         self.install_apply_actions(table_id=table_id,
  130                                    priority=5,
  131                                    match=match,
  132                                    actions=actions)
  133 
  134     @staticmethod
  135     def _dvr_to_src_mac_match(ofp, ofpp, vlan_tag, dst_mac):
  136         return ofpp.OFPMatch(vlan_vid=vlan_tag | ofp.OFPVID_PRESENT,
  137                              eth_dst=dst_mac)
  138 
  139     @staticmethod
  140     def _dvr_to_src_mac_table_id(network_type):
  141         if network_type == p_const.TYPE_VLAN:
  142             return constants.DVR_TO_SRC_MAC_VLAN
  143         else:
  144             return constants.DVR_TO_SRC_MAC
  145 
  146     def install_dvr_to_src_mac(self, network_type,
  147                                vlan_tag, gateway_mac, dst_mac, dst_port):
  148         table_id = self._dvr_to_src_mac_table_id(network_type)
  149         (_dp, ofp, ofpp) = self._get_dp()
  150         match = self._dvr_to_src_mac_match(ofp, ofpp,
  151                                            vlan_tag=vlan_tag, dst_mac=dst_mac)
  152         actions = [
  153             ofpp.OFPActionSetField(eth_src=gateway_mac),
  154         ]
  155         instructions = [
  156             ofpp.OFPInstructionActions(ofp.OFPIT_APPLY_ACTIONS, actions),
  157             ofpp.OFPInstructionGotoTable(table_id=constants.TRANSIENT_TABLE),
  158         ]
  159         self.install_instructions(table_id=table_id,
  160                                   priority=4,
  161                                   match=match,
  162                                   instructions=instructions)
  163         actions = [
  164             ofpp.OFPActionPopVlan(),
  165             ofpp.OFPActionOutput(dst_port, 0),
  166         ]
  167         self.install_apply_actions(table_id=constants.TRANSIENT_TABLE,
  168                                    priority=4,
  169                                    match=match,
  170                                    actions=actions)
  171 
  172     def delete_dvr_to_src_mac(self, network_type, vlan_tag, dst_mac):
  173         table_id = self._dvr_to_src_mac_table_id(network_type)
  174         (_dp, ofp, ofpp) = self._get_dp()
  175         match = self._dvr_to_src_mac_match(ofp, ofpp,
  176                                            vlan_tag=vlan_tag, dst_mac=dst_mac)
  177         for table in (table_id, constants.TRANSIENT_TABLE):
  178             self.uninstall_flows(
  179                 strict=True, priority=4, table_id=table, match=match)
  180 
  181     def add_dvr_mac_vlan(self, mac, port):
  182         self.install_goto(table_id=constants.LOCAL_SWITCHING,
  183                           priority=4,
  184                           in_port=port,
  185                           eth_src=mac,
  186                           dest_table_id=constants.DVR_TO_SRC_MAC_VLAN)
  187 
  188     def remove_dvr_mac_vlan(self, mac):
  189         # REVISIT(yamamoto): match in_port as well?
  190         self.uninstall_flows(table_id=constants.LOCAL_SWITCHING,
  191                              eth_src=mac)
  192 
  193     def add_dvr_mac_tun(self, mac, port):
  194         self.install_goto(table_id=constants.LOCAL_SWITCHING,
  195                           priority=2,
  196                           in_port=port,
  197                           eth_src=mac,
  198                           dest_table_id=constants.DVR_TO_SRC_MAC)
  199 
  200     def remove_dvr_mac_tun(self, mac, port):
  201         self.uninstall_flows(table_id=constants.LOCAL_SWITCHING,
  202                              in_port=port, eth_src=mac)
  203 
  204     def delete_dvr_dst_mac_for_arp(self, network_type,
  205                                    vlan_tag, gateway_mac, dvr_mac, rtr_port):
  206         table_id = self._dvr_to_src_mac_table_id(network_type)
  207         (_dp, ofp, ofpp) = self._get_dp()
  208         match = self._arp_dvr_dst_mac_match(ofp, ofpp, vlan_tag, dvr_mac)
  209         for table in table_id:
  210             self.uninstall_flows(
  211                 strict=True, priority=5, table_id=table, match=match)
  212 
  213     def add_dvr_gateway_mac_arp_vlan(self, mac, port):
  214         self.install_goto(table_id=constants.LOCAL_SWITCHING,
  215                           priority=5,
  216                           in_port=port,
  217                           eth_dst=mac,
  218                           dest_table_id=constants.ARP_DVR_MAC_TO_DST_MAC_VLAN)
  219 
  220     def remove_dvr_gateway_mac_arp_vlan(self, mac, port):
  221         self.uninstall_flows(table_id=constants.LOCAL_SWITCHING,
  222                              eth_dst=mac)
  223 
  224     def add_dvr_gateway_mac_arp_tun(self, mac, port):
  225         self.install_goto(table_id=constants.LOCAL_SWITCHING,
  226                           priority=5,
  227                           in_port=port,
  228                           eth_dst=mac,
  229                           dest_table_id=constants.ARP_DVR_MAC_TO_DST_MAC)
  230 
  231     def remove_dvr_gateway_mac_arp_tun(self, mac, port):
  232         self.uninstall_flows(table_id=constants.LOCAL_SWITCHING,
  233                              eth_dst=mac)
  234 
  235     @staticmethod
  236     def _arp_reply_match(ofp, ofpp, port):
  237         return ofpp.OFPMatch(in_port=port,
  238                              eth_type=ether_types.ETH_TYPE_ARP)
  239 
  240     @staticmethod
  241     def _icmpv6_reply_match(ofp, ofpp, port):
  242         return ofpp.OFPMatch(in_port=port,
  243                              eth_type=ether_types.ETH_TYPE_IPV6,
  244                              ip_proto=in_proto.IPPROTO_ICMPV6,
  245                              icmpv6_type=icmpv6.ND_NEIGHBOR_ADVERT)
  246 
  247     def install_icmpv6_na_spoofing_protection(self, port, ip_addresses):
  248         # Allow neighbor advertisements as long as they match addresses
  249         # that actually belong to the port.
  250         for ip in ip_addresses:
  251             masked_ip = self._cidr_to_os_ken(ip)
  252             self.install_goto(
  253                 table_id=constants.ARP_SPOOF_TABLE, priority=2,
  254                 eth_type=ether_types.ETH_TYPE_IPV6,
  255                 ip_proto=in_proto.IPPROTO_ICMPV6,
  256                 icmpv6_type=icmpv6.ND_NEIGHBOR_ADVERT,
  257                 ipv6_nd_target=masked_ip, in_port=port,
  258                 dest_table_id=constants.TRANSIENT_TABLE)
  259 
  260         # Now that the rules are ready, direct icmpv6 neighbor advertisement
  261         # traffic from the port into the anti-spoof table.
  262         (_dp, ofp, ofpp) = self._get_dp()
  263         match = self._icmpv6_reply_match(ofp, ofpp, port=port)
  264         self.install_goto(table_id=constants.LOCAL_SWITCHING,
  265                           priority=10,
  266                           match=match,
  267                           dest_table_id=constants.ARP_SPOOF_TABLE)
  268 
  269     def set_allowed_macs_for_port(self, port, mac_addresses=None,
  270                                   allow_all=False):
  271         if allow_all:
  272             self.uninstall_flows(table_id=constants.LOCAL_SWITCHING,
  273                                  in_port=port)
  274             self.uninstall_flows(table_id=constants.MAC_SPOOF_TABLE,
  275                                  in_port=port)
  276             return
  277         mac_addresses = mac_addresses or []
  278         for address in mac_addresses:
  279             self.install_goto(
  280                 table_id=constants.MAC_SPOOF_TABLE, priority=2,
  281                 eth_src=address, in_port=port,
  282                 dest_table_id=constants.TRANSIENT_TABLE)
  283         # normalize so we can see if macs are the same
  284         mac_addresses = {netaddr.EUI(mac) for mac in mac_addresses}
  285         flows = self.dump_flows(constants.MAC_SPOOF_TABLE)
  286         for flow in flows:
  287             matches = dict(flow.match.items())
  288             if matches.get('in_port') != port:
  289                 continue
  290             if not matches.get('eth_src'):
  291                 continue
  292             flow_mac = matches['eth_src']
  293             if netaddr.EUI(flow_mac) not in mac_addresses:
  294                 self.uninstall_flows(table_id=constants.MAC_SPOOF_TABLE,
  295                                      in_port=port, eth_src=flow_mac)
  296         self.install_goto(table_id=constants.LOCAL_SWITCHING,
  297                           priority=9, in_port=port,
  298                           dest_table_id=constants.MAC_SPOOF_TABLE)
  299 
  300     def install_arp_spoofing_protection(self, port, ip_addresses):
  301         # allow ARP replies as long as they match addresses that actually
  302         # belong to the port.
  303         for ip in ip_addresses:
  304             masked_ip = self._cidr_to_os_ken(ip)
  305             self.install_goto(table_id=constants.ARP_SPOOF_TABLE,
  306                               priority=2,
  307                               eth_type=ether_types.ETH_TYPE_ARP,
  308                               arp_spa=masked_ip,
  309                               in_port=port,
  310                               dest_table_id=constants.MAC_SPOOF_TABLE)
  311 
  312         # Now that the rules are ready, direct ARP traffic from the port into
  313         # the anti-spoof table.
  314         # This strategy fails gracefully because OVS versions that can't match
  315         # on ARP headers will just process traffic normally.
  316         (_dp, ofp, ofpp) = self._get_dp()
  317         match = self._arp_reply_match(ofp, ofpp, port=port)
  318         self.install_goto(table_id=constants.LOCAL_SWITCHING,
  319                           priority=10,
  320                           match=match,
  321                           dest_table_id=constants.ARP_SPOOF_TABLE)
  322 
  323     def delete_arp_spoofing_protection(self, port):
  324         (_dp, ofp, ofpp) = self._get_dp()
  325         match = self._arp_reply_match(ofp, ofpp, port=port)
  326         self.uninstall_flows(table_id=constants.LOCAL_SWITCHING,
  327                              match=match)
  328         match = self._icmpv6_reply_match(ofp, ofpp, port=port)
  329         self.uninstall_flows(table_id=constants.LOCAL_SWITCHING,
  330                              match=match)
  331         self.delete_arp_spoofing_allow_rules(port)
  332 
  333     def delete_arp_spoofing_allow_rules(self, port):
  334         self.uninstall_flows(table_id=constants.ARP_SPOOF_TABLE,
  335                              in_port=port)