ping6.c revision 313379eb6b9da55f7371adef39a92153a0707d4a
1/* 2 * 3 * Modified for AF_INET6 by Pedro Roque 4 * 5 * <roque@di.fc.ul.pt> 6 * 7 * Original copyright notice included bellow 8 */ 9 10/* 11 * Copyright (c) 1989 The Regents of the University of California. 12 * All rights reserved. 13 * 14 * This code is derived from software contributed to Berkeley by 15 * Mike Muuss. 16 * 17 * Redistribution and use in source and binary forms, with or without 18 * modification, are permitted provided that the following conditions 19 * are met: 20 * 1. Redistributions of source code must retain the above copyright 21 * notice, this list of conditions and the following disclaimer. 22 * 2. Redistributions in binary form must reproduce the above copyright 23 * notice, this list of conditions and the following disclaimer in the 24 * documentation and/or other materials provided with the distribution. 25 * 3. All advertising materials mentioning features or use of this software 26 * must display the following acknowledgement: 27 * This product includes software developed by the University of 28 * California, Berkeley and its contributors. 29 * 4. Neither the name of the University nor the names of its contributors 30 * may be used to endorse or promote products derived from this software 31 * without specific prior written permission. 32 * 33 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 34 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 35 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 36 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 37 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 38 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 39 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 40 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 41 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 42 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 43 * SUCH DAMAGE. 44 */ 45 46#ifndef lint 47char copyright[] = 48"@(#) Copyright (c) 1989 The Regents of the University of California.\n\ 49 All rights reserved.\n"; 50#endif /* not lint */ 51 52/* 53 * P I N G . C 54 * 55 * Using the InterNet Control Message Protocol (ICMP) "ECHO" facility, 56 * measure round-trip-delays and packet loss across network paths. 57 * 58 * Author - 59 * Mike Muuss 60 * U. S. Army Ballistic Research Laboratory 61 * December, 1983 62 * 63 * Status - 64 * Public Domain. Distribution Unlimited. 65 * Bugs - 66 * More statistics could always be gathered. 67 * This program has to run SUID to ROOT to access the ICMP socket. 68 */ 69#include "ping_common.h" 70 71#include <linux/filter.h> 72#include <netinet/ip6.h> 73#include <netinet/icmp6.h> 74#include <resolv.h> 75#ifndef WITHOUT_IFADDRS 76#include <ifaddrs.h> 77#endif 78 79#ifdef USE_IDN 80#include <stringprep.h> 81#endif 82 83#include "ping6_niquery.h" 84#include "in6_flowlabel.h" 85 86#ifndef SOL_IPV6 87#define SOL_IPV6 IPPROTO_IPV6 88#endif 89 90#ifndef SOL_ICMPV6 91#define SOL_ICMPV6 IPPROTO_ICMPV6 92#endif 93 94/* RFC3542 */ 95#ifndef ICMP6_DST_UNREACH_BEYONDSCOPE 96#define ICMP6_DST_UNREACH_BEYONDSCOPE ICMP6_DST_UNREACH_NOTNEIGHBOR 97#endif 98 99#if defined(ENABLE_PING6_RTHDR) && !defined(ENABLE_PING6_RTHDR_RFC3542) 100#ifndef IPV6_SRCRT_TYPE_0 101#define IPV6_SRCRT_TYPE_0 0 102#endif 103#endif 104 105#ifndef MLD_LISTENER_QUERY 106#define MLD_LISTENER_QUERY 130 107#define MLD_LISTENER_REPORT 131 108#define MLD_LISTENER_REDUCTION 132 109#endif 110 111#define BIT_CLEAR(nr, addr) do { ((__u32 *)(addr))[(nr) >> 5] &= ~(1U << ((nr) & 31)); } while(0) 112#define BIT_SET(nr, addr) do { ((__u32 *)(addr))[(nr) >> 5] |= (1U << ((nr) & 31)); } while(0) 113#define BIT_TEST(nr, addr) do { (__u32 *)(addr))[(nr) >> 5] & (1U << ((nr) & 31)); } while(0) 114 115#ifndef ICMP6_FILTER_WILLPASS 116#define ICMP6_FILTER_WILLPASS(type, filterp) \ 117 (BIT_TEST((type), filterp) == 0) 118 119#define ICMP6_FILTER_WILLBLOCK(type, filterp) \ 120 BIT_TEST((type), filterp) 121 122#define ICMP6_FILTER_SETPASS(type, filterp) \ 123 BIT_CLEAR((type), filterp) 124 125#define ICMP6_FILTER_SETBLOCK(type, filterp) \ 126 BIT_SET((type), filterp) 127 128#define ICMP6_FILTER_SETPASSALL(filterp) \ 129 memset(filterp, 0, sizeof(struct icmp6_filter)); 130 131#define ICMP6_FILTER_SETBLOCKALL(filterp) \ 132 memset(filterp, 0xFF, sizeof(struct icmp6_filter)); 133#endif 134 135#define MAXPACKET 128000 /* max packet size */ 136 137#ifdef SO_TIMESTAMP 138#define HAVE_SIN6_SCOPEID 1 139#endif 140 141#ifndef SCOPE_DELIMITER 142# define SCOPE_DELIMITER '%' 143#endif 144 145__u32 flowlabel; 146__u32 tclass; 147#ifdef ENABLE_PING6_RTHDR 148struct cmsghdr *srcrt; 149#endif 150 151struct sockaddr_in6 whereto; /* who to ping */ 152u_char outpack[MAXPACKET]; 153int maxpacket = sizeof(outpack); 154 155static unsigned char cmsgbuf[4096]; 156static int cmsglen = 0; 157 158static char * pr_addr(struct in6_addr *addr); 159static char * pr_addr_n(struct in6_addr *addr); 160static int pr_icmph(__u8 type, __u8 code, __u32 info); 161static void usage(void) __attribute((noreturn)); 162 163struct sockaddr_in6 source; 164char *device; 165int pmtudisc=-1; 166 167static int icmp_sock; 168 169#ifdef USE_GNUTLS 170# include <gnutls/openssl.h> 171#else 172# include <openssl/md5.h> 173#endif 174 175/* Node Information query */ 176int ni_query = -1; 177int ni_flag = 0; 178void *ni_subject = NULL; 179int ni_subject_len = 0; 180int ni_subject_type = -1; 181char *ni_group; 182 183static inline int ntohsp(__u16 *p) 184{ 185 __u16 v; 186 memcpy(&v, p, sizeof(v)); 187 return ntohs(v); 188} 189 190#if defined(ENABLE_PING6_RTHDR) && !defined(ENABLE_PING6_RTHDR_RFC3542) 191size_t inet6_srcrt_space(int type, int segments) 192{ 193 if (type != 0 || segments > 24) 194 return 0; 195 196 return (sizeof(struct cmsghdr) + sizeof(struct ip6_rthdr0) + 197 segments * sizeof(struct in6_addr)); 198} 199 200extern struct cmsghdr * inet6_srcrt_init(void *bp, int type) 201{ 202 struct cmsghdr *cmsg; 203 204 if (type) 205 return NULL; 206 207 memset(bp, 0, sizeof(struct cmsghdr) + sizeof(struct ip6_rthdr0)); 208 cmsg = (struct cmsghdr *) bp; 209 210 cmsg->cmsg_len = sizeof(struct cmsghdr) + sizeof(struct ip6_rthdr0); 211 cmsg->cmsg_level = SOL_IPV6; 212 cmsg->cmsg_type = IPV6_RTHDR; 213 214 return cmsg; 215} 216 217int inet6_srcrt_add(struct cmsghdr *cmsg, const struct in6_addr *addr) 218{ 219 struct ip6_rthdr0 *hdr; 220 221 hdr = (struct ip6_rthdr0 *) CMSG_DATA(cmsg); 222 223 cmsg->cmsg_len += sizeof(struct in6_addr); 224 hdr->ip6r0_len += sizeof(struct in6_addr) / 8; 225 226 memcpy(&hdr->ip6r0_addr[hdr->ip6r0_segleft++], addr, 227 sizeof(struct in6_addr)); 228 229 return 0; 230} 231#endif 232 233unsigned int if_name2index(const char *ifname) 234{ 235 unsigned int i = if_nametoindex(ifname); 236 if (!i) { 237 fprintf(stderr, "ping: unknown iface %s\n", ifname); 238 exit(2); 239 } 240 return i; 241} 242 243struct niquery_option { 244 char *name; 245 int namelen; 246 int has_arg; 247 int data; 248 int (*handler)(int index, const char *arg); 249}; 250 251#define NIQUERY_OPTION(_name, _has_arg, _data, _handler) \ 252 { \ 253 .name = _name, \ 254 .namelen = sizeof(_name) - 1, \ 255 .has_arg = _has_arg, \ 256 .data = _data, \ 257 .handler = _handler \ 258 } 259 260static int niquery_option_name_handler(int index, const char *arg); 261static int niquery_option_ipv6_handler(int index, const char *arg); 262static int niquery_option_ipv6_flag_handler(int index, const char *arg); 263static int niquery_option_ipv4_handler(int index, const char *arg); 264static int niquery_option_ipv4_flag_handler(int index, const char *arg); 265static int niquery_option_subject_addr_handler(int index, const char *arg); 266static int niquery_option_subject_name_handler(int index, const char *arg); 267static int niquery_option_help_handler(int index, const char *arg); 268 269struct niquery_option niquery_options[] = { 270 NIQUERY_OPTION("name", 0, 0, niquery_option_name_handler), 271 NIQUERY_OPTION("fqdn", 0, 0, niquery_option_name_handler), 272 NIQUERY_OPTION("ipv6", 0, 0, niquery_option_ipv6_handler), 273 NIQUERY_OPTION("ipv6-all", 0, NI_IPV6ADDR_F_ALL, niquery_option_ipv6_flag_handler), 274 NIQUERY_OPTION("ipv6-compatible", 0, NI_IPV6ADDR_F_COMPAT, niquery_option_ipv6_flag_handler), 275 NIQUERY_OPTION("ipv6-linklocal", 0, NI_IPV6ADDR_F_LINKLOCAL, niquery_option_ipv6_flag_handler), 276 NIQUERY_OPTION("ipv6-sitelocal", 0, NI_IPV6ADDR_F_SITELOCAL, niquery_option_ipv6_flag_handler), 277 NIQUERY_OPTION("ipv6-global", 0, NI_IPV6ADDR_F_GLOBAL, niquery_option_ipv6_flag_handler), 278 NIQUERY_OPTION("ipv4", 0, 0, niquery_option_ipv4_handler), 279 NIQUERY_OPTION("ipv4-all", 0, NI_IPV4ADDR_F_ALL, niquery_option_ipv4_flag_handler), 280 NIQUERY_OPTION("subject-ipv6", 1, NI_SUBJ_IPV6, niquery_option_subject_addr_handler), 281 NIQUERY_OPTION("subject-ipv4", 1, NI_SUBJ_IPV4, niquery_option_subject_addr_handler), 282 NIQUERY_OPTION("subject-name", 1, 0, niquery_option_subject_name_handler), 283 NIQUERY_OPTION("subject-fqdn", 1, -1, niquery_option_subject_name_handler), 284 NIQUERY_OPTION("help", 0, 0, niquery_option_help_handler), 285 {}, 286}; 287 288static inline int niquery_is_enabled(void) 289{ 290 return ni_query >= 0; 291} 292 293#if PING6_NONCE_MEMORY 294__u8 *ni_nonce_ptr; 295#else 296struct { 297 struct timeval tv; 298 pid_t pid; 299} ni_nonce_secret; 300#endif 301 302static void niquery_init_nonce(void) 303{ 304#if PING6_NONCE_MEMORY 305 struct timeval tv; 306 unsigned long seed; 307 308 seed = (unsigned long)getpid(); 309 if (!gettimeofday(&tv, NULL)) 310 seed ^= tv.tv_usec; 311 srand(seed); 312 313 ni_nonce_ptr = calloc(NI_NONCE_SIZE, MAX_DUP_CHK); 314 if (!ni_nonce_ptr) { 315 perror("ping6: calloc"); 316 exit(2); 317 } 318 319 ni_nonce_ptr[0] = ~0; 320#else 321 gettimeofday(&ni_nonce_secret.tv, NULL); 322 ni_nonce_secret.pid = getpid(); 323#endif 324} 325 326#if !PING6_NONCE_MEMORY 327static int niquery_nonce(__u8 *nonce, int fill) 328{ 329 static __u8 digest[MD5_DIGEST_LENGTH]; 330 static int seq = -1; 331 332 if (fill || seq != *(__u16 *)nonce || seq < 0) { 333 MD5_CTX ctxt; 334 335 MD5_Init(&ctxt); 336 MD5_Update(&ctxt, &ni_nonce_secret, sizeof(ni_nonce_secret)); 337 MD5_Update(&ctxt, nonce, sizeof(__u16)); 338 MD5_Final(digest, &ctxt); 339 340 seq = *(__u16 *)nonce; 341 } 342 343 if (fill) { 344 memcpy(nonce + sizeof(__u16), digest, NI_NONCE_SIZE - sizeof(__u16)); 345 return 0; 346 } else { 347 if (memcmp(nonce + sizeof(__u16), digest, NI_NONCE_SIZE - sizeof(__u16))) 348 return -1; 349 return ntohsp((__u16 *)nonce); 350 } 351} 352#endif 353 354static inline void niquery_fill_nonce(__u16 seq, __u8 *nonce) 355{ 356 __u16 v = htons(seq); 357#if PING6_NONCE_MEMORY 358 int i; 359 360 memcpy(&ni_nonce_ptr[NI_NONCE_SIZE * (seq % MAX_DUP_CHK)], &v, sizeof(v)); 361 362 for (i = sizeof(v); i < NI_NONCE_SIZE; i++) 363 ni_nonce_ptr[NI_NONCE_SIZE * (seq % MAX_DUP_CHK) + i] = 0x100 * (rand() / (RAND_MAX + 1.0)); 364 365 memcpy(nonce, &ni_nonce_ptr[NI_NONCE_SIZE * (seq % MAX_DUP_CHK)], NI_NONCE_SIZE); 366#else 367 memcpy(nonce, &v, sizeof(v)); 368 niquery_nonce(nonce, 1); 369#endif 370} 371 372static inline int niquery_check_nonce(__u8 *nonce) 373{ 374#if PING6_NONCE_MEMORY 375 __u16 seq = ntohsp((__u16 *)nonce); 376 if (memcmp(nonce, &ni_nonce_ptr[NI_NONCE_SIZE * (seq % MAX_DUP_CHK)], NI_NONCE_SIZE)) 377 return -1; 378 return seq; 379#else 380 return niquery_nonce(nonce, 0); 381#endif 382} 383 384static int niquery_set_qtype(int type) 385{ 386 if (niquery_is_enabled() && ni_query != type) { 387 printf("Qtype conflict\n"); 388 return -1; 389 } 390 ni_query = type; 391 return 0; 392} 393 394static int niquery_option_name_handler(int index, const char *arg) 395{ 396 if (niquery_set_qtype(NI_QTYPE_NAME) < 0) 397 return -1; 398 return 0; 399} 400 401static int niquery_option_ipv6_handler(int index, const char *arg) 402{ 403 if (niquery_set_qtype(NI_QTYPE_IPV6ADDR) < 0) 404 return -1; 405 return 0; 406} 407 408static int niquery_option_ipv6_flag_handler(int index, const char *arg) 409{ 410 if (niquery_set_qtype(NI_QTYPE_IPV6ADDR) < 0) 411 return -1; 412 ni_flag |= niquery_options[index].data; 413 return 0; 414} 415 416static int niquery_option_ipv4_handler(int index, const char *arg) 417{ 418 if (niquery_set_qtype(NI_QTYPE_IPV4ADDR) < 0) 419 return -1; 420 return 0; 421} 422 423static int niquery_option_ipv4_flag_handler(int index, const char *arg) 424{ 425 if (niquery_set_qtype(NI_QTYPE_IPV4ADDR) < 0) 426 return -1; 427 ni_flag |= niquery_options[index].data; 428 return 0; 429} 430 431static inline int niquery_is_subject_valid(void) 432{ 433 return ni_subject_type >= 0 && ni_subject; 434} 435 436static int niquery_set_subject_type(int type) 437{ 438 if (niquery_is_subject_valid() && ni_subject_type != type) { 439 printf("Subject type conflict\n"); 440 return -1; 441 } 442 ni_subject_type = type; 443 return 0; 444} 445 446#define ARRAY_SIZE(array) (sizeof(array) / sizeof(array[0])) 447#define OFFSET_OF(type,elem) ((size_t)&((type *)0)->elem) 448 449static int niquery_option_subject_addr_handler(int index, const char *arg) 450{ 451 struct addrinfo hints, *ai0, *ai; 452 int offset; 453 int gai; 454 455 if (niquery_set_subject_type(niquery_options[index].data) < 0) 456 return -1; 457 458 ni_subject_type = niquery_options[index].data; 459 460 memset(&hints, 0, sizeof(hints)); 461 462 switch (niquery_options[index].data) { 463 case NI_SUBJ_IPV6: 464 ni_subject_len = sizeof(struct in6_addr); 465 offset = OFFSET_OF(struct sockaddr_in6, sin6_addr); 466 hints.ai_family = AF_INET6; 467 break; 468 case NI_SUBJ_IPV4: 469 ni_subject_len = sizeof(struct in_addr); 470 offset = OFFSET_OF(struct sockaddr_in, sin_addr); 471 hints.ai_family = AF_INET; 472 break; 473 default: 474 /* should not happen. */ 475 offset = -1; 476 } 477 478 hints.ai_socktype = SOCK_DGRAM; 479#ifdef USE_IDN 480 hints.ai_flags = AI_IDN; 481#endif 482 483 gai = getaddrinfo(arg, 0, &hints, &ai0); 484 if (gai) { 485 fprintf(stderr, "Unknown host: %s\n", arg); 486 return -1; 487 } 488 489 for (ai = ai0; ai; ai = ai->ai_next) { 490 void *p = malloc(ni_subject_len); 491 if (!p) 492 continue; 493 memcpy(p, (__u8 *)ai->ai_addr + offset, ni_subject_len); 494 free(ni_subject); 495 ni_subject = p; 496 break; 497 } 498 freeaddrinfo(ai0); 499 500 return 0; 501} 502 503static int niquery_option_subject_name_handler(int index, const char *arg) 504{ 505 static char nigroup_buf[INET6_ADDRSTRLEN + 1 + IFNAMSIZ]; 506 unsigned char *dnptrs[2], **dpp, **lastdnptr; 507 int n; 508 int i; 509 char *name, *p; 510 char *canonname = NULL, *idn = NULL; 511 unsigned char *buf = NULL; 512 size_t namelen; 513 size_t buflen; 514 int dots, fqdn = niquery_options[index].data; 515 MD5_CTX ctxt; 516 __u8 digest[MD5_DIGEST_LENGTH]; 517#ifdef USE_IDN 518 int rc; 519#endif 520 521 if (niquery_set_subject_type(NI_SUBJ_NAME) < 0) 522 return -1; 523 524#ifdef USE_IDN 525 name = stringprep_locale_to_utf8(arg); 526 if (!name) { 527 fprintf(stderr, "ping6: IDN support failed.\n"); 528 exit(2); 529 } 530#else 531 name = strdup(arg); 532 if (!name) 533 goto oomexit; 534#endif 535 536 p = strchr(name, SCOPE_DELIMITER); 537 if (p) { 538 *p = '\0'; 539 if (strlen(p + 1) >= IFNAMSIZ) { 540 fprintf(stderr, "ping6: too long scope name.\n"); 541 exit(1); 542 } 543 } 544 545#ifdef USE_IDN 546 rc = idna_to_ascii_8z(name, &idn, 0); 547 if (rc) { 548 fprintf(stderr, "ping6: IDN encoding error: %s\n", 549 idna_strerror(rc)); 550 exit(2); 551 } 552#else 553 idn = strdup(name); 554 if (!idn) 555 goto oomexit; 556#endif 557 558 namelen = strlen(idn); 559 canonname = malloc(namelen + 1); 560 if (!canonname) 561 goto oomexit; 562 563 dots = 0; 564 for (i = 0; i < namelen + 1; i++) { 565 canonname[i] = isupper(idn[i]) ? tolower(idn[i]) : idn[i]; 566 if (idn[i] == '.') 567 dots++; 568 } 569 570 if (fqdn == 0) { 571 /* guess if hostname is FQDN */ 572 fqdn = dots ? 1 : -1; 573 } 574 575 buflen = namelen + 3 + 1; /* dn_comp() requrires strlen() + 3, 576 plus non-fqdn indicator. */ 577 buf = malloc(buflen); 578 if (!buf) { 579 fprintf(stderr, "ping6: out of memory.\n"); 580 goto errexit; 581 } 582 583 dpp = dnptrs; 584 lastdnptr = &dnptrs[ARRAY_SIZE(dnptrs)]; 585 586 *dpp++ = (unsigned char *)buf; 587 *dpp++ = NULL; 588 589 n = dn_comp(canonname, (unsigned char *)buf, buflen, dnptrs, lastdnptr); 590 if (n < 0) { 591 fprintf(stderr, "ping6: Inappropriate subject name: %s\n", canonname); 592 goto errexit; 593 } else if (n >= buflen) { 594 fprintf(stderr, "ping6: dn_comp() returned too long result.\n"); 595 goto errexit; 596 } 597 598 MD5_Init(&ctxt); 599 MD5_Update(&ctxt, buf, buf[0]); 600 MD5_Final(digest, &ctxt); 601 602 sprintf(nigroup_buf, "ff02::2:%02x%02x:%02x%02x%s%s", 603 digest[0], digest[1], digest[2], digest[3], 604 p ? "%" : "", 605 p ? p + 1 : ""); 606 607 if (fqdn < 0) 608 buf[n] = 0; 609 610 free(ni_subject); 611 612 ni_group = nigroup_buf; 613 ni_subject = buf; 614 ni_subject_len = n + (fqdn < 0); 615 ni_group = nigroup_buf; 616 617 free(canonname); 618 free(idn); 619 free(name); 620 621 return 0; 622oomexit: 623 fprintf(stderr, "ping6: out of memory.\n"); 624errexit: 625 free(buf); 626 free(canonname); 627 free(idn); 628 free(name); 629 exit(1); 630} 631 632int niquery_option_help_handler(int index, const char *arg) 633{ 634 fprintf(stderr, "ping6 -N suboptions\n" 635 "\tHelp:\n" 636 "\t\thelp\n" 637 "\tQuery:\n" 638 "\t\tname,\n" 639 "\t\tipv6,ipv6-all,ipv6-compatible,ipv6-linklocal,ipv6-sitelocal,ipv6-global,\n" 640 "\t\tipv4,ipv4-all,\n" 641 "\tSubject:\n" 642 "\t\tsubject-ipv6=addr,subject-ipv4=addr,subject-name=name,subject-fqdn=name,\n" 643 ); 644 exit(2); 645} 646 647int niquery_option_handler(const char *opt_arg) 648{ 649 struct niquery_option *p; 650 int i; 651 int ret = -1; 652 for (i = 0, p = niquery_options; p->name; i++, p++) { 653 if (strncmp(p->name, opt_arg, p->namelen)) 654 continue; 655 if (!p->has_arg) { 656 if (opt_arg[p->namelen] == '\0') { 657 ret = p->handler(i, NULL); 658 if (ret >= 0) 659 break; 660 } 661 } else { 662 if (opt_arg[p->namelen] == '=') { 663 ret = p->handler(i, &opt_arg[p->namelen] + 1); 664 if (ret >= 0) 665 break; 666 } 667 } 668 } 669 if (!p->name) 670 ret = niquery_option_help_handler(0, NULL); 671 return ret; 672} 673 674static int hextoui(const char *str) 675{ 676 unsigned long val; 677 char *ep; 678 679 errno = 0; 680 val = strtoul(str, &ep, 16); 681 if (*ep) { 682 if (!errno) 683 errno = EINVAL; 684 return -1; 685 } 686 687 if (val > UINT_MAX) { 688 errno = ERANGE; 689 return UINT_MAX; 690 } 691 692 return val; 693} 694 695int main(int argc, char *argv[]) 696{ 697 int ch, hold, packlen; 698 u_char *packet; 699 char *target; 700 struct addrinfo hints, *ai; 701 int gai; 702 struct sockaddr_in6 firsthop; 703 int socket_errno; 704 struct icmp6_filter filter; 705 int err; 706#ifdef __linux__ 707 int csum_offset, sz_opt; 708#endif 709 static uint32_t scope_id = 0; 710 711 limit_capabilities(); 712 713#ifdef USE_IDN 714 setlocale(LC_ALL, ""); 715#endif 716 717 enable_capability_raw(); 718 719 icmp_sock = socket(AF_INET6, SOCK_RAW, IPPROTO_ICMPV6); 720 socket_errno = errno; 721 722 disable_capability_raw(); 723 724 source.sin6_family = AF_INET6; 725 memset(&firsthop, 0, sizeof(firsthop)); 726 firsthop.sin6_family = AF_INET6; 727 728 preload = 1; 729 while ((ch = getopt(argc, argv, COMMON_OPTSTR "F:N:")) != EOF) { 730 switch(ch) { 731 case 'F': 732 flowlabel = hextoui(optarg); 733 if (errno || (flowlabel & ~IPV6_FLOWINFO_FLOWLABEL)) { 734 fprintf(stderr, "ping: Invalid flowinfo %s\n", optarg); 735 exit(2); 736 } 737 options |= F_FLOWINFO; 738 break; 739 case 'Q': 740 tclass = hextoui(optarg); 741 if (errno || (tclass & ~0xff)) { 742 fprintf(stderr, "ping: Invalid tclass %s\n", optarg); 743 exit(2); 744 } 745 options |= F_TCLASS; 746 break; 747 case 'I': 748 if (strchr(optarg, ':')) { 749 char *p, *addr = strdup(optarg); 750 751 if (!addr) { 752 fprintf(stderr, "ping: out of memory\n"); 753 exit(2); 754 } 755 756 p = strchr(addr, SCOPE_DELIMITER); 757 if (p) { 758 *p = '\0'; 759 device = optarg + (p - addr) + 1; 760 } 761 762 if (inet_pton(AF_INET6, addr, (char*)&source.sin6_addr) <= 0) { 763 fprintf(stderr, "ping: invalid source address %s\n", optarg); 764 exit(2); 765 } 766 767 options |= F_STRICTSOURCE; 768 769 free(addr); 770 } else { 771 device = optarg; 772 } 773 break; 774 case 'M': 775 if (strcmp(optarg, "do") == 0) 776 pmtudisc = IPV6_PMTUDISC_DO; 777 else if (strcmp(optarg, "dont") == 0) 778 pmtudisc = IPV6_PMTUDISC_DONT; 779 else if (strcmp(optarg, "want") == 0) 780 pmtudisc = IPV6_PMTUDISC_WANT; 781 else { 782 fprintf(stderr, "ping: wrong value for -M: do, dont, want are valid ones.\n"); 783 exit(2); 784 } 785 break; 786 case 'V': 787 printf("ping6 utility, iputils-%s\n", SNAPSHOT); 788 exit(0); 789 case 'N': 790 if (niquery_option_handler(optarg) < 0) { 791 usage(); 792 break; 793 } 794 break; 795 COMMON_OPTIONS 796 common_options(ch); 797 break; 798 default: 799 usage(); 800 } 801 } 802 argc -= optind; 803 argv += optind; 804 805#ifdef ENABLE_PING6_RTHDR 806 while (argc > 1) { 807 struct in6_addr *addr; 808 809 if (srcrt == NULL) { 810 int space; 811 812 fprintf(stderr, "ping6: Warning: " 813 "Source routing is deprecated by RFC5095.\n"); 814 815#ifdef ENABLE_PING6_RTHDR_RFC3542 816 space = inet6_rth_space(IPV6_RTHDR_TYPE_0, argc - 1); 817#else 818 space = inet6_srcrt_space(IPV6_SRCRT_TYPE_0, argc - 1); 819#endif 820 if (space == 0) { 821 fprintf(stderr, "srcrt_space failed\n"); 822 exit(2); 823 } 824#ifdef ENABLE_PING6_RTHDR_RFC3542 825 if (cmsglen + CMSG_SPACE(space) > sizeof(cmsgbuf)) { 826 fprintf(stderr, "no room for options\n"); 827 exit(2); 828 } 829#else 830 if (space + cmsglen > sizeof(cmsgbuf)) { 831 fprintf(stderr, "no room for options\n"); 832 exit(2); 833 } 834#endif 835 srcrt = (struct cmsghdr*)(cmsgbuf+cmsglen); 836#ifdef ENABLE_PING6_RTHDR_RFC3542 837 memset(srcrt, 0, CMSG_SPACE(0)); 838 srcrt->cmsg_len = CMSG_LEN(space); 839 srcrt->cmsg_level = IPPROTO_IPV6; 840 srcrt->cmsg_type = IPV6_RTHDR; 841 inet6_rth_init(CMSG_DATA(srcrt), space, IPV6_RTHDR_TYPE_0, argc - 1); 842 cmsglen += CMSG_SPACE(space); 843#else 844 cmsglen += CMSG_ALIGN(space); 845 inet6_srcrt_init(srcrt, IPV6_SRCRT_TYPE_0); 846#endif 847 } 848 849 target = *argv; 850 851 memset(&hints, 0, sizeof(hints)); 852 hints.ai_family = AF_INET6; 853#ifdef USE_IDN 854 hints.ai_flags = AI_IDN; 855#endif 856 gai = getaddrinfo(target, NULL, &hints, &ai); 857 if (gai) { 858 fprintf(stderr, "unknown host\n"); 859 exit(2); 860 } 861 addr = &((struct sockaddr_in6 *)(ai->ai_addr))->sin6_addr; 862#ifdef ENABLE_PING6_RTHDR_RFC3542 863 inet6_rth_add(CMSG_DATA(srcrt), addr); 864#else 865 inet6_srcrt_add(srcrt, addr); 866#endif 867 if (IN6_IS_ADDR_UNSPECIFIED(&firsthop.sin6_addr)) { 868 memcpy(&firsthop.sin6_addr, addr, 16); 869#ifdef HAVE_SIN6_SCOPEID 870 firsthop.sin6_scope_id = ((struct sockaddr_in6 *)(ai->ai_addr))->sin6_scope_id; 871 /* Verify scope_id is the same as previous nodes */ 872 if (firsthop.sin6_scope_id && scope_id && firsthop.sin6_scope_id != scope_id) { 873 fprintf(stderr, "scope discrepancy among the nodes\n"); 874 exit(2); 875 } else if (!scope_id) { 876 scope_id = firsthop.sin6_scope_id; 877 } 878#endif 879 } 880 freeaddrinfo(ai); 881 882 argv++; 883 argc--; 884 } 885#endif 886 887 if (niquery_is_enabled()) { 888 niquery_init_nonce(); 889 890 if (!niquery_is_subject_valid()) { 891 ni_subject = &whereto.sin6_addr; 892 ni_subject_len = sizeof(whereto.sin6_addr); 893 ni_subject_type = NI_SUBJ_IPV6; 894 } 895 } 896 897 if (argc > 1) { 898#ifndef ENABLE_PING6_RTHDR 899 fprintf(stderr, "ping6: Source routing is deprecated by RFC5095.\n"); 900#endif 901 usage(); 902 } else if (argc == 1) { 903 target = *argv; 904 } else { 905 if (ni_query < 0 && ni_subject_type != NI_SUBJ_NAME) 906 usage(); 907 target = ni_group; 908 } 909 910 memset(&hints, 0, sizeof(hints)); 911 hints.ai_family = AF_INET6; 912#ifdef USE_IDN 913 hints.ai_flags = AI_IDN; 914#endif 915 gai = getaddrinfo(target, NULL, &hints, &ai); 916 if (gai) { 917 fprintf(stderr, "unknown host\n"); 918 exit(2); 919 } 920 921 memcpy(&whereto, ai->ai_addr, sizeof(whereto)); 922 whereto.sin6_port = htons(IPPROTO_ICMPV6); 923 924 if (memchr(target, ':', strlen(target))) 925 options |= F_NUMERIC; 926 927 freeaddrinfo(ai); 928 929 if (IN6_IS_ADDR_UNSPECIFIED(&firsthop.sin6_addr)) { 930 memcpy(&firsthop.sin6_addr, &whereto.sin6_addr, 16); 931#ifdef HAVE_SIN6_SCOPEID 932 firsthop.sin6_scope_id = whereto.sin6_scope_id; 933 /* Verify scope_id is the same as intermediate nodes */ 934 if (firsthop.sin6_scope_id && scope_id && firsthop.sin6_scope_id != scope_id) { 935 fprintf(stderr, "scope discrepancy among the nodes\n"); 936 exit(2); 937 } else if (!scope_id) { 938 scope_id = firsthop.sin6_scope_id; 939 } 940#endif 941 } 942 943 hostname = target; 944 945 if (IN6_IS_ADDR_UNSPECIFIED(&source.sin6_addr)) { 946 socklen_t alen; 947 int probe_fd = socket(AF_INET6, SOCK_DGRAM, 0); 948 949 if (probe_fd < 0) { 950 perror("socket"); 951 exit(2); 952 } 953 if (device) { 954#if defined(IPV6_RECVPKTINFO) || defined(HAVE_SIN6_SCOPEID) 955 unsigned int iface = if_name2index(device); 956#endif 957#ifdef IPV6_RECVPKTINFO 958 struct in6_pktinfo ipi; 959 960 memset(&ipi, 0, sizeof(ipi)); 961 ipi.ipi6_ifindex = iface; 962#endif 963 964#ifdef HAVE_SIN6_SCOPEID 965 if (IN6_IS_ADDR_LINKLOCAL(&firsthop.sin6_addr) || 966 IN6_IS_ADDR_MC_LINKLOCAL(&firsthop.sin6_addr)) 967 firsthop.sin6_scope_id = iface; 968#endif 969 enable_capability_raw(); 970 if ( 971#ifdef IPV6_RECVPKTINFO 972 setsockopt(probe_fd, IPPROTO_IPV6, IPV6_PKTINFO, &ipi, sizeof(ipi)) == -1 && 973#endif 974 setsockopt(probe_fd, SOL_SOCKET, SO_BINDTODEVICE, device, strlen(device)+1) == -1) { 975 perror("setsockopt(SO_BINDTODEVICE)"); 976 exit(2); 977 } 978 disable_capability_raw(); 979 } 980 firsthop.sin6_port = htons(1025); 981 if (connect(probe_fd, (struct sockaddr*)&firsthop, sizeof(firsthop)) == -1) { 982 perror("connect"); 983 exit(2); 984 } 985 alen = sizeof(source); 986 if (getsockname(probe_fd, (struct sockaddr*)&source, &alen) == -1) { 987 perror("getsockname"); 988 exit(2); 989 } 990 source.sin6_port = 0; 991 close(probe_fd); 992 993#ifndef WITHOUT_IFADDRS 994 if (device) { 995 struct ifaddrs *ifa0, *ifa; 996 997 if (getifaddrs(&ifa0)) { 998 perror("getifaddrs"); 999 exit(2); 1000 } 1001 1002 for (ifa = ifa0; ifa; ifa = ifa->ifa_next) { 1003 if (!ifa->ifa_addr || ifa->ifa_addr->sa_family != AF_INET6) 1004 continue; 1005 if (!strncmp(ifa->ifa_name, device, sizeof(device) - 1) && 1006 IN6_ARE_ADDR_EQUAL(&((struct sockaddr_in6 *)ifa->ifa_addr)->sin6_addr, 1007 &source.sin6_addr)) 1008 break; 1009 } 1010 if (!ifa) 1011 fprintf(stderr, "ping6: Warning: source address might be selected on device other than %s.\n", device); 1012 1013 freeifaddrs(ifa0); 1014 } 1015#endif 1016 } 1017#ifdef HAVE_SIN6_SCOPEID 1018 else if (device && (IN6_IS_ADDR_LINKLOCAL(&source.sin6_addr) || 1019 IN6_IS_ADDR_MC_LINKLOCAL(&source.sin6_addr))) 1020 source.sin6_scope_id = if_name2index(device); 1021#endif 1022 1023 if (icmp_sock < 0) { 1024 errno = socket_errno; 1025 perror("ping: icmp open socket"); 1026 exit(2); 1027 } 1028 1029 if (device) { 1030 struct cmsghdr *cmsg; 1031 struct in6_pktinfo *ipi; 1032 1033 cmsg = (struct cmsghdr*)(cmsgbuf+cmsglen); 1034 cmsglen += CMSG_SPACE(sizeof(*ipi)); 1035 cmsg->cmsg_len = CMSG_LEN(sizeof(*ipi)); 1036 cmsg->cmsg_level = SOL_IPV6; 1037 cmsg->cmsg_type = IPV6_PKTINFO; 1038 1039 ipi = (struct in6_pktinfo*)CMSG_DATA(cmsg); 1040 memset(ipi, 0, sizeof(*ipi)); 1041 ipi->ipi6_ifindex = if_name2index(device); 1042 } 1043 1044 if ((whereto.sin6_addr.s6_addr16[0]&htons(0xff00)) == htons (0xff00)) { 1045 if (uid) { 1046 if (interval < 1000) { 1047 fprintf(stderr, "ping: multicast ping with too short interval.\n"); 1048 exit(2); 1049 } 1050 if (pmtudisc >= 0 && pmtudisc != IPV6_PMTUDISC_DO) { 1051 fprintf(stderr, "ping: multicast ping does not fragment.\n"); 1052 exit(2); 1053 } 1054 } 1055 if (pmtudisc < 0) 1056 pmtudisc = IPV6_PMTUDISC_DO; 1057 } 1058 1059 if (pmtudisc >= 0) { 1060 if (setsockopt(icmp_sock, SOL_IPV6, IPV6_MTU_DISCOVER, &pmtudisc, sizeof(pmtudisc)) == -1) { 1061 perror("ping: IPV6_MTU_DISCOVER"); 1062 exit(2); 1063 } 1064 } 1065 1066 if ((options&F_STRICTSOURCE) && 1067 bind(icmp_sock, (struct sockaddr*)&source, sizeof(source)) == -1) { 1068 perror("ping: bind icmp socket"); 1069 exit(2); 1070 } 1071 1072 if (datalen >= sizeof(struct timeval) && (ni_query < 0)) { 1073 /* can we time transfer */ 1074 timing = 1; 1075 } 1076 packlen = datalen + 8 + 4096 + 40 + 8; /* 4096 for rthdr */ 1077 if (!(packet = (u_char *)malloc((u_int)packlen))) { 1078 fprintf(stderr, "ping: out of memory.\n"); 1079 exit(2); 1080 } 1081 1082 working_recverr = 1; 1083 hold = 1; 1084 if (setsockopt(icmp_sock, SOL_IPV6, IPV6_RECVERR, (char *)&hold, sizeof(hold))) { 1085 fprintf(stderr, "WARNING: your kernel is veeery old. No problems.\n"); 1086 working_recverr = 0; 1087 } 1088 1089 /* Estimate memory eaten by single packet. It is rough estimate. 1090 * Actually, for small datalen's it depends on kernel side a lot. */ 1091 hold = datalen+8; 1092 hold += ((hold+511)/512)*(40+16+64+160); 1093 sock_setbufs(icmp_sock, hold); 1094 1095#ifdef __linux__ 1096 csum_offset = 2; 1097 sz_opt = sizeof(int); 1098 1099 err = setsockopt(icmp_sock, SOL_RAW, IPV6_CHECKSUM, &csum_offset, sz_opt); 1100 if (err < 0) { 1101 /* checksum should be enabled by default and setting this 1102 * option might fail anyway. 1103 */ 1104 fprintf(stderr, "setsockopt(RAW_CHECKSUM) failed - try to continue."); 1105 } 1106#endif 1107 1108 /* 1109 * select icmp echo reply as icmp type to receive 1110 */ 1111 1112 ICMP6_FILTER_SETBLOCKALL(&filter); 1113 1114 if (!working_recverr) { 1115 ICMP6_FILTER_SETPASS(ICMP6_DST_UNREACH, &filter); 1116 ICMP6_FILTER_SETPASS(ICMP6_PACKET_TOO_BIG, &filter); 1117 ICMP6_FILTER_SETPASS(ICMP6_TIME_EXCEEDED, &filter); 1118 ICMP6_FILTER_SETPASS(ICMP6_PARAM_PROB, &filter); 1119 } 1120 1121 if (niquery_is_enabled()) 1122 ICMP6_FILTER_SETPASS(ICMPV6_NI_REPLY, &filter); 1123 else 1124 ICMP6_FILTER_SETPASS(ICMP6_ECHO_REPLY, &filter); 1125 1126 err = setsockopt(icmp_sock, IPPROTO_ICMPV6, ICMP6_FILTER, &filter, 1127 sizeof(struct icmp6_filter)); 1128 1129 if (err < 0) { 1130 perror("setsockopt(ICMP6_FILTER)"); 1131 exit(2); 1132 } 1133 1134 if (options & F_NOLOOP) { 1135 int loop = 0; 1136 if (setsockopt(icmp_sock, IPPROTO_IPV6, IPV6_MULTICAST_LOOP, 1137 &loop, sizeof(loop)) == -1) { 1138 perror ("can't disable multicast loopback"); 1139 exit(2); 1140 } 1141 } 1142 if (options & F_TTL) { 1143 if (setsockopt(icmp_sock, IPPROTO_IPV6, IPV6_MULTICAST_HOPS, 1144 &ttl, sizeof(ttl)) == -1) { 1145 perror ("can't set multicast hop limit"); 1146 exit(2); 1147 } 1148 if (setsockopt(icmp_sock, IPPROTO_IPV6, IPV6_UNICAST_HOPS, 1149 &ttl, sizeof(ttl)) == -1) { 1150 perror ("can't set unicast hop limit"); 1151 exit(2); 1152 } 1153 } 1154 1155 if (1) { 1156 int on = 1; 1157 if ( 1158#ifdef IPV6_RECVHOPLIMIT 1159 setsockopt(icmp_sock, IPPROTO_IPV6, IPV6_RECVHOPLIMIT, 1160 &on, sizeof(on)) == -1 && 1161 setsockopt(icmp_sock, IPPROTO_IPV6, IPV6_2292HOPLIMIT, 1162 &on, sizeof(on)) == -1 1163#else 1164 setsockopt(icmp_sock, IPPROTO_IPV6, IPV6_HOPLIMIT, 1165 &on, sizeof(on)) == -1 1166#endif 1167 ){ 1168 perror ("can't receive hop limit"); 1169 exit(2); 1170 } 1171 } 1172 1173 if (options & F_TCLASS) { 1174#ifdef IPV6_TCLASS 1175 if (setsockopt(icmp_sock, IPPROTO_IPV6, IPV6_TCLASS, 1176 &tclass, sizeof(tclass)) == -1) { 1177 perror ("setsockopt(IPV6_TCLASS)"); 1178 exit(2); 1179 } 1180#else 1181 fprintf(stderr, "Traffic class is not supported.\n"); 1182#endif 1183 } 1184 1185 if (options&F_FLOWINFO) { 1186#ifdef IPV6_FLOWINFO_SEND 1187 int on = 1; 1188#endif 1189#ifdef IPV6_FLOWLABEL_MGR 1190 char freq_buf[CMSG_ALIGN(sizeof(struct in6_flowlabel_req)) + cmsglen]; 1191 struct in6_flowlabel_req *freq = (struct in6_flowlabel_req *)freq_buf; 1192 int freq_len = sizeof(*freq); 1193#ifdef ENABLE_PING6_RTHDR 1194 if (srcrt) 1195 freq_len = CMSG_ALIGN(sizeof(*freq)) + srcrt->cmsg_len; 1196#endif 1197 memset(freq, 0, sizeof(*freq)); 1198 freq->flr_label = htonl(flowlabel & IPV6_FLOWINFO_FLOWLABEL); 1199 freq->flr_action = IPV6_FL_A_GET; 1200 freq->flr_flags = IPV6_FL_F_CREATE; 1201 freq->flr_share = IPV6_FL_S_EXCL; 1202 memcpy(&freq->flr_dst, &whereto.sin6_addr, 16); 1203#ifdef ENABLE_PING6_RTHDR 1204 if (srcrt) 1205 memcpy(freq_buf + CMSG_ALIGN(sizeof(*freq)), srcrt, srcrt->cmsg_len); 1206#endif 1207 if (setsockopt(icmp_sock, IPPROTO_IPV6, IPV6_FLOWLABEL_MGR, 1208 freq, freq_len) == -1) { 1209 perror ("can't set flowlabel"); 1210 exit(2); 1211 } 1212 flowlabel = freq->flr_label; 1213#ifdef ENABLE_PING6_RTHDR 1214 if (srcrt) { 1215 cmsglen = (char*)srcrt - (char*)cmsgbuf; 1216 srcrt = NULL; 1217 } 1218#endif 1219#else 1220 fprintf(stderr, "Flow labels are not supported.\n"); 1221 exit(2); 1222#endif 1223 1224#ifdef IPV6_FLOWINFO_SEND 1225 whereto.sin6_flowinfo = flowlabel; 1226 if (setsockopt(icmp_sock, IPPROTO_IPV6, IPV6_FLOWINFO_SEND, 1227 &on, sizeof(on)) == -1) { 1228 perror ("can't send flowinfo"); 1229 exit(2); 1230 } 1231#else 1232 fprintf(stderr, "Flowinfo is not supported.\n"); 1233 exit(2); 1234#endif 1235 } 1236 1237 printf("PING %s(%s) ", hostname, pr_addr(&whereto.sin6_addr)); 1238 if (flowlabel) 1239 printf(", flow 0x%05x, ", (unsigned)ntohl(flowlabel)); 1240 if (device || (options&F_STRICTSOURCE)) { 1241 printf("from %s %s: ", 1242 pr_addr_n(&source.sin6_addr), device ? : ""); 1243 } 1244 printf("%d data bytes\n", datalen); 1245 1246 setup(icmp_sock); 1247 1248 drop_capabilities(); 1249 1250 main_loop(icmp_sock, packet, packlen); 1251} 1252 1253int receive_error_msg() 1254{ 1255 int res; 1256 char cbuf[512]; 1257 struct iovec iov; 1258 struct msghdr msg; 1259 struct cmsghdr *cmsg; 1260 struct sock_extended_err *e; 1261 struct icmp6_hdr icmph; 1262 struct sockaddr_in6 target; 1263 int net_errors = 0; 1264 int local_errors = 0; 1265 int saved_errno = errno; 1266 1267 iov.iov_base = &icmph; 1268 iov.iov_len = sizeof(icmph); 1269 msg.msg_name = (void*)⌖ 1270 msg.msg_namelen = sizeof(target); 1271 msg.msg_iov = &iov; 1272 msg.msg_iovlen = 1; 1273 msg.msg_flags = 0; 1274 msg.msg_control = cbuf; 1275 msg.msg_controllen = sizeof(cbuf); 1276 1277 res = recvmsg(icmp_sock, &msg, MSG_ERRQUEUE|MSG_DONTWAIT); 1278 if (res < 0) 1279 goto out; 1280 1281 e = NULL; 1282 for (cmsg = CMSG_FIRSTHDR(&msg); cmsg; cmsg = CMSG_NXTHDR(&msg, cmsg)) { 1283 if (cmsg->cmsg_level == SOL_IPV6) { 1284 if (cmsg->cmsg_type == IPV6_RECVERR) 1285 e = (struct sock_extended_err *)CMSG_DATA(cmsg); 1286 } 1287 } 1288 if (e == NULL) 1289 abort(); 1290 1291 if (e->ee_origin == SO_EE_ORIGIN_LOCAL) { 1292 local_errors++; 1293 if (options & F_QUIET) 1294 goto out; 1295 if (options & F_FLOOD) 1296 write_stdout("E", 1); 1297 else if (e->ee_errno != EMSGSIZE) 1298 fprintf(stderr, "ping: local error: %s\n", strerror(e->ee_errno)); 1299 else 1300 fprintf(stderr, "ping: local error: Message too long, mtu=%u\n", e->ee_info); 1301 nerrors++; 1302 } else if (e->ee_origin == SO_EE_ORIGIN_ICMP6) { 1303 struct sockaddr_in6 *sin6 = (struct sockaddr_in6*)(e+1); 1304 1305 if (res < sizeof(icmph) || 1306 memcmp(&target.sin6_addr, &whereto.sin6_addr, 16) || 1307 icmph.icmp6_type != ICMP6_ECHO_REQUEST || 1308 icmph.icmp6_id != ident) { 1309 /* Not our error, not an error at all. Clear. */ 1310 saved_errno = 0; 1311 goto out; 1312 } 1313 1314 net_errors++; 1315 nerrors++; 1316 if (options & F_QUIET) 1317 goto out; 1318 if (options & F_FLOOD) { 1319 write_stdout("\bE", 2); 1320 } else { 1321 print_timestamp(); 1322 printf("From %s icmp_seq=%u ", pr_addr(&sin6->sin6_addr), ntohs(icmph.icmp6_seq)); 1323 pr_icmph(e->ee_type, e->ee_code, e->ee_info); 1324 putchar('\n'); 1325 fflush(stdout); 1326 } 1327 } 1328 1329out: 1330 errno = saved_errno; 1331 return net_errors ? : -local_errors; 1332} 1333 1334/* 1335 * pinger -- 1336 * Compose and transmit an ICMP ECHO REQUEST packet. The IP packet 1337 * will be added on by the kernel. The ID field is our UNIX process ID, 1338 * and the sequence number is an ascending integer. The first 8 bytes 1339 * of the data portion are used to hold a UNIX "timeval" struct in VAX 1340 * byte-order, to compute the round-trip time. 1341 */ 1342int build_echo(__u8 *_icmph) 1343{ 1344 struct icmp6_hdr *icmph; 1345 int cc; 1346 1347 icmph = (struct icmp6_hdr *)_icmph; 1348 icmph->icmp6_type = ICMP6_ECHO_REQUEST; 1349 icmph->icmp6_code = 0; 1350 icmph->icmp6_cksum = 0; 1351 icmph->icmp6_seq = htons(ntransmitted+1); 1352 icmph->icmp6_id = ident; 1353 1354 if (timing) 1355 gettimeofday((struct timeval *)&outpack[8], 1356 (struct timezone *)NULL); 1357 1358 cc = datalen + 8; /* skips ICMP portion */ 1359 1360 return cc; 1361} 1362 1363 1364int build_niquery(__u8 *_nih) 1365{ 1366 struct ni_hdr *nih; 1367 int cc; 1368 1369 nih = (struct ni_hdr *)_nih; 1370 nih->ni_cksum = 0; 1371 1372 nih->ni_type = ICMPV6_NI_QUERY; 1373 cc = sizeof(*nih); 1374 datalen = 0; 1375 1376 niquery_fill_nonce(ntransmitted + 1, nih->ni_nonce); 1377 nih->ni_code = ni_subject_type; 1378 nih->ni_qtype = htons(ni_query); 1379 nih->ni_flags = ni_flag; 1380 memcpy(nih + 1, ni_subject, ni_subject_len); 1381 cc += ni_subject_len; 1382 1383 return cc; 1384} 1385 1386int send_probe(void) 1387{ 1388 int len, cc; 1389 1390 rcvd_clear(ntransmitted + 1); 1391 1392 if (niquery_is_enabled()) 1393 len = build_niquery(outpack); 1394 else 1395 len = build_echo(outpack); 1396 1397 if (cmsglen == 0) { 1398 cc = sendto(icmp_sock, (char *)outpack, len, confirm, 1399 (struct sockaddr *) &whereto, 1400 sizeof(struct sockaddr_in6)); 1401 } else { 1402 struct msghdr mhdr; 1403 struct iovec iov; 1404 1405 iov.iov_len = len; 1406 iov.iov_base = outpack; 1407 1408 memset(&mhdr, 0, sizeof(mhdr)); 1409 mhdr.msg_name = &whereto; 1410 mhdr.msg_namelen = sizeof(struct sockaddr_in6); 1411 mhdr.msg_iov = &iov; 1412 mhdr.msg_iovlen = 1; 1413 mhdr.msg_control = cmsgbuf; 1414 mhdr.msg_controllen = cmsglen; 1415 1416 cc = sendmsg(icmp_sock, &mhdr, confirm); 1417 } 1418 confirm = 0; 1419 1420 return (cc == len ? 0 : cc); 1421} 1422 1423void pr_echo_reply(__u8 *_icmph, int cc) 1424{ 1425 struct icmp6_hdr *icmph = (struct icmp6_hdr *) _icmph; 1426 printf(" icmp_seq=%u", ntohs(icmph->icmp6_seq)); 1427}; 1428 1429static void putchar_safe(char c) 1430{ 1431 if (isprint(c)) 1432 putchar(c); 1433 else 1434 printf("\\%03o", c); 1435} 1436 1437void pr_niquery_reply_name(struct ni_hdr *nih, int len) 1438{ 1439 __u8 *h = (__u8 *)(nih + 1); 1440 __u8 *p = h + 4; 1441 __u8 *end = (__u8 *)nih + len; 1442 int continued = 0; 1443 char buf[1024]; 1444 int ret; 1445 1446 len -= sizeof(struct ni_hdr) + 4; 1447 1448 if (len < 0) { 1449 printf(" parse error (too short)"); 1450 return; 1451 } 1452 while (p < end) { 1453 int fqdn = 1; 1454 int i; 1455 1456 memset(buf, 0xff, sizeof(buf)); 1457 1458 if (continued) 1459 putchar(','); 1460 1461 ret = dn_expand(h, end, p, buf, sizeof(buf)); 1462 if (ret < 0) { 1463 printf(" parse error (truncated)"); 1464 break; 1465 } 1466 if (p + ret < end && *(p + ret) == '\0') 1467 fqdn = 0; 1468 1469 putchar(' '); 1470 for (i = 0; i < strlen(buf); i++) 1471 putchar_safe(buf[i]); 1472 if (fqdn) 1473 putchar('.'); 1474 1475 p += ret + !fqdn; 1476 1477 continued = 1; 1478 } 1479} 1480 1481void pr_niquery_reply_addr(struct ni_hdr *nih, int len) 1482{ 1483 __u8 *h = (__u8 *)(nih + 1); 1484 __u8 *p = h + 4; 1485 __u8 *end = (__u8 *)nih + len; 1486 int af; 1487 int aflen; 1488 int continued = 0; 1489 int truncated; 1490 char buf[1024]; 1491 1492 switch (ntohs(nih->ni_qtype)) { 1493 case NI_QTYPE_IPV4ADDR: 1494 af = AF_INET; 1495 aflen = sizeof(struct in_addr); 1496 truncated = nih->ni_flags & NI_IPV6ADDR_F_TRUNCATE; 1497 break; 1498 case NI_QTYPE_IPV6ADDR: 1499 af = AF_INET6; 1500 aflen = sizeof(struct in6_addr); 1501 truncated = nih->ni_flags & NI_IPV4ADDR_F_TRUNCATE; 1502 break; 1503 default: 1504 /* should not happen */ 1505 af = aflen = truncated = 0; 1506 } 1507 p = h; 1508 if (len < 0) { 1509 printf(" parse error (too short)"); 1510 return; 1511 } 1512 1513 while (p < end) { 1514 if (continued) 1515 putchar(','); 1516 1517 if (p + sizeof(__u32) + aflen > end) { 1518 printf(" parse error (truncated)"); 1519 break; 1520 } 1521 if (!inet_ntop(af, p + sizeof(__u32), buf, sizeof(buf))) 1522 printf(" unexpeced error in inet_ntop(%s)", 1523 strerror(errno)); 1524 else 1525 printf(" %s", buf); 1526 p += sizeof(__u32) + aflen; 1527 1528 continued = 1; 1529 } 1530 if (truncated) 1531 printf(" (truncated)"); 1532} 1533 1534void pr_niquery_reply(__u8 *_nih, int len) 1535{ 1536 struct ni_hdr *nih = (struct ni_hdr *)_nih; 1537 1538 switch (nih->ni_code) { 1539 case NI_SUCCESS: 1540 switch (ntohs(nih->ni_qtype)) { 1541 case NI_QTYPE_NAME: 1542 pr_niquery_reply_name(nih, len); 1543 break; 1544 case NI_QTYPE_IPV4ADDR: 1545 case NI_QTYPE_IPV6ADDR: 1546 pr_niquery_reply_addr(nih, len); 1547 break; 1548 default: 1549 printf(" unknown qtype(0x%02x)", ntohs(nih->ni_qtype)); 1550 } 1551 break; 1552 case NI_REFUSED: 1553 printf(" refused"); 1554 break; 1555 case NI_UNKNOWN: 1556 printf(" unknown"); 1557 break; 1558 default: 1559 printf(" unknown code(%02x)", ntohs(nih->ni_code)); 1560 } 1561 printf("; seq=%u;", ntohsp((__u16*)nih->ni_nonce)); 1562} 1563 1564/* 1565 * parse_reply -- 1566 * Print out the packet, if it came from us. This logic is necessary 1567 * because ALL readers of the ICMP socket get a copy of ALL ICMP packets 1568 * which arrive ('tis only fair). This permits multiple copies of this 1569 * program to be run without having intermingled output (or statistics!). 1570 */ 1571int 1572parse_reply(struct msghdr *msg, int cc, void *addr, struct timeval *tv) 1573{ 1574 struct sockaddr_in6 *from = addr; 1575 __u8 *buf = msg->msg_iov->iov_base; 1576 struct cmsghdr *c; 1577 struct icmp6_hdr *icmph; 1578 int hops = -1; 1579 1580 for (c = CMSG_FIRSTHDR(msg); c; c = CMSG_NXTHDR(msg, c)) { 1581 if (c->cmsg_level != SOL_IPV6) 1582 continue; 1583 switch(c->cmsg_type) { 1584 case IPV6_HOPLIMIT: 1585#ifdef IPV6_2292HOPLIMIT 1586 case IPV6_2292HOPLIMIT: 1587#endif 1588 if (c->cmsg_len < CMSG_LEN(sizeof(int))) 1589 continue; 1590 memcpy(&hops, CMSG_DATA(c), sizeof(hops)); 1591 } 1592 } 1593 1594 1595 /* Now the ICMP part */ 1596 1597 icmph = (struct icmp6_hdr *) buf; 1598 if (cc < 8) { 1599 if (options & F_VERBOSE) 1600 fprintf(stderr, "ping: packet too short (%d bytes)\n", cc); 1601 return 1; 1602 } 1603 1604 if (icmph->icmp6_type == ICMP6_ECHO_REPLY) { 1605 if (icmph->icmp6_id != ident) 1606 return 1; 1607 if (gather_statistics((__u8*)icmph, sizeof(*icmph), cc, 1608 ntohs(icmph->icmp6_seq), 1609 hops, 0, tv, pr_addr(&from->sin6_addr), 1610 pr_echo_reply)) 1611 return 0; 1612 } else if (icmph->icmp6_type == ICMPV6_NI_REPLY) { 1613 struct ni_hdr *nih = (struct ni_hdr *)icmph; 1614 int seq = niquery_check_nonce(nih->ni_nonce); 1615 if (seq < 0) 1616 return 1; 1617 if (gather_statistics((__u8*)icmph, sizeof(*icmph), cc, 1618 seq, 1619 hops, 0, tv, pr_addr(&from->sin6_addr), 1620 pr_niquery_reply)) 1621 return 0; 1622 } else { 1623 int nexthdr; 1624 struct ip6_hdr *iph1 = (struct ip6_hdr*)(icmph+1); 1625 struct icmp6_hdr *icmph1 = (struct icmp6_hdr *)(iph1+1); 1626 1627 /* We must not ever fall here. All the messages but 1628 * echo reply are blocked by filter and error are 1629 * received with IPV6_RECVERR. Ugly code is preserved 1630 * however, just to remember what crap we avoided 1631 * using RECVRERR. :-) 1632 */ 1633 1634 if (cc < 8+sizeof(struct ip6_hdr)+8) 1635 return 1; 1636 1637 if (memcmp(&iph1->ip6_dst, &whereto.sin6_addr, 16)) 1638 return 1; 1639 1640 nexthdr = iph1->ip6_nxt; 1641 1642 if (nexthdr == 44) { 1643 nexthdr = *(__u8*)icmph1; 1644 icmph1++; 1645 } 1646 if (nexthdr == IPPROTO_ICMPV6) { 1647 if (icmph1->icmp6_type != ICMP6_ECHO_REQUEST || 1648 icmph1->icmp6_id != ident) 1649 return 1; 1650 acknowledge(ntohs(icmph1->icmp6_seq)); 1651 if (working_recverr) 1652 return 0; 1653 nerrors++; 1654 if (options & F_FLOOD) { 1655 write_stdout("\bE", 2); 1656 return 0; 1657 } 1658 print_timestamp(); 1659 printf("From %s: icmp_seq=%u ", pr_addr(&from->sin6_addr), ntohs(icmph1->icmp6_seq)); 1660 } else { 1661 /* We've got something other than an ECHOREPLY */ 1662 if (!(options & F_VERBOSE) || uid) 1663 return 1; 1664 print_timestamp(); 1665 printf("From %s: ", pr_addr(&from->sin6_addr)); 1666 } 1667 pr_icmph(icmph->icmp6_type, icmph->icmp6_code, ntohl(icmph->icmp6_mtu)); 1668 } 1669 1670 if (!(options & F_FLOOD)) { 1671 if (options & F_AUDIBLE) 1672 putchar('\a'); 1673 putchar('\n'); 1674 fflush(stdout); 1675 } else { 1676 putchar('\a'); 1677 fflush(stdout); 1678 } 1679 return 0; 1680} 1681 1682 1683int pr_icmph(__u8 type, __u8 code, __u32 info) 1684{ 1685 switch(type) { 1686 case ICMP6_DST_UNREACH: 1687 printf("Destination unreachable: "); 1688 switch (code) { 1689 case ICMP6_DST_UNREACH_NOROUTE: 1690 printf("No route"); 1691 break; 1692 case ICMP6_DST_UNREACH_ADMIN: 1693 printf("Administratively prohibited"); 1694 break; 1695 case ICMP6_DST_UNREACH_BEYONDSCOPE: 1696 printf("Beyond scope of source address"); 1697 break; 1698 case ICMP6_DST_UNREACH_ADDR: 1699 printf("Address unreachable"); 1700 break; 1701 case ICMP6_DST_UNREACH_NOPORT: 1702 printf("Port unreachable"); 1703 break; 1704 default: 1705 printf("Unknown code %d", code); 1706 break; 1707 } 1708 break; 1709 case ICMP6_PACKET_TOO_BIG: 1710 printf("Packet too big: mtu=%u", info); 1711 if (code) 1712 printf(", code=%d", code); 1713 break; 1714 case ICMP6_TIME_EXCEEDED: 1715 printf("Time exceeded: "); 1716 if (code == ICMP6_TIME_EXCEED_TRANSIT) 1717 printf("Hop limit"); 1718 else if (code == ICMP6_TIME_EXCEED_REASSEMBLY) 1719 printf("Defragmentation failure"); 1720 else 1721 printf("code %d", code); 1722 break; 1723 case ICMP6_PARAM_PROB: 1724 printf("Parameter problem: "); 1725 if (code == ICMP6_PARAMPROB_HEADER) 1726 printf("Wrong header field "); 1727 else if (code == ICMP6_PARAMPROB_NEXTHEADER) 1728 printf("Unknown header "); 1729 else if (code == ICMP6_PARAMPROB_OPTION) 1730 printf("Unknown option "); 1731 else 1732 printf("code %d ", code); 1733 printf ("at %u", info); 1734 break; 1735 case ICMP6_ECHO_REQUEST: 1736 printf("Echo request"); 1737 break; 1738 case ICMP6_ECHO_REPLY: 1739 printf("Echo reply"); 1740 break; 1741 case MLD_LISTENER_QUERY: 1742 printf("MLD Query"); 1743 break; 1744 case MLD_LISTENER_REPORT: 1745 printf("MLD Report"); 1746 break; 1747 case MLD_LISTENER_REDUCTION: 1748 printf("MLD Reduction"); 1749 break; 1750 default: 1751 printf("unknown icmp type: %u", type); 1752 1753 } 1754 return 0; 1755} 1756 1757#include <linux/filter.h> 1758 1759void install_filter(void) 1760{ 1761 static int once; 1762 static struct sock_filter insns[] = { 1763 BPF_STMT(BPF_LD|BPF_H|BPF_ABS, 4), /* Load icmp echo ident */ 1764 BPF_JUMP(BPF_JMP|BPF_JEQ|BPF_K, 0xAAAA, 0, 1), /* Ours? */ 1765 BPF_STMT(BPF_RET|BPF_K, ~0U), /* Yes, it passes. */ 1766 BPF_STMT(BPF_LD|BPF_B|BPF_ABS, 0), /* Load icmp type */ 1767 BPF_JUMP(BPF_JMP|BPF_JEQ|BPF_K, ICMP6_ECHO_REPLY, 1, 0), /* Echo? */ 1768 BPF_STMT(BPF_RET|BPF_K, ~0U), /* No. It passes. This must not happen. */ 1769 BPF_STMT(BPF_RET|BPF_K, 0), /* Echo with wrong ident. Reject. */ 1770 }; 1771 static struct sock_fprog filter = { 1772 sizeof insns / sizeof(insns[0]), 1773 insns 1774 }; 1775 1776 if (once) 1777 return; 1778 once = 1; 1779 1780 /* Patch bpflet for current identifier. */ 1781 insns[1] = (struct sock_filter)BPF_JUMP(BPF_JMP|BPF_JEQ|BPF_K, htons(ident), 0, 1); 1782 1783 if (setsockopt(icmp_sock, SOL_SOCKET, SO_ATTACH_FILTER, &filter, sizeof(filter))) 1784 perror("WARNING: failed to install socket filter\n"); 1785} 1786 1787 1788/* 1789 * pr_addr -- 1790 * Return an ascii host address as a dotted quad and optionally with 1791 * a hostname. 1792 */ 1793char * pr_addr(struct in6_addr *addr) 1794{ 1795 struct hostent *hp = NULL; 1796 static char *s; 1797 1798#ifdef USE_IDN 1799 free(s); 1800#endif 1801 1802 in_pr_addr = !setjmp(pr_addr_jmp); 1803 1804 if (!(exiting || options&F_NUMERIC)) 1805 hp = gethostbyaddr((__u8*)addr, sizeof(struct in6_addr), AF_INET6); 1806 1807 in_pr_addr = 0; 1808 1809 if (!hp 1810#ifdef USE_IDN 1811 || idna_to_unicode_lzlz(hp->h_name, &s, 0) != IDNA_SUCCESS 1812#endif 1813 ) 1814 s = NULL; 1815 1816 return hp ? (s ? s : hp->h_name) : pr_addr_n(addr); 1817} 1818 1819char * pr_addr_n(struct in6_addr *addr) 1820{ 1821 static char str[64]; 1822 inet_ntop(AF_INET6, addr, str, sizeof(str)); 1823 return str; 1824} 1825 1826#define USAGE_NEWLINE "\n " 1827 1828void usage(void) 1829{ 1830 fprintf(stderr, 1831 "Usage: ping6" 1832 " [-" 1833 "aAbBdDfhLnOqrRUvV" 1834 "]" 1835 " [-c count]" 1836 " [-i interval]" 1837 " [-I interface]" 1838 USAGE_NEWLINE 1839 " [-l preload]" 1840 " [-m mark]" 1841 " [-M pmtudisc_option]" 1842 USAGE_NEWLINE 1843 " [-N nodeinfo_option]" 1844 " [-p pattern]" 1845 " [-Q tclass]" 1846 " [-s packetsize]" 1847 USAGE_NEWLINE 1848 " [-S sndbuf]" 1849 " [-t ttl]" 1850 " [-T timestamp_option]" 1851 " [-w deadline]" 1852 USAGE_NEWLINE 1853 " [-W timeout]" 1854#ifdef ENABLE_PING6_RTHDR 1855 " [hop1 ...]" 1856#endif 1857 " destination" 1858 "\n" 1859 ); 1860 exit(2); 1861} 1862