Reintroduce "learn-action" firewall driver

This patch reintroduce the "learn-action" firewall driver
for OVS.

Change-Id: I821036ed31997a2fe44d39c55e7ccfe0cabdd22a
This commit is contained in:
Rodolfo Alonso Hernandez 2016-11-11 12:20:17 +00:00
parent 5086338a62
commit 26c06836bf
21 changed files with 3065 additions and 16 deletions

2
babel.cfg Normal file

@ -0,0 +1,2 @@
[python: **.py]

@ -168,13 +168,20 @@ OVS-DPDK you will need to create a flavor that requests hugepages.
Enable the OVS firewall
-----------------------
To enable the OVS firewall, you will need to modify(or add) the following
variable to local.conf:
To enable the OVS firewall integrated in Neutron, you will need to modify
(or add) the following variable to local.conf:
| [[post-config|/etc/neutron/plugins/ml2/ml2_conf.ini]]
| [securitygroup]
| firewall_driver = openvswitch
This is a stateful firewall and uses connection tracking to control the traffic
flows. In this repository also can be found a stateless firewall based in
OpenFlow 'learn action', which can be enabled by setting the following
variable:
| firewall_driver = networking_ovs_dpdk.agent.ovs_dpdk_firewall.OVSFirewallDriver
By default, the multicast support is enabled. The default aging time for the
IGMP subscriptions in the bridges is 3600 seconds. To configure the multicast
support, both variables could be setup in local.conf:

@ -194,16 +194,23 @@ OVS-DPDK you will need to create a flavor that requests hugepages.
Enable the OVS firewall
-----------------------
To enable the OVS firewall, you will need to modify (or add) the following
variable to local.conf:
To enable the OVS firewall integrated in Neutron, you will need to modify
(or add) the following variable to local.conf:
| [[post-config|/etc/neutron/plugins/ml2/ml2_conf.ini]]
| [securitygroup]
| firewall_driver = openvswitch
This is a stateful firewall and uses connection tracking to control the traffic
flows. In this repository also can be found a stateless firewall based in
OpenFlow 'learn action', which can be enabled by setting the following
variable:
| firewall_driver = networking_ovs_dpdk.agent.ovs_dpdk_firewall.OVSFirewallDriver
By default, the multicast support is enabled. The default aging time for the
IGMP subscriptions in the bridges is 3600 seconds. To configure the multicast
support, both variables can be setup in local.conf:
support, both variables could be setup in local.conf:
| [[local|localrc]]
| OVS_ENABLE_SG_FIREWALL_MULTICAST=[True/False]

@ -155,13 +155,20 @@ create a flavor that requests hugepages.
Enable the OVS firewall
-----------------------
To enable the OVS firewall, you will need to modify (or add) the following
variable to local.conf:
To enable the OVS firewall integrated in Neutron, you will need to modify
(or add) the following variable to local.conf:
| [[post-config|/etc/neutron/plugins/ml2/ml2_conf.ini]]
| [securitygroup]
| firewall_driver = openvswitch
This is a stateful firewall and uses connection tracking to control the traffic
flows. In this repository also can be found a stateless firewall based in
OpenFlow 'learn action', which can be enabled by setting the following
variable:
| firewall_driver = networking_ovs_dpdk.agent.ovs_dpdk_firewall.OVSFirewallDriver
By default, the multicast support is enabled. The default aging time for the
IGMP subscriptions in the bridges is 3600 seconds. To configure the multicast
support, both variables could be setup in local.conf:

@ -0,0 +1,19 @@
# -*- coding: utf-8 -*-
# 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.
import pbr.version
__version__ = pbr.version.VersionInfo(
'networking_ovs_dpdk').version_string()

