Projects that need IPv6 support

Our project uses a bunch of open source packages. I’ve been looking through them and this seems to be the current state:

  • Linux Kernel: Good to go in both 2.4 and 2.6
  • OpenPegasus CIM Broker: IPV6 support is underway, but not yet implemented.
  • SBLIM SFCBD IPV6 Support is built in, based on a compile time switch
  • OpenIPMI: IPMI Tool won’t accept a valid IPv6 address. This is a slightly different code source than the rest of the project, so it doesn’t mean that the rest of it won’t support IPv6.
  • OpenWSMAN
  • OpenSSL: Claims to be agnostic of the IP level. Since Open SSH is build on OpenSSL, and OpenSSH works, it works for at least a subset of it’s functionality.
  • OpenSSH: Connecting via IPv6 Works Confirmed for both ssh and scp.  scp is a pain.
  • OpenSLP: Seems to have IPv6 support, but it isvery recent.  It requires IPv6 multicast support.  Multicast has often been an after thought in switch implementations, so IPv6 multicast may have issues in the future.

Continuing support for IPv4 with a switch for IPv6

Although our product needs to support IPv6, it will be used by people in IPv4 mode for the near future. Since a call to the socket or bind system calls will fail if the underlying interface is not IPv6 enabled, we have to fall back to IPv6. So I am currently thinking we’ll have code like this:

int af_inet_version = AF_INET;

With code that can set that to AF_INET6 either read out of a config file or a command line argument.

Then later…

int rc = socket( af_inet_version, …);

And when calling bind, use af_inet_version to switch

Part of getting our product converted to IPv6 is project for reliable messaging. This code is no longer actively maintained. I’ve done a few simple greps through the code and agree with my co-worker who warned me that it is going to be quite tricky. Aside from the obvious calls to socket and bind, ISIS records addresses to be reused later. For example, In include/cl_inter.h the ioq structure contains

saddr io_address; /* Destination address */
saddr io_rcvaddr; /* Receive address (for statistics) */

where saddrs is a typedef in include/cl_typedefs.h

typedef struct sockaddr_in saddr;

I am thinking of an approach that would be to use a union:

struct sockaddress{
union{
struct sockaddr_in in;
struct sockaddr_in6 in6;
}addr;
};
struct sockaddress sin;
struct sockaddress pin;

switch(af_inet_version){
case AF_INET:
addrsize = sizeof(struct sockaddr_in);
sin.addr.in.sin_addr.s_addr = INADDR_ANY;
sin.addr.in.sin_port = htons(port);
sin.addr.in.sin_family = af_inet_version;
break;

case AF_INET6:
addrsize = sizeof(struct sockaddr_in6);
sin.addr.in6.sin6_addr = in6addr_any;
sin.addr.in6.sin6_port = htons(port);
sin.addr.in6.sin6_family = af_inet_version;
break;
}

I put the union inside a struct because I originally was going to put the AF_INET to AF_INET6 field into struct sockaddress. I may go back to that for the real code, and then I can support both IPv6 and IPv4 in a single system.

IPv6 Language Comparison

Language standard library support for IPv6.

What follows is an attempt to view the support for IPv6 in various
languages.

Java

Java has clean support for IPV6, and makes it easy to go between
IPv4 and IPv6 addresses. Example:

import java.net.*;public class IPTest{

private static void displayClasses(String host){
 		System.out.print("looking up host" +host+"tt");
 		try{
 			InetAddress[] address =	InetAddress.getAllByName(host);
 			System.out.print("[Success]:");
 			for (int i =0; i < address.length;i++){
 				System.out.println(address[i].getClass().getName());
 			}
 		}catch(UnknownHostException e){

System.out.println("[Unknown]");
 		}

}

public static void main(String[] args) {

displayClasses("fe80::218:8bff:fec4:284b");
 		displayClasses("fe80::218:8bff:fec4:284b/64");
 		displayClasses("00:18:8B:C4:28:4B");
 		displayClasses("::10.17.126.126");
 		displayClasses("10.17.126.126");
 		displayClasses("vmware.com");
 		displayClasses("adyoung-laptop");

}
 }

This code produces the following output

adyoung@adyoung-laptop$ java IPTest
 looking up hostfe80::218:8bff:fec4:284b         [Success]:java.net.Inet6Address
 looking up hostfe80::218:8bff:fec4:284b/64              [Unknown]
 looking up host00:18:8B:C4:28:4B                [Unknown]
 looking up host::10.17.126.126          [Success]:java.net.Inet6Address
 looking up host10.17.126.126            [Success]:java.net.Inet4Address
 looking up hostvmware.com               [Success]:java.net.Inet4Address
 looking up hostadyoung-laptop           [Success]:java.net.Inet4Address

