Annotation of embedaddon/quagga/zebra/rt_socket.c, revision 1.1.1.1

1.1       misho       1: /*
                      2:  * Kernel routing table updates by routing socket.
                      3:  * Copyright (C) 1997, 98 Kunihiro Ishiguro
                      4:  *
                      5:  * This file is part of GNU Zebra.
                      6:  *
                      7:  * GNU Zebra is free software; you can redistribute it and/or modify it
                      8:  * under the terms of the GNU General Public License as published by the
                      9:  * Free Software Foundation; either version 2, or (at your option) any
                     10:  * later version.
                     11:  *
                     12:  * GNU Zebra is distributed in the hope that it will be useful, but
                     13:  * WITHOUT ANY WARRANTY; without even the implied warranty of
                     14:  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
                     15:  * General Public License for more details.
                     16:  *
                     17:  * You should have received a copy of the GNU General Public License
                     18:  * along with GNU Zebra; see the file COPYING.  If not, write to the Free
                     19:  * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
                     20:  * 02111-1307, USA.  
                     21:  */
                     22: 
                     23: #include <zebra.h>
                     24: 
                     25: #include "if.h"
                     26: #include "prefix.h"
                     27: #include "sockunion.h"
                     28: #include "log.h"
                     29: #include "str.h"
                     30: #include "privs.h"
                     31: 
                     32: #include "zebra/debug.h"
                     33: #include "zebra/rib.h"
                     34: #include "zebra/rt.h"
                     35: #include "zebra/kernel_socket.h"
                     36: 
                     37: extern struct zebra_privs_t zserv_privs;
                     38: 
                     39: /* kernel socket export */
                     40: extern int rtm_write (int message, union sockunion *dest,
                     41:                       union sockunion *mask, union sockunion *gate,
                     42:                       unsigned int index, int zebra_flags, int metric);
                     43: 
                     44: /* Adjust netmask socket length. Return value is a adjusted sin_len
                     45:    value. */
                     46: static int
                     47: sin_masklen (struct in_addr mask)
                     48: {
                     49:   char *p, *lim;
                     50:   int len;
                     51:   struct sockaddr_in sin;
                     52: 
                     53:   if (mask.s_addr == 0) 
                     54:     return sizeof (long);
                     55: 
                     56:   sin.sin_addr = mask;
                     57:   len = sizeof (struct sockaddr_in);
                     58: 
                     59:   lim = (char *) &sin.sin_addr;
                     60:   p = lim + sizeof (sin.sin_addr);
                     61: 
                     62:   while (*--p == 0 && p >= lim) 
                     63:     len--;
                     64:   return len;
                     65: }
                     66: 
                     67: /* Interface between zebra message and rtm message. */
                     68: static int
                     69: kernel_rtm_ipv4 (int cmd, struct prefix *p, struct rib *rib, int family)
                     70: 
                     71: {
                     72:   struct sockaddr_in *mask = NULL;
                     73:   struct sockaddr_in sin_dest, sin_mask, sin_gate;
                     74:   struct nexthop *nexthop;
                     75:   int nexthop_num = 0;
                     76:   unsigned int ifindex = 0;
                     77:   int gate = 0;
                     78:   int error;
                     79:   char prefix_buf[INET_ADDRSTRLEN];
                     80: 
                     81:   if (IS_ZEBRA_DEBUG_RIB)
                     82:     inet_ntop (AF_INET, &p->u.prefix, prefix_buf, INET_ADDRSTRLEN);
                     83:   memset (&sin_dest, 0, sizeof (struct sockaddr_in));
                     84:   sin_dest.sin_family = AF_INET;
                     85: #ifdef HAVE_STRUCT_SOCKADDR_IN_SIN_LEN
                     86:   sin_dest.sin_len = sizeof (struct sockaddr_in);
                     87: #endif /* HAVE_STRUCT_SOCKADDR_IN_SIN_LEN */
                     88:   sin_dest.sin_addr = p->u.prefix4;
                     89: 
                     90:   memset (&sin_mask, 0, sizeof (struct sockaddr_in));
                     91: 
                     92:   memset (&sin_gate, 0, sizeof (struct sockaddr_in));
                     93:   sin_gate.sin_family = AF_INET;
                     94: #ifdef HAVE_STRUCT_SOCKADDR_IN_SIN_LEN
                     95:   sin_gate.sin_len = sizeof (struct sockaddr_in);
                     96: #endif /* HAVE_STRUCT_SOCKADDR_IN_SIN_LEN */
                     97: 
                     98:   /* Make gateway. */
                     99:   for (nexthop = rib->nexthop; nexthop; nexthop = nexthop->next)
                    100:     {
                    101:       gate = 0;
                    102:       char gate_buf[INET_ADDRSTRLEN] = "NULL";
                    103: 
                    104:       /*
                    105:        * XXX We need to refrain from kernel operations in some cases,
                    106:        * but this if statement seems overly cautious - what about
                    107:        * other than ADD and DELETE?
                    108:        */
                    109:       if ((cmd == RTM_ADD
                    110:           && CHECK_FLAG (nexthop->flags, NEXTHOP_FLAG_ACTIVE))
                    111:          || (cmd == RTM_DELETE
                    112:              && CHECK_FLAG (nexthop->flags, NEXTHOP_FLAG_FIB)
                    113:              ))
                    114:        {
                    115:          if (CHECK_FLAG (nexthop->flags, NEXTHOP_FLAG_RECURSIVE))
                    116:            {
                    117:              if (nexthop->rtype == NEXTHOP_TYPE_IPV4 ||
                    118:                  nexthop->rtype == NEXTHOP_TYPE_IPV4_IFINDEX)
                    119:                {
                    120:                  sin_gate.sin_addr = nexthop->rgate.ipv4;
                    121:                  gate = 1;
                    122:                }
                    123:              if (nexthop->rtype == NEXTHOP_TYPE_IFINDEX
                    124:                  || nexthop->rtype == NEXTHOP_TYPE_IFNAME
                    125:                  || nexthop->rtype == NEXTHOP_TYPE_IPV4_IFINDEX)
                    126:                ifindex = nexthop->rifindex;
                    127:            }
                    128:          else
                    129:            {
                    130:              if (nexthop->type == NEXTHOP_TYPE_IPV4 ||
                    131:                  nexthop->type == NEXTHOP_TYPE_IPV4_IFINDEX)
                    132:                {
                    133:                  sin_gate.sin_addr = nexthop->gate.ipv4;
                    134:                  gate = 1;
                    135:                }
                    136:              if (nexthop->type == NEXTHOP_TYPE_IFINDEX
                    137:                  || nexthop->type == NEXTHOP_TYPE_IFNAME
                    138:                  || nexthop->type == NEXTHOP_TYPE_IPV4_IFINDEX)
                    139:                ifindex = nexthop->ifindex;
                    140:              if (nexthop->type == NEXTHOP_TYPE_BLACKHOLE)
                    141:                {
                    142:                  struct in_addr loopback;
                    143:                  loopback.s_addr = htonl (INADDR_LOOPBACK);
                    144:                  sin_gate.sin_addr = loopback;
                    145:                  gate = 1;
                    146:                }
                    147:            }
                    148: 
                    149:          if (gate && p->prefixlen == 32)
                    150:            mask = NULL;
                    151:          else
                    152:            {
                    153:              masklen2ip (p->prefixlen, &sin_mask.sin_addr);
                    154:              sin_mask.sin_family = AF_INET;
                    155: #ifdef HAVE_STRUCT_SOCKADDR_IN_SIN_LEN
                    156:              sin_mask.sin_len = sin_masklen (sin_mask.sin_addr);
                    157: #endif /* HAVE_STRUCT_SOCKADDR_IN_SIN_LEN */
                    158:              mask = &sin_mask;
                    159:            }
                    160: 
                    161:          error = rtm_write (cmd,
                    162:                             (union sockunion *)&sin_dest, 
                    163:                             (union sockunion *)mask, 
                    164:                             gate ? (union sockunion *)&sin_gate : NULL,
                    165:                             ifindex,
                    166:                             rib->flags,
                    167:                             rib->metric);
                    168: 
                    169:            if (IS_ZEBRA_DEBUG_RIB)
                    170:            {
                    171:              if (!gate)
                    172:              {
                    173:                zlog_debug ("%s: %s/%d: attention! gate not found for rib %p",
                    174:                  __func__, prefix_buf, p->prefixlen, rib);
                    175:                rib_dump (__func__, (struct prefix_ipv4 *)p, rib);
                    176:              }
                    177:              else
                    178:                inet_ntop (AF_INET, &sin_gate.sin_addr, gate_buf, INET_ADDRSTRLEN);
                    179:            }
                    180:  
                    181:            switch (error)
                    182:            {
                    183:              /* We only flag nexthops as being in FIB if rtm_write() did its work. */
                    184:              case ZEBRA_ERR_NOERROR:
                    185:                nexthop_num++;
                    186:                if (IS_ZEBRA_DEBUG_RIB)
                    187:                  zlog_debug ("%s: %s/%d: successfully did NH %s",
                    188:                    __func__, prefix_buf, p->prefixlen, gate_buf);
                    189:                if (cmd == RTM_ADD)
                    190:                  SET_FLAG (nexthop->flags, NEXTHOP_FLAG_FIB);
                    191:                break;
                    192:  
                    193:              /* The only valid case for this error is kernel's failure to install
                    194:               * a multipath route, which is common for FreeBSD. This should be
                    195:               * ignored silently, but logged as an error otherwise.
                    196:               */
                    197:              case ZEBRA_ERR_RTEXIST:
                    198:                if (cmd != RTM_ADD)
                    199:                  zlog_err ("%s: rtm_write() returned %d for command %d",
                    200:                    __func__, error, cmd);
                    201:                continue;
                    202:                break;
                    203:  
                    204:              /* Given that our NEXTHOP_FLAG_FIB matches real kernel FIB, it isn't
                    205:               * normal to get any other messages in ANY case.
                    206:               */
                    207:              case ZEBRA_ERR_RTNOEXIST:
                    208:              case ZEBRA_ERR_RTUNREACH:
                    209:              default:
                    210:                /* This point is reachable regardless of debugging mode. */
                    211:                if (!IS_ZEBRA_DEBUG_RIB)
                    212:                  inet_ntop (AF_INET, &p->u.prefix, prefix_buf, INET_ADDRSTRLEN);
                    213:                zlog_err ("%s: %s/%d: rtm_write() unexpectedly returned %d for command %s",
                    214:                  __func__, prefix_buf, p->prefixlen, error, lookup (rtm_type_str, cmd));
                    215:                break;
                    216:            }
                    217:          } /* if (cmd and flags make sense) */
                    218:        else
                    219:          if (IS_ZEBRA_DEBUG_RIB)
                    220:            zlog_debug ("%s: odd command %s for flags %d",
                    221:              __func__, lookup (rtm_type_str, cmd), nexthop->flags);
                    222:      } /* for (nexthop = ... */
                    223:  
                    224:    /* If there was no useful nexthop, then complain. */
                    225:    if (nexthop_num == 0 && IS_ZEBRA_DEBUG_KERNEL)
                    226:      zlog_debug ("%s: No useful nexthops were found in RIB entry %p", __func__, rib);
                    227: 
                    228:   return 0; /*XXX*/
                    229: }
                    230: 
                    231: int
                    232: kernel_add_ipv4 (struct prefix *p, struct rib *rib)
                    233: {
                    234:   int route;
                    235: 
                    236:   if (zserv_privs.change(ZPRIVS_RAISE))
                    237:     zlog (NULL, LOG_ERR, "Can't raise privileges");
                    238:   route = kernel_rtm_ipv4 (RTM_ADD, p, rib, AF_INET);
                    239:   if (zserv_privs.change(ZPRIVS_LOWER))
                    240:     zlog (NULL, LOG_ERR, "Can't lower privileges");
                    241: 
                    242:   return route;
                    243: }
                    244: 
                    245: int
                    246: kernel_delete_ipv4 (struct prefix *p, struct rib *rib)
                    247: {
                    248:   int route;
                    249: 
                    250:   if (zserv_privs.change(ZPRIVS_RAISE))
                    251:     zlog (NULL, LOG_ERR, "Can't raise privileges");
                    252:   route = kernel_rtm_ipv4 (RTM_DELETE, p, rib, AF_INET);
                    253:   if (zserv_privs.change(ZPRIVS_LOWER))
                    254:     zlog (NULL, LOG_ERR, "Can't lower privileges");
                    255: 
                    256:   return route;
                    257: }
                    258: 
                    259: #ifdef HAVE_IPV6
                    260: 
                    261: /* Calculate sin6_len value for netmask socket value. */
                    262: static int
                    263: sin6_masklen (struct in6_addr mask)
                    264: {
                    265:   struct sockaddr_in6 sin6;
                    266:   char *p, *lim;
                    267:   int len;
                    268: 
                    269: #if defined (INRIA)
                    270:   if (IN_ANYADDR6 (mask)) 
                    271:     return sizeof (long);
                    272: #else /* ! INRIA */
                    273:   if (IN6_IS_ADDR_UNSPECIFIED (&mask)) 
                    274:     return sizeof (long);
                    275: #endif /* ! INRIA */
                    276: 
                    277:   sin6.sin6_addr = mask;
                    278:   len = sizeof (struct sockaddr_in6);
                    279: 
                    280:   lim = (char *) & sin6.sin6_addr;
                    281:   p = lim + sizeof (sin6.sin6_addr);
                    282: 
                    283:   while (*--p == 0 && p >= lim) 
                    284:     len--;
                    285: 
                    286:   return len;
                    287: }
                    288: 
                    289: /* Interface between zebra message and rtm message. */
                    290: static int
                    291: kernel_rtm_ipv6 (int message, struct prefix_ipv6 *dest,
                    292:                 struct in6_addr *gate, int index, int flags)
                    293: {
                    294:   struct sockaddr_in6 *mask;
                    295:   struct sockaddr_in6 sin_dest, sin_mask, sin_gate;
                    296: 
                    297:   memset (&sin_dest, 0, sizeof (struct sockaddr_in6));
                    298:   sin_dest.sin6_family = AF_INET6;
                    299: #ifdef SIN6_LEN
                    300:   sin_dest.sin6_len = sizeof (struct sockaddr_in6);
                    301: #endif /* SIN6_LEN */
                    302: 
                    303:   memset (&sin_mask, 0, sizeof (struct sockaddr_in6));
                    304: 
                    305:   memset (&sin_gate, 0, sizeof (struct sockaddr_in6));
                    306:   sin_gate.sin6_family = AF_INET6;
                    307: #ifdef SIN6_LEN
                    308:   sin_gate.sin6_len = sizeof (struct sockaddr_in6);
                    309: #endif /* SIN6_LEN */
                    310: 
                    311:   sin_dest.sin6_addr = dest->prefix;
                    312: 
                    313:   if (gate)
                    314:     memcpy (&sin_gate.sin6_addr, gate, sizeof (struct in6_addr));
                    315: 
                    316:   /* Under kame set interface index to link local address. */
                    317: #ifdef KAME
                    318: 
                    319: #define SET_IN6_LINKLOCAL_IFINDEX(a, i) \
                    320:   do { \
                    321:     (a).s6_addr[2] = ((i) >> 8) & 0xff; \
                    322:     (a).s6_addr[3] = (i) & 0xff; \
                    323:   } while (0)
                    324: 
                    325:   if (gate && IN6_IS_ADDR_LINKLOCAL(gate))
                    326:     SET_IN6_LINKLOCAL_IFINDEX (sin_gate.sin6_addr, index);
                    327: #endif /* KAME */
                    328: 
                    329:   if (gate && dest->prefixlen == 128)
                    330:     mask = NULL;
                    331:   else
                    332:     {
                    333:       masklen2ip6 (dest->prefixlen, &sin_mask.sin6_addr);
                    334:       sin_mask.sin6_family = AF_INET6;
                    335: #ifdef SIN6_LEN
                    336:       sin_mask.sin6_len = sin6_masklen (sin_mask.sin6_addr);
                    337: #endif /* SIN6_LEN */
                    338:       mask = &sin_mask;
                    339:     }
                    340: 
                    341:   return rtm_write (message, 
                    342:                    (union sockunion *) &sin_dest,
                    343:                    (union sockunion *) mask,
                    344:                    gate ? (union sockunion *)&sin_gate : NULL,
                    345:                    index,
                    346:                    flags,
                    347:                    0);
                    348: }
                    349: 
                    350: /* Interface between zebra message and rtm message. */
                    351: static int
                    352: kernel_rtm_ipv6_multipath (int cmd, struct prefix *p, struct rib *rib,
                    353:                           int family)
                    354: {
                    355:   struct sockaddr_in6 *mask;
                    356:   struct sockaddr_in6 sin_dest, sin_mask, sin_gate;
                    357:   struct nexthop *nexthop;
                    358:   int nexthop_num = 0;
                    359:   unsigned int ifindex = 0;
                    360:   int gate = 0;
                    361:   int error;
                    362: 
                    363:   memset (&sin_dest, 0, sizeof (struct sockaddr_in6));
                    364:   sin_dest.sin6_family = AF_INET6;
                    365: #ifdef SIN6_LEN
                    366:   sin_dest.sin6_len = sizeof (struct sockaddr_in6);
                    367: #endif /* SIN6_LEN */
                    368:   sin_dest.sin6_addr = p->u.prefix6;
                    369: 
                    370:   memset (&sin_mask, 0, sizeof (struct sockaddr_in6));
                    371: 
                    372:   memset (&sin_gate, 0, sizeof (struct sockaddr_in6));
                    373:   sin_gate.sin6_family = AF_INET6;
                    374: #ifdef HAVE_STRUCT_SOCKADDR_IN_SIN_LEN
                    375:   sin_gate.sin6_len = sizeof (struct sockaddr_in6);
                    376: #endif /* HAVE_STRUCT_SOCKADDR_IN_SIN_LEN */
                    377: 
                    378:   /* Make gateway. */
                    379:   for (nexthop = rib->nexthop; nexthop; nexthop = nexthop->next)
                    380:     {
                    381:       gate = 0;
                    382: 
                    383:       if ((cmd == RTM_ADD
                    384:           && CHECK_FLAG (nexthop->flags, NEXTHOP_FLAG_ACTIVE))
                    385:          || (cmd == RTM_DELETE
                    386: #if 0
                    387:              && CHECK_FLAG (nexthop->flags, NEXTHOP_FLAG_FIB)
                    388: #endif
                    389:              ))
                    390:        {
                    391:          if (CHECK_FLAG (nexthop->flags, NEXTHOP_FLAG_RECURSIVE))
                    392:            {
                    393:              if (nexthop->rtype == NEXTHOP_TYPE_IPV6
                    394:                  || nexthop->rtype == NEXTHOP_TYPE_IPV6_IFNAME
                    395:                  || nexthop->rtype == NEXTHOP_TYPE_IPV6_IFINDEX)
                    396:                {
                    397:                  sin_gate.sin6_addr = nexthop->rgate.ipv6;
                    398:                  gate = 1;
                    399:                }
                    400:              if (nexthop->rtype == NEXTHOP_TYPE_IFINDEX
                    401:                  || nexthop->rtype == NEXTHOP_TYPE_IFNAME
                    402:                  || nexthop->rtype == NEXTHOP_TYPE_IPV6_IFNAME
                    403:                  || nexthop->rtype == NEXTHOP_TYPE_IPV6_IFINDEX)
                    404:                ifindex = nexthop->rifindex;
                    405:            }
                    406:          else
                    407:            {
                    408:              if (nexthop->type == NEXTHOP_TYPE_IPV6
                    409:                  || nexthop->type == NEXTHOP_TYPE_IPV6_IFNAME
                    410:                  || nexthop->type == NEXTHOP_TYPE_IPV6_IFINDEX)
                    411:                {
                    412:                  sin_gate.sin6_addr = nexthop->gate.ipv6;
                    413:                  gate = 1;
                    414:                }
                    415:              if (nexthop->type == NEXTHOP_TYPE_IFINDEX
                    416:                  || nexthop->type == NEXTHOP_TYPE_IFNAME
                    417:                  || nexthop->type == NEXTHOP_TYPE_IPV6_IFNAME
                    418:                  || nexthop->type == NEXTHOP_TYPE_IPV6_IFINDEX)
                    419:                ifindex = nexthop->ifindex;
                    420:            }
                    421: 
                    422:          if (cmd == RTM_ADD)
                    423:            SET_FLAG (nexthop->flags, NEXTHOP_FLAG_FIB);
                    424:        }
                    425: 
                    426:       /* Under kame set interface index to link local address. */
                    427: #ifdef KAME
                    428: 
                    429: #define SET_IN6_LINKLOCAL_IFINDEX(a, i) \
                    430:       do { \
                    431:        (a).s6_addr[2] = ((i) >> 8) & 0xff; \
                    432:        (a).s6_addr[3] = (i) & 0xff; \
                    433:       } while (0)
                    434: 
                    435:       if (gate && IN6_IS_ADDR_LINKLOCAL(&sin_gate.sin6_addr))
                    436:        SET_IN6_LINKLOCAL_IFINDEX (sin_gate.sin6_addr, ifindex);
                    437: #endif /* KAME */
                    438: 
                    439:       if (gate && p->prefixlen == 128)
                    440:        mask = NULL;
                    441:       else
                    442:        {
                    443:          masklen2ip6 (p->prefixlen, &sin_mask.sin6_addr);
                    444:          sin_mask.sin6_family = AF_INET6;
                    445: #ifdef SIN6_LEN
                    446:          sin_mask.sin6_len = sin6_masklen (sin_mask.sin6_addr);
                    447: #endif /* SIN6_LEN */
                    448:          mask = &sin_mask;
                    449:        }
                    450: 
                    451:       error = rtm_write (cmd,
                    452:                        (union sockunion *) &sin_dest,
                    453:                        (union sockunion *) mask,
                    454:                        gate ? (union sockunion *)&sin_gate : NULL,
                    455:                        ifindex,
                    456:                        rib->flags,
                    457:                        rib->metric);
                    458: 
                    459: #if 0
                    460:       if (error)
                    461:        {
                    462:          zlog_info ("kernel_rtm_ipv6_multipath(): nexthop %d add error=%d.",
                    463:            nexthop_num, error);
                    464:        }
                    465: #endif
                    466: 
                    467:       nexthop_num++;
                    468:     }
                    469: 
                    470:   /* If there is no useful nexthop then return. */
                    471:   if (nexthop_num == 0)
                    472:     {
                    473:       if (IS_ZEBRA_DEBUG_KERNEL)
                    474:        zlog_debug ("kernel_rtm_ipv6_multipath(): No useful nexthop.");
                    475:       return 0;
                    476:     }
                    477: 
                    478:   return 0; /*XXX*/
                    479: }
                    480: 
                    481: int
                    482: kernel_add_ipv6 (struct prefix *p, struct rib *rib)
                    483: {
                    484:   int route;
                    485: 
                    486:   if (zserv_privs.change(ZPRIVS_RAISE))
                    487:     zlog (NULL, LOG_ERR, "Can't raise privileges");
                    488:   route =  kernel_rtm_ipv6_multipath (RTM_ADD, p, rib, AF_INET6);
                    489:   if (zserv_privs.change(ZPRIVS_LOWER))
                    490:     zlog (NULL, LOG_ERR, "Can't lower privileges");
                    491: 
                    492:   return route;
                    493: }
                    494: 
                    495: int
                    496: kernel_delete_ipv6 (struct prefix *p, struct rib *rib)
                    497: {
                    498:   int route;
                    499: 
                    500:   if (zserv_privs.change(ZPRIVS_RAISE))
                    501:     zlog (NULL, LOG_ERR, "Can't raise privileges");
                    502:   route =  kernel_rtm_ipv6_multipath (RTM_DELETE, p, rib, AF_INET6);
                    503:   if (zserv_privs.change(ZPRIVS_LOWER))
                    504:     zlog (NULL, LOG_ERR, "Can't lower privileges");
                    505: 
                    506:   return route;
                    507: }
                    508: 
                    509: /* Delete IPv6 route from the kernel. */
                    510: int
                    511: kernel_delete_ipv6_old (struct prefix_ipv6 *dest, struct in6_addr *gate,
                    512:                        unsigned int index, int flags, int table)
                    513: {
                    514:   int route;
                    515: 
                    516:   if (zserv_privs.change(ZPRIVS_RAISE))
                    517:     zlog (NULL, LOG_ERR, "Can't raise privileges");
                    518:   route = kernel_rtm_ipv6 (RTM_DELETE, dest, gate, index, flags);
                    519:   if (zserv_privs.change(ZPRIVS_LOWER))
                    520:     zlog (NULL, LOG_ERR, "Can't lower privileges");
                    521: 
                    522:   return route;
                    523: }
                    524: #endif /* HAVE_IPV6 */

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