Category Archives: Linux

Using bird to pull global BGP route counts

For an electronics project I’m working on I wanted a way to check the current global routing table every five minutes for both IPv4 and IPv6. I did not want to log into anyone else’s router or looking glass as checking every 5 minutes may be considered abuse.

So I thought to spin up a bird routing instance. I just wanted to receive the routes, not actually install them into the kernel on my linux box. From there I’d be able to check the table size sent over.

Nat Morris helped me out by sending a full tables over a multihop BGP session.

Installing bird is trivial. In order to ensure I’m only running BGP and not installing into the kernel, my configuration looks pretty simple.

/etc/bird.conf:

log syslog all;

router id x.x.x.x;

protocol device { }

protocol bgp {
        local as xxxxx;
        neighbor x.x.x.x as xxxxx;
        multihop;
        password "xxxxx";
}

/etc/bird6.conf:

log syslog all;

router id x.x.x.x;

protocol device { }

protocol bgp {
        local as xxxxx;
        neighbor x:x:x:x:x::x as xxxxx;
        source address x:x:x:x:x:x::x;
        multihop;
        password "xxxxx";
}

In order to get the figures I need, I’d usually have to log into the console of the daemon like so:

$ birdc
BIRD 1.3.7 ready.
bird> show protocols all bgp1
name     proto    table    state  since       info
bgp1     BGP      master   up     19:06       Established
  Preference:     100
  Input filter:   ACCEPT
  Output filter:  REJECT
  Routes:         511014 imported, 0 exported, 511014 preferred
  Route change stats:     received   rejected   filtered    ignored   accepted
    Import updates:         523721          0          0        826     522895
    Import withdraws:         1313          0        ---          0       1313
    Export updates:         522895     522895          0        ---          0
    Export withdraws:         1313        ---        ---        ---          0
  BGP state:          Established
    Neighbor address: x.x.x.x
    Neighbor AS:      xxxxx
    Neighbor ID:      x.x.x.x
    Neighbor caps:    refresh AS4
    Session:          external multihop AS4
    Source address:   x.x.x.x
    Hold timer:       142/180
    Keepalive timer:  33/60

I could get a script to log in and get the required information via regular expressions, but there has to be an easier way. Turns out you can push a command directly to bird without logging into it first:

$birdc 'show protocols all bgp1'
BIRD 1.3.7 ready.
name     proto    table    state  since       info
bgp1     BGP      master   up     19:06       Established
  Preference:     100
  Input filter:   ACCEPT
  Output filter:  REJECT
  Routes:         511025 imported, 0 exported, 511025 preferred
  Route change stats:     received   rejected   filtered    ignored   accepted
    Import updates:         523924          0          0        839     523085
    Import withdraws:         1329          0        ---          0       1329
    Export updates:         523085     523085          0        ---          0
    Export withdraws:         1329        ---        ---        ---          0
  BGP state:          Established
    Neighbor address: x.x.x.x
    Neighbor AS:      xxxxx
    Neighbor ID:      x.x.x.x.x
    Neighbor caps:    refresh AS4
    Session:          external multihop AS4
    Source address:   x.x.x.x
    Hold timer:       161/180
    Keepalive timer:  24/60

Still too much information, but we can use grep!

$birdc 'show protocols all bgp1' | grep 'Routes'
  Routes:         510977 imported, 0 exported, 510977 preferred

Better, but those fields are nicely tabbed so awk to the rescue! I’d like to get that route count. Easily done.

$birdc 'show protocols all bgp1' | grep 'Routes' | awk {'print $2'}
510976

I can now cron a script that will pull those values once every 5 minutes and generate an XML file which you can see right here.

Feel free to query that page and use it for your own projects. Just be aware there is NO SLA on it :)

Debian/Ubuntu PMTUD & uRPF

I originally started my PMTUD posts using Ubuntu 14.04. Halfway through the post I simply could not get Ubuntu to change it’s MTU on receipt of ICMP fragmentation needed messages. I then tried Debian and it worked. Windows also had no issues changing it’s MTU.

Wanting to finish off the post I switched to Debian and then would investigate the fault later.

Let’s remind ourselves of the original topology:
pmtu-1
Swap out Debian for Ubuntu in the above image.

