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 | | void dnr_init_defaults(struct AdvDNR *dnr, struct Interface *iface) |
174 | 0 | { |
175 | 0 | memset(dnr, 0, sizeof(struct AdvDNR)); |
176 | |
|
177 | 0 | dnr->AdvDNRPriority = DFLT_AdvDNRPriority; |
178 | 0 | dnr->AdvDNRLifetime = DFLT_AdvDNRLifetime(iface); |
179 | 0 | dnr->FlushDNRFlag = DFLT_FlushDNRFlag; |
180 | 0 | } |
181 | | |
182 | | int check_iface(struct Interface *iface) |
183 | 0 | { |
184 | 0 | int res = 0; |
185 | 0 | int MIPv6 = 0; |
186 | 0 | struct in6_addr zeroaddr; |
187 | 0 | memset(&zeroaddr, 0, sizeof(zeroaddr)); |
188 | | |
189 | | /* Check if we use Mobile IPv6 extensions */ |
190 | 0 | if (iface->ra_header_info.AdvHomeAgentFlag || iface->mipv6.AdvHomeAgentInfo || iface->mipv6.AdvIntervalOpt) { |
191 | 0 | MIPv6 = 1; |
192 | 0 | flog(LOG_INFO, "using Mobile IPv6 extensions"); |
193 | 0 | } |
194 | | |
195 | | /* Check forwarding on interface */ |
196 | 0 | if (check_ip6_iface_forwarding(iface->props.name) < 1) { |
197 | 0 | flog(LOG_WARNING, "IPv6 forwarding on interface seems to be disabled, but continuing anyway"); |
198 | 0 | } |
199 | |
|
200 | 0 | struct AdvPrefix *prefix = iface->AdvPrefixList; |
201 | 0 | while (!MIPv6 && prefix) { |
202 | 0 | if (prefix->AdvRouterAddr) { |
203 | 0 | MIPv6 = 1; |
204 | 0 | } |
205 | 0 | prefix = prefix->next; |
206 | 0 | } |
207 | |
|
208 | 0 | if (iface->MinRtrAdvInterval < 0) |
209 | 0 | iface->MinRtrAdvInterval = DFLT_MinRtrAdvInterval(iface); |
210 | |
|
211 | 0 | if ((iface->MinRtrAdvInterval < (MIPv6 ? MIN_MinRtrAdvInterval_MIPv6 : MIN_MinRtrAdvInterval)) || |
212 | 0 | (iface->MinRtrAdvInterval > MAX_MinRtrAdvInterval(iface))) { |
213 | 0 | flog(LOG_ERR, |
214 | 0 | "MinRtrAdvInterval for %s (%.2f) must be at least %.2f but no more than 3/4 of MaxRtrAdvInterval (%.2f)", |
215 | 0 | iface->props.name, iface->MinRtrAdvInterval, |
216 | 0 | MIPv6 ? MIN_MinRtrAdvInterval_MIPv6 : (int)MIN_MinRtrAdvInterval, MAX_MinRtrAdvInterval(iface)); |
217 | 0 | res = -1; |
218 | 0 | } |
219 | |
|
220 | 0 | if ((iface->MaxRtrAdvInterval < (MIPv6 ? MIN_MaxRtrAdvInterval_MIPv6 : MIN_MaxRtrAdvInterval)) || |
221 | 0 | (iface->MaxRtrAdvInterval > MAX_MaxRtrAdvInterval)) { |
222 | 0 | flog(LOG_ERR, "MaxRtrAdvInterval for %s (%.2f) must be between %.2f and %d", iface->props.name, |
223 | 0 | iface->MaxRtrAdvInterval, MIPv6 ? MIN_MaxRtrAdvInterval_MIPv6 : (int)MIN_MaxRtrAdvInterval, |
224 | 0 | MAX_MaxRtrAdvInterval); |
225 | 0 | res = -1; |
226 | 0 | } |
227 | |
|
228 | 0 | if (iface->MinDelayBetweenRAs < (MIPv6 ? MIN_DELAY_BETWEEN_RAS_MIPv6 : MIN_DELAY_BETWEEN_RAS)) { |
229 | 0 | flog(LOG_ERR, "MinDelayBetweenRAs for %s (%.2f) must be at least %.2f", iface->props.name, |
230 | 0 | iface->MinDelayBetweenRAs, MIPv6 ? MIN_DELAY_BETWEEN_RAS_MIPv6 : MIN_DELAY_BETWEEN_RAS); |
231 | 0 | res = -1; |
232 | 0 | } |
233 | |
|
234 | 0 | if ((iface->AdvLinkMTU != 0) && ((iface->AdvLinkMTU < MIN_AdvLinkMTU) || |
235 | 0 | (iface->sllao.if_maxmtu != -1 && (iface->AdvLinkMTU > iface->sllao.if_maxmtu)))) { |
236 | 0 | flog(LOG_ERR, "AdvLinkMTU for %s (%u) must be zero or between %u and %u", iface->props.name, iface->AdvLinkMTU, |
237 | 0 | MIN_AdvLinkMTU, iface->sllao.if_maxmtu); |
238 | 0 | res = -1; |
239 | 0 | } |
240 | |
|
241 | 0 | if (iface->ra_header_info.AdvReachableTime > MAX_AdvReachableTime) { |
242 | 0 | flog(LOG_ERR, "AdvReachableTime for %s (%u) must not be greater than %u", iface->props.name, |
243 | 0 | iface->ra_header_info.AdvReachableTime, MAX_AdvReachableTime); |
244 | 0 | res = -1; |
245 | 0 | } |
246 | |
|
247 | 0 | if (iface->ra_header_info.AdvDefaultLifetime < 0) |
248 | 0 | iface->ra_header_info.AdvDefaultLifetime = DFLT_AdvDefaultLifetime(iface); |
249 | |
|
250 | 0 | if ((iface->ra_header_info.AdvDefaultLifetime != 0) && |
251 | 0 | ((iface->ra_header_info.AdvDefaultLifetime > MAX_AdvDefaultLifetime) || |
252 | 0 | (iface->ra_header_info.AdvDefaultLifetime < MIN_AdvDefaultLifetime(iface)))) { |
253 | 0 | flog(LOG_ERR, "AdvDefaultLifetime for %s (%u) must be zero or between %u and %u", iface->props.name, |
254 | 0 | iface->ra_header_info.AdvDefaultLifetime, (int)MIN_AdvDefaultLifetime(iface), MAX_AdvDefaultLifetime); |
255 | 0 | res = -1; |
256 | 0 | } |
257 | | |
258 | | /* Mobile IPv6 ext */ |
259 | 0 | if (iface->mipv6.HomeAgentLifetime < 0) |
260 | 0 | iface->mipv6.HomeAgentLifetime = DFLT_HomeAgentLifetime(iface); |
261 | | |
262 | | /* Mobile IPv6 ext */ |
263 | 0 | if (iface->mipv6.AdvHomeAgentInfo) { |
264 | 0 | if ((iface->mipv6.HomeAgentLifetime > MAX_HomeAgentLifetime) || |
265 | 0 | (iface->mipv6.HomeAgentLifetime < MIN_HomeAgentLifetime)) { |
266 | 0 | flog(LOG_ERR, "HomeAgentLifetime for %s (%u) must be between %u and %u", iface->props.name, |
267 | 0 | iface->mipv6.HomeAgentLifetime, MIN_HomeAgentLifetime, MAX_HomeAgentLifetime); |
268 | 0 | res = -1; |
269 | 0 | } |
270 | 0 | } |
271 | | |
272 | | /* Mobile IPv6 ext */ |
273 | 0 | if (iface->mipv6.AdvHomeAgentInfo && !(iface->ra_header_info.AdvHomeAgentFlag)) { |
274 | 0 | flog(LOG_ERR, "AdvHomeAgentFlag for %s must be set with HomeAgentInfo", iface->props.name); |
275 | 0 | res = -1; |
276 | 0 | } |
277 | 0 | if (iface->mipv6.AdvMobRtrSupportFlag && !(iface->mipv6.AdvHomeAgentInfo)) { |
278 | 0 | flog(LOG_ERR, "AdvHomeAgentInfo for %s must be set with AdvMobRtrSupportFlag", iface->props.name); |
279 | 0 | res = -1; |
280 | 0 | } |
281 | | |
282 | | /* XXX: need this? prefix = iface->AdvPrefixList; */ |
283 | |
|
284 | 0 | while (prefix) { |
285 | 0 | if (prefix->PrefixLen > MAX_PrefixLen) { |
286 | 0 | flog(LOG_ERR, "invalid prefix length (%u) for %s", prefix->PrefixLen, iface->props.name); |
287 | 0 | res = -1; |
288 | 0 | } |
289 | |
|
290 | 0 | if (prefix->AdvPreferredLifetime > prefix->AdvValidLifetime) { |
291 | 0 | flog(LOG_ERR, "AdvValidLifetime for %s (%u) must be " |
292 | 0 | "greater than or equal to AdvPreferredLifetime for", |
293 | 0 | iface->props.name, prefix->AdvValidLifetime); |
294 | 0 | res = -1; |
295 | 0 | } |
296 | |
|
297 | 0 | prefix = prefix->next; |
298 | 0 | } |
299 | |
|
300 | 0 | struct AdvRoute *route = iface->AdvRouteList; |
301 | 0 | while (route) { |
302 | 0 | if (route->PrefixLen > MAX_PrefixLen) { |
303 | 0 | flog(LOG_ERR, "invalid route prefix length (%u) for %s", route->PrefixLen, iface->props.name); |
304 | 0 | res = -1; |
305 | 0 | } |
306 | | |
307 | | /* For the default route 0::/0, we need to explicitly check the |
308 | | * lifetime against the AdvDefaultLifetime value. |
309 | | * |
310 | | * If exactly one of the two has a zero value, then nodes processing |
311 | | * the RA may have a flap in their default route. |
312 | | * |
313 | | * AdvDefaultLifetime == 0 && route.AdvRouteLifetime > 0: |
314 | | * - default route is deleted and then re-added. |
315 | | * AdvDefaultLifetime > 0 && route.AdvRouteLifetime == 0: |
316 | | * - default route is added and then deleted. |
317 | | */ |
318 | 0 | if (IN6_IS_ADDR_UNSPECIFIED(&(route->Prefix))) { |
319 | 0 | int route_zerolife = (route->AdvRouteLifetime == 0); |
320 | 0 | int defaultroute_zerolife = (iface->ra_header_info.AdvDefaultLifetime == 0); |
321 | 0 | if (route_zerolife ^ defaultroute_zerolife) { |
322 | 0 | flog( |
323 | 0 | LOG_ERR, |
324 | 0 | "route 0::/0 lifetime (%u) conflicts with AdvDefaultLifetime (%u), default routes will flap!", |
325 | 0 | route->AdvRouteLifetime, iface->ra_header_info.AdvDefaultLifetime); |
326 | | // res = -1; // In some future version, abort on this configuration error. |
327 | 0 | } |
328 | 0 | } |
329 | |
|
330 | 0 | route = route->next; |
331 | 0 | } |
332 | |
|
333 | 0 | return res; |
334 | 0 | } |
335 | | |
336 | | struct Interface *find_iface_by_index(struct Interface *iface, int index) |
337 | 603 | { |
338 | 603 | for (; iface; iface = iface->next) { |
339 | 603 | if (iface->props.if_index == index) { |
340 | 603 | return iface; |
341 | 603 | } |
342 | 603 | } |
343 | | |
344 | 0 | return 0; |
345 | 603 | } |
346 | | |
347 | | struct Interface *find_iface_by_name(struct Interface *iface, const char *name) |
348 | 0 | { |
349 | 0 | if (!name) { |
350 | 0 | return 0; |
351 | 0 | } |
352 | | |
353 | 0 | for (; iface; iface = iface->next) { |
354 | 0 | if (strcmp(iface->props.name, name) == 0) { |
355 | 0 | return iface; |
356 | 0 | } |
357 | 0 | } |
358 | | |
359 | 0 | return 0; |
360 | 0 | } |
361 | | |
362 | | struct Interface *find_iface_by_time(struct Interface *iface) |
363 | 0 | { |
364 | 0 | if (!iface) { |
365 | 0 | return 0; |
366 | 0 | } |
367 | | |
368 | 0 | int timeout = next_time_msec(iface); |
369 | 0 | struct Interface *next = iface; |
370 | |
|
371 | 0 | for (iface = iface->next; iface; iface = iface->next) { |
372 | 0 | int t = next_time_msec(iface); |
373 | 0 | if (timeout > t) { |
374 | 0 | timeout = t; |
375 | 0 | next = iface; |
376 | 0 | } |
377 | 0 | } |
378 | |
|
379 | 0 | return next; |
380 | 0 | } |
381 | | |
382 | | void reschedule_iface(struct Interface *iface, double next) |
383 | 25 | { |
384 | | #ifdef HAVE_NETLINK |
385 | | if (!iface->state_info.changed && !iface->state_info.ready) { |
386 | | next = 10 * iface->MaxRtrAdvInterval; |
387 | | } else if (next == 0) { |
388 | | next = IFACE_SETUP_DELAY; |
389 | | } else |
390 | | #endif |
391 | 25 | if (iface->state_info.racount < MAX_INITIAL_RTR_ADVERTISEMENTS) { |
392 | 25 | next = min(MAX_INITIAL_RTR_ADVERT_INTERVAL, iface->MaxRtrAdvInterval); |
393 | 25 | } |
394 | | |
395 | 25 | dlog(LOG_DEBUG, 5, "%s next scheduled RA in %g second(s)", iface->props.name, next); |
396 | | |
397 | 25 | iface->times.next_multicast = next_timespec(next); |
398 | 25 | } |
399 | | |
400 | | void for_each_iface(struct Interface *ifaces, void (*foo)(struct Interface *, void *), void *data) |
401 | 0 | { |
402 | 0 | for (; ifaces; ifaces = ifaces->next) { |
403 | 0 | foo(ifaces, data); |
404 | 0 | } |
405 | 0 | } |
406 | | |
407 | | static void free_iface_list(struct Interface *iface) |
408 | 633 | { |
409 | 633 | while (iface) { |
410 | 0 | struct Interface *next_iface = iface->next; |
411 | |
|
412 | 0 | dlog(LOG_DEBUG, 4, "freeing interface %s", iface->props.name); |
413 | |
|
414 | 0 | struct AdvPrefix *prefix = iface->AdvPrefixList; |
415 | 0 | while (prefix) { |
416 | 0 | struct AdvPrefix *next_prefix = prefix->next; |
417 | |
|
418 | 0 | free(prefix); |
419 | 0 | prefix = next_prefix; |
420 | 0 | } |
421 | |
|
422 | 0 | struct AdvRoute *route = iface->AdvRouteList; |
423 | 0 | while (route) { |
424 | 0 | struct AdvRoute *next_route = route->next; |
425 | |
|
426 | 0 | free(route); |
427 | 0 | route = next_route; |
428 | 0 | } |
429 | |
|
430 | 0 | struct AdvRDNSS *rdnss = iface->AdvRDNSSList; |
431 | 0 | while (rdnss) { |
432 | 0 | struct AdvRDNSS *next_rdnss = rdnss->next; |
433 | |
|
434 | 0 | free(rdnss->AdvRDNSSAddr); |
435 | 0 | free(rdnss); |
436 | 0 | rdnss = next_rdnss; |
437 | 0 | } |
438 | |
|
439 | 0 | struct AdvDNSSL *dnssl = iface->AdvDNSSLList; |
440 | 0 | while (dnssl) { |
441 | 0 | struct AdvDNSSL *next_dnssl = dnssl->next; |
442 | |
|
443 | 0 | for (int i = 0; i < dnssl->AdvDNSSLNumber; i++) |
444 | 0 | free(dnssl->AdvDNSSLSuffixes[i]); |
445 | 0 | free(dnssl->AdvDNSSLSuffixes); |
446 | 0 | free(dnssl); |
447 | |
|
448 | 0 | dnssl = next_dnssl; |
449 | 0 | } |
450 | |
|
451 | 0 | struct AdvDNR *dnr = iface->AdvDNRList; |
452 | 0 | while (dnr) { |
453 | 0 | struct AdvDNR *next_dnr = dnr->next; |
454 | |
|
455 | 0 | free(dnr->AdvDNRAddrs); |
456 | 0 | free(dnr->AdvDNRADN); |
457 | 0 | free(dnr->AdvDNRSvcAlpn); |
458 | 0 | free(dnr->AdvDNRSvcDohpath); |
459 | 0 | free(dnr); |
460 | |
|
461 | 0 | dnr = next_dnr; |
462 | 0 | } |
463 | |
|
464 | 0 | struct AutogenIgnorePrefix *ignore_prefixes = iface->IgnorePrefixList; |
465 | 0 | while (ignore_prefixes) { |
466 | 0 | struct AutogenIgnorePrefix *next_ignore_prefix = ignore_prefixes->next; |
467 | |
|
468 | 0 | free(ignore_prefixes); |
469 | 0 | ignore_prefixes = next_ignore_prefix; |
470 | 0 | } |
471 | |
|
472 | 0 | struct Clients *clients = iface->ClientList; |
473 | 0 | while (clients) { |
474 | 0 | struct Clients *next_client = clients->next; |
475 | |
|
476 | 0 | free(clients); |
477 | 0 | clients = next_client; |
478 | 0 | } |
479 | |
|
480 | 0 | free(iface->props.if_addrs); |
481 | |
|
482 | 0 | free(iface->AdvCaptivePortalAPI); |
483 | |
|
484 | 0 | free(iface); |
485 | 0 | iface = next_iface; |
486 | 0 | } |
487 | 633 | } |
488 | | |
489 | | void free_ifaces(struct Interface *ifaces) |
490 | 633 | { |
491 | 633 | dlog(LOG_DEBUG, 3, "Freeing Interfaces"); |
492 | | |
493 | 633 | free_iface_list(ifaces); |
494 | 633 | } |