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 */
126static char *RCSID __attribute__ ((unused)) = "$USAGI: ninfod_addrs.c,v 1.18 2003-07-16 09:49:01 yoshfuji Exp $";
127
128/* ---------- */
129/* ipv6 address */
130void init_nodeinfo_ipv6addr(INIT_ARGS)
131{
132	DEBUG(LOG_DEBUG, "%s()\n", __func__);
133	return;
134}
135
136int 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
152int 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 */
310void init_nodeinfo_ipv4addr(INIT_ARGS)
311{
312	DEBUG(LOG_DEBUG, "%s()\n", __func__);
313	return;
314}
315
316int filter_ipv4addr(const struct in_addr *ifaddr, unsigned int flags)
317{
318	return 0;
319}
320
321int 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