@ -0,0 +1,990 @@
# Copyright 2011 VMware, Inc.
# 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.
import collections
import netaddr
import re
from neutron_lib import constants
from oslo_config import cfg
from oslo_log import log as logging
from networking_ovs_dpdk.common._i18n import _, _LW
from neutron.agent import firewall
from neutron.common import utils as neutron_utils
from neutron.plugins.ml2.drivers.openvswitch.agent.common import (constants
as ovs_constants)
from neutron.plugins.ml2.drivers.openvswitch.agent import \
ovs_agent_extension_api
LOG = logging.getLogger(__name__)
# OpenFlow Table IDs
OF_ZERO_TABLE = ovs_constants.LOCAL_SWITCHING
OF_SELECT_TABLE = ovs_constants.OVS_FIREWALL_TABLES[0]
OF_EGRESS_TABLE = ovs_constants.OVS_FIREWALL_TABLES[1]
OF_INGRESS_TABLE = ovs_constants.OVS_FIREWALL_TABLES[2]
OF_INGRESS_EXT_TABLE = ovs_constants.OVS_FIREWALL_TABLES[3]
# Openflow ZERO table priorities
OF_T0_ARP_INT_PRIO = 100
OF_T0_ARP_EXT_PRIO = 90
OF_T0_SELECT_TABLE_IN_PRIO = 50
OF_T0_SELECT_TABLE_EXT_PRIO = 40
OF_T0_BLOCK = 35
# Openflow SELECT table priorities
OF_SEL_SERVICES_INT_IGMP_PRIO = 200
OF_SEL_SERVICES_EXT_IGMP_PRIO = 190
OF_SEL_SERVICES_EXT_MULTICAST_PRIO = 180
OF_SEL_EGRESS_PRIO = 100
OF_SEL_INGRESS_PRIO = 100
OF_DROP_TRAFFIC_PRIO = 50
# Openflow EGRESS table priorities
OF_EGRESS_SERVICES_PRIO = 50
OF_EGRESS_ANTISPOOF_PRIO = 40
OF_EGRESS_PORT_RULE_PRIO = 30
OF_EGRESS_ALLOW_EGRESS_RULE_PRIO = 20
# Openflow INGRESS table priorities
OF_INGRESS_SERVICES_PRIO = 50
OF_INGRESS_ANTISPOOF_PRIO = 40
OF_INGRESS_PORT_RULE_PRIO = 30
OF_INGRESS_OUTBOUND_PRIO = 10
# Openflow INGRESS_EXT table priorities
OF_INGRESS_EXT_BLOCK_INT_MAC_PRIO = 100
OF_INGRESS_EXT_BLOCK_EXT_SOURCE_PRIO = 100
OF_INGRESS_EXT_BLOCK_MULTICAST_PRIO = 100
OF_INGRESS_EXT_ALLOW_EXT_TRAFFIC_PRIO = 50
# Openflow LEARN ACTIONS priorities
OF_LEARNED_HIGH_PRIO = 100
OF_LEARNED_LOW_PRIO = 90
INGRESS_DIRECTION = 'ingress'
EGRESS_DIRECTION = 'egress'
LEARN_IDLE_TIMEOUT = 30 # 30 seconds.
LEARN_HARD_TIMEOUT = 1800 # 30 minutes.
# Ethernet type.
IPv4 = "IPv4"
IPv6 = "IPv6"
# OpenFlow mnemonics.
OF_MNEMONICS = {
IPv6: {
"ip_dst": "ipv6_dst",
"ip_src": "ipv6_src",
"ip_proto": "ipv6",
},
IPv4: {
"ip_dst": "nw_dst",
"ip_src": "nw_src",
"ip_proto": "ip",
},
}
# Protocols.
IPV4_ROUTER_MESSAGES = [9, 10]
# Router Solicitation (133)
# Router Advertisement (134)
# Neighbor Solicitation (135)
# Neighbor Advertisement (136)
# Redirect (137)
ICMPV6_TYPE_RS = 133
ICMPV6_TYPE_RA = constants.ICMPV6_TYPE_RA or 134
ICMPv6_TYPE_NS = 135
ICMPV6_TYPE_NA = constants.ICMPV6_TYPE_NA or 136
ICMPV6_TYPE_RED = 137
IPV6_ND_MESSAGES = [ICMPV6_TYPE_RS, ICMPV6_TYPE_RA, ICMPv6_TYPE_NS,
ICMPV6_TYPE_NA, ICMPV6_TYPE_RED]
# Multicast Listener Query (130)
# Multicast Listener Report (131)
# Multicast Listener Done (132)
ICMPV6_TYPE_MLQ = 130
ICMPV6_TYPE_MLR = 131
ICMPV6_TYPE_MLD = 132
IPV6_MLD_MESSAGES = [ICMPV6_TYPE_MLQ, ICMPV6_TYPE_MLR, ICMPV6_TYPE_MLD]
IPv6_MULTICAST_PREFIX = "ff02::1:ff00:0/104"
DIRECTION_IP_PREFIX = {'ingress': 'source_ip_prefix',
'egress': 'dest_ip_prefix'}
ETH_PROTOCOL_TABLE = {IPv4: "0x0800",
IPv6: "0x86dd"}
IP_PROTOCOL_TABLE = {
constants.PROTO_NAME_TCP: constants.PROTO_NUM_TCP,
constants.PROTO_NAME_ICMP: constants.PROTO_NUM_ICMP,
constants.PROTO_NAME_IPV6_ICMP: constants.PROTO_NUM_IPV6_ICMP,
constants.PROTO_NAME_UDP: constants.PROTO_NUM_UDP,
"igmp": 2}
MULTICAST_MAC = "01:00:5e:00:00:00/01:00:5e:00:00:00"
ovs_opts = [
cfg.BoolOpt('enable_sg_firewall_multicast', default=False,
help=_("Allows multicast traffic coming into and going"
"outside OVS.")),
]
cfg.CONF.register_opts(ovs_opts, "OVS")
class OVSFirewallDriver(firewall.FirewallDriver):
"""Driver which enforces security groups through
Open vSwitch flows.
"""
def __init__(self, integration_bridge):
self._filtered_ports = {}
self._int_br = ovs_agent_extension_api.\
OVSCookieBridge(integration_bridge).deferred(full_ordered=True)
self._deferred = False
self._enable_multicast = \
cfg.CONF.OVS.enable_sg_firewall_multicast
# List of security group rules for ports residing on this host
self.sg_rules = {}
# List of security group member ips for ports residing on this
# host
self.sg_members = collections.defaultdict(
lambda: collections.defaultdict(list))
self.pre_sg_members = None
# Known ports managed.
self._filtered_in_ports = {}
@property
def ports(self):
return self._filtered_ports
def _vif_port_info(self, port_name):
"""Returns additional vif port info: internal vlan tag,
interfaces, segmentation id, net id, network type, physical
network.
"""
port_info = {'name': port_name}
other_config = self._int_br.br.db_get_val("Port", port_name,
"other_config")
# Default fields (also other fields could be present):
# net_uuid="e00e6a6a-c88a-4724-80a7-6368a94241d9"
# network_type=vlan
# physical_network=default
# segmentation_id="1402"
port_info.update(other_config)
port_info['tag'] = self._int_br.br.db_get_val("Port", port_name, "tag")
port_info['interfaces'] = \
self._int_br.br.db_get_val('Port', port_name, 'interfaces')
return port_info
def update_security_group_rules(self, sg_id, sg_rules):
LOG.debug("Update rules of security group (%s)", sg_id)
self.sg_rules[sg_id] = sg_rules
def update_security_group_members(self, sg_id, sg_members):
LOG.debug("Update members of security group (%s)", sg_id)
self.sg_members[sg_id] = collections.defaultdict(list, sg_members)
def security_group_updated(self, action_type, sec_group_ids,
device_ids=[]):
pass
def _expand_sg_rule_with_remote_ips(self, rule, port):
"""Expand a remote group rule to rule per remote group IP."""
remote_group_id = rule.get('remote_group_id')
if remote_group_id:
ethertype = rule['ethertype']
port_ips = port.get('fixed_ips', [])
for ip in self.sg_members[remote_group_id][ethertype]:
if ip not in port_ips:
ip_rule = rule.copy()
direction_ip_prefix = (
DIRECTION_IP_PREFIX[rule['direction']])
ip_prefix = str(netaddr.IPNetwork(ip).cidr)
ip_rule[direction_ip_prefix] = ip_prefix
yield ip_rule
else:
yield rule
def _write_proto(self, eth_type, protocol=None):
if protocol == "arp":
return "arp"
proto_str = "eth_type=%s" % ETH_PROTOCOL_TABLE[eth_type]
if protocol in IP_PROTOCOL_TABLE.keys():
proto_num = IP_PROTOCOL_TABLE[protocol]
if protocol == constants.PROTO_NAME_ICMP and \
eth_type == IPv6:
proto_num = constants.PROTO_NUM_IPV6_ICMP
proto_str += ",ip_proto=%s" % proto_num
return proto_str
def apply_port_filter(self, port):
pass
def _add_flow(self, **kwargs):
LOG.debug("OFW add rule: %s", kwargs)
if self._deferred:
self._int_br.add_flow(**kwargs)
else:
self._int_br.br.add_flow(**kwargs)
def _del_flows(self, **kwargs):
LOG.debug("OFW del rule: %s", kwargs)
if self._deferred:
self._int_br.delete_flows(**kwargs)
else:
self._int_br.br.delete_flows(**kwargs)
def _add_base_flows(self, port, vif_port):
"""Set base flows for every port."""
self._add_zero_table(port, vif_port)
self._add_selection_table(port, vif_port)
self._add_selection_table_services(port, vif_port)
self._add_selection_table_port_block(port)
self._add_egress_antispoof(port, vif_port)
self._add_egress_services(port, vif_port)
self._add_ingress_allow_outbound_traffic(port)
self._add_ingress_services(port, vif_port)
def _add_zero_table(self, port, vif_port):
"""Set arp flows. The rest of the traffic is sent to
SELECT_TABLE.
"""
segmentation_id = port['vinfo']['segmentation_id']
# ARP and ND traffic to be delivered to an internal port.
for fixed_ip in port['fixed_ips']:
# IPv4, ARP.
if self._ip_version_from_address(fixed_ip) == IPv4:
self._add_flow(priority=OF_T0_ARP_INT_PRIO,
table=OF_ZERO_TABLE,
proto=self._write_proto(IPv4, "arp"),
dl_vlan=segmentation_id,
nw_dst=fixed_ip,
actions='strip_vlan,output:%s'
% vif_port.ofport)
# IPv6, NS and NA.
if self._ip_version_from_address(fixed_ip) == IPv6:
for icmpv6_type in IPV6_ND_MESSAGES:
self._add_flow(priority=OF_T0_ARP_INT_PRIO,
table=OF_ZERO_TABLE,
proto=self._write_proto(IPv6,
constants.PROTO_NAME_IPV6_ICMP),
dl_vlan=segmentation_id,
icmpv6_type=icmpv6_type,
ipv6_dst=fixed_ip,
actions='strip_vlan,output:%s'
% vif_port.ofport)
# Internal port ARP messages to be delivered out of br-int.
self._add_flow(priority=OF_T0_ARP_EXT_PRIO,
table=OF_ZERO_TABLE,
proto='arp',
actions='normal')
# Internal port NS and NA messages to be delivered out of br-int.
for icmpv6_type in IPV6_ND_MESSAGES:
self._add_flow(priority=OF_T0_ARP_EXT_PRIO,
table=OF_ZERO_TABLE,
proto=self._write_proto(IPv6,
constants.PROTO_NAME_IPV6_ICMP),
icmpv6_type=icmpv6_type,
actions='normal')
# Select traffic.
# Incoming internal traffic: check mac, mod vlan.
self._add_flow(priority=OF_T0_SELECT_TABLE_IN_PRIO,
table=OF_ZERO_TABLE,
dl_src=port['mac_address'],
actions="mod_vlan_vid:%s,"
"load:0->NXM_NX_REG0[0..11],"
"load:0->NXM_NX_REG1[0..11],"
"resubmit(,%s)"
% (port['vinfo']['tag'], OF_SELECT_TABLE))
# Incoming external traffic: check external vlan tag, mod vlan.
self._add_flow(priority=OF_T0_SELECT_TABLE_EXT_PRIO,
table=OF_ZERO_TABLE,
dl_vlan=segmentation_id,
actions="mod_vlan_vid:%s,"
"load:%s->NXM_NX_REG0[0..11],"
"load:0->NXM_NX_REG1[0..11],"
"resubmit(,%s)"
% (port['vinfo']['tag'],
port['vinfo']['tag'],
OF_SELECT_TABLE))
# Block rest of traffic.
self._add_flow(priority=OF_T0_BLOCK,
table=OF_ZERO_TABLE,
actions="drop")
def _add_selection_table(self, port, vif_port):
"""Set traffic selection basic rules.
Allows all internal traffic matching mac/ip to egress table.
Allows all extenal traffic matching dst mac to ingress table.
"""
for fixed_ip in port['fixed_ips']:
if self._ip_version_from_address(fixed_ip) == IPv4:
self._add_flow(priority=OF_SEL_EGRESS_PRIO,
table=OF_SELECT_TABLE,
in_port=vif_port.ofport,
proto=self._write_proto(IPv4),
dl_vlan=port['vinfo']['tag'],
dl_src=port['mac_address'],
nw_src=fixed_ip,
actions='resubmit(,%s)' %
(OF_EGRESS_TABLE))
if self._ip_version_from_address(fixed_ip) == IPv6:
self._add_flow(priority=OF_SEL_EGRESS_PRIO,
table=OF_SELECT_TABLE,
in_port=vif_port.ofport,
proto=self._write_proto(IPv6),
dl_vlan=port['vinfo']['tag'],
dl_src=port['mac_address'],
ipv6_src=fixed_ip,
actions='resubmit(,%s)' %
(OF_EGRESS_TABLE))
# External traffic to ingress processing table
self._add_flow(
priority=OF_SEL_INGRESS_PRIO,
table=OF_SELECT_TABLE,
dl_vlan=port['vinfo']['tag'],
dl_dst=port['mac_address'],
actions='resubmit(,%d)'
% (OF_INGRESS_TABLE))
def _add_selection_table_services(self, port, vif_port):
"""Selection table services:
Allows DHCP traffic to request an IP address
IGMP snooping/MLD traffic and multicast traffic.
"""
# Allow DHCP requests from invalid address
self._add_flow(priority=OF_SEL_EGRESS_PRIO,
table=OF_SELECT_TABLE,
in_port=vif_port.ofport,
proto=self._write_proto(IPv4),
dl_vlan=port['vinfo']['tag'],
dl_src=port['mac_address'],
nw_src='0.0.0.0',
actions="resubmit(,%s)"
% (OF_EGRESS_TABLE))
if self._enable_multicast:
for fixed_ip in port['fixed_ips']:
if self._ip_version_from_address(fixed_ip) == IPv4:
# Add internal IGMP snooping traffic support. This traffic
# is sent to the bridge using the 'normal' action.
self._add_flow(priority=OF_SEL_SERVICES_INT_IGMP_PRIO,
table=OF_SELECT_TABLE,
in_port=vif_port.ofport,
proto=self._write_proto(IPv4, "igmp"),
dl_vlan=port['vinfo']['tag'],
dl_src=port['mac_address'],
dl_dst=MULTICAST_MAC,
nw_src=fixed_ip,
nw_dst=str(netaddr.ip.IPV4_MULTICAST.cidr),
actions='strip_vlan,normal')
if self._ip_version_from_address(fixed_ip) == IPv6:
# Add internal MLD snooping traffic support.
self._add_flow(priority=OF_SEL_SERVICES_INT_IGMP_PRIO,
table=OF_SELECT_TABLE,
in_port=vif_port.ofport,
proto=self._write_proto(IPv6,
constants.PROTO_NAME_IPV6_ICMP),
dl_vlan=port['vinfo']['tag'],
dl_src=port['mac_address'],
dl_dst=MULTICAST_MAC,
ipv6_src=fixed_ip,
ipv6_dst=str(netaddr.ip.IPV6_MULTICAST.cidr),
actions='strip_vlan,normal')
# Add external IGMP snooping traffic support.
self._add_flow(priority=OF_SEL_SERVICES_EXT_IGMP_PRIO,
table=OF_SELECT_TABLE,
proto=self._write_proto(IPv4, "igmp"),
dl_vlan=port['vinfo']['tag'],
dl_dst=MULTICAST_MAC,
nw_dst=str(netaddr.ip.IPV4_MULTICAST.cidr),
actions='normal')
# Add external MLD snooping traffic support.
self._add_flow(priority=OF_SEL_SERVICES_EXT_IGMP_PRIO,
table=OF_SELECT_TABLE,
proto=self._write_proto(IPv6,
constants.PROTO_NAME_IPV6_ICMP),
dl_vlan=port['vinfo']['tag'],
dl_dst=MULTICAST_MAC,
ipv6_dst=str(netaddr.ip.IPV6_MULTICAST.cidr),
actions='normal')
# Allow external multicast traffic to skip the internal MAC filter.
# This traffic is sent to the ingress table. Only TCP and UDP.
# REG0 in external traffic must match the internal VLAN tag.
for proto in [constants.PROTO_NAME_TCP,
constants.PROTO_NAME_UDP]:
self._add_flow(priority=OF_SEL_SERVICES_EXT_MULTICAST_PRIO,
table=OF_SELECT_TABLE,
reg0="%s" % port['vinfo']['tag'],
proto=self._write_proto(IPv4, proto),
dl_vlan=port['vinfo']['tag'],
dl_dst=MULTICAST_MAC,
nw_dst=str(netaddr.ip.IPV4_MULTICAST.cidr),
actions='load:1->NXM_NX_REG1[0..11],'
'resubmit(,%s)' % OF_INGRESS_TABLE)
self._add_flow(priority=OF_SEL_SERVICES_EXT_MULTICAST_PRIO,
table=OF_SELECT_TABLE,
reg0="%s" % port['vinfo']['tag'],
proto=self._write_proto(IPv6, proto),
dl_vlan=port['vinfo']['tag'],
dl_dst=MULTICAST_MAC,
ipv6_dst=str(netaddr.ip.IPV6_MULTICAST.cidr),
actions='load:1->NXM_NX_REG1[0..11],'
'resubmit(,%s)' % OF_INGRESS_TABLE)
def _add_selection_table_port_block(self, port):
"""Block rest of the traffic.
Drop all traffic not generated by or to a VM.
"""
for eth_type in ETH_PROTOCOL_TABLE.keys():
self._add_flow(
priority=OF_DROP_TRAFFIC_PRIO,
table=OF_SELECT_TABLE,
proto=self._write_proto(eth_type),
dl_vlan=port['vinfo']['tag'],
actions='drop')
def _add_egress_antispoof(self, port, vif_port):
"""Set antispoof rules.
Antispoof rules take precedence to any rules set by
the tenant in the security group.
"""
# No DHCPv4 server out from port.
self._add_flow(priority=OF_EGRESS_ANTISPOOF_PRIO,
table=OF_EGRESS_TABLE,
in_port=vif_port.ofport,
proto=self._write_proto(IPv4,
constants.PROTO_NAME_UDP),
dl_vlan=port['vinfo']['tag'],
udp_src=67,
udp_dst=68,
actions='drop')
# No DHCPv6 server out from port.
self._add_flow(priority=OF_EGRESS_ANTISPOOF_PRIO,
table=OF_EGRESS_TABLE,
in_port=vif_port.ofport,
proto=self._write_proto(IPv6,
constants.PROTO_NAME_UDP),
dl_vlan=port['vinfo']['tag'],
udp_src=547,
udp_dst=546,
actions='drop')
def _add_egress_services(self, port, vif_port):
"""Add service rules.
Allows traffic to DHCPv4/v6 servers.
Allows icmp traffic.
"""
# DHCP & DHCPv6.
for eth_type, udp_src, udp_dst in [(IPv4, 68, 67), (IPv6, 546, 547)]:
self._add_flow(
table=OF_EGRESS_TABLE,
priority=OF_EGRESS_SERVICES_PRIO,
dl_vlan=port['vinfo']['tag'],
dl_src=port['mac_address'],
in_port=vif_port.ofport,
proto=self._write_proto(eth_type, constants.PROTO_NAME_UDP),
udp_src=udp_src,
udp_dst=udp_dst,
actions='resubmit(,%s)' % OF_INGRESS_TABLE)
# Allows ICMP router advertisement / router selection.
for type in IPV4_ROUTER_MESSAGES:
self._add_flow(
table=OF_EGRESS_TABLE,
priority=OF_EGRESS_SERVICES_PRIO,
dl_vlan=port['vinfo']['tag'],
dl_src=port['mac_address'],
proto=self._write_proto(IPv4,
constants.PROTO_NAME_ICMP),
icmp_type=type,
actions='resubmit(,%s)' % OF_INGRESS_TABLE)
# Allows IPv6 MLD messages.
for icmpv6_type in IPV6_MLD_MESSAGES:
self._add_flow(
table=OF_EGRESS_TABLE,
priority=OF_EGRESS_SERVICES_PRIO,
dl_vlan=port['vinfo']['tag'],
dl_src=port['mac_address'],
in_port=vif_port.ofport,
proto=self._write_proto(IPv6,
constants.PROTO_NAME_IPV6_ICMP),
icmpv6_type=icmpv6_type,
actions='resubmit(,%s)' % OF_INGRESS_TABLE)
def _add_ingress_allow_outbound_traffic(self, port):
"""Allows ingress outbound traffic.
By default, all ingress traffic not matching any internal port
in the bridge is sent to the external ports.
"""
# If the traffic do not match any mac address inside the bridge,
# send the traffic to the INGRESS_EXT table.
self._add_flow(
table=OF_INGRESS_TABLE,
priority=OF_INGRESS_OUTBOUND_PRIO,
dl_vlan=port['vinfo']['tag'],
actions='resubmit(,%s)' % OF_INGRESS_EXT_TABLE)
# Blocks all traffic in this table if it's for an internal
# port (mac address).
self._add_flow(
table=OF_INGRESS_EXT_TABLE,
priority=OF_INGRESS_EXT_BLOCK_INT_MAC_PRIO,
dl_vlan=port['vinfo']['tag'],
dl_dst=port['mac_address'],
actions='drop')
# Blocks all traffic in this table if was sent by an external
# source. Prevents from sending back the traffic.
self._add_flow(
table=OF_INGRESS_EXT_TABLE,
priority=OF_INGRESS_EXT_BLOCK_EXT_SOURCE_PRIO,
dl_vlan=port['vinfo']['tag'],
reg0="%s" % port['vinfo']['tag'],
actions='drop')
# Use normal action to send the traffic outside the integration
# bridge.
self._add_flow(
table=OF_INGRESS_EXT_TABLE,
priority=OF_INGRESS_EXT_ALLOW_EXT_TRAFFIC_PRIO,
dl_vlan=port['vinfo']['tag'],
actions='strip_vlan,normal')
def _add_ingress_services(self, port, vif_port):
"""Add service rules.dl_vlan=port['vinfo']['tag'],
Allows traffic to DHCPv4/v6 servers
Allows specific icmp traffic (RA messages).
"""
# DHCP & DHCPv6.
for eth_type, udp_src, udp_dst in [(IPv4, 67, 68), (IPv6, 547, 546)]:
self._add_flow(
table=OF_INGRESS_TABLE,
priority=OF_INGRESS_SERVICES_PRIO,
proto=self._write_proto(eth_type, constants.PROTO_NAME_UDP),
dl_vlan=port['vinfo']['tag'],
dl_dst=port['mac_address'],
udp_src=udp_src,
udp_dst=udp_dst,
actions=self._get_ingress_actions(vif_port))
# ICMP RA messages.
for type in [9, 10]:
self._add_flow(
table=OF_INGRESS_TABLE,
priority=OF_INGRESS_SERVICES_PRIO,
proto=self._write_proto(IPv4,
constants.PROTO_NAME_ICMP),
dl_vlan=port['vinfo']['tag'],
dl_dst=port['mac_address'],
icmp_type=type,
actions=self._get_ingress_actions(vif_port))
# ICMP6 MLD messages.
for icmpv6_type in IPV6_MLD_MESSAGES:
self._add_flow(
table=OF_INGRESS_TABLE,
priority=OF_INGRESS_SERVICES_PRIO,
proto=self._write_proto(IPv6,
constants.PROTO_NAME_IPV6_ICMP),
dl_vlan=port['vinfo']['tag'],
dl_dst=port['mac_address'],
icmpv6_type=icmpv6_type,
actions=self._get_ingress_actions(vif_port))
if self._enable_multicast:
# Block the multicast traffic in the external ingress table.
self._add_flow(
table=OF_INGRESS_EXT_TABLE,
priority=OF_INGRESS_EXT_BLOCK_MULTICAST_PRIO,
dl_vlan=port['vinfo']['tag'],
reg1="1",
actions='drop')
def _get_ingress_actions(self, vif_port):
return 'strip_vlan,output:%(oport)s' % \
{'oport': vif_port.ofport}
def _remove_flows(self, port):
# Remove manual and "learn action" rules.
self._del_flows(dl_src=port["mac_address"])
self._del_flows(dl_dst=port["mac_address"])
# Remove antispoof rules.
if self._filtered_in_ports.get(port['device']):
self._del_flows(in_port=self._filtered_in_ports.get(
port['device']))
# Remove ARP rules.
for ipv4 in [ip for ip in port['fixed_ips'] if
self._ip_version_from_address(ip) == IPv4]:
self._del_flows(table=OF_ZERO_TABLE, nw_dst=ipv4,
proto=self._write_proto(IPv4, "arp"))
# Remove ND rules.
for ipv6 in [ip for ip in port['fixed_ips'] if
self._ip_version_from_address(ip) == IPv6]:
self._del_flows(table=OF_ZERO_TABLE, ipv6_dst=ipv6,
proto=self._write_proto(IPv6, constants.PROTO_NAME_IPV6_ICMP))
def _write_multicast_flow(self, flow, direction, port, port_match,
priority, ip_version):
"""Write a flow for the manual rule, allowing multicast traffic.
"""
# Multicast ingress traffic.
# Check the traffic protocol: only tcp or udp. Also check the
# multicast destination MAC and IP.
if self._enable_multicast \
and direction == INGRESS_DIRECTION \
and flow['proto'] in [constants.PROTO_NAME_TCP,
constants.PROTO_NAME_UDP]:
hp_flow = dict.copy(flow)
ip_dst = str(netaddr.ip.IPV6_MULTICAST.cidr) \
if ip_version == IPv6 \
else str(netaddr.ip.IPV4_MULTICAST.cidr)
hp_flow[OF_MNEMONICS[ip_version]['ip_dst']] = ip_dst
hp_flow['dl_vlan'] = port['vinfo']['tag']
hp_flow['priority'] = priority
hp_flow['dl_dst'] = MULTICAST_MAC
hp_flow['table'] = OF_INGRESS_TABLE
hp_flow['actions'] = \
"move:NXM_NX_REG0[0..11]->NXM_OF_VLAN_TCI[0..11],normal"
self._write_flows_per_port_match(hp_flow, port_match)
def _get_learn_action_rule(self, direction, priority,
port_range_min, port_range_max,
eth_type, ip_proto, vif_port):
# Write protocol string.
proto_str = self._write_proto(eth_type, ip_proto)
port_dst_str = ""
port_src_str = ""
if ip_proto in [constants.PROTO_NAME_TCP,
constants.PROTO_NAME_UDP]:
# Known L4 protocols with configurable ports.
port_dst_str = \
"NXM_OF_%(ip_proto)s_DST[]=NXM_OF_%(ip_proto)s_SRC[]," \
% {'ip_proto': ip_proto.upper()}
port_src_str = \
"NXM_OF_%(ip_proto)s_SRC[]=NXM_OF_%(ip_proto)s_DST[]," \
% {'ip_proto': ip_proto.upper()}
# Setup ICMPv4/v6 type and code.
icmp_type = ""
icmp_code = ""
if ip_proto == constants.PROTO_NAME_ICMP:
if port_range_min == 8:
icmp_type = "icmp_type=%s," % 0
elif port_range_min == 13:
icmp_type = "icmp_type=%s," % 14
elif port_range_min == 15:
icmp_type = "icmp_type=%s," % 16
elif port_range_min == 17:
icmp_type = "icmp_type=%s," % 18
elif port_range_min:
icmp_type = "icmp_type=%s," % port_range_min
if port_range_max:
icmp_code = "icmp_code=%s," % port_range_max
elif ip_proto == constants.PROTO_NAME_IPV6_ICMP:
if port_range_min:
icmp_type = "icmpv6_type=%s," % port_range_min
if port_range_max:
icmp_code = "icmpv6_code=%s," % port_range_max
# Source and destination IPs.
if eth_type == IPv4:
ip_dst = "NXM_OF_IP_DST[]=NXM_OF_IP_SRC[],"
ip_src = "NXM_OF_IP_SRC[]=NXM_OF_IP_DST[],"
else:
ip_dst = "NXM_NX_IPV6_DST[]=NXM_NX_IPV6_SRC[],"
ip_src = "NXM_NX_IPV6_SRC[]=NXM_NX_IPV6_DST[],"
# Learn action store table:
if direction == INGRESS_DIRECTION:
learn_action_table = OF_EGRESS_TABLE
elif direction == EGRESS_DIRECTION:
learn_action_table = OF_INGRESS_TABLE
learn_actions = "learn(table=%(table)s," \
"priority=%(priority)s," \
"idle_timeout=%(idle_timeout)s," \
"hard_timeout=%(hard_timeout)s," \
"%(proto)s," \
"NXM_OF_ETH_SRC[]=NXM_OF_ETH_DST[]," \
"NXM_OF_ETH_DST[]=NXM_OF_ETH_SRC[]," \
"%(ip_src)s" \
"%(ip_dst)s" \
"%(port_dst)s" \
"%(port_src)s" \
"%(icmp_type)s" \
"%(icmp_code)s" \
"NXM_OF_VLAN_TCI[0..11]," \
"load:NXM_NX_REG0[0..11]->NXM_OF_VLAN_TCI[0..11]," \
"output:NXM_OF_IN_PORT[])" % \
{'table': learn_action_table,
'priority': priority,
'idle_timeout': LEARN_IDLE_TIMEOUT,
'hard_timeout': LEARN_HARD_TIMEOUT,
'proto': proto_str,
'ip_src': ip_src,
'ip_dst': ip_dst,
'port_dst': port_dst_str,
'port_src': port_src_str,
'icmp_type': icmp_type,
'icmp_code': icmp_code}
if direction == EGRESS_DIRECTION:
return "%(learn_actions)s," \
"resubmit(,%(table)s)" % \
{'learn_actions': learn_actions,
'table': OF_INGRESS_TABLE}
elif direction == INGRESS_DIRECTION:
return "%(learn_actions)s," \
"strip_vlan,output:%(ofport)s" % \
{'learn_actions': learn_actions,
'ofport': vif_port.ofport}
def _select_sg_rules_for_port(self, port):
"""Select rules from the security groups the port is member of."""
port_sg_ids = port.get('security_groups', [])
port_rules = []
for sg_id in port_sg_ids:
for rule in self.sg_rules.get(sg_id, []):
port_rules.extend(
self._expand_sg_rule_with_remote_ips(rule, port))
return port_rules
def _write_flows_per_port_match(self, flow, port_match):
if port_match == "" or isinstance(flow[port_match], int):
self._add_flow(**flow)
elif isinstance(flow[port_match], list):
for portm in flow[port_match]:
hp_flow = dict.copy(flow)
hp_flow[port_match] = portm
self._add_flow(**hp_flow)
def _write_flows_per_ip(self, flow, rule, port, port_match, ip_proto):
"""Write the needed flows per each IP in the port."""
vif_port = self._int_br.br.get_vif_port_by_id(port['device'])
if not vif_port:
LOG.warning(_LW("Port %(port_id)s not present in bridge. Skip "
"applying rules for this port"),
{'port_id': port})
return
# Write a rule(s) per ip.
for fixed_ip in port['fixed_ips']:
# Check if the rule and the IP address have the same version.
if rule['ethertype'] != \
self._ip_version_from_address(fixed_ip):
continue
if rule['direction'] == EGRESS_DIRECTION:
flow[OF_MNEMONICS[rule['ethertype']]['ip_src']] = fixed_ip
elif rule['direction'] == INGRESS_DIRECTION:
flow[OF_MNEMONICS[rule['ethertype']]['ip_dst']] = fixed_ip
# Write learn actions.
# Default protocol: "ip". Create high priority rules
# for TCP and UDP protocols.
if not ip_proto:
for proto in [constants.PROTO_NAME_TCP,
constants.PROTO_NAME_UDP]:
hp_flow = dict.copy(flow)
hp_flow['proto'] = self._write_proto(rule['ethertype'],
proto)
hp_flow['actions'] = self._get_learn_action_rule(
rule['direction'],
OF_LEARNED_HIGH_PRIO,
"",
"",
rule['ethertype'],
proto,
vif_port)
self._write_flows_per_port_match(hp_flow, port_match)
# Write normal "learn action" for every flow.
flow['actions'] = self._get_learn_action_rule(
rule['direction'],
OF_LEARNED_LOW_PRIO,
rule.get('port_range_min'),
rule.get('port_range_max'),
rule['ethertype'],
rule.get('protocol'),
vif_port)
self._write_flows_per_port_match(flow, port_match)
# Write multicast rule.
self._write_multicast_flow(flow, rule['direction'], port,
port_match, OF_LEARNED_LOW_PRIO,
rule['ethertype'])
def _add_rules_flows(self, port):
rules = self._select_sg_rules_for_port(port)
for rule in rules:
ethertype = rule['ethertype']
direction = rule['direction']
protocol = rule.get('protocol')
port_range_min = rule.get('port_range_min')
port_range_max = rule.get('port_range_max')
source_ip_prefix = rule.get('source_ip_prefix')
dest_ip_prefix = rule.get('dest_ip_prefix')
flow = {}
# Direcction.
if direction == EGRESS_DIRECTION:
flow['priority'] = OF_EGRESS_PORT_RULE_PRIO
flow['table'] = OF_EGRESS_TABLE
flow["dl_src"] = port["mac_address"]
elif direction == INGRESS_DIRECTION:
flow['priority'] = OF_INGRESS_PORT_RULE_PRIO
flow['table'] = OF_INGRESS_TABLE
flow["dl_dst"] = port["mac_address"]
# Protocol.
flow['proto'] = self._write_proto(ethertype, protocol)
# Port range.
port_match = ""
if (port_range_min and port_range_max and
protocol in [constants.PROTO_NAME_TCP,
constants.PROTO_NAME_UDP]):
port_match = "%s_dst" % protocol
if port_range_max > port_range_min:
flow[port_match] = neutron_utils.port_rule_masking(
port_range_min,
port_range_max)
else:
flow[port_match] = int(port_range_min)
# Destination and source address.
if dest_ip_prefix and dest_ip_prefix != "0.0.0.0/0":
flow[OF_MNEMONICS[ethertype]["ip_dst"]] = dest_ip_prefix
if source_ip_prefix and source_ip_prefix != "0.0.0.0/0":
flow[OF_MNEMONICS[ethertype]["ip_src"]] = source_ip_prefix
# Write flow.
self._write_flows_per_ip(flow, rule, port, port_match, protocol)
def _apply_flows(self):
self._int_br.apply_flows()
def prepare_port_filter(self, port):
LOG.debug("OFW Preparing device (%s) filter: %s", port['device'],
port)
vif_port = self._int_br.br.get_vif_port_by_id(port['device'])
if not vif_port:
LOG.warning(_LW("Port %(port_id)s not present in bridge. Skip"
"applying rules for this port"),
{'port_id': port})
return
port['vinfo'] = self._vif_port_info(vif_port.port_name)
self._remove_flows(port)
self._filtered_ports[port['device']] = port
self._filtered_in_ports[port['device']] = vif_port.ofport
self._add_base_flows(port, vif_port)
self._add_rules_flows(port)
def update_port_filter(self, port):
LOG.debug("OFW Updating device (%s) filter: %s", port['device'],
port)
if port['device'] not in self._filtered_ports:
LOG.info(_('Attempted to update port filter which is not '
'filtered %s'), port['device'])
return
old_port = self._filtered_ports.get(port['device'])
vif_port = self._int_br.br.get_vif_port_by_id(port['device'])
if not vif_port:
LOG.warning(_LW("Port %(port_id)s not present in bridge. Skip"
"applying rules for this port"),
{'port_id': port})
return
port['vinfo'] = self._vif_port_info(vif_port.port_name)
self._remove_flows(old_port)
self._filtered_ports[port['device']] = port
self._filtered_in_ports[port['device']] = vif_port.ofport
self._add_base_flows(port, vif_port)
self._add_rules_flows(port)
def remove_port_filter(self, port):
LOG.debug("OFW Removing device (%s) filter: %s", port['device'],
port)
if not self._filtered_ports.get(port['device']):
LOG.info(_('Attempted to remove port filter which is not '
'filtered %r'), port)
return
self._remove_flows(port)
self._filtered_ports.pop(port['device'])
self._filtered_in_ports.pop(port['device'])
def filter_defer_apply_on(self):
LOG.debug("OFW defer_apply_on")
self._deferred = True
def filter_defer_apply_off(self):
LOG.debug("OFW defer_apply_off")
if self._deferred:
self._apply_flows()
self._deferred = False
@staticmethod
def _ip_version_from_address(ip_string):
ipv4_pattern = \
"^(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}" \
"(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$"
ipv6_pattern = "^(?:[0-9a-fA-F]{1,4}:){7}[0-9a-fA-F]{1,4}$"
ipv6_pattern_hexcompressed = \
"^((?:[0-9A-Fa-f]{1,4}(?::[0-9A-Fa-f]" \
"{1,4})*)?)::((?:[0-9A-Fa-f]{1,4}(?::[0-9A-Fa-f]{1,4})*)?)$"
ipv6_pattern_hex4dec = \
"^((?:[0-9A-Fa-f]{1,4}:){6,6})(25[0-5]|2[0-4]" \
"\d|[0-1]?\d?\d)(\.(25[0-5]|2[0-4]\d|[0-1]?\d?\d)){3}\z"
ipv6_pattern_hex4deccompressed = \
"^((?:[0-9A-Fa-f]{1,4}(?::[0-9A-Fa-f]" \
"{1,4})*)?) ::((?:[0-9A-Fa-f]{1,4}:)*)(25[0-5]|2[0-4]" \
"\d|[0-1]?\d?\d)(\.(25[0-5]|2[0-4]\d|[0-1]?\d?\d)){3}$"
if re.search(ipv4_pattern, ip_string):
return IPv4
if re.search(ipv6_pattern, ip_string) or \
re.search(ipv6_pattern_hexcompressed, ip_string) or \
re.search(ipv6_pattern_hex4dec, ip_string) or \
re.search(ipv6_pattern_hex4deccompressed, ip_string):
return IPv6
raise ValueError(_('Illegal IP string address'))

