Header Image

Twelve Days, wtf?

On Sunday I was making cookies (Snickerdoodles) and Ali had put on some Christmas music. The Twelve Days Of Christmas, The Sinatra Christmas Album version, came on, and I said to her, "I'm confused by this song. What are the twelve days of Christmas?" I went through a couple of scenarios in my head, and I finally said, "How many days are in Hanukkah?" She said, "Seven? I think." Me, "Huh. I really don't understand this song, where do the twelve days come from?"

Today, I was poking around on the intertubes and came across this video. I thought it was pretty funny how much this mirrored my thought process ...

Man In The Middle

I got pulled into a problem at work yesterday. The issue is that some people (randomly) begin experiencing one-way audio on a phone call that moments before had been working fine. Packet captures revealed that when the problem occurs the audio stream makes it all the way to the IP phone, but fails to be played on the user’s handset. A colleague had analyzed the data and found an anomaly around the time the audio stopped working -- the RTP timestamp in the packet jumped significantly. The working theory was that maybe the phone was confused by the time jump which caused it to quit playing the audio. It felt like a plausible theory, but it also felt like it could be a red herring.

The obvious thing would be to reproduce in a controlled environment to test the theory. At first we didn't think there was a good way to do this, but then I had an idea. The solution, insert something in the call path that could re-write RTP packets while still in transit to their destination. If the packets could be manipulated in real-time then we could trigger the timestamp jump at will during a phone call.

The rest of this entry details how that was accomplished. Read on if you are interested.

There are several ways to insert a device into the packet path. I happen to have a capable Cisco router front ending my network so I chose to use policy based routing to accomplish this part of the task.

To enable policy routing all that is needed is an access-list and a route-map. The access-list consists of a single entry

access-list 120 permit udp any host 192.168.1.30

The route-map isn’t much more complex

route-map pbr permit 10
   match ip address 120
   set ip next-hop 192.168.1.22

The logic is this: match any packets that are type UDP and are destined for the IP address of the phone (192.168.1.30), and change the next hop to be the Linux box. To enable the policy it needs to be applied to the ingress interface on the router with the command:

ip policy route-map pbr
By using tcpdump on the Linux box I was able to confirm that packets destined for the phone were now being redirected to the Linux box. This also had the side effect of breaking audio in one direction since the RTP packets were being sent to the Linux box and not the phone.

The next step was to write a program that could listen for and decode the packets. I used PERL and Net::Pcap for this. Net::Pcap provides a nice interface to libpcap, which is the same backend that tcpdump and Wireshark use. Once the information was decoded, the final step was to create a RAW socket. RAW sockets allow for hand crafted packets to be placed on the wire, and is the mechanism that allowed the manipulated packets to be sent on to their true destination. Below is the code that accomplishes this:

#!/usr/bin/perl

use strict;
use Socket;
use Net::Pcap ();
use POSIX ();

$| = 1;

our $cfg = {
  'dev'           => 'eth1',
  'seen'          => 0,
  'count'         => 0,
  'ether_hdr_len' => 14,
  'udp_hdr_len'   => 8,
  'ip_source_loc' => 12,
};

#------------------------------------------------------------------------------
# Open device for live capture
#------------------------------------------------------------------------------
my $err = '';
my $pcap = Net::Pcap::open_live($cfg->{'dev'}, 1546, 1, 0, \$err) ||
  die "Can't open $cfg->{'dev'} for sniffing ($err)\n";
#------------------------------------------------------------------------------

#------------------------------------------------------------------------------
# Create a filter to ensure we are only seeing packets of interest
#------------------------------------------------------------------------------
my $filt = "ether src 00:0b:be:59:92:20 && src host 10.10.10.10 && ".
  "dst host 192.168.1.30";
my $cfilt;
if (Net::Pcap::compile($pcap, \$cfilt, $filt, 1, 0) == -1) {
  die "Unable to compile filter string $filt\n";
}
Net::Pcap::setfilter($pcap, $cfilt);
#------------------------------------------------------------------------------

