Browse Source

Merge pull request #3810 from Feoramund/freebsd-core-net

Port `core:net` to FreeBSD
Jeroen van Rijn 1 year ago
parent
commit
cc24d2de3e

+ 1 - 1
core/flags/constants.odin

@@ -12,7 +12,7 @@ IMPORTING_TIME      :: #config(ODIN_CORE_FLAGS_USE_TIME, time.IS_SUPPORTED)
 
 // Override support for parsing `net` types.
 // TODO: Update this when the BSDs are supported.
-IMPORTING_NET       :: #config(ODIN_CORE_FLAGS_USE_NET, ODIN_OS == .Windows || ODIN_OS == .Linux || ODIN_OS == .Darwin)
+IMPORTING_NET       :: #config(ODIN_CORE_FLAGS_USE_NET, ODIN_OS == .Windows || ODIN_OS == .Linux || ODIN_OS == .Darwin || ODIN_OS == .FreeBSD)
 
 TAG_ARGS          :: "args"
 SUBTAG_NAME       :: "name"

+ 1 - 1
core/flags/errors_bsd.odin

@@ -1,4 +1,4 @@
-//+build freebsd, netbsd, openbsd
+//+build netbsd, openbsd
 package flags
 
 import "base:runtime"

+ 1 - 1
core/flags/errors_nonbsd.odin

@@ -1,4 +1,4 @@
-//+build !freebsd !netbsd !openbsd
+//+build !netbsd !openbsd
 package flags
 
 import "base:runtime"

+ 1 - 1
core/flags/internal_rtti_nonbsd.odin

@@ -1,5 +1,5 @@
 //+private
-//+build !freebsd !netbsd !openbsd
+//+build !netbsd !openbsd
 package flags
 
 import "core:net"

+ 4 - 2
core/net/addr.odin

@@ -1,4 +1,4 @@
-// +build windows, linux, darwin
+// +build windows, linux, darwin, freebsd
 package net
 
 /*
@@ -10,12 +10,14 @@ package net
 	Copyright 2022 Tetralux        <[email protected]>
 	Copyright 2022 Colin Davidson  <[email protected]>
 	Copyright 2022 Jeroen van Rijn <[email protected]>.
+	Copyright 2024 Feoramund       <[email protected]>.
 	Made available under Odin's BSD-3 license.
 
 	List of contributors:
 		Tetralux:        Initial implementation
 		Colin Davidson:  Linux platform code, OSX platform code, Odin-native DNS resolver
 		Jeroen van Rijn: Cross platform unification, code style, documentation
+		Feoramund:       FreeBSD platform code
 */
 
 import "core:strconv"
@@ -742,4 +744,4 @@ parse_ip_component :: proc(input: string, max_value := u64(max(u32)), bases := D
 get_network_interfaces :: proc() -> []Address {
 	// TODO: Implement using `enumerate_interfaces` and returning only the addresses of active interfaces.
 	return nil
-}
+}

+ 4 - 2
core/net/common.odin

@@ -1,4 +1,4 @@
-// +build windows, linux, darwin
+// +build windows, linux, darwin, freebsd
 package net
 
 /*
@@ -13,12 +13,14 @@ package net
 	Copyright 2022 Tetralux        <[email protected]>
 	Copyright 2022 Colin Davidson  <[email protected]>
 	Copyright 2022 Jeroen van Rijn <[email protected]>.
+	Copyright 2024 Feoramund       <[email protected]>.
 	Made available under Odin's BSD-3 license.
 
 	List of contributors:
 		Tetralux:        Initial implementation
 		Colin Davidson:  Linux platform code, OSX platform code, Odin-native DNS resolver
 		Jeroen van Rijn: Cross platform unification, code style, documentation
+		Feoramund:       FreeBSD platform code
 */
 
 import "base:runtime"
@@ -413,4 +415,4 @@ DNS_Record_Header :: struct #packed {
 DNS_Host_Entry :: struct {
 	name: string,
 	addr: Address,
-}
+}

+ 4 - 2
core/net/dns.odin

@@ -1,4 +1,4 @@
-// +build windows, linux, darwin
+// +build windows, linux, darwin, freebsd
 package net
 
 /*
@@ -10,12 +10,14 @@ package net
 	Copyright 2022 Tetralux        <[email protected]>
 	Copyright 2022 Colin Davidson  <[email protected]>
 	Copyright 2022 Jeroen van Rijn <[email protected]>.
+	Copyright 2024 Feoramund       <[email protected]>.
 	Made available under Odin's BSD-3 license.
 
 	List of contributors:
 		Tetralux:        Initial implementation
 		Colin Davidson:  Linux platform code, OSX platform code, Odin-native DNS resolver
 		Jeroen van Rijn: Cross platform unification, code style, documentation
+		Feoramund:       FreeBSD platform code
 */
 
 import "core:mem"
@@ -30,7 +32,7 @@ when ODIN_OS == .Windows {
 		resolv_conf        = "",
 		hosts_file         = "%WINDIR%\\system32\\drivers\\etc\\hosts",
 	}
-} else when ODIN_OS == .Linux || ODIN_OS == .Darwin || ODIN_OS == .OpenBSD || ODIN_OS == .NetBSD {
+} else when ODIN_OS == .Linux || ODIN_OS == .Darwin || ODIN_OS == .FreeBSD || ODIN_OS == .OpenBSD || ODIN_OS == .NetBSD {
 	DEFAULT_DNS_CONFIGURATION :: DNS_Configuration{
 		resolv_conf        = "/etc/resolv.conf",
 		hosts_file         = "/etc/hosts",

+ 3 - 1
core/net/dns_unix.odin

@@ -1,4 +1,4 @@
-//+build linux, darwin
+//+build linux, darwin, freebsd
 package net
 /*
 	Package net implements cross-platform Berkeley Sockets, DNS resolution and associated procedures.
@@ -9,12 +9,14 @@ package net
 	Copyright 2022 Tetralux        <[email protected]>
 	Copyright 2022 Colin Davidson  <[email protected]>
 	Copyright 2022 Jeroen van Rijn <[email protected]>.
+	Copyright 2024 Feoramund       <[email protected]>.
 	Made available under Odin's BSD-3 license.
 
 	List of contributors:
 		Tetralux:        Initial implementation
 		Colin Davidson:  Linux platform code, OSX platform code, Odin-native DNS resolver
 		Jeroen van Rijn: Cross platform unification, code style, documentation
+		Feoramund:       FreeBSD platform code
 */
 import "core:strings"
 

+ 2 - 0
core/net/dns_windows.odin

@@ -10,12 +10,14 @@ package net
 	Copyright 2022 Tetralux        <[email protected]>
 	Copyright 2022 Colin Davidson  <[email protected]>
 	Copyright 2022 Jeroen van Rijn <[email protected]>.
+	Copyright 2024 Feoramund       <[email protected]>.
 	Made available under Odin's BSD-3 license.
 
 	List of contributors:
 		Tetralux:        Initial implementation
 		Colin Davidson:  Linux platform code, OSX platform code, Odin-native DNS resolver
 		Jeroen van Rijn: Cross platform unification, code style, documentation
+		Feoramund:       FreeBSD platform code
 */
 
 import "core:strings"

+ 2 - 0
core/net/doc.odin

@@ -2,12 +2,14 @@
 	Copyright 2022 Tetralux        <[email protected]>
 	Copyright 2022 Colin Davidson  <[email protected]>
 	Copyright 2022 Jeroen van Rijn <[email protected]>.
+	Copyright 2024 Feoramund       <[email protected]>.
 	Made available under Odin's BSD-3 license.
 
 	List of contributors:
 		Tetralux:        Initial implementation
 		Colin Davidson:  Linux platform code, OSX platform code, Odin-native DNS resolver
 		Jeroen van Rijn: Cross platform unification, code style, documentation
+		Feoramund:       FreeBSD platform code
 */
 
 /*

+ 2 - 0
core/net/errors_darwin.odin

@@ -10,12 +10,14 @@ package net
 	Copyright 2022 Tetralux        <[email protected]>
 	Copyright 2022 Colin Davidson  <[email protected]>
 	Copyright 2022 Jeroen van Rijn <[email protected]>.
+	Copyright 2024 Feoramund       <[email protected]>.
 	Made available under Odin's BSD-3 license.
 
 	List of contributors:
 		Tetralux:        Initial implementation
 		Colin Davidson:  Linux platform code, OSX platform code, Odin-native DNS resolver
 		Jeroen van Rijn: Cross platform unification, code style, documentation
+		Feoramund:       FreeBSD platform code
 */
 
 import "core:c"

+ 217 - 0
core/net/errors_freebsd.odin

@@ -0,0 +1,217 @@
+//+build freebsd
+package net
+
+/*
+	Package net implements cross-platform Berkeley Sockets, DNS resolution and associated procedures.
+	For other protocols and their features, see subdirectories of this package.
+*/
+
+/*
+	Copyright 2022 Tetralux        <[email protected]>
+	Copyright 2022 Colin Davidson  <[email protected]>
+	Copyright 2022 Jeroen van Rijn <[email protected]>.
+	Copyright 2024 Feoramund       <[email protected]>.
+	Made available under Odin's BSD-3 license.
+
+	List of contributors:
+		Tetralux:        Initial implementation
+		Colin Davidson:  Linux platform code, OSX platform code, Odin-native DNS resolver
+		Jeroen van Rijn: Cross platform unification, code style, documentation
+		Feoramund:       FreeBSD platform code
+*/
+
+import "core:c"
+import "core:sys/freebsd"
+
+Create_Socket_Error :: enum c.int {
+	None                                 = 0,
+	Access_Denied                        = cast(c.int)freebsd.Errno.EACCES,
+	Family_Not_Supported_For_This_Socket = cast(c.int)freebsd.Errno.EAFNOSUPPORT,
+	Full_Per_Process_Descriptor_Table    = cast(c.int)freebsd.Errno.EMFILE,
+	Full_System_File_Table               = cast(c.int)freebsd.Errno.ENFILE,
+	No_Buffer_Space_Available            = cast(c.int)freebsd.Errno.ENOBUFS,
+	Insufficient_Permission              = cast(c.int)freebsd.Errno.EPERM,
+	Protocol_Unsupported_In_Family       = cast(c.int)freebsd.Errno.EPROTONOSUPPORT,
+	Socket_Type_Unsupported_By_Protocol  = cast(c.int)freebsd.Errno.EPROTOTYPE,
+}
+
+Dial_Error :: enum c.int {
+	None                        = 0,
+	Port_Required               = -1,
+	Not_Descriptor              = cast(c.int)freebsd.Errno.EBADF,
+	Invalid_Namelen             = cast(c.int)freebsd.Errno.EINVAL,
+	Not_Socket                  = cast(c.int)freebsd.Errno.ENOTSOCK,
+	Address_Unavailable         = cast(c.int)freebsd.Errno.EADDRNOTAVAIL,
+	Wrong_Family_For_Socket     = cast(c.int)freebsd.Errno.EAFNOSUPPORT,
+	Already_Connected           = cast(c.int)freebsd.Errno.EISCONN,
+	Timeout                     = cast(c.int)freebsd.Errno.ETIMEDOUT,
+	Refused_By_Remote_Host      = cast(c.int)freebsd.Errno.ECONNREFUSED,
+	// `Refused` alias for `core:net` tests.
+	// The above default name `Refused_By_Remote_Host` is more explicit.
+	Refused                     = Refused_By_Remote_Host,
+	Reset_By_Remote_Host        = cast(c.int)freebsd.Errno.ECONNRESET,
+	Network_Unreachable         = cast(c.int)freebsd.Errno.ENETUNREACH,
+	Host_Unreachable            = cast(c.int)freebsd.Errno.EHOSTUNREACH,
+	Address_In_Use              = cast(c.int)freebsd.Errno.EADDRINUSE,
+	Invalid_Address_Space       = cast(c.int)freebsd.Errno.EFAULT,
+	In_Progress                 = cast(c.int)freebsd.Errno.EINPROGRESS,
+	Interrupted_By_Signal       = cast(c.int)freebsd.Errno.EINTR,
+	Previous_Attempt_Incomplete = cast(c.int)freebsd.Errno.EALREADY,
+	Broadcast_Unavailable       = cast(c.int)freebsd.Errno.EACCES,
+	Auto_Port_Unavailable       = cast(c.int)freebsd.Errno.EAGAIN,
+
+	// NOTE: There are additional connect() error possibilities, but they are
+	// strictly for addresses in the UNIX domain.
+}
+
+Bind_Error :: enum c.int {
+	None                         = 0,
+	Kernel_Resources_Unavailable = cast(c.int)freebsd.Errno.EAGAIN,
+	Not_Descriptor               = cast(c.int)freebsd.Errno.EBADF,
+
+	// NOTE: bind() can also return EINVAL if the underlying `addrlen` is an
+	// invalid length for the address family. This shouldn't happen for the net
+	// package, but it's worth noting.
+	Already_Bound                = cast(c.int)freebsd.Errno.EINVAL,
+	Not_Socket                   = cast(c.int)freebsd.Errno.ENOTSOCK,
+	Given_Nonlocal_Address       = cast(c.int)freebsd.Errno.EADDRNOTAVAIL,
+	Address_In_Use               = cast(c.int)freebsd.Errno.EADDRINUSE,
+	Address_Family_Mismatch      = cast(c.int)freebsd.Errno.EAFNOSUPPORT,
+	Protected_Address            = cast(c.int)freebsd.Errno.EACCES,
+	Invalid_Address_Space        = cast(c.int)freebsd.Errno.EFAULT,
+
+	// NOTE: There are additional bind() error possibilities, but they are
+	// strictly for addresses in the UNIX domain.
+}
+
+Listen_Error :: enum c.int {
+	None                                    = 0,
+	Not_Descriptor                          = cast(c.int)freebsd.Errno.EBADF,
+	Socket_Not_Bound                        = cast(c.int)freebsd.Errno.EDESTADDRREQ,
+	Already_Connected                       = cast(c.int)freebsd.Errno.EINVAL,
+	Not_Socket                              = cast(c.int)freebsd.Errno.ENOTSOCK,
+	Listening_Not_Supported_For_This_Socket = cast(c.int)freebsd.Errno.EOPNOTSUPP,
+}
+
+Accept_Error :: enum c.int {
+	None                              = 0,
+	Not_Descriptor                    = cast(c.int)freebsd.Errno.EBADF,
+	Interrupted                       = cast(c.int)freebsd.Errno.EINTR,
+	Full_Per_Process_Descriptor_Table = cast(c.int)freebsd.Errno.EMFILE,
+	Full_System_File_Table            = cast(c.int)freebsd.Errno.ENFILE,
+	Not_Socket                        = cast(c.int)freebsd.Errno.ENOTSOCK,
+	Listen_Not_Called_On_Socket_Yet   = cast(c.int)freebsd.Errno.EINVAL,
+	Address_Not_Writable              = cast(c.int)freebsd.Errno.EFAULT,
+
+	// NOTE: This is the same as EWOULDBLOCK.
+	No_Connections_Available          = cast(c.int)freebsd.Errno.EAGAIN,
+	// `Would_Block` alias for `core:net` tests.
+	Would_Block                       = cast(c.int)freebsd.Errno.EAGAIN,
+
+	New_Connection_Aborted            = cast(c.int)freebsd.Errno.ECONNABORTED,
+}
+
+TCP_Recv_Error :: enum c.int {
+	None                                 = 0,
+	Not_Descriptor                       = cast(c.int)freebsd.Errno.EBADF,
+	Connection_Closed                    = cast(c.int)freebsd.Errno.ECONNRESET,
+	Not_Connected                        = cast(c.int)freebsd.Errno.ENOTCONN,
+	Not_Socket                           = cast(c.int)freebsd.Errno.ENOTSOCK,
+
+	// NOTE(Feoramund): The next two errors are only relevant for recvmsg(),
+	// but I'm including them for completeness's sake.
+	Full_Table_And_Pending_Data          = cast(c.int)freebsd.Errno.EMFILE,
+	Invalid_Message_Size                 = cast(c.int)freebsd.Errno.EMSGSIZE,
+
+	Timeout                              = cast(c.int)freebsd.Errno.EAGAIN,
+	Interrupted_By_Signal                = cast(c.int)freebsd.Errno.EINTR,
+	Buffer_Pointer_Outside_Address_Space = cast(c.int)freebsd.Errno.EFAULT,
+}
+
+UDP_Recv_Error :: enum c.int {
+	None                                 = 0,
+	Not_Descriptor                       = cast(c.int)freebsd.Errno.EBADF,
+	Connection_Closed                    = cast(c.int)freebsd.Errno.ECONNRESET,
+	Not_Connected                        = cast(c.int)freebsd.Errno.ENOTCONN,
+	Not_Socket                           = cast(c.int)freebsd.Errno.ENOTSOCK,
+
+	// NOTE(Feoramund): The next two errors are only relevant for recvmsg(),
+	// but I'm including them for completeness's sake.
+	Full_Table_And_Data_Discarded        = cast(c.int)freebsd.Errno.EMFILE,
+	Invalid_Message_Size                 = cast(c.int)freebsd.Errno.EMSGSIZE,
+
+	Timeout                              = cast(c.int)freebsd.Errno.EAGAIN,
+	Interrupted_By_Signal                = cast(c.int)freebsd.Errno.EINTR,
+	Buffer_Pointer_Outside_Address_Space = cast(c.int)freebsd.Errno.EFAULT,
+}
+
+TCP_Send_Error :: enum c.int {
+	None                              = 0,
+	Connection_Closed                 = cast(c.int)freebsd.Errno.ECONNRESET,
+	Not_Descriptor                    = cast(c.int)freebsd.Errno.EBADF,
+	Broadcast_Status_Mismatch         = cast(c.int)freebsd.Errno.EACCES,
+	Not_Connected                     = cast(c.int)freebsd.Errno.ENOTCONN,
+	Not_Socket                        = cast(c.int)freebsd.Errno.ENOTSOCK,
+	Argument_In_Invalid_Address_Space = cast(c.int)freebsd.Errno.EFAULT,
+
+	Message_Size_Breaks_Atomicity     = cast(c.int)freebsd.Errno.EMSGSIZE,
+
+	/* The socket is marked non-blocking, or MSG_DONTWAIT is
+	   specified, and the requested operation would block. */
+	Would_Block                       = cast(c.int)freebsd.Errno.EAGAIN,
+
+	/* NOTE: This error arises for two distinct reasons:
+
+	   1. The system was unable to allocate an internal buffer.
+	      The operation may succeed when buffers become available.
+
+	   2. The output queue for a network interface was full.
+	      This generally indicates that the interface has stopped
+	      sending, but may be caused by transient congestion.
+	*/
+	No_Buffer_Space_Available         = cast(c.int)freebsd.Errno.ENOBUFS,
+
+	Host_Unreachable                  = cast(c.int)freebsd.Errno.EHOSTUNREACH,
+	Already_Connected                 = cast(c.int)freebsd.Errno.EISCONN,
+	ICMP_Unreachable                  = cast(c.int)freebsd.Errno.ECONNREFUSED,
+	Host_Down                         = cast(c.int)freebsd.Errno.EHOSTDOWN,
+	Network_Down                      = cast(c.int)freebsd.Errno.ENETDOWN,
+	Jailed_Socket_Tried_To_Escape     = cast(c.int)freebsd.Errno.EADDRNOTAVAIL,
+	Cannot_Send_More_Data             = cast(c.int)freebsd.Errno.EPIPE,
+}
+
+// NOTE(Feoramund): The same as TCP errors go, as far as I'm aware.
+UDP_Send_Error :: distinct TCP_Send_Error
+
+Shutdown_Manner :: enum c.int {
+	Receive = cast(c.int)freebsd.Shutdown_Method.RD,
+	Send    = cast(c.int)freebsd.Shutdown_Method.WR,
+	Both    = cast(c.int)freebsd.Shutdown_Method.RDWR,
+}
+
+Shutdown_Error :: enum c.int {
+	None           = 0,
+	Not_Descriptor = cast(c.int)freebsd.Errno.EBADF,
+	Invalid_Manner = cast(c.int)freebsd.Errno.EINVAL,
+	Not_Connected  = cast(c.int)freebsd.Errno.ENOTCONN,
+	Not_Socket     = cast(c.int)freebsd.Errno.ENOTSOCK,
+}
+
+Socket_Option_Error :: enum c.int {
+	None                              = 0,
+	Value_Out_Of_Range                = -1,
+	Not_Descriptor                    = cast(c.int)freebsd.Errno.EBADF,
+	Not_Socket                        = cast(c.int)freebsd.Errno.ENOTSOCK,
+	Unknown_Option_For_Level          = cast(c.int)freebsd.Errno.ENOPROTOOPT,
+	Argument_In_Invalid_Address_Space = cast(c.int)freebsd.Errno.EFAULT,
+	// This error can arise for many different reasons.
+	Invalid_Value                     = cast(c.int)freebsd.Errno.EINVAL,
+	System_Memory_Allocation_Failed   = cast(c.int)freebsd.Errno.ENOMEM,
+	Insufficient_System_Resources     = cast(c.int)freebsd.Errno.ENOBUFS,
+}
+
+Set_Blocking_Error :: enum c.int {
+	None             = 0,
+	Not_Descriptor   = cast(c.int)freebsd.Errno.EBADF,
+	Wrong_Descriptor = cast(c.int)freebsd.Errno.ENOTTY,
+}

+ 2 - 0
core/net/errors_linux.odin

@@ -10,6 +10,7 @@ package net
 	Copyright 2022 Tetralux        <[email protected]>
 	Copyright 2022 Colin Davidson  <[email protected]>
 	Copyright 2022 Jeroen van Rijn <[email protected]>.
+	Copyright 2024 Feoramund       <[email protected]>.
 	Made available under Odin's BSD-3 license.
 
 	List of contributors:
@@ -17,6 +18,7 @@ package net
 		Colin Davidson:  Linux platform code, OSX platform code, Odin-native DNS resolver
 		Jeroen van Rijn: Cross platform unification, code style, documentation
 		flysand:         Move dependency from core:linux.Errno to core:sys/linux
+		Feoramund:       FreeBSD platform code
 */
 
 import "core:c"

+ 2 - 0
core/net/errors_windows.odin

@@ -10,12 +10,14 @@ package net
 	Copyright 2022 Tetralux        <[email protected]>
 	Copyright 2022 Colin Davidson  <[email protected]>
 	Copyright 2022 Jeroen van Rijn <[email protected]>.
+	Copyright 2024 Feoramund       <[email protected]>.
 	Made available under Odin's BSD-3 license.
 
 	List of contributors:
 		Tetralux:        Initial implementation
 		Colin Davidson:  Linux platform code, OSX platform code, Odin-native DNS resolver
 		Jeroen van Rijn: Cross platform unification, code style, documentation
+		Feoramund:       FreeBSD platform code
 */
 
 import "core:c"

+ 3 - 1
core/net/interface.odin

@@ -1,4 +1,4 @@
-// +build windows, linux, darwin
+// +build windows, linux, darwin, freebsd
 package net
 
 /*
@@ -10,12 +10,14 @@ package net
 	Copyright 2022 Tetralux        <[email protected]>
 	Copyright 2022 Colin Davidson  <[email protected]>
 	Copyright 2022 Jeroen van Rijn <[email protected]>.
+	Copyright 2024 Feoramund       <[email protected]>.
 	Made available under Odin's BSD-3 license.
 
 	List of contributors:
 		Tetralux:        Initial implementation
 		Colin Davidson:  Linux platform code, OSX platform code, Odin-native DNS resolver
 		Jeroen van Rijn: Cross platform unification, code style, documentation
+		Feoramund:       FreeBSD platform code
 */
 
 import "core:strings"

+ 2 - 1
core/net/interface_darwin.odin

@@ -10,13 +10,14 @@ package net
 	Copyright 2022 Tetralux        <[email protected]>
 	Copyright 2022 Colin Davidson  <[email protected]>
 	Copyright 2022 Jeroen van Rijn <[email protected]>.
+	Copyright 2024 Feoramund       <[email protected]>.
 	Made available under Odin's BSD-3 license.
 
 	List of contributors:
 		Tetralux:        Initial implementation
 		Colin Davidson:  Linux platform code, OSX platform code, Odin-native DNS resolver
 		Jeroen van Rijn: Cross platform unification, code style, documentation
-
+		Feoramund:       FreeBSD platform code
 */
 
 import "core:os"

+ 177 - 0
core/net/interface_freebsd.odin

@@ -0,0 +1,177 @@
+//+build freebsd
+package net
+
+/*
+	Package net implements cross-platform Berkeley Sockets, DNS resolution and associated procedures.
+	For other protocols and their features, see subdirectories of this package.
+*/
+
+/*
+	Copyright 2022 Tetralux        <[email protected]>
+	Copyright 2022 Colin Davidson  <[email protected]>
+	Copyright 2022 Jeroen van Rijn <[email protected]>.
+	Copyright 2024 Feoramund       <[email protected]>.
+	Made available under Odin's BSD-3 license.
+
+	List of contributors:
+		Tetralux:        Initial implementation
+		Colin Davidson:  Linux platform code, OSX platform code, Odin-native DNS resolver
+		Jeroen van Rijn: Cross platform unification, code style, documentation
+		Feoramund:       FreeBSD platform code
+*/
+
+import "core:c"
+import "core:strings"
+import "core:sys/freebsd"
+
+@(private)
+_enumerate_interfaces :: proc(allocator := context.allocator) -> (interfaces: []Network_Interface, err: Network_Error) {
+	// This is a simplified implementation of `getifaddrs` from the FreeBSD
+	// libc using only Odin and syscalls.
+	context.allocator = allocator
+
+	mib := [6]freebsd.MIB_Identifier {
+		.CTL_NET,
+		cast(freebsd.MIB_Identifier)freebsd.Protocol_Family.ROUTE,
+		freebsd.MIB_Identifier(0),
+		freebsd.MIB_Identifier(0),
+		.NET_RT_IFLISTL,
+		freebsd.MIB_Identifier(0),
+	}
+
+	// Figure out how much space we need.
+	needed: c.size_t = ---
+
+	errno := freebsd.sysctl(mib[:], nil, &needed, nil, 0)
+	if errno != nil {
+		return nil, .Unable_To_Enumerate_Network_Interfaces
+	}
+
+	// Allocate and get the entries.
+	buf, alloc_err := make([]byte, needed)
+	if alloc_err != nil {
+		return nil, .Unable_To_Enumerate_Network_Interfaces
+	}
+	defer delete(buf)
+
+	errno = freebsd.sysctl(mib[:], &buf[0], &needed, nil, 0)
+	if errno != nil {
+		return nil, .Unable_To_Enumerate_Network_Interfaces
+	}
+
+	// Build the interfaces with each message.
+	if_builder: [dynamic]Network_Interface
+	for message_pointer: uintptr = 0; message_pointer < cast(uintptr)needed; /**/ {
+		rtm := cast(^freebsd.Route_Message_Header)&buf[message_pointer]
+		if rtm.version != freebsd.RTM_VERSION {
+			continue
+		}
+
+		#partial switch rtm.type {
+		case .IFINFO:
+			ifm := cast(^freebsd.Interface_Message_Header_Len)&buf[message_pointer]
+			if .IFP not_in ifm.addrs {
+				// No name available.
+				break
+			}
+
+			dl := cast(^freebsd.Socket_Address_Data_Link)&buf[message_pointer + cast(uintptr)ifm.len]
+
+			if_data := cast(^freebsd.Interface_Data)&buf[message_pointer + cast(uintptr)ifm.data_off]
+
+			// This is done this way so the different message types can
+			// dynamically build a `Network_Interface`.
+			resize(&if_builder, max(len(if_builder), 1 + cast(int)ifm.index))
+			interface := if_builder[ifm.index]
+
+			interface.adapter_name = strings.clone_from_bytes(dl.data[0:dl.nlen])
+			interface.mtu = if_data.mtu
+
+			switch if_data.link_state {
+			case .UNKNOWN: /* Do nothing; the default value is valid. */
+			case .UP:   interface.link.state |= { .Up   }
+			case .DOWN: interface.link.state |= { .Down }
+			}
+
+			// TODO: Uncertain if these are equivalent:
+			// interface.link.transmit_speed = if_data.baudrate
+			// interface.link.receive_speed = if_data.baudrate
+
+			if dl.type == .LOOP {
+				interface.link.state |= { .Loopback }
+			} else {
+				interface.physical_address = physical_address_to_string(dl.data[dl.nlen:][:6])
+			}
+
+			if_builder[ifm.index] = interface
+
+		case .NEWADDR:
+			RTA_MASKS :: freebsd.Route_Address_Flags { .IFA, .NETMASK }
+			ifam := cast(^freebsd.Interface_Address_Message_Header_Len)&buf[message_pointer]
+			if ifam.addrs & RTA_MASKS == {} {
+				break
+			}
+
+			resize(&if_builder, max(len(if_builder), 1 + cast(int)ifam.index))
+			interface := if_builder[ifam.index]
+
+			address_pointer := message_pointer + cast(uintptr)ifam.len
+
+			lease: Lease
+			address_set: bool
+			for address_type in ifam.addrs {
+				ptr := cast(^freebsd.Socket_Address_Basic)&buf[address_pointer]
+
+				#partial switch address_type {
+				case .IFA:
+					#partial switch ptr.family {
+					case .INET:
+						real := cast(^freebsd.Socket_Address_Internet)ptr
+						lease.address = cast(IP4_Address)real.addr.addr8
+						address_set = true
+					case .INET6:
+						real := cast(^freebsd.Socket_Address_Internet6)ptr
+						lease.address = cast(IP6_Address)real.addr.addr16
+						address_set = true
+					}
+				case .NETMASK:
+					#partial switch ptr.family {
+					case .INET:
+						real := cast(^freebsd.Socket_Address_Internet)ptr
+						lease.netmask = cast(Netmask)cast(IP4_Address)real.addr.addr8
+					case .INET6:
+						real := cast(^freebsd.Socket_Address_Internet6)ptr
+						lease.netmask = cast(Netmask)cast(IP6_Address)real.addr.addr16
+					}
+				}
+
+				SALIGN : u8 : size_of(c.long) - 1
+				address_advance: uintptr = ---
+				if ptr.len > 0 {
+					address_advance = cast(uintptr)((ptr.len + SALIGN) & ~SALIGN)
+				} else {
+					address_advance = cast(uintptr)(SALIGN + 1)
+				}
+
+				address_pointer += address_advance
+			}
+
+			if address_set {
+				append(&interface.unicast, lease)
+			}
+
+			if_builder[ifam.index] = interface
+		}
+
+		message_pointer += cast(uintptr)rtm.msglen
+	}
+
+	// Remove any interfaces that were allocated but had no name.
+	#no_bounds_check for i := len(if_builder) - 1; i >= 0; i -= 1 {
+		if len(if_builder[i].adapter_name) == 0 {
+			ordered_remove(&if_builder, i)
+		}
+	}
+
+	return if_builder[:], nil
+}

