On Fri, Apr 09, 2004 at 08:02:30PM +0300, Vlad GALU wrote:
>       I understand that in order to add a new route to the routing
> table, one must fill a buffer with an ifa_msghdr header, followed by three
> socked adddress structures, representing the destination net/ip, the
> netmask and the gateway.
[routing table suckiness snipped]
>       Could anyone provide a quick/small example on how to fill this
> member ? I don't really understand what I should write into it.

It's a sucky one and I had to stare at src/sbin/route/* for a while til
the penny dropped. The netmasks in particular are packed in in a format
which isn't immediately obvious.

Have a look at the attached code, in particular, reply_rtmsg_resolve().

Also have a look at Section 19.11 of TCP/IP Illustrated Volume 2, and
Section 18.9 of UNIX Network Programming 3e Vol 1.

The API sucks, and it's something we'd like to change in future.

Regards,
BMS
/*      $FreeBSD$ */

/*
 * This is a hack to demonstrate the concept of hooking for the
 * RTM_RESOLVE message being sent from the FreeBSD routing code,
 * as a means of looking up routes on demand using a routing protocol
 * such as AODV.
 * This code will probably be vastly cleaned up and tested more thoroughly
 * before being used as the basis for a user-space BSD AODV implementation.
 */

/*
 * Copyright (c) 2003 Bruce M. Simpson <[EMAIL PROTECTED]>
 * 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. All advertising materials mentioning features or use of this software
 *    must display the following acknowledgement:
 *        This product includes software developed by Bruce M. Simpson.
 * 4. Neither the name of Bruce M. Simpson nor the names of co-
 *    contributors may be used to endorse or promote products derived
 *    from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY Bruce M. Simpson AND CONTRIBUTORS
 * ``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 Bruce M. Simpson OR CONTRIBUTORS
 * 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/param.h>
#include <sys/file.h>
#include <sys/socket.h>
#include <sys/sysctl.h>
#include <sys/ioctl.h>
#include <sys/sysctl.h>
#include <sys/types.h>
#include <sys/signal.h>

#include <net/if.h>
#include <net/if_var.h>
#include <net/if_mib.h>
#include <net/if_types.h>
#include <net/if_dl.h>
#include <net/route.h>
#include <netinet/in.h>
#include <netinet/if_ether.h>
#include <arpa/inet.h>
#include <netdb.h>

#include <ctype.h>
#include <err.h>
#include <errno.h>
#include <paths.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sysexits.h>
#include <unistd.h>
#include <ifaddrs.h>

union sockunion {
        struct  sockaddr sa;
        struct  sockaddr_in sin;
        struct  sockaddr_dl sdl;
        struct  sockaddr_storage ss;
};
typedef union sockunion sockunion_t;

void usage(void);
int add_xresolve_route(char *ifname, struct sockaddr_in *sin, int bits);
int inet_cidr_aton(char *s, struct in_addr *pin, int *bits);
int create_if(char *ifname);
int destroy_if(char *ifname);
int if2sockaddr(char *ifname, struct sockaddr_dl *sdl);
int handle_rtmsg(struct rt_msghdr *rtm, int msglen);
int handle_rtmsg_resolve(struct rt_msghdr *rtm, int msglen);
int reply_rtmsg_resolve(struct sockaddr_in *sin);

/*
 * We check for the existence of ifname.
 */
#if 1
#define _IFNAME "disc1"
#else
#define _IFNAME "lo0"
#endif

int rtsock = -1;
int created = 0;
char *ifname = _IFNAME;

void
sighand_term(int sig)
{
        /*
         * Destroying an interface is sufficient to delete the routes
         * pointing to it.
         */
        if (created)
                destroy_if(ifname);

        exit(EXIT_SUCCESS);
}

void
setup_signals(void)
{
        struct sigaction sa, osa;

        sa.sa_handler = sighand_term;
        sa.sa_flags = 0;
        sigemptyset(&sa.sa_mask);

        sigaction(SIGTERM, &sa, &osa);
        sigaction(SIGINT, &sa, &osa);
        sigaction(SIGQUIT, &sa, &osa);
        sigaction(SIGKILL, &sa, &osa);
}

int
main(int argc, char *argv[])
{
        int n;
        int bits;
        char msg[2048];
        struct sockaddr_in sin;

        if (geteuid() != 0)
                errx(1, "must be root to alter routing table");

        memset(&sin, 0, sizeof(sin));
        sin.sin_family = AF_INET;
        sin.sin_len = sizeof(sin);

        /* Parse network argument */
        if ((argc != 2)
            || (inet_cidr_aton(argv[1], &sin.sin_addr, &bits) != 1))
                usage();

        setup_signals();

        /* Open routing socket */
        rtsock = socket(PF_ROUTE, SOCK_RAW, 0);
        if (rtsock == -1)
                err(EX_OSERR, "socket");

        /* Check that the target interface exists; create it if it doesn't. */
        if (if_nametoindex(ifname) == 0) {
                warnx("interface %s does not exist, creating.", ifname);
                create_if(ifname);
                created = 1;
                add_xresolve_route(ifname, &sin, bits);
        }

        /* Routing event loop */
        for (;;) {
                n = read(rtsock, msg, sizeof(msg));
                handle_rtmsg((struct rt_msghdr *)msg, n);
        }

        if (rtsock != -1)
                close(rtsock);

        exit (EXIT_SUCCESS);
}

