Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

netfilter: nfnetlink: nfnetlink_unicast() reports EAGAIN instead of ENOBUFS

Frontend callback reports EAGAIN to nfnetlink to retry a command, this
is used to signal that module autoloading is required. Unfortunately,
nlmsg_unicast() reports EAGAIN in case the receiver socket buffer gets
full, so it enters a busy-loop.

This patch updates nfnetlink_unicast() to turn EAGAIN into ENOBUFS and
to use nlmsg_unicast(). Remove the flags field in nfnetlink_unicast()
since this is always MSG_DONTWAIT in the existing code which is exactly
what nlmsg_unicast() passes to netlink_unicast() as parameter.

Fixes: 96518518cc41 ("netfilter: add nftables")
Reported-by: Phil Sutter <phil@nwl.cc>
Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>

+40 -40
+1 -2
include/linux/netfilter/nfnetlink.h
··· 43 43 int nfnetlink_send(struct sk_buff *skb, struct net *net, u32 portid, 44 44 unsigned int group, int echo, gfp_t flags); 45 45 int nfnetlink_set_err(struct net *net, u32 portid, u32 group, int error); 46 - int nfnetlink_unicast(struct sk_buff *skb, struct net *net, u32 portid, 47 - int flags); 46 + int nfnetlink_unicast(struct sk_buff *skb, struct net *net, u32 portid); 48 47 49 48 static inline u16 nfnl_msg_type(u8 subsys, u8 msg_type) 50 49 {
+29 -32
net/netfilter/nf_tables_api.c
··· 815 815 nlh->nlmsg_seq, NFT_MSG_NEWTABLE, 0, 816 816 family, table); 817 817 if (err < 0) 818 - goto err; 818 + goto err_fill_table_info; 819 819 820 - return nlmsg_unicast(nlsk, skb2, NETLINK_CB(skb).portid); 820 + return nfnetlink_unicast(skb2, net, NETLINK_CB(skb).portid); 821 821 822 - err: 822 + err_fill_table_info: 823 823 kfree_skb(skb2); 824 824 return err; 825 825 } ··· 1563 1563 nlh->nlmsg_seq, NFT_MSG_NEWCHAIN, 0, 1564 1564 family, table, chain); 1565 1565 if (err < 0) 1566 - goto err; 1566 + goto err_fill_chain_info; 1567 1567 1568 - return nlmsg_unicast(nlsk, skb2, NETLINK_CB(skb).portid); 1568 + return nfnetlink_unicast(skb2, net, NETLINK_CB(skb).portid); 1569 1569 1570 - err: 1570 + err_fill_chain_info: 1571 1571 kfree_skb(skb2); 1572 1572 return err; 1573 1573 } ··· 3008 3008 nlh->nlmsg_seq, NFT_MSG_NEWRULE, 0, 3009 3009 family, table, chain, rule, NULL); 3010 3010 if (err < 0) 3011 - goto err; 3011 + goto err_fill_rule_info; 3012 3012 3013 - return nlmsg_unicast(nlsk, skb2, NETLINK_CB(skb).portid); 3013 + return nfnetlink_unicast(skb2, net, NETLINK_CB(skb).portid); 3014 3014 3015 - err: 3015 + err_fill_rule_info: 3016 3016 kfree_skb(skb2); 3017 3017 return err; 3018 3018 } ··· 3968 3968 3969 3969 err = nf_tables_fill_set(skb2, &ctx, set, NFT_MSG_NEWSET, 0); 3970 3970 if (err < 0) 3971 - goto err; 3971 + goto err_fill_set_info; 3972 3972 3973 - return nlmsg_unicast(nlsk, skb2, NETLINK_CB(skb).portid); 3973 + return nfnetlink_unicast(skb2, net, NETLINK_CB(skb).portid); 3974 3974 3975 - err: 3975 + err_fill_set_info: 3976 3976 kfree_skb(skb2); 3977 3977 return err; 3978 3978 } ··· 4860 4860 err = -ENOMEM; 4861 4861 skb = nlmsg_new(NLMSG_GOODSIZE, GFP_ATOMIC); 4862 4862 if (skb == NULL) 4863 - goto err1; 4863 + return err; 4864 4864 4865 4865 err = nf_tables_fill_setelem_info(skb, ctx, ctx->seq, ctx->portid, 4866 4866 NFT_MSG_NEWSETELEM, 0, set, &elem); 4867 4867 if (err < 0) 4868 - goto err2; 4868 + goto err_fill_setelem; 4869 4869 4870 - err = nfnetlink_unicast(skb, ctx->net, ctx->portid, MSG_DONTWAIT); 4871 - /* This avoids a loop in nfnetlink. */ 4872 - if (err < 0) 4873 - goto err1; 4870 + return nfnetlink_unicast(skb, ctx->net, ctx->portid); 4874 4871 4875 - return 0; 4876 - err2: 4872 + err_fill_setelem: 4877 4873 kfree_skb(skb); 4878 - err1: 4879 - /* this avoids a loop in nfnetlink. */ 4880 - return err == -EAGAIN ? -ENOBUFS : err; 4874 + return err; 4881 4875 } 4882 4876 4883 4877 /* called with rcu_read_lock held */ ··· 6176 6182 nlh->nlmsg_seq, NFT_MSG_NEWOBJ, 0, 6177 6183 family, table, obj, reset); 6178 6184 if (err < 0) 6179 - goto err; 6185 + goto err_fill_obj_info; 6180 6186 6181 - return nlmsg_unicast(nlsk, skb2, NETLINK_CB(skb).portid); 6182 - err: 6187 + return nfnetlink_unicast(skb2, net, NETLINK_CB(skb).portid); 6188 + 6189 + err_fill_obj_info: 6183 6190 kfree_skb(skb2); 6184 6191 return err; 6185 6192 } ··· 7040 7045 NFT_MSG_NEWFLOWTABLE, 0, family, 7041 7046 flowtable, &flowtable->hook_list); 7042 7047 if (err < 0) 7043 - goto err; 7048 + goto err_fill_flowtable_info; 7044 7049 7045 - return nlmsg_unicast(nlsk, skb2, NETLINK_CB(skb).portid); 7046 - err: 7050 + return nfnetlink_unicast(skb2, net, NETLINK_CB(skb).portid); 7051 + 7052 + err_fill_flowtable_info: 7047 7053 kfree_skb(skb2); 7048 7054 return err; 7049 7055 } ··· 7230 7234 err = nf_tables_fill_gen_info(skb2, net, NETLINK_CB(skb).portid, 7231 7235 nlh->nlmsg_seq); 7232 7236 if (err < 0) 7233 - goto err; 7237 + goto err_fill_gen_info; 7234 7238 7235 - return nlmsg_unicast(nlsk, skb2, NETLINK_CB(skb).portid); 7236 - err: 7239 + return nfnetlink_unicast(skb2, net, NETLINK_CB(skb).portid); 7240 + 7241 + err_fill_gen_info: 7237 7242 kfree_skb(skb2); 7238 7243 return err; 7239 7244 }
+8 -3
net/netfilter/nfnetlink.c
··· 149 149 } 150 150 EXPORT_SYMBOL_GPL(nfnetlink_set_err); 151 151 152 - int nfnetlink_unicast(struct sk_buff *skb, struct net *net, u32 portid, 153 - int flags) 152 + int nfnetlink_unicast(struct sk_buff *skb, struct net *net, u32 portid) 154 153 { 155 - return netlink_unicast(net->nfnl, skb, portid, flags); 154 + int err; 155 + 156 + err = nlmsg_unicast(net->nfnl, skb, portid); 157 + if (err == -EAGAIN) 158 + err = -ENOBUFS; 159 + 160 + return err; 156 161 } 157 162 EXPORT_SYMBOL_GPL(nfnetlink_unicast); 158 163