Skip to content
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

Add network library #299409

Open
wants to merge 28 commits into
base: master
Choose a base branch
from
Open

Conversation

djacu
Copy link
Member

@djacu djacu commented Mar 27, 2024

Description of changes

See #258250 for discussion about why this would be beneficial to nixpkgs.

This PR was created at the request of @infinisil to be smaller and include a design document.
The design document is fairly terse. Any comments or feedback are welcome to help improve it.

Considerations for this PR and differences from the previous PR.

  • Going forward, the number of functions exposed will be significantly less. Rather than exporting many functions, a few functions (currently one) that create IPv4Address attrsets will be exposed.
  • The attrset will have (lazy) attributes that accomplish the same functions as in the previous PR.
  • There are now tests and a design document similar to the fileset and path libraries.
  • Currently only IPv4 is implement to keep the PR small. IPv6 will be implemented in the future.
  • The number of attributes in the IPv4Address attrset is limited to keep the PR small. More attributes will be implemented in the future.

Things done


Add a 👍 reaction to pull requests you find important.

@djacu djacu requested a review from infinisil as a code owner March 27, 2024 04:32
@github-actions github-actions bot added the 6.topic: lib The Nixpkgs function library label Mar 27, 2024
@ofborg ofborg bot added 10.rebuild-darwin: 0 This PR does not cause any packages to rebuild on Darwin 10.rebuild-linux: 1-10 labels Mar 27, 2024
@aanderse
Copy link
Member

aanderse commented Apr 1, 2024

@djacu in your previous PR you included a subnetMaskToBitMask function which was very useful to me - any chance of including that functionality here?

for example...

let
  droplet = ...; # JSON information from my vps provider
in
{
  networking.interfaces.eth0.ipv4 = {
    addresses = [
      {
        address = droplet.public.ipv4.ip_address;
        prefixLength = with net.ipv4; subnetMaskToBitMask (cidrToIpAddress droplet.public.ipv4.netmask);
      }
    ];
  };
}

@djacu
Copy link
Member Author

djacu commented Apr 1, 2024

@djacu in your previous PR you included a subnetMaskToBitMask function which was very useful to me - any chance of including that functionality here?

for example...

let
  droplet = ...; # JSON information from my vps provider
in
{
  networking.interfaces.eth0.ipv4 = {
    addresses = [
      {
        address = droplet.public.ipv4.ip_address;
        prefixLength = with net.ipv4; subnetMaskToBitMask (cidrToIpAddress droplet.public.ipv4.netmask);
      }
    ];
  };
}

@aanderse yes! The plan is incrementally add many of the features in the previous PR and some new ones I found while looking at other implementations.

Copy link
Member

@roberth roberth left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A few suggestions. I haven't reviewed the IPv4 logic itself too thoroughly, and I think it'd be great if someone with more day to day exposure to these concepts could review that.

lib/network/internal.nix Outdated Show resolved Hide resolved
lib/tests/test-with-nix.nix Outdated Show resolved Hide resolved
lib/network/internal.nix Outdated Show resolved Hide resolved
lib/network/tests.sh Outdated Show resolved Hide resolved
lib/default.nix Show resolved Hide resolved
Copy link
Member

@infinisil infinisil left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is going in the right direction!

lib/network/README.md Outdated Show resolved Hide resolved
lib/network/default.nix Outdated Show resolved Hide resolved
lib/network/internal.nix Show resolved Hide resolved
lib/network/internal.nix Outdated Show resolved Hide resolved
lib/network/internal.nix Outdated Show resolved Hide resolved
*/
_encode =
num:
if num < 0 then
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

With the _parseCIDR approach detailed above, this function won't have to check bounds anymore, since you can internally guarantee it won't be called with invalid arguments.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For the time being this is true, but I have plans to add things in the future where having the bounds check will be necessary. Currently, the only part that requires it is subnet mask and I've already removed that per your other suggestion. So I'll remove this for now and we can hash it out in a future PR when subnet mask comes back in.

Sound good?