C++

While C++ can always default to C for network support, I wanted to
see what existed in the C++ way of doing things. There is nothing in
the standard library for network support, and nothing pending in TR1.
The third party libary for Asynchronous I/O (asio) does support
IPv6. Boost has not accepted this package yet, but the acceptance
process appears to be underway. This package has a class for ip
address abstraction: asio::ip::address.

#include <iostream>
 #include <boost/array.hpp>
 #include <asio.hpp>using asio::ip::address;
 using namespace std;

void displayAddr(char * addr){
 	cout << "parsing addr " << addr;
 	try{
 		address::from_string(addr) ;
 		cout << "t[success]" ;
 	}catch(...){
 		cout << "t[Failed]";
 	}
 	cout << endl;
 }

int main(int argc, char* argv[]){
 	displayAddr("fe80::218:8bff:fec4:284b");
 	displayAddr("fe80::218:8bff:fec4:284b/64");
 	displayAddr("00:18:8B:C4:28:4B");
 	displayAddr("::10.17.126.126");
 	displayAddr("10.17.126.126");
 	displayAddr("vmware.com");
 	displayAddr("adyoung-laptop");
 	return 0;
 }

This produces the following output:

parsing addr fe80::218:8bff:fec4:284b   [success]
 parsing addr fe80::218:8bff:fec4:284b/64        [Failed]
 parsing addr 00:18:8B:C4:28:4B  [Failed]
 parsing addr ::10.17.126.126    [success]
 parsing addr 10.17.126.126      [success]
 parsing addr vmware.com [Failed]
 parsing addr adyoung-laptop     [Failed]

So the major distinction between this and the Java code is that the
Java code accepts hostnames, this only accepts well formed IP
Addresses.

Python

Python has IPv6 support built in to recent version.

#!/usr/bin/pythonimport socket

def displayAddr(addr):
 	try :
 		addr_info = socket.getaddrinfo(addr,"");
 		print "Parsing ", addr , "t[Succeeded]"
 	except socket.gaierror :
 		print "Parsing ", addr, "t[Failed]"

def main():
 	displayAddr("fe80::218:8bff:fec4:284b");
 	displayAddr("fe80::218:8bff:fec4:284b/64");
 	displayAddr("00:18:8B:C4:28:4B");
 	displayAddr("::10.17.126.126");
 	displayAddr("10.17.126.126");
 	displayAddr("vmware.com");
 	displayAddr("adyoung-laptop");

if __name__ == '__main__':
 	main()

This Code produces the following output

 adyoung@adyoung-laptop$ ./SockTest.py
 Parsing  fe80::218:8bff:fec4:284b       [Succeeded]
 Parsing  fe80::218:8bff:fec4:284b/64    [Failed]
 Parsing  00:18:8B:C4:28:4B      [Failed]
 Parsing  ::10.17.126.126        [Succeeded]
 Parsing  10.17.126.126  [Succeeded]
 Parsing  vmware.com     [Succeeded]
 Parsing  adyoung-laptop         [Succeeded]

So, like Java, hostnames are correctly parsed the same as IP
addressed.

PERL

Perl has two APIs that look promising: Socket and Net::IP.

#!/usr/bin/perl
 use strict;
 use Socket;
 sub displayAddr{
 	my ($addr) = @_;
 	my $host = gethostbyname ($addr);
 	if ($host){
 		print ("parsed ".$addr."n");
 	}else{
 		print ("Unable to parse ".$addr."n");
 	}
 }
 displayAddr("fec0::218:8bff:fe81:f81e");
 displayAddr("fe80::218:8bff:fe81:f81e");
 displayAddr("fe80::218:8bff:fec4:284b");
 displayAddr("fe80:0:0:0:0:0:0:0:0:0:0:0:218:8bff:fec4:284b");
 displayAddr("fe80::218:8bff:fec4:284b/64");
 displayAddr("00:18:8B:C4:28:4B");
 displayAddr("::10.17.126.126");
 displayAddr("10.17.126.126");
 displayAddr("vmware.com");
 displayAddr("adyoung-laptop");
 displayAddr("ip6-allhosts");
 displayAddr("ip6-localnet");

This produces:

