0

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 = %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 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.

Abhishek Sagar
  • 1,189
  • 4
  • 20
  • 44

1 Answers1

0

I could successfully make it work. The issue is the way RTA_DST and RTA_GATEWAY is added. You need to use inet_prefix for dst. Please note dst.data[],dst.bytelen and dst.bitlen are all just some random values. Please initialize as required for your solution. (this is just to demonstrate that it works!)

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

As a sample I added 'dst' entry and commented out 'gw' entry. For 'gw' too you have to follow similar struct inet_prefix.

Entry created:

12.0.32.0        0.0.0.0         255.255.255.0   U     0      0        0 eth0
Pramod
  • 371
  • 4
  • 13
  • Did not see any such details for configuring the kernel routing table. Which kernel version you made it work ? I will test your soln on kernel 4.5 and update. Thanks much. – Abhishek Sagar Jun 06 '16 at 11:48
  • I tested it on 3.19.0-49. – Pramod Jun 06 '16 at 11:52
  • @abhishek, "Did not see any such details for configuring the kernel routing table" what does it mean? – Pramod Jun 06 '16 at 13:24
  • @AbhishekSagar: please read and understand my answer repeatedly until you get it. Or you can look at iproute2 package for the conversion routines for faster understanding. look at ip_route_modify and dig deeper functions like get_addr_l / get_addr – Pramod Jun 06 '16 at 19:05
  • i mean to say, how does dst.data[x] values which you mentioned maps to 12.0.32.0 ?? Also, your changes are not working for me, pls share your code if possible via GDrive link or otherwise if possible. Or email : sagar@brocade.com – Abhishek Sagar Jun 06 '16 at 19:14