X-Git-Url: http://git.onelab.eu/?a=blobdiff_plain;f=net%2Fdecnet%2Fdn_dev.c;h=ed083ab455b75f600ec5da51716ace4db8a04905;hb=refs%2Fheads%2Fvserver;hp=476455fbdb03058a67c0a6223738693df2fed373;hpb=16c70f8c1b54b61c3b951b6fb220df250fe09b32;p=linux-2.6.git diff --git a/net/decnet/dn_dev.c b/net/decnet/dn_dev.c index 476455fbd..ed083ab45 100644 --- a/net/decnet/dn_dev.c +++ b/net/decnet/dn_dev.c @@ -34,10 +34,10 @@ #include #include #include +#include #include #include #include -#include #include #include #include @@ -45,6 +45,8 @@ #include #include #include +#include +#include #include #include #include @@ -71,7 +73,7 @@ static BLOCKING_NOTIFIER_HEAD(dnaddr_chain); static struct dn_dev *dn_dev_create(struct net_device *dev, int *err); static void dn_dev_delete(struct net_device *dev); -static void rtmsg_ifa(int event, struct dn_ifaddr *ifa); +static void dn_ifaddr_notify(int event, struct dn_ifaddr *ifa); static int dn_eth_up(struct net_device *); static void dn_eth_down(struct net_device *); @@ -165,8 +167,7 @@ static int dn_forwarding_proc(ctl_table *, int, struct file *, void __user *, size_t *, loff_t *); static int dn_forwarding_sysctl(ctl_table *table, int __user *name, int nlen, void __user *oldval, size_t __user *oldlenp, - void __user *newval, size_t newlen, - void **context); + void __user *newval, size_t newlen); static struct dn_dev_sysctl_table { struct ctl_table_header *sysctl_header; @@ -253,12 +254,10 @@ static void dn_dev_sysctl_register(struct net_device *dev, struct dn_dev_parms * struct dn_dev_sysctl_table *t; int i; - t = kmalloc(sizeof(*t), GFP_KERNEL); + t = kmemdup(&dn_dev_sysctl, sizeof(*t), GFP_KERNEL); if (t == NULL) return; - memcpy(t, &dn_dev_sysctl, sizeof(*t)); - for(i = 0; i < ARRAY_SIZE(t->dn_dev_vars) - 1; i++) { long offset = (long)t->dn_dev_vars[i].data; t->dn_dev_vars[i].data = ((char *)parms) + offset; @@ -347,8 +346,7 @@ static int dn_forwarding_proc(ctl_table *table, int write, static int dn_forwarding_sysctl(ctl_table *table, int __user *name, int nlen, void __user *oldval, size_t __user *oldlenp, - void __user *newval, size_t newlen, - void **context) + void __user *newval, size_t newlen) { #ifdef CONFIG_DECNET_ROUTER struct net_device *dev = table->extra1; @@ -440,7 +438,7 @@ static void dn_dev_del_ifa(struct dn_dev *dn_db, struct dn_ifaddr **ifap, int de } } - rtmsg_ifa(RTM_DELADDR, ifa1); + dn_ifaddr_notify(RTM_DELADDR, ifa1); blocking_notifier_call_chain(&dnaddr_chain, NETDEV_DOWN, ifa1); if (destroy) { dn_dev_free_ifa(ifa1); @@ -475,7 +473,7 @@ static int dn_dev_insert_ifa(struct dn_dev *dn_db, struct dn_ifaddr *ifa) ifa->ifa_next = dn_db->ifa_list; dn_db->ifa_list = ifa; - rtmsg_ifa(RTM_NEWADDR, ifa); + dn_ifaddr_notify(RTM_NEWADDR, ifa); blocking_notifier_call_chain(&dnaddr_chain, NETDEV_UP, ifa); return 0; @@ -645,41 +643,62 @@ static struct dn_dev *dn_dev_by_index(int ifindex) return dn_dev; } -static int dn_dev_rtm_deladdr(struct sk_buff *skb, struct nlmsghdr *nlh, void *arg) +static struct nla_policy dn_ifa_policy[IFA_MAX+1] __read_mostly = { + [IFA_ADDRESS] = { .type = NLA_U16 }, + [IFA_LOCAL] = { .type = NLA_U16 }, + [IFA_LABEL] = { .type = NLA_STRING, + .len = IFNAMSIZ - 1 }, +}; + +static int dn_nl_deladdr(struct sk_buff *skb, struct nlmsghdr *nlh, void *arg) { - struct rtattr **rta = arg; + struct nlattr *tb[IFA_MAX+1]; struct dn_dev *dn_db; - struct ifaddrmsg *ifm = NLMSG_DATA(nlh); + struct ifaddrmsg *ifm; struct dn_ifaddr *ifa, **ifap; + int err = -EADDRNOTAVAIL; + + err = nlmsg_parse(nlh, sizeof(*ifm), tb, IFA_MAX, dn_ifa_policy); + if (err < 0) + goto errout; + ifm = nlmsg_data(nlh); if ((dn_db = dn_dev_by_index(ifm->ifa_index)) == NULL) - return -EADDRNOTAVAIL; + goto errout; - for(ifap = &dn_db->ifa_list; (ifa=*ifap) != NULL; ifap = &ifa->ifa_next) { - void *tmp = rta[IFA_LOCAL-1]; - if ((tmp && memcmp(RTA_DATA(tmp), &ifa->ifa_local, 2)) || - (rta[IFA_LABEL-1] && rtattr_strcmp(rta[IFA_LABEL-1], ifa->ifa_label))) + for (ifap = &dn_db->ifa_list; (ifa = *ifap); ifap = &ifa->ifa_next) { + if (tb[IFA_LOCAL] && + nla_memcmp(tb[IFA_LOCAL], &ifa->ifa_local, 2)) + continue; + + if (tb[IFA_LABEL] && nla_strcmp(tb[IFA_LABEL], ifa->ifa_label)) continue; dn_dev_del_ifa(dn_db, ifap, 1); return 0; } - return -EADDRNOTAVAIL; +errout: + return err; } -static int dn_dev_rtm_newaddr(struct sk_buff *skb, struct nlmsghdr *nlh, void *arg) +static int dn_nl_newaddr(struct sk_buff *skb, struct nlmsghdr *nlh, void *arg) { - struct rtattr **rta = arg; + struct nlattr *tb[IFA_MAX+1]; struct net_device *dev; struct dn_dev *dn_db; - struct ifaddrmsg *ifm = NLMSG_DATA(nlh); + struct ifaddrmsg *ifm; struct dn_ifaddr *ifa; - int rv; + int err; - if (rta[IFA_LOCAL-1] == NULL) + err = nlmsg_parse(nlh, sizeof(*ifm), tb, IFA_MAX, dn_ifa_policy); + if (err < 0) + return err; + + if (tb[IFA_LOCAL] == NULL) return -EINVAL; + ifm = nlmsg_data(nlh); if ((dev = __dev_get_by_index(ifm->ifa_index)) == NULL) return -ENODEV; @@ -693,106 +712,121 @@ static int dn_dev_rtm_newaddr(struct sk_buff *skb, struct nlmsghdr *nlh, void *a if ((ifa = dn_dev_alloc_ifa()) == NULL) return -ENOBUFS; - if (!rta[IFA_ADDRESS - 1]) - rta[IFA_ADDRESS - 1] = rta[IFA_LOCAL - 1]; - memcpy(&ifa->ifa_local, RTA_DATA(rta[IFA_LOCAL-1]), 2); - memcpy(&ifa->ifa_address, RTA_DATA(rta[IFA_ADDRESS-1]), 2); + if (tb[IFA_ADDRESS] == NULL) + tb[IFA_ADDRESS] = tb[IFA_LOCAL]; + + ifa->ifa_local = nla_get_le16(tb[IFA_LOCAL]); + ifa->ifa_address = nla_get_le16(tb[IFA_ADDRESS]); ifa->ifa_flags = ifm->ifa_flags; ifa->ifa_scope = ifm->ifa_scope; ifa->ifa_dev = dn_db; - if (rta[IFA_LABEL-1]) - rtattr_strlcpy(ifa->ifa_label, rta[IFA_LABEL-1], IFNAMSIZ); + + if (tb[IFA_LABEL]) + nla_strlcpy(ifa->ifa_label, tb[IFA_LABEL], IFNAMSIZ); else memcpy(ifa->ifa_label, dev->name, IFNAMSIZ); - rv = dn_dev_insert_ifa(dn_db, ifa); - if (rv) + err = dn_dev_insert_ifa(dn_db, ifa); + if (err) dn_dev_free_ifa(ifa); - return rv; + + return err; +} + +static inline size_t dn_ifaddr_nlmsg_size(void) +{ + return NLMSG_ALIGN(sizeof(struct ifaddrmsg)) + + nla_total_size(IFNAMSIZ) /* IFA_LABEL */ + + nla_total_size(2) /* IFA_ADDRESS */ + + nla_total_size(2); /* IFA_LOCAL */ } -static int dn_dev_fill_ifaddr(struct sk_buff *skb, struct dn_ifaddr *ifa, - u32 pid, u32 seq, int event, unsigned int flags) +static int dn_nl_fill_ifaddr(struct sk_buff *skb, struct dn_ifaddr *ifa, + u32 pid, u32 seq, int event, unsigned int flags) { struct ifaddrmsg *ifm; struct nlmsghdr *nlh; - unsigned char *b = skb->tail; - nlh = NLMSG_NEW(skb, pid, seq, event, sizeof(*ifm), flags); - ifm = NLMSG_DATA(nlh); + nlh = nlmsg_put(skb, pid, seq, event, sizeof(*ifm), flags); + if (nlh == NULL) + return -ENOBUFS; + ifm = nlmsg_data(nlh); ifm->ifa_family = AF_DECnet; ifm->ifa_prefixlen = 16; ifm->ifa_flags = ifa->ifa_flags | IFA_F_PERMANENT; ifm->ifa_scope = ifa->ifa_scope; ifm->ifa_index = ifa->ifa_dev->dev->ifindex; + if (ifa->ifa_address) - RTA_PUT(skb, IFA_ADDRESS, 2, &ifa->ifa_address); + NLA_PUT_LE16(skb, IFA_ADDRESS, ifa->ifa_address); if (ifa->ifa_local) - RTA_PUT(skb, IFA_LOCAL, 2, &ifa->ifa_local); + NLA_PUT_LE16(skb, IFA_LOCAL, ifa->ifa_local); if (ifa->ifa_label[0]) - RTA_PUT(skb, IFA_LABEL, IFNAMSIZ, &ifa->ifa_label); - nlh->nlmsg_len = skb->tail - b; - return skb->len; + NLA_PUT_STRING(skb, IFA_LABEL, ifa->ifa_label); -nlmsg_failure: -rtattr_failure: - skb_trim(skb, b - skb->data); - return -1; + return nlmsg_end(skb, nlh); + +nla_put_failure: + return nlmsg_cancel(skb, nlh); } -static void rtmsg_ifa(int event, struct dn_ifaddr *ifa) +static void dn_ifaddr_notify(int event, struct dn_ifaddr *ifa) { struct sk_buff *skb; - int size = NLMSG_SPACE(sizeof(struct ifaddrmsg)+128); + int err = -ENOBUFS; - skb = alloc_skb(size, GFP_KERNEL); - if (!skb) { - netlink_set_err(rtnl, 0, RTNLGRP_DECnet_IFADDR, ENOBUFS); - return; - } - if (dn_dev_fill_ifaddr(skb, ifa, 0, 0, event, 0) < 0) { - kfree_skb(skb); - netlink_set_err(rtnl, 0, RTNLGRP_DECnet_IFADDR, EINVAL); - return; - } - NETLINK_CB(skb).dst_group = RTNLGRP_DECnet_IFADDR; - netlink_broadcast(rtnl, skb, 0, RTNLGRP_DECnet_IFADDR, GFP_KERNEL); + skb = alloc_skb(dn_ifaddr_nlmsg_size(), GFP_KERNEL); + if (skb == NULL) + goto errout; + + err = dn_nl_fill_ifaddr(skb, ifa, 0, 0, event, 0); + /* failure implies BUG in dn_ifaddr_nlmsg_size() */ + BUG_ON(err < 0); + + err = rtnl_notify(skb, 0, RTNLGRP_DECnet_IFADDR, NULL, GFP_KERNEL); +errout: + if (err < 0) + rtnl_set_sk_err(RTNLGRP_DECnet_IFADDR, err); } -static int dn_dev_dump_ifaddr(struct sk_buff *skb, struct netlink_callback *cb) +static int dn_nl_dump_ifaddr(struct sk_buff *skb, struct netlink_callback *cb) { - int idx, dn_idx; - int s_idx, s_dn_idx; + int idx, dn_idx = 0, skip_ndevs, skip_naddr; struct net_device *dev; struct dn_dev *dn_db; struct dn_ifaddr *ifa; - s_idx = cb->args[0]; - s_dn_idx = dn_idx = cb->args[1]; + skip_ndevs = cb->args[0]; + skip_naddr = cb->args[1]; + read_lock(&dev_base_lock); - for(dev = dev_base, idx = 0; dev; dev = dev->next, idx++) { - if (idx < s_idx) + for (dev = dev_base, idx = 0; dev; dev = dev->next, idx++) { + if (idx < skip_ndevs) continue; - if (idx > s_idx) - s_dn_idx = 0; + else if (idx > skip_ndevs) { + /* Only skip over addresses for first dev dumped + * in this iteration (idx == skip_ndevs) */ + skip_naddr = 0; + } + if ((dn_db = dev->dn_ptr) == NULL) continue; - for(ifa = dn_db->ifa_list, dn_idx = 0; ifa; ifa = ifa->ifa_next, dn_idx++) { - if (dn_idx < s_dn_idx) + for (ifa = dn_db->ifa_list, dn_idx = 0; ifa; + ifa = ifa->ifa_next, dn_idx++) { + if (dn_idx < skip_naddr) continue; - if (dn_dev_fill_ifaddr(skb, ifa, - NETLINK_CB(cb->skb).pid, - cb->nlh->nlmsg_seq, - RTM_NEWADDR, - NLM_F_MULTI) <= 0) + if (dn_nl_fill_ifaddr(skb, ifa, NETLINK_CB(cb->skb).pid, + cb->nlh->nlmsg_seq, RTM_NEWADDR, + NLM_F_MULTI) < 0) goto done; } } done: read_unlock(&dev_base_lock); + cb->args[0] = idx; cb->args[1] = dn_idx; @@ -1111,16 +1145,23 @@ struct dn_dev *dn_dev_create(struct net_device *dev, int *err) init_timer(&dn_db->timer); dn_db->uptime = jiffies; + + dn_db->neigh_parms = neigh_parms_alloc(dev, &dn_neigh_table); + if (!dn_db->neigh_parms) { + dev->dn_ptr = NULL; + kfree(dn_db); + return NULL; + } + if (dn_db->parms.up) { if (dn_db->parms.up(dev) < 0) { + neigh_parms_release(&dn_neigh_table, dn_db->neigh_parms); dev->dn_ptr = NULL; kfree(dn_db); return NULL; } } - dn_db->neigh_parms = neigh_parms_alloc(dev, &dn_neigh_table); - dn_dev_sysctl_register(dev, &dn_db->parms); dn_dev_set_timer(dev); @@ -1409,16 +1450,14 @@ static struct file_operations dn_dev_seq_fops = { static struct rtnetlink_link dnet_rtnetlink_table[RTM_NR_MSGTYPES] = { - [RTM_NEWADDR - RTM_BASE] = { .doit = dn_dev_rtm_newaddr, }, - [RTM_DELADDR - RTM_BASE] = { .doit = dn_dev_rtm_deladdr, }, - [RTM_GETADDR - RTM_BASE] = { .dumpit = dn_dev_dump_ifaddr, }, + [RTM_NEWADDR - RTM_BASE] = { .doit = dn_nl_newaddr, }, + [RTM_DELADDR - RTM_BASE] = { .doit = dn_nl_deladdr, }, + [RTM_GETADDR - RTM_BASE] = { .dumpit = dn_nl_dump_ifaddr, }, #ifdef CONFIG_DECNET_ROUTER [RTM_NEWROUTE - RTM_BASE] = { .doit = dn_fib_rtm_newroute, }, [RTM_DELROUTE - RTM_BASE] = { .doit = dn_fib_rtm_delroute, }, [RTM_GETROUTE - RTM_BASE] = { .doit = dn_cache_getroute, .dumpit = dn_fib_dump, }, - [RTM_NEWRULE - RTM_BASE] = { .doit = dn_fib_rtm_newrule, }, - [RTM_DELRULE - RTM_BASE] = { .doit = dn_fib_rtm_delrule, }, [RTM_GETRULE - RTM_BASE] = { .dumpit = dn_fib_dump_rules, }, #else [RTM_GETROUTE - RTM_BASE] = { .doit = dn_cache_getroute,