dmake: do not set MAKEFLAGS=k
[unleashed/tickless.git] / usr / src / cmd / cmd-inet / usr.lib / ilbd / ilb / ilb_probe.c
blob0704e76cde02dae2a2150d543e0fcdd4e38f8fda
1 /*
2 * CDDL HEADER START
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
19 * CDDL HEADER END
23 * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
24 * Use is subject to license terms.
27 #include <sys/types.h>
28 #include <sys/socket.h>
29 #include <sys/time.h>
31 #include <netinet/in_systm.h>
32 #include <netinet/in.h>
33 #include <netinet/ip.h>
34 #include <netinet/ip6.h>
35 #include <arpa/inet.h>
36 #include <netinet/tcp.h>
37 #include <netinet/ip_icmp.h>
38 #include <netinet/icmp6.h>
39 #include <netinet/udp.h>
40 #include <netdb.h>
41 #include <unistd.h>
42 #include <stdio.h>
43 #include <stdlib.h>
44 #include <strings.h>
45 #include <errno.h>
46 #include <limits.h>
47 #include <signal.h>
48 #include <libgen.h>
49 #include <fcntl.h>
52 * The following values are what ilbd will set argv[0] to. This determines
53 * what type of probe to send out.
55 #define PROBE_PING "ilb_ping"
56 #define PROBE_PROTO "ilb_probe"
58 /* The transport protocol to use in the probe. Value of argv[3]. */
59 #define PROTO_TCP "TCP"
60 #define PROTO_UDP "UDP"
62 enum probe_type { ping_probe, tcp_probe, udp_probe };
64 /* Load balance mode. Value of argv[4]. */
65 #define MODE_DSR "DSR"
66 #define MODE_NAT "NAT"
67 #define MODE_HALF_NAT "HALF_NAT"
69 enum lb_mode { dsr, nat, half_nat };
71 /* Number of arguments to the command from ilbd. */
72 #define PROG_ARGC 7
74 /* Size of buffer used to receive ICMP packet */
75 #define RECV_PKT_SZ 256
78 * Struct to store the probe info (most is passed in using the argv[] array to
79 * the command given by ilbd). The argv[] contains the following.
81 * argv[0] is either PROBE_PING or PROBE_PROTO
82 * argv[1] is the VIP
83 * argv[2] is the backend server address
84 * argv[3] is the transport protocol used in the rule
85 * argv[4] is the load balance mode, "DSR", "NAT", "HALF-NAT"
86 * argv[5] is the probe port
87 * argv[6] is the probe timeout
89 * The following three fields are used in sending ICMP ECHO probe.
91 * echo_id is the ID set in the probe
92 * echo_seq is the sequence set in the probe
93 * echo_cookie is the random number data in a probe
94 * lport is the local port (in network byte order) used to send the probe
96 typedef struct {
97 enum probe_type probe;
98 struct in6_addr vip; /* argv[1] */
99 struct in6_addr srv_addr; /* argv[2] */
100 int proto; /* argv[3] */
101 enum lb_mode mode; /* argv[4] */
102 in_port_t port; /* argv[5] */
103 uint32_t timeout; /* argv[6] */
105 uint16_t echo_id;
106 uint16_t echo_seq;
107 uint32_t echo_cookie;
108 in_port_t lport;
109 } probe_param_t;
111 /* Global variable to indicate whether a timeout means success. */
112 static boolean_t timeout_is_good;
114 /* SIGALRM handler */
115 /* ARGSUSED */
116 static void
117 probe_exit(int s)
119 if (timeout_is_good) {
120 (void) printf("0");
121 exit(0);
122 } else {
123 (void) printf("-1");
124 exit(255);
129 * Checksum routine for Internet Protocol family headers (C Version)
130 * (copied from ping.c)
132 static ushort_t
133 in_cksum(ushort_t *addr, int len)
135 int nleft = len;
136 ushort_t *w = addr;
137 ushort_t answer;
138 ushort_t odd_byte = 0;
139 int sum = 0;
142 * Our algorithm is simple, using a 32 bit accumulator (sum),
143 * we add sequential 16 bit words to it, and at the end, fold
144 * back all the carry bits from the top 16 bits into the lower
145 * 16 bits.
147 while (nleft > 1) {
148 sum += *w++;
149 nleft -= 2;
152 /* mop up an odd byte, if necessary */
153 if (nleft == 1) {
154 *(uchar_t *)(&odd_byte) = *(uchar_t *)w;
155 sum += odd_byte;
159 * add back carry outs from top 16 bits to low 16 bits
161 sum = (sum >> 16) + (sum & 0xffff); /* add hi 16 to low 16 */
162 sum += (sum >> 16); /* add carry */
163 answer = ~sum; /* truncate to 16 bits */
164 return (answer);
167 /* It is assumed that argv[] contains PROBE_ARGC arguments. */
168 static boolean_t
169 parse_probe_param(char *argv[], probe_param_t *param)
171 int32_t port;
172 int64_t timeout;
173 struct in_addr v4addr;
175 if (strcmp(basename(argv[0]), PROBE_PING) == 0) {
176 param->probe = ping_probe;
177 } else {
178 if (strcmp(basename(argv[0]), PROBE_PROTO) != 0)
179 return (B_FALSE);
181 if (strcasecmp(argv[3], PROTO_TCP) == 0) {
182 param->probe = tcp_probe;
183 param->proto = IPPROTO_TCP;
184 } else if (strcasecmp(argv[3], PROTO_UDP) == 0) {
185 param->probe = udp_probe;
186 param->proto = IPPROTO_UDP;
187 } else {
188 return (B_FALSE);
192 if (strchr(argv[1], ':') != NULL) {
193 if (inet_pton(AF_INET6, argv[1], &param->vip) == 0)
194 return (B_FALSE);
195 } else if (strchr(argv[1], '.') != NULL) {
196 if (inet_pton(AF_INET, argv[1], &v4addr) == 0)
197 return (B_FALSE);
198 IN6_INADDR_TO_V4MAPPED(&v4addr, &param->vip);
199 } else {
200 return (B_FALSE);
204 * The address family of vip and srv_addr should be the same for
205 * now. But in future, we may allow them to be different... So
206 * we don't do a check here.
208 if (strchr(argv[2], ':') != NULL) {
209 if (inet_pton(AF_INET6, argv[2], &param->srv_addr) == 0)
210 return (B_FALSE);
211 } else if (strchr(argv[2], '.') != NULL) {
212 if (inet_pton(AF_INET, argv[2], &v4addr) == 0)
213 return (B_FALSE);
214 IN6_INADDR_TO_V4MAPPED(&v4addr, &param->srv_addr);
215 } else {
216 return (B_FALSE);
219 if (strcasecmp(argv[4], MODE_DSR) == 0)
220 param->mode = dsr;
221 else if (strcasecmp(argv[4], MODE_NAT) == 0)
222 param->mode = nat;
223 else if (strcasecmp(argv[4], MODE_HALF_NAT) == 0)
224 param->mode = half_nat;
225 else
226 return (B_FALSE);
228 if ((port = atoi(argv[5])) <= 0 || port > USHRT_MAX)
229 return (B_FALSE);
230 param->port = port;
232 if ((timeout = strtoll(argv[6], NULL, 10)) <= 0 || timeout > UINT_MAX)
233 return (B_FALSE);
234 param->timeout = timeout;
236 return (B_TRUE);
240 * Set up the destination address to be used to send a probe based on
241 * param.
243 static int
244 set_sockaddr(struct sockaddr_storage *addr, socklen_t *addr_len,
245 void **next_hop, probe_param_t *param)
247 int af;
248 struct in6_addr *param_addr;
249 struct sockaddr_in *v4_addr;
250 struct sockaddr_in6 *v6_addr;
251 boolean_t nh = B_FALSE;
253 switch (param->mode) {
254 case dsr:
255 param_addr = &param->vip;
256 nh = B_TRUE;
257 break;
258 case nat:
259 case half_nat:
260 param_addr = &param->srv_addr;
261 break;
263 if (IN6_IS_ADDR_V4MAPPED(param_addr)) {
264 af = AF_INET;
265 v4_addr = (struct sockaddr_in *)addr;
266 IN6_V4MAPPED_TO_INADDR(param_addr, &v4_addr->sin_addr);
267 v4_addr->sin_family = AF_INET;
268 v4_addr->sin_port = htons(param->port);
270 *addr_len = sizeof (*v4_addr);
271 } else {
272 af = AF_INET6;
273 v6_addr = (struct sockaddr_in6 *)addr;
274 v6_addr->sin6_family = AF_INET6;
275 v6_addr->sin6_addr = *param_addr;
276 v6_addr->sin6_port = htons(param->port);
277 v6_addr->sin6_flowinfo = 0;
278 v6_addr->sin6_scope_id = 0;
280 *addr_len = sizeof (*v6_addr);
283 if (!nh) {
284 *next_hop = NULL;
285 return (af);
288 if (af == AF_INET) {
289 ipaddr_t *nh_addr;
291 nh_addr = malloc(sizeof (ipaddr_t));
292 IN6_V4MAPPED_TO_IPADDR(&param->srv_addr, *nh_addr);
293 *next_hop = nh_addr;
294 } else {
295 struct sockaddr_in6 *nh_addr;
297 nh_addr = malloc(sizeof (*nh_addr));
298 nh_addr->sin6_family = AF_INET6;
299 nh_addr->sin6_addr = param->srv_addr;
300 nh_addr->sin6_flowinfo = 0;
301 nh_addr->sin6_scope_id = 0;
302 *next_hop = nh_addr;
305 return (af);
309 * Use TCP to check if the peer server is alive. Create a TCP socket and
310 * then call connect() to reach the peer server. If connect() does not
311 * return within the timeout period, the SIGALRM handler will be invoked
312 * and tell ilbd that the peer server is not alive.
314 static int
315 tcp_query(probe_param_t *param)
317 int ret;
318 int sd, af;
319 struct sockaddr_storage dst_addr;
320 socklen_t dst_addr_len;
321 void *next_hop;
322 hrtime_t start, end;
323 uint32_t rtt;
325 ret = 0;
326 next_hop = NULL;
328 af = set_sockaddr(&dst_addr, &dst_addr_len, &next_hop, param);
330 if ((sd = socket(af, SOCK_STREAM, param->proto)) == -1)
331 return (-1);
333 /* DSR mode, need to set the next hop */
334 if (next_hop != NULL) {
335 if (af == AF_INET) {
336 if (setsockopt(sd, IPPROTO_IP, IP_NEXTHOP, next_hop,
337 sizeof (ipaddr_t)) < 0) {
338 ret = -1;
339 goto out;
341 } else {
342 if (setsockopt(sd, IPPROTO_IPV6, IPV6_NEXTHOP,
343 next_hop, sizeof (struct sockaddr_in6)) < 0) {
344 ret = -1;
345 goto out;
350 timeout_is_good = B_FALSE;
351 (void) alarm(param->timeout);
352 start = gethrtime();
353 if (connect(sd, (struct sockaddr *)&dst_addr, dst_addr_len) != 0) {
354 ret = -1;
355 goto out;
357 end = gethrtime();
359 rtt = (end - start) / (NANOSEC / MICROSEC);
360 if (rtt == 0)
361 rtt = 1;
362 (void) printf("%u", rtt);
364 out:
365 (void) close(sd);
366 return (ret);
370 * Check if the ICMP packet is a port unreachable message in respnsed to
371 * our probe. Return -1 if no, 0 if yes.
373 static int
374 check_icmp_unreach_v4(struct icmp *icmph, probe_param_t *param)
376 struct udphdr *udph;
377 struct ip *iph;
379 if (icmph->icmp_type != ICMP_UNREACH)
380 return (-1);
381 if (icmph->icmp_code != ICMP_UNREACH_PORT)
382 return (-1);
384 /* LINTED E_BAD_PTR_CAST_ALIGN */
385 iph = (struct ip *)((char *)icmph + ICMP_MINLEN);
386 if (iph->ip_p != IPPROTO_UDP)
387 return (-1);
389 /* LINTED E_BAD_PTR_CAST_ALIGN */
390 udph = (struct udphdr *)((char *)iph + (iph->ip_hl << 2));
391 if (udph->uh_dport != htons(param->port))
392 return (-1);
393 if (udph->uh_sport != param->lport)
394 return (-1);
396 /* All matched, it is a response to the probe we sent. */
397 return (0);
401 * Check if the ICMP packet is a reply to our echo request. Need to match
402 * the ID and sequence.
404 static int
405 check_icmp_echo_v4(struct icmp *icmph, probe_param_t *param)
407 uint32_t cookie;
408 in_port_t port;
410 if (icmph->icmp_type != ICMP_ECHOREPLY)
411 return (-1);
412 if (icmph->icmp_id != param->echo_id)
413 return (-1);
414 if (icmph->icmp_seq != param->echo_seq)
415 return (-1);
417 bcopy(icmph->icmp_data, &cookie, sizeof (cookie));
418 if (cookie != param->echo_cookie)
419 return (-1);
420 bcopy(icmph->icmp_data + sizeof (cookie), &port, sizeof (port));
421 if (port != param->port)
422 return (-1);
424 /* All matched, it is a response to the echo we sent. */
425 return (0);
428 /* Verify if an ICMP packet is what we expect. */
429 static int
430 check_icmp_v4(char *buf, ssize_t rcvd, probe_param_t *param)
432 struct ip *iph;
433 struct icmp *icmph;
436 * We can dereference the length field without worry since the stack
437 * should not have sent up the packet if it is smaller than a normal
438 * ICMPv4 packet.
440 /* LINTED E_BAD_PTR_CAST_ALIGN */
441 iph = (struct ip *)buf;
442 /* LINTED E_BAD_PTR_CAST_ALIGN */
443 icmph = (struct icmp *)((char *)iph + (iph->ip_hl << 2));
446 * If we sent an UDP probe, check if the packet is a port
447 * unreachable message in response to our probe.
449 * If we sent an ICMP echo request, check if the packet is a reply
450 * to our echo request.
452 if (param->probe == udp_probe) {
453 /* Is the packet large enough for further checking? */
454 if (rcvd < 2 * sizeof (struct ip) + ICMP_MINLEN +
455 sizeof (struct udphdr)) {
456 return (-1);
458 return (check_icmp_unreach_v4(icmph, param));
459 } else {
460 if (rcvd < sizeof (struct ip) + ICMP_MINLEN)
461 return (-1);
462 return (check_icmp_echo_v4(icmph, param));
467 * Check if the ICMPv6 packet is a port unreachable message in respnsed to
468 * our probe. Return -1 if no, 0 if yes.
470 static int
471 check_icmp_unreach_v6(icmp6_t *icmp6h, probe_param_t *param)
473 ip6_t *ip6h;
474 struct udphdr *udph;
476 if (icmp6h->icmp6_type != ICMP6_DST_UNREACH)
477 return (-1);
478 if (icmp6h->icmp6_code != ICMP6_DST_UNREACH_NOPORT)
479 return (-1);
481 /* LINTED E_BAD_PTR_CAST_ALIGN */
482 ip6h = (ip6_t *)((char *)icmp6h + ICMP6_MINLEN);
483 if (ip6h->ip6_nxt != IPPROTO_UDP)
484 return (-1);
486 udph = (struct udphdr *)(ip6h + 1);
488 if (udph->uh_dport != htons(param->port))
489 return (-1);
490 if (udph->uh_sport != param->lport)
491 return (-1);
493 return (0);
497 * Check if the ICMPv6 packet is a reply to our echo request. Need to match
498 * the ID and sequence.
500 static int
501 check_icmp_echo_v6(icmp6_t *icmp6h, probe_param_t *param)
503 char *tmp;
504 uint32_t cookie;
505 in_port_t port;
507 if (icmp6h->icmp6_type != ICMP6_ECHO_REPLY)
508 return (-1);
509 if (icmp6h->icmp6_id != param->echo_id)
510 return (-1);
511 if (icmp6h->icmp6_seq != param->echo_seq)
512 return (-1);
513 tmp = (char *)icmp6h + ICMP6_MINLEN;
514 bcopy(tmp, &cookie, sizeof (cookie));
515 if (cookie != param->echo_cookie)
516 return (-1);
517 tmp += sizeof (cookie);
518 bcopy(tmp, &port, sizeof (port));
519 if (port != param->port)
520 return (-1);
522 /* All matched, it is a response to the echo we sent. */
523 return (0);
526 /* Verify if an ICMPv6 packet is what we expect. */
527 static int
528 check_icmp_v6(char *buf, ssize_t rcvd, probe_param_t *param)
530 icmp6_t *icmp6h;
532 /* LINTED E_BAD_PTR_CAST_ALIGN */
533 icmp6h = (icmp6_t *)(buf);
536 * If we sent an UDP probe, check if the packet is a port
537 * unreachable message.
539 * If we sent an ICMPv6 echo request, check if the packet is a reply.
541 if (param->probe == udp_probe) {
542 /* Is the packet large enough for further checking? */
543 if (rcvd < sizeof (ip6_t) + ICMP6_MINLEN +
544 sizeof (struct udphdr)) {
545 return (-1);
547 return (check_icmp_unreach_v6(icmp6h, param));
548 } else {
549 if (rcvd < ICMP6_MINLEN)
550 return (-1);
551 return (check_icmp_echo_v6(icmp6h, param));
556 * Wait for an ICMP reply indefinitely. If we get what we expect, return 0.
557 * If an error happnes, return -1.
559 static int
560 wait_icmp_reply(int af, int recv_sd, struct sockaddr_storage *exp_from,
561 probe_param_t *param)
563 char buf[RECV_PKT_SZ];
564 socklen_t from_len;
565 ssize_t rcvd;
566 int ret;
568 for (;;) {
569 if (af == AF_INET) {
570 struct sockaddr_in v4_from;
572 from_len = sizeof (v4_from);
573 if ((rcvd = recvfrom(recv_sd, buf, RECV_PKT_SZ, 0,
574 (struct sockaddr *)&v4_from, &from_len)) < 0) {
575 ret = -1;
576 break;
579 /* Packet not from our peer, ignore it. */
580 if ((((struct sockaddr_in *)exp_from)->sin_addr.s_addr)
581 != v4_from.sin_addr.s_addr) {
582 continue;
584 if (check_icmp_v4(buf, rcvd, param) == 0) {
585 ret = 0;
586 break;
588 } else {
589 struct sockaddr_in6 v6_from;
591 from_len = sizeof (struct sockaddr_in6);
592 if ((rcvd = recvfrom(recv_sd, buf, RECV_PKT_SZ, 0,
593 (struct sockaddr *)&v6_from, &from_len)) < 0) {
594 ret = -1;
595 break;
598 if (!IN6_ARE_ADDR_EQUAL(&(v6_from.sin6_addr),
599 &((struct sockaddr_in6 *)exp_from)->sin6_addr)) {
600 continue;
602 if (check_icmp_v6(buf, rcvd, param) == 0) {
603 ret = 0;
604 break;
608 return (ret);
611 /* Return the local port used (network byte order) in a socket. */
612 static int
613 get_lport(int sd, in_port_t *lport)
615 struct sockaddr_storage addr;
616 socklen_t addr_sz;
618 addr_sz = sizeof (addr);
619 if (getsockname(sd, (struct sockaddr *)&addr, &addr_sz) != 0)
620 return (-1);
621 if (addr.ss_family == AF_INET)
622 *lport = ((struct sockaddr_in *)&addr)->sin_port;
623 else
624 *lport = ((struct sockaddr_in6 *)&addr)->sin6_port;
625 return (0);
629 * Use UDP to check if the peer server is alive. Send a 0 length UDP packet
630 * to the peer server. If there is no one listening, the peer IP stack
631 * should send back a port unreachable ICMP(v4/v6) packet. If the peer
632 * server is alive, there should be no response. So if we get SIGALRM,
633 * the peer is alive.
635 static int
636 udp_query(probe_param_t *param)
638 int ret;
639 int send_sd, recv_sd, af;
640 struct sockaddr_storage dst_addr;
641 socklen_t addr_len;
642 void *next_hop;
643 char buf[1];
644 struct itimerval timeout;
645 uint64_t tm;
647 ret = 0;
648 next_hop = NULL;
650 af = set_sockaddr(&dst_addr, &addr_len, &next_hop, param);
652 if ((send_sd = socket(af, SOCK_DGRAM, param->proto)) == -1)
653 return (-1);
654 if ((recv_sd = socket(af, SOCK_RAW, (af == AF_INET) ? IPPROTO_ICMP :
655 IPPROTO_ICMPV6)) == -1) {
656 return (-1);
659 /* DSR mode, need to set the next hop */
660 if (next_hop != NULL) {
661 if (af == AF_INET) {
662 if (setsockopt(send_sd, IPPROTO_IP, IP_NEXTHOP,
663 next_hop, sizeof (ipaddr_t)) < 0) {
664 ret = -1;
665 goto out;
667 } else {
668 if (setsockopt(send_sd, IPPROTO_IPV6, IPV6_NEXTHOP,
669 next_hop, sizeof (struct sockaddr_in6)) < 0) {
670 ret = -1;
671 goto out;
677 * If ilbd asks us to wait at most t, we will wait for at most
678 * t', which is 3/4 of t. If we wait for too long, ilbd may
679 * timeout and kill us.
681 timeout.it_interval.tv_sec = 0;
682 timeout.it_interval.tv_usec = 0;
683 tm = (param->timeout * MICROSEC >> 2) * 3;
684 if (tm > MICROSEC) {
685 timeout.it_value.tv_sec = tm / MICROSEC;
686 timeout.it_value.tv_usec = tm - (timeout.it_value.tv_sec *
687 MICROSEC);
688 } else {
689 timeout.it_value.tv_sec = 0;
690 timeout.it_value.tv_usec = tm;
692 timeout_is_good = B_TRUE;
693 if (setitimer(ITIMER_REAL, &timeout, NULL) != 0) {
694 ret = -1;
695 goto out;
698 if (sendto(send_sd, buf, 0, 0, (struct sockaddr *)&dst_addr,
699 addr_len) != 0) {
700 ret = -1;
701 goto out;
703 if ((ret = get_lport(send_sd, &param->lport)) != 0)
704 goto out;
707 * If the server app is listening, we should not get back a
708 * response. So if wait_icmp_reply() returns, either there
709 * is an error or we get back something.
711 (void) wait_icmp_reply(af, recv_sd, &dst_addr, param);
712 ret = -1;
714 out:
715 (void) close(send_sd);
716 (void) close(recv_sd);
717 return (ret);
721 * Size (in uint32_t) of the ping packet to be sent to server. It includes
722 * a cookie (random number) + the target port. The cookie and port are used
723 * for matching ping request since there can be many such ping packets sent
724 * to different servers from the same source address and using the same VIP.
725 * The last two bytes are for padding.
728 #define PING_PKT_LEN \
729 ((ICMP_MINLEN + 2 * sizeof (uint32_t)) / sizeof (uint32_t))
732 * Try to get a random number from the pseudo random number device
733 * /dev/urandom. If there is any error, return (uint32_t)gethrtime()
734 * as a back up.
736 static uint32_t
737 get_random(void)
739 int fd;
740 uint32_t num;
742 if ((fd = open("/dev/urandom", O_RDONLY)) == -1)
743 return ((uint32_t)gethrtime());
745 if (read(fd, &num, sizeof (num)) != sizeof (num))
746 num = ((uint32_t)gethrtime());
748 (void) close(fd);
749 return (num);
753 * Use ICMP(v4/v6) echo request to check if the peer server machine is
754 * reachable. Send a echo request and expect to get back a echo reply.
756 static int
757 ping_query(probe_param_t *param)
759 int ret;
760 int sd, af;
761 struct sockaddr_storage dst_addr;
762 socklen_t dst_addr_len;
763 void *next_hop;
764 hrtime_t start, end;
765 uint32_t rtt;
766 uint32_t buf[PING_PKT_LEN];
767 struct icmp *icmph;
769 ret = 0;
770 next_hop = NULL;
772 af = set_sockaddr(&dst_addr, &dst_addr_len, &next_hop, param);
774 if ((sd = socket(af, SOCK_RAW, (af == AF_INET) ? IPPROTO_ICMP :
775 IPPROTO_ICMPV6)) == -1) {
776 return (-1);
779 /* DSR mode, need to set the next hop */
780 if (next_hop != NULL) {
781 if (af == AF_INET) {
782 if (setsockopt(sd, IPPROTO_IP, IP_NEXTHOP, next_hop,
783 sizeof (ipaddr_t)) < 0) {
784 ret = -1;
785 goto out;
787 } else {
788 if (setsockopt(sd, IPPROTO_IPV6, IPV6_NEXTHOP,
789 next_hop, sizeof (struct sockaddr_in6)) < 0) {
790 ret = -1;
791 goto out;
796 bzero(buf, sizeof (buf));
797 icmph = (struct icmp *)buf;
798 icmph->icmp_type = af == AF_INET ? ICMP_ECHO : ICMP6_ECHO_REQUEST;
799 icmph->icmp_code = 0;
800 icmph->icmp_cksum = 0;
801 icmph->icmp_id = htons(gethrtime() % USHRT_MAX);
802 icmph->icmp_seq = htons(gethrtime() % USHRT_MAX);
804 param->echo_cookie = get_random();
805 bcopy(&param->echo_cookie, icmph->icmp_data,
806 sizeof (param->echo_cookie));
807 bcopy(&param->port, icmph->icmp_data + sizeof (param->echo_cookie),
808 sizeof (param->port));
809 icmph->icmp_cksum = in_cksum((ushort_t *)buf, sizeof (buf));
810 param->echo_id = icmph->icmp_id;
811 param->echo_seq = icmph->icmp_seq;
813 timeout_is_good = B_FALSE;
814 (void) alarm(param->timeout);
815 start = gethrtime();
816 if (sendto(sd, buf, sizeof (buf), 0, (struct sockaddr *)&dst_addr,
817 dst_addr_len) != sizeof (buf)) {
818 ret = -1;
819 goto out;
821 if (wait_icmp_reply(af, sd, &dst_addr, param) != 0) {
822 ret = -1;
823 goto out;
825 end = gethrtime();
827 rtt = (end - start) / (NANOSEC / MICROSEC);
828 if (rtt == 0)
829 rtt = 1;
830 (void) printf("%u", rtt);
832 out:
833 (void) close(sd);
834 return (ret);
838 main(int argc, char *argv[])
840 probe_param_t param;
841 int ret;
843 /* ilbd should pass in PROG_ARGC parameters. */
844 if (argc != PROG_ARGC) {
845 (void) printf("-1");
846 return (-1);
849 if (signal(SIGALRM, probe_exit) == SIG_ERR) {
850 (void) printf("-1");
851 return (-1);
854 if (!parse_probe_param(argv, &param)) {
855 (void) printf("-1");
856 return (-1);
859 switch (param.probe) {
860 case ping_probe:
861 ret = ping_query(&param);
862 break;
863 case tcp_probe:
864 ret = tcp_query(&param);
865 break;
866 case udp_probe:
867 ret = udp_query(&param);
868 break;
871 if (ret == -1)
872 (void) printf("-1");
874 return (ret);