mirror of
https://mirrors.bfsu.edu.cn/git/linux.git
synced 2024-11-11 12:28:41 +08:00
net/ipv6: split up ipv6_flowlabel_opt
Split ipv6_flowlabel_opt into a subfunction for each action and a small wrapper. Signed-off-by: Christoph Hellwig <hch@lst.de> Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
parent
b43c615313
commit
ff6a4cf214
@ -533,185 +533,208 @@ int ipv6_flowlabel_opt_get(struct sock *sk, struct in6_flowlabel_req *freq,
|
||||
return -ENOENT;
|
||||
}
|
||||
|
||||
#define socklist_dereference(__sflp) \
|
||||
rcu_dereference_protected(__sflp, lockdep_is_held(&ip6_sk_fl_lock))
|
||||
|
||||
static int ipv6_flowlabel_put(struct sock *sk, struct in6_flowlabel_req *freq)
|
||||
{
|
||||
struct ipv6_pinfo *np = inet6_sk(sk);
|
||||
struct ipv6_fl_socklist __rcu **sflp;
|
||||
struct ipv6_fl_socklist *sfl;
|
||||
|
||||
if (freq->flr_flags & IPV6_FL_F_REFLECT) {
|
||||
if (sk->sk_protocol != IPPROTO_TCP)
|
||||
return -ENOPROTOOPT;
|
||||
if (!np->repflow)
|
||||
return -ESRCH;
|
||||
np->flow_label = 0;
|
||||
np->repflow = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
spin_lock_bh(&ip6_sk_fl_lock);
|
||||
for (sflp = &np->ipv6_fl_list;
|
||||
(sfl = socklist_dereference(*sflp)) != NULL;
|
||||
sflp = &sfl->next) {
|
||||
if (sfl->fl->label == freq->flr_label)
|
||||
goto found;
|
||||
}
|
||||
spin_unlock_bh(&ip6_sk_fl_lock);
|
||||
return -ESRCH;
|
||||
found:
|
||||
if (freq->flr_label == (np->flow_label & IPV6_FLOWLABEL_MASK))
|
||||
np->flow_label &= ~IPV6_FLOWLABEL_MASK;
|
||||
*sflp = sfl->next;
|
||||
spin_unlock_bh(&ip6_sk_fl_lock);
|
||||
fl_release(sfl->fl);
|
||||
kfree_rcu(sfl, rcu);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ipv6_flowlabel_renew(struct sock *sk, struct in6_flowlabel_req *freq)
|
||||
{
|
||||
struct ipv6_pinfo *np = inet6_sk(sk);
|
||||
struct net *net = sock_net(sk);
|
||||
struct ipv6_fl_socklist *sfl;
|
||||
int err;
|
||||
|
||||
rcu_read_lock_bh();
|
||||
for_each_sk_fl_rcu(np, sfl) {
|
||||
if (sfl->fl->label == freq->flr_label) {
|
||||
err = fl6_renew(sfl->fl, freq->flr_linger,
|
||||
freq->flr_expires);
|
||||
rcu_read_unlock_bh();
|
||||
return err;
|
||||
}
|
||||
}
|
||||
rcu_read_unlock_bh();
|
||||
|
||||
if (freq->flr_share == IPV6_FL_S_NONE &&
|
||||
ns_capable(net->user_ns, CAP_NET_ADMIN)) {
|
||||
struct ip6_flowlabel *fl = fl_lookup(net, freq->flr_label);
|
||||
|
||||
if (fl) {
|
||||
err = fl6_renew(fl, freq->flr_linger,
|
||||
freq->flr_expires);
|
||||
fl_release(fl);
|
||||
return err;
|
||||
}
|
||||
}
|
||||
return -ESRCH;
|
||||
}
|
||||
|
||||
static int ipv6_flowlabel_get(struct sock *sk, struct in6_flowlabel_req *freq,
|
||||
void __user *optval, int optlen)
|
||||
{
|
||||
struct ipv6_fl_socklist *sfl, *sfl1 = NULL;
|
||||
struct ip6_flowlabel *fl, *fl1 = NULL;
|
||||
struct ipv6_pinfo *np = inet6_sk(sk);
|
||||
struct net *net = sock_net(sk);
|
||||
int uninitialized_var(err);
|
||||
|
||||
if (freq->flr_flags & IPV6_FL_F_REFLECT) {
|
||||
if (net->ipv6.sysctl.flowlabel_consistency) {
|
||||
net_info_ratelimited("Can not set IPV6_FL_F_REFLECT if flowlabel_consistency sysctl is enable\n");
|
||||
return -EPERM;
|
||||
}
|
||||
|
||||
if (sk->sk_protocol != IPPROTO_TCP)
|
||||
return -ENOPROTOOPT;
|
||||
np->repflow = 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (freq->flr_label & ~IPV6_FLOWLABEL_MASK)
|
||||
return -EINVAL;
|
||||
if (net->ipv6.sysctl.flowlabel_state_ranges &&
|
||||
(freq->flr_label & IPV6_FLOWLABEL_STATELESS_FLAG))
|
||||
return -ERANGE;
|
||||
|
||||
fl = fl_create(net, sk, freq, optval, optlen, &err);
|
||||
if (!fl)
|
||||
return err;
|
||||
|
||||
sfl1 = kmalloc(sizeof(*sfl1), GFP_KERNEL);
|
||||
|
||||
if (freq->flr_label) {
|
||||
err = -EEXIST;
|
||||
rcu_read_lock_bh();
|
||||
for_each_sk_fl_rcu(np, sfl) {
|
||||
if (sfl->fl->label == freq->flr_label) {
|
||||
if (freq->flr_flags & IPV6_FL_F_EXCL) {
|
||||
rcu_read_unlock_bh();
|
||||
goto done;
|
||||
}
|
||||
fl1 = sfl->fl;
|
||||
if (!atomic_inc_not_zero(&fl1->users))
|
||||
fl1 = NULL;
|
||||
break;
|
||||
}
|
||||
}
|
||||
rcu_read_unlock_bh();
|
||||
|
||||
if (!fl1)
|
||||
fl1 = fl_lookup(net, freq->flr_label);
|
||||
if (fl1) {
|
||||
recheck:
|
||||
err = -EEXIST;
|
||||
if (freq->flr_flags&IPV6_FL_F_EXCL)
|
||||
goto release;
|
||||
err = -EPERM;
|
||||
if (fl1->share == IPV6_FL_S_EXCL ||
|
||||
fl1->share != fl->share ||
|
||||
((fl1->share == IPV6_FL_S_PROCESS) &&
|
||||
(fl1->owner.pid != fl->owner.pid)) ||
|
||||
((fl1->share == IPV6_FL_S_USER) &&
|
||||
!uid_eq(fl1->owner.uid, fl->owner.uid)))
|
||||
goto release;
|
||||
|
||||
err = -ENOMEM;
|
||||
if (!sfl1)
|
||||
goto release;
|
||||
if (fl->linger > fl1->linger)
|
||||
fl1->linger = fl->linger;
|
||||
if ((long)(fl->expires - fl1->expires) > 0)
|
||||
fl1->expires = fl->expires;
|
||||
fl_link(np, sfl1, fl1);
|
||||
fl_free(fl);
|
||||
return 0;
|
||||
|
||||
release:
|
||||
fl_release(fl1);
|
||||
goto done;
|
||||
}
|
||||
}
|
||||
err = -ENOENT;
|
||||
if (!(freq->flr_flags & IPV6_FL_F_CREATE))
|
||||
goto done;
|
||||
|
||||
err = -ENOMEM;
|
||||
if (!sfl1)
|
||||
goto done;
|
||||
|
||||
err = mem_check(sk);
|
||||
if (err != 0)
|
||||
goto done;
|
||||
|
||||
fl1 = fl_intern(net, fl, freq->flr_label);
|
||||
if (fl1)
|
||||
goto recheck;
|
||||
|
||||
if (!freq->flr_label) {
|
||||
if (copy_to_user(&((struct in6_flowlabel_req __user *) optval)->flr_label,
|
||||
&fl->label, sizeof(fl->label))) {
|
||||
/* Intentionally ignore fault. */
|
||||
}
|
||||
}
|
||||
|
||||
fl_link(np, sfl1, fl);
|
||||
return 0;
|
||||
done:
|
||||
fl_free(fl);
|
||||
kfree(sfl1);
|
||||
return err;
|
||||
}
|
||||
|
||||
int ipv6_flowlabel_opt(struct sock *sk, char __user *optval, int optlen)
|
||||
{
|
||||
int uninitialized_var(err);
|
||||
struct net *net = sock_net(sk);
|
||||
struct ipv6_pinfo *np = inet6_sk(sk);
|
||||
struct in6_flowlabel_req freq;
|
||||
struct ipv6_fl_socklist *sfl1 = NULL;
|
||||
struct ipv6_fl_socklist *sfl;
|
||||
struct ipv6_fl_socklist __rcu **sflp;
|
||||
struct ip6_flowlabel *fl, *fl1 = NULL;
|
||||
|
||||
|
||||
if (optlen < sizeof(freq))
|
||||
return -EINVAL;
|
||||
|
||||
if (copy_from_user(&freq, optval, sizeof(freq)))
|
||||
return -EFAULT;
|
||||
|
||||
switch (freq.flr_action) {
|
||||
case IPV6_FL_A_PUT:
|
||||
if (freq.flr_flags & IPV6_FL_F_REFLECT) {
|
||||
if (sk->sk_protocol != IPPROTO_TCP)
|
||||
return -ENOPROTOOPT;
|
||||
if (!np->repflow)
|
||||
return -ESRCH;
|
||||
np->flow_label = 0;
|
||||
np->repflow = 0;
|
||||
return 0;
|
||||
}
|
||||
spin_lock_bh(&ip6_sk_fl_lock);
|
||||
for (sflp = &np->ipv6_fl_list;
|
||||
(sfl = rcu_dereference_protected(*sflp,
|
||||
lockdep_is_held(&ip6_sk_fl_lock))) != NULL;
|
||||
sflp = &sfl->next) {
|
||||
if (sfl->fl->label == freq.flr_label) {
|
||||
if (freq.flr_label == (np->flow_label&IPV6_FLOWLABEL_MASK))
|
||||
np->flow_label &= ~IPV6_FLOWLABEL_MASK;
|
||||
*sflp = sfl->next;
|
||||
spin_unlock_bh(&ip6_sk_fl_lock);
|
||||
fl_release(sfl->fl);
|
||||
kfree_rcu(sfl, rcu);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
spin_unlock_bh(&ip6_sk_fl_lock);
|
||||
return -ESRCH;
|
||||
|
||||
return ipv6_flowlabel_put(sk, &freq);
|
||||
case IPV6_FL_A_RENEW:
|
||||
rcu_read_lock_bh();
|
||||
for_each_sk_fl_rcu(np, sfl) {
|
||||
if (sfl->fl->label == freq.flr_label) {
|
||||
err = fl6_renew(sfl->fl, freq.flr_linger, freq.flr_expires);
|
||||
rcu_read_unlock_bh();
|
||||
return err;
|
||||
}
|
||||
}
|
||||
rcu_read_unlock_bh();
|
||||
|
||||
if (freq.flr_share == IPV6_FL_S_NONE &&
|
||||
ns_capable(net->user_ns, CAP_NET_ADMIN)) {
|
||||
fl = fl_lookup(net, freq.flr_label);
|
||||
if (fl) {
|
||||
err = fl6_renew(fl, freq.flr_linger, freq.flr_expires);
|
||||
fl_release(fl);
|
||||
return err;
|
||||
}
|
||||
}
|
||||
return -ESRCH;
|
||||
|
||||
return ipv6_flowlabel_renew(sk, &freq);
|
||||
case IPV6_FL_A_GET:
|
||||
if (freq.flr_flags & IPV6_FL_F_REFLECT) {
|
||||
struct net *net = sock_net(sk);
|
||||
if (net->ipv6.sysctl.flowlabel_consistency) {
|
||||
net_info_ratelimited("Can not set IPV6_FL_F_REFLECT if flowlabel_consistency sysctl is enable\n");
|
||||
return -EPERM;
|
||||
}
|
||||
|
||||
if (sk->sk_protocol != IPPROTO_TCP)
|
||||
return -ENOPROTOOPT;
|
||||
|
||||
np->repflow = 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (freq.flr_label & ~IPV6_FLOWLABEL_MASK)
|
||||
return -EINVAL;
|
||||
|
||||
if (net->ipv6.sysctl.flowlabel_state_ranges &&
|
||||
(freq.flr_label & IPV6_FLOWLABEL_STATELESS_FLAG))
|
||||
return -ERANGE;
|
||||
|
||||
fl = fl_create(net, sk, &freq, optval, optlen, &err);
|
||||
if (!fl)
|
||||
return err;
|
||||
sfl1 = kmalloc(sizeof(*sfl1), GFP_KERNEL);
|
||||
|
||||
if (freq.flr_label) {
|
||||
err = -EEXIST;
|
||||
rcu_read_lock_bh();
|
||||
for_each_sk_fl_rcu(np, sfl) {
|
||||
if (sfl->fl->label == freq.flr_label) {
|
||||
if (freq.flr_flags&IPV6_FL_F_EXCL) {
|
||||
rcu_read_unlock_bh();
|
||||
goto done;
|
||||
}
|
||||
fl1 = sfl->fl;
|
||||
if (!atomic_inc_not_zero(&fl1->users))
|
||||
fl1 = NULL;
|
||||
break;
|
||||
}
|
||||
}
|
||||
rcu_read_unlock_bh();
|
||||
|
||||
if (!fl1)
|
||||
fl1 = fl_lookup(net, freq.flr_label);
|
||||
if (fl1) {
|
||||
recheck:
|
||||
err = -EEXIST;
|
||||
if (freq.flr_flags&IPV6_FL_F_EXCL)
|
||||
goto release;
|
||||
err = -EPERM;
|
||||
if (fl1->share == IPV6_FL_S_EXCL ||
|
||||
fl1->share != fl->share ||
|
||||
((fl1->share == IPV6_FL_S_PROCESS) &&
|
||||
(fl1->owner.pid != fl->owner.pid)) ||
|
||||
((fl1->share == IPV6_FL_S_USER) &&
|
||||
!uid_eq(fl1->owner.uid, fl->owner.uid)))
|
||||
goto release;
|
||||
|
||||
err = -ENOMEM;
|
||||
if (!sfl1)
|
||||
goto release;
|
||||
if (fl->linger > fl1->linger)
|
||||
fl1->linger = fl->linger;
|
||||
if ((long)(fl->expires - fl1->expires) > 0)
|
||||
fl1->expires = fl->expires;
|
||||
fl_link(np, sfl1, fl1);
|
||||
fl_free(fl);
|
||||
return 0;
|
||||
|
||||
release:
|
||||
fl_release(fl1);
|
||||
goto done;
|
||||
}
|
||||
}
|
||||
err = -ENOENT;
|
||||
if (!(freq.flr_flags&IPV6_FL_F_CREATE))
|
||||
goto done;
|
||||
|
||||
err = -ENOMEM;
|
||||
if (!sfl1)
|
||||
goto done;
|
||||
|
||||
err = mem_check(sk);
|
||||
if (err != 0)
|
||||
goto done;
|
||||
|
||||
fl1 = fl_intern(net, fl, freq.flr_label);
|
||||
if (fl1)
|
||||
goto recheck;
|
||||
|
||||
if (!freq.flr_label) {
|
||||
if (copy_to_user(&((struct in6_flowlabel_req __user *) optval)->flr_label,
|
||||
&fl->label, sizeof(fl->label))) {
|
||||
/* Intentionally ignore fault. */
|
||||
}
|
||||
}
|
||||
|
||||
fl_link(np, sfl1, fl);
|
||||
return 0;
|
||||
|
||||
return ipv6_flowlabel_get(sk, &freq, optval, optlen);
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
done:
|
||||
fl_free(fl);
|
||||
kfree(sfl1);
|
||||
return err;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PROC_FS
|
||||
|
Loading…
Reference in New Issue
Block a user