When I initially started to test, I dropped the MTU between R1 and R2 to 1400. The link between R2 and R4 was kept at 1500. If the user requested a file from the server at this point, Ubuntu would attempt to send at 1500 and get it’s packet dropped at R1. R1 would send a Fragmentation Needed packet back to the Ubuntu server, which would adjust it’s MTU and then send at 1400.

When I changed the MTU between R1 and R2 back up to 1500 and dropped R2-R4 down to 1400, it no longer worked. Debian and Windows did work. I ran tcpdump on Ubuntu and confirmed that it was definitely getting Fragmentation Needed packets. Ubuntu was only acting on Fragmentation Needed packets if it came from it’s default gateway, R1. Any router further along in the path was getting it’s ICMP packets ignored.

In order to understand what the problem is I need to show more about the topology. While the above diagram shows how thins are connected for the most part, it is missing a couple of things. All the devices are running inside virtualbox linked to GNS3. eth1 of all the servers are connected to the above topology, while eth0 was connected via NAT to my host PC so I could install software:
PMTUD-uRPF
Each device had a static route to 192.168.0.0/16 to go out eth1 while their default route was out eth0. Some of you may be sensing what the issue is already…

The point to point links between the virtual routers are using the 10.0.0.0/8 space.

If Ubuntu received an ICMP packet from 192.168.4.1, it’s local default gateway on R1, there were no issues. If it received a packet from R2 or R4′s local interfaces, the packet was dropped. Debian and Windows both didn’t have problems, even though they are configured the same way.

sysctl.conf

I’ve touched on sysctl.conf before the the PMTUD posts, but there is an important difference in the defaults of Ubuntu and Debian. Take a look at this.
Debian:
Screen Shot 2014-09-02 at 9.11.50 am
Ubuntu:
Screen Shot 2014-09-02 at 9.12.01 am

uRPF

Ubuntu has Unicast Reverse Path Forwarding on by default. Debian has it off by default. In sysctl.conf on both machines, the required configuration setting is commented out:
Screen Shot 2014-09-02 at 9.15.44 am
R2 was originating it’s ICMP packets from it’s local interface, 10.0.12.2 in my example. Ubuntu did receive that packet, but it failed the RPF check and so was ignored. To confirm I tested this in two different ways:

  • Add a static route to 10.0.0.0/8 out eth1
  • Disable uRPF check on Ubuntu

Each test individually allowed the original PMTUD to work. What’s odd is that the sysctl.conf file in Ubuntu says that you need to uncomment the lines to turn on uRPF, but it’s on by default. Uncommenting the lines and setting the value to 1 is the same as leaving them commented. In Debian the default is to disable uRPF. In that distro you would need to uncomment the uRPF lines and set the value to 1 to turn the feature on.

Conclusions

  • If a server is multi-homed, PMTUD could break if the ICMP message arrives on an interface that the server is not expecting.
  • If you do have a server multi-homed, it would probably be best to turn off uRPF

Final prep for my home lab

So now that I’ve got all the real equipment, it’s time to deploy everything.

As noted before, this is the way that I’ll be working: http://mellowd.co.uk/ccie/?p=1516

This is currently how the physical stuff actually looks like:


