Line | Count | Source |
1 | | /* |
2 | | * |
3 | | * Authors: |
4 | | * Lars Fenneberg <lf@elemental.net> |
5 | | * |
6 | | * This software is Copyright 1996,1997 by the above mentioned author(s), |
7 | | * All Rights Reserved. |
8 | | * |
9 | | * The license which is distributed with this software in the file COPYRIGHT |
10 | | * applies to this software. If your distribution is missing this file, you |
11 | | * may request it from <reubenhwk@gmail.com>. |
12 | | * |
13 | | */ |
14 | | |
15 | | #include "config.h" |
16 | | #include "defaults.h" |
17 | | #include "includes.h" |
18 | | #include "radvd.h" |
19 | | |
20 | | #define IFACE_SETUP_DELAY 1 |
21 | | |
22 | | void iface_init_defaults(struct Interface *iface) |
23 | 0 | { |
24 | 0 | memset(iface, 0, sizeof(struct Interface)); |
25 | |
|
26 | 0 | iface->state_info.changed = 1; |
27 | |
|
28 | 0 | iface->IgnoreIfMissing = DFLT_IgnoreIfMissing; |
29 | 0 | iface->AdvSendAdvert = DFLT_AdvSendAdv; |
30 | 0 | iface->MaxRtrAdvInterval = DFLT_MaxRtrAdvInterval; |
31 | 0 | iface->AdvSourceLLAddress = DFLT_AdvSourceLLAddress; |
32 | 0 | iface->RemoveAdvOnExit = DFLT_RemoveAdvOnExit; |
33 | 0 | iface->MinDelayBetweenRAs = DFLT_MinDelayBetweenRAs; |
34 | 0 | iface->MinRtrAdvInterval = -1; |
35 | 0 | iface->UnicastOnly = DFLT_UnicastOnly; |
36 | 0 | iface->UnrestrictedUnicast = DFLT_UnrestrictedUnicast; |
37 | 0 | iface->AdvRASolicitedUnicast = DFLT_AdvRASolicitedUnicast; |
38 | |
|
39 | 0 | iface->ra_header_info.AdvDefaultPreference = DFLT_AdvDefaultPreference; |
40 | 0 | iface->ra_header_info.AdvDefaultLifetime = -1; |
41 | 0 | iface->ra_header_info.AdvReachableTime = DFLT_AdvReachableTime; |
42 | 0 | iface->ra_header_info.AdvRetransTimer = DFLT_AdvRetransTimer; |
43 | 0 | iface->ra_header_info.AdvCurHopLimit = DFLT_AdvCurHopLimit; |
44 | 0 | iface->ra_header_info.AdvHomeAgentFlag = DFLT_AdvHomeAgentFlag; |
45 | |
|
46 | 0 | iface->mipv6.AdvIntervalOpt = DFLT_AdvIntervalOpt; |
47 | 0 | iface->mipv6.AdvHomeAgentInfo = DFLT_AdvHomeAgentInfo; |
48 | 0 | iface->mipv6.HomeAgentPreference = DFLT_HomeAgentPreference; |
49 | 0 | iface->mipv6.AdvMobRtrSupportFlag = DFLT_AdvMobRtrSupportFlag; |
50 | 0 | iface->mipv6.HomeAgentLifetime = -1; |
51 | |
|
52 | 0 | iface->AdvLinkMTU = DFLT_AdvLinkMTU; |
53 | 0 | iface->AdvRAMTU = DFLT_AdvRAMTU; |
54 | 0 | } |
55 | | |
56 | | void touch_iface(struct Interface *iface) |
57 | 0 | { |
58 | 0 | iface->state_info.changed = 1; |
59 | 0 | iface->state_info.ready = 0; |
60 | 0 | iface->state_info.racount = 0; |
61 | 0 | reschedule_iface(iface, 0); |
62 | 0 | } |
63 | | |
64 | | int setup_iface(int sock, struct Interface *iface) |
65 | 0 | { |
66 | 0 | iface->state_info.changed = 0; |
67 | 0 | iface->state_info.ready = 0; |
68 | | |
69 | | /* The device index must be setup first so we can search it later */ |
70 | 0 | if (update_device_index(iface) < 0) { |
71 | 0 | return -1; |
72 | 0 | } |
73 | | |
74 | | /* Check IFF_UP, IFF_RUNNING and IFF_MULTICAST */ |
75 | 0 | if (check_device(sock, iface) < 0) { |
76 | 0 | return -2; |
77 | 0 | } |
78 | | |
79 | | /* Set iface->max_mtu and iface hardware address */ |
80 | 0 | if (update_device_info(sock, iface) < 0) { |
81 | 0 | return -3; |
82 | 0 | } |
83 | | |
84 | | /* Make sure the settings in the config file for this interface are ok (this depends |
85 | | * on iface->max_mtu already being set). */ |
86 | 0 | if (check_iface(iface) < 0) { |
87 | 0 | return -4; |
88 | 0 | } |
89 | | |
90 | | /* Save the first link local address seen on the specified interface to |
91 | | * iface->props.if_addr and keep a list off all addrs in iface->props.if_addrs */ |
92 | 0 | if (setup_iface_addrs(iface) < 0) { |
93 | 0 | return -5; |
94 | 0 | } |
95 | | |
96 | | /* Check if we a usable RA source address */ |
97 | 0 | if (iface->props.if_addr_rasrc == NULL) { |
98 | 0 | dlog(LOG_DEBUG, 5, "no configured AdvRASrcAddress present, skipping send"); |
99 | 0 | return -6; |
100 | 0 | } |
101 | | |
102 | | /* join the allrouters multicast group so we get the solicitations */ |
103 | 0 | if (setup_allrouters_membership(sock, iface) < 0) { |
104 | 0 | return -7; |
105 | 0 | } |
106 | | |
107 | 0 | iface->state_info.ready = 1; |
108 | |
|
109 | 0 | dlog(LOG_DEBUG, 4, "%s is ready", iface->props.name); |
110 | |
|
111 | 0 | return 0; |
112 | 0 | } |
113 | | |
114 | | int cleanup_iface(int sock, struct Interface *iface) |
115 | 0 | { |
116 | | /* leave the allrouters multicast group */ |
117 | 0 | cleanup_allrouters_membership(sock, iface); |
118 | 0 | return 0; |
119 | 0 | } |
120 | | void prefix_init_defaults(struct AdvPrefix *prefix) |
121 | 0 | { |
122 | 0 | memset(prefix, 0, sizeof(struct AdvPrefix)); |
123 | |
|
124 | 0 | prefix->AdvOnLinkFlag = DFLT_AdvOnLinkFlag; |
125 | 0 | prefix->AdvAutonomousFlag = DFLT_AdvAutonomousFlag; |
126 | 0 | prefix->AdvRouterAddr = DFLT_AdvRouterAddr; |
127 | 0 | prefix->AdvValidLifetime = DFLT_AdvValidLifetime; |
128 | 0 | prefix->AdvPreferredLifetime = DFLT_AdvPreferredLifetime; |
129 | 0 | prefix->DeprecatePrefixFlag = DFLT_DeprecatePrefixFlag; |
130 | 0 | prefix->DecrementLifetimesFlag = DFLT_DecrementLifetimesFlag; |
131 | |
|
132 | 0 | prefix->curr_validlft = prefix->AdvValidLifetime; |
133 | 0 | prefix->curr_preferredlft = prefix->AdvPreferredLifetime; |
134 | 0 | } |
135 | | |
136 | | void nat64prefix_init_defaults(struct NAT64Prefix *prefix, struct Interface *iface) |
137 | 0 | { |
138 | 0 | memset(prefix, 0, sizeof(struct NAT64Prefix)); |
139 | |
|
140 | 0 | prefix->AdvValidLifetime = min(DFLT_NAT64MaxValidLifetime, 3*(iface->MaxRtrAdvInterval)); |
141 | |
|
142 | 0 | prefix->curr_validlft = prefix->AdvValidLifetime; |
143 | 0 | } |
144 | | |
145 | | void route_init_defaults(struct AdvRoute *route, struct Interface *iface) |
146 | 0 | { |
147 | 0 | memset(route, 0, sizeof(struct AdvRoute)); |
148 | |
|
149 | 0 | route->AdvRouteLifetime = DFLT_AdvRouteLifetime(iface); |
150 | 0 | route->AdvRoutePreference = DFLT_AdvRoutePreference; |
151 | 0 | route->RemoveRouteFlag = DFLT_RemoveRouteFlag; |
152 | 0 | } |
153 | | |
154 | | void rdnss_init_defaults(struct AdvRDNSS *rdnss, struct Interface *iface) |
155 | 0 | { |
156 | 0 | memset(rdnss, 0, sizeof(struct AdvRDNSS)); |
157 | |
|
158 | 0 | rdnss->AdvRDNSSLifetime = DFLT_AdvRDNSSLifetime(iface); |
159 | 0 | rdnss->AdvRDNSSNumber = 0; |
160 | 0 | rdnss->FlushRDNSSFlag = DFLT_FlushRDNSSFlag; |
161 | 0 | } |
162 | | |
163 | | void dnssl_init_defaults(struct AdvDNSSL *dnssl, struct Interface *iface) |
164 | 0 | { |
165 | 0 | memset(dnssl, 0, sizeof(struct AdvDNSSL)); |
166 | |
|
167 | 0 | dnssl->AdvDNSSLLifetime = DFLT_AdvDNSSLLifetime(iface); |
168 | 0 | dnssl->FlushDNSSLFlag = DFLT_FlushDNSSLFlag; |
169 | 0 | } |
170 | | |
171 | | int check_iface(struct Interface *iface) |
172 | 0 | { |
173 | 0 | int res = 0; |
174 | 0 | int MIPv6 = 0; |
175 | 0 | struct in6_addr zeroaddr; |
176 | 0 | memset(&zeroaddr, 0, sizeof(zeroaddr)); |
177 | | |
178 | | /* Check if we use Mobile IPv6 extensions */ |
179 | 0 | if (iface->ra_header_info.AdvHomeAgentFlag || iface->mipv6.AdvHomeAgentInfo || iface->mipv6.AdvIntervalOpt) { |
180 | 0 | MIPv6 = 1; |
181 | 0 | flog(LOG_INFO, "using Mobile IPv6 extensions"); |
182 | 0 | } |
183 | | |
184 | | /* Check forwarding on interface */ |
185 | 0 | if (check_ip6_iface_forwarding(iface->props.name) < 1) { |
186 | 0 | flog(LOG_WARNING, "IPv6 forwarding on interface seems to be disabled, but continuing anyway"); |
187 | 0 | } |
188 | |
|
189 | 0 | struct AdvPrefix *prefix = iface->AdvPrefixList; |
190 | 0 | while (!MIPv6 && prefix) { |
191 | 0 | if (prefix->AdvRouterAddr) { |
192 | 0 | MIPv6 = 1; |
193 | 0 | } |
194 | 0 | prefix = prefix->next; |
195 | 0 | } |
196 | |
|
197 | 0 | if (iface->MinRtrAdvInterval < 0) |
198 | 0 | iface->MinRtrAdvInterval = DFLT_MinRtrAdvInterval(iface); |
199 | |
|
200 | 0 | if ((iface->MinRtrAdvInterval < (MIPv6 ? MIN_MinRtrAdvInterval_MIPv6 : MIN_MinRtrAdvInterval)) || |
201 | 0 | (iface->MinRtrAdvInterval > MAX_MinRtrAdvInterval(iface))) { |
202 | 0 | flog(LOG_ERR, |
203 | 0 | "MinRtrAdvInterval for %s (%.2f) must be at least %.2f but no more than 3/4 of MaxRtrAdvInterval (%.2f)", |
204 | 0 | iface->props.name, iface->MinRtrAdvInterval, |
205 | 0 | MIPv6 ? MIN_MinRtrAdvInterval_MIPv6 : (int)MIN_MinRtrAdvInterval, MAX_MinRtrAdvInterval(iface)); |
206 | 0 | res = -1; |
207 | 0 | } |
208 | |
|
209 | 0 | if ((iface->MaxRtrAdvInterval < (MIPv6 ? MIN_MaxRtrAdvInterval_MIPv6 : MIN_MaxRtrAdvInterval)) || |
210 | 0 | (iface->MaxRtrAdvInterval > MAX_MaxRtrAdvInterval)) { |
211 | 0 | flog(LOG_ERR, "MaxRtrAdvInterval for %s (%.2f) must be between %.2f and %d", iface->props.name, |
212 | 0 | iface->MaxRtrAdvInterval, MIPv6 ? MIN_MaxRtrAdvInterval_MIPv6 : (int)MIN_MaxRtrAdvInterval, |
213 | 0 | MAX_MaxRtrAdvInterval); |
214 | 0 | res = -1; |
215 | 0 | } |
216 | |
|
217 | 0 | if (iface->MinDelayBetweenRAs < (MIPv6 ? MIN_DELAY_BETWEEN_RAS_MIPv6 : MIN_DELAY_BETWEEN_RAS)) { |
218 | 0 | flog(LOG_ERR, "MinDelayBetweenRAs for %s (%.2f) must be at least %.2f", iface->props.name, |
219 | 0 | iface->MinDelayBetweenRAs, MIPv6 ? MIN_DELAY_BETWEEN_RAS_MIPv6 : MIN_DELAY_BETWEEN_RAS); |
220 | 0 | res = -1; |
221 | 0 | } |
222 | |
|
223 | 0 | if ((iface->AdvLinkMTU != 0) && ((iface->AdvLinkMTU < MIN_AdvLinkMTU) || |
224 | 0 | (iface->sllao.if_maxmtu != -1 && (iface->AdvLinkMTU > iface->sllao.if_maxmtu)))) { |
225 | 0 | flog(LOG_ERR, "AdvLinkMTU for %s (%u) must be zero or between %u and %u", iface->props.name, iface->AdvLinkMTU, |
226 | 0 | MIN_AdvLinkMTU, iface->sllao.if_maxmtu); |
227 | 0 | res = -1; |
228 | 0 | } |
229 | |
|
230 | 0 | if (iface->ra_header_info.AdvReachableTime > MAX_AdvReachableTime) { |
231 | 0 | flog(LOG_ERR, "AdvReachableTime for %s (%u) must not be greater than %u", iface->props.name, |
232 | 0 | iface->ra_header_info.AdvReachableTime, MAX_AdvReachableTime); |
233 | 0 | res = -1; |
234 | 0 | } |
235 | |
|
236 | 0 | if (iface->ra_header_info.AdvDefaultLifetime < 0) |
237 | 0 | iface->ra_header_info.AdvDefaultLifetime = DFLT_AdvDefaultLifetime(iface); |
238 | |
|
239 | 0 | if ((iface->ra_header_info.AdvDefaultLifetime != 0) && |
240 | 0 | ((iface->ra_header_info.AdvDefaultLifetime > MAX_AdvDefaultLifetime) || |
241 | 0 | (iface->ra_header_info.AdvDefaultLifetime < MIN_AdvDefaultLifetime(iface)))) { |
242 | 0 | flog(LOG_ERR, "AdvDefaultLifetime for %s (%u) must be zero or between %u and %u", iface->props.name, |
243 | 0 | iface->ra_header_info.AdvDefaultLifetime, (int)MIN_AdvDefaultLifetime(iface), MAX_AdvDefaultLifetime); |
244 | 0 | res = -1; |
245 | 0 | } |
246 | | |
247 | | /* Mobile IPv6 ext */ |
248 | 0 | if (iface->mipv6.HomeAgentLifetime < 0) |
249 | 0 | iface->mipv6.HomeAgentLifetime = DFLT_HomeAgentLifetime(iface); |
250 | | |
251 | | /* Mobile IPv6 ext */ |
252 | 0 | if (iface->mipv6.AdvHomeAgentInfo) { |
253 | 0 | if ((iface->mipv6.HomeAgentLifetime > MAX_HomeAgentLifetime) || |
254 | 0 | (iface->mipv6.HomeAgentLifetime < MIN_HomeAgentLifetime)) { |
255 | 0 | flog(LOG_ERR, "HomeAgentLifetime for %s (%u) must be between %u and %u", iface->props.name, |
256 | 0 | iface->mipv6.HomeAgentLifetime, MIN_HomeAgentLifetime, MAX_HomeAgentLifetime); |
257 | 0 | res = -1; |
258 | 0 | } |
259 | 0 | } |
260 | | |
261 | | /* Mobile IPv6 ext */ |
262 | 0 | if (iface->mipv6.AdvHomeAgentInfo && !(iface->ra_header_info.AdvHomeAgentFlag)) { |
263 | 0 | flog(LOG_ERR, "AdvHomeAgentFlag for %s must be set with HomeAgentInfo", iface->props.name); |
264 | 0 | res = -1; |
265 | 0 | } |
266 | 0 | if (iface->mipv6.AdvMobRtrSupportFlag && !(iface->mipv6.AdvHomeAgentInfo)) { |
267 | 0 | flog(LOG_ERR, "AdvHomeAgentInfo for %s must be set with AdvMobRtrSupportFlag", iface->props.name); |
268 | 0 | res = -1; |
269 | 0 | } |
270 | | |
271 | | /* XXX: need this? prefix = iface->AdvPrefixList; */ |
272 | |
|
273 | 0 | while (prefix) { |
274 | 0 | if (prefix->PrefixLen > MAX_PrefixLen) { |
275 | 0 | flog(LOG_ERR, "invalid prefix length (%u) for %s", prefix->PrefixLen, iface->props.name); |
276 | 0 | res = -1; |
277 | 0 | } |
278 | |
|
279 | 0 | if (prefix->AdvPreferredLifetime > prefix->AdvValidLifetime) { |
280 | 0 | flog(LOG_ERR, "AdvValidLifetime for %s (%u) must be " |
281 | 0 | "greater than or equal to AdvPreferredLifetime for", |
282 | 0 | iface->props.name, prefix->AdvValidLifetime); |
283 | 0 | res = -1; |
284 | 0 | } |
285 | |
|
286 | 0 | prefix = prefix->next; |
287 | 0 | } |
288 | |
|
289 | 0 | struct AdvRoute *route = iface->AdvRouteList; |
290 | 0 | while (route) { |
291 | 0 | if (route->PrefixLen > MAX_PrefixLen) { |
292 | 0 | flog(LOG_ERR, "invalid route prefix length (%u) for %s", route->PrefixLen, iface->props.name); |
293 | 0 | res = -1; |
294 | 0 | } |
295 | | |
296 | | /* For the default route 0::/0, we need to explicitly check the |
297 | | * lifetime against the AdvDefaultLifetime value. |
298 | | * |
299 | | * If exactly one of the two has a zero value, then nodes processing |
300 | | * the RA may have a flap in their default route. |
301 | | * |
302 | | * AdvDefaultLifetime == 0 && route.AdvRouteLifetime > 0: |
303 | | * - default route is deleted and then re-added. |
304 | | * AdvDefaultLifetime > 0 && route.AdvRouteLifetime == 0: |
305 | | * - default route is added and then deleted. |
306 | | */ |
307 | 0 | if (IN6_IS_ADDR_UNSPECIFIED(&(route->Prefix))) { |
308 | 0 | int route_zerolife = (route->AdvRouteLifetime == 0); |
309 | 0 | int defaultroute_zerolife = (iface->ra_header_info.AdvDefaultLifetime == 0); |
310 | 0 | if (route_zerolife ^ defaultroute_zerolife) { |
311 | 0 | flog( |
312 | 0 | LOG_ERR, |
313 | 0 | "route 0::/0 lifetime (%u) conflicts with AdvDefaultLifetime (%u), default routes will flap!", |
314 | 0 | route->AdvRouteLifetime, iface->ra_header_info.AdvDefaultLifetime); |
315 | | // res = -1; // In some future version, abort on this configuration error. |
316 | 0 | } |
317 | 0 | } |
318 | |
|
319 | 0 | route = route->next; |
320 | 0 | } |
321 | |
|
322 | 0 | return res; |
323 | 0 | } |
324 | | |
325 | | struct Interface *find_iface_by_index(struct Interface *iface, int index) |
326 | 0 | { |
327 | 0 | for (; iface; iface = iface->next) { |
328 | 0 | if (iface->props.if_index == index) { |
329 | 0 | return iface; |
330 | 0 | } |
331 | 0 | } |
332 | | |
333 | 0 | return 0; |
334 | 0 | } |
335 | | |
336 | | struct Interface *find_iface_by_name(struct Interface *iface, const char *name) |
337 | 0 | { |
338 | 0 | if (!name) { |
339 | 0 | return 0; |
340 | 0 | } |
341 | | |
342 | 0 | for (; iface; iface = iface->next) { |
343 | 0 | if (strcmp(iface->props.name, name) == 0) { |
344 | 0 | return iface; |
345 | 0 | } |
346 | 0 | } |
347 | | |
348 | 0 | return 0; |
349 | 0 | } |
350 | | |
351 | | struct Interface *find_iface_by_time(struct Interface *iface) |
352 | 0 | { |
353 | 0 | if (!iface) { |
354 | 0 | return 0; |
355 | 0 | } |
356 | | |
357 | 0 | int timeout = next_time_msec(iface); |
358 | 0 | struct Interface *next = iface; |
359 | |
|
360 | 0 | for (iface = iface->next; iface; iface = iface->next) { |
361 | 0 | int t = next_time_msec(iface); |
362 | 0 | if (timeout > t) { |
363 | 0 | timeout = t; |
364 | 0 | next = iface; |
365 | 0 | } |
366 | 0 | } |
367 | |
|
368 | 0 | return next; |
369 | 0 | } |
370 | | |
371 | | void reschedule_iface(struct Interface *iface, double next) |
372 | 0 | { |
373 | | #ifdef HAVE_NETLINK |
374 | | if (!iface->state_info.changed && !iface->state_info.ready) { |
375 | | next = 10 * iface->MaxRtrAdvInterval; |
376 | | } else if (next == 0) { |
377 | | next = IFACE_SETUP_DELAY; |
378 | | } else |
379 | | #endif |
380 | 0 | if (iface->state_info.racount < MAX_INITIAL_RTR_ADVERTISEMENTS) { |
381 | 0 | next = min(MAX_INITIAL_RTR_ADVERT_INTERVAL, iface->MaxRtrAdvInterval); |
382 | 0 | } |
383 | |
|
384 | 0 | dlog(LOG_DEBUG, 5, "%s next scheduled RA in %g second(s)", iface->props.name, next); |
385 | |
|
386 | 0 | iface->times.next_multicast = next_timespec(next); |
387 | 0 | } |
388 | | |
389 | | void for_each_iface(struct Interface *ifaces, void (*foo)(struct Interface *, void *), void *data) |
390 | 0 | { |
391 | 0 | for (; ifaces; ifaces = ifaces->next) { |
392 | 0 | foo(ifaces, data); |
393 | 0 | } |
394 | 0 | } |
395 | | |
396 | | static void free_iface_list(struct Interface *iface) |
397 | 549 | { |
398 | 549 | while (iface) { |
399 | 0 | struct Interface *next_iface = iface->next; |
400 | |
|
401 | 0 | dlog(LOG_DEBUG, 4, "freeing interface %s", iface->props.name); |
402 | |
|
403 | 0 | struct AdvPrefix *prefix = iface->AdvPrefixList; |
404 | 0 | while (prefix) { |
405 | 0 | struct AdvPrefix *next_prefix = prefix->next; |
406 | |
|
407 | 0 | free(prefix); |
408 | 0 | prefix = next_prefix; |
409 | 0 | } |
410 | |
|
411 | 0 | struct AdvRoute *route = iface->AdvRouteList; |
412 | 0 | while (route) { |
413 | 0 | struct AdvRoute *next_route = route->next; |
414 | |
|
415 | 0 | free(route); |
416 | 0 | route = next_route; |
417 | 0 | } |
418 | |
|
419 | 0 | struct AdvRDNSS *rdnss = iface->AdvRDNSSList; |
420 | 0 | while (rdnss) { |
421 | 0 | struct AdvRDNSS *next_rdnss = rdnss->next; |
422 | |
|
423 | 0 | free(rdnss->AdvRDNSSAddr); |
424 | 0 | free(rdnss); |
425 | 0 | rdnss = next_rdnss; |
426 | 0 | } |
427 | |
|
428 | 0 | struct AdvDNSSL *dnssl = iface->AdvDNSSLList; |
429 | 0 | while (dnssl) { |
430 | 0 | struct AdvDNSSL *next_dnssl = dnssl->next; |
431 | |
|
432 | 0 | for (int i = 0; i < dnssl->AdvDNSSLNumber; i++) |
433 | 0 | free(dnssl->AdvDNSSLSuffixes[i]); |
434 | 0 | free(dnssl->AdvDNSSLSuffixes); |
435 | 0 | free(dnssl); |
436 | |
|
437 | 0 | dnssl = next_dnssl; |
438 | 0 | } |
439 | |
|
440 | 0 | struct AutogenIgnorePrefix *ignore_prefixes = iface->IgnorePrefixList; |
441 | 0 | while (ignore_prefixes) { |
442 | 0 | struct AutogenIgnorePrefix *next_ignore_prefix = ignore_prefixes->next; |
443 | |
|
444 | 0 | free(ignore_prefixes); |
445 | 0 | ignore_prefixes = next_ignore_prefix; |
446 | 0 | } |
447 | |
|
448 | 0 | struct Clients *clients = iface->ClientList; |
449 | 0 | while (clients) { |
450 | 0 | struct Clients *next_client = clients->next; |
451 | |
|
452 | 0 | free(clients); |
453 | 0 | clients = next_client; |
454 | 0 | } |
455 | |
|
456 | 0 | free(iface->props.if_addrs); |
457 | |
|
458 | 0 | free(iface->AdvCaptivePortalAPI); |
459 | |
|
460 | 0 | free(iface); |
461 | 0 | iface = next_iface; |
462 | 0 | } |
463 | 549 | } |
464 | | |
465 | | void free_ifaces(struct Interface *ifaces) |
466 | 549 | { |
467 | 549 | dlog(LOG_DEBUG, 3, "Freeing Interfaces"); |
468 | | |
469 | 549 | free_iface_list(ifaces); |
470 | 549 | } |