mirror of
https://github.com/tbsdtv/linux_media.git
synced 2025-07-23 20:51:03 +02:00
net: bridge: Get SIOCGIFBR/SIOCSIFBR ioctl working in compat mode
In compat mode SIOC{G,S}IFBR ioctls were only supporting BRCTL_GET_VERSION returning an artificially version to spur userland tool to use SIOCDEVPRIVATE instead. But some userland tools ignore that and use SIOC{G,S}IFBR unconditionally as seen with busybox's brctl. Example of non working 32-bit brctl with CONFIG_COMPAT=y: $ brctl show brctl: SIOCGIFBR: Invalid argument Example of fixed 32-bit brctl with CONFIG_COMPAT=y: $ brctl show bridge name bridge id STP enabled interfaces br0 Signed-off-by: Remi Pommarel <repk@triplefau.lt> Co-developed-by: Arnd Bergmann <arnd@arndb.de> Signed-off-by: Arnd Bergmann <arnd@arndb.de> Acked-by: Nikolay Aleksandrov <nikolay@nvidia.com> Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
committed by
David S. Miller
parent
32f52e8e78
commit
fd3a459000
@@ -102,37 +102,56 @@ static int add_del_if(struct net_bridge *br, int ifindex, int isadd)
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#define BR_UARGS_MAX 4
|
||||||
|
static int br_dev_read_uargs(unsigned long *args, size_t nr_args,
|
||||||
|
void __user **argp, void __user *data)
|
||||||
|
{
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
if (nr_args < 2 || nr_args > BR_UARGS_MAX)
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
|
if (in_compat_syscall()) {
|
||||||
|
unsigned int cargs[BR_UARGS_MAX];
|
||||||
|
int i;
|
||||||
|
|
||||||
|
ret = copy_from_user(cargs, data, nr_args * sizeof(*cargs));
|
||||||
|
if (ret)
|
||||||
|
goto fault;
|
||||||
|
|
||||||
|
for (i = 0; i < nr_args; ++i)
|
||||||
|
args[i] = cargs[i];
|
||||||
|
|
||||||
|
*argp = compat_ptr(args[1]);
|
||||||
|
} else {
|
||||||
|
ret = copy_from_user(args, data, nr_args * sizeof(*args));
|
||||||
|
if (ret)
|
||||||
|
goto fault;
|
||||||
|
*argp = (void __user *)args[1];
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
fault:
|
||||||
|
return -EFAULT;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Legacy ioctl's through SIOCDEVPRIVATE
|
* Legacy ioctl's through SIOCDEVPRIVATE
|
||||||
* This interface is deprecated because it was too difficult
|
* This interface is deprecated because it was too difficult
|
||||||
* to do the translation for 32/64bit ioctl compatibility.
|
* to do the translation for 32/64bit ioctl compatibility.
|
||||||
*/
|
*/
|
||||||
int br_dev_siocdevprivate(struct net_device *dev, struct ifreq *rq, void __user *data, int cmd)
|
int br_dev_siocdevprivate(struct net_device *dev, struct ifreq *rq,
|
||||||
|
void __user *data, int cmd)
|
||||||
{
|
{
|
||||||
struct net_bridge *br = netdev_priv(dev);
|
struct net_bridge *br = netdev_priv(dev);
|
||||||
struct net_bridge_port *p = NULL;
|
struct net_bridge_port *p = NULL;
|
||||||
unsigned long args[4];
|
unsigned long args[4];
|
||||||
void __user *argp;
|
void __user *argp;
|
||||||
int ret = -EOPNOTSUPP;
|
int ret;
|
||||||
|
|
||||||
if (in_compat_syscall()) {
|
ret = br_dev_read_uargs(args, ARRAY_SIZE(args), &argp, data);
|
||||||
unsigned int cargs[4];
|
if (ret)
|
||||||
|
return ret;
|
||||||
if (copy_from_user(cargs, data, sizeof(cargs)))
|
|
||||||
return -EFAULT;
|
|
||||||
|
|
||||||
args[0] = cargs[0];
|
|
||||||
args[1] = cargs[1];
|
|
||||||
args[2] = cargs[2];
|
|
||||||
args[3] = cargs[3];
|
|
||||||
|
|
||||||
argp = compat_ptr(args[1]);
|
|
||||||
} else {
|
|
||||||
if (copy_from_user(args, data, sizeof(args)))
|
|
||||||
return -EFAULT;
|
|
||||||
|
|
||||||
argp = (void __user *)args[1];
|
|
||||||
}
|
|
||||||
|
|
||||||
switch (args[0]) {
|
switch (args[0]) {
|
||||||
case BRCTL_ADD_IF:
|
case BRCTL_ADD_IF:
|
||||||
@@ -301,6 +320,9 @@ int br_dev_siocdevprivate(struct net_device *dev, struct ifreq *rq, void __user
|
|||||||
|
|
||||||
case BRCTL_GET_FDB_ENTRIES:
|
case BRCTL_GET_FDB_ENTRIES:
|
||||||
return get_fdb_entries(br, argp, args[2], args[3]);
|
return get_fdb_entries(br, argp, args[2], args[3]);
|
||||||
|
|
||||||
|
default:
|
||||||
|
ret = -EOPNOTSUPP;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!ret) {
|
if (!ret) {
|
||||||
@@ -313,12 +335,15 @@ int br_dev_siocdevprivate(struct net_device *dev, struct ifreq *rq, void __user
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int old_deviceless(struct net *net, void __user *uarg)
|
static int old_deviceless(struct net *net, void __user *data)
|
||||||
{
|
{
|
||||||
unsigned long args[3];
|
unsigned long args[3];
|
||||||
|
void __user *argp;
|
||||||
|
int ret;
|
||||||
|
|
||||||
if (copy_from_user(args, uarg, sizeof(args)))
|
ret = br_dev_read_uargs(args, ARRAY_SIZE(args), &argp, data);
|
||||||
return -EFAULT;
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
|
||||||
switch (args[0]) {
|
switch (args[0]) {
|
||||||
case BRCTL_GET_VERSION:
|
case BRCTL_GET_VERSION:
|
||||||
@@ -337,7 +362,7 @@ static int old_deviceless(struct net *net, void __user *uarg)
|
|||||||
|
|
||||||
args[2] = get_bridge_ifindices(net, indices, args[2]);
|
args[2] = get_bridge_ifindices(net, indices, args[2]);
|
||||||
|
|
||||||
ret = copy_to_user((void __user *)args[1], indices,
|
ret = copy_to_user(argp, indices,
|
||||||
array_size(args[2], sizeof(int)))
|
array_size(args[2], sizeof(int)))
|
||||||
? -EFAULT : args[2];
|
? -EFAULT : args[2];
|
||||||
|
|
||||||
@@ -353,7 +378,7 @@ static int old_deviceless(struct net *net, void __user *uarg)
|
|||||||
if (!ns_capable(net->user_ns, CAP_NET_ADMIN))
|
if (!ns_capable(net->user_ns, CAP_NET_ADMIN))
|
||||||
return -EPERM;
|
return -EPERM;
|
||||||
|
|
||||||
if (copy_from_user(buf, (void __user *)args[1], IFNAMSIZ))
|
if (copy_from_user(buf, argp, IFNAMSIZ))
|
||||||
return -EFAULT;
|
return -EFAULT;
|
||||||
|
|
||||||
buf[IFNAMSIZ-1] = 0;
|
buf[IFNAMSIZ-1] = 0;
|
||||||
|
20
net/socket.c
20
net/socket.c
@@ -3233,21 +3233,6 @@ static int compat_ifr_data_ioctl(struct net *net, unsigned int cmd,
|
|||||||
return dev_ioctl(net, cmd, &ifreq, data, NULL);
|
return dev_ioctl(net, cmd, &ifreq, data, NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Since old style bridge ioctl's endup using SIOCDEVPRIVATE
|
|
||||||
* for some operations; this forces use of the newer bridge-utils that
|
|
||||||
* use compatible ioctls
|
|
||||||
*/
|
|
||||||
static int old_bridge_ioctl(compat_ulong_t __user *argp)
|
|
||||||
{
|
|
||||||
compat_ulong_t tmp;
|
|
||||||
|
|
||||||
if (get_user(tmp, argp))
|
|
||||||
return -EFAULT;
|
|
||||||
if (tmp == BRCTL_GET_VERSION)
|
|
||||||
return BRCTL_VERSION + 1;
|
|
||||||
return -EINVAL;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int compat_sock_ioctl_trans(struct file *file, struct socket *sock,
|
static int compat_sock_ioctl_trans(struct file *file, struct socket *sock,
|
||||||
unsigned int cmd, unsigned long arg)
|
unsigned int cmd, unsigned long arg)
|
||||||
{
|
{
|
||||||
@@ -3259,9 +3244,6 @@ static int compat_sock_ioctl_trans(struct file *file, struct socket *sock,
|
|||||||
return sock_ioctl(file, cmd, (unsigned long)argp);
|
return sock_ioctl(file, cmd, (unsigned long)argp);
|
||||||
|
|
||||||
switch (cmd) {
|
switch (cmd) {
|
||||||
case SIOCSIFBR:
|
|
||||||
case SIOCGIFBR:
|
|
||||||
return old_bridge_ioctl(argp);
|
|
||||||
case SIOCWANDEV:
|
case SIOCWANDEV:
|
||||||
return compat_siocwandev(net, argp);
|
return compat_siocwandev(net, argp);
|
||||||
case SIOCGSTAMP_OLD:
|
case SIOCGSTAMP_OLD:
|
||||||
@@ -3290,6 +3272,8 @@ static int compat_sock_ioctl_trans(struct file *file, struct socket *sock,
|
|||||||
case SIOCGSTAMP_NEW:
|
case SIOCGSTAMP_NEW:
|
||||||
case SIOCGSTAMPNS_NEW:
|
case SIOCGSTAMPNS_NEW:
|
||||||
case SIOCGIFCONF:
|
case SIOCGIFCONF:
|
||||||
|
case SIOCSIFBR:
|
||||||
|
case SIOCGIFBR:
|
||||||
return sock_ioctl(file, cmd, arg);
|
return sock_ioctl(file, cmd, arg);
|
||||||
|
|
||||||
case SIOCGIFFLAGS:
|
case SIOCGIFFLAGS:
|
||||||
|
Reference in New Issue
Block a user