Use system protocol assigments for iptables protocol map
Merge the system protocol assignments into the iptables name to protocol mapping array, IPTABLES_PROTOCOL_NAME_MAP, so that systems with updated or new values in /etc/protocols can successfully install iptables rules. This was done as an IptablesFirewallDriver() instance mapping since there is typically only a single instance per-agent, and it also allows us to more easily unit test it. Change-Id: Ib73def4e2a9e3644462fdee312768382fcb800a5 Closes-Bug: #1783378
This commit is contained in:
parent
f4422ee121
commit
034db863a0
|
@ -14,9 +14,13 @@
|
|||
# under the License.
|
||||
|
||||
import collections
|
||||
import ctypes
|
||||
from ctypes import util
|
||||
import sys
|
||||
|
||||
import netaddr
|
||||
from neutron_lib import constants
|
||||
from neutron_lib.utils import helpers
|
||||
from oslo_config import cfg
|
||||
from oslo_log import log as logging
|
||||
from oslo_utils import netutils
|
||||
|
@ -40,6 +44,7 @@ CHAIN_NAME_PREFIX = {constants.INGRESS_DIRECTION: 'i',
|
|||
IPSET_DIRECTION = {constants.INGRESS_DIRECTION: 'src',
|
||||
constants.EGRESS_DIRECTION: 'dst'}
|
||||
comment_rule = iptables_manager.comment_rule
|
||||
libc = ctypes.CDLL(util.find_library('libc.so.6'))
|
||||
|
||||
|
||||
def get_hybrid_port_name(port_name):
|
||||
|
@ -87,6 +92,7 @@ class IptablesFirewallDriver(firewall.FirewallDriver):
|
|||
self.updated_rule_sg_ids = set()
|
||||
self.updated_sg_members = set()
|
||||
self.devices_with_updated_sg_members = collections.defaultdict(list)
|
||||
self._iptables_protocol_name_map = {}
|
||||
|
||||
@property
|
||||
def ports(self):
|
||||
|
@ -663,10 +669,44 @@ class IptablesFirewallDriver(firewall.FirewallDriver):
|
|||
comment=ic.ALLOW_ASSOC)]
|
||||
return iptables_rules
|
||||
|
||||
def _local_protocol_name_map(self):
|
||||
local_protocol_name_map = {}
|
||||
try:
|
||||
class protoent(ctypes.Structure):
|
||||
_fields_ = [("p_name", ctypes.c_char_p),
|
||||
("p_aliases", ctypes.POINTER(ctypes.c_char_p)),
|
||||
("p_proto", ctypes.c_int)]
|
||||
libc.getprotoent.restype = ctypes.POINTER(protoent)
|
||||
libc.setprotoent(0)
|
||||
while True:
|
||||
pr = libc.getprotoent()
|
||||
if not pr:
|
||||
break
|
||||
r = pr[0]
|
||||
p_name = helpers.safe_decode_utf8(r.p_name)
|
||||
local_protocol_name_map[str(r.p_proto)] = p_name
|
||||
except Exception:
|
||||
LOG.exception("Unable to create local protocol name map: %s",
|
||||
sys.exc_info()[0])
|
||||
finally:
|
||||
libc.endprotoent()
|
||||
return local_protocol_name_map
|
||||
|
||||
def _protocol_name_map(self):
|
||||
if not self._iptables_protocol_name_map:
|
||||
tmp_map = n_const.IPTABLES_PROTOCOL_NAME_MAP.copy()
|
||||
tmp_map.update(self._local_protocol_name_map())
|
||||
self._iptables_protocol_name_map = tmp_map
|
||||
return self._iptables_protocol_name_map
|
||||
|
||||
def _iptables_protocol_name(self, protocol):
|
||||
# protocol zero is a special case and requires no '-p'
|
||||
if protocol and protocol != '0':
|
||||
return self._protocol_name_map().get(protocol, protocol)
|
||||
|
||||
def _protocol_arg(self, protocol, is_port):
|
||||
iptables_rule = []
|
||||
rule_protocol = n_const.IPTABLES_PROTOCOL_NAME_MAP.get(protocol,
|
||||
protocol)
|
||||
rule_protocol = self._iptables_protocol_name(protocol)
|
||||
# protocol zero is a special case and requires no '-p'
|
||||
if rule_protocol:
|
||||
iptables_rule = ['-p', rule_protocol]
|
||||
|
@ -682,7 +722,7 @@ class IptablesFirewallDriver(firewall.FirewallDriver):
|
|||
if port_range_min is None:
|
||||
return args
|
||||
|
||||
protocol = n_const.IPTABLES_PROTOCOL_NAME_MAP.get(protocol, protocol)
|
||||
protocol = self._iptables_protocol_name(protocol)
|
||||
if protocol in ['icmp', 'ipv6-icmp']:
|
||||
protocol_type = 'icmpv6' if protocol == 'ipv6-icmp' else 'icmp'
|
||||
# Note(xuhanp): port_range_min/port_range_max represent
|
||||
|
|
|
@ -439,6 +439,22 @@ class IptablesFirewallTestCase(BaseIptablesFirewallTestCase):
|
|||
egress = None
|
||||
self._test_prepare_port_filter(rule, ingress, egress)
|
||||
|
||||
def test_filter_ipv4_ingress_protocol_999_local(self):
|
||||
# There is no protocol 999, so let's return a mapping
|
||||
# that says there is and make sure the rule is created
|
||||
# using the name and not the number.
|
||||
rule = {'ethertype': 'IPv4',
|
||||
'direction': 'ingress',
|
||||
'protocol': '999'}
|
||||
ingress = mock.call.add_rule('ifake_dev',
|
||||
'-p fooproto -j RETURN',
|
||||
top=False, comment=None)
|
||||
egress = None
|
||||
with mock.patch.object(self.firewall,
|
||||
'_local_protocol_name_map') as lpnm:
|
||||
lpnm.return_value = {'999': 'fooproto'}
|
||||
self._test_prepare_port_filter(rule, ingress, egress)
|
||||
|
||||
def test_filter_ipv4_egress(self):
|
||||
rule = {'ethertype': 'IPv4',
|
||||
'direction': 'egress'}
|
||||
|
|
Loading…
Reference in New Issue