normal socket ioctl to net interface ioctl

本文详细介绍了网络接口ioctl命令的基本概念、 ioctl调用方式、不同类别ioctl命令的使用场景,以及用户程序如何安全地访问这些信息。通过具体实例展示了如何处理不同类型的ioctl命令,包括路由表操作、ARP/RARP缓存更新、网络接口信息读取等。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Write down for wifi framework.

Describe how the flow is transaferred from a normal socket to the specific network interface ioctl. 

 

An artical from Vipul Gupta

 

In general an ioctl call in a user program looks like ioctl(int fd, int command, (char *) argstruct). For ioctl calls related to the networking code (these are the only ones we will deal with in this note), the file descriptor fd is actually a socket descriptor returned by the socket() system call. The command could be any one of those listed in /usr/include/linux/sockios.h. These commands are subdivided into a number of categories depending on what aspect of networking they deal with:

 

changing the routing table (e.g. SIOCADDRT, SIOCDELRT),

reading/updating the ARP/RARP caches (e.g. SIOCDARP, SIOCSRARP),

generic functions related to network interfaces (e.g. SIOCGIFNAME, SIOCSIFADDR etc)

The Goodies directory contains a number of sample programs illustrating the use of networking ioctl calls. As you look at these programs, notice how the structure used for argstruct depends on the ioctl command type. For example, routing table related ioctls use the rtentry structure defined in /usr/include/linux/route.h (see adddefault.c for an example) and ARP related ioctls use the arpreq structure defined in /usr/include/linux/if_arp.h (see arpread.c).

 

Network interface related ioctl commands typically look like SIOCxIFyyyy where x is either S (set, write) or G (get, read). The getifinfo.c program uses such commands to read the IP address, hardware address, broadcast address and flags associated with a network interface. For these ioctls, the third argument is an ifreq structure which is defined in /usr/include/linux/if.h. In some cases, new ioctl commands may be needed in addition to those defined in sockios.h, e.g. the WaveLAN wireless networking card maintains information about wireless signal strength which may be of use to a user program. How should user programs be allowed access to this information? Our first instinct may be to define a new ioctl command in sockios.h, e.g. SIOCGIFWVLNSS (Get WaVeLaN Signal Strength). Unfortunately, this command makes no sense at all for other interfaces (e.g. loopback) and attempts to use this ioctl command on interfaces other than WaveLAN cards should be disallowed. What we need, then, is a mechanism to define interface specific ioctl commands. Luckily, the Linux OS already has built-in hooks for this purpose. If you look at sockios.h again, you will notice that each device has a predefined SIOCDEVPRIVATE ioctl command. The implementation of this command is left totally upto the person writing the corresponding device driver.

 

By convention, a user program invokes a device specific ioctl command as ioctl(sockid, SIOCDEVPRIVATE, (char *) &ifr) where ifr is defined as struct ifreq ifr. It fills ifr.ifr_name with the interface name associated with the device, e.g. on tether, the WaveLAN card is named eth1. Typically, a user program will also need to exchange command arguments and results with the kernel and that is done through the ifr.ifr_data field, e.g. the signal strength infromation for the WaveLAN card could be returned in this field. The Linux source code already includes two devices de4x5 and ewrk3 that define and implement device specific ioctl commands. The source code for these drivers is in de4x5.h, de4x5.c, ewrk3.h, ewrk3.c, (in /usr/src/linux/drivers/net/). Both drivers define their own private structures (struct ewrk3_ioctl and struct de4x5_ioctl) for exchanging information between user programs and the drivers. Before the ioctl call, the user program fills out the necessary fields in this structure and points ifr.ifr_data to it.

 

Before we go any further into the driver code for ewrk3 and de4x5, let us trace through various steps in the processing of an ioctl call. All interface-type ioctl requests (SIOCxIFyyyy and SIOCDEVPRIVATE) result in dev_ioctl() (in /usr/src/linux/net/core/dev.c) being called. This is just a wrapper and most of the real action is left for dev_ifsioc() (also in dev.c). About the only thing dev_ioctl() does is check whether a calling process has the appropriate permissions to issue the command (e.g. commands to alter the routing tables require root permissions). One of the first things dev_ifsioc() does is get the device structre (struct device defined in /usr/include/linux/netdevice.h) corresponding to the device named in ifr.ifr_name. This is followed by the code to implement generic interface commands (e.g. SIOCGIFADDR) inside a giant switch statement. The SIOCDEVPRIVATE command and any others with codes from 0x89F0 through 0x89FF end up in the default: branch for this switch. Here, the kernel checks to see if a device specific ioctl handler has been set up in the device structure. The handler is maintained as a function pointer in the do_ioctl field of the device structure. If the handler has been set, the kernel invokes it.

 

