Generating Layer 3 Interface DNS records for cisco devices

Traceroute looks up the PTR dns records for each hop. A lot of providers create ptr records that describe the layer 3 interface of a given router:

akonkol@use:~$ traceroute 8.8.8.8
traceroute to 8.8.8.8 (8.8.8.8), 30 hops max, 60 byte packets
 1  router1-dal.linode.com (67.18.7.161)  0.451 ms  0.568 ms  0.698 ms
 2  ae2.car01.dllstx2.networklayer.com (67.18.7.89)  0.184 ms  0.215 ms  24.803 ms
 3  po101.dsr01.dllstx2.networklayer.com (70.87.254.73)  0.667 ms  0.726 ms  1.026 ms
 4  po21.dsr01.dllstx3.networklayer.com (70.87.255.65)  0.793 ms  0.860 ms  0.911 ms
 5  ae16.bbr02.eq01.dal03.networklayer.com (173.192.18.228)  0.470 ms  0.451 ms  0.456 ms
 6  ae7.bbr01.eq01.dal03.networklayer.com (173.192.18.208)  0.440 ms  0.437 ms  0.417 ms
 7  50.97.16.37 (50.97.16.37)  0.486 ms  0.512 ms  0.491 ms
 8  72.14.233.85 (72.14.233.85)  0.555 ms 72.14.233.77 (72.14.233.77)  10.054 ms  10.044 ms
 9  64.233.175.148 (64.233.175.148)  7.835 ms 72.14.237.219 (72.14.237.219)  3.690 ms  3.676 ms
10  209.85.249.69 (209.85.249.69)  7.489 ms 72.14.237.123 (72.14.237.123)  7.450 ms 209.85.249.66 (209.85.249.66)  7.409 ms
11  216.239.46.39 (216.239.46.39)  7.366 ms  7.332 ms 216.239.46.63 (216.239.46.63)  7.357 ms
12  * * *
13  google-public-dns-a.google.com (8.8.8.8)  7.410 ms  7.436 ms  7.418 ms

Depending on how much equipment you manage you could have hundreds or even thousands of layer 3 interfaces. This problem intrigued me so I wrote a script that leverages tratto; a framework that I built back in 2012 to ssh/telnet to devices. The script is called l3toptr.py which gets layer 3 interface information and constructs a fqdn.

Connection Code

The following snippet shows a few things: setting up the ssh session, issuing a "show ip interface brief" storing the results in "ip_ints."

#connection setup
import Connectiviy, Systems

device = args['device']
username = args['username']
os_type = Systems.OperatingSystems['IOS']


session = Connectivity.Session(device,port,transport,os_type)
session.login(username, password)
ip_ints = session.sendcommand("sho ip int br")
session.logout()

Parsing Code

We take ip_ints and for each line that contains an ip address, we split the line by "whitespace" which allows us to access the interface name and number separately allowing us to assign these values to int_name and int_ip. Based on the interface name we come up with an abbreviation: GE for gigabit interfaces, FE for fast ethernet interfaces,etc... Since DNS records do now allow for slashes, we replace those with hyphens and assign it to clean_suffix.

#for each line of sho ip int brief

for line in ip_ints.split('\r\n'):
   contains_ip = re.findall( r'[0-9]+(?:\.[0-9]+){3}', line )
   if contains_ip:
        line_chunks = line.split()

        #[0]= gigabitethernet1/2
        int_name = line_chunks[0]

        #[1] = 10.a.b.c
        int_ip = line_chunks[1]

        if "GigabitEthernet" in int_name:
           prefix = "GE"
           suffix = int_name[15:]

        if "FastEthernet" in int_name:
           prefix = "FE"
           suffix = int_name[12:]

        if "Loopback" in int_name:
           prefix ="LO"
           suffix = int_name[8:]

        if "Vlan" in int_name:
           prefix="VL"
           suffix=int_name[4:]

        if "Tunnel" in int_name:
           prefix="TU"
           suffix= int_name[6:]

        #ignore NVIs
        if "NVI" in int_name:
          break

        #replace interface number slash with hyphen
        clean_suffix = re.sub('\/','-',suffix)

Schema and Return Code