adyoung@adyoung-laptop$ less ip-test.pl
 adyoung@adyoung-laptop$ ./ip-test.pl
 IP  : fec0:0000:0000:0000:0218:8bff:fe81:f81e Type: RESERVED
 IP  : fe80:0000:0000:0000:0218:8bff:fe81:f81e Type: LINK-LOCAL-UNICAST
 IP  : fe80:0000:0000:0000:0218:8bff:fec4:284b Type: LINK-LOCAL-UNICAST
 cannot parse fe80:0:0:0:0:0:0:0:0:0:0:0:218:8bff:fec4:284b
 cannot parse fe80::218:8bff:fec4:284b/64
 cannot parse 00:18:8B:C4:28:4B
 IP  : 0000:0000:0000:0000:0000:0000:0a11:7e7e Type: IPV4COMP
 IP  : 10.17.126.126 Type: PRIVATE
 cannot parse vmware.com
 cannot parse adyoung-laptop
 cannot parse ip6-allhosts
 cannot parse ip6-localnet

So it handles IPv4 and IPv6., but not host names.
The alternate API, Socket, is older. It doesn ot seem to have the
new Posix function getaddrinfo, so I treid the old gethostbyname:

#!/usr/bin/perl

use strict;
 use Socket;
 sub displayAddr{
 	my ($addr) = @_;
 	my $host = gethostbyname ($addr);
 	if ($host){
 		print ("parsed ".$addr."n");
 	}else{
 		print ("Unable to parse ".$addr."n");
 	}
 }
 displayAddr("fec0::218:8bff:fe81:f81e");
 displayAddr("fe80::218:8bff:fe81:f81e");
 displayAddr("fe80::218:8bff:fec4:284b");
 displayAddr("fe80:0:0:0:0:0:0:0:0:0:0:0:218:8bff:fec4:284b");
 displayAddr("fe80::218:8bff:fec4:284b/64");
 displayAddr("00:18:8B:C4:28:4B");
 displayAddr("::10.17.126.126");
 displayAddr("10.17.126.126");
 displayAddr("vmware.com");
 displayAddr("adyoung-laptop");
 displayAddr("ip6-allhosts");
 displayAddr("ip6-localnet");

This produced the following output:

Unable to parse fec0::218:8bff:fe81:f81e
 Unable to parse fe80::218:8bff:fe81:f81e
 Unable to parse fe80::218:8bff:fec4:284b
 Unable to parse fe80:0:0:0:0:0:0:0:0:0:0:0:218:8bff:fec4:284b
 Unable to parse fe80::218:8bff:fec4:284b/64
 Unable to parse 00:18:8B:C4:28:4B
 Unable to parse ::10.17.126.126
 parsed 10.17.126.126
 parsed vmware.com
 parsed adyoung-laptop
 Unable to parse ip6-allhosts
 Unable to parse ip6-localnet

It was able to handle IPv4 and domain names, but not IPv6. This was
on a system that had an IPv6 interface, so it was not the problem
shown in the straight C section

C#

This code exercizes the IPAddress and Dns classes.

using System;
 using System.Net;public class NetTest
 {
 	public static void Main(string[] args){
 		DisplayAddr("fec0::218:8bff:fe81:f81e");
 		DisplayAddr("fe80::218:8bff:fe81:f81e");
 		DisplayAddr("fe80::218:8bff:fec4:284b");
 		DisplayAddr("fe80:0:0:0:0:0:0:0:0:0:0:0:218:8bff:fec4:284b");
 		DisplayAddr("fe80::218:8bff:fec4:284b/64");
 		DisplayAddr("00:18:8B:C4:28:4B");
 		DisplayAddr("::10.17.126.126");
 		DisplayAddr("10.17.126.126");
 		DisplayAddr("vmware.com");
 		DisplayAddr("adyoung-laptop");
 		DisplayAddr("ip6-allhosts");
 		DisplayAddr("ip6-localnet");
 	}

public static void DisplayAddr(string host){
 		try{
 			IPAddress addr = IPAddress.Parse(host);
 			Console.WriteLine("addr has address:"
 					+ addr.ToString());
 		}catch(Exception ){
 			Console.WriteLine("unable to parse :" + host);
 			try{
 				IPHostEntry hostEntry = Dns.GetHostByName(host);

Console.WriteLine("addr has address:"
 						+ hostEntry.AddressList[0]
 						.ToString());
 			}catch(Exception ){
 				Console.WriteLine("Cannot get host from DNS:",host);
 			}
 		}
 	}
 }

This code produces the following output.