#------------------------------------------------------------------------------
# Start the capture.  The subroutine process_packet will be called for each
# packet that matches the filter.
#------------------------------------------------------------------------------
my $rv = Net::Pcap::loop($pcap, -1, \&process_packet, undef);

die "Net::Pcap::loop stopped due to an unexpected error\n" if ($rv == -1);
#------------------------------------------------------------------------------

sub process_packet {
  our $cfg;

  my $user_data = shift;
  my $meta_data = shift;
  my $packet    = shift;

  #----------------------------------------------------------------------------
  # Get IP header length.  IP headers can be variable in length though they
  # are typically 20 bytes.  The first byte in the header contains the version
  # and length.
  #----------------------------------------------------------------------------
  my $ver_len_fld = ord substr($packet,$cfg->{'ether_hdr_len'},1);
  my $ip_hdr_len  = (($ver_len_fld & 0xF) * 32) / 8;
  
  #----------------------------------------------------------------------------
  # The RAW socket needs to be created, but only once.  The first time through
  # parse the packet and pull out interesting bits.  The only thing that is 
  # really needed is the destination IP address.
  #----------------------------------------------------------------------------
  if (!exists $cfg->{'sock'}) {
    #--------------------------------------------------------------------------
    # Get source/destination IPs and ports
    #--------------------------------------------------------------------------
    print "Source loc: $cfg->{'ether_hdr_len'}+$cfg->{'ip_source_loc'}\n";
    my $source_int = hex unpack(
      "H*",substr($packet,$cfg->{'ether_hdr_len'}+$cfg->{'ip_source_loc'},4)
    );

    my $dest_int = hex unpack(
      "H*",substr($packet,$cfg->{'ether_hdr_len'}+$cfg->{'ip_source_loc'}+4,4)
    );
  
    my $source_port = hex unpack(
      "H*",substr($packet,($cfg->{'ether_hdr_len'}+$ip_hdr_len),2)
    );

    my $dest_port = hex unpack(
      "H*",substr($packet,($cfg->{'ether_hdr_len'}+$ip_hdr_len+2),2)
    );
  
    #--------------------------------------------------------------------------
    # In the raw packet the source and destination IPs are in long integer 
    # format.  Convert that to dotted decimal.
    #--------------------------------------------------------------------------
    my $source_ip = int_to_ip($source_int);
    my $dest_ip   = int_to_ip($dest_int);
    #--------------------------------------------------------------------------

    #--------------------------------------------------------------------------
    # Get the RTP timestamp
    #--------------------------------------------------------------------------
    my $timestamp = hex unpack(
      "H*",
      substr(
        $packet,($cfg->{'ether_hdr_len'}+$ip_hdr_len+$cfg->{'udp_hdr_len'}+4),4
      )
    );
    #--------------------------------------------------------------------------

    print "Src: $source_ip:$source_port / Dst: $dest_ip:$dest_port / ",
      "TS: $timestamp\n";

    #--------------------------------------------------------------------------
    # Create the RAW socket.  This will allow us to take the captured packet
    # and put it back on the wire.
    #--------------------------------------------------------------------------
    socket($cfg->{'sock'}, AF_INET, SOCK_RAW, 255) || 
      die "Socket creation failed ($!)\n";
    setsockopt($cfg->{'sock'}, 0, 1, 1);

    #--------------------------------------------------------------------------
    # For this type of socket you have to specify the destination address, so
    # pack and store that for later.
    #--------------------------------------------------------------------------
    $cfg->{'dest'} = pack('Sna4x8', AF_INET, $dest_port, $dest_ip);
  }

  #----------------------------------------------------------------------------
  # Keep count of the packets we've seen.  We'll use this to decide when to
  # jump the timestamp.
  #
  # Print the value so we know how much longer until the jump
  #----------------------------------------------------------------------------
  $cfg->{'count'}++;
  print "Count: $cfg->{'count'}\r";
  #----------------------------------------------------------------------------

  #----------------------------------------------------------------------------
  # After 20,000 packets are seen, modify the RTP timestamp to see if the
  # issue can be reproduced.
  #----------------------------------------------------------------------------
  if ($cfg->{'count'} >= 20000) {
    #--------------------------------------------------------------------------
    # Get the timestamp of the current packet
    #--------------------------------------------------------------------------
    my $timestamp = hex unpack(
      "H*",
      substr(
        $packet,($cfg->{'ether_hdr_len'}+$ip_hdr_len+$cfg->{'udp_hdr_len'}+4),4
      )
    );
    my $ts = $timestamp + 3000000000;

    #--------------------------------------------------------------------------
    # Update the RTP timestamp field in the packet with the new value.
    #--------------------------------------------------------------------------
    substr(
      $packet,
      ($cfg->{'ether_hdr_len'}+$ip_hdr_len+$cfg->{'udp_hdr_len'}+4),
      4,
      pack("N",$ts)
    );

    #--------------------------------------------------------------------------
    # Alert that the jump has occurred.
    #--------------------------------------------------------------------------
    if (!$cfg->{'seen'}) {
      print "TS: $timestamp - New TS: $ts -- Epoch: ",time(),"\n\n";
      $cfg->{'seen'} = 1;
    }
  }

  #----------------------------------------------------------------------------
  # Strip the original Ethernet header off of the packet.  This machine will
  # apply its own Ethernet header.
  #----------------------------------------------------------------------------
  my $p = substr($packet,$cfg->{'ether_hdr_len'});
  #----------------------------------------------------------------------------

  #----------------------------------------------------------------------------
  # Send the manipulated packet on to its destination
  #----------------------------------------------------------------------------
  send($cfg->{'sock'}, $p, 0, $cfg->{'dest'});
  #----------------------------------------------------------------------------

  #print_dump($packet);
}

