aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--core/net/interface_darwin.odin102
-rw-r--r--core/net/socket_darwin.odin25
-rw-r--r--core/os/os_darwin.odin36
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) -> ! ---
}