From de7d264740062f2d68c0d3fada3dd5f32673f418 Mon Sep 17 00:00:00 2001 From: Ryan Vanden Bos Date: Wed, 14 Aug 2024 14:46:59 -0700 Subject: [PATCH 1/4] Initial rework --- README.md | 2 +- SPEC.md | 39 +++++++++++++++++++++++++-------------- 2 files changed, 26 insertions(+), 15 deletions(-) diff --git a/README.md b/README.md index 1d38bc1..e1af72c 100644 --- a/README.md +++ b/README.md @@ -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). diff --git a/SPEC.md b/SPEC.md index 0ad56ad..5d776b5 100644 --- a/SPEC.md +++ b/SPEC.md @@ -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. @@ -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: `(bits / 8) + (bits % 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, 2 bytes 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 representing 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 @@ -51,10 +61,11 @@ Here are the steps that tools that implement this spec need to follow to generat 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. 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. +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 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. +6. Convert the IPv4-mapped IPv6 address back to an IPv4 address +7. 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 From 6d4d657b0322626d67374a2247d688d2133fcd87 Mon Sep 17 00:00:00 2001 From: Ryan Vanden Bos Date: Wed, 14 Aug 2024 14:57:30 -0700 Subject: [PATCH 2/4] Small edits --- SPEC.md | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/SPEC.md b/SPEC.md index 5d776b5..f451248 100644 --- a/SPEC.md +++ b/SPEC.md @@ -37,18 +37,18 @@ Here are the steps that tools that implement this spec need to follow to generat 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` + `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: `(bits / 8) + (bits % 8 != 0)`) + 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, 2 bytes would have to be 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 representing of the previous bit representation + `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. @@ -62,10 +62,10 @@ Here are the steps that tools that implement this spec need to follow to generat 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. 3. Calculate `IP6_PREFIX` by subtracting 32 from 128 and then adding back the IPv4 prefix supplied. 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 +5. Use the newly created prefix and address to create an IPv6 network address. 5. Follow steps 4 to 10 in the [IPv6 Addresses](#ipv6-addresses) section. -6. Convert the IPv4-mapped IPv6 address back to an IPv4 address +6. Convert the IPv4-mapped IPv6 address back to an IPv4 address. 7. Return the IP address representation as a native IPv4 object of their - programming langauge so other tools can work with it easily + programming langauge so other tools can work with it easily. [CIDR notation]: https://en.wikipedia.org/wiki/Classless_Inter-Domain_Routing#CIDR_notation From 0a9a9889025923f89cb3ef75a5b88208c320b350 Mon Sep 17 00:00:00 2001 From: Ryan Vanden Bos Date: Wed, 14 Aug 2024 14:59:58 -0700 Subject: [PATCH 3/4] Specify ipv4 network validation --- SPEC.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/SPEC.md b/SPEC.md index f451248..444feb3 100644 --- a/SPEC.md +++ b/SPEC.md @@ -59,13 +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. 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. -5. Follow steps 4 to 10 in the [IPv6 Addresses](#ipv6-addresses) section. -6. Convert the IPv4-mapped IPv6 address back to an IPv4 address. -7. Return the IP address representation as a native IPv4 object of their +6. Follow steps 4 to 10 in the [IPv6 Addresses](#ipv6-addresses) section. +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 From 04b920712c93cf19370b92736ca241da8f73ffd1 Mon Sep 17 00:00:00 2001 From: Ryan Vanden Bos Date: Wed, 14 Aug 2024 15:02:31 -0700 Subject: [PATCH 4/4] Specify ipv4 -> ipv6 conversion --- SPEC.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/SPEC.md b/SPEC.md index 444feb3..09e3305 100644 --- a/SPEC.md +++ b/SPEC.md @@ -63,7 +63,7 @@ Here are the steps that tools that implement this spec need to follow to generat 3. Calculate `IP6_PREFIX` by subtracting 32 from 128 and then adding back the IPv4 prefix supplied. 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. +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.