1 /*
2 * route-win32.c
3 *
4 * Copyright (c) 2002 Dug Song <dugsong@monkey.org>
5 *
6 * $Id: route-win32.c 589 2005-02-15 07:11:32Z dugsong $
7 */
8
9 #ifdef _WIN32
10 #include "dnet_winconfig.h"
11 #else
12 #include "config.h"
13 #endif
14
15 #include <ws2tcpip.h>
16 #include <iphlpapi.h>
17
18 #include <errno.h>
19 #include <stdlib.h>
20 #include <string.h>
21
22 #include "dnet.h"
23
24 typedef DWORD (WINAPI *GETIPFORWARDTABLE2)(ADDRESS_FAMILY, PMIB_IPFORWARD_TABLE2 *);
25
26 struct route_handle {
27 HINSTANCE iphlpapi;
28 MIB_IPFORWARDTABLE *ipftable;
29 MIB_IPFORWARD_TABLE2 *ipftable2;
30 };
31
32 route_t *
route_open(void)33 route_open(void)
34 {
35 route_t *r;
36
37 r = calloc(1, sizeof(route_t));
38 if (r == NULL)
39 return NULL;
40 r->iphlpapi = GetModuleHandle("iphlpapi.dll");
41
42 return r;
43 }
44
45 int
route_add(route_t * route,const struct route_entry * entry)46 route_add(route_t *route, const struct route_entry *entry)
47 {
48 MIB_IPFORWARDROW ipfrow;
49 struct addr net;
50
51 memset(&ipfrow, 0, sizeof(ipfrow));
52
53 if (GetBestInterface(entry->route_gw.addr_ip,
54 &ipfrow.dwForwardIfIndex) != NO_ERROR)
55 return (-1);
56
57 if (addr_net(&entry->route_dst, &net) < 0 ||
58 net.addr_type != ADDR_TYPE_IP)
59 return (-1);
60
61 ipfrow.dwForwardDest = net.addr_ip;
62 addr_btom(entry->route_dst.addr_bits,
63 &ipfrow.dwForwardMask, IP_ADDR_LEN);
64 ipfrow.dwForwardNextHop = entry->route_gw.addr_ip;
65 ipfrow.dwForwardType = 4; /* XXX - next hop != final dest */
66 ipfrow.dwForwardProto = 3; /* XXX - MIB_PROTO_NETMGMT */
67
68 if (CreateIpForwardEntry(&ipfrow) != NO_ERROR)
69 return (-1);
70
71 return (0);
72 }
73
74 int
route_delete(route_t * route,const struct route_entry * entry)75 route_delete(route_t *route, const struct route_entry *entry)
76 {
77 MIB_IPFORWARDROW ipfrow;
78 DWORD mask;
79
80 if (entry->route_dst.addr_type != ADDR_TYPE_IP ||
81 GetBestRoute(entry->route_dst.addr_ip,
82 IP_ADDR_ANY, &ipfrow) != NO_ERROR)
83 return (-1);
84
85 addr_btom(entry->route_dst.addr_bits, &mask, IP_ADDR_LEN);
86
87 if (ipfrow.dwForwardDest != entry->route_dst.addr_ip ||
88 ipfrow.dwForwardMask != mask) {
89 errno = ENXIO;
90 SetLastError(ERROR_NO_DATA);
91 return (-1);
92 }
93 if (DeleteIpForwardEntry(&ipfrow) != NO_ERROR)
94 return (-1);
95
96 return (0);
97 }
98
99 int
route_get(route_t * route,struct route_entry * entry)100 route_get(route_t *route, struct route_entry *entry)
101 {
102 MIB_IPFORWARDROW ipfrow;
103 DWORD mask;
104 intf_t *intf;
105 struct intf_entry intf_entry;
106
107 if (entry->route_dst.addr_type != ADDR_TYPE_IP ||
108 GetBestRoute(entry->route_dst.addr_ip,
109 IP_ADDR_ANY, &ipfrow) != NO_ERROR)
110 return (-1);
111
112 if (ipfrow.dwForwardProto == 2 && /* XXX - MIB_IPPROTO_LOCAL */
113 (ipfrow.dwForwardNextHop|IP_CLASSA_NET) !=
114 (IP_ADDR_LOOPBACK|IP_CLASSA_NET) &&
115 !IP_LOCAL_GROUP(ipfrow.dwForwardNextHop)) {
116 errno = ENXIO;
117 SetLastError(ERROR_NO_DATA);
118 return (-1);
119 }
120 addr_btom(entry->route_dst.addr_bits, &mask, IP_ADDR_LEN);
121
122 entry->route_gw.addr_type = ADDR_TYPE_IP;
123 entry->route_gw.addr_bits = IP_ADDR_BITS;
124 entry->route_gw.addr_ip = ipfrow.dwForwardNextHop;
125 entry->metric = ipfrow.dwForwardMetric1;
126
127 entry->intf_name[0] = '\0';
128 intf = intf_open();
129 if (intf_get_index(intf, &intf_entry,
130 AF_INET, ipfrow.dwForwardIfIndex) == 0) {
131 strlcpy(entry->intf_name, intf_entry.intf_name, sizeof(entry->intf_name));
132 }
133 intf_close(intf);
134
135 return (0);
136 }
137
138 static int
route_loop_getipforwardtable(route_t * r,route_handler callback,void * arg)139 route_loop_getipforwardtable(route_t *r, route_handler callback, void *arg)
140 {
141 struct route_entry entry;
142 intf_t *intf;
143 ULONG len;
144 int i, ret;
145
146 for (len = sizeof(r->ipftable[0]); ; ) {
147 if (r->ipftable)
148 free(r->ipftable);
149 r->ipftable = malloc(len);
150 if (r->ipftable == NULL)
151 return (-1);
152 ret = GetIpForwardTable(r->ipftable, &len, FALSE);
153 if (ret == NO_ERROR)
154 break;
155 else if (ret != ERROR_INSUFFICIENT_BUFFER)
156 return (-1);
157 }
158
159 intf = intf_open();
160
161 ret = 0;
162 for (i = 0; i < (int)r->ipftable->dwNumEntries; i++) {
163 struct intf_entry intf_entry;
164
165 entry.route_dst.addr_type = ADDR_TYPE_IP;
166 entry.route_dst.addr_bits = IP_ADDR_BITS;
167
168 entry.route_gw.addr_type = ADDR_TYPE_IP;
169 entry.route_gw.addr_bits = IP_ADDR_BITS;
170
171 entry.route_dst.addr_ip = r->ipftable->table[i].dwForwardDest;
172 addr_mtob(&r->ipftable->table[i].dwForwardMask, IP_ADDR_LEN,
173 &entry.route_dst.addr_bits);
174 entry.route_gw.addr_ip =
175 r->ipftable->table[i].dwForwardNextHop;
176 entry.metric = r->ipftable->table[i].dwForwardMetric1;
177
178 /* Look up the interface name. */
179 entry.intf_name[0] = '\0';
180 intf_entry.intf_len = sizeof(intf_entry);
181 if (intf_get_index(intf, &intf_entry,
182 AF_INET, r->ipftable->table[i].dwForwardIfIndex) == 0) {
183 strlcpy(entry.intf_name, intf_entry.intf_name, sizeof(entry.intf_name));
184 }
185
186 if ((ret = (*callback)(&entry, arg)) != 0)
187 break;
188 }
189
190 intf_close(intf);
191
192 return ret;
193 }
194
195 static int
route_loop_getipforwardtable2(GETIPFORWARDTABLE2 GetIpForwardTable2,route_t * r,route_handler callback,void * arg)196 route_loop_getipforwardtable2(GETIPFORWARDTABLE2 GetIpForwardTable2,
197 route_t *r, route_handler callback, void *arg)
198 {
199 struct route_entry entry;
200 intf_t *intf;
201 ULONG i;
202 int ret;
203
204 ret = GetIpForwardTable2(AF_UNSPEC, &r->ipftable2);
205 if (ret != NO_ERROR)
206 return (-1);
207
208 intf = intf_open();
209
210 ret = 0;
211 for (i = 0; i < r->ipftable2->NumEntries; i++) {
212 struct intf_entry intf_entry;
213 MIB_IPFORWARD_ROW2 *row;
214 MIB_IPINTERFACE_ROW ifrow;
215 ULONG metric;
216
217 row = &r->ipftable2->Table[i];
218 addr_ston((struct sockaddr *) &row->DestinationPrefix.Prefix, &entry.route_dst);
219 entry.route_dst.addr_bits = row->DestinationPrefix.PrefixLength;
220 addr_ston((struct sockaddr *) &row->NextHop, &entry.route_gw);
221
222 /* Look up the interface name. */
223 entry.intf_name[0] = '\0';
224 intf_entry.intf_len = sizeof(intf_entry);
225 if (intf_get_index(intf, &intf_entry,
226 row->DestinationPrefix.Prefix.si_family,
227 row->InterfaceIndex) == 0) {
228 strlcpy(entry.intf_name, intf_entry.intf_name, sizeof(entry.intf_name));
229 }
230
231 ifrow.Family = row->DestinationPrefix.Prefix.si_family;
232 ifrow.InterfaceLuid = row->InterfaceLuid;
233 ifrow.InterfaceIndex = row->InterfaceIndex;
234 if (GetIpInterfaceEntry(&ifrow) != NO_ERROR) {
235 return (-1);
236 }
237 metric = ifrow.Metric + row->Metric;
238 if (metric < INT_MAX)
239 entry.metric = metric;
240 else
241 entry.metric = INT_MAX;
242
243 if ((ret = (*callback)(&entry, arg)) != 0)
244 break;
245 }
246
247 intf_close(intf);
248
249 return ret;
250 }
251
252 int
route_loop(route_t * r,route_handler callback,void * arg)253 route_loop(route_t *r, route_handler callback, void *arg)
254 {
255 GETIPFORWARDTABLE2 GetIpForwardTable2;
256
257 /* GetIpForwardTable2 is only available on Vista and later, dynamic load. */
258 GetIpForwardTable2 = NULL;
259 if (r->iphlpapi != NULL)
260 GetIpForwardTable2 = (GETIPFORWARDTABLE2) GetProcAddress(r->iphlpapi, "GetIpForwardTable2");
261
262 if (GetIpForwardTable2 == NULL)
263 return route_loop_getipforwardtable(r, callback, arg);
264 else
265 return route_loop_getipforwardtable2(GetIpForwardTable2, r, callback, arg);
266 }
267
268 route_t *
route_close(route_t * r)269 route_close(route_t *r)
270 {
271 if (r != NULL) {
272 if (r->iphlpapi != NULL)
273 FreeLibrary(r->iphlpapi);
274 if (r->ipftable != NULL)
275 free(r->ipftable);
276 if (r->ipftable2 != NULL)
277 FreeMibTable(r->ipftable2);
278 free(r);
279 }
280 return (NULL);
281 }
282