[ovs-dev] [PATCH 3/4] ipsec: IPv6 default route support for Libreswan

Mark Gray mark.d.gray at redhat.com
Tue Mar 30 16:15:20 UTC 2021


On 30/03/2021 15:28, Aaron Conole wrote:
> Mark Gray <mark.d.gray at redhat.com> writes:
> 
>> When configuring IPsec, "ovs-monitor-ipsec" honours
>> the 'local_ip' option in the 'Interface' table by configuring
>> the 'left' side of the Libreswan connection with 'local_ip'.
>> If 'local_ip' is not specified, "ovs-monitor-ipsec" sets
>> 'left' to '%defaultroute' which is interpreted as the IP
>> address of the default gateway interface.
>>
>> However, when 'remote_ip' is an IPv6 address, Libreswan
>> still interprets '%defaultroute' as the IPv4 address on the
>> default gateway interface (see:
>> https://github.com/libreswan/libreswan/issues/416) giving
>> an "address family inconsistency" error.
>>
>> This patch resolves this issue by specifying the
>> connection as IPv6 when the 'remote_ip' is IPv6 and
>> 'local_ip' has not been set.
>>
>> Signed-off-by: Mark Gray <mark.d.gray at redhat.com>
>> ---
>>  ipsec/ovs-monitor-ipsec.in | 54 +++++++++++++++++++++++++++++++++++++-
>>  1 file changed, 53 insertions(+), 1 deletion(-)
>>
>> diff --git a/ipsec/ovs-monitor-ipsec.in b/ipsec/ovs-monitor-ipsec.in
>> index 9f412aaaf25a..b8cfb0a8ae79 100755
>> --- a/ipsec/ovs-monitor-ipsec.in
>> +++ b/ipsec/ovs-monitor-ipsec.in
>> @@ -14,10 +14,11 @@
>>  # limitations under the License.
>>  
>>  import argparse
>> +import copy
> 
> I think it's okay to get things in alphabetical order, but it's not
> related.

I'll change back

> 
>> +import ipaddress
>>  import re
>>  import subprocess
>>  import sys
>> -import copy
>>  import os
>>  from string import Template
>>  
>> @@ -413,6 +414,11 @@ conn prevent_unencrypted_vxlan
>>      leftprotoport=udp/4789
>>      mark={0}
>>  
>> +"""
>> +
>> +    IPV6_CONN = """\
>> +    hostaddrfamily=ipv6
>> +    clientaddrfamily=ipv6
>>  """
>>  
>>      auth_tmpl = {"psk": Template("""\
>> @@ -528,6 +534,9 @@ conn prevent_unencrypted_vxlan
>>          else:
>>              auth_section = self.auth_tmpl["pki_ca"].substitute(tunnel.conf)
>>  
>> +        if tunnel.conf["address_family"] == "IPv6":
>> +            auth_section = self.IPV6_CONN + auth_section
>> +
>>          vals = tunnel.conf.copy()
>>          vals["auth_section"] = auth_section
>>          vals["version"] = tunnel.version
>> @@ -795,6 +804,7 @@ class IPsecTunnel(object):
>>    Tunnel Type:    $tunnel_type
>>    Local IP:       $local_ip
>>    Remote IP:      $remote_ip
>> +  Address Family: $address_family
>>    SKB mark:       $skb_mark
>>    Local cert:     $certificate
>>    Local name:     $local_name
>> @@ -836,6 +846,9 @@ class IPsecTunnel(object):
>>              "tunnel_type": row.type,
>>              "local_ip": options.get("local_ip", "%defaultroute"),
>>              "remote_ip": options.get("remote_ip"),
>> +            "address_family": self._get_conn_address_family(
>> +                                                   options.get("remote_ip"),
>> +                                                   options.get("local_ip")),
>>              "skb_mark": monitor.conf["skb_mark"],
>>              "certificate": monitor.conf["pki"]["certificate"],
>>              "private_key": monitor.conf["pki"]["private_key"],
>> @@ -904,6 +917,24 @@ class IPsecTunnel(object):
>>  
>>          return header + conf + status + spds + sas + cons + "\n"
>>  
>> +    def _get_conn_address_family(self, remote_ip, local_ip):
>> +        remote = address_family(remote_ip)
>> +        local = address_family(local_ip)
>> +
>> +        if local == "IPv4" and remote == "IPv4":
>> +            return "IPv4"
>> +        elif local == "IPv6" and remote == "IPv6":
>> +            return "IPv6"
>> +        elif remote == "IPv4" and local_ip is None:
>> +            return "IPv4"
>> +        elif remote == "IPv6" and local_ip is None:
>> +            return "IPv6"
>> +        elif remote != local:
>> +            # remote family and local family are mismatched
>> +            return None
>> +        else:
>> +            return None
>> +
> 
> I think we can shrink this whole section to:
> 
> 
>     def _get_conn_address_family(self, remote_ip, local_ip):
>         remote = address_family(remote_ip)
>         local = address_family(local_ip)
> 
>         if local is None:
>            return remote
>         elif local != remote:
>            return None
> 
>         return remote

Yes, you are right. Simpler.
> 
> 
>>      def _is_valid_tunnel_conf(self):
>>          """This function verifies if IPsec tunnel has valid configuration
>>          set in 'conf'.  If it is valid, then it returns True.  Otherwise,
>> @@ -1160,6 +1191,27 @@ class IPsecMonitor(object):
>>  
>>          return m.group(1)
>>  
>> +def is_ipv4(address):
>> +    try:
>> +        ipaddress.IPv4Address(address)
>> +    except ipaddress.AddressValueError:
>> +        return False
>> +    return True
>> +
>> +def is_ipv6(address):
>> +    try:
>> +        ipaddress.IPv6Address(address)
>> +    except ipaddress.AddressValueError:
>> +        return False
>> +    return True
>> +
>> +def address_family(address):
>> +    if is_ipv4(address):
>> +        return "IPv4"
>> +    elif is_ipv6(address):
>> +        return "IPv6"
>> +    else:
>> +        return None
> 
> Maybe the above 3 functions can look like:
> 
> 
>    def address_family(address):
>       try:
>          ip = ipaddress.ip_address(address)
>          ipstr = str(type(ip))
>       except ValueError:
>          return None
> 
>       if ipstr.find('v6') != -1:
>          return "IPv6"
>       return "IPv4"
> 
Yes, better.
> 
> Note that I use ValueError above, because when I tried to test locally,
> I got the following from ipaddress:
> 
>    ValueError: 'asdffdsa' does not appear to be an IPv4 or IPv6 address

IIRC I did try an invalid IP address. Maybe the behavior depends on the
version of Python. I tested this on my machine and got the same behavior
as you so I will accept that change
> 
> And the block:
> 
>    >>> try:
>    ...     ip6=ipaddress.ip_address('asdffdsa')
>    ... except ipaddress.AddressValueError:
>    ...     print("nope")
>    ... 
>    Traceback (most recent call last):
>      File "<stdin>", line 2, in <module>
>      File "/usr/lib64/python3.7/ipaddress.py", line 54, in ip_address
>        address)
>    ValueError: 'asdffdsa' does not appear to be an IPv4 or IPv6 address
>    >>> try:
>    ...     ip6=ipaddress.ip_address('asdffdsa')
>    ... except ValueError:
>    ...     print("nope")
>    ... 
>    nope
> 
> So, I guess even though it implies that it will always throw
> AddressValueException in the documentation, it doesn't :-/
> 
>>  
>>  def unixctl_xfrm_policies(conn, unused_argv, unused_aux):
>>      global xfrm
> 



More information about the dev mailing list