使用rtnetlink套接字在linux路由表中安装新路由

时间:2016-05-26 15:57:12

标签: linux sockets kernel netlink

我编写了一个用户空间程序,应该在路由表中安装新路由。我注意到的是,如果字节sendmsg fn返回正确,但是程序无法安装新路由,则为no。当我尝试在linux内核上使用gdb诊断问题时,我发现垃圾数据正在从用户空间发送到内核netlink套接字。

输出

[root@localhost rtnetlink]# ./netlink_add_route.exe
bytes send = 52

分享代码:

typedef struct _request
{
    struct nlmsghdr netlink_header;
    struct rtmsg rt_message;
    char buffer[1024];
} req_t;


int addattr_l(struct nlmsghdr *n, int maxlen, int type, void *data, int alen)
{
    /* alen is the length of the data. Add sizeof(struct rtattr) to it to accomodate
    type, length, value format for rtattr */
    int len = RTA_LENGTH(alen); // (RTA_ALIGN(sizeof(struct rtattr)) + (len))
    struct rtattr *rta;
    /* size of request should not be violated*/
    if (NLMSG_ALIGN(n->nlmsg_len) + len > maxlen)
            return -1;

    /* go to end of buffer in request data structure*/
    rta = (struct rtattr*)(((char*)n) + NLMSG_ALIGN(n->nlmsg_len));
    /* specify attribute using TLV format*/
    rta->rta_type = type;
    rta->rta_len = len;
    memcpy(RTA_DATA(rta), data, alen);
    /* increase the nlmsg_len to accomodate the added new attribute*/
    n->nlmsg_len = NLMSG_ALIGN(n->nlmsg_len) + len;
    return 0;
}


static void
initialisation(){
    /* initialise the request structure*/
    int index = 3,
    gw  = 3232236545/*192.168.4.1*/,
    dst = 3232235776/*192.168.1.0*/;

    memset(&request, 0, sizeof(request));
    /* set the nlmsg_len = nl header + underlying structure*/
    request.netlink_header.nlmsg_len = NLMSG_LENGTH(sizeof(struct rtmsg)); /*NLMSG_HDRLEN + sizeof(struct rtmsg);*/
    /* set the flags that facilitates adding a route in routing table*/
    request.netlink_header.nlmsg_flags = NLM_F_REQUEST|NLM_F_CREATE;
    /* note that inet_rtm_newroute() is the fn in kernel which will be eventually called to add a new route in routing table*/
    request.netlink_header.nlmsg_type = RTM_NEWROUTE;
    /* Now filling the rtmsg*/
    request.rt_message.rtm_family = AF_INET;
    request.rt_message.rtm_table = RT_TABLE_MAIN;
    request.rt_message.rtm_protocol = RTPROT_BOOT;/*Route installed during boot*/
    request.rt_message.rtm_scope = RT_SCOPE_UNIVERSE;
    request.rt_message.rtm_type = RTN_UNICAST; /*Gateway or direct route  */

    /* Add routing info*/
    addattr_l(&request.netlink_header, sizeof(request), RTA_GATEWAY, &gw,    sizeof(gw));
    addattr_l(&request.netlink_header, sizeof(request), RTA_DST,     &dst,   sizeof(dst));
    addattr_l(&request.netlink_header, sizeof(request), RTA_OIF,     &index, sizeof(index));
    /* For adding a route, the gateway, destination address and the interface
    will suffice, now the netlink packet is all set to go to the kernel*/
}

static void
send_request(int fd){
    int rc = 0;
    struct msghdr msg;
    struct sockaddr_nl nladdr;
    struct iovec iov;
    iov.iov_base  =  (void*)&request.netlink_header;
    iov.iov_len = request.netlink_header.nlmsg_len ;/* Total length : from request start to end of last attribute*/
    memset(&nladdr, 0, sizeof(nladdr));
    nladdr.nl_family = AF_NETLINK;
    nladdr.nl_pid = 0; /* For Linux Kernel  */
    nladdr.nl_groups = 0;

    msg.msg_name = (void *)&nladdr;
    msg.msg_namelen = sizeof(nladdr);
    msg.msg_iov = &iov;
    msg.msg_iovlen = 1;
    rc = sendmsg(fd, &msg, 0);
    printf("bytes send = %d\n", rc);
}



int
main(int argc, char *argv[])
{
 struct sockaddr_nl la; 
 int fd = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE);
 if(fd < 0){
    printf("socket creation failed\n");
    return -1;
 }
 bzero(&la, sizeof(la));
 la.nl_family = AF_NETLINK;
 la.nl_pid = getpid();
 la.nl_groups = 0;
 if(bind(fd, (struct sockaddr*) &la, sizeof(la)) < 0){
            printf("Bind failed\n");
            return -1;
 }
initialisation();
send_request(fd);
close(fd);
}

gdb输出

