1 /* $USAGI: ninfod_addrs.c,v 1.18 2003-07-16 09:49:01 yoshfuji Exp $ */
2 /*
3 * Copyright (C) 2002 USAGI/WIDE Project.
4 * All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 * 1. Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 * 3. Neither the name of the project nor the names of its contributors
15 * may be used to endorse or promote products derived from this software
16 * without specific prior written permission.
17 *
18 * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
19 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21 * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
22 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
24 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
25 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
26 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
27 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
28 * SUCH DAMAGE.
29 */
30 /*
31 * Author:
32 * YOSHIFUJI Hideaki <yoshfuji@linux-ipv6.org>
33 */
34
35 #if HAVE_CONFIG_H
36 #include "config.h"
37 #endif
38
39 #if HAVE_SYS_TYPES_H
40 # include <sys/types.h>
41 #endif
42
43 #if STDC_HEADERS
44 # include <stdio.h>
45 # include <stdlib.h>
46 # include <stddef.h>
47 #else
48 # if HAVE_STDLIB_H
49 # include <stdlib.h>
50 # endif
51 #endif
52 #if HAVE_STRING_H
53 # if !STDC_HEADERS && HAVE_MEMORY_H
54 # include <memory.h>
55 # endif
56 # include <string.h>
57 #endif
58 #if HAVE_STRINGS_H
59 # include <strings.h>
60 #endif
61 #if HAVE_INTTYPES_H
62 # include <inttypes.h>
63 #else
64 # if HAVE_STDINT_H
65 # include <stdint.h>
66 # endif
67 #endif
68 #if HAVE_UNISTD_H
69 # include <unistd.h>
70 #endif
71
72 #if TIME_WITH_SYS_TIME
73 # include <sys/time.h>
74 # include <time.h>
75 #else
76 # if HAVE_SYS_TIME_H
77 # include <sys/time.h>
78 # else
79 # include <time.h>
80 # endif
81 #endif
82
83 #if HAVE_SYS_UIO_H
84 #include <sys/uio.h>
85 #endif
86
87 #include <sys/socket.h>
88 #if HAVE_LINUX_RTNETLINK_H
89 #include <asm/types.h>
90 #include <linux/rtnetlink.h>
91 #endif
92
93 #if HAVE_NETINET_IN_H
94 # include <netinet/in.h>
95 #endif
96
97 #if HAVE_NETINET_IP6_H
98 # include <netinet/ip6.h>
99 #endif
100
101 #if HAVE_NETINET_ICMP6_H
102 # include <netinet/icmp6.h>
103 #endif
104 #ifndef HAVE_STRUCT_ICMP6_NODEINFO
105 # include "icmp6_nodeinfo.h"
106 #endif
107
108 #if HAVE_NETDB_H
109 # include <netdb.h>
110 #endif
111 #include <errno.h>
112
113 #if HAVE_SYSLOG_H
114 # include <syslog.h>
115 #endif
116
117 #include "ninfod.h"
118 #include "ni_ifaddrs.h"
119
120 #ifndef offsetof
121 # define offsetof(aggregate,member) ((size_t)&((aggregate *)0)->member)
122 #endif
123
124 /* ---------- */
125 /* ID */
126 static char *RCSID __attribute__ ((unused)) = "$USAGI: ninfod_addrs.c,v 1.18 2003-07-16 09:49:01 yoshfuji Exp $";
127
128 /* ---------- */
129 /* ipv6 address */
init_nodeinfo_ipv6addr(INIT_ARGS)130 void init_nodeinfo_ipv6addr(INIT_ARGS)
131 {
132 DEBUG(LOG_DEBUG, "%s()\n", __func__);
133 return;
134 }
135
filter_ipv6addr(const struct in6_addr * ifaddr,unsigned int flags)136 int filter_ipv6addr(const struct in6_addr *ifaddr, unsigned int flags)
137 {
138 if (IN6_IS_ADDR_UNSPECIFIED(ifaddr) ||
139 IN6_IS_ADDR_LOOPBACK(ifaddr)) {
140 return 1;
141 } else if (IN6_IS_ADDR_V4COMPAT(ifaddr) ||
142 IN6_IS_ADDR_V4MAPPED(ifaddr)) {
143 return !(flags & NI_NODEADDR_FLAG_COMPAT);
144 } else if (IN6_IS_ADDR_LINKLOCAL(ifaddr)) {
145 return !(flags & NI_NODEADDR_FLAG_LINKLOCAL);
146 } else if (IN6_IS_ADDR_SITELOCAL(ifaddr)) {
147 return !(flags & NI_NODEADDR_FLAG_SITELOCAL);
148 }
149 return !(flags & NI_NODEADDR_FLAG_GLOBAL);
150 }
151
pr_nodeinfo_ipv6addr(CHECKANDFILL_ARGS)152 int pr_nodeinfo_ipv6addr(CHECKANDFILL_ARGS)
153 {
154 struct ni_ifaddrs *ifa0;
155 unsigned int ifindex = 0;
156
157 DEBUG(LOG_DEBUG, "%s()\n", __func__);
158
159 if (subject && subjlen != sizeof(struct in6_addr)) {
160 DEBUG(LOG_INFO,
161 "%s(): invalid subject length %zu for IPv6 Address Subject\n",
162 __func__, subjlen);
163 return 1;
164 }
165 if (ni_ifaddrs(&ifa0, AF_INET6))
166 return -1; /* failed to get addresses */
167
168 /* pass 0: consider subject and determine subjected interface */
169 if (subject) {
170 struct ni_ifaddrs *ifa;
171
172 for (ifa = ifa0; ifa; ifa = ifa->ifa_next) {
173 if (!ifa->ifa_addr)
174 continue;
175 if (ifa->ifa_flags & (IFA_F_TENTATIVE|IFA_F_SECONDARY))
176 continue;
177 if (!ifindex &&
178 IN6_ARE_ADDR_EQUAL(&p->pktinfo.ipi6_addr,
179 (struct in6_addr *)subject)) {
180 /*
181 * if subject is equal to destination
182 * address, receiving interface is
183 * the candidate subject interface.
184 */
185 ifindex = p->pktinfo.ipi6_ifindex;
186 }
187 if (!IN6_IS_ADDR_LOOPBACK((struct in6_addr *)subject) &&
188 IN6_ARE_ADDR_EQUAL((struct in6_addr *)ifa->ifa_addr,
189 (struct in6_addr *)subject)) {
190 /*
191 * address is assigned on some interface.
192 * if multiple interfaces have the same interface,
193 * 1) prefer receiving interface
194 * 2) use first found one
195 */
196 if (!ifindex ||
197 (p->pktinfo.ipi6_ifindex == ifindex))
198 ifindex = ifa->ifa_ifindex;
199 }
200 }
201 if (!ifindex) {
202 ni_freeifaddrs(ifa0);
203 return 1; /* subject not found */
204 }
205 if (subj_if)
206 *subj_if = ifindex;
207 } else {
208 ifindex = subj_if ? *subj_if : 0;
209 if (ifindex == 0)
210 ifindex = p->pktinfo.ipi6_ifindex;
211 if (ifindex == 0) {
212 ni_freeifaddrs(ifa0);
213 return 1; /* XXX */
214 }
215 }
216
217 if (reply) {
218 struct ni_ifaddrs *ifa;
219 unsigned int addrs0 = 0, paddrs0 = 0;
220 unsigned int addrs, paddrs = 0, daddrs = 0;
221
222 flags &= ~NI_NODEADDR_FLAG_TRUNCATE;
223
224 /* pass 1: count addresses and preferred addresses to be returned */
225 for (ifa = ifa0; ifa; ifa = ifa->ifa_next) {
226 if (!ifa->ifa_addr)
227 continue;
228 if (ifa->ifa_flags & (IFA_F_TENTATIVE|IFA_F_SECONDARY))
229 continue;
230 if (!(flags & NI_NODEADDR_FLAG_ALL) &&
231 ifa->ifa_ifindex != ifindex)
232 continue;
233 if (filter_ipv6addr((struct in6_addr *)ifa->ifa_addr, flags))
234 continue;
235
236 if (addrs0 + 1 >= ((MAX_REPLY_SIZE - sizeof(struct icmp6_nodeinfo)) / (sizeof(uint32_t) + sizeof(struct in6_addr)))) {
237 flags |= ~NI_NODEADDR_FLAG_TRUNCATE;
238 break;
239 }
240
241 addrs0++;
242 if (!(ifa->ifa_flags & IFA_F_DEPRECATED))
243 paddrs0++;
244 }
245
246 p->reply.ni_type = ICMP6_NI_REPLY;
247 p->reply.ni_code = ICMP6_NI_SUCCESS;
248 p->reply.ni_cksum = 0;
249 p->reply.ni_qtype = htons(NI_QTYPE_NODEADDR);
250 p->reply.ni_flags = flags&(NI_NODEADDR_FLAG_COMPAT|
251 NI_NODEADDR_FLAG_LINKLOCAL|
252 NI_NODEADDR_FLAG_SITELOCAL|
253 NI_NODEADDR_FLAG_GLOBAL);
254
255 /* pass 2: store addresses */
256 p->replydatalen = (sizeof(uint32_t)+sizeof(struct in6_addr)) * addrs0;
257 p->replydata = p->replydatalen ? ni_malloc(p->replydatalen) : NULL;
258
259 if (p->replydatalen && !p->replydata) {
260 p->reply.ni_flags |= NI_NODEADDR_FLAG_TRUNCATE;
261 addrs0 = paddrs0 = 0;
262 }
263
264 for (ifa = ifa0, addrs = 0;
265 ifa && addrs < addrs0;
266 ifa = ifa->ifa_next) {
267 char *cp;
268 uint32_t ttl;
269
270 if (!ifa->ifa_addr)
271 continue;
272 if (ifa->ifa_flags & (IFA_F_TENTATIVE|IFA_F_SECONDARY))
273 continue;
274 if (!(flags & NI_NODEADDR_FLAG_ALL) &&
275 ((subj_if && *subj_if) ? (ifa->ifa_ifindex != *subj_if) :
276 (ifa->ifa_ifindex != p->pktinfo.ipi6_ifindex)))
277 continue;
278 if (filter_ipv6addr((struct in6_addr *)ifa->ifa_addr, flags))
279 continue;
280
281 #if ENABLE_TTL
282 if (ifa->ifa_cacheinfo) {
283 ttl = ifa->ifa_cacheinfo->ifa_valid > 0x7fffffff ?
284 htonl(0x7fffffff) : htonl(ifa->ifa_cacheinfo->ifa_valid);
285 } else {
286 ttl = (ifa->ifa_flags & IFA_F_PERMANENT) ? htonl(0x7fffffff) : 0;
287 }
288 #else
289 ttl = 0;
290 #endif
291
292 cp = p->replydata +
293 (sizeof(uint32_t)+sizeof(struct in6_addr)) * (ifa->ifa_flags & IFA_F_DEPRECATED ? paddrs0+daddrs : paddrs);
294 memcpy(cp, &ttl, sizeof(ttl));
295 memcpy(cp + sizeof(ttl), ifa->ifa_addr, sizeof(struct in6_addr));
296
297 addrs++;
298 if (ifa->ifa_flags & IFA_F_DEPRECATED)
299 daddrs++;
300 else
301 paddrs++;
302 }
303 }
304
305 ni_freeifaddrs(ifa0);
306 return 0;
307 }
308
309 /* ipv4 address */
init_nodeinfo_ipv4addr(INIT_ARGS)310 void init_nodeinfo_ipv4addr(INIT_ARGS)
311 {
312 DEBUG(LOG_DEBUG, "%s()\n", __func__);
313 return;
314 }
315
filter_ipv4addr(const struct in_addr * ifaddr,unsigned int flags)316 int filter_ipv4addr(const struct in_addr *ifaddr, unsigned int flags)
317 {
318 return 0;
319 }
320
pr_nodeinfo_ipv4addr(CHECKANDFILL_ARGS)321 int pr_nodeinfo_ipv4addr(CHECKANDFILL_ARGS)
322 {
323 struct ni_ifaddrs *ifa0;
324 unsigned int ifindex = 0;
325
326 DEBUG(LOG_DEBUG, "%s()\n", __func__);
327
328 if (subject && subjlen != sizeof(struct in_addr)) {
329 DEBUG(LOG_INFO,
330 "%s(): invalid subject length %zu for IPv4 Address Subject\n",
331 __func__, subjlen);
332 return 1;
333 }
334 if (ni_ifaddrs(&ifa0, AF_INET))
335 return -1; /* failed to get addresses */
336
337 /* pass 0: consider subject and determine subjected interface */
338 if (subject) {
339 struct ni_ifaddrs *ifa;
340
341 for (ifa = ifa0; ifa; ifa = ifa->ifa_next) {
342 if (!ifa->ifa_addr)
343 continue;
344 if (ifa->ifa_flags & (IFA_F_TENTATIVE|IFA_F_SECONDARY))
345 continue;
346 if ((((struct in_addr *)subject)->s_addr != htonl(INADDR_LOOPBACK)) &&
347 memcmp((struct in_addr *)ifa->ifa_addr,
348 (struct in_addr *)subject,
349 sizeof(struct in_addr)) == 0) {
350 /*
351 * address is assigned on some interface.
352 * if multiple interfaces have the same interface,
353 * 1) prefer receiving interface
354 * 2) use first found one
355 */
356 if (!ifindex ||
357 (p->pktinfo.ipi6_ifindex == ifindex))
358 ifindex = ifa->ifa_ifindex;
359 }
360 }
361 if (!ifindex) {
362 ni_freeifaddrs(ifa0);
363 return 1; /* subject not found */
364 }
365 if (subj_if)
366 *subj_if = ifindex;
367 } else {
368 ifindex = subj_if ? *subj_if : 0;
369 if (ifindex == 0)
370 ifindex = p->pktinfo.ipi6_ifindex;
371 if (ifindex == 0) {
372 ni_freeifaddrs(ifa0);
373 return 1; /* XXX */
374 }
375 }
376
377 if (reply) {
378 struct ni_ifaddrs *ifa;
379 unsigned int addrs0 = 0, paddrs0 = 0;
380 unsigned int addrs, paddrs = 0, daddrs = 0;
381
382 flags &= ~NI_IPV4ADDR_FLAG_TRUNCATE;
383
384 /* pass 1: count addresses and preferred addresses to be returned */
385 for (ifa = ifa0; ifa; ifa = ifa->ifa_next) {
386 if (!ifa->ifa_addr)
387 continue;
388 #if 1 /* not used in kernel */
389 if (ifa->ifa_flags & (IFA_F_TENTATIVE))
390 continue;
391 #endif
392 if (!(flags & NI_NODEADDR_FLAG_ALL) &&
393 ((subj_if && *subj_if) ? (ifa->ifa_ifindex != *subj_if) :
394 (ifa->ifa_ifindex != p->pktinfo.ipi6_ifindex)))
395 continue;
396 if (filter_ipv4addr((struct in_addr *)ifa->ifa_addr, flags))
397 continue;
398
399 if (addrs0 + 1 >= ((MAX_REPLY_SIZE - sizeof(struct icmp6_nodeinfo)) / (sizeof(uint32_t) + sizeof(struct in_addr)))) {
400 flags |= NI_IPV4ADDR_FLAG_TRUNCATE;
401 break;
402 }
403
404 addrs0++;
405 if (!(ifa->ifa_flags & IFA_F_DEPRECATED))
406 paddrs0++;
407 }
408
409 p->reply.ni_type = ICMP6_NI_REPLY;
410 p->reply.ni_code = ICMP6_NI_SUCCESS;
411 p->reply.ni_cksum = 0;
412 p->reply.ni_qtype = htons(NI_QTYPE_IPV4ADDR);
413 p->reply.ni_flags = flags & NI_IPV4ADDR_FLAG_ALL;
414
415 /* pass 2: store addresses */
416 p->replydatalen = (sizeof(uint32_t)+sizeof(struct in_addr)) * addrs0;
417 p->replydata = addrs0 ? ni_malloc(p->replydatalen) : NULL;
418
419 if (p->replydatalen && !p->replydata) {
420 p->reply.ni_flags |= NI_NODEADDR_FLAG_TRUNCATE;
421 addrs0 = paddrs0 = 0;
422 }
423
424 for (ifa = ifa0, addrs = 0;
425 ifa && addrs < addrs0;
426 ifa = ifa->ifa_next) {
427 char *cp;
428 uint32_t ttl;
429
430 if (!ifa->ifa_addr)
431 continue;
432 #if 1 /* not used in kernel */
433 if (ifa->ifa_flags & (IFA_F_TENTATIVE))
434 continue;
435 #endif
436 if (!(flags & NI_NODEADDR_FLAG_ALL) &&
437 (ifa->ifa_ifindex != ifindex))
438 continue;
439 if (filter_ipv4addr((struct in_addr *)ifa->ifa_addr, flags))
440 continue;
441
442 #if ENABLE_TTL
443 if (ifa->ifa_cacheinfo) {
444 ttl = ifa->ifa_cacheinfo->ifa_valid > 0x7fffffff ?
445 htonl(0x7fffffff) : htonl(ifa->ifa_cacheinfo->ifa_valid);
446 } else {
447 ttl = 0; /*XXX*/
448 }
449 #else
450 ttl = 0;
451 #endif
452
453 cp = (p->replydata +
454 (sizeof(uint32_t)+sizeof(struct in_addr)) * (ifa->ifa_flags & IFA_F_DEPRECATED ? paddrs0+daddrs : paddrs));
455 memcpy(cp, &ttl, sizeof(ttl));
456 memcpy(cp + sizeof(ttl), ifa->ifa_addr, sizeof(struct in_addr));
457
458 addrs++;
459 if (ifa->ifa_flags & IFA_F_DEPRECATED)
460 daddrs++;
461 else
462 paddrs++;
463 }
464 }
465
466 ni_freeifaddrs(ifa0);
467 return 0;
468 }
469
470