The remaining code checks for any user defined schema. There are three variables you can specify in this "schema" (or format string): interface_name, interface_number, hostname, and ip_address. You would specify something like this to use a custom fqdn format: ./l3toPTR.py -u akonkol -d 10.24.2.1 -s "interface_name.hostname.mycompany.com" and interface_name and hostname would be replaced with the dynamically learned information for device living at 10.24.2.1.

if args['schema']:
           schema = args['schema']
           dns_line = schema.replace('interface_name',prefix)
           dns_line = dns_line.replace('interface_number',clean_suffix)
           dns_line = dns_line.replace('hostname',hostname)
           dns_line = dns_line.replace('ip_address',int_ip)
           print dns_line
        else:
           print prefix + clean_suffix  + "." + hostname +  domainname + "," +  int_ip

Some Examples:

akonkol@echo:~/Code$ ./l3toPTR.py -u akonkol -d 10.45.1.2
Password:
GE0-0.chicagorouter02.network.exampleco.com,10.45.1.2
GE0-1.chicagorouter02.network.exampleco.com,24.140.215.81
LO10.chicagorouter02.network.exampleco.com,10.0.45.2
LO99.chicagorouter02.network.exampleco.com,24.140.215.82



akonkol@echo:~/Code$ ./l3toPTR.py -u akonkol -d 10.45.1.2 -dn konkol.com
Password:
GE0-0.chicagorouter02.konkol.com,10.45.1.2
GE0-1.chicagorouter02.konkol.com,24.140.215.81
LO10.chicagorouter02.konkol.com,10.0.45.2
LO99.chicagorouter02.konkol.com,24.140.215.82



akonkol@echo:~/Code$ ./l3toPTR.py -u akonkol -d 10.45.1.2 -s "interface_name-interface_number.mycompany.com,ip_address"
Password:
GE-0-0.mycompany.com,10.45.1.2
GE-0-1.mycompany.com,24.140.215.81
LO-10.mycompany.com,10.0.45.2
LO-99.mycompany.com,24.140.215.82

Wait a second, this isn't actually creating the DNS entries! You would be correct, and I'm willing to bet that if you've read this far that you can figure how to do that... There are a few ways to do this and it all depends on what you are using for your DNS server (bind, windows, etc). The above script generates a CSV output which you could loop through and use to create dns records. Here is a great article on creating ptr records in bulk on a windows dns server DNS Bulk PTR records creation.

Tagged as cisco , dns ios , ptr python , tratto
Written by Andrew Konkol on July 22nd, 2014 | 0 Comments

Using Django's Template system for network configs

I wrote hatch a few years ago to make cookie cutter network config templates. One thing I've wanted to implement for a while is a DSL for use in hatch. I've experimented with jscc and more recently with peg.js. While writing grammar for peg.js I said to myself "it would be great if I could used Django's template system and filters without re-inventing the wheel."

So I started experimenting and found out its pretty easy to do. A little more tinkering and I could possibly replace hatch's mechanics with Django's builtin template, tag, and filter code.

#!/usr/bin/python
from django.conf import settings
from django import template
settings.configure(DEBUG=True, TEMPLATE_DEBUG=True)
#https://github.com/django/django/blob/master/django/template/__init__.py

s = u'conf t\n ip address {{ip_address}} {{subnet_mask}} \n end \n write'
t = template.Template(s)
c = template.Context({'ip_address': '192.168.1.1','subnet_mask':'255.255.255.0'})
rendered = t.render(c)
print rendered

print "----------------------------------"

s = u'conf t\n ip address 10.{{site_code}}.1.1 {{subnet_mask}} \n end \n write'
t = template.Template(s)
c = template.Context({'site_code': '66','subnet_mask':'255.255.255.0'})
rendered = t.render(c)
print rendered


print "----------------------------------"

s = u'conf t\n {% if site_code == 66 %}snmp-server location Sydney{% endif%}'
t = template.Template(s)
c = template.Context({'site_code': 66})
rendered = t.render(c)
print rendered

akonkol@dev:~/Code/messy$ ./temp_hack.py
conf t
 ip address 192.168.1.1 255.255.255.0
 end
 write
----------------------------------
conf t
 ip address 10.66.1.1 255.255.255.0
 end
 write
----------------------------------
conf t
 snmp-server location Sydney

