Home | Linux | NetworkManager |     Share This Page
Controlling NetworkManager

Getting along with an important Linux utility

All content Copyright © 2008, P. LutusMessage Page

Introduction | Some Detail | NM Block Diagram | GConf | The Problem | The Solution | Notes

 
Introduction

Over time Linux network management has gradually become more complex, to the degree that the default configuration tools ('system-config-network' on Fedora and 'network-admin' on Debian/Ubuntu) now appear out of their depth.

In the old days a typical Linux system had a single network interface controller (hereafter NIC) with a single configuration and a single IP address, and network configuration consisted of deciding whether an address was to be assigned dynamically using DHCP or be defined using static addressing, and which runlevels had network access. Very simple.

How quickly things change. Now we have Linux systems with multiple NICs of many kinds including wired Ethernet, wireless 802.11 a, b, g and n, vendor-specific mobile broadband plugin adaptors and other similar devices. These new technologies have completely overwhelmed the old network configuration and management software.

A relatively new project named NetworkManager (hereafter NM) addresses these shortcomings. At the time of writing (Winter 2008) NM is a work in progress and hasn't been fully fleshed out, but it shows great promise, to the degree that I strongly recommend that Linux system administrators learn how it works and, more important, learn how to control it.

NM was originally meant to work alongside existing network management schemes and simplify connections to wireless access ports (hereafter WAPs) under circumstances where WAPs might appear and disappear over time. But this limited role was eventually seen as unworkable, and since then NM has been expanded to control almost all network interfaces (with the single exception of local loopback).

What this means is that NM can replace all prior network control schemes, and in recent Fedora and Debian/Ubuntu releases, it does just that. The older control tools are still present, but to allow NM to function (and IMHO) they should be set up to defer to NM.

Some Detail

NM consists of a system daemon (controlled via /etc/init.d/NetworkManager) and a user configuration tool, typically 'nm-applet'. It's important to add that, because NM uses HAL for its hardware interface and D-Bus for communications, anyone can write a custom utility to control NM for particular ends.

It is important to understand that, as normally configured and at the time of writing, NM is user-centric, not system-centric (this issue will probably be addressed in a future version), consequently it isn't appropriate for systems with multiple simultaneous GUI logons. When a particular user logs on, the NM control applet 'nm-applet' takes charge and chooses an appropriate network strategy. This means your system may need to fall back to a more conventional network configuration between logons or while in a runlevel that doesn't support GUI logons.

A modern Linux distribution gets its networking information using this general scheme:
  • While in runlevel 3, or runlevel 5 before a user logs on:
    • Fedora: configurations located in files at /etc/sysconfig/network-scripts/ifcfg-*
    • Debian/Ubuntu: configurations located in /etc/network/interfaces
  • After a user logs on to Gnome, KDE or a similar environment and NM kicks in:
    • Fedora/Debian/Ubuntu: configurations obtained from 'GConf' and normally located under (user directory)/.gconf/system/networking/connections

Notice that, once a user logs on, NM's user-level configuration is located and managed in the same way regardless of Linux distribution, by way of GConf. I regard this common approach to be one of NM's advantages, at a time when many Linux distributions seem to be narcissistically wandering away from any discipline about common protocols and procedures. This uniform configuration also makes it possible to write network applications that work the same on all Linux distributions.

It is also important to understand that there are two strategies for controlling NM — the GConf-maintained database just described and D-Bus, an interprocess communication protocol. These methods serve different purposes and work at different levels, but they provide a high degree of control and a way to control network activities in a distribution-neutral way.

NM Block Diagram

Before moving into greater detail, here is a structural overview of NM. It is said that a picture is worth a thousand words. This one might be worth 210 words if I've drawn it correctly:

Notice the connection at the left between the NM root daemon and the system networking devices block. This is how NM controls network interfaces in ways that HAL cannot, but everyone hopes this workaround will become unnecessary in time.

GConf

Because NM uses GConf for data storage, and assuming we don't want to make hand entries for each network configuration, we need to learn how to automatically edit the GConf database.

GConf is a database scheme for managing configurations that has been widely adopted. It's easy to control by way of command-line utilities (a current example is gconftool-2) or GUI-based editors like gconf-editor. We'll be focusing our attention on gconftool-2.

