2019-04-15 01:27:12 +00:00
|
|
|
#
|
|
|
|
# Chronos IP Network
|
|
|
|
# (c) Copyright 2018-Present
|
|
|
|
# Status Research & Development GmbH
|
|
|
|
#
|
|
|
|
# Licensed under either of
|
|
|
|
# Apache License, version 2.0, (LICENSE-APACHEv2)
|
|
|
|
# MIT license (LICENSE-MIT)
|
|
|
|
|
|
|
|
## This module implements various IP network utility procedures.
|
exception tracking (#166)
* exception tracking
This PR adds minimal exception tracking to chronos, moving the goalpost
one step further.
In particular, it becomes invalid to raise exceptions from `callSoon`
callbacks: this is critical for writing correct error handling because
there's no reasonable way that a user of chronos can possibly _reason_
about exceptions coming out of there: the event loop will be in an
indeterminite state when the loop is executing an _random_ callback.
As expected, there are several issues in the error handling of chronos:
in particular, it will end up in an inconsistent internal state whenever
the selector loop operations fail, because the internal state update
functions are not written in an exception-safe way. This PR turns this
into a Defect, which probably is not the optimal way of handling things
- expect more work to be done here.
Some API have no way of reporting back errors to callers - for example,
when something fails in the accept loop, there's not much it can do, and
no way to report it back to the user of the API - this has been fixed
with the new accept flow - the old one should be deprecated.
Finally, there is information loss in the API: in composite operations
like `poll` and `waitFor` there's no way to differentiate internal
errors from user-level errors originating from callbacks.
* store `CatchableError` in future
* annotate proc's with correct raises information
* `selectors2` to avoid non-CatchableError IOSelectorsException
* `$` should never raise
* remove unnecessary gcsafe annotations
* fix exceptions leaking out of timer waits
* fix some imports
* functions must signal raising the union of all exceptions across all
platforms to enable cross-platform code
* switch to unittest2
* add `selectors2` which supercedes the std library version and fixes
several exception handling issues in there
* fixes
* docs, platform-independent eh specifiers for some functions
* add feature flag for strict exception mode
also bump version to 3.0.0 - _most_ existing code should be compatible
with this version of exception handling but some things might need
fixing - callbacks, existing raises specifications etc.
* fix AsyncCheck for non-void T
2021-03-24 09:08:33 +00:00
|
|
|
|
2022-08-06 10:56:06 +00:00
|
|
|
when (NimMajor, NimMinor) < (1, 4):
|
|
|
|
{.push raises: [Defect].}
|
|
|
|
else:
|
|
|
|
{.push raises: [].}
|
exception tracking (#166)
* exception tracking
This PR adds minimal exception tracking to chronos, moving the goalpost
one step further.
In particular, it becomes invalid to raise exceptions from `callSoon`
callbacks: this is critical for writing correct error handling because
there's no reasonable way that a user of chronos can possibly _reason_
about exceptions coming out of there: the event loop will be in an
indeterminite state when the loop is executing an _random_ callback.
As expected, there are several issues in the error handling of chronos:
in particular, it will end up in an inconsistent internal state whenever
the selector loop operations fail, because the internal state update
functions are not written in an exception-safe way. This PR turns this
into a Defect, which probably is not the optimal way of handling things
- expect more work to be done here.
Some API have no way of reporting back errors to callers - for example,
when something fails in the accept loop, there's not much it can do, and
no way to report it back to the user of the API - this has been fixed
with the new accept flow - the old one should be deprecated.
Finally, there is information loss in the API: in composite operations
like `poll` and `waitFor` there's no way to differentiate internal
errors from user-level errors originating from callbacks.
* store `CatchableError` in future
* annotate proc's with correct raises information
* `selectors2` to avoid non-CatchableError IOSelectorsException
* `$` should never raise
* remove unnecessary gcsafe annotations
* fix exceptions leaking out of timer waits
* fix some imports
* functions must signal raising the union of all exceptions across all
platforms to enable cross-platform code
* switch to unittest2
* add `selectors2` which supercedes the std library version and fixes
several exception handling issues in there
* fixes
* docs, platform-independent eh specifiers for some functions
* add feature flag for strict exception mode
also bump version to 3.0.0 - _most_ existing code should be compatible
with this version of exception handling but some things might need
fixing - callbacks, existing raises specifications etc.
* fix AsyncCheck for non-void T
2021-03-24 09:08:33 +00:00
|
|
|
|
2021-08-26 11:22:29 +00:00
|
|
|
import std/strutils
|
|
|
|
import stew/endians2
|
exception tracking (#166)
* exception tracking
This PR adds minimal exception tracking to chronos, moving the goalpost
one step further.
In particular, it becomes invalid to raise exceptions from `callSoon`
callbacks: this is critical for writing correct error handling because
there's no reasonable way that a user of chronos can possibly _reason_
about exceptions coming out of there: the event loop will be in an
indeterminite state when the loop is executing an _random_ callback.
As expected, there are several issues in the error handling of chronos:
in particular, it will end up in an inconsistent internal state whenever
the selector loop operations fail, because the internal state update
functions are not written in an exception-safe way. This PR turns this
into a Defect, which probably is not the optimal way of handling things
- expect more work to be done here.
Some API have no way of reporting back errors to callers - for example,
when something fails in the accept loop, there's not much it can do, and
no way to report it back to the user of the API - this has been fixed
with the new accept flow - the old one should be deprecated.
Finally, there is information loss in the API: in composite operations
like `poll` and `waitFor` there's no way to differentiate internal
errors from user-level errors originating from callbacks.
* store `CatchableError` in future
* annotate proc's with correct raises information
* `selectors2` to avoid non-CatchableError IOSelectorsException
* `$` should never raise
* remove unnecessary gcsafe annotations
* fix exceptions leaking out of timer waits
* fix some imports
* functions must signal raising the union of all exceptions across all
platforms to enable cross-platform code
* switch to unittest2
* add `selectors2` which supercedes the std library version and fixes
several exception handling issues in there
* fixes
* docs, platform-independent eh specifiers for some functions
* add feature flag for strict exception mode
also bump version to 3.0.0 - _most_ existing code should be compatible
with this version of exception handling but some things might need
fixing - callbacks, existing raises specifications etc.
* fix AsyncCheck for non-void T
2021-03-24 09:08:33 +00:00
|
|
|
import ./common
|
2019-04-15 01:27:12 +00:00
|
|
|
export common
|
|
|
|
|
|
|
|
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 `==`*(m1, m2: IpMask): bool {.inline.} =
|
|
|
|
## Returns ``true`` if masks ``m1`` and ``m2`` are equal in IP family and
|
|
|
|
## by value.
|
|
|
|
if m1.family == m2.family:
|
2023-02-16 16:18:05 +00:00
|
|
|
case m1.family
|
|
|
|
of AddressFamily.IPv4:
|
|
|
|
(m1.mask4 == m2.mask4)
|
|
|
|
of AddressFamily.IPv6:
|
|
|
|
((m1.mask6[0] == m2.mask6[0]) and (m1.mask6[1] == m2.mask6[1]))
|
|
|
|
else:
|
|
|
|
true
|
|
|
|
else:
|
|
|
|
false
|
2019-04-15 01:27:12 +00:00
|
|
|
|
|
|
|
proc init*(t: typedesc[IpMask], family: AddressFamily, prefix: int): IpMask =
|
|
|
|
## Initialize mask of IP family ``family`` from prefix length ``prefix``.
|
2023-02-16 16:18:05 +00:00
|
|
|
case family
|
|
|
|
of AddressFamily.IPv4:
|
2019-04-15 01:27:12 +00:00
|
|
|
if prefix <= 0:
|
2023-02-16 16:18:05 +00:00
|
|
|
IpMask(family: AddressFamily.IPv4, mask4: 0'u32)
|
|
|
|
elif prefix < 32:
|
|
|
|
let mask = 0xFFFF_FFFF'u32 shl (32 - prefix)
|
|
|
|
IpMask(family: AddressFamily.IPv4, mask4: mask.toBE())
|
2019-04-15 01:27:12 +00:00
|
|
|
else:
|
2023-02-16 16:18:05 +00:00
|
|
|
IpMask(family: AddressFamily.IPv4, mask4: 0xFFFF_FFFF'u32)
|
|
|
|
of AddressFamily.IPv6:
|
2019-04-15 01:27:12 +00:00
|
|
|
if prefix <= 0:
|
2023-02-16 16:18:05 +00:00
|
|
|
IpMask(family: AddressFamily.IPv6, mask6: [0'u64, 0'u64])
|
2019-04-15 01:27:12 +00:00
|
|
|
elif prefix >= 128:
|
2023-02-16 16:18:05 +00:00
|
|
|
IpMask(family: AddressFamily.IPv6,
|
|
|
|
mask6: [0xFFFF_FFFF_FFFF_FFFF'u64, 0xFFFF_FFFF_FFFF_FFFF'u64])
|
2019-04-15 01:27:12 +00:00
|
|
|
else:
|
|
|
|
if prefix > 64:
|
2023-02-16 16:18:05 +00:00
|
|
|
let mask = 0xFFFF_FFFF_FFFF_FFFF'u64 shl (128 - prefix)
|
|
|
|
IpMask(family: AddressFamily.IPv6,
|
|
|
|
mask6: [0xFFFF_FFFF_FFFF_FFFF'u64, mask.toBE()])
|
2019-04-15 01:27:12 +00:00
|
|
|
elif prefix == 64:
|
2023-02-16 16:18:05 +00:00
|
|
|
IpMask(family: AddressFamily.IPv6,
|
|
|
|
mask6: [0xFFFF_FFFF_FFFF_FFFF'u64, 0'u64])
|
2019-04-15 01:27:12 +00:00
|
|
|
else:
|
2023-02-16 16:18:05 +00:00
|
|
|
let mask = 0xFFFF_FFFF_FFFF_FFFF'u64 shl (64 - prefix)
|
|
|
|
IpMask(family: AddressFamily.IPv6, mask6: [mask.toBE(), 0'u64])
|
|
|
|
else:
|
|
|
|
IpMask(family: family)
|
2019-04-15 01:27:12 +00:00
|
|
|
|
|
|
|
proc init*(t: typedesc[IpMask], netmask: TransportAddress): IpMask =
|
|
|
|
## Initialize network mask using address ``netmask``.
|
2023-02-16 16:18:05 +00:00
|
|
|
case netmask.family
|
|
|
|
of AddressFamily.IPv4:
|
|
|
|
IpMask(family: AddressFamily.IPv4,
|
|
|
|
mask4: uint32.fromBytes(netmask.address_v4))
|
|
|
|
of AddressFamily.IPv6:
|
|
|
|
IpMask(family: AddressFamily.IPv6,
|
|
|
|
mask6: [uint64.fromBytes(netmask.address_v6.toOpenArray(0, 7)),
|
|
|
|
uint64.fromBytes(netmask.address_v6.toOpenArray(8, 15))])
|
|
|
|
else:
|
|
|
|
IpMask(family: netmask.family)
|
2019-04-15 01:27:12 +00:00
|
|
|
|
|
|
|
proc initIp*(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))
|
2023-02-16 16:18:05 +00:00
|
|
|
t.init(tip)
|
2019-04-15 01:27:12 +00:00
|
|
|
except ValueError:
|
2023-02-16 16:18:05 +00:00
|
|
|
IpMask(family: AddressFamily.None)
|
2019-04-15 01:27:12 +00:00
|
|
|
|
|
|
|
proc init*(t: typedesc[IpMask], netmask: string): IpMask =
|
|
|
|
## Initialize network mask using hexadecimal string representation
|
|
|
|
## ``netmask``.
|
|
|
|
##
|
|
|
|
## If ``netmask`` mask is invalid, result IpMask.family will be set to
|
|
|
|
## ``AddressFamily.None``.
|
|
|
|
const
|
|
|
|
hexNumbers = {'0'..'9'}
|
|
|
|
hexCapitals = {'A'..'F'}
|
|
|
|
hexLowers = {'a'..'f'}
|
|
|
|
let length = len(netmask)
|
|
|
|
if length == 8 or length == (2 + 8):
|
|
|
|
## IPv4 mask
|
|
|
|
var offset = 0
|
|
|
|
if length == 2 + 8:
|
|
|
|
offset = 2
|
|
|
|
var res = IpMask(family: AddressFamily.IPv4)
|
|
|
|
var r, v: uint32
|
2023-02-16 16:18:05 +00:00
|
|
|
for i in 0 ..< 8:
|
2019-04-15 01:27:12 +00:00
|
|
|
if netmask[offset + i] in hexNumbers:
|
2023-02-16 16:18:05 +00:00
|
|
|
v = uint32(ord(netmask[offset + i]) - ord('0'))
|
2019-04-15 01:27:12 +00:00
|
|
|
elif netmask[offset + i] in hexCapitals:
|
2023-02-16 16:18:05 +00:00
|
|
|
v = uint32(ord(netmask[offset + i]) - ord('A') + 10)
|
2019-04-15 01:27:12 +00:00
|
|
|
elif netmask[offset + i] in hexLowers:
|
2023-02-16 16:18:05 +00:00
|
|
|
v = uint32(ord(netmask[offset + i]) - ord('a') + 10)
|
2019-04-15 01:27:12 +00:00
|
|
|
else:
|
|
|
|
return
|
|
|
|
r = (r shl 4) or v
|
2020-07-12 16:22:47 +00:00
|
|
|
res.mask4 = r.toBE()
|
2023-02-16 16:18:05 +00:00
|
|
|
res
|
2019-04-15 01:27:12 +00:00
|
|
|
elif length == 32 or length == (2 + 32):
|
|
|
|
## IPv6 mask
|
|
|
|
var offset = 0
|
|
|
|
if length == 2 + 32:
|
|
|
|
offset = 2
|
|
|
|
var res = IpMask(family: AddressFamily.IPv6)
|
|
|
|
for i in 0..1:
|
|
|
|
var r, v: uint64
|
2023-02-16 16:18:05 +00:00
|
|
|
for i in 0 ..< 16:
|
2019-04-15 01:27:12 +00:00
|
|
|
if netmask[offset + i] in hexNumbers:
|
2023-02-16 16:18:05 +00:00
|
|
|
v = uint64(ord(netmask[offset + i]) - ord('0'))
|
2019-04-15 01:27:12 +00:00
|
|
|
elif netmask[offset + i] in hexCapitals:
|
2023-02-16 16:18:05 +00:00
|
|
|
v = uint64(ord(netmask[offset + i]) - ord('A') + 10)
|
2019-04-15 01:27:12 +00:00
|
|
|
elif netmask[offset + i] in hexLowers:
|
2023-02-16 16:18:05 +00:00
|
|
|
v = uint64(ord(netmask[offset + i]) - ord('a') + 10)
|
2019-04-15 01:27:12 +00:00
|
|
|
else:
|
|
|
|
return
|
|
|
|
r = (r shl 4) or v
|
|
|
|
offset += 16
|
2020-07-12 16:22:47 +00:00
|
|
|
res.mask6[i] = r.toBE()
|
2023-02-16 16:18:05 +00:00
|
|
|
res
|
|
|
|
else:
|
|
|
|
IpMask(family: AddressFamily.None)
|
2019-04-15 01:27:12 +00:00
|
|
|
|
|
|
|
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.
|
2023-02-16 16:18:05 +00:00
|
|
|
case address.family
|
|
|
|
of AddressFamily.IPv4:
|
|
|
|
var address6: array[16, uint8]
|
|
|
|
address6[10] = 0xFF'u8
|
|
|
|
address6[11] = 0xFF'u8
|
|
|
|
let ip4 = uint32.fromBytes(address.address_v4)
|
|
|
|
address6[12 .. 15] = ip4.toBytes()
|
|
|
|
TransportAddress(family: AddressFamily.IPv6, port: address.port,
|
|
|
|
address_v6: address6)
|
|
|
|
of AddressFamily.IPv6:
|
|
|
|
address
|
|
|
|
else:
|
|
|
|
raiseAssert "Invalid address family type"
|
2019-04-15 01:27:12 +00:00
|
|
|
|
|
|
|
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.
|
2023-02-16 16:18:05 +00:00
|
|
|
case address.family
|
|
|
|
of AddressFamily.IPv6:
|
|
|
|
let data0 = uint64.fromBytes(address.address_v6.toOpenArray(0, 7))
|
|
|
|
let data1 = uint16.fromBytes(address.address_v6.toOpenArray(8, 9))
|
|
|
|
let data2 = uint16.fromBytes(address.address_v6.toOpenArray(10, 11))
|
|
|
|
(data0 == 0x00'u64) and (data1 == 0x00'u16) and (data2 == 0xFFFF'u16)
|
|
|
|
else:
|
|
|
|
false
|
2019-04-15 01:27:12 +00:00
|
|
|
|
|
|
|
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.
|
2023-02-16 16:18:05 +00:00
|
|
|
case address.family
|
|
|
|
of AddressFamily.IPv4:
|
|
|
|
address
|
|
|
|
of AddressFamily.IPv6:
|
2019-04-15 01:27:12 +00:00
|
|
|
if isV4Mapped(address):
|
2023-02-16 16:18:05 +00:00
|
|
|
let data = uint32.fromBytes(address.address_v6.toOpenArray(12, 15))
|
|
|
|
TransportAddress(family: AddressFamily.IPv4, port: address.port,
|
|
|
|
address_v4: data.toBytes())
|
|
|
|
else:
|
|
|
|
TransportAddress(family: AddressFamily.None)
|
|
|
|
else:
|
|
|
|
TransportAddress(family: AddressFamily.None)
|
2019-04-15 01:27:12 +00:00
|
|
|
|
|
|
|
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``.
|
2023-02-16 16:18:05 +00:00
|
|
|
if (a.family == AddressFamily.IPv4) and (m.family == AddressFamily.IPv6):
|
2019-04-15 01:27:12 +00:00
|
|
|
if (m.mask6[0] == 0xFFFF_FFFF_FFFF_FFFF'u64) and
|
|
|
|
(m.mask6[1] and 0xFFFF_FFFF'u64) == 0xFFFF_FFFF'u64:
|
2023-02-16 16:18:05 +00:00
|
|
|
let
|
|
|
|
mask = uint32((m.mask6[1] shr 32) and 0xFFFF_FFFF'u64)
|
|
|
|
data = uint32.fromBytes(a.address_v4)
|
|
|
|
TransportAddress(family: AddressFamily.IPv4, port: a.port,
|
|
|
|
address_v4: (data and mask).toBytes())
|
|
|
|
else:
|
|
|
|
TransportAddress(family: AddressFamily.None)
|
|
|
|
elif (a.family == AddressFamily.IPv6) and (m.family == AddressFamily.IPv4):
|
2019-04-15 01:27:12 +00:00
|
|
|
var ip = a.toIPv4()
|
2023-02-16 16:18:05 +00:00
|
|
|
if ip.family != AddressFamily.IPv4:
|
|
|
|
return TransportAddress(family: AddressFamily.None)
|
|
|
|
let data = uint32.fromBytes(ip.address_v4)
|
|
|
|
ip.address_v4[0 .. 3] = (data and m.mask4).toBytes()
|
|
|
|
var res = ip.toIPv6()
|
|
|
|
res.port = a.port
|
|
|
|
res
|
2019-04-15 01:27:12 +00:00
|
|
|
elif a.family == AddressFamily.IPv4 and m.family == AddressFamily.IPv4:
|
2023-02-16 16:18:05 +00:00
|
|
|
let data = uint32.fromBytes(a.address_v4)
|
|
|
|
TransportAddress(family: AddressFamily.IPv4, port: a.port,
|
|
|
|
address_v4: (data and m.mask4).toBytes())
|
2019-04-15 01:27:12 +00:00
|
|
|
elif a.family == AddressFamily.IPv6 and m.family == AddressFamily.IPv6:
|
2023-02-16 16:18:05 +00:00
|
|
|
var address6: array[16, uint8]
|
|
|
|
let
|
|
|
|
data0 = uint64.fromBytes(a.address_v6.toOpenArray(0, 7))
|
|
|
|
data1 = uint64.fromBytes(a.address_v6.toOpenArray(8, 15))
|
|
|
|
address6[0 .. 7] = (data0 and m.mask6[0]).toBytes()
|
|
|
|
address6[8 .. 15] = (data1 and m.mask6[1]).toBytes()
|
|
|
|
TransportAddress(family: AddressFamily.IPv6, port: a.port,
|
|
|
|
address_v6: address6)
|
|
|
|
else:
|
|
|
|
TransportAddress(family: AddressFamily.None)
|
2019-04-15 01:27:12 +00:00
|
|
|
|
|
|
|
proc prefix*(mask: IpMask): int =
|
|
|
|
## Returns number of bits set `1` in IP mask ``mask``.
|
|
|
|
##
|
|
|
|
## Procedure returns ``-1`` if mask is not canonical, e.g. has holes with
|
|
|
|
## ``0`` bits between ``1`` bits.
|
2023-02-16 16:18:05 +00:00
|
|
|
case mask.family
|
|
|
|
of AddressFamily.IPv4:
|
|
|
|
var
|
|
|
|
res = 0
|
|
|
|
n = mask.mask4.fromBE()
|
2019-04-15 01:27:12 +00:00
|
|
|
while n != 0:
|
2023-02-16 16:18:05 +00:00
|
|
|
if (n and 0x8000_0000'u32) == 0'u32: return -1
|
2019-04-15 01:27:12 +00:00
|
|
|
n = n shl 1
|
2023-02-16 16:18:05 +00:00
|
|
|
inc(res)
|
|
|
|
res
|
|
|
|
of AddressFamily.IPv6:
|
|
|
|
let mask6 = [mask.mask6[0].fromBE(), mask.mask6[1].fromBE()]
|
|
|
|
var res = 0
|
|
|
|
if mask6[0] == 0xFFFF_FFFF_FFFF_FFFF'u64:
|
|
|
|
res += 64
|
|
|
|
if mask6[1] == 0xFFFF_FFFF_FFFF_FFFF'u64:
|
|
|
|
res + 64
|
2019-04-15 01:27:12 +00:00
|
|
|
else:
|
2023-02-16 16:18:05 +00:00
|
|
|
var n = mask6[1]
|
2019-04-15 01:27:12 +00:00
|
|
|
while n != 0:
|
2023-02-16 16:18:05 +00:00
|
|
|
if (n and 0x8000_0000_0000_0000'u64) == 0'u64: return -1
|
2019-04-15 01:27:12 +00:00
|
|
|
n = n shl 1
|
2023-02-16 16:18:05 +00:00
|
|
|
inc(res)
|
|
|
|
res
|
2019-04-15 01:27:12 +00:00
|
|
|
else:
|
2023-02-16 16:18:05 +00:00
|
|
|
var n = mask6[0]
|
2019-04-15 01:27:12 +00:00
|
|
|
while n != 0:
|
2023-02-16 16:18:05 +00:00
|
|
|
if (n and 0x8000_0000_0000_0000'u64) == 0'u64: return -1
|
2019-04-15 01:27:12 +00:00
|
|
|
n = n shl 1
|
2023-02-16 16:18:05 +00:00
|
|
|
inc(res)
|
|
|
|
if mask6[1] != 0x00'u64: return -1
|
|
|
|
res
|
|
|
|
else:
|
|
|
|
-1
|
2019-04-15 01:27:12 +00:00
|
|
|
|
|
|
|
proc subnetMask*(mask: IpMask): TransportAddress =
|
|
|
|
## Returns TransportAddress representation of IP mask ``mask``.
|
2023-02-16 16:18:05 +00:00
|
|
|
case mask.family
|
|
|
|
of AddressFamily.IPv4:
|
|
|
|
TransportAddress(family: AddressFamily.IPv4,
|
|
|
|
address_v4: mask.mask4.toBytes())
|
|
|
|
of AddressFamily.IPv6:
|
|
|
|
var address6: array[16, uint8]
|
|
|
|
address6[0 .. 7] = mask.mask6[0].toBytes()
|
|
|
|
address6[8 .. 15] = mask.mask6[1].toBytes()
|
|
|
|
TransportAddress(family: AddressFamily.IPv6, address_v6: address6)
|
|
|
|
else:
|
|
|
|
TransportAddress(family: mask.family)
|
2019-04-15 01:27:12 +00:00
|
|
|
|
|
|
|
proc `$`*(mask: IpMask, include0x = false): string =
|
|
|
|
## Returns hexadecimal string representation of IP mask ``mask``.
|
2023-02-16 16:18:05 +00:00
|
|
|
case mask.family
|
|
|
|
of AddressFamily.IPv4:
|
|
|
|
var res = if include0x: "0x" else: ""
|
2019-04-15 01:27:12 +00:00
|
|
|
var n = 32
|
2023-02-16 16:18:05 +00:00
|
|
|
var m = mask.mask4.fromBE()
|
2019-04-15 01:27:12 +00:00
|
|
|
while n > 0:
|
|
|
|
n -= 4
|
2022-11-02 07:09:15 +00:00
|
|
|
var c = int((m shr n) and 0x0F)
|
2019-04-15 01:27:12 +00:00
|
|
|
if c < 10:
|
2023-02-16 16:18:05 +00:00
|
|
|
res.add(chr(ord('0') + c))
|
2019-04-15 01:27:12 +00:00
|
|
|
else:
|
2023-02-16 16:18:05 +00:00
|
|
|
res.add(chr(ord('A') + (c - 10)))
|
|
|
|
res
|
|
|
|
of AddressFamily.IPv6:
|
|
|
|
let mask6 = [mask.mask6[0].fromBE(), mask.mask6[1].fromBE()]
|
|
|
|
var res = if include0x: "0x" else: ""
|
|
|
|
for i in 0 .. 1:
|
2019-04-15 01:27:12 +00:00
|
|
|
var n = 64
|
2023-02-16 16:18:05 +00:00
|
|
|
var m = mask6[i]
|
2019-04-15 01:27:12 +00:00
|
|
|
while n > 0:
|
|
|
|
n -= 4
|
2022-11-02 07:09:15 +00:00
|
|
|
var c = int((m shr n) and 0x0F)
|
2019-04-15 01:27:12 +00:00
|
|
|
if c < 10:
|
2023-02-16 16:18:05 +00:00
|
|
|
res.add(chr(ord('0') + c))
|
2019-04-15 01:27:12 +00:00
|
|
|
else:
|
2023-02-16 16:18:05 +00:00
|
|
|
res.add(chr(ord('A') + (c - 10)))
|
|
|
|
res
|
2019-04-15 01:27:12 +00:00
|
|
|
else:
|
2023-02-16 16:18:05 +00:00
|
|
|
"Unknown mask family: " & $mask.family
|
2019-04-15 01:27:12 +00:00
|
|
|
|
exception tracking (#166)
* exception tracking
This PR adds minimal exception tracking to chronos, moving the goalpost
one step further.
In particular, it becomes invalid to raise exceptions from `callSoon`
callbacks: this is critical for writing correct error handling because
there's no reasonable way that a user of chronos can possibly _reason_
about exceptions coming out of there: the event loop will be in an
indeterminite state when the loop is executing an _random_ callback.
As expected, there are several issues in the error handling of chronos:
in particular, it will end up in an inconsistent internal state whenever
the selector loop operations fail, because the internal state update
functions are not written in an exception-safe way. This PR turns this
into a Defect, which probably is not the optimal way of handling things
- expect more work to be done here.
Some API have no way of reporting back errors to callers - for example,
when something fails in the accept loop, there's not much it can do, and
no way to report it back to the user of the API - this has been fixed
with the new accept flow - the old one should be deprecated.
Finally, there is information loss in the API: in composite operations
like `poll` and `waitFor` there's no way to differentiate internal
errors from user-level errors originating from callbacks.
* store `CatchableError` in future
* annotate proc's with correct raises information
* `selectors2` to avoid non-CatchableError IOSelectorsException
* `$` should never raise
* remove unnecessary gcsafe annotations
* fix exceptions leaking out of timer waits
* fix some imports
* functions must signal raising the union of all exceptions across all
platforms to enable cross-platform code
* switch to unittest2
* add `selectors2` which supercedes the std library version and fixes
several exception handling issues in there
* fixes
* docs, platform-independent eh specifiers for some functions
* add feature flag for strict exception mode
also bump version to 3.0.0 - _most_ existing code should be compatible
with this version of exception handling but some things might need
fixing - callbacks, existing raises specifications etc.
* fix AsyncCheck for non-void T
2021-03-24 09:08:33 +00:00
|
|
|
proc ip*(mask: IpMask): string {.raises: [Defect, ValueError].} =
|
2019-04-15 01:27:12 +00:00
|
|
|
## Returns IP address text representation of IP mask ``mask``.
|
2023-02-16 16:18:05 +00:00
|
|
|
case mask.family
|
|
|
|
of AddressFamily.IPv4:
|
|
|
|
var address4: array[4, uint8]
|
|
|
|
copyMem(addr address4[0], unsafeAddr mask.mask4, sizeof(uint32))
|
|
|
|
$IpAddress(family: IpAddressFamily.IPv4, address_v4: address4)
|
|
|
|
of AddressFamily.Ipv6:
|
|
|
|
var address6: array[16, uint8]
|
|
|
|
copyMem(addr address6[0], unsafeAddr mask.mask6[0], 16)
|
|
|
|
$IpAddress(family: IpAddressFamily.IPv6, address_v6: address6)
|
2019-04-15 01:27:12 +00:00
|
|
|
else:
|
2023-02-16 16:18:05 +00:00
|
|
|
raise newException(ValueError, "Invalid mask family type")
|
2019-04-15 01:27:12 +00:00
|
|
|
|
|
|
|
proc init*(t: typedesc[IpNet], host: TransportAddress,
|
|
|
|
prefix: int): IpNet {.inline.} =
|
|
|
|
## Initialize IP Network using host address ``host`` and prefix length
|
|
|
|
## ``prefix``.
|
2023-02-16 16:18:05 +00:00
|
|
|
IpNet(mask: IpMask.init(host.family, prefix), host: host)
|
2019-04-15 01:27:12 +00:00
|
|
|
|
|
|
|
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.
|
2023-02-16 16:18:05 +00:00
|
|
|
doAssert(host.family == mask.family)
|
|
|
|
IpNet(mask: IpMask.init(mask), host: host)
|
2019-04-15 01:27:12 +00:00
|
|
|
|
|
|
|
proc init*(t: typedesc[IpNet], host: TransportAddress,
|
|
|
|
mask: IpMask): IpNet {.inline.} =
|
|
|
|
## Initialize IP Network using host address ``host`` and network mask
|
|
|
|
## ``mask``.
|
2023-02-16 16:18:05 +00:00
|
|
|
IpNet(mask: mask, host: host)
|
2019-04-15 01:27:12 +00:00
|
|
|
|
exception tracking (#166)
* exception tracking
This PR adds minimal exception tracking to chronos, moving the goalpost
one step further.
In particular, it becomes invalid to raise exceptions from `callSoon`
callbacks: this is critical for writing correct error handling because
there's no reasonable way that a user of chronos can possibly _reason_
about exceptions coming out of there: the event loop will be in an
indeterminite state when the loop is executing an _random_ callback.
As expected, there are several issues in the error handling of chronos:
in particular, it will end up in an inconsistent internal state whenever
the selector loop operations fail, because the internal state update
functions are not written in an exception-safe way. This PR turns this
into a Defect, which probably is not the optimal way of handling things
- expect more work to be done here.
Some API have no way of reporting back errors to callers - for example,
when something fails in the accept loop, there's not much it can do, and
no way to report it back to the user of the API - this has been fixed
with the new accept flow - the old one should be deprecated.
Finally, there is information loss in the API: in composite operations
like `poll` and `waitFor` there's no way to differentiate internal
errors from user-level errors originating from callbacks.
* store `CatchableError` in future
* annotate proc's with correct raises information
* `selectors2` to avoid non-CatchableError IOSelectorsException
* `$` should never raise
* remove unnecessary gcsafe annotations
* fix exceptions leaking out of timer waits
* fix some imports
* functions must signal raising the union of all exceptions across all
platforms to enable cross-platform code
* switch to unittest2
* add `selectors2` which supercedes the std library version and fixes
several exception handling issues in there
* fixes
* docs, platform-independent eh specifiers for some functions
* add feature flag for strict exception mode
also bump version to 3.0.0 - _most_ existing code should be compatible
with this version of exception handling but some things might need
fixing - callbacks, existing raises specifications etc.
* fix AsyncCheck for non-void T
2021-03-24 09:08:33 +00:00
|
|
|
proc init*(t: typedesc[IpNet], network: string): IpNet {.
|
|
|
|
raises: [Defect, TransportAddressError].} =
|
2019-04-15 01:27:12 +00:00
|
|
|
## Initialize IP Network from string representation in format
|
|
|
|
## <address>/<prefix length> or <address>/<netmask address>.
|
|
|
|
var parts = network.rsplit("/", maxsplit = 1)
|
|
|
|
var host, mhost: TransportAddress
|
|
|
|
var ipaddr: IpAddress
|
|
|
|
var mask: IpMask
|
|
|
|
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:
|
|
|
|
try:
|
|
|
|
prefix = parseInt(parts[1])
|
2023-06-05 11:03:38 +00:00
|
|
|
except ValueError:
|
2019-04-15 01:27:12 +00:00
|
|
|
prefix = -1
|
|
|
|
if prefix == -1:
|
|
|
|
ipaddr = parseIpAddress(parts[1])
|
|
|
|
if ipaddr.family == IpAddressFamily.IPv4:
|
|
|
|
mhost = TransportAddress(family: AddressFamily.IPv4)
|
|
|
|
mhost.address_v4 = ipaddr.address_v4
|
|
|
|
elif ipaddr.family == IpAddressFamily.IPv6:
|
|
|
|
mhost = TransportAddress(family: AddressFamily.IPv6)
|
|
|
|
mhost.address_v6 = ipaddr.address_v6
|
|
|
|
mask = IpMask.init(mhost)
|
|
|
|
if mask.family != host.family:
|
|
|
|
raise newException(TransportAddressError,
|
|
|
|
"Incorrect network address!")
|
|
|
|
else:
|
|
|
|
if (ipaddr.family == IpAddressFamily.IPv4 and
|
|
|
|
(prefix < 0 or prefix > 32)) or
|
|
|
|
(ipaddr.family == IpAddressFamily.IPv6 and
|
2019-04-15 08:30:52 +00:00
|
|
|
(prefix < 0 or prefix > 128)) or
|
|
|
|
(prefix == 0 and parts[1][0] notin {'0'..'9'}): # /-0 case
|
2019-04-15 01:27:12 +00:00
|
|
|
raise newException(TransportAddressError,
|
|
|
|
"Incorrect network address!")
|
|
|
|
if prefix == -1:
|
|
|
|
result = t.init(host, mask)
|
|
|
|
else:
|
|
|
|
result = t.init(host, prefix)
|
2023-06-05 11:03:38 +00:00
|
|
|
except ValueError as exc:
|
|
|
|
raise newException(TransportAddressError, exc.msg)
|
2019-04-15 01:27:12 +00:00
|
|
|
|
|
|
|
proc `==`*(n1, n2: IpNet): bool {.inline.} =
|
|
|
|
## Returns ``true`` if networks ``n1`` and ``n2`` are equal in IP family and
|
|
|
|
## by value.
|
2023-02-16 16:18:05 +00:00
|
|
|
if n1.host.family != n2.host.family:
|
|
|
|
return false
|
|
|
|
case n1.host.family
|
|
|
|
of AddressFamily.IPv4:
|
|
|
|
(n1.host.address_v4 == n2.host.address_v4) and (n1.mask == n2.mask)
|
|
|
|
of AddressFamily.IPv6:
|
|
|
|
(n1.host.address_v6 == n2.host.address_v6) and (n1.mask == n2.mask)
|
|
|
|
else:
|
|
|
|
false
|
2019-04-15 01:27:12 +00:00
|
|
|
|
|
|
|
proc contains*(net: IpNet, address: TransportAddress): bool =
|
|
|
|
## Returns ``true`` if ``address`` belongs to IP Network ``net``
|
2023-02-16 16:18:05 +00:00
|
|
|
if net.host.family != address.family:
|
|
|
|
return false
|
|
|
|
var host1 = mask(address, net.mask)
|
|
|
|
var host2 = mask(net.host, net.mask)
|
|
|
|
host2.port = host1.port
|
|
|
|
host1 == host2
|
2019-04-15 01:27:12 +00:00
|
|
|
|
|
|
|
proc broadcast*(net: IpNet): TransportAddress =
|
|
|
|
## Returns broadcast address for IP Network ``net``.
|
2023-02-16 16:18:05 +00:00
|
|
|
case net.host.family
|
|
|
|
of AddressFamily.IPv4:
|
|
|
|
let
|
|
|
|
host = uint32.fromBytes(net.host.address_v4)
|
|
|
|
mask = net.mask.mask4
|
|
|
|
TransportAddress(family: AddressFamily.IPv4,
|
|
|
|
address_v4: (host or (not(mask))).toBytes())
|
|
|
|
of AddressFamily.IPv6:
|
|
|
|
var address6: array[16, uint8]
|
|
|
|
let
|
|
|
|
host0 = uint64.fromBytes(net.host.address_v6.toOpenArray(0, 7))
|
|
|
|
host1 = uint64.fromBytes(net.host.address_v6.toOpenArray(8, 15))
|
|
|
|
data0 = net.mask.mask6[0]
|
|
|
|
data1 = net.mask.mask6[1]
|
|
|
|
address6[0 .. 7] = (host0 or (not(data0))).toBytes()
|
|
|
|
address6[8 .. 15] = (host1 or (not(data1))).toBytes()
|
|
|
|
TransportAddress(family: AddressFamily.IPv6, address_v6: address6)
|
|
|
|
else:
|
|
|
|
TransportAddress(family: AddressFamily.None)
|
2019-04-15 01:27:12 +00:00
|
|
|
|
|
|
|
proc subnetMask*(net: IpNet): TransportAddress =
|
|
|
|
## Returns netmask address for IP Network ``net``.
|
2023-02-16 16:18:05 +00:00
|
|
|
subnetMask(net.mask)
|
2019-04-15 01:27:12 +00:00
|
|
|
|
|
|
|
proc network*(net: IpNet): TransportAddress {.inline.} =
|
|
|
|
## Returns network address (host address masked with network mask) for
|
|
|
|
## IP Network ``net``.
|
2023-02-16 16:18:05 +00:00
|
|
|
mask(net.host, net.mask)
|
2019-04-15 01:27:12 +00:00
|
|
|
|
|
|
|
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
|
2023-02-16 16:18:05 +00:00
|
|
|
doAssert(address1.family == address2.family)
|
|
|
|
case address1.family
|
|
|
|
of AddressFamily.IPv4:
|
|
|
|
let
|
|
|
|
data1 = uint32.fromBytes(address1.address_v4)
|
|
|
|
data2 = uint32.fromBytes(address2.address_v4)
|
|
|
|
TransportAddress(family: AddressFamily.IPv4,
|
|
|
|
address_v4: (data1 and data2).toBytes())
|
|
|
|
of AddressFamily.IPv6:
|
|
|
|
var address6: array[16, uint8]
|
|
|
|
let
|
|
|
|
data1 = uint64.fromBytes(address1.address_v6.toOpenArray(0, 7))
|
|
|
|
data2 = uint64.fromBytes(address1.address_v6.toOpenArray(8, 15))
|
|
|
|
data3 = uint64.fromBytes(address2.address_v6.toOpenArray(0, 7))
|
|
|
|
data4 = uint64.fromBytes(address2.address_v6.toOpenArray(8, 15))
|
|
|
|
address6[0 .. 7] = (data1 and data3).toBytes()
|
|
|
|
address6[8 .. 15] = (data2 and data4).toBytes()
|
|
|
|
TransportAddress(family: AddressFamily.IPv6, address_v6: address6)
|
|
|
|
else:
|
|
|
|
raiseAssert "Invalid address family type"
|
2019-04-15 01:27:12 +00:00
|
|
|
|
|
|
|
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
|
2023-02-16 16:18:05 +00:00
|
|
|
doAssert(address1.family == address2.family)
|
|
|
|
case address1.family
|
|
|
|
of AddressFamily.IPv4:
|
|
|
|
let
|
|
|
|
data1 = uint32.fromBytes(address1.address_v4)
|
|
|
|
data2 = uint32.fromBytes(address2.address_v4)
|
|
|
|
TransportAddress(family: AddressFamily.IPv4,
|
|
|
|
address_v4: (data1 or data2).toBytes())
|
|
|
|
of AddressFamily.IPv6:
|
|
|
|
var address6: array[16, uint8]
|
|
|
|
let
|
|
|
|
data1 = uint64.fromBytes(address1.address_v6.toOpenArray(0, 7))
|
|
|
|
data2 = uint64.fromBytes(address1.address_v6.toOpenArray(8, 15))
|
|
|
|
data3 = uint64.fromBytes(address2.address_v6.toOpenArray(0, 7))
|
|
|
|
data4 = uint64.fromBytes(address2.address_v6.toOpenArray(8, 15))
|
|
|
|
address6[0 .. 7] = (data1 or data3).toBytes()
|
|
|
|
address6[8 .. 15] = (data2 or data4).toBytes()
|
|
|
|
TransportAddress(family: AddressFamily.IPv6, address_v6: address6)
|
|
|
|
else:
|
|
|
|
raiseAssert "Invalid address family type"
|
2019-04-15 01:27:12 +00:00
|
|
|
|
|
|
|
proc `not`*(address: TransportAddress): TransportAddress =
|
|
|
|
## Bitwise ``not`` operation for ``address``.
|
2023-02-16 16:18:05 +00:00
|
|
|
case address.family
|
|
|
|
of AddressFamily.IPv4:
|
|
|
|
let data = not(uint32.fromBytes(address.address_v4))
|
|
|
|
TransportAddress(family: AddressFamily.IPv4, address_v4: data.toBytes())
|
|
|
|
of AddressFamily.IPv6:
|
|
|
|
var address6: array[16, uint8]
|
|
|
|
let
|
|
|
|
data1 = not(uint64.fromBytes(address.address_v6.toOpenArray(0, 7)))
|
|
|
|
data2 = not(uint64.fromBytes(address.address_v6.toOpenArray(8, 15)))
|
|
|
|
address6[0 .. 7] = data1.toBytes()
|
|
|
|
address6[8 .. 15] = data2.toBytes()
|
|
|
|
TransportAddress(family: AddressFamily.IPv6, address_v6: address6)
|
|
|
|
else:
|
|
|
|
address
|
|
|
|
|
|
|
|
proc `+`*(address: TransportAddress, v: int|uint): TransportAddress =
|
|
|
|
## Add to IPv4/IPv6 transport ``address`` integer ``v``.
|
|
|
|
if v == 0: return address
|
|
|
|
case address.family
|
|
|
|
of AddressFamily.IPv4:
|
|
|
|
let
|
|
|
|
av = uint32.fromBytesBE(address.address_v4)
|
|
|
|
address4 =
|
|
|
|
when v is int:
|
|
|
|
if v <= 0:
|
|
|
|
# Case when v == 0 is already covered.
|
|
|
|
let v32 = uint32(uint64(not(v) + 1) and 0xFFFF_FFFF'u64)
|
|
|
|
(av - v32).toBytesBE()
|
|
|
|
else:
|
|
|
|
let v32 = uint32(uint64(v) and 0xFFFF_FFFF'u64)
|
|
|
|
(av + v32).toBytesBE()
|
|
|
|
else:
|
|
|
|
let v32 = uint32(uint64(v) and 0xFFFF_FFFF'u64)
|
|
|
|
(av + v32).toBytesBE()
|
|
|
|
TransportAddress(family: AddressFamily.IPv4, port: address.port,
|
|
|
|
address_v4: address4)
|
|
|
|
of AddressFamily.IPv6:
|
|
|
|
let a2 = uint64.fromBytesBE(address.address_v6.toOpenArray(8, 15))
|
|
|
|
var
|
|
|
|
a1 = uint64.fromBytesBE(address.address_v6.toOpenArray(0, 7))
|
|
|
|
address6: array[16, uint8]
|
|
|
|
when v is int:
|
|
|
|
if v <= 0:
|
|
|
|
# Case when v == 0 is already covered
|
|
|
|
let a3 = a2 - uint64(not(int64(v)) + 1)
|
|
|
|
if a3 > a2: a1 = a1 - 1'u64
|
|
|
|
address6[0 .. 7] = a1.toBytesBE()
|
|
|
|
address6[8 .. 15] = a3.toBytesBE()
|
|
|
|
else:
|
|
|
|
let a3 = a2 + uint64(v)
|
|
|
|
if a3 < a2: a1 = a1 + 1'u64
|
|
|
|
address6[0 .. 7] = a1.toBytesBE()
|
|
|
|
address6[8 .. 15] = a3.toBytesBE()
|
|
|
|
else:
|
|
|
|
# v is unsigned so it is always bigger than zero.
|
|
|
|
let a3 = a2 + uint64(v)
|
|
|
|
if a3 < a2: a1 = a1 + 1'u64
|
|
|
|
address6[0 .. 7] = a1.toBytesBE()
|
|
|
|
address6[8 .. 15] = a3.toBytesBE()
|
|
|
|
|
|
|
|
TransportAddress(family: AddressFamily.IPv6, port: address.port,
|
|
|
|
address_v6: address6)
|
|
|
|
else:
|
|
|
|
address
|
|
|
|
|
|
|
|
proc `-`*(address: TransportAddress, v: int|uint): TransportAddress =
|
|
|
|
## Sub from IPv4/IPv6 transport ``address`` integer ``v``.
|
|
|
|
if v == 0: return address
|
|
|
|
case address.family
|
|
|
|
of AddressFamily.IPv4:
|
|
|
|
let
|
|
|
|
av = uint32.fromBytesBE(address.address_v4)
|
|
|
|
address4 =
|
|
|
|
when v is int:
|
|
|
|
if v <= 0:
|
|
|
|
# Case when v == 0 is already covered.
|
|
|
|
let v32 = uint32(uint64(not(v) + 1) and 0xFFFF_FFFF'u64)
|
|
|
|
(av + v32).toBytesBE()
|
|
|
|
else:
|
|
|
|
let v32 = uint32(uint64(v) and 0xFFFF_FFFF'u64)
|
|
|
|
(av - v32).toBytesBE()
|
|
|
|
else:
|
|
|
|
let v32 = uint32(uint64(v) and 0xFFFF_FFFF'u64)
|
|
|
|
(av - v32).toBytesBE()
|
|
|
|
TransportAddress(family: AddressFamily.IPv4, port: address.port,
|
|
|
|
address_v4: address4)
|
|
|
|
of AddressFamily.IPv6:
|
|
|
|
let a2 = uint64.fromBytesBE(address.address_v6.toOpenArray(8, 15))
|
|
|
|
var
|
|
|
|
a1 = uint64.fromBytesBE(address.address_v6.toOpenArray(0, 7))
|
|
|
|
address6: array[16, uint8]
|
|
|
|
when v is int:
|
|
|
|
if v <= 0:
|
|
|
|
# Case when v == 0 is already covered
|
|
|
|
let a3 = a2 + uint64(not(int64(v)) + 1)
|
|
|
|
if a3 < a2: a1 = a1 + 1'u64
|
|
|
|
address6[0 .. 7] = a1.toBytesBE()
|
|
|
|
address6[8 .. 15] = a3.toBytesBE()
|
|
|
|
else:
|
|
|
|
let a3 = a2 - uint64(v)
|
|
|
|
if a3 > a2: a1 = a1 - 1'u64
|
|
|
|
address6[0 .. 7] = a1.toBytesBE()
|
|
|
|
address6[8 .. 15] = a3.toBytesBE()
|
|
|
|
else:
|
|
|
|
# v is unsigned so it is always bigger than zero.
|
|
|
|
let a3 = a2 - uint64(v)
|
|
|
|
if a3 > a2: a1 = a1 - 1'u64
|
|
|
|
address6[0 .. 7] = a1.toBytesBE()
|
|
|
|
address6[8 .. 15] = a3.toBytesBE()
|
|
|
|
|
|
|
|
TransportAddress(family: AddressFamily.IPv6, port: address.port,
|
|
|
|
address_v6: address6)
|
|
|
|
else:
|
|
|
|
address
|
|
|
|
|
|
|
|
proc inc*(address: var TransportAddress, v: int = 1) =
|
|
|
|
## Increment IPv4/IPv6 transport ``address`` by integer ``v``.
|
2019-04-15 01:27:12 +00:00
|
|
|
address = address + v
|
|
|
|
|
2023-02-16 16:18:05 +00:00
|
|
|
proc dec*(address: var TransportAddress, v: int = 1) =
|
|
|
|
## Decrement IPv4/IPv6 transport ``address`` by integer ``v``.
|
|
|
|
address = address - v
|
|
|
|
|
2019-04-15 01:27:12 +00:00
|
|
|
proc `$`*(net: IpNet): string =
|
|
|
|
## Return string representation of IP network in format:
|
|
|
|
## <IPv4 or IPv6 address>/<prefix length>.
|
2023-02-16 16:18:05 +00:00
|
|
|
var res =
|
|
|
|
case net.host.family
|
|
|
|
of AddressFamily.IPv4:
|
|
|
|
$IpAddress(family: IpAddressFamily.IPv4, address_v4: net.host.address_v4)
|
|
|
|
of AddressFamily.IPv6:
|
|
|
|
$IpAddress(family: IpAddressFamily.IPv6, address_v6: net.host.address_v6)
|
2019-04-15 01:27:12 +00:00
|
|
|
else:
|
2023-02-16 16:18:05 +00:00
|
|
|
return "Invalid network address family"
|
2019-04-15 01:27:12 +00:00
|
|
|
|
2023-02-16 16:18:05 +00:00
|
|
|
res.add("/")
|
|
|
|
let prefix = net.mask.prefix()
|
|
|
|
if prefix == -1:
|
|
|
|
try:
|
|
|
|
res.add(net.mask.ip())
|
|
|
|
except ValueError:
|
|
|
|
return "Invalid network mask address"
|
|
|
|
else:
|
|
|
|
res.add($prefix)
|
|
|
|
res
|
|
|
|
|
|
|
|
template a4(): untyped {.dirty.} =
|
|
|
|
address.address_v4
|
|
|
|
template a6(): untyped {.dirty.} =
|
|
|
|
address.address_v6
|
|
|
|
|
|
|
|
proc isNone*(address: TransportAddress): bool {.inline.} =
|
|
|
|
## Returns ``true`` if ``address`` is not initialized yet, e.g. its ``family``
|
2019-04-15 01:27:12 +00:00
|
|
|
## field is not set or equal to ``AddressFamily.None``.
|
2023-02-16 16:18:05 +00:00
|
|
|
address.family == AddressFamily.None
|
2019-04-15 01:27:12 +00:00
|
|
|
|
|
|
|
proc isZero*(address: TransportAddress): bool {.inline.} =
|
|
|
|
## Returns ``true`` if ``address`` is full of zeros, but its ``family`` is
|
|
|
|
## not ``AddressFamily.None``.
|
2023-02-16 16:18:05 +00:00
|
|
|
case address.family
|
|
|
|
of AddressFamily.IPv4:
|
|
|
|
uint32.fromBytes(a4()) == 0'u32
|
|
|
|
of AddressFamily.IPv6:
|
|
|
|
(uint64.fromBytes(a6.toOpenArray(0, 7)) == 0'u64) and
|
|
|
|
(uint64.fromBytes(a6.toOpenArray(8, 15)) == 0'u64)
|
|
|
|
of AddressFamily.Unix:
|
|
|
|
len($cast[cstring](unsafeAddr address.address_un[0])) == 0
|
|
|
|
else:
|
|
|
|
false
|
|
|
|
|
|
|
|
proc isUnspecified*(address: TransportAddress): bool {.inline.} =
|
|
|
|
## Alias for isZero().
|
|
|
|
isZero(address)
|
2019-04-15 01:27:12 +00:00
|
|
|
|
|
|
|
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
|
2023-02-16 16:18:05 +00:00
|
|
|
case address.family
|
|
|
|
of AddressFamily.IPv4:
|
|
|
|
(a4[0] and 0xF0'u8) == 0xE0'u8
|
|
|
|
of AddressFamily.IPv6:
|
|
|
|
a6[0] == 0xFF'u8
|
|
|
|
else:
|
|
|
|
false
|
|
|
|
|
|
|
|
proc isUnicast*(address: TransportAddress): bool =
|
|
|
|
## Returns ``true`` if ``address`` is unicast, as defined by [IETF RFC 4291].
|
|
|
|
## Any address that is not a IPv6 multicast address `FF00::/8` is unicast.
|
|
|
|
case address.family
|
|
|
|
of AddressFamily.IPv4:
|
|
|
|
not(isZero(address) and not isMulticast(address))
|
|
|
|
of AddressFamily.IPv6:
|
|
|
|
not(isMulticast(address))
|
|
|
|
else:
|
|
|
|
false
|
2019-04-15 01:27:12 +00:00
|
|
|
|
|
|
|
proc isInterfaceLocalMulticast*(address: TransportAddress): bool =
|
|
|
|
## Returns ``true`` if ``address`` is interface local multicast address.
|
|
|
|
##
|
|
|
|
## ``IPv4``: N/A (always returns ``false``)
|
2023-02-16 16:18:05 +00:00
|
|
|
case address.family
|
|
|
|
of AddressFamily.IPv4:
|
|
|
|
false
|
|
|
|
of AddressFamily.IPv6:
|
|
|
|
(a6[0] == 0xFF'u8) and ((a6[1] and 0x0F'u8) == 0x01'u8)
|
|
|
|
else:
|
|
|
|
false
|
2019-04-15 01:27:12 +00:00
|
|
|
|
|
|
|
proc isLinkLocalMulticast*(address: TransportAddress): bool =
|
2019-08-16 10:27:27 +00:00
|
|
|
## Returns ``true`` if ``address`` is link local multicast address.
|
2019-04-15 01:27:12 +00:00
|
|
|
##
|
|
|
|
## ``IPv4``: 224.0.0.0 - 224.0.0.255
|
2023-02-16 16:18:05 +00:00
|
|
|
case address.family
|
|
|
|
of AddressFamily.IPv4:
|
|
|
|
(a4[0] == 224'u8) and (a4[1] == 0'u8) and (a4[2] == 0'u8)
|
|
|
|
of AddressFamily.IPv6:
|
|
|
|
(a6[0] == 0xFF'u8) and ((a6[1] and 0x0F'u8) == 0x02'u8)
|
|
|
|
else:
|
|
|
|
false
|
|
|
|
|
|
|
|
proc isUniqueLocal*(address: TransportAddress): bool =
|
|
|
|
## Returns ``true`` if ``address`` is a unique local IPv6 unicast address.
|
|
|
|
##
|
|
|
|
## ``IPv6``: FC00::/7
|
|
|
|
case address.family
|
|
|
|
of AddressFamily.IPv4:
|
|
|
|
false
|
|
|
|
of AddressFamily.IPv6:
|
|
|
|
(a6[0] and 0xFE'u8) == 0xFC'u8
|
|
|
|
else:
|
|
|
|
false
|
|
|
|
|
|
|
|
proc isUnicastLinkLocal*(address: TransportAddress): bool =
|
|
|
|
## Returns ``true`` if ``address`` is a IPv6 unicast address with link-local
|
|
|
|
## scope.
|
|
|
|
##
|
|
|
|
## NOTE: While [RFC 4291 section 2.5.3] mentions about the [loopback address]
|
|
|
|
## `::1` that "it is treated as having Link-Local scope", this does not mean
|
|
|
|
## that the loopback address actually has link-local scope and procedure
|
|
|
|
## will return `false` on it.
|
|
|
|
##
|
|
|
|
## ``IPv6``: FE80::/10
|
|
|
|
case address.family
|
|
|
|
of AddressFamily.IPv4:
|
|
|
|
false
|
|
|
|
of AddressFamily.IPv6:
|
|
|
|
((a6[0] and 0xFF'u8) == 0xFE'u8) and ((a6[1] and 0xC0'u8) == 0x80'u8)
|
|
|
|
else:
|
|
|
|
false
|
2019-04-15 01:27:12 +00:00
|
|
|
|
|
|
|
proc isLoopback*(address: TransportAddress): bool =
|
|
|
|
## Returns ``true`` if ``address`` is loopback address.
|
|
|
|
##
|
|
|
|
## ``IPv4``: 127.0.0.0 - 127.255.255.255
|
|
|
|
##
|
|
|
|
## ``IPv6``: ::1
|
2023-02-16 16:18:05 +00:00
|
|
|
case address.family
|
|
|
|
of AddressFamily.IPv4:
|
|
|
|
a4[0] == 127'u8
|
|
|
|
of AddressFamily.IPv6:
|
|
|
|
(uint64.fromBytes(a6.toOpenArray(0, 7)) == 0x00'u64) and
|
|
|
|
(uint64.fromBytesBE(a6.toOpenArray(8, 15)) == 0x01'u64)
|
|
|
|
else:
|
|
|
|
false
|
2019-04-15 01:27:12 +00:00
|
|
|
|
|
|
|
proc isAnyLocal*(address: TransportAddress): bool =
|
|
|
|
## Returns ``true`` if ``address`` is a wildcard address.
|
|
|
|
##
|
|
|
|
## ``IPv4``: 0.0.0.0
|
|
|
|
##
|
|
|
|
## ``IPv6``: ::
|
2023-02-16 16:18:05 +00:00
|
|
|
case address.family
|
|
|
|
of AddressFamily.IPv4:
|
|
|
|
uint32.fromBytes(a4) == 0'u32
|
|
|
|
of AddressFamily.IPv6:
|
|
|
|
(uint64.fromBytes(a6.toOpenArray(0, 7)) == 0x00'u64) and
|
|
|
|
(uint64.fromBytes(a6.toOpenArray(8, 15)) == 0x00'u64)
|
|
|
|
else:
|
|
|
|
false
|
2019-04-15 01:27:12 +00:00
|
|
|
|
|
|
|
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
|
2023-02-16 16:18:05 +00:00
|
|
|
case address.family
|
|
|
|
of AddressFamily.IPv4:
|
|
|
|
(a4[0] == 169'u8) and (a4[1] == 254'u8)
|
|
|
|
of AddressFamily.IPv6:
|
|
|
|
(a6[0] == 0xFE'u8) and ((a6[1] and 0xC0'u8) == 0x80'u8)
|
|
|
|
else:
|
|
|
|
false
|
2019-04-15 01:27:12 +00:00
|
|
|
|
|
|
|
proc isLinkLocalUnicast*(address: TransportAddress): bool {.inline.} =
|
2023-02-16 16:18:05 +00:00
|
|
|
isLinkLocal(address)
|
2019-04-15 01:27:12 +00:00
|
|
|
|
|
|
|
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
|
2023-02-16 16:18:05 +00:00
|
|
|
case address.family
|
|
|
|
of AddressFamily.IPv4:
|
|
|
|
(a4[0] == 10'u8) or ((a4[0] == 172'u8) and ((a4[1] and 0xF0) == 16)) or
|
|
|
|
((a4[0] == 192'u8) and ((a4[1] == 168'u8)))
|
|
|
|
of AddressFamily.IPv6:
|
|
|
|
(a6[0] == 0xFE'u8) and ((a6[1] and 0xC0'u8) == 0xC0'u8)
|
|
|
|
else:
|
|
|
|
false
|
|
|
|
|
|
|
|
proc isPrivate*(address: TransportAddress): bool =
|
|
|
|
## Alias for ``isSiteLocal()``.
|
|
|
|
isSiteLocal(address)
|
2019-04-15 01:27:12 +00:00
|
|
|
|
|
|
|
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
|
2023-02-16 16:18:05 +00:00
|
|
|
case address.family
|
|
|
|
of AddressFamily.IPv4:
|
|
|
|
(a4[0] >= 224'u8) and (a4[0] <= 238'u8) and
|
|
|
|
not((a4[0] == 224'u8) and (a4[1] == 0'u8) and (a4[2] == 0'u8))
|
|
|
|
of AddressFamily.IPv6:
|
|
|
|
(a6[0] == 0xFF'u8) and ((a6[1] and 0x0F'u8) == 0x0E'u8)
|
|
|
|
else:
|
|
|
|
false
|
|
|
|
|
|
|
|
proc isShared*(address: TransportAddress): bool =
|
|
|
|
## Returns ``true`` if ``address`` is part of the Shared Address Space
|
|
|
|
## defined in [IETF RFC 6598]
|
|
|
|
##
|
|
|
|
## ``IPv4``: 100.64.0.0/10
|
|
|
|
case address.family
|
|
|
|
of AddressFamily.IPv4:
|
|
|
|
(a4[0] == 100'u8) and ((a4[1] and 0xC0'u8) == 0x40'u8)
|
|
|
|
of AddressFamily.IPv6:
|
|
|
|
false
|
|
|
|
else:
|
|
|
|
false
|
|
|
|
|
|
|
|
proc isBroadcast*(address: TransportAddress): bool =
|
|
|
|
## Returns ``true`` if ``address`` is a broadcast address.
|
|
|
|
##
|
|
|
|
## ``IPv4``: 255.255.255.255
|
|
|
|
case address.family
|
|
|
|
of AddressFamily.IPv4:
|
|
|
|
uint32.fromBytes(a4) == 0xFFFF_FFFF'u32
|
|
|
|
of AddressFamily.IPv6:
|
|
|
|
false
|
|
|
|
else:
|
|
|
|
false
|
|
|
|
|
|
|
|
proc isBenchmarking*(address: TransportAddress): bool =
|
|
|
|
## Returns ``true`` if ``address`` is part of the `198.18.0.0/15` range,
|
|
|
|
## which is reserved for network devices benchmarking. This range is defined
|
|
|
|
## in [IETF RFC 2544] as `198.18.0.0` through `198.19.255.255` but
|
|
|
|
## [errata 423] corrects it to `198.18.0.0/15`.
|
|
|
|
##
|
|
|
|
## ``IPv4``: 198.18.0.0/15
|
|
|
|
##
|
|
|
|
## ``IPv6``: 2001:2::/48
|
|
|
|
case address.family
|
|
|
|
of AddressFamily.IPv4:
|
|
|
|
(a4[0] == 198'u8) and ((a4[1] and 0xFE'u8) == 18'u8)
|
|
|
|
of AddressFamily.IPv6:
|
|
|
|
(uint16.fromBytesBE(a6.toOpenArray(0, 1)) == 0x2001'u16) and
|
|
|
|
(uint16.fromBytesBE(a6.toOpenArray(2, 3)) == 0x02'u16) and
|
|
|
|
(uint16.fromBytes(a6.toOpenArray(4, 5)) == 0x00'u16)
|
|
|
|
else:
|
|
|
|
false
|
|
|
|
|
|
|
|
proc isDocumentation*(address: TransportAddress): bool =
|
|
|
|
## Returns ``true`` if ``address`` is in a range designated for documentation.
|
|
|
|
##
|
|
|
|
## ``IPv4``: 192.0.2.0/24 (TEST-NET-1)
|
|
|
|
## 198.51.100.0/24 (TEST-NET-2)
|
|
|
|
## 203.0.113.0/24 (TEST-NET-3)
|
|
|
|
##
|
|
|
|
## ``IPv6``: 2001:DB8::/32
|
|
|
|
case address.family
|
|
|
|
of AddressFamily.IPv4:
|
|
|
|
((a4[0] == 192'u8) and (a4[1] == 0'u8) and (a4[2] == 2'u8)) or
|
|
|
|
((a4[0] == 198'u8) and (a4[1] == 51'u8) and (a4[2] == 100'u8)) or
|
|
|
|
((a4[0] == 203'u8) and (a4[1] == 0'u8) and (a4[2] == 113'u8))
|
|
|
|
of AddressFamily.IPv6:
|
|
|
|
(uint16.fromBytesBE(a6.toOpenArray(0, 1)) == 0x2001'u16) and
|
|
|
|
(uint16.fromBytesBE(a6.toOpenArray(2, 3)) == 0xDB8'u16)
|
|
|
|
else:
|
|
|
|
false
|
|
|
|
|
|
|
|
proc isReserved*(address: TransportAddress): bool =
|
|
|
|
## Returns ``true`` if ``address`` is reserved by IANA for future use.
|
|
|
|
## [IETF RFC 1112] defines the block of reserved addresses as `240.0.0.0/4`.
|
|
|
|
##
|
|
|
|
## NOTE: As IANA assigns new addresses, this procedure will be updated. This
|
|
|
|
## may result in non-reserved addresses being treated as reserved in code
|
|
|
|
## that relies on an outdated version of this procedure.
|
|
|
|
##
|
|
|
|
## ``IPv4``: 240.0.0.0/4
|
|
|
|
case address.family
|
|
|
|
of AddressFamily.IPv4:
|
|
|
|
(a4[0] and 240'u8) == 240'u8
|
|
|
|
of AddressFamily.IPv6:
|
|
|
|
false
|
|
|
|
else:
|
|
|
|
false
|
|
|
|
|
|
|
|
proc isGlobal*(address: TransportAddress): bool =
|
|
|
|
## Returns ``true`` if ``address`` appears to be globally reachable as
|
|
|
|
## specified by the [IANA IPv4 Special-Purpose Address Registry].
|
|
|
|
case address.family
|
|
|
|
of AddressFamily.IPv4:
|
|
|
|
not(
|
|
|
|
(a4[0] == 0) or
|
|
|
|
address.isPrivate() or
|
|
|
|
address.isShared() or
|
|
|
|
address.isLoopback() or
|
|
|
|
address.isLinkLocal() or
|
|
|
|
# address reserver for future protocols `192.0.0.0/24`.
|
|
|
|
((a4[0] == 192'u8) and (a4[1] == 0'u8) and (a4[2] == 0'u8)) or
|
|
|
|
address.isDocumentation() or
|
|
|
|
address.isBenchmarking() or
|
|
|
|
address.isReserved() or
|
|
|
|
address.isBroadcast()
|
|
|
|
)
|
|
|
|
of AddressFamily.IPv6:
|
|
|
|
not(
|
|
|
|
address.isUnspecified() or
|
|
|
|
address.isLoopback() or
|
|
|
|
(
|
|
|
|
# IPv4-Mapped `::FFFF:0:0/96`
|
|
|
|
(uint64.fromBytes(a6.toOpenArray(0, 7)) == 0x00'u64) and
|
|
|
|
(uint16.fromBytes(a6.toOpenArray(8, 9)) == 0x00'u16) and
|
|
|
|
(uint16.fromBytes(a6.toOpenArray(10, 11)) == 0xFFFF'u16)
|
|
|
|
) or
|
|
|
|
(
|
|
|
|
# IPv4-IPv6 Translation `64:FF9B:1::/48`
|
|
|
|
(uint16.fromBytesBE(a6.toOpenArray(0, 1)) == 0x64'u16) and
|
|
|
|
(uint16.fromBytesBE(a6.toOpenArray(2, 3)) == 0xFF9B'u16) and
|
|
|
|
(uint16.fromBytesBE(a6.toOpenArray(4, 5)) == 0x01'u16)
|
|
|
|
) or
|
|
|
|
(
|
|
|
|
# Discard-Only Address Block `100::/64`
|
|
|
|
(uint16.fromBytesBE(a6.toOpenArray(0, 1)) == 0x100'u16) and
|
|
|
|
(uint32.fromBytes(a6.toOpenArray(2, 5)) == 0x00'u32) and
|
|
|
|
(uint16.fromBytes(a6.toOpenArray(6, 7)) == 0x00'u16)
|
|
|
|
) or
|
|
|
|
(
|
|
|
|
# IETF Protocol Assignments `2001::/23`
|
|
|
|
(uint16.fromBytesBE(a6.toOpenArray(0, 1)) == 0x2001'u16) and
|
|
|
|
(uint16.fromBytesBE(a6.toOpenArray(2, 3)) < 0x200'u16) and
|
|
|
|
not(
|
|
|
|
(
|
|
|
|
# Port Control Protocol Anycast `2001:1::1`
|
|
|
|
(uint32.fromBytesBE(a6.toOpenArray(0, 3)) == 0x20010001'u32) and
|
|
|
|
(uint32.fromBytes(a6.toOpenArray(4, 7)) == 0x00'u32) and
|
|
|
|
(uint32.fromBytes(a6.toOpenArray(8, 11)) == 0x00'u32) and
|
|
|
|
(uint32.fromBytesBE(a6.toOpenArray(12, 15)) == 0x01'u32)
|
|
|
|
) or
|
|
|
|
(
|
|
|
|
# Traversal Using Relays around NAT Anycast `2001:1::2`
|
|
|
|
(uint32.fromBytesBE(a6.toOpenArray(0, 3)) == 0x20010001'u32) and
|
|
|
|
(uint32.fromBytes(a6.toOpenArray(4, 7)) == 0x00'u32) and
|
|
|
|
(uint32.fromBytes(a6.toOpenArray(8, 11)) == 0x00'u32) and
|
|
|
|
(uint32.fromBytesBE(a6.toOpenArray(12, 15)) == 0x02'u32)
|
|
|
|
) or
|
|
|
|
(
|
|
|
|
# AMT `2001:3::/32`
|
|
|
|
(uint16.fromBytesBE(a6.toOpenArray(0, 1)) == 0x2001'u16) and
|
|
|
|
(uint16.fromBytesBE(a6.toOpenArray(2, 3)) == 0x03'u16)
|
|
|
|
) or
|
|
|
|
(
|
|
|
|
# AS112-v6 `2001:4:112::/48`
|
|
|
|
(uint16.fromBytesBE(a6.toOpenArray(0, 1)) == 0x2001'u16) and
|
|
|
|
(uint16.fromBytesBE(a6.toOpenArray(2, 3)) == 0x04'u16) and
|
|
|
|
(uint16.fromBytesBE(a6.toOpenArray(4, 5)) == 0x112'u16) and
|
|
|
|
(uint16.fromBytes(a6.toOpenArray(6, 7)) == 0x00'u16)
|
|
|
|
) or
|
|
|
|
(
|
|
|
|
# ORCHIDv2 `2001:20::/28`
|
|
|
|
(uint16.fromBytesBE(a6.toOpenArray(0, 1)) == 0x2001'u16) and
|
|
|
|
(uint16.fromBytesBE(a6.toOpenArray(2, 3)) >= 0x20'u16) and
|
|
|
|
(uint16.fromBytesBE(a6.toOpenArray(2, 3)) <= 0x2F'u16)
|
|
|
|
)
|
|
|
|
)
|
|
|
|
) or
|
|
|
|
address.isDocumentation() or
|
|
|
|
address.isUniqueLocal() or
|
|
|
|
address.isUnicastLinkLocal()
|
|
|
|
)
|
|
|
|
else:
|
|
|
|
false
|