Lorenzo Colitti | 313379e | 2013-07-11 01:07:11 +0900 | [diff] [blame] | 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 |
| 47 | char 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 |
| 148 | struct cmsghdr *srcrt; |
| 149 | #endif |
| 150 | |
| 151 | struct sockaddr_in6 whereto; /* who to ping */ |
| 152 | u_char outpack[MAXPACKET]; |
| 153 | int maxpacket = sizeof(outpack); |
| 154 | |
| 155 | static unsigned char cmsgbuf[4096]; |
| 156 | static int cmsglen = 0; |
| 157 | |
| 158 | static char * pr_addr(struct in6_addr *addr); |
| 159 | static char * pr_addr_n(struct in6_addr *addr); |
| 160 | static int pr_icmph(__u8 type, __u8 code, __u32 info); |
| 161 | static void usage(void) __attribute((noreturn)); |
| 162 | |
| 163 | struct sockaddr_in6 source; |
| 164 | char *device; |
| 165 | int pmtudisc=-1; |
| 166 | |
| 167 | static 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 */ |
| 176 | int ni_query = -1; |
| 177 | int ni_flag = 0; |
| 178 | void *ni_subject = NULL; |
| 179 | int ni_subject_len = 0; |
| 180 | int ni_subject_type = -1; |
| 181 | char *ni_group; |
| 182 | |
| 183 | static 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) |
| 191 | size_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 | |
| 200 | extern 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 | |
| 217 | int 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 | |
| 233 | unsigned 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 | |
| 243 | struct 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 | |
| 260 | static int niquery_option_name_handler(int index, const char *arg); |
| 261 | static int niquery_option_ipv6_handler(int index, const char *arg); |
| 262 | static int niquery_option_ipv6_flag_handler(int index, const char *arg); |
| 263 | static int niquery_option_ipv4_handler(int index, const char *arg); |
| 264 | static int niquery_option_ipv4_flag_handler(int index, const char *arg); |
| 265 | static int niquery_option_subject_addr_handler(int index, const char *arg); |
| 266 | static int niquery_option_subject_name_handler(int index, const char *arg); |
| 267 | static int niquery_option_help_handler(int index, const char *arg); |
| 268 | |
| 269 | struct 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 | |
| 288 | static 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 |
| 296 | struct { |
| 297 | struct timeval tv; |
| 298 | pid_t pid; |
| 299 | } ni_nonce_secret; |
| 300 | #endif |
| 301 | |
| 302 | static 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 |
| 327 | static 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 | |
| 354 | static 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 | |
| 372 | static 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 | |
| 384 | static 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 | |
| 394 | static 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 | |
| 401 | static 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 | |
| 408 | static 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 | |
| 416 | static 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 | |
| 423 | static 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 | |
| 431 | static inline int niquery_is_subject_valid(void) |
| 432 | { |
| 433 | return ni_subject_type >= 0 && ni_subject; |
| 434 | } |
| 435 | |
| 436 | static 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 | |
| 449 | static 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 | |
| 503 | static 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; |
| 622 | oomexit: |
| 623 | fprintf(stderr, "ping6: out of memory.\n"); |
| 624 | errexit: |
| 625 | free(buf); |
| 626 | free(canonname); |
| 627 | free(idn); |
| 628 | free(name); |
| 629 | exit(1); |
| 630 | } |
| 631 | |
| 632 | int 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 | |
| 647 | int 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 | |
| 674 | static 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 | |
| 695 | int 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 | |
| 1253 | int 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 | |
| 1329 | out: |
| 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 | */ |
| 1342 | int 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 | |
| 1364 | int 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 | |
| 1386 | int 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 | |
| 1423 | void 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 | |
| 1429 | static void putchar_safe(char c) |
| 1430 | { |
| 1431 | if (isprint(c)) |
| 1432 | putchar(c); |
| 1433 | else |
| 1434 | printf("\\%03o", c); |
| 1435 | } |
| 1436 | |
| 1437 | void 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 | |
| 1481 | void 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 | |
| 1534 | void 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 | */ |
| 1571 | int |
| 1572 | parse_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 | |
| 1683 | int 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 | |
| 1759 | void 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 | */ |
| 1793 | char * 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 | |
| 1819 | char * 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 | |
| 1828 | void 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 | } |