OpenCores
URL https://opencores.org/ocsvn/or1k/or1k/trunk

Subversion Repositories or1k

[/] [or1k/] [trunk/] [linux/] [linux-2.4/] [net/] [core/] [rtnetlink.c] - Diff between revs 1278 and 1765

Only display areas with differences | Details | Blame | View Log

Rev 1278 Rev 1765
/*
/*
 * INET         An implementation of the TCP/IP protocol suite for the LINUX
 * INET         An implementation of the TCP/IP protocol suite for the LINUX
 *              operating system.  INET is implemented using the  BSD Socket
 *              operating system.  INET is implemented using the  BSD Socket
 *              interface as the means of communication with the user level.
 *              interface as the means of communication with the user level.
 *
 *
 *              Routing netlink socket interface: protocol independent part.
 *              Routing netlink socket interface: protocol independent part.
 *
 *
 * Authors:     Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru>
 * Authors:     Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru>
 *
 *
 *              This program is free software; you can redistribute it and/or
 *              This program is free software; you can redistribute it and/or
 *              modify it under the terms of the GNU General Public License
 *              modify it under the terms of the GNU General Public License
 *              as published by the Free Software Foundation; either version
 *              as published by the Free Software Foundation; either version
 *              2 of the License, or (at your option) any later version.
 *              2 of the License, or (at your option) any later version.
 *
 *
 *      Fixes:
 *      Fixes:
 *      Vitaly E. Lavrov                RTA_OK arithmetics was wrong.
 *      Vitaly E. Lavrov                RTA_OK arithmetics was wrong.
 */
 */
 
 
