From bc2cf49ade08fa509ac8ca4f53d8f1af55f839a4 Mon Sep 17 00:00:00 2001 From: Julien Fortin Date: Tue, 14 Apr 2020 18:19:10 +0200 Subject: [PATCH] nlmanager: ipnetwork: don't set prefixlen flag if prefix has default value nlmanager: ipnetwork: fix 'IPv4Address' object has no attribute 'split' exception nlpacket: use IP[v4|6]Address object when network mask is not provided or needed nlpacket: AttributeIPAddress: fix decode handler for Routes During the python2 to 3 migration there was some refactoring Seems like some code specific to Route-decoding was removed This patch is fixing the issue by re-adding this code and tweaking it a little bit (to make it nice and clean :)) Signed-off-by: Julien Fortin --- ifupdown2/nlmanager/ipnetwork.py | 38 ++++++++++++++++++++++-------- ifupdown2/nlmanager/nlpacket.py | 40 ++++++++++++++++++-------------- 2 files changed, 50 insertions(+), 28 deletions(-) diff --git a/ifupdown2/nlmanager/ipnetwork.py b/ifupdown2/nlmanager/ipnetwork.py index 0c1954e..2e02554 100644 --- a/ifupdown2/nlmanager/ipnetwork.py +++ b/ifupdown2/nlmanager/ipnetwork.py @@ -27,16 +27,25 @@ class IPNetwork: __INIT_WITH_PREFIXLEN = 0b01 - def __init__(self, ip, prefixlen=None, scope=0): + ip_family_handler = { + 0: ipaddress.ip_address, + 4: ipaddress.IPv4Address, + 6: ipaddress.IPv6Address + } + + def __init__(self, ip, prefixlen=None, scope=0, family=0): + + if isinstance(ip, IPNetwork): + prefixlen = ip.prefixlen + scope = ip.scope + family = ip.version + ip = ip.ip + self.__scope = scope self.__flags = 0 if isinstance(ip, int): - self._ip = ipaddress.ip_address(ip) - ip = str(self._ip) - elif isinstance(ip, IPNetwork): - self._ip = ip._ip - self.__prefixlen = ip.prefixlen + self._ip = self.ip_family_handler[family](ip) else: if not prefixlen: try: @@ -53,11 +62,12 @@ class IPNetwork: self.__prefixlen = int(prefixlen) except ValueError: if isinstance(prefixlen, str) and "." in prefixlen: - self.__prefixlen = ipaddress.ip_network("{}/{}".format(ip, prefixlen), strict=False).prefixlen + self.__prefixlen = ipaddress.ip_network("{}/{}".format(self.ip, prefixlen), strict=False).prefixlen else: raise - self.__flags |= self.__INIT_WITH_PREFIXLEN + if (self.ip.version == 4 and self.prefixlen != 32) or (self.ip.version == 6 and self.prefixlen != 128): + self.__flags |= self.__INIT_WITH_PREFIXLEN def __hash__(self): return int(self._ip) ^ self.__prefixlen ^ self.version @@ -105,7 +115,7 @@ class IPNetwork: class IPv4Network(IPNetwork): def __init__(self, *args, **kwargs): - super(IPv4Network, self).__init__(*args, **kwargs) + super(IPv4Network, self).__init__(family=4, *args, **kwargs) if self.version != 4: self._ip = ipaddress.IPv4Address(self._ip) @@ -113,7 +123,7 @@ class IPv4Network(IPNetwork): class IPv6Network(IPNetwork): def __init__(self, *args, **kwargs): - super(IPv6Network, self).__init__(*args, **kwargs) + super(IPv6Network, self).__init__(family=6, *args, **kwargs) if self.version != 6: self._ip = ipaddress.IPv6Address(self._ip) @@ -151,3 +161,11 @@ class IPv4Address(IPv4Network): def __repr__(self): return str(self._ip) + +class IPv6Address(IPv6Network): + def __init__(self, *args, **kwargs): + super(IPv6Address, self).__init__(*args, **kwargs) + self.ignore_prefixlen() + + def __repr__(self): + return str(self._ip) diff --git a/ifupdown2/nlmanager/nlpacket.py b/ifupdown2/nlmanager/nlpacket.py index e892f47..c855e5e 100644 --- a/ifupdown2/nlmanager/nlpacket.py +++ b/ifupdown2/nlmanager/nlpacket.py @@ -1053,12 +1053,12 @@ class Attribute(object): @staticmethod def decode_ipv4_address_attribute(data, _=None): - return ipnetwork.IPNetwork(unpack(">L", data[4:8])[0]) + return ipnetwork.IPv4Address(unpack(">L", data[4:8])[0]) @staticmethod def decode_ipv6_address_attribute(data, _=None): (data1, data2) = unpack(">QQ", data[4:20]) - return ipnetwork.IPNetwork(data1 << 64 | data2) + return ipnetwork.IPv6Address(data1 << 64 | data2) @staticmethod def decode_bond_ad_info_attribute(data, info_data_end): @@ -1391,11 +1391,11 @@ class AttributeIPAddress(Attribute): prefixlen = parent_msg.dst_len if self.family in (AF_INET, AF_BRIDGE): - self.value = ipnetwork.IPNetwork(unpack(self.PACK, self.data[4:])[0], prefixlen, scope) + self.value = ipnetwork.IPv4Network(unpack(self.PACK, self.data[4:])[0], prefixlen, scope) elif self.family == AF_INET6: (data1, data2) = unpack(self.PACK, self.data[4:]) - self.value = ipnetwork.IPNetwork(data1 << 64 | data2, prefixlen, scope) + self.value = ipnetwork.IPv6Network(data1 << 64 | data2, prefixlen, scope) else: self.log.debug("AttributeIPAddress: decode: unsupported address family ({})".format(self.family)) @@ -1437,6 +1437,12 @@ class AttributeIPAddress(Attribute): return line_number +class AttributeIPAddressNoMask(AttributeIPAddress): + def decode(self, *args, **kwargs): + super(AttributeIPAddressNoMask, self).decode(*args, **kwargs) + self.value = self.value.ip + + class AttributeMACAddress(Attribute): def __init__(self, atype, string, family, logger): @@ -1453,7 +1459,7 @@ class AttributeMACAddress(Attribute): try: # GRE interface uses a 4-byte IP address for this attribute if self.length == 8: - self.value = ipnetwork.IPNetwork(unpack('>L', self.data[4:])[0]) + self.value = ipnetwork.IPv4Address(unpack('>L', self.data[4:])[0]) # MAC Address elif self.length == 10: @@ -1462,7 +1468,7 @@ class AttributeMACAddress(Attribute): self.value = mac_int_to_str(self.raw) # GREv6 interface uses a 16-byte IP address for this attribute elif self.length == 20: - self.value = ipnetwork.IPNetwork(unpack('>L', self.data[16:])[0]) + self.value = ipnetwork.IPv6Address(unpack('>L', self.data[16:])[0]) else: self.log.info("Length of MACAddress attribute not supported: %d" % self.length) @@ -1959,14 +1965,14 @@ struct rtnexthop { if self.family == AF_INET: if len(data) < self.IPV4_LEN: break - nexthop = ipnetwork.IPNetwork(unpack('>L', data[:self.IPV4_LEN])[0]) + nexthop = ipnetwork.IPv4Address(unpack('>L', data[:self.IPV4_LEN])[0]) self.value.append((nexthop, rtnh_ifindex, rtnh_flags, rtnh_hops)) elif self.family == AF_INET6: if len(data) < self.IPV6_LEN: break (data1, data2) = unpack('>QQ', data[:self.IPV6_LEN]) - nexthop = ipnetwork.IPNetwork(data1 << 64 | data2) + nexthop = ipnetwork.IPv6Address(data1 << 64 | data2) self.value.append((nexthop, rtnh_ifindex, rtnh_flags, rtnh_hops)) data = data[(rtnh_len-self.RTNH_LEN-self.HEADER_LEN):] @@ -2845,8 +2851,7 @@ class AttributeIFLA_LINKINFO(Attribute): sub_attr_payload[sub_attr_length_index] = sub_attr_length # add padding - for x in range(self.pad_bytes_needed(sub_attr_length)): - sub_attr_pack_layout.append('x') + sub_attr_pack_layout[-1] = "%s%s" % (sub_attr_pack_layout[-1], "x" * self.pad_bytes_needed(sub_attr_length)) # The [1:] is to remove the leading = so that when we do the ''.join() later # we do not end up with an = in the middle of the pack layout string. There @@ -3088,7 +3093,6 @@ class AttributeIFLA_PROTINFO(Attribute): # # Until we cross that bridge though we will keep things nice and simple and # pack everything via a single pack() call. - for (sub_attr_type, sub_attr_value) in self.value.items(): sub_attr_pack_layout = ['=', 'HH'] sub_attr_payload = [0, sub_attr_type] @@ -4502,10 +4506,10 @@ class AttributeMDBA_MDB(Attribute): info = [ifindex,state,flags,vid] proto = unpack('=H',sub_attr_data[28:30])[0] if proto == htons(ETH_P_IP): - ip_addr = ipnetwork.IPNetwork(unpack('>L', sub_attr_data[12:16])[0]) + ip_addr = ipnetwork.IPv4Address(unpack('>L', sub_attr_data[12:16])[0]) else: (data1, data2) = unpack('>QQ',sub_attr_data[12:28]) - ip_addr = ipnetwork.IPNetwork(data1 << 64 | data2) + ip_addr = ipnetwork.IPv6Address(data1 << 64 | data2) info.append(ip_addr) @@ -4540,10 +4544,10 @@ class AttributeMDBA_MDB(Attribute): info = list(info) proto = unpack('=H',sub_attr_data[28:30])[0] if proto == 8: - ip_addr = ipnetwork.IPNetwork(unpack('>L', sub_attr_data[12:16])[0]) + ip_addr = ipnetwork.IPv4Address(unpack('>L', sub_attr_data[12:16])[0]) else: (data1, data2) = unpack('>QQ',sub_attr_data[12:28]) - ip_addr = ipnetwork.IPNetwork(data1 << 64 | data2) + ip_addr = ipnetwork.IPv6Address(data1 << 64 | data2) info.append(ip_addr) self.value[MDB.MDBA_MDB_ENTRY][MDB.MDBA_MDB_ENTRY_INFO] = info @@ -4677,7 +4681,7 @@ class AttributeMDBA_SET_ENTRY(Attribute): elif proto == htons(ETH_P_IPV6): self.PACK = '=IBBHQQHxx' (ifindex, flags, state,vid, data1,data2, proto) = unpack(self.PACK, self.data[4:]) - ip = ipnetwork.IPNetwork(data1 << 64 | data2) + ip = ipnetwork.IPv6Address(data1 << 64 | data2) else: raise Exception("%d Invalid Proto" % proto) self.LEN = calcsize(self.PACK) @@ -4951,7 +4955,7 @@ class Neighbor(NetlinkPacket): attribute_to_class = { NDA_UNSPEC : ('NDA_UNSPEC', AttributeGeneric), - NDA_DST : ('NDA_DST', AttributeIPAddress), + NDA_DST : ('NDA_DST', AttributeIPAddressNoMask), NDA_LLADDR : ('NDA_LLADDR', AttributeMACAddress), NDA_CACHEINFO : ('NDA_CACHEINFO', AttributeFourByteList), NDA_PROBES : ('NDA_PROBES', AttributeFourByteValue), @@ -5300,7 +5304,7 @@ class Route(NetlinkPacket): dst = self.get_attribute_value(self.RTA_DST) if dst: - return "%s/%d" % (dst, self.src_len) + return "%s" % dst else: if self.family == AF_INET: return "0.0.0.0/0"