April 2007


This post describes how to set up host networking for VirtualBox on Ubuntu 7.04 Feisty. The procedure probably works for older versions of Ubuntu.

The outcome of the procedure is that you’ll have a second network card for your virtual machine, which you can configure from inside the virtual machine as if it was a real network card connected to the same ethernet as your real network card.

I assume that you either

  • get your IP address with DHCP
  • or have a static IP address

If you’re on DSL or cable modem with PPPoE you probably don’t want host networking, but NAT, which works out of the box.

We’re going to

  1. create a virtual network interface
  2. set up an ethernet bridge
  3. add your physical network interface and the virtual interfaces to the bridge

Preparations

At first let’s install the needed software:

$ sudo apt-get install bridge-utils uml-utilities

The virtual network interface is configured with the package uml-utilities. uml stands for User Mode Linux, so understandably the package is preconfigured for use with User Mode Linux. In order to work correctly with VirtualBox, in /etc/network/if-pre-up.d/uml-utilities replace

chown root:uml-net /dev/net/tun

with

chown root:vboxusers /dev/net/tun

As we’re going to change the network configuration, we’re going to shut down the network. So don’t close this browser window :)

$ sudo /etc/init.d/networking stop

Creating a Virtual Network Interface

Now on to configuring the virtual network interface. Ubuntu and Debian store network configuration in the file /etc/network/interfaces. Assuming your network card is eth0, you’ll find a stanza beginning with the following in the file:

auto eth0
iface eth0 inet .....

Actually at install Ubuntu writes several such stanzas into the file so that they automatically work. Create a backup copy of /etc/network/interfaces (Now.) Then comment out all interfaces you don’t use so that NetworkManager doesn’t change the network configuration when we don’t want to. Leave in the lo interface, it’s required for local IP communication.

auto lo
iface lo inet loopback

auto eth0
iface eth0 inet .....

#auto eth1
#iface eth1 inet dhcp

#auto ath0
#iface ath0 inet dhcp

#auto wlan0
#iface wlan0 inet dhcp

The next step is to create a virtual network interface. This is the network interface that will be used by the virtual machine. We don’t configure it – the virtual machine will do it. Add the following stanza under the lo interface:

auto tap0
iface tap0 inet manual
tunctl_user markus
uml_proxy_arp markus.mynetwork.loc
uml_proxy_ether eth0

Replace markus.mynetwork.loc with the host name or IP address you get from DHCP for your host machine (not the virtual machine). Also replace eth0 with your real network interface, if that is not eth0, and markus with your user name on the system. The user name set here will be the only user with permission to work with the virtual network interface.

Static IP address

If you have a static IP address, replace markus.mynetwork.loc instead with your static IP address.

Creating the bridge

Now we’ll set up the bridge. Add the following stanza after the tap0 stanza:

auto br0
iface br0 inet dhcp
bridge_ports eth0 tap0
bridge_maxwait 0

Static IP address

If you have a static IP address, you have to carry over the network settings from eth0 to br0. Assuming your eth0 stanza looks something like this:

auto eth0
iface eth0 inet static
address 192.168.1.2
netmask 255.255.255.0
gateway 192.168.1.254

change the br0 stanza to look like this:

auto br0
iface br0 inet static
address 192.168.1.2
netmask 255.255.255.0
gateway 192.168.1.254
bridge_ports eth0 tap0
bridge_maxwait 0

In any case, change the stanza for eth0 to the following:

auto eth0
iface eth0 inet manual

That’s it. Enable networking again:

$ sudo /etc/init.d/networking start

You can use ifconfig -a to see if all devices were correctly configured. You can now use host networking in VirtualBox. Enter tap0 in the interface name field. The fields setup application and terminate application can be left empty.

More than one virtual network interface

You can run more than one OS at the same time and have all them use host networking. Add a tap interface for all of them. Here’s an example for three virtual network interfaces:

auto tap0
iface tap0 inet manual
tunctl_user markus
uml_proxy_arp markus.mynetwork.loc
uml_proxy_ether eth0

auto tap1
iface tap1 inet manual
tunctl_user markus
uml_proxy_arp markus.mynetwork.loc
uml_proxy_ether eth0

auto tap2
iface tap2 inet manual
tunctl_user markus
uml_proxy_arp markus.mynetwork.loc
uml_proxy_ether eth0

auto br0
iface br0 inet dhcp
bridge_ports eth0 tap0 tap1 tap2
bridge_maxwait 0

Have fun, and let me know if that post was helpful to you :)

