aboutsummaryrefslogtreecommitdiff
path: root/core/net/interface_linux.odin
diff options
context:
space:
mode:
Diffstat (limited to 'core/net/interface_linux.odin')
-rw-r--r--core/net/interface_linux.odin147
1 files changed, 147 insertions, 0 deletions
diff --git a/core/net/interface_linux.odin b/core/net/interface_linux.odin
new file mode 100644
index 000000000..c5973fa2d
--- /dev/null
+++ b/core/net/interface_linux.odin
@@ -0,0 +1,147 @@
+//+build linux, darwin, openbsd, !windows
+/*
+ Copyright 2022 Tetralux <tetraluxonpc@gmail.com>
+ Copyright 2022 Colin Davidson <colrdavidson@gmail.com>
+ Copyright 2022 Jeroen van Rijn <nom@duclavier.com>.
+ Made available under Odin's BSD-3 license.
+
+ List of contributors:
+ Tetralux: Initial implementation
+ Colin Davidson: Linux platform code, OSX platform code, Odin-native DNS resolver
+ Jeroen van Rijn: Cross platform unification, code style, documentation
+*/
+
+/*
+ Package net implements cross-platform Berkeley Sockets, DNS resolution and associated procedures.
+ For other protocols and their features, see subdirectories of this package.
+*/
+package net
+
+/*
+ This file uses `getifaddrs` libc call to enumerate interfaces.
+
+ TODO: When we have raw sockets, split off into its own file for Linux so we can use the NETLINK protocol and bypass libc.
+*/
+
+import "core:os"
+import "core:strings"
+
+/*
+ `enumerate_interfaces` retrieves a list of network interfaces with their associated properties.
+*/
+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.sa_family) {
+ case os.AF_INET, os.AF_INET6:
+ address = sockaddr_to_endpoint(ifaddr.address).address
+
+ case os.AF_PACKET:
+ /*
+ For some obscure reason the 64-bit `getifaddrs` calls returns a pointer to a
+ 32-bit `RTNL_LINK_STATS` structure, which of course means that tx/rx byte count
+ is truncated beyond usefulness.
+
+ We're not going to retrieve stats now. Instead this serves as a reminder to use
+ the NETLINK protocol for this purpose.
+
+ But in case you were curious:
+ stats := transmute(^os.rtnl_link_stats)ifaddr.data
+ fmt.println(stats)
+ */
+ case:
+ }
+ }
+
+ if ifaddr.netmask != nil {
+ switch int(ifaddr.netmask.sa_family) {
+ case os.AF_INET, os.AF_INET6:
+ netmask = Netmask(sockaddr_to_endpoint(ifaddr.netmask).address)
+ case:
+ }
+ }
+
+ if ifaddr.broadcast_or_dest != nil && .BROADCAST in ifaddr.flags {
+ switch int(ifaddr.broadcast_or_dest.sa_family) {
+ case os.AF_INET, os.AF_INET6:
+ broadcast := sockaddr_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
+ }
+
+ /*
+ Free the OS structures.
+ */
+ os._freeifaddrs(head)
+
+ /*
+ 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[:], {}
+} \ No newline at end of file