Index:


Programming

  • print_rfc_or_draft

    This is a simple program that prints an Internet Draft (ID) or RFC.

     

    Because of the difference between the number of lines on laser printers and line / dot-matrix printers, each “page” of the draft actually takes up 2 pages and you end up with lots of pages with just a one line footer.

    This script tries to fix that by downloading the draft, converting it to a PDF and then printing it.

     

      This script prints an Internet Draft (or RFC). It does this by
      reading the file (possibly first fetching it from the Internet),
      converting it to a PDF (and doing basic munging on it and then
      sending the PDF to lpr to actually do the printing.
    
      Usage:
        -F, --file <filename>  -- print this local file.
        -O, --outdir <dir>     -- save the converted PDF in this dir.
        -h, --help             -- this help.
        <url>                  -- fetch (and print) the file at the URL.
      Example:
          ./print_rfc_or_draft.py  http://tools.ietf.org/rfc/rfc5635.txt
             Fetches and prints RFC5635 from the ietf.org site and prints it.
    
          ./print_rfc_or_draft.py -F ~wkumari/docs/IETF/MyDrafts/deprecate-as-sets-00.txt
             Prints the local file called deprecate-as-sets-00.txt.
    

     

    Source is: print_rfc_or_draft.py

  • Bash: Getting process exit status

    In order to get the exit status for a process:

    echo $?

  • C: Timestamping a received packet

    Sometimes it is useful to be able to tell exactly when a packet arrives. This is especially important for network monitoring applications — if a packet arrives when the monitoring machine is busy, your latency measurements get skewed.

    Linux stamps the time each packet was received and makes the information available through an ioctl – most other OS have a similar method.

     

    Here is how to get at that info:

    /****************************************************************************
    * timestamp_packet.
    *
    * This code is a quick and dirty test of getting the timestamp a packet was
    * recieved by the kernel using IOCTL SIOCGSTAMP
    *
    * This code based upon example code in the "Unix Network Sockets Programming"
    * books.
    *
    ***************************************************************************/
    #include <sys/types.h>
    #include <sys/socket.h>
    #include <asm/sockios.h>
    #include <netinet/in.h>
    #include <netdb.h>
    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    #include <sys/time.h>
    #define MAX_PACKET_SIZE 2048
    /****************************************************************************
    * error
    * Write an error message and aborts..
    ***************************************************************************/
    void error(char *msg)
    {
    perror(msg);
    exit(0);
    }
    /****************************************************************************
    * get_time
    *
    * Gets the time using both gettimeofday and IOCTL and prints delta.
    *
    * Returns:
    * 1 on success.
    ***************************************************************************/
    int get_time (int sock, char * packet, uint length) {
    // Places to store the timestamps.
    struct timeval tv_tod, tv_ioctl;
    int error;
    // Get the time packet was recieved with usec resolution
    gettimeofday (&tv_tod, 0);
    // and now use the IOCTL to get the time the packet was stamped by the kernel.
    error = ioctl(sock, SIOCGSTAMP, &tv_ioctl);
    printf ("gettimeofday: %d.%d\t ioctl: %d.%d\t delta: %d.%d\n",
    tv_tod.tv_sec, tv_tod.tv_usec,
    tv_ioctl.tv_sec, tv_ioctl.tv_usec,
    tv_tod.tv_sec - tv_ioctl.tv_sec,
    tv_tod.tv_usec - tv_ioctl.tv_usec
    );
    return 1;
    }
    /****************************************************************************
    * main
    * Parses arguments, opens port.
    * Takes:
    * A port number to listen on.
    * Returns:
    * 1 on success.
    ***************************************************************************/
    int main(int argc, char *argv[])
    {
    int sock, length, fromlen, n;
    struct sockaddr_in server;
    struct sockaddr_in from;
    char buf[MAX_PACKET_SIZE];
    unsigned char pending_packets=0;
    // Parse out the prot number. Abort if no port given.
    if (argc < 2) {
    fprintf(stderr, "Useage:\n %s port\n", argv[0]);
    exit(0);
    }
    // Standard create and listen on a UDP port snippet.
    sock=socket(AF_INET, SOCK_DGRAM, 0);
    if (sock < 0) error("Opening socket");
    length = sizeof(server);
    bzero(&server,length);
    server.sin_family=AF_INET;
    server.sin_addr.s_addr=INADDR_ANY;
    server.sin_port=htons(atoi(argv[1]));
    if (bind(sock,(struct sockaddr *)&server,length)<0)
    error("binding");
    fromlen = sizeof(struct sockaddr_in);
    // Loop, getting packets, parsing the header and doing stuff...
    while (1) {
    n = recvfrom(sock,buf,MAX_PACKET_SIZE,0,(struct sockaddr *)&from,(socklen_t *)&fromlen);
    if (n < 0) error("recvfrom");
    get_time (sock, buf, n);
    }
    }

     

  • Perl: Generating TACACS+ reports

    I used to work at a place where there were many network outages caused by network engineers doing poorly planned maintenance. There was a change -management process, but it was so onerous that people tried to get around it (also, most of the engineers figured that none of their changes could possibly cause an outage). Things got so bad that we had to run a daily report against the TACACS logs to look for mavericks (and to try and figure out who decided that “ip classful” or OSPF virtual links or …  was a good idea).

     

    This icky piece of Perl generates reports from TACACS+ logs — I think that it only works correctly from Cisco and lookalikes (eg. Foundry), Juniper accounting log formats are somewhat different. I cannot share that as I wrote it while employed by my current employer, but the changes are trivial.

    #!/usr/bin/perl
    use IO::Socket;
    while () {
    ($datetime, $device, $name, $cmd) = (split (/\t/))[0,1,2,11];
    # Ignore "sho running config" and "ping" if done by monitoring
    # Change this to be whatever user you use for monitoring.
    unless (($name =~ /monitoring/)&&(($cmd =~ /ping/)||($cmd =~ /running-con/)||($cmd =~ /terminal leng/))) {
    if ($cmd =~ /cmd/) {
    $cmd =~ s/cmd=//; # Take out the annoying "cmd="
    $cmd =~ s///; # and the
    chomp ($cmd);
    $machine = gethostbyaddr(inet_aton($device), AF_INET);
    unless ($machine) { $machine = $device; } # If cannot loook up name, $machine gets the IP
    $machine =~ s/\.example\.com//; # Fill in your domain here.
    write;
    }
    }
    }
    # Example log line:
    #Fri Jan 17 16:32:18 2003 core1.example.com: wkumari did show running-config
    format STDOUT =
    @<<<<<<<<<<<<<<<<<<<<<<<< @<<<<<<<<<<<<<< @<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< @<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
    $datetime, $name, $machine, $cmd
    .
    format STDOUT_TOP =
    Page @<<<<
    $%
    Date and Time User Device Command
    ========================= =============== ================================ ========================================
    .

  • Python: Setting up logging.

    I like the Python logging module, but for some reason I always have issues remembering how to set it up. This is my cheat sheet:

    import logging
    logger = logging.getLogger('App Name')
    def SetupLogging():
    logger.setLevel(logging.DEBUG)
    #create console handler and set level to debug
    ch = logging.StreamHandler()
    ch.setLevel(logging.DEBUG)
    #create formatter
    # was:%(asctime)s - %(name)s - %(levelname)s: %(message)s"
    formatter = logging.Formatter("%(levelname)s(%(name)s): %(message)s")
    #add formatter to ch
    ch.setFormatter(formatter)
    #add ch to logger
    logger.addHandler(ch)
    logger.error("Whoops, an error!")
    logger.debug ("Doing something boring")

    The formatter options are:

    %(name)s            Name of the logger (logging channel)
    %(levelno)s Numeric logging level for the message (DEBUG, INFO,
    WARNING, ERROR, CRITICAL)
    %(levelname)s Text logging level for the message ("DEBUG", "INFO",
    "WARNING", "ERROR", "CRITICAL")
    %(pathname)s Full pathname of the source file where the logging
    call was issued (if available)
    %(filename)s Filename portion of pathname
    %(module)s Module (name portion of filename)
    %(lineno)d Source line number where the logging call was issued
    (if available)
    %(created)f Time when the LogRecord was created (time.time()
    return value)
    %(asctime)s Textual time when the LogRecord was created
    %(msecs)d Millisecond portion of the creation time
    %(relativeCreated)d Time in milliseconds when the LogRecord was created,
    relative to the time the logging module was loaded
    (typically at application startup time)
    %(thread)d Thread ID (if available)
    %(process)d Process ID (if available)
    %(message)s The result of record.getMessage(), computed just as
    the record is emitted

     

  • Python: Making a dictionary of lists (a hash of arrays!)

    One of the standard Perl tricks is making a hash of arrays (pun intended!).

    In Python this is a little trickier.

    For example, you read in a file of the form: device location — you will often need to to something like get all of the devices in New York, so you want to be able to do something like:

    ny_devices = device_list["ny"]

    The first thing you try is:

    (device, location) = line.split(" ")
    device_list[location].append(device)

    This dies with: KeyError: ‘ny’ — this is because the key “ny” doesn’t exist yet.

    One way to deal with this is:

    (device, location) = line.split(" ")
    if location not in device_list:
    # Add empty list for location
    device_list[location] = []
    device_list[location].append(device)

    A much cleaner way to do this is:

    device_list.setdefault(location,[]).append(device)