Skip to content

Commit

Permalink
decnet: Parse netlink attributes on our own
Browse files Browse the repository at this point in the history
decnet is the only subsystem left that is relying on the global
netlink attribute buffer rta_buf. It's horrible design and we
want to get rid of it.

This converts all of decnet to do implicit attribute parsing. It
also gets rid of the error prone struct dn_kern_rta.

Yes, the fib_magic() stuff is not pretty.

It's compiled tested but I need someone with appropriate hardware
to test the patch since I don't have access to it.

Cc: [email protected]
Signed-off-by: Thomas Graf <[email protected]>
Signed-off-by: David S. Miller <[email protected]>
  • Loading branch information
tgraf authored and davem330 committed Mar 22, 2013
1 parent 9b924db commit 58d7d8f
Show file tree
Hide file tree
Showing 4 changed files with 160 additions and 148 deletions.
28 changes: 6 additions & 22 deletions include/net/dn_fib.h
Original file line number Diff line number Diff line change
@@ -1,24 +1,9 @@
#ifndef _NET_DN_FIB_H
#define _NET_DN_FIB_H

/* WARNING: The ordering of these elements must match ordering
* of RTA_* rtnetlink attribute numbers.
*/
struct dn_kern_rta {
void *rta_dst;
void *rta_src;
int *rta_iif;
int *rta_oif;
void *rta_gw;
u32 *rta_priority;
void *rta_prefsrc;
struct rtattr *rta_mx;
struct rtattr *rta_mp;
unsigned char *rta_protoinfo;
u32 *rta_flow;
struct rta_cacheinfo *rta_ci;
struct rta_session *rta_sess;
};
#include <linux/netlink.h>

extern const struct nla_policy rtm_dn_policy[];

