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);
}

Reply via email to