]> git.meshlink.io Git - catta/blob - src/iface-windows.c
5ad14606d9b1f0821e5d3c2f5743ba23360cccda
[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 // integrate the information from an IP_ADAPTER_UNICAST_ADDRESS structure for
32 // given CattaHwInterface into the CattaInterfaceMonitor
33 static void ip_adapter_unicast_address(CattaInterfaceMonitor *m,
34                                        CattaHwInterface *hw,
35                                        IP_ADAPTER_UNICAST_ADDRESS *a)
36 {
37     CattaInterface *iface;
38     CattaAddress addr;
39     CattaInterfaceAddress *ifaddr;
40     struct sockaddr *sa = a->Address.lpSockaddr;
41
42     // skip transient addresses; to quote MSDN: "The IP address is a cluster
43     // address and should not be used by most applications."
44     // http://msdn.microsoft.com/en-us/library/windows/desktop/aa366066(v=vs.85).aspx
45     if(a->Flags & IP_ADAPTER_ADDRESS_TRANSIENT)
46         return;
47
48     // fill addr struct for address lookup
49     switch(sa->sa_family) {
50     case AF_INET:
51         memcpy(addr.data.data, &((struct sockaddr_in *)sa)->sin_addr, sizeof(struct in_addr));
52         break;
53     case AF_INET6:
54         memcpy(addr.data.data, &((struct sockaddr_in6 *)sa)->sin6_addr, sizeof(struct in6_addr));
55         break;
56     default:
57         catta_log_debug("unexpected address family on interface %d: %u", hw->index, sa->sa_family);
58         return;
59     }
60     addr.proto = catta_af_to_proto(sa->sa_family);
61
62     // get protocol-specific CattaInterface object
63     if(!(iface = catta_interface_monitor_get_interface(m, hw->index, addr.proto))) {
64         catta_log_error("CattaInterface (index %d, proto %d) not found", hw->index, addr.proto);
65         return;
66     }
67
68     // find or allocate a CattaInterfaceAddress struct for this address
69     if(!(ifaddr = catta_interface_monitor_get_address(m, iface, &addr))) {
70         if(!(ifaddr = catta_interface_address_new(m, iface, &addr, a->OnLinkPrefixLength))) {
71             catta_log_error("out of memory in ip_adapter_unicast_address");
72             return;
73         }
74     }
75
76     // set global scope flag
77     // XXX should we use the IP_ADAPTER_ADDRESS_DNS_ELIGIBLE flag for this?
78     //     it looks like it gets set for IPv4 addresses that are not localhost
79     //     and for IPv6 addresses with global scope. not sure about multicast
80     //     addresses.
81     if(addr.proto == CATTA_PROTO_INET6)
82         ifaddr->global_scope = !(IN6_IS_ADDR_LINKLOCAL((struct in6_addr *)addr.data.data)
83                                  || IN6_IS_ADDR_MULTICAST((struct in6_addr *)addr.data.data));
84     else
85         ifaddr->global_scope = 1;
86
87     // XXX debugging, remove
88     {
89         char s[CATTA_ADDRESS_STR_MAX];
90         catta_log_debug(" address: %s\n"
91                         "   global_scope: %d\n"
92                         "   flags: 0x%.4x",
93             catta_address_snprint(s, sizeof(s), &addr),
94             ifaddr->global_scope,
95             (unsigned int)a->Flags);
96     }
97 }
98
99 // integrate the information from an IP_ADAPTER_ADDRESSES structure
100 // as returned by GetAdaptersAddresses into the CattaInterfaceMonitor
101 static void ip_adapter(CattaInterfaceMonitor *m, IP_ADAPTER_ADDRESSES *p)
102 {
103     IP_ADAPTER_UNICAST_ADDRESS *a;
104     CattaIfIndex idx;
105     CattaHwInterface *hw;
106     size_t n;
107
108     // we want an index specific to the hardware interface, but Windows
109     // has one for IPv4 and one for IPv6. it seems like these are always the
110     // same unless one of the protocols is not available. let's have a bunch of
111     // checks...
112     if(!p->IfIndex && !p->Ipv6IfIndex) {
113         return; // no usable protocols
114     } else if(!p->IfIndex) {
115         idx = p->Ipv6IfIndex;   // IPv6 but no IPv4 (huh!)
116     } else if(!p->Ipv6IfIndex) {
117         idx = p->IfIndex;       // IPv4 but no IPv6
118     } else if(p->IfIndex == p->Ipv6IfIndex) {
119         idx = p->IfIndex;       // same index for both protocols
120     } else {
121         // both indexes valid but not equal
122         catta_log_error("unsupported interface: %ls (IfIndex and Ipv6IfIndex differ: %u/%u)",
123             p->FriendlyName, (unsigned int)p->IfIndex, (unsigned int)p->Ipv6IfIndex);
124         return;
125     }
126
127     // find the CattaHwInterface by index or allocate a new one
128     if((hw = catta_interface_monitor_get_hw_interface(m, idx)) == NULL) {
129         if((hw = catta_hw_interface_new(m, idx)) == NULL) {
130             catta_log_error("catta_hw_interface_new failed in ip_adapter_address");
131             return;
132         }
133     }
134
135     // fill the CattaHwInterface struct with data
136     hw->flags_ok =
137         (p->OperStatus == IfOperStatusUp) &&
138         !(p->IfType == IF_TYPE_SOFTWARE_LOOPBACK) &&
139         !(p->Flags & IP_ADAPTER_NO_MULTICAST) &&
140         (m->server->config.allow_point_to_point || !(p->IfType == IF_TYPE_PPP));
141             // XXX what about IF_TYPE_TUNNEL?
142
143     n = wcstombs(NULL, p->FriendlyName, 0) + 1;
144     catta_free(hw->name);
145     hw->name = catta_new(char, n);
146     wcstombs(hw->name, p->FriendlyName, n);
147
148     hw->mtu = p->Mtu;
149
150     hw->mac_address_size = p->PhysicalAddressLength;
151     if(hw->mac_address_size > CATTA_MAC_ADDRESS_MAX)
152         hw->mac_address_size = CATTA_MAC_ADDRESS_MAX;
153     memcpy(hw->mac_address, p->PhysicalAddress, hw->mac_address_size);
154
155     // XXX debugging, remove
156     {
157         char mac[256];
158         catta_log_debug(" name: %s\n"
159                         " index: %d\n"
160                         "   IfIndex: %u\n"
161                         "   Ipv6IfIndex: %u\n"
162                         " mtu: %d\n"
163                         " mac: %s\n"
164                         " flags_ok: %d\n"
165                         "   type: %u\n"
166                         "   status: %u\n"
167                         "   multicast: %d\n"
168                         "   flags: 0x%.4x",
169             hw->name, hw->index,
170             (unsigned int)p->IfIndex, (unsigned int)p->Ipv6IfIndex,
171             hw->mtu,
172             catta_format_mac_address(mac, sizeof(mac), hw->mac_address, hw->mac_address_size),
173             hw->flags_ok,
174             (unsigned int)p->IfType,
175             (unsigned int)p->OperStatus,
176             !(p->Flags & IP_ADAPTER_NO_MULTICAST),
177             (unsigned int)p->Flags);
178     }
179
180     // process addresses
181     // XXX remove addresses that are no longer in the list
182     for(a=p->FirstUnicastAddress; a; a=a->Next)
183         ip_adapter_unicast_address(m, hw, a);
184     catta_log_debug("=====");
185 }
186
187
188 int catta_interface_monitor_init_osdep(CattaInterfaceMonitor *m)
189 {
190     (void)*m;   // silence "unused paramter" warning
191
192     // XXX register callbacks to get notified of interface/address changes
193
194     return 0;
195 }
196
197 void catta_interface_monitor_free_osdep(CattaInterfaceMonitor *m)
198 {
199     (void)*m;   // silence "unused paramter" warning
200 }
201
202 void catta_interface_monitor_sync(CattaInterfaceMonitor *m)
203 {
204     IP_ADAPTER_ADDRESSES *buf = NULL;
205     IP_ADAPTER_ADDRESSES *p;
206     ULONG bufsize = 15000;
207     ULONG r;
208
209     // allocate a buffer and call GetAdaptersAddresses
210     // retry with the correct size if the buffer was too small
211     do {
212         catta_free(buf);    // no-op on first iteration
213         if((buf = catta_malloc(bufsize)) == NULL) {
214             catta_log_error("malloc failed in catta_interface_monitor_sync");
215             return;
216         }
217
218         r = GetAdaptersAddresses(AF_UNSPEC, 0, NULL, buf, &bufsize);
219     } while(r == ERROR_BUFFER_OVERFLOW);
220
221     if(r != NO_ERROR) {
222         catta_log_error("GetAdaptersAddresses failed: %u", (unsigned int)r);
223         return;
224     }
225
226     // XXX remove interfaces for adapters that are no longer in the list
227
228     // create 'CattaInterface's for every adapter
229     for(p=buf; p; p=p->Next)
230         ip_adapter(m, p);
231
232     catta_free(buf);
233
234     m->list_complete = 1;
235     catta_interface_monitor_check_relevant(m);
236     catta_interface_monitor_update_rrs(m, 0);
237     catta_log_info("Network interface enumeration completed.");
238 }