Home | History | Annotate | Download | only in src
      1 /*
      2  * routeup.c - listens for routes coming up, tells stdout
      3  * Copyright (c) 2012 The Chromium Authors. All rights reserved.
      4  * Use of this source code is governed by a BSD-style license that can be
      5  * found in the LICENSE file.
      6  *
      7  * We emit 'n' for a route coming up.
      8  */
      9 
     10 #include "config.h"
     11 
     12 #ifdef linux
     13 #include <asm/types.h>
     14 #endif
     15 
     16 #include <sys/socket.h>   /* needed for linux/if.h for struct sockaddr */
     17 #include <errno.h>
     18 #include <fcntl.h>
     19 #include <linux/if.h>
     20 #include <linux/netlink.h>
     21 #include <linux/rtnetlink.h>
     22 #include <linux/sockios.h>
     23 #include <stdio.h>
     24 #include <string.h>
     25 #include <sys/ioctl.h>
     26 #include <sys/select.h>
     27 #include <unistd.h>
     28 
     29 #include "src/util.h"
     30 #include "src/routeup.h"
     31 
     32 int verbose;
     33 int verbose_debug;
     34 
     35 /*
     36  * Set up the supplied context by creating and binding its netlink socket.
     37  * Returns 0 for success, 1 for failure.
     38  */
     39 int API
     40 routeup_setup (struct routeup *rtc)
     41 {
     42   struct sockaddr_nl sa;
     43   memset (&sa, 0, sizeof (sa));
     44   sa.nl_family = AF_NETLINK;
     45   sa.nl_groups = RTMGRP_IPV4_ROUTE | RTMGRP_IPV6_ROUTE;
     46   rtc->netlinkfd = socket (AF_NETLINK, SOCK_RAW, NETLINK_ROUTE);
     47   if (rtc->netlinkfd < 0)
     48     {
     49       perror ("netlink socket() failed");
     50       return 1;
     51     }
     52   if (bind (rtc->netlinkfd, (struct sockaddr *) &sa, sizeof (sa)) < 0)
     53     {
     54       perror ("netlink bind() failed");
     55       close (rtc->netlinkfd);
     56       return 1;
     57     }
     58   if (fcntl (rtc->netlinkfd, F_SETFL, O_NONBLOCK) < 0)
     59     {
     60       perror ("netlink fcntl(O_NONBLOCK) failed");
     61       close (rtc->netlinkfd);
     62       return 1;
     63     }
     64   return 0;
     65 }
     66 
     67 /*
     68  * Handle a single netlink message.
     69  * Returns 0 if there was a route status change, 1 if there
     70  * were no valid nlmsghdrs, and -1 if there was a read error.
     71  */
     72 int API
     73 routeup_process (struct routeup *rtc)
     74 {
     75   char buf[4096];
     76   ssize_t ret;
     77   size_t sz;
     78   struct nlmsghdr *nh;
     79   if ( (ret = read (rtc->netlinkfd, buf, sizeof (buf))) < 0)
     80     return -1;
     81   sz = (size_t) ret;
     82   for (nh = (struct nlmsghdr *) buf; NLMSG_OK (nh, sz);
     83        nh = NLMSG_NEXT (nh, sz))
     84     {
     85       /*
     86        * Unpack the netlink message into a bunch of... well...
     87        * netlink messages. The terminology is overloaded. Walk
     88        * through the message until we find a header of type
     89        * NLMSG_DONE.
     90        */
     91       if (nh->nlmsg_type == NLMSG_DONE)
     92         break;
     93       if (nh->nlmsg_type != RTM_NEWROUTE)
     94         continue;
     95       /*
     96        * Clear out the socket so we don't keep old messages
     97        * queued up and eventually overflow the receive buffer.
     98        */
     99       while (read (rtc->netlinkfd, buf, sizeof (buf)) > 0)
    100         /* loop through receive queue */;
    101       if (errno != EAGAIN) return -1;
    102       return 0;
    103     }
    104   return 1;
    105 }
    106 
    107 
    108 /*
    109  * Blocks until we get a route status change message then calls
    110  * route_process().  Returns 0 if there was a route state change, 1 if there
    111  * was a timeout, and -1 if there was a read error.
    112  */
    113 int API
    114 routeup_once (struct routeup *rtc, unsigned int timeout)
    115 {
    116   int ret;
    117   struct timeval remaining;
    118   struct timeval *rp = timeout ? &remaining : NULL;
    119   fd_set fds;
    120   remaining.tv_sec = timeout;
    121   remaining.tv_usec = 0;
    122   FD_ZERO (&fds);
    123   FD_SET (rtc->netlinkfd, &fds);
    124   while (select (rtc->netlinkfd + 1, &fds, NULL, NULL, rp) >= 0)
    125     {
    126       FD_ZERO (&fds);
    127       FD_SET (rtc->netlinkfd, &fds);
    128       if (timeout && !remaining.tv_sec && !remaining.tv_usec)
    129         return 1;
    130       ret = routeup_process (rtc);
    131       if (ret == 1)
    132         continue;
    133       return ret;
    134     }
    135   return -1;
    136 }
    137 
    138 /* Tear down the supplied context by closing its netlink socket. */
    139 void API
    140 routeup_teardown (struct routeup *rtc)
    141 {
    142   close (rtc->netlinkfd);
    143 }
    144 
    145 #ifdef ROUTEUP_MAIN
    146 int API
    147 main ()
    148 {
    149   struct routeup rtc;
    150   if (routeup_setup (&rtc))
    151     return 1;
    152   while (!routeup_once (&rtc, 0))
    153     {
    154       printf ("n\n");
    155       fflush (stdout);
    156     }
    157   routeup_teardown (&rtc);
    158   return 0;
    159 }
    160 #endif /* ROUTEUP_MAIN */
    161