Support IPv6 for netlink_conntrack
Currently, netlink_conntrack driver does not work with IPv6 - support is added with this change. Change-Id: I9f86f936389f8c895345f23ba99b2a7b792e4e0b
This commit is contained in:
parent
469593e84d
commit
9ff85d60db
|
@ -78,5 +78,9 @@ NFCT_CB_FAILURE = -1
|
|||
NFNL_SUBSYS_CTNETLINK = 0
|
||||
|
||||
BUFFER = 1024
|
||||
# IPv6 address memory buffer
|
||||
ADDR_BUFFER_6 = 16
|
||||
ADDR_BUFFER_4 = 4
|
||||
|
||||
IPVERSION_SOCKET = {4: socket.AF_INET, 6: socket.AF_INET6}
|
||||
IPVERSION_BUFFER = {4: ADDR_BUFFER_4, 6: ADDR_BUFFER_6}
|
||||
|
|
|
@ -56,6 +56,7 @@ DATA_CALLBACK = None
|
|||
|
||||
ATTR_POSITIONS = {
|
||||
'icmp': [('type', 6), ('code', 7), ('src', 4), ('dst', 5), ('id', 8)],
|
||||
'icmpv6': [('type', 6), ('code', 7), ('src', 4), ('dst', 5), ('id', 8)],
|
||||
'tcp': [('sport', 7), ('dport', 8), ('src', 5), ('dst', 6)],
|
||||
'udp': [('sport', 6), ('dport', 7), ('src', 4), ('dst', 5)]
|
||||
}
|
||||
|
@ -91,10 +92,10 @@ class ConntrackManager(object):
|
|||
def __init__(self, family_socket=None):
|
||||
self.family_socket = family_socket
|
||||
self.set_functions = {
|
||||
'src': {4: nfct.nfct_set_attr_u32,
|
||||
6: nfct.nfct_set_attr_u64},
|
||||
'dst': {4: nfct.nfct_set_attr_u32,
|
||||
6: nfct.nfct_set_attr_u64},
|
||||
'src': {4: nfct.nfct_set_attr,
|
||||
6: nfct.nfct_set_attr},
|
||||
'dst': {4: nfct.nfct_set_attr,
|
||||
6: nfct.nfct_set_attr},
|
||||
'ipversion': {4: nfct.nfct_set_attr_u8,
|
||||
6: nfct.nfct_set_attr_u8},
|
||||
'protocol': {4: nfct.nfct_set_attr_u8,
|
||||
|
@ -109,8 +110,8 @@ class ConntrackManager(object):
|
|||
6: nfct.nfct_set_attr_u16},
|
||||
'dport': {4: nfct.nfct_set_attr_u16,
|
||||
6: nfct.nfct_set_attr_u16}, }
|
||||
self.converters = {'src': libc.inet_addr,
|
||||
'dst': libc.inet_addr,
|
||||
self.converters = {'src': str,
|
||||
'dst': str,
|
||||
'ipversion': nl_constants.IPVERSION_SOCKET.get,
|
||||
'protocol': constants.IP_PROTOCOL_MAP.get,
|
||||
'code': int,
|
||||
|
@ -164,12 +165,22 @@ class ConntrackManager(object):
|
|||
if result == nl_constants.NFCT_CB_FAILURE:
|
||||
LOG.warning("Netlink query failed")
|
||||
|
||||
def _convert_text_to_binary(self, source, addr_family):
|
||||
dest = ctypes.create_string_buffer(
|
||||
nl_constants.IPVERSION_BUFFER[addr_family])
|
||||
libc.inet_pton(nl_constants.IPVERSION_SOCKET[addr_family],
|
||||
source, dest)
|
||||
return dest.raw
|
||||
|
||||
def _set_attributes(self, conntrack, entry):
|
||||
ipversion = entry.get('ipversion', 4)
|
||||
for attr, value in entry.items():
|
||||
set_function = self.set_functions[attr][ipversion]
|
||||
target = TARGET[attr][ipversion]
|
||||
converter = self.converters[attr]
|
||||
if attr in ['src', 'dst']:
|
||||
# convert src and dst of IPv4 and IPv6 into same format
|
||||
value = self._convert_text_to_binary(value, ipversion)
|
||||
set_function(conntrack, target, converter(value))
|
||||
|
||||
def _callback_register(self, message_type, callback_func, data):
|
||||
|
|
|
@ -13,6 +13,7 @@
|
|||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
from neutron_lib import constants
|
||||
from oslo_log import log as logging
|
||||
|
||||
from neutron_fwaas.privileged import netlink_lib as nl_lib
|
||||
|
@ -110,16 +111,21 @@ class ConntrackNetlink(conntrack_base.ConntrackDriverBase):
|
|||
ENTRY_MATCHES = 0
|
||||
ENTRY_IS_HIGHER = 1
|
||||
rule_ipversion = rule_filter[0]
|
||||
|
||||
if entry[0] < rule_ipversion:
|
||||
return ENTRY_IS_LOWER
|
||||
elif entry[0] > rule_ipversion:
|
||||
return ENTRY_IS_HIGHER
|
||||
rule_protocol = rule_filter[1]
|
||||
|
||||
if rule_protocol:
|
||||
if rule_protocol == constants.PROTO_NAME_IPV6_ICMP:
|
||||
rule_protocol = constants.PROTO_NAME_IPV6_ICMP_LEGACY
|
||||
if entry[1] < rule_protocol:
|
||||
return ENTRY_IS_LOWER
|
||||
elif entry[1] > rule_protocol:
|
||||
return ENTRY_IS_HIGHER
|
||||
|
||||
sport_range = rule_filter[2]
|
||||
if sport_range:
|
||||
sport_range = [int(port) for port in sport_range]
|
||||
|
|
|
@ -16,24 +16,34 @@
|
|||
from neutron.agent.linux import utils as linux_utils
|
||||
from neutron.tests.common import net_helpers
|
||||
from neutron.tests.functional import base as functional_base
|
||||
from oslo_log import log as logging
|
||||
|
||||
import neutron_fwaas.privileged.netlink_lib as nl_lib
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
CONNTRACK_CMDS = (
|
||||
['conntrack', '-I', '-p', 'tcp',
|
||||
'-s', '1.1.1.1', '-d', '2.2.2.2',
|
||||
'--sport', '1', '--dport', '2',
|
||||
'--state', 'ESTABLISHED', '--timeout', '1234'],
|
||||
['conntrack', '-I', '-p', 'udp',
|
||||
'-s', '1.1.1.1', '-d', '2.2.2.2',
|
||||
'--sport', '1', '--dport', '2',
|
||||
'--timeout', '1234'],
|
||||
['conntrack', '-I', '-p', 'icmp',
|
||||
'-s', '1.1.1.1', '-d', '2.2.2.2',
|
||||
'--icmp-type', '8', '--icmp-code', '0', '--icmp-id', '3333',
|
||||
'--timeout', '1234'],
|
||||
)
|
||||
|
||||
def check_nf_conntrack_ipv6_is_loaded():
|
||||
try:
|
||||
output = linux_utils.execute(['lsmod'])
|
||||
except RuntimeError:
|
||||
msg = "Failed execute command lsmod!"
|
||||
raise RuntimeError(msg)
|
||||
if 'nf_conntrack_ipv6' in output:
|
||||
return True
|
||||
return False
|
||||
|
||||
|
||||
def _create_entries(namespace, conntrack_cmds):
|
||||
for cmd in conntrack_cmds:
|
||||
exec_cmd = ['ip', 'netns', 'exec', namespace] + cmd
|
||||
try:
|
||||
linux_utils.execute(exec_cmd,
|
||||
run_as_root=True,
|
||||
check_exit_code=True,
|
||||
extra_ok_codes=[1])
|
||||
except RuntimeError:
|
||||
raise Exception('Error while creating entry')
|
||||
|
||||
|
||||
class NetlinkLibTestCase(functional_base.BaseSudoTestCase):
|
||||
|
@ -45,20 +55,28 @@ class NetlinkLibTestCase(functional_base.BaseSudoTestCase):
|
|||
as expected.
|
||||
"""
|
||||
|
||||
def _create_entries(self, namespace, conntrack_cmds):
|
||||
for cmd in conntrack_cmds:
|
||||
exec_cmd = ['ip', 'netns', 'exec', namespace] + cmd
|
||||
try:
|
||||
linux_utils.execute(exec_cmd,
|
||||
run_as_root=True,
|
||||
check_exit_code=True,
|
||||
extra_ok_codes=[1])
|
||||
except RuntimeError:
|
||||
raise Exception('Error while creating entry')
|
||||
CONNTRACK_CMDS = (
|
||||
['conntrack', '-I', '-p', 'tcp',
|
||||
'-s', '1.1.1.1', '-d', '2.2.2.2',
|
||||
'--sport', '1', '--dport', '2',
|
||||
'--state', 'ESTABLISHED', '--timeout', '1234'],
|
||||
['conntrack', '-I', '-p', 'udp',
|
||||
'-s', '1.1.1.1', '-d', '2.2.2.2',
|
||||
'--sport', '1', '--dport', '2',
|
||||
'--timeout', '1234'],
|
||||
['conntrack', '-I', '-p', 'icmp',
|
||||
'-s', '1.1.1.1', '-d', '2.2.2.2',
|
||||
'--icmp-type', '8', '--icmp-code', '0', '--icmp-id', '3333',
|
||||
'--timeout', '1234'],
|
||||
['conntrack', '-I', '-p', 'icmp',
|
||||
'-s', '1.1.1.1', '-d', '2.2.2.2',
|
||||
'--icmp-type', '8', '--icmp-code', '0', '--icmp-id', '3333',
|
||||
'--timeout', '1234'],
|
||||
)
|
||||
|
||||
def test_list_entries(self):
|
||||
namespace = self.useFixture(net_helpers.NamespaceFixture()).name
|
||||
self._create_entries(namespace, CONNTRACK_CMDS)
|
||||
_create_entries(namespace, self.CONNTRACK_CMDS)
|
||||
expected = (
|
||||
(4, 'icmp', 8, 0, '1.1.1.1', '2.2.2.2', 3333),
|
||||
(4, 'tcp', 1, 2, '1.1.1.1', '2.2.2.2'),
|
||||
|
@ -69,7 +87,7 @@ class NetlinkLibTestCase(functional_base.BaseSudoTestCase):
|
|||
|
||||
def _delete_entry(self, delete_entries, remain_entries):
|
||||
namespace = self.useFixture(net_helpers.NamespaceFixture()).name
|
||||
self._create_entries(namespace, CONNTRACK_CMDS)
|
||||
_create_entries(namespace, self.CONNTRACK_CMDS)
|
||||
nl_lib.delete_entries(namespace=namespace, entries=delete_entries)
|
||||
entries_list = nl_lib.list_entries(namespace)
|
||||
self.assertEqual(remain_entries, entries_list)
|
||||
|
@ -109,7 +127,61 @@ class NetlinkLibTestCase(functional_base.BaseSudoTestCase):
|
|||
|
||||
def test_flush_entries(self):
|
||||
namespace = self.useFixture(net_helpers.NamespaceFixture()).name
|
||||
self._create_entries(namespace, CONNTRACK_CMDS)
|
||||
_create_entries(namespace, self.CONNTRACK_CMDS)
|
||||
nl_lib.flush_entries(namespace)
|
||||
entries_list = nl_lib.list_entries(namespace)
|
||||
self.assertEqual((), entries_list)
|
||||
|
||||
|
||||
class NetlinkLibTestCaseIPv6(functional_base.BaseSudoTestCase):
|
||||
|
||||
CONNTRACK_CMDS = (
|
||||
['conntrack', '-I', '-p', 'icmp',
|
||||
'-s', '1.1.1.1', '-d', '2.2.2.2',
|
||||
'--icmp-type', '8', '--icmp-code', '0', '--icmp-id', '3333',
|
||||
'--timeout', '1234'],
|
||||
['conntrack', '-I', '-p', 'icmpv6',
|
||||
'-s', '10::10', '-d', '20::20',
|
||||
'--icmpv6-type', '128', '--icmpv6-code', '0', '--icmpv6-id', '3456',
|
||||
'--timeout', '1234'],
|
||||
)
|
||||
|
||||
def setUp(self):
|
||||
super(NetlinkLibTestCaseIPv6, self).setUp()
|
||||
if not check_nf_conntrack_ipv6_is_loaded():
|
||||
self.skipTest(
|
||||
"nf_conntrack_ipv6 module wasn't loaded. Please load"
|
||||
"this module into your system if you want to use "
|
||||
"netlink conntrack with ipv6"
|
||||
)
|
||||
|
||||
def test_list_entries(self):
|
||||
namespace = self.useFixture(net_helpers.NamespaceFixture()).name
|
||||
_create_entries(namespace, self.CONNTRACK_CMDS)
|
||||
expected = (
|
||||
(4, 'icmp', 8, 0, '1.1.1.1', '2.2.2.2', 3333),
|
||||
(6, 'icmpv6', 128, 0, '10::10', '20::20', 3456),
|
||||
)
|
||||
entries_list = nl_lib.list_entries(namespace=namespace)
|
||||
self.assertEqual(expected, entries_list)
|
||||
|
||||
def _delete_entry(self, delete_entries, remain_entries):
|
||||
namespace = self.useFixture(net_helpers.NamespaceFixture()).name
|
||||
_create_entries(namespace, self.CONNTRACK_CMDS)
|
||||
nl_lib.delete_entries(namespace=namespace, entries=delete_entries)
|
||||
entries_list = nl_lib.list_entries(namespace)
|
||||
self.assertEqual(remain_entries, entries_list)
|
||||
|
||||
def test_delete_icmpv6_entry(self):
|
||||
icmp_entry = [(6, 'icmpv6', 128, 0, '10::10', '20::20', 3456)]
|
||||
remain_entries = (
|
||||
(4, 'icmp', 8, 0, '1.1.1.1', '2.2.2.2', 3333),
|
||||
)
|
||||
self._delete_entry(icmp_entry, remain_entries)
|
||||
|
||||
def test_flush_entries(self):
|
||||
namespace = self.useFixture(net_helpers.NamespaceFixture()).name
|
||||
_create_entries(namespace, self.CONNTRACK_CMDS)
|
||||
nl_lib.flush_entries(namespace)
|
||||
entries_list = nl_lib.list_entries(namespace)
|
||||
self.assertEqual((), entries_list)
|
||||
|
|
|
@ -32,6 +32,9 @@ FAKE_TCP_ENTRY = {'ipversion': 4, 'protocol': 'tcp',
|
|||
FAKE_UDP_ENTRY = {'ipversion': 4, 'protocol': 'udp',
|
||||
'sport': 1, 'dport': 2,
|
||||
'src': '1.1.1.1', 'dst': '2.2.2.2'}
|
||||
FAKE_ICMPV6_ENTRY = {'ipversion': 6, 'protocol': 'ipv6-icmp',
|
||||
'sport': 1, 'dport': 2, 'type': '8', 'code': '0',
|
||||
'id': 3456, 'src': '10::10', 'dst': '20::20'}
|
||||
|
||||
|
||||
class NetlinkLibTestCase(base.BaseTestCase):
|
||||
|
@ -107,13 +110,56 @@ class NetlinkLibTestCase(base.BaseTestCase):
|
|||
calls = [
|
||||
mock.call(conntrack_filter,
|
||||
nl_constants.ATTR_IPV4_SRC,
|
||||
nl_lib.libc.inet_addr(FAKE_ENTRY['src'])),
|
||||
str(conntrack._convert_text_to_binary(
|
||||
FAKE_ENTRY['src'], 4))),
|
||||
mock.call(conntrack_filter,
|
||||
nl_constants.ATTR_IPV4_DST,
|
||||
nl_lib.libc.inet_addr(FAKE_ENTRY['dst'])),
|
||||
str(conntrack._convert_text_to_binary(
|
||||
FAKE_ENTRY['dst'], 4))),
|
||||
]
|
||||
nl_lib.nfct.nfct_set_attr_u32.assert_has_calls(calls,
|
||||
any_order=True)
|
||||
nl_lib.nfct.nfct_set_attr.assert_has_calls(calls, any_order=True)
|
||||
nl_lib.nfct.nfct_destroy.assert_called_once()
|
||||
nl_lib.nfct.nfct_close.assert_called_once()
|
||||
|
||||
def test_conntrack_delete_icmpv6_entry(self):
|
||||
conntrack_filter = mock.Mock()
|
||||
nl_lib.nfct.nfct_new.return_value = conntrack_filter
|
||||
with nl_lib.ConntrackManager() as conntrack:
|
||||
nl_lib.nfct.nfct_open.assert_called_once()
|
||||
conntrack.delete_entries([FAKE_ICMPV6_ENTRY])
|
||||
calls = [
|
||||
mock.call(conntrack_filter,
|
||||
nl_constants.ATTR_L3PROTO,
|
||||
nl_constants.IPVERSION_SOCKET[6]),
|
||||
mock.call(conntrack_filter,
|
||||
nl_constants.ATTR_L4PROTO,
|
||||
constants.IP_PROTOCOL_MAP['ipv6-icmp']),
|
||||
mock.call(conntrack_filter,
|
||||
nl_constants.ATTR_ICMP_CODE,
|
||||
int(FAKE_ICMPV6_ENTRY['code'])),
|
||||
mock.call(conntrack_filter,
|
||||
nl_constants.ATTR_ICMP_TYPE,
|
||||
int(FAKE_ICMPV6_ENTRY['type']))
|
||||
]
|
||||
nl_lib.nfct.nfct_set_attr_u8.assert_has_calls(calls,
|
||||
any_order=True)
|
||||
calls = [
|
||||
mock.call(conntrack_filter,
|
||||
nl_constants.ATTR_ICMP_ID,
|
||||
nl_lib.libc.htons(FAKE_ICMPV6_ENTRY['id'])),
|
||||
]
|
||||
nl_lib.nfct.nfct_set_attr_u16.assert_has_calls(calls)
|
||||
calls = [
|
||||
mock.call(conntrack_filter,
|
||||
nl_constants.ATTR_IPV6_SRC,
|
||||
str(conntrack._convert_text_to_binary(
|
||||
FAKE_ENTRY['src'], 6))),
|
||||
mock.call(conntrack_filter,
|
||||
nl_constants.ATTR_IPV6_DST,
|
||||
str(conntrack._convert_text_to_binary(
|
||||
FAKE_ENTRY['dst'], 6))),
|
||||
]
|
||||
nl_lib.nfct.nfct_set_attr.assert_has_calls(calls, any_order=True)
|
||||
nl_lib.nfct.nfct_destroy.assert_called_once()
|
||||
nl_lib.nfct.nfct_close.assert_called_once()
|
||||
|
||||
|
@ -146,13 +192,14 @@ class NetlinkLibTestCase(base.BaseTestCase):
|
|||
calls = [
|
||||
mock.call(conntrack_filter,
|
||||
nl_constants.ATTR_IPV4_SRC,
|
||||
nl_lib.libc.inet_addr(FAKE_UDP_ENTRY['src'])),
|
||||
str(conntrack._convert_text_to_binary(
|
||||
FAKE_UDP_ENTRY['src'], 4))),
|
||||
mock.call(conntrack_filter,
|
||||
nl_constants.ATTR_IPV4_DST,
|
||||
nl_lib.libc.inet_addr(FAKE_UDP_ENTRY['dst'])),
|
||||
str(conntrack._convert_text_to_binary(
|
||||
FAKE_UDP_ENTRY['dst'], 4))),
|
||||
]
|
||||
nl_lib.nfct.nfct_set_attr_u32.assert_has_calls(calls,
|
||||
any_order=True)
|
||||
nl_lib.nfct.nfct_set_attr.assert_has_calls(calls, any_order=True)
|
||||
nl_lib.nfct.nfct_destroy.assert_called_once()
|
||||
nl_lib.nfct.nfct_close.assert_called_once()
|
||||
|
||||
|
@ -185,13 +232,14 @@ class NetlinkLibTestCase(base.BaseTestCase):
|
|||
calls = [
|
||||
mock.call(conntrack_filter,
|
||||
nl_constants.ATTR_IPV4_SRC,
|
||||
nl_lib.libc.inet_addr(FAKE_TCP_ENTRY['src'])),
|
||||
str(conntrack._convert_text_to_binary(
|
||||
FAKE_TCP_ENTRY['src'], 4))),
|
||||
mock.call(conntrack_filter,
|
||||
nl_constants.ATTR_IPV4_DST,
|
||||
nl_lib.libc.inet_addr(FAKE_TCP_ENTRY['dst'])),
|
||||
str(conntrack._convert_text_to_binary(
|
||||
FAKE_TCP_ENTRY['dst'], 4))),
|
||||
]
|
||||
nl_lib.nfct.nfct_set_attr_u32.assert_has_calls(calls,
|
||||
any_order=True)
|
||||
nl_lib.nfct.nfct_set_attr.assert_has_calls(calls, any_order=True)
|
||||
nl_lib.nfct.nfct_destroy.assert_called_once()
|
||||
nl_lib.nfct.nfct_close.assert_called_once()
|
||||
|
||||
|
@ -253,24 +301,29 @@ class NetlinkLibTestCase(base.BaseTestCase):
|
|||
calls = [
|
||||
mock.call(conntrack_filter,
|
||||
nl_constants.ATTR_IPV4_SRC,
|
||||
nl_lib.libc.inet_addr(FAKE_TCP_ENTRY['src'])),
|
||||
str(conntrack._convert_text_to_binary(
|
||||
FAKE_TCP_ENTRY['src'], 4))),
|
||||
mock.call(conntrack_filter,
|
||||
nl_constants.ATTR_IPV4_DST,
|
||||
nl_lib.libc.inet_addr(FAKE_TCP_ENTRY['dst'])),
|
||||
str(conntrack._convert_text_to_binary(
|
||||
FAKE_TCP_ENTRY['dst'], 4))),
|
||||
mock.call(conntrack_filter,
|
||||
nl_constants.ATTR_IPV4_SRC,
|
||||
nl_lib.libc.inet_addr(FAKE_UDP_ENTRY['src'])),
|
||||
str(conntrack._convert_text_to_binary(
|
||||
FAKE_UDP_ENTRY['src'], 4))),
|
||||
mock.call(conntrack_filter,
|
||||
nl_constants.ATTR_IPV4_DST,
|
||||
nl_lib.libc.inet_addr(FAKE_UDP_ENTRY['dst'])),
|
||||
str(conntrack._convert_text_to_binary(
|
||||
FAKE_UDP_ENTRY['dst'], 4))),
|
||||
mock.call(conntrack_filter,
|
||||
nl_constants.ATTR_IPV4_SRC,
|
||||
nl_lib.libc.inet_addr(FAKE_ENTRY['src'])),
|
||||
str(conntrack._convert_text_to_binary(
|
||||
FAKE_ENTRY['src'], 4))),
|
||||
mock.call(conntrack_filter,
|
||||
nl_constants.ATTR_IPV4_DST,
|
||||
nl_lib.libc.inet_addr(FAKE_ENTRY['dst'])),
|
||||
str(conntrack._convert_text_to_binary(
|
||||
FAKE_UDP_ENTRY['dst'], 4))),
|
||||
]
|
||||
nl_lib.nfct.nfct_set_attr_u32.assert_has_calls(calls,
|
||||
any_order=True)
|
||||
nl_lib.nfct.nfct_set_attr.assert_has_calls(calls, any_order=True)
|
||||
nl_lib.nfct.nfct_destroy.assert_called_once()
|
||||
nl_lib.nfct.nfct_close.assert_called_once()
|
||||
|
|
Loading…
Reference in New Issue