So, to implement device specific ioctls, all one needs to do is write a device specific ioctl handler and have the do_ioctl field in the corresponding device structure point to it. For the ewrk3 device, this function is called ewrk3_ioctl() (in ewrk3.c) and the corresponding device structure is initialized in ewrk3_init(). The ewrk3_ioctl() code clearly indicates the use of ifr.ifr_data for exchanging information between the device driver and user program. Note that this area of memory can be used for bidirectional information exchange. For example, in the ewrk3 driver code, the first two bytes of ifr.ifr_data are used to convey the specific action (e.g. EWRK3_SET_PROM, EWRK3_CLR_PROM) desired by the user (the driver implements multiple device specific commands that are all invoked through SIOCDEVPRIVATE). Also, and the buffer pointed to by the fifth byte in ifr.ifr_data is used to exchange other information (hardware address when using EWRK3_SET_HWADDR or EWRK3_GET_HWADDR).

 

As you go through ewrk3_ioctl(), keep in mind that normally a user process cannot directly access kernel memory. For this reason, two special procedures memcpy_tofs() and memcpy_fromfs() are provided to driver writers. The kernel procedure memcpy_tofs(arg1, arg2, arg3) copies arg3 bytes from starting address arg2 (in kernel space) to address arg1 (in user space). Similarly, memcpy_fromfs(arg1, arg2, arg3) copies arg3 bytes from starting address arg2 (in user space) to address arg1 (in kernel space). These procedures are preceded by calls to verify_area() to verify that the process has appropriate access permissions. Also notice the use of the printk() function to print debugging information. This function is similar to printf() but cannot handle floating point. The printf() function is not available to kernel code. The output generated by printk() is logged into /usr/adm/messages. For more information on these and related procedures, look at the section titled Supporting Functions in Michael K. Johnson's "Linux Kernel Hacker's Guide" which is accessible from the Linux Documentation Home Page.

 

Let me paste the code extracted from kernel 2.6.xx. 

asmlinkage long sys_ioctl(unsigned int fd, unsigned int cmd, unsigned long arg)

{

      struct file * filp;

      int error = -EBADF;

      int fput_needed;

 

      filp = fget_light(fd, &fput_needed);

      if (!filp)

            goto out;

 

      error = security_file_ioctl(filp, cmd, arg);

      if (error)

            goto out_fput;

 

      error = vfs_ioctl(filp, fd, cmd, arg);

 out_fput:

      fput_light(filp, fput_needed);

 out:

      return error;

}

 

 

int vfs_ioctl(struct file *filp, unsigned int fd, unsigned int cmd, unsigned long arg)

{

      unsigned int flag;

      int on, error = 0;

 

      switch (cmd) {

            case FIOCLEX:

                  set_close_on_exec(fd, 1);

                  break;

 

            case FIONCLEX:

                  set_close_on_exec(fd, 0);

                  break;

 

            case FIONBIO:

                  if ((error = get_user(on, (int __user *)arg)) != 0)

                        break;

                  flag = O_NONBLOCK;

#ifdef __sparc__

                  /* SunOS compatibility item. */

                  if(O_NONBLOCK != O_NDELAY)

                        flag |= O_NDELAY;

#endif

                  if (on)

                        filp->f_flags |= flag;

                  else

                        filp->f_flags &= ~flag;

                  break;

 

            case FIOASYNC:

                  if ((error = get_user(on, (int __user *)arg)) != 0)

                        break;

                  flag = on ? FASYNC : 0;

 

                  /* Did FASYNC state change ? */

                  if ((flag ^ filp->f_flags) & FASYNC) {

                        if (filp->f_op && filp->f_op->fasync) {

                              lock_kernel();

                              error = filp->f_op->fasync(fd, filp, on);

                              unlock_kernel();

                        }

                        else error = -ENOTTY;

                  }

                  if (error != 0)

                        break;

 

                  if (on)

                        filp->f_flags |= FASYNC;

                  else

                        filp->f_flags &= ~FASYNC;

                  break;

 

            case FIOQSIZE:

                  if (S_ISDIR(filp->f_path.dentry->d_inode->i_mode) ||

                      S_ISREG(filp->f_path.dentry->d_inode->i_mode) ||

                      S_ISLNK(filp->f_path.dentry->d_inode->i_mode)) {

                        loff_t res = inode_get_bytes(filp->f_path.dentry->d_inode);

                        error = copy_to_user((loff_t __user *)arg, &res, sizeof(res)) ? -EFAULT : 0;

                  }

                  else

                        error = -ENOTTY;

                  break;

            default:

                  if (S_ISREG(filp->f_path.dentry->d_inode->i_mode))

                        error = file_ioctl(filp, cmd, arg);

                  else

                        error = do_ioctl(filp, cmd, arg);

                  break;

      }

      return error;

}

 

