Tuesday, September 22, 2009

MarkUtils-Codec: Base64, URL, and other byte/char conversions

This is an overdue introduction of my latest addition to MarkUtils. MarkUtils-Codec could be considered a high-performance replacement for Apache Commons Codec. Like Commons Codec, this implementation has support for Base64, URL (a.k.a. Percent, and covered previously), and Hexadecimal encodings and decodings. Also like Commons Codec, this implementation utilizes a number of interfaces that allow various codecs to be used interchangeably. Unlike Commons Codec, this implementation is designed to be higher performing, as it is written for streaming use with the Buffer classes. The most significant advantage to this design is lower memory requirements and usage, especially when working with longer lengths of data.

MarkUtils-Codec is really a follow-up to one of my previous posts, Improving URLEncoder/URLDecoder Performance in Java. While the API I proposed and sample code I provided solved an immediate need, the lack of proper interfaces made it difficult to replace with other codecs, such as Base64. The options to plug-in to other standard streaming classes was also limited. For example, there was no clear way to create an InputStream that would read decoded data from encoded data. This library is meant as a complete replacement, as I have placed the "urlCodec" library in archival status.

Until I have a suitable place to host the Javadocs online, please reference them in the downloads available at ziesemer.dev.java.net.

The highest-level API interface is com.ziesemer.utils.codec.ICoder. Verbatim from the Javadoc, this is the "Base API for high-performance encoding and decoding between various Buffers. Supports conversions between ByteBuffers and CharBuffers through the IByteToCharEncoder and ICharToByteDecoder child interfaces. This API is similar in design to CharsetEncoder and CharsetDecoder."

Do note that the relation to the Charset classes may seem a bit backwards. When a character set is decoded, the input is bytes and the output is characters. The purpose of this library is to encode any data (as bytes) into character data that can safely be sent through various non-byte transports, e.g. HTTP forms. For this purpose, decoding takes characters and input and produces bytes as output.

Here is a simple example of supported direct usage, taking no advantage of streaming capabilities. This is included as one of the JUnit tests within the com.ziesemer.utils.codec.DemoTest class:

/**
 * Simple usage, taking no advantage of streaming capabilities.
 */
@Test
public void testDirectSimple() throws Exception{
  IByteToCharEncoder encoder = new URLEncoder();
  ICharToByteDecoder decoder = new URLDecoder();
  
  // Random test data.
  byte[] rawData = new byte[1 << 10];
  new Random().nextBytes(rawData);
  
  // Encode.
  CharBuffer cbOut = encoder.code(ByteBuffer.wrap(rawData));
  
  // Decode (round-trip).
  ByteBuffer bbOut = decoder.code(cbOut);
  
  // Verify.
  byte[] result = new byte[bbOut.remaining()];
  bbOut.get(result);
  Assert.assertArrayEquals(rawData, result);
}

Or an even simpler example, using convenience methods. Note that the Base64 codec can be replaced with URL (percent), Hex, or another provided codec:

byte[] sampleBytes = new byte[]{0, 1, 2, 3};
String enc = new Base64Encoder().encodeToString(sampleBytes);
System.out.println(enc); // Yields: AAECAw==
byte[] dec = new Base64Decoder().decodeToBytes(enc);
System.out.println(Arrays.equals(sampleBytes, dec)); // Yields: true

A number of input/output wrappers are also included in the "com.ziesemer.utils.codec.io" package, allowing for transparent use as a standard Java IO reader, writer, or stream. The signatures of the required constructors are also shown. Each class also provides an alternate constructor that can be used to fine-tune the read buffer size.

  • CharDecoderInputStream(ICharToByteDecoder decoder, Reader reader)

    Reads raw bytes from encoded characters. Counter-part to CharEncoderReader. This is a pull-interface; CharDecoderWriter is the equivalent push-interface.

    Can be adapted to read characters to the consumer (instead of raw bytes) by wrapping in a InputStreamReader. This is only valid if the decoded form of the data is known to only contain valid characters. Can also be adapted to read bytes from a provider (instead of characters) by using a InputStreamReader as the Reader.

  • CharDecoderWriter(ICharToByteDecoder decoder, OutputStream outputStream)

    Accepts encoded characters, and writes the raw bytes. Counter-part to CharEncoderOutputStream. This is a push-interface; CharDecoderInputStream is the equivalent pull-interface.

    Can be adapted to accept bytes from a provider (instead of characters) by wrapping in a OutputStreamWriter.

  • CharEncoderOutputStream(IByteToCharEncoder encoder, Writer writer)

    Accepts raw bytes, and writes the encoded characters. Counter-part to CharDecoderWriter. This is a push-interface; CharEncoderReader is the equivalent pull-interface.

    Can be adapted to write bytes to the consumer (instead of characters) by using a OutputStreamWriter as the Writer. Can also be adapted to accept characters from the provider (instead of raw bytes) by wrapping in a OutputStreamWriter.

  • CharEncoderReader(IByteToCharEncoder encoder, InputStream reader)

    Reads encoded characters from raw bytes. Counter-part to CharDecoderInputStream. This is a pull-interface; CharEncoderOutputStream is the equivalent push-interface.

    Can be adapted to read characters to the consumer (instead of raw bytes) by wrapping in a InputStreamReader.

