linux route 实现

1. 实现前的准备工作

我们首先应该知道可以用 route, ip 命令来设置系统的静态路由表。

#route                      # <== 查看路由表
Destination     Gateway          Genmask           Flags  Metric  Ref    Use Iface
10.0.0.0        *                255.255.255.0     U      1       0      0 eth0
link-local      *                255.255.0.0       U      1000    0      0 eth0
default         RTA1025W.home    0.0.0.0           UG     0       0      0 eth0

First entry tell you the following:

  • Any traffic with destination 10.0.0.0 will not be using a gateway (thats the * on the line), will be using a 255.255.255.0 net mask, route is UP (that's the meaning of the U) and which interface the route uses. IF you do ifconfig -a you will probably see that your eth0 IP address is within the 10.0.0.0 range, so this is you local network route.

Third entry tells you the following:

  • If any traffic does not fit the traffic defined on any other rules then use this route. Imagine you want to visit www.yahoo.com, the address will be translated (I don't know what is the result address but its not in the 10.0.0.0 range for sure), because it wont fit on the 1st and the 2nd route it will be routed using the RTA1025W.home gateway, the metric will not mather and it will be using the interface defined on the default route line.

Q: Great, thanks! One more: if I send a datagram to a computer in my network, doesn't it have to go through the router? If so, isn't it using a gateway? Thanks

A: You router is your gateway in your route table, is your router not RTA1025W?

Q: Yes it is. So when two computers in my home network communicate, they have to go through the router right? I'm asking because for the first rule it is not defined as a gateway. Maybe its a gateway only when it is used to send information outside of the network?

A: Yeah, but the first rule tells your system that if the address translate from a hostname fits the first line (so all 10.0.0.0 addresses you can create with a netmask of 255.255.255.0), will not be routed to a gateway, that way your computers can communicate with each other without having to pass on the router (you can actually turn it off and your computers can contact each other).

1.1 查看一个命令的系统调用过程

  root # strace route ...

  rout # strace ip ...

  通过上面的命令可以看到,route命令主要使用了 ioctl() 函数来设置路由,而ip命令主要使用了 ioctl(), recvmsg(), sendmsg()等函数。为什么不用ioctl()来统一设置呢?这是一个问题。

1.2 获取源码

  root # route --version
  net-tools 1.60

route命令来自 net-tools 1.60 源码包:net-tools 1.60下载

  root@fs000:~# ip -V
  ip utility, iproute2-ss051107

ip命令来自 iproute2 源码包:iproute2 下载

1.3 学习源码

1. 数据结构

/* This structure gets passed by the SIOCADDRT and SIOCDELRT calls. */
struct rtentry

/* Structure describing an Internet socket address. */
struct sockaddr_in

/* ipv6 route struct */
struct in6_rtmsg rt;

/*  for IPv6. */
struct sockaddr_in6

/* interface request struct */
struct ifreq

 2. ioctl() 函数

  其主要功能与参数可以参考:Linux 网络编程之ioctl函数

2. 代码实现

/*
 * author: wangsd
 * description: setting the static route
 * copyright: GPL
 * history:
 * 2012.10.31    1.0 version    ipv4/ipv6 route add/del
 */

#include <net/route.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <string.h>
#include <sys/ioctl.h>
#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#include <unistd.h>
#include <net/if.h>       /* ifreq struct */
#include <netdb.h>


#define RTACTION_ADD 1   /* add action */
#define RTACTION_DEL 2   /* del action */

void usage();
int inet_setroute(int action, char **args);
int inet6_setroute(int action, char **args);

/* main function */
int main(int argc, char **argv)
{
    int action = 0;
    if(argc < 5)
    {
        usage();
        return -1;
    }
    if(strcmp(argv[1], "-A"))
    {
        usage();
        return -1;
    }

    if(!strcmp(argv[3], "add"))
    {
        action = RTACTION_ADD;
    }
    if(!strcmp(argv[3], "del"))
    {
        action = RTACTION_DEL;
    }

    /* add or del a ipv4 route item */
    if(!strcmp(argv[2], "inet"))
    {
        inet_setroute(action, argv+4);
    }
    /* add  or del a ipv6 route item */
    if(!strcmp(argv[2], "inet6"))
    {
        inet6_setroute(action, argv+4);
    }
    return 0;
}


/* print usage information */
void usage()
{
    printf("IPv4 Command: route -A inet add/del -net/-host TARGET netmask "
            "NETMASK gw GETWAY dev DEVICE mtu MTU\n");
    printf("IPv6 Command: route -A inet6 add/del -net TARGET/PREFIX "
            "gw GETWAY dev DEVICE mtu MTU\n");
    return ;
}

/*
 *  IPv4 add/del route item in route table
 */
int inet_setroute(int action, char **args)
{
    struct rtentry route;  /* route item struct */
    char target[128] = {0};
    char gateway[128] = {0};
    char netmask[128] = {0};

    struct sockaddr_in *addr;

    int skfd;

    /* clear route struct by 0 */
    memset((char *)&route, 0x00, sizeof(route));

    /* default target is net (host)*/
    route.rt_flags = RTF_UP ;

    args++;
    while(args)
    {
        if(*args == NULL)
        {
            break;
        }
        if(!strcmp(*args, "-net"))
        {/* default is a network target */
            args++;
            strcpy(target, *args);
            addr = (struct sockaddr_in*) &route.rt_dst;
            addr->sin_family = AF_INET;
            addr->sin_addr.s_addr = inet_addr(target);
            args++;
            continue;
        }
        else if(!strcmp(*args, "-host"))
        {/* target is a host */
            args++;
            strcpy(target, *args);
            addr = (struct sockaddr_in*) &route.rt_dst;
            addr->sin_family = AF_INET;
            addr->sin_addr.s_addr = inet_addr(target);
            route.rt_flags |= RTF_HOST;
            args++;
            continue;
        }
        else
        {
            usage();
            return -1;
        }
        if(!strcmp(*args, "netmask"))
        {/* netmask setting */
            args++;
            strcpy(netmask, *args);
            addr = (struct sockaddr_in*) &route.rt_genmask;
            addr->sin_family = AF_INET;
            addr->sin_addr.s_addr = inet_addr(netmask);
            args++;
            continue;
        }
        if(!strcmp(*args, "gw") || !strcmp(*args, "gateway"))
        {/* gateway setting */
            args++;
            strcpy(gateway, *args);
            addr = (struct sockaddr_in*) &route.rt_gateway;
            addr->sin_family = AF_INET;
            addr->sin_addr.s_addr = inet_addr(gateway);
            route.rt_flags |= RTF_GATEWAY;
            args++;
            continue;
        }
        if(!strcmp(*args, "device") || !strcmp(*args, "dev"))
        {/* device setting */
            args++;
            route.rt_dev = *args;
            args++;
            continue;
        }
        if(!strcmp(*args, "mtu"))
        {/* mtu setting */
            args++;
            route.rt_flags |= RTF_MTU;
            route.rt_mtu = atoi(*args);
            args++;
            continue;
        }
        /* if you have other options, please put them in this place,
          like the options above. */
    }

    /* create a socket */
    skfd = socket(AF_INET, SOCK_DGRAM, 0);
    if(skfd < 0)
    {
        perror("socket");
        return -1;
    }

    /* tell the kernel to accept this route */
    if(action == RTACTION_DEL)
    {/* del a route item */
        if(ioctl(skfd, SIOCDELRT, &route) < 0)
        {
            perror("SIOCDELRT");
            close(skfd);
            return -1;
        }
    }
    else
    {/* add a route item */
        if(ioctl(skfd, SIOCADDRT, &route) < 0)
        {
            perror("SIOCADDRT");
            close(skfd);
            return -1;
        }
    }
    (void) close(skfd);
    return 0;
}

int INET6_resolve(char *name, struct sockaddr_in6 *sin6);
int INET6_input(int type, char *bufp, struct sockaddr *sap);
int INET6_getsock(char *bufp, struct sockaddr *sap);

/* IPv6 add/del route item in route table */
/* main part of this function is from net-tools inet6_sr.c file */
int inet6_setroute(int action, char **args)
{
    struct in6_rtmsg rt;          /* ipv6 route struct */
    struct ifreq ifr;             /* interface request struct */
    struct sockaddr_in6 sa6;      /* ipv6 socket address */
    char target[128];
    char gateway[128] = "NONE";
    int metric;
    int prefix_len;               /* network prefix length */
    char *devname = NULL;         /* device name */
    char *cp;
    int mtu = 0;

    int skfd = -1;

    if (*args == NULL )
    {
        usage();
        return -1;
    }

    args++;
    strcpy(target, *args);

    if (!strcmp(target, "default"))
    {
        prefix_len = 0;
        memset(&sa6, 0, sizeof(sa6));
    } else
    {
        if ((cp = strchr(target, '/')))
        {
            prefix_len = atol(cp + 1);
            if ((prefix_len < 0) || (prefix_len > 128))
                usage();
            *cp = 0;
        } else
        {
            prefix_len = 128;
        }
        if (INET6_input(1, target, (struct sockaddr *) &sa6) < 0
                && INET6_input(0, target, (struct sockaddr *) &sa6) < 0)
        {
            return (1);
        }
    }

    /* Clean out the RTREQ structure. */
    memset((char *) &rt, 0, sizeof(struct in6_rtmsg));

    memcpy(&rt.rtmsg_dst, sa6.sin6_addr.s6_addr, sizeof(struct in6_addr));

    /* Fill in the other fields. */
    rt.rtmsg_flags = RTF_UP;
    if (prefix_len == 128)
        rt.rtmsg_flags |= RTF_HOST;
    rt.rtmsg_metric = 1;
    rt.rtmsg_dst_len = prefix_len;

    args++;
    while (*args)
     {
         if (!strcmp(*args, "metric"))
         {
             args++;
             if (!*args || !isdigit(**args))
             {
                 usage();
                 return -1;
             }
             metric = atoi(*args);
             rt.rtmsg_metric = metric;
             args++;
             continue;
         }
         if (!strcmp(*args, "gw") || !strcmp(*args, "gateway"))
         {
             args++;
             if (!*args)
                 return -1;
             if (rt.rtmsg_flags & RTF_GATEWAY)
                 return -1;
             strcpy(gateway, *args);
             if (INET6_input(1, gateway, (struct sockaddr *) &sa6) < 0)
             {
                 return -1;
             }
             memcpy(&rt.rtmsg_gateway, sa6.sin6_addr.s6_addr,
                     sizeof(struct in6_addr));
             rt.rtmsg_flags |= RTF_GATEWAY;
             args++;
             continue;
         }
         if (!strcmp(*args, "mod"))
         {
             args++;
             rt.rtmsg_flags |= RTF_MODIFIED;
             continue;
         }
         if (!strcmp(*args, "dyn"))
         {
             args++;
             rt.rtmsg_flags |= RTF_DYNAMIC;
             continue;
         }
         if (!strcmp(*args, "mtu"))
         {
             args++;
             mtu = atoi(*args);
             args++;
             continue;
         }
         if (!strcmp(*args, "device") || !strcmp(*args, "dev"))
         {
             args++;
             if (!*args)
                 return -1;
         } else if (args[1])
             return -1;

         devname = *args;
         args++;
     }

    /* Create a socket to the INET6 kernel. */
    if ((skfd = socket(AF_INET6, SOCK_DGRAM, 0)) < 0)
    {
        perror("socket");
        return -1;
    }

    memset(&ifr, 0, sizeof(ifr));

    if (devname)
    {/* device setting */
        strcpy(ifr.ifr_name, devname);

        if (ioctl(skfd, SIOGIFINDEX, &ifr) < 0)
        {
            perror("SIOGIFINDEX");
            return -1;
        }
        rt.rtmsg_ifindex = ifr.ifr_ifindex;
    }
    
    if (mtu)
    {/* mtu setting */
        ifr.ifr_mtu = mtu;

        if (ioctl(skfd, SIOCSIFMTU, &ifr) < 0)
        {
            perror("SIOCGIFMTU");
            return -1;
        }
    }

    /* Tell the kernel to accept this route. */
    if (action == RTACTION_DEL)
    {
        if (ioctl(skfd, SIOCDELRT, &rt) < 0)
        {
            perror("SIOCDELRT");
            close(skfd);
            return -1;
        }
    } else
    {
        if (ioctl(skfd, SIOCADDRT, &rt) < 0)
        {
            perror("SIOCADDRT");
            close(skfd);
            return -1;
        }
    }

    /* Close the socket. */
    (void) close(skfd);
    return (0);
}


/*
 * following functions are ipv6 address transfrom
 * (from string to address struct and so on.)
 * these functions from net-tools inet6.c file.
 */

int INET6_resolve(char *name, struct sockaddr_in6 *sin6)
{
    struct addrinfo req, *ai;
    int s;

    memset (&req, '\0', sizeof req);
    req.ai_family = AF_INET6;
    if ((s = getaddrinfo(name, NULL, &req, &ai))) 
    {
        //perror("getaddrinfo");
        fprintf(stderr, "getaddrinfo: %s: %d\n", name, s);
        return -1;
    }
    memcpy(sin6, ai->ai_addr, sizeof(struct sockaddr_in6));

    freeaddrinfo(ai);

    return (0);
}
int INET6_getsock(char *bufp, struct sockaddr *sap)
{
    struct sockaddr_in6 *sin6;

    sin6 = (struct sockaddr_in6 *) sap;
    sin6->sin6_family = AF_INET6;
    sin6->sin6_port = 0;

    if (inet_pton(AF_INET6, bufp, sin6->sin6_addr.s6_addr) <= 0)
    return (-1);

    return 16;            /* ?;) */
}

int INET6_input(int type, char *bufp, struct sockaddr *sap)
{
    switch (type) 
    {
    case 1:
        return (INET6_getsock(bufp, sap));
    default:
        return (INET6_resolve(bufp, (struct sockaddr_in6 *) sap));
    }
}

/*------------------------end-----------------------------------*/

命令投入的例子:

### IPv4
my_route -A inet add -net 192.56.76.0 netmask 255.255.255.0 dev eth0
my_route -A inet add -net 192.56.76.0 netmask 255.255.255.0 dev eth0
### IPv6
my_route -A inet6 add -net ::/0 dev eth0 mtu 1500
my_route -A inet6 del -net ::/0 dev eth0 mtu 1500

原文地址:https://www.cnblogs.com/wangshide/p/2740410.html