Although I’m from Germany, I live in Novosibirsk at the moment. Novosibirsk is in Russia, so I listen to russian music. The player I use is muine. Unfortunately the artist and title information looks like this:

The reason is that windows software that adds meta tags to music files uses the default russian 8-bit encoding CP1251. All ID3 versions except for the newest ones only allow ISO-8859-1 as the tag encoding. So muine, according to the standard, interprets the tags in ISO-8859-1. Let’s change that.

I’m using Ubuntu 6.10. Let’s have a look at the muine sources:

~/src/deb$ apt-get install build-essential
~/src/deb$ apt-get source muine
...
dpkg-source: extracting muine in muine-0.8.5
dpkg-source: unpacking muine_0.8.5.orig.tar.gz
dpkg-source: applying ./muine_0.8.5-1ubuntu4.diff.gz
~/src/deb$ cd muine-0.8.5/
~/src/deb/muine-0.8.5$ ls src/
...
AddWindowEntry.cs             DndUtils.cs      Metadata.cs          SkipToWindow.cs
...

The file Metadata.cs looks like it’s responsible for the ID3 tags. Searching it for title shows the following lines:

                // Properties :: Title (get;)
                [DllImport ("libmuine")]
                private static extern IntPtr metadata_get_title (IntPtr metadata);

DllImport imports a binary library file. The next line declares a function metadata_get_title which is implemented in libmuine. Let’s look at that.

~/src/deb/muine-0.8.5$ ls libmuine/
...
gsequence.c  metadata.c   player-gst-0.8.c  rb-cell-renderer-pixbuf.c
...

Searching for title in metadata.c gives us the following line:

        metadata->title = get_mp3_comment_value (tag, ID3_FRAME_TITLE, 0);

Which leads us to get_mp3_comment_value. Let’s look at its definition:

get_mp3_comment_value (struct id3_tag *tag,
                       const char *field_name,
                       int index)
{
...
        frame = id3_tag_findframe (tag, field_name, 0);
...
        field = id3_frame_field (frame, 1);
...
        ucs4 = id3_field_getstrings (field, index);
...
        utf8 = id3_ucs4_utf8duplicate (ucs4);
...
}

get_mp3_comment_value calls a lot of functions the name of which starts with id3. The functions are not defined in metadata.c. They aren’t defined anywhere in the muine source code:

~/src/deb/muine-0.8.5$ grep -r id3_field_getstrings .
./libmuine/metadata.c:  latin1 = id3_ucs4_latin1duplicate (id3_field_getstrings (field, 0));
./libmuine/metadata.c:  ucs4 = id3_field_getstrings (field, index);

Only calls to that function. In metadata.c there’s an include statement that includes id3tag.h. Looks like what we need. Let’s download the source for the corresponding library:

