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