User Tools

Site Tools


net_ioctl

Overview

How net-based IOCTL calls are made – prerequisite for other wiki pages.

UAPI links:

Kernel links:

Sample userspace call: ethtool

Git repo is here.

send_ioctl()

From ethtool.c, a simple wrapper around the ioctl() call:

#ifndef TEST_ETHTOOL
int send_ioctl(struct cmd_context *ctx, void *cmd)
{
        ctx->ifr.ifr_data = cmd;        // command-specific structure
        return ioctl(ctx->fd, SIOCETHTOOL, &ctx->ifr);
}
#endif

QUESTION: What is returned in ifreq->cmd?

struct cmd_context

From internal.h, struct cmd_context showing the embedded struct ifreq:

/* Context for sub-commands */
struct cmd_context {
        const char *devname;    /* net device name */
        int fd;                 /* socket suitable for ethtool ioctl */
        struct ifreq ifr;       /* ifreq suitable for ethtool ioctl */
        int argc;               /* number of arguments to the sub-command */
        char **argp;            /* arguments to the sub-command */
};

struct ifreq

From include/uapi/linux/if.h, the unsigned long arg passed with ioctl():

#if __UAPI_DEF_IF_IFREQ
struct ifreq {
#define IFHWADDRLEN     6
        union
        {
                char    ifrn_name[IFNAMSIZ];            /* if name, e.g. "en0" */
        } ifr_ifrn;

        union {
                struct  sockaddr ifru_addr;
                struct  sockaddr ifru_dstaddr;
                struct  sockaddr ifru_broadaddr;
                struct  sockaddr ifru_netmask;
                struct  sockaddr ifru_hwaddr;
                short   ifru_flags;
                int     ifru_ivalue;
                int     ifru_mtu;
                struct  ifmap ifru_map;
                char    ifru_slave[IFNAMSIZ];   /* Just fits the size */
                char    ifru_newname[IFNAMSIZ];
                void __user *   ifru_data;
                struct  if_settings ifru_settings;
        } ifr_ifru;
};
#endif /* __UAPI_DEF_IF_IFREQ */

#define ifr_name        ifr_ifrn.ifrn_name      /* interface name       */
#define ifr_hwaddr      ifr_ifru.ifru_hwaddr    /* MAC address          */
#define ifr_addr        ifr_ifru.ifru_addr      /* address              */
#define ifr_dstaddr     ifr_ifru.ifru_dstaddr   /* other end of p-p lnk */
#define ifr_broadaddr   ifr_ifru.ifru_broadaddr /* broadcast address    */
#define ifr_netmask     ifr_ifru.ifru_netmask   /* interface net mask   */
#define ifr_flags       ifr_ifru.ifru_flags     /* flags                */
#define ifr_metric      ifr_ifru.ifru_ivalue    /* metric               */
#define ifr_mtu         ifr_ifru.ifru_mtu       /* mtu                  */
#define ifr_map         ifr_ifru.ifru_map       /* device map           */
#define ifr_slave       ifr_ifru.ifru_slave     /* slave device         */
#define ifr_data        ifr_ifru.ifru_data      /* for use by interface */
#define ifr_ifindex     ifr_ifru.ifru_ivalue    /* interface index      */
#define ifr_bandwidth   ifr_ifru.ifru_ivalue    /* link bandwidth       */
#define ifr_qlen        ifr_ifru.ifru_ivalue    /* Queue length         */
#define ifr_newname     ifr_ifru.ifru_newname   /* New name             */
#define ifr_settings    ifr_ifru.ifru_settings  /* Device/proto settings*/

Kernel workflow

socket_file_ops

See The new way of ioctl().

From net/socket.c:

static const struct file_operations socket_file_ops = {
	.owner =	THIS_MODULE,
	.llseek =	no_llseek,
	.read_iter =	sock_read_iter,
	.write_iter =	sock_write_iter,
	.poll =		sock_poll,
	.unlocked_ioctl = sock_ioctl,   <-- this one
#ifdef CONFIG_COMPAT
	.compat_ioctl = compat_sock_ioctl,
#endif
	.mmap =		sock_mmap,
	.release =	sock_close,
	.fasync =	sock_fasync,
	.sendpage =	sock_sendpage,
	.splice_write = generic_splice_sendpage,
	.splice_read =	sock_splice_read,
};

sock_ioctl()

Also from net/socket.c – anything related to SIOCDEVPRIVATE is seriously deprecated (see here):

