Установка нового маршрута в таблице маршрутизации linux с помощью сокета rtnetlink

Я написал пользовательскую космическую программу, которая должна установить новый маршрут в таблице маршрутизации. Что я замечаю, нет, если байты sendmsgFN возвращает правильно, но программа не в состоянии установить новый маршрут. Когда я пытаюсь диагностировать проблему с помощью gdb на ядре linux, я нахожу, что данные мусора отправляются из пользовательского пространства в сокет ядра 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 = %dn", 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 failedn");
    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 failedn");
            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

I have written a user space program that should install a new route in routing table. What i am noticing is , no if bytes sendmsg fn returns is correct, yet program is failing to install a new route. When i try to diagnose a problem using gdb on linux kernel, i find that garbage data is being sent from user space to kernel netlink socket.

output

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

Sharing code:

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 = %dn", 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 failedn");
    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 failedn");
            return -1;
 }
initialisation();
send_request(fd);
close(fd);
}

gdb output

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

As you can see above, the msg recieved in kernel is not accessible. When i apply the gdb on sendmsg fn in application and expand the msg being sent to kernel, i find everything perfect. What is causing this msg corruption in between user space and kernel ? Can anyone pls help diagnose the issue ?

I am using kernel version 4.5.3.

sharing the link to src file if someone wants to run the program.
https://drive.google.com/file/d/0B56H_R1fVFZXRjNWRkZ3M09pY2s/view?usp=sharing

Thanks.

= (struct msghdr *) 0xbb71e3c
(gdb) p *(struct msghdr *) 0xbb71e3c
= {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
= (struct user_msghdr *) 0xbfd5cc40
(gdb) p *(struct user_msghdr *) 0xbfd5cc40
Cannot access memory at address 0xbfd5cc40

Как вы можете видеть выше, msg, полученный в ядре, недоступен. Когда я применяю gdb на sendmsg fn в приложении и расширяю msg, отправляемый в ядро, я нахожу все идеально. Что вызывает это повреждение msg между пользовательским пространством и ядром ? Может кто-нибудь pls помочь диагностировать проблему ?

Я использую версию ядра 4.5.3.

поделитесь ссылкой на файл src, если кто-то хочет запустить программу.
https://drive.google.com/file/d/0B56H_R1fVFZXRjNWRkZ3M09pY2s/view?usp=sharing

Спасибо.

1 ответ

  1. Я мог бы успешно заставить его работать. Проблема заключается в способе добавления 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));
    

    В качестве примера я добавил запись » dst «и прокомментировал запись «gw».
    Для ‘gw’ тоже нужно следовать аналогичной структуре inet_prefix.

    Запись создана:

    12.0.32.0        0.0.0.0         255.255.255.0   U     0      0        0 eth0