Skip to content

Change Spec to Reflect Rework #2

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 4 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ This repository contains a [specification](SPEC.md) for generating unique and re

## Uniqueness

Any IP address generated for any given thing with a unique name in a particular subnet has a very low probability of colliding with another IP address generated with a different name in the same subnet. This probability will obviously depend on how many IP addresses are available in that particular subnet. As such we recommend the following:-
Any IP address generated for any given thing with a unique name in a particular subnet has a very low probability of colliding with another IP address generated with a different name in the same subnet. This probability will obviously depend on how many IP addresses are available in that particular subnet. As such we recommend the following:

- Prefer IPv6 over IPv4 wherever possible
- Use a network prefix that gives you the largest address space possible. That is, the smaller the network prefix the better. For IPv6 we recommend that you use a `/80` block or lower (this includes the popular `/64` and `/48` blocks).
Expand Down
43 changes: 27 additions & 16 deletions SPEC.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ The specification consists of two sections:

To help clarify what this spec is all about, we will use a very simple example.

A user (let us call him John) wants to deploy their app which needs access to a database management system (let us call it JohnDB) for storing their data. John uses `fd52:f6b0:3162::/48` as his subnet so he wants his IP addresses allocated in that network. JohnDB can be configured to listen for connections on a particular IP address using the environment variable `JOHNDB_ADDRESS`.
A user (let us call him John) wants to deploy his app which needs access to a database management system (let us call it JohnDB) for storing his data. John uses `fd52:f6b0:3162::/48` as his subnet so he wants his IP addresses allocated in that network. JohnDB can be configured to listen for connections on a particular IP address using the environment variable `JOHNDB_ADDRESS`.

In his JohnDB startup script John uses a command line tool that implements this spec to generate an IP address using his network address `fd52:f6b0:3162::/48` and database name `johndb`. The same startup script then uses the `ip` command to add this address to his network interface so `johndb` can bind to it when it starts up. His stop script deletes the generated IP address off the interface using either the environment variable or regenerating the same IP address using the previous tool.

Expand All @@ -32,15 +32,25 @@ Here are the steps that tools that implement this spec need to follow to generat

1. Accept at least a network address in [CIDR notation] and the `NAME` of the thing for which the IP address is being generated. It should be any arbitrary text so users can have the flexibility to use UUIDs or any other identifiers they want.
2. Validate the network address, returning immediately with an error if it isn't valid.
3. Validate that the network prefix is less that 128, returning immediately with an error if it isn't. In such a case we only have 2 choices, return the same IP address or return an error since this prefix states that this is already a complete IP address. This spec chooses the latter as it helps detect mistakes.
4. Calculate `NETWORK_LENGTH` by dividing the network prefix by 4, discarding any decimals (i.e, we are only interested in the integer). This gives us the total number of characters that we must never touch.
5. Expand the IPv6 address fully and save the first `NETWORK_LENGTH` characters as `NETWORK_HASH`. This hash should be in lowercase hexadecimal.
6. Calculate `ADDRESS_LENGTH` by subtracting `NETWORK_LENGTH` from 32.
7. Calculate `BLAKE_LEN` by first dividing `ADDRESS_LENGTH` by 2 and then adding the remainder of diving `ADDRESS_LENGTH` by 2.
8. Compute `ADDRESS_HASH` by generating a `blake2b` hash of the `NAME` using `BLAKE_LEN` as output length. This hash should be in lowercase hexadecimal.
9. Create an `IP_HASH` by first joining `NETWORK_HASH` and `ADDRESS_HASH` in that order and then taking only the first 32 characters of the resulting string.
10. Convert the `IP_HASH` to IPv6 format by placing a colon after every 4 characters but not at the end of the hash.
11. Return the resulting IP address in compressed form. Libraries should return this as a native IPv6 object of their programming language where possible so other tools can work with it easily.
3. Validate that the network prefix is less than or equal to 128, returning immediately with an error if it isn't. If it is equal to 128, return the network address itself.
4. Create `NETWORK_LENGTH` from the network address, representing the length of the network part of the address in bits.
5. Create `NETWORK_HASH` as a bit representation of the network address (`u128`).
6. Compute `ADDRESS_LENGTH` by subtracting `NETWORK_LEN` from 128.
7. Compute `ADDRESS_HASH` by generating a `blake2b` hash of the `NAME` using
`ADDRESS_LEN`.
- Since `ADDRESS_LEN` is in bits, you must compute the corresponding amount
of bytes that need to be generated from a `blake2b` hash by dividing it by 8
and adding 1 if it is not evenly divisible by 8 (e.g., in Rust: `(ADDRESS_LEN / 8) + (ADDRESS_LEN % 8 != 0)`).
- Once the correct amount of bytes has been generated, convert the
collection of bytes to a singular bit representation (`u128`), zeroing out
any additional bits generated.
- E.g., If the `ADDRESS_LENGTH` was 7, 1 byte would have to be generated.
However, this results in an extra bit that would need to be zeroed out.
8. Create `IP_HASH` by calculating the bitwise OR (`|`) of `NETWORK_HASH` and
`ADDRESS_HASH`.
9. Return the IP address representation of the previous bit representation
(`IP_HASH`). Libraries should return this as a native IPv6 object of their
programming language so other tools can work with it easily.

## IPv4 Addresses

Expand All @@ -49,12 +59,13 @@ IPv4 addresses are calculated using the same method as IPv6 addresses. We simply
Here are the steps that tools that implement this spec need to follow to generate IPv4 addresses:

1. Follow steps 1 and 2 in the [IPv6 Addresses](#ipv6-addresses) section.
2. Validate that the network prefix is less that 32, returning immediately with an error if it isn't. In such a case we only have 2 choices, return the same IP address or return an error since this prefix states that this is already a complete IP address. This spec chooses the latter as it helps detect mistakes.
2. Validate that the network prefix is less than or equal to 32, returning immediately with an error if it isn't. If it is equal to 32, return the network address itself.
3. Calculate `IP6_PREFIX` by subtracting 32 from 128 and then adding back the IPv4 prefix supplied.
4. Format as IPv6 network address by joining "::" and the supplied IPv4 address and "/" and `IP6_PREFIX` in that order.
5. Follow steps 4 to 10 in the [IPv6 Addresses](#ipv6-addresses) section.
6. Format the resulting IP address in the IP6to4 format (eg ::10.19.28.118/104).
7. Drop the leading "::" and trailing subnet prefix part (ie. "/104" in the example above).
10. Follow step 11 in the [IPv6 Addresses](#ipv6-addresses) section.
4. Convert the IPv4 address to a [Ipv4-mapped IPv6 address](https://en.wikipedia.org/wiki/IPv6#IPv4-mapped_IPv6_addresses)
5. Use the newly created prefix and address to create an IPv6 network address.
6. Follow steps 4 to 10 in the [IPv6 Addresses](#ipv6-addresses) section, using said IPv6 network address.
7. Convert the IPv4-mapped IPv6 address back to an IPv4 address.
8. Return the IP address representation as a native IPv4 object of their
programming langauge so other tools can work with it easily.

[CIDR notation]: https://en.wikipedia.org/wiki/Classless_Inter-Domain_Routing#CIDR_notation