aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorSven Eckelmann <sven@narfation.org>2018-10-30 17:01:24 -0400
committerSimon Wunderlich <sw@simonwunderlich.de>2018-11-12 04:41:51 -0500
commitfb69be697916a2d0a9badcdef7f20fcfad1233bc (patch)
tree179a1043fce3fed4bd1741b9cbb6f2425ceecfee
parent9264c85c8b42d76862ae4ed307f4523e8bbab100 (diff)
batman-adv: Add inconsistent hardif netlink dump detection
The netlink dump functionality transfers a large number of entries from the kernel to userspace. It is rather likely that the transfer has to interrupted and later continued. During that time, it can happen that either new entries are added or removed. The userspace could than either receive some entries multiple times or miss entries. Commit 670dc2833d14 ("netlink: advertise incomplete dumps") introduced a mechanism to inform userspace about this problem. Userspace can then decide whether it is necessary or not to retry dumping the information again. The netlink dump functions have to be switched to exclusive locks to avoid changes while the current message is prepared. And an external generation sequence counter is introduced which tracks all modifications of the list. Reported-by: Matthias Schiffer <mschiffer@universe-factory.net> Signed-off-by: Sven Eckelmann <sven@narfation.org> Signed-off-by: Simon Wunderlich <sw@simonwunderlich.de>
-rw-r--r--net/batman-adv/hard-interface.c3
-rw-r--r--net/batman-adv/main.c1
-rw-r--r--net/batman-adv/main.h1
-rw-r--r--net/batman-adv/netlink.c24
4 files changed, 19 insertions, 10 deletions
diff --git a/net/batman-adv/hard-interface.c b/net/batman-adv/hard-interface.c
index 781c5b6e6e8e..508f4416dfc9 100644
--- a/net/batman-adv/hard-interface.c
+++ b/net/batman-adv/hard-interface.c
@@ -951,6 +951,7 @@ batadv_hardif_add_interface(struct net_device *net_dev)
951 batadv_check_known_mac_addr(hard_iface->net_dev); 951 batadv_check_known_mac_addr(hard_iface->net_dev);
952 kref_get(&hard_iface->refcount); 952 kref_get(&hard_iface->refcount);
953 list_add_tail_rcu(&hard_iface->list, &batadv_hardif_list); 953 list_add_tail_rcu(&hard_iface->list, &batadv_hardif_list);
954 batadv_hardif_generation++;
954 955
955 return hard_iface; 956 return hard_iface;
956 957
@@ -993,6 +994,7 @@ void batadv_hardif_remove_interfaces(void)
993 list_for_each_entry_safe(hard_iface, hard_iface_tmp, 994 list_for_each_entry_safe(hard_iface, hard_iface_tmp,
994 &batadv_hardif_list, list) { 995 &batadv_hardif_list, list) {
995 list_del_rcu(&hard_iface->list); 996 list_del_rcu(&hard_iface->list);
997 batadv_hardif_generation++;
996 batadv_hardif_remove_interface(hard_iface); 998 batadv_hardif_remove_interface(hard_iface);
997 } 999 }
998 rtnl_unlock(); 1000 rtnl_unlock();
@@ -1054,6 +1056,7 @@ static int batadv_hard_if_event(struct notifier_block *this,
1054 case NETDEV_UNREGISTER: 1056 case NETDEV_UNREGISTER:
1055 case NETDEV_PRE_TYPE_CHANGE: 1057 case NETDEV_PRE_TYPE_CHANGE:
1056 list_del_rcu(&hard_iface->list); 1058 list_del_rcu(&hard_iface->list);
1059 batadv_hardif_generation++;
1057 1060
1058 batadv_hardif_remove_interface(hard_iface); 1061 batadv_hardif_remove_interface(hard_iface);
1059 break; 1062 break;
diff --git a/net/batman-adv/main.c b/net/batman-adv/main.c
index c75e47826949..d1ed839fd32b 100644
--- a/net/batman-adv/main.c
+++ b/net/batman-adv/main.c
@@ -74,6 +74,7 @@
74 * list traversals just rcu-locked 74 * list traversals just rcu-locked
75 */ 75 */
76struct list_head batadv_hardif_list; 76struct list_head batadv_hardif_list;
77unsigned int batadv_hardif_generation;
77static int (*batadv_rx_handler[256])(struct sk_buff *skb, 78static int (*batadv_rx_handler[256])(struct sk_buff *skb,
78 struct batadv_hard_iface *recv_if); 79 struct batadv_hard_iface *recv_if);
79 80
diff --git a/net/batman-adv/main.h b/net/batman-adv/main.h
index b68a41190eb0..b572066325e4 100644
--- a/net/batman-adv/main.h
+++ b/net/batman-adv/main.h
@@ -247,6 +247,7 @@ static inline int batadv_print_vid(unsigned short vid)
247} 247}
248 248
249extern struct list_head batadv_hardif_list; 249extern struct list_head batadv_hardif_list;
250extern unsigned int batadv_hardif_generation;
250 251
251extern unsigned char batadv_broadcast_addr[]; 252extern unsigned char batadv_broadcast_addr[];
252extern struct workqueue_struct *batadv_event_workqueue; 253extern struct workqueue_struct *batadv_event_workqueue;
diff --git a/net/batman-adv/netlink.c b/net/batman-adv/netlink.c
index 0d9459b69bdb..2dc3304cee54 100644
--- a/net/batman-adv/netlink.c
+++ b/net/batman-adv/netlink.c
@@ -29,11 +29,11 @@
29#include <linux/if_ether.h> 29#include <linux/if_ether.h>
30#include <linux/init.h> 30#include <linux/init.h>
31#include <linux/kernel.h> 31#include <linux/kernel.h>
32#include <linux/list.h>
32#include <linux/netdevice.h> 33#include <linux/netdevice.h>
33#include <linux/netlink.h> 34#include <linux/netlink.h>
34#include <linux/printk.h> 35#include <linux/printk.h>
35#include <linux/rculist.h> 36#include <linux/rtnetlink.h>
36#include <linux/rcupdate.h>
37#include <linux/skbuff.h> 37#include <linux/skbuff.h>
38#include <linux/stddef.h> 38#include <linux/stddef.h>
39#include <linux/types.h> 39#include <linux/types.h>
@@ -445,23 +445,27 @@ out:
445 * batadv_netlink_dump_hardif_entry() - Dump one hard interface into a message 445 * batadv_netlink_dump_hardif_entry() - Dump one hard interface into a message
446 * @msg: Netlink message to dump into 446 * @msg: Netlink message to dump into
447 * @portid: Port making netlink request 447 * @portid: Port making netlink request
448 * @seq: Sequence number of netlink message 448 * @cb: Control block containing additional options
449 * @hard_iface: Hard interface to dump 449 * @hard_iface: Hard interface to dump
450 * 450 *
451 * Return: error code, or 0 on success 451 * Return: error code, or 0 on success
452 */ 452 */
453static int 453static int
454batadv_netlink_dump_hardif_entry(struct sk_buff *msg, u32 portid, u32 seq, 454batadv_netlink_dump_hardif_entry(struct sk_buff *msg, u32 portid,
455 struct netlink_callback *cb,
455 struct batadv_hard_iface *hard_iface) 456 struct batadv_hard_iface *hard_iface)
456{ 457{
457 struct net_device *net_dev = hard_iface->net_dev; 458 struct net_device *net_dev = hard_iface->net_dev;
458 void *hdr; 459 void *hdr;
459 460
460 hdr = genlmsg_put(msg, portid, seq, &batadv_netlink_family, NLM_F_MULTI, 461 hdr = genlmsg_put(msg, portid, cb->nlh->nlmsg_seq,
462 &batadv_netlink_family, NLM_F_MULTI,
461 BATADV_CMD_GET_HARDIFS); 463 BATADV_CMD_GET_HARDIFS);
462 if (!hdr) 464 if (!hdr)
463 return -EMSGSIZE; 465 return -EMSGSIZE;
464 466
467 genl_dump_check_consistent(cb, hdr);
468
465 if (nla_put_u32(msg, BATADV_ATTR_HARD_IFINDEX, 469 if (nla_put_u32(msg, BATADV_ATTR_HARD_IFINDEX,
466 net_dev->ifindex) || 470 net_dev->ifindex) ||
467 nla_put_string(msg, BATADV_ATTR_HARD_IFNAME, 471 nla_put_string(msg, BATADV_ATTR_HARD_IFNAME,
@@ -498,7 +502,6 @@ batadv_netlink_dump_hardifs(struct sk_buff *msg, struct netlink_callback *cb)
498 struct batadv_hard_iface *hard_iface; 502 struct batadv_hard_iface *hard_iface;
499 int ifindex; 503 int ifindex;
500 int portid = NETLINK_CB(cb->skb).portid; 504 int portid = NETLINK_CB(cb->skb).portid;
501 int seq = cb->nlh->nlmsg_seq;
502 int skip = cb->args[0]; 505 int skip = cb->args[0];
503 int i = 0; 506 int i = 0;
504 507
@@ -516,23 +519,24 @@ batadv_netlink_dump_hardifs(struct sk_buff *msg, struct netlink_callback *cb)
516 return -ENODEV; 519 return -ENODEV;
517 } 520 }
518 521
519 rcu_read_lock(); 522 rtnl_lock();
523 cb->seq = batadv_hardif_generation << 1 | 1;
520 524
521 list_for_each_entry_rcu(hard_iface, &batadv_hardif_list, list) { 525 list_for_each_entry(hard_iface, &batadv_hardif_list, list) {
522 if (hard_iface->soft_iface != soft_iface) 526 if (hard_iface->soft_iface != soft_iface)
523 continue; 527 continue;
524 528
525 if (i++ < skip) 529 if (i++ < skip)
526 continue; 530 continue;
527 531
528 if (batadv_netlink_dump_hardif_entry(msg, portid, seq, 532 if (batadv_netlink_dump_hardif_entry(msg, portid, cb,
529 hard_iface)) { 533 hard_iface)) {
530 i--; 534 i--;
531 break; 535 break;
532 } 536 }
533 } 537 }
534 538
535 rcu_read_unlock(); 539 rtnl_unlock();
536 540
537 dev_put(soft_iface); 541 dev_put(soft_iface);
538 542