#------------------------------------------------------------------------------
# Convert and integer based IP to dotted decimal notation
#------------------------------------------------------------------------------
sub int_to_ip {
 my $int = shift;

 return join(".",unpack('C4',pack('N',$int)));
}

#------------------------------------------------------------------------------
# This routine does a side-by-side hex and ascii dump of the packet contents.
# It is only used for troubleshooting.  Courtesy of John Jetmore
#------------------------------------------------------------------------------
sub print_dump {
  my $packet = shift;

  my $n = length($packet);

  print "Packet length: $n\n";

  my $c = 0;  # counter
  my $i = 16; # increment value
  my $s = ''; # the subject string

  while ($packet && ($packet =~ s|^(.{1,$i})||smi)) {
    $s = $1;
    my @c = map { ord($_); } (split('', $s));
    $s =~ s|[^\x21-\x7E]|.|g;

    my $hfs = ''; my $hc = 0;
    for (my $hc = 0; $hc < $i; $hc++) {
      $hfs .= ' ' if (!($hc%4));
      if ($hc < scalar(@c)) { 
        $hfs .= '%02X '; 
      } else { 
        $hfs .= '   '; 
      }
    }

    printf("%04d:$hfs  %-16s\n", $c, @c, $s);
    $c += $i;
  }
 
  print "\n";
}

In the end, causing the RTP timestamp to jump did not have any impact on the call audio. At least now we know to move on and look for other causes.

Devil's Path

David and I did our annual hiking trip back in September. It was absolutely the hardest terrain we've ever covered. We were hard pressed to do seven miles a day. I can only imagine what it would have been like if I hadn't made significant strides in reducing my pack weight.

