Fix iptables mapping of 'ipip' protocol

Map 'ipip' to use the string 'ipencap' so the
IptablesFirewallDriver class in neutron works correctly.
Once neutron-lib is bumped this can be removed.

Add tests for IP protocol 'ipip', '4' and '94' to make
sure the IptablesFirewallDriver class in neutron treats
them correctly.

Long description below.

This is one of those confusing edge cases and I think
Linux is conspiring against us. Let me explain.

1) neutron-lib does correctly define the protocol name 'ipip' as 4.

2) The linux kernel uses the same in in.h:

 IPPROTO_IPIP = 4
 IPPROTO_BEETPH = 94 (?)

3) iptables maps 'ipip' to 94 and 'ipencap' to 4.

 # for num in {0..255}; do iptables -A INPUT -p $num; done
 # iptables-save | grep -E 'ipip|ipencap'
 -A INPUT -p ipencap
 -A INPUT -p ipip

4) /etc/protocols does the same as iptables:

 grep -E 'ipencap|ipip' /etc/protocols
 ipencap 4 IP-ENCAP # IP encapsulated in IP (officially ``IP'')
 ipip 94 IPIP # IP-within-IP Encapsulation Protocol

5) getprotoby{name|number} does what /etc/protocols does:

 $ getprotobyname ipip
 struct protoent: (0x7fbbbcca9c60)
   p_name ipip
   p_aliases IPIP
   p_proto 94

 $ getprotobynumber 4
 struct protoent: (0x7fc51ad86be0)
   p_name ipencap
   p_aliases IP-ENCAP
   p_proto 4

Neutron actually builds a mapping based on the getprotoby*
calls, so in the iptables case it winds-up doing the wrong
thing.

Partial-bug: #2054324
Change-Id: Icc84b54be07d39059723d6c233c03aa130102423
This commit is contained in:
Brian Haley 2024-02-22 18:33:14 -05:00
parent ebb7ec1eed
commit 793dfb04d0
2 changed files with 40 additions and 0 deletions

View File

@ -792,6 +792,10 @@ class IptablesFirewallDriver(firewall.FirewallDriver):
if not self._iptables_protocol_name_map:
tmp_map = constants.IPTABLES_PROTOCOL_NAME_MAP.copy()
tmp_map.update(self._local_protocol_name_map())
# TODO(haleyb): remove once neutron-lib with fix is available
# - 'ipip' uses 'ipencap' to match IPPROTO_IPIP from in.h,
# which is IP-ENCAP/'4' in /etc/protocols (see bug #2054324)
tmp_map[constants.PROTO_NAME_IPIP] = 'ipencap'
self._iptables_protocol_name_map = tmp_map
return self._iptables_protocol_name_map

View File

@ -499,6 +499,42 @@ class IptablesFirewallTestCase(BaseIptablesFirewallTestCase):
egress = None
self._test_prepare_port_filter(rule, ingress, egress)
def test_filter_ipv4_ingress_protocol_ipip(self):
# 'ipip' via the API uses 'ipencap' to match what iptables-save
# uses, which is IP-ENCAP/'4' from /etc/protocols (see bug #2054324)
rule = {'ethertype': 'IPv4',
'direction': 'ingress',
'protocol': 'ipip'}
ingress = mock.call.add_rule('ifake_dev',
'-p ipencap -j RETURN',
top=False, comment=None)
egress = None
self._test_prepare_port_filter(rule, ingress, egress)
def test_filter_ipv4_ingress_protocol_ipip_by_num(self):
# '4' via the API uses 'ipencap' to match what iptables-save
# uses, which is IP-ENCAP/'4' from /etc/protocols (see bug #2054324)
rule = {'ethertype': 'IPv4',
'direction': 'ingress',
'protocol': '4'}
ingress = mock.call.add_rule('ifake_dev',
'-p ipencap -j RETURN',
top=False, comment=None)
egress = None
self._test_prepare_port_filter(rule, ingress, egress)
def test_filter_ipv4_ingress_protocol_ipencap_by_num(self):
# '94' via the API uses 'ipip' to match what iptables-save
# uses, which is IPIP/'94' from /etc/protocols (see bug #2054324)
rule = {'ethertype': 'IPv4',
'direction': 'ingress',
'protocol': '94'}
ingress = mock.call.add_rule('ifake_dev',
'-p ipip -j RETURN',
top=False, comment=None)
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