Also included are a number of "character lists" (in the com.ziesemer.utils.codec.charLists package), particularly to support the different Base64 variations.

Please refer to the included JUnit tests (currently 169) for usage examples.

Download

com.ziesemer.utils.codec is available on ziesemer.java.net under the GPL license, complete with source code, a compiled .jar, generated JavaDocs, and a suite of JUnit tests. Download the com.ziesemer.utils.codec-*.zip distribution from here. Please report any bugs or feature requests on the java.net Issue Tracker.

Monday, September 7, 2009

"networking restart" issues, VLANs under Ubuntu

For this post, I'm using Ubuntu Linux 9.04 / "Jaunty Jackalope". This is somewhat a follow-up to my Ubuntu Linux Router Upgrade Project.

Errors during "/etc/init.d/networking restart"

First, assuming a statically-configured LAN for server use, with NetworkManager disabled:

/etc/network/interfaces:

auto lo
iface lo inet loopback

auto eth0
iface eth0 inet static
  address 192.168.1.1
  netmask 255.255.255.0
$ sudo /etc/init.d/networking restart
 * Reconfiguring network interfaces...                                   [ OK ]

The networking simply restarts, without showing any errors or warnings. However, this quickly changes once an additional network adapter is configured. This could be an additional physical adapter, but for my purposes, I had added a virtual LAN (VLAN) to work with my Dell PowerConnect 2716. For Ubuntu, this simply requires installing the "vlan" package, and defining the virtual LAN with an additional entry in /etc/network/interfaces. Note that this is done using a "<base name>.<VLAN ID>" naming scheme. As shown below, I'm adding an interface for VLAN 2:

auto lo
iface lo inet loopback

auto eth0
iface eth0 inet static
  address 192.168.1.1
  netmask 255.255.255.0

auto eth0.2
iface eth0.2 inet static
  address 192.168.2.1
  netmask 255.255.255.0

Please add a comment if you can find any official documentation that documents this functionality of the interfaces file, as I can't. However, this appears to be driven by "/etc/network/if-pre-up.d/vlan" and "/etc/network/if-post-down.d/vlan".

This also requires the "8021q" module, but at least in Jaunty, it is already available by default, as shown by "lsmod | grep 8021q".

This is where I started running into errors:

$ sudo /etc/init.d/networking restart
 * Reconfiguring network interfaces...
RTNETLINK answers: No such process
Removed VLAN -:eth0.2:-
 * if-up.d/mountnfs[eth0]: waiting for interface eth0.2 before doing NFS mounts
Set name-type for VLAN subsystem. Should be visible in /proc/net/vlan/config
Added VLAN with VID == 2 to IF -:eth0:-
                                                                         [ OK ]

This doesn't actually cause any issues, but it isn't right and should be fixed. I don't see any way to make "networking restart" give more verbose output. Additionally, nothing helpful appears in the logs. However, most of what the networking script does is call "ifdown -a --exclude=lo" and "ifup -a", where "-a" is "affect all interfaces marked auto". Fortunately, repeating this with adding "-v" for verbose mode yields some details:

$ sudo ifdown -av --exclude=lo && sudo ifup -av
Configuring interface eth0=eth0 (inet)
run-parts --verbose /etc/network/if-down.d
run-parts: executing /etc/network/if-down.d/avahi-autoipd
run-parts: executing /etc/network/if-down.d/wpasupplicant

ifconfig eth0 down
run-parts --verbose /etc/network/if-post-down.d
run-parts: executing /etc/network/if-post-down.d/avahi-daemon
run-parts: executing /etc/network/if-post-down.d/bridge
run-parts: executing /etc/network/if-post-down.d/vlan
run-parts: executing /etc/network/if-post-down.d/wireless-tools
run-parts: executing /etc/network/if-post-down.d/wpasupplicant
Configuring interface eth0.2=eth0.2 (inet)
run-parts --verbose /etc/network/if-down.d
run-parts: executing /etc/network/if-down.d/avahi-autoipd
RTNETLINK answers: No such process
run-parts: executing /etc/network/if-down.d/wpasupplicant

ifconfig eth0.2 down
run-parts --verbose /etc/network/if-post-down.d
run-parts: executing /etc/network/if-post-down.d/avahi-daemon
run-parts: executing /etc/network/if-post-down.d/bridge
run-parts: executing /etc/network/if-post-down.d/vlan
Removed VLAN -:eth0.2:-
run-parts: executing /etc/network/if-post-down.d/wireless-tools
run-parts: executing /etc/network/if-post-down.d/wpasupplicant
Configuring interface eth0=eth0 (inet)
run-parts --verbose /etc/network/if-pre-up.d
run-parts: executing /etc/network/if-pre-up.d/bridge
run-parts: executing /etc/network/if-pre-up.d/dhclient3-apparmor
run-parts: executing /etc/network/if-pre-up.d/vlan
run-parts: executing /etc/network/if-pre-up.d/wireless-tools
run-parts: executing /etc/network/if-pre-up.d/wpasupplicant

