diff --git a/ipnet.nim b/ipnet.nim new file mode 100644 index 0000000..1145658 --- /dev/null +++ b/ipnet.nim @@ -0,0 +1,588 @@ +import endians, strutils +import chronos + +type + IpMask* = object + case family*: AddressFamily + of AddressFamily.None, AddressFamily.Unix: + discard + of AddressFamily.IPv4: + mask4*: uint32 + of AddressFamily.IPv6: + mask6*: array[2, uint64] + + IpNet* = object + host*: TransportAddress + mask*: IpMask + +proc toNetworkOrder(mask: IpMask): IpMask {.inline.} = + ## Converts ``mask`` from host order (which can be big/little-endian) to + ## network order (which is big-endian) representation. + result = IpMask(family: mask.family) + if mask.family == AddressFamily.IPv4: + bigEndian32(cast[pointer](addr result.mask4), + cast[pointer](unsafeAddr mask.mask4)) + elif mask.family == AddressFamily.IPv6: + bigEndian64(cast[pointer](addr result.mask6[0]), + cast[pointer](unsafeAddr mask.mask6[0])) + bigEndian64(cast[pointer](addr result.mask6[1]), + cast[pointer](unsafeAddr mask.mask6[1])) + +proc toHostOrder(mask: IpMask): IpMask {.inline.} = + ## Converts ``mask`` from network order (which is big-endian) back to + ## host representation (which can be big/little-endian). + when system.cpuEndian == bigEndian: + result = mask + else: + result = IpMask(family: mask.family) + if mask.family == AddressFamily.IPv4: + swapEndian32(cast[pointer](addr result.mask4), + cast[pointer](unsafeAddr mask.mask4)) + elif mask.family == AddressFamily.IPv6: + swapEndian64(cast[pointer](addr result.mask6[0]), + cast[pointer](unsafeAddr mask.mask6[0])) + swapEndian64(cast[pointer](addr result.mask6[1]), + cast[pointer](unsafeAddr mask.mask6[1])) + +proc init*(t: typedesc[IpMask], family: AddressFamily, prefix: int): IpMask = + ## Initialize mask of IP family ``family`` from prefix length ``prefix``. + ## + ## For IPv4 mask, if ``prefix`` is ``0`` or more then ``32`` it will be set + ## to ``32``. + ## + ## For IPv6 mask, if ``prefix`` is ``0`` or more then ``128`` it will be set + ## to ``128``. + if family == AddressFamily.IPv4: + result = IpMask(family: AddressFamily.IPv4) + result.mask4 = 0xFFFF_FFFF'u32 + if prefix > 0 and prefix < 32: + result.mask4 = 0xFFFF_FFFF'u32 + result.mask4 = cast[uint32](result.mask4 shl (32 - prefix)) + elif family == AddressFamily.IPv6: + result = IpMask(family: AddressFamily.IPv6) + if prefix >= 128 or prefix <= 0: + result.mask6[0] = 0xFFFF_FFFF_FFFF_FFFF'u64 + result.mask6[1] = 0xFFFF_FFFF_FFFF_FFFF'u64 + else: + result.mask6[0] = 0xFFFF_FFFF_FFFF_FFFF'u64 + if prefix > 64: + result.mask6[1] = 0xFFFF_FFFF_FFFF_FFFF'u64 + result.mask6[1] = result.mask6[1] shl (128 - prefix) + elif prefix == 64: + result.mask6[1] = 0x00'u64 + else: + result.mask6[0] = result.mask6[0] shl (64 - prefix) + result.mask6[1] = 0x00'u64 + result = result.toNetworkOrder() + +proc init*(t: typedesc[IpMask], netmask: TransportAddress): IpMask = + ## Initialize network mask using address ``netmask``. + if netmask.family == AddressFamily.IPv4: + result.family = netmask.family + result.mask4 = cast[ptr uint32](unsafeAddr netmask.address_v4[0])[] + elif netmask.family == AddressFamily.IPv6: + result.family = netmask.family + result.mask6[0] = cast[ptr uint64](unsafeAddr netmask.address_v6[0])[] + result.mask6[1] = cast[ptr uint64](unsafeAddr netmask.address_v6[8])[] + +proc init*(t: typedesc[IpMask], netmask: string): IpMask = + ## Initialize network mask using IPv4 or IPv6 address in text representation + ## ``netmask``. + ## + ## If ``netmask`` address string is invalid, result IpMask.family will be + ## set to ``AddressFamily.None``. + try: + var ip = parseIpAddress(netmask) + var tip = initTAddress(ip, Port(0)) + result = t.init(tip) + except ValueError: + discard + +proc toIPv6*(address: TransportAddress): TransportAddress = + ## Map IPv4 ``address`` to IPv6 address. + ## + ## If ``address`` is IPv4 address then it will be mapped as: + ## <80 bits of zeros> + <16 bits of ones> + <32-bit IPv4 address>. + ## + ## If ``address`` is IPv6 address it will be returned without any changes. + if address.family == AddressFamily.IPv4: + result = TransportAddress(family: AddressFamily.IPv6) + result.address_v6[10] = 0xFF'u8 + result.address_v6[11] = 0xFF'u8 + let data = cast[ptr uint32](unsafeAddr address.address_v4[0])[] + cast[ptr uint32](addr result.address_v6[12])[] = data + elif address.family == AddressFamily.IPv6: + result = address + +proc isV4Mapped*(address: TransportAddress): bool = + ## Returns ``true`` if ``address`` is (IPv4 to IPv6) mapped address, e.g. + ## 0000:0000:0000:0000:0000:FFFF:xxxx:xxxx + ## + ## Procedure returns ``false`` if ``address`` family is IPv4. + if address.family == AddressFamily.IPv6: + let data0 = cast[ptr uint64](unsafeAddr address.address_v6[0])[] + let data1 = cast[ptr uint16](unsafeAddr address.address_v6[8])[] + let data2 = cast[ptr uint16](unsafeAddr address.address_v6[10])[] + result = (data0 == 0'u64) and (data1 == 0x00'u16) and (data2 == 0xFFFF'u16) + +proc toIPv4*(address: TransportAddress): TransportAddress = + ## Get IPv4 from (IPv4 to IPv6) mapped address. + ## + ## If ``address`` is IPv4 address it will be returned without any changes. + ## + ## If ``address`` is not IPv4 to IPv6 mapped address, then result family will + ## be set to AddressFamily.None. + if address.family == AddressFamily.IPv6: + if isV4Mapped(address): + result = TransportAddress(family: AddressFamily.IPv4) + let data = cast[ptr uint32](unsafeAddr address.address_v6[12])[] + cast[ptr uint32](addr result.address_v4[0])[] = data + elif address.family == AddressFamily.IPv4: + result = address + +proc mask*(a: TransportAddress, m: IpMask): TransportAddress = + ## Apply IP mask ``m`` to address ``a`` and return result address. + ## + ## If ``a`` family is IPv4 and ``m`` family is IPv6, masking is still + ## possible when ``m`` has ``FFFF:FFFF:FFFF:FFFF:FFFF:FFFF`` prefix. Returned + ## value will be IPv4 address. + ## + ## If ``a`` family is IPv6 and ``m`` family is IPv4, masking is still + ## possible when ``a`` holds (IPv4 to IPv6) mapped address. Returned value + ## will be IPv6 address. + ## + ## If ``a`` family is IPv4 and ``m`` family is IPv4, returned value will be + ## IPv4 address. + ## + ## If ``a`` family is IPv6 and ``m`` family is IPv6, returned value will be + ## IPv6 address. + ## + ## In all other cases returned address will have ``AddressFamily.None``. + if a.family == AddressFamily.IPv4 and m.family == AddressFamily.IPv6: + if (m.mask6[0] == 0xFFFF_FFFF_FFFF_FFFF'u64) and + (m.mask6[1] and 0xFFFF_FFFF'u64) == 0xFFFF_FFFF'u64: + result = TransportAddress(family: a.family) + let mask = cast[uint32](m.mask6[1] shr 32) + let data = cast[ptr uint32](unsafeAddr a.address_v4[0])[] + cast[ptr uint32](addr result.address_v4[0])[] = data and mask + result.port = a.port + elif a.family == AddressFamily.IPv6 and m.family == AddressFamily.IPv4: + var ip = a.toIPv4() + if ip.family == AddressFamily.IPv4: + let data = cast[ptr uint32](addr ip.address_v4[0])[] + cast[ptr uint32](addr ip.address_v4[0])[] = data and m.mask4 + result = ip.toIPv6() + result.port = a.port + elif a.family == AddressFamily.IPv4 and m.family == AddressFamily.IPv4: + result = TransportAddress(family: AddressFamily.IPv4) + let data = cast[ptr uint32](unsafeAddr a.address_v4[0])[] + cast[ptr uint32](addr result.address_v4[0])[] = data and m.mask4 + result.port = a.port + elif a.family == AddressFamily.IPv6 and m.family == AddressFamily.IPv6: + result = TransportAddress(family: AddressFamily.IPv6) + let data0 = cast[ptr uint64](unsafeAddr a.address_v6[0])[] + let data1 = cast[ptr uint64](unsafeAddr a.address_v6[8])[] + cast[ptr uint64](addr result.address_v6[0])[] = data0 and m.mask6[0] + cast[ptr uint64](addr result.address_v6[8])[] = data1 and m.mask6[1] + result.port = a.port + +proc prefix*(mask: IpMask): int = + ## Returns number of bits set `1` in IP mask ``mask``. + var hmask = mask.toHostOrder() + if hmask.family == AddressFamily.IPv4: + var n = hmask.mask4 + while n != 0: + n = n shl 1 + inc(result) + elif hmask.family == AddressFamily.IPv6: + if hmask.mask6[0] == 0xFFFF_FFFF_FFFF_FFFF'u64: + result += 64 + if hmask.mask6[1] == 0xFFFF_FFFF_FFFF_FFFF'u64: + result += 64: + else: + var n = hmask.mask6[1] + while n != 0: + n = n shl 1 + inc(result) + else: + var n = hmask.mask6[0] + while n != 0: + n = n shl 1 + inc(result) + +proc subnetMask*(mask: IpMask): TransportAddress = + ## Returns TransportAddress representation of IP mask ``mask``. + result = TransportAddress(family: mask.family) + if mask.family == AddressFamily.IPv4: + cast[ptr uint32](addr result.address_v4[0])[] = mask.mask4 + elif mask.family == AddressFamily.IPv6: + cast[ptr uint64](addr result.address_v6[0])[] = mask.mask6[0] + cast[ptr uint64](addr result.address_v6[8])[] = mask.mask6[1] + +proc `$`*(mask: IpMask): string = + ## Returns hexadecimal string representation of IP mask ``mask``. + var host = mask.toHostOrder() + result = "" + if host.family == AddressFamily.IPv4: + result = "0x" + var n = 32 + var m = host.mask4 + while n > 0: + n -= 4 + var c = cast[int]((m shr n) and 0x0F) + if c < 10: + result.add(chr(ord('0') + c)) + else: + result.add(chr(ord('A') + (c - 10))) + elif host.family == AddressFamily.IPv6: + result = "0x" + for i in 0..1: + var n = 64 + var m = host.mask6[i] + while n > 0: + n -= 4 + var c = cast[int]((m shr n) and 0x0F) + if c < 10: + result.add(chr(ord('0') + c)) + else: + result.add(chr(ord('A') + (c - 10))) + +proc init*(t: typedesc[IpNet], host: TransportAddress, + prefix: int): IpNet {.inline.} = + ## Initialize IP Network using host address ``host`` and prefix length + ## ``prefix``. + result.mask = IpMask.init(host.family, prefix) + result.host = mask(host, result.mask) + +proc init*(t: typedesc[IpNet], host, mask: TransportAddress): IpNet {.inline.} = + ## Initialize IP Network using host address ``host`` and network mask + ## address ``mask``. + ## + ## Note that ``host`` and ``mask`` must be from the same IP family. + if host.family == mask.family: + result.mask = IpMask.init(mask) + result.host = mask(host, result.mask) + +proc init*(t: typedesc[IpNet], host: TransportAddress, + mask: IpMask): IpNet {.inline.} = + ## Initialize IP Network using host address ``host`` and network mask + ## ``mask``. + result.mask = mask + result.host = result.mask(host) + +proc init*(t: typedesc[IpNet], network: string): IpNet = + ## Initialize IP Network from string representation in format + ##
/. + ## + ## Not if is not present or its value is bigger then maximum + ## value (32 for IPv4 and 128 for IPv6), then `/32` and `/128` will be used + ## as prefix length. + var parts = network.rsplit("/", maxsplit = 1) + var host: TransportAddress + var ipaddr: IpAddress + var prefix: int + try: + ipaddr = parseIpAddress(parts[0]) + if ipaddr.family == IpAddressFamily.IPv4: + host = TransportAddress(family: AddressFamily.IPv4) + host.address_v4 = ipaddr.address_v4 + prefix = 32 + elif ipaddr.family == IpAddressFamily.IPv6: + host = TransportAddress(family: AddressFamily.IPv6) + host.address_v6 = ipaddr.address_v6 + prefix = 128 + if len(parts) > 1: + prefix = parseInt(parts[1]) + if ipaddr.family == IpAddressFamily.IPv4 and + (prefix < 0 or prefix > 32): + prefix = 32 + elif ipaddr.family == IpAddressFamily.IPv6 and + (prefix < 0 or prefix > 128): + prefix = 128 + result = t.init(host, prefix) + except: + raise newException(TransportAddressError, "Incorrect address family!") + +proc contains*(net: IpNet, address: TransportAddress): bool = + ## Returns ``true`` if ``address`` belongs to IP Network ``net`` + if net.host.family == address.family: + var host = mask(address, net.mask) + result = (net.host == host) + +proc broadcast*(net: IpNet): TransportAddress = + ## Returns broadcast address for IP Network ``net``. + result = TransportAddress(family: net.host.family) + if result.family == AddressFamily.IPv4: + var address = cast[ptr uint32](unsafeAddr net.host.address_v4[0])[] + var mask = cast[ptr uint32](unsafeAddr net.mask.mask4)[] + cast[ptr uint32](addr result.address_v4[0])[] = address or (not(mask)) + elif result.family == AddressFamily.IPv6: + var address0 = cast[ptr uint64](unsafeAddr net.host.address_v6[0])[] + var address1 = cast[ptr uint64](unsafeAddr net.host.address_v6[8])[] + var data0 = cast[ptr uint64](unsafeAddr net.mask.mask6[0])[] + var data1 = cast[ptr uint64](unsafeAddr net.mask.mask6[1])[] + cast[ptr uint64](addr result.address_v6[0])[] = address0 or (not(data0)) + cast[ptr uint64](addr result.address_v6[8])[] = address1 or (not(data1)) + +proc subnetMask*(net: IpNet): TransportAddress = + ## Returns netmask address for IP Network ``net`` + result = TransportAddress(family: net.host.family) + if result.family == AddressFamily.IPv4: + var address = cast[ptr uint32](unsafeAddr net.mask.mask4)[] + cast[ptr uint32](addr result.address_v4[0])[] = address + elif result.family == AddressFamily.IPv6: + var address0 = cast[ptr uint64](unsafeAddr net.mask.mask6[0])[] + var address1 = cast[ptr uint64](unsafeAddr net.mask.mask6[1])[] + cast[ptr uint64](addr result.address_v6[0])[] = address0 + cast[ptr uint64](addr result.address_v6[8])[] = address1 + +proc `and`*(address1, address2: TransportAddress): TransportAddress = + ## Bitwise ``and`` operation for ``address1 and address2``. + ## + ## Note only IPv4 and IPv6 addresses are supported. ``address1`` and + ## ``address2`` must be in equal IP family + if address1.family == address2.family: + if address1.family == AddressFamily.IPv4: + let data1 = cast[ptr uint32](unsafeAddr address1.address_v4[0])[] + let data2 = cast[ptr uint32](unsafeAddr address2.address_v4[0])[] + result = TransportAddress(family: address1.family) + cast[ptr uint32](addr result.address_v4[0])[] = data1 and data2 + elif address1.family == AddressFamily.IPv6: + let data1 = cast[ptr uint64](unsafeAddr address1.address_v6[0])[] + let data2 = cast[ptr uint64](unsafeAddr address1.address_v6[8])[] + let data3 = cast[ptr uint64](unsafeAddr address2.address_v6[0])[] + let data4 = cast[ptr uint64](unsafeAddr address2.address_v6[8])[] + result = TransportAddress(family: address1.family) + cast[ptr uint64](addr result.address_v6[0])[] = data1 and data3 + cast[ptr uint64](addr result.address_v6[8])[] = data2 and data4 + +proc `or`*(address1, address2: TransportAddress): TransportAddress = + ## Bitwise ``or`` operation for ``address1 or address2``. + ## + ## Note only IPv4 and IPv6 addresses are supported. ``address1`` and + ## ``address2`` must be in equal IP family + if address1.family == address2.family: + if address1.family == AddressFamily.IPv4: + let data1 = cast[ptr uint32](unsafeAddr address1.address_v4[0])[] + let data2 = cast[ptr uint32](unsafeAddr address2.address_v4[0])[] + result = TransportAddress(family: address1.family) + cast[ptr uint32](addr result.address_v4[0])[] = data1 or data2 + elif address1.family == AddressFamily.IPv6: + let data1 = cast[ptr uint64](unsafeAddr address1.address_v6[0])[] + let data2 = cast[ptr uint64](unsafeAddr address1.address_v6[8])[] + let data3 = cast[ptr uint64](unsafeAddr address2.address_v6[0])[] + let data4 = cast[ptr uint64](unsafeAddr address2.address_v6[8])[] + result = TransportAddress(family: address1.family) + cast[ptr uint64](addr result.address_v6[0])[] = data1 or data3 + cast[ptr uint64](addr result.address_v6[8])[] = data2 or data4 + +proc `not`*(address: TransportAddress): TransportAddress = + ## Bitwise ``not`` operation for ``address``. + if address.family == AddressFamily.IPv4: + let data = cast[ptr uint32](unsafeAddr address.address_v4[0])[] + result = TransportAddress(family: address.family) + cast[ptr uint32](addr result.address_v4[0])[] = not(data) + elif address.family == AddressFamily.IPv6: + let data1 = cast[ptr uint64](unsafeAddr address.address_v6[0])[] + let data2 = cast[ptr uint64](unsafeAddr address.address_v6[8])[] + result = TransportAddress(family: address.family) + cast[ptr uint64](addr result.address_v6[0])[] = not(data1) + cast[ptr uint64](addr result.address_v6[8])[] = not(data2) + +proc `+`*(address: TransportAddress, v: uint): TransportAddress = + ## Add to IPv4/IPv6 transport ``address`` unsigned integer ``v``. + result = TransportAddress(family: address.family) + if address.family == AddressFamily.IPv4: + var a: uint64 + let data = cast[ptr uint32](unsafeAddr address.address_v4[0]) + when system.cpuEndian == bigEndian: + a = data + else: + swapEndian32(addr a, data) + a = a + v + bigEndian32(cast[pointer](addr result.address_v4[0]), addr a) + elif address.family == AddressFamily.IPv6: + var a1, a2: uint64 + let data1 = cast[ptr uint64](unsafeAddr address.address_v6[0]) + let data2 = cast[ptr uint64](unsafeAddr address.address_v6[8]) + when system.cpuEndian == bigEndian: + a1 = data1 + a2 = data2 + else: + swapEndian64(addr a1, data1) + swapEndian64(addr a2, data2) + var a3 = a2 + v + if a3 < a2: + ## Overflow + a1 = a1 + 1 + bigEndian64(cast[pointer](addr result.address_v6[0]), addr a1) + bigEndian64(cast[pointer](addr result.address_v6[8]), addr a3) + +proc inc*(address: var TransportAddress, v: uint = 1'u) = + ## Increment IPv4/IPv6 transport ``address`` by unsigned integer ``v``. + address = address + v + +proc `$`*(net: IpNet): string = + ## Return string representation of IP network in format: + ## /. + if net.host.family == AddressFamily.IPv4: + var a = IpAddress(family: IpAddressFamily.IPv4, + address_v4: net.host.address_v4) + result = $a + result.add("/") + result.add($net.mask.prefix()) + elif net.host.family == AddressFamily.IPv6: + var a = IpAddress(family: IpAddressFamily.IPv6, + address_v6: net.host.address_v6) + result = $a + result.add("/") + result.add($net.mask.prefix()) + +proc isUnspecified*(address: TransportAddress): bool {.inline.} = + ## Returns ``true`` if ``address`` is not specified yet, e.g. its ``family`` + ## field is not set or equal to ``AddressFamily.None``. + if address.family == AddressFamily.None: + result = true + +proc isMulticast*(address: TransportAddress): bool = + ## Returns ``true`` if ``address`` is a multicast address. + ## + ## ``IPv4``: 224.0.0.0 - 239.255.255.255 + ## + ## ``IPv6``: FF00:: - FFFF:FFFF:FFFF:FFFF:FFFF:FFFF:FFFF:FFFF + if address.family == AddressFamily.IPv4: + result = ((address.address_v4[0] and 0xF0'u8) == 0xE0'u8) + elif address.family == AddressFamily.IPv6: + result = (address.address_v6[0] == 0xFF'u8) + +proc isInterfaceLocalMulticast*(address: TransportAddress): bool = + ## Returns ``true`` if ``address`` is interface local multicast address. + ## + ## ``IPv4``: N/A (always returns ``false``) + if address.family == AddressFamily.IPv6: + result = (address.address_v6[0] == 0xFF'u8) and + ((address.address_v6[1] and 0x0F'u8) == 0x01'u8) + +proc isLinkLocalMulticast*(address: TransportAddress): bool = + ## Returns ``true`` if ``address` is link local multicast address. + ## + ## ``IPv4``: 224.0.0.0 - 224.0.0.255 + if address.family == AddressFamily.IPv4: + result = (address.address_v4[0] == 224'u8) and + (address.address_v4[1] == 0'u8) and + (address.address_v4[2] == 0'u8) + elif address.family == AddressFamily.IPv6: + result = (address.address_v6[0] == 0xFF'u8) and + ((address.address_v6[1] and 0x0F'u8) == 0x02'u8) + +proc isLoopback*(address: TransportAddress): bool = + ## Returns ``true`` if ``address`` is loopback address. + ## + ## ``IPv4``: 127.0.0.0 - 127.255.255.255 + ## + ## ``IPv6``: ::1 + if address.family == AddressFamily.IPv4: + result = (address.address_v4[0] == 127'u8) + elif address.family == AddressFamily.IPv6: + var test = 0 + for i in 0..<(len(address.address_v6) - 1): + test = test or cast[int](address.address_v6[i]) + result = (test == 0) and (address.address_v6[15] == 1'u8) + +proc isAnyLocal*(address: TransportAddress): bool = + ## Returns ``true`` if ``address`` is a wildcard address. + ## + ## ``IPv4``: 0.0.0.0 + ## + ## ``IPv6``: :: + if address.family == AddressFamily.IPv4: + let data = cast[ptr uint32](unsafeAddr address.address_v4[0])[] + result = (data == 0'u32) + elif address.family == AddressFamily.IPv6: + let data1 = cast[ptr uint32](unsafeAddr address.address_v6[0])[] + let data2 = cast[ptr uint32](unsafeAddr address.address_v6[4])[] + let data3 = cast[ptr uint32](unsafeAddr address.address_v6[8])[] + let data4 = cast[ptr uint32](unsafeAddr address.address_v6[12])[] + result = ((data1 or data2 or data3 or data4) == 0'u32) + +proc isLinkLocal*(address: TransportAddress): bool = + ## Returns ``true`` if ``address`` is link local address. + ## + ## ``IPv4``: 169.254.0.0 - 169.254.255.255 + ## + ## ``IPv6``: FE80:: - FEBF:FFFF:FFFF:FFFF:FFFF:FFFF:FFFF:FFFF + if address.family == AddressFamily.IPv4: + result = (address.address_v4[0] == 169'u8) and + (address.address_v4[1] == 254'u8) + elif address.family == AddressFamily.IPv6: + result = (address.address_v6[0] == 0xFE'u8) and + ((address.address_v6[1] and 0xC0'u8) == 0x80'u8) + +proc isLinkLocalUnicast*(address: TransportAddress): bool {.inline.} = + result = isLinkLocal(address) + +proc isSiteLocal*(address: TransportAddress): bool = + ## Returns ``true`` if ``address`` is site local address. + ## + ## ``IPv4``: 10.0.0.0 - 10.255.255.255, 172.16.0.0 - 172.31.255.255, + ## 192.168.0.0 - 192.168.255.255 + ## + ## ``IPv6``: FEC0:: - FEFF:FFFF:FFFF:FFFF:FFFF:FFFF:FFFF:FFFF + if address.family == AddressFamily.IPv4: + result = (address.address_v4[0] == 10'u8) or + ((address.address_v4[0] == 172'u8) and + ((address.address_v4[1] and 0xF0) == 16)) or + ((address.address_v4[0] == 192'u8) and + ((address.address_v4[1] == 168'u8))) + elif address.family == AddressFamily.IPv6: + result = (address.address_v6[0] == 0xFE'u8) and + ((address.address_v6[1] and 0xC0'u8) == 0xC0'u8) + +proc isGlobalMulticast*(address: TransportAddress): bool = + ## Returns ``true`` if the multicast address has global scope. + ## + ## ``IPv4``: 224.0.1.0 - 238.255.255.255 + ## + ## ``IPv6``: FF0E:: - FFFE:FFFF:FFFF:FFFF:FFFF:FFFF:FFFF:FFFF + if address.family == AddressFamily.IPv4: + result = (address.address_v4[0] >= 224'u8) and + (address.address_v4[0] <= 238'u8) and + not( + (address.address_v4[0] == 224'u8) and + (address.address_v4[1] == 0'u8) and + (address.address_v4[2] == 0'u8) + ) + elif address.family == AddressFamily.IPv6: + result = (address.address_v6[0] == 0xFF'u8) and + ((address.address_v6[1] and 0x0F'u8) == 0x0E'u8) + +when isMainModule: + var MaskVectors = [ + ["192.168.1.127:1024", "255.255.255.128", "192.168.1.0:1024"], + ["192.168.1.127:1024", "255.255.255.192", "192.168.1.64:1024"], + ["192.168.1.127:1024", "255.255.255.224", "192.168.1.96:1024"], + ["192.168.1.127:1024", "ffff:ffff:ffff:ffff:ffff:ffff:ffff:ff80", + "192.168.1.0:1024"], + ["192.168.1.127:1024", "ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffc0", + "192.168.1.64:1024"], + ["192.168.1.127:1024", "ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffe0", + "192.168.1.96:1024"], + ["192.168.1.127:1024", "255.0.255.0", "192.0.1.0:1024"], + ["[2001:db8::1]:1024", "ffff:ff80::", "[2001:d80::]:1024"], + ["[2001:db8::1]:1024", "f0f0:0f0f::", "[2000:d08::]:1024"] + ] + for item in MaskVectors: + var a = initTAddress(item[0]) + var m = IpMask.init(item[1]) + var r = a.mask(m) + echo r + # var temp = [0'u8, 0, 255, 255, 255, 255, 255, 0] + # echo toHex(cast[ptr uint64](addr temp[0])[]) + # # {IPv4(192, 168, 1, 127), IPMask(ParseIP("255.255.255.192")), IPv4(192, 168, 1, 64)}, + # echo initTAddress("255.255.255.128", 0).toIPv6() + # var a = initTAddress("255.255.255.0", 0).toIPv6() + # echo a + # var m = IpMask.init(a) + # var b = initTAddress("192.168.1.127", 0) + # echo b.mask(m) + # # echo a.mask(m) diff --git a/tests/testipnet.nim b/tests/testipnet.nim new file mode 100644 index 0000000..c8f1e33 --- /dev/null +++ b/tests/testipnet.nim @@ -0,0 +1,219 @@ +import unittest, strutils +import ../libp2p/ipnet/ipnet + +const MaskVectors = [ + ["192.168.1.127:1024", "255.255.255.128", "192.168.1.0:1024"], + ["192.168.1.127:1024", "255.255.255.192", "192.168.1.64:1024"], + ["192.168.1.127:1024", "255.255.255.224", "192.168.1.96:1024"], + ["192.168.1.127:1024", "ffff:ffff:ffff:ffff:ffff:ffff:ffff:ff80", + "192.168.1.0:1024"], + ["192.168.1.127:1024", "ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffc0", + "192.168.1.64:1024"], + ["192.168.1.127:1024", "ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffe0", + "192.168.1.96:1024"], + ["192.168.1.127:1024", "255.0.255.0", "192.0.1.0:1024"], + ["[2001:db8::1]:1024", "ffff:ff80::", "[2001:d80::]:1024"], + ["[2001:db8::1]:1024", "f0f0:0f0f::", "[2000:d08::]:1024"] +] + +const NonCanonicalMasks = [ + ["ip", "0.255.255.255", "-1"], + ["ip", "255.0.255.255", "-1"], + ["ip", "255.255.0.255", "-1"], + ["ip", "255.255.255.0", "24"], + ["ms", "0FFFFFFF", "-1"], + ["ms", "F0FFFFFF", "-1"], + ["ms", "FF0FFFFF", "-1"], + ["ms", "FFF0FFFF", "-1"], + ["ms", "FFFF0FFF", "-1"], + ["ms", "FFFFF0FF", "-1"], + ["ms", "FFFFFF0F", "-1"], + ["ms", "FFFFFFF0", "28"], + ["ip", "00FF:FFFF:FFFF:FFFF:FFFF:FFFF:FFFF:FFFF", "-1"], + ["ip", "FF00:FFFF:FFFF:FFFF:FFFF:FFFF:FFFF:FFFF", "-1"], + ["ip", "FFFF:00FF:FFFF:FFFF:FFFF:FFFF:FFFF:FFFF", "-1"], + ["ip", "FFFF:FF00:FFFF:FFFF:FFFF:FFFF:FFFF:FFFF", "-1"], + ["ip", "FFFF:FFFF:00FF:FFFF:FFFF:FFFF:FFFF:FFFF", "-1"], + ["ip", "FFFF:FFFF:FF00:FFFF:FFFF:FFFF:FFFF:FFFF", "-1"], + ["ip", "FFFF:FFFF:FFFF:00FF:FFFF:FFFF:FFFF:FFFF", "-1"], + ["ip", "FFFF:FFFF:FFFF:FF00:FFFF:FFFF:FFFF:FFFF", "-1"], + ["ip", "FFFF:FFFF:FFFF:FFFF:00FF:FFFF:FFFF:FFFF", "-1"], + ["ip", "FFFF:FFFF:FFFF:FFFF:FF00:FFFF:FFFF:FFFF", "-1"], + ["ip", "FFFF:FFFF:FFFF:FFFF:FFFF:00FF:FFFF:FFFF", "-1"], + ["ip", "FFFF:FFFF:FFFF:FFFF:FFFF:FF00:FFFF:FFFF", "-1"], + ["ip", "FFFF:FFFF:FFFF:FFFF:FFFF:FFFF:00FF:FFFF", "-1"], + ["ip", "FFFF:FFFF:FFFF:FFFF:FFFF:FFFF:FF00:FFFF", "-1"], + ["ip", "FFFF:FFFF:FFFF:FFFF:FFFF:FFFF:FFFF:00FF", "-1"], + ["ip", "FFFF:FFFF:FFFF:FFFF:FFFF:FFFF:FFFF:FF00", "120"], + ["ms", "0FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF", "-1"], + ["ms", "F0FFFFFFFFFFFFFFFFFFFFFFFFFFFFFF", "-1"], + ["ms", "FF0FFFFFFFFFFFFFFFFFFFFFFFFFFFFF", "-1"], + ["ms", "FFF0FFFFFFFFFFFFFFFFFFFFFFFFFFFF", "-1"], + ["ms", "FFFF0FFFFFFFFFFFFFFFFFFFFFFFFFFF", "-1"], + ["ms", "FFFFF0FFFFFFFFFFFFFFFFFFFFFFFFFF", "-1"], + ["ms", "FFFFFF0FFFFFFFFFFFFFFFFFFFFFFFFF", "-1"], + ["ms", "FFFFFFF0FFFFFFFFFFFFFFFFFFFFFFFF", "-1"], + ["ms", "FFFFFFFF0FFFFFFFFFFFFFFFFFFFFFFF", "-1"], + ["ms", "FFFFFFFFF0FFFFFFFFFFFFFFFFFFFFFF", "-1"], + ["ms", "FFFFFFFFFF0FFFFFFFFFFFFFFFFFFFFF", "-1"], + ["ms", "FFFFFFFFFFF0FFFFFFFFFFFFFFFFFFFF", "-1"], + ["ms", "FFFFFFFFFFFF0FFFFFFFFFFFFFFFFFFF", "-1"], + ["ms", "FFFFFFFFFFFFF0FFFFFFFFFFFFFFFFFF", "-1"], + ["ms", "FFFFFFFFFFFFFF0FFFFFFFFFFFFFFFFF", "-1"], + ["ms", "FFFFFFFFFFFFFFF0FFFFFFFFFFFFFFFF", "-1"], + ["ms", "FFFFFFFFFFFFFFFF0FFFFFFFFFFFFFFF", "-1"], + ["ms", "FFFFFFFFFFFFFFFFF0FFFFFFFFFFFFFF", "-1"], + ["ms", "FFFFFFFFFFFFFFFFFF0FFFFFFFFFFFFF", "-1"], + ["ms", "FFFFFFFFFFFFFFFFFFF0FFFFFFFFFFFF", "-1"], + ["ms", "FFFFFFFFFFFFFFFFFFFF0FFFFFFFFFFF", "-1"], + ["ms", "FFFFFFFFFFFFFFFFFFFFF0FFFFFFFFFF", "-1"], + ["ms", "FFFFFFFFFFFFFFFFFFFFFF0FFFFFFFFF", "-1"], + ["ms", "FFFFFFFFFFFFFFFFFFFFFFF0FFFFFFFF", "-1"], + ["ms", "FFFFFFFFFFFFFFFFFFFFFFFF0FFFFFFF", "-1"], + ["ms", "FFFFFFFFFFFFFFFFFFFFFFFFF0FFFFFF", "-1"], + ["ms", "FFFFFFFFFFFFFFFFFFFFFFFFFF0FFFFF", "-1"], + ["ms", "FFFFFFFFFFFFFFFFFFFFFFFFFFF0FFFF", "-1"], + ["ms", "FFFFFFFFFFFFFFFFFFFFFFFFFFFF0FFF", "-1"], + ["ms", "FFFFFFFFFFFFFFFFFFFFFFFFFFFFF0FF", "-1"], + ["ms", "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0F", "-1"], + ["ms", "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0", "124"], +] + +const NetworkVectors = [ + ["135.104.0.0/32", "true", "135.104.0.0:0", "FFFFFFFF"], + ["0.0.0.0/24", "true", "0.0.0.0:0", "FFFFFF00"], + ["135.104.0.0/24", "true", "135.104.0.0:0", "FFFFFF00"], + ["135.104.0.1/32", "true", "135.104.0.1:0", "FFFFFFFF"], + ["135.104.0.1/24", "true", "135.104.0.1:0", "FFFFFF00"], + ["::1/128", "true", "[::1]:0", "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF"], + ["abcd:2345::/127", "true", "[abcd:2345::]:0", + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE"], + ["abcd:2345::/65", "true", "[abcd:2345::]:0", + "FFFFFFFFFFFFFFFF8000000000000000"], + ["abcd:2345::/64", "true", "[abcd:2345::]:0", + "FFFFFFFFFFFFFFFF0000000000000000"], + ["abcd:2345::/63", "true", "[abcd:2345::]:0", + "FFFFFFFFFFFFFFFE0000000000000000"], + ["abcd:2345::/33", "true", "[abcd:2345::]:0", + "FFFFFFFF800000000000000000000000"], + ["abcd:2345::/32", "true", "[abcd:2345::]:0", + "FFFFFFFF000000000000000000000000"], + ["abcd:2344::/31", "true", "[abcd:2344::]:0", + "FFFFFFFE000000000000000000000000"], + ["abcd:2300::/24", "true", "[abcd:2300::]:0", + "FFFFFF00000000000000000000000000"], + ["abcd:2345::/24", "true", "[abcd:2345::]:0", + "FFFFFF00000000000000000000000000"], + ["2001:db8::/48", "true", "[2001:db8::]:0", + "FFFFFFFFFFFF00000000000000000000"], + ["2001:db8::1/48", "true", "[2001:db8::1]:0", + "FFFFFFFFFFFF00000000000000000000"], + ["192.168.1.1/255.255.255.0", "true", "192.168.1.1:0", "FFFFFF00"], + ["192.168.1.1/35", "false", "", ""], + ["2001:db8::1/-1", "false", "", ""], + ["2001:db8::1/-0", "false", "", ""], + ["-0.0.0.0/32", "false", "", ""], + ["0.-1.0.0/32", "false", "", ""], + ["0.0.-2.0/32", "false", "", ""], + ["0.0.0.-3/32", "false", "", ""], + ["0.0.0.0/-0", "false", "", ""], + ["", "false", "", ""] +] + +const NetworkContainsVectors = [ + ["172.16.1.1:1024", "172.16.0.0/12", "true"], + ["172.24.0.1:1024", "172.16.0.0/13", "false"], + ["192.168.0.3:1024", "192.168.0.0/0.0.255.252", "true"], + ["192.168.0.4:1024", "192.168.0.0/0.255.0.252", "false"], + ["[2001:db8:1:2::1]:1024", "2001:db8:1::/47", "true"], + ["[2001:db8:1:2::1]:1024", "2001:db8:2::/47", "false"], + ["[2001:db8:1:2::1]:1024", "2001:db8:1::/ffff:0:ffff::", "true"], + ["[2001:db8:1:2::1]:1024", "2001:db8:1::/0:0:0:ffff::", "false"] +] + +suite "IP network utilities test suite": + + test "IpMask test vectors": + for item in MaskVectors: + var a = initTAddress(item[0]) + var m = IpMask.initIp(item[1]) + var r = a.mask(m) + check $r == item[2] + + test "IpMask serialization/deserialization test": + for i in 1..32: + var m = IpMask.init(AddressFamily.IPv4, i) + check m.prefix() == i + var s0x = `$`(m, true) + var s = $m + var sip = m.ip() + var m1 = IpMask.init(s0x) + var m2 = IpMask.init(s) + var m3 = IpMask.initIp(sip) + check: + m == m1 + m == m2 + m == m3 + for i in 1..128: + var m = IpMask.init(AddressFamily.IPv6, i) + check m.prefix() == i + var s0x = `$`(m, true) + var s = $m + var sip = m.ip() + var m1 = IpMask.init(s0x) + var m2 = IpMask.init(s) + var m3 = IpMask.initIp(sip) + check: + m == m1 + m == m2 + m == m3 + + test "IpMask non-canonical masks": + for item in NonCanonicalMasks: + var m: IpMask + if item[0] == "ip": + m = IpMask.initIp(item[1]) + elif item[0] == "ms": + m = IpMask.init(item[1]) + var c = $(m.prefix()) + check: + c == item[2] + + test "IpNet test vectors": + for item in NetworkVectors: + var res: bool + var inet: IpNet + try: + inet = IpNet.init(item[0]) + res = true + except: + res = false + check: + $res == item[1] + if res: + check: + $inet.host == item[2] + $inet.mask == $item[3] + + test "IpNet contains test vectors": + for item in NetworkContainsVectors: + var a = initTAddress(item[0]) + var n = IpNet.init(item[1]) + var res = a in n + check: + $res == item[2] + + test "IpNet serialization/deserialization test": + var ip4 = initTAddress("192.168.1.0:1024") + for i in 1..32: + var net = IpNet.init(ip4, i) + var s1 = $net + var net2 = IpNet.init(s1) + check net == net2 + + var ip6 = initTAddress("[8000:f123:f456:cafe::]:1024") + for i in 1..128: + var net = IpNet.init(ip6, i) + var s1 = $net + var net2 = IpNet.init(s1) + check net == net2 diff --git a/tests/testnative.nim b/tests/testnative.nim index c14ac8f..7ca7d4c 100644 --- a/tests/testnative.nim +++ b/tests/testnative.nim @@ -2,3 +2,4 @@ import unittest import testvarint, testbase32, testbase58, testbase64 import testrsa, testecnist, tested25519, testcrypto import testmultibase, testmultihash, testmultiaddress, testcid, testpeer +import testipnet, testiface