@ -0,0 +1,47 @@
# 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.
#
# derived from:
# http://docs.openstack.org/developer/oslo.i18n/usage.html
import oslo_i18n
DOMAIN = "networking-ovs-dpdk"
_translators = oslo_i18n.TranslatorFactory(domain=DOMAIN)
# The primary translation function using the well-known name "_"
_ = _translators.primary
# The contextual translation function using the name "_C"
# requires oslo.i18n >=2.1.0
_C = _translators.contextual_form
# The plural translation function using the name "_P"
# requires oslo.i18n >=2.1.0
_P = _translators.plural_form
# Translators for log levels.
#
# The abbreviated names are meant to reflect the usual use of a short
# name like '_'. The "L" is for "log" and the other letter comes from
# the level.
_LI = _translators.log_info
_LW = _translators.log_warning
_LE = _translators.log_error
_LC = _translators.log_critical
def get_available_languages():
return oslo_i18n.get_available_languages(DOMAIN)

@ -0,0 +1,105 @@
# Copyright 2012 Red Hat, Inc.
#
# 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_config import cfg
from networking_ovs_dpdk.common import constants
from neutron.agent.common import config
from neutron.plugins.common import constants as p_const
DEFAULT_BRIDGE_MAPPINGS = []
DEFAULT_VLAN_RANGES = []
DEFAULT_TUNNEL_RANGES = []
DEFAULT_TUNNEL_TYPES = []
ovs_opts = [
cfg.StrOpt('integration_bridge', default='br-int',
help=_("Integration bridge to use.")),
cfg.StrOpt('tunnel_bridge', default='br-tun',
help=_("Tunnel bridge to use.")),
cfg.StrOpt('int_peer_patch_port', default='patch-tun',
help=_("Peer patch port in integration bridge for tunnel "
"bridge.")),
cfg.StrOpt('tun_peer_patch_port', default='patch-int',
help=_("Peer patch port in tunnel bridge for integration "
"bridge.")),
cfg.IPOpt('local_ip', version=4,
help=_("Local IP address of tunnel endpoint.")),
cfg.ListOpt('bridge_mappings',
default=DEFAULT_BRIDGE_MAPPINGS,
help=_("List of <physical_network>:<bridge>. "
"Deprecated for ofagent.")),
cfg.BoolOpt('use_veth_interconnection', default=False,
help=_("Use veths instead of patch ports to interconnect the "
"integration bridge to physical bridges.")),
cfg.StrOpt('of_interface', default='ovsdpdk-ofctl',
choices=['ovs-ofctl', 'ovsdpdk-ofctl'],
help=_("OpenFlow interface to use.")),
]
agent_opts = [
cfg.IntOpt('polling_interval', default=2,
help=_("The number of seconds the agent will wait between "
"polling for local device changes.")),
cfg.BoolOpt('minimize_polling',
default=True,
help=_("Minimize polling by monitoring ovsdb for interface "
"changes.")),
cfg.IntOpt('ovsdb_monitor_respawn_interval',
default=constants.DEFAULT_OVSDBMON_RESPAWN,
help=_("The number of seconds to wait before respawning the "
"ovsdb monitor after losing communication with it.")),
cfg.ListOpt('tunnel_types', default=DEFAULT_TUNNEL_TYPES,
help=_("Network types supported by the agent "
"(gre and/or vxlan).")),
cfg.IntOpt('vxlan_udp_port', default=p_const.VXLAN_UDP_PORT,
help=_("The UDP port to use for VXLAN tunnels.")),
cfg.IntOpt('veth_mtu',
help=_("MTU size of veth interfaces")),
cfg.BoolOpt('l2_population', default=False,
help=_("Use ML2 l2population mechanism driver to learn "
"remote MAC and IPs and improve tunnel scalability.")),
cfg.BoolOpt('arp_responder', default=False,
help=_("Enable local ARP responder if it is supported. "
"Requires OVS 2.1 and ML2 l2population driver. "
"Allows the switch (when supporting an overlay) "
"to respond to an ARP request locally without "
"performing a costly ARP broadcast into the overlay.")),
cfg.BoolOpt('prevent_arp_spoofing', default=True,
help=_("Enable suppression of ARP responses that don't match "
"an IP address that belongs to the port from which "
"they originate. Note: This prevents the VMs attached "
"to this agent from spoofing, it doesn't protect them "
"from other devices which have the capability to spoof "
"(e.g. bare metal or VMs attached to agents without "
"this flag set to True). Spoofing rules will not be "
"added to any ports that have port security disabled. "
"This requires a version of OVS that supports matching "
"ARP headers.")),
cfg.BoolOpt('dont_fragment', default=True,
help=_("Set or un-set the don't fragment (DF) bit on "
"outgoing IP packet carrying GRE/VXLAN tunnel.")),
cfg.BoolOpt('enable_distributed_routing', default=False,
help=_("Make the l2 agent run in DVR mode.")),
cfg.IntOpt('quitting_rpc_timeout', default=10,
help=_("Set new timeout in seconds for new rpc calls after "
"agent receives SIGTERM. If value is set to 0, rpc "
"timeout won't be changed"))
]
cfg.CONF.register_opts(ovs_opts, "OVS")
cfg.CONF.register_opts(agent_opts, "AGENT")
config.register_agent_state_opts_helper(cfg.CONF)

