5.2 Managing Network Devices

   


Now that we know how a network device can be represented by the net_device structure in the Linux kernel, this section discusses the management of network devices. First, we will describe how network devices can be linked, then we will introduce methods that can be used to manage and manipulate network devices. As was mentioned earlier, this section will look at network devices only from the "top" their uniform interface for protocol instances of the higher-order layers.

All network devices in the Linux kernel are connected in a linear list (Figure 5-3). The kernel variable dev_base represents the entry point to the list of registered network devices, pointing to the first element in the list, which, in turn, uses next to point to the next element. Each net_device structure represents one network device.

Figure 5-3. Linking net_device structures.

graphics/05fig03.gif


The proc directory (/proc/net/dev) or the (easier to read) command ifconfig -a can be used to call the list of currently registered devices. (See Appendix C.1.)

5.2.1 Registering and Unregistering Network Devices

We know from the previous section that network devices are managed in the list dev_base. This list stores all registered network devices, regardless of whether they are activated. When register_netdevice() is used to add a new device to this list, then we first have to create and initialize a net_device structure for it. This process can be done in two different ways:

  • If we specified in the kernel configuration that the driver of a network device should be integrated permanently into the kernel, then there is already a net_device structure. A clever mechanism with preprocessor definitions creates different instances of the net_device structure during the translation, depending on the kernel configuration, and these instances are used for the existing network adapters when booting.

    For example, to integrate the driver of an Ethernet card into the kernel, eight net_device structures are created for Ethernet network devices, and these structures are initially not allocated to any card.

  • If the driver was translated as a kernel module, then the driver itself has to create a net_device structure for each existing network adapter. This can be done by the module itself or, for Ethernet drivers, by use of the function init_etherdev().

The list of network devices is used to store entries for those network adapters actually existing. For this reason, before an entry is added to this list, we check for whether a network adapter can be found for a driver. To check this, each of these drivers has an init() or probe() function. It is very specific to each adapter and will be described in more detail in Section 5.3. Among the exception, are logical network devices, such as loopback (lo) and PPP (ppp0), which don't have to rely on underlying hardware.

The following discussion assumes that we want to register a network device based on an Ethernet adapter. During booting of the system or loading of the driver module, the appropriate adapter was found. However, before the network device can be added to the list of known network devices (dev_base), the net_device structure should have been initialized so that the network device can actually be activated and used. The kernel has certain functions (mainly for Ethernet adapters) that facilitate the driver programmer's work.

init_netdev()

drivers/net/net_init.c


init_netdev(dev, sizeof_priv, mask, setup) initializes the most important elements from the general range of the net_device structure. (See Section 5.1.1.) First, however, we have to check on whether there is a net_device structure at all. If the value null was entered for dev in the call, then init_alloc_dev() is used to create a new net_device structure, which is also added to the list of network devices by register_netdevice() at the end of the initializing process. If the net_device structure existed already before the call, then the caller has to register it.

Subsequently, the name of the network device is verified. If the array dev->name consists of an empty string or begins with a blank, then the kernel uses the method dev_alloc_name() to allocate a name. In this case, there is an option to use the mask parameter to specify a prefix, which is extended to a valid name for a network device by the known scheme. For example, the prefix test%d produces the name test0, test1, and so on, depending on the network devices already existing with this prefix. If the prefix does not contain a formatting character (%d), then the name should be unique; otherwise, consecutive numbering is not possible, and the function will return an error. Consequently, the network device cannot be initialized.

Once a network device has a unique name, we verify that parameters for its hardware configuration have been stated when the system boots. For this purpose, the list dev_boot_setup in the method netdev_boot_setup_check() is searched for an entry with the name of the new network device. If there is such an entry, then the parameters irq, mem_start, mem_end, and base_addr are taken and added to the net_device structure of the network device.

Now the general part of the initialization of the net_device structure is completed. In the calling of init_netdev, a pointer in the setup parameter has to be passed to a function, which can be used for individual configuration. In general, the setup function handles layer-2 initialization tasks. When one is using init_etherdev(), reference is made to the ether_setup() method, which initializes the Ethernet-specific function pointers in the net_device structure. Finally, init_netdev() returns with a pointer to the network device.

init_etherdev()

drivers/net/net_init.c


init_etherdev(dev, priv_size) can be used by Ethernet-based network devices. The function does nothing but call init_netdev() with the correct parameters. Similar functions are also available for other MAC protocols (FDDI, HIPPI, etc.).

 struct net_device *init_etherdev(struct net_device *dev, int sizeof_priv) {        return init_netdev(dev, sizeof_priv, "eth%d", ether_setup); } 

ether_setup()

drivers/net/net_init.c


