diff options
| -rw-r--r-- | core/net/interface_darwin.odin | 102 | ||||
| -rw-r--r-- | core/net/socket_darwin.odin | 25 | ||||
| -rw-r--r-- | core/os/os_darwin.odin | 36 |
3 files changed, 158 insertions, 5 deletions
diff --git a/core/net/interface_darwin.odin b/core/net/interface_darwin.odin index 90d996a4a..54b2a0b3b 100644 --- a/core/net/interface_darwin.odin +++ b/core/net/interface_darwin.odin @@ -19,14 +19,106 @@ package net */ +import "core:os" +import "core:strings" + @(private) _enumerate_interfaces :: proc(allocator := context.allocator) -> (interfaces: []Network_Interface, err: Network_Error) { context.allocator = allocator + head: ^os.ifaddrs + + if res := os._getifaddrs(&head); res < 0 { + return {}, .Unable_To_Enumerate_Network_Interfaces + } + + /* + Unlike Windows, *nix regrettably doesn't return all it knows about an interface in one big struct. + We're going to have to iterate over a list and coalesce information as we go. + */ + ifaces: map[string]^Network_Interface + defer delete(ifaces) + + for ifaddr := head; ifaddr != nil; ifaddr = ifaddr.next { + adapter_name := string(ifaddr.name) + + /* + Check if we have seen this interface name before so we can reuse the `Network_Interface`. + Else, create a new one. + */ + if adapter_name not_in ifaces { + ifaces[adapter_name] = new(Network_Interface) + ifaces[adapter_name].adapter_name = strings.clone(adapter_name) + } + iface := ifaces[adapter_name] + + address: Address + netmask: Netmask + + if ifaddr.address != nil { + switch int(ifaddr.address.family) { + case os.AF_INET, os.AF_INET6: + address = _sockaddr_basic_to_endpoint(ifaddr.address).address + case: + } + } + + if ifaddr.netmask != nil { + switch int(ifaddr.netmask.family) { + case os.AF_INET, os.AF_INET6: + netmask = Netmask(_sockaddr_basic_to_endpoint(ifaddr.netmask).address) + case: + } + } + + if ifaddr.broadcast_or_dest != nil && .BROADCAST in ifaddr.flags { + switch int(ifaddr.broadcast_or_dest.family) { + case os.AF_INET, os.AF_INET6: + broadcast := _sockaddr_basic_to_endpoint(ifaddr.broadcast_or_dest).address + append(&iface.multicast, broadcast) + case: + } + } + + if address != nil { + lease := Lease{ + address = address, + netmask = netmask, + } + append(&iface.unicast, lease) + } + + /* + TODO: Refine this based on the type of adapter. + */ + state := Link_State{} + + if .UP in ifaddr.flags { + state |= {.Up} + } + + if .DORMANT in ifaddr.flags { + state |= {.Dormant} + } + + if .LOOPBACK in ifaddr.flags { + state |= {.Loopback} + } + iface.link.state = state + } - // TODO: Implement. Can probably use the (current) Linux implementation, - // which will itself be switched over to talking to the kernel via NETLINK protocol - // once we have raw sockets. + /* + Free the OS structures. + */ + os._freeifaddrs(head) - unimplemented() -}
\ No newline at end of file + /* + Turn the map into a slice to return. + */ + _interfaces := make([dynamic]Network_Interface, 0, allocator) + for _, iface in ifaces { + append(&_interfaces, iface^) + free(iface) + } + return _interfaces[:], {} +} diff --git a/core/net/socket_darwin.odin b/core/net/socket_darwin.odin index 081892afd..2585d134b 100644 --- a/core/net/socket_darwin.odin +++ b/core/net/socket_darwin.odin @@ -23,6 +23,7 @@ import "core:os" import "core:time" Socket_Option :: enum c.int { + Broadcast = c.int(os.SO_BROADCAST), Reuse_Address = c.int(os.SO_REUSEADDR), Keep_Alive = c.int(os.SO_KEEPALIVE), Out_Of_Bounds_Data_Inline = c.int(os.SO_OOBINLINE), @@ -238,6 +239,7 @@ _set_option :: proc(s: Any_Socket, option: Socket_Option, value: any, loc := #ca switch option { case + .Broadcast, .Reuse_Address, .Keep_Alive, .Out_Of_Bounds_Data_Inline, @@ -369,3 +371,26 @@ _sockaddr_to_endpoint :: proc(native_addr: ^os.SOCKADDR_STORAGE_LH) -> (ep: Endp } return } + +@(private) +_sockaddr_basic_to_endpoint :: proc(native_addr: ^os.SOCKADDR) -> (ep: Endpoint) { + switch u16(native_addr.family) { + case u16(os.AF_INET): + addr := cast(^os.sockaddr_in) native_addr + port := int(addr.sin_port) + ep = Endpoint { + address = IP4_Address(transmute([4]byte) addr.sin_addr), + port = port, + } + case u16(os.AF_INET6): + addr := cast(^os.sockaddr_in6) native_addr + port := int(addr.sin6_port) + ep = Endpoint { + address = IP6_Address(transmute([8]u16be) addr.sin6_addr), + port = port, + } + case: + panic("native_addr is neither IP4 or IP6 address") + } + return +} diff --git a/core/os/os_darwin.odin b/core/os/os_darwin.odin index d8ba40fd0..9a9f3ed04 100644 --- a/core/os/os_darwin.odin +++ b/core/os/os_darwin.odin @@ -354,6 +354,39 @@ in6_addr :: struct #packed { s6_addr: [16]u8, } +SIOCGIFFLAG :: enum c.int { + UP = 0, /* Interface is up. */ + BROADCAST = 1, /* Broadcast address valid. */ + DEBUG = 2, /* Turn on debugging. */ + LOOPBACK = 3, /* Is a loopback net. */ + POINT_TO_POINT = 4, /* Interface is point-to-point link. */ + NO_TRAILERS = 5, /* Avoid use of trailers. */ + RUNNING = 6, /* Resources allocated. */ + NOARP = 7, /* No address resolution protocol. */ + PROMISC = 8, /* Receive all packets. */ + ALL_MULTI = 9, /* Receive all multicast packets. Unimplemented. */ + MASTER = 10, /* Master of a load balancer. */ + SLAVE = 11, /* Slave of a load balancer. */ + MULTICAST = 12, /* Supports multicast. */ + PORTSEL = 13, /* Can set media type. */ + AUTOMEDIA = 14, /* Auto media select active. */ + DYNAMIC = 15, /* Dialup device with changing addresses. */ + LOWER_UP = 16, + DORMANT = 17, + ECHO = 18, +} +SIOCGIFFLAGS :: bit_set[SIOCGIFFLAG; c.int] + +ifaddrs :: struct { + next: ^ifaddrs, + name: cstring, + flags: SIOCGIFFLAGS, + address: ^SOCKADDR, + netmask: ^SOCKADDR, + broadcast_or_dest: ^SOCKADDR, // Broadcast or Point-to-Point address + data: rawptr, // Address-specific data. +} + Timeval :: struct { seconds: i64, microseconds: int, @@ -474,6 +507,9 @@ foreign libc { @(link_name="send") _unix_send :: proc(socket: int, buffer: rawptr, buffer_len: c.size_t, flags: int) -> c.ssize_t --- @(link_name="shutdown") _unix_shutdown :: proc(socket: int, how: int) -> int --- + @(link_name="getifaddrs") _getifaddrs :: proc(ifap: ^^ifaddrs) -> (c.int) --- + @(link_name="freeifaddrs") _freeifaddrs :: proc(ifa: ^ifaddrs) --- + @(link_name="exit") _unix_exit :: proc(status: c.int) -> ! --- } |