Skip to content

Commit d2d489b

Browse files
ecsvsimonwunderlich
authored andcommitted
batman-adv: Add inconsistent multicast 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 670dc28 ("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. The already existing generation sequence counter from the hash helper can be used for this simple hash. Reported-by: Matthias Schiffer <[email protected]> Signed-off-by: Sven Eckelmann <[email protected]> Signed-off-by: Simon Wunderlich <[email protected]>
1 parent 6b7b40a commit d2d489b

File tree

1 file changed

+28
-23
lines changed

1 file changed

+28
-23
lines changed

net/batman-adv/multicast.c

Lines changed: 28 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1365,22 +1365,26 @@ int batadv_mcast_mesh_info_put(struct sk_buff *msg,
13651365
* to a netlink socket
13661366
* @msg: buffer for the message
13671367
* @portid: netlink port
1368-
* @seq: Sequence number of netlink message
1368+
* @cb: Control block containing additional options
13691369
* @orig_node: originator to dump the multicast flags of
13701370
*
13711371
* Return: 0 or error code.
13721372
*/
13731373
static int
1374-
batadv_mcast_flags_dump_entry(struct sk_buff *msg, u32 portid, u32 seq,
1374+
batadv_mcast_flags_dump_entry(struct sk_buff *msg, u32 portid,
1375+
struct netlink_callback *cb,
13751376
struct batadv_orig_node *orig_node)
13761377
{
13771378
void *hdr;
13781379

1379-
hdr = genlmsg_put(msg, portid, seq, &batadv_netlink_family,
1380-
NLM_F_MULTI, BATADV_CMD_GET_MCAST_FLAGS);
1380+
hdr = genlmsg_put(msg, portid, cb->nlh->nlmsg_seq,
1381+
&batadv_netlink_family, NLM_F_MULTI,
1382+
BATADV_CMD_GET_MCAST_FLAGS);
13811383
if (!hdr)
13821384
return -ENOBUFS;
13831385

1386+
genl_dump_check_consistent(cb, hdr);
1387+
13841388
if (nla_put(msg, BATADV_ATTR_ORIG_ADDRESS, ETH_ALEN,
13851389
orig_node->orig)) {
13861390
genlmsg_cancel(msg, hdr);
@@ -1405,31 +1409,35 @@ batadv_mcast_flags_dump_entry(struct sk_buff *msg, u32 portid, u32 seq,
14051409
* table to a netlink socket
14061410
* @msg: buffer for the message
14071411
* @portid: netlink port
1408-
* @seq: Sequence number of netlink message
1409-
* @head: bucket to dump
1412+
* @cb: Control block containing additional options
1413+
* @hash: hash to dump
1414+
* @bucket: bucket index to dump
14101415
* @idx_skip: How many entries to skip
14111416
*
14121417
* Return: 0 or error code.
14131418
*/
14141419
static int
1415-
batadv_mcast_flags_dump_bucket(struct sk_buff *msg, u32 portid, u32 seq,
1416-
struct hlist_head *head, long *idx_skip)
1420+
batadv_mcast_flags_dump_bucket(struct sk_buff *msg, u32 portid,
1421+
struct netlink_callback *cb,
1422+
struct batadv_hashtable *hash,
1423+
unsigned int bucket, long *idx_skip)
14171424
{
14181425
struct batadv_orig_node *orig_node;
14191426
long idx = 0;
14201427

1421-
rcu_read_lock();
1422-
hlist_for_each_entry_rcu(orig_node, head, hash_entry) {
1428+
spin_lock_bh(&hash->list_locks[bucket]);
1429+
cb->seq = atomic_read(&hash->generation) << 1 | 1;
1430+
1431+
hlist_for_each_entry(orig_node, &hash->table[bucket], hash_entry) {
14231432
if (!test_bit(BATADV_ORIG_CAPA_HAS_MCAST,
14241433
&orig_node->capa_initialized))
14251434
continue;
14261435

14271436
if (idx < *idx_skip)
14281437
goto skip;
14291438

1430-
if (batadv_mcast_flags_dump_entry(msg, portid, seq,
1431-
orig_node)) {
1432-
rcu_read_unlock();
1439+
if (batadv_mcast_flags_dump_entry(msg, portid, cb, orig_node)) {
1440+
spin_unlock_bh(&hash->list_locks[bucket]);
14331441
*idx_skip = idx;
14341442

14351443
return -EMSGSIZE;
@@ -1438,7 +1446,7 @@ batadv_mcast_flags_dump_bucket(struct sk_buff *msg, u32 portid, u32 seq,
14381446
skip:
14391447
idx++;
14401448
}
1441-
rcu_read_unlock();
1449+
spin_unlock_bh(&hash->list_locks[bucket]);
14421450

14431451
return 0;
14441452
}
@@ -1447,27 +1455,25 @@ batadv_mcast_flags_dump_bucket(struct sk_buff *msg, u32 portid, u32 seq,
14471455
* __batadv_mcast_flags_dump() - dump multicast flags table to a netlink socket
14481456
* @msg: buffer for the message
14491457
* @portid: netlink port
1450-
* @seq: Sequence number of netlink message
1458+
* @cb: Control block containing additional options
14511459
* @bat_priv: the bat priv with all the soft interface information
14521460
* @bucket: current bucket to dump
14531461
* @idx: index in current bucket to the next entry to dump
14541462
*
14551463
* Return: 0 or error code.
14561464
*/
14571465
static int
1458-
__batadv_mcast_flags_dump(struct sk_buff *msg, u32 portid, u32 seq,
1466+
__batadv_mcast_flags_dump(struct sk_buff *msg, u32 portid,
1467+
struct netlink_callback *cb,
14591468
struct batadv_priv *bat_priv, long *bucket, long *idx)
14601469
{
14611470
struct batadv_hashtable *hash = bat_priv->orig_hash;
14621471
long bucket_tmp = *bucket;
1463-
struct hlist_head *head;
14641472
long idx_tmp = *idx;
14651473

14661474
while (bucket_tmp < hash->size) {
1467-
head = &hash->table[bucket_tmp];
1468-
1469-
if (batadv_mcast_flags_dump_bucket(msg, portid, seq, head,
1470-
&idx_tmp))
1475+
if (batadv_mcast_flags_dump_bucket(msg, portid, cb, hash,
1476+
*bucket, &idx_tmp))
14711477
break;
14721478

14731479
bucket_tmp++;
@@ -1550,8 +1556,7 @@ int batadv_mcast_flags_dump(struct sk_buff *msg, struct netlink_callback *cb)
15501556
return ret;
15511557

15521558
bat_priv = netdev_priv(primary_if->soft_iface);
1553-
ret = __batadv_mcast_flags_dump(msg, portid, cb->nlh->nlmsg_seq,
1554-
bat_priv, bucket, idx);
1559+
ret = __batadv_mcast_flags_dump(msg, portid, cb, bat_priv, bucket, idx);
15551560

15561561
batadv_hardif_put(primary_if);
15571562
return ret;

0 commit comments

Comments
 (0)