#include <linux/config.h>
#include <linux/config.h>
#include <linux/errno.h>
#include <linux/errno.h>
#include <linux/types.h>
#include <linux/types.h>
#include <linux/socket.h>
#include <linux/socket.h>
#include <linux/kernel.h>
#include <linux/kernel.h>
#include <linux/major.h>
#include <linux/major.h>
#include <linux/sched.h>
#include <linux/sched.h>
#include <linux/timer.h>
#include <linux/timer.h>
#include <linux/string.h>
#include <linux/string.h>
#include <linux/sockios.h>
#include <linux/sockios.h>
#include <linux/net.h>
#include <linux/net.h>
#include <linux/fcntl.h>
#include <linux/fcntl.h>
#include <linux/mm.h>
#include <linux/mm.h>
#include <linux/slab.h>
#include <linux/slab.h>
#include <linux/interrupt.h>
#include <linux/interrupt.h>
#include <linux/capability.h>
#include <linux/capability.h>
#include <linux/skbuff.h>
#include <linux/skbuff.h>
#include <linux/init.h>
#include <linux/init.h>
 
 
#include <asm/uaccess.h>
#include <asm/uaccess.h>
#include <asm/system.h>
#include <asm/system.h>
#include <asm/string.h>
#include <asm/string.h>
 
 
#include <linux/inet.h>
#include <linux/inet.h>
#include <linux/netdevice.h>
#include <linux/netdevice.h>
#include <net/ip.h>
#include <net/ip.h>
#include <net/protocol.h>
#include <net/protocol.h>
#include <net/arp.h>
#include <net/arp.h>
#include <net/route.h>
#include <net/route.h>
#include <net/udp.h>
#include <net/udp.h>
#include <net/sock.h>
#include <net/sock.h>
#include <net/pkt_sched.h>
#include <net/pkt_sched.h>
 
 
DECLARE_MUTEX(rtnl_sem);
DECLARE_MUTEX(rtnl_sem);
 
 
void rtnl_lock(void)
void rtnl_lock(void)
{
{
        rtnl_shlock();
        rtnl_shlock();
        rtnl_exlock();
        rtnl_exlock();
}
}
 
 
void rtnl_unlock(void)
void rtnl_unlock(void)
{
{
        rtnl_exunlock();
        rtnl_exunlock();
        rtnl_shunlock();
        rtnl_shunlock();
}
}
 
 
int rtattr_parse(struct rtattr *tb[], int maxattr, struct rtattr *rta, int len)
int rtattr_parse(struct rtattr *tb[], int maxattr, struct rtattr *rta, int len)
{
{
        memset(tb, 0, sizeof(struct rtattr*)*maxattr);
        memset(tb, 0, sizeof(struct rtattr*)*maxattr);
 
 
        while (RTA_OK(rta, len)) {
        while (RTA_OK(rta, len)) {
                unsigned flavor = rta->rta_type;
                unsigned flavor = rta->rta_type;
                if (flavor && flavor <= maxattr)
                if (flavor && flavor <= maxattr)
                        tb[flavor-1] = rta;
                        tb[flavor-1] = rta;
                rta = RTA_NEXT(rta, len);
                rta = RTA_NEXT(rta, len);
        }
        }
        return 0;
        return 0;
}
}
 
 
struct sock *rtnl;
struct sock *rtnl;
 
 
struct rtnetlink_link * rtnetlink_links[NPROTO];
struct rtnetlink_link * rtnetlink_links[NPROTO];
 
 
static const int rtm_min[(RTM_MAX+1-RTM_BASE)/4] =
static const int rtm_min[(RTM_MAX+1-RTM_BASE)/4] =
{
{
        NLMSG_LENGTH(sizeof(struct ifinfomsg)),
        NLMSG_LENGTH(sizeof(struct ifinfomsg)),
        NLMSG_LENGTH(sizeof(struct ifaddrmsg)),
        NLMSG_LENGTH(sizeof(struct ifaddrmsg)),
        NLMSG_LENGTH(sizeof(struct rtmsg)),
        NLMSG_LENGTH(sizeof(struct rtmsg)),
        NLMSG_LENGTH(sizeof(struct ndmsg)),
        NLMSG_LENGTH(sizeof(struct ndmsg)),
        NLMSG_LENGTH(sizeof(struct rtmsg)),
        NLMSG_LENGTH(sizeof(struct rtmsg)),
        NLMSG_LENGTH(sizeof(struct tcmsg)),
        NLMSG_LENGTH(sizeof(struct tcmsg)),
        NLMSG_LENGTH(sizeof(struct tcmsg)),
        NLMSG_LENGTH(sizeof(struct tcmsg)),
        NLMSG_LENGTH(sizeof(struct tcmsg))
        NLMSG_LENGTH(sizeof(struct tcmsg))
};
};
 
 
static const int rta_max[(RTM_MAX+1-RTM_BASE)/4] =
static const int rta_max[(RTM_MAX+1-RTM_BASE)/4] =
{
{
        IFLA_MAX,
        IFLA_MAX,
        IFA_MAX,
        IFA_MAX,
        RTA_MAX,
        RTA_MAX,
        NDA_MAX,
        NDA_MAX,
        RTA_MAX,
        RTA_MAX,
        TCA_MAX,
        TCA_MAX,
        TCA_MAX,
        TCA_MAX,
        TCA_MAX
        TCA_MAX
};
};
 
 
void __rta_fill(struct sk_buff *skb, int attrtype, int attrlen, const void *data)
void __rta_fill(struct sk_buff *skb, int attrtype, int attrlen, const void *data)
{
{
        struct rtattr *rta;
        struct rtattr *rta;
        int size = RTA_LENGTH(attrlen);
        int size = RTA_LENGTH(attrlen);
 
 
        rta = (struct rtattr*)skb_put(skb, RTA_ALIGN(size));
        rta = (struct rtattr*)skb_put(skb, RTA_ALIGN(size));
        rta->rta_type = attrtype;
        rta->rta_type = attrtype;
        rta->rta_len = size;
        rta->rta_len = size;
        memcpy(RTA_DATA(rta), data, attrlen);
        memcpy(RTA_DATA(rta), data, attrlen);
}
}
 
 
int rtnetlink_send(struct sk_buff *skb, u32 pid, unsigned group, int echo)
int rtnetlink_send(struct sk_buff *skb, u32 pid, unsigned group, int echo)
{
{
        int err = 0;
        int err = 0;
 
 
        NETLINK_CB(skb).dst_groups = group;
        NETLINK_CB(skb).dst_groups = group;
        if (echo)
        if (echo)
                atomic_inc(&skb->users);
                atomic_inc(&skb->users);
        netlink_broadcast(rtnl, skb, pid, group, GFP_KERNEL);
        netlink_broadcast(rtnl, skb, pid, group, GFP_KERNEL);
        if (echo)
        if (echo)
                err = netlink_unicast(rtnl, skb, pid, MSG_DONTWAIT);
                err = netlink_unicast(rtnl, skb, pid, MSG_DONTWAIT);
        return err;
        return err;
}
}
 
 
int rtnetlink_put_metrics(struct sk_buff *skb, unsigned *metrics)
int rtnetlink_put_metrics(struct sk_buff *skb, unsigned *metrics)
{
{
        struct rtattr *mx = (struct rtattr*)skb->tail;
        struct rtattr *mx = (struct rtattr*)skb->tail;
        int i;
        int i;
 
 
        RTA_PUT(skb, RTA_METRICS, 0, NULL);
        RTA_PUT(skb, RTA_METRICS, 0, NULL);
        for (i=0; i<RTAX_MAX; i++) {
        for (i=0; i<RTAX_MAX; i++) {
                if (metrics[i])
                if (metrics[i])
                        RTA_PUT(skb, i+1, sizeof(unsigned), metrics+i);
                        RTA_PUT(skb, i+1, sizeof(unsigned), metrics+i);
        }
        }
        mx->rta_len = skb->tail - (u8*)mx;
        mx->rta_len = skb->tail - (u8*)mx;
        if (mx->rta_len == RTA_LENGTH(0))
        if (mx->rta_len == RTA_LENGTH(0))
                skb_trim(skb, (u8*)mx - skb->data);
                skb_trim(skb, (u8*)mx - skb->data);
        return 0;
        return 0;
 
 
rtattr_failure:
rtattr_failure:
        skb_trim(skb, (u8*)mx - skb->data);
        skb_trim(skb, (u8*)mx - skb->data);
        return -1;
        return -1;
}
}
 
 
 
 
static int rtnetlink_fill_ifinfo(struct sk_buff *skb, struct net_device *dev,
static int rtnetlink_fill_ifinfo(struct sk_buff *skb, struct net_device *dev,
                                 int type, u32 pid, u32 seq, u32 change)
                                 int type, u32 pid, u32 seq, u32 change)
{
{
        struct ifinfomsg *r;
        struct ifinfomsg *r;
        struct nlmsghdr  *nlh;
        struct nlmsghdr  *nlh;
        unsigned char    *b = skb->tail;
        unsigned char    *b = skb->tail;
 
 
        nlh = NLMSG_PUT(skb, pid, seq, type, sizeof(*r));
        nlh = NLMSG_PUT(skb, pid, seq, type, sizeof(*r));
        if (pid) nlh->nlmsg_flags |= NLM_F_MULTI;
        if (pid) nlh->nlmsg_flags |= NLM_F_MULTI;
        r = NLMSG_DATA(nlh);
        r = NLMSG_DATA(nlh);
        r->ifi_family = AF_UNSPEC;
        r->ifi_family = AF_UNSPEC;
        r->ifi_type = dev->type;
        r->ifi_type = dev->type;
        r->ifi_index = dev->ifindex;
        r->ifi_index = dev->ifindex;
        r->ifi_flags = dev->flags;
        r->ifi_flags = dev->flags;
        r->ifi_change = change;
        r->ifi_change = change;
 
 
        if (!netif_running(dev) || !netif_carrier_ok(dev))
        if (!netif_running(dev) || !netif_carrier_ok(dev))
                r->ifi_flags &= ~IFF_RUNNING;
                r->ifi_flags &= ~IFF_RUNNING;
        else
        else
                r->ifi_flags |= IFF_RUNNING;
                r->ifi_flags |= IFF_RUNNING;
 
 
        RTA_PUT(skb, IFLA_IFNAME, strlen(dev->name)+1, dev->name);
        RTA_PUT(skb, IFLA_IFNAME, strlen(dev->name)+1, dev->name);
        if (dev->addr_len) {
        if (dev->addr_len) {
                RTA_PUT(skb, IFLA_ADDRESS, dev->addr_len, dev->dev_addr);
                RTA_PUT(skb, IFLA_ADDRESS, dev->addr_len, dev->dev_addr);
                RTA_PUT(skb, IFLA_BROADCAST, dev->addr_len, dev->broadcast);
                RTA_PUT(skb, IFLA_BROADCAST, dev->addr_len, dev->broadcast);
        }
        }
        if (1) {
        if (1) {
                unsigned mtu = dev->mtu;
                unsigned mtu = dev->mtu;
                RTA_PUT(skb, IFLA_MTU, sizeof(mtu), &mtu);
                RTA_PUT(skb, IFLA_MTU, sizeof(mtu), &mtu);
        }
        }
        if (dev->ifindex != dev->iflink)
        if (dev->ifindex != dev->iflink)
                RTA_PUT(skb, IFLA_LINK, sizeof(int), &dev->iflink);
                RTA_PUT(skb, IFLA_LINK, sizeof(int), &dev->iflink);
        if (dev->qdisc_sleeping)
        if (dev->qdisc_sleeping)
                RTA_PUT(skb, IFLA_QDISC,
                RTA_PUT(skb, IFLA_QDISC,
                        strlen(dev->qdisc_sleeping->ops->id) + 1,
                        strlen(dev->qdisc_sleeping->ops->id) + 1,
                        dev->qdisc_sleeping->ops->id);
                        dev->qdisc_sleeping->ops->id);
        if (dev->master)
        if (dev->master)
                RTA_PUT(skb, IFLA_MASTER, sizeof(int), &dev->master->ifindex);
                RTA_PUT(skb, IFLA_MASTER, sizeof(int), &dev->master->ifindex);
        if (dev->get_stats) {
        if (dev->get_stats) {
                struct net_device_stats *stats = dev->get_stats(dev);
                struct net_device_stats *stats = dev->get_stats(dev);
                if (stats)
                if (stats)
                        RTA_PUT(skb, IFLA_STATS, sizeof(*stats), stats);
                        RTA_PUT(skb, IFLA_STATS, sizeof(*stats), stats);
        }
        }
        nlh->nlmsg_len = skb->tail - b;
        nlh->nlmsg_len = skb->tail - b;
        return skb->len;
        return skb->len;
 
 
nlmsg_failure:
nlmsg_failure:
rtattr_failure:
rtattr_failure:
        skb_trim(skb, b - skb->data);
        skb_trim(skb, b - skb->data);
        return -1;
        return -1;
}
}
 
 
int rtnetlink_dump_ifinfo(struct sk_buff *skb, struct netlink_callback *cb)
int rtnetlink_dump_ifinfo(struct sk_buff *skb, struct netlink_callback *cb)
{
{
        int idx;
        int idx;
        int s_idx = cb->args[0];
        int s_idx = cb->args[0];
        struct net_device *dev;
        struct net_device *dev;
 
 
        read_lock(&dev_base_lock);
        read_lock(&dev_base_lock);
        for (dev=dev_base, idx=0; dev; dev = dev->next, idx++) {
        for (dev=dev_base, idx=0; dev; dev = dev->next, idx++) {
                if (idx < s_idx)
                if (idx < s_idx)
                        continue;
                        continue;
                if (rtnetlink_fill_ifinfo(skb, dev, RTM_NEWLINK, NETLINK_CB(cb->skb).pid, cb->nlh->nlmsg_seq, 0) <= 0)
                if (rtnetlink_fill_ifinfo(skb, dev, RTM_NEWLINK, NETLINK_CB(cb->skb).pid, cb->nlh->nlmsg_seq, 0) <= 0)
                        break;
                        break;
        }
        }
        read_unlock(&dev_base_lock);
        read_unlock(&dev_base_lock);
        cb->args[0] = idx;
        cb->args[0] = idx;
 
 
        return skb->len;
        return skb->len;
}
}
 
 
int rtnetlink_dump_all(struct sk_buff *skb, struct netlink_callback *cb)
int rtnetlink_dump_all(struct sk_buff *skb, struct netlink_callback *cb)
{
{
        int idx;
        int idx;
        int s_idx = cb->family;
        int s_idx = cb->family;
 
 
        if (s_idx == 0)
        if (s_idx == 0)
                s_idx = 1;
                s_idx = 1;
        for (idx=1; idx<NPROTO; idx++) {
        for (idx=1; idx<NPROTO; idx++) {
                int type = cb->nlh->nlmsg_type-RTM_BASE;
                int type = cb->nlh->nlmsg_type-RTM_BASE;
                if (idx < s_idx || idx == PF_PACKET)
                if (idx < s_idx || idx == PF_PACKET)
                        continue;
                        continue;
                if (rtnetlink_links[idx] == NULL ||
                if (rtnetlink_links[idx] == NULL ||
                    rtnetlink_links[idx][type].dumpit == NULL)
                    rtnetlink_links[idx][type].dumpit == NULL)
                        continue;
                        continue;
                if (idx > s_idx)
                if (idx > s_idx)
                        memset(&cb->args[0], 0, sizeof(cb->args));
                        memset(&cb->args[0], 0, sizeof(cb->args));
                if (rtnetlink_links[idx][type].dumpit(skb, cb))
                if (rtnetlink_links[idx][type].dumpit(skb, cb))
                        break;
                        break;
        }
        }
        cb->family = idx;
        cb->family = idx;
 
 
        return skb->len;
        return skb->len;
}
}
 
 
void rtmsg_ifinfo(int type, struct net_device *dev, unsigned change)
void rtmsg_ifinfo(int type, struct net_device *dev, unsigned change)
{
{
        struct sk_buff *skb;
        struct sk_buff *skb;
        int size = NLMSG_GOODSIZE;
        int size = NLMSG_GOODSIZE;
 
 
        skb = alloc_skb(size, GFP_KERNEL);
        skb = alloc_skb(size, GFP_KERNEL);
        if (!skb)
        if (!skb)
                return;
                return;
 
 
        if (rtnetlink_fill_ifinfo(skb, dev, type, 0, 0, change) < 0) {
        if (rtnetlink_fill_ifinfo(skb, dev, type, 0, 0, change) < 0) {
                kfree_skb(skb);
                kfree_skb(skb);
                return;
                return;
        }
        }
        NETLINK_CB(skb).dst_groups = RTMGRP_LINK;
        NETLINK_CB(skb).dst_groups = RTMGRP_LINK;
        netlink_broadcast(rtnl, skb, 0, RTMGRP_LINK, GFP_KERNEL);
        netlink_broadcast(rtnl, skb, 0, RTMGRP_LINK, GFP_KERNEL);
}
}
 
 
static int rtnetlink_done(struct netlink_callback *cb)
static int rtnetlink_done(struct netlink_callback *cb)
{
{
        return 0;
        return 0;
}
}
 
 
/* Process one rtnetlink message. */
/* Process one rtnetlink message. */
 
 
static __inline__ int
static __inline__ int
rtnetlink_rcv_msg(struct sk_buff *skb, struct nlmsghdr *nlh, int *errp)
rtnetlink_rcv_msg(struct sk_buff *skb, struct nlmsghdr *nlh, int *errp)
{
{
        struct rtnetlink_link *link;
        struct rtnetlink_link *link;
        struct rtnetlink_link *link_tab;
        struct rtnetlink_link *link_tab;
        struct rtattr   *rta[RTATTR_MAX];
        struct rtattr   *rta[RTATTR_MAX];
 
 
        int exclusive = 0;
        int exclusive = 0;
        int sz_idx, kind;
        int sz_idx, kind;
        int min_len;
        int min_len;
        int family;
        int family;
        int type;
        int type;
        int err;
        int err;
 
 
        /* Only requests are handled by kernel now */
        /* Only requests are handled by kernel now */
        if (!(nlh->nlmsg_flags&NLM_F_REQUEST))
        if (!(nlh->nlmsg_flags&NLM_F_REQUEST))
                return 0;
                return 0;
 
 
        type = nlh->nlmsg_type;
        type = nlh->nlmsg_type;
 
 
        /* A control message: ignore them */
        /* A control message: ignore them */
        if (type < RTM_BASE)
        if (type < RTM_BASE)
                return 0;
                return 0;
 
 
        /* Unknown message: reply with EINVAL */
        /* Unknown message: reply with EINVAL */
        if (type > RTM_MAX)
        if (type > RTM_MAX)
                goto err_inval;
                goto err_inval;
 
 
        type -= RTM_BASE;
        type -= RTM_BASE;
 
 
        /* All the messages must have at least 1 byte length */
        /* All the messages must have at least 1 byte length */
        if (nlh->nlmsg_len < NLMSG_LENGTH(sizeof(struct rtgenmsg)))
        if (nlh->nlmsg_len < NLMSG_LENGTH(sizeof(struct rtgenmsg)))
                return 0;
                return 0;
 
 
        family = ((struct rtgenmsg*)NLMSG_DATA(nlh))->rtgen_family;
        family = ((struct rtgenmsg*)NLMSG_DATA(nlh))->rtgen_family;
        if (family > NPROTO) {
        if (family > NPROTO) {
                *errp = -EAFNOSUPPORT;
                *errp = -EAFNOSUPPORT;
                return -1;
                return -1;
        }
        }
 
 
        link_tab = rtnetlink_links[family];
        link_tab = rtnetlink_links[family];
        if (link_tab == NULL)
        if (link_tab == NULL)
                link_tab = rtnetlink_links[PF_UNSPEC];
                link_tab = rtnetlink_links[PF_UNSPEC];
        link = &link_tab[type];
        link = &link_tab[type];
 
 
        sz_idx = type>>2;
        sz_idx = type>>2;
        kind = type&3;
        kind = type&3;
 
 
        if (kind != 2 && !cap_raised(NETLINK_CB(skb).eff_cap, CAP_NET_ADMIN)) {
        if (kind != 2 && !cap_raised(NETLINK_CB(skb).eff_cap, CAP_NET_ADMIN)) {
                *errp = -EPERM;
                *errp = -EPERM;
                return -1;
                return -1;
        }
        }
 
 
        if (kind == 2 && nlh->nlmsg_flags&NLM_F_DUMP) {
        if (kind == 2 && nlh->nlmsg_flags&NLM_F_DUMP) {
                u32 rlen;
                u32 rlen;
 
 
                if (link->dumpit == NULL)
                if (link->dumpit == NULL)
                        link = &(rtnetlink_links[PF_UNSPEC][type]);
                        link = &(rtnetlink_links[PF_UNSPEC][type]);
 
 
                if (link->dumpit == NULL)
                if (link->dumpit == NULL)
                        goto err_inval;
                        goto err_inval;
 
 
                if ((*errp = netlink_dump_start(rtnl, skb, nlh,
                if ((*errp = netlink_dump_start(rtnl, skb, nlh,
                                                link->dumpit,
                                                link->dumpit,
                                                rtnetlink_done)) != 0) {
                                                rtnetlink_done)) != 0) {
                        return -1;
                        return -1;
                }
                }
                rlen = NLMSG_ALIGN(nlh->nlmsg_len);
                rlen = NLMSG_ALIGN(nlh->nlmsg_len);
                if (rlen > skb->len)
                if (rlen > skb->len)
                        rlen = skb->len;
                        rlen = skb->len;
                skb_pull(skb, rlen);
                skb_pull(skb, rlen);
                return -1;
                return -1;
        }
        }
 
 
        if (kind != 2) {
        if (kind != 2) {
                if (rtnl_exlock_nowait()) {
                if (rtnl_exlock_nowait()) {
                        *errp = 0;
                        *errp = 0;
                        return -1;
                        return -1;
                }
                }
                exclusive = 1;
                exclusive = 1;
        }
        }
 
 
        memset(&rta, 0, sizeof(rta));
        memset(&rta, 0, sizeof(rta));
 
 
        min_len = rtm_min[sz_idx];
        min_len = rtm_min[sz_idx];
        if (nlh->nlmsg_len < min_len)
        if (nlh->nlmsg_len < min_len)
                goto err_inval;
                goto err_inval;
 
 
        if (nlh->nlmsg_len > min_len) {
        if (nlh->nlmsg_len > min_len) {
                int attrlen = nlh->nlmsg_len - NLMSG_ALIGN(min_len);
                int attrlen = nlh->nlmsg_len - NLMSG_ALIGN(min_len);
                struct rtattr *attr = (void*)nlh + NLMSG_ALIGN(min_len);
                struct rtattr *attr = (void*)nlh + NLMSG_ALIGN(min_len);
 
 
                while (RTA_OK(attr, attrlen)) {
                while (RTA_OK(attr, attrlen)) {
                        unsigned flavor = attr->rta_type;
                        unsigned flavor = attr->rta_type;
                        if (flavor) {
                        if (flavor) {
                                if (flavor > rta_max[sz_idx])
                                if (flavor > rta_max[sz_idx])
                                        goto err_inval;
                                        goto err_inval;
                                rta[flavor-1] = attr;
                                rta[flavor-1] = attr;
                        }
                        }
                        attr = RTA_NEXT(attr, attrlen);
                        attr = RTA_NEXT(attr, attrlen);
                }
                }
        }
        }
 
 
        if (link->doit == NULL)
        if (link->doit == NULL)
                link = &(rtnetlink_links[PF_UNSPEC][type]);
                link = &(rtnetlink_links[PF_UNSPEC][type]);
        if (link->doit == NULL)
        if (link->doit == NULL)
                goto err_inval;
                goto err_inval;
        err = link->doit(skb, nlh, (void *)&rta);
        err = link->doit(skb, nlh, (void *)&rta);
 
 
        if (exclusive)
        if (exclusive)
                rtnl_exunlock();
                rtnl_exunlock();
        *errp = err;
        *errp = err;
        return err;
        return err;
 
 
err_inval:
err_inval:
        if (exclusive)
        if (exclusive)
                rtnl_exunlock();
                rtnl_exunlock();
        *errp = -EINVAL;
        *errp = -EINVAL;
        return -1;
        return -1;
}
}
 
 
/*
/*
 * Process one packet of messages.
 * Process one packet of messages.
 * Malformed skbs with wrong lengths of messages are discarded silently.
 * Malformed skbs with wrong lengths of messages are discarded silently.
 */
 */
 
 
