Mercurial > illumos > onarm
comparison usr/src/cmd/fm/modules/common/ip-transport/ip.c @ 0:c9caec207d52 b86
Initial porting based on b86
author | Koji Uno <koji.uno@sun.com> |
---|---|
date | Tue, 02 Jun 2009 18:56:50 +0900 |
parents | |
children | 1a15d5aaf794 |
comparison
equal
deleted
inserted
replaced
-1:000000000000 | 0:c9caec207d52 |
---|---|
1 /* | |
2 * CDDL HEADER START | |
3 * | |
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. | |
7 * | |
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. | |
12 * | |
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] | |
18 * | |
19 * CDDL HEADER END | |
20 */ | |
21 | |
22 /* | |
23 * Copyright 2006 Sun Microsystems, Inc. All rights reserved. | |
24 * Use is subject to license terms. | |
25 */ | |
26 | |
27 #pragma ident "@(#)ip.c 1.2 06/03/08 SMI" | |
28 | |
29 #include <sys/types.h> | |
30 #include <sys/socket.h> | |
31 #include <sys/sysmacros.h> | |
32 #include <sys/fm/protocol.h> | |
33 | |
34 #include <netinet/in.h> | |
35 #include <arpa/inet.h> | |
36 | |
37 #include <strings.h> | |
38 #include <unistd.h> | |
39 #include <pthread.h> | |
40 #include <alloca.h> | |
41 #include <fcntl.h> | |
42 #include <errno.h> | |
43 #include <netdb.h> | |
44 #include <poll.h> | |
45 | |
46 #include <fm/fmd_api.h> | |
47 | |
48 #define IP_MAGIC "\177FMA" /* magic string identifying a packet header */ | |
49 #define IP_MAGLEN 4 /* length of magic string */ | |
50 | |
51 typedef struct ip_hdr { | |
52 char iph_magic[IP_MAGLEN]; /* magic string */ | |
53 uint32_t iph_size; /* packed size */ | |
54 } ip_hdr_t; | |
55 | |
56 typedef struct ip_buf { | |
57 void *ipb_buf; /* data buffer */ | |
58 size_t ipb_size; /* size of buffer */ | |
59 } ip_buf_t; | |
60 | |
61 typedef struct ip_xprt { | |
62 fmd_xprt_t *ipx_xprt; /* transport handle */ | |
63 int ipx_flags; /* transport flags */ | |
64 int ipx_fd; /* socket file descriptor */ | |
65 int ipx_done; /* flag indicating connection closed */ | |
66 pthread_t ipx_tid; /* recv-side auxiliary thread */ | |
67 ip_buf_t ipx_sndbuf; /* buffer for sending events */ | |
68 ip_buf_t ipx_rcvbuf; /* buffer for receiving events */ | |
69 struct ip_xprt *ipx_next; /* next ip_xprt in global list */ | |
70 } ip_xprt_t; | |
71 | |
72 typedef struct ip_stat { | |
73 fmd_stat_t ips_accfail; /* failed accepts */ | |
74 fmd_stat_t ips_badmagic; /* invalid packet headers */ | |
75 fmd_stat_t ips_packfail; /* failed packs */ | |
76 fmd_stat_t ips_unpackfail; /* failed unpacks */ | |
77 } ip_stat_t; | |
78 | |
79 static void ip_xprt_create(fmd_xprt_t *, int, int); | |
80 static void ip_xprt_destroy(ip_xprt_t *); | |
81 | |
82 static ip_stat_t ip_stat = { | |
83 { "accfail", FMD_TYPE_UINT64, "failed accepts" }, | |
84 { "badmagic", FMD_TYPE_UINT64, "invalid packet headers" }, | |
85 { "packfail", FMD_TYPE_UINT64, "failed packs" }, | |
86 { "unpackfail", FMD_TYPE_UINT64, "failed unpacks" }, | |
87 }; | |
88 | |
89 static fmd_hdl_t *ip_hdl; /* module handle */ | |
90 static pthread_mutex_t ip_lock; /* lock for ip_xps list */ | |
91 static ip_xprt_t *ip_xps; /* list of active transports */ | |
92 static nvlist_t *ip_auth; /* authority to use for transport(s) */ | |
93 static size_t ip_size; /* default buffer size */ | |
94 static volatile int ip_quit; /* signal to quit */ | |
95 static int ip_qlen; /* queue length for listen(3SOCKET) */ | |
96 static int ip_mtbf; /* mtbf for simulating packet drop */ | |
97 static hrtime_t ip_burp; /* make mtbf slower by adding this much delay */ | |
98 static int ip_translate; /* call fmd_xprt_translate() before sending */ | |
99 static char *ip_host; /* host to connect to (or NULL if server) */ | |
100 static char *ip_port; /* port to connect to (or bind to if server) */ | |
101 static struct addrinfo *ip_ail; /* addr info list for ip_host/ip_port */ | |
102 static uint_t ip_retry; /* retry count for ip_xprt_setup() */ | |
103 static hrtime_t ip_sleep; /* sleep delay for ip_xprt_setup() */ | |
104 | |
105 /* | |
106 * Allocate space in ipx_sndbuf for a header and a packed XDR encoding of | |
107 * the specified nvlist, and then send the buffer to our remote peer. | |
108 */ | |
109 /*ARGSUSED*/ | |
110 static int | |
111 ip_xprt_send(fmd_hdl_t *hdl, fmd_xprt_t *xp, fmd_event_t *ep, nvlist_t *nvl) | |
112 { | |
113 ip_xprt_t *ipx = fmd_xprt_getspecific(hdl, xp); | |
114 | |
115 size_t size, nvsize; | |
116 char *buf, *nvbuf; | |
117 ip_hdr_t *iph; | |
118 ssize_t r, n; | |
119 int err; | |
120 | |
121 /* | |
122 * For testing purposes, if ip_mtbf is non-zero, use this to pseudo- | |
123 * randomly simulate the need for retries. If ip_burp is also set, | |
124 * then we also suspend the transport for a bit and wake it up again. | |
125 */ | |
126 if (ip_mtbf != 0 && gethrtime() % ip_mtbf == 0) { | |
127 if (ip_burp != 0) { | |
128 fmd_hdl_debug(ip_hdl, "burping ipx %p", (void *)ipx); | |
129 ipx->ipx_flags |= FMD_XPRT_SUSPENDED; | |
130 (void) fmd_timer_install(ip_hdl, ipx, NULL, ip_burp); | |
131 fmd_xprt_suspend(ip_hdl, xp); | |
132 } | |
133 return (FMD_SEND_RETRY); | |
134 } | |
135 | |
136 if (ip_translate && (nvl = fmd_xprt_translate(hdl, xp, ep)) == NULL) { | |
137 fmd_hdl_error(hdl, "failed to translate event %p", (void *)ep); | |
138 return (FMD_SEND_FAILED); | |
139 } | |
140 | |
141 (void) nvlist_size(nvl, &nvsize, NV_ENCODE_XDR); | |
142 size = r = sizeof (ip_hdr_t) + nvsize; | |
143 | |
144 if (ipx->ipx_sndbuf.ipb_size < size) { | |
145 fmd_hdl_free(hdl, ipx->ipx_sndbuf.ipb_buf, | |
146 ipx->ipx_sndbuf.ipb_size); | |
147 ipx->ipx_sndbuf.ipb_size = P2ROUNDUP(size, 16); | |
148 ipx->ipx_sndbuf.ipb_buf = fmd_hdl_alloc(hdl, | |
149 ipx->ipx_sndbuf.ipb_size, FMD_SLEEP); | |
150 } | |
151 | |
152 buf = ipx->ipx_sndbuf.ipb_buf; | |
153 iph = (ip_hdr_t *)(uintptr_t)buf; | |
154 nvbuf = buf + sizeof (ip_hdr_t); | |
155 | |
156 bcopy(IP_MAGIC, iph->iph_magic, IP_MAGLEN); | |
157 iph->iph_size = htonl(nvsize); | |
158 err = nvlist_pack(nvl, &nvbuf, &nvsize, NV_ENCODE_XDR, 0); | |
159 | |
160 if (ip_translate) | |
161 nvlist_free(nvl); | |
162 | |
163 if (err != 0) { | |
164 fmd_hdl_error(ip_hdl, "failed to pack event for " | |
165 "transport %p: %s\n", (void *)ipx->ipx_xprt, strerror(err)); | |
166 ip_stat.ips_packfail.fmds_value.ui64++; | |
167 return (FMD_SEND_FAILED); | |
168 } | |
169 | |
170 while (!ip_quit && r != 0) { | |
171 if ((n = send(ipx->ipx_fd, buf, r, 0)) < 0) { | |
172 if (errno != EINTR && errno != EWOULDBLOCK) { | |
173 fmd_hdl_debug(ip_hdl, | |
174 "failed to send on ipx %p", (void *)ipx); | |
175 return (FMD_SEND_FAILED); | |
176 } | |
177 continue; | |
178 } | |
179 buf += n; | |
180 r -= n; | |
181 } | |
182 | |
183 return (FMD_SEND_SUCCESS); | |
184 } | |
185 | |
186 /* | |
187 * Receive a chunk of data of the specified size from our remote peer. The | |
188 * data is received into ipx_rcvbuf, and then a pointer to the buffer is | |
189 * returned. NOTE: The data is only valid until the next call to ip_xprt_recv. | |
190 * If the connection breaks or ip_quit is set during receive, NULL is returned. | |
191 */ | |
192 static void * | |
193 ip_xprt_recv(ip_xprt_t *ipx, size_t size) | |
194 { | |
195 char *buf = ipx->ipx_rcvbuf.ipb_buf; | |
196 ssize_t n, r = size; | |
197 | |
198 if (ipx->ipx_rcvbuf.ipb_size < size) { | |
199 fmd_hdl_free(ip_hdl, ipx->ipx_rcvbuf.ipb_buf, | |
200 ipx->ipx_rcvbuf.ipb_size); | |
201 ipx->ipx_rcvbuf.ipb_size = P2ROUNDUP(size, 16); | |
202 ipx->ipx_rcvbuf.ipb_buf = buf = fmd_hdl_alloc(ip_hdl, | |
203 ipx->ipx_rcvbuf.ipb_size, FMD_SLEEP); | |
204 } | |
205 | |
206 while (!ip_quit && r != 0) { | |
207 if ((n = recv(ipx->ipx_fd, buf, r, MSG_WAITALL)) == 0) { | |
208 ipx->ipx_done++; | |
209 return (NULL); | |
210 } | |
211 | |
212 if (n < 0) { | |
213 if (errno != EINTR && errno != EWOULDBLOCK) { | |
214 fmd_hdl_debug(ip_hdl, | |
215 "failed to recv on ipx %p", (void *)ipx); | |
216 } | |
217 continue; | |
218 } | |
219 | |
220 buf += n; | |
221 r -= n; | |
222 } | |
223 | |
224 return (r ? NULL: ipx->ipx_rcvbuf.ipb_buf); | |
225 } | |
226 | |
227 static nvlist_t * | |
228 ip_xprt_auth(const struct sockaddr *sap) | |
229 { | |
230 const struct sockaddr_in6 *sin6 = (const void *)sap; | |
231 const struct sockaddr_in *sin = (const void *)sap; | |
232 | |
233 char buf[INET6_ADDRSTRLEN + 16]; | |
234 struct in_addr v4addr; | |
235 in_port_t port; | |
236 | |
237 nvlist_t *nvl; | |
238 size_t n; | |
239 int err; | |
240 | |
241 if (ip_auth != NULL) | |
242 err = nvlist_dup(ip_auth, &nvl, 0); | |
243 else | |
244 err = nvlist_alloc(&nvl, 0, 0); | |
245 | |
246 if (err != 0) { | |
247 fmd_hdl_abort(ip_hdl, "failed to create nvlist for " | |
248 "authority: %s\n", strerror(err)); | |
249 } | |
250 | |
251 if (ip_auth != NULL) | |
252 return (nvl); | |
253 | |
254 if (sap->sa_family == AF_INET6 && | |
255 IN6_IS_ADDR_V4MAPPED(&sin6->sin6_addr)) { | |
256 IN6_V4MAPPED_TO_INADDR(&sin6->sin6_addr, &v4addr); | |
257 (void) inet_ntop(AF_INET, &v4addr, buf, sizeof (buf)); | |
258 port = ntohs(sin6->sin6_port); | |
259 } else if (sap->sa_family == AF_INET6) { | |
260 (void) inet_ntop(AF_INET6, &sin6->sin6_addr, buf, sizeof (buf)); | |
261 port = ntohs(sin6->sin6_port); | |
262 } else { | |
263 (void) inet_ntop(AF_INET, &sin->sin_addr, buf, sizeof (buf)); | |
264 port = ntohs(sin->sin_port); | |
265 } | |
266 | |
267 n = strlen(buf); | |
268 (void) snprintf(buf + n, sizeof (buf) - n, ":%u", port); | |
269 fmd_hdl_debug(ip_hdl, "ip_authority %s=%s\n", FM_FMRI_AUTH_SERVER, buf); | |
270 | |
271 (void) nvlist_add_uint8(nvl, FM_VERSION, FM_FMRI_AUTH_VERSION); | |
272 (void) nvlist_add_string(nvl, FM_FMRI_AUTH_SERVER, buf); | |
273 | |
274 return (nvl); | |
275 } | |
276 | |
277 static void | |
278 ip_xprt_accept(ip_xprt_t *ipx) | |
279 { | |
280 struct sockaddr_storage sa; | |
281 socklen_t salen = sizeof (sa); | |
282 fmd_xprt_t *xp; | |
283 int fd; | |
284 | |
285 if ((fd = accept(ipx->ipx_fd, (struct sockaddr *)&sa, &salen)) == -1) { | |
286 fmd_hdl_error(ip_hdl, "failed to accept connection"); | |
287 ip_stat.ips_accfail.fmds_value.ui64++; | |
288 return; | |
289 } | |
290 | |
291 xp = fmd_xprt_open(ip_hdl, ipx->ipx_flags, | |
292 ip_xprt_auth((struct sockaddr *)&sa), NULL); | |
293 ip_xprt_create(xp, fd, ipx->ipx_flags); | |
294 } | |
295 | |
296 static void | |
297 ip_xprt_recv_event(ip_xprt_t *ipx) | |
298 { | |
299 ip_hdr_t *iph; | |
300 nvlist_t *nvl; | |
301 size_t size; | |
302 void *buf; | |
303 int err; | |
304 | |
305 if ((iph = ip_xprt_recv(ipx, sizeof (ip_hdr_t))) == NULL) | |
306 return; /* connection broken */ | |
307 | |
308 if (bcmp(iph->iph_magic, IP_MAGIC, IP_MAGLEN) != 0) { | |
309 fmd_hdl_error(ip_hdl, | |
310 "invalid hdr magic %x.%x.%x.%x from transport %p\n", | |
311 iph->iph_magic[0], iph->iph_magic[1], iph->iph_magic[2], | |
312 iph->iph_magic[3], (void *)ipx->ipx_xprt); | |
313 ip_stat.ips_badmagic.fmds_value.ui64++; | |
314 return; | |
315 } | |
316 | |
317 size = ntohl(iph->iph_size); | |
318 | |
319 if ((buf = ip_xprt_recv(ipx, size)) == NULL) | |
320 return; /* connection broken */ | |
321 | |
322 if ((err = nvlist_unpack(buf, size, &nvl, 0)) != 0) { | |
323 fmd_hdl_error(ip_hdl, "failed to unpack event from " | |
324 "transport %p: %s\n", (void *)ipx->ipx_xprt, strerror(err)); | |
325 ip_stat.ips_unpackfail.fmds_value.ui64++; | |
326 } else | |
327 fmd_xprt_post(ip_hdl, ipx->ipx_xprt, nvl, 0); | |
328 | |
329 if (fmd_xprt_error(ip_hdl, ipx->ipx_xprt)) { | |
330 fmd_hdl_error(ip_hdl, "protocol error on transport %p", | |
331 (void *)ipx->ipx_xprt); | |
332 ipx->ipx_done++; | |
333 } | |
334 } | |
335 | |
336 static void | |
337 ip_xprt_thread(void *arg) | |
338 { | |
339 ip_xprt_t *ipx = arg; | |
340 struct sockaddr_storage sa; | |
341 socklen_t salen = sizeof (sa); | |
342 struct pollfd pfd; | |
343 id_t id; | |
344 | |
345 while (!ip_quit && !ipx->ipx_done) { | |
346 if (ipx->ipx_xprt != NULL || (ipx->ipx_flags & FMD_XPRT_ACCEPT)) | |
347 pfd.events = POLLIN; | |
348 else | |
349 pfd.events = POLLOUT; | |
350 | |
351 pfd.fd = ipx->ipx_fd; | |
352 pfd.revents = 0; | |
353 | |
354 if (poll(&pfd, 1, -1) <= 0) | |
355 continue; /* loop around and check ip_quit */ | |
356 | |
357 if (pfd.revents & (POLLHUP | POLLERR)) { | |
358 fmd_hdl_debug(ip_hdl, "hangup fd %d\n", ipx->ipx_fd); | |
359 break; | |
360 } | |
361 | |
362 if (pfd.revents & POLLOUT) { | |
363 /* | |
364 * Once we're connected, there's no reason to have our | |
365 * calls to recv() and send() be non-blocking since we | |
366 * we have separate threads for each: clear O_NONBLOCK. | |
367 */ | |
368 (void) fcntl(ipx->ipx_fd, F_SETFL, | |
369 fcntl(ipx->ipx_fd, F_GETFL, 0) & ~O_NONBLOCK); | |
370 | |
371 if (getpeername(ipx->ipx_fd, (struct sockaddr *)&sa, | |
372 &salen) != 0) { | |
373 fmd_hdl_error(ip_hdl, "failed to get peer name " | |
374 "for fd %d", ipx->ipx_fd); | |
375 bzero(&sa, sizeof (sa)); | |
376 } | |
377 | |
378 ipx->ipx_xprt = fmd_xprt_open(ip_hdl, ipx->ipx_flags, | |
379 ip_xprt_auth((struct sockaddr *)&sa), ipx); | |
380 | |
381 fmd_hdl_debug(ip_hdl, "connect fd %d\n", ipx->ipx_fd); | |
382 continue; | |
383 } | |
384 | |
385 if (pfd.revents & POLLIN) { | |
386 if (ipx->ipx_xprt == NULL) | |
387 ip_xprt_accept(ipx); | |
388 else | |
389 ip_xprt_recv_event(ipx); | |
390 } | |
391 } | |
392 | |
393 id = fmd_timer_install(ip_hdl, ipx, NULL, 0); | |
394 fmd_hdl_debug(ip_hdl, "close fd %d (timer %d)\n", ipx->ipx_fd, (int)id); | |
395 } | |
396 | |
397 static void | |
398 ip_xprt_create(fmd_xprt_t *xp, int fd, int flags) | |
399 { | |
400 ip_xprt_t *ipx = fmd_hdl_zalloc(ip_hdl, sizeof (ip_xprt_t), FMD_SLEEP); | |
401 | |
402 ipx->ipx_xprt = xp; | |
403 ipx->ipx_flags = flags; | |
404 ipx->ipx_fd = fd; | |
405 ipx->ipx_tid = fmd_thr_create(ip_hdl, ip_xprt_thread, ipx); | |
406 | |
407 if (ipx->ipx_xprt != NULL) | |
408 fmd_xprt_setspecific(ip_hdl, ipx->ipx_xprt, ipx); | |
409 | |
410 (void) pthread_mutex_lock(&ip_lock); | |
411 | |
412 ipx->ipx_next = ip_xps; | |
413 ip_xps = ipx; | |
414 | |
415 (void) pthread_mutex_unlock(&ip_lock); | |
416 } | |
417 | |
418 static void | |
419 ip_xprt_destroy(ip_xprt_t *ipx) | |
420 { | |
421 ip_xprt_t *ipp, **ppx = &ip_xps; | |
422 | |
423 (void) pthread_mutex_lock(&ip_lock); | |
424 | |
425 for (ipp = *ppx; ipp != NULL; ipp = ipp->ipx_next) { | |
426 if (ipp != ipx) | |
427 ppx = &ipp->ipx_next; | |
428 else | |
429 break; | |
430 } | |
431 | |
432 if (ipp != ipx) { | |
433 (void) pthread_mutex_unlock(&ip_lock); | |
434 fmd_hdl_abort(ip_hdl, "ipx %p not on xps list\n", (void *)ipx); | |
435 } | |
436 | |
437 *ppx = ipx->ipx_next; | |
438 ipx->ipx_next = NULL; | |
439 | |
440 (void) pthread_mutex_unlock(&ip_lock); | |
441 | |
442 fmd_thr_signal(ip_hdl, ipx->ipx_tid); | |
443 fmd_thr_destroy(ip_hdl, ipx->ipx_tid); | |
444 | |
445 if (ipx->ipx_xprt != NULL) | |
446 fmd_xprt_close(ip_hdl, ipx->ipx_xprt); | |
447 | |
448 fmd_hdl_free(ip_hdl, ipx->ipx_sndbuf.ipb_buf, ipx->ipx_sndbuf.ipb_size); | |
449 fmd_hdl_free(ip_hdl, ipx->ipx_rcvbuf.ipb_buf, ipx->ipx_rcvbuf.ipb_size); | |
450 | |
451 (void) close(ipx->ipx_fd); | |
452 fmd_hdl_free(ip_hdl, ipx, sizeof (ip_xprt_t)); | |
453 } | |
454 | |
455 /* | |
456 * Loop through the addresses that were returned by getaddrinfo() in _fmd_init | |
457 * and for each one attempt to create a socket and initialize it. If we are | |
458 * successful, return zero. If we fail, we check ip_retry: if it is non-zero | |
459 * we return the last errno and let our caller retry ip_xprt_setup() later. If | |
460 * ip_retry reaches zero, we call fmd_hdl_abort() with an appropriate message. | |
461 */ | |
462 static int | |
463 ip_xprt_setup(fmd_hdl_t *hdl) | |
464 { | |
465 int err, fd, oflags, xflags, optval = 1; | |
466 struct addrinfo *aip; | |
467 const char *s1, *s2; | |
468 | |
469 if (ip_host != NULL) | |
470 xflags = FMD_XPRT_RDWR; | |
471 else | |
472 xflags = FMD_XPRT_RDWR | FMD_XPRT_ACCEPT; | |
473 | |
474 for (aip = ip_ail; aip != NULL; aip = aip->ai_next) { | |
475 if (aip->ai_family != AF_INET && aip->ai_family != AF_INET6) | |
476 continue; /* ignore anything that isn't IPv4 or IPv6 */ | |
477 | |
478 if ((fd = socket(aip->ai_family, | |
479 aip->ai_socktype, aip->ai_protocol)) == -1) { | |
480 err = errno; | |
481 continue; | |
482 } | |
483 | |
484 oflags = fcntl(fd, F_GETFL, 0); | |
485 (void) fcntl(fd, F_SETFL, oflags | O_NONBLOCK); | |
486 | |
487 if (xflags & FMD_XPRT_ACCEPT) { | |
488 err = setsockopt(fd, SOL_SOCKET, | |
489 SO_REUSEADDR, &optval, sizeof (optval)) != 0 || | |
490 bind(fd, aip->ai_addr, aip->ai_addrlen) != 0 || | |
491 listen(fd, ip_qlen) != 0; | |
492 } else { | |
493 err = connect(fd, aip->ai_addr, | |
494 aip->ai_addrlen) != 0 && errno != EINPROGRESS; | |
495 } | |
496 | |
497 if (err == 0) { | |
498 ip_xprt_create(NULL, fd, xflags); | |
499 freeaddrinfo(ip_ail); | |
500 ip_ail = NULL; | |
501 return (0); | |
502 } | |
503 | |
504 err = errno; | |
505 (void) close(fd); | |
506 } | |
507 | |
508 if (ip_host != NULL) { | |
509 s1 = "failed to connect to"; | |
510 s2 = ip_host; | |
511 } else { | |
512 s1 = "failed to listen on"; | |
513 s2 = ip_port; | |
514 } | |
515 | |
516 if (err == EACCES || ip_retry-- == 0) | |
517 fmd_hdl_abort(hdl, "%s %s: %s\n", s1, s2, strerror(err)); | |
518 | |
519 fmd_hdl_debug(hdl, "%s %s: %s (will retry)\n", s1, s2, strerror(err)); | |
520 return (err); | |
521 } | |
522 | |
523 /* | |
524 * Timeout handler for the transport module. We use three types of timeouts: | |
525 * | |
526 * (a) arg is NULL: attempt ip_xprt_setup(), re-install timeout to retry | |
527 * (b) arg is non-NULL, FMD_XPRT_SUSPENDED: call fmd_xprt_resume() on arg | |
528 * (c) arg is non-NULL, !FMD_XPRT_SUSPENDED: call ip_xprt_destroy() on arg | |
529 * | |
530 * Case (c) is required as we need to cause the module's main thread, which | |
531 * runs this timeout handler, to join with the transport's auxiliary thread. | |
532 */ | |
533 static void | |
534 ip_timeout(fmd_hdl_t *hdl, id_t id, void *arg) | |
535 { | |
536 ip_xprt_t *ipx = arg; | |
537 | |
538 if (ipx == NULL) { | |
539 if (ip_xprt_setup(hdl) != 0) | |
540 (void) fmd_timer_install(hdl, NULL, NULL, ip_sleep); | |
541 } else if (ipx->ipx_flags & FMD_XPRT_SUSPENDED) { | |
542 fmd_hdl_debug(hdl, "timer %d waking ipx %p\n", (int)id, arg); | |
543 ipx->ipx_flags &= ~FMD_XPRT_SUSPENDED; | |
544 fmd_xprt_resume(hdl, ipx->ipx_xprt); | |
545 } else { | |
546 fmd_hdl_debug(hdl, "timer %d closing ipx %p\n", (int)id, arg); | |
547 ip_xprt_destroy(ipx); | |
548 } | |
549 } | |
550 | |
551 static const fmd_prop_t fmd_props[] = { | |
552 { "ip_authority", FMD_TYPE_STRING, NULL }, | |
553 { "ip_bufsize", FMD_TYPE_SIZE, "4k" }, | |
554 { "ip_burp", FMD_TYPE_TIME, "0" }, | |
555 { "ip_enable", FMD_TYPE_BOOL, "false" }, | |
556 { "ip_mtbf", FMD_TYPE_INT32, "0" }, | |
557 { "ip_port", FMD_TYPE_STRING, "664" }, | |
558 { "ip_qlen", FMD_TYPE_INT32, "32" }, | |
559 { "ip_retry", FMD_TYPE_UINT32, "50" }, | |
560 { "ip_server", FMD_TYPE_STRING, NULL }, | |
561 { "ip_sleep", FMD_TYPE_TIME, "10s" }, | |
562 { "ip_translate", FMD_TYPE_BOOL, "false" }, | |
563 { NULL, 0, NULL } | |
564 }; | |
565 | |
566 static const fmd_hdl_ops_t fmd_ops = { | |
567 NULL, /* fmdo_recv */ | |
568 ip_timeout, /* fmdo_timeout */ | |
569 NULL, /* fmdo_close */ | |
570 NULL, /* fmdo_stats */ | |
571 NULL, /* fmdo_gc */ | |
572 ip_xprt_send, /* fmdo_send */ | |
573 }; | |
574 | |
575 static const fmd_hdl_info_t fmd_info = { | |
576 "IP Transport Agent", "1.0", &fmd_ops, fmd_props | |
577 }; | |
578 | |
579 /* | |
580 * Initialize the ip-transport module as either a server or a client. Note | |
581 * that the ip-transport module is not enabled by default under Solaris: | |
582 * at present we require a developer or tool to setprop ip_enable=true. | |
583 * If ip-transport is needed in the future out-of-the-box on one or more Sun | |
584 * platforms, the code to check 'ip_enable' should be replaced with: | |
585 * | |
586 * (a) configuring ip-transport to operate in client mode by default, | |
587 * (b) a platform-specific configuration mechanism, or | |
588 * (c) a means to assure security and prevent denial-of-service attacks. | |
589 * | |
590 * Note that (c) is only an issue when the transport module operates | |
591 * in server mode (i.e. with the ip_server property set to NULL) on a | |
592 * generic Solaris system which may be exposed directly to the Internet. | |
593 */ | |
594 void | |
595 _fmd_init(fmd_hdl_t *hdl) | |
596 { | |
597 struct addrinfo aih; | |
598 char *auth, *p, *q, *r, *s; | |
599 int err; | |
600 | |
601 if (fmd_hdl_register(hdl, FMD_API_VERSION, &fmd_info) != 0) | |
602 return; /* failed to register handle */ | |
603 | |
604 if (fmd_prop_get_int32(hdl, "ip_enable") == FMD_B_FALSE) { | |
605 fmd_hdl_unregister(hdl); | |
606 return; | |
607 } | |
608 | |
609 (void) fmd_stat_create(hdl, FMD_STAT_NOALLOC, | |
610 sizeof (ip_stat) / sizeof (fmd_stat_t), (fmd_stat_t *)&ip_stat); | |
611 | |
612 ip_hdl = hdl; | |
613 (void) pthread_mutex_init(&ip_lock, NULL); | |
614 | |
615 ip_burp = fmd_prop_get_int64(hdl, "ip_burp"); | |
616 ip_mtbf = fmd_prop_get_int32(hdl, "ip_mtbf"); | |
617 ip_qlen = fmd_prop_get_int32(hdl, "ip_qlen"); | |
618 ip_retry = fmd_prop_get_int32(hdl, "ip_retry"); | |
619 ip_sleep = fmd_prop_get_int64(hdl, "ip_sleep"); | |
620 ip_translate = fmd_prop_get_int32(hdl, "ip_translate"); | |
621 | |
622 ip_size = (size_t)fmd_prop_get_int64(hdl, "ip_bufsize"); | |
623 ip_size = MAX(ip_size, sizeof (ip_hdr_t)); | |
624 | |
625 ip_host = fmd_prop_get_string(hdl, "ip_server"); | |
626 ip_port = fmd_prop_get_string(hdl, "ip_port"); | |
627 | |
628 bzero(&aih, sizeof (aih)); | |
629 aih.ai_flags = AI_ADDRCONFIG; | |
630 aih.ai_family = AF_UNSPEC; | |
631 aih.ai_socktype = SOCK_STREAM; | |
632 | |
633 if (ip_host != NULL) | |
634 fmd_hdl_debug(hdl, "resolving %s:%s\n", ip_host, ip_port); | |
635 else | |
636 aih.ai_flags |= AI_PASSIVE; | |
637 | |
638 err = getaddrinfo(ip_host, ip_port, &aih, &ip_ail); | |
639 | |
640 if (err != 0) { | |
641 fmd_prop_free_string(hdl, ip_host); | |
642 fmd_prop_free_string(hdl, ip_port); | |
643 | |
644 fmd_hdl_abort(hdl, "failed to resolve host %s port %s: %s\n", | |
645 ip_host ? ip_host : "<none>", ip_port, gai_strerror(err)); | |
646 } | |
647 | |
648 /* | |
649 * If ip_authority is set, tokenize this string and turn it into an | |
650 * FMA authority represented as a name-value pair list. We will use | |
651 * this authority for all transports created by this module. If | |
652 * ip_authority isn't set, we'll compute authorities on the fly. | |
653 */ | |
654 if ((auth = fmd_prop_get_string(hdl, "ip_authority")) != NULL) { | |
655 (void) nvlist_alloc(&ip_auth, 0, 0); | |
656 (void) nvlist_add_uint8(ip_auth, | |
657 FM_VERSION, FM_FMRI_AUTH_VERSION); | |
658 | |
659 s = alloca(strlen(auth) + 1); | |
660 (void) strcpy(s, auth); | |
661 fmd_prop_free_string(hdl, auth); | |
662 | |
663 for (p = strtok_r(s, ",", &q); p != NULL; | |
664 p = strtok_r(NULL, ",", &q)) { | |
665 | |
666 if ((r = strchr(p, '=')) == NULL) { | |
667 fmd_prop_free_string(hdl, ip_host); | |
668 fmd_prop_free_string(hdl, ip_port); | |
669 freeaddrinfo(ip_ail); | |
670 | |
671 fmd_hdl_abort(hdl, "ip_authority element <%s> " | |
672 "must be in <name>=<value> form\n", p); | |
673 } | |
674 | |
675 *r = '\0'; | |
676 (void) nvlist_add_string(ip_auth, p, r + 1); | |
677 *r = '='; | |
678 } | |
679 } | |
680 | |
681 /* | |
682 * Call ip_xprt_setup() to connect or bind. If it fails and ip_retry | |
683 * is non-zero, install a timer to try again after 'ip_sleep' nsecs. | |
684 */ | |
685 if (ip_xprt_setup(hdl) != 0) | |
686 (void) fmd_timer_install(hdl, NULL, NULL, ip_sleep); | |
687 } | |
688 | |
689 void | |
690 _fmd_fini(fmd_hdl_t *hdl) | |
691 { | |
692 ip_quit++; /* set quit flag before signalling auxiliary threads */ | |
693 | |
694 while (ip_xps != NULL) | |
695 ip_xprt_destroy(ip_xps); | |
696 | |
697 if (ip_auth != NULL) | |
698 nvlist_free(ip_auth); | |
699 if (ip_ail != NULL) | |
700 freeaddrinfo(ip_ail); | |
701 | |
702 fmd_prop_free_string(hdl, ip_host); | |
703 fmd_prop_free_string(hdl, ip_port); | |
704 | |
705 fmd_hdl_unregister(hdl); | |
706 } |