In a previous post I wrote about my plans to reduce pack weight. I bought the do-it-yourself G4 pattern from Quest Outfitters. I think a sensible person would have just bought the pre-made pack from Gossamer Gear. The guys in the how-to lightweight backpack books talk about how great it is to make your own equipment, and I guess I drank the Kool-Aid. The problem was I had never sewn anything with a machine; for that matter I didn't even own one. But that wasn't a problem Wal-Mart was only half an hour away. To learn my way around the machine I made some stuff sacks. I didn't have a pattern for them, so I just modeled them after store bought stuff sacks, and guessed at the best way to make the seams. To my surprise they turned out pretty good. After gaining some confidence, I immediately started procrastinating and didn't return to the project for 10 months. In fact, I didn't try to sew the backpack until the week before we were supposed to go on the trip.

It turns out sewing isn't that hard -- at least the kind of sewing I needed to do. Making the pack was a piece of cake. The instructions were pretty good, and for those few times where they were lacking I found a Yahoo! Group with a lot of great information about constructing the pack. In the end, it took 25 hours; I only made a couple of mistakes; and the pack turned out great.

My goal was to get my base pack weight down to 10lbs. It has been around 23lbs on previous hikes with a total weight in the 45lb range. I know, WTF, right? My store bought pack weighed a whopping 5.56lbs -- that's with nothing in it! The pack I sewed weighed 15 ounces. Let me say that again -- My original pack weighed 89oz; the pack I sewed, 15oz. I don't have specifics about what else I cut, but I can tell you I weighed every single item down to the ounce and sometimes the gram and cut everywhere I could. When all was said and done I ended up with a base pack weight of 11.5lbs. Just shy of my 10lb goal, but respectable none-the-less. Clothes added 3.33lbs; food added 5.43lbs; and water (1 liter) added 2.5lbs. I also carried one liter of wine the first day, and 500ml the second. This brought my total pack weight to 25lbs.

I tried several new things. For clothes I only had two sets: on-trail, and off-trail. We got a lot of rain on this trip, so I ended up spending most of my on-trail time wet. With water I took a chance and never carried more than one liter at a time. When I knew we were getting close to stopping for the day I would fill up a 2 liter bladder and top off my bottle. Though there were times on the third day where water was scarce it ultimately worked out pretty well.

As I said this was some of the hardest terrain we've done. The first day was short. As usual, we were late getting on the trail -- it is just something that can't be helped when you're with David. The down pour started immediately. We came to one lean-to within an hour, but felt like it was too early to stop, so we continue on to the next one. It wasn't that far away, but it was all up hill. By the time we got there it was dusk. I wore my "breathable" rain gear (jacket and pants), but I was so wet from sweating that I'm sure I would have been just as good off without them. Of course, this is fall in the Catskills so once we stopped, chill set in pretty quick. We had a few housekeeping items to attend to. David took the task of getting water -- there was a spring about a 100 yards from the lean-to. I found a place to hang the food bags and got it setup. Once this was out of the way I changed into my evening wear and was able to warm up.

A lot of times lean-tos have log books that the guests contribute to. This can be a source of good information, humor, etc. Shortly after we got there we started hearing really weird noises from a variety of positions somewhere out in front of the lean-to. This got mine and David's dander up. David had a keen interest in the noise so I decided to go to sleep. Once I started snoring David figured, "If Jamie's not worried, then I guess I can go ahead and go to sleep too." The problem was I was only sleep soundly because I knew David was keeping watch. Once I knew David had laid down I was no longer able to sleep as peacefully. The weird random noises go on all night. We had no idea what it was, but it sounded little so we didn't know if a bigger momma or daddy something was going to show up at some point. The next morning David notices the log book and starts to read it. It was at this point we found out it was just a Porcupine. If only we had read that the night before!

The first half of the second day was all downhill; it was steep, but not unruly. We came across a large pond at the bottom. There was a day use area with tables. It is a luxury not often found on the trail so we decided to eat lunch there.

