Example #1
0
void
test_rth_space()
{
	socklen_t len;

	set_funcname("test_rth_space", sizeof("test_rth_space\0"));

	/*
	 * Test: invalid routing header type.
	 */
	len = inet6_rth_space(~IPV6_RTHDR_TYPE_0, 0);
	checknum(0, len, 0, "invalid routing header type\0");

	/*
	 * Test: valid number of segments.
	 */
	len = inet6_rth_space(IPV6_RTHDR_TYPE_0, 0);
	checknum(0, len, 1, "0 segments\0");
	len = inet6_rth_space(IPV6_RTHDR_TYPE_0, 127);
	checknum(0, len, 1, "0 segments\0");

	/*
	 * Test: invalid number of segments.
	 */
	len = inet6_rth_space(IPV6_RTHDR_TYPE_0, -1);
	checknum(0, len, 0, "-1 segments\0");
	len = inet6_rth_space(IPV6_RTHDR_TYPE_0, 128);
	checknum(0, len, 0, "128 segments\0");
}
Example #2
0
int Inet6_rth_space(int type, int segments)
{
    int ret;

    ret = inet6_rth_space(type, segments);
    if (ret < 0) err_quit("inet6_rth_space 错误");

    return ret;
}
Example #3
0
void runSuccess() {
    int t = anyint();
    int s = anyint();
    int len = inet6_rth_space(t, s);
    if (len) {
        char* bp = my_malloc(len);
        inet6_rth_init(bp, len, t, s);
    }
}
Example #4
0
void runFailure2() {
    int t = anyint();
    int s = anyint();
    int len = inet6_rth_space(t, s);
    if (len) {
        char* bp = my_malloc(len);
        inet6_rth_init(bp, 0, t, s);
    }
}
Example #5
0
int
Inet6_rth_space(int type, int segments)
{
	int ret;
	
	ret = inet6_rth_space(type, segments);
	if (ret < 0)
		err_quit("inet6_rth_space error: %s", strerror(errno));

	return ret;
}
Example #6
0
void *inet6_rth_init (void *bp, socklen_t bp_len, int type, int segments)
{
	socklen_t needlen;

	needlen = inet6_rth_space (type, segments);
	if ((needlen == 0) || (bp_len < needlen))
		return NULL;

	memset (bp, 0, needlen);
	((uint8_t *)bp)[1] = segments * 2; /* type 0 specific */
	((uint8_t *)bp)[2] = type;
	return bp;
}
Example #7
0
void testValues() {
    f = 2;
    
    int t = anyint();
    int s = anyint();
    int len = inet6_rth_space(t, s);
    if (len) {
        char* bp = my_malloc(len);
        char* result = inet6_rth_init(bp, len, t, s);
        //@ assert result == \null || result == bp;
    }

    //@ assert f == 2;
    //@ assert vacuous: \false;
}
Example #8
0
/*
 * Initialize the msghdr for specifying hoplimit, outgoing interface and routing
 * header for the probe packets.
 */
void
set_ancillary_data(struct msghdr *msgp, int hoplimit,
    union any_in_addr *gwIPlist, int gw_cnt, uint_t if_index)
{
	size_t hoplimit_space;
	size_t rthdr_space;
	size_t pktinfo_space;
	size_t bufspace;
	struct cmsghdr *cmsgp;
	uchar_t *cmsg_datap;
	int i;

	msgp->msg_control = NULL;
	msgp->msg_controllen = 0;

	/*
	 * Need to figure out size of buffer needed for ancillary data
	 * containing routing header and packet info options.
	 *
	 * Portable heuristic to compute upper bound on space needed for
	 * N ancillary data options. It assumes up to _MAX_ALIGNMENT padding
	 * after both header and data as the worst possible upper bound on space
	 * consumed by padding.
	 * It also adds one extra "sizeof (struct cmsghdr)" for the last option.
	 * This is needed because we would like to use CMSG_NXTHDR() while
	 * composing the buffer. The CMSG_NXTHDR() macro is designed better for
	 * parsing than composing the buffer. It requires the pointer it returns
	 * to leave space in buffer for addressing a cmsghdr and we want to make
	 * sure it works for us while we skip beyond the last ancillary data
	 * option.
	 *
	 * bufspace[i]  = sizeof(struct cmsghdr) + <pad after header> +
	 *		<option[i] content length> + <pad after data>;
	 *
	 * total_bufspace = bufspace[0] + bufspace[1] + ...
	 *		    ... + bufspace[N-1] + sizeof (struct cmsghdr);
	 */

