Skip to content

Commit

Permalink
[NETLINK]: Do precise netlink message allocations where possible
Browse files Browse the repository at this point in the history
Account for the netlink message header size directly in nlmsg_new()
instead of relying on the caller calculate it correctly.

Replaces error handling of message construction functions when
constructing notifications with bug traps since a failure implies
a bug in calculating the size of the skb.

Signed-off-by: Thomas Graf <[email protected]>
Acked-by: Paul Moore <[email protected]>
Signed-off-by: David S. Miller <[email protected]>
  • Loading branch information
tgraf authored and David S. Miller committed Dec 3, 2006
1 parent a94f723 commit 339bf98
Show file tree
Hide file tree
Showing 21 changed files with 233 additions and 107 deletions.
1 change: 1 addition & 0 deletions include/linux/netlink.h
Original file line number Diff line number Diff line change
Expand Up @@ -174,6 +174,7 @@ int netlink_sendskb(struct sock *sk, struct sk_buff *skb, int protocol);
*/
#define NLMSG_GOODORDER 0
#define NLMSG_GOODSIZE (SKB_MAX_ORDER(0, NLMSG_GOODORDER))
#define NLMSG_DEFAULT_SIZE (NLMSG_GOODSIZE - NLMSG_HDRLEN)


struct netlink_callback
Expand Down
1 change: 1 addition & 0 deletions include/net/fib_rules.h
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ struct fib_rules_ops
struct nlmsghdr *,
struct fib_rule_hdr *);
u32 (*default_pref)(void);
size_t (*nlmsg_payload)(struct fib_rule *);

int nlgroup;
struct nla_policy *policy;
Expand Down
9 changes: 5 additions & 4 deletions include/net/netlink.h
Original file line number Diff line number Diff line change
Expand Up @@ -500,14 +500,15 @@ static inline struct nlmsghdr *nlmsg_put_answer(struct sk_buff *skb,

/**
* nlmsg_new - Allocate a new netlink message
* @size: maximum size of message
* @payload: size of the message payload
* @flags: the type of memory to allocate.
*
* Use NLMSG_GOODSIZE if size isn't know and you need a good default size.
* Use NLMSG_DEFAULT_SIZE if the size of the payload isn't known
* and a good default is needed.
*/
static inline struct sk_buff *nlmsg_new(int size, gfp_t flags)
static inline struct sk_buff *nlmsg_new(size_t payload, gfp_t flags)
{
return alloc_skb(size, flags);
return alloc_skb(nlmsg_total_size(payload), flags);
}

/**
Expand Down
3 changes: 1 addition & 2 deletions kernel/taskstats.c
Original file line number Diff line number Diff line change
Expand Up @@ -77,8 +77,7 @@ static int prepare_reply(struct genl_info *info, u8 cmd, struct sk_buff **skbp,
/*
* If new attributes are added, please revisit this allocation
*/
size = nlmsg_total_size(genlmsg_total_size(size));
skb = nlmsg_new(size, GFP_KERNEL);
skb = nlmsg_new(genlmsg_total_size(size), GFP_KERNEL);
if (!skb)
return -ENOMEM;

Expand Down
21 changes: 15 additions & 6 deletions net/bridge/br_netlink.c
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,18 @@
#include <net/netlink.h>
#include "br_private.h"

static inline size_t br_nlmsg_size(void)
{
return NLMSG_ALIGN(sizeof(struct ifinfomsg))
+ nla_total_size(IFNAMSIZ) /* IFLA_IFNAME */
+ nla_total_size(MAX_ADDR_LEN) /* IFLA_ADDRESS */
+ nla_total_size(4) /* IFLA_MASTER */
+ nla_total_size(4) /* IFLA_MTU */
+ nla_total_size(4) /* IFLA_LINK */
+ nla_total_size(1) /* IFLA_OPERSTATE */
+ nla_total_size(1); /* IFLA_PROTINFO */
}

/*
* Create one netlink message for one interface
* Contains port and master info as well as carrier and bridge state.
Expand Down Expand Up @@ -77,19 +89,16 @@ static int br_fill_ifinfo(struct sk_buff *skb, const struct net_bridge_port *por
void br_ifinfo_notify(int event, struct net_bridge_port *port)
{
struct sk_buff *skb;
int payload = sizeof(struct ifinfomsg) + 128;
int err = -ENOBUFS;

pr_debug("bridge notify event=%d\n", event);
skb = nlmsg_new(nlmsg_total_size(payload), GFP_ATOMIC);
skb = nlmsg_new(br_nlmsg_size(), GFP_ATOMIC);
if (skb == NULL)
goto errout;

err = br_fill_ifinfo(skb, port, 0, 0, event, 0);
if (err < 0) {
kfree_skb(skb);
goto errout;
}
/* failure implies BUG in br_nlmsg_size() */
BUG_ON(err < 0);

err = rtnl_notify(skb, 0, RTNLGRP_LINK, NULL, GFP_ATOMIC);
errout:
Expand Down
24 changes: 19 additions & 5 deletions net/core/fib_rules.c
Original file line number Diff line number Diff line change
Expand Up @@ -306,6 +306,22 @@ int fib_nl_delrule(struct sk_buff *skb, struct nlmsghdr* nlh, void *arg)
return err;
}

