File:  [ELWIX - Embedded LightWeight unIX -] / embedaddon / coova-chilli / src / net.c
Revision 1.1.1.1 (vendor branch): download - view: text, annotated - select for diffs - revision graph
Tue Feb 21 22:48:25 2012 UTC (13 years, 1 month ago) by misho
Branches: coova-chilli, MAIN
CVS tags: v1_0_12, HEAD
coova-chilli

    1: /*
    2:  * net library functions.
    3:  * Copyright (C) 2003, 2004, 2005, 2006 Mondru AB.
    4:  * Copyright (c) 2006-2008 David Bird <david@coova.com>
    5:  *
    6:  * The contents of this file may be used under the terms of the GNU
    7:  * General Public License Version 2, provided that the above copyright
    8:  * notice and this permission notice is included in all copies or
    9:  * substantial portions of the software.
   10:  *
   11:  */
   12: 
   13: #include "system.h"
   14: #include "syserr.h"
   15: #include "options.h"
   16: #include "net.h"
   17: 
   18: int dev_set_flags(char const *dev, int flags) {
   19:   struct ifreq ifr;
   20:   int fd;
   21:   
   22:   memset(&ifr, 0, sizeof(ifr));
   23:   ifr.ifr_flags = flags;
   24:   strncpy(ifr.ifr_name, dev, IFNAMSIZ);
   25:   ifr.ifr_name[IFNAMSIZ-1] = 0; /* Make sure to terminate */
   26: 
   27:   if ((fd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
   28:     log_err(errno,"socket() failed");
   29:     return -1;
   30:   }
   31: 
   32:   if (ioctl(fd, SIOCSIFFLAGS, &ifr)) {
   33:     log_err(errno,"ioctl(SIOCSIFFLAGS) failed");
   34:     close(fd);
   35:     return -1;
   36:   }
   37: 
   38:   close(fd);
   39: 
   40:   return 0;
   41: }
   42: 
   43: int dev_get_flags(char const *dev, int *flags) {
   44:   struct ifreq ifr;
   45:   int fd;
   46:   
   47:   memset(&ifr, 0, sizeof(ifr));
   48:   strncpy(ifr.ifr_name, dev, IFNAMSIZ);
   49:   ifr.ifr_name[IFNAMSIZ-1] = 0; 
   50: 
   51:   if ((fd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
   52:     log_err(errno, "socket() failed");
   53:     return -1;
   54:   }
   55: 
   56:   if (ioctl(fd, SIOCGIFFLAGS, &ifr)) {
   57:     log_err(errno, "ioctl(SIOCSIFFLAGS) failed");
   58:     close(fd);
   59:     return -1;
   60:   }
   61: 
   62:   close(fd);
   63: 
   64:   *flags = ifr.ifr_flags;
   65: 
   66:   return 0;
   67: }
   68: 
   69: int dev_set_address(char const *devname, struct in_addr *address, 
   70: 		    struct in_addr *dstaddr, struct in_addr *netmask) {
   71:   struct ifreq ifr;
   72:   int fd;
   73: 
   74:   memset (&ifr, 0, sizeof (ifr));
   75:   ifr.ifr_addr.sa_family = AF_INET;
   76:   ifr.ifr_dstaddr.sa_family = AF_INET;
   77: 
   78: #if defined(__linux__)
   79:   ifr.ifr_netmask.sa_family = AF_INET;
   80: 
   81: #elif defined(__FreeBSD__) || defined (__APPLE__) || defined (__OpenBSD__) || defined (__NetBSD__)
   82:   ((struct sockaddr_in *) &ifr.ifr_addr)->sin_len = sizeof (struct sockaddr_in);
   83:   ((struct sockaddr_in *) &ifr.ifr_dstaddr)->sin_len = sizeof (struct sockaddr_in);
   84: #endif
   85: 
   86:   strncpy(ifr.ifr_name, devname, IFNAMSIZ);
   87:   ifr.ifr_name[IFNAMSIZ-1] = 0; /* Make sure to terminate */
   88: 
   89:   /* Create a channel to the NET kernel. */
   90:   if ((fd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
   91:     log_err(errno, "socket() failed");
   92:     return -1;
   93:   }
   94: 
   95:   if (address) { /* Set the interface address */
   96:     ((struct sockaddr_in *) &ifr.ifr_addr)->sin_addr.s_addr = address->s_addr;
   97:     if (ioctl(fd, SIOCSIFADDR, (void *) &ifr) < 0) {
   98:       if (errno != EEXIST) {
   99: 	log_err(errno, "ioctl(SIOCSIFADDR) failed");
  100:       }
  101:       else {
  102: 	log_warn(errno, "ioctl(SIOCSIFADDR): Address already exists");
  103:       }
  104:       close(fd);
  105:       return -1;
  106:     }
  107:   }
  108: 
  109:   if (dstaddr) { /* Set the destination address */
  110:     ((struct sockaddr_in *) &ifr.ifr_dstaddr)->sin_addr.s_addr = dstaddr->s_addr;
  111:     if (ioctl(fd, SIOCSIFDSTADDR, (caddr_t) &ifr) < 0) {
  112:       log_err(errno, "ioctl(SIOCSIFDSTADDR) failed");
  113:       close(fd);
  114:       return -1; 
  115:     }
  116:   }
  117: 
  118:   if (netmask) { /* Set the netmask */
  119: #if defined(__linux__)
  120:     ((struct sockaddr_in *) &ifr.ifr_netmask)->sin_addr.s_addr =  netmask->s_addr;
  121: 
  122: #elif defined(__FreeBSD__) || defined (__APPLE__) || defined (__OpenBSD__) || defined (__NetBSD__)
  123:     ((struct sockaddr_in *) &ifr.ifr_addr)->sin_addr.s_addr =  netmask->s_addr;
  124: 
  125: #elif defined(__sun__)
  126:     ((struct sockaddr_in *) &ifr.ifr_addr)->sin_addr.s_addr =  netmask->s_addr;
  127: #else
  128: #error  "Unknown platform!" 
  129: #endif
  130: 
  131:     if (ioctl(fd, SIOCSIFNETMASK, (void *) &ifr) < 0) {
  132:       log_err(errno, "ioctl(SIOCSIFNETMASK) failed");
  133:       close(fd);
  134:       return -1;
  135:     }
  136:   }
  137:   
  138:   close(fd);
  139:   
  140:   return dev_set_flags(devname, IFF_UP | IFF_RUNNING); 
  141: }
  142: 
  143: int net_init(net_interface *netif, char *ifname, uint16_t protocol, int promisc, uint8_t *mac) {
  144:   memset(netif, 0, sizeof(net_interface));
  145:   strncpy(netif->devname, ifname, IFNAMSIZ);
  146:   netif->devname[IFNAMSIZ] = 0;
  147:   netif->protocol = protocol;
  148: 
  149:   if (promisc) netif->flags |= NET_PROMISC;
  150:   
  151:   if (mac) {
  152:     netif->flags |= NET_USEMAC;
  153:     memcpy(netif->hwaddr, mac, PKT_ETH_ALEN);
  154:   }
  155:   
  156:   return net_open(netif);
  157: }
  158: 
  159: int net_open(net_interface *netif) {
  160:   net_close(netif);
  161:   net_gflags(netif);
  162: 
  163:   if (!(netif->devflags & IFF_UP) || !(netif->devflags & IFF_RUNNING)) {
  164:     struct in_addr noaddr;
  165:     net_sflags(netif, netif->devflags | IFF_NOARP);
  166:     memset(&noaddr, 0, sizeof(noaddr));
  167:     dev_set_address(netif->devname, &noaddr, NULL, NULL);
  168:   }
  169: 
  170:   return net_open_eth(netif);
  171: }
  172: 
  173: int net_reopen(net_interface *netif) {
  174:   net_close(netif);
  175:   return net_open(netif);
  176: }
  177: 
  178: int net_set_address(net_interface *netif, struct in_addr *address, 
  179: 		    struct in_addr *dstaddr, struct in_addr *netmask) {
  180:   netif->address.s_addr = address->s_addr;
  181:   netif->gateway.s_addr = dstaddr->s_addr;
  182:   netif->netmask.s_addr = netmask->s_addr;
  183: 
  184:   return dev_set_address(netif->devname, address, dstaddr, netmask);
  185: }
  186: 
  187: ssize_t net_read(net_interface *netif, void *d, size_t dlen) {
  188:   ssize_t len;
  189:   
  190:   if ((len = read(netif->fd, d, dlen)) < 0) {
  191: #ifdef ENETDOWN
  192:     if (errno == ENETDOWN) {
  193:       net_reopen(netif);
  194:     }
  195: #endif
  196:     log_err(errno, "read(fd=%d, len=%d) == %d", netif->fd, dlen, len);
  197:     return -1;
  198:   }
  199: 
  200:   return len;
  201: }
  202: 
  203: ssize_t net_write(net_interface *netif, void *d, size_t dlen) {
  204:   ssize_t len;
  205:   
  206:   if ((len = write(netif->fd, d, dlen)) < 0) {
  207: #ifdef ENETDOWN
  208:     if (errno == ENETDOWN) {
  209:       net_reopen(netif);
  210:     }
  211: #endif
  212:     log_err(errno, "write(fd=%d, len=%d) failed", netif->fd, dlen);
  213:     return -1;
  214:   }
  215: 
  216:   return len;
  217: }
  218: 
  219: int net_route(struct in_addr *dst, struct in_addr *gateway, struct in_addr *mask, int delete) {
  220: 
  221:   /* TODO: solaris!  */
  222: 
  223: #if defined(__linux__)
  224:   struct rtentry r;
  225:   int fd;
  226: 
  227:   memset (&r, 0, sizeof (r));
  228:   r.rt_flags = RTF_UP | RTF_GATEWAY; /* RTF_HOST not set */
  229: 
  230:   /* Create a channel to the NET kernel. */
  231:   if ((fd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
  232:     log_err(errno, "socket() failed");
  233:     return -1;
  234:   }
  235: 
  236:   r.rt_dst.sa_family     = AF_INET;
  237:   r.rt_gateway.sa_family = AF_INET;
  238:   r.rt_genmask.sa_family = AF_INET;
  239:   ((struct sockaddr_in *) &r.rt_dst)->sin_addr.s_addr = dst->s_addr;
  240:   ((struct sockaddr_in *) &r.rt_gateway)->sin_addr.s_addr = gateway->s_addr;
  241:   ((struct sockaddr_in *) &r.rt_genmask)->sin_addr.s_addr = mask->s_addr;
  242:   
  243:   if (delete) {
  244:     if (ioctl(fd, SIOCDELRT, (void *) &r) < 0) {
  245:       log_err(errno,"ioctl(SIOCDELRT) failed");
  246:       close(fd);
  247:       return -1;
  248:     }
  249:   }
  250:   else {
  251:     if (ioctl(fd, SIOCADDRT, (void *) &r) < 0) {
  252:       log_err(errno, "ioctl(SIOCADDRT) failed");
  253:       close(fd);
  254:       return -1;
  255:     }
  256:   }
  257:   close(fd);
  258:   return 0;
  259:   
  260: #elif defined(__FreeBSD__) || defined (__APPLE__) || defined (__OpenBSD__) || defined (__NetBSD__)
  261: 
  262:   struct {
  263:     struct rt_msghdr rt;
  264:     struct sockaddr_in dst;
  265:     struct sockaddr_in gate;
  266:     struct sockaddr_in mask;
  267:   } req;
  268:   
  269:   int fd;
  270:   struct rt_msghdr *rtm;
  271:   
  272:   if ((fd = socket(AF_ROUTE, SOCK_RAW, 0)) == -1) {
  273:     log_err(errno, "socket() failed");
  274:     return -1;
  275:   }
  276:   
  277:   memset(&req, 0, sizeof(req));
  278:   
  279:   rtm  = &req.rt;
  280:   
  281:   rtm->rtm_msglen = sizeof(req);
  282:   rtm->rtm_version = RTM_VERSION;
  283:   if (delete) {
  284:     rtm->rtm_type = RTM_DELETE;
  285:   }
  286:   else {
  287:     rtm->rtm_type = RTM_ADD;
  288:   }
  289:   rtm->rtm_flags = RTF_UP | RTF_GATEWAY | RTF_STATIC;  /* TODO */
  290:   rtm->rtm_addrs = RTA_DST | RTA_GATEWAY | RTA_NETMASK;
  291:   rtm->rtm_pid = getpid();      
  292:   rtm->rtm_seq = 0044;                                 /* TODO */
  293:   
  294:   req.dst.sin_family       = AF_INET;
  295:   req.dst.sin_len          = sizeof(req.dst);
  296:   req.mask.sin_family      = AF_INET;
  297:   req.mask.sin_len         = sizeof(req.mask);
  298:   req.gate.sin_family      = AF_INET;
  299:   req.gate.sin_len         = sizeof(req.gate);
  300:   
  301:   req.dst.sin_addr.s_addr  = dst->s_addr;
  302:   req.mask.sin_addr.s_addr = mask->s_addr;
  303:   req.gate.sin_addr.s_addr = gateway->s_addr;
  304:   
  305:   if (write(fd, rtm, rtm->rtm_msglen) < 0) {
  306:     log_err(errno, "write() failed");
  307:     close(fd);
  308:     return -1;
  309:   }
  310:   close(fd);
  311:   return 0;
  312:   
  313: #elif defined(__sun__)
  314:   log_err(errno, "Could not set up routing on Solaris. Please add route manually.");
  315:   return 0;
  316: #else
  317: #error  "Unknown platform!"
  318: #endif
  319: }
  320: 
  321: #if defined(__linux__)
  322: 
  323: /**
  324:  * Opens an Ethernet interface. As an option the interface can be set in
  325:  * promisc mode. If not null macaddr and ifindex are filled with the
  326:  * interface mac address and index
  327:  **/
  328: int net_open_eth(net_interface *netif) {
  329:   struct ifreq ifr;
  330:   struct packet_mreq mr;
  331:   struct sockaddr_ll sa;
  332:   int option = 1;
  333: 
  334:   memset(&ifr, 0, sizeof(ifr));
  335: 
  336:   /* Create socket */
  337:   if ((netif->fd = socket(PF_PACKET, SOCK_RAW, htons(netif->protocol))) < 0) {
  338:     if (errno == EPERM) {
  339:       log_err(errno, "Cannot create raw socket. Must be root.");
  340:     }
  341: 
  342:     log_err(errno, "socket(domain=%d, type=%lx, protocol=%d) failed",
  343: 	    PF_PACKET, SOCK_RAW, netif->protocol);
  344: 
  345:     return -1;
  346:   }
  347: 
  348:   /* Enable reception and transmission of broadcast frames */
  349:   if (setsockopt(netif->fd, SOL_SOCKET, SO_BROADCAST, &option, sizeof(option)) < 0) {
  350:     log_err(errno, "setsockopt(s=%d, level=%d, optname=%d, optlen=%d) failed",
  351: 	    netif->fd, SOL_SOCKET, SO_BROADCAST, sizeof(option));
  352:     return -1;
  353:   }
  354:   
  355:   /* Get the MAC address of our interface */
  356:   strncpy(ifr.ifr_name, netif->devname, sizeof(ifr.ifr_name));
  357:   if (ioctl(netif->fd, SIOCGIFHWADDR, &ifr) < 0) {
  358:     log_err(errno, "ioctl(d=%d, request=%d) failed", netif->fd, SIOCGIFHWADDR);
  359:     return -1;
  360:   }
  361: 
  362:   if (ifr.ifr_hwaddr.sa_family == ARPHRD_ETHER) {
  363: 
  364:     netif->flags |= NET_ETHHDR;
  365: 
  366:     if ((netif->flags & NET_USEMAC) == 0)
  367:       memcpy(netif->hwaddr, ifr.ifr_hwaddr.sa_data, PKT_ETH_ALEN);
  368:   }
  369:   
  370:   if (netif->hwaddr[0] & 0x01) {
  371:     log_err(0, "Ethernet has broadcast or multicast address: %.16s", netif->devname);
  372:   }
  373: 
  374:   /* Get the current interface address, network, and any destination address */
  375: 
  376:   /* Verify that MTU = ETH_DATA_LEN */
  377:   strncpy(ifr.ifr_name, netif->devname, sizeof(ifr.ifr_name));
  378:   if (ioctl(netif->fd, SIOCGIFMTU, &ifr) < 0) {
  379:     log_err(errno, "ioctl(d=%d, request=%d) failed", netif->fd, SIOCGIFMTU);
  380:     return -1;
  381:   }
  382:   if (ifr.ifr_mtu != ETH_DATA_LEN) {
  383:     log_err(0, "MTU does not match EHT_DATA_LEN: %d %d", ifr.ifr_mtu, ETH_DATA_LEN);
  384:     return -1;
  385:   }
  386: 
  387:   /* Get ifindex */
  388:   strncpy(ifr.ifr_name, netif->devname, sizeof(ifr.ifr_name));
  389:   if (ioctl(netif->fd, SIOCGIFINDEX, &ifr) < 0) {
  390:     log_err(errno, "ioctl(SIOCFIGINDEX) failed");
  391:   }
  392:   netif->ifindex = ifr.ifr_ifindex;
  393:   
  394:   /* Set interface in promisc mode */
  395:   if (netif->flags & NET_PROMISC) {
  396:     memset(&mr,0,sizeof(mr));
  397:     mr.mr_ifindex = ifr.ifr_ifindex;
  398:     mr.mr_type =  PACKET_MR_PROMISC;
  399:     if (setsockopt(netif->fd, SOL_PACKET, PACKET_ADD_MEMBERSHIP, (char *)&mr, sizeof(mr)) < 0) {
  400:       log_err(errno, "setsockopt(s=%d, level=%d, optname=%d, optlen=%d) failed",
  401: 	      netif->fd, SOL_SOCKET, PACKET_ADD_MEMBERSHIP, sizeof(mr));
  402:       return -1;
  403:     }
  404:   }
  405: 
  406:   /* Bind to particular interface */
  407:   memset(&sa, 0, sizeof(sa));
  408:   sa.sll_family = AF_PACKET;
  409:   sa.sll_protocol = htons(netif->protocol);
  410:   sa.sll_ifindex = ifr.ifr_ifindex;
  411: 
  412:   if (bind(netif->fd, (struct sockaddr *) &sa, sizeof(sa)) < 0) {
  413:     log_err(errno, "bind(sockfd=%d) failed", netif->fd);
  414:     return -1;
  415:   }
  416: 
  417:   return 0;
  418: }
  419: 
  420: #elif defined (__FreeBSD__) || defined (__APPLE__) || defined (__OpenBSD__) || defined (__NetBSD__)
  421: 
  422: int net_getmac(const char *ifname, char *macaddr) {
  423: 
  424:   struct ifaddrs *ifap, *ifa;
  425:   struct sockaddr_dl *sdl;
  426: 
  427:   if (getifaddrs(&ifap)) {
  428:     log_err(errno, "getifaddrs() failed!");
  429:     return -1;
  430:   }
  431: 
  432:   ifa = ifap;
  433:   while (ifa) {
  434:     if ((strcmp(ifa->ifa_name, ifname) == 0) &&
  435: 	(ifa->ifa_addr->sa_family == AF_LINK)) {
  436:       sdl = (struct sockaddr_dl *)ifa->ifa_addr;
  437:       switch(sdl->sdl_type) {
  438:       case IFT_ETHER:
  439: #ifdef IFT_IEEE80211
  440:       case IFT_IEEE80211:
  441: #endif
  442: 	break;
  443:       default:
  444: 	continue;
  445:       }
  446:       if (sdl->sdl_alen != PKT_ETH_ALEN) {
  447: 	log_err(errno, "Wrong sdl_alen!");
  448: 	freeifaddrs(ifap);
  449: 	return -1;
  450:       }
  451:       memcpy(macaddr, LLADDR(sdl), PKT_ETH_ALEN);
  452:       freeifaddrs(ifap);
  453:       return 0;
  454:     }
  455:     ifa = ifa->ifa_next;
  456:   }  
  457:   freeifaddrs(ifap);
  458:   return -1;
  459: }
  460: 
  461: /**
  462:  * Opens an Ethernet interface. As an option the interface can be set in
  463:  * promisc mode. If not null macaddr and ifindex are filled with the
  464:  * interface mac address and index
  465:  **/
  466: 
  467: /* Relevant IOCTLs
  468: FIONREAD Get the number of bytes in input buffer
  469: SIOCGIFADDR Get interface address (IP)
  470: BIOCGBLEN, BIOCSBLEN Get and set required buffer length
  471: BIOCGDLT Type of underlying data interface
  472: BIOCPROMISC Set in promisc mode
  473: BIOCFLUSH Flushes the buffer of incoming packets
  474: BIOCGETIF, BIOCSETIF Set hardware interface. Uses ift_name
  475: BIOCSRTIMEOUT, BIOCGRTIMEOUT Set and get timeout for reads
  476: BIOCGSTATS Return stats for the interface
  477: BIOCIMMEDIATE Return immediately from reads as soon as packet arrives.
  478: BIOCSETF Set filter
  479: BIOCVERSION Return the version of BPF
  480: BIOCSHDRCMPLT BIOCGHDRCMPLT Set flag of wheather to fill in MAC address
  481: BIOCSSEESENT BIOCGSEESENT Return locally generated packets */
  482: 
  483: int net_open_eth(net_interface *netif) {
  484:   char devname[IFNAMSIZ+5]; /* "/dev/" + ifname */
  485:   int devnum;
  486:   struct ifreq ifr;
  487:   struct ifaliasreq areq;
  488:   int local_fd;
  489:   struct bpf_version bv;
  490: 
  491:   u_int32_t ipaddr;
  492:   struct sockaddr_dl hwaddr;
  493:   unsigned int value;
  494: 
  495:   /* Find suitable device */
  496:   for (devnum = 0; devnum < 255; devnum++) { /* TODO 255 */ 
  497:     snprintf(devname, sizeof(devname), "/dev/bpf%d", devnum);
  498:     devname[sizeof(devname)] = 0;
  499:     if ((netif->fd = open(devname, O_RDWR)) >= 0) break;
  500:     if (errno != EBUSY) break;
  501:   } 
  502:   if (netif->fd < 0) {
  503:     log_err(errno, "Can't find bpf device");
  504:     return -1;
  505:   }
  506: 
  507:   /* Set the interface */
  508:   memset(&ifr, 0, sizeof(ifr));
  509:   strncpy(ifr.ifr_name, netif->devname, sizeof(ifr.ifr_name));
  510:   if (ioctl(netif->fd, BIOCSETIF, &ifr) < 0) {
  511:     log_err(errno,"ioctl() failed");
  512:     return -1;
  513:   }
  514: 
  515:   /* Get and validate BPF version */
  516:   if (ioctl(netif->fd, BIOCVERSION, &bv) < 0) {
  517:     log_err(errno,"ioctl() failed!");
  518:     return -1;
  519:   }  
  520:   if (bv.bv_major != BPF_MAJOR_VERSION ||
  521:       bv.bv_minor < BPF_MINOR_VERSION) {
  522:     log_err(errno,"wrong BPF version!");
  523:     return -1;
  524:   }
  525: 
  526:   /* Get the MAC address of our interface */
  527:   if (net_getmac(netif->devname, netif->hwaddr)) {
  528:     log_err(0,"Did not find MAC address!");
  529:   }
  530:   else {
  531:     netif->flags |= NET_ETHHDR;
  532:   }
  533:   
  534:   if (netif->hwaddr[0] & 0x01) {
  535:     log_err(0, "Ethernet has broadcast or multicast address: %.16s", netif->devname);
  536:     return -1;
  537:   }
  538: 
  539:   /* Set interface in promisc mode */
  540:   if (netif->flags & NET_PROMISC) {
  541:     value = 1;
  542:     if (ioctl(netif->fd, BIOCPROMISC, NULL) < 0) {
  543:       log_err(errno,"ioctl() failed!");
  544:       return -1;
  545:     }  
  546:     value = 1;
  547:     if (ioctl(netif->fd, BIOCSHDRCMPLT, &value) < 0) {
  548:       log_err(errno,"ioctl() failed!");
  549:       return -1;
  550:     }  
  551:   }
  552:   else {
  553:     value = 0;
  554:     if (ioctl(netif->fd, BIOCSHDRCMPLT, &value) < 0) {
  555:       log_err(errno,"ioctl() failed!");
  556:       return -1;
  557:     }  
  558:   }
  559: 
  560:   /* Make sure reads return as soon as packet has been received */
  561:   value = 1;
  562:   if (ioctl(netif->fd, BIOCIMMEDIATE, &value) < 0) {
  563:     log_err(errno,"ioctl() failed!");
  564:     return -1;
  565:   }  
  566: 
  567:   return 0;
  568: }
  569: 
  570: #endif
  571: 

FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>