Jump to content

ioctl

From Wikipedia, the free encyclopedia

This is an old revision of this page, as edited by Tqbf (talk | contribs) at 17:40, 19 January 2008 (Background: fix). The present address (URL) is a permanent link to this revision, which may differ significantly from the current revision.

In computing, an ioctl (Template:PronEng) is part of the user-to-kernel interface of a conventional operating system. Short for "Input/output control", ioctls are typically employed to allow userspace code to communicate with hardware devices.

Background

Conventional operating systems can be divided into two layers, userland and the kernel. Application code such as a web browser resides in userland, while the underlying facilities of the operating system, such as the network stack, reside in the kernel. Kernel code handles sensitive resources and implements the security and reliability barriers between applications; for this reason, user mode applications are prevented by the CPU from directly accessing kernel resources.

Userland applications make most requests of conventional kernels by means of system calls, which are special subroutine calls that transition the operating system from user mode to kernel mode. This is usually implemented with a system call vector, in which the requestor indicates the desired system call with an index number: for instance, "exit" might be system call 1, and "write" might be system call 4. The system call vector and index is used to look up a kernel function to handle the request. In this way, conventional operating systems typically provide several hundred system calls.

Though an expedient design for system calls, this presents challenges for device drivers. By necessity, most hardware peripherals are directly addressable only within the kernel. But user code often must communicate with devices; for instance, an administrator might configure the media type on an Ethernet interface. Modern operating systems support a diversity of devices, many of which offer complex user-visible interfaces, and not all of which are known to the operating system vendor, so it is difficult to provide a device API with hardcoded system call numbers.

Ioctl interfaces skirt this problem by allocating a single system call shared by all device drivers. Through this system call, specific requests of various devices are vectored, typically with a handle to the device and a request number. The kernel can thus dynamically dispatch calls to devices without creating an unmanageable hardcoded system call table.

Uses

Terminals

One use of ioctls exposed to end-user applications is terminal I/O.

Unix operating systems have traditionally made heavy use of command line interfaces. The Unix command line interface is built on pseudo terminals (ptys), which emulate hardware text terminals such as VT100. Ptys are controlled and configured as if they were hardware devices, using ioctl calls. For instance, the window size of a pty is set using the TIOCSWINSZ ioctl.

Hardware device configuration

The most common use of ioctls is to control hardware devices.

For example, on Win32 systems, ioctl calls are used to communicate with USB devices, or to discover drive geometry information for attached storage devices.

Ioctls are used on Unix systems to configure the network interfaces. For example, on BSD Unix systems such as Mac OS X, the IP subnet mask for an interface is configured by opening a network socket and then invoking the SIOCSIFNETMASK ioctl on it.

Kernel extensions

When applications need to extend the kernel, for instance to accelerate network processing, ioctl calls provide a convenient way to bridge userland code to kernel extensions. Kernel extensions can provide a location in the filesystem that can be opened by name, through which an arbitrary number of ioctl calls can be dispatched, allowing the extension to be programmed without adding system calls to the operating system.

For example, on the Solaris operating system, the ipfilter packet firewall is programmed using the SIOCIPF* ioctl calls.

Implementations

Unix

A Unix ioctl call takes as parameters:

  1. an open file descriptor
  2. a request code number
  3. either an integer value, possibly unsigned (going to the driver) or a pointer to data (either going to the driver or to come back from it).

The kernel generally dispatches an ioctl straight to the device driver, which can interpret the request number and data in whatever way required. The writers of each driver document request numbers for that particular driver and provide them as constants in a header file.

Some Unix systems have conventions which encode within the request number the size of the data to be transferred to/from the device driver, the direction of the data transfer and the identity of the driver implementing the request. Regardless of whether such a convention is followed, the kernel and the driver collaborate to deliver a uniform error code (denoted by the symbolic constant ENOTTY) to an application which makes a request of a driver which does not recognise it.

