On Sat, 10 Jul 2010 00:02:16 +0200, Christian Taube <[email protected]>
wrote:
> What I am missing now is a way to run a script when the IP address on
> the pppoe interface changes. There seems to be no mechanism
> like the /etc/ppp/ppp.linkup script provided by the user-space pppoe(8)
> driver.
You might try the program below. Just compile with:
cc -O2 -o watchaddr watchaddr.c
You can then run something like:
./watchaddr -i pppoe0 /path/to/script
and whenever an address is added to or removed from pppoe0,
/path/to/script will be run. Omit the "-i pppoe0" option, and it will
run when an address changes on any interface.
Beware that changing an address involves an address both being removed
and being added, so your script will run twice.
(It looks like this functionality could easily be added to
ifstated(8), but a diff for that would be a bit more involved than
whipping up this one off program. ;-)
#include <sys/types.h>
#include <sys/socket.h>
#include <net/if.h>
#include <net/route.h>
#include <err.h>
#include <errno.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
void
usage(void)
{
extern char *__progname;
fprintf(stderr, "usage: %s [-i interface] command ...\n", __progname);
exit(1);
}
int
main(int argc, char *argv[])
{
char msg[2048];
int fd;
ssize_t n;
pid_t pid;
struct rt_msghdr *rtm;
struct ifa_msghdr *ifam;
unsigned int rtfilter;
unsigned int ifindex = 0;
int ch;
while ((ch = getopt(argc, argv, "i:")) != -1) {
switch (ch) {
case 'i':
ifindex = if_nametoindex(optarg);
if (ifindex == 0)
errx(1, "no interface: %s", optarg);
break;
case '?':
default:
usage();
}
}
argc -= optind;
argv += optind;
if (argv[0] == NULL)
usage();
fd = socket(PF_ROUTE, SOCK_RAW, 0);
if (fd == -1)
err(1, "unable to open route socket");
rtfilter = ROUTE_FILTER(RTM_NEWADDR) | ROUTE_FILTER(RTM_DELADDR);
if (setsockopt(fd, PF_ROUTE, ROUTE_MSGFILTER, &rtfilter,
sizeof(rtfilter)) == -1)
err(1, "unable to set route message filter");
for (;;) {
n = read(fd, msg, sizeof(msg));
if (n == -1) {
if (errno == EINTR)
continue;
err(1, "unable to read from route socket");
}
if (n == 0)
errx(1, "eof on route socket");
rtm = (struct rt_msghdr *)msg;
if (n < sizeof(rtm->rtm_msglen) || n < rtm->rtm_msglen) {
warnx("route message truncated");
continue;
}
if (rtm->rtm_version != RTM_VERSION) {
warnx("bad route message version");
continue;
}
if (rtm->rtm_type != RTM_NEWADDR &&
rtm->rtm_type != RTM_DELADDR) {
warnx("unexpected route message");
continue;
}
ifam = (struct ifa_msghdr *)rtm;
if (ifindex != 0 && ifam->ifam_index != ifindex)
continue;
pid = fork();
if (pid == -1) {
warnx("unable to fork child");
continue;
}
if (pid == 0) {
execvp(argv[0], argv);
err(1, "unable to exec command");
}
}
/* NOTREACHED */
}