/*
* routeup.c - listens for routes coming up, tells stdout
* Copyright (c) 2012 The Chromium Authors. All rights reserved.
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*
* We emit 'n' for a route coming up.
*/
#include "config.h"
#ifdef linux
#include <asm/types.h>
#endif
#include <sys/socket.h> /* needed for linux/if.h for struct sockaddr */
#include <errno.h>
#include <fcntl.h>
#include <linux/if.h>
#include <linux/netlink.h>
#include <linux/rtnetlink.h>
#include <linux/sockios.h>
#include <stdio.h>
#include <string.h>
#include <sys/ioctl.h>
#include <sys/select.h>
#include <unistd.h>
#include "src/util.h"
#include "src/routeup.h"
int verbose;
int verbose_debug;
/*
* Set up the supplied context by creating and binding its netlink socket.
* Returns 0 for success, 1 for failure.
*/
int API
routeup_setup (struct routeup *rtc)
{
struct sockaddr_nl sa;
memset (&sa, 0, sizeof (sa));
sa.nl_family = AF_NETLINK;
sa.nl_groups = RTMGRP_IPV4_ROUTE | RTMGRP_IPV6_ROUTE;
rtc->netlinkfd = socket (AF_NETLINK, SOCK_RAW, NETLINK_ROUTE);
if (rtc->netlinkfd < 0)
{
perror ("netlink socket() failed");
return 1;
}
if (bind (rtc->netlinkfd, (struct sockaddr *) &sa, sizeof (sa)) < 0)
{
perror ("netlink bind() failed");
close (rtc->netlinkfd);
return 1;
}
if (fcntl (rtc->netlinkfd, F_SETFL, O_NONBLOCK) < 0)
{
perror ("netlink fcntl(O_NONBLOCK) failed");
close (rtc->netlinkfd);
return 1;
}
return 0;
}
/*
* Handle a single netlink message.
* Returns 0 if there was a route status change, 1 if there
* were no valid nlmsghdrs, and -1 if there was a read error.
*/
int API
routeup_process (struct routeup *rtc)
{
char buf[4096];
ssize_t ret;
size_t sz;
struct nlmsghdr *nh;
if ( (ret = read (rtc->netlinkfd, buf, sizeof (buf))) < 0)
return -1;
sz = (size_t) ret;
for (nh = (struct nlmsghdr *) buf; NLMSG_OK (nh, sz);
nh = NLMSG_NEXT (nh, sz))
{
/*
* Unpack the netlink message into a bunch of... well...
* netlink messages. The terminology is overloaded. Walk
* through the message until we find a header of type
* NLMSG_DONE.
*/
if (nh->nlmsg_type == NLMSG_DONE)
break;
if (nh->nlmsg_type != RTM_NEWROUTE)
continue;
/*
* Clear out the socket so we don't keep old messages
* queued up and eventually overflow the receive buffer.
*/
while (read (rtc->netlinkfd, buf, sizeof (buf)) > 0)
/* loop through receive queue */;
if (errno != EAGAIN) return -1;
return 0;
}
return 1;
}
/*
* Blocks until we get a route status change message then calls
* route_process(). Returns 0 if there was a route state change, 1 if there
* was a timeout, and -1 if there was a read error.
*/
int API
routeup_once (struct routeup *rtc, unsigned int timeout)
{
int ret;
struct timeval remaining;
struct timeval *rp = timeout ? &remaining : NULL;
fd_set fds;
remaining.tv_sec = timeout;
remaining.tv_usec = 0;
FD_ZERO (&fds);
FD_SET (rtc->netlinkfd, &fds);
while (select (rtc->netlinkfd + 1, &fds, NULL, NULL, rp) >= 0)
{
FD_ZERO (&fds);
FD_SET (rtc->netlinkfd, &fds);
if (timeout && !remaining.tv_sec && !remaining.tv_usec)
return 1;
ret = routeup_process (rtc);
if (ret == 1)
continue;
return ret;
}
return -1;
}
/* Tear down the supplied context by closing its netlink socket. */
void API
routeup_teardown (struct routeup *rtc)
{
close (rtc->netlinkfd);
}
#ifdef ROUTEUP_MAIN
int API
main ()
{
struct routeup rtc;
if (routeup_setup (&rtc))
return 1;
while (!routeup_once (&rtc, 0))
{
printf ("n\n");
fflush (stdout);
}
routeup_teardown (&rtc);
return 0;
}
#endif /* ROUTEUP_MAIN */