The mnemonic ENOTTY (traditionally associated with the textual message "Not a typewriter") derives from the fact that in the earliest systems that incorporated an ioctl call, only the teletype (tty) device raised this error. Though the symbolic mnemonic is fixed by compatibility requirements, some modern systems more helpfully render a more general message such as "Inappropriate device control operation" (or a localisation thereof).

TCSETS exemplifies an ioctl on a serial port. The normal read and write calls on a serial port receive and send data bytes. An ioctl(fd,TCSETS,data) call, separate from such normal I/O, controls various driver options like handling of special characters, or the output signals on the port (such as the DTR signal).

Win32

A Win32 DeviceIoControl takes as parameters:

  1. an open object handle (the Win32 equivalent of a file descriptor)
  2. a request code number (the "control code")
  3. a buffer for input parameters
  4. a buffer for output results
  5. an OVERLAPPED structure, if overlapped I/O is being used.

Alternatives

Other vectored call interfaces

Devices and kernel extensions may be linked to userland using additional new system calls, although this approach is rarely taken, because operating system developers try to keep the system call interface focused and efficient.

On Unix operating systems, two other vectored call interfaces are popular: the fcntl ("file control") system call configures open files, and is used in situations such as enabling non-blocking I/O; and the setsockopt ("set socket option") system call configures open network sockets, a facility used to configure the ipfw packet firewall on BSD Unix systems.

Memory mapping

Device interfaces and input/output capabilities are sometimes provided using memory-mapped files. Applications that interact with devices open a location on the filesystem corresponding to the device, as they would for an ioctl call, but then use memory mapping system calls to tie a portion of their address space to that of the kernel.

This interface is a far more efficient way to provide bulk data transfer between a device and a userland application; individual ioctl or read/write system calls inflict overhead due to repeated userland-to-kernel transitions, where access to a memory-mapped range of addresses incurs no such overhead.

Implications

Complexity

Ioctl calls minimize the complexity of the kernel's system call interface. However, by providing a place for developers to "stash" bits and pieces of kernel programming interfaces, ioctls complicate the overall user-to-kernel API. A kernel that provides several hundred system calls may provide several thousand ioctl calls.

Though the interface to ioctl calls appears somewhat different from conventional system calls, there is in practice little difference between an ioctl and a system call; an ioctl is simply a system call with a different dispatching mechanism. Many of the arguments against expanding the kernel system call interface could therefore be applied to ioctl interfaces.

To application developers, system calls appear no different from application subroutines; they are simply function calls that take arguments and return values. The runtime libraries of the OS mask the complexity involved in invoking system calls. Unfortunately, runtime libraries do not make ioctl calls as transparent. Simple operations like discovering the IP addresses for a machine often require tangled messes of ioctl calls, each requiring magic numbers and argument structures.

Libpcap and libdnet are two examples of third-party wrapper libraries designed to mask the complexity of ioctl interfaces, for packet capture and packet I/O, respectively.

Security

The user-to-kernel interfaces of mainstream operating systems are often audited heavily for code flaws and security vulnerabilities prior to release. These audits typically focus on the well-documented system call interfaces; for instance, auditors might ensure that sensitive security calls such as changing user IDs are only available to administrative users.

Ioctl interfaces are more complicated, more diverse, and thus harder to audit than system calls. Furthermore, because ioctls can be provided by third-party developers, often after the core operating system has been released, ioctl call implementations may receive less scrutiny and thus harbor more vulnerabilities. Finally, many ioctl calls, particularly for third-party device drivers, are undocumented.

Some modern operating systems protect the kernel from hostile userland code (such as applications that have been infected by buffer overflow exploits) using system call wrappers. System call wrappers implement role-based access control by specifying which system calls can be invoked by which applications; wrappers can, for instance, be used to "revoke" the right of a mail program to spawn other programs. Ioctl interfaces complicate system call wrappers because there are large numbers of them, each taking different arguments, some of which may be required by normal programs.

References