addr has address:fec0::218:8bff:fe81:f81e
 addr has address:fe80::218:8bff:fe81:f81e
 addr has address:fe80::218:8bff:fec4:284b
 unable to parse :fe80:0:0:0:0:0:0:0:0:0:0:0:218:8bff:fec4:284b
 Cannot get host from DNS:
 addr has address:fe80::218:8bff:fec4:284b
 unable to parse :00:18:8B:C4:28:4B
 Cannot get host from DNS:
 addr has address:::10.17.126.126
 addr has address:10.17.126.126
 unable to parse :vmware.com
 addr has address:10.19.249.99
 unable to parse :adyoung-laptop
 addr has address:10.17.124.70
 unable to parse :ip6-allhosts
 Cannot get host from DNS:
 unable to parse :ip6-localnet
 Cannot get host from DNS:

C# Handles both IPv4 and IPv6 address equally well. The IPAddress
class does not handle host names. The Dns service does an explicit
DNS lookup, not a call via the NSSwitch functions. This leaves a
hole for hosts declared via YP, /etc/hosts, LDAP, or other naming
services. I ran this under both Mono and Microsoft Visual Studio.
On MSVS, it called out that GetHostByName was deprecated, but the
replacement call, GetHostEntry, had the same behavior.

Straight C
(Posix)

The obvious method does not work:

#include <sys/types.h>
 #include <sys/socket.h>
 #include <netdb.h>
 #include <stdio.h>void displayAddr(char *addr){
         struct hostent * hostent;

hostent =gethostbyname(addr);
         if (hostent){
                 printf( "Parsing %s t[Succeeded]n", addr);
         }else{

printf( "Parsing %s t[Failed]n", addr);
         }
 }

int main(){
         displayAddr("fe80::218:8bff:fec4:284b");
         displayAddr("fe80::218:8bff:fec4:284b/64");
         displayAddr("00:18:8B:C4:28:4B");
         displayAddr("::10.17.126.126");
         displayAddr("10.17.126.126");
         displayAddr("vmware.com");
         displayAddr("adyoung-laptop");
         return 0;
 }

This code produces the following output:

adyoung@adyoung-laptop$ ./socktest
 Parsing fe80::218:8bff:fec4:284b        [Failed]
 Parsing fe80::218:8bff:fec4:284b/64     [Failed]
 Parsing 00:18:8B:C4:28:4B       [Failed]
 Parsing ::10.17.126.126         [Failed]
 Parsing 10.17.126.126   [Succeeded]
 Parsing vmware.com      [Succeeded]
 Parsing adyoung-laptop  [Succeeded]

Thus it does not deal with IPv6 addresses correctly. This seems to
be at odds with the man page which states:

The gethostbyname() function returns a structure of type hostent for the given  host  name.   Here name  is  either  a  host name, or an IPv4 address in standard dot notation, or an IPv6 address in colon (and possibly dot) notation.

The next attempt is to using the call specified in the porting
doc:getaddrinfo

#include <sys/types.h>
 #include <sys/socket.h>
 #include <netdb.h>
 #include <stdio.h>void displayAddr(char *addr){
 	struct addrinfo * addrinfo;
 	int rc = getaddrinfo(addr,NULL,NULL,&addrinfo);
 	if (0 ==  rc ){
 		printf( "Parsing %s t[Succeeded]n", addr);
 		freeaddrinfo(addrinfo);
 	}else{

printf( "Parsing %s t[Failed]n", addr);
 	}
 }

int main(){
 	displayAddr("fec0::218:8bff:fe81:f81e");
 	displayAddr("fe80::218:8bff:fe81:f81e");
 	displayAddr("fe80::218:8bff:fec4:284b");
 	displayAddr("fe80:0:0:0:0:0:0:0:0:0:0:0:218:8bff:fec4:284b");
 	displayAddr("fe80::218:8bff:fec4:284b/64");
 	displayAddr("00:18:8B:C4:28:4B");
 	displayAddr("::10.17.126.126");
 	displayAddr("10.17.126.126");
 	displayAddr("vmware.com");
 	displayAddr("adyoung-laptop");
 	return 0;
 }

This Code Worked differently Depending on whether the machine had an
IPv6 interface configured. Without and IPv6 Interface:

adyoung@adyoung-laptop$ ./getaddrinfo-test
 Parsing fec0::218:8bff:fe81:f81e        [Failed]
 Parsing fe80::218:8bff:fe81:f81e        [Failed]
 Parsing fe80::218:8bff:fec4:284b        [Failed]
 Parsing fe80:0:0:0:0:0:0:0:0:0:0:0:218:8bff:fec4:284b   [Failed]
 Parsing fe80::218:8bff:fec4:284b/64     [Failed]
 Parsing 00:18:8B:C4:28:4B       [Failed]
 Parsing ::10.17.126.126         [Failed]
 Parsing 10.17.126.126   [Succeeded]
 Parsing vmware.com      [Succeeded]
 Parsing adyoung-laptop  [Succeeded]