static inline int rtnetlink_rcv_skb(struct sk_buff *skb)
static inline int rtnetlink_rcv_skb(struct sk_buff *skb)
{
{
        int err;
        int err;
        struct nlmsghdr * nlh;
        struct nlmsghdr * nlh;
 
 
        while (skb->len >= NLMSG_SPACE(0)) {
        while (skb->len >= NLMSG_SPACE(0)) {
                u32 rlen;
                u32 rlen;
 
 
                nlh = (struct nlmsghdr *)skb->data;
                nlh = (struct nlmsghdr *)skb->data;
                if (nlh->nlmsg_len < sizeof(*nlh) || skb->len < nlh->nlmsg_len)
                if (nlh->nlmsg_len < sizeof(*nlh) || skb->len < nlh->nlmsg_len)
                        return 0;
                        return 0;
                rlen = NLMSG_ALIGN(nlh->nlmsg_len);
                rlen = NLMSG_ALIGN(nlh->nlmsg_len);
                if (rlen > skb->len)
                if (rlen > skb->len)
                        rlen = skb->len;
                        rlen = skb->len;
                if (rtnetlink_rcv_msg(skb, nlh, &err)) {
                if (rtnetlink_rcv_msg(skb, nlh, &err)) {
                        /* Not error, but we must interrupt processing here:
                        /* Not error, but we must interrupt processing here:
                         *   Note, that in this case we do not pull message
                         *   Note, that in this case we do not pull message
                         *   from skb, it will be processed later.
                         *   from skb, it will be processed later.
                         */
                         */
                        if (err == 0)
                        if (err == 0)
                                return -1;
                                return -1;
                        netlink_ack(skb, nlh, err);
                        netlink_ack(skb, nlh, err);
                } else if (nlh->nlmsg_flags&NLM_F_ACK)
                } else if (nlh->nlmsg_flags&NLM_F_ACK)
                        netlink_ack(skb, nlh, 0);
                        netlink_ack(skb, nlh, 0);
                skb_pull(skb, rlen);
                skb_pull(skb, rlen);
        }
        }
 
 
        return 0;
        return 0;
}
}
 
 
/*
/*
 *  rtnetlink input queue processing routine:
 *  rtnetlink input queue processing routine:
 *      - try to acquire shared lock. If it is failed, defer processing.
 *      - try to acquire shared lock. If it is failed, defer processing.
 *      - feed skbs to rtnetlink_rcv_skb, until it refuse a message,
 *      - feed skbs to rtnetlink_rcv_skb, until it refuse a message,
 *        that will occur, when a dump started and/or acquisition of
 *        that will occur, when a dump started and/or acquisition of
 *        exclusive lock failed.
 *        exclusive lock failed.
 */
 */
 
 