static inline size_t fib_rule_nlmsg_size(struct fib_rules_ops *ops,
struct fib_rule *rule)
{
size_t payload = NLMSG_ALIGN(sizeof(struct fib_rule_hdr))
+ nla_total_size(IFNAMSIZ) /* FRA_IFNAME */
+ nla_total_size(4) /* FRA_PRIORITY */
+ nla_total_size(4) /* FRA_TABLE */
+ nla_total_size(4) /* FRA_FWMARK */
+ nla_total_size(4); /* FRA_FWMASK */

if (ops->nlmsg_payload)
payload += ops->nlmsg_payload(rule);

return payload;
}

static int fib_nl_fill_rule(struct sk_buff *skb, struct fib_rule *rule,
u32 pid, u32 seq, int type, int flags,
struct fib_rules_ops *ops)
Expand Down Expand Up @@ -384,15 +400,13 @@ static void notify_rule_change(int event, struct fib_rule *rule,
struct sk_buff *skb;
int err = -ENOBUFS;

skb = nlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL);
skb = nlmsg_new(fib_rule_nlmsg_size(ops, rule), GFP_KERNEL);
if (skb == NULL)
goto errout;

err = fib_nl_fill_rule(skb, rule, pid, nlh->nlmsg_seq, event, 0, ops);
if (err < 0) {
kfree_skb(skb);
goto errout;
}
/* failure implies BUG in fib_rule_nlmsg_size() */
BUG_ON(err < 0);

