Requirements for IP address and port APIs

Following up on the last post, here are the requirements we've identified.

Addresses

Basic type requirements:
We assume that the basic address types will be structs that areHashableandSendable, and own their backing storage. See below for discussion on view-only access.

Initialization requirements:

  • Create addresses from buffer ([UInt8] ), or create from a Span derived from that buffer. Creating from Span is likely preferred. It is assumed that these forms are in network byte order.
  • Create addresses from the raw integer types that are used in the C sockaddr structs — UInt32 for IPv4 and (UInt32, UInt32, UInt32, UInt32) for IPv6. Note that using these types can raise some byte ordering concerns.
  • Create from a String , to parse an address from a string. Note that parsing needs to handle subnets as well (see below).
  • Also specify interface scope during initialization (for IPv6 link local, etc)

Interface-scoped addressing is one slightly tricky point that I haven’t seen brought up in the other examples. Supporting this requires being able to associate the address with a particular interface index/name. Supporting this implies that there is at least a basic type for Interface in the currency types. This also impacts string parsing and generation (interface scopes use%at the end of the address).

As mentioned above, we also have anEthernetAddresstype, which isn’t strictly an IP address, but has many similar implementation considerations. Having an 6-byte long type is a useful thing to consider in the generalized shape of these objects.

Queryable property requirements:

String description needs to be queryable.

IPv4: isBroadcast, isLoopback, isLinkLocal, isSiteLocal, isLocalGroup, isZeroNet, isMulticast, isInLoopbackRange, isDSLite, is6to4RelayAnycast, isPrivateUse, isSharedAddressSpace

IPv6: isLoopback, is6to4, isIPv4Mapped, isIPv4Mapped, isScopeLinkLocal, isMulticastLinkLocal, isMulticastInterfaceLocal, isScopeEmbedded, isSiteLocal, isUniqueLocal, isUnspecified, multicastScope, multicastFlags, isMulticast

Additionally, there should be some type that indicates an address family, or an enum that holds either v4 or v6, etc.

Static addresses:

As conveniences, provide static instances for theany,broadcast, andloopbackaddresses.

CIDR -Like in swift-network-evolution ( swift-network-evolution/Sources/SwiftNetwork/Utilities/IPAddress+CIDR.swift at main · apple/swift-network-evolution · GitHub ), we’d want to include CIDR parsing support for both IPv4 and IPv6 addresses that can take a CIDR string and produce a network address paired with its subnet mask. Additionally, we’d want the ability to check whether a given address is contained within that subnet, and falls back to domain pattern matching when the pattern isn’t a CIDR string.

NAT64 -Similar to what is done in swift-network-evolution ( swift-network-evolution/Sources/SwiftNetwork/Utilities/NAT64.swift at main · apple/swift-network-evolution · GitHub ), we’d like to include a NAT64 Prefix type that holds an IPv6 address paired with its length, and have the ability to embed an IPv4 address into an IPv6 address and extract it back out using the specified NAT64Prefix. We’d utilize the queryable property requirements above to make sure we don’t synthesize addresses that aren’t meant to be synthesized.

Ports

We assume that the port type will be struct that isHashableandSendable, and own its backing storage. The expected storage would be aUInt16. Ports have more complex byte-ordering considerations than addresses, since they need to be handled in packets in network byte order, but are usually dealt with in host byte order in applications.

Initialization requirements:

  • Create from a UInt16, with clear indication of byte ordering
  • Create from a String of the port, like "443" ; this has the nice side effect of avoiding byte ordering confusion.

Similarly, access to the port raw value needs to be clear about byte ordering. It also must be possible to access the string representation of the port.

It’s also useful to have a way to access static well-known ports, like done here: NWEndpoint.Port | Apple Developer Documentation . There are various ways this could be spelled, but can include enums, mapping from URL scheme strings into ports, etc.

Design Questions

What is the backing storage for addresses?

In the past, we’ve seen usage ofUInt32, tuples ofUInt32, orUInt128for the IPv4 and IPv6 addresses. However, we can also consider modeling addresses as being backed byInlineArray[4 of UInt8], or[16 of UInt8](and[6 of UInt8]for Ethernet addresses). Having an array of bytes with easy access to particular octets seems more correct conceptually. However, there are pros and cons.

Integer types are automaticallyHashable, available far back in Swift, and match C struct types. However, they are clumsy/inefficient forSpanaccess, introduce potential byte ordering confusion, and are harder to access on a per-octet level.

Inline arrays automatically supportSpanaccess, provide per-octet access, and more correctly model the concept of an address. However, they aren’t available in older Swift versions, and don’t natively supportHashableyet.

Hashableconformance for inline arrays is being discussed here ( Conform InlineArray to Hashable ) so with that added, we’d like to propose using inline arrays for addresses.

Should address properties and hashes be accessible without ownership?

Most of the properties and hashability of addresses are typically modeled as functions or computed variables on the owning struct. However, it might be useful to define most of these functions on a type that is just aviewof an IP address. This would allow querying properties on a span of bytes within a packet that represent an IP address.

For example, consider a model where there is anIPv6Addressstruct that owns the bytes for the address. There could be another non-escapable lifetime-constrained type,IPv6AddressVieworIPv6AddressPropertiesthat provides computed variables for properties about the address, and also offersHashableconformance. TheIPv6AddressViewcould be accessed on an ownedIPv6Address, or initialized from aSpanof 16 bytes.

It’s not clear if this model is necessary, but if we think it will be useful, it might be nice to put it in from the start.

How should interface scopes be represented?

(See discussion above) (2/2)

3 Likes