The official llllloooooo blog

Wednesday, November 4, 2020

tcpdump displays oui Unknown when displaying Ethernet headers

When you run the popular lightweight packet sniffer tcpdump and you command it to display link layer or Ethernet headers by specifying the -e option you will most likely see a message saying oui Unknown as per the following example...

tcpdump -e -t -i eth0 udp port chargen
dc:a6:32:11:22:33 (oui Unknown) > 00:1c:58:33:22:11 (oui Unknown), ethertype IPv6 (0x86dd), length 63: fd55:5555:5555:5555:dea6:32ff:fe11:2233.33031 > fd55:5555:5555:5555::1.chargen: UDP, length 1

As I'm sure that anyone who is reading this knows, an Organizationally Unique Identifier (OUI) is a 3 byte code at the start of an Ethernet Address that is meant to identify the manufacturer of the device or Ethernet interface that the Ethernet Address belongs to.

By default tcpdump only recognizes a very, very small list of OUI codes that belong to very old equipment and so the chances are that when you run tcpdump -e you'll see the oui Unknown message displayed.

You can fix that by recompiling tcpdump to include a more up to date list of OUI codes with the help of a python script called make-oui.py which is available at

https://gist.githubusercontent.com/gvanem/1643c946fb2395b6c8a05c3ec8904e13/raw/620085609082829a0f5572ad2359b21df2c1b0f0/make-oui.py

This script downloads the latest version of the OUI codes. By default it also downloads a list of something called Enterprise Numbers which map certain numbers to equipment Manufacturer names. This Enterprise Numbers list only has relevance to tcpdump when you're decoding RADIUS packets or MPLS LSP Ping packets. I can see some people wanting to decode RADIUS packets but MPLS LSP Ping would be extremely rare except for in very sophisticated high end ISP or Enterprise networks.

I really need to stop procrastinating and implement MPLS in
my home network.

