]> git.proxmox.com Git - mirror_ubuntu-kernels.git/commitdiff
gtp: fix wrong condition in gtp_genl_dump_pdp()
authorTaehee Yoo <ap420073@gmail.com>
Wed, 11 Dec 2019 08:23:17 +0000 (08:23 +0000)
committerJakub Kicinski <jakub.kicinski@netronome.com>
Sun, 15 Dec 2019 01:15:24 +0000 (17:15 -0800)
gtp_genl_dump_pdp() is ->dumpit() callback of GTP module and it is used
to dump pdp contexts. it would be re-executed because of dump packet size.

If dump packet size is too big, it saves current dump pointer
(gtp interface pointer, bucket, TID value) then it restarts dump from
last pointer.
Current GTP code allows adding zero TID pdp context but dump code
ignores zero TID value. So, last dump pointer will not be found.

In addition, this patch adds missing rcu_read_lock() in
gtp_genl_dump_pdp().

Fixes: 459aa660eb1d ("gtp: add initial driver for datapath of GPRS Tunneling Protocol (GTP-U)")
Signed-off-by: Taehee Yoo <ap420073@gmail.com>
Signed-off-by: Jakub Kicinski <jakub.kicinski@netronome.com>
drivers/net/gtp.c

index 8b742edf793ddbbf7bf93946c6fbd5ad790f7d9d..a010e0a11c3389d19daca3e96e659c742fc410a9 100644 (file)
@@ -38,7 +38,6 @@ struct pdp_ctx {
        struct hlist_node       hlist_addr;
 
        union {
-               u64             tid;
                struct {
                        u64     tid;
                        u16     flow;
@@ -1244,43 +1243,46 @@ static int gtp_genl_dump_pdp(struct sk_buff *skb,
                                struct netlink_callback *cb)
 {
        struct gtp_dev *last_gtp = (struct gtp_dev *)cb->args[2], *gtp;
+       int i, j, bucket = cb->args[0], skip = cb->args[1];
        struct net *net = sock_net(skb->sk);
-       struct gtp_net *gn = net_generic(net, gtp_net_id);
-       unsigned long tid = cb->args[1];
-       int i, k = cb->args[0], ret;
        struct pdp_ctx *pctx;
+       struct gtp_net *gn;
+
+       gn = net_generic(net, gtp_net_id);
 
        if (cb->args[4])
                return 0;
 
+       rcu_read_lock();
        list_for_each_entry_rcu(gtp, &gn->gtp_dev_list, list) {
                if (last_gtp && last_gtp != gtp)
                        continue;
                else
                        last_gtp = NULL;
 
-               for (i = k; i < gtp->hash_size; i++) {
-                       hlist_for_each_entry_rcu(pctx, &gtp->tid_hash[i], hlist_tid) {
-                               if (tid && tid != pctx->u.tid)
-                                       continue;
-                               else
-                                       tid = 0;
-
-                               ret = gtp_genl_fill_info(skb,
-                                                        NETLINK_CB(cb->skb).portid,
-                                                        cb->nlh->nlmsg_seq,
-                                                        cb->nlh->nlmsg_type, pctx);
-                               if (ret < 0) {
+               for (i = bucket; i < gtp->hash_size; i++) {
+                       j = 0;
+                       hlist_for_each_entry_rcu(pctx, &gtp->tid_hash[i],
+                                                hlist_tid) {
+                               if (j >= skip &&
+                                   gtp_genl_fill_info(skb,
+                                           NETLINK_CB(cb->skb).portid,
+                                           cb->nlh->nlmsg_seq,
+                                           cb->nlh->nlmsg_type, pctx)) {
                                        cb->args[0] = i;
-                                       cb->args[1] = pctx->u.tid;
+                                       cb->args[1] = j;
                                        cb->args[2] = (unsigned long)gtp;
                                        goto out;
                                }
+                               j++;
                        }
+                       skip = 0;
                }
+               bucket = 0;
        }
        cb->args[4] = 1;
 out:
+       rcu_read_unlock();
        return skb->len;
 }