The next segment was uphill, and pretty steep (or so we thought). On the way up we came across a couple of guys who were witnessing for a woman doing hikes for the 3500 club. Once at the top it was an easy trek across the ridge. This was the last time anything would be easy, and we soon learned why the trail we was called Devil's Path. Shortly there after, we vowed never to hike a trail with "Devil" in the name. After starting the descent, we came across an abandoned pack (the first of two). The trail was brutal. It took us over two hours to do less than a mile. It was just getting dark as we got down. As luck would have it a stream crossed the trail about a half mile from the lean-to; we filled up for the night.

It was approaching seven when we got to the lean-to. We built a fire and made dinner. Sometime after dark set in we started hearing a couple of guys approaching from the opposite direction that we came from. It was over two hours before they reached the lean-to. The guys got caught off guard by how difficult the hike was. They came down some pretty treacherous stuff in pitch black. Hiking up it the next day, I have no idea how they did it without getting hurt. When they showed up at the lean-to they were exhausted and had been out of water for some time. Fortunately, we were fully stocked. I'm not sure about David, but I sort of felt like Ned from The Three Amigos. The scene when they are in the desert Lucky goes to drink from his canteen and it is empty. Dusty's next and he gets a mouth full of sand. Meanwhile Ned drinks 'til his heart is content and tosses the remaining water on the ground. In our case, we had a nice fire going; we were sitting back having finished a spaghetti and meatball dinner enjoying our remaining wine. Unlike the oblivious Ned we were happy to share.

The hike the next day was grueling. Straight up. Straight down. All rocks. For a while I was confused, I thought we went on a hiking trip, not a rock climbing adventure. The guys had pointed out water was scarce, but with a little ingenuity we were able to get what we needed. Sometime in the afternoon we came to a fork in the road. In one direction we could hike out, in the other a path leading to Devil's Kitchen lean-to. After some napkin math we determine we probably couldn't make it to the lean-to by dark, so we headed out.

View the photo album for this entry.

Photo Album Link Clearance

I used to try and write a blog entry per photo-album, but lately I've got lazy. So instead you get the photo-album link clearance blog entry.

Apple picking @ Hurd's Orchard

Old Time Fiddler's Convention in Athens, AL

Baltimore

Christmas @ Jen and Rob's

I Often Misread Things

I often misread things ... today I saw the headline

Practical Steps to Mitigate Virtualization Security Risks
I read it as
Practical Steps to Mitigate Virtual Security Risks
and I'm thinking to myself why would anyone be worried about "virtual" security risks?

You Have To Wait For It

I just ran across this video. There is a pretty funny bit just over two minutes in, but the best part is at the end.

New Years

When I was eight, Prince released 1999. This song suggested New Years Eve 1999 was going to be something for the story books. We would usher in a new century, and in the process, we would send off the previous one with a party unparalleled. I was (to quote Paul Simon) "born at the right time" -- when 1999 rolled around I’d just be hitting my mid-twenties.

In the eighth grade I decided that I wanted to be an engineer. Initially, I thought chemical engineer, but after I discovered computers it quickly changed to computer engineer. In college I got hooked on programming and networking. It became clear I wasn't going to be mucking about with circuit level design -- Unix, routers, and programming was the life for me.

This proved to be a fateful decision. At work we kicked around the Y2K issue for a couple of years, but it wasn't until mid-1999 that we started to take it seriously. PERL and C were our primary languages. The biggest problem was with our home grown code. No-one used the localtime function properly (guilty). It returns an array including two digits that represent the year. In the 1900s this representation looked like the last two digits of a four digit year. Who needs documentation -- it's pretty clear what the author was going for, right? In actuality, this value is the number of years since 1900. So we had a bunch of code that would concatenate "19" with the value returned from the localtime call and thus we had a 4-digit year, or so we thought. In 1999 this logic would yield 1999. In 2000 this logic would yield 19100. We had worse things than this, but they are too embarrassing to talk about openly. Let's just say it involved actually trying to account for the century change and failing miserably. The funny thing is all this bad, convoluted logic was replaced with the simplest of things (once we read the documentation) -- just add 1900 to the value returned by localtime and viola.