Tagged as cisco , django template , templates
Written by Andrew Konkol on January 13th, 2014 | 0 Comments

Generate IP Addresses and valid CIDR notations

I needed to test some networking tools I've been developing, and to do that I needed IP addresses. To get IP addresses I wrote this snippet.

#!/usr/bin/python

import random
def generate_ip():
   CLASSES='ABC'
   ip_class = random.choice(CLASSES)

   if ip_class == "A":
        first_octet = random.randint(1,126)

   if ip_class == "B":
        first_octet = random.randint(128,191)

   if ip_class == "C":
        first_octet = random.randint(192,223)

   second_octet = random.randint(0,254)
   third_octet = random.randint(0,254)
   fourth_octet = random.randint(0,254)
   return "%i.%i.%i.%i" %(first_octet,second_octet,third_octet,fourth_octet)

I also needed to generate random valid slash notations for a given ip:

def generate_cidr(network_id):
   first_octet = int(str(network_id).split(".")[0])

   if first_octet <= 126 and first_octet >= 1:
      cidr_bits =  random.randint(8,30)

   if first_octet >=127 and first_octet <= 191:
      cidr_bits =  random.randint(16,30)

   if first_octet >=192 and first_octet <= 223:
      cidr_bits =  random.randint(24,30)

   return cidr_bits
Tagged as cidr , IP python
Written by Andrew Konkol on October 22nd, 2013 | 0 Comments

Using common linux utilities over IPV6

I've needed to test a variety of network architectures and access-lists over the past couple of weeks.  Most linux utilities require some sort of ipv6 flag on the command-line which has been constantly challenging my muscle memory.

 

IPv6 enabled sites used for testing:

www.kame.net (if you see the swimming turtle you are accessing the site with ipv6)

2001:4860:4860::8888 (google's ipv6 equivalent of 8.8.8.8)

scanme.nmap.org (2600:3c01::f03c:91ff:fe93:cd19 host to portscan)

facebook.com (2a03:2880:2110:df07:face:b00c:0:1)

six.use.io (2600:3c02::f03c:91ff:fedf:21c3) 

 

DNS lookups:

dig -6 @2001:4860:4860::8888 facebook.com AAAA

or 

dig -6 @2001:4860:4860::8888 facebook.com AAAA +short

snmpwalk:

snmpwalk -v2c -c public udp6:[2001:0db8:85a3:0:0:8a2e:0370:99]:161 .1.3.6.1.4.1.9.9.109.1.1.1.1.4

ping/traceroute:

ping6 2001:0db8:85a3:0:0:8a2e:0370:99

traceroute6 2001:0db8:85a3:0:0:8a2e:0370:99

web browsing:

lynx http://\[2a03:2880:2110:df07:face:b00c:0:1\]

nmap:

nmap -sV -6 2600:3c01::f03c:91ff:fe93:cd19

telnet:

telnet 2a03:2880:2110:df07:face:b00c:0:1 80

ssh:

ssh 2a03:2880:2110:df07:face:b00c:0:1
Tagged as cli , ipv6 linux
Written by Andrew Konkol on September 11th, 2013 | 0 Comments

Cold Brew Fridge Coffee Bucket

I've been expierementing with different brewing vessels and I decided to make my own.  I used spare parts I had from beer brewing.

 

Bill of Materials:

Item Source Price
2 Gallon Food Grade Bucket  Brew Camp  $5.00
Bottling spigot  Brew Camp  $3.00
Fine mesh steeping bag  Brew Camp  $4.60
Step bit  NewEgg  N/A

Step one: Drill out 1" hole as close to the bottom of the bucket as possible keeping enough room for the spigot nut to have clearance.

Step two: Remove one rubber gasket from spigot nipple, slide spigot assembly into 1" hole, slide rubber gasket over the nipple and screw on the nylon nut over the nipple until hand tight

Step three: Drill small hole in lid to allow for ventilation while using spigot

Step four: Put grounds in nylon bag and tie tight, place bag in bucket, add water, let sit overnight in fridge.

Ste five: Turn spigot downward and turn handle to pour.

Tagged as brew , coffee cold , cold brew fridge
Written by Andrew Konkol on August 25th, 2013 | 0 Comments