static void rtnetlink_rcv(struct sock *sk, int len)
static void rtnetlink_rcv(struct sock *sk, int len)
{
{
        do {
        do {
                struct sk_buff *skb;
                struct sk_buff *skb;
 
 
                if (rtnl_shlock_nowait())
                if (rtnl_shlock_nowait())
                        return;
                        return;
 
 
                while ((skb = skb_dequeue(&sk->receive_queue)) != NULL) {
                while ((skb = skb_dequeue(&sk->receive_queue)) != NULL) {
                        if (rtnetlink_rcv_skb(skb)) {
                        if (rtnetlink_rcv_skb(skb)) {
                                if (skb->len)
                                if (skb->len)
                                        skb_queue_head(&sk->receive_queue, skb);
                                        skb_queue_head(&sk->receive_queue, skb);
                                else
                                else
                                        kfree_skb(skb);
                                        kfree_skb(skb);
                                break;
                                break;
                        }
                        }
                        kfree_skb(skb);
                        kfree_skb(skb);
                }
                }
 
 
                up(&rtnl_sem);
                up(&rtnl_sem);
        } while (rtnl && rtnl->receive_queue.qlen);
        } while (rtnl && rtnl->receive_queue.qlen);
}
}
 
 
static struct rtnetlink_link link_rtnetlink_table[RTM_MAX-RTM_BASE+1] =
static struct rtnetlink_link link_rtnetlink_table[RTM_MAX-RTM_BASE+1] =
{
{
        { NULL,                 NULL,                   },
        { NULL,                 NULL,                   },
        { NULL,                 NULL,                   },
        { NULL,                 NULL,                   },
        { NULL,                 rtnetlink_dump_ifinfo,  },
        { NULL,                 rtnetlink_dump_ifinfo,  },
        { NULL,                 NULL,                   },
        { NULL,                 NULL,                   },
 
 
        { NULL,                 NULL,                   },
        { NULL,                 NULL,                   },
        { NULL,                 NULL,                   },
        { NULL,                 NULL,                   },
        { NULL,                 rtnetlink_dump_all,     },
        { NULL,                 rtnetlink_dump_all,     },
        { NULL,                 NULL,                   },
        { NULL,                 NULL,                   },
 
 
        { NULL,                 NULL,                   },
        { NULL,                 NULL,                   },
        { NULL,                 NULL,                   },
        { NULL,                 NULL,                   },
        { NULL,                 rtnetlink_dump_all,     },
        { NULL,                 rtnetlink_dump_all,     },
        { NULL,                 NULL,                   },
        { NULL,                 NULL,                   },
 
 
        { neigh_add,            NULL,                   },
        { neigh_add,            NULL,                   },
        { neigh_delete,         NULL,                   },
        { neigh_delete,         NULL,                   },
        { NULL,                 neigh_dump_info,        },
        { NULL,                 neigh_dump_info,        },
        { NULL,                 NULL,                   },
        { NULL,                 NULL,                   },
 
 
        { NULL,                 NULL,                   },
        { NULL,                 NULL,                   },
        { NULL,                 NULL,                   },
        { NULL,                 NULL,                   },
        { NULL,                 NULL,                   },
        { NULL,                 NULL,                   },
        { NULL,                 NULL,                   },
        { NULL,                 NULL,                   },
};
};
 
 
 
 
static int rtnetlink_event(struct notifier_block *this, unsigned long event, void *ptr)
static int rtnetlink_event(struct notifier_block *this, unsigned long event, void *ptr)
{
{
        struct net_device *dev = ptr;
        struct net_device *dev = ptr;
        switch (event) {
        switch (event) {
        case NETDEV_UNREGISTER:
        case NETDEV_UNREGISTER:
                rtmsg_ifinfo(RTM_DELLINK, dev, ~0U);
                rtmsg_ifinfo(RTM_DELLINK, dev, ~0U);
                break;
                break;
        case NETDEV_REGISTER:
        case NETDEV_REGISTER:
                rtmsg_ifinfo(RTM_NEWLINK, dev, ~0U);
                rtmsg_ifinfo(RTM_NEWLINK, dev, ~0U);
                break;
                break;
        case NETDEV_UP:
        case NETDEV_UP:
        case NETDEV_DOWN:
        case NETDEV_DOWN:
                rtmsg_ifinfo(RTM_NEWLINK, dev, IFF_UP|IFF_RUNNING);
                rtmsg_ifinfo(RTM_NEWLINK, dev, IFF_UP|IFF_RUNNING);
                break;
                break;
        case NETDEV_CHANGE:
        case NETDEV_CHANGE:
        case NETDEV_GOING_DOWN:
        case NETDEV_GOING_DOWN:
                break;
                break;
        default:
        default:
                rtmsg_ifinfo(RTM_NEWLINK, dev, 0);
                rtmsg_ifinfo(RTM_NEWLINK, dev, 0);
                break;
                break;
        }
        }
        return NOTIFY_DONE;
        return NOTIFY_DONE;
}
}
 
 
struct notifier_block rtnetlink_dev_notifier = {
struct notifier_block rtnetlink_dev_notifier = {
        rtnetlink_event,
        rtnetlink_event,
        NULL,
        NULL,
        0
        0
};
};
 
 
 
 
void __init rtnetlink_init(void)
void __init rtnetlink_init(void)
{
{
#ifdef RTNL_DEBUG
#ifdef RTNL_DEBUG
        printk("Initializing RT netlink socket\n");
        printk("Initializing RT netlink socket\n");
#endif
#endif
        rtnl = netlink_kernel_create(NETLINK_ROUTE, rtnetlink_rcv);
        rtnl = netlink_kernel_create(NETLINK_ROUTE, rtnetlink_rcv);
        if (rtnl == NULL)
        if (rtnl == NULL)
                panic("rtnetlink_init: cannot initialize rtnetlink\n");
                panic("rtnetlink_init: cannot initialize rtnetlink\n");
        netlink_set_nonroot(NETLINK_ROUTE, NL_NONROOT_RECV);
        netlink_set_nonroot(NETLINK_ROUTE, NL_NONROOT_RECV);
        register_netdevice_notifier(&rtnetlink_dev_notifier);
        register_netdevice_notifier(&rtnetlink_dev_notifier);
        rtnetlink_links[PF_UNSPEC] = link_rtnetlink_table;
        rtnetlink_links[PF_UNSPEC] = link_rtnetlink_table;
        rtnetlink_links[PF_PACKET] = link_rtnetlink_table;
        rtnetlink_links[PF_PACKET] = link_rtnetlink_table;
}
}
 
 

powered by: WebSVN 2.1.0

© copyright 1999-2024 OpenCores.org, equivalent to Oliscience, all rights reserved. OpenCores®, registered trademark.