Now there were a couple of things I noticed when trying to get InternetworkExpert’s topologies onto my lab. Firstly I need to hard-code both my emulated routers and my real switches ethernet interfaces to 100Mb/full. Some of the interface names have also changed (s0/1/0 becomes s0/1, ethernet becomes fastethernet, etc) – Having to go through all the configs and doing it manually would take forever. Thankfully Daniel (http://lostintransit.se/) created a script to modify his startup configs. I’ve taken that script and modified it a bit more to come up with the following script:

#!/bin/sh
# Shell script to convert INE vol1 configs to Dynamips format
# Changes interface names
# Created by Daniel Dib @ http://http://lostintransit.se/
# Modified by Darren @ http://mellowd.co.uk/ccie

#Check if configs exist
for config in r1 r2 r3 r4 r5 r6 sw1 sw2 sw3 sw4
do
  if [ ! -r "$config.txt" ]; then
    doerror "Configuration file $config.txt does not exist"
  fi
done

echo "Files exist, continuing script..."

echo "Correcting configurations..."

echo "Starting with r1-r6..."

sed "
/FastEthernet0\/0/a \ speed 100\n duplex full
" r1.txt > r1.cfg

sed "
/FastEthernet0\/0/a \ speed 100\n duplex full
" r2.txt > r2.cfg

sed "
/FastEthernet0\/0/a \ speed 100\n duplex full
/FastEthernet0\/1/a \ speed 100\n duplex full
" r3.txt > r3.cfg

sed "
s/Serial0\/0\/0/Serial0\/0/g
s/Serial0\/1\/0/Serial0\/1/g
/FastEthernet0\/0/a \ speed 100\n duplex full
/FastEthernet0\/1/a \ speed 100\n duplex full
" r4.txt > r4.cfg

sed "
s/Serial0\/0\/0/Serial0\/0/g
s/Serial0\/1\/0/Serial0\/1/g
/FastEthernet0\/0/a \ speed 100\n duplex full
/FastEthernet0\/1/a \ speed 100\n duplex full
" r5.txt > r5.cfg

sed "
s/Serial0\/0\/0/Serial0\/0/g
/FastEthernet0\/0/a \ speed 100\n duplex full
/FastEthernet0\/1/a \ speed 100\n duplex full
" r6.txt > r6.cfg

echo "Routers done, starting with switches..."

sed "
s/switchport host/switchport mode access/g
/FastEthernet0\/1/a \ speed 100\n duplex full
/FastEthernet0\/2/a \ speed 100\n duplex full
/FastEthernet0\/3/a \ speed 100\n duplex full
/FastEthernet0\/4/a \ speed 100\n duplex full
/FastEthernet0\/5/a \ speed 100\n duplex full
/FastEthernet0\/6/a \ speed 100\n duplex full
/FastEthernet0\/7/a \ speed 100\n duplex full
/FastEthernet0\/8/a \ speed 100\n duplex full
/FastEthernet0\/9/a \ speed 100\n duplex full
/FastEthernet0\/10/a \ speed 100\n duplex full
/FastEthernet0\/11/a \ speed 100\n duplex full
/FastEthernet0\/12/a \ speed 100\n duplex full
/FastEthernet0\/13/a \ speed 100\n duplex full
/FastEthernet0\/14/a \ speed 100\n duplex full
/FastEthernet0\/15/a \ speed 100\n duplex full
/FastEthernet0\/16/a \ speed 100\n duplex full
/FastEthernet0\/17/a \ speed 100\n duplex full
/FastEthernet0\/18/a \ speed 100\n duplex full
/FastEthernet0\/19/a \ speed 100\n duplex full
/FastEthernet0\/20/a \ speed 100\n duplex full
/FastEthernet0\/21/a \ speed 100\n duplex full
/FastEthernet0\/22/a \ speed 100\n duplex full
/FastEthernet0\/23/a \ speed 100\n duplex full
/FastEthernet0\/24/a \ speed 100\n duplex full
" sw1.txt > sw1.cfg

sed "

s/switchport host/switchport mode access/g
/FastEthernet0\/1/a \ speed 100\n duplex full
/FastEthernet0\/2/a \ speed 100\n duplex full
/FastEthernet0\/3/a \ speed 100\n duplex full
/FastEthernet0\/4/a \ speed 100\n duplex full
/FastEthernet0\/5/a \ speed 100\n duplex full
/FastEthernet0\/6/a \ speed 100\n duplex full
/FastEthernet0\/7/a \ speed 100\n duplex full
/FastEthernet0\/8/a \ speed 100\n duplex full
/FastEthernet0\/9/a \ speed 100\n duplex full
/FastEthernet0\/10/a \ speed 100\n duplex full
/FastEthernet0\/11/a \ speed 100\n duplex full
/FastEthernet0\/12/a \ speed 100\n duplex full
/FastEthernet0\/13/a \ speed 100\n duplex full
/FastEthernet0\/14/a \ speed 100\n duplex full
/FastEthernet0\/15/a \ speed 100\n duplex full
/FastEthernet0\/16/a \ speed 100\n duplex full
/FastEthernet0\/17/a \ speed 100\n duplex full
/FastEthernet0\/18/a \ speed 100\n duplex full
/FastEthernet0\/19/a \ speed 100\n duplex full
/FastEthernet0\/20/a \ speed 100\n duplex full
/FastEthernet0\/21/a \ speed 100\n duplex full
/FastEthernet0\/22/a \ speed 100\n duplex full
/FastEthernet0\/23/a \ speed 100\n duplex full
/FastEthernet0\/24/a \ speed 100\n duplex full
" sw2.txt > sw2.cfg

sed "

s/switchport host/switchport mode access/g
/FastEthernet0\/1/a \ speed 100\n duplex full
/FastEthernet0\/2/a \ speed 100\n duplex full
/FastEthernet0\/3/a \ speed 100\n duplex full
/FastEthernet0\/4/a \ speed 100\n duplex full
/FastEthernet0\/5/a \ speed 100\n duplex full
/FastEthernet0\/6/a \ speed 100\n duplex full
/FastEthernet0\/7/a \ speed 100\n duplex full
/FastEthernet0\/8/a \ speed 100\n duplex full
/FastEthernet0\/9/a \ speed 100\n duplex full
/FastEthernet0\/10/a \ speed 100\n duplex full
/FastEthernet0\/11/a \ speed 100\n duplex full
/FastEthernet0\/12/a \ speed 100\n duplex full
/FastEthernet0\/13/a \ speed 100\n duplex full
/FastEthernet0\/14/a \ speed 100\n duplex full
/FastEthernet0\/15/a \ speed 100\n duplex full
/FastEthernet0\/16/a \ speed 100\n duplex full
/FastEthernet0\/17/a \ speed 100\n duplex full
/FastEthernet0\/18/a \ speed 100\n duplex full
/FastEthernet0\/19/a \ speed 100\n duplex full
/FastEthernet0\/20/a \ speed 100\n duplex full
/FastEthernet0\/21/a \ speed 100\n duplex full
/FastEthernet0\/22/a \ speed 100\n duplex full
/FastEthernet0\/23/a \ speed 100\n duplex full
/FastEthernet0\/24/a \ speed 100\n duplex full
" sw3.txt > sw3.cfg

sed "

s/switchport host/switchport mode access/g
/FastEthernet0\/1/a \ speed 100\n duplex full
/FastEthernet0\/2/a \ speed 100\n duplex full
/FastEthernet0\/3/a \ speed 100\n duplex full
/FastEthernet0\/4/a \ speed 100\n duplex full
/FastEthernet0\/5/a \ speed 100\n duplex full
/FastEthernet0\/6/a \ speed 100\n duplex full
/FastEthernet0\/7/a \ speed 100\n duplex full
/FastEthernet0\/8/a \ speed 100\n duplex full
/FastEthernet0\/9/a \ speed 100\n duplex full
/FastEthernet0\/10/a \ speed 100\n duplex full
/FastEthernet0\/11/a \ speed 100\n duplex full
/FastEthernet0\/12/a \ speed 100\n duplex full
/FastEthernet0\/13/a \ speed 100\n duplex full
/FastEthernet0\/14/a \ speed 100\n duplex full
/FastEthernet0\/15/a \ speed 100\n duplex full
/FastEthernet0\/16/a \ speed 100\n duplex full
/FastEthernet0\/17/a \ speed 100\n duplex full
/FastEthernet0\/18/a \ speed 100\n duplex full
/FastEthernet0\/19/a \ speed 100\n duplex full
/FastEthernet0\/20/a \ speed 100\n duplex full
/FastEthernet0\/21/a \ speed 100\n duplex full
/FastEthernet0\/22/a \ speed 100\n duplex full
/FastEthernet0\/23/a \ speed 100\n duplex full
/FastEthernet0\/24/a \ speed 100\n duplex full
" sw4.txt > sw4.cfg

The BB1, BB2 and BB3 routers also need to be changed, but as the config never changes in any of the labs you can just download the fixed versions of all 3 from here:

I also need a script on my dynamips box that will up all the interfaces on the NICs and then run dynamips. This is my start script:

#!/bin/bash
#Bring interfaces up
ifconfig eth1 up
ifconfig eth2 up
ifconfig eth3 up
ifconfig eth4 up
ifconfig eth5 up
ifconfig eth6 up
ifconfig eth7 up
ifconfig eth8 up
ifconfig eth9 up
ifconfig eth10 up
ifconfig eth11 up
ifconfig eth12 up

#Start Hypervisor
dynamips -H 7200 &

Finally, my actual dynagen .net file I use on my laptop looks like so:

 autostart = false

[10.20.30.12:7200]
 workingdir = /data/dynamips/working

[[3725]]
 image = /data/dynamips/ios/3725/c3725-adventerprisek9-mz.124-15.T14.UNCOMPRESSED.bin
 ram = 142
 idlepc = 0x6026be14
 ghostios = True
 mmap = True

[[ROUTER r1]]
        model = 3725
        console = 2001
        f0/0 = nio_linux_eth:eth4
        s0/0 = FRAME 1

[[ROUTER r2]]
        model = 3725
        console = 2002
        f0/0 = nio_linux_eth:eth5
        s0/0 = FRAME 2

[[ROUTER r3]]
        model = 3725
        console = 2003
        slot1 = NM-4T
        f0/0 = nio_linux_eth:eth8
        f0/1 = nio_linux_eth:eth12
        s1/0 = FRAME 3
        s1/1 = FRAME 13
        s1/2 = r1 s0/1
        s1/3 = r2 s0/1

[[ROUTER r4]]
        model = 3725
        console = 2004
        f0/0 = nio_linux_eth:eth7
        f0/1 = nio_linux_eth:eth11
        s0/0 = FRAME 4
        s0/1 = r5 s0/1

[[ROUTER r5]]
        model = 3725
        console = 2005
        f0/0 = nio_linux_eth:eth2
        f0/1 = nio_linux_eth:eth6
        s0/0 = FRAME 5

[[ROUTER r6]]
        model = 3725
        console = 2006
        f0/0 = nio_linux_eth:eth3
        f0/1 = nio_linux_eth:eth1
        s0/0 = FRAME 6

[[ROUTER bb1]]
        model = 3725
        console = 2007
        slot1 = NM-4T
        s1/0 = bb3 s1/0
        s1/1 = FRAME 21

[[ROUTER bb2]]
        model = 3725
        console = 2008
        f0/0 = nio_linux_eth:eth10

[[ROUTER bb3]]
        model = 3725
        console = 2009
        slot1 = NM-4T
        f0/0 = nio_linux_eth:eth9

[[FRSW FRAME]]

  #
  # R1 to FRSW
  #
  1:102 = 2:201
  1:103 = 3:301
  1:113 = 13:311
  1:104 = 4:401
  1:105 = 5:501
  #
  # R2 to FRSW
  #
  2:203 = 3:302
  2:213 = 13:312
  2:204 = 4:402
  2:205 = 5:502
  #
  # R3 to FRSW
  #
  3:304 = 4:403
  3:305 = 5:503
  13:314 = 4:413
  13:315 = 5:513
  #
  # R4 to FRSW
  #
  4:405 = 5:504
  #
  # R6 to FRSW
  #
  6:51 = 21:51
  6:100 = 21:100
  6:101 = 21:101
  6:201 = 21:201
  6:301 = 21:301
  6:401 = 21:401

Capture packets directly inside dynamips

I had no idea that dynagen could actually do this, but it’s pretty damn awesome. Dynagen/Dynamips can output any interface’s traffic directly to a .cap file ready to be read in tcpdump or Wireshark.

How do we do this?

Let’s take a simple topology. R2 and R4 are running OSPF with each other, directly connected via their Fa0/1 interfaces. I want to capture packets going in and out of R2′s interface.

Dynamips/Dynagen has started:

=> list
Name       Type       State      Server          Console
R2         7200       running    localhost:7200  2002
R4         7200       running    localhost:7200  2004

You start capturing like so:

=> capture R2 fa0/1 /tmp/R2.cap

Stop the capture:

=> no capture R2 fa0/1

We now have a file named R2.cap in the /tmp folder. We can open it either in tcpdump or Wireshark:

darreno@Zenoss:/tmp$ sudo tcpdump -r R2.cap
reading from file R2.cap, link-type EN10MB (Ethernet)
16:37:26.959354 IP 7.3.24.2 > OSPF-ALL.MCAST.NET: OSPFv2, Hello, length 56
16:37:28.930017 CDPv2, ttl: 180s, Device-ID 'R2', length 318
16:37:32.827991
16:37:36.959745 IP 7.3.24.2 > OSPF-ALL.MCAST.NET: OSPFv2, Hello, length 56
16:37:42.855127
16:37:46.930856 IP 7.3.24.2 > OSPF-ALL.MCAST.NET: OSPFv2, Hello, length 56
16:37:52.846687
16:37:56.940269 IP 7.3.24.2 > OSPF-ALL.MCAST.NET: OSPFv2, Hello, length 56
16:38:02.826550
16:38:06.943788 IP 7.3.24.2 > OSPF-ALL.MCAST.NET: OSPFv2, Hello, length 56
16:38:12.836321
16:38:16.947188 IP 7.3.24.2 > OSPF-ALL.MCAST.NET: OSPFv2, Hello, length 56
16:38:22.835339
16:38:26.932277 IP 7.3.24.2 > OSPF-ALL.MCAST.NET: OSPFv2, Hello, length 56
16:38:28.940528 CDPv2, ttl: 180s, Device-ID 'R2', length 318

Very handy indeed!

Why buy a terminal server when an old PC will do

My lab will require me to console into 4 switches. I don’t want to buy another router to be my terminal server, and I also don’t want to have to swap out console cables every 2 minutes.

Thankfully I don’t have to. Linux has a handy tool to turn a regular PC with a bunch of USB serial ports into a terminal server.

All you need is a bunch of USB ports (a USB hub will do), some rollover cables and a bunch of these which I found on ebay:

I bought 3 of the above, as the PC itself already has 1 serial port.

The app I’m going to use is called ser2net. Let’s install it

darreno@CCIE:~$ sudo apt-get install ser2net

Now let’s see if Ubuntu 10.10 has reconised my USB serial cables:

darreno@CCIE:~$ dmesg | grep tty
[    0.000000] console [tty0] enabled
[    0.932016] serial8250: ttyS0 at I/O 0x3f8 (irq = 4) is a 16550A
[    0.932314] 00:05: ttyS0 at I/O 0x3f8 (irq = 4) is a 16550A
[    6.625434] usb 6-1: ch341-uart converter now attached to ttyUSB0
[    6.649441] usb 6-3: ch341-uart converter now attached to ttyUSB1
[    6.672443] usb 3-2: ch341-uart converter now attached to ttyUSB2

Looks good so far. ttyS0 is my physical serial port and the ttyUSBx ports are the 3 USB-Serial cables. I’ll now configure ser2net to map TCP ports to my onboard port and my 3 USB ports.

darreno@CCIE:~$ sudo vi /etc/ser2net.conf

At the bottom of that file you’ll see this:

2000:telnet:600:/dev/ttyS0:9600 8DATABITS NONE 1STOPBIT banner
2001:telnet:600:/dev/ttyS1:9600 8DATABITS NONE 1STOPBIT banner
3000:telnet:600:/dev/ttyS0:19200 8DATABITS NONE 1STOPBIT banner
3001:telnet:600:/dev/ttyS1:19200 8DATABITS NONE 1STOPBIT banner

Let’s change it to this:

2000:telnet:600:/dev/ttyS0:9600 8DATABITS NONE 1STOPBIT banner
2001:telnet:600:/dev/ttyUSB0:9600 8DATABITS NONE 1STOPBIT banner
2002:telnet:600:/dev/ttyUSB1:9600 8DATABITS NONE 1STOPBIT banner
2003:telnet:600:/dev/ttyUSB2:9600 8DATABITS NONE 1STOPBIT banner

save and quit, then restart ser2net:

darreno@CCIE:~$ sudo /etc/init.d/ser2net restart
 * Restarting Serial port to network proxy ser2net                                                          [ OK ]

Now to test!

My dynamips box has an IP of 10.20.30.11. If I want to get to the physical serial port from my laptop, I only need to do this:

C:\Users\Darren>telnet 10.20.30.11 2000
ser2net port 2000 device /dev/ttyS0 [9600 N81] (Debian GNU/Linux)

C3560#
C3560#

What about a USB cable?

C:\Users\Darren>telnet 10.20.30.11 2003
ser2net port 2003 device /dev/ttyUSB2 [9600 N81] (Debian GNU/Linux)

C3550#
C3550#

I use a tabbed telnet/SSH app called ZOC. From there I can specify the telnet ports and which switch they map to. I can now console into all of them without the need of a terminal server. The actual USB-Serial cables only cost me £2 each, and you can have loads of them.

(2011/02/19) EDIT: Dariush below mentioned that you can get multiple serial adaptors through a single USB port. This is an example: http://www.amazon.co.uk/NEWLink-Serial-Quad-Cable-Adaptor/dp/B003DA5TG4

If you don’t want to go the USB hub root, this could certainly work. Serial cables aren’t exactly high bandwidth. I’ve even found an 8 port model here, but no price: http://www.delock.com/produkte/gruppen/USB+Adapter/Delock_Adapter_USB_8x_Serial_61519.html