ether_setup(dev) is called in init_netdev() to initialize the Ethernet-specific parameters and methods of a network device. It adds the pointers of the MAC protocol-specific functions for Ethernet adapters to the net_device structure (hard_header, mtu, ...). In addition, it sets the flags (dev?gt;flags) of the network device to IFF_BROADCAST|IFF|MULTICAST.

 dev?gt;change_mtu          = eth_change_mtu; dev?gt;hard_header         = eth_header; dev?gt;rebuild_header      = eth_rebuild_header; dev?gt;set_mac_address     = eth_mac_addr; dev?gt;hard_header_cache   = eth_header_cache; dev?gt;header_cache_update = eth_header_cache_update; dev?gt;hard_header_parse   = eth_header_parse; dev?gt;type                = ARPHRD_ETHER; dev?gt;hard_header_len     = ETH_HLEN; dev?gt;mtu                 = 1500;      /* eth_mtu */ dev?gt;addr_len            = ETH_ALEN; dev?gt;tx_queue_len        = 100;       /* Ethernet wants good queues */ memset(dev?gt;broadcast,OxFF, ETH_ALEN); dev?gt;flags               = IFF_BROADCAST|IFF_MULTICAST; 

register_netdevice()

net/core/dev.c


register_netdevice(dev) is responsible for registering the network device represented by the passed net_device structure with the kernel. If the function dev?gt;init() exists, then the network adapter is first searched and initialized by the driver function init(). Subsequently, there is a check on whether a network device with the requested name is already available. If so, then register_netdevice() returns an error message; otherwise, the network device is simply appended to the end of the linked list dev_base, so that it is available for general use.

In addition, the state dev->state is set to LINK_STATE_PRESENT in register_netdevice(), and dev_init_scheduler() sets the scheduling process for the new network device to the standard FIFO mechanism. This method is also used to cause the function dev_watchdog_init() to initialize the timer to detect transmission problems (dev->watchdog_timer; see Section 5.3.4). However, this timer is not started before the network adapter is activated (dev_open(); see Section 5.2.2).

Finally, the network device obtains a new number (dev->ifindex), and notifier_call_chain(&netdev_chain, NETDEV_REGISTER, dev) calls all registered methods in the notification chain netdev_chain.

unregister_netdevice()

net/core/dev.c


unregister_netdevice(dev) removes the net_device structure passed as parameter from the list of registered network devices (dev_base). If the network device is still in active state (IFF_UP), it is now closed by the driver function dev->close(). Subsequently, it is searched in the list dev_base and removed. If it is not found, then the function returns an error message.

Once the structure has been removed, unregister_netdevice() synchronizes itself to the NET-RX software interrupt by the big reader lock BR_NETPROTO_LOCK. Subsequently, the queuing discipline is released by dev_shutdown(), and all registered functions in the notification chain netdev_chain are informed about the NETDEV_UNREGISTER event. Subsequently, the destructor of the network driver is called, if it exists. Only the more recent network drivers have destructors, so we have to check periodically on whether existing references to the network device (dev->refcnt) disappeared for older drivers to be able to actually remove the network device.

5.2.2 Opening and Closing Network Devices

The previous section described how a network device is registered; the current section explains how one can be activated and deactivated. As with other device types in the Linux system, activating and deactivating is also referred to as opening and closing. To open and close a network device, the administrator can use the command ifconfig. It is used not only to activate and deactivate network devices, but also to configure them. More specifically, this command is used to set protocol-specific parameters, such as addresses and subnet masks, and to modify interface parameters for the network adapter (hardware parameters).

In addition, ifconfig can be used to change the flags of a network device. The syntax of ifconfig and its options are listed in Appendix C.1. Naturally, before a network device can be activated, it has to be registered.

Activating a Network Device

When we use ifconfig name address up to activate a network device, ifconfig uses the ioctl() command SIOCSIFADDR (Socket I/O Control Set InterFace ADDRess) to allocate the specified address to the network device name. The handling routine for the INET address family is devinet_ioctl.

Subsequently, the ioctl() command SIOCSIFFLAGS (Socket I/O Control Set InterFace FLAGS) is used to set the IFF_UP flag for the network device in the handling method dev_ifsioc(). To manipulate the flag, we use the method dev_change_flags(dev, flags), which also causes the method dev_open(dev) to be called when the IFF_UP flag is set.

dev_open()

net/core/dev.c