With an IPv6 interface.

-bash-3.00$ ./getaddrinfo-test
 Parsing fec0::218:8bff:fe81:f81e        [Succeeded]
 Parsing fe80::218:8bff:fe81:f81e        [Succeeded]
 Parsing fe80::218:8bff:fec4:284b        [Succeeded]
 Parsing fe80:0:0:0:0:0:0:0:0:0:0:0:218:8bff:fec4:284b   [Failed]
 Parsing fe80::218:8bff:fec4:284b/64     [Failed]
 Parsing 00:18:8B:C4:28:4B       [Failed]
 Parsing ::10.17.126.126         [Succeeded]
 Parsing 10.17.126.126   [Succeeded]
 Parsing vmware.com      [Succeeded]
 Parsing adyoung-laptop  [Failed]

The getaddrinfo function call gives us a way to determine the
correct family to use to connect to the host. If we add this to the
displayAddr function:

		switch( addrinfo->ai_family){
 			case AF_INET6:
 				printf( "socket family = AF_INET6n");
 				break;			case AF_INET:
 				printf( "socket family = AF_INETn");
 				break;
 		}

and request the resolution of a few more hosts:

	displayAddr("ip6-allhosts");
 	displayAddr("ip6-localnet");

We get:

Parsing fec0::218:8bff:fe81:f81e        [Succeeded]socket family = AF_INET6
 Parsing fe80::218:8bff:fe81:f81e        [Succeeded]socket family = AF_INET6
 Parsing fe80::218:8bff:fec4:284b        [Succeeded]socket family = AF_INET6
 Parsing fe80:0:0:0:0:0:0:0:0:0:0:0:218:8bff:fec4:284b   [Failed]
 Name or service not known
 Parsing fe80::218:8bff:fec4:284b/64     [Failed]
 Name or service not known
 Parsing 00:18:8B:C4:28:4B       [Failed]
 Name or service not known
 Parsing ::10.17.126.126         [Succeeded]socket family = AF_INET6
 Parsing 10.17.126.126   [Succeeded]socket family = AF_INET
 Parsing vmware.com      [Succeeded]socket family = AF_INET
 Parsing adyoung-laptop  [Succeeded]socket family = AF_INET
 Parsing ip6-allhosts    [Succeeded]socket family = AF_INET6
 Parsing ip6-localnet    [Succeeded]socket family = AF_INET6

So we can take the approach where the applications store hosts in a
free string format, presumably by host names, but perhaps by IPv4 or
IPv6 addresses, and we will use gethostinfo to decide how to connect.
For example, (without error handling)

void connectTo(char * host){
 	struct addrinfo * addrinfo;
 	struct protoent * protoent;
 	protoent = getprotobyname("tcp");
 	int rc = getaddrinfo(host,NULL,NULL,&addrinfo);
 	int sockfd = socket( addrinfo->ai_family,SOCK_STREAM,protoent->p_proto);
 }

Visual C++

The code for VC++ is very similar to posix, with slightly
different build requirments. Note the addition of the winsock
initialization code.

// nettest.cpp : Defines the entry point for the console application.
//

#include "stdafx.h"
#include <stdio.h>
void displayAddr(char *addr){
        struct addrinfo * addrinfo;
        int rc = getaddrinfo(addr,NULL,NULL,&addrinfo);
        if (0 ==  rc ){
                printf( "Parsing %s t[Succeeded]", addr);
                switch( addrinfo->ai_family){
                                                case AF_INET6:
                                                        printf( "socket family = AF_INET6n");
                                                        break;

                                                case AF_INET:
                                                        printf( "socket family = AF_INETn");
                                                        break;
                }
                freeaddrinfo(addrinfo);
        }else{
                printf( "Parsing %s t[Failed]n", addr);
                printf("%sn",gai_strerror(rc));
        }
}

void connectTo(char * host){
        struct addrinfo * addrinfo;
        struct protoent * protoent;
        protoent = getprotobyname("tcp");
        int rc = getaddrinfo(host,NULL,NULL,&addrinfo);
        int sockfd = socket( addrinfo->ai_family,SOCK_STREAM,protoent->p_proto);
}
WSAData wsaData;
int _tmain(int argc, _TCHAR* argv[])
{
        int iResult;

        // Initialize Winsock
        iResult = WSAStartup(MAKEWORD(2,2), &wsaData);
        if (iResult != 0) {
                printf("WSAStartup failed: %dn", iResult);
                return 1;
        }

        displayAddr("fec0::218:8bff:fe81:f81e");
        displayAddr("fe80::218:8bff:fe81:f81e");
        displayAddr("fe80::218:8bff:fec4:284b");
        displayAddr("fe80:0:0:0:0:0:0:0:0:0:0:0:218:8bff:fec4:284b");
        displayAddr("fe80::218:8bff:fec4:284b/64");
        displayAddr("00:18:8B:C4:28:4B");
        displayAddr("::10.17.126.126");
        displayAddr("10.17.126.126");
        displayAddr("vmware.com");
        displayAddr("adyoung-laptop");
        displayAddr("ip6-allhosts");
        displayAddr("ip6-localnet");

        return 0;
}

