Skip to content

Commit 6f81652

Browse files
ecsvsimonwunderlich
authored andcommitted
batman-adv: Add inconsistent dat 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 24d71b9 commit 6f81652

File tree

1 file changed

+23
-19
lines changed

1 file changed

+23
-19
lines changed

net/batman-adv/distributed-arp-table.c

Lines changed: 23 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -863,23 +863,27 @@ int batadv_dat_cache_seq_print_text(struct seq_file *seq, void *offset)
863863
* netlink socket
864864
* @msg: buffer for the message
865865
* @portid: netlink port
866-
* @seq: Sequence number of netlink message
866+
* @cb: Control block containing additional options
867867
* @dat_entry: entry to dump
868868
*
869869
* Return: 0 or error code.
870870
*/
871871
static int
872-
batadv_dat_cache_dump_entry(struct sk_buff *msg, u32 portid, u32 seq,
872+
batadv_dat_cache_dump_entry(struct sk_buff *msg, u32 portid,
873+
struct netlink_callback *cb,
873874
struct batadv_dat_entry *dat_entry)
874875
{
875876
int msecs;
876877
void *hdr;
877878

878-
hdr = genlmsg_put(msg, portid, seq, &batadv_netlink_family,
879-
NLM_F_MULTI, BATADV_CMD_GET_DAT_CACHE);
879+
hdr = genlmsg_put(msg, portid, cb->nlh->nlmsg_seq,
880+
&batadv_netlink_family, NLM_F_MULTI,
881+
BATADV_CMD_GET_DAT_CACHE);
880882
if (!hdr)
881883
return -ENOBUFS;
882884

885+
genl_dump_check_consistent(cb, hdr);
886+
883887
msecs = jiffies_to_msecs(jiffies - dat_entry->last_update);
884888

885889
if (nla_put_in_addr(msg, BATADV_ATTR_DAT_CACHE_IP4ADDRESS,
@@ -901,27 +905,31 @@ batadv_dat_cache_dump_entry(struct sk_buff *msg, u32 portid, u32 seq,
901905
* a netlink socket
902906
* @msg: buffer for the message
903907
* @portid: netlink port
904-
* @seq: Sequence number of netlink message
905-
* @head: bucket to dump
908+
* @cb: Control block containing additional options
909+
* @hash: hash to dump
910+
* @bucket: bucket index to dump
906911
* @idx_skip: How many entries to skip
907912
*
908913
* Return: 0 or error code.
909914
*/
910915
static int
911-
batadv_dat_cache_dump_bucket(struct sk_buff *msg, u32 portid, u32 seq,
912-
struct hlist_head *head, int *idx_skip)
916+
batadv_dat_cache_dump_bucket(struct sk_buff *msg, u32 portid,
917+
struct netlink_callback *cb,
918+
struct batadv_hashtable *hash, unsigned int bucket,
919+
int *idx_skip)
913920
{
914921
struct batadv_dat_entry *dat_entry;
915922
int idx = 0;
916923

917-
rcu_read_lock();
918-
hlist_for_each_entry_rcu(dat_entry, head, hash_entry) {
924+
spin_lock_bh(&hash->list_locks[bucket]);
925+
cb->seq = atomic_read(&hash->generation) << 1 | 1;
926+
927+
hlist_for_each_entry(dat_entry, &hash->table[bucket], hash_entry) {
919928
if (idx < *idx_skip)
920929
goto skip;
921930

922-
if (batadv_dat_cache_dump_entry(msg, portid, seq,
923-
dat_entry)) {
924-
rcu_read_unlock();
931+
if (batadv_dat_cache_dump_entry(msg, portid, cb, dat_entry)) {
932+
spin_unlock_bh(&hash->list_locks[bucket]);
925933
*idx_skip = idx;
926934

927935
return -EMSGSIZE;
@@ -930,7 +938,7 @@ batadv_dat_cache_dump_bucket(struct sk_buff *msg, u32 portid, u32 seq,
930938
skip:
931939
idx++;
932940
}
933-
rcu_read_unlock();
941+
spin_unlock_bh(&hash->list_locks[bucket]);
934942

935943
return 0;
936944
}
@@ -951,7 +959,6 @@ int batadv_dat_cache_dump(struct sk_buff *msg, struct netlink_callback *cb)
951959
struct batadv_hashtable *hash;
952960
struct batadv_priv *bat_priv;
953961
int bucket = cb->args[0];
954-
struct hlist_head *head;
955962
int idx = cb->args[1];
956963
int ifindex;
957964
int ret = 0;
@@ -977,10 +984,7 @@ int batadv_dat_cache_dump(struct sk_buff *msg, struct netlink_callback *cb)
977984
}
978985

979986
while (bucket < hash->size) {
980-
head = &hash->table[bucket];
981-
982-
if (batadv_dat_cache_dump_bucket(msg, portid,
983-
cb->nlh->nlmsg_seq, head,
987+
if (batadv_dat_cache_dump_bucket(msg, portid, cb, hash, bucket,
984988
&idx))
985989
break;
986990

0 commit comments

Comments
 (0)