1/* $USAGI: ninfod_core.c,v 1.29 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#if STDC_HEADERS
43# include <stdio.h>
44# include <stdlib.h>
45# include <stddef.h>
46#else
47# if HAVE_STDLIB_H
48#  include <stdlib.h>
49# endif
50#endif
51#if ENABLE_THREADS && HAVE_PTHREAD_H
52# include <pthread.h>
53#endif
54#if HAVE_STRING_H
55# if !STDC_HEADERS && HAVE_MEMORY_H
56#  include <memory.h>
57# endif
58# include <string.h>
59#endif
60#if HAVE_STRINGS_H
61# include <strings.h>
62#endif
63#if HAVE_INTTYPES_H
64# include <inttypes.h>
65#else
66# if HAVE_STDINT_H
67#  include <stdint.h>
68# endif
69#endif
70#if HAVE_UNISTD_H
71# include <unistd.h>
72#endif
73
74#if TIME_WITH_SYS_TIME
75# include <sys/time.h>
76# include <time.h>
77#else
78# if HAVE_SYS_TIME_H
79#  include <sys/time.h>
80# else
81#  include <time.h>
82# endif
83#endif
84
85#if HAVE_SYS_UIO_H
86#include <sys/uio.h>
87#endif
88
89#if HAVE_NETINET_IN_H
90# include <netinet/in.h>
91#endif
92
93#if HAVE_NETINET_ICMP6_H
94# include <netinet/icmp6.h>
95#endif
96#ifndef HAVE_STRUCT_ICMP6_NODEINFO
97# include "icmp6_nodeinfo.h"
98#endif
99
100#if HAVE_NETDB_H
101# include <netdb.h>
102#endif
103#include <errno.h>
104
105#if HAVE_SYSLOG_H
106# include <syslog.h>
107#endif
108
109#include "ninfod.h"
110
111#ifndef offsetof
112# define offsetof(aggregate,member)	((size_t)&((aggregate *)0)->member)
113#endif
114
115#define ARRAY_SIZE(a)		(sizeof(a) / sizeof(a[0]))
116
117/* ---------- */
118/* ID */
119static char *RCSID __attribute__ ((unused)) = "$USAGI: ninfod_core.c,v 1.29 2003-07-16 09:49:01 yoshfuji Exp $";
120
121/* Variables */
122int initialized = 0;
123
124#if ENABLE_THREADS && HAVE_LIBPTHREAD
125pthread_attr_t pattr;
126#endif
127
128static uint32_t suptypes[(MAX_SUPTYPES+31)>>5];
129static size_t suptypes_len;
130
131/* ---------- */
132struct subjinfo {
133	uint8_t	code;
134	char	*name;
135	int	(*checksubj)(CHECKANDFILL_ARGS);
136	int	(*init)(INIT_ARGS);
137};
138
139static struct subjinfo subjinfo_table [] = {
140	[ICMP6_NI_SUBJ_IPV6] = {
141		.code = ICMP6_NI_SUBJ_IPV6,
142		.name = "IPv6",
143		//.init = init_nodeinfo_ipv6addr,
144		.checksubj = pr_nodeinfo_ipv6addr,
145	},
146	[ICMP6_NI_SUBJ_FQDN] = {
147		.code = ICMP6_NI_SUBJ_FQDN,
148		.name = "FQDN",
149		//.init = init_nodeinfo_nodename,
150		.checksubj = pr_nodeinfo_nodename,
151	},
152	[ICMP6_NI_SUBJ_IPV4] = {
153		.code = ICMP6_NI_SUBJ_IPV4,
154		.name = "IPv4",
155		//.init = init_nodeinfo_ipv4addr,
156		.checksubj = pr_nodeinfo_ipv4addr,
157	},
158};
159
160static struct subjinfo subjinfo_null = {
161	.name = "null",
162	.checksubj = pr_nodeinfo_noop,
163};
164
165static __inline__ struct subjinfo *subjinfo_lookup(int code)
166{
167	if (code >= ARRAY_SIZE(subjinfo_table))
168		return NULL;
169	if (subjinfo_table[code].name == NULL)
170		return NULL;
171	return &subjinfo_table[code];
172}
173
174/* ---------- */
175#define QTYPEINFO_F_RATELIMIT	0x1
176
177struct qtypeinfo {
178	uint16_t qtype;
179	char	*name;
180	int	(*getreply)(CHECKANDFILL_ARGS);
181	void	(*init)(INIT_ARGS);
182	int	flags;
183};
184
185static struct qtypeinfo qtypeinfo_table[] = {
186	[NI_QTYPE_NOOP]		= {
187		.qtype = NI_QTYPE_NOOP,
188		.name = "NOOP",
189		.getreply = pr_nodeinfo_noop,
190	},
191#if ENABLE_SUPTYPES
192	[NI_QTYPE_SUPTYPES]	= {
193		.qtype = NI_QTYPE_SUPTYPES,
194		.name = "SupTypes",
195		.getreply = pr_nodeinfo_suptypes,
196		.init = init_nodeinfo_suptypes,
197	},
198#endif
199	[NI_QTYPE_DNSNAME]	= {
200		.qtype = NI_QTYPE_DNSNAME,
201		.name = "DnsName",
202		.getreply = pr_nodeinfo_nodename,
203		.init = init_nodeinfo_nodename,
204	},
205	[NI_QTYPE_NODEADDR]	= {
206		.qtype = NI_QTYPE_NODEADDR,
207		.name = "NodeAddr",
208		.getreply = pr_nodeinfo_ipv6addr,
209		.init = init_nodeinfo_ipv6addr,
210	},
211	[NI_QTYPE_IPV4ADDR]	= {
212		.qtype = NI_QTYPE_IPV4ADDR,
213		.name = "IPv4Addr",
214		.getreply = pr_nodeinfo_ipv4addr,
215		.init = init_nodeinfo_ipv4addr,
216	},
217};
218
219static struct qtypeinfo qtypeinfo_unknown = {
220	.name = "unknown",
221	.getreply = pr_nodeinfo_unknown,
222	.flags = QTYPEINFO_F_RATELIMIT,
223};
224
225static struct qtypeinfo qtypeinfo_refused = {
226	.name = "refused",
227	.getreply = pr_nodeinfo_refused,
228	.flags = QTYPEINFO_F_RATELIMIT,
229};
230
231static __inline__ struct qtypeinfo *qtypeinfo_lookup(int qtype)
232{
233	if (qtype >= ARRAY_SIZE(qtypeinfo_table))
234		return &qtypeinfo_unknown;
235	if (qtypeinfo_table[qtype].name == NULL)
236		return &qtypeinfo_unknown;
237	return &qtypeinfo_table[qtype];
238}
239
240/* ---------- */
241/* noop */
242int pr_nodeinfo_noop(CHECKANDFILL_ARGS)
243{
244	DEBUG(LOG_DEBUG, "%s()\n", __func__);
245
246	if (subjlen) {
247		DEBUG(LOG_WARNING,
248		      "%s(): invalid subject length(%zu)\n",
249		      __func__, subjlen);
250		return 1;
251	}
252
253	if (reply) {
254		p->reply.ni_type = ICMP6_NI_REPLY;
255		p->reply.ni_code = ICMP6_NI_SUCCESS;
256		p->reply.ni_cksum = 0;
257		p->reply.ni_qtype = htons(NI_QTYPE_NOOP);
258		p->reply.ni_flags = flags;
259	}
260
261	if (subj_if)
262		*subj_if = 0;
263
264	return 0;
265}
266
267#if ENABLE_SUPTYPES
268/* suptypes */
269int pr_nodeinfo_suptypes(CHECKANDFILL_ARGS)
270{
271	DEBUG(LOG_DEBUG, "%s()\n", __func__);
272
273	if (subjlen) {
274		DEBUG(LOG_WARNING, "%s(): invalid subject length(%zu)\n",
275		      __func__, subjlen);
276		return 1;
277	}
278
279	if (reply) {
280		p->reply.ni_type = ICMP6_NI_REPLY;
281		p->reply.ni_code = ICMP6_NI_SUCCESS;
282		p->reply.ni_cksum = 0;
283		p->reply.ni_qtype = htons(NI_QTYPE_SUPTYPES);
284		p->reply.ni_flags = flags&~NI_SUPTYPE_FLAG_COMPRESS;
285
286		p->replydatalen = suptypes_len<<2;
287		p->replydata = ni_malloc(p->replydatalen);
288		if (p->replydata == NULL) {
289			p->replydatalen = -1;
290			return -1;	/*XXX*/
291		}
292
293		memcpy(p->replydata, suptypes, p->replydatalen);
294	}
295	return 0;
296}
297
298void init_nodeinfo_suptypes(INIT_ARGS)
299{
300	size_t w, b;
301	int i;
302
303	if (!forced && initialized)
304		return;
305
306	memset(suptypes, 0, sizeof(suptypes));
307	suptypes_len = 0;
308
309	for (i=0; i < ARRAY_SIZE(qtypeinfo_table); i++) {
310		unsigned short qtype;
311
312		if (qtypeinfo_table[i].name == NULL)
313			continue;
314		qtype = qtypeinfo_table[i].qtype;
315		w = qtype>>5;
316		b = qtype&0x1f;
317		if (w >= ARRAY_SIZE(suptypes)) {
318			/* This is programming error. */
319			DEBUG(LOG_ERR, "Warning: Too Large Supported Types\n");
320			exit(1);
321		}
322		suptypes[w] |= htonl(1<<b);
323
324		if (suptypes_len < w)
325			suptypes_len = w;
326	}
327	suptypes_len++;
328}
329#endif
330
331/* ---------- */
332/* unknown qtype response */
333int pr_nodeinfo_unknown(CHECKANDFILL_ARGS)
334{
335	if (!reply)
336		return -1;	/*???*/
337
338	p->reply.ni_type = ICMP6_NI_REPLY;
339	p->reply.ni_code = ICMP6_NI_UNKNOWN;
340	p->reply.ni_cksum = 0;
341	//p->reply.ni_qtype = 0;
342	p->reply.ni_flags = flags;
343
344	p->replydata = NULL;
345	p->replydatalen = 0;
346
347	return 0;
348}
349
350/* refused response */
351int pr_nodeinfo_refused(CHECKANDFILL_ARGS)
352{
353	if (!reply)
354		return -1;	/*???*/
355
356	p->reply.ni_type = ICMP6_NI_REPLY;
357	p->reply.ni_code = ICMP6_NI_REFUSED;
358	p->reply.ni_cksum = 0;
359	//p->reply.ni_qtype = 0;
360	p->reply.ni_flags = flags;
361
362	p->replydata = NULL;
363	p->replydatalen = 0;
364
365	return 0;
366}
367
368/* ---------- */
369/* Policy */
370static int ni_policy(struct packetcontext *p)
371{
372	const struct in6_addr *saddr = &((const struct sockaddr_in6 *)&p->addr)->sin6_addr;
373
374	/*
375	 * >0: reply
376	 *  0: refused
377	 * <0: discard
378	 */
379
380	/* Default policy is to refuse queries from
381	 * non-local addresses; loopback, link-local or
382	 * site-local are okay
383	 */
384	if (!(IN6_IS_ADDR_LINKLOCAL(saddr) ||
385	      IN6_IS_ADDR_SITELOCAL(saddr) ||
386	      IN6_IS_ADDR_LOOPBACK(saddr)))
387		return 0;
388	return 1;
389}
390
391/* ---------- */
392void init_core(int forced)
393{
394	int i;
395
396	DEBUG(LOG_DEBUG, "%s()\n", __func__);
397
398	if (!initialized || forced) {
399		struct timeval tv;
400		unsigned int seed = 0;
401		pid_t pid;
402
403		if (gettimeofday(&tv, NULL) < 0) {
404			DEBUG(LOG_WARNING, "%s(): failed to gettimeofday()\n", __func__);
405		} else {
406			seed = (tv.tv_usec & 0xffffffff);
407		}
408
409		pid = getpid();
410		seed ^= (((unsigned long)pid) & 0xffffffff);
411
412		srand(seed);
413
414#if ENABLE_THREADS && HAVE_LIBPTHREAD
415		if (initialized)
416			pthread_attr_destroy(&pattr);
417
418		pthread_attr_init(&pattr);
419		pthread_attr_setdetachstate(&pattr, PTHREAD_CREATE_DETACHED);
420#endif
421	}
422
423	for (i=0; i < ARRAY_SIZE(subjinfo_table); i++) {
424		if (subjinfo_table[i].name == NULL)
425			continue;
426		if (subjinfo_table[i].init)
427			subjinfo_table[i].init(forced);
428	}
429
430	for (i=0; i < ARRAY_SIZE(qtypeinfo_table); i++) {
431		if (qtypeinfo_table[i].name == NULL)
432			continue;
433		if (qtypeinfo_table[i].init)
434			qtypeinfo_table[i].init(forced);
435	}
436
437	initialized = 1;
438
439	return;
440}
441
442#if ENABLE_THREADS && HAVE_LIBPTHREAD
443static void *ni_send_thread(void *data)
444{
445	int ret;
446	DEBUG(LOG_DEBUG, "%s(): thread=%ld\n", __func__, pthread_self());
447	ret = ni_send(data);
448	DEBUG(LOG_DEBUG, "%s(): thread=%ld => %d\n", __func__, pthread_self(), ret);
449	return NULL;
450}
451#else
452static int ni_send_fork(struct packetcontext *p)
453{
454	pid_t child = fork();
455	if (child < 0)
456		return -1;
457	if (child == 0) {
458		pid_t grandchild = fork();
459		if (grandchild < 0)
460			exit(1);
461		if (grandchild == 0) {
462			int ret;
463			DEBUG(LOG_DEBUG, "%s(): worker=%d\n",
464			      __func__, getpid());
465			ret = ni_send(p);
466			DEBUG(LOG_DEBUG, "%s(): worker=%d => %d\n",
467			      __func__, getpid(), ret);
468			exit(ret > 0 ? 1 : 0);
469		}
470		ni_free(p->replydata);
471		ni_free(p);
472		exit(0);
473	} else {
474		waitpid(child, NULL, 0);
475		ni_free(p->replydata);
476		ni_free(p);
477	}
478	return 0;
479}
480#endif
481
482static int ni_ratelimit(void)
483{
484	static struct timeval last;
485	struct timeval tv, sub;
486
487	if (gettimeofday(&tv, NULL) < 0) {
488		DEBUG(LOG_WARNING, "%s(): gettimeofday(): %s\n",
489		      __func__, strerror(errno));
490		return -1;
491	}
492
493	if (!timerisset(&last)) {
494		last = tv;
495		return 0;
496	}
497
498	timersub(&tv, &last, &sub);
499
500	if (sub.tv_sec < 1)
501		return 1;
502
503	last = tv;
504	return 0;
505}
506
507int pr_nodeinfo(struct packetcontext *p)
508{
509	struct icmp6_nodeinfo *query = (struct icmp6_nodeinfo *)p->query;
510
511	char *subject = (char *)(query + 1);
512	size_t subjlen;
513	struct subjinfo *subjinfo;
514	struct qtypeinfo *qtypeinfo;
515	int replyonsubjcheck = 0;
516	unsigned int subj_if;
517#if ENABLE_DEBUG
518	char printbuf[128];
519	int i;
520	char *cp;
521#endif
522#if ENABLE_THREADS && HAVE_PTHREAD_H
523	pthread_t thread;
524#endif
525	int rc;
526
527	/* Step 0: Check destination address
528	 *		discard non-linklocal multicast
529	 *		discard non-nigroup multicast address(?)
530	 */
531	if (IN6_IS_ADDR_MULTICAST(&p->pktinfo.ipi6_addr)) {
532		if (!IN6_IS_ADDR_MC_LINKLOCAL(&p->pktinfo.ipi6_addr)) {
533			DEBUG(LOG_WARNING,
534			      "Destination is non-link-local multicast address.\n");
535			ni_free(p);
536			return -1;
537		}
538#if 0
539		/* Do not discard NI Queries to multicast address
540		 * other than its own NI Group Address(es) by default.
541		 */
542		if (!check_nigroup(&p->pktinfo.ipi6_addr)) {
543			DEBUG(LOG_WARNING,
544			      "Destination is link-local multicast address other than "
545			      "NI Group address.\n");
546			ni_free(p);
547			return -1;
548		}
549#endif
550	}
551
552	/* Step 1: Check length */
553	if (p->querylen < sizeof(struct icmp6_nodeinfo)) {
554		DEBUG(LOG_WARNING, "Query too short\n");
555		ni_free(p);
556		return -1;
557	}
558
559#if ENABLE_DEBUG
560	cp = printbuf;
561	for (i = 0; i < sizeof(query->icmp6_ni_nonce); i++) {
562		cp += sprintf(cp, " %02x", query->icmp6_ni_nonce[i]);
563	}
564	DEBUG(LOG_DEBUG, "%s(): qtype=%d, flags=0x%04x, nonce[] = {%s }\n",
565	      __func__,
566	      ntohs(query->ni_qtype), ntohs(query->ni_flags), printbuf);
567#endif
568
569	subjlen = p->querylen - sizeof(struct icmp6_nodeinfo);
570
571	/* Step 2: Check Subject Code */
572	switch(htons(query->ni_qtype)) {
573	case NI_QTYPE_NOOP:
574	case NI_QTYPE_SUPTYPES:
575		if (query->ni_code != ICMP6_NI_SUBJ_FQDN) {
576			DEBUG(LOG_WARNING,
577			      "%s(): invalid/unknown code %u\n",
578			      __func__, query->ni_code);
579			subjlen = 0;
580		}
581		subjinfo = &subjinfo_null;
582		break;
583	default:
584		subjinfo = subjinfo_lookup(query->ni_code);
585		if (!subjinfo) {
586			DEBUG(LOG_WARNING,
587			      "%s(): unknown code %u\n",
588			      __func__, query->ni_code);
589			ni_free(p);
590			return -1;
591		}
592	}
593
594	/* Step 3: Lookup Qtype */
595	qtypeinfo = qtypeinfo_lookup(ntohs(query->ni_qtype));
596
597	/* Step 4: Check Subject
598	 *         (And fill reply if it is available now)
599	 */
600	if (qtypeinfo->getreply == subjinfo->checksubj)
601		replyonsubjcheck = 1;
602
603	if (subjinfo->checksubj(p,
604				subject, subjlen,
605				query->ni_flags,
606				replyonsubjcheck ? NULL : &subj_if,
607				replyonsubjcheck)) {
608		if (p->replydatalen < 0) {
609			DEBUG(LOG_WARNING,
610			      "failed to make reply: %s\n",
611			      strerror(errno));
612		}
613		ni_free(p);
614		return -1;
615	}
616
617	/* XXX: Step 5: Check the policy */
618	rc = ni_policy(p);
619	if (rc <= 0) {
620		ni_free(p->replydata);
621		p->replydata = NULL;
622		p->replydatalen = 0;
623		if (rc < 0) {
624			DEBUG(LOG_WARNING, "Ignored by policy.\n");
625			ni_free(p);
626			return -1;
627		}
628		DEBUG(LOG_WARNING, "Refused by policy.\n");
629		replyonsubjcheck = 0;
630		qtypeinfo = &qtypeinfo_refused;
631	}
632
633	/* Step 6: Fill the reply if not yet done */
634	if (!replyonsubjcheck) {
635		if (qtypeinfo->getreply(p,
636					NULL, 0,
637					query->ni_flags,
638					&subj_if,
639					1)) {
640			if (p->replydatalen) {
641				DEBUG(LOG_WARNING,
642				      "failed to make reply: %s\n",
643				      strerror(errno));
644			}
645			ni_free(p);
646			return -1;
647		}
648	}
649
650	/* Step 7: Rate Limit */
651	if (qtypeinfo->flags&QTYPEINFO_F_RATELIMIT &&
652	    ni_ratelimit()) {
653		ni_free(p->replydata);
654		ni_free(p);
655		return -1;
656	}
657
658	/* Step 8: Fill Qtype / Nonce */
659	p->reply.ni_qtype = query->ni_qtype;
660	memcpy(p->reply.icmp6_ni_nonce, query->icmp6_ni_nonce, sizeof(p->reply.icmp6_ni_nonce));
661
662	/* Step 9: Source address selection */
663	if (IN6_IS_ADDR_MULTICAST(&p->pktinfo.ipi6_addr)) {
664		/* if query was sent to multicast address,
665		 * use source address selection in kernel.
666		 * XXX: anycast?
667		 */
668		memset(&p->pktinfo.ipi6_addr, 0, sizeof(p->pktinfo.ipi6_addr));
669
670	 	/* Random Delay between zero and MAX_ANYCAST_DELAY_TIME is
671		 * required if query was sent to anycast or multicast address.
672		 */
673		p->delay = (int) (MAX_ANYCAST_DELAY_TIME*rand()/(RAND_MAX+1.0));
674	} else {
675		p->delay = 0;
676	}
677
678	/* Step 10: Send the reply
679	 * XXX: with possible random delay */
680#if ENABLE_THREADS && HAVE_LIBPTHREAD
681	/* ni_send_thread() frees p */
682	if (pthread_create(&thread, &pattr, ni_send_thread, p)) {
683		ni_free(p->replydata);
684		ni_free(p);
685		return -1;
686	}
687#else
688	/* ni_send_fork() frees p */
689	if (ni_send_fork(p)) {
690		ni_free(p->replydata);
691		ni_free(p);
692		return -1;
693	}
694#endif
695
696	return 0;
697}
698
699