The function dev_open(dev) opens a network device (i.e., the network device is activated and can be used). If the network device is already active (IFF_UP), or if it has not been registered ((!netif_device_present(dev)), then the function returns an error message.

The actual initialization (i.e., the device-specific functions) is executed by the open() function of the network driver, if it exists. If this initialization is successful (i.e., no error occurred), then the net_device specific states are set as follows:

  • dev->flags assumes the state IFF_UP.

  • dev->state is set to LINK_START_START.

  • The multicast state is activated by dev_mc_upload().

  • The queue and the scheduler of the network device are activated (dev_activate(dev)). At the same time, the method dev_watchdog_up(dev) starts the timer to detect transmission problems. (See Section 5.3.4.) The timer calls the method dev_watchdog() every dev->watchdog_timeo ticks to check that the network adapter works properly.

  • The notification chain netdev_chain is informed about the event NETDEV_UP.

Deactivating a Network Device

When we use the command ifconfig name down in dev_ifsioc to deactivate a network device, the method dev_change_flags(dev, flags) in the variable dev->flags deletes the IFF_UP flag. The general part of transferring the network device into the inactive state is done by dev_close(). The adapter-specific actions are executed in the driver method dev->stop().

dev_close()

net/core/dev.c


If the network device dev is in the IFF_UP state, then it is deactivated by dev_close(dev) in the following steps:

  • All methods in the notification chain netdev_chain are informed about the upcoming deactivation of the network device (NETDEV_GOING_DOWN) and can act accordingly.

  • Next, dev_deactivate(dev) removes the packet scheduler dev->qdisc, and the LINK_STATE_START bit in dev->state is deleted. In addition, dev_watchdog_down() stops the timer used to detect transmission problems.

  • The driver function dev->stop() deactivates the network adapter.

  • Next, all protocols concerned are notified that the network device was stopped (notifier_call_chain(.., NETDEV_DOWN, ..)).

  • Finally, the reference counter that points to the net_device structure is decremented by one.

5.2.3 Creating and Finding Network Devices

dev_alloc_name()

net/core/dev.c


Each network device in Linux has a unique name. As mentioned earlier, network devices are not represented in the file system, in contrast to character and block devices, which means that they are not addressed by major and minor numbers. In general, network devices are named by the network type. Table 5-2 shows a few of the names currently used. In addition, devices of the same type are shown and numbered in ascending order, starting from zero (e.g., isdn0, isdn1, etc.).

Table 5-2. Naming convention for Linux network devices.

Name

Network Device Type

eth

Ethernet (802.3, Ethernet V2), 10 Mbit/s or 100 Mbit/s

tr

Token Ring (802.5)

atm

Asynchronous Transfer Mode

sl

SLIP (Serial Line Interface Protocol)

ppp

PPP (Point-to-Point Protocol)

plip

PLIP (Parallel Line Interface Protocol)

tunl

IPIP Tunnel

isdn

ISDN (Integrated Services Digital Network)

dummy

Dummy-Device

lo

Loopback-Device


Notice, however, that there are exceptions in allocating network adapters to a category. Linux is strongly oriented to Ethernet, and some functions in the kernel facilitate handling of Ethernet-like adapters, so some (non-Ethernet) adapters also use the category ethn, including some ISDN cards.

The convention used in Linux to name network devices has several benefits:

  • When one is designing applications and creating configuration scripts, it is simpler to address network devices without knowing their manufacturers and their hardware parameters (interrupt number, port number).

  • When replacing equal-type hardware, for example to upgrade an Ethernet adapter from 10 Mbit/s to 100 Mbit/s, we don't have to change the network setting; simply accessing ethn is sufficient.

dev_alloc()

net/core/dev.c


dev_alloc(name, err) reserves memory for a net_device structure. Subsequently, a name is assigned to the network device. As described in connection with init_netdev(), we can use name to specify a prefix. In both cases, the method dev_alloc_name() is used to construct the name.

The parameter err is a pointer to an integer variable, which is contained in the return value of dev_alloc_name() wherever an error occurs. If an error occurs, then dev_alloc() always returns a NULL pointer. Naturally, if the if is successful, it returns a pointer to the new net_device structure.

dev_get...()

net/core/dev.c


Various kernel components need to access a specific network device from time to time. We can search for the right net_device structure in different ways. net/core/dev.c has several functions that facilitate this search. These functions step through the linear list of network devices, starting with dev_base, and, when they find it, they return a pointer to the net_device structure we looked for:

  • dev_get_by_name(name) searches for the network device specified by name.

  • dev_get(name) also searches for the device specified by name.

  • dev_get_by_index(ifindex) uses the index of the network device as search criterion.

  • dev_getbyhwaddr(type, ha) searches for the network device by the MAC address ha and the type.

dev_load()

net/core/dev.c


When a network device is unavailable (i.e., not present in the list of registered devices), then we can use dev_load(name) to request the corresponding driver module. To request a driver module, the kernel has to support the automatic loading of modules. Also, the process in which the request for the network device originates has to have privileges to load the module (CAP_SYS_MODULE):

 if (!dev_get(name) && capable(CAP_SYS_MODULE))              request_module(name); 

5.2.4 Notification Chains for State Changes

As mentioned above, network devices can be registered and removed dynamically. Also, their state can change in the course of time. For example, a network device can change its hardware address or name.

On the network-device level, a state change did not cause problems, but protocol instances in the higher layers use the services of network devices. For efficiency and simplicity reasons, these protocol instances often store references to the network devices they use. When the state of a network device changes, then these stored states become invalid. The protocol instances concerned should be notified about this fact. Unfortunately, a network device does not normally know the protocols that use its services, or what references they store.

For this reason, protocols can register themselves to be notified of state changes in network devices. This concept is called notifier chains; it consists of a list (netdev_chain) of notifier_block structures. Figure 5-4 shows an example of how they are structured. Each of these notifier_block elements includes a request for notification when the state of a network device changes:

  • notifier_call() is a pointer to a handling routine, which handles the notification about a network device's state change. Each protocol instance that stores states of network devices should have such a function. For example, the method bridge_device_event() is used for the instance of a transparent bridge described in Chapter 12.

    Figure 5-4. Notification chain for network devices.

    graphics/05fig04.gif


    If a state change occurs in a network device, then all handling routines stored in the list netdev_chain are called, where the following arguments are passed:

    • a pointer to the notifier_block structure, the handling routine of which is actually called;

    • a pointer to the network device (net_device) that changed its state;

    • the state representing the cause for this notification.

  • priority specifies a priority for processing of notifications.

  • next points to the next notifier_block element in the list and is used for concatenation.

The concept of notifier chains is used not only for network devices, but also for other states that can change. For this reason, the implementation of these notifier chains is generic and can easily be used for other purposes. For example, there is a reboot_notifier_list chain informing that the system is about to reboot.

In the networking area, the concept of notifier chains is used for state changes in network devices. The following list shows possible causes for notifications. Subsequently, we introduce three important functions of this concept.

  • NETDEV_UP activates a network device (by dev_open).

  • NETDEV_DOWN deactivates a network device. As a consequence of this message, all references to this network device should be removed.

  • NETDEV_CHANGE informs that a network device changed its state.

  • NETDEV_REGISTER means that a network device was registered but no instance of it has yet been opened.

  • NETDEV_UNREGISTER informs that a network driver was removed.

  • NETDEV_CHANGEMTU means that the MTU (Maximum Transfer Unit) changed.

  • NETDEV_CHANGEADDR means that the hardware address of a network device changed.

  • NETDEV_CHANGENAME means that the name of a network device changed.

When a registered handling routine is called, it can use the appropriate functions based on the type passed. For example, when a network device is deactivated (NETDEV_DOWN) in KIDS (see Chapter 22), the corresponding hook is deleted. The method notifier_call_chain(&netdev_chain, EVENT, dev), which will be described below, is used to call the notifier chain.

notifier_call_chain()

net/core/dev.c


notifier_call_chain(&netdev_chain, EVENT, dev) informs all handling methods registered in the list netdev_chain about the EVENT, where the events described above can occur. notifier_call_chain() is very simple. The linking in the list is used to call all registered functions, one after the other. If one of the functions called returns the constant NOTIFY_STOP_MASK, then the notification is stopped. This is useful to prevent there being several reactions to one event. Otherwise, all registered handling routines are always informed about the event each time this function is called. The registered handling method alone decides whether the message is meaningful for it.

register_netdevice_notifier()

net/core/dev.c


register_netdevice_notifier(nb) is merely another representation of the method notifier_chain_register(&netdev_chain, nb), which inserts the notifier_block structure passed to the list netdev_chain. The position within the list is determined by the priority.

unregister_netdevice_notifier()

net/core/dev.c


unregister_netdevice_notifier(nb) removes the specified notifier_block structure from the list netdev_chain. The function notifier_chain_unregister() is used for this purpose.

5.2.5 Transmitting Over Network Devices

dev_queue_xmit()

net/core/dev.c


dev_queue_xmit(skb) is used by protocol instances of the higher protocols to send a packet in the form of the socket buffer skb over a network device, which is specified by the parameter skb->dev in the socket buffer structure. Section 6.2.2 explains how a packet is transmitted in detail.


       


    Linux Network Architecture
    Linux Network Architecture
    ISBN: 131777203
    EAN: N/A
    Year: 2004
    Pages: 187

    flylib.com © 2008-2017.
    If you may any questions please contact us: flylib@qtcs.net