	rthdr_space = 0;
	pktinfo_space = 0;
	/* We'll always set the hoplimit of the outgoing packets */
	hoplimit_space = sizeof (int);
	bufspace = sizeof (struct cmsghdr) + _MAX_ALIGNMENT +
	    hoplimit_space + _MAX_ALIGNMENT;

	if (gw_cnt > 0) {
		rthdr_space = inet6_rth_space(IPV6_RTHDR_TYPE_0, gw_cnt);
		bufspace += sizeof (struct cmsghdr) + _MAX_ALIGNMENT +
		    rthdr_space + _MAX_ALIGNMENT;
	}

	if (if_index != 0) {
		pktinfo_space = sizeof (struct in6_pktinfo);
		bufspace += sizeof (struct cmsghdr) + _MAX_ALIGNMENT +
		    pktinfo_space + _MAX_ALIGNMENT;
	}

	/*
	 * We need to temporarily set the msgp->msg_controllen to bufspace
	 * (we will later trim it to actual length used). This is needed because
	 * CMSG_NXTHDR() uses it to check we have not exceeded the bounds.
	 */
	bufspace += sizeof (struct cmsghdr);
	msgp->msg_controllen = bufspace;

	msgp->msg_control = (struct cmsghdr *)malloc(bufspace);
	if (msgp->msg_control == NULL) {
		Fprintf(stderr, "%s: malloc %s\n", prog, strerror(errno));
		exit(EXIT_FAILURE);
	}
	cmsgp = CMSG_FIRSTHDR(msgp);

	/*
	 * Fill ancillary data. First hoplimit, then rthdr and pktinfo if
	 * needed.
	 */

	/* set hoplimit ancillary data */
	cmsgp->cmsg_level = IPPROTO_IPV6;
	cmsgp->cmsg_type = IPV6_HOPLIMIT;
	cmsg_datap = CMSG_DATA(cmsgp);
	/* LINTED E_BAD_PTR_CAST_ALIGN */
	*(int *)cmsg_datap = hoplimit;
	cmsgp->cmsg_len = cmsg_datap + hoplimit_space - (uchar_t *)cmsgp;
	cmsgp = CMSG_NXTHDR(msgp, cmsgp);

	/* set rthdr ancillary data if needed */
	if (gw_cnt > 0) {
		struct ip6_rthdr0 *rthdr0p;

		cmsgp->cmsg_level = IPPROTO_IPV6;
		cmsgp->cmsg_type = IPV6_RTHDR;
		cmsg_datap = CMSG_DATA(cmsgp);

		/*
		 * Initialize rthdr structure
		 */
		/* LINTED E_BAD_PTR_CAST_ALIGN */
		rthdr0p = (struct ip6_rthdr0 *)cmsg_datap;
		if (inet6_rth_init(rthdr0p, rthdr_space,
		    IPV6_RTHDR_TYPE_0, gw_cnt) == NULL) {
			Fprintf(stderr, "%s: inet6_rth_init failed\n",
			    prog);
			exit(EXIT_FAILURE);
		}

		/*
		 * Stuff in gateway addresses
		 */
		for (i = 0; i < gw_cnt; i++) {
			if (inet6_rth_add(rthdr0p,
			    &gwIPlist[i].addr6) == -1) {
				Fprintf(stderr,
				    "%s: inet6_rth_add\n", prog);
				exit(EXIT_FAILURE);
			}
		}

		cmsgp->cmsg_len = cmsg_datap + rthdr_space - (uchar_t *)cmsgp;
		cmsgp = CMSG_NXTHDR(msgp, cmsgp);
	}

	/* set pktinfo ancillary data if needed */
	if (if_index != 0) {
		struct in6_pktinfo *pktinfop;

		cmsgp->cmsg_level = IPPROTO_IPV6;
		cmsgp->cmsg_type = IPV6_PKTINFO;
		cmsg_datap = CMSG_DATA(cmsgp);

		/* LINTED E_BAD_PTR_CAST_ALIGN */
		pktinfop = (struct in6_pktinfo *)cmsg_datap;
		/*
		 * We don't know if pktinfop->ipi6_addr is aligned properly,
		 * therefore let's use bcopy, instead of assignment.
		 */
		(void) bcopy(&in6addr_any, &pktinfop->ipi6_addr,
		sizeof (struct in6_addr));

		/*
		 *  We can assume pktinfop->ipi6_ifindex is 32 bit aligned.
		 */
		pktinfop->ipi6_ifindex = if_index;
		cmsgp->cmsg_len = cmsg_datap + pktinfo_space - (uchar_t *)cmsgp;
		cmsgp = CMSG_NXTHDR(msgp, cmsgp);
	}

