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