diff options
| author | Feoramund <161657516+Feoramund@users.noreply.github.com> | 2024-06-26 01:18:43 -0400 |
|---|---|---|
| committer | Feoramund <161657516+Feoramund@users.noreply.github.com> | 2024-06-26 10:05:24 -0400 |
| commit | e61d893a749d6219c498152bb53d750a384d13f4 (patch) | |
| tree | 37ab259dc9e08eb43cf193f0b8b2034ad1b9dadf /core/net/interface_freebsd.odin | |
| parent | 10ce76fcc2ce45a28858ca1562e2b802a781ee58 (diff) | |
Port `core:net` to FreeBSD
Diffstat (limited to 'core/net/interface_freebsd.odin')
| -rw-r--r-- | core/net/interface_freebsd.odin | 158 |
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 +} |