Annotation of embedaddon/quagga/lib/sockunion.c, revision 1.1.1.3
1.1 misho 1: /* Socket union related function.
2: * Copyright (c) 1997, 98 Kunihiro Ishiguro
3: *
4: * This file is part of GNU Zebra.
5: *
6: * GNU Zebra is free software; you can redistribute it and/or modify it
7: * under the terms of the GNU General Public License as published by the
8: * Free Software Foundation; either version 2, or (at your option) any
9: * later version.
10: *
11: * GNU Zebra is distributed in the hope that it will be useful, but
12: * WITHOUT ANY WARRANTY; without even the implied warranty of
13: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14: * General Public License for more details.
15: *
16: * You should have received a copy of the GNU General Public License
17: * along with GNU Zebra; see the file COPYING. If not, write to the Free
18: * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
19: * 02111-1307, USA.
20: */
21:
22: #include <zebra.h>
23:
24: #include "prefix.h"
25: #include "vty.h"
26: #include "sockunion.h"
27: #include "memory.h"
28: #include "str.h"
29: #include "log.h"
30:
31: #ifndef HAVE_INET_ATON
32: int
33: inet_aton (const char *cp, struct in_addr *inaddr)
34: {
35: int dots = 0;
36: register u_long addr = 0;
37: register u_long val = 0, base = 10;
38:
39: do
40: {
41: register char c = *cp;
42:
43: switch (c)
44: {
45: case '0': case '1': case '2': case '3': case '4': case '5':
46: case '6': case '7': case '8': case '9':
47: val = (val * base) + (c - '0');
48: break;
49: case '.':
50: if (++dots > 3)
51: return 0;
52: case '\0':
53: if (val > 255)
54: return 0;
55: addr = addr << 8 | val;
56: val = 0;
57: break;
58: default:
59: return 0;
60: }
61: } while (*cp++) ;
62:
63: if (dots < 3)
64: addr <<= 8 * (3 - dots);
65: if (inaddr)
66: inaddr->s_addr = htonl (addr);
67: return 1;
68: }
69: #endif /* ! HAVE_INET_ATON */
70:
71:
72: #ifndef HAVE_INET_PTON
73: int
74: inet_pton (int family, const char *strptr, void *addrptr)
75: {
76: if (family == AF_INET)
77: {
78: struct in_addr in_val;
79:
80: if (inet_aton (strptr, &in_val))
81: {
82: memcpy (addrptr, &in_val, sizeof (struct in_addr));
83: return 1;
84: }
85: return 0;
86: }
87: errno = EAFNOSUPPORT;
88: return -1;
89: }
90: #endif /* ! HAVE_INET_PTON */
91:
92: #ifndef HAVE_INET_NTOP
93: const char *
94: inet_ntop (int family, const void *addrptr, char *strptr, size_t len)
95: {
96: unsigned char *p = (unsigned char *) addrptr;
97:
98: if (family == AF_INET)
99: {
100: char temp[INET_ADDRSTRLEN];
101:
102: snprintf(temp, sizeof(temp), "%d.%d.%d.%d", p[0], p[1], p[2], p[3]);
103:
104: if (strlen(temp) >= len)
105: {
106: errno = ENOSPC;
107: return NULL;
108: }
109: strcpy(strptr, temp);
110: return strptr;
111: }
112:
113: errno = EAFNOSUPPORT;
114: return NULL;
115: }
116: #endif /* ! HAVE_INET_NTOP */
117:
118: const char *
119: inet_sutop (union sockunion *su, char *str)
120: {
121: switch (su->sa.sa_family)
122: {
123: case AF_INET:
124: inet_ntop (AF_INET, &su->sin.sin_addr, str, INET_ADDRSTRLEN);
125: break;
126: #ifdef HAVE_IPV6
127: case AF_INET6:
128: inet_ntop (AF_INET6, &su->sin6.sin6_addr, str, INET6_ADDRSTRLEN);
129: break;
130: #endif /* HAVE_IPV6 */
131: }
132: return str;
133: }
134:
135: int
136: str2sockunion (const char *str, union sockunion *su)
137: {
138: int ret;
139:
140: memset (su, 0, sizeof (union sockunion));
141:
142: ret = inet_pton (AF_INET, str, &su->sin.sin_addr);
143: if (ret > 0) /* Valid IPv4 address format. */
144: {
145: su->sin.sin_family = AF_INET;
146: #ifdef HAVE_STRUCT_SOCKADDR_IN_SIN_LEN
147: su->sin.sin_len = sizeof(struct sockaddr_in);
148: #endif /* HAVE_STRUCT_SOCKADDR_IN_SIN_LEN */
149: return 0;
150: }
151: #ifdef HAVE_IPV6
152: ret = inet_pton (AF_INET6, str, &su->sin6.sin6_addr);
153: if (ret > 0) /* Valid IPv6 address format. */
154: {
155: su->sin6.sin6_family = AF_INET6;
156: #ifdef SIN6_LEN
157: su->sin6.sin6_len = sizeof(struct sockaddr_in6);
158: #endif /* SIN6_LEN */
159: return 0;
160: }
161: #endif /* HAVE_IPV6 */
162: return -1;
163: }
164:
165: const char *
166: sockunion2str (union sockunion *su, char *buf, size_t len)
167: {
168: if (su->sa.sa_family == AF_INET)
169: return inet_ntop (AF_INET, &su->sin.sin_addr, buf, len);
170: #ifdef HAVE_IPV6
171: else if (su->sa.sa_family == AF_INET6)
172: return inet_ntop (AF_INET6, &su->sin6.sin6_addr, buf, len);
173: #endif /* HAVE_IPV6 */
174: return NULL;
175: }
176:
177: union sockunion *
178: sockunion_str2su (const char *str)
179: {
1.1.1.3 ! misho 180: union sockunion *su = XCALLOC (MTYPE_SOCKUNION, sizeof (union sockunion));
! 181:
! 182: if (!str2sockunion (str, su))
! 183: return su;
! 184:
1.1 misho 185: XFREE (MTYPE_SOCKUNION, su);
186: return NULL;
187: }
188:
189: /* Convert IPv4 compatible IPv6 address to IPv4 address. */
190: static void
191: sockunion_normalise_mapped (union sockunion *su)
192: {
193: struct sockaddr_in sin;
194:
195: #ifdef HAVE_IPV6
196: if (su->sa.sa_family == AF_INET6
197: && IN6_IS_ADDR_V4MAPPED (&su->sin6.sin6_addr))
198: {
199: memset (&sin, 0, sizeof (struct sockaddr_in));
200: sin.sin_family = AF_INET;
201: sin.sin_port = su->sin6.sin6_port;
202: memcpy (&sin.sin_addr, ((char *)&su->sin6.sin6_addr) + 12, 4);
203: memcpy (su, &sin, sizeof (struct sockaddr_in));
204: }
205: #endif /* HAVE_IPV6 */
206: }
207:
208: /* Return socket of sockunion. */
209: int
210: sockunion_socket (union sockunion *su)
211: {
212: int sock;
213:
214: sock = socket (su->sa.sa_family, SOCK_STREAM, 0);
215: if (sock < 0)
216: {
217: zlog (NULL, LOG_WARNING, "Can't make socket : %s", safe_strerror (errno));
218: return -1;
219: }
220:
221: return sock;
222: }
223:
224: /* Return accepted new socket file descriptor. */
225: int
226: sockunion_accept (int sock, union sockunion *su)
227: {
228: socklen_t len;
229: int client_sock;
230:
231: len = sizeof (union sockunion);
232: client_sock = accept (sock, (struct sockaddr *) su, &len);
233:
234: sockunion_normalise_mapped (su);
235: return client_sock;
236: }
237:
238: /* Return sizeof union sockunion. */
239: static int
240: sockunion_sizeof (union sockunion *su)
241: {
242: int ret;
243:
244: ret = 0;
245: switch (su->sa.sa_family)
246: {
247: case AF_INET:
248: ret = sizeof (struct sockaddr_in);
249: break;
250: #ifdef HAVE_IPV6
251: case AF_INET6:
252: ret = sizeof (struct sockaddr_in6);
253: break;
254: #endif /* AF_INET6 */
255: }
256: return ret;
257: }
258:
259: /* return sockunion structure : this function should be revised. */
1.1.1.2 misho 260: static const char *
261: sockunion_log (union sockunion *su, char *buf, size_t len)
1.1 misho 262: {
263: switch (su->sa.sa_family)
264: {
265: case AF_INET:
1.1.1.2 misho 266: return inet_ntop(AF_INET, &su->sin.sin_addr, buf, len);
267:
1.1 misho 268: #ifdef HAVE_IPV6
269: case AF_INET6:
1.1.1.2 misho 270: return inet_ntop(AF_INET6, &(su->sin6.sin6_addr), buf, len);
1.1 misho 271: break;
272: #endif /* HAVE_IPV6 */
1.1.1.2 misho 273:
1.1 misho 274: default:
1.1.1.2 misho 275: snprintf (buf, len, "af_unknown %d ", su->sa.sa_family);
276: return buf;
1.1 misho 277: }
278: }
279:
280: /* sockunion_connect returns
281: -1 : error occured
282: 0 : connect success
283: 1 : connect is in progress */
284: enum connect_result
285: sockunion_connect (int fd, union sockunion *peersu, unsigned short port,
286: unsigned int ifindex)
287: {
288: int ret;
289: int val;
290: union sockunion su;
291:
292: memcpy (&su, peersu, sizeof (union sockunion));
293:
294: switch (su.sa.sa_family)
295: {
296: case AF_INET:
297: su.sin.sin_port = port;
298: break;
299: #ifdef HAVE_IPV6
300: case AF_INET6:
301: su.sin6.sin6_port = port;
302: #ifdef KAME
303: if (IN6_IS_ADDR_LINKLOCAL(&su.sin6.sin6_addr) && ifindex)
304: {
305: #ifdef HAVE_STRUCT_SOCKADDR_IN6_SIN6_SCOPE_ID
306: /* su.sin6.sin6_scope_id = ifindex; */
307: #ifdef MUSICA
308: su.sin6.sin6_scope_id = ifindex;
309: #endif
310: #endif /* HAVE_STRUCT_SOCKADDR_IN6_SIN6_SCOPE_ID */
311: #ifndef MUSICA
312: SET_IN6_LINKLOCAL_IFINDEX (su.sin6.sin6_addr, ifindex);
313: #endif
314: }
315: #endif /* KAME */
316: break;
317: #endif /* HAVE_IPV6 */
318: }
319:
320: /* Make socket non-block. */
321: val = fcntl (fd, F_GETFL, 0);
322: fcntl (fd, F_SETFL, val|O_NONBLOCK);
323:
324: /* Call connect function. */
325: ret = connect (fd, (struct sockaddr *) &su, sockunion_sizeof (&su));
326:
327: /* Immediate success */
328: if (ret == 0)
329: {
330: fcntl (fd, F_SETFL, val);
331: return connect_success;
332: }
333:
334: /* If connect is in progress then return 1 else it's real error. */
335: if (ret < 0)
336: {
337: if (errno != EINPROGRESS)
338: {
1.1.1.2 misho 339: char str[SU_ADDRSTRLEN];
1.1 misho 340: zlog_info ("can't connect to %s fd %d : %s",
1.1.1.2 misho 341: sockunion_log (&su, str, sizeof str),
342: fd, safe_strerror (errno));
1.1 misho 343: return connect_error;
344: }
345: }
346:
347: fcntl (fd, F_SETFL, val);
348:
349: return connect_in_progress;
350: }
351:
352: /* Make socket from sockunion union. */
353: int
354: sockunion_stream_socket (union sockunion *su)
355: {
356: int sock;
357:
358: if (su->sa.sa_family == 0)
359: su->sa.sa_family = AF_INET_UNION;
360:
361: sock = socket (su->sa.sa_family, SOCK_STREAM, 0);
362:
363: if (sock < 0)
364: zlog (NULL, LOG_WARNING, "can't make socket sockunion_stream_socket");
365:
366: return sock;
367: }
368:
369: /* Bind socket to specified address. */
370: int
371: sockunion_bind (int sock, union sockunion *su, unsigned short port,
372: union sockunion *su_addr)
373: {
374: int size = 0;
375: int ret;
376:
377: if (su->sa.sa_family == AF_INET)
378: {
379: size = sizeof (struct sockaddr_in);
380: su->sin.sin_port = htons (port);
381: #ifdef HAVE_STRUCT_SOCKADDR_IN_SIN_LEN
382: su->sin.sin_len = size;
383: #endif /* HAVE_STRUCT_SOCKADDR_IN_SIN_LEN */
384: if (su_addr == NULL)
1.1.1.3 ! misho 385: sockunion2ip (su) = htonl (INADDR_ANY);
1.1 misho 386: }
387: #ifdef HAVE_IPV6
388: else if (su->sa.sa_family == AF_INET6)
389: {
390: size = sizeof (struct sockaddr_in6);
391: su->sin6.sin6_port = htons (port);
392: #ifdef SIN6_LEN
393: su->sin6.sin6_len = size;
394: #endif /* SIN6_LEN */
395: if (su_addr == NULL)
396: {
397: #if defined(LINUX_IPV6) || defined(NRL)
398: memset (&su->sin6.sin6_addr, 0, sizeof (struct in6_addr));
399: #else
400: su->sin6.sin6_addr = in6addr_any;
401: #endif /* LINUX_IPV6 */
402: }
403: }
404: #endif /* HAVE_IPV6 */
405:
406:
407: ret = bind (sock, (struct sockaddr *)su, size);
408: if (ret < 0)
409: zlog (NULL, LOG_WARNING, "can't bind socket : %s", safe_strerror (errno));
410:
411: return ret;
412: }
413:
414: int
415: sockopt_reuseaddr (int sock)
416: {
417: int ret;
418: int on = 1;
419:
420: ret = setsockopt (sock, SOL_SOCKET, SO_REUSEADDR,
421: (void *) &on, sizeof (on));
422: if (ret < 0)
423: {
424: zlog (NULL, LOG_WARNING, "can't set sockopt SO_REUSEADDR to socket %d", sock);
425: return -1;
426: }
427: return 0;
428: }
429:
430: #ifdef SO_REUSEPORT
431: int
432: sockopt_reuseport (int sock)
433: {
434: int ret;
435: int on = 1;
436:
437: ret = setsockopt (sock, SOL_SOCKET, SO_REUSEPORT,
438: (void *) &on, sizeof (on));
439: if (ret < 0)
440: {
441: zlog (NULL, LOG_WARNING, "can't set sockopt SO_REUSEPORT to socket %d", sock);
442: return -1;
443: }
444: return 0;
445: }
446: #else
447: int
448: sockopt_reuseport (int sock)
449: {
450: return 0;
451: }
452: #endif /* 0 */
453:
454: int
455: sockopt_ttl (int family, int sock, int ttl)
456: {
457: int ret;
458:
459: #ifdef IP_TTL
460: if (family == AF_INET)
461: {
462: ret = setsockopt (sock, IPPROTO_IP, IP_TTL,
463: (void *) &ttl, sizeof (int));
464: if (ret < 0)
465: {
466: zlog (NULL, LOG_WARNING, "can't set sockopt IP_TTL %d to socket %d", ttl, sock);
467: return -1;
468: }
469: return 0;
470: }
471: #endif /* IP_TTL */
472: #ifdef HAVE_IPV6
473: if (family == AF_INET6)
474: {
475: ret = setsockopt (sock, IPPROTO_IPV6, IPV6_UNICAST_HOPS,
476: (void *) &ttl, sizeof (int));
477: if (ret < 0)
478: {
479: zlog (NULL, LOG_WARNING, "can't set sockopt IPV6_UNICAST_HOPS %d to socket %d",
480: ttl, sock);
481: return -1;
482: }
483: return 0;
484: }
485: #endif /* HAVE_IPV6 */
486: return 0;
487: }
488:
489: int
490: sockopt_cork (int sock, int onoff)
491: {
492: #ifdef TCP_CORK
493: return setsockopt (sock, IPPROTO_TCP, TCP_CORK, &onoff, sizeof(onoff));
494: #else
495: return 0;
496: #endif
497: }
498:
499: int
500: sockopt_minttl (int family, int sock, int minttl)
501: {
502: #ifdef IP_MINTTL
503: if (family == AF_INET)
504: {
505: int ret = setsockopt (sock, IPPROTO_IP, IP_MINTTL, &minttl, sizeof(minttl));
506: if (ret < 0)
507: zlog (NULL, LOG_WARNING,
508: "can't set sockopt IP_MINTTL to %d on socket %d: %s",
509: minttl, sock, safe_strerror (errno));
510: return ret;
511: }
512: #endif /* IP_MINTTL */
513: #ifdef IPV6_MINHOPCNT
514: if (family == AF_INET6)
515: {
516: int ret = setsockopt (sock, IPPROTO_IPV6, IPV6_MINHOPCNT, &minttl, sizeof(minttl));
517: if (ret < 0)
518: zlog (NULL, LOG_WARNING,
519: "can't set sockopt IPV6_MINHOPCNT to %d on socket %d: %s",
520: minttl, sock, safe_strerror (errno));
521: return ret;
522: }
523: #endif
524:
525: errno = EOPNOTSUPP;
526: return -1;
527: }
528:
1.1.1.2 misho 529: int
530: sockopt_v6only (int family, int sock)
531: {
532: int ret, on = 1;
533:
534: #ifdef HAVE_IPV6
535: #ifdef IPV6_V6ONLY
536: if (family == AF_INET6)
537: {
538: ret = setsockopt (sock, IPPROTO_IPV6, IPV6_V6ONLY,
539: (void *) &on, sizeof (int));
540: if (ret < 0)
541: {
542: zlog (NULL, LOG_WARNING, "can't set sockopt IPV6_V6ONLY "
543: "to socket %d", sock);
544: return -1;
545: }
546: return 0;
547: }
548: #endif /* IPV6_V6ONLY */
549: #endif /* HAVE_IPV6 */
550: return 0;
551: }
552:
1.1 misho 553: /* If same family and same prefix return 1. */
554: int
555: sockunion_same (union sockunion *su1, union sockunion *su2)
556: {
557: int ret = 0;
558:
559: if (su1->sa.sa_family != su2->sa.sa_family)
560: return 0;
561:
562: switch (su1->sa.sa_family)
563: {
564: case AF_INET:
565: ret = memcmp (&su1->sin.sin_addr, &su2->sin.sin_addr,
566: sizeof (struct in_addr));
567: break;
568: #ifdef HAVE_IPV6
569: case AF_INET6:
570: ret = memcmp (&su1->sin6.sin6_addr, &su2->sin6.sin6_addr,
571: sizeof (struct in6_addr));
572: break;
573: #endif /* HAVE_IPV6 */
574: }
575: if (ret == 0)
576: return 1;
577: else
578: return 0;
579: }
580:
581: /* After TCP connection is established. Get local address and port. */
582: union sockunion *
583: sockunion_getsockname (int fd)
584: {
585: int ret;
586: socklen_t len;
587: union
588: {
589: struct sockaddr sa;
590: struct sockaddr_in sin;
591: #ifdef HAVE_IPV6
592: struct sockaddr_in6 sin6;
593: #endif /* HAVE_IPV6 */
594: char tmp_buffer[128];
595: } name;
596: union sockunion *su;
597:
598: memset (&name, 0, sizeof name);
599: len = sizeof name;
600:
601: ret = getsockname (fd, (struct sockaddr *)&name, &len);
602: if (ret < 0)
603: {
604: zlog_warn ("Can't get local address and port by getsockname: %s",
605: safe_strerror (errno));
606: return NULL;
607: }
608:
609: if (name.sa.sa_family == AF_INET)
610: {
611: su = XCALLOC (MTYPE_SOCKUNION, sizeof (union sockunion));
612: memcpy (su, &name, sizeof (struct sockaddr_in));
613: return su;
614: }
615: #ifdef HAVE_IPV6
616: if (name.sa.sa_family == AF_INET6)
617: {
618: su = XCALLOC (MTYPE_SOCKUNION, sizeof (union sockunion));
619: memcpy (su, &name, sizeof (struct sockaddr_in6));
620: sockunion_normalise_mapped (su);
621: return su;
622: }
623: #endif /* HAVE_IPV6 */
624: return NULL;
625: }
626:
627: /* After TCP connection is established. Get remote address and port. */
628: union sockunion *
629: sockunion_getpeername (int fd)
630: {
631: int ret;
632: socklen_t len;
633: union
634: {
635: struct sockaddr sa;
636: struct sockaddr_in sin;
637: #ifdef HAVE_IPV6
638: struct sockaddr_in6 sin6;
639: #endif /* HAVE_IPV6 */
640: char tmp_buffer[128];
641: } name;
642: union sockunion *su;
643:
644: memset (&name, 0, sizeof name);
645: len = sizeof name;
646: ret = getpeername (fd, (struct sockaddr *)&name, &len);
647: if (ret < 0)
648: {
649: zlog (NULL, LOG_WARNING, "Can't get remote address and port: %s",
650: safe_strerror (errno));
651: return NULL;
652: }
653:
654: if (name.sa.sa_family == AF_INET)
655: {
656: su = XCALLOC (MTYPE_SOCKUNION, sizeof (union sockunion));
657: memcpy (su, &name, sizeof (struct sockaddr_in));
658: return su;
659: }
660: #ifdef HAVE_IPV6
661: if (name.sa.sa_family == AF_INET6)
662: {
663: su = XCALLOC (MTYPE_SOCKUNION, sizeof (union sockunion));
664: memcpy (su, &name, sizeof (struct sockaddr_in6));
665: sockunion_normalise_mapped (su);
666: return su;
667: }
668: #endif /* HAVE_IPV6 */
669: return NULL;
670: }
671:
672: /* Print sockunion structure */
673: static void __attribute__ ((unused))
674: sockunion_print (union sockunion *su)
675: {
676: if (su == NULL)
677: return;
678:
679: switch (su->sa.sa_family)
680: {
681: case AF_INET:
682: printf ("%s\n", inet_ntoa (su->sin.sin_addr));
683: break;
684: #ifdef HAVE_IPV6
685: case AF_INET6:
686: {
687: char buf [SU_ADDRSTRLEN];
688:
689: printf ("%s\n", inet_ntop (AF_INET6, &(su->sin6.sin6_addr),
690: buf, sizeof (buf)));
691: }
692: break;
693: #endif /* HAVE_IPV6 */
694:
695: #ifdef AF_LINK
696: case AF_LINK:
697: {
698: struct sockaddr_dl *sdl;
699:
700: sdl = (struct sockaddr_dl *)&(su->sa);
701: printf ("link#%d\n", sdl->sdl_index);
702: }
703: break;
704: #endif /* AF_LINK */
705: default:
706: printf ("af_unknown %d\n", su->sa.sa_family);
707: break;
708: }
709: }
710:
711: #ifdef HAVE_IPV6
712: static int
713: in6addr_cmp (struct in6_addr *addr1, struct in6_addr *addr2)
714: {
715: unsigned int i;
716: u_char *p1, *p2;
717:
718: p1 = (u_char *)addr1;
719: p2 = (u_char *)addr2;
720:
721: for (i = 0; i < sizeof (struct in6_addr); i++)
722: {
723: if (p1[i] > p2[i])
724: return 1;
725: else if (p1[i] < p2[i])
726: return -1;
727: }
728: return 0;
729: }
730: #endif /* HAVE_IPV6 */
731:
732: int
733: sockunion_cmp (union sockunion *su1, union sockunion *su2)
734: {
735: if (su1->sa.sa_family > su2->sa.sa_family)
736: return 1;
737: if (su1->sa.sa_family < su2->sa.sa_family)
738: return -1;
739:
740: if (su1->sa.sa_family == AF_INET)
741: {
1.1.1.3 ! misho 742: if (ntohl (sockunion2ip (su1)) == ntohl (sockunion2ip (su2)))
1.1 misho 743: return 0;
1.1.1.3 ! misho 744: if (ntohl (sockunion2ip (su1)) > ntohl (sockunion2ip (su2)))
1.1 misho 745: return 1;
746: else
747: return -1;
748: }
749: #ifdef HAVE_IPV6
750: if (su1->sa.sa_family == AF_INET6)
751: return in6addr_cmp (&su1->sin6.sin6_addr, &su2->sin6.sin6_addr);
752: #endif /* HAVE_IPV6 */
753: return 0;
754: }
755:
756: /* Duplicate sockunion. */
757: union sockunion *
758: sockunion_dup (union sockunion *su)
759: {
760: union sockunion *dup = XCALLOC (MTYPE_SOCKUNION, sizeof (union sockunion));
761: memcpy (dup, su, sizeof (union sockunion));
762: return dup;
763: }
764:
765: void
766: sockunion_free (union sockunion *su)
767: {
768: XFREE (MTYPE_SOCKUNION, su);
769: }
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>