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