static int file_ioctl(struct file *filp, unsigned int cmd,

            unsigned long arg)

{

      int error;

      int block;

      struct inode * inode = filp->f_path.dentry->d_inode;

      int __user *p = (int __user *)arg;

 

      switch (cmd) {

            case FIBMAP:

            {

                  struct address_space *mapping = filp->f_mapping;

                  int res;

                  /* do we support this mess? */

                  if (!mapping->a_ops->bmap)

                        return -EINVAL;

                  if (!capable(CAP_SYS_RAWIO))

                        return -EPERM;

                  if ((error = get_user(block, p)) != 0)

                        return error;

 

                  lock_kernel();

                  res = mapping->a_ops->bmap(mapping, block);

                  unlock_kernel();

                  return put_user(res, p);

            }

            case FIGETBSZ:

                  return put_user(inode->i_sb->s_blocksize, p);

            case FIONREAD:

                  return put_user(i_size_read(inode) - filp->f_pos, p);

      }

 

      return do_ioctl(filp, cmd, arg);

}

 

 

static long do_ioctl(struct file *filp, unsigned int cmd,

            unsigned long arg)

{

      int error = -ENOTTY;

      void *f;

 

      if (!filp->f_op)

            goto out;

 

      if (filp->f_op->unlocked_ioctl) {

            error = filp->f_op->unlocked_ioctl(filp, cmd, arg); // which is sock_ioctl for socket

            if (error == -ENOIOCTLCMD)

                  error = -EINVAL;

            goto out;

      } else if ((f = filp->f_op->ioctl)) {

            lock_kernel();

            if (!filp->f_op->ioctl) {

                  printk("%s: ioctl %p disappeared\n", __FUNCTION__, f);

                  print_symbol("symbol: %s\n", (unsigned long)f);

                  dump_stack();

            } else {

                  error = filp->f_op->ioctl(filp->f_path.dentry->d_inode,

                                      filp, cmd, arg);

            }

            unlock_kernel();

      }

 

 out:

      return error;

}

 

static long sock_ioctl(struct file *file, unsigned cmd, unsigned long arg)

{

      struct socket *sock;

      void __user *argp = (void __user *)arg;

      int pid, err;

 

      sock = file->private_data;

      if (cmd >= SIOCDEVPRIVATE && cmd <= (SIOCDEVPRIVATE + 15)) {

            err = dev_ioctl(cmd, argp);

      } else

#ifdef CONFIG_WIRELESS_EXT

      if (cmd >= SIOCIWFIRST && cmd <= SIOCIWLAST) {

            err = dev_ioctl(cmd, argp);

      } else

#endif                        /* CONFIG_WIRELESS_EXT */

            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(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(argp);

                  mutex_unlock(&vlan_ioctl_mutex);

                  break;

            case SIOCADDDLCI:

            case SIOCDELDLCI:

                  err = -ENOPKG;

                  if (!dlci_ioctl_hook)

                        request_module("dlci");

 

                  if (dlci_ioctl_hook) {

                        mutex_lock(&dlci_ioctl_mutex);

                        err = dlci_ioctl_hook(cmd, argp);

                        mutex_unlock(&dlci_ioctl_mutex);

                  }

                  break;

            default:

                  err = sock->ops->ioctl(sock, cmd, arg); // inet_ioctl for AF_INET

 

                  /*

                   * If this ioctl is unknown try to hand it down

                   * to the NIC driver.

                   */

                  if (err == -ENOIOCTLCMD)

                        err = dev_ioctl(cmd, argp);

                  break;

            }

      return err;

}

 

int dev_ioctl(unsigned int cmd, void __user *arg)