void
usage(void)
{
        fprintf(stderr, "usage: rtmhack <testnet>\n"
                "<testnet> specifies the test network in CIDR notation\n");
        exit(EXIT_FAILURE);
}

/*
 * Like inet_aton(), but handle an optional CIDR prefix.
 */
int
inet_cidr_aton(char *s, struct in_addr *pin, int *bits)
{
        char *q;

        q = NULL;
        *bits = 32;

        if ((q = strchr(s, '/')) != NULL) {
                *bits = strtoul(q+1, 0, 0);
                *q = '\0';
        }

        return (inet_aton(s, pin));
}

/*
 * create an instance of a named clonable interface.
 * Return 0 if successful, or -1 if an error occurred.
 */
int
create_if(char *ifname)
{
        int s, retval;
        struct ifreq ifr;

        retval = 0;

        s = socket(AF_INET, SOCK_DGRAM, 0);
        if (s == -1)
                err(1, "socket");

        memset(&ifr, 0, sizeof(ifr));
        (void) strlcpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name));
        if (ioctl(s, SIOCIFCREATE, &ifr) < 0) {
                retval = -1;
                warn("SIOCIFCREATE");
        }

        close(s);
        return (retval);
}

/*
 * destroy an instance of a named clonable interface.
 * Return 0 if successful, or -1 if an error occurred.
 */
int
destroy_if(char *ifname)
{
        int s, retval;
        struct ifreq ifr;

        retval = 0;

        s = socket(AF_INET, SOCK_DGRAM, 0);
        if (s == -1)
                err(1, "socket");

        memset(&ifr, 0, sizeof(ifr));
        (void) strlcpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name));
        if (ioctl(s, SIOCIFDESTROY, &ifr) < 0) {
                retval = -1;
                warn("SIOCIFDESTROY");
        }

        close(s);
        return (retval);
}

/*
 * Copy the sockaddr_dl structure corresponding to the named interface
 * into the structure pointed to by sdl.
 * Returns 0 if successful, or -1 if the structure found was not valid.
 */
int
if2sockaddr(char *ifname, struct sockaddr_dl *sdl)
{
        struct ifaddrs *ifap, *ifa;
        struct sockaddr_dl *isdl;

        if (getifaddrs(&ifap))
                err(1, "getifaddrs");

        isdl = NULL;

        for (ifa = ifap; ifa; ifa = ifa->ifa_next) {
                if (ifa->ifa_addr->sa_family != AF_LINK)
                        continue;

                if (strcmp(ifname, ifa->ifa_name))
                        continue;

                isdl = (struct sockaddr_dl *)ifa->ifa_addr;
        }

        if (isdl)
                memcpy(sdl, isdl, isdl->sdl_len);

        return ((isdl != NULL) ? 0 : -1);
}

/*
 * Given the prefix length of an IPv4 CIDR network address,
 * fill out a sockaddr_in structure accordingly for use with
 * BSD routing code.
 *
 * Return the value of the sin_len member as a hint.
 */
int
inet_makenetmask(int bits, struct sockaddr_in *so_mask)
{
        char *cp;
        unsigned long mask;
        int len;
        const int maxbits = 32;

        memset(so_mask, 0, sizeof(struct sockaddr_in));

        mask = 0xFFFFFFFF << (maxbits - bits);
        so_mask->sin_addr.s_addr = htonl(mask);

        /* count number of bytes in mask containing set bits */
        cp = (char *)(&so_mask->sin_addr + 1);
        while (*--cp == 0 && cp > (char *)&so_mask)
                ;
        so_mask->sin_len = len = 1 + cp - (char *)&so_mask;

        return (len);
}

/*
 * Bind an cloning XRESOLVE route, for the given network/host,
 * to a named interface.
 *
 * The packing of the rtm message is all important. The kernel
 * expects it in a certain way. This routine seems to work but
 * 'route monitor' is reporting some junk at the end of the netmask.
 *
 * Return 0 if successful, or -1 if an error occurred.
 */