I had two main contributions to the effort: I wrote a program to search through all of the files on our servers and identify offending PERL code. And I went through the source code for smail our MTA at the time and confirmed it was Y2K compliant. Others were not so lucky. I recall Jonathan doing testing for one of our online banking customers. He borrowed an old RS/6000 from the bank and spent weeks running through scenarios -- talk about tedious.

I can't say when it was, but probably sometime in October 1999 it become clear there wasn't going to be a party comes New Years Eve. That's a helluva birthday present. You probably had a grand ole time that New Years, while I and a lot of others in the tech industry spent the night sober, bored, and missing out. How could I know that my fate was sealed all the way back in the eighth grade when I thought, "I want to be an engineer when I grow up."

Did you know in 2038 we get to repeat this whole exercise all over again? Right now this one scares me, but 2038 is a long way off ... so who knows. Hopefully we won't have too many of these pesky 32-bit machines around by then, or better yet I'll be retired.

Ever since that night on the turn of the century when I missed the party to end all parties New Years just hasn't been the same for me. I can count the number of times I've stayed up until midnight on two fingers. I'll be in dreamland come midnight, but have a drink for me anyway. And don't forget this year is one second longer, so don't go getting premature on me.

Happy Frickin' New Year!

Donald Paul Holland

Anniston - A Memorial Service for Mr. Donald Paul Holland, 72, will be held at 11 a.m. on Saturday, December 13, 2008, at McCoy United Methodist Church with Pastor Mary Quillin and Sandy Aids officiating.

Mr. Holland died Wednesday, December 10, 2008, at Northeast Alabama Regional Medical Center.

Mr. Holland was retired from Baptist Health Systems. He was also a member of the Elks Club, a loyal Alabama fan, and of the Methodist faith. He is preceded in death by his parents, Mr. and Mrs. Dan Holland; his sister, Rose White; a brother, Daniel Holland; and a stepdaughter, Angela M. Hill.

Mr. Holland is survived by his wife of 25 years, Gloria B. Holland; daughter, Angela Parton; brotherin-law, Luther L. White; a granddaughter, Paisley Parton; three grandsons, Reid Parton, Jeromy Hill, and James P. Hill and his wife, Ali; great-grandson, Cameron Hill; several nieces and nephews; and a host of loving friends.

Honorary pallbearers will be E. Witte Aids, Marty White, Luther White, Jimmy Fox, Glenn Jones, Mike Smith, Charles Cox and Jeromy Hill.

The family requests memorials be made to a favorite charity.

Source: Anniston Star

o' mice an' men

Sometimes even the worst laid plans go awry. I find myself stuck in the Cincinnati airport as I try to make my way to Alabama. A hastily thrown together itinerary this morning left me with just enough time to make my flight from Albany.

Now I'm sitting @ Moe's Pub wishing I was driving to Anniston. I just want to be there. To steal a line, "The lord may take, but I can give, I can still give." I can give -- solace, support, love. I want to be there for my Granby, and I want her there for me.

In case none of this makes sense -- My Grandfather has been having health problems for some time now. Yesterday he took an unexpected turn for the worst ...

Walton Warriors

Across the street from the booth I usually occupy at McDonald's, I've noticed three signs on a telephone pole: "CLEAR EYES" "FULL HEARTS" "CAN'T LOSE"


FNL

We may not be in West Texas, but listening to people talk around here you get the impression football is every bit as important.

The Warriors' championship bid ended prematurely a couple of weekends ago, and just a few days later the signs were gone.

I'm not one to really follow sports, but I did like the imagery of the signs. I am a fan of Friday Night Lights, and maybe this is more a testament to the way the show is shot. The same stark contrast I see in the show, I can see in Walton when I look out the window. Maybe that is just what the show intends; that everyone can see the fictional Dillon in their surroundings -- bring your own small town (of course).

copyright © 2004-2009 | mt | license jamie@stratusnine.com