{

      struct ifreq ifr;

      int ret;

      char *colon;

 

      /* One special case: SIOCGIFCONF takes ifconf argument

         and requires shared lock, because it sleeps writing

         to user space.

       */

 

      if (cmd == SIOCGIFCONF) {

            rtnl_lock();

            ret = dev_ifconf((char __user *) arg);

            rtnl_unlock();

            return ret;

      }

      if (cmd == SIOCGIFNAME)

            return dev_ifname((struct ifreq __user *)arg);

 

      if (copy_from_user(&ifr, arg, sizeof(struct ifreq)))

            return -EFAULT;

 

      ifr.ifr_name[IFNAMSIZ-1] = 0;

 

      colon = strchr(ifr.ifr_name, ':');

      if (colon)

            *colon = 0;

 

      /*

       *    See which interface the caller is talking about.

       */

 

      switch (cmd) {

            /*

             *    These ioctl calls:

             *    - can be done by all.

             *    - atomic and do not require locking.

             *    - return a value

             */

            case SIOCGIFFLAGS:

            case SIOCGIFMETRIC:

            case SIOCGIFMTU:

            case SIOCGIFHWADDR:

            case SIOCGIFSLAVE:

            case SIOCGIFMAP:

            case SIOCGIFINDEX:

            case SIOCGIFTXQLEN:

                  dev_load(ifr.ifr_name);

                  read_lock(&dev_base_lock);

                  ret = dev_ifsioc(&ifr, cmd);

                  read_unlock(&dev_base_lock);

                  if (!ret) {

                        if (colon)

                              *colon = ':';

                        if (copy_to_user(arg, &ifr,

                                     sizeof(struct ifreq)))

                              ret = -EFAULT;

                  }

                  return ret;

 

            case SIOCETHTOOL:

                  dev_load(ifr.ifr_name);

                  rtnl_lock();

                  ret = dev_ethtool(&ifr);

                  rtnl_unlock();

                  if (!ret) {

                        if (colon)

                              *colon = ':';

                        if (copy_to_user(arg, &ifr,

                                     sizeof(struct ifreq)))

                              ret = -EFAULT;

                  }

                  return ret;

 

            /*

             *    These ioctl calls:

             *    - require superuser power.

             *    - require strict serialization.

             *    - return a value

             */

            case SIOCGMIIPHY:

            case SIOCGMIIREG:

            case SIOCSIFNAME:

                  if (!capable(CAP_NET_ADMIN))

                        return -EPERM;

                  dev_load(ifr.ifr_name);

                  rtnl_lock();

                  ret = dev_ifsioc(&ifr, cmd);

                  rtnl_unlock();

                  if (!ret) {

                        if (colon)

                              *colon = ':';

                        if (copy_to_user(arg, &ifr,

                                     sizeof(struct ifreq)))

                              ret = -EFAULT;

                  }

                  return ret;

 

            /*

             *    These ioctl calls:

             *    - require superuser power.

             *    - require strict serialization.

             *    - do not return a value

             */

            case SIOCSIFFLAGS:

            case SIOCSIFMETRIC:

            case SIOCSIFMTU:

            case SIOCSIFMAP:

            case SIOCSIFHWADDR:

            case SIOCSIFSLAVE:

            case SIOCADDMULTI:

            case SIOCDELMULTI:

            case SIOCSIFHWBROADCAST:

            case SIOCSIFTXQLEN:

            case SIOCSMIIREG:

            case SIOCBONDENSLAVE:

            case SIOCBONDRELEASE:

            case SIOCBONDSETHWADDR:

            case SIOCBONDCHANGEACTIVE:

            case SIOCBRADDIF:

            case SIOCBRDELIF:

                  if (!capable(CAP_NET_ADMIN))

                        return -EPERM;

                  /* fall through */

            case SIOCBONDSLAVEINFOQUERY:

            case SIOCBONDINFOQUERY:

                  dev_load(ifr.ifr_name);

                  rtnl_lock();

                  ret = dev_ifsioc(&ifr, cmd);

                  rtnl_unlock();

                  return ret;

 

            case SIOCGIFMEM:

                  /* Get the per device memory space. We can add this but

                   * currently do not support it */

            case SIOCSIFMEM:

                  /* Set the per device memory buffer space.

                   * Not applicable in our case */

            case SIOCSIFLINK:

                  return -EINVAL;

 

            /*

             *    Unknown or private ioctl.

             */

            default:

                  if (cmd == SIOCWANDEV ||

                      (cmd >= SIOCDEVPRIVATE &&

                       cmd <= SIOCDEVPRIVATE + 15)) {

                        dev_load(ifr.ifr_name);

                        rtnl_lock();

                        ret = dev_ifsioc(&ifr, cmd);

                        rtnl_unlock();

                        if (!ret && copy_to_user(arg, &ifr,

                                           sizeof(struct ifreq)))

                              ret = -EFAULT;

                        return ret;

                  }

                  /* Take care of Wireless Extensions */

                  if (cmd >= SIOCIWFIRST && cmd <= SIOCIWLAST)

                        return wext_handle_ioctl(&ifr, cmd, arg);

                  return -EINVAL;

      }

}

 

 

int wext_handle_ioctl(struct ifreq *ifr, unsigned int cmd,

                  void __user *arg)

