ERSPAN to PCAP script

From Cisco Clue

Jump to: navigation, search

6500s can SPAN (port or vlan mirror) to GRE encapsulation, allowing mirroring over multiple layer3 hops. Very useful

This feature is ESPECIALLY useful on 6500s - see 6500 SPAN the RP - allowing you to use the *hardware* to mirror the traffic that goes to the CPU

The following script runs on Linux (and possibly other Unices). It captures the GRE traffic and either outputs a summary or, if the -pcap argument is given, outputs an ethernet-style PCAP data stream (which can be piped to "tcpdump -r -")

This script is Licensed under the GPL version 2 ONLY

The script:

 
#!/usr/bin/python
#
# Copyright 2007 - Phil Mayers phil dot mayers at gmail dot com
# This software is licensed under the GPL version 2 ONLY

# For the latest public version, check:
#  http://cisco.cluepon.net/index.php/ERSPAN_to_PCAP_script

#    This program is free software; you can redistribute it and/or modify
#    it under the terms of version 2 of the the GNU General Public License
#    as published by the Free Software Foundation
#
#    This program is distributed in the hope that it will be useful,
#    but WITHOUT ANY WARRANTY; without even the implied warranty of
#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#    GNU General Public License for more details.
#
#    You should have received a copy of the GNU General Public License
#    along with this program; if not, write to the Free Software
#    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA

# This program opens a GRE socket and received Cisco ERSPAN (port mirror
# over GRE) packets, and either outputs a one-line summary or if the -pcap
# command line argument is given, outputs an "ethernet" pcap stream which
# can be written to a file, or piped to "tcpdump -r -"

# This program contains a number of assumptions that are probably only valid
# in simple cases. There are a number of fields in the ERSPAN header that
# undoubtedly indicate non-ethernet packets (e.g. for POS cards on 7600s)
# but I don't have the hardware to test it, so can't verify that. It would
# be wonderful if Cisco documented (or even RFCed!) the ERSPAN format. Then,
# wireshark could implement it natively and it would all be good



from socket import *
import struct
import time
import sys

# magic, major_ver, minor_ver, gmt_offset, sigfigs, snaplen, captype
PCAP_FILE_MAGIC = 0xa1b2c3d4L
PCAP_FILE_MAJOR = 2
PCAP_FILE_MINOR = 4
PCAP_FILE_HEADER = "IHHiIII"
PCAP_FILE_HEADER_LEN = struct.calcsize(PCAP_FILE_HEADER)
PCAP_RECORD_HEADER = "llII"
PCAP_RECORD_HEADER_LEN = struct.calcsize(PCAP_RECORD_HEADER)

def parseIP(d):
        # parse an IP packet
        b0 = ord(d[0])
        version = b0 >> 4
        if version!=4:
                return {}
        hlen = b0 & 0xf
        hlen = hlen * 4
        if hlen<20:
                return {}
        tos, dlen, id, flags, ttl, protocol, cksum = struct.unpack('>BHHHBBH', d[1:12])
        src, dst = [inet_ntoa(x) for x in (d[12:16], d[16:20])]
        offset = flags & 0x1fff
        df = flags & 0x4000
        mf = flags & 0x2000
        if hlen>20:
                options = d[20:hlen-20]
        else:
                options = ''
        payload = d[hlen:]
        del d
        return locals()

s = socket(AF_INET, SOCK_RAW, IPPROTO_GRE)
if '-pcap' in sys.argv[1:]:
        pcap = True
        sys.stdout.write(struct.pack(PCAP_FILE_HEADER, PCAP_FILE_MAGIC, PCAP_FILE_MAJOR, PCAP_FILE_MINOR, 0, 0, 65535, 1)) #pcap.DLT_EN10MB))
else:
        pcap = False

while True:
        d, (ssrc ,port) = s.recvfrom(65535)
        now = time.time()
        sec = int(now)
        usec = int((now-sec)*1000000)
        pkt = parseIP(d)
        if not pkt:
                continue
        elif pkt['protocol']!=47:
                continue
        elif len(pkt['payload'])<8+14+20:
                # unknown+ether+ip
                continue
        # GRE
        hdr, data = pkt['payload'][:4], pkt['payload'][4:]
        f, proto = struct.unpack('>HH', hdr)
        f_1 = (f & 0xf000) >> 12
        f_2 = (f & 0x0f00) >> 8
        flg = (f & 0x00ff) >> 5
        ver = (f & 0x0007)

        if f_1 & 0x8:
                cksum_present = True
        else:
                cksum_present = False

        if f_1 & 0x4:
                routing_present = True
        else:
                routing_present = False

        if f_1 & 0x2:
                key_present = True
        else:
                key_present = False

        if f_1 & 0x1:
                seq_present = True
        else:
                seq_present = False

        if cksum_present or routing_present:
                cksum, offset = struct.unpack('>HH', data[:4])
                data = data[4:]
        else:
                cksum = 0
                offset = 0

        if key_present:
                key = struct.unpack('>I', data[:4])
                data = data[4:]
        else:
                key = 0
        if seq_present:
                seq, = struct.unpack('>I', data[:4])
                data = data[4:]
        else:
                seq = 0


        unknown, rest = data[:8], data[8:]
        if pcap:
                sys.stdout.write(struct.pack(PCAP_RECORD_HEADER, sec, usec, len(rest), len(rest))+rest)
                sys.stdout.flush()
                continue
        ethdst = ':'.join(['%02x' % ord(x) for x in rest[:6]])
        ethsrc = ':'.join(['%02x' % ord(x) for x in rest[6:12]])
        ethtype = ( ord(rest[12]) << 8 ) + ord(rest[13])
        inner_payload = rest[14:]
        if ethtype==0x800:
                inner_pkt = parseIP(inner_payload)
                if not inner_pkt:
                        msg = '- - -'
                else:
                        msg = '%(dst)s %(src)s %(protocol)i' % inner_pkt
        else:
                msg = '* * *'

        print ssrc, seq, ':'.join(['%02x' % ord(x) for x in unknown]), ethdst, ethsrc, hex(ethtype), msg
Personal tools