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