#! /usr/bin/env python
## PyRT: Python Routeing Toolkit
## ISIS module: provides ISIS listener and ISIS PDU parsers
## Copyright (C) 2001 Richard Mortier <mort@sprintlabs.com>, Sprint ATL
## This program is free software; you can redistribute it and/or
## modify it under the terms of the GNU General Public License as
## published by the Free Software Foundation; either version 2 of the
## License, or (at your option) any later version.
## 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., 59 Temple Place, Suite 330, Boston, MA
## 02111-1307 USA
#
# $Id: isis.py,v 1.16 2002/01/17 00:14:13 mort Exp $
#
# refs: http://www.rware.demon.co.uk/isis.htm, RFC1195, RFC1142,
# This is a good deal grimmer than the BGP module since ISIS, by default on
# Ethernet/802.3 links, is encapsulated directly within the frame. As a
# consequence we need PF_PACKET and SOCK_RAW to get it -- THESE ARE ONLY
# SUPPORTED IN PYTHON >= 2.0. As a result this will not be as portable as I'd
# like. Stick to Linux 2.2.x and higher kernels with packet sockets
# (CONFIG_PACKET) enabled; I've tested on RH7.1 std. install. Also, it must
# run as root :-((
# Explanation of which bits we slurp: we are looking for ISIS packets carried
# in IEEE 802.3 frames. This means that we have the following octet layout:
# MAC header (IEEE 802.3):
# ss-ss-ss-ss-ss-ss :: <6:src MAC>
# dd-dd-dd-dd-dd-dd :: <6:dst MAC>
# ll-ll :: <2:length> == 0x05dc == 1500 (payload only)
# LLC header (IEEE 802.2):
# dsap :: <1:DSAP> == 0xfe ...by RFC1340, p53, "IEEE 802 Numbers of interest"
# ssap :: <1:SSAP> == 0xfe ...("ISO CLNS IS 8473")
# ctrl :: <1 or 2: control> == 0x03 ("unnumbered information")
# In fact, from (after some moulinexing :-)
# http://cell-relay.indiana.edu/cell-relay/docs/rfc/1483/1483.4.1.html
# In LLC Encapsulation the protocol of the routed PDU is identified by
# prefixing the PDU by an IEEE 802.2 LLC header, which is possibly followed by
# an IEEE 802.1a SubNetwork Attachment Point (SNAP) header. ... The presence
# of a SNAP header is indicated by the LLC header value 0xAA-AA-03.
# ...
# The LLC header value 0xFE-FE-03 identifies that a routed ISO PDU (see [6]
# and Appendix B) follows. The Control field value 0x03 specifies Unnumbered
# Information Command PDU. ... The routed ISO protocol is identified by a one
# octet NLPID field that is part of Protocol Data. NLPID values are
# administered by ISO and CCITT. They are defined in ISO/IEC TR 9577 [6] and
# some of the currently defined ones are listed in Appendix C.
# ...
# Appendix C. Partial List of NLPIDs
# 0x00 Null Network Layer or Inactive Set (not used with ATM)
# 0x80 SNAP
# 0x81 ISO CLNP
# 0x82 ISO ESIS
# 0x83 ISO ISIS
# 0xCC Internet IP
# ie. we have 14 octets MAC header, 3 octets LLC header, and then we are in
# the ISIS packet, starting with the NLPID 0x83. Phew.
# Note 1: AFI 49 (pfx on area code) is public CLNS space a la 10.x.x.x in IP
# Note 2: Actually, although the intro. above says this is grimmer, it is in
# fact quite a lot nicer once adjacency is established. ISIS is a much nicer
# protocol than BGP which sucks high vacuum.
import sys, getopt, socket, string, os.path, struct, time, select, fcntl, math
from mutils import *
#-------------------------------------------------------------------------------
VERSION = "1.0"
INDENT = " "
RETX_THRESH = 1
RCV_BUF_SZ = 2048
MAC_PKT_LEN = 1514
MAC_HDR_LEN = 17
ISIS_PKT_LEN = 1500
ISIS_PDU_LEN = ISIS_PKT_LEN-3
ISIS_LLC_HDR = (0xfe, 0xfe, 0x03, 0x83)
ISIS_HDR_LEN = 8
ISIS_HELLO_HDR_LEN = 19
ISIS_LSP_HDR_LEN = 19
ISIS_CSN_HDR_LEN = 25
ISIS_PSN_HDR_LEN = 9
AllL1ISs = struct.pack("6B", 0x01, 0x80, 0xc2, 0x00, 0x00, 0x14)
AllL2ISs = struct.pack("6B", 0x01, 0x80, 0xc2, 0x00, 0x00, 0x15)
################################################################################
DLIST = []
NLPIDS = { 0x00L: "NULL",
0x80L: "SNAP",
0x81L: "CLNP",
0x82L: "ESIS",
0x83L: "ISIS",
0x8EL: "IPV6",
0xCCL: "IP",
}
DLIST = DLIST + [NLPIDS]
MSG_TYPES = { 0L: "NULL",
2L: "ESH",
4L: "ISH",
6L: "RD",
15L: "L1LANHello",
16L: "L2LANHello",
17L: "PPHello",
18L: "L1LSP",
20L: "L2LSP",
24L: "L1CSN",
25L: "L2CSN",
26L: "L1PSN",
27L: "L2PSN",
}
DLIST = DLIST + [MSG_TYPES]
CIRCUIT_TYPES = { 0L: "reserved", # ignore entire PDU
1L: "L1Circuit",
2L: "L2Circuit",
3L: "L1L2Circuit",
}
DLIST = DLIST + [CIRCUIT_TYPES]
FLAGS = {1L: "SUPPORT_IP",
2L: "SUPPORT_CLNP",
}
DLIST = DLIST + [FLAGS]
VLEN_FIELDS = { 0L: "Null", # null
1L: "AreaAddress", # area address
2L: "LSPIISNeighbor", # ISIS (CLNP) neighbour (in LSP)
3L: "ESNeighbor", # end system (CLNP) neighbour
4L: "PartDIS", #
5L: "PrefixNeighbor", #
6L: "IIHIISNeighbor", # ISIS (CLNP) neighbour (in ISH)
8L: "Padding", # zero padding
9L: "LSPEntries", # LSPs ack'd in this CSNP/PSNP
10L: "Authentication", #
12L: "OptionalChecksum", #
14L: "LSPBufferSize", #
22L: "TEIISNeighbor", #
128L: "IPIntReach", # 'internal' reachable IP subnets
129L: "ProtoSupported", # NLPIDs this IS can relay
130L: "IPExtReach", # 'external' reachable IP subnets
131L: "IPInterDomInfo", # interdomain routeing info
132L: "IPIfAddr", # IP address(es) of the interface
133L: "IPAuthInfo_ILLEGAL", # deprecated
134L: "TERouterID", # TE router ID
135L: "TEIPReach", # 'wide metric TLV'
137L: "DynamicHostname", # dynamic hostname support
180L: "LeafNode", #
222L: "MultipleTopologyISN", #
229L: "MultipleTopologies", #
232L: "IPv6IfAddr", #
235L: "MTIPReach", #
236L: "IPv6IPReach", #
240L: "ThreeWayHello", #
254L: "IPSumReach", #
}
DLIST = DLIST + [VLEN_FIELDS]
STATES = { 0L: "NULL",
1L: "INITIALISING",
2L: "UP",
3L: "DOWN",
}
DLIST = DLIST + [STATES]
for d in DLIST:
for k in d.keys():
d[ d[k] ] = k
################################################################################
def padPkt(tgt_len, pkt):
pad_len = tgt_len - len(pkt)
if pad_len > 0:
full, part = divmod(pad_len, 257)
pkt = pkt + (full*struct.pack("BB 255s",
VLEN_FIELDS["Padding"], 255, 255*'\000'))
pkt = pkt + struct.pack("BB %ds" % (part-2, ),
VLEN_FIELDS["Padding"], part-2, (part-2)*'\000')
return pkt
#-------------------------------------------------------------------------------
def parseMacHdr(pkt):
(dst_mac, src_mac, length, dsap, ssap, ctrl, nlpid) =\
struct.unpack(">6s 6s H B B B B", pkt[0:MAC_HDR_LEN+1])
if (dsap, ssap, ctrl, nlpid) != ISIS_LLC_HDR:
raise LLCExc
return (src_mac, dst_mac, length, dsap, ssap, ctrl)
#-------------------------------------------------------------------------------
def parseIsisHdr(pkt):
(nlpid, hdr_len, ver_proto_id, resvd, msg_type, ver, eco, user_eco) =\
struct.unpack(">8B", pkt[0:ISIS_HDR_LEN])
return (nlpid, hdr_len, ver_proto_id, resvd,
msg_type, ver, eco, user_eco)
#-------------------------------------------------------------------------------
def parsePsnHdr(pkt):
(pdu_len, src_id) = struct.unpack("> H 7s", pkt[:ISIS_PSN_HDR_LEN])
return (pdu_len, src_id)
#-------------------------------------------------------------------------------
def parseCsnHdr(pkt):
(pdu_len, src_id, start_lsp_id, end_lsp_id) =\
struct.unpack("> H 7s 8s 8s", pkt[:ISIS_CSN_HDR_LEN])
return (pdu_len, src_id, start_lsp_id, end_lsp_id)
#-------------------------------------------------------------------------------
def parseLspHdr(pkt):
(pdu_len, lifetime, lsp_id, seq_no, cksm, bits) =\
struct.unpack("> HH 8s LHB", pkt[:ISIS_LSP_HDR_LEN])
lsp_id = struct.unpack("> 6sBB", lsp_id)
return (pdu_len, lifetime, lsp_id, seq_no, cksm, bits)
################################################################################
def parseIsisMsg(msg_len, msg, verbose=1, level=0):
(src_mac, dst_mac, length, dsap, ssap, ctrl) = parseMacHdr(msg)
(nlpid, hdr_len, ver_proto_id, resvd, msg_type, ver, eco, user_eco) =\
parseIsisHdr(msg[MAC_HDR_LEN:MAC_HDR_LEN+ISIS_HDR_LEN])
if verbose > 1:
print prtbin(level*INDENT, msg[:MAC_HDR_LEN])
if verbose > 0:
print level*INDENT +\
"%s (len=%d):" % (MSG_TYPES[msg_type], length)
print (level+1)*INDENT +\
"src mac: %s, dst mac: %s" %\
(str2hex(src_mac), str2hex(dst_mac))
print (level+1)*INDENT +\
"len: %d, LLC: 0x%0.2x.%0.2x.%0.2x" %\
(length, dsap, ssap, ctrl)
if verbose > 1:
print prtbin((level+1)*INDENT,
msg[MAC_HDR_LEN:MAC_HDR_LEN+ISIS_HDR_LEN])
if verbose > 0:
print (level+1)*INDENT +\
"hdr_len: %d, protocol id: %d, version: %d, " %\
(hdr_len, ver_proto_id, ver) +\
"eco: %d, user eco: %d" % (eco, user_eco)
rv = {"T": msg_type,
"L": msg_len,
"H": {},
"V": {}
}
rv["H"]["SRC_MAC"] = src_mac
rv["H"]["DST_MAC"] = dst_mac
rv["H"]["LENGTH"] = length
rv["H"]["DSAP"] = dsap
rv["H"]["SSAP"] = ssap
rv["H"]["CTRL"] = ctrl
rv["H"]["NLPID"] = nlpid
rv["H"]["HDR_LEN"] = hdr_len
rv["H"]["VER_PROTO_ID"] = ver_proto_id
rv["H"]["VER"] = ver
rv["H"]["ECO"] = eco
rv["H"]["USER_ECO"] = user_eco
msg = msg[MAC_HDR_LEN+ISIS_HDR_LEN:]
if msg_type in MSG_TYPES.keys():
if msg_type in (MSG_TYPES["L1LANHello"], MSG_TYPES["L2LANHello"]):
(rv["V"]["CIRCUIT_TYPE"],
rv["V"]["SRC_ID"],
rv["V"]["HOLDTIMER"],
rv["V"]["PDU_LEN"],
rv["V"]["PRIO"],
rv["V"]["LAN_ID"],
rv["V"]["VFIELDS"]) = parseIsisIsh(msg_len, msg, verbose, level)
elif msg_type == MSG_TYPES["PPHello"]:
parseIsisPPIsh(msg_len, msg, verbose, level)
elif msg_type in (MSG_TYPES["L1LSP"], MSG_TYPES["L2LSP"]):
(rv["V"]["PDU_LEN"],
rv["V"]["LIFETIME"],
rv["V"]["LSP_ID"],
rv["V"]["SEQ_NO"],
rv["V"]["CKSM"],
rv["V"]["BITS"],
rv["V"]["VFIELDS"]) = parseIsisLsp(msg_len, msg, verbose, level)
elif msg_type in (MSG_TYPES["L1CSN"], MSG_TYPES["L2CSN"]):
(rv["V"]["PDU_LEN"],
rv["V"]["SRC_ID"],
rv["V"]["START_LSP_ID"],
rv["V"]["END_LSP_ID"],
rv["V"]["VFIELDS"]) = parseIsisCsn(msg_len, msg, verbose, level)
elif msg_type in (MSG_TYPES["L1PSN"], MSG_TYPES["L2PSN"]):
(rv["V"]["PDU_LEN"],
rv["V"]["SRC_ID"],
rv["V"]["VFIELDS"]) = parseIsisPsn(msg_len, msg, verbose, level)
else:
if verbose > 0:
print level*INDENT + "[ *** %s *** ]" % MSG_TYPES[msg_type]
else:
if verbose > 0:
print level*INDENT + "[ UNKNOWN ISIS message: ", `msg_type`, " ]"
return rv
################################################################################
def parseIsisIsh(msg_len, msg, verbose=1, level=0):
(circuit_type, src_id, holdtimer,
pdu_len, prio, lan_id) = struct.unpack("> B 6s H H B 7s",
msg[:ISIS_HELLO_HDR_LEN])
if verbose > 1:
print prtbin(level*INDENT, msg[:ISIS_HELLO_HDR_LEN])
if verbose > 0:
print (level+1)*INDENT +\
"circuit type: %s, holdtimer: %d, " %\
(CIRCUIT_TYPES[circuit_type], holdtimer) +\
"PDU len: %d, priority: %d" % (pdu_len, (prio&0x7f))
print (level+1)*INDENT + "src id: %s, LAN id: %s" %\
(str2hex(src_id), str2hex(lan_id))
vfields = parseVLenFields(msg[ISIS_HELLO_HDR_LEN:], verbose, level)
return (circuit_type, src_id, holdtimer, pdu_len, prio, lan_id, vfields)
#-------------------------------------------------------------------------------
def parseIsisPPIsh(msg_len, msg, verbose=1, level=0):
print level*INDENT + "[ *** PP ISH NOT PARSED *** ]"
#-------------------------------------------------------------------------------
def parseIsisLsp(msg_len, msg, verbose=1, level=0):
(pdu_len, lifetime, lsp_id, seq_no, cksm, bits) = parseLspHdr(msg)
if verbose > 0:
if verbose > 1:
print prtbin(level*INDENT, msg[:ISIS_LSP_HDR_LEN])
print (level+1)*INDENT +\
"PDU len: %d, lifetime: %d, seq.no: %d, cksm: %s" %\
(pdu_len, lifetime, seq_no, int2hex(cksm))
print (level+1)*INDENT +\
"LSP ID: src: %s, pn: %s, LSP no: %d" %\
(str2hex(lsp_id[0]), int2hex(lsp_id[1]), lsp_id[2])
p = bits & (1<<7)
att = (bits & (1<<6)) * "error " + (bits & (1<<5)) * "expense " +\
(bits & (1<<4)) * "delay " + (bits & (1<<3)) * "default"
hty = (bits & (1<<2)) >> 2
ist = bits & ((1<<1) | (1<<0))
print (level+1)*INDENT +\
"partition repair: %s, hippity: %s, type: %s" %\
(("no", "yes")[p], ("no", "yes")[hty],
("UNUSED", "L1", "UNUSED", "L1+L2")[ist])
print (level+1)*INDENT + "attached: %s" % att
vfields = parseVLenFields(msg[ISIS_LSP_HDR_LEN:], verbose, level)
return (pdu_len, lifetime, lsp_id, seq_no, cksm, bits, vfields)
#-------------------------------------------------------------------------------
def parseIsisCsn(msg_len, msg, verbose=1, level=0):
(pdu_len, src_id, start_lsp_id, end_lsp_id) = parseCsnHdr(msg)
if verbose > 0:
if verbose > 1:
print prtbin(level*INDENT, msg[:ISIS_CSN_HDR_LEN])
print (level+1)*INDENT +\
"PDU len: %d, src ID: %s" % (pdu_len, str2hex(src_id))
print (level+1)*INDENT +\
"start LSP ID: %s" % (str2hex(start_lsp_id),)
print (level+1)*INDENT +\
"end LSP ID: %s" % (str2hex(end_lsp_id),)
vfields = parseVLenFields(msg[ISIS_CSN_HDR_LEN:], verbose, level)
return (pdu_len, src_id, start_lsp_id, end_lsp_id, vfields)
#-------------------------------------------------------------------------------
def parseIsisPsn(msg_len, msg, verbose=1, level=0):
(pdu_len, src_id) = parsePsnHdr(msg)
if verbose > 0:
if verbose > 1:
print prtbin(level*INDENT, msg[:ISIS_PSN_HDR_LEN])
print (level+1)*INDENT +\
"PDU len: %d, src ID: %s" % (pdu_len, str2hex(src_id))
vfields = parseVLenFields(msg[ISIS_PSN_HDR_LEN:], verbose, level)
return (pdu_len, src_id, vfields)
################################################################################
def parseVLenFields(fields, verbose=1, level=0):
vfields = {}
while len(fields) > 1:
# XXX: strange -- have seen single null byte vfields...
(ftype, flen) = struct.unpack(">BB", fields[0:2])
if not vfields.has_key(ftype):
vfields[ftype] = []
vfields[ftype].append(
parseVLenField(ftype, flen, fields[2:2+flen], verbose, level+1)
)
fields = fields[2+flen:]
return vfields
#-------------------------------------------------------------------------------
def parseVLenField(ftype, flen, fval, verbose=1, level=0):
rv = { "L" : flen,
}
if verbose > 1 and ftype not in (VLEN_FIELDS["Padding"],
VLEN_FIELDS["Null"]):
print prtbin(level*INDENT, `ftype`+`flen`+fval)
if ftype in VLEN_FIELDS.keys():
if verbose > 0 and ftype not in (VLEN_FIELDS["Padding"],
VLEN_FIELDS["Null"]):
print level*INDENT +\
"field: %s, length: %d" % (VLEN_FIELDS[ftype], flen)
level = level + 1
if ftype == VLEN_FIELDS["Null"]:
pass
elif ftype == VLEN_FIELDS["AreaAddress"]:
## 1
rv["V"] = []
areas = ""
while len(fval) > 0:
(l,) = struct.unpack("> B", fval[0])
rv["V"].append(fval[1:1+l])
areas = areas + '0x' + str2hex(fval[1:1+l]) + ", "
fval = fval[1+l:]
if verbose > 0:
print level*INDENT + "area addresses: " + areas
elif ftype == VLEN_FIELDS["LSPIISNeighbor"]:
## 2
rv["V"] = []
vflag = struct.unpack("> B", fval[0])
fval = fval[1:]
cnt = 0
while len(fval) > 0:
cnt = cnt + 1
default, delay, expense, error, nid =\
struct.unpack("> BBBB 7s", fval[0:11])
is_neighbour = { 'DEFAULT': default,
'DELAY' : delay,
'EXPENSE': expense,
'ERROR' : error,
'NID' : nid,
}
rv["V"].append(is_neighbour)
if verbose > 0:
print level*INDENT +\
"IS Neighbour %d: id: %s" % (cnt, str2hex(nid))
print (level+1)*INDENT +\
"default: %d, delay: %d, expense: %d, error: %d" %\
(default, delay, expense, error)
fval = fval[11:]
elif ftype == VLEN_FIELDS["ESNeighbor"]:
## 3
default, delay, expense, error = struct.unpack("> 4B", fval[0:4])
rv["V"] = { 'DEFAULT' : default,
'DELAY' : delay,
'EXPENSE' : expense,
'ERROR' : error,
'NIDS' : []
}
if verbose > 0:
print level*INDENT +\
"default: %d, delay: %d, expense: %d, error: %d" %\
(default, delay, expense, error)
fval = fval[4:]
cnt = 0
while len(fval) > 0:
cnt = cnt + 1
(nid,) = struct.unpack("> 6s", fval[0:6])
rv["V"]["NIDS"].append(nid)
if verbose > 0:
print level*INDENT +\
"ES Neighbour %d: %s" % (cnt, str2hex(nid))
fval = fval[6:]
elif ftype == VLEN_FIELDS["IIHIISNeighbor"]:
## 6
rv["V"] = []
cnt = 0
while len(fval) > 0:
cnt = cnt + 1
(nid,) = struct.unpack("> 6s", fval[0:6])
rv["V"].append(nid)
if verbose > 0:
print level*INDENT +\
"IS Neighbour %d: %s" % (cnt, str2hex(nid))
fval = fval[6:]
elif ftype == VLEN_FIELDS["Padding"]:
## 8
rv["V"] = None
elif ftype == VLEN_FIELDS["LSPEntries"]:
## 9
rv["V"] = []
cnt = 0
while len(fval) > 0:
cnt = cnt + 1
lifetime, lsp_id, lsp_seq_no, cksm =\
struct.unpack("> H 8s L H", fval[:16])
lsp_id = struct.unpack("> 6sBB", lsp_id)
lsp_entry = { "ID" : lsp_id[0],
"PN" : lsp_id[1],
"NM" : lsp_id[2],
"LIFETIME" : lifetime,
"SEQ_NO" : lsp_seq_no,
"CKSM" : cksm
}
rv["V"].append(lsp_entry)
if verbose > 0:
print level*INDENT +\
"%d: LSP ID: src: %s, pn: %s, LSP no: %d" %\
(cnt, str2hex(lsp_id[0]), int2hex(lsp_id[1]), lsp_id[2])
print (level+1)*INDENT +\
"lifetime: %d, seq.no: %d, cksm: %s" %\
(lifetime, lsp_seq_no, int2hex(cksm))
fval = fval[16:]
elif ftype == VLEN_FIELDS["IPIntReach"]:
## 128
rv["V"] = []
cnt = 0
while len(fval) > 0:
cnt = cnt + 1
default, delay, expense, error, addr, mask =\
struct.unpack("> 4B LL", fval[0:12])
ipif = { 'DEFAULT': default,
'DELAY' : delay,
'EXPENSE': expense,
'ERROR' : error,
'ADDR' : addr,
'MASK' : mask
}
rv["V"].append(ipif)
if verbose > 0:
print level*INDENT +\
"%d: default: %d, delay: %d, expense: %d, error: %d" %\
(cnt, default, delay, expense, error)
print (level+1)*INDENT +\
"addr/mask: %s/%s" % (id2str(addr), id2str(mask))
fval = fval[12:]
elif ftype == VLEN_FIELDS["ProtoSupported"]:
## 129
prots = struct.unpack("> %dB" % flen, fval)
prots_strs = map(lambda x: '%s' % x,
map(lambda x: NLPIDS[x], prots))
rv["V"] = prots_strs
if verbose > 0:
print level*INDENT + "protocols supported: " + `prots_strs`
elif ftype == VLEN_FIELDS["IPExtReach"]:
## 130
rv["V"] = []
cnt = 0
while len(fval) > 0:
cnt = cnt + 1
default, delay, expense, error, addr, mask =\
struct.unpack("> 4B LL", fval[0:12])
ipif = { 'DEFAULT': default,
'DELAY' : delay,
'EXPENSE': expense,
'ERROR' : error,
'ADDR' : addr,
'MASK' : mask
}
rv["V"].append(ipif)
if verbose > 0:
print level*INDENT +\
"%d: default: %d, delay: %d, expense: %d, error: %d" %\
(cnt, default, delay, expense, error)
print (level+1)*INDENT +\
"addr/mask: %s/%s" % (id2str(addr), id2str(mask))
fval = fval[12:]
elif ftype == VLEN_FIELDS["IPInterDomInfo"]:
## 131
rv["V"] = None
if verbose > 0:
print level*INDENT + "[ IPInterDomInfo ]"
elif ftype == VLEN_FIELDS["IPIfAddr"]:
## 132
addrs = struct.unpack("> %dL" % (flen/4, ), fval)
addrs_strs = map(lambda x: id2str(x), addrs)
rv["V"] = addrs_strs
if verbose > 0:
print level*INDENT + "interface IP addresses: " + `addrs_strs`
elif ftype == VLEN_FIELDS["DynamicHostname"]:
## 137
name = struct.unpack("> %ds" % flen, fval)
rv["V"] = name
if verbose > 0:
print level*INDENT + "dynamic hostname: '%s'" % name
else:
if verbose > 0:
print level*INDENT + "[ *** %s *** ]" % VLEN_FIELDS[ftype]
else:
if verbose > 0:
print level*INDENT + \
"[ UNKNOWN ISIS variable length field: ", `ftype`, " ]"
return rv
################################################################################
class LLCExc(Exception): pass
class VLenFieldExc(Exception): pass
#-------------------------------------------------------------------------------
class Isis:
_eth_p_802_2 = socket.htons(0x0004)
_dev_str = "eth0"
_version = 1
_version_proto_id = 1
_hold_multiplier = 3
_holdtimer = 10
#---------------------------------------------------------------------------
class Adj:
def __init__(self, atype, rx_ish, tx_ish):
self._state = STATES["INITIALISING"]
self._type = atype
self._tx_ish = tx_ish
self._rx_ish = rx_ish
self._rtx_at = 0
(src_mac, None, None, None, None, None) = parseMacHdr(rx_ish)
self._nbr_mac_addr = src_mac
hdr_start = MAC_HDR_LEN + ISIS_HDR_LEN
hdr_end = hdr_start + ISIS_HELLO_HDR_LEN
(None, src_id, ht, None, prio, lan_id) =\
struct.unpack(">B 6s H H B 7s", rx_ish[hdr_start:hdr_end])
self._holdtimer = ht
self._nbr_src_id = src_id
self._nbr_lan_id = lan_id
self._nbr_areas = []
fields = rx_ish[MAC_HDR_LEN+ISIS_HDR_LEN+ISIS_HELLO_HDR_LEN:]
while len(fields) > 0:
(ftype, flen) = struct.unpack(">BB", fields[0:2])
fval = fields[2:2+flen]
if ftype == VLEN_FIELDS["AreaAddress"]:
while len(fval) > 0:
(l,) = struct.unpack("B", fval[0])
self._nbr_areas.append(fval[1:1+l])
fval = fval[1+l:]
fields = fields[2+flen:]
def __repr__(self):
ret = """st: %s, ht: %d, retx: %d, neighbour areas: %s,
nbr src id: %s, lan id: %s""" %\
(STATES[self._state], self._holdtimer, self._rtx_at,
`map(str2hex, self._nbr_areas)`,
str2hex(self._nbr_src_id), str2hex(self._nbr_lan_id))
return ret
#---------------------------------------------------------------------------
def __init__(self, dev, area_addr, src_id=None, lan_id=None, src_ip=None):
self._sock = socket.socket(socket.PF_PACKET, socket.SOCK_RAW,
Isis._eth_p_802_2)
self._sockaddr = (dev, 0x0000)
self._sock.bind(self._sockaddr)
self._sockname = self._sock.getsockname()
# XXX HACK: want to query _sock for IP addr; can't figure out
# how at the moment
if src_ip:
self._src_ip = src_ip
else:
self._src_ip = str2id(socket.gethostbyname(socket.gethostname()))
self._src_mac = self._sockname[-1]
self._area_addr = area_addr
if src_id:
self._src_id = src_id
else:
self._src_id = self._src_mac
if lan_id:
self._lan_id = lan_id
else:
self._lan_id = self._src_id + '\001'
self._adjs = { }
self._rcvd = ""
self._mrtd = None
def __repr__(self):
ret = """Passive ISIS speaker, version %s:
Src IP: %s, Src MAC: %s
Area address: %s
Src ID: %s
LAN ID: %s
Adjs: %s\n""" %\
(VERSION,
id2str(self._src_ip), str2hex(self._src_mac),
str2hex(self._area_addr), str2hex(self._src_id),
str2hex(self._lan_id), `self._adjs`)
return ret
def close(self):
self._sock.close()
self._mrtd.close()
#---------------------------------------------------------------------------
def recvMsg(self, verbose=1, level=0):
self._rcvd = self._sock.recv(RCV_BUF_SZ)
(src_mac, dst_mac, length, dsap, ssap, ctrl) = parseMacHdr(self._rcvd)
if verbose > 2:
print "%srecvMsg: recv: len=%d%s" %\
(level*INDENT,
len(self._rcvd), prthex((level+1)*INDENT, self._rcvd))
if verbose > 1:
print "%srecvMsg: src: %s\n dst: %s" %\
(level*INDENT, str2hex(src_mac), str2hex(dst_mac))
print " len: %d" % (length, )
print " dsap: %#0.2x, ssap: %#0.2x, ctl: %#0.2x" %\
(dsap, ssap, ctrl)
return (len(self._rcvd), self._rcvd)
def sendMsg(self, pkt, verbose=1, level=0):
(src_mac, dst_mac, length, dsap, ssap, ctrl) = parseMacHdr(pkt)
(nlpid, hdr_len, ver_proto_id, resvd,
msg_type, ver, eco, user_eco) = parseIsisHdr(pkt)
if DUMP_MRTD == 1:
self._mrtd.writeIsisMsg(msg_type, len(pkt), pkt)
elif DUMP_MRTD == 2:
self._mrtd.writeIsis2Msg(msg_type, len(pkt), pkt)
if verbose > 2:
print "%ssendMsg: send: len=%d%s" %\
(level*INDENT, len(pkt), prthex((level+1)*INDENT, pkt))
if verbose > 1:
print "%ssendMsg: src: %s\n dst: %s" %\
(level*INDENT, str2hex(src_mac), str2hex(dst_mac))
print " len: %d" % (length, )
print " dsap: %#0.2x, ssap: %#0.2x, ctl: %#0.2x" %\
(dsap, ssap, ctrl)
if verbose > 0:
parseIsisMsg(len(pkt), pkt, verbose, level)
if len(pkt) <= MAC_PKT_LEN:
self._sock.send(pkt)
def parseMsg(self, verbose=1, level=0):
try:
(msg_len, msg) = self.recvMsg(verbose, level)
except (LLCExc):
if verbose > 1:
print "[ *** Non ISIS frame received *** ]"
return
(nlpid, hdr_len, ver_proto_id, resvd,
msg_type, ver, eco, user_eco) = parseIsisHdr(msg)
if DUMP_MRTD == 1:
self._mrtd.writeIsisMsg(msg_type, msg_len, msg)
elif DUMP_MRTD == 2:
self._mrtd.writeIsis2Msg(msg_type, msg_len, msg)
if verbose > 2:
print "%sparseMsg: len=%d%s" %\
(level*INDENT, msg_len, prthex((level+1)*INDENT, msg))
rv = parseIsisMsg(msg_len, msg, verbose, level)
self.processFsm(msg, verbose, level)
return rv
#---------------------------------------------------------------------------
def mkMacHdr(self, dst_mac, src_mac):
hdr = struct.pack(">6s 6s H 3B ", dst_mac, src_mac, ISIS_PKT_LEN,
ISIS_LLC_HDR[0], ISIS_LLC_HDR[1], ISIS_LLC_HDR[2])
return hdr
def mkIsisHdr(self, msg_type, hdr_len):
nlpid = NLPIDS["ISIS"]
ret = struct.pack("8B", nlpid, hdr_len, Isis._version_proto_id,
0, msg_type, Isis._version, 0, 0)
return ret
def mkIshHdr(self, circuit, src_id, holdtimer, pdu_len, prio, lan_id):
ret = struct.pack(">B 6s H H B 7s",
circuit, src_id, holdtimer, pdu_len, prio, lan_id)
return ret
def mkVLenField(self, ftype_str, flen, fval=None):
ftype = VLEN_FIELDS[ftype_str]
ret = struct.pack("2B", ftype, flen)
if ftype == VLEN_FIELDS["AreaAddress"]:
for i in range(len(fval)):
ret = ret +\
struct.pack("B %ds" % fval[i][0], fval[i][0], fval[i][1])
elif ftype == VLEN_FIELDS["Padding"]:
return padPkt(flen+2, "")
elif ftype == VLEN_FIELDS["ProtoSupported"]:
for i in range(flen):
ret = ret + struct.pack("B", fval[i])
elif ftype == VLEN_FIELDS["IPIfAddr"]:
for i in range(flen/4):
ret = ret + struct.pack(">L", fval[i])
elif ftype == VLEN_FIELDS["IIHIISNeighbor"]:
for i in range(flen/6):
ret = ret + struct.pack("6s", fval[i])
else:
raise VLenFieldExc
return ret
def mkIsh(self, ln, lan_id, holdtimer):
isns = []
if ln == 1:
dst_mac = AllL1ISs
for adj in self._adjs.keys():
if self._adjs[adj].has_key(1):
isns.append(str2mac(adj))
msg_type = MSG_TYPES["L1LANHello"]
elif ln == 2:
dst_mac = AllL2ISs
for adj in self._adjs.keys():
if self._adjs[adj].has_key(2):
isns.append(str2mac(adj))
msg_type = MSG_TYPES["L2LANHello"]
ish = self.mkMacHdr(dst_mac, self._src_mac)
ish = ish + self.mkIsisHdr(msg_type, ISIS_HDR_LEN + ISIS_HELLO_HDR_LEN)
prio = 0 # we don't ever want to be elected Designated System
ish = ish + self.mkIshHdr(CIRCUIT_TYPES["L1L2Circuit"], self._src_id,
holdtimer, ISIS_PDU_LEN, prio, lan_id)
ish = ish + self.mkVLenField("ProtoSupported", 1, (NLPIDS["IP"],))
ish = ish + self.mkVLenField("AreaAddress", 1+len(self._area_addr),
((len(self._area_addr), self._area_addr),))
ish = ish + self.mkVLenField("IPIfAddr", 4, (self._src_ip,))
if len(isns) > 0:
ish = ish + self.mkVLenField("IIHIISNeighbor", len(isns)*6, isns)
ish = padPkt(MAC_PKT_LEN, ish)
return ish
############################################################################
def processFsm(self, msg, verbose=1, level=0):
(src_mac, None, None, None, None, None) = parseMacHdr(msg)
(None, None, None, None,
msg_type, None, None, None) = parseIsisHdr(msg[MAC_HDR_LEN:])
hdr_start = MAC_HDR_LEN + ISIS_HDR_LEN
hdr_end = hdr_start + ISIS_HELLO_HDR_LEN
(None, src_id, None, None, None, lan_id) =\
struct.unpack("> B 6s H H B 7s", msg[hdr_start:hdr_end])
smac = str2hex(src_mac)
if not self._adjs.has_key(smac):
self._adjs[smac] = { }
if msg_type in (MSG_TYPES["L1LANHello"], MSG_TYPES["L2LANHello"]):
k = msg_type - 14 # L1 or L2?
if not self._adjs[smac].has_key(k):
# new adjacency
adj = Isis.Adj(k, msg, self.mkIsh(k, self._lan_id, Isis._holdtimer))
self._adjs[smac][k] = adj
else:
# existing adjacency
adj = self._adjs[smac][k]
adj._state = STATES["UP"]
adj._rx_ish = msg
adj._tx_ish = self.mkIsh(k, lan_id,
Isis._holdtimer*Isis._hold_multiplier)
if adj._rtx_at <= RETX_THRESH:
self.sendMsg(adj._tx_ish, verbose, level)
else:
pass
#---------------------------------------------------------------------------
################################################################################
if __name__ == "__main__":
import mrtd
#---------------------------------------------------------------------------
global VERBOSE, DUMP_MRTD
VERBOSE = 1
DUMP_MRTD = 0
file_pfx = mrtd.DEFAULT_FILE
file_sz = mrtd.DEFAULT_SIZE
mrtd_type = None
area_addr = None
src_id = None
lan_id = None
#---------------------------------------------------------------------------
def usage():
print """Usage: %s [ options ] where options are ([*] required):
-h|--help : Help
-v|--verbose : Be verbose
-q|--quiet : Be quiet
-a|--area-addr : set the area address to which this IS belongs
-i|--ip-addr : *** HACK *** set the IP address to advertise
-s|--src-id : set the source ID of this IS
-l|--lan-id : set the LAN ID of this IS (def: "<srcid>:01")
--device : Set the device to receive on (def: %s)
-d|--dump : Dump MRTd::PROTOCOL_ISIS format
-y|--dump-isis2 : Dump MRTd::PROTOCOL_ISIS2 format
-f|--file : Set file prefix for MRTd dump (def: %s)
-z|--size : Size of output file(s) (min: %d)""" %\
(os.path.basename(sys.argv[0]), Isis._dev_str,
mrtd.DEFAULT_FILE, mrtd.MIN_FILE_SZ)
sys.exit(0)
#---------------------------------------------------------------------------
if len(sys.argv) < 2:
usage()
try:
opts, args = getopt.getopt(sys.argv[1:],
"hqvVdyf:s:l:a:z:i:",
("help", "quiet", "verbose", "VERBOSE",
"dump", "dump-isis2",
"file-pfx=", "file-size=", "device=",
"src-id=", "lan-id=", "area-addr=", "ip-addr=" ))
except (getopt.error):
usage()
for (x, y) in opts:
if x in ('-h', '--help'):
usage()
elif x in ('-q', '--quiet'):
VERBOSE = 0
elif x in ('-v', '--verbose'):
VERBOSE = 2
elif x in ('-V', '--VERBOSE'):
VERBOSE = 3
elif x in ('-d', '--dump'):
DUMP_MRTD = 1
mrtd_type = mrtd.MSG_TYPES["PROTOCOL_ISIS"]
elif x in ('-y', '--dump-isis2'):
DUMP_MRTD = 2
mrtd_type = mrtd.MSG_TYPES["PROTOCOL_ISIS2"]
elif x in ('-f', '--file-pfx'):
file_pfx = y
elif x in ('--device', ):
Isis._dev_str = y
elif x in ('-s', '--src-id'):
src_id = map(lambda x: int(x, 16), string.split(y, '.'))
src_id = struct.pack("6B",
src_id[0], src_id[1], src_id[2],
src_id[3], src_id[4], src_id[5])
elif x in ('-l', '--lan-id'):
lan_id = map(lambda x: int(x, 16), string.split(y, '.'))
lan_id = struct.pack("7B",
lan_id[0], lan_id[1], lan_id[2],
lan_id[3], lan_id[4], lan_id[5], lan_id[6])
elif x in ('-a', '--area-addr'):
area_addr = map(lambda x: int(x, 16), string.split(y, '.'))
# this is grim, but that's not important right now...
area_addr_str = ""
for i in range(len(area_addr)):
area_addr_str = struct.pack("%ds B" % len(area_addr_str),
area_addr_str, area_addr[i])
area_addr = area_addr_str
elif x in ('-z', '--file-size'):
file_sz = max(string.atof(y), mrtd.MIN_FILE_SZ)
elif x in ('-i', '--ip-addr'):
src_ip = str2id(y)
else:
usage()
#---------------------------------------------------------------------------
if not area_addr:
usage()
isis = Isis(Isis._dev_str, area_addr, src_id, lan_id, src_ip)
isis._mrtd = mrtd.Mrtd(file_pfx, "w+b", file_sz, mrtd_type, isis)
if VERBOSE > 1:
print `isis`
try:
timeout = Isis._holdtimer
while 1: # main loop
before = time.time()
rfds, None, None = select.select([isis._sock], [], [], timeout)
after = time.time()
elapsed = after - before
if rfds != []:
# need to rx pkt(s)
rv = isis.parseMsg(VERBOSE, 0)
else:
# need to tx pkt(s) of some sort
timeout = Isis._holdtimer
for mac in isis._adjs.keys():
for a in isis._adjs[mac].keys():
adj = isis._adjs[mac][a]
adj._rtx_at = adj._rtx_at - elapsed
if adj._rtx_at <= RETX_THRESH:
isis.sendMsg(adj._tx_ish, VERBOSE, 0)
adj._rtx_at = adj._holdtimer
timeout = min(timeout, adj._rtx_at-RETX_THRESH)
except (KeyboardInterrupt):
isis.close()
sys.exit(1)
################################################################################
################################################################################
syntax highlighted by Code2HTML, v. 0.9.1