	msgp->msg_controllen = (char *)cmsgp - (char *)msgp->msg_control;
}
int main(int argc, char *argv[])
{
	int ch, hold, packlen;
	u_char *packet;
	char *target;
	struct addrinfo hints, *ai;
	int gai;
	struct sockaddr_in6 firsthop;
	int socket_errno = 0;
	struct icmp6_filter filter;
	int err;
#ifdef __linux__
	int csum_offset, sz_opt;
#endif
	static uint32_t scope_id = 0;

#ifdef ANDROID
	android_check_security();
#endif

	limit_capabilities();

#ifdef USE_IDN
	setlocale(LC_ALL, "");
#endif

	icmp_sock = socket(AF_INET6, SOCK_DGRAM, IPPROTO_ICMPV6);
	if (icmp_sock < 0) {
		enable_capability_raw();
		icmp_sock = socket(AF_INET6, SOCK_RAW, IPPROTO_ICMPV6);
		socket_errno = errno;
		disable_capability_raw();
		using_ping_socket = 0;
	}

	source.sin6_family = AF_INET6;
	memset(&firsthop, 0, sizeof(firsthop));
	firsthop.sin6_family = AF_INET6;

	preload = 1;
	while ((ch = getopt(argc, argv, COMMON_OPTSTR "F:N:")) != EOF) {
		switch(ch) {
		case 'F':
			flowlabel = hextoui(optarg);
			if (errno || (flowlabel & ~IPV6_FLOWINFO_FLOWLABEL)) {
				fprintf(stderr, "ping: Invalid flowinfo %s\n", optarg);
				exit(2);
			}
			options |= F_FLOWINFO;
			break;
		case 'Q':
			tclass = hextoui(optarg);
			if (errno || (tclass & ~0xff)) {
				fprintf(stderr, "ping: Invalid tclass %s\n", optarg);
				exit(2);
			}
			options |= F_TCLASS;
			break;
		case 'I':
			if (strchr(optarg, ':')) {
				char *p, *addr = strdup(optarg);

				if (!addr) {
					fprintf(stderr, "ping: out of memory\n");
					exit(2);
				}

				p = strchr(addr, SCOPE_DELIMITER);
				if (p) {
					*p = '\0';
					device = optarg + (p - addr) + 1;
				}

				if (inet_pton(AF_INET6, addr, (char*)&source.sin6_addr) <= 0) {
					fprintf(stderr, "ping: invalid source address %s\n", optarg);
					exit(2);
				}

				options |= F_STRICTSOURCE;

				free(addr);
			} else {
				device = optarg;
			}
			break;
		case 'M':
			if (strcmp(optarg, "do") == 0)
				pmtudisc = IPV6_PMTUDISC_DO;
			else if (strcmp(optarg, "dont") == 0)
				pmtudisc = IPV6_PMTUDISC_DONT;
			else if (strcmp(optarg, "want") == 0)
				pmtudisc = IPV6_PMTUDISC_WANT;
			else {
				fprintf(stderr, "ping: wrong value for -M: do, dont, want are valid ones.\n");
				exit(2);
			}
			break;
		case 'V':
			printf("ping6 utility, iputils-%s\n", SNAPSHOT);
			exit(0);
		case 'N':
			if (using_ping_socket) {
				fprintf(stderr, "ping: -N requires raw socket permissions\n");
				exit(2);
			}
			if (niquery_option_handler(optarg) < 0) {
				usage();
				break;
			}
			break;
		COMMON_OPTIONS
			common_options(ch);
			break;
		default:
			usage();
		}
	}
	argc -= optind;
	argv += optind;

#ifdef ENABLE_PING6_RTHDR
	while (argc > 1) {
		struct in6_addr *addr;

		if (srcrt == NULL) {
			int space;

			fprintf(stderr, "ping6: Warning: "
					"Source routing is deprecated by RFC5095.\n");

#ifdef ENABLE_PING6_RTHDR_RFC3542
			space = inet6_rth_space(IPV6_RTHDR_TYPE_0, argc - 1);
#else
			space = inet6_srcrt_space(IPV6_SRCRT_TYPE_0, argc - 1);
#endif
			if (space == 0)	{
				fprintf(stderr, "srcrt_space failed\n");
				exit(2);
			}
#ifdef ENABLE_PING6_RTHDR_RFC3542
			if (cmsglen + CMSG_SPACE(space) > sizeof(cmsgbuf)) {
				fprintf(stderr, "no room for options\n");
				exit(2);
			}
#else
			if (space + cmsglen > sizeof(cmsgbuf)) {
				fprintf(stderr, "no room for options\n");
				exit(2);
			}
#endif
			srcrt = (struct cmsghdr*)(cmsgbuf+cmsglen);
#ifdef ENABLE_PING6_RTHDR_RFC3542
			memset(srcrt, 0, CMSG_SPACE(0));
			srcrt->cmsg_len = CMSG_LEN(space);
			srcrt->cmsg_level = IPPROTO_IPV6;
			srcrt->cmsg_type = IPV6_RTHDR;
			inet6_rth_init(CMSG_DATA(srcrt), space, IPV6_RTHDR_TYPE_0, argc - 1);
			cmsglen += CMSG_SPACE(space);
#else
			cmsglen += CMSG_ALIGN(space);
			inet6_srcrt_init(srcrt, IPV6_SRCRT_TYPE_0);
#endif
		}

		target = *argv;

		memset(&hints, 0, sizeof(hints));
		hints.ai_family = AF_INET6;
#ifdef USE_IDN
		hints.ai_flags = AI_IDN;
#endif
		gai = getaddrinfo(target, NULL, &hints, &ai);
		if (gai) {
			fprintf(stderr, "unknown host\n");
			exit(2);
		}
		addr = &((struct sockaddr_in6 *)(ai->ai_addr))->sin6_addr;
#ifdef ENABLE_PING6_RTHDR_RFC3542
		inet6_rth_add(CMSG_DATA(srcrt), addr);
#else
		inet6_srcrt_add(srcrt, addr);
#endif
		if (IN6_IS_ADDR_UNSPECIFIED(&firsthop.sin6_addr)) {
			memcpy(&firsthop.sin6_addr, addr, 16);
#ifdef HAVE_SIN6_SCOPEID
			firsthop.sin6_scope_id = ((struct sockaddr_in6 *)(ai->ai_addr))->sin6_scope_id;
			/* Verify scope_id is the same as previous nodes */
			if (firsthop.sin6_scope_id && scope_id && firsthop.sin6_scope_id != scope_id) {
				fprintf(stderr, "scope discrepancy among the nodes\n");
				exit(2);
			} else if (!scope_id) {
				scope_id = firsthop.sin6_scope_id;
			}
#endif
		}
		freeaddrinfo(ai);

		argv++;
		argc--;
	}
#endif

	if (niquery_is_enabled()) {
		niquery_init_nonce();

		if (!niquery_is_subject_valid()) {
			ni_subject = &whereto.sin6_addr;
			ni_subject_len = sizeof(whereto.sin6_addr);
			ni_subject_type = NI_SUBJ_IPV6;
		}
	}

	if (argc > 1) {
#ifndef ENABLE_PING6_RTHDR
		fprintf(stderr, "ping6: Source routing is deprecated by RFC5095.\n");
#endif
		usage();
	} else if (argc == 1) {
		target = *argv;
	} else {
		if (ni_query < 0 && ni_subject_type != NI_SUBJ_NAME)
			usage();
		target = ni_group;
	}

	memset(&hints, 0, sizeof(hints));
	hints.ai_family = AF_INET6;
#ifdef USE_IDN
	hints.ai_flags = AI_IDN;
#endif
	gai = getaddrinfo(target, NULL, &hints, &ai);
	if (gai) {
		fprintf(stderr, "unknown host\n");
		exit(2);
	}

	memcpy(&whereto, ai->ai_addr, sizeof(whereto));
	whereto.sin6_port = htons(IPPROTO_ICMPV6);

	if (memchr(target, ':', strlen(target)))
		options |= F_NUMERIC;

	freeaddrinfo(ai);

	if (IN6_IS_ADDR_UNSPECIFIED(&firsthop.sin6_addr)) {
		memcpy(&firsthop.sin6_addr, &whereto.sin6_addr, 16);
#ifdef HAVE_SIN6_SCOPEID
		firsthop.sin6_scope_id = whereto.sin6_scope_id;
		/* Verify scope_id is the same as intermediate nodes */
		if (firsthop.sin6_scope_id && scope_id && firsthop.sin6_scope_id != scope_id) {
			fprintf(stderr, "scope discrepancy among the nodes\n");
			exit(2);
		} else if (!scope_id) {
			scope_id = firsthop.sin6_scope_id;
		}
#endif
	}

	hostname = target;

	if (IN6_IS_ADDR_UNSPECIFIED(&source.sin6_addr)) {
		socklen_t alen;
		int probe_fd = socket(AF_INET6, SOCK_DGRAM, 0);

		if (probe_fd < 0) {
			perror("socket");
			exit(2);
		}
		if (device) {
#if defined(IPV6_RECVPKTINFO) || defined(HAVE_SIN6_SCOPEID)
			unsigned int iface = if_name2index(device);
#endif
#ifdef IPV6_RECVPKTINFO
			struct in6_pktinfo ipi;

			memset(&ipi, 0, sizeof(ipi));
			ipi.ipi6_ifindex = iface;
#endif

#ifdef HAVE_SIN6_SCOPEID
			if (IN6_IS_ADDR_LINKLOCAL(&firsthop.sin6_addr) ||
			    IN6_IS_ADDR_MC_LINKLOCAL(&firsthop.sin6_addr))
				firsthop.sin6_scope_id = iface;
#endif
			enable_capability_raw();
			if (
#ifdef IPV6_RECVPKTINFO
			    setsockopt(probe_fd, IPPROTO_IPV6, IPV6_PKTINFO, &ipi, sizeof(ipi)) == -1 &&
#endif
			    setsockopt(probe_fd, SOL_SOCKET, SO_BINDTODEVICE, device, strlen(device)+1) == -1) {
				perror("setsockopt(SO_BINDTODEVICE)");
				exit(2);
			}
			disable_capability_raw();
		}
		firsthop.sin6_port = htons(1025);
		if (connect(probe_fd, (struct sockaddr*)&firsthop, sizeof(firsthop)) == -1) {
			perror("connect");
			exit(2);
		}
		alen = sizeof(source);
		if (getsockname(probe_fd, (struct sockaddr*)&source, &alen) == -1) {
			perror("getsockname");
			exit(2);
		}
		source.sin6_port = 0;
		close(probe_fd);

#ifndef WITHOUT_IFADDRS
		if (device) {
			struct ifaddrs *ifa0, *ifa;

			if (getifaddrs(&ifa0)) {
				perror("getifaddrs");
				exit(2);
			}

			for (ifa = ifa0; ifa; ifa = ifa->ifa_next) {
				if (!ifa->ifa_addr || ifa->ifa_addr->sa_family != AF_INET6)
					continue;
				if (!strncmp(ifa->ifa_name, device, sizeof(device) - 1) &&
				    IN6_ARE_ADDR_EQUAL(&((struct sockaddr_in6 *)ifa->ifa_addr)->sin6_addr,
						       &source.sin6_addr))
					break;
			}
			if (!ifa)
				fprintf(stderr, "ping6: Warning: source address might be selected on device other than %s.\n", device);

			freeifaddrs(ifa0);
		}
#endif
	}
#ifdef HAVE_SIN6_SCOPEID
	else if (device && (IN6_IS_ADDR_LINKLOCAL(&source.sin6_addr) ||
			    IN6_IS_ADDR_MC_LINKLOCAL(&source.sin6_addr)))
		source.sin6_scope_id = if_name2index(device);
#endif

	if (icmp_sock < 0) {
		errno = socket_errno;
		perror("ping: icmp open socket");
		exit(2);
	}

	if (device) {
		struct cmsghdr *cmsg;
		struct in6_pktinfo *ipi;

		cmsg = (struct cmsghdr*)(cmsgbuf+cmsglen);
		cmsglen += CMSG_SPACE(sizeof(*ipi));
		cmsg->cmsg_len = CMSG_LEN(sizeof(*ipi));
		cmsg->cmsg_level = SOL_IPV6;
		cmsg->cmsg_type = IPV6_PKTINFO;

		ipi = (struct in6_pktinfo*)CMSG_DATA(cmsg);
		memset(ipi, 0, sizeof(*ipi));
		ipi->ipi6_ifindex = if_name2index(device);
	}

	if ((whereto.sin6_addr.s6_addr16[0]&htons(0xff00)) == htons (0xff00)) {
		if (uid) {
			if (interval < 1000) {
				fprintf(stderr, "ping: multicast ping with too short interval.\n");
				exit(2);
			}
			if (pmtudisc >= 0 && pmtudisc != IPV6_PMTUDISC_DO) {
				fprintf(stderr, "ping: multicast ping does not fragment.\n");
				exit(2);
			}
		}
		if (pmtudisc < 0)
			pmtudisc = IPV6_PMTUDISC_DO;
	}

	if (pmtudisc >= 0) {
		if (setsockopt(icmp_sock, SOL_IPV6, IPV6_MTU_DISCOVER, &pmtudisc, sizeof(pmtudisc)) == -1) {
			perror("ping: IPV6_MTU_DISCOVER");
			exit(2);
		}
	}

	if ((options&F_STRICTSOURCE) &&
	    bind(icmp_sock, (struct sockaddr*)&source, sizeof(source)) == -1) {
		perror("ping: bind icmp socket");
		exit(2);
	}

	if (datalen >= sizeof(struct timeval) && (ni_query < 0)) {
		/* can we time transfer */
		timing = 1;
	}
	packlen = datalen + 8 + 4096 + 40 + 8; /* 4096 for rthdr */
	if (!(packet = (u_char *)malloc((u_int)packlen))) {
		fprintf(stderr, "ping: out of memory.\n");
		exit(2);
	}

	working_recverr = 1;
	hold = 1;
	if (setsockopt(icmp_sock, SOL_IPV6, IPV6_RECVERR, (char *)&hold, sizeof(hold))) {
		fprintf(stderr, "WARNING: your kernel is veeery old. No problems.\n");
		working_recverr = 0;
	}

	/* Estimate memory eaten by single packet. It is rough estimate.
	 * Actually, for small datalen's it depends on kernel side a lot. */
	hold = datalen+8;
	hold += ((hold+511)/512)*(40+16+64+160);
	sock_setbufs(icmp_sock, hold);

	if (!using_ping_socket) {
#ifdef __linux__
		csum_offset = 2;
		sz_opt = sizeof(int);

		err = setsockopt(icmp_sock, SOL_RAW, IPV6_CHECKSUM,
				 &csum_offset, sz_opt);
		if (err < 0) {
			/* checksum should be enabled by default and setting
			 * this option might fail anyway.
			 */
			fprintf(stderr, "setsockopt(RAW_CHECKSUM) failed"
				" - try to continue.");
		}
#endif

		/*
		 *	select icmp echo reply as icmp type to receive
		 */

		ICMP6_FILTER_SETBLOCKALL(&filter);

		if (!working_recverr) {
			ICMP6_FILTER_SETPASS(ICMP6_DST_UNREACH, &filter);
			ICMP6_FILTER_SETPASS(ICMP6_PACKET_TOO_BIG, &filter);
			ICMP6_FILTER_SETPASS(ICMP6_TIME_EXCEEDED, &filter);
			ICMP6_FILTER_SETPASS(ICMP6_PARAM_PROB, &filter);
		}

		if (niquery_is_enabled())
			ICMP6_FILTER_SETPASS(ICMPV6_NI_REPLY, &filter);
		else
			ICMP6_FILTER_SETPASS(ICMP6_ECHO_REPLY, &filter);

		err = setsockopt(icmp_sock, IPPROTO_ICMPV6, ICMP6_FILTER,
				 &filter, sizeof(struct icmp6_filter));

		if (err < 0) {
			perror("setsockopt(ICMP6_FILTER)");
			exit(2);
		}
	}

	if (options & F_NOLOOP) {
		int loop = 0;
		if (setsockopt(icmp_sock, IPPROTO_IPV6, IPV6_MULTICAST_LOOP,
				&loop, sizeof(loop)) == -1) {
			perror ("can't disable multicast loopback");
			exit(2);
		}
	}
	if (options & F_TTL) {
		if (setsockopt(icmp_sock, IPPROTO_IPV6, IPV6_MULTICAST_HOPS,
			       &ttl, sizeof(ttl)) == -1) {
			perror ("can't set multicast hop limit");
			exit(2);
		}
		if (setsockopt(icmp_sock, IPPROTO_IPV6, IPV6_UNICAST_HOPS,
			       &ttl, sizeof(ttl)) == -1) {
			perror ("can't set unicast hop limit");
			exit(2);
		}
	}

	if (1) {
		int on = 1;
		if (
#ifdef IPV6_RECVHOPLIMIT
		    setsockopt(icmp_sock, IPPROTO_IPV6, IPV6_RECVHOPLIMIT,
			       &on, sizeof(on)) == -1 &&
		    setsockopt(icmp_sock, IPPROTO_IPV6, IPV6_2292HOPLIMIT,
			       &on, sizeof(on)) == -1
#else
		    setsockopt(icmp_sock, IPPROTO_IPV6, IPV6_HOPLIMIT,
			       &on, sizeof(on)) == -1
#endif
		   ){
			perror ("can't receive hop limit");
			exit(2);
		}
	}

	if (options & F_TCLASS) {
#ifdef IPV6_TCLASS
		if (setsockopt(icmp_sock, IPPROTO_IPV6, IPV6_TCLASS,
			       &tclass, sizeof(tclass)) == -1) {
			perror ("setsockopt(IPV6_TCLASS)");
			exit(2);
		}
#else
		fprintf(stderr, "Traffic class is not supported.\n");
#endif
	}

	if (options&F_FLOWINFO) {
#ifdef IPV6_FLOWINFO_SEND
		int on = 1;
#endif
#ifdef IPV6_FLOWLABEL_MGR
		char freq_buf[CMSG_ALIGN(sizeof(struct in6_flowlabel_req)) + cmsglen];
		struct in6_flowlabel_req *freq = (struct in6_flowlabel_req *)freq_buf;
		int freq_len = sizeof(*freq);
#ifdef ENABLE_PING6_RTHDR
		if (srcrt)
			freq_len = CMSG_ALIGN(sizeof(*freq)) + srcrt->cmsg_len;
#endif
		memset(freq, 0, sizeof(*freq));
		freq->flr_label = htonl(flowlabel & IPV6_FLOWINFO_FLOWLABEL);
		freq->flr_action = IPV6_FL_A_GET;
		freq->flr_flags = IPV6_FL_F_CREATE;
		freq->flr_share = IPV6_FL_S_EXCL;
		memcpy(&freq->flr_dst, &whereto.sin6_addr, 16);
#ifdef ENABLE_PING6_RTHDR
		if (srcrt)
			memcpy(freq_buf + CMSG_ALIGN(sizeof(*freq)), srcrt, srcrt->cmsg_len);
#endif
		if (setsockopt(icmp_sock, IPPROTO_IPV6, IPV6_FLOWLABEL_MGR,
			       freq, freq_len) == -1) {
			perror ("can't set flowlabel");
			exit(2);
		}
		flowlabel = freq->flr_label;
#ifdef ENABLE_PING6_RTHDR
		if (srcrt) {
			cmsglen = (char*)srcrt - (char*)cmsgbuf;
			srcrt = NULL;
		}
#endif
#else
		fprintf(stderr, "Flow labels are not supported.\n");
		exit(2);
#endif

#ifdef IPV6_FLOWINFO_SEND
		whereto.sin6_flowinfo = flowlabel;
		if (setsockopt(icmp_sock, IPPROTO_IPV6, IPV6_FLOWINFO_SEND,
			       &on, sizeof(on)) == -1) {
			perror ("can't send flowinfo");
			exit(2);
		}
#else
		fprintf(stderr, "Flowinfo is not supported.\n");
		exit(2);
#endif
	}

	printf("PING %s(%s) ", hostname, pr_addr(&whereto.sin6_addr));
	if (flowlabel)
		printf(", flow 0x%05x, ", (unsigned)ntohl(flowlabel));
	if (device || (options&F_STRICTSOURCE)) {
		printf("from %s %s: ",
		       pr_addr_n(&source.sin6_addr), device ? : "");
	}
/**
 * mh_send - send mobility header message
 * @addrs: bundle of addresses
 * @mh_vec: scatter/gather array
 * @iovlen: array block count
 * @bind_key: key for calculating binding auth. data
 *
 * Sends a mobility header message to @dst with @src source address.
 * Mobility header is created from the @mh_vec vector array created by
 * the caller and initialized with mh_create() and mh_create_opt_*()
 * calls.  Padding is done automatically and mobility header length is
 * set.  Binding authorization data is calculated if present.  Returns
 * number of bytes sent on success, otherwise negative error value.
 **/
int mh_send(const struct in6_addr_bundle *addrs, const struct iovec *mh_vec,
	    int iovlen, const uint8_t *bind_key, int oif)
{
	struct ip6_mh_opt_auth_data lbad;
	struct sockaddr_in6 daddr;

	struct iovec iov[2*(IP6_MHOPT_MAX+1)];
	struct msghdr msg;
	struct cmsghdr *cmsg;
	int cmsglen;
	struct in6_pktinfo pinfo;
	int ret = 0, on = 1;
	struct ip6_mh *mh;
	int iov_count;
	socklen_t rthlen = 0;

	iov_count = mh_try_pad(mh_vec, iov, iovlen);
	mh = (struct ip6_mh *)iov[0].iov_base;
	mh->ip6mh_hdrlen = (mh_length(iov, iov_count) >> 3) - 1;

	/*
	 * We use MH out policy for all address. Then we should update it
	 * to refresh its bundle in kernel to be used with correct
	 * route, IPsec SA and neighbor cache entry for the destination.
	 * IKE daemon does the same thing for rekeying process.
	 */
        if (xfrm_cn_policy_mh_out_touch(1) < 0) {
                MDBG("MH out policy touch failed: BA for "
                     "%x:%x:%x:%x:%x:%x:%x:%x\n", NIP6ADDR(addrs->dst));
        }

	MDBG("sending MH type %d\n"
	     "from %x:%x:%x:%x:%x:%x:%x:%x\n"
	     "to %x:%x:%x:%x:%x:%x:%x:%x\n",
	     mh->ip6mh_type, NIP6ADDR(addrs->src), NIP6ADDR(addrs->dst));

	if (addrs->local_coa)
		MDBG("local CoA %x:%x:%x:%x:%x:%x:%x:%x\n",
		     NIP6ADDR(addrs->local_coa));

	if (addrs->remote_coa)
		MDBG("remote CoA %x:%x:%x:%x:%x:%x:%x:%x\n",
		     NIP6ADDR(addrs->remote_coa));

	if (bind_key) {
		assert(iov_count > 1);
		struct ip6_mh_opt_auth_data *bauth;
		struct iovec *biov;
		struct in6_addr *cn = NULL;
		MDBG("Adding bind auth data\n");
		if (mh->ip6mh_type == IP6_MH_TYPE_BU)
			cn = addrs->dst;
		else
			cn = addrs->src;
		assert(addrs->bind_coa != NULL && cn != NULL);
		biov = &iov[iov_count - 1];
		bauth = (struct ip6_mh_opt_auth_data *)biov->iov_base;

		if (bauth->ip6moad_type == IP6_MHOPT_BAUTH) {
			size_t orig_len = biov->iov_len;

			MDBG("Adding auth_data\n");
			memcpy(&lbad, bauth, sizeof(lbad));

			/* temporarily set iov_len to option header
			 * length for auth data calculation */
			biov->iov_len -= MIPV6_DIGEST_LEN;
			biov->iov_base = &lbad;
			calculate_auth_data(iov, iov_count, addrs->bind_coa,
					    cn, bind_key, lbad.ip6moad_data);
			biov->iov_len = orig_len;
		}
	}

	memset(&daddr, 0, sizeof(struct sockaddr_in6));
	daddr.sin6_family = AF_INET6;
	daddr.sin6_addr = *addrs->dst;
	daddr.sin6_port = htons(IPPROTO_MH);

	memset(&pinfo, 0, sizeof(pinfo));
	pinfo.ipi6_addr = *addrs->src;
	pinfo.ipi6_ifindex = oif;

	cmsglen = CMSG_SPACE(sizeof(pinfo));
	if (addrs->remote_coa != NULL) {
		rthlen = inet6_rth_space(IPV6_RTHDR_TYPE_2, 1);
		if (!rthlen) {
			MDBG("inet6_rth_space error\n");
			return -1;
		}
		cmsglen += CMSG_SPACE(rthlen);
	}
	cmsg = malloc(cmsglen);
	if (cmsg == NULL) {
		MDBG("malloc failed\n");
		return -ENOMEM;
	}
	memset(cmsg, 0, cmsglen);
	memset(&msg, 0, sizeof(msg));

	msg.msg_control = cmsg;
	msg.msg_controllen = cmsglen;
	msg.msg_iov = iov;
	msg.msg_iovlen = iov_count;
	msg.msg_name = (void *)&daddr;
	msg.msg_namelen = sizeof(daddr);

	cmsg = CMSG_FIRSTHDR(&msg);
	cmsg->cmsg_len = CMSG_LEN(sizeof(pinfo));
	cmsg->cmsg_level = IPPROTO_IPV6;
	cmsg->cmsg_type = IPV6_PKTINFO;
	memcpy(CMSG_DATA(cmsg), &pinfo, sizeof(pinfo));

	if (addrs->remote_coa != NULL) {
		void *rthp;

		cmsg = CMSG_NXTHDR(&msg, cmsg);
		if (cmsg == NULL) {
			free(msg.msg_control);
			MDBG("internal error\n");
			return -2;
		}
		cmsg->cmsg_len = CMSG_LEN(rthlen);
		cmsg->cmsg_level = IPPROTO_IPV6;
		cmsg->cmsg_type = IPV6_RTHDR;
		rthp = CMSG_DATA(cmsg);
		rthp = inet6_rth_init(rthp, rthlen, IPV6_RTHDR_TYPE_2, 1);
		if (rthp == NULL) {
			free(msg.msg_control);
			MDBG("inet6_rth_init error\n");
			return -3;
		}
		inet6_rth_add(rthp, addrs->remote_coa);
		rthp = NULL;
	}

	pthread_mutex_lock(&mh_sock.send_mutex);
	setsockopt(mh_sock.fd, IPPROTO_IPV6, IPV6_PKTINFO,
		   &on, sizeof(int));
	ret = sendmsg(mh_sock.fd, &msg, 0);
	if (ret < 0)
		dbg("sendmsg: %s\n", strerror(errno));

	pthread_mutex_unlock(&mh_sock.send_mutex);

	free(msg.msg_control);

	return ret;
}