diameter: initial commit with a generic Diameter encoder / decode

This commit is contained in:
mich 2019-08-01 17:01:23 +02:00
parent da0e9157fe
commit 8441a2c261
4 changed files with 6704 additions and 0 deletions

View File

@ -0,0 +1,271 @@
# -*- coding: UTF-8 -*-
#/**
# * Software Name : pycrate
# * Version : 0.4
# *
# * Copyright 2019. Benoit Michau. P1Sec.
# *
# * This library is free software; you can redistribute it and/or
# * modify it under the terms of the GNU Lesser General Public
# * License as published by the Free Software Foundation; either
# * version 2.1 of the License, or (at your option) any later version.
# *
# * This library 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
# * Lesser General Public License for more details.
# *
# * You should have received a copy of the GNU Lesser General Public
# * License along with this library; if not, write to the Free Software
# * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
# * MA 02110-1301 USA
# *
# *--------------------------------------------------------
# * File Name : pycrate_diameter/Diameter.py
# * Created : 2019-07-30
# * Authors : Benoit Michau
# *--------------------------------------------------------
#*/
#__all__ = [
# ]
#------------------------------------------------------------------------------#
# IETF RFC 6733
# https://tools.ietf.org/html/rfc6733
#------------------------------------------------------------------------------#
import datetime
from pycrate_core.utils import *
from pycrate_core.elt import *
from pycrate_core.base import *
from pycrate_core.repr import *
try:
from .parse_iana_diameter_xml import *
AppID_dict, Cmd_dict, AVPCodes_dict, AVPSpecVal_dict = build_dict_from_xml(FILENAME_aaa_parameters)
AddrFamNums_dict = build_dict_from_xml(FILENAME_address_family_numbers)
except Exception as err:
log('warning: unable to extract Diameter dict from xml files, %s' % err)
AppID_dict, Cmd_dict, AVPCodes_dict, AVPSpecVal_dict = {}, {}, {}, {}
AddrFamNums_dict = {}
#------------------------------------------------------------------------------#
# 4.2. Basic AVP Data Formats
#------------------------------------------------------------------------------#
# Basic AVP Data formats with direct pycrate correspondence:
#
# OctetString: Buf()
# Integer32: Int32()
# Integer64: Int64()
# Unsigned32: Uint32()
# Unsigned64: Uint64()
class OctetString(Buf):
pass
class Integer32(Int32):
pass
class Integer64(Int64):
pass
class Unsigned32(Uint32):
pass
class Unsigned64(Uint64):
pass
# Float32 and Float64 require IEEE-754-1985 format definition
class _IEEE_754_1985(Envelope):
# TODO: define set_val() and get_val() to handle floating / scientific numbers
pass
class Float32(_IEEE_754_1985):
_GEN = (
Uint('S', bl=1, dic={0: '+', 1: '-'}),
Uint('E', bl=8),
Uint('F', bl=23)
)
class Float64(_IEEE_754_1985):
_GEN = (
Uint('S', bl=1, dic={0: '+', 1: '-'}),
Uint('E', bl=11),
Uint('F', bl=52)
)
#------------------------------------------------------------------------------#
# 4.3. Derived AVP Data Formats
#------------------------------------------------------------------------------#
# Derived AVP Data formats with direct pycrate correspondence:
#
# UTF8String: UTF8String()
# DiameterIdentity: Buf(), ascii-encoded FQDN / Realm
# DiameterURI: UTF8String(), ascii or UTF8-encoded URI
# Enumerated: Int32(), with a dict provided
# IPFilterRule: Buf(), ascii-encoded filter rule
class DiameterIdentity(Buf):
pass
class DiameterURI(UTF8String):
pass
class Enumerated(Int32):
pass
class IPFiterRule(Buf):
pass
class Address(Envelope):
# TODO: provides a better Value representation, at least for IPv4 and IPv6
_GEN = (
Uint16('AddressType', dic=AddrFamNums_dict),
Buf('Value', rep=REPR_HEX)
)
class Time(Buf):
# TODO: provides a way to set time and represent it correctly
# see Python ntplib source, e.g. https://github.com/remh/ntplib
_bl = 32
_rep = REPR_HEX
#------------------------------------------------------------------------------#
# 4.1. AVP Header
#------------------------------------------------------------------------------#
class AVPHdr(Envelope):
ENV_SEL_TRANS = False
_GEN = (
Uint32('Code', dic=AVPCodes_dict),
Uint('V', bl=1),
Uint('M', bl=1),
Uint('P', bl=1),
Uint('reserved', bl=5),
Uint24('Len'),
Uint32('VendorID')
)
def __init__(self, *args, **kwargs):
Envelope.__init__(self, *args, **kwargs)
self[6].set_transauto(lambda: False if self[1].get_val() else True)
class AVP(Envelope):
_GEN = (
AVPHdr(),
Buf('AVPData', rep=REPR_HEX, hier=1),
Buf('AVPPad', rep=REPR_HEX)
)
def __init__(self, *args, **kwargs):
Envelope.__init__(self, *args, **kwargs)
self[0][5].set_valauto(lambda: 12 + self[1].get_len() if self[0][1].get_val() else \
8 + self[1].get_len())
self[1].set_blauto(lambda: (self[0][5].get_val() - 12) << 3 if self[0][1].get_val() else \
(self[0][5].get_val() - 8) << 3)
self[2].set_blauto(lambda: (-self[1].get_len()%4) << 3)
def GenerateAVP(Code, DataType, M=0, P=0, VendorID=None):
"""generate a specific Diameter AVP with the appropriate Code and Data type
"""
val_hdr = {'Code': Code, 'M': M, 'P': P}
if VendorID is not None:
val_hdr['V'] = 1
val_hdr['VendorID'] = VendorID
#
if isinstance(DataType._bl, integer_types) and DataType._bl % 32 == 0:
# fixed length AVP, no padding required
class AVP(Envelope):
_GEN = (
AVPHdr(val=val_hdr),
DataType('AVPData', hier=1)
)
def __init__(self, *args, **kwargs):
Envelope.__init__(self, *args, **kwargs)
self[0][5].set_valauto(lambda: 12 + (self[1]._bl >> 3) if self[0][1].get_val() else \
8 + (self[1]._bl >> 3))
else:
# variable length AVP, padding may be required
class AVP(Envelope):
_GEN = (
AVPHdr(val=val_hdr),
DataType('AVPData', hier=1),
Buf('AVPPad', rep=REPR_HEX)
)
def __init__(self, *args, **kwargs):
Envelope.__init__(self, *args, **kwargs)
self[0][5].set_valauto(lambda: 12 + self[1].get_len() if self[0][1].get_val() else \
8 + self[1].get_len())
self[1].set_blauto(lambda: (self[0][5].get_val() - 12) << 3 if self[0][1].get_val() else \
(self[0][5].get_val() - 8) << 3)
self[2].set_blauto(lambda: (-self[1].get_len()%4) << 3)
#
return AVP
#------------------------------------------------------------------------------#
# 3. Diameter Header
#------------------------------------------------------------------------------#
class DiameterHdr(Envelope):
_GEN = (
Uint8('Vers', val=1),
Uint24('Len'),
Uint('R', bl=1),
Uint('P', bl=1),
Uint('E', bl=1),
Uint('T', bl=1),
Uint('reserved', bl=4),
Uint24('Cmd', dic=Cmd_dict),
Uint32('AppID', dic=AppID_dict),
Uint32('HHID', rep=REPR_HEX),
Uint32('EEID', rep=REPR_HEX)
)
class DiameterGeneric(Envelope):
_GEN = (
DiameterHdr(),
Sequence('AVPs', GEN=AVP(), hier=1)
)
def __init__(self, *args, **kwargs):
Envelope.__init__(self, *args, **kwargs)
self[0][1].set_valauto(lambda: 20 + self[1].get_len())
def _from_char(self, char):
if self.get_trans():
return
self[0]._from_char(char)
#
avps_len = self[0][1].get_val() - 20
char_len = char.len_byte()
if char_len > avps_len:
char._len_bit -= 8*(char_len - avps_len)
restore_char_len = True
char_bl = char._len_bit
else:
restore_char_len = False
#
self[1]._from_char(char)
#
if restore_char_len:
char._len_bit = char_bl

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,330 @@
<?xml version='1.0' encoding='UTF-8'?>
<?xml-stylesheet type="text/xsl" href="address-family-numbers.xsl"?>
<?oxygen RNGSchema="address-family-numbers.rng" type="xml"?>
<registry xmlns="http://www.iana.org/assignments" id="address-family-numbers">
<title>Address Family Numbers</title>
<updated>2018-04-02</updated>
<registry id="address-family-numbers-2">
<title>Address Family Numbers</title>
<xref type="rfc" data="rfc2453"/>
<xref type="rfc" data="rfc2677"/>
<xref type="rfc" data="rfc2858"/>
<note>Registrations also appear in <xref type="registry" data="ianaaddressfamilynumbers-mib">ianaaddressfamilynumbers-mib</xref>.
</note>
<note>When this registry is modified, the YANG module <xref type="registry" data="iana-routing-types">iana-routing-types</xref>
must be updated as defined in <xref type="rfc" data="rfc8294"/>.
</note>
<range>
<value>1-16383</value>
<registration_rule>Standards Action</registration_rule>
</range>
<range>
<value>16384-32767</value>
<registration_rule>First Come First Served</registration_rule>
</range>
<record>
<value>0</value>
<description>Reserved</description>
<registration/>
</record>
<record>
<value>1</value>
<description>IP (IP version 4)</description>
<registration/>
</record>
<record>
<value>2</value>
<description>IP6 (IP version 6)</description>
<registration/>
</record>
<record>
<value>3</value>
<description>NSAP</description>
<registration/>
</record>
<record>
<value>4</value>
<description>HDLC (8-bit multidrop)</description>
<registration/>
</record>
<record>
<value>5</value>
<description>BBN 1822</description>
<registration/>
</record>
<record>
<value>6</value>
<description>802 (includes all 802 media plus Ethernet "canonical format")</description>
<registration/>
</record>
<record>
<value>7</value>
<description>E.163</description>
<registration/>
</record>
<record>
<value>8</value>
<description>E.164 (SMDS, Frame Relay, ATM)</description>
<registration/>
</record>
<record>
<value>9</value>
<description>F.69 (Telex)</description>
<registration/>
</record>
<record>
<value>10</value>
<description>X.121 (X.25, Frame Relay)</description>
<registration/>
</record>
<record>
<value>11</value>
<description>IPX</description>
<registration/>
</record>
<record>
<value>12</value>
<description>Appletalk</description>
<registration/>
</record>
<record>
<value>13</value>
<description>Decnet IV</description>
<registration/>
</record>
<record>
<value>14</value>
<description>Banyan Vines</description>
<registration/>
</record>
<record date="1995-10">
<value>15</value>
<description>E.164 with NSAP format subaddress</description>
<xref type="text">ATM Forum UNI 3.1. October 1995.</xref>
<xref type="person" data="Andy_Malis"/>
<registration/>
</record>
<record>
<value>16</value>
<description>DNS (Domain Name System)</description>
<registration/>
</record>
<record date="2000-03">
<value>17</value>
<description>Distinguished Name</description>
<xref type="person" data="Charles_Lynn"/>
<registration/>
</record>
<record date="2000-03">
<value>18</value>
<description>AS Number</description>
<xref type="person" data="Charles_Lynn"/>
<registration/>
</record>
<record date="2000-09">
<value>19</value>
<description>XTP over IP version 4</description>
<xref type="person" data="Mike_Saul"/>
<registration/>
</record>
<record date="2000-09">
<value>20</value>
<description>XTP over IP version 6</description>
<xref type="person" data="Mike_Saul"/>
<registration/>
</record>
<record date="2000-09">
<value>21</value>
<description>XTP native mode XTP</description>
<xref type="person" data="Mike_Saul"/>
<registration/>
</record>
<record date="2002-03">
<value>22</value>
<description>Fibre Channel World-Wide Port Name</description>
<xref type="person" data="Mark_Bakke"/>
<registration/>
</record>
<record date="2002-03">
<value>23</value>
<description>Fibre Channel World-Wide Node Name</description>
<xref type="person" data="Mark_Bakke"/>
<registration/>
</record>
<record date="2002-03">
<value>24</value>
<description>GWID</description>
<xref type="person" data="Subra_Hegde"/>
<registration/>
</record>
<record>
<value>25</value>
<description>AFI for L2VPN information</description>
<xref type="rfc" data="rfc4761"/>
<xref type="rfc" data="rfc6074"/>
<registration/>
</record>
<record>
<value>26</value>
<description>MPLS-TP Section Endpoint Identifier</description>
<xref type="rfc" data="rfc7212"/>
<registration/>
</record>
<record>
<value>27</value>
<description>MPLS-TP LSP Endpoint Identifier</description>
<xref type="rfc" data="rfc7212"/>
<registration/>
</record>
<record>
<value>28</value>
<description>MPLS-TP Pseudowire Endpoint Identifier</description>
<xref type="rfc" data="rfc7212"/>
<registration/>
</record>
<record date="2014-04-29">
<value>29</value>
<description>MT IP: Multi-Topology IP version 4</description>
<xref type="rfc" data="rfc7307"/>
<registration/>
</record>
<record date="2014-04-29">
<value>30</value>
<description>MT IPv6: Multi-Topology IP version 6</description>
<xref type="rfc" data="rfc7307"/>
<registration/>
</record>
<record>
<value>31-16383</value>
<description>Unassigned</description>
<registration/>
</record>
<record date="2008-05-13">
<value>16384</value>
<description>EIGRP Common Service Family</description>
<xref type="person" data="Donnie_Savage"/>
<registration>2008-05-13</registration>
</record>
<record date="2008-05-13">
<value>16385</value>
<description>EIGRP IPv4 Service Family</description>
<xref type="person" data="Donnie_Savage"/>
<registration>2008-05-13</registration>
</record>
<record date="2008-05-13">
<value>16386</value>
<description>EIGRP IPv6 Service Family</description>
<xref type="person" data="Donnie_Savage"/>
<registration>2008-05-13</registration>
</record>
<record date="2009-11-12">
<value>16387</value>
<description>LISP Canonical Address Format (LCAF)</description>
<xref type="person" data="David_Meyer"/>
<registration>2009-11-12</registration>
</record>
<record date="2013-03-20" updated="2016-03-22">
<value>16388</value>
<description>BGP-LS</description>
<xref type="rfc" data="rfc7752"/>
<registration>2013-03-20</registration>
</record>
<record date="2013-05-06">
<value>16389</value>
<description>48-bit MAC</description>
<xref type="rfc" data="rfc7042"/>
<registration>2013-05-06</registration>
</record>
<record date="2013-05-06">
<value>16390</value>
<description>64-bit MAC</description>
<xref type="rfc" data="rfc7042"/>
<registration>2013-05-06</registration>
</record>
<record date="2013-09-25">
<value>16391</value>
<description>OUI</description>
<xref type="rfc" data="rfc7961"/>
<registration>2013-09-25</registration>
</record>
<record date="2013-09-25">
<value>16392</value>
<description>MAC/24</description>
<xref type="rfc" data="rfc7961"/>
<registration>2013-09-25</registration>
</record>
<record date="2013-09-25">
<value>16393</value>
<description>MAC/40</description>
<xref type="rfc" data="rfc7961"/>
<registration>2013-09-25</registration>
</record>
<record date="2013-09-25">
<value>16394</value>
<description>IPv6/64</description>
<xref type="rfc" data="rfc7961"/>
<registration>2013-09-25</registration>
</record>
<record date="2013-09-25">
<value>16395</value>
<description>RBridge Port ID</description>
<xref type="rfc" data="rfc7961"/>
<registration>2013-09-25</registration>
</record>
<record date="2014-09-02" updated="2014-11-07">
<value>16396</value>
<description>TRILL Nickname</description>
<xref type="rfc" data="rfc7455"/>
<registration>2014-09-02</registration>
</record>
<record>
<value>16397-65534</value>
<description>Unassigned</description>
<registration/>
</record>
<record>
<value>65535</value>
<description>Reserved</description>
<registration/>
</record>
</registry>
<people>
<person id="Andy_Malis">
<name>Andy Malis</name>
<uri>mailto:agmalis&amp;gmail.com</uri>
<updated>2013-05-28</updated>
</person>
<person id="Charles_Lynn">
<name>Charles Lynn</name>
<uri>mailto:clynn&amp;bbn.com</uri>
<updated>2000-03</updated>
</person>
<person id="David_Meyer">
<name>David Meyer</name>
<uri>mailto:dmm&amp;1-4-5.net</uri>
<updated>2009-11-12</updated>
</person>
<person id="Donnie_Savage">
<name>Donnie Savage</name>
<uri>mailto:dsavage&amp;cisco.com</uri>
<updated>2008-05-13</updated>
</person>
<person id="Mark_Bakke">
<name>Mark Bakke</name>
<uri>mailto:mbakke&amp;cisco.com</uri>
<updated>2002-03</updated>
</person>
<person id="Mike_Saul">
<name>Mike Saul</name>
<uri>mailto:mike&amp;nentat.com</uri>
<updated>2000-09</updated>
</person>
<person id="Subra_Hegde">
<name>Subra Hegde</name>
<uri>mailto:subrah&amp;cisco.com</uri>
<updated>2002-03</updated>
</person>
</people>
</registry>

