ikavalio/zig-netip
A simple IP address library for zig
This is mostly an educational project to implement a library similar to go's netip using zig idioms and comptime features.
The library targets the latest stable release which is currently 0.13
.
Ip4Addr
, Ip6Addr
, Ip6AddrScoped
(and an Addr
union) address types that're small value types.
They can be converted to std.net.Ip4Address
or
std.net.Ip6Address
. All types have a bunch of comptime
friendly methods, e.g. parse
, get
, toArray
, and
flexible-ish format
specifiers.Ip4Prefix
, Ip6Prefix
(and a Prefix
union) address types that're built on top of
Ip4Addr
and Ip6Addr
abstractions.Check the netip tests for more.
test "Addr Example" {
// ipv4 create
const v4_addr1 = comptime try Ip4Addr.parse("192.0.2.1");
const v4_addr2 = try Addr.parse("192.0.2.1");
const v4_addr3 = Ip4Addr.fromArray(u8, [_]u8{ 192, 0, 2, 2 });
const v4_addr4 = Ip4Addr.fromArray(u16, [_]u16{ 0xC000, 0x0202 });
const v4_addr5 = Addr.init4(Ip4Addr.init(0xC0000203));
const v4_addr6 = Ip4Addr.fromNetAddress(try std.net.Ip4Address.parse("192.0.2.3", 1));
// ipv6 create
const v6_addr1 = comptime try Ip6Addr.parse("2001:db8::1");
const v6_addr2 = try Addr.parse("2001:db8::1");
const v6_addr3 = Ip6Addr.fromArray(u8, [_]u8{ 0x20, 0x1, 0xd, 0xb8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x2 });
const v6_addr4 = Ip6Addr.fromArray(u16, [_]u16{ 0x2001, 0xdb8, 0, 0, 0, 0, 0, 0x2 });
const v6_addr5 = Addr.init6(Ip6Addr.init(0x2001_0db8_0000_0000_0000_0000_0000_0003));
const v6_addr6 = Ip6Addr.fromNetAddress(try std.net.Ip6Address.parse("2001:db8::3", 1));
// ipv6 scoped
const v6_scoped1 = comptime try Ip6AddrScoped.parse("2001:db8::1%eth2");
const v6_scoped2 = try Addr.parse("2001:db8::2%4");
// handle parsing errors
try testing.expect(Ip4Addr.parse("-=_=-") == Ip4Addr.ParseError.InvalidCharacter);
try testing.expect(Addr.parse("0.-=_=-") == Addr.ParseError.InvalidCharacter);
try testing.expect(Ip6Addr.parse("-=_=-") == Ip6Addr.ParseError.InvalidCharacter);
try testing.expect(Addr.parse("::-=_=-") == Addr.ParseError.InvalidCharacter);
// copy
const v4_addr7 = v4_addr5;
const v6_addr8 = v6_addr3;
_ = .{v4_addr7, v4_addr4, v4_addr6, v6_scoped1, v6_scoped2, v6_addr4, v6_addr6};
// compare via values
try testing.expectEqual(math.Order.eq, order(v4_addr1, v4_addr2.v4));
try testing.expectEqual(math.Order.lt, order(v6_addr1, v6_addr8));
try testing.expectEqual(math.Order.gt, order(v6_addr8, v6_addr1));
try testing.expectEqual(math.Order.gt, order(v6_addr2, v4_addr2)); // cross AF comparison
// print
try testing.expectFmt("192.0.2.1", "{}", .{v4_addr1});
try testing.expectFmt("c0.00.02.02", "{X}", .{v4_addr3});
try testing.expectFmt("11000000.0.10.11", "{b}", .{v4_addr5});
try testing.expectFmt("2001:db8::1", "{}", .{v6_addr1});
try testing.expectFmt("2001:db8:0:0:0:0:0:2", "{xE}", .{v6_addr3});
try testing.expectFmt("2001:0db8::0003", "{X}", .{v6_addr5});
try testing.expectFmt("2001:0db8:0000:0000:0000:0000:0000:0001", "{XE}", .{v6_addr2});
}
test "Prefix Example" {
// create a ipv6 prefix
const v6_prefix1 = try Ip6Prefix.init(try Ip6Addr.parse("2001:db8:85a3::1"), 48);
const v6_prefix2 = try Prefix.parse("2001:db8:85a3::/48");
// create a prefix
const v4_prefix1 = try Ip4Prefix.init(try Ip4Addr.parse("192.0.2.1"), 24);
const v4_prefix2 = try Prefix.parse("192.0.2.1/24");
// compare mask bits
try testing.expectEqual(v6_prefix1.maskBits(), v6_prefix2.v6.maskBits());
try testing.expectEqual(v4_prefix1.maskBits(), v4_prefix2.v4.maskBits());
// handle parsing errors
try testing.expectError(Prefix.ParseError.Overflow, Prefix.parse("2001:db8::/256"));
try testing.expectError(Prefix.ParseError.Overflow, Prefix.parse("1.1.1.1/33"));
// print
try testing.expectFmt("2001:db8:85a3::1/48", "{}", .{v6_prefix1});
try testing.expectFmt("2001:0db8:85a3::0001/48", "{X}", .{v6_prefix1});
try testing.expectFmt("2001:db8:85a3::-2001:db8:85a3:ffff:ffff:ffff:ffff:ffff", "{R}", .{v6_prefix1});
try testing.expectFmt("192.0.2.0/24", "{}", .{v4_prefix1.canonical()});
try testing.expectFmt("192.0.2.0-192.0.2.255", "{R}", .{v4_prefix1});
// contains address
try testing.expect(v6_prefix2.containsAddr(try Addr.parse("2001:db8:85a3:cafe::efac")));
try testing.expect(v4_prefix2.containsAddr(try Addr.parse("192.0.2.42")));
// inclusion and overlap test
try testing.expectEqual(PrefixInclusion.sub, v6_prefix1.testInclusion(try Ip6Prefix.parse("2001:db8::/32")));
try testing.expect(v6_prefix2.overlaps(try Prefix.parse("2001:db8::/32")));
try testing.expectEqual(PrefixInclusion.sub, v4_prefix1.testInclusion(try Ip4Prefix.parse("192.0.2.0/16")));
try testing.expect(v4_prefix2.overlaps(try Prefix.parse("192.0.2.0/16")));
}