{

      int ret;

 

      /* If command is `set a parameter', or

       * `get the encoding parameters', check if

       * the user has the right to do it */

      if ((IW_IS_SET(cmd) || cmd == SIOCGIWENCODE || cmd == SIOCGIWENCODEEXT)

          && !capable(CAP_NET_ADMIN))

            return -EPERM;

 

      dev_load(ifr->ifr_name);

      rtnl_lock();

      ret = wireless_process_ioctl(ifr, cmd);

      rtnl_unlock();

      if (IW_IS_GET(cmd) && copy_to_user(arg, ifr, sizeof(struct ifreq)))

            return -EFAULT;

      return ret;

}

 

/* ---------------------------------------------------------------- */

/*

 * Main IOCTl dispatcher.

 * Check the type of IOCTL and call the appropriate wrapper...

 */

static int wireless_process_ioctl(struct ifreq *ifr, unsigned int cmd)

{

      struct net_device *dev;

      iw_handler  handler;

 

      /* Permissions are already checked in dev_ioctl() before calling us.

       * The copy_to/from_user() of ifr is also dealt with in there */

 

      /* Make sure the device exist */

      if ((dev = __dev_get_by_name(ifr->ifr_name)) == NULL)

            return -ENODEV;

 

      /* A bunch of special cases, then the generic case...

       * Note that 'cmd' is already filtered in dev_ioctl() with

       * (cmd >= SIOCIWFIRST && cmd <= SIOCIWLAST) */

      if (cmd == SIOCGIWSTATS)

            return ioctl_standard_call(dev, ifr, cmd,

                                 &iw_handler_get_iwstats);

 

      if (cmd == SIOCGIWPRIV && dev->wireless_handlers)

            return ioctl_standard_call(dev, ifr, cmd,

                                 &iw_handler_get_private);

 

      /* Basic check */

      if (!netif_device_present(dev))

            return -ENODEV;

 

      /* New driver API : try to find the handler */

      handler = get_handler(dev, cmd);

      if (handler) {

            /* Standard and private are not the same */

            if (cmd < SIOCIWFIRSTPRIV)

                  return ioctl_standard_call(dev, ifr, cmd, handler);

            else

                  return ioctl_private_call(dev, ifr, cmd, handler);

      }

      /* Old driver API : call driver ioctl handler */

      if (dev->do_ioctl)

            return dev->do_ioctl(dev, ifr, cmd);

      return -EOPNOTSUPP;

}

 

static iw_handler get_handler(struct net_device *dev, unsigned int cmd)

{

      /* Don't "optimise" the following variable, it will crash */

      unsigned int      index;            /* *MUST* be unsigned */

 

      /* Check if we have some wireless handlers defined */

      if (dev->wireless_handlers == NULL)

            return NULL;

 

      /* Try as a standard command */

      index = cmd - SIOCIWFIRST;

      if (index < dev->wireless_handlers->num_standard)

            return dev->wireless_handlers->standard[index];

 

      /* Try as a private command */

      index = cmd - SIOCIWFIRSTPRIV;

      if (index < dev->wireless_handlers->num_private)

            return dev->wireless_handlers->private[index];

 

      /* Not found */

      return NULL;

}

 

/************************** IOCTL SUPPORT **************************/

/*

 * The original user space API to configure all those Wireless Extensions

 * is through IOCTLs.

 * In there, we check if we need to call the new driver API (iw_handler)

 * or just call the driver ioctl handler.

 */

 

/* ---------------------------------------------------------------- */

/*

 * Wrapper to call a standard Wireless Extension handler.

 * We do various checks and also take care of moving data between

 * user space and kernel space.

 */

static int ioctl_standard_call(struct net_device *    dev,

                         struct ifreq *         ifr,

                         unsigned int           cmd,

                         iw_handler       handler)