+ 2 - 0
core/net/interface_linux.odin

@@ -10,12 +10,14 @@ package net
 	Copyright 2022 Tetralux        <[email protected]>
 	Copyright 2022 Colin Davidson  <[email protected]>
 	Copyright 2022 Jeroen van Rijn <[email protected]>.
+	Copyright 2024 Feoramund       <[email protected]>.
 	Made available under Odin's BSD-3 license.
 
 	List of contributors:
 		Tetralux:        Initial implementation
 		Colin Davidson:  Linux platform code, OSX platform code, Odin-native DNS resolver
 		Jeroen van Rijn: Cross platform unification, code style, documentation
+		Feoramund:       FreeBSD platform code
 
 	This file uses `getifaddrs` libc call to enumerate interfaces.
 	TODO: When we have raw sockets, split off into its own file for Linux so we can use the NETLINK protocol and bypass libc.

+ 2 - 0
core/net/interface_windows.odin

@@ -10,12 +10,14 @@ package net
 	Copyright 2022 Tetralux        <[email protected]>
 	Copyright 2022 Colin Davidson  <[email protected]>
 	Copyright 2022 Jeroen van Rijn <[email protected]>.
+	Copyright 2024 Feoramund       <[email protected]>.
 	Made available under Odin's BSD-3 license.
 
 	List of contributors:
 		Tetralux:        Initial implementation
 		Colin Davidson:  Linux platform code, OSX platform code, Odin-native DNS resolver
 		Jeroen van Rijn: Cross platform unification, code style, documentation
+		Feoramund:       FreeBSD platform code
 */
 
 import sys     "core:sys/windows"

+ 3 - 1
core/net/socket.odin

