diff options
Diffstat (limited to 'net/ipv4')
-rw-r--r-- | net/ipv4/devinet.c | 18 | ||||
-rw-r--r-- | net/ipv4/fib_rules.c | 8 | ||||
-rw-r--r-- | net/ipv4/fib_semantics.c | 36 |
3 files changed, 51 insertions, 11 deletions
diff --git a/net/ipv4/devinet.c b/net/ipv4/devinet.c index 7602c79a389..f38cbbae0ae 100644 --- a/net/ipv4/devinet.c +++ b/net/ipv4/devinet.c @@ -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) { @@ -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: diff --git a/net/ipv4/fib_rules.c b/net/ipv4/fib_rules.c index fd4a8cd4c06..b837c33e040 100644 --- a/net/ipv4/fib_rules.c +++ b/net/ipv4/fib_rules.c @@ -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), @@ -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, diff --git a/net/ipv4/fib_semantics.c b/net/ipv4/fib_semantics.c index 884d176e008..e63b8a98fb4 100644 --- a/net/ipv4/fib_semantics.c +++ b/net/ipv4/fib_semantics.c @@ -273,25 +273,49 @@ int ip_fib_check_default(__be32 gw, struct net_device *dev) return -1; } +static inline size_t fib_nlmsg_size(struct fib_info *fi) +{ + size_t payload = NLMSG_ALIGN(sizeof(struct rtmsg)) + + nla_total_size(4) /* RTA_TABLE */ + + nla_total_size(4) /* RTA_DST */ + + nla_total_size(4) /* RTA_PRIORITY */ + + nla_total_size(4); /* RTA_PREFSRC */ + + /* 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 flow and gateway attribute */ + nhsize += 2 * nla_total_size(4); + + /* all nexthops are packed in a nested attribute */ + payload += nla_total_size(fi->fib_nhs * nhsize); + } + + return payload; +} + void rtmsg_fib(int event, __be32 key, struct fib_alias *fa, int dst_len, u32 tb_id, struct nl_info *info) { struct sk_buff *skb; - int payload = sizeof(struct rtmsg) + 256; u32 seq = info->nlh ? info->nlh->nlmsg_seq : 0; int err = -ENOBUFS; - skb = nlmsg_new(nlmsg_total_size(payload), GFP_KERNEL); + skb = nlmsg_new(fib_nlmsg_size(fa->fa_info), GFP_KERNEL); if (skb == NULL) goto errout; err = fib_dump_info(skb, info->pid, seq, event, tb_id, fa->fa_type, fa->fa_scope, key, dst_len, fa->fa_tos, fa->fa_info, 0); - if (err < 0) { - kfree_skb(skb); - goto errout; - } + /* failure implies BUG in fib_nlmsg_size() */ + BUG_ON(err < 0); err = rtnl_notify(skb, info->pid, RTNLGRP_IPV4_ROUTE, info->nlh, GFP_KERNEL); |