Browse Source

Merge pull request #2833 from simonwashere/darwin_net_interface

darwin  enumerate_interfaces
Jeroen van Rijn 1 year ago
parent
commit
0c10b951a9
3 changed files with 149 additions and 5 deletions
  1. 97 5
      core/net/interface_darwin.odin
  2. 25 0
      core/net/socket_darwin.odin
  3. 27 0
      core/os/os_darwin.odin

+ 97 - 5
core/net/interface_darwin.odin

@@ -19,14 +19,106 @@ package net
 
 
 */
 */
 
 
+import "core:os"
+import "core:strings"
+
 @(private)
 @(private)
 _enumerate_interfaces :: proc(allocator := context.allocator) -> (interfaces: []Network_Interface, err: Network_Error) {
 _enumerate_interfaces :: proc(allocator := context.allocator) -> (interfaces: []Network_Interface, err: Network_Error) {
 	context.allocator = allocator
 	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()
-}
+	/*
+		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[:], {}
+}

+ 25 - 0
core/net/socket_darwin.odin

@@ -23,6 +23,7 @@ import "core:os"
 import "core:time"
 import "core:time"
 
 
 Socket_Option :: enum c.int {
 Socket_Option :: enum c.int {
+	Broadcast                 = c.int(os.SO_BROADCAST),
 	Reuse_Address             = c.int(os.SO_REUSEADDR),
 	Reuse_Address             = c.int(os.SO_REUSEADDR),
 	Keep_Alive                = c.int(os.SO_KEEPALIVE),
 	Keep_Alive                = c.int(os.SO_KEEPALIVE),
 	Out_Of_Bounds_Data_Inline = c.int(os.SO_OOBINLINE),
 	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 {
 	switch option {
 	case
 	case
+		.Broadcast,
 		.Reuse_Address,
 		.Reuse_Address,
 		.Keep_Alive,
 		.Keep_Alive,
 		.Out_Of_Bounds_Data_Inline,
 		.Out_Of_Bounds_Data_Inline,
@@ -369,3 +371,26 @@ _sockaddr_to_endpoint :: proc(native_addr: ^os.SOCKADDR_STORAGE_LH) -> (ep: Endp
 	}
 	}
 	return
 	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
+}

+ 27 - 0
core/os/os_darwin.odin

@@ -354,6 +354,30 @@ in6_addr :: struct #packed {
 	s6_addr: [16]u8,
 	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. */
+}
+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 {
 Timeval :: struct {
 	seconds: i64,
 	seconds: i64,
 	microseconds: int,
 	microseconds: int,
@@ -474,6 +498,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="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="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) -> ! ---
 	@(link_name="exit")    _unix_exit :: proc(status: c.int) -> ! ---
 }
 }