struct dn_fib_res {
struct fib_rule *r;
Expand Down Expand Up @@ -93,10 +78,10 @@ struct dn_fib_table {
u32 n;

int (*insert)(struct dn_fib_table *t, struct rtmsg *r,
struct dn_kern_rta *rta, struct nlmsghdr *n,
struct nlattr *attrs[], struct nlmsghdr *n,
struct netlink_skb_parms *req);
int (*delete)(struct dn_fib_table *t, struct rtmsg *r,
struct dn_kern_rta *rta, struct nlmsghdr *n,
struct nlattr *attrs[], struct nlmsghdr *n,
struct netlink_skb_parms *req);
int (*lookup)(struct dn_fib_table *t, const struct flowidn *fld,
struct dn_fib_res *res);
Expand All @@ -116,13 +101,12 @@ extern void dn_fib_cleanup(void);
extern int dn_fib_ioctl(struct socket *sock, unsigned int cmd,
unsigned long arg);
extern struct dn_fib_info *dn_fib_create_info(const struct rtmsg *r,
struct dn_kern_rta *rta,
struct nlattr *attrs[],
const struct nlmsghdr *nlh, int *errp);
extern int dn_fib_semantic_match(int type, struct dn_fib_info *fi,
const struct flowidn *fld,
struct dn_fib_res *res);
extern void dn_fib_release_info(struct dn_fib_info *fi);
extern __le16 dn_fib_get_attr16(struct rtattr *attr, int attrlen, int type);
extern void dn_fib_flush(void);
extern void dn_fib_select_multipath(const struct flowidn *fld,
struct dn_fib_res *res);
Expand Down
211 changes: 115 additions & 96 deletions net/decnet/dn_fib.c
Original file line number Diff line number Diff line change
Expand Up @@ -145,22 +145,10 @@ static inline struct dn_fib_info *dn_fib_find_info(const struct dn_fib_info *nfi
return NULL;
}

__le16 dn_fib_get_attr16(struct rtattr *attr, int attrlen, int type)
static int dn_fib_count_nhs(const struct nlattr *attr)
{
while(RTA_OK(attr,attrlen)) {
if (attr->rta_type == type)
return *(__le16*)RTA_DATA(attr);
attr = RTA_NEXT(attr, attrlen);
}

return 0;
}

static int dn_fib_count_nhs(struct rtattr *rta)
{
int nhs = 0;
struct rtnexthop *nhp = RTA_DATA(rta);
int nhlen = RTA_PAYLOAD(rta);
struct rtnexthop *nhp = nla_data(attr);
int nhs = 0, nhlen = nla_len(attr);

while(nhlen >= (int)sizeof(struct rtnexthop)) {
if ((nhlen -= nhp->rtnh_len) < 0)
Expand All @@ -172,10 +160,11 @@ static int dn_fib_count_nhs(struct rtattr *rta)
return nhs;
}

static int dn_fib_get_nhs(struct dn_fib_info *fi, const struct rtattr *rta, const struct rtmsg *r)
static int dn_fib_get_nhs(struct dn_fib_info *fi, const struct nlattr *attr,
const struct rtmsg *r)
{
struct rtnexthop *nhp = RTA_DATA(rta);
int nhlen = RTA_PAYLOAD(rta);
struct rtnexthop *nhp = nla_data(attr);
int nhlen = nla_len(attr);

change_nexthops(fi) {
int attrlen = nhlen - sizeof(struct rtnexthop);
Expand All @@ -187,7 +176,10 @@ static int dn_fib_get_nhs(struct dn_fib_info *fi, const struct rtattr *rta, cons
nh->nh_weight = nhp->rtnh_hops + 1;

if (attrlen) {
nh->nh_gw = dn_fib_get_attr16(RTNH_DATA(nhp), attrlen, RTA_GATEWAY);
struct nlattr *gw_attr;

gw_attr = nla_find((struct nlattr *) (nhp + 1), attrlen, RTA_GATEWAY);
nh->nh_gw = gw_attr ? nla_get_le16(gw_attr) : 0;
}
nhp = RTNH_NEXT(nhp);
} endfor_nexthops(fi);
Expand Down Expand Up @@ -268,7 +260,8 @@ static int dn_fib_check_nh(const struct rtmsg *r, struct dn_fib_info *fi, struct
}


struct dn_fib_info *dn_fib_create_info(const struct rtmsg *r, struct dn_kern_rta *rta, const struct nlmsghdr *nlh, int *errp)
struct dn_fib_info *dn_fib_create_info(const struct rtmsg *r, struct nlattr *attrs[],
const struct nlmsghdr *nlh, int *errp)
{
int err;
struct dn_fib_info *fi = NULL;
Expand All @@ -281,11 +274,9 @@ struct dn_fib_info *dn_fib_create_info(const struct rtmsg *r, struct dn_kern_rta
if (dn_fib_props[r->rtm_type].scope > r->rtm_scope)
goto err_inval;

if (rta->rta_mp) {
nhs = dn_fib_count_nhs(rta->rta_mp);
if (nhs == 0)
goto err_inval;
}
if (attrs[RTA_MULTIPATH] &&
(nhs = dn_fib_count_nhs(attrs[RTA_MULTIPATH])) == 0)
goto err_inval;

fi = kzalloc(sizeof(*fi)+nhs*sizeof(struct dn_fib_nh), GFP_KERNEL);
err = -ENOBUFS;
Expand All @@ -295,53 +286,65 @@ struct dn_fib_info *dn_fib_create_info(const struct rtmsg *r, struct dn_kern_rta
fi->fib_protocol = r->rtm_protocol;
fi->fib_nhs = nhs;
fi->fib_flags = r->rtm_flags;
if (rta->rta_priority)
fi->fib_priority = *rta->rta_priority;
if (rta->rta_mx) {
int attrlen = RTA_PAYLOAD(rta->rta_mx);
struct rtattr *attr = RTA_DATA(rta->rta_mx);

while(RTA_OK(attr, attrlen)) {
unsigned int flavour = attr->rta_type;
if (attrs[RTA_PRIORITY])
fi->fib_priority = nla_get_u32(attrs[RTA_PRIORITY]);

if (attrs[RTA_METRICS]) {
struct nlattr *attr;
int rem;

if (flavour) {
if (flavour > RTAX_MAX)
nla_for_each_nested(attr, attrs[RTA_METRICS], rem) {
int type = nla_type(attr);

if (type) {
if (type > RTAX_MAX || nla_len(attr) < 4)
goto err_inval;
fi->fib_metrics[flavour-1] = *(unsigned int *)RTA_DATA(attr);

fi->fib_metrics[type-1] = nla_get_u32(attr);
}
attr = RTA_NEXT(attr, attrlen);
}
}
if (rta->rta_prefsrc)
memcpy(&fi->fib_prefsrc, rta->rta_prefsrc, 2);

if (rta->rta_mp) {
if ((err = dn_fib_get_nhs(fi, rta->rta_mp, r)) != 0)
if (attrs[RTA_PREFSRC])
fi->fib_prefsrc = nla_get_le16(attrs[RTA_PREFSRC]);

if (attrs[RTA_MULTIPATH]) {
if ((err = dn_fib_get_nhs(fi, attrs[RTA_MULTIPATH], r)) != 0)
goto failure;
if (rta->rta_oif && fi->fib_nh->nh_oif != *rta->rta_oif)

if (attrs[RTA_OIF] &&
fi->fib_nh->nh_oif != nla_get_u32(attrs[RTA_OIF]))
goto err_inval;
if (rta->rta_gw && memcmp(&fi->fib_nh->nh_gw, rta->rta_gw, 2))

if (attrs[RTA_GATEWAY] &&
fi->fib_nh->nh_gw != nla_get_le16(attrs[RTA_GATEWAY]))
goto err_inval;
} else {
struct dn_fib_nh *nh = fi->fib_nh;
if (rta->rta_oif)
nh->nh_oif = *rta->rta_oif;
if (rta->rta_gw)
memcpy(&nh->nh_gw, rta->rta_gw, 2);

if (attrs[RTA_OIF])
nh->nh_oif = nla_get_u32(attrs[RTA_OIF]);

if (attrs[RTA_GATEWAY])
nh->nh_gw = nla_get_le16(attrs[RTA_GATEWAY]);

nh->nh_flags = r->rtm_flags;
nh->nh_weight = 1;
}

if (r->rtm_type == RTN_NAT) {
if (rta->rta_gw == NULL || nhs != 1 || rta->rta_oif)
if (!attrs[RTA_GATEWAY] || nhs != 1 || attrs[RTA_OIF])
goto err_inval;
memcpy(&fi->fib_nh->nh_gw, rta->rta_gw, 2);

fi->fib_nh->nh_gw = nla_get_le16(attrs[RTA_GATEWAY]);
goto link_it;
}

if (dn_fib_props[r->rtm_type].error) {
if (rta->rta_gw || rta->rta_oif || rta->rta_mp)
if (attrs[RTA_GATEWAY] || attrs[RTA_OIF] || attrs[RTA_MULTIPATH])
goto err_inval;

goto link_it;
}

Expand All @@ -367,8 +370,8 @@ struct dn_fib_info *dn_fib_create_info(const struct rtmsg *r, struct dn_kern_rta
}

if (fi->fib_prefsrc) {
if (r->rtm_type != RTN_LOCAL || rta->rta_dst == NULL ||
memcmp(&fi->fib_prefsrc, rta->rta_dst, 2))
if (r->rtm_type != RTN_LOCAL || !attrs[RTA_DST] ||
fi->fib_prefsrc != nla_get_le16(attrs[RTA_DST]))
if (dnet_addr_type(fi->fib_prefsrc) != RTN_LOCAL)
goto err_inval;
}
Expand Down Expand Up @@ -486,29 +489,24 @@ void dn_fib_select_multipath(const struct flowidn *fld, struct dn_fib_res *res)
spin_unlock_bh(&dn_fib_multipath_lock);
}

const struct nla_policy rtm_dn_policy[RTA_MAX + 1] = {
[RTA_DST] = { .type = NLA_U16 },
[RTA_SRC] = { .type = NLA_U16 },
[RTA_IIF] = { .type = NLA_U32 },
[RTA_OIF] = { .type = NLA_U32 },
[RTA_GATEWAY] = { .type = NLA_U16 },
[RTA_PRIORITY] = { .type = NLA_U32 },
[RTA_PREFSRC] = { .type = NLA_U16 },
[RTA_METRICS] = { .type = NLA_NESTED },
[RTA_MULTIPATH] = { .type = NLA_NESTED },
[RTA_TABLE] = { .type = NLA_U32 },
[RTA_MARK] = { .type = NLA_U32 },
};

static int dn_fib_check_attr(struct rtmsg *r, struct rtattr **rta)
{
int i;

for(i = 1; i <= RTA_MAX; i++) {
struct rtattr *attr = rta[i-1];
if (attr) {
if (RTA_PAYLOAD(attr) < 4 && RTA_PAYLOAD(attr) != 2)
return -EINVAL;
if (i != RTA_MULTIPATH && i != RTA_METRICS &&
i != RTA_TABLE)
rta[i-1] = (struct rtattr *)RTA_DATA(attr);
}
}

return 0;
}

static inline u32 rtm_get_table(struct rtattr **rta, u8 table)
static inline u32 rtm_get_table(struct nlattr *attrs[], u8 table)
{
if (rta[RTA_TABLE - 1])
table = nla_get_u32((struct nlattr *) rta[RTA_TABLE - 1]);
if (attrs[RTA_TABLE])
table = nla_get_u32(attrs[RTA_TABLE]);

return table;
}
Expand All @@ -517,46 +515,50 @@ static int dn_fib_rtm_delroute(struct sk_buff *skb, struct nlmsghdr *nlh, void *
{
struct net *net = sock_net(skb->sk);
struct dn_fib_table *tb;
struct rtattr **rta = arg;
struct rtmsg *r = NLMSG_DATA(nlh);
struct rtmsg *r = nlmsg_data(nlh);
struct nlattr *attrs[RTA_MAX+1];
int err;

if (!capable(CAP_NET_ADMIN))
return -EPERM;

if (!net_eq(net, &init_net))
return -EINVAL;

if (dn_fib_check_attr(r, rta))
return -EINVAL;
err = nlmsg_parse(nlh, sizeof(*r), attrs, RTA_MAX, rtm_dn_policy);
if (err < 0)
return err;

tb = dn_fib_get_table(rtm_get_table(rta, r->rtm_table), 0);
if (tb)
return tb->delete(tb, r, (struct dn_kern_rta *)rta, nlh, &NETLINK_CB(skb));
tb = dn_fib_get_table(rtm_get_table(attrs, r->rtm_table), 0);
if (!tb)
return -ESRCH;

return -ESRCH;
return tb->delete(tb, r, attrs, nlh, &NETLINK_CB(skb));
}

static int dn_fib_rtm_newroute(struct sk_buff *skb, struct nlmsghdr *nlh, void *arg)
{
struct net *net = sock_net(skb->sk);
struct dn_fib_table *tb;
struct rtattr **rta = arg;
struct rtmsg *r = NLMSG_DATA(nlh);
struct rtmsg *r = nlmsg_data(nlh);
struct nlattr *attrs[RTA_MAX+1];
int err;

if (!capable(CAP_NET_ADMIN))
return -EPERM;

if (!net_eq(net, &init_net))
return -EINVAL;

if (dn_fib_check_attr(r, rta))
return -EINVAL;
err = nlmsg_parse(nlh, sizeof(*r), attrs, RTA_MAX, rtm_dn_policy);
if (err < 0)
return err;

tb = dn_fib_get_table(rtm_get_table(rta, r->rtm_table), 1);
if (tb)
return tb->insert(tb, r, (struct dn_kern_rta *)rta, nlh, &NETLINK_CB(skb));
tb = dn_fib_get_table(rtm_get_table(attrs, r->rtm_table), 1);
if (!tb)
return -ENOBUFS;

return -ENOBUFS;
return tb->insert(tb, r, attrs, nlh, &NETLINK_CB(skb));
}

static void fib_magic(int cmd, int type, __le16 dst, int dst_len, struct dn_ifaddr *ifa)
Expand All @@ -566,10 +568,31 @@ static void fib_magic(int cmd, int type, __le16 dst, int dst_len, struct dn_ifad
struct nlmsghdr nlh;
struct rtmsg rtm;
} req;
struct dn_kern_rta rta;
struct {
struct nlattr hdr;
__le16 dst;
} dst_attr = {
.dst = dst,
};
struct {
struct nlattr hdr;
__le16 prefsrc;
} prefsrc_attr = {
.prefsrc = ifa->ifa_local,
};
struct {
struct nlattr hdr;
u32 oif;
} oif_attr = {
.oif = ifa->ifa_dev->dev->ifindex,
};
struct nlattr *attrs[RTA_MAX+1] = {
[RTA_DST] = (struct nlattr *) &dst_attr,
[RTA_PREFSRC] = (struct nlattr * ) &prefsrc_attr,
[RTA_OIF] = (struct nlattr *) &oif_attr,
};

memset(&req.rtm, 0, sizeof(req.rtm));
memset(&rta, 0, sizeof(rta));

if (type == RTN_UNICAST)
tb = dn_fib_get_table(RT_MIN_TABLE, 1);
Expand All @@ -591,14 +614,10 @@ static void fib_magic(int cmd, int type, __le16 dst, int dst_len, struct dn_ifad
req.rtm.rtm_scope = (type != RTN_LOCAL ? RT_SCOPE_LINK : RT_SCOPE_HOST);
req.rtm.rtm_type = type;

rta.rta_dst = &dst;
rta.rta_prefsrc = &ifa->ifa_local;
rta.rta_oif = &ifa->ifa_dev->dev->ifindex;

if (cmd == RTM_NEWROUTE)
tb->insert(tb, &req.rtm, &rta, &req.nlh, NULL);
tb->insert(tb, &req.rtm, attrs, &req.nlh, NULL);
else
tb->delete(tb, &req.rtm, &rta, &req.nlh, NULL);
tb->delete(tb, &req.rtm, attrs, &req.nlh, NULL);
}

static void dn_fib_add_ifaddr(struct dn_ifaddr *ifa)
Expand Down
Loading

0 comments on commit 58d7d8f

Please sign in to comment.