1#!/usr/bin/env python3 2# 3# Copyright (c) 2019, The OpenThread Authors. 4# All rights reserved. 5# 6# Redistribution and use in source and binary forms, with or without 7# modification, are permitted provided that the following conditions are met: 8# 1. Redistributions of source code must retain the above copyright 9# notice, this list of conditions and the following disclaimer. 10# 2. Redistributions in binary form must reproduce the above copyright 11# notice, this list of conditions and the following disclaimer in the 12# documentation and/or other materials provided with the distribution. 13# 3. Neither the name of the copyright holder nor the 14# names of its contributors may be used to endorse or promote products 15# derived from this software without specific prior written permission. 16# 17# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 18# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 21# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 22# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 23# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 24# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 25# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 26# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 27# POSSIBILITY OF SUCH DAMAGE. 28# 29import ipaddress 30from typing import Union 31 32from pktverify.bytes import Bytes 33 34 35class EthAddr(Bytes): 36 """ 37 Represents an Ethernet address. 38 """ 39 40 def __init__(self, addr: Union[str, bytearray, 'Bytes']): 41 super().__init__(addr) 42 if len(self) not in (6, 8): 43 raise ValueError((addr, self)) 44 45 def __repr__(self): 46 return '%s(%r)' % (self.__class__.__name__, self.format_octets()) 47 48 def __str__(self): 49 return self.format_octets() 50 51 52class ExtAddr(Bytes): 53 """ 54 Represents an WPAN Extended address. 55 """ 56 57 def __init__(self, addr: Union[str, bytearray, 'Bytes']): 58 super().__init__(addr) 59 if len(self) != 8: 60 raise ValueError((addr, self)) 61 62 def __repr__(self): 63 return '%s(%r)' % (self.__class__.__name__, self.format_octets()) 64 65 def __str__(self): 66 return self.format_octets() 67 68 69class Ipv6Addr(Bytes): 70 """ 71 Represents an Ip6 address. 72 """ 73 74 def __init__(self, addr: Union[str, bytearray, 'Bytes']): 75 if isinstance(addr, str): 76 # try to parse compacted ipv6 address 77 try: 78 addr = Ipv6Addr._expand(addr) 79 except ipaddress.AddressValueError: 80 pass 81 82 super().__init__(addr) 83 if len(self) != 16: 84 raise ValueError(addr) 85 self._addr = ipaddress.IPv6Address(self) 86 87 def __repr__(self): 88 return '%s(%r)' % (self.__class__.__name__, self.format_hextets()) 89 90 def __str__(self): 91 return self.format_hextets() 92 93 @staticmethod 94 def _expand(addr) -> str: 95 assert isinstance(addr, str) 96 a = ipaddress.IPv6Address(addr) 97 return a.exploded 98 99 @property 100 def is_global(self) -> bool: 101 """ 102 Returns if the Ip6 address is global. 103 """ 104 if self._addr.is_global: 105 return True 106 107 if self._addr.is_link_local or self._addr.is_multicast or self._addr.is_loopback: 108 return False 109 110 return True 111 112 @property 113 def is_dua(self) -> bool: 114 """ 115 Returns if the Ip6 address is Domain Unicast Address. 116 """ 117 from pktverify import consts 118 return self.startswith(consts.DOMAIN_PREFIX) 119 120 @property 121 def is_backbone_gua(self) -> bool: 122 """ 123 Returns if the Ip6 address is Backbone address. 124 """ 125 from pktverify import consts 126 return self.startswith(consts.BACKBONE_IPV6_PREFIX) 127 128 @property 129 def is_link_local(self) -> bool: 130 """ 131 Returns if the Ip6 address is link local. 132 """ 133 return self._addr.is_link_local 134 135 @property 136 def is_multicast(self) -> bool: 137 """ 138 Returns if the Ip6 address is multicast. 139 """ 140 return self._addr.is_multicast 141 142 @property 143 def is_mleid(self) -> bool: 144 """ 145 Returns if the Ip6 address is ML-EID. 146 """ 147 from pktverify.consts import DEFAULT_MESH_LOCAL_PREFIX 148 return self.startswith(DEFAULT_MESH_LOCAL_PREFIX) 149 150 151if __name__ == '__main__': 152 a = EthAddr("010203040506") 153 assert a == EthAddr("01:02:03:04:05:06") 154 assert a == EthAddr("0102:0304:0506") 155 assert eval(repr(a)) == a 156 assert a == str(a) 157 assert str(a) == a 158 159 print(a, repr(a)) 160 assert isinstance(a[:], Bytes) 161 assert a[:3] == "010203" 162 assert a[3:] == "040506" 163 164 a = ExtAddr("0102030405060708") 165 assert a == ExtAddr("01:02:03:04:05:06:07:08") 166 assert a == ExtAddr("0102:0304:0506:0708") 167 assert eval(repr(a)) == a 168 assert a == str(a) 169 assert str(a) == a 170 171 print(a, repr(a)) 172 assert isinstance(a[:], Bytes) 173 assert a[:4] == "01020304" 174 assert a[4:] == "05060708" 175 176 a = Ipv6Addr("00112233445566778899aabbccddeeff") 177 assert a == Ipv6Addr("00:11:22:33:44:55:66:77:88:99:aa:bb:cc:dd:ee:ff") 178 assert a == Ipv6Addr("0011:2233:4455:6677:8899:aabb:ccdd:eeff") 179 assert eval(repr(a)) == a 180 assert a == str(a) 181 assert str(a) == a 182 183 print(a, repr(a)) 184 assert isinstance(a[:], Bytes) 185 assert a[:4] == "00112233" 186 assert a[-4:] == "ccddeeff" 187 188 assert Ipv6Addr("fd00:db8::ff:fe00:8001") == Ipv6Addr("fd00:0db8:0000:0000:0000:00ff:fe00:8001") 189 print(Ipv6Addr("fdde:ad00:beef:0:9d87:85f0:3358:3fff")) 190