@ -0,0 +1,90 @@
# Copyright (c) 2012 OpenStack Foundation.
#
# 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 neutron.plugins.common import constants as p_const
# Special vlan_id value in ovs_vlan_allocations table indicating flat network
FLAT_VLAN_ID = -1
# Topic for tunnel notifications between the plugin and agent
TUNNEL = 'tunnel'
# Name prefixes for veth device or patch port pair linking the integration
# bridge with the physical bridge for a physical network
PEER_INTEGRATION_PREFIX = 'int-'
PEER_PHYSICAL_PREFIX = 'phy-'
# Nonexistent peer used to create patch ports without associating them, it
# allows to define flows before association
NONEXISTENT_PEER = 'nonexistent-peer'
# The different types of tunnels
TUNNEL_NETWORK_TYPES = [p_const.TYPE_GRE, p_const.TYPE_VXLAN]
# Various tables for DVR use of integration bridge flows
LOCAL_SWITCHING = 0
DVR_TO_SRC_MAC = 1
DVR_TO_SRC_MAC_VLAN = 2
# Various tables for tunneling flows
DVR_PROCESS = 1
PATCH_LV_TO_TUN = 2
GRE_TUN_TO_LV = 3
VXLAN_TUN_TO_LV = 4
DVR_NOT_LEARN = 9
LEARN_FROM_TUN = 10
UCAST_TO_TUN = 20
ARP_RESPONDER = 21
FLOOD_TO_TUN = 22
# Various tables for DVR use of physical bridge flows
DVR_PROCESS_VLAN = 1
LOCAL_VLAN_TRANSLATION = 2
DVR_NOT_LEARN_VLAN = 3
# Tables for integration bridge
# Table 0 is used for forwarding.
CANARY_TABLE = 23
# Table for ARP poison/spoofing prevention rules
ARP_SPOOF_TABLE = 24
# type for ARP reply in ARP header
ARP_REPLY = '0x2'
# Map tunnel types to tables number
TUN_TABLE = {p_const.TYPE_GRE: GRE_TUN_TO_LV,
p_const.TYPE_VXLAN: VXLAN_TUN_TO_LV}
# The default respawn interval for the ovsdb monitor
DEFAULT_OVSDBMON_RESPAWN = 30
# Represent invalid OF Port
OFPORT_INVALID = -1
ARP_RESPONDER_ACTIONS = ('move:NXM_OF_ETH_SRC[]->NXM_OF_ETH_DST[],'
'mod_dl_src:%(mac)s,'
'load:0x2->NXM_OF_ARP_OP[],'
'move:NXM_NX_ARP_SHA[]->NXM_NX_ARP_THA[],'
'move:NXM_OF_ARP_SPA[]->NXM_OF_ARP_TPA[],'
'load:%(mac)#x->NXM_NX_ARP_SHA[],'
'load:%(ip)#x->NXM_OF_ARP_SPA[],'
'in_port')
# Represent ovs status
OVS_RESTARTED = 0
OVS_NORMAL = 1
OVS_DEAD = 2

