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
routeup_setup(struct routeup * rtc)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
routeup_process(struct routeup * rtc)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
routeup_once(struct routeup * rtc,unsigned int timeout)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
routeup_teardown(struct routeup * rtc)140 routeup_teardown (struct routeup *rtc)
141 {
142   close (rtc->netlinkfd);
143 }
144 
145 #ifdef ROUTEUP_MAIN
146 int API
main()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