err = rtnl_notify(skb, pid, ops->nlgroup, nlh, GFP_KERNEL);
errout:
Expand Down
17 changes: 12 additions & 5 deletions net/core/neighbour.c
Original file line number Diff line number Diff line change
Expand Up @@ -2410,20 +2410,27 @@ static struct file_operations neigh_stat_seq_fops = {
#endif /* CONFIG_PROC_FS */

#ifdef CONFIG_ARPD
static inline size_t neigh_nlmsg_size(void)
{
return NLMSG_ALIGN(sizeof(struct ndmsg))
+ nla_total_size(MAX_ADDR_LEN) /* NDA_DST */
+ nla_total_size(MAX_ADDR_LEN) /* NDA_LLADDR */
+ nla_total_size(sizeof(struct nda_cacheinfo))
+ nla_total_size(4); /* NDA_PROBES */
}

static void __neigh_notify(struct neighbour *n, int type, int flags)
{
struct sk_buff *skb;
int err = -ENOBUFS;

skb = nlmsg_new(NLMSG_GOODSIZE, GFP_ATOMIC);
skb = nlmsg_new(neigh_nlmsg_size(), GFP_ATOMIC);
if (skb == NULL)
goto errout;

err = neigh_fill_info(skb, n, 0, 0, type, flags);
if (err < 0) {
kfree_skb(skb);
goto errout;
}
/* failure implies BUG in neigh_nlmsg_size() */
BUG_ON(err < 0);

err = rtnl_notify(skb, 0, RTNLGRP_NEIGH, NULL, GFP_ATOMIC);
errout:
Expand Down
39 changes: 26 additions & 13 deletions net/core/rtnetlink.c
Original file line number Diff line number Diff line change
Expand Up @@ -273,6 +273,25 @@ static void copy_rtnl_link_stats(struct rtnl_link_stats *a,
a->tx_compressed = b->tx_compressed;
};

static inline size_t if_nlmsg_size(int iwbuflen)
{
return NLMSG_ALIGN(sizeof(struct ifinfomsg))
+ nla_total_size(IFNAMSIZ) /* IFLA_IFNAME */
+ nla_total_size(IFNAMSIZ) /* IFLA_QDISC */
+ nla_total_size(sizeof(struct rtnl_link_ifmap))
+ nla_total_size(sizeof(struct rtnl_link_stats))
+ nla_total_size(MAX_ADDR_LEN) /* IFLA_ADDRESS */
+ nla_total_size(MAX_ADDR_LEN) /* IFLA_BROADCAST */
+ nla_total_size(4) /* IFLA_TXQLEN */
+ nla_total_size(4) /* IFLA_WEIGHT */
+ nla_total_size(4) /* IFLA_MTU */
+ nla_total_size(4) /* IFLA_LINK */
+ nla_total_size(4) /* IFLA_MASTER */
+ nla_total_size(1) /* IFLA_OPERSTATE */
+ nla_total_size(1) /* IFLA_LINKMODE */
+ nla_total_size(iwbuflen);
}

static int rtnl_fill_ifinfo(struct sk_buff *skb, struct net_device *dev,
void *iwbuf, int iwbuflen, int type, u32 pid,
u32 seq, u32 change, unsigned int flags)
Expand Down Expand Up @@ -558,7 +577,7 @@ static int rtnl_getlink(struct sk_buff *skb, struct nlmsghdr* nlh, void *arg)
struct sk_buff *nskb;
char *iw_buf = NULL, *iw = NULL;
int iw_buf_len = 0;
int err, payload;
int err;

err = nlmsg_parse(nlh, sizeof(*ifm), tb, IFLA_MAX, ifla_policy);
if (err < 0)
Expand Down Expand Up @@ -587,20 +606,16 @@ static int rtnl_getlink(struct sk_buff *skb, struct nlmsghdr* nlh, void *arg)
}
#endif /* CONFIG_NET_WIRELESS_RTNETLINK */

payload = NLMSG_ALIGN(sizeof(struct ifinfomsg) +
nla_total_size(iw_buf_len));
nskb = nlmsg_new(nlmsg_total_size(payload), GFP_KERNEL);
nskb = nlmsg_new(if_nlmsg_size(iw_buf_len), GFP_KERNEL);
if (nskb == NULL) {
err = -ENOBUFS;
goto errout;
}

err = rtnl_fill_ifinfo(nskb, dev, iw, iw_buf_len, RTM_NEWLINK,
NETLINK_CB(skb).pid, nlh->nlmsg_seq, 0, 0);
if (err <= 0) {
kfree_skb(nskb);
goto errout;
}
/* failure impilies BUG in if_nlmsg_size or wireless_rtnetlink_get */
BUG_ON(err < 0);

err = rtnl_unicast(nskb, NETLINK_CB(skb).pid);
errout:
Expand Down Expand Up @@ -639,15 +654,13 @@ void rtmsg_ifinfo(int type, struct net_device *dev, unsigned change)
struct sk_buff *skb;
int err = -ENOBUFS;

skb = nlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL);
skb = nlmsg_new(if_nlmsg_size(0), GFP_KERNEL);
if (skb == NULL)
goto errout;

err = rtnl_fill_ifinfo(skb, dev, NULL, 0, type, 0, 0, change, 0);
if (err < 0) {
kfree_skb(skb);
goto errout;
}
/* failure implies BUG in if_nlmsg_size() */
BUG_ON(err < 0);

err = rtnl_notify(skb, 0, RTNLGRP_LINK, NULL, GFP_KERNEL);
errout:
Expand Down
6 changes: 6 additions & 0 deletions net/decnet/dn_rules.c
Original file line number Diff line number Diff line change
Expand Up @@ -241,6 +241,12 @@ static u32 dn_fib_rule_default_pref(void)
return 0;
}

static size_t dn_fib_rule_nlmsg_payload(struct fib_rule *rule)
{
return nla_total_size(2) /* dst */
+ nla_total_size(2); /* src */
}

