aboutsummaryrefslogtreecommitdiff
path: root/src/ifaddr/ifaddrposix.nim
blob: 1fca199fe2fee34858bcdfc13a646179e67dd799 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
import bitops
from nativesockets import getAddrString, ntohl, Port
import os
from posix import SockAddr, AF_INET, AF_INET6, Sockaddr_in, Sockaddr_in6, SockLen
import tables

import private/utils

type
    ifaddrs {.importc: "struct ifaddrs", header: "<ifaddrs.h>".} = object
        ifa_next: ptr ifaddrs
        ifa_name: cstring
        ifa_flags: cuint
        ifa_addr: ptr SockAddr
        ifa_netmask: ptr SockAddr
        # Skip fields we don't care about

proc getifaddrs(ifap: ptr ptr ifaddrs): cint {.importc, header: "<ifaddrs.h>".}

proc freeifaddrs(ifa: ptr ifaddrs) {.importc, header: "<ifaddrs.h>".}

proc getPrefixLen(sa: ptr SockAddr): int =
    if sa.sa_family.uint32 == AF_INET.uint32:
        let sa4 = cast[ptr Sockaddr_in](sa)
        result = countLeadingZeroBits(bitnot(ntohl(sa4.sin_addr.s_addr)))
    elif sa.sa_family.uint32 == AF_INET6.uint32:
        # TODO: not sure if this is exactly the right thing to do ...
        let sa6 = cast[ptr Sockaddr_in6](sa)
        for i in 0..15:
            inc(result, countSetBits(sa6.sin6_addr.s6_addr[i].uint8))
    else:
        result = 0

proc getAdapters*(): seq[Adapter] =
    var addrs: ptr ifaddrs

    if getifaddrs(addr addrs) != 0:
        raiseOSError(osLastError(), "getifaddrs")

    defer: freeifaddrs(addrs)

    var ips = initOrderedTable[string, Adapter]()

    var curr = addrs
    while curr != nil:
        let name = $curr.ifa_name
        if curr.ifa_addr != nil:
            var
                ip: IpAddress
                flowInfo: uint32
                scopeId: uint32
            try:
                fromSockAddrPtr(curr.ifa_addr, ip, flowInfo, scopeId)
            except:
                curr = curr.ifa_next
                continue
            if name notin ips:
                ips[name] = Adapter(name: name, niceName: name, ips: @[])
            let prefix = getPrefixLen(curr.ifa_netmask)
            case ip.family
            of IpAddressFamily.IPv6:
                ips[name].ips.add(IP(family: IpAddressFamily.IPv6,
                    flowInfo: flowInfo,
                    scopeId: scopeId,
                    ip: ip,
                    networkPrefix: prefix,
                    niceName: name))
            of IpAddressFamily.IPv4:
                ips[name].ips.add(IP(family: IpAddressFamily.IPv4,
                    ip: ip,
                    networkPrefix: prefix,
                    niceName: name))
        curr = curr.ifa_next

    result = @[]
    for v in ips.values:
        result.add(v)