|
@@ -2911,22 +2911,40 @@ static int packet_release(struct socket *sock)
|
|
|
* Attach a packet hook.
|
|
|
*/
|
|
|
|
|
|
-static int packet_do_bind(struct sock *sk, struct net_device *dev, __be16 proto)
|
|
|
+static int packet_do_bind(struct sock *sk, const char *name, int ifindex,
|
|
|
+ __be16 proto)
|
|
|
{
|
|
|
struct packet_sock *po = pkt_sk(sk);
|
|
|
struct net_device *dev_curr;
|
|
|
__be16 proto_curr;
|
|
|
bool need_rehook;
|
|
|
+ struct net_device *dev = NULL;
|
|
|
+ int ret = 0;
|
|
|
+ bool unlisted = false;
|
|
|
|
|
|
- if (po->fanout) {
|
|
|
- if (dev)
|
|
|
- dev_put(dev);
|
|
|
-
|
|
|
+ if (po->fanout)
|
|
|
return -EINVAL;
|
|
|
- }
|
|
|
|
|
|
lock_sock(sk);
|
|
|
spin_lock(&po->bind_lock);
|
|
|
+ rcu_read_lock();
|
|
|
+
|
|
|
+ if (name) {
|
|
|
+ dev = dev_get_by_name_rcu(sock_net(sk), name);
|
|
|
+ if (!dev) {
|
|
|
+ ret = -ENODEV;
|
|
|
+ goto out_unlock;
|
|
|
+ }
|
|
|
+ } else if (ifindex) {
|
|
|
+ dev = dev_get_by_index_rcu(sock_net(sk), ifindex);
|
|
|
+ if (!dev) {
|
|
|
+ ret = -ENODEV;
|
|
|
+ goto out_unlock;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ if (dev)
|
|
|
+ dev_hold(dev);
|
|
|
|
|
|
proto_curr = po->prot_hook.type;
|
|
|
dev_curr = po->prot_hook.dev;
|
|
@@ -2934,14 +2952,29 @@ static int packet_do_bind(struct sock *sk, struct net_device *dev, __be16 proto)
|
|
|
need_rehook = proto_curr != proto || dev_curr != dev;
|
|
|
|
|
|
if (need_rehook) {
|
|
|
- unregister_prot_hook(sk, true);
|
|
|
+ if (po->running) {
|
|
|
+ rcu_read_unlock();
|
|
|
+ __unregister_prot_hook(sk, true);
|
|
|
+ rcu_read_lock();
|
|
|
+ dev_curr = po->prot_hook.dev;
|
|
|
+ if (dev)
|
|
|
+ unlisted = !dev_get_by_index_rcu(sock_net(sk),
|
|
|
+ dev->ifindex);
|
|
|
+ }
|
|
|
|
|
|
po->num = proto;
|
|
|
po->prot_hook.type = proto;
|
|
|
- po->prot_hook.dev = dev;
|
|
|
|
|
|
- po->ifindex = dev ? dev->ifindex : 0;
|
|
|
- packet_cached_dev_assign(po, dev);
|
|
|
+ if (unlikely(unlisted)) {
|
|
|
+ dev_put(dev);
|
|
|
+ po->prot_hook.dev = NULL;
|
|
|
+ po->ifindex = -1;
|
|
|
+ packet_cached_dev_reset(po);
|
|
|
+ } else {
|
|
|
+ po->prot_hook.dev = dev;
|
|
|
+ po->ifindex = dev ? dev->ifindex : 0;
|
|
|
+ packet_cached_dev_assign(po, dev);
|
|
|
+ }
|
|
|
}
|
|
|
if (dev_curr)
|
|
|
dev_put(dev_curr);
|
|
@@ -2949,7 +2982,7 @@ static int packet_do_bind(struct sock *sk, struct net_device *dev, __be16 proto)
|
|
|
if (proto == 0 || !need_rehook)
|
|
|
goto out_unlock;
|
|
|
|
|
|
- if (!dev || (dev->flags & IFF_UP)) {
|
|
|
+ if (!unlisted && (!dev || (dev->flags & IFF_UP))) {
|
|
|
register_prot_hook(sk);
|
|
|
} else {
|
|
|
sk->sk_err = ENETDOWN;
|
|
@@ -2958,9 +2991,10 @@ static int packet_do_bind(struct sock *sk, struct net_device *dev, __be16 proto)
|
|
|
}
|
|
|
|
|
|
out_unlock:
|
|
|
+ rcu_read_unlock();
|
|
|
spin_unlock(&po->bind_lock);
|
|
|
release_sock(sk);
|
|
|
- return 0;
|
|
|
+ return ret;
|
|
|
}
|
|
|
|
|
|
/*
|
|
@@ -2972,8 +3006,6 @@ static int packet_bind_spkt(struct socket *sock, struct sockaddr *uaddr,
|
|
|
{
|
|
|
struct sock *sk = sock->sk;
|
|
|
char name[15];
|
|
|
- struct net_device *dev;
|
|
|
- int err = -ENODEV;
|
|
|
|
|
|
/*
|
|
|
* Check legality
|
|
@@ -2983,19 +3015,13 @@ static int packet_bind_spkt(struct socket *sock, struct sockaddr *uaddr,
|
|
|
return -EINVAL;
|
|
|
strlcpy(name, uaddr->sa_data, sizeof(name));
|
|
|
|
|
|
- dev = dev_get_by_name(sock_net(sk), name);
|
|
|
- if (dev)
|
|
|
- err = packet_do_bind(sk, dev, pkt_sk(sk)->num);
|
|
|
- return err;
|
|
|
+ return packet_do_bind(sk, name, 0, pkt_sk(sk)->num);
|
|
|
}
|
|
|
|
|
|
static int packet_bind(struct socket *sock, struct sockaddr *uaddr, int addr_len)
|
|
|
{
|
|
|
struct sockaddr_ll *sll = (struct sockaddr_ll *)uaddr;
|
|
|
struct sock *sk = sock->sk;
|
|
|
- struct net_device *dev = NULL;
|
|
|
- int err;
|
|
|
-
|
|
|
|
|
|
/*
|
|
|
* Check legality
|
|
@@ -3006,16 +3032,8 @@ static int packet_bind(struct socket *sock, struct sockaddr *uaddr, int addr_len
|
|
|
if (sll->sll_family != AF_PACKET)
|
|
|
return -EINVAL;
|
|
|
|
|
|
- if (sll->sll_ifindex) {
|
|
|
- err = -ENODEV;
|
|
|
- dev = dev_get_by_index(sock_net(sk), sll->sll_ifindex);
|
|
|
- if (dev == NULL)
|
|
|
- goto out;
|
|
|
- }
|
|
|
- err = packet_do_bind(sk, dev, sll->sll_protocol ? : pkt_sk(sk)->num);
|
|
|
-
|
|
|
-out:
|
|
|
- return err;
|
|
|
+ return packet_do_bind(sk, NULL, sll->sll_ifindex,
|
|
|
+ sll->sll_protocol ? : pkt_sk(sk)->num);
|
|
|
}
|
|
|
|
|
|
static struct proto packet_proto = {
|