aboutsummaryrefslogtreecommitdiff
path: root/core/net/interface_freebsd.odin
diff options
context:
space:
mode:
authorFeoramund <161657516+Feoramund@users.noreply.github.com>2024-06-26 01:18:43 -0400
committerFeoramund <161657516+Feoramund@users.noreply.github.com>2024-06-26 10:05:24 -0400
commite61d893a749d6219c498152bb53d750a384d13f4 (patch)
tree37ab259dc9e08eb43cf193f0b8b2034ad1b9dadf /core/net/interface_freebsd.odin
parent10ce76fcc2ce45a28858ca1562e2b802a781ee58 (diff)
Port `core:net` to FreeBSD
Diffstat (limited to 'core/net/interface_freebsd.odin')
-rw-r--r--core/net/interface_freebsd.odin158
1 files changed, 158 insertions, 0 deletions
diff --git a/core/net/interface_freebsd.odin b/core/net/interface_freebsd.odin
new file mode 100644
index 000000000..f3c455b21
--- /dev/null
+++ b/core/net/interface_freebsd.odin
@@ -0,0 +1,158 @@
+//+build freebsd
+package net
+
+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
+}