{

      struct iwreq *                      iwr = (struct iwreq *) ifr;

      const struct iw_ioctl_description * descr;

      struct iw_request_info              info;

      int                           ret = -EINVAL;

 

      /* Get the description of the IOCTL */

      if ((cmd - SIOCIWFIRST) >= standard_ioctl_num)

            return -EOPNOTSUPP;

      descr = &(standard_ioctl[cmd - SIOCIWFIRST]);

 

      /* Prepare the call */

      info.cmd = cmd;

      info.flags = 0;

 

      /* Check if we have a pointer to user space data or not */

      if (descr->header_type != IW_HEADER_TYPE_POINT) {

 

            /* No extra arguments. Trivial to handle */

            ret = handler(dev, &info, &(iwr->u), NULL);

 

            /* Generate an event to notify listeners of the change */

            if ((descr->flags & IW_DESCR_FLAG_EVENT) &&

               ((ret == 0) || (ret == -EIWCOMMIT)))

                  wireless_send_event(dev, cmd, &(iwr->u), NULL);

      } else {

            char *      extra;

            int   extra_size;

            int   user_length = 0;

            int   err;

            int   essid_compat = 0;

 

            /* Calculate space needed by arguments. Always allocate

             * for max space. Easier, and won't last long... */

            extra_size = descr->max_tokens * descr->token_size;

 

            /* Check need for ESSID compatibility for WE < 21 */

            switch (cmd) {

            case SIOCSIWESSID:

            case SIOCGIWESSID:

            case SIOCSIWNICKN:

            case SIOCGIWNICKN:

                  if (iwr->u.data.length == descr->max_tokens + 1)

                        essid_compat = 1;

                  else if (IW_IS_SET(cmd) && (iwr->u.data.length != 0)) {

                        char essid[IW_ESSID_MAX_SIZE + 1];

 

                        err = copy_from_user(essid, iwr->u.data.pointer,

                                         iwr->u.data.length *

                                         descr->token_size);

                        if (err)

                              return -EFAULT;

 

                        if (essid[iwr->u.data.length - 1] == '\0')

                              essid_compat = 1;

                  }

                  break;

            default:

                  break;

            }

 

            iwr->u.data.length -= essid_compat;

 

            /* Check what user space is giving us */

            if (IW_IS_SET(cmd)) {

                  /* Check NULL pointer */

                  if ((iwr->u.data.pointer == NULL) &&

                     (iwr->u.data.length != 0))

                        return -EFAULT;

                  /* Check if number of token fits within bounds */

                  if (iwr->u.data.length > descr->max_tokens)

                        return -E2BIG;

                  if (iwr->u.data.length < descr->min_tokens)

                        return -EINVAL;

            } else {

                  /* Check NULL pointer */

                  if (iwr->u.data.pointer == NULL)

                        return -EFAULT;

                  /* Save user space buffer size for checking */

                  user_length = iwr->u.data.length;

 

                  /* Don't check if user_length > max to allow forward

                   * compatibility. The test user_length < min is

                   * implied by the test at the end. */

 

                  /* Support for very large requests */

                  if ((descr->flags & IW_DESCR_FLAG_NOMAX) &&

                     (user_length > descr->max_tokens)) {

                        /* Allow userspace to GET more than max so

                         * we can support any size GET requests.

                         * There is still a limit : -ENOMEM. */

                        extra_size = user_length * descr->token_size;

                       /* Note : user_length is originally a __u16,

                         * and token_size is controlled by us,

                         * so extra_size won't get negative and

                         * won't overflow... */

                  }

            }

 

            /* Create the kernel buffer */

            /*    kzalloc ensures NULL-termination for essid_compat */

            extra = kzalloc(extra_size, GFP_KERNEL);

            if (extra == NULL)

                  return -ENOMEM;

 

            /* If it is a SET, get all the extra data in here */

            if (IW_IS_SET(cmd) && (iwr->u.data.length != 0)) {

                  err = copy_from_user(extra, iwr->u.data.pointer,

                                   iwr->u.data.length *

                                   descr->token_size);

                  if (err) {

                        kfree(extra);

                        return -EFAULT;

                  }

            }

 

            /* Call the handler */

            ret = handler(dev, &info, &(iwr->u), extra);

 

            iwr->u.data.length += essid_compat;

 

            /* If we have something to return to the user */

            if (!ret && IW_IS_GET(cmd)) {

                  /* Check if there is enough buffer up there */

                  if (user_length < iwr->u.data.length) {

                        kfree(extra);

                        return -E2BIG;

                  }

 

                  err = copy_to_user(iwr->u.data.pointer, extra,

                                 iwr->u.data.length *

                                 descr->token_size);

                  if (err)

                        ret =  -EFAULT;

            }

 

            /* Generate an event to notify listeners of the change */

            if ((descr->flags & IW_DESCR_FLAG_EVENT) &&

               ((ret == 0) || (ret == -EIWCOMMIT))) {

                  if (descr->flags & IW_DESCR_FLAG_RESTRICT)

                        /* If the event is restricted, don't

                         * export the payload */

                        wireless_send_event(dev, cmd, &(iwr->u), NULL);

                  else

                        wireless_send_event(dev, cmd, &(iwr->u),

                                        extra);

            }

 

            /* Cleanup - I told you it wasn't that long ;-) */

            kfree(extra);

      }

 

      /* Call commit handler if needed and defined */

      if (ret == -EIWCOMMIT)

            ret = call_commit_handler(dev);

 

      /* Here, we will generate the appropriate event if needed */

 

      return ret;

}

 

/* ---------------------------------------------------------------- */