static long sock_ioctl(struct file *file, unsigned cmd, unsigned long arg)
{
        struct socket *sock;
        struct sock *sk;
        void __user *argp = (void __user *)arg;
        int pid, err;
        struct net *net;

        sock = file->private_data;
        sk = sock->sk;
        net = sock_net(sk);
        if (unlikely(cmd >= SIOCDEVPRIVATE && cmd <= (SIOCDEVPRIVATE + 15))) {
                struct ifreq ifr;
                bool need_copyout;
                if (copy_from_user(&ifr, argp, sizeof(struct ifreq)))
                        return -EFAULT;
                err = dev_ioctl(net, cmd, &ifr, &need_copyout);
                if (!err && need_copyout)
                        if (copy_to_user(argp, &ifr, sizeof(struct ifreq)))
                                return -EFAULT;
        } else

This reflects wireless extensions, which should not be relevant:

#ifdef CONFIG_WEXT_CORE
        if (cmd >= SIOCIWFIRST && cmd <= SIOCIWLAST) {
                err = wext_handle_ioctl(net, cmd, argp);
        } else
#endif

Finally, this is where we should end up:

                switch (cmd) {
                case FIOSETOWN:
                case SIOCSPGRP:
                        err = -EFAULT;
                        if (get_user(pid, (int __user *)argp))
                                break;
                        err = f_setown(sock->file, pid, 1);
                        break;
                case FIOGETOWN:
                case SIOCGPGRP:
                        err = put_user(f_getown(sock->file),
                                       (int __user *)argp);
                        break;
                case SIOCGIFBR:
                case SIOCSIFBR:
                case SIOCBRADDBR:
                case SIOCBRDELBR:
                        err = -ENOPKG;
                        if (!br_ioctl_hook)
                                request_module("bridge");

                        mutex_lock(&br_ioctl_mutex);
                        if (br_ioctl_hook)
                                err = br_ioctl_hook(net, cmd, argp);
                        mutex_unlock(&br_ioctl_mutex);
                        break;
                case SIOCGIFVLAN:
                case SIOCSIFVLAN:
                        err = -ENOPKG;
                        if (!vlan_ioctl_hook)
                                request_module("8021q");

                        mutex_lock(&vlan_ioctl_mutex);
                        if (vlan_ioctl_hook)
                                err = vlan_ioctl_hook(net, argp);
                        mutex_unlock(&vlan_ioctl_mutex);
                        break;
                case SIOCADDDLCI:
                case SIOCDELDLCI:
                        err = -ENOPKG;
                        if (!dlci_ioctl_hook)
                                request_module("dlci");

                        mutex_lock(&dlci_ioctl_mutex);
                        if (dlci_ioctl_hook)
                                err = dlci_ioctl_hook(cmd, argp);
                        mutex_unlock(&dlci_ioctl_mutex);
                        break;
                case SIOCGSKNS:
                        err = -EPERM;
                        if (!ns_capable(net->user_ns, CAP_NET_ADMIN))
                                break;

                        err = open_related_ns(&net->ns, get_net_ns);
                        break;
                default:
                        err = sock_do_ioctl(net, sock, cmd, arg);    <-- there
                        break;
                }
        return err;
}

sock_do_ioctl()

Still in net/socket.c:

static long sock_do_ioctl(struct net *net, struct socket *sock,
                                 unsigned int cmd, unsigned long arg)
{
        int err;
        void __user *argp = (void __user *)arg;

        err = sock->ops->ioctl(sock, cmd, arg);

        /*
         * If this ioctl is unknown try to hand it down
         * to the NIC driver.
         */
        if (err != -ENOIOCTLCMD)
                return err;

        if (cmd == SIOCGIFCONF) {
                struct ifconf ifc;
                if (copy_from_user(&ifc, argp, sizeof(struct ifconf)))
                        return -EFAULT;
                rtnl_lock();
                err = dev_ifconf(net, &ifc, sizeof(struct ifreq));
                rtnl_unlock();
                if (!err && copy_to_user(argp, &ifc, sizeof(struct ifconf)))
                        err = -EFAULT;
        } else {
                struct ifreq ifr;
                bool need_copyout;
                if (copy_from_user(&ifr, argp, sizeof(struct ifreq)))
                        return -EFAULT;
                err = dev_ioctl(net, cmd, &ifr, &need_copyout);    <-- here???
                if (!err && need_copyout)
                        if (copy_to_user(argp, &ifr, sizeof(struct ifreq)))
                                return -EFAULT;
        }
        return err;
}
net_ioctl.txt · Last modified: 2018/09/04 10:31 by rpjday