The file stdafx.h contains the includes

#include <winsock2.h>
#include <ws2tcpip.h>

This produced the output:

Parsing fec0::218:8bff:fe81:f81e        [Succeeded]socket family = AF_INET6
Parsing fe80::218:8bff:fe81:f81e        [Succeeded]socket family = AF_INET6
Parsing fe80::218:8bff:fec4:284b        [Succeeded]socket family = AF_INET6
Parsing fe80:0:0:0:0:0:0:0:0:0:0:0:218:8bff:fec4:284b   [Failed]
N
Parsing fe80::218:8bff:fec4:284b/64     [Failed]
N
Parsing 00:18:8B:C4:28:4B       [Failed]
N
Parsing ::10.17.126.126         [Succeeded]socket family = AF_INET6
Parsing 10.17.126.126   [Succeeded]socket family = AF_INET
Parsing vmware.com      [Succeeded]socket family = AF_INET
Parsing adyoung-laptop  [Failed]
N
Parsing ip6-allhosts    [Succeeded]socket family = AF_INET6
Parsing ip6-localnet    [Succeeded]socket family = AF_INET6

Again, this only worked if there was an IPv6 interface configured on
the system. Also, the default localhost, allhosts, and localnet
names from Linux were not supported on Windows. These had been
declared in /etc/hosts. Once these entries were added to
c:\windows\system32\drivers\etc\hosts it worked correctly. The error
reporting function does not work, but it does resolve and link. It
merely prints out the letter ‘N’.

ssh proxy into the corporate network

I need to log in to Bugzilla. I am working at home. What do I do?

Inside the firwall Bugzilla is at 10.10.10.31.

sudo ssh -X adyoung@gateway.mycompany.com -L 8080:10.10.10.31:80

Add an entry in /etc/hosts

127.0.0.1 localhost bugzilla.mycompany.com

This is obviously a very short term solution. The longer one is to get squid set up on my workstation in my office and have ssh port forward to that machine.

OK, here is a better solution:

ssh -X adyoung@gateway.mycompany.com  -L 3128:10.11.12.200:3128

I chose 3128 because that is the port for squid, the web proxy that is running on the host  at 10.11.12.200.  Now I tell mozilla that I need a proxy, tell it to find the proxy at localhost, port 3128.  Hit save and I’m in.

Three management technologies

There are several competing technologies to handle hardware management. I say hardware management because, while they also do software, that is not what they are all about. The three technologies are the Simple Network Management Protocol (SNMP), Intelligent Power Management Interface (IPMI) and Web Based Enterprise Management (WBEM). Yes, there are certainly more technologies out there that are related to these technologies, and that may fill comparable roles, but these three seem to be the ones that control the center right now, each with a separate set of strengths, weaknesses, and proponents.

These three technologies each attempt to provide a unified approach to handling the monitoring and control of software. As such they each attempt to provide a standard object model of the underlying components. SNMP and WBEM both provide a standard file format for specifying the meta data of the components they control and a standard network protocol for remote access. IPMI provides a standard view of components without interface file format.

Solutions for managing a hardware system have to solve four problems: persistent object references, property set queries and changes, remote method invocation, and asynchronous event monitoring. In order to monitor or change a component in the system, you first need to be able to find that component.

Of the three, SNMP is by far the oldest and most established. It has the benefit of being defined primarily by the fact that it is a network protocol. Of course, there are numerous version of SNMP, as it has evolved through the years, and so some of the more recent additions are less well accepted and tested. The biggest thing that SNMP has in its favor is that it is defined strictly as a wire protocol, providing the highest degree of interoperability, at least in theory. Of course, HTTP is defined strictly as a wire protocol and we have all seen the incompatibility issues between Netscape and IE. However, the wider array of software tools that any given piece of hardware has to work with means that people code conservatively. Thus interoperability is high, at the cost that people code to the lowest common denominator of the spec, and use primarily the best tested features. By far the most common use of SNMP I have encountered has been for devices sending out status updates. There are various tools for monitoring these updates, consolidating them, and reporting the health of a distributed system. At Penguin we put some effort into supporting Ganglia and Nagios, both of which provide some SNMP support.

