Loading net/ipv6/ip6_flowlabel.c +166 −143 Original line number Diff line number Diff line Loading @@ -533,27 +533,16 @@ int ipv6_flowlabel_opt_get(struct sock *sk, struct in6_flowlabel_req *freq, return -ENOENT; } int ipv6_flowlabel_opt(struct sock *sk, char __user *optval, int optlen) #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) { 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; struct ipv6_fl_socklist *sfl; switch (freq.flr_action) { case IPV6_FL_A_PUT: if (freq.flr_flags & IPV6_FL_F_REFLECT) { if (freq->flr_flags & IPV6_FL_F_REFLECT) { if (sk->sk_protocol != IPPROTO_TCP) return -ENOPROTOOPT; if (!np->repflow) Loading @@ -562,13 +551,18 @@ int ipv6_flowlabel_opt(struct sock *sk, char __user *optval, int optlen) 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; (sfl = socklist_dereference(*sflp)) != NULL; sflp = &sfl->next) { if (sfl->fl->label == freq.flr_label) { if (freq.flr_label == (np->flow_label&IPV6_FLOWLABEL_MASK)) 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); Loading @@ -576,35 +570,49 @@ int ipv6_flowlabel_opt(struct sock *sk, char __user *optval, int optlen) kfree_rcu(sfl, rcu); return 0; } } spin_unlock_bh(&ip6_sk_fl_lock); return -ESRCH; case IPV6_FL_A_RENEW: 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); 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 && if (freq->flr_share == IPV6_FL_S_NONE && ns_capable(net->user_ns, CAP_NET_ADMIN)) { fl = fl_lookup(net, freq.flr_label); struct ip6_flowlabel *fl = fl_lookup(net, freq->flr_label); if (fl) { err = fl6_renew(fl, freq.flr_linger, freq.flr_expires); err = fl6_renew(fl, freq->flr_linger, freq->flr_expires); fl_release(fl); return err; } } return -ESRCH; } case IPV6_FL_A_GET: if (freq.flr_flags & IPV6_FL_F_REFLECT) { 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; Loading @@ -612,29 +620,28 @@ int ipv6_flowlabel_opt(struct sock *sk, char __user *optval, int optlen) if (sk->sk_protocol != IPPROTO_TCP) return -ENOPROTOOPT; np->repflow = 1; return 0; } if (freq.flr_label & ~IPV6_FLOWLABEL_MASK) if (freq->flr_label & ~IPV6_FLOWLABEL_MASK) return -EINVAL; if (net->ipv6.sysctl.flowlabel_state_ranges && (freq.flr_label & IPV6_FLOWLABEL_STATELESS_FLAG)) (freq->flr_label & IPV6_FLOWLABEL_STATELESS_FLAG)) return -ERANGE; fl = fl_create(net, sk, &freq, optval, optlen, &err); fl = fl_create(net, sk, freq, optval, optlen, &err); if (!fl) return err; sfl1 = kmalloc(sizeof(*sfl1), GFP_KERNEL); if (freq.flr_label) { 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) { if (sfl->fl->label == freq->flr_label) { if (freq->flr_flags & IPV6_FL_F_EXCL) { rcu_read_unlock_bh(); goto done; } Loading @@ -647,11 +654,11 @@ int ipv6_flowlabel_opt(struct sock *sk, char __user *optval, int optlen) rcu_read_unlock_bh(); if (!fl1) fl1 = fl_lookup(net, freq.flr_label); fl1 = fl_lookup(net, freq->flr_label); if (fl1) { recheck: err = -EEXIST; if (freq.flr_flags&IPV6_FL_F_EXCL) if (freq->flr_flags&IPV6_FL_F_EXCL) goto release; err = -EPERM; if (fl1->share == IPV6_FL_S_EXCL || Loading Loading @@ -679,7 +686,7 @@ int ipv6_flowlabel_opt(struct sock *sk, char __user *optval, int optlen) } } err = -ENOENT; if (!(freq.flr_flags&IPV6_FL_F_CREATE)) if (!(freq->flr_flags & IPV6_FL_F_CREATE)) goto done; err = -ENOMEM; Loading @@ -690,11 +697,11 @@ int ipv6_flowlabel_opt(struct sock *sk, char __user *optval, int optlen) if (err != 0) goto done; fl1 = fl_intern(net, fl, freq.flr_label); fl1 = fl_intern(net, fl, freq->flr_label); if (fl1) goto recheck; if (!freq.flr_label) { if (!freq->flr_label) { if (copy_to_user(&((struct in6_flowlabel_req __user *) optval)->flr_label, &fl->label, sizeof(fl->label))) { /* Intentionally ignore fault. */ Loading @@ -703,17 +710,33 @@ int ipv6_flowlabel_opt(struct sock *sk, char __user *optval, int optlen) fl_link(np, sfl1, fl); return 0; default: return -EINVAL; } done: fl_free(fl); kfree(sfl1); return err; } int ipv6_flowlabel_opt(struct sock *sk, char __user *optval, int optlen) { struct in6_flowlabel_req freq; 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: return ipv6_flowlabel_put(sk, &freq); case IPV6_FL_A_RENEW: return ipv6_flowlabel_renew(sk, &freq); case IPV6_FL_A_GET: return ipv6_flowlabel_get(sk, &freq, optval, optlen); default: return -EINVAL; } } #ifdef CONFIG_PROC_FS struct ip6fl_iter_state { Loading Loading
net/ipv6/ip6_flowlabel.c +166 −143 Original line number Diff line number Diff line Loading @@ -533,27 +533,16 @@ int ipv6_flowlabel_opt_get(struct sock *sk, struct in6_flowlabel_req *freq, return -ENOENT; } int ipv6_flowlabel_opt(struct sock *sk, char __user *optval, int optlen) #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) { 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; struct ipv6_fl_socklist *sfl; switch (freq.flr_action) { case IPV6_FL_A_PUT: if (freq.flr_flags & IPV6_FL_F_REFLECT) { if (freq->flr_flags & IPV6_FL_F_REFLECT) { if (sk->sk_protocol != IPPROTO_TCP) return -ENOPROTOOPT; if (!np->repflow) Loading @@ -562,13 +551,18 @@ int ipv6_flowlabel_opt(struct sock *sk, char __user *optval, int optlen) 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; (sfl = socklist_dereference(*sflp)) != NULL; sflp = &sfl->next) { if (sfl->fl->label == freq.flr_label) { if (freq.flr_label == (np->flow_label&IPV6_FLOWLABEL_MASK)) 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); Loading @@ -576,35 +570,49 @@ int ipv6_flowlabel_opt(struct sock *sk, char __user *optval, int optlen) kfree_rcu(sfl, rcu); return 0; } } spin_unlock_bh(&ip6_sk_fl_lock); return -ESRCH; case IPV6_FL_A_RENEW: 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); 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 && if (freq->flr_share == IPV6_FL_S_NONE && ns_capable(net->user_ns, CAP_NET_ADMIN)) { fl = fl_lookup(net, freq.flr_label); struct ip6_flowlabel *fl = fl_lookup(net, freq->flr_label); if (fl) { err = fl6_renew(fl, freq.flr_linger, freq.flr_expires); err = fl6_renew(fl, freq->flr_linger, freq->flr_expires); fl_release(fl); return err; } } return -ESRCH; } case IPV6_FL_A_GET: if (freq.flr_flags & IPV6_FL_F_REFLECT) { 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; Loading @@ -612,29 +620,28 @@ int ipv6_flowlabel_opt(struct sock *sk, char __user *optval, int optlen) if (sk->sk_protocol != IPPROTO_TCP) return -ENOPROTOOPT; np->repflow = 1; return 0; } if (freq.flr_label & ~IPV6_FLOWLABEL_MASK) if (freq->flr_label & ~IPV6_FLOWLABEL_MASK) return -EINVAL; if (net->ipv6.sysctl.flowlabel_state_ranges && (freq.flr_label & IPV6_FLOWLABEL_STATELESS_FLAG)) (freq->flr_label & IPV6_FLOWLABEL_STATELESS_FLAG)) return -ERANGE; fl = fl_create(net, sk, &freq, optval, optlen, &err); fl = fl_create(net, sk, freq, optval, optlen, &err); if (!fl) return err; sfl1 = kmalloc(sizeof(*sfl1), GFP_KERNEL); if (freq.flr_label) { 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) { if (sfl->fl->label == freq->flr_label) { if (freq->flr_flags & IPV6_FL_F_EXCL) { rcu_read_unlock_bh(); goto done; } Loading @@ -647,11 +654,11 @@ int ipv6_flowlabel_opt(struct sock *sk, char __user *optval, int optlen) rcu_read_unlock_bh(); if (!fl1) fl1 = fl_lookup(net, freq.flr_label); fl1 = fl_lookup(net, freq->flr_label); if (fl1) { recheck: err = -EEXIST; if (freq.flr_flags&IPV6_FL_F_EXCL) if (freq->flr_flags&IPV6_FL_F_EXCL) goto release; err = -EPERM; if (fl1->share == IPV6_FL_S_EXCL || Loading Loading @@ -679,7 +686,7 @@ int ipv6_flowlabel_opt(struct sock *sk, char __user *optval, int optlen) } } err = -ENOENT; if (!(freq.flr_flags&IPV6_FL_F_CREATE)) if (!(freq->flr_flags & IPV6_FL_F_CREATE)) goto done; err = -ENOMEM; Loading @@ -690,11 +697,11 @@ int ipv6_flowlabel_opt(struct sock *sk, char __user *optval, int optlen) if (err != 0) goto done; fl1 = fl_intern(net, fl, freq.flr_label); fl1 = fl_intern(net, fl, freq->flr_label); if (fl1) goto recheck; if (!freq.flr_label) { if (!freq->flr_label) { if (copy_to_user(&((struct in6_flowlabel_req __user *) optval)->flr_label, &fl->label, sizeof(fl->label))) { /* Intentionally ignore fault. */ Loading @@ -703,17 +710,33 @@ int ipv6_flowlabel_opt(struct sock *sk, char __user *optval, int optlen) fl_link(np, sfl1, fl); return 0; default: return -EINVAL; } done: fl_free(fl); kfree(sfl1); return err; } int ipv6_flowlabel_opt(struct sock *sk, char __user *optval, int optlen) { struct in6_flowlabel_req freq; 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: return ipv6_flowlabel_put(sk, &freq); case IPV6_FL_A_RENEW: return ipv6_flowlabel_renew(sk, &freq); case IPV6_FL_A_GET: return ipv6_flowlabel_get(sk, &freq, optval, optlen); default: return -EINVAL; } } #ifdef CONFIG_PROC_FS struct ip6fl_iter_state { Loading