Netlink solution to improve FWaaS performance
When the Firewall is updated, the conntrack entries will be deleted by conntrack-tools with each rule associated with each firewall rules. In large scale system, updating so much rules will call a large number of subprocesses to implement the "conntrack -D" commands. That will consume the system resource and time. This patch proves of using Netlink to delete conntrack entries when updating firewall-rules. Using Netlink will save about 90 percent of time that used by conntrack-tools. For detail information, visit: https://goo.gl/3tm9Fx Change-Id: I5babfd02090547ad886552201f843fa34761ce8a Co-Authored-By: Cao Xuan Hoang <hoangcx@vn.fujitsu.com>
This commit is contained in:
parent
c3310516d7
commit
6d5afd1a6f
|
@ -4,4 +4,7 @@
|
|||
|
||||
[Filters]
|
||||
|
||||
privsep-rootwrap: PathFilter, privsep-helper, root, privsep-helper, --config-file, /etc/(?!\.\.).*, --privsep_context, neutron_fwaas.privileged.default
|
||||
privsep: PathFilter, privsep-helper, root,
|
||||
--config-file, /etc,
|
||||
--privsep_context, neutron_fwaas.privileged.default,
|
||||
--privsep_sock_path, /
|
||||
|
|
|
@ -0,0 +1,268 @@
|
|||
# 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.
|
||||
#
|
||||
# Some parts are based on python-conntrack:
|
||||
# Copyright (c) 2009-2011,2015 Andrew Grigorev <andrew@ei-grad.ru>
|
||||
#
|
||||
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
# of this software and associated documentation files (the "Software"), to deal
|
||||
# in the Software without restriction, including without limitation the rights
|
||||
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
# copies of the Software, and to permit persons to whom the Software is
|
||||
# furnished to do so, subject to the following conditions:
|
||||
#
|
||||
# The above copyright notice and this permission notice shall be included in
|
||||
# all copies or substantial portions of the Software.
|
||||
#
|
||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
# THE SOFTWARE.
|
||||
#
|
||||
|
||||
"""
|
||||
Conntrack - A simple python interface to libnetfilter_conntrack using ctypes.
|
||||
"""
|
||||
|
||||
NFCT_OF_SHOW_LAYER3_BIT = 0
|
||||
NFCT_OF_SHOW_LAYER3 = (1 << NFCT_OF_SHOW_LAYER3_BIT)
|
||||
NFCT_OF_TIME_BIT = 1
|
||||
NFCT_OF_TIME = (1 << NFCT_OF_TIME_BIT)
|
||||
NFCT_OF_ID_BIT = 2
|
||||
NFCT_OF_ID = (1 << NFCT_OF_ID_BIT)
|
||||
|
||||
|
||||
CONNTRACK = 0
|
||||
|
||||
# Callback return codes
|
||||
NFCT_CB_FAILURE = -1 # failure
|
||||
NFCT_CB_STOP = 0 # stop the query
|
||||
NFCT_CB_CONTINUE = 1 # keep iterating through data
|
||||
NFCT_CB_STOLEN = 2 # like continue, but ct is not freed
|
||||
|
||||
|
||||
# Queries
|
||||
NFCT_Q_CREATE = 0
|
||||
NFCT_Q_UPDATE = 1
|
||||
NFCT_Q_DESTROY = 2
|
||||
NFCT_Q_GET = 3
|
||||
NFCT_Q_FLUSH = 4
|
||||
NFCT_Q_DUMP = 5
|
||||
NFCT_Q_DUMP_RESET = 6
|
||||
NFCT_Q_CREATE_UPDATE = 7
|
||||
NFCT_Q_DUMP_FILTER = 8
|
||||
NFCT_Q_DUMP_FILTER_RESET = 9
|
||||
|
||||
# Message types
|
||||
NFCT_T_UNKNOWN = 0
|
||||
NFCT_T_NEW_BIT = 0
|
||||
NFCT_T_NEW = (1 << NFCT_T_NEW_BIT)
|
||||
NFCT_T_UPDATE_BIT = 1
|
||||
NFCT_T_UPDATE = (1 << NFCT_T_UPDATE_BIT)
|
||||
NFCT_T_DESTROY_BIT = 2
|
||||
NFCT_T_DESTROY = (1 << NFCT_T_DESTROY_BIT)
|
||||
|
||||
NFCT_T_ALL = NFCT_T_NEW | NFCT_T_UPDATE | NFCT_T_DESTROY
|
||||
NFCT_T_ERROR_BIT = 31
|
||||
NFCT_T_ERROR = (1 << NFCT_T_ERROR_BIT)
|
||||
|
||||
# Attributes
|
||||
ATTR_ORIG_IPV4_SRC = 0 # u32 bits
|
||||
ATTR_IPV4_SRC = ATTR_ORIG_IPV4_SRC # alias
|
||||
ATTR_ORIG_IPV4_DST = 1 # u32 bits
|
||||
ATTR_IPV4_DST = ATTR_ORIG_IPV4_DST # alias
|
||||
ATTR_REPL_IPV4_SRC = 2 # u32 bits
|
||||
ATTR_REPL_IPV4_DST = 3 # u32 bits
|
||||
ATTR_ORIG_IPV6_SRC = 4 # u128 bits
|
||||
ATTR_IPV6_SRC = ATTR_ORIG_IPV6_SRC # alias
|
||||
ATTR_ORIG_IPV6_DST = 5 # u128 bits
|
||||
ATTR_IPV6_DST = ATTR_ORIG_IPV6_DST # alias
|
||||
ATTR_REPL_IPV6_SRC = 6 # u128 bits
|
||||
ATTR_REPL_IPV6_DST = 7 # u128 bits
|
||||
ATTR_ORIG_PORT_SRC = 8 # u16 bits
|
||||
ATTR_PORT_SRC = ATTR_ORIG_PORT_SRC # alias
|
||||
ATTR_ORIG_PORT_DST = 9 # u16 bits
|
||||
ATTR_PORT_DST = ATTR_ORIG_PORT_DST # alias
|
||||
ATTR_REPL_PORT_SRC = 10 # u16 bits
|
||||
ATTR_REPL_PORT_DST = 11 # u16 bits
|
||||
ATTR_ICMP_TYPE = 12 # u8 bits
|
||||
ATTR_ICMP_CODE = 13 # u8 bits
|
||||
ATTR_ICMP_ID = 14 # u16 bits
|
||||
ATTR_ORIG_L3PROTO = 15 # u8 bits
|
||||
ATTR_L3PROTO = ATTR_ORIG_L3PROTO # alias
|
||||
ATTR_REPL_L3PROTO = 16 # u8 bits
|
||||
ATTR_ORIG_L4PROTO = 17 # u8 bits
|
||||
ATTR_L4PROTO = ATTR_ORIG_L4PROTO # alias
|
||||
ATTR_REPL_L4PROTO = 18 # u8 bits
|
||||
ATTR_TCP_STATE = 19 # u8 bits
|
||||
ATTR_SNAT_IPV4 = 20 # u32 bits
|
||||
ATTR_DNAT_IPV4 = 21 # u32 bits
|
||||
ATTR_SNAT_PORT = 22 # u16 bits
|
||||
ATTR_DNAT_PORT = 23 # u16 bits
|
||||
ATTR_TIMEOUT = 24 # u32 bits
|
||||
ATTR_MARK = 25 # u32 bits
|
||||
ATTR_ORIG_COUNTER_PACKETS = 26 # u32 bits
|
||||
ATTR_REPL_COUNTER_PACKETS = 27 # u32 bits
|
||||
ATTR_ORIG_COUNTER_BYTES = 28 # u32 bits
|
||||
ATTR_REPL_COUNTER_BYTES = 29 # u32 bits
|
||||
ATTR_USE = 30 # u32 bits
|
||||
ATTR_ID = 31 # u32 bits
|
||||
ATTR_STATUS = 32 # u32 bits
|
||||
ATTR_TCP_FLAGS_ORIG = 33 # u8 bits
|
||||
ATTR_TCP_FLAGS_REPL = 34 # u8 bits
|
||||
ATTR_TCP_MASK_ORIG = 35 # u8 bits
|
||||
ATTR_TCP_MASK_REPL = 36 # u8 bits
|
||||
ATTR_MASTER_IPV4_SRC = 37 # u32 bits
|
||||
ATTR_MASTER_IPV4_DST = 38 # u32 bits
|
||||
ATTR_MASTER_IPV6_SRC = 39 # u128 bits
|
||||
ATTR_MASTER_IPV6_DST = 40 # u128 bits
|
||||
ATTR_MASTER_PORT_SRC = 41 # u16 bits
|
||||
ATTR_MASTER_PORT_DST = 42 # u16 bits
|
||||
ATTR_MASTER_L3PROTO = 43 # u8 bits
|
||||
ATTR_MASTER_L4PROTO = 44 # u8 bits
|
||||
ATTR_SECMARK = 45 # u32 bits
|
||||
ATTR_ORIG_NAT_SEQ_CORRECTION_POS = 46 # u32 bits
|
||||
ATTR_ORIG_NAT_SEQ_OFFSET_BEFORE = 47 # u32 bits
|
||||
ATTR_ORIG_NAT_SEQ_OFFSET_AFTER = 48 # u32 bits
|
||||
ATTR_REPL_NAT_SEQ_CORRECTION_POS = 49 # u32 bits
|
||||
ATTR_REPL_NAT_SEQ_OFFSET_BEFORE = 50 # u32 bits
|
||||
ATTR_REPL_NAT_SEQ_OFFSET_AFTER = 51 # u32 bits
|
||||
ATTR_SCTP_STATE = 52 # u8 bits
|
||||
ATTR_SCTP_VTAG_ORIG = 53 # u32 bits
|
||||
ATTR_SCTP_VTAG_REPL = 54 # u32 bits
|
||||
ATTR_HELPER_NAME = 55 # string (30 bytes max)
|
||||
ATTR_DCCP_STATE = 56 # u8 bits
|
||||
ATTR_DCCP_ROLE = 57 # u8 bits
|
||||
ATTR_DCCP_HANDSHAKE_SEQ = 58 # u64 bits
|
||||
ATTR_MAX = 59
|
||||
ATTR_GRP_ORIG_IPV4 = 0 # struct nfct_attr_grp_ipv4
|
||||
ATTR_GRP_REPL_IPV4 = 1 # struct nfct_attr_grp_ipv4
|
||||
ATTR_GRP_ORIG_IPV6 = 2 # struct nfct_attr_grp_ipv6
|
||||
ATTR_GRP_REPL_IPV6 = 3 # struct nfct_attr_grp_ipv6
|
||||
ATTR_GRP_ORIG_PORT = 4 # struct nfct_attr_grp_port
|
||||
ATTR_GRP_REPL_PORT = 5 # struct nfct_attr_grp_port
|
||||
ATTR_GRP_ICMP = 6 # struct nfct_attr_grp_icmp
|
||||
ATTR_GRP_MASTER_IPV4 = 7 # struct nfct_attr_grp_ipv4
|
||||
ATTR_GRP_MASTER_IPV6 = 8 # struct nfct_attr_grp_ipv6
|
||||
ATTR_GRP_MASTER_PORT = 9 # struct nfct_attr_grp_port
|
||||
ATTR_GRP_ORIG_COUNTERS = 10 # struct nfct_attr_grp_ctrs
|
||||
ATTR_GRP_REPL_COUNTERS = 11 # struct nfct_attr_grp_ctrs
|
||||
ATTR_GRP_MAX = 12
|
||||
ATTR_EXP_MASTER = 0 # pointer to conntrack object
|
||||
ATTR_EXP_EXPECTED = 1 # pointer to conntrack object
|
||||
ATTR_EXP_MASK = 2 # pointer to conntrack object
|
||||
ATTR_EXP_TIMEOUT = 3 # u32 bits
|
||||
ATTR_EXP_MAX = 4
|
||||
|
||||
# NFCT_*printf output format
|
||||
NFCT_O_PLAIN = 0
|
||||
NFCT_O_DEFAULT = NFCT_O_PLAIN
|
||||
NFCT_O_XML = 1
|
||||
NFCT_O_MAX = 2
|
||||
|
||||
|
||||
NFCT_CMP_ALL = 0
|
||||
NFCT_CMP_ORIG = (1 << 0)
|
||||
NFCT_CMP_REPL = (1 << 1)
|
||||
NFCT_CMP_TIMEOUT_EQ = (1 << 2)
|
||||
NFCT_CMP_TIMEOUT_GT = (1 << 3)
|
||||
NFCT_CMP_TIMEOUT_GE = (NFCT_CMP_TIMEOUT_EQ | NFCT_CMP_TIMEOUT_GT)
|
||||
NFCT_CMP_TIMEOUT_LT = (1 << 4)
|
||||
NFCT_CMP_TIMEOUT_LE = (NFCT_CMP_TIMEOUT_EQ | NFCT_CMP_TIMEOUT_LT)
|
||||
NFCT_CMP_MASK = (1 << 5)
|
||||
NFCT_CMP_STRICT = (1 << 6)
|
||||
|
||||
# Conntrack options
|
||||
CT_OPT_ORIG_SRC_BIT = 0
|
||||
CT_OPT_ORIG_SRC = (1 << CT_OPT_ORIG_SRC_BIT)
|
||||
|
||||
CT_OPT_ORIG_DST_BIT = 1
|
||||
CT_OPT_ORIG_DST = (1 << CT_OPT_ORIG_DST_BIT)
|
||||
|
||||
CT_OPT_ORIG = (CT_OPT_ORIG_SRC | CT_OPT_ORIG_DST)
|
||||
|
||||
CT_OPT_REPL_SRC_BIT = 2
|
||||
CT_OPT_REPL_SRC = (1 << CT_OPT_REPL_SRC_BIT)
|
||||
|
||||
CT_OPT_REPL_DST_BIT = 3
|
||||
CT_OPT_REPL_DST = (1 << CT_OPT_REPL_DST_BIT)
|
||||
|
||||
CT_OPT_REPL = (CT_OPT_REPL_SRC | CT_OPT_REPL_DST)
|
||||
|
||||
CT_OPT_PROTO_BIT = 4
|
||||
CT_OPT_PROTO = (1 << CT_OPT_PROTO_BIT)
|
||||
|
||||
CT_OPT_TUPLE_ORIG = (CT_OPT_ORIG | CT_OPT_PROTO)
|
||||
CT_OPT_TUPLE_REPL = (CT_OPT_REPL | CT_OPT_PROTO)
|
||||
|
||||
CT_OPT_TIMEOUT_BIT = 5
|
||||
CT_OPT_TIMEOUT = (1 << CT_OPT_TIMEOUT_BIT)
|
||||
|
||||
CT_OPT_STATUS_BIT = 6
|
||||
CT_OPT_STATUS = (1 << CT_OPT_STATUS_BIT)
|
||||
|
||||
CT_OPT_ZERO_BIT = 7
|
||||
CT_OPT_ZERO = (1 << CT_OPT_ZERO_BIT)
|
||||
|
||||
CT_OPT_EVENT_MASK_BIT = 8
|
||||
CT_OPT_EVENT_MASK = (1 << CT_OPT_EVENT_MASK_BIT)
|
||||
|
||||
CT_OPT_EXP_SRC_BIT = 9
|
||||
CT_OPT_EXP_SRC = (1 << CT_OPT_EXP_SRC_BIT)
|
||||
|
||||
CT_OPT_EXP_DST_BIT = 10
|
||||
CT_OPT_EXP_DST = (1 << CT_OPT_EXP_DST_BIT)
|
||||
|
||||
CT_OPT_MASK_SRC_BIT = 11
|
||||
CT_OPT_MASK_SRC = (1 << CT_OPT_MASK_SRC_BIT)
|
||||
|
||||
CT_OPT_MASK_DST_BIT = 12
|
||||
CT_OPT_MASK_DST = (1 << CT_OPT_MASK_DST_BIT)
|
||||
|
||||
CT_OPT_NATRANGE_BIT = 13
|
||||
CT_OPT_NATRANGE = (1 << CT_OPT_NATRANGE_BIT)
|
||||
|
||||
CT_OPT_MARK_BIT = 14
|
||||
CT_OPT_MARK = (1 << CT_OPT_MARK_BIT)
|
||||
|
||||
CT_OPT_ID_BIT = 15
|
||||
CT_OPT_ID = (1 << CT_OPT_ID_BIT)
|
||||
|
||||
CT_OPT_FAMILY_BIT = 16
|
||||
CT_OPT_FAMILY = (1 << CT_OPT_FAMILY_BIT)
|
||||
|
||||
CT_OPT_SRC_NAT_BIT = 17
|
||||
CT_OPT_SRC_NAT = (1 << CT_OPT_SRC_NAT_BIT)
|
||||
|
||||
CT_OPT_DST_NAT_BIT = 18
|
||||
CT_OPT_DST_NAT = (1 << CT_OPT_DST_NAT_BIT)
|
||||
|
||||
CT_OPT_OUTPUT_BIT = 19
|
||||
CT_OPT_OUTPUT = (1 << CT_OPT_OUTPUT_BIT)
|
||||
|
||||
CT_OPT_SECMARK_BIT = 20
|
||||
CT_OPT_SECMARK = (1 << CT_OPT_SECMARK_BIT)
|
||||
|
||||
CT_OPT_BUFFERSIZE_BIT = 21
|
||||
CT_OPT_BUFFERSIZE = (1 << CT_OPT_BUFFERSIZE_BIT)
|
||||
|
||||
CT_OPT_ANY_NAT_BIT = 22
|
||||
CT_OPT_ANY_NAT = (1 << CT_OPT_ANY_NAT_BIT)
|
||||
|
||||
CT_OPT_ZONE_BIT = 23
|
||||
CT_OPT_ZONE = (1 << CT_OPT_ZONE_BIT)
|
||||
|
||||
CT_COMPARISON = (CT_OPT_PROTO | CT_OPT_ORIG | CT_OPT_REPL | CT_OPT_MARK |
|
||||
CT_OPT_SECMARK | CT_OPT_STATUS | CT_OPT_ID | CT_OPT_ZONE)
|
|
@ -0,0 +1,26 @@
|
|||
# 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_privsep import capabilities as c
|
||||
from oslo_privsep import priv_context
|
||||
|
||||
# It is expected that most (if not all) neutron operations can be
|
||||
# executed with these privileges.
|
||||
default = priv_context.PrivContext(
|
||||
__name__,
|
||||
cfg_section='privsep',
|
||||
pypath=__name__ + '.default',
|
||||
# TODO(gus): CAP_SYS_ADMIN is required (only?) for manipulating
|
||||
# network namespaces. SYS_ADMIN is a lot of scary powers, so
|
||||
# consider breaking this out into a separate minimal context.
|
||||
capabilities=[c.CAP_SYS_ADMIN, c.CAP_NET_ADMIN],
|
||||
)
|
|
@ -0,0 +1,272 @@
|
|||
# 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.
|
||||
#
|
||||
# Some parts are based on python-conntrack:
|
||||
# Copyright (c) 2009-2011,2015 Andrew Grigorev <andrew@ei-grad.ru>
|
||||
#
|
||||
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
# of this software and associated documentation files (the "Software"), to deal
|
||||
# in the Software without restriction, including without limitation the rights
|
||||
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
# copies of the Software, and to permit persons to whom the Software is
|
||||
# furnished to do so, subject to the following conditions:
|
||||
#
|
||||
# The above copyright notice and this permission notice shall be included in
|
||||
# all copies or substantial portions of the Software.
|
||||
#
|
||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
# THE SOFTWARE.
|
||||
#
|
||||
|
||||
"""
|
||||
Conntrack - A simple python interface to libnetfilter_conntrack using ctypes.
|
||||
"""
|
||||
|
||||
import ctypes as c
|
||||
from ctypes.util import find_library
|
||||
import os
|
||||
from socket import AF_INET
|
||||
from socket import AF_INET6
|
||||
|
||||
from pyroute2 import netns as pynetns
|
||||
|
||||
from oslo_log import log as logging
|
||||
|
||||
from neutron_fwaas._i18n import _LE
|
||||
from neutron_fwaas.common import netlink_constants as nl_constants
|
||||
|
||||
from neutron_fwaas import privileged
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
nfct = c.CDLL(find_library('netfilter_conntrack'))
|
||||
libc = c.CDLL(find_library('libc.so.6'))
|
||||
|
||||
proto_num = {
|
||||
'tcp': 6,
|
||||
'udp': 17,
|
||||
'icmp': 1,
|
||||
}
|
||||
|
||||
family_socket = {
|
||||
4: AF_INET,
|
||||
6: AF_INET6,
|
||||
}
|
||||
|
||||
NFCT_CALLBACK = c.CFUNCTYPE(c.c_int, c.c_int, c.c_void_p, c.c_void_p)
|
||||
|
||||
|
||||
def _list(family=4):
|
||||
"""
|
||||
Get list of active conntrack entries.
|
||||
|
||||
:param: family: ipversion of conntrack entries to be listed.
|
||||
:return: entries: list of conntrack entries.
|
||||
"""
|
||||
entries = []
|
||||
buf = c.create_string_buffer(1024)
|
||||
|
||||
@NFCT_CALLBACK
|
||||
def cb(type, ct, data):
|
||||
nfct.nfct_snprintf(buf, 1024, ct, type, 0, nl_constants.NFCT_OF_TIME)
|
||||
entries.append(buf.value)
|
||||
return nl_constants.NFCT_CB_CONTINUE
|
||||
|
||||
h = nfct.nfct_open(nl_constants.CONNTRACK, 0)
|
||||
|
||||
if not h:
|
||||
LOG.exception(_LE("nfct_open failed!"))
|
||||
return entries
|
||||
|
||||
nfct.nfct_callback_register(h, nl_constants.NFCT_T_ALL, cb, 0)
|
||||
ret = nfct.nfct_query(h, nl_constants.NFCT_Q_DUMP,
|
||||
c.byref(c.c_int(family_socket[family])))
|
||||
if ret == -1:
|
||||
nfct.nfct_close(h)
|
||||
LOG.exception(_LE("nfct_query failed!"))
|
||||
return entries
|
||||
nfct.nfct_close(h)
|
||||
return entries
|
||||
|
||||
|
||||
def _kill(**kwargs):
|
||||
"""
|
||||
Delete specified conntrack entries.
|
||||
|
||||
:param: kwargs: entry information
|
||||
:return: None
|
||||
"""
|
||||
family = kwargs.get('family', 4)
|
||||
protocol = kwargs.get('protocol', 'tcp')
|
||||
source_address = kwargs.get('src', '0.0.0.0')
|
||||
destination_address = kwargs.get('dst', '0.0.0.0')
|
||||
|
||||
ct = nfct.nfct_new()
|
||||
if not ct:
|
||||
LOG.exception(_LE("nfct_new failed!"))
|
||||
return
|
||||
|
||||
nfct.nfct_set_attr_u8(ct, nl_constants.ATTR_L3PROTO, family_socket[family])
|
||||
|
||||
if family == 4:
|
||||
nfct.nfct_set_attr_u32(ct, nl_constants.ATTR_IPV4_SRC,
|
||||
libc.inet_addr(source_address))
|
||||
nfct.nfct_set_attr_u32(ct, nl_constants.ATTR_IPV4_DST,
|
||||
libc.inet_addr(destination_address))
|
||||
elif family == 6:
|
||||
nfct.nfct_set_attr_u64(ct, nl_constants.ATTR_IPV6_SRC,
|
||||
libc.inet_addr(source_address))
|
||||
nfct.nfct_set_attr_u64(ct, nl_constants.ATTR_IPV6_DST,
|
||||
libc.inet_addr(destination_address))
|
||||
else:
|
||||
LOG.exception(_LE("Unsupported protocol family!"))
|
||||
|
||||
nfct.nfct_set_attr_u8(ct, nl_constants.ATTR_L4PROTO, proto_num[protocol])
|
||||
|
||||
if protocol == 'icmp':
|
||||
nfct.nfct_set_attr_u8(ct, nl_constants.ATTR_ICMP_TYPE,
|
||||
kwargs.get('icmp_type', 8))
|
||||
nfct.nfct_set_attr_u8(ct, nl_constants.ATTR_ICMP_CODE,
|
||||
kwargs.get('icmp_code', 0))
|
||||
nfct.nfct_set_attr_u16(ct, nl_constants.ATTR_ICMP_ID,
|
||||
libc.htons(kwargs.get('icmp_id'), 0))
|
||||
else:
|
||||
nfct.nfct_set_attr_u16(ct, nl_constants.ATTR_PORT_SRC,
|
||||
libc.htons(kwargs.get('sport')))
|
||||
nfct.nfct_set_attr_u16(ct, nl_constants.ATTR_PORT_DST,
|
||||
libc.htons(kwargs.get('dport')))
|
||||
h = nfct.nfct_open(nl_constants.CONNTRACK, 0)
|
||||
if not h:
|
||||
LOG.exception(_LE("nfct_open failed!"))
|
||||
else:
|
||||
ret = nfct.nfct_query(h, nl_constants.NFCT_Q_DESTROY, ct)
|
||||
if ret == -1:
|
||||
LOG.exception(_LE("Deleting conntrack failed"))
|
||||
nfct.nfct_close(h)
|
||||
nfct.nfct_destroy(ct)
|
||||
|
||||
|
||||
def _flush():
|
||||
ct = nfct.nfct_new()
|
||||
if not ct:
|
||||
libc.perror("nfct_new")
|
||||
raise LOG.exception(_LE("nfct_new failed!"))
|
||||
return
|
||||
h = nfct.nfct_open(nl_constants.CONNTRACK, 0)
|
||||
if not h:
|
||||
libc.perror("nfct_open")
|
||||
raise LOG.exception(_LE("nfct_open failed!"))
|
||||
else:
|
||||
ret = nfct.nfct_query(h, nl_constants.NFCT_Q_FLUSH, ct)
|
||||
if ret == -1:
|
||||
libc.perror("nfct_query")
|
||||
raise LOG.exception(_LE("nfct_query failed!"))
|
||||
nfct.nfct_close(h)
|
||||
nfct.nfct_destroy(ct)
|
||||
|
||||
|
||||
def _parse_entry(entry, ipversion):
|
||||
"""
|
||||
Parse entry to a tuple
|
||||
|
||||
:param entry: Array from entry string split
|
||||
:param ipversion: ipversion used to get this entry
|
||||
:return: a tuple of parsed entry
|
||||
example: (4, 'tcp', '1111', '2222', '1.1.1.1', '2.2.2.2')
|
||||
"""
|
||||
protocol = entry[1]
|
||||
if protocol == 'tcp':
|
||||
src_address = entry[5].split('=')[1]
|
||||
dst_address = entry[6].split('=')[1]
|
||||
sport = entry[7].split('=')[1]
|
||||
dport = entry[8].split('=')[1]
|
||||
elif protocol == 'udp':
|
||||
src_address = entry[4].split('=')[1]
|
||||
dst_address = entry[5].split('=')[1]
|
||||
sport = entry[6].split('=')[1]
|
||||
dport = entry[7].split('=')[1]
|
||||
elif protocol == 'icmp':
|
||||
src_address = entry[4].split('=')[1]
|
||||
dst_address = entry[5].split('=')[1]
|
||||
icmp_type = entry[6].split('=')[1]
|
||||
icmp_code = entry[7].split('=')[1]
|
||||
icmp_id = entry[8].split('=')[1]
|
||||
parsed_entry = (ipversion, protocol, icmp_type, icmp_code,
|
||||
src_address, dst_address, icmp_id,)
|
||||
return parsed_entry
|
||||
parsed_entry = (ipversion, protocol, sport,
|
||||
dport, src_address, dst_address,)
|
||||
return parsed_entry
|
||||
|
||||
|
||||
@privileged.default.entrypoint
|
||||
def list_entries(namespace):
|
||||
"""
|
||||
List, parse and sort all entries
|
||||
|
||||
:param namespace:
|
||||
:return: sorted list of entry tuples.
|
||||
example: [(4, 'icmp', '8', '0', '1.1.1.1', '2.2.2.2'),
|
||||
(4, 'tcp', '1111', '2222', '1.1.1.1', '2.2.2.2')]
|
||||
"""
|
||||
entries = []
|
||||
if namespace:
|
||||
fd = pynetns.setns(namespace)
|
||||
ipversions = [4, 6]
|
||||
for ipversion in ipversions:
|
||||
xentries = _list(ipversion)
|
||||
for entry in xentries:
|
||||
sentry = entry.split()
|
||||
xentry = _parse_entry(sentry, ipversion)
|
||||
entries.append(xentry)
|
||||
os.close(fd)
|
||||
return sorted(entries)
|
||||
|
||||
|
||||
def _kill_entry(entry):
|
||||
"""
|
||||
Kill the entry
|
||||
|
||||
:param entry: (ipversion, protocol, sport, dport, saddress, daddress)
|
||||
"""
|
||||
|
||||
if entry[1] == 'icmp':
|
||||
_kill(family=entry[0], protocol=entry[1],
|
||||
src=entry[4], dst=entry[5],
|
||||
icmp_type=int(entry[2]), icmp_code=int(entry[3]),
|
||||
icmp_id=int(entry[6]))
|
||||
else:
|
||||
_kill(family=entry[0], protocol=entry[1],
|
||||
src=entry[4], dst=entry[5],
|
||||
sport=int(entry[2]), dport=int(entry[3]))
|
||||
|
||||
|
||||
@privileged.default.entrypoint
|
||||
def kill_entries(namespace, entries):
|
||||
if namespace:
|
||||
fd = pynetns.setns(namespace)
|
||||
for entry in entries:
|
||||
_kill_entry(entry)
|
||||
os.close(fd)
|
||||
|
||||
|
||||
@privileged.default.entrypoint
|
||||
def flush_entries(namespace):
|
||||
if namespace:
|
||||
fd = pynetns.setns(namespace)
|
||||
_flush()
|
||||
os.close(fd)
|
|
@ -13,6 +13,7 @@
|
|||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
from neutron.agent.common import config
|
||||
from neutron.common import rpc as n_rpc
|
||||
from neutron import context
|
||||
from oslo_config import cfg
|
||||
|
@ -61,6 +62,7 @@ class FWaaSL3AgentExtension(l3_extension.L3AgentExtension):
|
|||
f_resources.FIREWALL_RULE]
|
||||
|
||||
def initialize(self, connection, driver_type):
|
||||
config.setup_privsep()
|
||||
self._register_rpc_consumers(connection)
|
||||
|
||||
def consume_api(self, agent_api):
|
||||
|
|
|
@ -12,14 +12,13 @@
|
|||
# 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.agent.linux import iptables_manager
|
||||
from neutron.agent.linux import utils as linux_utils
|
||||
from oslo_log import log as logging
|
||||
|
||||
from neutron_fwaas._i18n import _LE
|
||||
from neutron_fwaas.common import fwaas_constants as f_const
|
||||
from neutron_fwaas.extensions import firewall as fw_ext
|
||||
from neutron_fwaas.privileged import netlink_lib
|
||||
from neutron_fwaas.services.firewall.drivers import fwaas_base
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
@ -257,26 +256,6 @@ class IptablesFwaasDriver(fwaas_base.FwaasDriverBase):
|
|||
def _find_new_rules(self, pre_firewall, firewall):
|
||||
return self._find_removed_rules(firewall, pre_firewall)
|
||||
|
||||
def _get_conntrack_cmd_from_rule(self, ipt_mgr, rule=None):
|
||||
prefixcmd = ['ip', 'netns', 'exec'] + [ipt_mgr.namespace]
|
||||
cmd = ['conntrack', '-D']
|
||||
if rule:
|
||||
conntrack_filter = self._get_conntrack_filter_from_rule(rule)
|
||||
exec_cmd = prefixcmd + cmd + conntrack_filter
|
||||
else:
|
||||
exec_cmd = prefixcmd + cmd
|
||||
return exec_cmd
|
||||
|
||||
def _remove_conntrack_by_cmd(self, cmd):
|
||||
if cmd:
|
||||
try:
|
||||
linux_utils.execute(cmd, run_as_root=True,
|
||||
check_exit_code=True,
|
||||
extra_ok_codes=[1])
|
||||
except RuntimeError:
|
||||
LOG.exception(
|
||||
_LE("Failed execute conntrack command %s"), str(cmd))
|
||||
|
||||
def _remove_conntrack_new_firewall(self, agent_mode, apply_list, firewall):
|
||||
"""Remove conntrack when create new firewall"""
|
||||
routers_list = list(set(apply_list))
|
||||
|
@ -285,8 +264,7 @@ class IptablesFwaasDriver(fwaas_base.FwaasDriverBase):
|
|||
agent_mode, router_info)
|
||||
for ipt_if_prefix in ipt_if_prefix_list:
|
||||
ipt_mgr = ipt_if_prefix['ipt']
|
||||
cmd = self._get_conntrack_cmd_from_rule(ipt_mgr)
|
||||
self._remove_conntrack_by_cmd(cmd)
|
||||
self._flush_conntrack_netlink(ipt_mgr.namespace)
|
||||
|
||||
def _remove_conntrack_updated_firewall(self, agent_mode,
|
||||
apply_list, pre_firewall, firewall):
|
||||
|
@ -302,27 +280,78 @@ class IptablesFwaasDriver(fwaas_base.FwaasDriverBase):
|
|||
i_rules = self._find_new_rules(pre_firewall, firewall)
|
||||
r_rules = self._find_removed_rules(pre_firewall, firewall)
|
||||
removed_conntrack_rules_list = ch_rules + i_rules + r_rules
|
||||
rules = []
|
||||
for rule in removed_conntrack_rules_list:
|
||||
cmd = self._get_conntrack_cmd_from_rule(ipt_mgr, rule)
|
||||
self._remove_conntrack_by_cmd(cmd)
|
||||
rules.append(self._get_filters_from_rules(rule))
|
||||
rules = sorted(list(set(rules)))
|
||||
self._remove_conntrack_netlink(ipt_mgr.namespace, rules)
|
||||
|
||||
def _get_conntrack_filter_from_rule(self, rule):
|
||||
"""Get conntrack filter from rule.
|
||||
The key for get conntrack filter is protocol, destination_port
|
||||
and source_port. If we want to take more keys, add to the list.
|
||||
@staticmethod
|
||||
def _entry2delete(rule, entry):
|
||||
"""
|
||||
conntrack_filter = []
|
||||
keys = [['-p', 'protocol'], ['-f', 'ip_version'],
|
||||
['--dport', 'destination_port'], ['--sport', 'source_port']]
|
||||
Check if an entry will be delete or not
|
||||
|
||||
:param rule: (ipversion, protocol, sport, dport)
|
||||
:param entry: (ipversion, protocol, sport, dport, saddress, daddress)
|
||||
:return: True if the entry matches the rule
|
||||
The entry matches the rule if it has the same ipversion, protocol or
|
||||
entry source port, destination port sequentially in rule source port,
|
||||
destination port range.
|
||||
"""
|
||||
return (
|
||||
(entry[0] == rule[0]) and (not rule[1] or entry[1] == rule[1]) and
|
||||
(not rule[2] or int(entry[2]) in range(int(rule[2].split(':')[0]),
|
||||
int(rule[2].split(':')[-1]) + 1)) and
|
||||
(not rule[2] or int(entry[2]) in range(int(rule[2].split(':')[0]),
|
||||
int(rule[2].split(':')[-1]) + 1)))
|
||||
|
||||
def _remove_conntrack_netlink(self, namespace, rules):
|
||||
# Getting a list of all entries
|
||||
entries = netlink_lib.list_entries(namespace)
|
||||
|
||||
# Compare each entry to each rule to define that this entry
|
||||
# is to delete or not.
|
||||
# rule and entry have the same parameters order to be comparable:
|
||||
# rule: (ipversion, protocol, sport, dport)
|
||||
# entry: (ipversion, protocol, sport, dport, saddress, daddress)
|
||||
# rules and entries were sorted lists of tuples to reduce the
|
||||
# number of comparisons.
|
||||
dentries = []
|
||||
ientry = 0
|
||||
entryNumber = len(entries)
|
||||
for rule in rules:
|
||||
while ientry < entryNumber and entries[ientry] < rule:
|
||||
ientry += 1
|
||||
while (ientry < entryNumber and
|
||||
self._entry2delete(rule, entries[ientry])):
|
||||
dentries.append(entries[ientry])
|
||||
ientry += 1
|
||||
|
||||
# Calling to netlink_lib.kill_entries to delete entries
|
||||
netlink_lib.kill_entries(namespace, dentries)
|
||||
|
||||
def _flush_conntrack_netlink(self, namespace):
|
||||
netlink_lib.flush_entries(namespace)
|
||||
|
||||
def _get_filters_from_rules(self, rule):
|
||||
"""Parse parameters from firewall rules
|
||||
|
||||
:param: rule: A firewall rule
|
||||
:return filter: Tuple of parameters
|
||||
example: (4, 'tcp', 1111, 2222, '1.1.1.1', '2.2.2.2')
|
||||
"""
|
||||
|
||||
keys = ['ip_version', 'protocol', 'source_port', 'destination_port']
|
||||
addr_keys = ['source_ip_address', 'destination_ip_address']
|
||||
rule_filter = []
|
||||
for key in keys:
|
||||
if rule.get(key[1]):
|
||||
if key[1] == 'ip_version':
|
||||
conntrack_filter.append(key[0])
|
||||
conntrack_filter.append('ipv' + str(rule.get(key[1])))
|
||||
else:
|
||||
conntrack_filter.append(key[0])
|
||||
conntrack_filter.append(rule.get(key[1]))
|
||||
return conntrack_filter
|
||||
rule_filter.append(rule.get(key) or '')
|
||||
for key in addr_keys:
|
||||
if not rule.get(key):
|
||||
rule_filter.append('')
|
||||
else:
|
||||
rule_filter.append(rule.get(key))
|
||||
return tuple(rule_filter)
|
||||
|
||||
def _remove_default_chains(self, nsid):
|
||||
"""Remove fwaas default policy chain."""
|
||||
|
|
|
@ -27,6 +27,8 @@ FAKE_DST_PREFIX = '20.0.0.0/24'
|
|||
FAKE_PROTOCOL = 'tcp'
|
||||
FAKE_SRC_PORT = 5000
|
||||
FAKE_DST_PORT = 22
|
||||
FAKE_ENTRY = [(4, 'icmp', '8', '0', '1000', '1.1.1.1', '2.2.2.2'),
|
||||
(4, 'tcp', '1111', '23', '1.1.1.1', '2.2.2.2'), ]
|
||||
FAKE_FW_ID = 'fake-fw-uuid'
|
||||
FW_LEGACY = 'legacy'
|
||||
|
||||
|
@ -40,6 +42,12 @@ class IptablesFwaasTestCase(base.BaseTestCase):
|
|||
self.iptables_cls_p = mock.patch(
|
||||
'neutron.agent.linux.iptables_manager.IptablesManager')
|
||||
self.iptables_cls_p.start()
|
||||
self.netlink_kill_p = mock.patch(
|
||||
'neutron_fwaas.privileged.netlink_lib.kill_entries')
|
||||
self.netlink_kill_p.start()
|
||||
self.netlink_flush_p = mock.patch(
|
||||
'neutron_fwaas.privileged.netlink_lib.flush_entries')
|
||||
self.netlink_flush = self.netlink_flush_p.start()
|
||||
self.firewall = fwaas.IptablesFwaasDriver()
|
||||
|
||||
def _fake_rules_v4(self, fwid, apply_list):
|
||||
|
@ -285,13 +293,11 @@ class IptablesFwaasTestCase(base.BaseTestCase):
|
|||
self.firewall.create_firewall(FW_LEGACY, apply_list, firewall)
|
||||
for router_info_inst in apply_list:
|
||||
namespace = router_info_inst.iptables_manager.namespace
|
||||
cmd = ['ip', 'netns', 'exec', namespace, 'conntrack', '-D']
|
||||
calls = [
|
||||
mock.call(cmd, run_as_root=True, check_exit_code=True,
|
||||
extra_ok_codes=[1])]
|
||||
self.utils_exec.assert_has_calls(calls)
|
||||
calls = [mock.call(namespace)]
|
||||
self.netlink_flush.assert_has_calls(calls)
|
||||
|
||||
def test_remove_conntrack_inserted_rule(self):
|
||||
@mock.patch('neutron_fwaas.privileged.netlink_lib.kill_entries')
|
||||
def test_remove_conntrack_inserted_rule(self, mock_kill):
|
||||
apply_list = self._fake_apply_list()
|
||||
rule_list = self._fake_rules_v4(FAKE_FW_ID, apply_list)
|
||||
firewall = self._fake_firewall(rule_list)
|
||||
|
@ -304,21 +310,22 @@ class IptablesFwaasTestCase(base.BaseTestCase):
|
|||
'id': 'fake-fw-rule'}
|
||||
rule_list.insert(2, insert_rule)
|
||||
firewall = self._fake_firewall(rule_list)
|
||||
self.firewall.update_firewall(FW_LEGACY, apply_list, firewall)
|
||||
for router_info_inst in apply_list:
|
||||
namespace = router_info_inst.iptables_manager.namespace
|
||||
cmd1 = ['ip', 'netns', 'exec', namespace, 'conntrack',
|
||||
'-D', '-p', 'tcp', '-f', 'ipv4', '--dport', '23']
|
||||
cmd2 = ['ip', 'netns', 'exec', namespace, 'conntrack',
|
||||
'-D', '-p', 'icmp', '-f', 'ipv4']
|
||||
calls = [
|
||||
mock.call(cmd1, run_as_root=True, check_exit_code=True,
|
||||
extra_ok_codes=[1]),
|
||||
mock.call(cmd2, run_as_root=True, check_exit_code=True,
|
||||
extra_ok_codes=[1])]
|
||||
self.utils_exec.assert_has_calls(calls)
|
||||
with mock.patch('neutron_fwaas.privileged.'
|
||||
'netlink_lib.list_entries') as list_entries:
|
||||
list_entries.return_value = FAKE_ENTRY
|
||||
self.firewall.update_firewall(FW_LEGACY, apply_list, firewall)
|
||||
calls = [
|
||||
mock.call(namespace, [(4, 'icmp', '8', '0', '1000',
|
||||
'1.1.1.1', '2.2.2.2'),
|
||||
(4, 'tcp', '1111', '23',
|
||||
'1.1.1.1', '2.2.2.2')])
|
||||
]
|
||||
mock_kill.assert_has_calls(calls)
|
||||
|
||||
def test_remove_conntrack_removed_rule(self):
|
||||
@mock.patch('neutron_fwaas.privileged.netlink_lib.kill_entries')
|
||||
def test_remove_conntrack_removed_rule(self, mock_kill):
|
||||
apply_list = self._fake_apply_list()
|
||||
rule_list = self._fake_rules_v4(FAKE_FW_ID, apply_list)
|
||||
firewall = self._fake_firewall(rule_list)
|
||||
|
@ -327,21 +334,20 @@ class IptablesFwaasTestCase(base.BaseTestCase):
|
|||
remove_rule = rule_list[1]
|
||||
rule_list.remove(remove_rule)
|
||||
firewall = self._fake_firewall(rule_list)
|
||||
self.firewall.update_firewall(FW_LEGACY, apply_list, firewall)
|
||||
for router_info_inst in apply_list:
|
||||
namespace = router_info_inst.iptables_manager.namespace
|
||||
cmd1 = ['ip', 'netns', 'exec', namespace, 'conntrack',
|
||||
'-D', '-p', 'tcp', '-f', 'ipv4', '--dport', '23']
|
||||
cmd2 = ['ip', 'netns', 'exec', namespace, 'conntrack',
|
||||
'-D', '-p', 'tcp', '-f', 'ipv4', '--dport', '22']
|
||||
calls = [
|
||||
mock.call(cmd1, run_as_root=True, check_exit_code=True,
|
||||
extra_ok_codes=[1]),
|
||||
mock.call(cmd2, run_as_root=True, check_exit_code=True,
|
||||
extra_ok_codes=[1])]
|
||||
self.utils_exec.assert_has_calls(calls)
|
||||
with mock.patch('neutron_fwaas.privileged.'
|
||||
'netlink_lib.list_entries') as list_entries:
|
||||
list_entries.return_value = FAKE_ENTRY
|
||||
self.firewall.update_firewall(FW_LEGACY, apply_list, firewall)
|
||||
calls = [
|
||||
mock.call(namespace, [(4, 'tcp', '1111', '23',
|
||||
'1.1.1.1', '2.2.2.2')])
|
||||
]
|
||||
mock_kill.assert_has_calls(calls)
|
||||
|
||||
def test_remove_conntrack_changed_rule(self):
|
||||
@mock.patch('neutron_fwaas.privileged.netlink_lib.kill_entries')
|
||||
def test_remove_conntrack_changed_rule(self, mock_kill):
|
||||
apply_list = self._fake_apply_list()
|
||||
rule_list = self._fake_rules_v4(FAKE_FW_ID, apply_list)
|
||||
firewall = self._fake_firewall(rule_list)
|
||||
|
@ -349,20 +355,18 @@ class IptablesFwaasTestCase(base.BaseTestCase):
|
|||
income_rule = {'enabled': True,
|
||||
'action': 'deny',
|
||||
'ip_version': 4,
|
||||
'protocol': 'icmp',
|
||||
'protocol': 'tcp',
|
||||
'id': 'fake-fw-rule2'}
|
||||
rule_list[1] = income_rule
|
||||
rule_list[2] = income_rule
|
||||
firewall = self._fake_firewall(rule_list)
|
||||
self.firewall.update_firewall(FW_LEGACY, apply_list, firewall)
|
||||
for router_info_inst in apply_list:
|
||||
namespace = router_info_inst.iptables_manager.namespace
|
||||
cmd1 = ['ip', 'netns', 'exec', namespace, 'conntrack', '-D',
|
||||
'-p', 'tcp', '-f', 'ipv4', '--dport', '22']
|
||||
cmd2 = ['ip', 'netns', 'exec', namespace, 'conntrack', '-D',
|
||||
'-p', 'icmp', '-f', 'ipv4']
|
||||
calls = [
|
||||
mock.call(cmd1, run_as_root=True, check_exit_code=True,
|
||||
extra_ok_codes=[1]),
|
||||
mock.call(cmd2, run_as_root=True, check_exit_code=True,
|
||||
extra_ok_codes=[1])]
|
||||
self.utils_exec.assert_has_calls(calls)
|
||||
with mock.patch('neutron_fwaas.privileged.'
|
||||
'netlink_lib.list_entries') as list_entries:
|
||||
list_entries.return_value = FAKE_ENTRY
|
||||
self.firewall.update_firewall(FW_LEGACY, apply_list, firewall)
|
||||
calls = [
|
||||
mock.call(namespace, [(4, 'tcp', '1111', '23',
|
||||
'1.1.1.1', '2.2.2.2')])
|
||||
]
|
||||
mock_kill.assert_has_calls(calls)
|
||||
|
|
|
@ -16,7 +16,8 @@ oslo.messaging>=5.14.0 # Apache-2.0
|
|||
oslo.serialization>=1.10.0 # Apache-2.0
|
||||
oslo.service>=1.10.0 # Apache-2.0
|
||||
oslo.utils>=3.18.0 # Apache-2.0
|
||||
|
||||
oslo.privsep>=1.9.0 # Apache-2.0
|
||||
pyroute2>=0.4.12 # Apache-2.0 (+ dual licensed GPL2)
|
||||
# This project does depend on neutron as a library, but the
|
||||
# openstack tooling does not play nicely with projects that
|
||||
# are not publicly available in pypi.
|
||||
|
|
Loading…
Reference in New Issue