I’ve had a love/hate relationship with IPMI for the past couple of years. My earliest exposure to IPMI was dealing with power cycling machines that were running the Linux Kernel. In theory, all I should have to do was to enable the LAN interface on the machine, and I could use ipmitool to reboot the machine like this:

/usr/bin/ipmitool -I lan -U root -H 10.1.1.100 -a chassis power cycle

IPMI was implemented on the motherboard of the machine, and listened to the same network port that was used during normal operations. When the Linux kernel crashed, the port did not respond to IPMI packets. It turned out the network interface was blindly sending all packets to the Linux kernel, regardless of the kernel’s state. The solution was to implement a heartbeat, which required a later version of the Linux Kernel than we were capable of supporting at that time. So IPMI was useless to me.

Well, not completely. The other thing that IPMI supports is called serial over LAN. THe unfortunate acronym for this is SOL. SOL is a way of connecting to the console of a machine via the network interface. Unlike a telnet session, this session is not managed by any of the network daemons. Also, fo us, it allowed us to view the boot messages of a machine. It was a pain to set up, but it kept us from having to find doubly terminated serial cables and spare laptops in order to view a machines status.

Much of my current work is defined by WBEM. I was first exposed to this technology while contracting at Sun Microsystems. We were building configuration tools for online storage arrays. I was on the client side team, but was working on middleware, not the user interface. Just as SNMP allowed you to query the state of something on the network, WBEM had the concept of objects, properties, and requesting the values of a set of properties in bulk across the network. My job was to provide a simple interface to these objects to the business object developers. Layers upon layers. Just like a cake. There was another team of people who worked directly for Sun that were developing the WBEM code on the far side of the wire (called Providers in WBEM speak). WBEM provides the flexibility to set all or the properties, a single property, or any subset in between. The provider developers used this mechanism to set related properties at once. The result was an implicit interface: If you set P1, you must set p2. This is bogus, error prone, and really just plain wrong. My solution was to fetch all of the properties, cache them, and then set them all each time.

WBEM requires a broker, a daemon that listens for network requests and provides a process space for the providers. There are two main open source projects that provide this broker. The first is tog-pegasus, which comes installed with Red Hat Enterprise Linux. The second is Open WBEM, which comes with various versions of SuSE Linux from Novell. However, since WBEM is trying to get into the same space that SNMP currently owns, there has been a demand for a lighter weight version for embedded and small scale deployments. Thus the third project, the small footprint CIM broker or SFCB) which is part of SBLIM.

Data type for IP addresses

I am looking at some code that is IPv4 specific. It stores network addresses as a tuple of a uin32 for the address, a uin16 for the port, and a uin16 type code. I suspect the reason for the type code being a uint16 as opposed to enum is that enums are 32bits in C, and they wanted to pack a everything into 64 bits total.

How would this be stored in IPv6? Ports and types could stay the same, but the address needs to handle 128 bits, not 32. In /usr/include/netinet/ip6.h We see that the ipv6 header is defined with source and destinations of type struct in6_addr. This has the interesting definition of:

I am looking at some code that is IPv4 specific. It stores network addresses as a tuple of a uin32 for the address, a uin16 for the port, and a uin16 type code. I suspect the reason for the type code being a uint16 as opposed to enum is that enums are 32bits in C, and they wanted to packa everything into 64 bits total.

How would this be stored in IPv6? Ports and types could stay the same, but the address needs to handle 128 bits, not 32. In /usr/include/netinet/ip6.h We see that the ipv6 header is defined with source and destinations of type struct in6_addr. This can be found in /usr/include/netinet/in.h and is defined as:

struct in6_addr
{
union
{
uint8_t u6_addr8[16];
uint16_t u6_addr16[8];
uint32_t u6_addr32[4];
} in6_u;
#define s6_addr in6_u.u6_addr8
#define s6_addr16 in6_u.u6_addr16
#define s6_addr32 in6_u.u6_addr32
};

So you have choices. All these fields are arrays, and they are all the same size. One issues is endian-ness. To me, it makes the most sense to work with the array of bytes (or octets) as defined uint8_t u6_addr8[16] as it avoids the endian issues, but using the structure means that the programmer has choices.