The way to get the script to work is to first download it to the directory containing the tcpdump source code. (I've tested this with tcpdump version 4.9.3). Naturally make sure the script is executable

# wget https://gist.githubusercontent.com/gvanem/1643c946fb2395b6c8a05c3ec8904e13/raw/620085609082829a0f5572ad2359b21df2c1b0f0/make-oui.py
# chmod +x make-oui.py

I suggest that you DO perform the next command but it is optional. If you perform the next step the tool will NOT download the Enterprise Numbers that I talked about. Just to reemphasize, you don't need these Enterprise Numbers if you just want to look at Ethernet OUI names. If you do skip the next step so that you can have tcpdump include Enterprise Numbers then the resultant binary executable is about 2M bigger than it would be otherwise!!

# echo "" > enterprise-numbers

Now run the script as follows

./make-oui.py 
Downloading oui-generated.txt from http://standards-oui.ieee.org/oui.txt 
Got 4488 kBytes (100%)
Wrote 28843 OUI records to oui-generated.c
A local enterprise-numbers already exist.
Appended 0 SMI records to oui-generated.c

This script has now generated a file called oui-generated.c which we can now use to replace the original oui.c file included in the tcpdump source code.

cp oui.c oui.c.orig
cp oui-generated.c oui.c

Finally build and install tcpdump as per normal (Remember to make sure libpcap is installed first). 

Note that the new oui.c file will generate some warning messages when it compiles as per the following. For some reason the make-oui.py tool adds some functions to oui.c that aren't actually used but they are harmless.

./oui.c: In function 'oui_val_to_name':
./oui.c:28883:25: warning: implicit declaration of function 'bsearch' [-Wimplicit-function-declaration]
28883 |   const struct tok *t = bsearch (&oui, oui_values, num, sizeof(oui_values[0]),
      |                         ^~~~~~~
./oui.c:28883:25: warning: initialization of 'const struct tok *' from 'int' makes pointer from integer without a cast [-Wint-conversion]
./oui.c: In function 'smi_val_to_name':
./oui.c:28891:25: warning: initialization of 'const struct tok *' from 'int' makes pointer from integer without a cast [-Wint-conversion]
28891 |   const struct tok *t = bsearch (&smi, smi_values, num, sizeof(smi_values[0]),

If you really want to get rid of those warnings then just edit the new oui.c file and remove all the lines at the bottom beginning with the one that says

/* Comparision routine needed by bsearch() routines.

If we re-run the new binary with the same input as the command shown at the top of this post, you'll see that I'm sending a packet from my Raspberry Pi to a Cisco router.

tcpdump -e -t -i eth0 udp port chargen
dc:a6:32:11:22:33 (oui Raspberry Pi Trading Ltd) > 00:1c:58:33:22:11 (oui Cisco Systems, Inc), ethertype IPv6 (0x86dd), length 63: fd55:5555:5555:5555:dea6:32ff:fe11:2233.33031 > fd55:5555:5555:5555::1.chargen: UDP, length 1

Note that the tcpdump binary executable that includes the up to date OUI list will be about 600kbytes larger than the normal tcpdump binary without the OUI list. If you decide that you want to include the Enterprise Numbers then add about another 2Mbytes to that. 

I'd love it if anyone out there has actually decoded a packet with tcpdump (i.e. an MPLS LSP Ping or RADIUS packet). Please let me know in the comments because you are very cool!

Monday, November 2, 2020

ifconfig in LFS doesn't display IPv6 information

The default version of ifconfig installed as part of Linux From Scratch doesn't work for IPv6. It doesn't display any ipv6 info as per the following output

# ifconfig -i eth0
eth0      Link encap:Ethernet  HWaddr D1:AA:42:11:22:33
          inet addr:10.1.2.3  Bcast:10.1.2.255  Mask:255.255.255.0
          UP BROADCAST RUNNING MULTICAST  MTU:1500  Metric:1
          RX packets:2062 errors:0 dropped:1 overruns:0 frame:0
          TX packets:237 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:1000
          RX bytes:161416  TX bytes:77392

ifconfig -V
ifconfig (GNU inetutils) 1.9.4
Copyright (C) 2015 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>.
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.

Written by Marcus Brinkmann.

Note that there's no IPv6 address in the ifconfig command output? I know that IPv6 is working because I can use the new ip addr command from the "iproute2" suite to show me the active IPv6 addresses on the device. I can also ping6 to the local loopback interface address (::1) and I can ping6 from the device to a remote IPv6 address. 

You need to go ahead and install the version of ifconfig that comes with the net-tools suite as seen in Beyond LFS at

http://www.linuxfromscratch.org/blfs/view/svn/basicnet/net-tools.html

You'll probably have to install the net-tools suite any way to get the important command netstat which can be used to see what ports are open on your system.

When you go to follow the instructions to compile net-tools as seen at the link above DO NOT apply the remove_dups patch that they suggest. That will stop you from building the IPv6 capable version of ifconfig. It's up to you if you want to use the hostname tool from net-tools or keep the old one. I can't see much difference. Neither of them displays an IPv6 address. 

In addition when you run the "make config" command which asks you questions about what features you want, you must say no to the following features

DECnet protocol family (HAVE_AFDECnet)
STRIP (Metricom radio) support (HAVE_HWSTRIP)
Token ring (generic) support (HAVE_HWTR)

I admit, it is a little grating not to have DECnet or Token ring support but if I am magically transported back to 1987 I'll worry about it then. 

Back to the Future IV was all ready to film. It was about Marty needing
to go back to the past to get some DECnet drivers to install on his modern
day Linux system. For some reason the movie couldn't secure financing.

After building net-tools I backed up the original inetutils version of the ifconfig and hostname executables and deleted the old inetutils version of the ifconfig man page. I used the following commands as root

cp /bin/hostname /bin/hostname.inetutils
cp /sbin/ifconfig /sbin/ifconfig.inetutils
rm /usr/share/man/man1/ifconfig.1

Finally I ran make install to install the newer and better versions of both tools. It works beautifully now as per the output below (Note that the command syntax is a little different, but not significantly)

ifconfig eth0
eth0: flags=4163<UP,BROADCAST,RUNNING,MULTICAST>  mtu 1500  metric 1
        inet 10.1.2.3  netmask 255.255.255.0  broadcast 10.1.2.255
        inet6 fe80::d3aa:42ff:fe11:2233  prefixlen 64  scopeid 0x20<link>
        inet6 200a:111b:222c:333d:d3aa:42ff:fe11:2233  prefixlen 64  scopeid 0x0<global>
        ether d1:aa:42:11:22:33  txqueuelen 1000  (Ethernet)
        RX packets 2184  bytes 171492 (167.4 KiB)
        RX errors 0  dropped 1  overruns 0  frame 0
        TX packets 254  bytes 78754 (76.9 KiB)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0

# ifconfig -V

net-tools 1.60

ifconfig 1.42 (2001-04-13)

P.S. If you really do want DECnet support then you can try to install libdnet as seen at 

http://libdnet.sourceforge.net/

Note that it hasn't been updated since 2005 so I have no idea if it will work or not. 

As for Token Ring and STRIP support.....if you actually manage to get one of those WORKING, and I mean not just compiled but PHYSICALLY WORKING BETWEEN DEVICES using a Raspberry Pi then I will send you MONEY to tell me what hardware and setup you are using because that would be AWESOME!!

Token Ring was obviously superior to Ethernet. The world would
have been a better place if Token Ring won the LAN wars.

Getting IPv6 services working with xinetd on LFS

This is another post in the series of my trying to get Linux From Scratch working on a Raspberry Pi to my satisfaction.

If you are an IPv6 obsessive like me then you won't accept any tool or service that does not natively and easily support IPv6. xinetd doesn't really make it easy and obvious how to enable small services for IPv6 but it can probably be forgiven since development seems to have finished on it in about 2007. This post is the list of steps I went through to get xinetd working and working with IPv6.

xinetd is a more modern and secure replacement for the older inetd tool which can be used to start many small old school services such as telnet / echo / discard / chargen / daytime / systat etc. These are the kinds of tools that you can enable on a cisco router using the command "service tcp-small-servers" .

Even though the Linux From Scratch manuals do not encourage installing inetd or xinetd I still think it's useful to have these cool little services available. Anyone who knows about these tools already knows that they are not encrypted or secure. For that reason it makes sense to only make them available on your local trusted network segments. 

I managed to compile xinetd on the raspberry pi by first installing the tirpc library as per the instructions at

http://www.linuxfromscratch.org/blfs/downloads/stable/BLFS-BOOK-10.0-nochunks.html#libtirpc

Then I slightly modified the installed libtirpc include files so that they looked like the "old" rpc include files that xinetd depends on by using the following shell commands. This is required so that xinetd compiles rpc tools nicely.

# First change to the directory where the original 
#rpc include files (netdb.h) are
cd /usr/include/rpc

# Next move them all to the tirpc include directory
mv netdb.h ../tirpc/rpc

# Delete the old rpc directory and link it to the 
# more modern tirpc version
cd /usr/include
rm -rf rpc
ln -sfv tirpc/rpc rpc

To obtain xinetd follow the instructions as seen in an older version of Beyond LFS as seen at

http://www.linuxfromscratch.org/blfs/view/7.7/server/xinetd.html

And download the source gz file from

ftp://anduin.linuxfromscratch.org/BLFS/svn/x/xinetd-2.3.15.tar.gz

Decompress and compile as per the instructions seen in the link above but before you type "make" run the following command to tell the build to use the newly installed tirpc libraries.

export LDFLAGS="-ltirpc"

Now to IPv6. The secret is that you have to use the largely undocumented xinetd configuration file attribute v6only . This attribute isn't even mentioned in the xinetd.conf man page and good luck finding details on your favorite search engine. The only reason I knew about it was the thread I've linked below that talks about others trying to get IPv6 working with xinetd services

* xinetd doesn't listen on IPv6 by default
https://bugzilla.redhat.com/show_bug.cgi?id=195265

If you try to do this without using the secret v6only attribute then you might end up with error messages in your log file (/var/log/daemon.log on my system) that look something like

xinetd[123]: bind failed (Address already in use (errno = 98)). service = chargen-stream-v6
xinetd[123]: Service chargen-stream-v6 failed to start and is deactivated.

Basically what you have to do is when you define a service in your xinetd.conf files is to specify it twice. First the IPv4 version and then the IPv6 version. For example, for tcp chargen (port 19) I have the following in my config file

# IPv4 version of tcp chargen
service chargen
{
        disable         = no
        type            = internal
        id              = chargen-stream
        socket_type     = stream
        protocol        = tcp
        user            = root
        wait            = no
        flags           = IPV4
}

# IPv6 version of tcp chargen
service chargen
{
        disable         = no
        type            = internal
        id              = chargen-stream-v6
        socket_type     = stream
        protocol        = tcp
        user            = root
        wait            = no
        flags           = IPV6
        v6only          = yes
}


Technically you don't need the "flags = IPV4" statement in the first paragraph because xinetd will only create an IPv4 socket anyway even if you leave this statement out, but including this attribute makes the paragraphs a lot clearer in describing what they are doing.

So assuming you have xinetd services working for IPv4 you should be able to get them working for IPv6 by just copying each paragraph, changing the "id" slightly to make it unique, and then adding the "flags = IPV6" and "v6only = yes" attributes.

Good luck!