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