Obviously one can use 'nm-applet' to create and edit network configurations, and this is actually a good way to find out which database entries are required and what they look like, but eventually we may want to automate the process, for example to set up an entire network of similar machines. Let's look at the relationship between a connection as displayed in 'nm-applet' and as stored in 'GConf':

  • Enter the 'nm-applet' connections editor (right-click the nm-applet icon and choose "Edit Connections ...").
  • If there is no "Wired" connection already configured, create one by clicking "Add ...", and enter some information such as a name.
  • Click "Apply".
  • Open a user-level shell session (in Gnome, "Applications ... System Tools ... Konsole", in KDE, "System ... Konsole Terminal").
  • Enter this:
    $ gconftool-2 --recursive-list /system/networking/connections/1
                    
    (The '$' at the left is just to remind the reader that this is a user-level shell session, it isn't part of the entry.) Here is a typical response for a completely defined wired connection:
    /system/networking/connections/1/ipv4:
      routes = []
      addresses = [16820416,24,1610655936]
      method = manual
      name = ipv4
      dns = [16777343,33554559]
     /system/networking/connections/1/802-3-ethernet:
      mac-address = [26,0,219,151,160,141]
      duplex = full
      name = 802-3-ethernet
     /system/networking/connections/1/connection:
      id = Wired eth0
      timestamp = 1228498226
      type = 802-3-ethernet
      uuid = bb1debea-5023-4a15-87ad-b00ba19c2202
      name = connection
      autoconnect = true
                    

Here are some notes about this GConf listing:

  • The "addresses" list of three numbers are:
    • The connection's IP address expressed as an integer (see below).
    • The network class ("C" = 24,"B" = 16,"A" = 8). This entry is equivalent to a netmask (e.g. 24 = 255.255.255.0).
    • The gateway address expressed as an integer.
  • The "method" entry can have one of several values, among which are "manual" for statically assigned address and "auto" for DHCP.
  • The "dns" entry is a list of DNS server IPs expressed as integers. The list can be empty when it isn't needed, as for DHCP.
  • The "mac-address" entry expresses a MAC address as a list of decimal integers, and is used to distinguish between interfaces on a system with more than one. If your system has at most one wired and one wireless interface, you don't need to worry about this value.
  • The "id" entry is the connection's name as displayed by NM.
  • If set to "true", the "autoconnect" entry grants NM permission to dynamically make a connection under circumstances to be explained below.

NM expresses dotted IPs as integers for storage. The conversion looks like this:

  • Dotted IP (a.b.c.d) to integer: a + (b * 28) + (c * 216) + (d * 224)
  • I know this looks backwards, but that's the way it is.
NM chooses a connection according to these general priorities (and assuming the user's connections are marked "autoconnect"):
  1. Highest priority is given to a functioning wired connection.
  2. If (1) is not available, then connection is made to a wireless AP that is defined or that has been connected to before.
  3. If (2) is not available, then connection is made to any available wireless AP.
  4. This is by no means the whole decision tree, and it will doubtless change over time.

Remember NM's basic purpose is to intelligently acquire a network connection while on the move. But it's becoming clear that NM might serve as a better, more uniform way to configure any system, not just a laptop with a wireless NIC. One important reason for this expanded role is the uniform data storage scheme that is easy to manipulate and that conceals differences between Linux distributions.

Here is an example of reading and writing a name-value pair from the GConf database, using the example connection definition above as our test case:

  • Read a single name-value pair:
    $ gconftool-2 --get /system/networking/connections/1/connection/id
        (response) Wired eth0
                    
  • Write a single name-value pair:
    $ gconftool-2 --type string --set /system/networking/connections/1/connection/id 'My Wired Connection'
                    

Notice about the write instruction that a type must be provided, and that the value field must be quoted if it contains spaces or any characters with special meaning to the shell. Types include:

  • bool: true/false
  • string: 'any string of characters'
  • int: 12345
  • float: 12.345
  • list: [16820416,24,1610655936]

Because a list contains members with their own data type, each list specifier must also have a "--list-type" specifier:

$ gconftool-2 --type list --list-type int --set /system/networking/connections/1/ipv4/addresses "[16820416,24,1610655936]"
            
There is one additional instruction that is very useful while creating connection definitions:
$ gconftool-2 --recursive-unset /system/networking/connections
            

This instruction wipes out all defined connections — it is normally issued prior to creating a new set by way of a script, and it assures that there are no name-value pairs left over from prior definitions.

The Problem

The example problem I present is real enough, and it is why I learned how to control NM. I have a small local network of machines that must be able to communicate with each other as well as the outside world. Because the machines must communicate with each other as peers, and because DHCP is possibly the most obtuse, shortsighted piece of software ever written, in order to resolve local addresses I use static addressing and a list of IPs in the /etc/hosts file of each machine.

This simple scheme worked all right until wireless became important and until NM became the default way to control networking. Once NM took over, things fell apart because NM can't manage static wireless addresses if they are gotten from the older network configuration files. After performing elaborate experiments to make sure I wasn't the cause of the problem, I reported it as a bug (550610) and began looking into some other way to specify addresses to NM.

Obviously I could have waited until DHCP is rewritten to allow people to discover which address DHCP assigned to which machine, or I could have waited until NM learns how to assign static addresses to wireless interfaces based on information contained in configuration files. But being mortal, it occurred to me that I would be long dead before someone said, "Hey! I just had an amazing dream! It came to me that DHCP could reveal which machine got which address! I'm a genius! I should patent this idea!" But, unwilling to wait for this miracle, I began the present project.

Here is a concise statement of the problem (and bear with me, dear reader, the solution coming up can be applied to many similar problems):

A group of machines on a local network, sometimes connected via wired interfaces, but sometimes connected via wireless 802.11 NICs in mobile service, need to reliably communicate with each other, thus they need to have static addresses regardless of which NIC (wired/wireless) is active. The machines communicate with each other by way of Secure Shell (hereafter SSH).

As mentioned before, the earlier solution to this problem broke down once NM became the default network configuration tool, and I didn't want to simply abandon NM, which appeared to have a number of very attractive features.

The Solution

The solution I wrote has the form of a Ruby script that programs my network's machines in two passes. In the first pass the script detects each machine's requirements. If a particular machine has a wireless NIC, that machine gets wireless connection definitions to allow it to communicate by way of one of three wireless access points I have available. The Ruby script visits each machine on the network by way of SSH, detects its configuration, and produces corresponding connection definitions. On the second pass the connection definitions are transmitted via SSH to the GConf database on each target machine.

  • Click here for a pretty-printed version of the Ruby listing.
  • Click here for a plain-text version of the Ruby listing.
I want to emphasize that this Ruby program isn't likely to be useful to anyone but me as written, but it has all the essential components for configuring NM in a general way. Here are some notes about its methods:
  • "int_to_ip" and _ip_to_int" perform the conversions between the dotted string IP form and the integer form.
  • "string_to_int_array" creates a list of integers out of the characters in a string. This is used to represent a wireless SSID that might include Unicode characters in an environment that doesn't support Unicode.
  • I use the method "has_wireless" to detect which of the machines has wireless interfaces. Readers might want to change this routine to simply detect the presence of the word "wireless" in the output of "/sbin/lspci".
  • The method "get_ip_list" creates a hash with hostnames as keys and IP addresses as values. This is another example of a method that would almost certainly need to be rewritten for any network other than my own.
  • The next large block in the program is used to construct data subtrees in the connection database — wired and wireless connections with all the required details.
  • The methods "mktab" and "showtree" are used only for debugging, to see exactly what the data tree contains.
  • "build_database" traverses the system list and creates a database of connection definitions.
  • "program_system" and "program_system_recursive" walk the data tree and create commands that gconftool-2 will recognize. To save time, the gconftool-2 commands are combined into a long string and submitted at once in a single SSH transaction with the target system.
  • The array "system_list" contains a list of local network host names. This is another example that readers will need to change for their own purposes. An example Ruby definition would be:
      system_list = [ "hostname1", "hostname2", "hostname3" ]
                    
  • The "wap_list" string contains a list of my wireless access point SSIDs, for use in constructing wireless connections. NM uses the SSID information to identify a particular access point, and will only use an existing connection definition if it can find the SSID in its database. This is the key to getting static wireless address assignment.
Notes

It was while creating this method that I first acquired a sense that I could have any control over what NM chose to do. Until now I watched in awe as NM did whatever it pleased and blithely ignored my efforts to send it signals by way of network configuration files.

I wonder whether "ignore the user's wishes and inputs" actually represents a trend in open-source programming. I hope not.

Remember about the methods described in this article that, if you use static addressing or for any reason don't want automatic DHCP configuration on your primary wired NIC at boot time, in runlevel 3, or before logon in runlevel 5, you will need at least one conventional configuration file — either:

  • The file /etc/sysconfig/network-scripts/ifcfg-eth0 on Fedora.
  • An entry in /etc/network/interfaces on Debian/Ubuntu.

The symptom that this requirement hasn't been met will be the presence within nm-applet of a read-only connection profile with the name "Auto eth0" that tries to create a DHCP connection, and that cannot be edited or removed.

I had intended to include a section about controlling NM by way of the D-Bus interprocess communication protocol, but present D-Bus command-line utilities are rather primitive and limited in functionality and there aren't many D-Bus/programming language interfaces available yet, so this will have to wait until D-Bus is better represented within scripting languages like Ruby.

It would be nice if NM had a way to control the state of individual interfaces. At the moment there is no way to enable or disable individual connections in any reliable way, partly because of the NM strategy of doing everything by itself without brooking any interference from the user. I may look into creating a D-Bus control applet for that purpose.

 

Home | Linux | NetworkManager |     Share This Page