]> git.meshlink.io Git - catta/blob - src/iface-windows.c
6f14371431414ff8355fca080b240347fba0eff1
[catta] / src / iface-windows.c
1 /***
2   This file is part of catta.
3
4   catta is free software; you can redistribute it and/or modify it
5   under the terms of the GNU Lesser General Public License as
6   published by the Free Software Foundation; either version 2.1 of the
7   License, or (at your option) any later version.
8
9   catta is distributed in the hope that it will be useful, but WITHOUT
10   ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
11   or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General
12   Public License for more details.
13
14   You should have received a copy of the GNU Lesser General Public
15   License along with catta; if not, write to the Free Software
16   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
17   USA.
18 ***/
19
20 #include "iface-windows.h"
21 #include "iface.h"
22
23 #include <stdlib.h> // wcstombs
24 #include <catta/malloc.h>
25 #include <catta/log.h>
26 #include <iphlpapi.h>
27 #include "hashmap.h"
28 #include "util.h"   // catta_format_mac_address
29
30
31 // for the luid-to-idx hashmap
32 static unsigned luid_hash(const void *data)
33 {
34     return ((NET_LUID *)data)->Info.NetLuidIndex;
35 }
36 static int luid_equal(const void *a, const void *b)
37 {
38     return (((NET_LUID *)a)->Value == ((NET_LUID *)b)->Value);
39 }
40
41 static CattaIfIndex find_ifindex(CattaInterfaceMonitor *m, NET_LUID luid)
42 {
43     CattaIfIndex *pi = NULL;
44     NET_LUID *key = NULL;
45
46     if((pi = catta_hashmap_lookup(m->osdep.idxmap, &luid)) == NULL) {
47         // allocate memory for the hashmap key and value
48         key = catta_malloc(sizeof(luid));
49         pi = catta_malloc(sizeof(CattaIfIndex));
50         if(!key || !pi)
51             goto fail;
52
53         *key = luid;
54             
55         // find an index for this luid
56         *pi = m->osdep.nidx;
57         if(*pi < 0)  // overflow
58             goto fail;
59
60         // register the index
61         if(catta_hashmap_replace(m->osdep.idxmap, key, pi) < 0)
62             goto fail;
63         m->osdep.nidx++;
64     }
65
66     return *pi;
67
68 fail:
69     catta_free(key);
70     catta_free(pi);
71     return -1;
72 }
73
74 // integrate the information from an IP_ADAPTER_UNICAST_ADDRESS structure for
75 // given CattaHwInterface into the CattaInterfaceMonitor
76 static void ip_adapter_unicast_address(CattaInterfaceMonitor *m,
77                                        CattaHwInterface *hw,
78                                        IP_ADAPTER_UNICAST_ADDRESS *a)
79 {
80     CattaInterface *iface;
81     CattaAddress addr;
82     CattaInterfaceAddress *ifaddr;
83     struct sockaddr *sa = a->Address.lpSockaddr;
84
85     // fill addr struct for address lookup
86     switch(sa->sa_family) {
87     case AF_INET:
88         memcpy(addr.data.data, &((struct sockaddr_in *)sa)->sin_addr, sizeof(struct in_addr));
89         break;
90     case AF_INET6:
91         memcpy(addr.data.data, &((struct sockaddr_in6 *)sa)->sin6_addr, sizeof(struct in6_addr));
92         break;
93     default:
94         catta_log_debug("unexpected address family on interface %d: %u", hw->index, sa->sa_family);
95         return;
96     }
97     addr.proto = catta_af_to_proto(sa->sa_family);
98
99     // get protocol-specific CattaInterface object
100     if(!(iface = catta_interface_monitor_get_interface(m, hw->index, addr.proto))) {
101         catta_log_error("CattaInterface (index %d, proto %d) not found", hw->index, addr.proto);
102         return;
103     }
104
105     // find or allocate a CattaInterfaceAddress struct for this address
106     if(!(ifaddr = catta_interface_monitor_get_address(m, iface, &addr))) {
107         if(!(ifaddr = catta_interface_address_new(m, iface, &addr, a->OnLinkPrefixLength))) {
108             catta_log_error("out of memory in ip_adapter_unicast_address");
109             return;
110         }
111     }
112
113     // set global scope flag
114     if(addr.proto == CATTA_PROTO_INET6)
115         ifaddr->global_scope = !(IN6_IS_ADDR_LINKLOCAL((struct in6_addr *)addr.data.data)
116                                  || IN6_IS_ADDR_MULTICAST((struct in6_addr *)addr.data.data));
117     else
118         ifaddr->global_scope = 1;
119
120     // XXX debugging, remove
121     {
122         char s[CATTA_ADDRESS_STR_MAX];
123         catta_log_debug(" address: %s\n"
124                         "   global_scope: %d",
125             catta_address_snprint(s, sizeof(s), &addr),
126             ifaddr->global_scope);
127     }
128 }
129
130 // integrate the information from an IP_ADAPTER_ADDRESSES structure
131 // as returned by GetAdaptersAddresses into the CattaInterfaceMonitor
132 static void ip_adapter(CattaInterfaceMonitor *m, IP_ADAPTER_ADDRESSES *p)
133 {
134     IP_ADAPTER_UNICAST_ADDRESS *a;
135     CattaIfIndex idx;
136     CattaHwInterface *hw;
137     size_t n;
138
139     // look up the interface index by LUID
140     if((idx = find_ifindex(m, p->Luid)) < 0) {
141         catta_log_error("could not allocate index ip_adapter_address");
142         return;
143     }
144
145     // find the CattaHwInterface by index or allocate a new one
146     if((hw = catta_interface_monitor_get_hw_interface(m, idx)) == NULL) {
147         if((hw = catta_hw_interface_new(m, idx)) == NULL) {
148             catta_log_error("catta_hw_interface_new failed in ip_adapter_address");
149             return;
150         }
151     }
152
153     // fill the CattaHwInterface struct with data
154     hw->flags_ok =
155         (p->OperStatus == IfOperStatusUp) &&
156         !(p->IfType == IF_TYPE_SOFTWARE_LOOPBACK) &&
157         !(p->Flags & IP_ADAPTER_NO_MULTICAST) &&
158         (m->server->config.allow_point_to_point || !(p->IfType == IF_TYPE_PPP));
159             // XXX what about IF_TYPE_TUNNEL?
160
161     n = wcstombs(NULL, p->FriendlyName, 0) + 1;
162     catta_free(hw->name);
163     hw->name = catta_new(char, n);
164     wcstombs(hw->name, p->FriendlyName, n);
165
166     hw->mtu = p->Mtu;
167
168     hw->mac_address_size = p->PhysicalAddressLength;
169     if(hw->mac_address_size > CATTA_MAC_ADDRESS_MAX)
170         hw->mac_address_size = CATTA_MAC_ADDRESS_MAX;
171     memcpy(hw->mac_address, p->PhysicalAddress, hw->mac_address_size);
172
173     // XXX debugging, remove
174     {
175         char mac[256];
176         catta_log_debug(" name: %s\n"
177                         " index: %d\n"
178                         " mtu: %d\n"
179                         " mac: %s\n"
180                         " flags_ok: %d\n"
181                         "   type: %u\n"
182                         "   status: %u\n"
183                         "   multicast: %d\n"
184                         "   flags: 0x%.4x",
185             hw->name, hw->index,
186             hw->mtu,
187             catta_format_mac_address(mac, sizeof(mac), hw->mac_address, hw->mac_address_size),
188             hw->flags_ok,
189             (unsigned int)p->IfType,
190             (unsigned int)p->OperStatus,
191             !(p->Flags & IP_ADAPTER_NO_MULTICAST),
192             (unsigned int)p->Flags);
193     }
194
195     // process addresses
196     // XXX remove addresses that are no longer in the list
197     for(a=p->FirstUnicastAddress; a; a=a->Next)
198         ip_adapter_unicast_address(m, hw, a);
199     catta_log_debug("=====");
200 }
201
202
203 int catta_interface_monitor_init_osdep(CattaInterfaceMonitor *m)
204 {
205     m->osdep.nidx = 0;
206     m->osdep.idxmap = catta_hashmap_new(luid_hash, luid_equal, catta_free, catta_free);
207     if(m->osdep.idxmap == NULL) {
208         catta_log_error("out of memory in catta_interface_monitor_init_osdep");
209         return -1;
210     }
211
212     // XXX register callbacks to get notified of interface/address changes
213
214     return 0;
215 }
216
217 void catta_interface_monitor_free_osdep(CattaInterfaceMonitor *m)
218 {
219     catta_hashmap_free(m->osdep.idxmap);
220 }
221
222 void catta_interface_monitor_sync(CattaInterfaceMonitor *m)
223 {
224     IP_ADAPTER_ADDRESSES *buf = NULL;
225     IP_ADAPTER_ADDRESSES *p;
226     ULONG bufsize = 15000;
227     ULONG r;
228
229     // allocate a buffer and call GetAdaptersAddresses
230     // retry with the correct size if the buffer was too small
231     do {
232         catta_free(buf);    // no-op on first iteration
233         if((buf = catta_malloc(bufsize)) == NULL) {
234             catta_log_error("malloc failed in catta_interface_monitor_sync");
235             return;
236         }
237
238         r = GetAdaptersAddresses(AF_UNSPEC, 0, NULL, buf, &bufsize);
239     } while(r == ERROR_BUFFER_OVERFLOW);
240
241     if(r != NO_ERROR) {
242         catta_log_error("GetAdaptersAddresses failed: %u", (unsigned int)r);
243         return;
244     }
245
246     // XXX remove interfaces for adapters that are no longer in the list
247
248     // create 'CattaInterface's for every adapter
249     for(p=buf; p; p=p->Next)
250         ip_adapter(m, p);
251
252     catta_free(buf);
253
254     m->list_complete = 1;
255     catta_interface_monitor_check_relevant(m);
256     catta_interface_monitor_update_rrs(m, 0);
257     catta_log_info("Network interface enumeration completed.");
258 }