The code in question is written to be non-os specific, which is perhaps why they define their own data type for addresses. To make this code IPv6 compliant, I would start with a typedef for netaddress uint32. Then everywhere that used a network address, I would replace the uin32 definition with netaddress. Some people like to use the _t suffix for type names, but I am a little more resistant to anything that smells like Hungarian notation. Once everything used netaddress it would be easier to switch the ipv4 specfic calls to ipv6.

IPv6 Lessons learned since last post

Ok I’ve learned a couple things and there are some boneheaded things in this last post:

First:  the FEC0:: trick is deprecated.  There really is no reason the FE80:: addresses should not work across a switch, so long as there is no router involved.   It might be an OS option.  I’ll have to check.

Second, the address is fe80::x:x:x:x/64.  The top half under the fe80 is all zeros.  That is the netmask, not the top half of the MAC address.  So, while it is cool that they have the same top halves, that is not why the two mac addresses are on the same network.

Getting an IPv6 private address for local use.

Cheap hack to get an IPv6 address that is routable based on info I learned here.

sudo ip addr add \

` /sbin/ifconfig eth0 | awk ‘/inet6/ && /fe80/ {sub(“fe80″,”fec0”,$3); print $3 }’` \

dev eth0

The feco prefix is defined to be non-routable, but visable beyond the current computer, much like 10.x.x.x or 192.168.x.x in IPv4. the 0xfe80 is scope link.

Once I did this on two machine connected by a simple switch, I was able to ssh from one to the other.

One nice thing is that both mac addresses are identical in their top half, so they show up on the same subnet. I guess that means they come from the same vendor?

Support for IPv4 in IPv6

Backwards compatibility can mean make or break for a new technology. One reason why AMD has been successful with it’s 64bit chips is that they can run the vast body of 32bit application without a recompile. If IPv6 is to be as successful, it has to be similarly successful in interoperating with IPv4.

The Linux implementation of the IPv6 server socket API handles connections from IPv4 clients with the same code as it handles IPv6 client connections. I’ve taken and rewritten it to work with IPv6. Commented out lines show the original code. Just about every line I added has the number 6 in it somewhere.

#include
#include
#include
#include
#include
#include
#include

#define PORT 0x1234
#define DIRSIZE 8192

main()
{
char dir[DIRSIZE]; /* used for incomming dir name, and
outgoing data */
int sd, sd_current, cc, fromlen, tolen;
int addrlen;
// struct sockaddr_in sin;
// struct sockaddr_in pin;
struct sockaddr_in6 sin;
struct sockaddr_in6 pin;

/* get an internet domain socket */
// if ((sd = socket(AF_INET, SOCK_STREAM, 0)) == -1) {
if ((sd = socket(AF_INET6, SOCK_STREAM, 0)) == -1) {
perror(“socket”);
exit(1);
}

/* complete the socket structure */
memset(&sin, 0, sizeof(sin));
// sin.sin_family = AF_INET;
sin.sin6_family = AF_INET6;
// sin.sin_addr.s_addr = INADDR_ANY;
sin.sin6_addr = in6addr_any;
// sin.sin_port = htons(PORT);
sin.sin6_port = htons(PORT);

/* bind the socket to the port number */
if (bind(sd, (struct sockaddr *) &sin, sizeof(sin)) == -1) {
perror(“bind”);
exit(1);
}

/* show that we are willing to listen */
if (listen(sd, 5) == -1) {
perror(“listen”);
exit(1);
}
/* wait for a client to talk to us */
addrlen = sizeof(pin);
if ((sd_current = accept(sd, (struct sockaddr *) &pin, &addrlen)) == -1) {
perror(“accept”);
exit(1);
}
/* if you want to see the ip address and port of the client, uncomment the
next two lines */

/*
printf(“Hi there, from %s#\n”,inet_ntoa(pin.sin_addr));
printf(“Coming from port %d\n”,ntohs(pin.sin_port));
*/

char src_addr_str[INET6_ADDRSTRLEN];

if (inet_ntop(AF_INET6, &pin.sin6_addr,
src_addr_str,INET6_ADDRSTRLEN)){
printf(“Hi there, from  %s#\n”, src_addr_str);
printf(“Coming from port %d\n”,ntohs(pin.sin6_port));
}
/* get a message from the client */
if (recv(sd_current, dir, sizeof(dir), 0) == -1) {
perror(“recv”);
exit(1);
}

/* get the directory contents */

/* read_dir(dir); */

strcat (dir,” DUDE”);

/* acknowledge the message, reply w/ the file names */
if (send(sd_current, dir, strlen(dir), 0) == -1) {
perror(“send”);
exit(1);
}

/* close up both sockets */
close(sd_current); close(sd);

/* give client a chance to properly shutdown */
sleep(1);
}