~/src/deb/muine-0.8.5$ apt-cache search id3tag
libid3tag0 - ID3 tag reading library from the MAD project
libid3tag0-dev - ID3 tag reading library from the MAD project
mp3rename - Rename mp3 files based on id3tags
somaplayer - player audio for the soma suite
~/src/deb/muine-0.8.5$ cd ..
~/src/deb$ apt-get source libid3tag0
...
dpkg-source: extracting libid3tag in libid3tag-0.15.1b
dpkg-source: unpacking libid3tag_0.15.1b.orig.tar.gz
dpkg-source: applying ./libid3tag_0.15.1b-8.diff.gz
~/src/deb$ cd libid3tag-0.15.1b/
~/src/deb/libid3tag-0.15.1b$ grep -r id3_field_getstrings .
...
./field.c:id3_ucs4_t const *id3_field_getstrings(union id3_field const *field,
...

The function is defined in field.c. It accesses an array stringlist. That array is filled in the function id3_field_parse. This function calls another function, id3_parse_string that extracts the string values of a field.

~/src/deb/libid3tag-0.15.1b$ grep -r id3_parse_string *
parse.c:id3_ucs4_t *id3_parse_string(id3_byte_t const **ptr, id3_length_t length,
parse.h:id3_ucs4_t *id3_parse_string(id3_byte_t const **, id3_length_t,

This function is defined in parse.c. For ISO-8859-1 fields it calls id3_latin1_deserialize.

~/src/deb/libid3tag-0.15.1b$ grep -r id3_latin1_deserialize *
latin1.c:id3_ucs4_t *id3_latin1_deserialize(id3_byte_t const **ptr, id3_length_t length)
...

id3_latin1_deserialize is defined in latin1.c. It calls id3_latin1_decode to convert the latin1 string to UCS-4, which in turn calls id3_latin1_decodechar to convert a single character. We’re there: we have found the place we have to change:

/*
 * NAME:        latin1->decodechar()
 * DESCRIPTION: decode a (single) latin1 char into a single ucs4 char
 */
id3_length_t id3_latin1_decodechar(id3_latin1_t const *latin1,
                                   id3_ucs4_t *ucs4)
{
  *ucs4 = *latin1;

  return 1;
}

The function is very simple: ISO-8859-1 is a subset of unicode, so only a direct assignment is needed. For CP1251 things are different. Looking at the wikipedia page for CP1251, we see that the letters of the russian alphabet start at 0xC0 with the upper case letters, followed by the lower case letters to 0xFF. Using gnome-character-map, we find that the corresponding unicode code points are U+0410 through U+044F and that the letters are in the same order. Very convenient. Let’s change the function to return the correct unicode values for the CP1251 letters 0xC0 through 0xFF:

id3_length_t id3_latin1_decodechar(id3_latin1_t const *latin1,
				   id3_ucs4_t *ucs4)
{
  if (*latin1 >= 0xc0)
    *ucs4 = 0x410 + (*latin1 - 0xc0);
  else
    *ucs4 = *latin1;

  return 1;
}

The unicode encoding used here, UCS-4, just packs the unicode code point in a 32 bit integer, so we can just directly assign the unicode value. Now on to compiling the changed libid3tag.

~/src/deb/libid3tag-0.15.1b$ sudo apt-get build-dep libid3tag0
...
~/src/deb/libid3tag-0.15.1b$ sudo apt-get install fakeroot
...
~/src/deb/libid3tag-0.15.1b$ fakeroot dpkg-buildpackage -uc -us
...
~/src/deb/libid3tag-0.15.1b$ sudo dpkg -i ../libid3tag0_0.15.1b-8_i386.deb
...

Now let’s delete the muine song database so that it re-reads the metadata.

~/src/deb/libid3tag-0.15.1b$ rm ~/.gnome2/muine/*

Start muine and import the music file:

Победа! :)

Update: Added install of build-essential at the beginning.

By default Gecko based browsers (Firefox, Epiphany) don’t take punctuation symbols for a word boundary. This means that when you navigate by-word in the URL bar, text inputs or text areas using Ctrl-Left, Ctrl-Right or Ctrl-Backspace, it moves right to the next resp. previous white space. I have found this to be inconvenient when editing URLs, or source code snippets in bug reports and in other places. Thanks to an entry Unbreaking Firefox’s address bar from Marius Gedminas I came to learn how to unbreak this behaviour: Search for punctuation in about:config; Gecko finds layout.word_select.stop_at_punctuation which you need to set to true. More information in the blog post I linked to.

I can generally recommend Marius Gdeminas’ blog, I found quite a lot of interesting things in there, including a link to his GTimeLog, Bash prompts: showing the command in the window title, xsel: the power of Unix, …

In short: Before you switch between branches, delete all files that are ignored by subversion. If you don’t, you will receive errors like these:

svn: Won't delete locally modified directory '.'
svn: Left locally modified or unversioned files

svn status looks weird after such a failed switch:

markus@markus:/var/www/community$ svn status
!      .
    S  app
!   S  admin
!      admin/tmp
?      admin/tmp/cache/models
!      admin/tmp/cache
    S  sql
...

What’s going on? Let’s assume that I checked out a branch and want to switch to the trunk. In the branch a directory admin/tmp/cache/models was added and its svn:ignore was set to *, that is, all files inside that directory are ignored. Now there appeared files inside this directory. I issue the switch to branch command and got the above error message. That means, that there were some files inside models that are ignored by subversion. The switch operation deleted the .svn directory in admin/tmp/cache/models but didn’t remove the directory itself because it won’t delete files that it doesn’t control – specificially the ignored files in admin/tmp/cache/models. How to get out of that situation?

At first, switch back to where you were, i.e. to the branch. This will give you the following error:

...
svn: Failed to add directory 'admin/tmp/cache/models': object of the same name already exists

svn status looks much better now:

markus@markus:/var/www/community$ svn status
!      .
!      admin
!      admin/tmp
?      admin/tmp/cache/models
!      admin/tmp/cache

Now delete the unversioned models directory and switch again to the branch:

...
A    admin/tmp/cache/models
...

Now delete all ignored files by hand. To find out which files exist that are ignored, use svn status --no-ignore. Note that ignored files interfere with the switch only if they are in a directory that exists only in the branch. Now the switch to the trunk should work without a glitch.