@@ -1,4 +1,4 @@
-// +build windows, linux, darwin
+// +build windows, linux, darwin, freebsd
 package net
 
 /*
@@ -10,12 +10,14 @@ package net
 	Copyright 2022-2023 Tetralux        <[email protected]>
 	Copyright 2022-2023 Colin Davidson  <[email protected]>
 	Copyright 2022-2023 Jeroen van Rijn <[email protected]>.
+	Copyright 2024 Feoramund       <[email protected]>.
 	Made available under Odin's BSD-3 license.
 
 	List of contributors:
 		Tetralux:        Initial implementation
 		Colin Davidson:  Linux platform code, OSX platform code, Odin-native DNS resolver
 		Jeroen van Rijn: Cross platform unification, code style, documentation
+		Feoramund:       FreeBSD platform code
 */
 
 any_socket_to_socket :: proc "contextless" (socket: Any_Socket) -> Socket {

+ 2 - 0
core/net/socket_darwin.odin

@@ -10,12 +10,14 @@ package net
 	Copyright 2022 Tetralux        <[email protected]>
 	Copyright 2022 Colin Davidson  <[email protected]>
 	Copyright 2022 Jeroen van Rijn <[email protected]>.
+	Copyright 2024 Feoramund       <[email protected]>.
 	Made available under Odin's BSD-3 license.
 
 	List of contributors:
 		Tetralux:        Initial implementation
 		Colin Davidson:  Linux platform code, OSX platform code, Odin-native DNS resolver
 		Jeroen van Rijn: Cross platform unification, code style, documentation
+		Feoramund:       FreeBSD platform code
 */
 
 import "core:c"

+ 404 - 0
core/net/socket_freebsd.odin

@@ -0,0 +1,404 @@
+//+build freebsd
+package net
+
+/*
+	Package net implements cross-platform Berkeley Sockets, DNS resolution and associated procedures.
+	For other protocols and their features, see subdirectories of this package.
+*/
+
+/*
+	Copyright 2022 Tetralux        <[email protected]>
+	Copyright 2022 Colin Davidson  <[email protected]>
+	Copyright 2022 Jeroen van Rijn <[email protected]>.
+	Copyright 2024 Feoramund       <[email protected]>.
+	Made available under Odin's BSD-3 license.
+
+	List of contributors:
+		Tetralux:        Initial implementation
+		Colin Davidson:  Linux platform code, OSX platform code, Odin-native DNS resolver
+		Jeroen van Rijn: Cross platform unification, code style, documentation
+		Feoramund:       FreeBSD platform code
+*/
+
+import "core:c"
+import "core:sys/freebsd"
+import "core:time"
+
+Fd :: freebsd.Fd
+
+Socket_Option :: enum c.int {
+	// TODO: Test and implement more socket options.
+	// DEBUG
+	Reuse_Address             = cast(c.int)freebsd.Socket_Option.REUSEADDR,
+	Keep_Alive                = cast(c.int)freebsd.Socket_Option.KEEPALIVE,
+	// DONTROUTE
+	Broadcast                 = cast(c.int)freebsd.Socket_Option.BROADCAST,
+	Use_Loopback              = cast(c.int)freebsd.Socket_Option.USELOOPBACK,
+	Linger                    = cast(c.int)freebsd.Socket_Option.LINGER,
+	Out_Of_Bounds_Data_Inline = cast(c.int)freebsd.Socket_Option.OOBINLINE,
+	Reuse_Port                = cast(c.int)freebsd.Socket_Option.REUSEPORT,
+	// TIMESTAMP
+	No_SIGPIPE_From_EPIPE     = cast(c.int)freebsd.Socket_Option.NOSIGPIPE,
+	// ACCEPTFILTER
+	// BINTIME
+	// NO_OFFLOAD
+	// NO_DDP
+	Reuse_Port_Load_Balancing = cast(c.int)freebsd.Socket_Option.REUSEPORT_LB,
+	// RERROR
+
+	Send_Buffer_Size          = cast(c.int)freebsd.Socket_Option.SNDBUF,
+	Receive_Buffer_Size       = cast(c.int)freebsd.Socket_Option.RCVBUF,
+	// SNDLOWAT
+	// RCVLOWAT
+	Send_Timeout              = cast(c.int)freebsd.Socket_Option.SNDTIMEO,
+	Receive_Timeout           = cast(c.int)freebsd.Socket_Option.RCVTIMEO,
+}
+
+@(private)
+_create_socket :: proc(family: Address_Family, protocol: Socket_Protocol) -> (socket: Any_Socket, err: Network_Error) {
+	sys_family:      freebsd.Protocol_Family = ---
+	sys_protocol:    freebsd.Protocol        = ---
+	sys_socket_type: freebsd.Socket_Type     = ---
+
+	switch family {
+	case .IP4: sys_family = .INET
+	case .IP6: sys_family = .INET6
+	}
+
+	switch protocol {
+	case .TCP: sys_protocol = .TCP; sys_socket_type = .STREAM
+	case .UDP: sys_protocol = .UDP; sys_socket_type = .DGRAM
+	}
+
+	new_socket, errno := freebsd.socket(sys_family, sys_socket_type, sys_protocol)
+	if errno != nil {
+		err = cast(Create_Socket_Error)errno
+		return
+	}
+
+	// NOTE(Feoramund): By default, FreeBSD will generate SIGPIPE if an EPIPE
+	// error is raised during the writing of a socket that may be closed.
+	// This behavior is unlikely to be expected by general users.
+	//
+	// There are two workarounds. One is to apply the .NOSIGNAL flag when using
+	// the `sendto` syscall. However, that would prevent users of this library
+	// from re-enabling the SIGPIPE-raising functionality, if they really
+	// wanted it.
+	//
+	// So I have disabled it here with this socket option for all sockets.
+	truth: b32 = true
+	errno = freebsd.setsockopt(new_socket, .SOCKET, .NOSIGPIPE, &truth, size_of(truth))
+	if errno != nil {
+		err = cast(Socket_Option_Error)errno
+		return
+	}
+
+	switch protocol {
+	case .TCP: return cast(TCP_Socket)new_socket, nil
+	case .UDP: return cast(UDP_Socket)new_socket, nil
+	}
+
+	return
+}
+
+@(private)
+_dial_tcp_from_endpoint :: proc(endpoint: Endpoint, options := default_tcp_options) -> (socket: TCP_Socket, err: Network_Error) {
+	if endpoint.port == 0 {
+		return 0, .Port_Required
+	}
+
+	family := family_from_endpoint(endpoint)
+	new_socket := create_socket(family, .TCP) or_return
+	socket = new_socket.(TCP_Socket)
+
+	sockaddr := _endpoint_to_sockaddr(endpoint)
+	errno := freebsd.connect(cast(Fd)socket, &sockaddr, cast(freebsd.socklen_t)sockaddr.len)
+	if errno != nil {
+		err = cast(Dial_Error)errno
+		return
+	}
+
+	return
+}
+
+@(private)
+_bind :: proc(socket: Any_Socket, ep: Endpoint) -> (err: Network_Error) {
+	sockaddr := _endpoint_to_sockaddr(ep)
+	real_socket := any_socket_to_socket(socket)
+	errno := freebsd.bind(cast(Fd)real_socket, &sockaddr, cast(freebsd.socklen_t)sockaddr.len)
+	if errno != nil {
+		err = cast(Bind_Error)errno
+	}
+	return
+}
+
+@(private)
+_listen_tcp :: proc(interface_endpoint: Endpoint, backlog := 1000) -> (socket: TCP_Socket, err: Network_Error) {
+	family := family_from_endpoint(interface_endpoint)
+	new_socket := create_socket(family, .TCP) or_return
+	socket = new_socket.(TCP_Socket)
+
+	bind(socket, interface_endpoint) or_return
+
+	errno := freebsd.listen(cast(Fd)socket, backlog)
+	if errno != nil {
+		err = cast(Listen_Error)errno
+		return
+	}
+
+	return
+}
+
+@(private)
+_accept_tcp :: proc(sock: TCP_Socket, options := default_tcp_options) -> (client: TCP_Socket, source: Endpoint, err: Network_Error) {
+	sockaddr: freebsd.Socket_Address_Storage
+
+	result, errno := freebsd.accept(cast(Fd)sock, &sockaddr)
+	if errno != nil {
+		err = cast(Accept_Error)errno
+		return
+	}
+
+	client = cast(TCP_Socket)result
+	source = _sockaddr_to_endpoint(&sockaddr)
+	return
+}
+
+@(private)
+_close :: proc(socket: Any_Socket) {
+	real_socket := cast(Fd)any_socket_to_socket(socket)
+	// TODO: This returns an error number, but the `core:net` interface does not handle it.
+	_ = freebsd.close(real_socket)
+}
+
+@(private)
+_recv_tcp :: proc(socket: TCP_Socket, buf: []byte) -> (bytes_read: int, err: Network_Error) {
+	if len(buf) == 0 {
+		return
+	}
+	result, errno := freebsd.recv(cast(Fd)socket, buf, .NONE)
+	if errno != nil {
+		err = cast(TCP_Recv_Error)errno
+		return
+	}
+	return result, nil
+}
+
+@(private)
+_recv_udp :: proc(socket: UDP_Socket, buf: []byte) -> (bytes_read: int, remote_endpoint: Endpoint, err: Network_Error) {
+	if len(buf) == 0 {
+		return
+	}
+	from: freebsd.Socket_Address_Storage
+
+	result, errno := freebsd.recvfrom(cast(Fd)socket, buf, .NONE, &from)
+	if errno != nil {
+		err = cast(UDP_Recv_Error)errno
+		return
+	}
+	return result, _sockaddr_to_endpoint(&from), nil
+}
+
+@(private)
+_send_tcp :: proc(socket: TCP_Socket, buf: []byte) -> (bytes_written: int, err: Network_Error) {
+	for bytes_written < len(buf) {
+		limit := min(int(max(i32)), len(buf) - bytes_written)
+		remaining := buf[bytes_written:][:limit]
+
+		result, errno := freebsd.send(cast(Fd)socket, remaining, .NONE)
+		if errno != nil {
+			err = cast(TCP_Send_Error)errno
+			return
+		}
+		bytes_written += result
+	}
+	return
+}
+
+@(private)
+_send_udp :: proc(socket: UDP_Socket, buf: []byte, to: Endpoint) -> (bytes_written: int, err: Network_Error) {
+	toaddr := _endpoint_to_sockaddr(to)
+	for bytes_written < len(buf) {
+		limit := min(int(max(i32)), len(buf) - bytes_written)
+		remaining := buf[bytes_written:][:limit]
+
+		result, errno := freebsd.sendto(cast(Fd)socket, remaining, .NONE, &toaddr)
+		if errno != nil {
+			err = cast(UDP_Send_Error)errno
+			return
+		}
+		bytes_written += result
+	}
+	return
+}
+
+@(private)
+_shutdown :: proc(socket: Any_Socket, manner: Shutdown_Manner) -> (err: Network_Error) {
+	real_socket := cast(Fd)any_socket_to_socket(socket)
+	errno := freebsd.shutdown(real_socket, cast(freebsd.Shutdown_Method)manner)
+	if errno != nil {
+		return cast(Shutdown_Error)errno
+	}
+	return
+}
+
+@(private)
+_set_option :: proc(socket: Any_Socket, option: Socket_Option, value: any, loc := #caller_location) -> Network_Error {
+	// NOTE(Feoramund): I found that FreeBSD, like Linux, requires at least 32
+	// bits for a boolean socket option value. Nothing less will work.
+	bool_value: b32
+	// TODO: Assuming no larger than i32, but the system may accept i64.
+	int_value: i32
+	timeval_value: freebsd.timeval
+
+	ptr: rawptr
+	len: freebsd.socklen_t
+
+	switch option {
+	case
+		.Reuse_Address,
+		.Keep_Alive,
+		.Broadcast,
+		.Use_Loopback,
+		.Out_Of_Bounds_Data_Inline,
+		.Reuse_Port,
+		.No_SIGPIPE_From_EPIPE,
+		.Reuse_Port_Load_Balancing:
+			switch real in value {
+			case bool: bool_value = cast(b32)real
+			case b8:   bool_value = cast(b32)real
+			case b16:  bool_value = cast(b32)real
+			case b32:  bool_value = real
+			case b64:  bool_value = cast(b32)real
+			case:
+				panic("set_option() value must be a boolean here", loc)
+			}
+			ptr = &bool_value
+			len = size_of(bool_value)
+	case
+		.Linger,
+		.Send_Timeout,
+		.Receive_Timeout:
+			t, ok := value.(time.Duration)
+			if !ok {
+				panic("set_option() value must be a time.Duration here", loc)
+			}
+
+			micros := cast(freebsd.time_t)time.duration_microseconds(t)
+			timeval_value.usec = cast(freebsd.suseconds_t)micros % 1e6
+			timeval_value.sec = (micros - cast(freebsd.time_t)timeval_value.usec) / 1e6
+
+			ptr = &timeval_value
+			len = size_of(timeval_value)
+	case
+		.Receive_Buffer_Size,
+		.Send_Buffer_Size:
+			switch real in value {
+			case   i8: int_value = cast(i32)real
+			case   u8: int_value = cast(i32)real
+			case  i16: int_value = cast(i32)real
+			case  u16: int_value = cast(i32)real
+			case  i32: int_value = real
+			case  u32:
+				if real > u32(max(i32)) { return .Value_Out_Of_Range }
+				int_value = cast(i32)real
+			case  i64:
+				if real > i64(max(i32)) || real < i64(min(i32)) { return .Value_Out_Of_Range }
+				int_value = cast(i32)real
+			case  u64:
+				if real > u64(max(i32)) { return .Value_Out_Of_Range }
+				int_value = cast(i32)real
+			case i128:
+				if real > i128(max(i32)) || real < i128(min(i32)) { return .Value_Out_Of_Range }
+				int_value = cast(i32)real
+			case u128:
+				if real > u128(max(i32)) { return .Value_Out_Of_Range }
+				int_value = cast(i32)real
+			case  int:
+				if real > int(max(i32)) || real < int(min(i32)) { return .Value_Out_Of_Range }
+				int_value = cast(i32)real
+			case uint:
+				if real > uint(max(i32)) { return .Value_Out_Of_Range }
+				int_value = cast(i32)real
+			case:
+				panic("set_option() value must be an integer here", loc)
+			}
+			ptr = &int_value
+			len = size_of(int_value)
+	case:
+		unimplemented("set_option() option not yet implemented", loc)
+	}
+
+	real_socket := any_socket_to_socket(socket)
+	errno := freebsd.setsockopt(cast(Fd)real_socket, .SOCKET, cast(freebsd.Socket_Option)option, ptr, len)
+	if errno != nil {
+		return cast(Socket_Option_Error)errno
+	}
+
+	return nil
+}
+
+@(private)
+_set_blocking :: proc(socket: Any_Socket, should_block: bool) -> (err: Network_Error) {
+	real_socket := any_socket_to_socket(socket)
+
+	flags, errno := freebsd.fcntl_getfl(cast(freebsd.Fd)real_socket)
+	if errno != nil {
+		return cast(Set_Blocking_Error)errno
+	}
+
+	if should_block {
+		flags &= ~{ .NONBLOCK }
+	} else {
+		flags |= { .NONBLOCK }
+	}
+
+	errno = freebsd.fcntl_setfl(cast(freebsd.Fd)real_socket, flags)
+	if errno != nil {
+		return cast(Set_Blocking_Error)errno
+	}
+
+	return
+}
+
+@(private)
+_endpoint_to_sockaddr :: proc(ep: Endpoint) -> (sockaddr: freebsd.Socket_Address_Storage) {
+	switch addr in ep.address {
+	case IP4_Address:
+		(cast(^freebsd.Socket_Address_Internet)(&sockaddr))^ = {
+			len = size_of(freebsd.Socket_Address_Internet),
+			family = .INET,
+			port = cast(freebsd.in_port_t)ep.port,
+			addr = transmute(freebsd.IP4_Address)addr,
+		}
+	case IP6_Address:
+		(cast(^freebsd.Socket_Address_Internet6)(&sockaddr))^ = {
+			len = size_of(freebsd.Socket_Address_Internet),
+			family = .INET6,
+			port = cast(freebsd.in_port_t)ep.port,
+			addr = transmute(freebsd.IP6_Address)addr,
+		}
+	}
+	return
+}
+
+@(private)
+_sockaddr_to_endpoint :: proc(native_addr: ^freebsd.Socket_Address_Storage) -> (ep: Endpoint) {
+	#partial switch native_addr.family {
+	case .INET:
+		addr := cast(^freebsd.Socket_Address_Internet)native_addr
+		ep = {
+			address = cast(IP4_Address)addr.addr.addr8,
+			port = cast(int)addr.port,
+		}
+	case .INET6:
+		addr := cast(^freebsd.Socket_Address_Internet6)native_addr
+		ep = {
+			address = cast(IP6_Address)addr.addr.addr16,
+			port = cast(int)addr.port,
+		}
+	case:
+		panic("native_addr is neither an IP4 or IP6 address")
+	}
+	return
+}

+ 2 - 0
core/net/socket_linux.odin

@@ -10,6 +10,7 @@ package net
 	Copyright 2022 Tetralux        <[email protected]>
 	Copyright 2022 Colin Davidson  <[email protected]>
 	Copyright 2022 Jeroen van Rijn <[email protected]>.
+	Copyright 2024 Feoramund       <[email protected]>.
 	Made available under Odin's BSD-3 license.
 
 	List of contributors:
@@ -17,6 +18,7 @@ package net
 		Colin Davidson:  Linux platform code, OSX platform code, Odin-native DNS resolver
 		Jeroen van Rijn: Cross platform unification, code style, documentation
 		flysand:         Move dependency from core:os to core:sys/linux
+		Feoramund:       FreeBSD platform code
 */
 
 import "core:c"

+ 2 - 0
core/net/socket_windows.odin

@@ -10,12 +10,14 @@ package net
 	Copyright 2022 Tetralux        <[email protected]>
 	Copyright 2022 Colin Davidson  <[email protected]>
 	Copyright 2022 Jeroen van Rijn <[email protected]>.
+	Copyright 2024 Feoramund       <[email protected]>.
 	Made available under Odin's BSD-3 license.
 
 	List of contributors:
 		Tetralux:        Initial implementation
 		Colin Davidson:  Linux platform code, OSX platform code, Odin-native DNS resolver
 		Jeroen van Rijn: Cross platform unification, code style, documentation
+		Feoramund:       FreeBSD platform code
 */
 
 import "core:c"

+ 2 - 0
core/net/url.odin

@@ -8,12 +8,14 @@ package net
 	Copyright 2022 Tetralux        <[email protected]>
 	Copyright 2022 Colin Davidson  <[email protected]>
 	Copyright 2022 Jeroen van Rijn <[email protected]>.
+	Copyright 2024 Feoramund       <[email protected]>.
 	Made available under Odin's BSD-3 license.
 
 	List of contributors:
 		Tetralux:        Initial implementation
 		Colin Davidson:  Linux platform code, OSX platform code, Odin-native DNS resolver
 		Jeroen van Rijn: Cross platform unification, code style, documentation
+		Feoramund:       FreeBSD platform code
 */
 
 import "core:strings"

+ 17 - 24
core/sync/futex_freebsd.odin

@@ -3,35 +3,27 @@
 package sync
 
 import "core:c"
+import "core:sys/freebsd"
 import "core:time"
 