ifconfig eth0 192.168.1.1 netmask 255.255.255.0       up

run-parts --verbose /etc/network/if-up.d
run-parts: executing /etc/network/if-up.d/avahi-autoipd
run-parts: executing /etc/network/if-up.d/avahi-daemon
run-parts: executing /etc/network/if-up.d/ip
run-parts: executing /etc/network/if-up.d/mountnfs
 * if-up.d/mountnfs[eth0]: waiting for interface eth0.2 before doing NFS mounts
run-parts: executing /etc/network/if-up.d/ntpdate
run-parts: executing /etc/network/if-up.d/wpasupplicant
Configuring interface eth0.2=eth0.2 (inet)
run-parts --verbose /etc/network/if-pre-up.d
run-parts: executing /etc/network/if-pre-up.d/bridge
run-parts: executing /etc/network/if-pre-up.d/dhclient3-apparmor
run-parts: executing /etc/network/if-pre-up.d/vlan
Set name-type for VLAN subsystem. Should be visible in /proc/net/vlan/config
Added VLAN with VID == 2 to IF -:eth0:-
run-parts: executing /etc/network/if-pre-up.d/wireless-tools
run-parts: executing /etc/network/if-pre-up.d/wpasupplicant

ifconfig eth0.2 192.168.2.1 netmask 255.255.255.0       up

run-parts --verbose /etc/network/if-up.d
run-parts: executing /etc/network/if-up.d/avahi-autoipd
run-parts: executing /etc/network/if-up.d/avahi-daemon
run-parts: executing /etc/network/if-up.d/ip
run-parts: executing /etc/network/if-up.d/mountnfs
run-parts: executing /etc/network/if-up.d/ntpdate
run-parts: executing /etc/network/if-up.d/wpasupplicant

So the error is apparently coming from "/etc/network/if-down.d/avahi-autoipd". (Avahi is a free Zero configuration networking (zeroconf) implementation.) I temporarily edited the "avahi-autoipd" script to add "-x" to enable debugging by changing the first line to "#!/bin/sh -ex". What is happening is that this script first checks for the existence of a route matching "169.254.0.0/16", but doesn't check for what interface it is on. If it finds a matching route, even on another interface, it makes a call to delete the route, but only for the current interface. When this combination doesn't exist, the "RTNETLINK answers: No such process" message is given.

Resolution: The scripts could just be fixed to include the interface in the search. I opened a bug report to Ubuntu on this: #425854. However, a "server"-type system shouldn't be handling these automatic types of network routings, so I just disabled these scripts:

sudo chmod -x /etc/network/if-up.d/avahi-autoipd
sudo chmod -x /etc/network/if-down.d/avahi-autoipd

Shown fixed:

$ sudo /etc/init.d/networking restart
 * Reconfiguring network interfaces...
Removed VLAN -:eth0.2:-
 * if-up.d/mountnfs[eth0]: waiting for interface eth0.2 before doing NFS mounts
Set name-type for VLAN subsystem. Should be visible in /proc/net/vlan/config
Added VLAN with VID == 2 to IF -:eth0:-
                                                                         [ OK ]

DHCP server binding issues

Another issue I noticed with running "/etc/init.d/networking restart" is that the DHCP server ("dhcpd" / "dhcp3-server") quit responding to requests. (Previous posting on dhcp3-server.) Using Wireshark, I noticed that DHCP requests were still being received, but no responses were being sent. Nothing applicable is shown in any of the logs, other than that dhcpd isn't even acknowledging the requests after the interface is brought back up.

Tools such as "lsof" "netstat" show that dhcpd is still properly bound to the interfaces. Other applications such as the SSH server (sshd) don't have this issue, and continue accepting connections even after the interface goes down and comes back up, so I'm not sure why this is an issue for the DHCP server. However, I can't get things to resume working properly short of restarting the DHCP server using "sudo /etc/init.d/dhcp3-server restart" As I'm starting to work with IPv6, I noticed the exact same issue with radvd.

I wasn't exactly sure where the best place was to automate these restarts. I could probably add a script to "/etc/network/if-up.d/", but would then have to include a check that I wasn't unnecessarily restarting the servers for every interface, as they should only be concerned with the interface they are serving requests on, e.g. eth0. For now, I just added these commands to "/etc/network/interfaces":

# …

auto eth0
iface eth0 inet static
  address 192.168.1.1
  netmask 255.255.255.0
  post-up /etc/init.d/dhcp3-server restart
  post-up /etc/init.d/radvd restart

# …