Handle routing table in kernel

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Hi,

With the new advanced routing, is rtnetlink the only way to handle routing table (multiple tables)?

Following module tries to add a route in a timer, i.e.,
    ip route add 10.10.10.10 via 192.168.0.1 dev eth0 

However, it hangs the system when the timer goes off. If no timer is used, the result is correct. So rtnetlink cannot be used in a timer? Or may be there is a locking issue in softirq?

Thank you very much.

LML
------------------
#include <linux/config.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/socket.h>
#include <linux/skbuff.h>
#include <linux/netlink.h>
#include <linux/rtnetlink.h>
#include <linux/slab.h>
#include <asm/uaccess.h>
#include <linux/wait.h>
#include <linux/sched.h>

MODULE_LICENSE("GPL");

struct req_type {
    struct nlmsghdr n;
    struct rtmsg r;
    char buf[256];
};

static struct timer_list func_timer;

static struct socket *rtnl_sock;
static struct sockaddr_nl rtnl_sin;
static struct req_type req;

int rtnl_open(void)
{
    int ret = 0;

    if((ret=(sock_create(PF_NETLINK, SOCK_DGRAM, NETLINK_ROUTE, &rtnl_sock))) < 0) {
        printk("cannot open rtnl socket.\n");
        return ret;
    }

    memset(&rtnl_sin, 0, sizeof(rtnl_sin));
    rtnl_sin.nl_family = PF_NETLINK;

    if((ret=rtnl_sock->ops->bind(rtnl_sock, (struct sockaddr *)&rtnl_sin, sizeof(rtnl_sin))) < 0){
        printk("Cannot bind netlink socket.");
        return ret;
    }

    return ret;
}

int rtnl_close(void)
{
    sock_release(rtnl_sock);

    return 0;
}

int rtnl_talk(struct nlmsghdr *n)
{
    mm_segment_t oldfs;

    struct iovec iov = { (void *)n, n->nlmsg_len };
    struct msghdr msg = {
        (void *)&rtnl_sin, sizeof(rtnl_sin), &iov, 1, NULL, 0, 0
    };

    oldfs=get_fs();
    set_fs(get_ds());

    if((sock_sendmsg(rtnl_sock, &msg, n->nlmsg_len)) < 0)
        printk("sock_sendmsg error.\n");

    set_fs(oldfs);

    return 0;
}

int addattr_l(struct nlmsghdr *n, int maxlen, int type, void *data, int alen)
{
    int len = RTA_LENGTH(alen);
    struct rtattr *rta;

    if (NLMSG_ALIGN(n->nlmsg_len) + len > maxlen)
        return -1;

    rta = (struct rtattr*)(((char*)n) + NLMSG_ALIGN(n->nlmsg_len));
    rta->rta_type = type;
    rta->rta_len = len;
    memcpy(RTA_DATA(rta), data, alen);
    n->nlmsg_len = NLMSG_ALIGN(n->nlmsg_len) + len;

    return 0;
}

int addattr32(struct nlmsghdr *n, int maxlen, int type, __u32 data)
{
    int len = RTA_LENGTH(4);
    struct rtattr *rta;
    if (NLMSG_ALIGN(n->nlmsg_len) + len > maxlen)
        return -1;
    rta = (struct rtattr*)(((char*)n) + NLMSG_ALIGN(n->nlmsg_len));
    rta->rta_type = type;
    rta->rta_len = len;
    memcpy(RTA_DATA(rta), &data, 4);
    n->nlmsg_len = NLMSG_ALIGN(n->nlmsg_len) + len;
    return 0;
}

static int func(void)
{
    int ret = 0;

    __u32 dip = htonl(0x0a0a0a0a); // 10.10.10.10
    __u32 gw = htonl(0xc0a80001); // 192.168.0.1
    unsigned short nlm_type = RTM_NEWROUTE;
    unsigned char tab_id = RT_TABLE_MAIN;

    if((ret=rtnl_open()) < 0)
        return ret;

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

    req.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct rtmsg));
    req.n.nlmsg_type = nlm_type;
    req.n.nlmsg_flags = NLM_F_REQUEST | NLM_F_CREATE;

    req.r.rtm_dst_len = 32;
    req.r.rtm_family = PF_INET;
    req.r.rtm_scope = RT_SCOPE_UNIVERSE;
    req.r.rtm_type = RTN_UNICAST;
    req.r.rtm_protocol= RTPROT_STATIC;
    req.r.rtm_table = tab_id;

    addattr_l(&req.n, sizeof(req), RTA_DST, &dip, sizeof(__u32));
    addattr_l(&req.n, sizeof(req), RTA_GATEWAY, &gw, sizeof(__u32));
    addattr32(&req.n, sizeof(req), RTA_OIF, 2);

    if((ret=rtnl_talk(&req.n)) < 0) {
        rtnl_close();
        return ret;
    }

    rtnl_close();

    return 0;
}

void timer_func(unsigned long ptr)
{
    printk(KERN_INFO "timer_func...\n");
    func();
}

static int init(void)
{

    printk(KERN_INFO "...start\n");

    init_timer(&func_timer);
    func_timer.function = timer_func;
    func_timer.data = 0;
    func_timer.expires = jiffies + 5*HZ;
    add_timer(&func_timer);

    return 0;
}

static void fini(void)
{
    del_timer(&func_timer);
}

module_init(init);
module_exit(fini); 
------------------

--
Kernelnewbies: Help each other learn about the Linux kernel.
Archive:       http://mail.nl.linux.org/kernelnewbies/
FAQ:           http://kernelnewbies.org/faq/


[Index of Archives]     [Newbies FAQ]     [Linux Kernel Mentors]     [Linux Kernel Development]     [IETF Annouce]     [Git]     [Networking]     [Security]     [Bugtraq]     [Yosemite]     [MIPS Linux]     [ARM Linux]     [Linux RAID]     [Linux SCSI]     [Linux ACPI]
  Powered by Linux