-UMTX_OP_WAIT :: 2
-UMTX_OP_WAKE :: 3
-
-ETIMEDOUT :: 60
-
-foreign import libc "system:c"
-
-foreign libc {
-	_umtx_op :: proc "c" (obj: rawptr, op: c.int, val: c.ulong, uaddr: rawptr, uaddr2: rawptr) -> c.int ---
-	__error :: proc "c" () -> ^c.int ---
-}
-
 _futex_wait :: proc "contextless" (f: ^Futex, expected: u32) -> bool {
-	timeout := [2]i64{14400, 0} // 4 hours
+	timeout := freebsd.timespec {14400, 0} // 4 hours
+	timeout_size := cast(rawptr)cast(uintptr)size_of(timeout)
+
 	for {
-		res := _umtx_op(f, UMTX_OP_WAIT, c.ulong(expected), nil, &timeout)
+		errno := freebsd._umtx_op(f, .WAIT_UINT, cast(c.ulong)expected, timeout_size, &timeout)
 
-		if res != -1 {
+		if errno == nil {
 			return true
 		}
 
-		if __error()^ == ETIMEDOUT {
+		if errno == .ETIMEDOUT {
 			continue
 		}
 
 		_panic("_futex_wait failure")
 	}
+
 	unreachable()
 }
 
@@ -40,14 +32,15 @@ _futex_wait_with_timeout :: proc "contextless" (f: ^Futex, expected: u32, durati
 		return false
 	}
 
-	timeout := [2]i64{i64(duration/1e9), i64(duration%1e9)}
+	timeout := freebsd.timespec {cast(freebsd.time_t)duration / 1e9, cast(c.long)duration % 1e9}
+	timeout_size := cast(rawptr)cast(uintptr)size_of(timeout)
 
-	res := _umtx_op(f, UMTX_OP_WAIT, c.ulong(expected), nil, &timeout)
-	if res != -1 {
+	errno := freebsd._umtx_op(f, .WAIT_UINT, cast(c.ulong)expected, timeout_size, &timeout)
+	if errno == nil {
 		return true
 	}
 
-	if __error()^ == ETIMEDOUT {
+	if errno == .ETIMEDOUT {
 		return false
 	}
 
@@ -55,17 +48,17 @@ _futex_wait_with_timeout :: proc "contextless" (f: ^Futex, expected: u32, durati
 }
 
 _futex_signal :: proc "contextless" (f: ^Futex) {
-	res := _umtx_op(f, UMTX_OP_WAKE, 1, nil, nil)
+	errno := freebsd._umtx_op(f, .WAKE, 1, nil, nil)
 
-	if res == -1 {
+	if errno != nil {
 		_panic("_futex_signal failure")
 	}
 }
 
 _futex_broadcast :: proc "contextless" (f: ^Futex)  {
-	res := _umtx_op(f, UMTX_OP_WAKE, c.ulong(max(i32)), nil, nil)
+	errno := freebsd._umtx_op(f, .WAKE, cast(c.ulong)max(i32), nil, nil)
 
-	if res == -1 {
+	if errno != nil {
 		_panic("_futex_broadcast failure")
 	}
 }

+ 515 - 0
core/sys/freebsd/syscalls.odin

@@ -0,0 +1,515 @@
+package sys_freebsd
+
+/*
+	(c) Copyright 2024 Feoramund <[email protected]>.
+	Made available under Odin's BSD-3 license.
+
+	List of contributors:
+		Feoramund: Initial implementation.
+*/
+
+import "base:intrinsics"
+import "core:c"
+
+// FreeBSD 15 syscall numbers
+// See: https://alfonsosiciliano.gitlab.io/posts/2023-08-28-freebsd-15-system-calls.html
+
+SYS_open       : uintptr : 5
+SYS_close      : uintptr : 6
+SYS_getpid     : uintptr : 20
+SYS_recvfrom   : uintptr : 29
+SYS_accept     : uintptr : 30
+SYS_fcntl      : uintptr : 92
+SYS_socket     : uintptr : 97
+SYS_connect    : uintptr : 98
+SYS_bind       : uintptr : 104
+SYS_listen     : uintptr : 106
+SYS_sendto     : uintptr : 133
+SYS_shutdown   : uintptr : 134
+SYS_setsockopt : uintptr : 105
+SYS_sysctl     : uintptr : 202
+SYS__umtx_op   : uintptr : 454
+SYS_accept4    : uintptr : 541
+
+//
+// Odin syscall wrappers
+//
+
+// Open or create a file for reading, writing or executing.
+//
+// The open() function appeared in Version 1 AT&T UNIX.
+// The openat() function was introduced in FreeBSD 8.0.
+open :: proc "contextless" (path: string, flags: File_Status_Flags, mode: int = 0o000) -> (Fd, Errno) {
+	result, ok := intrinsics.syscall_bsd(SYS_open,
+		cast(uintptr)raw_data(path),
+		cast(uintptr)transmute(c.int)flags,
+		cast(uintptr)mode)
+
+	if !ok {
+		return 0, cast(Errno)result
+	}
+
+	return cast(Fd)result, nil
+}
+
+// Delete a descriptor.
+//
+// The open() function appeared in Version 1 AT&T UNIX.
+close :: proc "contextless" (fd: Fd) -> Errno {
+	result, _ := intrinsics.syscall_bsd(SYS_close,
+		cast(uintptr)fd)
+
+	return cast(Errno)result
+}
+
+// Get parent or calling process identification.
+//
+// The getpid() function appeared in Version 7 AT&T UNIX.
+getpid :: proc "contextless" () -> pid_t {
+	// This always succeeds.
+	result, _ := intrinsics.syscall_bsd(SYS_getpid)
+	return cast(pid_t)result
+}
+
+// Receive message(s) from a socket.
+//
+// The recv() function appeared in 4.2BSD.
+// The recvmmsg() function appeared in FreeBSD 11.0.
+recvfrom :: proc "contextless" (s: Fd, buf: []u8, flags: Recv_Flags, from: ^$T) -> (int, Errno)
+where
+	intrinsics.type_is_subtype_of(T, Socket_Address_Header)
+{
+	fromlen: socklen_t = size_of(T)
+
+	result, ok := intrinsics.syscall_bsd(SYS_recvfrom,
+		cast(uintptr)s,
+		cast(uintptr)raw_data(buf),
+		cast(uintptr)len(buf),
+		cast(uintptr)flags,
+		cast(uintptr)from,
+		cast(uintptr)&fromlen)
+
+	// `from.len` will be modified by the syscall, so we shouldn't need to pass
+	// `fromlen` back from this API.
+
+	if !ok {
+		return 0, cast(Errno)result
+	}
+
+	return cast(int)result, nil
+}
+
+// Receive message(s) from a socket.
+//
+// The recv() function appeared in 4.2BSD.
+// The recvmmsg() function appeared in FreeBSD 11.0.
+recv :: proc "contextless" (s: Fd, buf: []u8, flags: Recv_Flags) -> (int, Errno) {
+	// This is a wrapper over recvfrom().
+	result, ok := intrinsics.syscall_bsd(SYS_recvfrom,
+		cast(uintptr)s,
+		cast(uintptr)raw_data(buf),
+		cast(uintptr)len(buf),
+		cast(uintptr)flags,
+		0,
+		0)
+
+	if !ok {
+		return 0, cast(Errno)result
+	}
+
+	return cast(int)result, nil
+}
+
+// Accept a connection on a socket.
+//
+// The accept() system call appeared in 4.2BSD.
+accept_T :: proc "contextless" (s: Fd, sockaddr: ^$T) -> (Fd, Errno)
+where
+	intrinsics.type_is_subtype_of(T, Socket_Address_Header)
+{
+	// sockaddr must contain a valid pointer, or this will segfault because
+	// we're telling the syscall that there's memory available to write to.
+	addrlen: socklen_t = size_of(T)
+
+	result, ok := intrinsics.syscall_bsd(SYS_accept,
+		cast(uintptr)s,
+		cast(uintptr)sockaddr,
+		cast(uintptr)&addrlen)
+
+	if !ok {
+		return 0, cast(Errno)result
+	}
+
+	sockaddr.len = cast(u8)addrlen
+
+	return cast(Fd)result, nil
+}
+
+
+// Accept a connection on a socket.
+//
+// The accept() system call appeared in 4.2BSD.
+accept_nil :: proc "contextless" (s: Fd) -> (Fd, Errno) {
+	result, ok := intrinsics.syscall_bsd(SYS_accept,
+		cast(uintptr)s,
+		cast(uintptr)0,
+		cast(uintptr)0)
+
+	if !ok {
+		return 0, cast(Errno)result
+	}
+
+	return cast(Fd)result, nil
+}
+
+accept :: proc { accept_T, accept_nil }
+
+// File control.
+//
+// The fcntl() system call appeared in 4.2BSD.
+// The F_DUP2FD constant first appeared in FreeBSD 7.1.
+//
+// NOTE: If you know at compile-time what command you're calling, use one of the
+// `fcntl_*` procedures instead to preserve type safety.
+fcntl :: proc "contextless" (fd: Fd, cmd: File_Control_Command, arg: c.int) -> (int, Errno) {
+	result, ok := intrinsics.syscall_bsd(SYS_fcntl,
+		cast(uintptr)fd,
+		cast(uintptr)cmd,
+		cast(uintptr)arg)
+
+	if !ok {
+		return 0, cast(Errno)result
+	}
+
+	return cast(int)result, nil
+}
+
+// TODO: Implement more type-safe fcntl commands.
+
+fcntl_dupfd :: proc "contextless" (fd: Fd, newfd: Fd) -> (Fd, Errno) {
+	result, ok := intrinsics.syscall_bsd(SYS_fcntl,
+		cast(uintptr)fd,
+		cast(uintptr)File_Control_Command.DUPFD,
+		cast(uintptr)newfd)
+
+	if !ok {
+		return 0, cast(Errno)result
+	}
+
+	return cast(Fd)result, nil
+}
+
+fcntl_getfd :: proc "contextless" (fd: Fd) -> (bool, Errno) {
+	result, ok := intrinsics.syscall_bsd(SYS_fcntl,
+		cast(uintptr)fd,
+		cast(uintptr)File_Control_Command.GETFD)
+
+	if !ok {
+		return false, cast(Errno)result
+	}
+
+	return result & FD_CLOEXEC > 0, nil
+}
+
+fcntl_setfd :: proc "contextless" (fd: Fd, close_on_exec: bool) -> Errno {
+	result, _ := intrinsics.syscall_bsd(SYS_fcntl,
+		cast(uintptr)fd,
+		cast(uintptr)File_Control_Command.SETFD,
+		(close_on_exec ? FD_CLOEXEC : 0))
+
+	return cast(Errno)result
+}
+
+fcntl_getfl :: proc "contextless" (fd: Fd) -> (File_Status_Flags, Errno) {
+	result, ok := intrinsics.syscall_bsd(SYS_fcntl,
+		cast(uintptr)fd,
+		cast(uintptr)File_Control_Command.GETFL)
+
+	if !ok {
+		return nil, cast(Errno)result
+	}
+
+	return transmute(File_Status_Flags)cast(c.int)result, nil
+}
+
+fcntl_setfl :: proc "contextless" (fd: Fd, flags: File_Status_Flags) -> Errno {
+	result, _ := intrinsics.syscall_bsd(SYS_fcntl,
+		cast(uintptr)fd,
+		cast(uintptr)File_Control_Command.SETFL,
+		cast(uintptr)transmute(c.int)flags)
+
+	return cast(Errno)result
+}
+
+fcntl_getown :: proc "contextless" (fd: Fd) -> (pid_t, Errno) {
+	result, ok := intrinsics.syscall_bsd(SYS_fcntl,
+		cast(uintptr)fd,
+		cast(uintptr)File_Control_Command.GETOWN)
+
+	if !ok {
+		return 0, cast(Errno)result
+	}
+
+	return cast(pid_t)result, nil
+}
+
+fcntl_setown :: proc "contextless" (fd: Fd, pid: pid_t) -> Errno {
+	result, _ := intrinsics.syscall_bsd(SYS_fcntl,
+		cast(uintptr)fd,
+		cast(uintptr)File_Control_Command.SETOWN,
+		cast(uintptr)pid)
+
+	return cast(Errno)result
+}
+
+fcntl_getlk :: proc "contextless" (fd: Fd, flock: ^File_Lock) -> Errno {
+	result, _ := intrinsics.syscall_bsd(SYS_fcntl,
+		cast(uintptr)fd,
+		cast(uintptr)File_Control_Command.GETLK,
+		cast(uintptr)flock)
+
+	return cast(Errno)result
+}
+
+fcntl_setlk :: proc "contextless" (fd: Fd, flock: ^File_Lock) -> Errno {
+	result, _ := intrinsics.syscall_bsd(SYS_fcntl,
+		cast(uintptr)fd,
+		cast(uintptr)File_Control_Command.SETLK,
+		cast(uintptr)flock)
+
+	return cast(Errno)result
+}
+
+fcntl_add_seals :: proc "contextless" (fd: Fd, seals: File_Seals) -> Errno {
+	result, _ := intrinsics.syscall_bsd(SYS_fcntl,
+		cast(uintptr)fd,
+		cast(uintptr)File_Control_Command.ADD_SEALS,
+		cast(uintptr)transmute(c.int)seals)
+
+	return cast(Errno)result
+}
+
+fcntl_get_seals :: proc "contextless" (fd: Fd) -> (File_Seals, Errno) {
+	result, ok := intrinsics.syscall_bsd(SYS_fcntl,
+		cast(uintptr)fd,
+		cast(uintptr)File_Control_Command.GET_SEALS)
+
+	if !ok {
+		return nil, cast(Errno)result
+	}
+
+	return transmute(File_Seals)cast(c.int)result, nil
+}
+
+//
+// End type-safe fcntl commands.
+//
+
+// Create an endpoint for communication.
+//
+// The socket() system call appeared in 4.2BSD.
+socket :: proc "contextless" (domain: Protocol_Family, type: Socket_Type, protocol: Protocol) -> (Fd, Errno) {
+	result, ok := intrinsics.syscall_bsd(SYS_socket,
+		cast(uintptr)domain,
+		cast(uintptr)type,
+		cast(uintptr)protocol)
+
+	if !ok {
+		return 0, cast(Errno)result
+	}
+
+	return cast(Fd)result, nil
+}
+
+// Initiate a connection on a socket.
+//
+// The connect() system call appeared in 4.2BSD.
+connect :: proc "contextless" (fd: Fd, sockaddr: ^$T, addrlen: socklen_t) -> Errno
+where
+	intrinsics.type_is_subtype_of(T, Socket_Address_Header)
+{
+	result, _ := intrinsics.syscall_bsd(SYS_connect,
+		cast(uintptr)fd,
+		cast(uintptr)sockaddr,
+		cast(uintptr)addrlen)
+
+	return cast(Errno)result
+}
+
+
+// Assign a local protocol address to a socket.
+//
+// The bind() system call appeared in 4.2BSD.
+bind :: proc "contextless" (s: Fd, sockaddr: ^$T, addrlen: socklen_t) -> Errno
+where
+	intrinsics.type_is_subtype_of(T, Socket_Address_Header)
+{
+	result, _ := intrinsics.syscall_bsd(SYS_bind,
+		cast(uintptr)s,
+		cast(uintptr)sockaddr,
+		cast(uintptr)addrlen)
+
+	return cast(Errno)result
+}
+
+// Listen for connections on a socket.
+//
+// The listen() system call appeared in 4.2BSD.
+listen :: proc "contextless" (s: Fd, backlog: int) -> Errno {
+	result, _ := intrinsics.syscall_bsd(SYS_listen,
+		cast(uintptr)s,
+		cast(uintptr)backlog)
+
+	return cast(Errno)result
+}
+
+// Send message(s) from a socket.
+//
+// The send() function appeared in 4.2BSD.
+// The sendmmsg() function appeared in FreeBSD 11.0.
+sendto :: proc "contextless" (s: Fd, msg: []u8, flags: Send_Flags, to: ^$T) -> (int, Errno)
+where
+	intrinsics.type_is_subtype_of(T, Socket_Address_Header)
+{
+	result, ok := intrinsics.syscall_bsd(SYS_sendto,
+		cast(uintptr)s,
+		cast(uintptr)raw_data(msg),
+		cast(uintptr)len(msg),
+		cast(uintptr)flags,
+		cast(uintptr)to,
+		cast(uintptr)to.len)
+
+	if !ok {
+		return 0, cast(Errno)result
+	}
+
+	return cast(int)result, nil
+}
+
+// Send message(s) from a socket.
+//
+// The send() function appeared in 4.2BSD.
+// The sendmmsg() function appeared in FreeBSD 11.0.
+send :: proc "contextless" (s: Fd, msg: []u8, flags: Send_Flags) -> (int, Errno) {
+	// This is a wrapper over sendto().
+	result, ok := intrinsics.syscall_bsd(SYS_sendto,
+		cast(uintptr)s,
+		cast(uintptr)raw_data(msg),
+		cast(uintptr)len(msg),
+		cast(uintptr)flags,
+		0,
+		0)
+
+	if !ok {
+		return 0, cast(Errno)result
+	}
+
+	return cast(int)result, nil
+}
+
+// Disable sends and/or receives on a socket.
+//
+// The shutdown() system call appeared in 4.2BSD.
+shutdown :: proc "contextless" (s: Fd, how: Shutdown_Method) -> Errno {
+	result, _ := intrinsics.syscall_bsd(SYS_shutdown,
+		cast(uintptr)s,
+		cast(uintptr)how)
+
+	return cast(Errno)result
+}
+
+// Get and set options on sockets.
+//
+// The getsockopt() and setsockopt() system calls appeared in 4.2BSD.
+setsockopt :: proc "contextless" (s: Fd, level: Valid_Socket_Option_Level, optname: Socket_Option, optval: rawptr, optlen: socklen_t) -> Errno {
+	real_level: uintptr
+	switch which in level {
+	case Protocol_Family:     real_level = cast(uintptr)which
+	case Socket_Option_Level: real_level = cast(uintptr)which
+	}
+
+	result, _ := intrinsics.syscall_bsd(SYS_setsockopt,
+		cast(uintptr)s,
+		real_level,
+		cast(uintptr)optname,
+		cast(uintptr)optval,
+		cast(uintptr)optlen)
+
+	return cast(Errno)result
+}
+
+// Get or set system information.
+//
+// The sysctl() function first appeared in 4.4BSD.
+sysctl :: proc "contextless" (mib: []MIB_Identifier, oldp: rawptr, oldlenp: ^c.size_t, newp: rawptr, newlen: c.size_t) -> Errno {
+	result, _ := intrinsics.syscall_bsd(SYS_sysctl,
+		cast(uintptr)raw_data(mib),
+		cast(uintptr)len(mib),
+		cast(uintptr)oldp,
+		cast(uintptr)oldlenp,
+		cast(uintptr)newp,
+		cast(uintptr)newlen)
+
+	return cast(Errno)result
+}
+
+// Interface for implementation of userspace threading synchronization primitives.
+//
+// The _umtx_op() system call is non-standard and is used by the 1:1 Threading
+// Library (libthr, -lthr) to implement IEEE Std 1003.1-2001 (“POSIX.1”)
+// pthread(3) functionality.
+_umtx_op :: proc "contextless" (obj: rawptr, op: Userland_Mutex_Operation, val: c.ulong, uaddr, uaddr2: rawptr) -> Errno {
+	result, _ := intrinsics.syscall_bsd(SYS__umtx_op,
+		cast(uintptr)obj,
+		cast(uintptr)op,
+		cast(uintptr)val,
+		cast(uintptr)uaddr,
+		cast(uintptr)uaddr2)
+
+	return cast(Errno)result
+}
+
+// Accept a connection on a socket.
+//
+// The accept4() system call appeared in FreeBSD 10.0.
+accept4_T :: proc "contextless" (s: Fd, sockaddr: ^$T, flags: Socket_Flags = {}) -> (Fd, Errno)
+where
+	intrinsics.type_is_subtype_of(T, Socket_Address_Header)
+{
+	// `sockaddr` must contain a valid pointer, or this will segfault because
+	// we're telling the syscall that there's memory available to write to.
+	addrlen: u32 = size_of(T)
+
+	result, ok := intrinsics.syscall_bsd(SYS_accept4,
+		cast(uintptr)s,
+		cast(uintptr)sockaddr,
+		cast(uintptr)&addrlen,
+		cast(uintptr)transmute(c.int)flags)
+
+	if !ok {
+		return 0, cast(Errno)result
+	}
+
+	sockaddr.len = cast(u8)addrlen
+
+	return cast(Fd)result, nil
+}
+
+// Accept a connection on a socket.
+//
+// The accept4() system call appeared in FreeBSD 10.0.
+accept4_nil :: proc "contextless" (s: Fd, flags: Socket_Flags = {}) -> (Fd, Errno) {
+	result, ok := intrinsics.syscall_bsd(SYS_accept4,
+		cast(uintptr)s,
+		cast(uintptr)0,
+		cast(uintptr)0,
+		cast(uintptr)transmute(c.int)flags)
+
+	if !ok {
+		return 0, cast(Errno)result
+	}
+
+	return cast(Fd)result, nil
+}
+
+accept4 :: proc { accept4_nil, accept4_T }

+ 1587 - 0
core/sys/freebsd/types.odin

@@ -0,0 +1,1587 @@
+package sys_freebsd
+
+/*
+	(c) Copyright 2024 Feoramund <[email protected]>.
+	Made available under Odin's BSD-3 license.
+
+	List of contributors:
+		Feoramund: Initial implementation.
+*/
+
+import "core:c"
+
+// These definitions have been extracted from a system running FreeBSD 14.0-RELEASE.
+// Most comments come from system header files.
+//
+// Where applicable, original C struct and define names are indicated in line
+// comments above Odin declarations.
+//
+// This data is separated into blocks by original header file. If you happen to
+// add or change something in this file, mind the organizational structure.
+
+Fd :: distinct c.int
+
+//
+// #include <x86/_types.h>
+//         &
+// #include <arm/_types.h>
+//
+
+time_t :: distinct i64
+
+//
+// #include <sys/_types.h>
+//
+
+off_t       :: distinct i64
+pid_t       :: distinct i32
+sa_family_t :: distinct u8
+socklen_t   :: distinct u32
+suseconds_t :: distinct c.long /* microseconds (signed) */
+
+//
+// #include <sys/types.h>
+//
+
+in_port_t :: distinct u16be
+
+//
+// #include <sys/_timespec.h>
+//
+
+timespec :: struct {
+	sec: time_t,  /* seconds */
+	nsec: c.long, /* and nanoseconds */
+}
+
+//
+// #include <sys/_timeval.h>
+//
+
+timeval :: struct {
+	sec:  time_t,      /* seconds */
+	usec: suseconds_t, /* and microseconds */
+}
+
+//
+// #include <sys/errno.h>
+//
+
+Errno :: enum c.int {
+	NONE            = 0,
+	EPERM           = 1,
+	ENOENT          = 2,
+	ESRCH           = 3,
+	EINTR           = 4,
+	EIO             = 5,
+	ENXIO           = 6,
+	E2BIG           = 7,
+	ENOEXEC         = 8,
+	EBADF           = 9,
+	ECHILD          = 10,
+	EDEADLK         = 11,
+	ENOMEM          = 12,
+	EACCES          = 13,
+	EFAULT          = 14,
+	ENOTBLK         = 15,
+	EBUSY           = 16,
+	EEXIST          = 17,
+	EXDEV           = 18,
+	ENODEV          = 19,
+	ENOTDIR         = 20,
+	EISDIR          = 21,
+	EINVAL          = 22,
+	ENFILE          = 23,
+	EMFILE          = 24,
+	ENOTTY          = 25,
+	ETXTBSY         = 26,
+	EFBIG           = 27,
+	ENOSPC          = 28,
+	ESPIPE          = 29,
+	EROFS           = 30,
+	EMLINK          = 31,
+	EPIPE           = 32,
+	EDOM            = 33,
+	ERANGE          = 34,
+	EAGAIN          = 35,
+	EWOULDBLOCK     = EAGAIN,
+	EINPROGRESS     = 36,
+	EALREADY        = 37,
+	ENOTSOCK        = 38,
+	EDESTADDRREQ    = 39,
+	EMSGSIZE        = 40,
+	EPROTOTYPE      = 41,
+	ENOPROTOOPT     = 42,
+	EPROTONOSUPPORT = 43,
+	ESOCKTNOSUPPORT = 44,
+	EOPNOTSUPP      = 45,
+	ENOTSUP         = EOPNOTSUPP,
+	EPFNOSUPPORT    = 46,
+	EAFNOSUPPORT    = 47,
+	EADDRINUSE      = 48,
+	EADDRNOTAVAIL   = 49,
+	ENETDOWN        = 50,
+	ENETUNREACH     = 51,
+	ENETRESET       = 52,
+	ECONNABORTED    = 53,
+	ECONNRESET      = 54,
+	ENOBUFS         = 55,
+	EISCONN         = 56,
+	ENOTCONN        = 57,
+	ESHUTDOWN       = 58,
+	ETOOMANYREFS    = 59,
+	ETIMEDOUT       = 60,
+	ECONNREFUSED    = 61,
+	ELOOP           = 62,
+	ENAMETOOLONG    = 63,
+	EHOSTDOWN       = 64,
+	EHOSTUNREACH    = 65,
+	ENOTEMPTY       = 66,
+	EPROCLIM        = 67,
+	EUSERS          = 68,
+	EDQUOT          = 69,
+	ESTALE          = 70,
+	EREMOTE         = 71,
+	EBADRPC         = 72,
+	ERPCMISMATCH    = 73,
+	EPROGUNAVAIL    = 74,
+	EPROGMISMATCH   = 75,
+	EPROCUNAVAIL    = 76,
+	ENOLCK          = 77,
+	ENOSYS          = 78,
+	EFTYPE          = 79,
+	EAUTH           = 80,
+	ENEEDAUTH       = 81,
+	EIDRM           = 82,
+	ENOMSG          = 83,
+	EOVERFLOW       = 84,
+	ECANCELED       = 85,
+	EILSEQ          = 86,
+	ENOATTR         = 87,
+	EDOOFUS         = 88,
+	EBADMSG         = 89,
+	EMULTIHOP       = 90,
+	ENOLINK         = 91,
+	EPROTO          = 92,
+	ENOTCAPABLE     = 93,
+	ECAPMODE        = 94,
+	ENOTRECOVERABLE = 95,
+	EOWNERDEAD      = 96,
+	EINTEGRITY      = 97,
+}
+
+//
+// #include <sys/socket.h>
+//
+
+/*
+ * Types
+ */
+// #define SOCK_*
+Socket_Type :: enum c.int {
+	STREAM    = 1, /* stream socket */
+	DGRAM     = 2, /* datagram socket */
+	RAW       = 3, /* raw-protocol interface */
+	RDM       = 4, /* reliably-delivered message */
+	SEQPACKET = 5, /* sequenced packet stream */
+
+	/*
+	 * Creation flags, OR'ed into socket() and socketpair() type argument.
+	 */
+	CLOEXEC   = 0x10000000,
+	NONBLOCK  = 0x20000000,
+}
+
+Socket_Flag_Index :: enum c.int {
+	CLOEXEC  = 28, // 0x10000000
+	NONBLOCK = 29, // 0x20000000
+}
+
+Socket_Flags :: bit_set[Socket_Flag_Index; c.int]
+
+/*
+ * Option flags per-socket.
+ */
+// #define SO_*
+Socket_Option :: enum c.int {
+	DEBUG             = 0x00000001, /* turn on debugging info recording */
+	ACCEPTCONN        = 0x00000002, /* socket has had listen() */
+	REUSEADDR         = 0x00000004, /* allow local address reuse */
+	KEEPALIVE         = 0x00000008, /* keep connections alive */
+	DONTROUTE         = 0x00000010, /* just use interface addresses */
+	BROADCAST         = 0x00000020, /* permit sending of broadcast msgs */
+	USELOOPBACK       = 0x00000040, /* bypass hardware when possible */
+	LINGER            = 0x00000080, /* linger on close if data present */
+	OOBINLINE         = 0x00000100, /* leave received OOB data in line */
+	REUSEPORT         = 0x00000200, /* allow local address & port reuse */
+	TIMESTAMP         = 0x00000400, /* timestamp received dgram traffic */
+	NOSIGPIPE         = 0x00000800, /* no SIGPIPE from EPIPE */
+	ACCEPTFILTER      = 0x00001000, /* there is an accept filter */
+	BINTIME           = 0x00002000, /* timestamp received dgram traffic */
+	NO_OFFLOAD        = 0x00004000, /* socket cannot be offloaded */
+	NO_DDP            = 0x00008000, /* disable direct data placement */
+	REUSEPORT_LB      = 0x00010000, /* reuse with load balancing */
+	RERROR            = 0x00020000, /* keep track of receive errors */
+
+	/*
+	 * Additional options, not kept in so_options.
+	 */
+	SNDBUF            = 0x1001,      /* send buffer size */
+	RCVBUF            = 0x1002,      /* receive buffer size */
+	SNDLOWAT          = 0x1003,      /* send low-water mark */
+	RCVLOWAT          = 0x1004,      /* receive low-water mark */
+	SNDTIMEO          = 0x1005,      /* send timeout */
+	RCVTIMEO          = 0x1006,      /* receive timeout */
+	ERROR             = 0x1007,      /* get error status and clear */
+	TYPE              = 0x1008,      /* get socket type */
+	LABEL             = 0x1009,      /* socket's MAC label */
+	PEERLABEL         = 0x1010,      /* socket's peer's MAC label */
+	LISTENQLIMIT      = 0x1011,      /* socket's backlog limit */
+	LISTENQLEN        = 0x1012,      /* socket's complete queue length */
+	LISTENINCQLEN     = 0x1013,      /* socket's incomplete queue length */
+	SETFIB            = 0x1014,      /* use this FIB to route */
+	USER_COOKIE       = 0x1015,      /* user cookie (dummynet etc.) */
+	PROTOCOL          = 0x1016,      /* get socket protocol (Linux name) */
+	PROTOTYPE         = PROTOCOL,    /* alias for SO_PROTOCOL (SunOS name) */
+	TS_CLOCK          = 0x1017,      /* clock type used for SO_TIMESTAMP */
+	MAX_PACING_RATE   = 0x1018,      /* socket's max TX pacing rate (Linux name) */
+	DOMAIN            = 0x1019,      /* get socket domain */
+
+	TS_REALTIME_MICRO = 0,           /* microsecond resolution, realtime */
+	TS_BINTIME        = 1,           /* sub-nanosecond resolution, realtime */
+	TS_REALTIME       = 2,           /* nanosecond resolution, realtime */
+	TS_MONOTONIC      = 3,           /* nanosecond resolution, monotonic */
+	TS_DEFAULT        = TS_REALTIME_MICRO,
+	TS_CLOCK_MAX      = TS_MONOTONIC,
+}
+
+Valid_Socket_Option_Level :: union #no_nil {
+	Protocol_Family,
+	Socket_Option_Level,
+}
+
+/*
+ * Level number for (get/set)sockopt() to apply to socket itself.
+ */
+// #define SOL_*
+Socket_Option_Level :: enum c.int {
+	SOCKET = 0xffff, /* options for socket level */
+}
+
+// #define MSG_*
+Message_Flag :: enum c.int {
+	OOB          = 0x00000001, /* process out-of-band data */
+	PEEK         = 0x00000002, /* peek at incoming message */
+	DONTROUTE    = 0x00000004, /* send without using routing tables */
+	EOR          = 0x00000008, /* data completes record */
+	TRUNC        = 0x00000010, /* data discarded before delivery */
+	CTRUNC       = 0x00000020, /* control data lost before delivery */
+	WAITALL      = 0x00000040, /* wait for full request or error */
+	DONTWAIT     = 0x00000080, /* this message should be nonblocking */
+	EOF          = 0x00000100, /* data completes connection */
+	/*             0x00000200     unused */
+	/*             0x00000400     unused */
+	/*             0x00000800     unused */
+	/*             0x00001000     unused */
+	NOTIFICATION = 0x00002000, /* SCTP notification */
+	NBIO         = 0x00004000, /* FIONBIO mode, used by fifofs */
+	COMPAT       = 0x00008000, /* used in sendit() */
+	SOCALLBCK    = 0x00010000, /* for use by socket callbacks - soreceive (TCP) */
+	NOSIGNAL     = 0x00020000, /* do not generate SIGPIPE on EOF */
+	CMSG_CLOEXEC = 0x00040000, /* make received fds close-on-exec */
+	WAITFORONE   = 0x00080000, /* for recvmmsg() */
+}
+
+// Specific subset of `MSG_*` defines that are only for `recv*`.
+Recv_Flags :: enum c.int {
+	NONE         = 0,
+	OOB          = cast(c.int)Message_Flag.OOB,          /* process out-of-band data */
+	PEEK         = cast(c.int)Message_Flag.PEEK,         /* peek at incoming message */
+	TRUNC        = cast(c.int)Message_Flag.TRUNC,        /* return real packet or datagram length */
+	WAITALL      = cast(c.int)Message_Flag.WAITALL,      /* wait for full request or error */
+	DONTWAIT     = cast(c.int)Message_Flag.DONTWAIT,     /* do not block */
+	CMSG_CLOEXEC = cast(c.int)Message_Flag.CMSG_CLOEXEC, /* set received fds close-on-exec */
+	WAITFORONE   = cast(c.int)Message_Flag.WAITFORONE,   /* do not block after receiving the first message */
+}
+
+// Specific subset of `MSG_*` defines that are only for `send*`.
+Send_Flags :: enum c.int {
+	NONE      = 0,
+	OOB       = cast(c.int)Message_Flag.OOB,       /* process out-of-band data */
+	DONTROUTE = cast(c.int)Message_Flag.DONTROUTE, /* bypass routing, use direct interface */
+	EOR       = cast(c.int)Message_Flag.EOR,       /* data completes record */
+	DONTWAIT  = cast(c.int)Message_Flag.DONTWAIT,  /* do not block */
+	EOF       = cast(c.int)Message_Flag.EOF,       /* data completes transaction */
+	NOSIGNAL  = cast(c.int)Message_Flag.NOSIGNAL,  /* do not generate SIGPIPE on EOF */
+}
+
+// Socket address struct header without protocol-specific data.
+//
+// Inherit from this if you want a custom socket address datatype for use with
+// bind(), listen(), et cetera.
+Socket_Address_Header :: struct #packed {
+	len: c.uchar,           /* address length */
+	family: Address_Family, /* address family */
+}
+
+// struct sockaddr
+Socket_Address_Basic :: struct #packed {
+	using _: Socket_Address_Header,
+	data: [14]c.char,
+}
+
+/*
+ * howto arguments for shutdown(2), specified by Posix.1g.
+ */
+// #define SHUT_*
+Shutdown_Method :: enum c.int {
+	RD   = 0, /* shut down the reading side */
+	WR   = 1, /* shut down the writing side */
+	RDWR = 2, /* shut down both sides */
+}
+
+// #define AF_*
+Address_Family :: enum sa_family_t {
+	UNSPEC      = 0,
+	LOCAL       = 1,
+	UNIX        = LOCAL,
+	INET        = 2,
+	IMPLINK     = 3,
+	PUP         = 4,
+	CHAOS       = 5,
+	NETBIOS     = 6,
+	ISO         = 7,
+	OSI         = ISO,
+	ECMA        = 8,
+	DATAKIT     = 9,
+	CCITT       = 10,
+	SNA         = 11,
+	DECnet      = 12,
+	DLI         = 13,
+	LAT         = 14,
+	HYLINK      = 15,
+	APPLETALK   = 16,
+	ROUTE       = 17,
+	LINK        = 18,
+	PSEUDO_XTP  = 19,
+	COIP        = 20,
+	CNT         = 21,
+	PSEUDO_RTIP = 22,
+	IPX         = 23,
+	SIP         = 24,
+	PSEUDO_PIP  = 25,
+	ISDN        = 26,
+	E164        = ISDN,
+	PSEUDO_KEY  = 27,
+	INET6       = 28,
+	NATM        = 29,
+	ATM         = 30,
+	NETGRAPH    = 32,
+	SLOW        = 33,
+	SCLUSTER    = 34,
+	ARP         = 35,
+	BLUETOOTH   = 36,
+	IEEE80211   = 37,
+	NETLINK     = 38,
+	INET_SDP    = 40,
+	INET6_SDP   = 42,
+	HYPERV      = 43,
+	DIVERT      = 44,
+	MAX         = 44,
+	VENDOR00    = 39,
+	VENDOR01    = 41,
+	VENDOR03    = 45,
+	VENDOR04    = 47,
+	VENDOR05    = 49,
+	VENDOR06    = 51,
+	VENDOR07    = 53,
+	VENDOR08    = 55,
+	VENDOR09    = 57,
+	VENDOR10    = 59,
+	VENDOR11    = 61,
+	VENDOR12    = 63,
+	VENDOR13    = 65,
+	VENDOR14    = 67,
+	VENDOR15    = 69,
+	VENDOR16    = 71,
+	VENDOR17    = 73,
+	VENDOR18    = 75,
+	VENDOR19    = 77,
+	VENDOR20    = 79,
+	VENDOR21    = 81,
+	VENDOR22    = 83,
+	VENDOR23    = 85,
+	VENDOR24    = 87,
+	VENDOR25    = 89,
+	VENDOR26    = 91,
+	VENDOR27    = 93,
+	VENDOR28    = 95,
+	VENDOR29    = 97,
+	VENDOR30    = 99,
+	VENDOR31    = 101,
+	VENDOR32    = 103,
+	VENDOR33    = 105,
+	VENDOR34    = 107,
+	VENDOR35    = 109,
+	VENDOR36    = 111,
+	VENDOR37    = 113,
+	VENDOR38    = 115,
+	VENDOR39    = 117,
+	VENDOR40    = 119,
+	VENDOR41    = 121,
+	VENDOR42    = 123,
+	VENDOR43    = 125,
+	VENDOR44    = 127,
+	VENDOR45    = 129,
+	VENDOR46    = 131,
+	VENDOR47    = 133,
+}
+
+// #define PF_*
+Protocol_Family :: enum sa_family_t {
+	UNSPEC    = cast(sa_family_t)Address_Family.UNSPEC,
+	LOCAL     = cast(sa_family_t)Address_Family.LOCAL,
+	UNIX      = LOCAL,
+	INET      = cast(sa_family_t)Address_Family.INET,
+	IMPLINK   = cast(sa_family_t)Address_Family.IMPLINK,
+	PUP       = cast(sa_family_t)Address_Family.PUP,
+	CHAOS     = cast(sa_family_t)Address_Family.CHAOS,
+	NETBIOS   = cast(sa_family_t)Address_Family.NETBIOS,
+	ISO       = cast(sa_family_t)Address_Family.ISO,
+	OSI       = cast(sa_family_t)Address_Family.ISO,
+	ECMA      = cast(sa_family_t)Address_Family.ECMA,
+	DATAKIT   = cast(sa_family_t)Address_Family.DATAKIT,
+	CCITT     = cast(sa_family_t)Address_Family.CCITT,
+	SNA       = cast(sa_family_t)Address_Family.SNA,
+	DECnet    = cast(sa_family_t)Address_Family.DECnet,
+	DLI       = cast(sa_family_t)Address_Family.DLI,
+	LAT       = cast(sa_family_t)Address_Family.LAT,
+	HYLINK    = cast(sa_family_t)Address_Family.HYLINK,
+	APPLETALK = cast(sa_family_t)Address_Family.APPLETALK,
+	ROUTE     = cast(sa_family_t)Address_Family.ROUTE,
+	LINK      = cast(sa_family_t)Address_Family.LINK,
+	XTP       = cast(sa_family_t)Address_Family.PSEUDO_XTP,
+	COIP      = cast(sa_family_t)Address_Family.COIP,
+	CNT       = cast(sa_family_t)Address_Family.CNT,
+	SIP       = cast(sa_family_t)Address_Family.SIP,
+	IPX       = cast(sa_family_t)Address_Family.IPX,
+	RTIP      = cast(sa_family_t)Address_Family.PSEUDO_RTIP,
+	PIP       = cast(sa_family_t)Address_Family.PSEUDO_PIP,
+	ISDN      = cast(sa_family_t)Address_Family.ISDN,
+	KEY       = cast(sa_family_t)Address_Family.PSEUDO_KEY,
+	INET6     = cast(sa_family_t)Address_Family.INET6,
+	NATM      = cast(sa_family_t)Address_Family.NATM,
+	ATM       = cast(sa_family_t)Address_Family.ATM,
+	NETGRAPH  = cast(sa_family_t)Address_Family.NETGRAPH,
+	SLOW      = cast(sa_family_t)Address_Family.SLOW,
+	SCLUSTER  = cast(sa_family_t)Address_Family.SCLUSTER,
+	ARP       = cast(sa_family_t)Address_Family.ARP,
+	BLUETOOTH = cast(sa_family_t)Address_Family.BLUETOOTH,
+	IEEE80211 = cast(sa_family_t)Address_Family.IEEE80211,
+	NETLINK   = cast(sa_family_t)Address_Family.NETLINK,
+	INET_SDP  = cast(sa_family_t)Address_Family.INET_SDP,
+	INET6_SDP = cast(sa_family_t)Address_Family.INET6_SDP,
+	DIVERT    = cast(sa_family_t)Address_Family.DIVERT,
+	MAX       = cast(sa_family_t)Address_Family.MAX,
+}
+
+//
+// /etc/protocols
+//
+
+Protocol :: enum c.int {
+	IP              = 0,
+	ICMP            = 1,
+	IGMP            = 2,
+	GGP             = 3,
+	IP_ENCAP        = 4,
+	ST2             = 5,
+	TCP             = 6,
+	CBT             = 7,
+	EGP             = 8,
+	IGP             = 9,
+	BBN_RCC_MON     = 10,
+	NVP_II          = 11,
+	PUP             = 12,
+	ARGUS           = 13,
+	EMCON           = 14,
+	XNET            = 15,
+	CHAOS           = 16,
+	UDP             = 17,
+	MUX             = 18,
+	DCN_MEAS        = 19,
+	HMP             = 20,
+	PRM             = 21,
+	XNS_IDP         = 22,
+	TRUNK_1         = 23,
+	TRUNK_2         = 24,
+	LEAF_1          = 25,
+	LEAF_2          = 26,
+	RDP             = 27,
+	IRTP            = 28,
+	ISO_TP4         = 29,
+	NETBLT          = 30,
+	MFE_NSP         = 31,
+	MERIT_INP       = 32,
+	DCCP            = 33,
+	THREE_PC        = 34,
+	IDPR            = 35,
+	XTP             = 36,
+	DDP             = 37,
+	IDPR_CMTP       = 38,
+	TP_PlusPlus     = 39,
+	IL              = 40,
+	IPV6            = 41,
+	SDRP            = 42,
+	IPV6_ROUTE      = 43,
+	IPV6_FRAG       = 44,
+	IDRP            = 45,
+	RSVP            = 46,
+	GRE             = 47,
+	DSR             = 48,
+	BNA             = 49,
+	ESP             = 50,
+	AH              = 51,
+	I_NLSP          = 52,
+	SWIPE           = 53,
+	NARP            = 54,
+	MOBILE          = 55,
+	TLSP            = 56,
+	SKIP            = 57,
+	IPV6_ICMP       = 58,
+	IPV6_NONXT      = 59,
+	IPV6_OPTS       = 60,
+	CFTP            = 62,
+	SAT_EXPAK       = 64,
+	KRYPTOLAN       = 65,
+	RVD             = 66,
+	IPPC            = 67,
+	SAT_MON         = 69,
+	VISA            = 70,
+	IPCV            = 71,
+	CPNX            = 72,
+	CPHB            = 73,
+	WSN             = 74,
+	PVP             = 75,
+	BR_SAT_MON      = 76,
+	SUN_ND          = 77,
+	WB_MON          = 78,
+	WB_EXPAK        = 79,
+	ISO_IP          = 80,
+	VMTP            = 81,
+	SECURE_VMTP     = 82,
+	VINES           = 83,
+	TTP             = 84,
+	IPTM            = 84,
+	NSFNET_IGP      = 85,
+	DGP             = 86,
+	TCF             = 87,
+	EIGRP           = 88,
+	OSPFIGP         = 89,
+	Sprite_RPC      = 90,
+	LARP            = 91,
+	MTP             = 92,
+	AX_25           = 93,
+	IPIP            = 94,
+	MICP            = 95,
+	SCC_SP          = 96,
+	ETHERIP         = 97,
+	ENCAP           = 98,
+	GMTP            = 100,
+	IFMP            = 101,
+	PNNI            = 102,
+	PIM             = 103,
+	ARIS            = 104,
+	SCPS            = 105,
+	QNX             = 106,
+	A_N             = 107,
+	IPComp          = 108,
+	SNP             = 109,
+	Compaq_Peer     = 110,
+	IPX_in_IP       = 111,
+	CARP            = 112,
+	PGM             = 113,
+	L2TP            = 115,
+	DDX             = 116,
+	IATP            = 117,
+	STP             = 118,
+	SRP             = 119,
+	UTI             = 120,
+	SMP             = 121,
+	SM              = 122,
+	PTP             = 123,
+	ISIS            = 124,
+	FIRE            = 125,
+	CRTP            = 126,
+	CRUDP           = 127,
+	SSCOPMCE        = 128,
+	IPLT            = 129,
+	SPS             = 130,
+	PIPE            = 131,
+	SCTP            = 132,
+	FC              = 133,
+	RSVP_E2E_IGNORE = 134,
+	Mobility_Header = 135,
+	UDPLite         = 136,
+	MPLS_IN_IP      = 137,
+	MANET           = 138,
+	HIP             = 139,
+	SHIM6           = 140,
+	WESP            = 141,
+	ROHC            = 142,
+	PFSYNC          = 240,
+	DIVERT          = 258,
+}
+
+//
+// #include <sys/fcntl.h>
+//
+
+/*
+ * Constants used for fcntl(2)
+ */
+
+/* command values */
+// #define F_*
+File_Control_Command :: enum c.int {
+	DUPFD          = 0,  /* duplicate file descriptor */
+	GETFD          = 1,  /* get file descriptor flags */
+	SETFD          = 2,  /* set file descriptor flags */
+	GETFL          = 3,  /* get file status flags */
+	SETFL          = 4,  /* set file status flags */
+	GETOWN         = 5,  /* get SIGIO/SIGURG proc/pgrp */
+	SETOWN         = 6,  /* set SIGIO/SIGURG proc/pgrp */
+	OGETLK         = 7,  /* get record locking information */
+	OSETLK         = 8,  /* set record locking information */
+	OSETLKW        = 9,  /* F_SETLK; wait if blocked */
+	DUP2FD         = 10, /* duplicate file descriptor to arg */
+	GETLK          = 11, /* get record locking information */
+	SETLK          = 12, /* set record locking information */
+	SETLKW         = 13, /* F_SETLK; wait if blocked */
+	SETLK_REMOTE   = 14, /* debugging support for remote locks */
+	READAHEAD      = 15, /* read ahead */
+	RDAHEAD        = 16, /* Darwin compatible read ahead */
+	DUPFD_CLOEXEC  = 17, /* Like F_DUPFD, but FD_CLOEXEC is set */
+	DUP2FD_CLOEXEC = 18, /* Like F_DUP2FD, but FD_CLOEXEC is set */
+	ADD_SEALS      = 19,
+	GET_SEALS      = 20,
+	ISUNIONSTACK   = 21, /* Kludge for libc, don't use it. */
+	KINFO          = 22, /* Return kinfo_file for this fd */
+}
+
+/* Seals (F_ADD_SEALS, F_GET_SEALS). */
+// #define F_SEAL_*
+File_Seal_Index :: enum c.int {
+	SEAL   = 0, // 0x0001, /* Prevent adding sealings */
+	SHRINK = 1, // 0x0002, /* May not shrink */
+	GROW   = 2, // 0x0004, /* May not grow */
+	WRITE  = 3, // 0x0008, /* May not write */
+}
+
+File_Seals :: bit_set[File_Seal_Index; c.int]
+
+/* file descriptor flags (F_GETFD, F_SETFD) */
+FD_CLOEXEC :: 1 /* close-on-exec flag */
+
+/* record locking flags (F_GETLK, F_SETLK, F_SETLKW) */
+// #define F_*
+Record_Lock_Flag :: enum c.int {
+	RDLCK       = 1, /* shared or read lock */
+	UNLCK       = 2, /* unlock */
+	WRLCK       = 3, /* exclusive or write lock */
+	UNLCKSYS    = 4, /* purge locks for a given system ID */
+	CANCEL      = 5, /* cancel an async lock request */
+}
+
+// struct flock
+File_Lock :: struct {
+	 start:  off_t,            /* starting offset */
+	 len:    off_t,            /* len = 0 means until end of file */
+	 pid:    pid_t,            /* lock owner */
+	 type:   Record_Lock_Flag, /* lock type: read/write, etc. */
+	 whence: c.short,          /* type of l_start */
+	 sysid:  c.int,            /* remote system id or zero for local */
+}
+
+/*
+ * File status flags: these are used by open(2), fcntl(2).
+ * They are also used (indirectly) in the kernel file structure f_flags,
+ * which is a superset of the open/fcntl flags.  Open flags and f_flags
+ * are inter-convertible using OFLAGS(fflags) and FFLAGS(oflags).
+ * Open/fcntl flags begin with O_; kernel-internal flags begin with F.
+ */
+File_Status_Flag :: enum c.int {
+	/* open-only flags */
+	RDONLY          = 0x0000,     /* open for reading only */
+	WRONLY          = 0x0001,     /* open for writing only */
+	RDWR            = 0x0002,     /* open for reading and writing */
+	ACCMODE         = 0x0003,     /* mask for above modes */
+
+	/**/
+	NONBLOCK        = 0x0004,     /* no delay */
+	APPEND          = 0x0008,     /* set append mode */
+	SHLOCK          = 0x0010,     /* open with shared file lock */
+	EXLOCK          = 0x0020,     /* open with exclusive file lock */
+	ASYNC           = 0x0040,     /* signal pgrp when data ready */
+	FSYNC           = 0x0080,     /* synchronous writes */
+	SYNC            = 0x0080,     /* POSIX synonym for O_FSYNC */
+	NOFOLLOW        = 0x0100,     /* don't follow symlinks */
+	CREAT           = 0x0200,     /* create if nonexistent */
+	TRUNC           = 0x0400,     /* truncate to zero length */
+	EXCL            = 0x0800,     /* error if already exists */
+
+	/* Defined by POSIX 1003.1; BSD default, but must be distinct from O_RDONLY. */
+	NOCTTY          = 0x8000,     /* don't assign controlling terminal */
+
+	/* Attempt to bypass buffer cache */
+	DIRECT          = 0x00010000,
+
+	DIRECTORY       = 0x00020000, /* Fail if not directory */
+	EXEC            = 0x00040000, /* Open for execute only */
+	SEARCH          = EXEC,
+
+	/* Defined by POSIX 1003.1-2008; BSD default, but reserve for future use. */
+	TTY_INIT        = 0x00080000, /* Restore default termios attributes */
+
+	CLOEXEC         = 0x00100000,
+	VERIFY          = 0x00200000, /* open only after verification */
+	PATH            = 0x00400000, /* fd is only a path */
+	RESOLVE_BENEATH = 0x00800000, /* Do not allow name resolution to walk out of cwd */
+	DSYNC           = 0x01000000, /* POSIX data sync */
+	EMPTY_PATH      = 0x02000000,
+}
+
+File_Status_Index :: enum c.int {
+	// No RDONLY (0x00), as that is implied and also impossible to express in a bit_set.
+
+	// The comments below come from the documentation for `fcntl`.
+	WRONLY          = 0,
+	RDWR            = 1,
+
+	/* Non-blocking I/O; if no data is available to a read(2)
+	   system call, or if a write(2) operation would block, the
+	   read or write call returns -1 with the error EAGAIN. */
+	NONBLOCK        = 2,
+
+	/* Force each write to append at the end of file; corresponds
+	   to the O_APPEND flag of open(2). */
+	APPEND          = 3,
+
+	SHLOCK          = 4,
+	EXLOCK          = 5,
+
+	/* Enable the SIGIO signal to be sent to the process group when
+	   I/O is possible, e.g., upon availability of data to be read. */
+	ASYNC           = 6,
+
+	/* Enable synchronous writes.  Corresponds to the O_SYNC flag
+	   of open(2).  O_FSYNC is an historical synonym for O_SYNC. */
+	SYNC            = 7,
+
+	FSYNC           = 7,
+	NOFOLLOW        = 8,
+	CREAT           = 9,
+	TRUNC           = 10,
+	EXCL            = 11,
+
+	NOCTTY          = 15,
+
+	/* Minimize or eliminate the cache effects of reading and
+	   writing.  The system will attempt to avoid caching the data
+	   you read or write.  If it cannot avoid caching the data, it
+	   will minimize the impact the data has on the cache.  Use of
+	   this flag can drastically reduce performance if not used
+	   with care. */
+	DIRECT          = 16,
+
+	DIRECTORY       = 17,
+	EXEC            = 18,
+	TTY_INIT        = 19,
+	CLOEXEC         = 20,
+	VERIFY          = 21,
+	PATH            = 22,
+	RESOLVE_BENEATH = 23,
+
+	/* Enable synchronous data writes.  Corresponds to the O_DSYNC
+	   flag of open(2). */
+	DSYNC           = 24,
+
+	EMPTY_PATH      = 25,
+}
+
+File_Status_Flags :: bit_set[File_Status_Index; c.int]
+
+//
+// #include <sys/_sockaddr_storage.h>
+//
+
+@private _SS_MAXSIZE   :: 128
+@private _SS_ALIGNSIZE :: size_of(i64)
+@private _SS_PAD1SIZE  :: _SS_ALIGNSIZE - size_of(c.uchar) - size_of(Address_Family)
+@private _SS_PAD2SIZE  :: _SS_MAXSIZE - size_of(c.uchar) - size_of(Address_Family) - _SS_PAD1SIZE - _SS_ALIGNSIZE
+
+/*
+ * RFC 2553: protocol-independent placeholder for socket addresses
+ */
+// struct sockaddr_storage
+Socket_Address_Storage :: struct {
+	using _: Socket_Address_Header,
+	_pad1:  [_SS_PAD1SIZE]c.char,
+	_align: i64,                  /* force desired struct alignment */
+	_pad2:  [_SS_PAD2SIZE]c.char,
+}
+
+//
+// #include <sys/sysctl.h>
+//
+
+// MIB, or Management Information Base. Used in sysctl().
+MIB_Identifier :: enum c.int {
+	/*
+	* Top-level identifiers
+	*/
+	CTL_SYSCTL = 0,                          /* "magic" numbers */
+	CTL_KERN = 1,                            /* "high kernel": proc, limits */
+	CTL_VM = 2,                              /* virtual memory */
+	CTL_VFS = 3,                             /* filesystem, mount type is next */
+	CTL_NET = 4,                             /* network, see socket.h */
+	CTL_DEBUG = 5,                           /* debugging parameters */
+	CTL_HW = 6,                              /* generic cpu/io */
+	CTL_MACHDEP = 7,                         /* machine dependent */
+	CTL_USER = 8,                            /* user-level */
+	CTL_P1003_1B = 9,                        /* POSIX 1003.1B */
+
+	/*
+	* CTL_SYSCTL identifiers
+	*/
+	CTL_SYSCTL_DEBUG = 0,                    /* printf all nodes */
+	CTL_SYSCTL_NAME = 1,                     /* string name of OID */
+	CTL_SYSCTL_NEXT = 2,                     /* next OID, honoring CTLFLAG_SKIP */
+	CTL_SYSCTL_NAME2OID = 3,                 /* int array of name */
+	CTL_SYSCTL_OIDFMT = 4,                   /* OID's kind and format */
+	CTL_SYSCTL_OIDDESCR = 5,                 /* OID's description */
+	CTL_SYSCTL_OIDLABEL = 6,                 /* aggregation label */
+	CTL_SYSCTL_NEXTNOSKIP = 7,               /* next OID, ignoring CTLFLAG_SKIP */
+
+	/*
+	* CTL_KERN identifiers
+	*/
+	KERN_OSTYPE = 1,                         /* string: system version */
+	KERN_OSRELEASE = 2,                      /* string: system release */
+	KERN_OSREV = 3,                          /* int: system revision */
+	KERN_VERSION = 4,                        /* string: compile time info */
+	KERN_MAXVNODES = 5,                      /* int: max vnodes */
+	KERN_MAXPROC = 6,                        /* int: max processes */
+	KERN_MAXFILES = 7,                       /* int: max open files */
+	KERN_ARGMAX = 8,                         /* int: max arguments to exec */
+	KERN_SECURELVL = 9,                      /* int: system security level */
+	KERN_HOSTNAME = 10,                      /* string: hostname */
+	KERN_HOSTID = 11,                        /* int: host identifier */
+	KERN_CLOCKRATE = 12,                     /* struct: struct clockrate */
+	/* was: #define KERN_VNODE13; disabled in 2003 and removed in 2023 */
+	KERN_PROC = 14,                          /* struct: process entries */
+	KERN_FILE = 15,                          /* struct: file entries */
+	KERN_PROF = 16,                          /* node: kernel profiling info */
+	KERN_POSIX1 = 17,                        /* int: POSIX.1 version */
+	KERN_NGROUPS = 18,                       /* int: # of supplemental group ids */
+	KERN_JOB_CONTROL = 19,                   /* int: is job control available */
+	KERN_SAVED_IDS = 20,                     /* int: saved set-user/group-ID */
+	KERN_BOOTTIME = 21,                      /* struct: time kernel was booted */
+	KERN_NISDOMAINNAME = 22,                 /* string: YP domain name */
+	KERN_UPDATEINTERVAL = 23,                /* int: update process sleep time */
+	KERN_OSRELDATE = 24,                     /* int: kernel release date */
+	KERN_NTP_PLL = 25,                       /* node: NTP PLL control */
+	KERN_BOOTFILE = 26,                      /* string: name of booted kernel */
+	KERN_MAXFILESPERPROC = 27,               /* int: max open files per proc */
+	KERN_MAXPROCPERUID = 28,                 /* int: max processes per uid */
+	KERN_DUMPDEV = 29,                       /* struct cdev *: device to dump on */
+	KERN_IPC = 30,                           /* node: anything related to IPC */
+	KERN_DUMMY = 31,                         /* unused */
+	KERN_PS_STRINGS = 32,                    /* int: address of PS_STRINGS */
+	KERN_USRSTACK = 33,                      /* int: address of USRSTACK */
+	KERN_LOGSIGEXIT = 34,                    /* int: do we log sigexit procs? */
+	KERN_IOV_MAX = 35,                       /* int: value of UIO_MAXIOV */
+	KERN_HOSTUUID = 36,                      /* string: host UUID identifier */
+	KERN_ARND = 37,                          /* int: from arc4rand() */
+	KERN_MAXPHYS = 38,                       /* int: MAXPHYS value */
+	KERN_LOCKF = 39,                         /* struct: lockf reports */
+	/*
+	* KERN_PROC subtypes
+	*/
+	KERN_PROC_ALL = 0,                       /* everything */
+	KERN_PROC_PID = 1,                       /* by process id */
+	KERN_PROC_PGRP = 2,                      /* by process group id */
+	KERN_PROC_SESSION = 3,                   /* by session of pid */
+	KERN_PROC_TTY = 4,                       /* by controlling tty */
+	KERN_PROC_UID = 5,                       /* by effective uid */
+	KERN_PROC_RUID = 6,                      /* by real uid */
+	KERN_PROC_ARGS = 7,                      /* get/set arguments/proctitle */
+	KERN_PROC_PROC = 8,                      /* only return procs */
+	KERN_PROC_SV_NAME = 9,                   /* get syscall vector name */
+	KERN_PROC_RGID = 10,                     /* by real group id */
+	KERN_PROC_GID = 11,                      /* by effective group id */
+	KERN_PROC_PATHNAME = 12,                 /* path to executable */
+	KERN_PROC_OVMMAP = 13,                   /* Old VM map entries for process */
+	KERN_PROC_OFILEDESC = 14,                /* Old file descriptors for process */
+	KERN_PROC_KSTACK = 15,                   /* Kernel stacks for process */
+	KERN_PROC_INC_THREAD = 0x10,             /* modifier for pid, pgrp, tty, uid, ruid, gid, rgid and proc. This effectively uses 16-31 */
+	KERN_PROC_VMMAP = 32,                    /* VM map entries for process */
+	KERN_PROC_FILEDESC = 33,                 /* File descriptors for process */
+	KERN_PROC_GROUPS = 34,                   /* process groups */
+	KERN_PROC_ENV = 35,                      /* get environment */
+	KERN_PROC_AUXV = 36,                     /* get ELF auxiliary vector */
+	KERN_PROC_RLIMIT = 37,                   /* process resource limits */
+	KERN_PROC_PS_STRINGS = 38,               /* get ps_strings location */
+	KERN_PROC_UMASK = 39,                    /* process umask */
+	KERN_PROC_OSREL = 40,                    /* osreldate for process binary */
+	KERN_PROC_SIGTRAMP = 41,                 /* signal trampoline location */
+	KERN_PROC_CWD = 42,                      /* process current working directory */
+	KERN_PROC_NFDS = 43,                     /* number of open file descriptors */
+	KERN_PROC_SIGFASTBLK = 44,               /* address of fastsigblk magic word */
+	KERN_PROC_VM_LAYOUT = 45,                /* virtual address space layout info */
+
+	/*
+	* KERN_IPC identifiers
+	*/
+	KIPC_MAXSOCKBUF = 1,                     /* int: max size of a socket buffer */
+	KIPC_SOCKBUF_WASTE = 2,                  /* int: wastage factor in sockbuf */
+	KIPC_SOMAXCONN = 3,                      /* int: max length of connection q */
+	KIPC_MAX_LINKHDR = 4,                    /* int: max length of link header */
+	KIPC_MAX_PROTOHDR = 5,                   /* int: max length of network header */
+	KIPC_MAX_HDR = 6,                        /* int: max total length of headers */
+	KIPC_MAX_DATALEN = 7,                    /* int: max length of data? */
+
+	/*
+	 * Definitions for network related sysctl, CTL_NET.
+	 *
+	 * Second level is protocol family.
+	 * Third level is protocol number.
+	 *
+	 * Further levels are defined by the individual families.
+	 */
+
+	/*
+	 * PF_ROUTE - Routing table
+	 *
+	 * Three additional levels are defined:
+	 *      Fourth: address family, 0 is wildcard
+	 *      Fifth: type of info, defined below
+	 *      Sixth: flag(s) to mask with for NET_RT_FLAGS
+	 */
+	NET_RT_DUMP = 1,                         /* dump; may limit to a.f. */
+	NET_RT_FLAGS = 2,                        /* by flags, e.g. RESOLVING */
+	NET_RT_IFLIST = 3,                       /* survey interface list */
+	NET_RT_IFMALIST = 4,                     /* return multicast address list */
+	NET_RT_IFLISTL = 5,                      /* Survey interface list, using 'l'en versions of msghdr structs. */
+	NET_RT_NHOP = 6,                         /* dump routing nexthops */
+	NET_RT_NHGRP = 7,                        /* dump routing nexthop groups */
+
+	/*
+	* CTL_HW identifiers
+	*/
+	HW_MACHINE = 1,                          /* string: machine class */
+	HW_MODEL = 2,                            /* string: specific machine model */
+	HW_NCPU = 3,                             /* int: number of cpus */
+	HW_BYTEORDER = 4,                        /* int: machine byte order */
+	HW_PHYSMEM = 5,                          /* int: total memory */
+	HW_USERMEM = 6,                          /* int: non-kernel memory */
+	HW_PAGESIZE = 7,                         /* int: software page size */
+	HW_DISKNAMES = 8,                        /* strings: disk drive names */
+	HW_DISKSTATS = 9,                        /* struct: diskstats[] */
+	HW_FLOATINGPT = 10,                      /* int: has HW floating point? */
+	HW_MACHINE_ARCH = 11,                    /* string: machine architecture */
+	HW_REALMEM = 12,                         /* int: 'real' memory */
+
+	/*
+	* CTL_USER definitions
+	*/
+	USER_CS_PATH = 1,                        /* string: _CS_PATH */
+	USER_BC_BASE_MAX = 2,                    /* int: BC_BASE_MAX */
+	USER_BC_DIM_MAX = 3,                     /* int: BC_DIM_MAX */
+	USER_BC_SCALE_MAX = 4,                   /* int: BC_SCALE_MAX */
+	USER_BC_STRING_MAX = 5,                  /* int: BC_STRING_MAX */
+	USER_COLL_WEIGHTS_MAX = 6,               /* int: COLL_WEIGHTS_MAX */
+	USER_EXPR_NEST_MAX = 7,                  /* int: EXPR_NEST_MAX */
+	USER_LINE_MAX = 8,                       /* int: LINE_MAX */
+	USER_RE_DUP_MAX = 9,                     /* int: RE_DUP_MAX */
+	USER_POSIX2_VERSION = 10,                /* int: POSIX2_VERSION */
+	USER_POSIX2_C_BIND = 11,                 /* int: POSIX2_C_BIND */
+	USER_POSIX2_C_DEV = 12,                  /* int: POSIX2_C_DEV */
+	USER_POSIX2_CHAR_TERM = 13,              /* int: POSIX2_CHAR_TERM */
+	USER_POSIX2_FORT_DEV = 14,               /* int: POSIX2_FORT_DEV */
+	USER_POSIX2_FORT_RUN = 15,               /* int: POSIX2_FORT_RUN */
+	USER_POSIX2_LOCALEDEF = 16,              /* int: POSIX2_LOCALEDEF */
+	USER_POSIX2_SW_DEV = 17,                 /* int: POSIX2_SW_DEV */
+	USER_POSIX2_UPE = 18,                    /* int: POSIX2_UPE */
+	USER_STREAM_MAX = 19,                    /* int: POSIX2_STREAM_MAX */
+	USER_TZNAME_MAX = 20,                    /* int: POSIX2_TZNAME_MAX */
+	USER_LOCALBASE = 21,                     /* string: _PATH_LOCALBASE */
+
+	CTL_P1003_1B_ASYNCHRONOUS_IO = 1,        /* boolean */
+	CTL_P1003_1B_MAPPED_FILES = 2,           /* boolean */
+	CTL_P1003_1B_MEMLOCK = 3,                /* boolean */
+	CTL_P1003_1B_MEMLOCK_RANGE = 4,          /* boolean */
+	CTL_P1003_1B_MEMORY_PROTECTION = 5,      /* boolean */
+	CTL_P1003_1B_MESSAGE_PASSING = 6,        /* boolean */
+	CTL_P1003_1B_PRIORITIZED_IO = 7,         /* boolean */
+	CTL_P1003_1B_PRIORITY_SCHEDULING = 8,    /* boolean */
+	CTL_P1003_1B_REALTIME_SIGNALS = 9,       /* boolean */
+	CTL_P1003_1B_SEMAPHORES = 10,            /* boolean */
+	CTL_P1003_1B_FSYNC = 11,                 /* boolean */
+	CTL_P1003_1B_SHARED_MEMORY_OBJECTS = 12, /* boolean */
+	CTL_P1003_1B_SYNCHRONIZED_IO = 13,       /* boolean */
+	CTL_P1003_1B_TIMERS = 14,                /* boolean */
+	CTL_P1003_1B_AIO_LISTIO_MAX = 15,        /* int */
+	CTL_P1003_1B_AIO_MAX = 16,               /* int */
+	CTL_P1003_1B_AIO_PRIO_DELTA_MAX = 17,    /* int */
+	CTL_P1003_1B_DELAYTIMER_MAX = 18,        /* int */
+	CTL_P1003_1B_MQ_OPEN_MAX = 19,           /* int */
+	CTL_P1003_1B_PAGESIZE = 20,              /* int */
+	CTL_P1003_1B_RTSIG_MAX = 21,             /* int */
+	CTL_P1003_1B_SEM_NSEMS_MAX = 22,         /* int */
+	CTL_P1003_1B_SEM_VALUE_MAX = 23,         /* int */
+	CTL_P1003_1B_SIGQUEUE_MAX = 24,          /* int */
+	CTL_P1003_1B_TIMER_MAX = 25,             /* int */
+}
+
+//
+// #include <net/route.h>
+//
+
+// struct rt_metrics
+Route_Metrics :: struct {
+	locks:    c.ulong,    /* Kernel must leave these values alone */
+	mtu:      c.ulong,    /* MTU for this path */
+	hopcount: c.ulong,    /* max hops expected */
+	expire:   c.ulong,    /* lifetime for route, e.g. redirect */
+	recvpipe: c.ulong,    /* inbound delay-bandwidth product */
+	sendpipe: c.ulong,    /* outbound delay-bandwidth product */
+	ssthresh: c.ulong,    /* outbound gateway buffer limit */
+	rtt:      c.ulong,    /* estimated round trip time */
+	rttvar:   c.ulong,    /* estimated rtt variance */
+	pksent:   c.ulong,    /* packets sent using this route */
+	weight:   c.ulong,    /* route weight */
+	nhidx:    c.ulong,    /* route nexhop index */
+	filler:   [2]c.ulong, /* will be used for T/TCP later */
+}
+
+// struct rt_msghdr
+Route_Message_Header :: struct {
+	msglen:  c.ushort,           /* to skip over non-understood messages */
+	version: c.uchar,            /* future binary compatibility */
+	type:    Route_Message_Type, /* message type */
+	index:   c.ushort,           /* index for associated ifp */
+	_spare1: c.ushort,
+	flags:   c.int,              /* flags, incl. kern & message, e.g. DONE */
+	addrs:   c.int,              /* bitmask identifying sockaddrs in msg */
+	pid:     pid_t,              /* identify sender */
+	seq:     c.int,              /* for sender to identify action */
+	errno:   c.int,              /* why failed */
+	fmask:   c.int,              /* bitmask used in RTM_CHANGE message */
+	inits:   c.ulong,            /* which metrics we are initializing */
+	rmx:     Route_Metrics,      /* metrics themselves */
+}
+
+RTM_VERSION :: 5 /* Up the ante and ignore older versions */
+
+/*
+ * Message types.
+ *
+ * The format for each message is annotated below using the following
+ * identifiers:
+ *
+ * (1) struct rt_msghdr
+ * (2) struct ifa_msghdr
+ * (3) struct if_msghdr
+ * (4) struct ifma_msghdr
+ * (5) struct if_announcemsghdr
+ *
+ */
+// #define RTM_*
+Route_Message_Type :: enum c.uchar {
+	ADD        = 0x1,  /* (1) Add Route */
+	DELETE     = 0x2,  /* (1) Delete Route */
+	CHANGE     = 0x3,  /* (1) Change Metrics or flags */
+	GET        = 0x4,  /* (1) Report Metrics */
+	LOSING     = 0x5,  /* (1) Kernel Suspects Partitioning */
+	REDIRECT   = 0x6,  /* (1) Told to use different route */
+	MISS       = 0x7,  /* (1) Lookup failed on this address */
+	LOCK       = 0x8,  /* (1) fix specified metrics */
+	        /* = 0x9  */
+	        /* = 0xa  */
+	RESOLVE    = 0xb,  /* (1) req to resolve dst to LL addr */
+	NEWADDR    = 0xc,  /* (2) address being added to iface */
+	DELADDR    = 0xd,  /* (2) address being removed from iface */
+	IFINFO     = 0xe,  /* (3) iface going up/down etc. */
+	NEWMADDR   = 0xf,  /* (4) mcast group membership being added to if */
+	DELMADDR   = 0x10, /* (4) mcast group membership being deleted */
+	IFANNOUNCE = 0x11, /* (5) iface arrival/departure */
+	IEEE80211  = 0x12, /* (5) IEEE80211 wireless event */
+}
+
+/*
+ * Bitmask values for rtm_addrs.
+ */
+// #define RTA_*
+Route_Address_Flag :: enum c.int {
+	DST     = 0x1,  /* destination sockaddr present */
+	GATEWAY = 0x2,  /* gateway sockaddr present */
+	NETMASK = 0x4,  /* netmask sockaddr present */
+	GENMASK = 0x8,  /* cloning mask sockaddr present */
+	IFP     = 0x10, /* interface name sockaddr present */
+	IFA     = 0x20, /* interface addr sockaddr present */
+	AUTHOR  = 0x40, /* sockaddr for author of redirect */
+	BRD     = 0x80, /* for NEWADDR, broadcast or p-p dest addr */
+}
+
+/*
+ * Index offsets for sockaddr array for alternate internal encoding.
+ */
+// #define RTAX_*
+Route_Address_Index :: enum c.int {
+	DST     = 0, /* destination sockaddr present */
+	GATEWAY = 1, /* gateway sockaddr present */
+	NETMASK = 2, /* netmask sockaddr present */
+	GENMASK = 3, /* cloning mask sockaddr present */
+	IFP     = 4, /* interface name sockaddr present */
+	IFA     = 5, /* interface addr sockaddr present */
+	AUTHOR  = 6, /* sockaddr for author of redirect */
+	BRD     = 7, /* for NEWADDR, broadcast or p-p dest addr */
+	MAX     = 8, /* size of array to allocate */
+}
+
+// The value stored in rtm_addrs and similar (ifm_addrs, etc.)
+Route_Address_Flags :: bit_set[Route_Address_Index; c.int]
+
+//
+// #include <net/if.h>
+//
+
+/*
+ * Values for if_link_state.
+ */
+// #define LINK_STATE_*
+Link_State :: enum u8 {
+	UNKNOWN = 0, /* link invalid/unknown */
+	DOWN    = 1, /* link is down */
+	UP      = 2, /* link is up */
+}
+
+/*
+ * Structure describing information about an interface
+ * which may be of interest to management entities.
+ */
+// struct if_data
+Interface_Data :: struct {
+	/* generic interface information */
+	type:       u8,         /* ethernet, tokenring, etc */
+	physical:   u8,         /* e.g., AUI, Thinnet, 10base-T, etc */
+	addrlen:    u8,         /* media address length */
+	hdrlen:     u8,         /* media header length */
+	link_state: Link_State, /* current link state */
+	vhid:       u8,         /* carp vhid */
+	datalen:    u16,        /* length of this data struct */
+	mtu:        u32,        /* maximum transmission unit */
+	metric:     u32,        /* routing metric (external only) */
+	baudrate:   u64,        /* linespeed */
+	/* volatile statistics */
+	ipackets:   u64,        /* packets received on interface */
+	ierrors:    u64,        /* input errors on interface */
+	opackets:   u64,        /* packets sent on interface */
+	oerrors:    u64,        /* output errors on interface */
+	collisions: u64,        /* collisions on csma interfaces */
+	ibytes:     u64,        /* total number of octets received */
+	obytes:     u64,        /* total number of octets sent */
+	imcasts:    u64,        /* packets received via multicast */
+	omcasts:    u64,        /* packets sent via multicast */
+	iqdrops:    u64,        /* dropped on input */
+	oqdrops:    u64,        /* dropped on output */
+	noproto:    u64,        /* destined for unsupported protocol */
+	hwassist:   u64,        /* HW offload capabilities, see IFCAP */
+
+	/* Unions are here to make sizes MI. */
+	_epoch: struct #raw_union { /* uptime at attach or stat reset */
+		tt: time_t,
+		ph: u64,
+	},
+
+	_lastchange: struct #raw_union { /* time of last administrative change */
+		tv: timeval,
+		ph: struct {
+			ph1: u64,
+			ph2: u64,
+		},
+	},
+}
+
+/*
+ * The 'l' version shall be used by new interfaces, like NET_RT_IFLISTL.  It is
+ * extensible after ifm_data_off or within ifm_data.  Both the if_msghdr and
+ * if_data now have a member field detailing the struct length in addition to
+ * the routing message length.  Macros are provided to find the start of
+ * ifm_data and the start of the socket address strucutres immediately following
+ * struct if_msghdrl given a pointer to struct if_msghdrl.
+ */
+// struct if_msghdrl
+Interface_Message_Header_Len :: struct {
+	msglen:   c.ushort,            /* to skip over non-understood messages */
+	version:  c.uchar,             /* future binary compatibility */
+	type:     c.uchar,             /* message type */
+	addrs:    Route_Address_Flags, /* like rtm_addrs */
+	flags:    c.int,               /* value of if_flags */
+	index:    c.ushort,            /* index for associated ifp */
+	_spare1:  c.ushort,            /* spare space to grow if_index, see if_var.h */
+	len:      c.ushort,            /* length of if_msghdrl incl. if_data */
+	data_off: c.ushort,            /* offset of if_data from beginning */
+	_spare2:  c.int,
+	data:     Interface_Data,      /* statistics and other data about if */
+}
+
+/*
+ * The 'l' version shall be used by new interfaces, like NET_RT_IFLISTL.  It is
+ * extensible after ifam_metric or within ifam_data.  Both the ifa_msghdrl and
+ * if_data now have a member field detailing the struct length in addition to
+ * the routing message length.  Macros are provided to find the start of
+ * ifm_data and the start of the socket address strucutres immediately following
+ * struct ifa_msghdrl given a pointer to struct ifa_msghdrl.
+ */
+// struct ifa_msghdrl
+Interface_Address_Message_Header_Len :: struct {
+	msglen:   c.ushort,            /* to skip over non-understood messages */
+	version:  c.uchar,             /* future binary compatibility */
+	type:     c.uchar,             /* message type */
+	addrs:    Route_Address_Flags, /* like rtm_addrs */
+	flags:    c.int,               /* value of ifa_flags */
+	index:    c.ushort,            /* index for associated ifp */
+	_spare1:  c.ushort,            /* spare space to grow if_index, see if_var.h */
+	len:      c.ushort,            /* length of ifa_msghdrl incl. if_data */
+	data_off: c.ushort,            /* offset of if_data from beginning */
+	metric:   c.int,               /* value of ifa_ifp->if_metric */
+	data:     Interface_Data,      /* statistics and other data about if or address */
+}
+
+//
+// #include <net/if_types.h>
+//
+
+// enum ifType
+Interface_Type :: enum c.uchar {
+	OTHER                         = 0x1,  /* none of the following */
+	ARPA_1822                     = 0x2,  /* old-style arpanet imp */
+	HDH1822                       = 0x3,  /* HDH arpanet imp */
+	X25DDN                        = 0x4,  /* x25 to imp */
+	X25                           = 0x5,  /* PDN X25 interface (RFC877) */
+	ETHER                         = 0x6,  /* Ethernet CSMA/CD */
+	ISO88023                      = 0x7,  /* CMSA/CD */
+	ISO88024                      = 0x8,  /* Token Bus */
+	ISO88025                      = 0x9,  /* Token Ring */
+	ISO88026                      = 0xa,  /* MAN */
+	STARLAN                       = 0xb,
+	P10                           = 0xc,  /* Proteon 10MBit ring */
+	P80                           = 0xd,  /* Proteon 80MBit ring */
+	HY                            = 0xe,  /* Hyperchannel */
+	FDDI                          = 0xf,
+	LAPB                          = 0x10,
+	SDLC                          = 0x11,
+	T1                            = 0x12,
+	CEPT                          = 0x13, /* E1 - european T1 */
+	ISDNBASIC                     = 0x14,
+	ISDNPRIMARY                   = 0x15,
+	PTPSERIAL                     = 0x16, /* Proprietary PTP serial */
+	PPP                           = 0x17, /* RFC 1331 */
+	LOOP                          = 0x18, /* loopback */
+	EON                           = 0x19, /* ISO over IP */
+	XETHER                        = 0x1a, /* obsolete 3MB experimental ethernet */
+	NSIP                          = 0x1b, /* XNS over IP */
+	SLIP                          = 0x1c, /* IP over generic TTY */
+	ULTRA                         = 0x1d, /* Ultra Technologies */
+	DS3                           = 0x1e, /* Generic T3 */
+	SIP                           = 0x1f, /* SMDS */
+	FRELAY                        = 0x20, /* Frame Relay DTE only */
+	RS232                         = 0x21,
+	PARA                          = 0x22, /* parallel-port */
+	ARCNET                        = 0x23,
+	ARCNETPLUS                    = 0x24,
+	ATM                           = 0x25, /* ATM cells */
+	MIOX25                        = 0x26,
+	SONET                         = 0x27, /* SONET or SDH */
+	X25PLE                        = 0x28,
+	ISO88022LLC                   = 0x29,
+	LOCALTALK                     = 0x2a,
+	SMDSDXI                       = 0x2b,
+	FRELAYDCE                     = 0x2c, /* Frame Relay DCE */
+	V35                           = 0x2d,
+	HSSI                          = 0x2e,
+	HIPPI                         = 0x2f,
+	MODEM                         = 0x30, /* Generic Modem */
+	AAL5                          = 0x31, /* AAL5 over ATM */
+	SONETPATH                     = 0x32,
+	SONETVT                       = 0x33,
+	SMDSICIP                      = 0x34, /* SMDS InterCarrier Interface */
+	PROPVIRTUAL                   = 0x35, /* Proprietary Virtual/internal */
+	PROPMUX                       = 0x36, /* Proprietary Multiplexing */
+	IEEE80212                     = 0x37, /* 100BaseVG */
+	FIBRECHANNEL                  = 0x38, /* Fibre Channel */
+	HIPPIINTERFACE                = 0x39, /* HIPPI interfaces	 */
+	FRAMERELAYINTERCONNECT        = 0x3a, /* Obsolete, use 0x20 either 0x2c */
+	AFLANE8023                    = 0x3b, /* ATM Emulated LAN for 802.3 */
+	AFLANE8025                    = 0x3c, /* ATM Emulated LAN for 802.5 */
+	CCTEMUL                       = 0x3d, /* ATM Emulated circuit		  */
+	FASTETHER                     = 0x3e, /* Fast Ethernet (100BaseT) */
+	ISDN                          = 0x3f, /* ISDN and X.25	    */
+	V11                           = 0x40, /* CCITT V.11/X.21		*/
+	V36                           = 0x41, /* CCITT V.36			*/
+	G703AT64K                     = 0x42, /* CCITT G703 at 64Kbps */
+	G703AT2MB                     = 0x43, /* Obsolete see DS1-MIB */
+	QLLC                          = 0x44, /* SNA QLLC			*/
+	FASTETHERFX                   = 0x45, /* Fast Ethernet (100BaseFX)	*/
+	CHANNEL                       = 0x46, /* channel			*/
+	IEEE80211                     = 0x47, /* radio spread spectrum (unused) */
+	IBM370PARCHAN                 = 0x48, /* IBM System 360/370 OEMI Channel */
+	ESCON                         = 0x49, /* IBM Enterprise Systems Connection */
+	DLSW                          = 0x4a, /* Data Link Switching */
+	ISDNS                         = 0x4b, /* ISDN S/T interface */
+	ISDNU                         = 0x4c, /* ISDN U interface */
+	LAPD                          = 0x4d, /* Link Access Protocol D */
+	IPSWITCH                      = 0x4e, /* IP Switching Objects */
+	RSRB                          = 0x4f, /* Remote Source Route Bridging */
+	ATMLOGICAL                    = 0x50, /* ATM Logical Port */
+	DS0                           = 0x51, /* Digital Signal Level 0 */
+	DS0BUNDLE                     = 0x52, /* group of ds0s on the same ds1 */
+	BSC                           = 0x53, /* Bisynchronous Protocol */
+	ASYNC                         = 0x54, /* Asynchronous Protocol */
+	CNR                           = 0x55, /* Combat Net Radio */
+	ISO88025DTR                   = 0x56, /* ISO 802.5r DTR */
+	EPLRS                         = 0x57, /* Ext Pos Loc Report Sys */
+	ARAP                          = 0x58, /* Appletalk Remote Access Protocol */
+	PROPCNLS                      = 0x59, /* Proprietary Connectionless Protocol*/
+	HOSTPAD                       = 0x5a, /* CCITT-ITU X.29 PAD Protocol */
+	TERMPAD                       = 0x5b, /* CCITT-ITU X.3 PAD Facility */
+	FRAMERELAYMPI                 = 0x5c, /* Multiproto Interconnect over FR */
+	X213                          = 0x5d, /* CCITT-ITU X213 */
+	ADSL                          = 0x5e, /* Asymmetric Digital Subscriber Loop */
+	RADSL                         = 0x5f, /* Rate-Adapt. Digital Subscriber Loop*/
+	SDSL                          = 0x60, /* Symmetric Digital Subscriber Loop */
+	VDSL                          = 0x61, /* Very H-Speed Digital Subscrib. Loop*/
+	ISO88025CRFPINT               = 0x62, /* ISO 802.5 CRFP */
+	MYRINET                       = 0x63, /* Myricom Myrinet */
+	VOICEEM                       = 0x64, /* voice recEive and transMit */
+	VOICEFXO                      = 0x65, /* voice Foreign Exchange Office */
+	VOICEFXS                      = 0x66, /* voice Foreign Exchange Station */
+	VOICEENCAP                    = 0x67, /* voice encapsulation */
+	VOICEOVERIP                   = 0x68, /* voice over IP encapsulation */
+	ATMDXI                        = 0x69, /* ATM DXI */
+	ATMFUNI                       = 0x6a, /* ATM FUNI */
+	ATMIMA                        = 0x6b, /* ATM IMA		      */
+	PPPMULTILINKBUNDLE            = 0x6c, /* PPP Multilink Bundle */
+	IPOVERCDLC                    = 0x6d, /* IBM ipOverCdlc */
+	IPOVERCLAW                    = 0x6e, /* IBM Common Link Access to Workstn */
+	STACKTOSTACK                  = 0x6f, /* IBM stackToStack */
+	VIRTUALIPADDRESS              = 0x70, /* IBM VIPA */
+	MPC                           = 0x71, /* IBM multi-protocol channel support */
+	IPOVERATM                     = 0x72, /* IBM ipOverAtm */
+	ISO88025FIBER                 = 0x73, /* ISO 802.5j Fiber Token Ring */
+	TDLC                          = 0x74, /* IBM twinaxial data link control */
+	GIGABITETHERNET               = 0x75, /* Gigabit Ethernet */
+	HDLC                          = 0x76, /* HDLC */
+	LAPF                          = 0x77, /* LAP F */
+	V37                           = 0x78, /* V.37 */
+	X25MLP                        = 0x79, /* Multi-Link Protocol */
+	X25HUNTGROUP                  = 0x7a, /* X25 Hunt Group */
+	TRANSPHDLC                    = 0x7b, /* Transp HDLC */
+	INTERLEAVE                    = 0x7c, /* Interleave channel */
+	FAST                          = 0x7d, /* Fast channel */
+	IP                            = 0x7e, /* IP (for APPN HPR in IP networks) */
+	DOCSCABLEMACLAYER             = 0x7f, /* CATV Mac Layer */
+	DOCSCABLEDOWNSTREAM           = 0x80, /* CATV Downstream interface */
+	DOCSCABLEUPSTREAM             = 0x81, /* CATV Upstream interface */
+	A12MPPSWITCH                  = 0x82, /* Avalon Parallel Processor */
+	TUNNEL                        = 0x83, /* Encapsulation interface */
+	COFFEE                        = 0x84, /* coffee pot */
+	CES                           = 0x85, /* Circiut Emulation Service */
+	ATMSUBINTERFACE               = 0x86, /* (x)  ATM Sub Interface */
+	L2VLAN                        = 0x87, /* Layer 2 Virtual LAN using 802.1Q */
+	L3IPVLAN                      = 0x88, /* Layer 3 Virtual LAN - IP Protocol */
+	L3IPXVLAN                     = 0x89, /* Layer 3 Virtual LAN - IPX Prot. */
+	DIGITALPOWERLINE              = 0x8a, /* IP over Power Lines */
+	MEDIAMAILOVERIP               = 0x8b, /* (xxx)  Multimedia Mail over IP */
+	DTM                           = 0x8c, /* Dynamic synchronous Transfer Mode */
+	DCN                           = 0x8d, /* Data Communications Network */
+	IPFORWARD                     = 0x8e, /* IP Forwarding Interface */
+	MSDSL                         = 0x8f, /* Multi-rate Symmetric DSL */
+	IEEE1394                      = 0x90, /* IEEE1394 High Performance SerialBus*/
+	IFGSN                         = 0x91, /* HIPPI-6400 */
+	DVBRCCMACLAYER                = 0x92, /* DVB-RCC MAC Layer */
+	DVBRCCDOWNSTREAM              = 0x93, /* DVB-RCC Downstream Channel */
+	DVBRCCUPSTREAM                = 0x94, /* DVB-RCC Upstream Channel */
+	ATMVIRTUAL                    = 0x95, /* ATM Virtual Interface */
+	MPLSTUNNEL                    = 0x96, /* MPLS Tunnel Virtual Interface */
+	SRP                           = 0x97, /* Spatial Reuse Protocol */
+	VOICEOVERATM                  = 0x98, /* Voice over ATM */
+	VOICEOVERFRAMERELAY           = 0x99, /* Voice Over Frame Relay */
+	IDSL                          = 0x9a, /* Digital Subscriber Loop over ISDN */
+	COMPOSITELINK                 = 0x9b, /* Avici Composite Link Interface */
+	SS7SIGLINK                    = 0x9c, /* SS7 Signaling Link */
+	PROPWIRELESSP2P               = 0x9d, /* Prop. P2P wireless interface */
+	FRFORWARD                     = 0x9e, /* Frame forward Interface */
+	RFC1483                       = 0x9f, /* Multiprotocol over ATM AAL5 */
+	USB                           = 0xa0, /* USB Interface */
+	IEEE8023ADLAG                 = 0xa1, /* IEEE 802.3ad Link Aggregate*/
+	BGPPOLICYACCOUNTING           = 0xa2, /* BGP Policy Accounting */
+	FRF16MFRBUNDLE                = 0xa3, /* FRF.16 Multilink Frame Relay*/
+	H323GATEKEEPER                = 0xa4, /* H323 Gatekeeper */
+	H323PROXY                     = 0xa5, /* H323 Voice and Video Proxy */
+	MPLS                          = 0xa6, /* MPLS */
+	MFSIGLINK                     = 0xa7, /* Multi-frequency signaling link */
+	HDSL2                         = 0xa8, /* High Bit-Rate DSL, 2nd gen. */
+	SHDSL                         = 0xa9, /* Multirate HDSL2 */
+	DS1FDL                        = 0xaa, /* Facility Data Link (4Kbps) on a DS1*/
+	POS                           = 0xab, /* Packet over SONET/SDH Interface */
+	DVBASILN                      = 0xac, /* DVB-ASI Input */
+	DVBASIOUT                     = 0xad, /* DVB-ASI Output */
+	PLC                           = 0xae, /* Power Line Communications */
+	NFAS                          = 0xaf, /* Non-Facility Associated Signaling */
+	TR008                         = 0xb0, /* TROO8 */
+	GR303RDT                      = 0xb1, /* Remote Digital Terminal */
+	GR303IDT                      = 0xb2, /* Integrated Digital Terminal */
+	ISUP                          = 0xb3, /* ISUP */
+	PROPDOCSWIRELESSMACLAYER      = 0xb4, /* prop/Wireless MAC Layer */
+	PROPDOCSWIRELESSDOWNSTREAM    = 0xb5, /* prop/Wireless Downstream */
+	PROPDOCSWIRELESSUPSTREAM      = 0xb6, /* prop/Wireless Upstream */
+	HIPERLAN2                     = 0xb7, /* HIPERLAN Type 2 Radio Interface */
+	PROPBWAP2MP                   = 0xb8, /* PropBroadbandWirelessAccess P2MP*/
+	SONETOVERHEADCHANNEL          = 0xb9, /* SONET Overhead Channel */
+	DIGITALWRAPPEROVERHEADCHANNEL = 0xba, /* Digital Wrapper Overhead */
+	AAL2                          = 0xbb, /* ATM adaptation layer 2 */
+	RADIOMAC                      = 0xbc, /* MAC layer over radio links */
+	ATMRADIO                      = 0xbd, /* ATM over radio links */
+	IMT                           = 0xbe, /* Inter-Machine Trunks */
+	MVL                           = 0xbf, /* Multiple Virtual Lines DSL */
+	REACHDSL                      = 0xc0, /* Long Reach DSL */
+	FRDLCIENDPT                   = 0xc1, /* Frame Relay DLCI End Point */
+	ATMVCIENDPT                   = 0xc2, /* ATM VCI End Point */
+	OPTICALCHANNEL                = 0xc3, /* Optical Channel */
+	OPTICALTRANSPORT              = 0xc4, /* Optical Transport */
+	INFINIBAND                    = 0xc7, /* Infiniband */
+	INFINIBANDLAG                 = 0xc8, /* Infiniband Link Aggregate */
+	BRIDGE                        = 0xd1, /* Transparent bridge interface */
+	STF                           = 0xd7, /* 6to4 interface */
+
+	/*
+	 * Not based on IANA assignments.  Conflicting with IANA assignments.
+	 * We should make them negative probably.
+	 * This requires changes to struct if_data.
+	 */
+	GIF                           = 0xf0, /* Generic tunnel interface */
+	PVC                           = 0xf1, /* Unused */
+	ENC                           = 0xf4, /* Encapsulating interface */
+	PFLOG                         = 0xf6, /* PF packet filter logging */
+	PFSYNC                        = 0xf7, /* PF packet filter synchronization */
+	WIREGUARD                     = 0xf8, /* WireGuard tunnel */
+}
+
+//
+// #include <net/if_dl.h>
+//
+
+/*
+ * Structure of a Link-Level sockaddr:
+ */
+// struct sockaddr_dl
+Socket_Address_Data_Link :: struct {
+	using _: Socket_Address_Header,
+	index:  c.ushort,       /* if != 0, system given index for interface */
+	type:   Interface_Type, /* interface type */
+	nlen:   c.uchar,        /* interface name length, no trailing 0 reqd. */
+	alen:   c.uchar,        /* link level address length */
+	slen:   c.uchar,        /* link layer selector length */
+	data:   [46]c.char,     /* minimum work area, can be larger; contains both if name and ll address */
+}
+
+//
+// #include <netinet/in.h>
+//
+
+in_addr_t :: distinct u32be
+
+/* Internet address (a structure for historical reasons). */
+// struct in_addr
+IP4_Address :: struct #raw_union {
+	// NOTE(Feoramund): I have modified this struct from its C definition by
+	// introducing the byte variant to make it easier to work with.
+	addr8: [4]u8,
+	addr32: in_addr_t,
+}
+
+/* Socket address, internet style. */
+// struct sockaddr_in
+Socket_Address_Internet :: struct #packed {
+	using _: Socket_Address_Header,
+	port:    in_port_t,
+	addr:    IP4_Address,
+	zero:    [8]c.char,
+}
+
+//
+// #include <netinet6/in6.h>
+//
+
+/*
+ * IPv6 address
+ */
+// struct in6_addr
+IP6_Address :: struct #raw_union {
+	addr8:  [16]u8,
+	addr16: [8]u16be,
+	addr32: [4]u32be,
+}
+
+/*
+ * Socket address for IPv6
+ */
+// struct sockaddr_in6
+Socket_Address_Internet6 :: struct #packed {
+	using _:  Socket_Address_Header,
+	port:     in_port_t,           /* Transport layer port # */
+	flowinfo: u32,                 /* IP6 flow information */
+	addr:     IP6_Address,         /* IP6 address */
+	scope_id: u32,                 /* scope zone index */
+}
+
+//
+// #include <sys/umtx.h>
+//
+
+/* op code for _umtx_op */
+// #define UMTX_OP_*
+Userland_Mutex_Operation :: enum c.int {
+	LOCK              = 0,  /* COMPAT10 */
+	UNLOCK            = 1,  /* COMPAT10 */
+	WAIT              = 2,
+	WAKE              = 3,
+	MUTEX_TRYLOCK     = 4,
+	MUTEX_LOCK        = 5,
+	MUTEX_UNLOCK      = 6,
+	SET_CEILING       = 7,
+	CV_WAIT           = 8,
+	CV_SIGNAL         = 9,
+	CV_BROADCAST      = 10,
+	WAIT_UINT         = 11,
+	RW_RDLOCK         = 12,
+	RW_WRLOCK         = 13,
+	RW_UNLOCK         = 14,
+	WAIT_UINT_PRIVATE = 15,
+	WAKE_PRIVATE      = 16,
+	MUTEX_WAIT        = 17,
+	MUTEX_WAKE        = 18, /* deprecated */
+	SEM_WAIT          = 19, /* deprecated */
+	SEM_WAKE          = 20, /* deprecated */
+	NWAKE_PRIVATE     = 21,
+	MUTEX_WAKE2       = 22,
+	SEM2_WAIT         = 23,
+	SEM2_WAKE         = 24,
+	SHM               = 25,
+	ROBUST_LISTS      = 26,
+	GET_MIN_TIMEOUT   = 27,
+	SET_MIN_TIMEOUT   = 28,
+}