File diff suppressed because it is too large Load Diff

6
openstack-common.conf Normal file

@ -0,0 +1,6 @@
[DEFAULT]
# The list of modules to copy from oslo-incubator.git
# The base module to hold the copy of openstack.common
base=networking_ovs_dpdk

@ -124,6 +124,12 @@
# Patches are downloaded and applied in the same order they are listed here.
# Example: ovs_dpdk_patches='file:///root/dpdk1.patch file:///root/dpdk2.patch'
#
# [*ovs_enable_sg_firewall_multicast*]
# *todo
#
# [*ovs_multicast_snooping_aging_time*]
# *todo
#
# [*ovs_emc_size*]
# (number) Defines the value which will be replaced in constant EM_FLOW_HASH_SHIFT in ovs lib/dpif-netdev.c.
# The constant represents count of bits for hash.

@ -5,3 +5,7 @@
pbr>=0.11,<2.0
Babel>=1.3
six>=1.9.0
netaddr>=0.7.12
oslo.service>=0.1.0 # Apache-2.0

@ -1,6 +1,6 @@
[metadata]
name = networking-ovs-dpdk
summary = A collection of installation tools to deploy DPDK accelerated Open vSwitch with OpenStack
summary = A collection of drivers and installation tools to deploy and manage DPDK accelerated Open vSwitch with Openstack.
description-file =
README.rst
author = OpenStack
@ -12,6 +12,15 @@ classifier =
Intended Audience :: System Administrators
License :: OSI Approved :: Apache Software License
Operating System :: POSIX :: Linux
Programming Language :: Python
Programming Language :: Python :: 2
Programming Language :: Python :: 2.7
Programming Language :: Python :: 3
Programming Language :: Python :: 3.4
[files]
packages =
networking_ovs_dpdk
[build_sphinx]
source-dir = doc/source
@ -25,3 +34,21 @@ upload-dir = doc/build/html
all_files = 1
build-dir = releasenotes/build
source-dir = releasenotes/source
[compile_catalog]
directory = networking_ovs_dpdk/locale
domain = networking-ovs-dpdk
[update_catalog]
domain = networking-ovs-dpdk
output_dir = networking_ovs_dpdk/locale
input_file = networking_ovs_dpdk/locale/networking-ovs-dpdk.pot
[extract_messages]
keywords = _ gettext ngettext l_ lazy_gettext
mapping_file = babel.cfg
output_file = networking_ovs_dpdk/locale/networking-ovs-dpdk.pot
[entry_points]
neutron.agent.firewall_drivers =
ovs_learn_action = networking_ovs_dpdk.agent.ovs_dpdk_firewall:OVSFirewallDriver