int dn_fib_dump_rules(struct sk_buff *skb, struct netlink_callback *cb)
{
return fib_rules_dump(skb, cb, AF_DECnet);
Expand Down
34 changes: 29 additions & 5 deletions net/decnet/dn_table.c
Original file line number Diff line number Diff line change
Expand Up @@ -263,6 +263,32 @@ static int dn_fib_nh_match(struct rtmsg *r, struct nlmsghdr *nlh, struct dn_kern
return 0;
}

static inline size_t dn_fib_nlmsg_size(struct dn_fib_info *fi)
{
size_t payload = NLMSG_ALIGN(struct rtmsg)
+ nla_total_size(4) /* RTA_TABLE */
+ nla_total_size(2) /* RTA_DST */
+ nla_total_size(4); /* RTA_PRIORITY */

/* space for nested metrics */
payload += nla_total_size((RTAX_MAX * nla_total_size(4)));

if (fi->fib_nhs) {
/* Also handles the special case fib_nhs == 1 */

/* each nexthop is packed in an attribute */
size_t nhsize = nla_total_size(sizeof(struct rtnexthop));

/* may contain a gateway attribute */
nhsize += nla_total_size(4);

/* all nexthops are packed in a nested attribute */
payload += nla_total_size(fi->fib_nhs * nhsize);
}

return payload;
}

static int dn_fib_dump_info(struct sk_buff *skb, u32 pid, u32 seq, int event,
u32 tb_id, u8 type, u8 scope, void *dst, int dst_len,
struct dn_fib_info *fi, unsigned int flags)
Expand Down Expand Up @@ -335,17 +361,15 @@ static void dn_rtmsg_fib(int event, struct dn_fib_node *f, int z, u32 tb_id,
u32 pid = req ? req->pid : 0;
int err = -ENOBUFS;

skb = nlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL);
skb = nlmsg_new(dn_fib_nlmsg_size(DN_FIB_INFO(f), GFP_KERNEL));
if (skb == NULL)
goto errout;

err = dn_fib_dump_info(skb, pid, nlh->nlmsg_seq, event, tb_id,
f->fn_type, f->fn_scope, &f->fn_key, z,
DN_FIB_INFO(f), 0);
if (err < 0) {
kfree_skb(skb);
goto errout;
}
/* failure implies BUG in dn_fib_nlmsg_size() */
BUG_ON(err < 0);

err = rtnl_notify(skb, pid, RTNLGRP_DECnet_ROUTE, nlh, GFP_KERNEL);
errout:
Expand Down
18 changes: 13 additions & 5 deletions net/ipv4/devinet.c
Original file line number Diff line number Diff line change
Expand Up @@ -1120,6 +1120,16 @@ static struct notifier_block ip_netdev_notifier = {
.notifier_call =inetdev_event,
};

static inline size_t inet_nlmsg_size(void)
{
return NLMSG_ALIGN(sizeof(struct ifaddrmsg))
+ nla_total_size(4) /* IFA_ADDRESS */
+ nla_total_size(4) /* IFA_LOCAL */
+ nla_total_size(4) /* IFA_BROADCAST */
+ nla_total_size(4) /* IFA_ANYCAST */
+ nla_total_size(IFNAMSIZ); /* IFA_LABEL */
}

static int inet_fill_ifaddr(struct sk_buff *skb, struct in_ifaddr *ifa,
u32 pid, u32 seq, int event, unsigned int flags)
{
Expand Down Expand Up @@ -1208,15 +1218,13 @@ static void rtmsg_ifa(int event, struct in_ifaddr* ifa, struct nlmsghdr *nlh,
u32 seq = nlh ? nlh->nlmsg_seq : 0;
int err = -ENOBUFS;

skb = nlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL);
skb = nlmsg_new(inet_nlmsg_size(), GFP_KERNEL);
if (skb == NULL)
goto errout;

err = inet_fill_ifaddr(skb, ifa, pid, seq, event, 0);
if (err < 0) {
kfree_skb(skb);
goto errout;
}
/* failure implies BUG in inet_nlmsg_size() */
BUG_ON(err < 0);

err = rtnl_notify(skb, pid, RTNLGRP_IPV4_IFADDR, nlh, GFP_KERNEL);
errout:
Expand Down
8 changes: 8 additions & 0 deletions net/ipv4/fib_rules.c
Original file line number Diff line number Diff line change
Expand Up @@ -299,6 +299,13 @@ static u32 fib4_rule_default_pref(void)
return 0;
}

static size_t fib4_rule_nlmsg_payload(struct fib_rule *rule)
{
return nla_total_size(4) /* dst */
+ nla_total_size(4) /* src */
+ nla_total_size(4); /* flow */
}

static struct fib_rules_ops fib4_rules_ops = {
.family = AF_INET,
.rule_size = sizeof(struct fib4_rule),
Expand All @@ -308,6 +315,7 @@ static struct fib_rules_ops fib4_rules_ops = {
.compare = fib4_rule_compare,
.fill = fib4_rule_fill,
.default_pref = fib4_rule_default_pref,
.nlmsg_payload = fib4_rule_nlmsg_payload,
.nlgroup = RTNLGRP_IPV4_RULE,
.policy = fib4_rule_policy,
.rules_list = &fib4_rules,
Expand Down
Loading

0 comments on commit 339bf98

Please sign in to comment.