View File

@ -0,0 +1,148 @@
# -*- coding: UTF-8 -*-
#/**
# * Software Name : pycrate
# * Version : 0.4
# *
# * Copyright 2019. Benoit Michau. P1Sec.
# *
# * This library is free software; you can redistribute it and/or
# * modify it under the terms of the GNU Lesser General Public
# * License as published by the Free Software Foundation; either
# * version 2.1 of the License, or (at your option) any later version.
# *
# * This library 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
# * Lesser General Public License for more details.
# *
# * You should have received a copy of the GNU Lesser General Public
# * License along with this library; if not, write to the Free Software
# * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
# * MA 02110-1301 USA
# *
# *--------------------------------------------------------
# * File Name : pycrate_diameter/parse_iana_diameter_xml.py
# * Created : 2019-07-30
# * Authors : Benoit Michau
# *--------------------------------------------------------
#*/
__all__ = [
'FILENAME_aaa_parameters',
'FILENAME_address_family_numbers',
'build_dict_from_xml'
]
import os
import re
from lxml import etree
FILENAME_aaa_parameters = os.path.dirname(__file__) + '/aaa-parameters.xml'
FILENAME_address_family_numbers = os.path.dirname(__file__) + '/address-family-numbers.xml'
'''
xml file structure for aaa-parameters.xml
AVP Codes:
AVP Code -> Attribute Name
AVP Specific Values:
Attribute Name, AVP Code:
AVP Value -> Value Name
...
AVP Flags Value
Bit -> Name
Application ID
ID Value -> Name
Command Codes
Code Value -> Name
xml file structure for address-family-numbers.xml
Address Family Numbers:
Number -> Description
'''
# some regexp
RE_INT = re.compile(r'[1-9]{1}[0-9]{0,}')
RE_HEX = re.compile(r'0x[0-9]{2,4,6,8,10,12,14,16}')
RE_SV_CODE = re.compile(r'\(code (%s)\)' % RE_INT.pattern)
def build_dict_from_xml(filename=FILENAME_aaa_parameters):
if filename == FILENAME_aaa_parameters:
T = etree.parse(filename).getroot()
for child in T.getchildren():
subchild_list = child.getchildren()
if not subchild_list:
pass
elif subchild_list[0].text == 'AVP Codes':
dict_avp_codes = build_dict_from_val_name(
[i for i in subchild_list if i.tag == '{http://www.iana.org/assignments}record'])
elif subchild_list[0].text == 'AVP Specific Values':
dict_avp_spec_val = build_dict_from_avp_spec_val(
[i for i in subchild_list if i.tag == '{http://www.iana.org/assignments}registry'])
elif subchild_list[0].text == 'Application IDs':
dict_app_id = build_dict_from_val_name(
[i for i in subchild_list if i.tag == '{http://www.iana.org/assignments}record'])
elif subchild_list[0].text == 'Command Codes':
dict_cmd_codes = build_dict_from_val_name(
[i for i in subchild_list if i.tag == '{http://www.iana.org/assignments}record'])
#
return (dict_app_id,
dict_cmd_codes,
dict_avp_codes,
dict_avp_spec_val)
#
elif filename == FILENAME_address_family_numbers:
T = etree.parse(filename).getroot()
for child in T.getchildren():
subchild_list = child.getchildren()
if not subchild_list:
pass
elif subchild_list[0].text == 'Address Family Numbers':
dict_addr_fam_nums = build_dict_from_val_name(
[i for i in subchild_list if i.tag == '{http://www.iana.org/assignments}record'])
#
return dict_addr_fam_nums
#
else:
print('error: invalid filename')
def build_dict_from_val_name(recordlist):
d = {}
for rec in recordlist:
code, name = rec.getchildren()[:2]
m = RE_INT.match(code.text)
if m:
d[int(m.group())] = name.text
return d
def build_dict_from_avp_spec_val(registrylist):
d = {}
for reg in registrylist:
childlist = reg.getchildren()
m = RE_SV_CODE.search(childlist[0].text)
if m:
code = int(m.group(1))
if code not in d:
d[code] = {}
subd = d[code]
for rec in [i for i in childlist if i.tag == '{http://www.iana.org/assignments}record']:
val, name = rec.getchildren()[:2]
m = RE_INT.match(val.text)
if m:
subd[int(m.group())] = name.text
# can also have flag values, e.g. 0x0000000000000020
else:
m = RE_HEX.match(val.text)
if m:
subd[int(m.group(), 16)] = name.text
return d