+ 3 - 1
tests/core/net/test_core_net.odin

@@ -1,14 +1,16 @@
 /*
 	Copyright 2021 Jeroen van Rijn <[email protected]>.
+	Copyright 2024 Feoramund       <[email protected]>.
 	Made available under Odin's BSD-3 license.
 
 	List of contributors:
 		Jeroen van Rijn: Initial implementation.
 		graphitemaster:  pton/ntop IANA test vectors
+		Feoramund:       FreeBSD-specific tests.
 
 	A test suite for `core:net`
 */
-//+build !netbsd !freebsd !openbsd
+//+build !netbsd !openbsd
 package test_core_net
 
 import "core:testing"

+ 89 - 0
tests/core/net/test_core_net_freebsd.odin

@@ -0,0 +1,89 @@
+/*
+	Copyright 2021 Jeroen van Rijn <[email protected]>.
+	Copyright 2024 Feoramund       <[email protected]>.
+	Made available under Odin's BSD-3 license.
+
+	List of contributors:
+		Jeroen van Rijn: Initial implementation.
+		graphitemaster:  pton/ntop IANA test vectors
+		Feoramund:       FreeBSD-specific tests.
+
+	A test suite for `core:net`
+*/
+//+build freebsd
+package test_core_net
+
+import "core:net"
+import "core:time"
+import "core:testing"
+
+ENDPOINT_DUPLICATE_BINDING := net.Endpoint{net.IP4_Address{127, 0, 0, 1}, 11000}
+ENDPOINT_EPIPE_TEST        := net.Endpoint{net.IP4_Address{127, 0, 0, 1}, 11001}
+
+@test
+test_duplicate_binding :: proc(t: ^testing.T) {
+	// FreeBSD has the capacity to permit multiple processes and sockets to
+	// bind on the same port with the right option.
+
+	raw_socket1, err_create1 := net.create_socket(.IP4, .TCP)
+	if !testing.expect_value(t, err_create1, nil) {
+		return
+	}
+	defer net.close(raw_socket1)
+	tcp_socket1 := raw_socket1.(net.TCP_Socket)
+	err_set1 := net.set_option(tcp_socket1, .Reuse_Port, true)
+	if !testing.expect_value(t, err_set1, nil) {
+		return
+	}
+	err_bind1 := net.bind(tcp_socket1, ENDPOINT_DUPLICATE_BINDING)
+	if !testing.expect_value(t, err_bind1, nil) {
+		return
+	}
+
+	raw_socket2, err_create2 := net.create_socket(.IP4, .TCP)
+	if !testing.expect_value(t, err_create2, nil) {
+		return
+	}
+	defer net.close(raw_socket2)
+	tcp_socket2 := raw_socket2.(net.TCP_Socket)
+	err_set2 := net.set_option(tcp_socket2, .Reuse_Port, true)
+	if !testing.expect_value(t, err_set2, nil) {
+		return
+	}
+	err_bind2 := net.bind(tcp_socket2, ENDPOINT_DUPLICATE_BINDING)
+	if !testing.expect_value(t, err_bind2, nil) {
+		return
+	}
+}
+
+@test
+test_sigpipe_bypass :: proc(t: ^testing.T) {
+	// If the internals aren't working as expected, this test will fail by raising SIGPIPE.
+
+	server_socket, listen_err := net.listen_tcp(ENDPOINT_EPIPE_TEST)
+	if !testing.expect_value(t, listen_err, nil) {
+		return
+	}
+	defer net.close(server_socket)
+
+	client_socket, dial_err := net.dial_tcp(ENDPOINT_EPIPE_TEST)
+	if !testing.expect_value(t, dial_err, nil) {
+		return
+	}
+	defer net.close(client_socket)
+
+	time.sleep(10 * time.Millisecond)
+
+	net.close(server_socket)
+
+	time.sleep(10 * time.Millisecond)
+
+	data := "Hellope!"
+	bytes_written, err_send := net.send(client_socket, transmute([]u8)data)
+	if !testing.expect_value(t, err_send, net.TCP_Send_Error.Cannot_Send_More_Data) {
+		return
+	}
+	if !testing.expect_value(t, bytes_written, 0) {
+		return
+	}
+}