@ -2,9 +2,23 @@
# of appearance. Changing the order has an impact on the overall integration
# process, which may cause wedges in the gate later.
coverage>=3.6
hacking>=0.9.2,<0.10
coverage>=4.0
fixtures>=3.0.0 # Apache-2.0/BSD
discover
python-subunit
sphinx>=1.1.2
oslosphinx
oslotest>=1.1.0.0a1
testrepository>=0.0.18
testscenarios>=0.4
testtools>=0.9.34
flake8==2.1.0
tempest-lib>=0.2.0
vine>=1.1.3
# releasenotes
reno>=1.6.2 # Apache2
-e git+https://github.com/openstack/neutron.git@master#egg=neutron

29
tox.ini

@ -1,6 +1,6 @@
[tox]
minversion = 1.6
envlist = docs
envlist = py27,pep8,docs
skipsdist = True
sitepackages=False
@ -18,12 +18,7 @@ commands =
# mode. To do this define the TRACE_FAILONLY environmental variable.
[testenv:pep8]
commands = /bin/true
ignore_outcome = True
[testenv:py27]
commands = /bin/true
ignore_outcome = True
commands = flake8
[testenv:venv]
commands = {posargs}
@ -37,3 +32,23 @@ commands = python setup.py build_sphinx
[testenv:releasenotes]
commands =
sphinx-build -a -E -W -d releasenotes/build/doctrees -b html releasenotes/source releasenotes/build/html
[flake8]
# H803 skipped on purpose per list discussion.
# E123, E125 skipped as they are invalid PEP-8.
# E125 continuation line does not distinguish itself from next logical line
# E126 continuation line over-indented for hanging indent
# E128 continuation line under-indented for visual indent
# E129 visually indented line with same indent as next logical line
# E265 block comment should start with #
# H301 one import per line
# H305 imports not grouped correctly
# H307 like imports should be grouped together
# H402 one line docstring needs punctuation
# H404 multi line docstring should start with a summary
# H405 multi line docstring summary not separated with an empty line
# H904 Wrap long lines in parentheses instead of a backslash
ignore = E123,E125,E126,E128,E129,E265,H301,H305,H307,H402,H404,H405,H904,H803
show-source = True
builtins = _
exclude=.venv,.git,.tox,dist,doc,*openstack/common*,*lib/python*,*egg,build