int
add_xresolve_route(char *ifname, struct sockaddr_in *so_dst, int bits)
{
#define ROUNDUP(a) \
        ((a) > 0 ? (1 + (((a) - 1) | (sizeof(long) - 1))) : sizeof(long))
#define NEXTADDR(cp, l, u) \
        do {                                            \
                l = (u)->sa.sa_len;                     \
                (l) = ROUNDUP(l);                       \
                memmove((cp), (u), (l));                \
                (cp) += (l);                            \
        } while (0)
        /* */
        struct {
                struct rt_msghdr rtm;
                sockunion_t addrs[RTAX_MAX];
        } r;
        sockunion_t so_iface;
        sockunion_t so_mask;
        int rlen, len, masklen;
        char *cp;

        cp = (char *)&r.addrs[0];
        masklen = rlen = len = 0;
        memset(&r, 0, sizeof(r));
        memset(&so_iface, 0, sizeof(so_iface));
        memset(&so_mask, 0, sizeof(so_mask));

        r.rtm.rtm_version = RTM_VERSION;
        r.rtm.rtm_type = RTM_ADD;
        r.rtm.rtm_pid = getpid();
        r.rtm.rtm_seq = 0;
        r.rtm.rtm_flags = RTF_XRESOLVE | RTF_CLONING | RTF_UP;
        r.rtm.rtm_addrs = RTA_DST | RTA_GATEWAY | RTA_NETMASK;

        if2sockaddr(ifname, &so_iface.sdl);
        masklen = inet_makenetmask(bits, &so_mask.sin);

        NEXTADDR(cp, len, (sockunion_t *)so_dst);
        NEXTADDR(cp, len, &so_iface);
        NEXTADDR(cp, len, &so_mask);

        r.rtm.rtm_msglen = len = cp - (char *)&r;
        rlen = write(rtsock, &r, len);
        if (rlen < 0)
                warn("write");

        return ((rlen > 0) ? 0 : -1);
#undef NEXTADDR
#undef ROUNDUP
}

/*
 * routing socket message dispatcher
 */
int
handle_rtmsg(struct rt_msghdr *rtm, int msglen)
{
        if (rtm->rtm_version != RTM_VERSION) {
                (void) printf("bad routing message version %d\n",
                        rtm->rtm_version);
                return (-1);
        }

        switch (rtm->rtm_type) {
        case RTM_RESOLVE:
                (void) printf("rtm_type %d: RTM_RESOLVE\n", rtm->rtm_type);
                handle_rtmsg_resolve(rtm, msglen);
                break;
        default:
                (void) printf("rtm_type %d: ignored\n", rtm->rtm_type);
        }

        return (0);
}

/*
 * Dispatch routine for RTM_RESOLVE routing messages.
 * Return 0 if successful; otherwise, return -1 if an error occurred.
 */
int
handle_rtmsg_resolve(struct rt_msghdr *rtm, int msglen)
{
        struct sockaddr_in *sin;
        struct sockaddr *sa;
        void *sp;

        /*
         * ignore messages from ourselves
         */
        if (rtm->rtm_pid == getpid()) {
                printf("heard own message, ignoring\n");
                return (0);
        }

        printf("rtm_index: %04x rtm_addrs: %08x\n",
                rtm->rtm_index, rtm->rtm_addrs);

        /*
         * The message must contain the address for which a route is
         * being requested, otherwise it is invalid.
         */
        if (!(rtm->rtm_addrs & RTA_DST)) {
                warnx("RTM_RESOLVE message does not contain destination");
                return (-1);
        }

        sa = sp = (rtm + 1);
        if (sa->sa_family != AF_INET) {
                warnx("RTM_RESOLVE contains non-AF_INET destination %d",
                        sa->sa_family);
                return (-1);
        }
        sin = (struct sockaddr_in *)sa;
        printf("route requested for %s\n", inet_ntoa(sin->sin_addr));

        /*
         * XXX: Should check if the requested destination is within the
         * network prefix specified on the command line.
         */
        reply_rtmsg_resolve(sin);
        printf("route resolved for %s\n", inet_ntoa(sin->sin_addr));

        return (0);
}

/*
 * Modify a given route in response to an RTM_RESOLVE message from the kernel.
 * Return 0 if successful; otherwise, return -1.
 */
int
reply_rtmsg_resolve(struct sockaddr_in *sin)
{
        struct {
                struct rt_msghdr rtm;
                struct sockaddr addrs[RTAX_MAX];
        } r;
        struct sockaddr_dl sdl;
        int len;

        memset(&r, 0, sizeof(r));
        r.rtm.rtm_version = RTM_VERSION;
        r.rtm.rtm_type = RTM_CHANGE;
        r.rtm.rtm_pid = getpid();
        r.rtm.rtm_seq = 0;

        if2sockaddr("lo0", &sdl);
        memcpy(&r.addrs[RTAX_DST], sin, sin->sin_len);
        memcpy(&r.addrs[RTAX_GATEWAY], &sdl, sdl.sdl_len);
        memset(&r.addrs[RTAX_IFP], 0, sizeof(r.addrs[RTAX_IFP]));
        memset(&r.addrs[RTAX_IFA], 0, sizeof(r.addrs[RTAX_IFA]));
        r.rtm.rtm_addrs = RTA_DST | RTA_GATEWAY | RTA_IFP | RTA_IFA;

        r.rtm.rtm_flags = RTF_DONE;
        r.rtm.rtm_msglen = sizeof(r);

        len = write(rtsock, &r, r.rtm.rtm_msglen);
        if (len != r.rtm.rtm_msglen)
                warn("write");

        return ((len > 0) ? 0 : -1);
}

Attachment: pgp00000.pgp
Description: PGP signature

Reply via email to