ipv6: enable IPV6_FLOWLABEL_MGR for getsockopt
authorFlorent Fourcot <florent.fourcot@enst-bretagne.fr>
Thu, 7 Nov 2013 16:53:12 +0000 (17:53 +0100)
committerDavid S. Miller <davem@davemloft.net>
Fri, 8 Nov 2013 18:42:57 +0000 (13:42 -0500)
It is already possible to set/put/renew a label
with IPV6_FLOWLABEL_MGR and setsockopt. This patch
add the possibility to get information about this
label (current value, time before expiration, etc).

It helps application to take decision for a renew
or a release of the label.

v2:
 * Add spin_lock to prevent race condition
 * return -ENOENT if no result found
 * check if flr_action is GET

v3:
 * move the spin_lock to protect only the
   relevant code

Signed-off-by: Florent Fourcot <florent.fourcot@enst-bretagne.fr>
Acked-by: Hannes Frederic Sowa <hannes@stressinduktion.org>
Signed-off-by: David S. Miller <davem@davemloft.net>
include/net/ipv6.h
net/ipv6/ip6_flowlabel.c
net/ipv6/ipv6_sockglue.c

index dd96638ab8ff6390b498aa3c8729eb16d46496a4..2a5f668cd6836fae6ff45e551f8dacfd93dacd8e 100644 (file)
@@ -250,6 +250,7 @@ struct ipv6_txoptions *fl6_merge_options(struct ipv6_txoptions *opt_space,
                                         struct ipv6_txoptions *fopt);
 void fl6_free_socklist(struct sock *sk);
 int ipv6_flowlabel_opt(struct sock *sk, char __user *optval, int optlen);
+int ipv6_flowlabel_opt_get(struct sock *sk, struct in6_flowlabel_req *freq);
 int ip6_flowlabel_init(void);
 void ip6_flowlabel_cleanup(void);
 
index 819578e924999b5c8c620f40442e34fe30fc0071..4a06ed01d0c313e74d9bb8d78b480757bbdb4f20 100644 (file)
@@ -475,6 +475,32 @@ static inline void fl_link(struct ipv6_pinfo *np, struct ipv6_fl_socklist *sfl,
        spin_unlock_bh(&ip6_sk_fl_lock);
 }
 
+int ipv6_flowlabel_opt_get(struct sock *sk, struct in6_flowlabel_req *freq)
+{
+       struct ipv6_pinfo *np = inet6_sk(sk);
+       struct ipv6_fl_socklist *sfl;
+
+       rcu_read_lock_bh();
+
+       for_each_sk_fl_rcu(np, sfl) {
+               if (sfl->fl->label == (np->flow_label & IPV6_FLOWLABEL_MASK)) {
+                       spin_lock_bh(&ip6_fl_lock);
+                       freq->flr_label = sfl->fl->label;
+                       freq->flr_dst = sfl->fl->dst;
+                       freq->flr_share = sfl->fl->share;
+                       freq->flr_expires = (sfl->fl->expires - jiffies) / HZ;
+                       freq->flr_linger = sfl->fl->linger / HZ;
+
+                       spin_unlock_bh(&ip6_fl_lock);
+                       rcu_read_unlock_bh();
+                       return 0;
+               }
+       }
+       rcu_read_unlock_bh();
+
+       return -ENOENT;
+}
+
 int ipv6_flowlabel_opt(struct sock *sk, char __user *optval, int optlen)
 {
        int uninitialized_var(err);
index 4919a8e6063ed81b608bfe497a3d7d96f7fb3ca8..1c6ce3119ff8ce45f1b406c9b1356a2400f6fc1d 100644 (file)
@@ -1212,6 +1212,34 @@ static int do_ipv6_getsockopt(struct sock *sk, int level, int optname,
                val = np->sndflow;
                break;
 
+       case IPV6_FLOWLABEL_MGR:
+       {
+               struct in6_flowlabel_req freq;
+
+               if (len < sizeof(freq))
+                       return -EINVAL;
+
+               if (copy_from_user(&freq, optval, sizeof(freq)))
+                       return -EFAULT;
+
+               if (freq.flr_action != IPV6_FL_A_GET)
+                       return -EINVAL;
+
+               len = sizeof(freq);
+               memset(&freq, 0, sizeof(freq));
+
+               val = ipv6_flowlabel_opt_get(sk, &freq);
+               if (val < 0)
+                       return val;
+
+               if (put_user(len, optlen))
+                       return -EFAULT;
+               if (copy_to_user(optval, &freq, len))
+                       return -EFAULT;
+
+               return 0;
+       }
+
        case IPV6_ADDR_PREFERENCES:
                val = 0;