/*

 * Wrapper to call a private Wireless Extension handler.

 * We do various checks and also take care of moving data between

 * user space and kernel space.

 * It's not as nice and slimline as the standard wrapper. The cause

 * is struct iw_priv_args, which was not really designed for the

 * job we are going here.

 *

 * IMPORTANT : This function prevent to set and get data on the same

 * IOCTL and enforce the SET/GET convention. Not doing it would be

 * far too hairy...

 * If you need to set and get data at the same time, please don't use

 * a iw_handler but process it in your ioctl handler (i.e. use the

 * old driver API).

 */

static int ioctl_private_call(struct net_device *dev, struct ifreq *ifr,

                        unsigned int cmd, iw_handler handler)

{

      struct iwreq *                iwr = (struct iwreq *) ifr;

      const struct iw_priv_args *   descr = NULL;

      struct iw_request_info        info;

      int                     extra_size = 0;

      int                     i;

      int                     ret = -EINVAL;

 

      /* Get the description of the IOCTL */

      for (i = 0; i < dev->wireless_handlers->num_private_args; i++)

            if (cmd == dev->wireless_handlers->private_args[i].cmd) {

                  descr = &(dev->wireless_handlers->private_args[i]);

                  break;

            }

 

      /* Compute the size of the set/get arguments */

      if (descr != NULL) {

            if (IW_IS_SET(cmd)) {

                  int   offset = 0; /* For sub-ioctls */

                  /* Check for sub-ioctl handler */

                  if (descr->name[0] == '\0')

                        /* Reserve one int for sub-ioctl index */

                        offset = sizeof(__u32);

 

                  /* Size of set arguments */

                  extra_size = get_priv_size(descr->set_args);

 

                  /* Does it fits in iwr ? */

                  if ((descr->set_args & IW_PRIV_SIZE_FIXED) &&

                     ((extra_size + offset) <= IFNAMSIZ))

                        extra_size = 0;

            } else {

                  /* Size of get arguments */

                  extra_size = get_priv_size(descr->get_args);

 

                  /* Does it fits in iwr ? */

                  if ((descr->get_args & IW_PRIV_SIZE_FIXED) &&

                     (extra_size <= IFNAMSIZ))

                        extra_size = 0;

            }

      }

 

      /* Prepare the call */

      info.cmd = cmd;

      info.flags = 0;

 

      /* Check if we have a pointer to user space data or not. */

      if (extra_size == 0) {

            /* No extra arguments. Trivial to handle */

            ret = handler(dev, &info, &(iwr->u), (char *) &(iwr->u));

      } else {

            char *      extra;

            int   err;

 

            /* Check what user space is giving us */

            if (IW_IS_SET(cmd)) {

                  /* Check NULL pointer */

                  if ((iwr->u.data.pointer == NULL) &&

                     (iwr->u.data.length != 0))

                        return -EFAULT;

 

                  /* Does it fits within bounds ? */

                  if (iwr->u.data.length > (descr->set_args &

                                     IW_PRIV_SIZE_MASK))

                        return -E2BIG;

            } else if (iwr->u.data.pointer == NULL)

                  return -EFAULT;

 

            /* Always allocate for max space. Easier, and won't last

             * long... */

            extra = kmalloc(extra_size, GFP_KERNEL);

            if (extra == NULL)

                  return -ENOMEM;

 

            /* If it is a SET, get all the extra data in here */

            if (IW_IS_SET(cmd) && (iwr->u.data.length != 0)) {

                  err = copy_from_user(extra, iwr->u.data.pointer,

                                   extra_size);

                  if (err) {

                        kfree(extra);

                        return -EFAULT;

                  }

            }

 

            /* Call the handler */

            ret = handler(dev, &info, &(iwr->u), extra);

 

            /* If we have something to return to the user */

            if (!ret && IW_IS_GET(cmd)) {

 

                  /* Adjust for the actual length if it's variable,

                   * avoid leaking kernel bits outside. */

                  if (!(descr->get_args & IW_PRIV_SIZE_FIXED)) {

                        extra_size = adjust_priv_size(descr->get_args,

                                                &(iwr->u));

                  }

 

                  err = copy_to_user(iwr->u.data.pointer, extra,

                                 extra_size);

                  if (err)

                        ret =  -EFAULT;

            }

 

            /* Cleanup - I told you it wasn't that long ;-) */

            kfree(extra);

      }

 

 

      /* Call commit handler if needed and defined */

      if (ret == -EIWCOMMIT)

            ret = call_commit_handler(dev);

 

      return ret;

}

