Hi!
Please forgive me if I missed something about the kernel pppoe but my initial
tests with the program were positive. However I noticed that when my ISP
disconnects the connection, it reauthenticates with a new IP and the default
route has to be set anew for the outgoing IP's to change. One can write a
few dirty scripts to do this with crontab or you can try out my new program
that I wrote just for this purpose. It waits until the IP on the pppoe0
interface changes and then it sets a new default route. Initial tests with
this program show it works positively.
program below - peter
/*
* Copyright (c) 2005 Peter Philipp
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. The name of the author may not be used to endorse or promote products
* derived from this software without specific prior written permission
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/ioctl.h>
#include <net/if.h>
#include <net/route.h>
#include <netinet/in.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include <string.h>
#include <unistd.h>
#include <syslog.h>
#define INTERFACE "pppoe0"
#define BUFLEN (sizeof(struct rt_msghdr) + 512)
#define WAITPERIOD 60
extern char *__progname;
u_int32_t get_ip(char *interface);
int
main(int argc, char *argv[])
{
struct timeval tv;
struct sockaddr_in *sin;
struct rt_msghdr *rtm, *rtm2;
in_addr_t last_address;
in_addr_t curr_address;
int so, sel;
int rs, n;
int seq, ch;
int waitperiod = WAITPERIOD;
pid_t pid;
char *interface = INTERFACE;
char *buf, *sbuf;
char lastip[INET_ADDRSTRLEN];
char currip[INET_ADDRSTRLEN];
if (geteuid() != 0) {
fprintf(stderr, "must be root\n");
exit(1);
}
while ((ch = getopt(argc, argv, "i:w:")) != -1) {
switch (ch) {
case 'i':
interface = optarg;
break;
case 'w':
waitperiod = atoi(optarg);
break;
default:
fprintf(stderr, "usage: %s [-i interface][-w
waittime]\n", __progname);
exit(1);
}
}
last_address = get_ip(interface);
seq = arc4random();
openlog(__progname, LOG_NDELAY | LOG_PID | LOG_CONS, LOG_DAEMON);
daemon(0, 0);
for (;;) {
tv.tv_sec = waitperiod;
tv.tv_usec = 0;
sel = select(0, NULL, NULL, NULL, &tv);
if (sel < 0)
continue;
curr_address = get_ip(interface);
if (last_address != curr_address) {
inet_ntop(AF_INET, &last_address, (char *)&lastip,
sizeof(lastip));
inet_ntop(AF_INET, &curr_address, (char *)&currip,
sizeof(currip));
syslog(LOG_INFO, "interface %s changed its address from
%s to %s, adding new default route", interface, lastip, currip);
last_address = curr_address;
rs = socket(AF_ROUTE, SOCK_RAW, 0);
if (rs < 0) {
syslog(LOG_INFO, "socket: %m");
continue;
}
sbuf = calloc(1, BUFLEN);
if (sbuf == NULL) {
syslog(LOG_INFO, "calloc: %m");
close(rs);
continue;
}
rtm = (struct rt_msghdr *)sbuf;
rtm->rtm_msglen = sizeof(struct rt_msghdr) +
sizeof(struct sockaddr_in);
rtm->rtm_version = RTM_VERSION;
rtm->rtm_type = RTM_GET;
rtm->rtm_addrs = RTA_DST;
rtm->rtm_pid = pid = getpid();
rtm->rtm_seq = seq;
sin = (struct sockaddr_in *) (rtm + 1);
sin->sin_family = AF_INET;
sin->sin_addr.s_addr = 0;
if (send(rs, rtm, rtm->rtm_msglen, 0) < 0) {
syslog(LOG_INFO, "send: %m");
close(rs);
continue;
}
do {
n = recv(rs, rtm, BUFLEN, 0);
} while (rtm->rtm_type != RTM_GET || rtm->rtm_seq !=
seq ||
rtm->rtm_pid != pid);
buf = calloc(1, BUFLEN);
if (buf == NULL) {
perror("calloc");
close(rs);
continue;
}
rtm2 = (struct rt_msghdr *)buf;
memcpy(rtm2, rtm, rtm->rtm_msglen);
rtm2->rtm_type = RTM_DELETE;
rtm2->rtm_seq = ++seq;
if (send(rs, rtm2, rtm2->rtm_msglen, 0) < 0) {
syslog(LOG_INFO, "send: %m");
close(rs);
continue;
}
do {
n = recv(rs, rtm2, BUFLEN, 0);
} while (rtm2->rtm_type != RTM_DELETE || rtm2->rtm_seq
!= seq ||
rtm2->rtm_pid != pid);
free(buf);
buf = calloc(1, BUFLEN);
if (buf == NULL) {
perror("calloc");
close(rs);
continue;
}
rtm2 = (struct rt_msghdr *)buf;
memcpy(rtm2, rtm, rtm->rtm_msglen);
rtm2->rtm_flags &= ~(RTF_DONE);
rtm2->rtm_type = RTM_ADD;
rtm2->rtm_seq = ++seq;
if (send(rs, rtm2, rtm2->rtm_msglen, 0) < 0) {
syslog(LOG_INFO, "send: %m");
close(rs);
continue;
}
do {
n = recv(rs, rtm2, BUFLEN, 0);
} while (rtm2->rtm_type != RTM_ADD || rtm2->rtm_seq !=
seq ||
rtm2->rtm_pid != pid);
free(buf);
free(sbuf);
close(rs);
}
}
/* NOTREACHED */
}
u_int32_t
get_ip(char *interface)
{
int so;
struct sockaddr_in *sin;
static struct ifreq ifr;
so = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
if (so < 0) {
return 0;
}
memset(&ifr, 0, sizeof(ifr));
strlcpy(ifr.ifr_name, interface, sizeof(ifr.ifr_name));
if (ioctl(so, SIOCGIFADDR, (caddr_t)&ifr) < 0) {
return 0;
}
sin = (struct sockaddr_in *)&ifr.ifr_addr;
close(so);
return (sin->sin_addr.s_addr);
}