lib/network/internal.nix Outdated Show resolved Hide resolved
_parse "192.168.0.1/24"
=> {
address = "192.168.0.1";
cidr = "192.168.0.1/24";
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
cidr = "192.168.0.1/24";

Doesn't seem to be in the result.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You sure? I just created a test that fails to check the _parse function and it definitely is in there.

:!./tests.sh                                                                                                                                          
test case at ./tests.sh:138 failed: internal.ipv4._parse "192.168.0.1" "24" should have evaluated to "192.168.0.1":
"192.168.0.1"

but it evaluated to
{ address = "192.168.0.1"; cidr = "192.168.0.1/24"; prefixLength = "24"; subnetMask = "255.255.255.0"; }

shell returned 1

```nix
_parse "192.168.0.1/24"
=> {
address = "192.168.0.1";
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shouldn't this return something like [ 192 168 0 1 ], such that other functions can more easily work with the data instead of having to re-parse it?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

After thinking about it, that is a good idea. But I think this should be stored in an internal variable like _address with the string representation stored in address. The reason being that the general use case for the IP address is in it's string representation. But yeah having the internal representation as a list of ints is probably the best state to store it internally.

=> {
address = "192.168.0.1";
cidr = "192.168.0.1/24";
prefixLength = "24";
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
prefixLength = "24";
prefixLength = 24;

Similarly

djacu and others added 2 commits April 11, 2024 14:54
Update upper bound check for ipv4 range

Co-authored-by: Silvan Mosberger <github@infinisil.com>
@djacu djacu requested a review from roberth April 12, 2024 06:25
@djacu
Copy link
Member Author

djacu commented Apr 12, 2024

So this is the line in default.nix that is causing the manuals to fail to build. If I remove it, it builds.

  - [`lib.network.ipv4.fromCidrString`](#function-library-lib.network.ipv4.fromCidrString):

I think it is because it the fromCidrString doc comment is not rendering. But I have no idea why... 🙃

@djacu
Copy link
Member Author

djacu commented Apr 13, 2024

So this is the line in default.nix that is causing the manuals to fail to build. If I remove it, it builds.

  - [`lib.network.ipv4.fromCidrString`](#function-library-lib.network.ipv4.fromCidrString):

I think it is because it the fromCidrString doc comment is not rendering. But I have no idea why... 🙃

I may have found out why -> https://matrix.to/#/!avYyleMexqjFHoqrME:nixos.org/$L7FoTeNQhJOOeZRdrv-kWY4IEe2_FBALlI7f8BX2qOw?via=nixos.org&via=matrix.org&via=nixos.dev

It seems at the moment, function references can't be nested.

@infinisil
Copy link
Member

They can, but you need to use ipv4.fromCidrString = for now. This is also how lib.path.subpath.isValid works:

subpath.isValid =

@djacu
Copy link
Member Author

djacu commented Apr 29, 2024

@infinisil I've responded to you comments/suggestions. Not everything is resolved as I need more feedback from you.

Can you take a look when you have a chance?

@nixos-discourse
Copy link

This pull request has been mentioned on NixOS Discourse. There might be relevant details there:

https://discourse.nixos.org/t/announcing-our-google-summer-of-code-participants/44618/3

Copy link
Member

@RaitoBezarius RaitoBezarius left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This doesn't parse IPv4, but a subset of IPv4: 192.1 is not valid with this parser, though, it's a valid IP address.

@djacu
Copy link
Member Author

djacu commented May 2, 2024

This doesn't parse IPv4, but a subset of IPv4: 192.1 is not valid with this parser, though, it's a valid IP address.

Please provide sources next time. This is a deep rabbit hole of of RFCs and specifications.

RFC 791 specifies "Addresses are fixed length of four octets (32 bits)." in Section 2.3. RFC 6943 Section 3.1.1 also defines an address this way. It also references RFC3493 Section 6.1 and several functions from IEEE 1003.1 for alternative implementations including octal and hexadecimal representations as well as representations with less than 4 parts. However, it categorizes these short hand forms as "loose" forms and mentions their issues with ambiguity and security. This ambiguity would have to be resolved before an implementation could be submitted.

Additionally, I referenced Python, Rust, and Haskell as references. As far as I can tell, none of them implement this short hand notation and only parse addresses where all 4 octets are present.
Python's implementation
Rust's implementation
Haskell's implementation

Finally, this is a MVP version of #258250 which was reduced in size and scope at @infinisil's request (and came with some design improvements). Adding short hand IP address parsing should not be included at this time to keep the scope small. It could be added as a feature in the future after a design has been submitted to mitigate the ambiguities. However, since 3 other major languages don't implement this, I don't see why we should either.

@aanderse
Copy link
Member

aanderse commented May 3, 2024

hey @djacu 👋

thank you for all of the work you have put into this! it looks like a fair bit of effort on your part to address all the helpful comments from @infinisil and @roberth! ❤️

i also appreciate the research you did in order to respond to @RaitoBezarius! it seems like you covered the concern quite well and have demonstrated established norms, all of which make sense to me from my years of experience


is everyone happy with merging this? i think it would be very helpful to have this PR merged so it doesn't cause any problems for the GSoC student who intends to continue with the great work you've started here ... i would hate to see a GSoC student or Dan do a bunch of redundant work

@infinisil - all good? 🙂

@Janik-Haag
Copy link
Member

hey @aanderse I think there is still a bunch of improvements that should be made in this pr.
@djacu and me already talked in DMs and he is happy passing the task on to @woojiq (the GSoC contributor).

You can see a rough timeline here: https://summerofcode.withgoogle.com/organizations/nixos-foundation/programs/2024/timeline

@aanderse
Copy link
Member

aanderse commented May 3, 2024

@Janik-Haag awesome to hear! thanks for the update, i'm looking forward to tracking this

@RaitoBezarius
Copy link
Member

This doesn't parse IPv4, but a subset of IPv4: 192.1 is not valid with this parser, though, it's a valid IP address.

Please provide sources next time. This is a deep rabbit hole of of RFCs and specifications.

Apologies, I lack of time these last days.

RFC 791 specifies "Addresses are fixed length of four octets (32 bits)." in Section 2.3. RFC 6943 Section 3.1.1 also defines an address this way. It also references RFC3493 Section 6.1 and several functions from IEEE 1003.1 for alternative implementations including octal and hexadecimal representations as well as representations with less than 4 parts. However, it categorizes these short hand forms as "loose" forms and mentions their issues with ambiguity and security. This ambiguity would have to be resolved before an implementation could be submitted.

Additionally, I referenced Python, Rust, and Haskell as references. As far as I can tell, none of them implement this short hand notation and only parse addresses where all 4 octets are present. Python's implementation Rust's implementation Haskell's implementation

Finally, this is a MVP version of #258250 which was reduced in size and scope at @infinisil's request (and came with some design improvements). Adding short hand IP address parsing should not be included at this time to keep the scope small. It could be added as a feature in the future after a design has been submitted to mitigate the ambiguities. However, since 3 other major languages don't implement this, I don't see why we should either.

I do not think that 3 major languages not implementing this is ground enough for rejection, my rationale is that any parser we should have in lib should always be liberal in the inputs and strict in the output. A program that is known to handle this is ping or mtr for example. I do not think I would focus on major implementations but on classical networking tools as this is a network library.

We could adopt ping parser for example.

I didn't understand this was an MVP, I will retract my feedback then.

@RaitoBezarius RaitoBezarius dismissed their stale review May 4, 2024 08:33

thought it was a complete library; it's only a MVP

- `prefixLength`: A prefix length.
- `subnetMask`: A subnet mask.

- [`lib.network.ipv4.fromCidrString`](#function-library-lib.network.ipv4.fromCidrString):
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am not really familiar with the documentation syntax, but it looks like L19-21 content redundant with documentation of function below... Does it really need to be duplicated?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does this comment even get rendered?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think fromCidrString was not meant to be part of the above list.

- `prefixLength`: A prefix length.
- `subnetMask`: A subnet mask.

- [`lib.network.ipv4.fromCidrString`](#function-library-lib.network.ipv4.fromCidrString):
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does this comment even get rendered?

- `prefixLength`: A prefix length.
- `subnetMask`: A subnet mask.

- [`lib.network.ipv4.fromCidrString`](#function-library-lib.network.ipv4.fromCidrString):
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think fromCidrString was not meant to be part of the above list.

prefixLength
];
inherit address prefixLength;
};
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Consider setting _type = "ipv4Address"; for runtime type checking. This can improve error messages when addresses end up in wrong places; useful even if this library doesn't check _type.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This also reminds me that a type in the module system would be nice (for later perhaps).
It could parse both stringly definitions and pass through _type-ed definitions.

@woojiq woojiq mentioned this pull request Jun 10, 2024
3 tasks
@wegank wegank added the 2.status: merge conflict This PR has merge conflicts with the target branch label Jul 4, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
2.status: merge conflict This PR has merge conflicts with the target branch 6.topic: lib The Nixpkgs function library 8.has: documentation 10.rebuild-darwin: 0 This PR does not cause any packages to rebuild on Darwin 10.rebuild-linux: 1-10
Projects
None yet
Development

Successfully merging this pull request may close these issues.

9 participants