<think>好的,我现在需要帮助用户了解如何在编程中使用ioctlsocket。用户的问题是关于socketioctl的用法或解决相关问题,特别是如何结合使用ioctlsocket进行编程。首先,我需要回忆这两个系统调用的基本概念以及它们之间的关系。 首先,socket是用于网络通信的接口,创建网络连接时使用。而ioctl是一个用于设备输入输出控制的系统调用,通常用于对设备进行配置或获取设备信息。在socket编程中,ioctl可能被用来获取或设置socket相关的底层参数,比如获取接口信息、修改socket选项等。不过,现在很多功能可能已经被更专门的函数取代,比如getsockopt和setsockopt,但ioctl仍然在某些情况下使用。 接下来,我需要考虑用户可能的实际应用场景。例如,用户可能需要获取网络接口的信息,比如IP地址、子网掩码等,或者调整socket的行为,比如设置非阻塞模式。这时候,使用ioctl可能是必要的步骤。 然后,我需要查找相关的系统调用文档或资料,确认正确的参数和用法。例如,在Linux系统中,使用ioctl的SIOCGIFADDR命令来获取接口地址,或者使用FIONBIO来设置非阻塞模式。同时,需要注意不同操作系统之间可能存在差异,比如Linux和BSD系统在ioctl的使用上可能有所不同,但用户的问题没有明确指定操作系统,可能需要以Linux为例进行说明。 还需要考虑用户可能的错误,比如参数传递错误、结构体未正确初始化等。这时候需要提醒用户注意这些细节,并提供示例代码来说明正确的使用方法。例如,在C语言中使用ioctl时,需要包含正确的头文件,如<sys/ioctl.h>和<net/if.h>,并且正确填充struct ifreq结构体。 另外,用户可能对ioctlsocket之间的关系不太清楚,需要解释ioctl在这里的作用,即通过文件描述符(socket也是一个文件描述符)来控制底层设备的参数。同时,可能需要指出在某些情况下,更推荐使用专用的函数,比如fcntl来设置非阻塞模式,而不是直接使用ioctl,以提高代码的可移植性和可读性。 最后,需要生成相关问题,帮助用户进一步学习。例如,如何获取网络接口列表,如何设置socket为非阻塞模式,ioctl的其他常用命令等。这些相关问题应该围绕用户的主要问题展开,提供更多深入学习的方向。 总结一下,回答的结构应该是先介绍ioctlsocket编程中的基本用法,然后给出具体的示例代码,并解释每个步骤的作用,最后提醒注意事项和常见问题,并生成相关问题。需要确保代码示例正确,语法正确,并且符合用户的实际需求。</think>### 使用 ioctlsocket 的编程方法 #### 1. 基本概念 - **socket**:用于网络通信的文件描述符,支持 TCP/UDP 等协议。 - **ioctl**:系统调用,用于控制设备或文件描述符的底层参数,例如获取网络接口信息、调整 socket 属性等。在 socket 编程中,常用命令包括 `SIOCGIFADDR`(获取接口地址)和 `FIONBIO`(设置非阻塞模式)[^2]。 #### 2. 常见用途与代码示例 ##### (1) 获取网络接口的 IP 地址 ```c #include <stdio.h> #include <string.h> #include <sys/ioctl.h> #include <net/if.h> #include <arpa/inet.h> int main() { int sockfd = socket(AF_INET, SOCK_DGRAM, 0); struct ifreq ifr; strncpy(ifr.ifr_name, "eth0", IFNAMSIZ); // 指定接口名称 if (ioctl(sockfd, SIOCGIFADDR, &ifr) == -1) { perror("ioctl error"); return 1; } struct sockaddr_in *addr = (struct sockaddr_in *)&ifr.ifr_addr; printf("IP Address: %s\n", inet_ntoa(addr->sin_addr)); close(sockfd); return 0; } ``` ##### (2) 设置 socket 为非阻塞模式 ```c #include <unistd.h> #include <fcntl.h> #include <sys/ioctl.h> int set_nonblock(int sockfd) { int flags = 1; if (ioctl(sockfd, FIONBIO, &flags) == -1) { perror("ioctl FIONBIO failed"); return -1; } return 0; } ``` #### 3. 注意事项 1. **头文件依赖**:需包含 `<sys/ioctl.h>` 和 `<net/if.h>`。 2. **兼容性问题**:`ioctl` 的某些命令可能因操作系统不同而存在差异。例如,设置非阻塞模式更推荐使用 `fcntl`: ```c fcntl(sockfd, F_SETFL, O_NONBLOCK); ``` 3. **结构体初始化**:操作 `struct ifreq` 前需清零,避免脏数据: ```c memset(&ifr, 0, sizeof(ifr)); ``` #### 4. 常见错误 - **未找到接口**:若指定的网络接口(如 `eth0`)不存在,`ioctl` 会返回 `-1`。 - **权限不足**:操作网络接口可能需要 root 权限。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值