ip6_tunnel: fix error code when tunnel exists

After commit 2b0bb01b6e, the kernel returns -ENOBUFS when user tries to add
an existing tunnel with ioctl API:
$ ip -6 tunnel add ip6tnl1 mode ip6ip6 dev eth1
add tunnel "ip6tnl0" failed: No buffer space available

It's confusing, the right error is EEXIST.

This patch also change a bit the code returned:
 - ENOBUFS -> ENOMEM
 - ENOENT -> ENODEV

Fixes: 2b0bb01b6e ("ip6_tunnel: Return an error when adding an existing tunnel.")
CC: Steffen Klassert <steffen.klassert@secunet.com>
Reported-by: Pierre Cheynier <me@pierre-cheynier.net>
Signed-off-by: Nicolas Dichtel <nicolas.dichtel@6wind.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
Nicolas Dichtel 2015-03-16 15:56:05 +01:00 committed by David S. Miller
parent ad41faa88e
commit 37355565ba

View File

@ -314,7 +314,7 @@ out:
* Create tunnel matching given parameters. * Create tunnel matching given parameters.
* *
* Return: * Return:
* created tunnel or NULL * created tunnel or error pointer
**/ **/
static struct ip6_tnl *ip6_tnl_create(struct net *net, struct __ip6_tnl_parm *p) static struct ip6_tnl *ip6_tnl_create(struct net *net, struct __ip6_tnl_parm *p)
@ -322,7 +322,7 @@ static struct ip6_tnl *ip6_tnl_create(struct net *net, struct __ip6_tnl_parm *p)
struct net_device *dev; struct net_device *dev;
struct ip6_tnl *t; struct ip6_tnl *t;
char name[IFNAMSIZ]; char name[IFNAMSIZ];
int err; int err = -ENOMEM;
if (p->name[0]) if (p->name[0])
strlcpy(name, p->name, IFNAMSIZ); strlcpy(name, p->name, IFNAMSIZ);
@ -348,7 +348,7 @@ static struct ip6_tnl *ip6_tnl_create(struct net *net, struct __ip6_tnl_parm *p)
failed_free: failed_free:
ip6_dev_free(dev); ip6_dev_free(dev);
failed: failed:
return NULL; return ERR_PTR(err);
} }
/** /**
@ -362,7 +362,7 @@ failed:
* tunnel device is created and registered for use. * tunnel device is created and registered for use.
* *
* Return: * Return:
* matching tunnel or NULL * matching tunnel or error pointer
**/ **/
static struct ip6_tnl *ip6_tnl_locate(struct net *net, static struct ip6_tnl *ip6_tnl_locate(struct net *net,
@ -380,13 +380,13 @@ static struct ip6_tnl *ip6_tnl_locate(struct net *net,
if (ipv6_addr_equal(local, &t->parms.laddr) && if (ipv6_addr_equal(local, &t->parms.laddr) &&
ipv6_addr_equal(remote, &t->parms.raddr)) { ipv6_addr_equal(remote, &t->parms.raddr)) {
if (create) if (create)
return NULL; return ERR_PTR(-EEXIST);
return t; return t;
} }
} }
if (!create) if (!create)
return NULL; return ERR_PTR(-ENODEV);
return ip6_tnl_create(net, p); return ip6_tnl_create(net, p);
} }
@ -1420,7 +1420,7 @@ ip6_tnl_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
} }
ip6_tnl_parm_from_user(&p1, &p); ip6_tnl_parm_from_user(&p1, &p);
t = ip6_tnl_locate(net, &p1, 0); t = ip6_tnl_locate(net, &p1, 0);
if (t == NULL) if (IS_ERR(t))
t = netdev_priv(dev); t = netdev_priv(dev);
} else { } else {
memset(&p, 0, sizeof(p)); memset(&p, 0, sizeof(p));
@ -1445,7 +1445,7 @@ ip6_tnl_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
ip6_tnl_parm_from_user(&p1, &p); ip6_tnl_parm_from_user(&p1, &p);
t = ip6_tnl_locate(net, &p1, cmd == SIOCADDTUNNEL); t = ip6_tnl_locate(net, &p1, cmd == SIOCADDTUNNEL);
if (cmd == SIOCCHGTUNNEL) { if (cmd == SIOCCHGTUNNEL) {
if (t != NULL) { if (!IS_ERR(t)) {
if (t->dev != dev) { if (t->dev != dev) {
err = -EEXIST; err = -EEXIST;
break; break;
@ -1457,14 +1457,15 @@ ip6_tnl_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
else else
err = ip6_tnl_update(t, &p1); err = ip6_tnl_update(t, &p1);
} }
if (t) { if (!IS_ERR(t)) {
err = 0; err = 0;
ip6_tnl_parm_to_user(&p, &t->parms); ip6_tnl_parm_to_user(&p, &t->parms);
if (copy_to_user(ifr->ifr_ifru.ifru_data, &p, sizeof(p))) if (copy_to_user(ifr->ifr_ifru.ifru_data, &p, sizeof(p)))
err = -EFAULT; err = -EFAULT;
} else } else {
err = (cmd == SIOCADDTUNNEL ? -ENOBUFS : -ENOENT); err = PTR_ERR(t);
}
break; break;
case SIOCDELTUNNEL: case SIOCDELTUNNEL:
err = -EPERM; err = -EPERM;
@ -1478,7 +1479,7 @@ ip6_tnl_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
err = -ENOENT; err = -ENOENT;
ip6_tnl_parm_from_user(&p1, &p); ip6_tnl_parm_from_user(&p1, &p);
t = ip6_tnl_locate(net, &p1, 0); t = ip6_tnl_locate(net, &p1, 0);
if (t == NULL) if (IS_ERR(t))
break; break;
err = -EPERM; err = -EPERM;
if (t->dev == ip6n->fb_tnl_dev) if (t->dev == ip6n->fb_tnl_dev)
@ -1672,12 +1673,13 @@ static int ip6_tnl_newlink(struct net *src_net, struct net_device *dev,
struct nlattr *tb[], struct nlattr *data[]) struct nlattr *tb[], struct nlattr *data[])
{ {
struct net *net = dev_net(dev); struct net *net = dev_net(dev);
struct ip6_tnl *nt; struct ip6_tnl *nt, *t;
nt = netdev_priv(dev); nt = netdev_priv(dev);
ip6_tnl_netlink_parms(data, &nt->parms); ip6_tnl_netlink_parms(data, &nt->parms);
if (ip6_tnl_locate(net, &nt->parms, 0)) t = ip6_tnl_locate(net, &nt->parms, 0);
if (!IS_ERR(t))
return -EEXIST; return -EEXIST;
return ip6_tnl_create2(dev); return ip6_tnl_create2(dev);
@ -1697,8 +1699,7 @@ static int ip6_tnl_changelink(struct net_device *dev, struct nlattr *tb[],
ip6_tnl_netlink_parms(data, &p); ip6_tnl_netlink_parms(data, &p);
t = ip6_tnl_locate(net, &p, 0); t = ip6_tnl_locate(net, &p, 0);
if (!IS_ERR(t)) {
if (t) {
if (t->dev != dev) if (t->dev != dev)
return -EEXIST; return -EEXIST;
} else } else