| #!/usr/bin/env python3 |
| # |
| # Copyright (c) 2019, The OpenThread Authors. |
| # All rights reserved. |
| # |
| # Redistribution and use in source and binary forms, with or without |
| # modification, are permitted provided that the following conditions are met: |
| # 1. Redistributions of source code must retain the above copyright |
| # notice, this list of conditions and the following disclaimer. |
| # 2. Redistributions in binary form must reproduce the above copyright |
| # notice, this list of conditions and the following disclaimer in the |
| # documentation and/or other materials provided with the distribution. |
| # 3. Neither the name of the copyright holder nor the |
| # names of its contributors may be used to endorse or promote products |
| # derived from this software without specific prior written permission. |
| # |
| # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" |
| # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
| # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
| # ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE |
| # LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR |
| # CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF |
| # SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS |
| # INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN |
| # CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) |
| # ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE |
| # POSSIBILITY OF SUCH DAMAGE. |
| # |
| import ipaddress |
| from typing import Union |
| |
| from pktverify.bytes import Bytes |
| |
| |
| class EthAddr(Bytes): |
| """ |
| Represents an Ethernet address. |
| """ |
| |
| def __init__(self, addr: Union[str, bytearray, 'Bytes']): |
| super().__init__(addr) |
| if len(self) not in (6, 8): |
| raise ValueError((addr, self)) |
| |
| def __repr__(self): |
| return '%s(%r)' % (self.__class__.__name__, self.format_octets()) |
| |
| def __str__(self): |
| return self.format_octets() |
| |
| |
| class ExtAddr(Bytes): |
| """ |
| Represents an WPAN Extended address. |
| """ |
| |
| def __init__(self, addr: Union[str, bytearray, 'Bytes']): |
| super().__init__(addr) |
| if len(self) != 8: |
| raise ValueError((addr, self)) |
| |
| def __repr__(self): |
| return '%s(%r)' % (self.__class__.__name__, self.format_octets()) |
| |
| def __str__(self): |
| return self.format_octets() |
| |
| |
| class Ipv6Addr(Bytes): |
| """ |
| Represents an Ip6 address. |
| """ |
| |
| def __init__(self, addr: Union[str, bytearray, 'Bytes']): |
| if isinstance(addr, str): |
| # try to parse compacted ipv6 address |
| try: |
| addr = Ipv6Addr._expand(addr) |
| except ipaddress.AddressValueError: |
| pass |
| |
| super().__init__(addr) |
| if len(self) != 16: |
| raise ValueError(addr) |
| self._addr = ipaddress.IPv6Address(self) |
| |
| def __repr__(self): |
| return '%s(%r)' % (self.__class__.__name__, self.format_hextets()) |
| |
| def __str__(self): |
| return self.format_hextets() |
| |
| @staticmethod |
| def _expand(addr) -> str: |
| assert isinstance(addr, str) |
| a = ipaddress.IPv6Address(addr) |
| return a.exploded |
| |
| @property |
| def is_global(self) -> bool: |
| """ |
| Returns if the Ip6 address is global. |
| """ |
| if self._addr.is_global: |
| return True |
| |
| if self._addr.is_link_local or self._addr.is_multicast or self._addr.is_loopback: |
| return False |
| |
| return True |
| |
| @property |
| def is_dua(self) -> bool: |
| """ |
| Returns if the Ip6 address is Domain Unicast Address. |
| """ |
| from pktverify import consts |
| return self.startswith(consts.DOMAIN_PREFIX) |
| |
| @property |
| def is_backbone_gua(self) -> bool: |
| """ |
| Returns if the Ip6 address is Backbone address. |
| """ |
| from pktverify import consts |
| return self.startswith(consts.BACKBONE_IPV6_PREFIX) |
| |
| @property |
| def is_link_local(self) -> bool: |
| """ |
| Returns if the Ip6 address is link local. |
| """ |
| return self._addr.is_link_local |
| |
| @property |
| def is_multicast(self) -> bool: |
| """ |
| Returns if the Ip6 address is multicast. |
| """ |
| return self._addr.is_multicast |
| |
| @property |
| def is_mleid(self) -> bool: |
| """ |
| Returns if the Ip6 address is ML-EID. |
| """ |
| from pktverify.consts import DEFAULT_MESH_LOCAL_PREFIX |
| return self.startswith(DEFAULT_MESH_LOCAL_PREFIX) |
| |
| |
| if __name__ == '__main__': |
| a = EthAddr("010203040506") |
| assert a == EthAddr("01:02:03:04:05:06") |
| assert a == EthAddr("0102:0304:0506") |
| assert eval(repr(a)) == a |
| assert a == str(a) |
| assert str(a) == a |
| |
| print(a, repr(a)) |
| assert isinstance(a[:], Bytes) |
| assert a[:3] == "010203" |
| assert a[3:] == "040506" |
| |
| a = ExtAddr("0102030405060708") |
| assert a == ExtAddr("01:02:03:04:05:06:07:08") |
| assert a == ExtAddr("0102:0304:0506:0708") |
| assert eval(repr(a)) == a |
| assert a == str(a) |
| assert str(a) == a |
| |
| print(a, repr(a)) |
| assert isinstance(a[:], Bytes) |
| assert a[:4] == "01020304" |
| assert a[4:] == "05060708" |
| |
| a = Ipv6Addr("00112233445566778899aabbccddeeff") |
| assert a == Ipv6Addr("00:11:22:33:44:55:66:77:88:99:aa:bb:cc:dd:ee:ff") |
| assert a == Ipv6Addr("0011:2233:4455:6677:8899:aabb:ccdd:eeff") |
| assert eval(repr(a)) == a |
| assert a == str(a) |
| assert str(a) == a |
| |
| print(a, repr(a)) |
| assert isinstance(a[:], Bytes) |
| assert a[:4] == "00112233" |
| assert a[-4:] == "ccddeeff" |
| |
| assert Ipv6Addr("fd00:db8::ff:fe00:8001") == Ipv6Addr("fd00:0db8:0000:0000:0000:00ff:fe00:8001") |
| print(Ipv6Addr("fdde:ad00:beef:0:9d87:85f0:3358:3fff")) |