Breakpoint 1, inet_rtm_newroute (skb=0xbb53f20, nlh=0xb96ed00) at net/ipv4/fib_frontend.c:732
732     {
(gdb) bt
#0  inet_rtm_newroute (skb=0xbb53f20, nlh=0xb96ed00) at net/ipv4/fib_frontend.c:732
#1  0x081f1b28 in rtnetlink_rcv_msg (skb=0xbb53f20, nlh=0xb96ed00) at net/core/rtnetlink.c:3412
#2  0x081fcd07 in netlink_rcv_skb (skb=0xbb53f20, cb=0x81f19de <rtnetlink_rcv_msg>)
    at net/netlink/af_netlink.c:3017
#3  0x081efcb9 in rtnetlink_rcv (skb=0xbb53f20) at net/core/rtnetlink.c:3418
#4  0x081fc7e4 in netlink_unicast_kernel (ssk=<optimized out>, skb=<optimized out>, sk=<optimized out>)        at net/netlink/af_netlink.c:1834
#5  netlink_unicast (ssk=0xbb03000, skb=0xbb53f20, portid=0, nonblock=0) at net/netlink/af_netlink.c:1860
#6  0x081fcbf3 in netlink_sendmsg (sock=0xa50a700, msg=0xbb71e3c, len=52) at net/netlink/af_netlink.c:2511
#7  0x081cf181 in sock_sendmsg_nosec (msg=<optimized out>, sock=<optimized out>) at net/socket.c:611
#8  sock_sendmsg (sock=0xa50a700, msg=<optimized out>) at net/socket.c:621
#9  0x081cf781 in ___sys_sendmsg (sock=0xa50a700, msg=<optimized out>, msg_sys=0xbb71e3c, flags=0,
    used_address=0x0) at net/socket.c:1947
#10 0x081d044f in __sys_sendmsg (fd=3, msg=0xbfd5cc40, flags=0) at net/socket.c:1981
#11 0x081d0a97 in SYSC_sendmsg (flags=<optimized out>, msg=<optimized out>, fd=<optimized out>)
    at net/socket.c:1992
#12 SyS_sendmsg (flags=0, msg=-1076507584, fd=3) at net/socket.c:1988
#13 SYSC_socketcall (args=<optimized out>, call=<optimized out>) at net/socket.c:2397
#14 SyS_socketcall (call=16, args=-1076507632) at net/socket.c:2315
#15 0x0805ba59 in handle_syscall (r=0xbbab3f0) at arch/um/kernel/skas/syscall.c:37
#16 0x08069147 in handle_trap (local_using_sysemu=<optimized out>, regs=<optimized out>,
    pid=<optimized out>) at arch/um/os-Linux/skas/process.c:172
#17 userspace (regs=0xbbab3f0) at arch/um/os-Linux/skas/process.c:384
#18 0x080594df in fork_handler () at arch/um/kernel/process.c:154
#19 0x00000000 in ?? ()
(gdb) f 6
#6  0x081fcbf3 in netlink_sendmsg (sock=0xa50a700, msg=0xbb71e3c, len=52) at net/netlink/af_netlink.c:2511
2511            err = netlink_unicast(sk, skb, dst_portid, msg->msg_flags&MSG_DONTWAIT);
(gdb) p msg
$1 = (struct msghdr *) 0xbb71e3c
(gdb) p *(struct msghdr *) 0xbb71e3c
$2 = {msg_name = 0xbb71d98, msg_namelen = 12, msg_iter = {type = 1, iov_offset = 0, count = 0, {
      iov = 0xbb71d60, kvec = 0xbb71d60, bvec = 0xbb71d60}, nr_segs = 0}, msg_control = 0x4,
  msg_controllen = 0, msg_flags = 0, msg_iocb = 0x0}
(gdb) f 10
#10 0x081d044f in __sys_sendmsg (fd=3, msg=0xbfd5cc40, flags=0) at net/socket.c:1981
1981            err = ___sys_sendmsg(sock, msg, &msg_sys, flags, NULL);
(gdb) p msg
$3 = (struct user_msghdr *) 0xbfd5cc40
(gdb) p *(struct user_msghdr *) 0xbfd5cc40
Cannot access memory at address 0xbfd5cc40

如上所示,内核中收到的消息无法访问。当我在应用程序中的sendmsg fn上应用gdb并扩展发送到内核的msg时,我发现一切都很完美。是什么导致用户空间和内核之间的这种msg损坏?任何人都可以帮助诊断问题吗?

我使用的是内核版本4.5.3。

如果有人想要运行该程序,则共享src文件的链接。 https://drive.google.com/file/d/0B56H_R1fVFZXRjNWRkZ3M09pY2s/view?usp=sharing

感谢。

1 个答案:

答案 0 :(得分:0)

我可以成功地让它发挥作用。问题是添加RTA_DST和RTA_GATEWAY的方式。您需要为dst使用inet_prefix。 请注意dst.data [],dst.bytelen和dst.bitlen都只是一些随机值。请根据您的解决方案的要求进行初始化。 (这只是为了证明它有效!)

typedef struct
{
        __u16 flags;
        __u16 bytelen;
        __s16 bitlen;

        __u16 family;
        __u32 data[8];
} inet_prefix;


    inet_prefix dst;
    dst.data[0] = 11;
    dst.data[1] = 12;
    dst.data[2] = 13;
    dst.data[3] = 14;
    dst.data[4] = 15;
    dst.data[5] = 16;
    dst.data[6] = 17;
    dst.data[7] = 17;
    dst.bytelen = 32;
    dst.bitlen = dst.bytelen * 8;

/* mask */
request.rt_message.rtm_dst_len = 24;

//addattr_l(&request.netlink_header, sizeof(request), RTA_GATEWAY, &gw,    sizeof(gw));
addattr_l(&request.netlink_header, sizeof(request), RTA_DST,     &dst,   sizeof(dst));

作为样本,我添加了&#39; dst&#39;进入并注释掉了&#39; gw&#39;条目。 对于&#39; gw&#39;你也必须遵循类似的struct inet_prefix。

创建条目:

12.0.32.0        0.0.0.0         255.255.255.0   U     0      0        0 eth0