packet lib: add BFD support.
Signed-off-by: Wei-Li Tang <alextwl@xinguard.com> Signed-off-by: FUJITA Tomonori <fujita.tomonori@lab.ntt.co.jp>
This commit is contained in:
parent
bcc2c13484
commit
d1f9408314
744
ryu/lib/packet/bfd.py
Normal file
744
ryu/lib/packet/bfd.py
Normal file
@ -0,0 +1,744 @@
|
||||
# Copyright (C) 2014 Xinguard, Inc.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
# implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
"""
|
||||
BFD Control packet parser/serializer
|
||||
|
||||
RFC 5880
|
||||
BFD Control packet format
|
||||
|
||||
0 1 2 3
|
||||
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
|Vers | Diag |Sta|P|F|C|A|D|M| Detect Mult | Length |
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
| My Discriminator |
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
| Your Discriminator |
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
| Desired Min TX Interval |
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
| Required Min RX Interval |
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
| Required Min Echo RX Interval |
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
|
||||
An optional Authentication Section MAY be present in the following
|
||||
format of types:
|
||||
|
||||
1. Format of Simple Password Authentication Section
|
||||
|
||||
0 1 2 3
|
||||
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
| Auth Type | Auth Len | Auth Key ID | Password... |
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
| ... |
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
|
||||
2. Format of Keyed MD5 and Meticulous Keyed MD5 Authentication Section
|
||||
|
||||
0 1 2 3
|
||||
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
| Auth Type | Auth Len | Auth Key ID | Reserved |
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
| Sequence Number |
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
| Auth Key/Digest... |
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
| ... |
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
|
||||
3. Format of Keyed SHA1 and Meticulous Keyed SHA1 Authentication Section
|
||||
|
||||
0 1 2 3
|
||||
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
| Auth Type | Auth Len | Auth Key ID | Reserved |
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
| Sequence Number |
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
| Auth Key/Hash... |
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
| ... |
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
|
||||
"""
|
||||
import binascii
|
||||
import hashlib
|
||||
import random
|
||||
import struct
|
||||
|
||||
from . import packet_base
|
||||
from ryu.lib import addrconv
|
||||
from ryu.lib import stringify
|
||||
|
||||
BFD_STATE_ADMIN_DOWN = 0
|
||||
BFD_STATE_DOWN = 1
|
||||
BFD_STATE_INIT = 2
|
||||
BFD_STATE_UP = 3
|
||||
|
||||
BFD_STATE_NAME = {0: "AdminDown",
|
||||
1: "Down",
|
||||
2: "Init",
|
||||
3: "Up"}
|
||||
|
||||
BFD_FLAG_POLL = 1 << 5
|
||||
BFD_FLAG_FINAL = 1 << 4
|
||||
BFD_FLAG_CTRL_PLANE_INDEP = 1 << 3
|
||||
BFD_FLAG_AUTH_PRESENT = 1 << 2
|
||||
BFD_FLAG_DEMAND = 1 << 1
|
||||
BFD_FLAG_MULTIPOINT = 1
|
||||
|
||||
BFD_DIAG_NO_DIAG = 0
|
||||
BFD_DIAG_CTRL_DETECT_TIME_EXPIRED = 1
|
||||
BFD_DIAG_ECHO_FUNC_FAILED = 2
|
||||
BFD_DIAG_NEIG_SIG_SESS_DOWN = 3
|
||||
BFD_DIAG_FWD_PLANE_RESET = 4
|
||||
BFD_DIAG_PATH_DOWN = 5
|
||||
BFD_DIAG_CONCAT_PATH_DOWN = 6
|
||||
BFD_DIAG_ADMIN_DOWN = 7
|
||||
BFD_DIAG_REV_CONCAT_PATH_DOWN = 8
|
||||
|
||||
BFD_DIAG_CODE_NAME = {0: "No Diagnostic",
|
||||
1: "Control Detection Time Expired",
|
||||
2: "Echo Function Failed",
|
||||
3: "Neighbor Signaled Session Down",
|
||||
4: "Forwarding Plane Reset",
|
||||
5: "Path Down",
|
||||
6: "Concatenated Path Down",
|
||||
7: "Administratively Down",
|
||||
8: "Reverse Concatenated Path Down"}
|
||||
|
||||
BFD_AUTH_RESERVED = 0
|
||||
BFD_AUTH_SIMPLE_PASS = 1
|
||||
BFD_AUTH_KEYED_MD5 = 2
|
||||
BFD_AUTH_METICULOUS_KEYED_MD5 = 3
|
||||
BFD_AUTH_KEYED_SHA1 = 4
|
||||
BFD_AUTH_METICULOUS_KEYED_SHA1 = 5
|
||||
|
||||
BFD_AUTH_TYPE_NAME = {0: "Reserved",
|
||||
1: "Simple Password",
|
||||
2: "Keyed MD5",
|
||||
3: "Meticulous Keyed MD5",
|
||||
4: "Keyed SHA1",
|
||||
5: "Meticulous Keyed SHA1"}
|
||||
|
||||
|
||||
class bfd(packet_base.PacketBase):
|
||||
"""BFD (RFC 5880) Control packet encoder/decoder class.
|
||||
|
||||
The serialized packet would looks like the ones described
|
||||
in the following sections.
|
||||
|
||||
* RFC 5880 Generic BFD Control Packet Format
|
||||
|
||||
An instance has the following attributes at least.
|
||||
Most of them are same to the on-wire counterparts but in host byte order.
|
||||
|
||||
__init__ takes the corresponding args in this order.
|
||||
|
||||
.. tabularcolumns:: |l|L|
|
||||
|
||||
============================== ============================================
|
||||
Attribute Description
|
||||
============================== ============================================
|
||||
ver The version number of the protocol.
|
||||
This class implements protocol version 1.
|
||||
diag A diagnostic code specifying the local
|
||||
system's reason for the last change in
|
||||
session state.
|
||||
state The current BFD session state as seen by
|
||||
the transmitting system.
|
||||
flags Bitmap of the following flags.
|
||||
|
||||
| BFD_FLAG_POLL
|
||||
| BFD_FLAG_FINAL
|
||||
| BFD_FLAG_CTRL_PLANE_INDEP
|
||||
| BFD_FLAG_AUTH_PRESENT
|
||||
| BFD_FLAG_DEMAND
|
||||
| BFD_FLAG_MULTIPOINT
|
||||
detect_mult Detection time multiplier.
|
||||
my_discr My Discriminator.
|
||||
your_discr Your Discriminator.
|
||||
desired_min_tx_interval Desired Min TX Interval. (in microseconds)
|
||||
required_min_rx_interval Required Min RX Interval. (in microseconds)
|
||||
required_min_echo_rx_interval Required Min Echo RX Interval.
|
||||
(in microseconds)
|
||||
auth_cls (Optional) Authentication Section instance.
|
||||
It's defined only when the Authentication
|
||||
Present (A) bit is set in flags.
|
||||
Assign an instance of the following classes:
|
||||
``SimplePassword``, ``KeyedMD5``,
|
||||
``MeticulousKeyedMD5``, ``KeyedSHA1``, and
|
||||
``MeticulousKeyedSHA1``.
|
||||
length (Optional) Length of the BFD Control packet,
|
||||
in bytes.
|
||||
============================== ============================================
|
||||
"""
|
||||
|
||||
_PACK_STR = '!BBBBIIIII'
|
||||
_PACK_STR_LEN = struct.calcsize(_PACK_STR)
|
||||
|
||||
_TYPE = {
|
||||
'ascii': []
|
||||
}
|
||||
|
||||
_auth_parsers = {}
|
||||
|
||||
def __init__(self, ver=1, diag=0, state=0, flags=0, detect_mult=0,
|
||||
my_discr=0, your_discr=0, desired_min_tx_interval=0,
|
||||
required_min_rx_interval=0, required_min_echo_rx_interval=0,
|
||||
auth_cls=None, length=None):
|
||||
super(bfd, self).__init__()
|
||||
|
||||
self.ver = ver
|
||||
self.diag = diag
|
||||
self.state = state
|
||||
self.flags = flags
|
||||
self.detect_mult = detect_mult
|
||||
self.my_discr = my_discr
|
||||
self.your_discr = your_discr
|
||||
self.desired_min_tx_interval = desired_min_tx_interval
|
||||
self.required_min_rx_interval = required_min_rx_interval
|
||||
self.required_min_echo_rx_interval = required_min_echo_rx_interval
|
||||
self.auth_cls = auth_cls
|
||||
if isinstance(length, int):
|
||||
self.length = length
|
||||
else:
|
||||
self.length = len(self)
|
||||
|
||||
def __len__(self):
|
||||
if self.flags & BFD_FLAG_AUTH_PRESENT and self.auth_cls is not None:
|
||||
return self._PACK_STR_LEN + len(self.auth_cls)
|
||||
else:
|
||||
return self._PACK_STR_LEN
|
||||
|
||||
@classmethod
|
||||
def parser(cls, buf):
|
||||
(diag, flags, detect_mult, length, my_discr, your_discr,
|
||||
desired_min_tx_interval, required_min_rx_interval,
|
||||
required_min_echo_rx_interval) = \
|
||||
struct.unpack_from(cls._PACK_STR, buf[:cls._PACK_STR_LEN])
|
||||
|
||||
ver = diag >> 5
|
||||
diag = diag & 0x1f
|
||||
state = flags >> 6
|
||||
flags = flags & 0x3f
|
||||
|
||||
if flags & BFD_FLAG_AUTH_PRESENT:
|
||||
(auth_type,) = struct.unpack_from('!B', buf[cls._PACK_STR_LEN])
|
||||
auth_cls = cls._auth_parsers[auth_type].\
|
||||
parser(buf[cls._PACK_STR_LEN:])[0]
|
||||
else:
|
||||
auth_cls = None
|
||||
|
||||
msg = cls(ver, diag, state, flags, detect_mult,
|
||||
my_discr, your_discr, desired_min_tx_interval,
|
||||
required_min_rx_interval, required_min_echo_rx_interval,
|
||||
auth_cls)
|
||||
|
||||
return msg, None, None
|
||||
|
||||
def serialize(self, payload, prev):
|
||||
if self.flags & BFD_FLAG_AUTH_PRESENT and self.auth_cls is not None:
|
||||
return self.pack() + \
|
||||
self.auth_cls.serialize(payload=None, prev=self)
|
||||
else:
|
||||
return self.pack()
|
||||
|
||||
def pack(self):
|
||||
"""
|
||||
Encode a BFD Control packet without authentication section.
|
||||
"""
|
||||
diag = (self.ver << 5) + self.diag
|
||||
flags = (self.state << 6) + self.flags
|
||||
length = len(self)
|
||||
|
||||
return struct.pack(self._PACK_STR, diag, flags, self.detect_mult,
|
||||
length, self.my_discr, self.your_discr,
|
||||
self.desired_min_tx_interval,
|
||||
self.required_min_rx_interval,
|
||||
self.required_min_echo_rx_interval)
|
||||
|
||||
def authenticate(self, *args, **kwargs):
|
||||
"""Authenticate this packet.
|
||||
|
||||
Returns a boolean indicates whether the packet can be authenticated
|
||||
or not.
|
||||
|
||||
Returns ``False`` if the Authentication Present (A) is not set in the
|
||||
flag of this packet.
|
||||
|
||||
Returns ``False`` if the Authentication Section for this packet is not
|
||||
present.
|
||||
|
||||
For the description of the arguemnts of this method, refer to the
|
||||
authentication method of the Authentication Section classes.
|
||||
"""
|
||||
if not self.flags & BFD_FLAG_AUTH_PRESENT or \
|
||||
not issubclass(self.auth_cls.__class__, BFDAuth):
|
||||
return False
|
||||
|
||||
return self.auth_cls.authenticate(self, *args, **kwargs)
|
||||
|
||||
@classmethod
|
||||
def set_auth_parser(cls, auth_cls):
|
||||
cls._auth_parsers[auth_cls.auth_type] = auth_cls
|
||||
|
||||
@classmethod
|
||||
def register_auth_type(cls, auth_type):
|
||||
def _set_type(auth_cls):
|
||||
auth_cls.set_type(auth_cls, auth_type)
|
||||
cls.set_auth_parser(auth_cls)
|
||||
return auth_cls
|
||||
return _set_type
|
||||
|
||||
|
||||
class BFDAuth(stringify.StringifyMixin):
|
||||
"""Base class of BFD (RFC 5880) Authentication Section
|
||||
|
||||
An instance has the following attributes at least.
|
||||
Most of them are same to the on-wire counterparts but in host byte order.
|
||||
|
||||
.. tabularcolumns:: |l|L|
|
||||
|
||||
=========== ============================================
|
||||
Attribute Description
|
||||
=========== ============================================
|
||||
auth_type The authentication type in use.
|
||||
auth_len The length, in bytes, of the authentication
|
||||
section, including the ``auth_type`` and
|
||||
``auth_len`` fields.
|
||||
=========== ============================================
|
||||
"""
|
||||
_PACK_HDR_STR = '!BB'
|
||||
_PACK_HDR_STR_LEN = struct.calcsize(_PACK_HDR_STR)
|
||||
|
||||
auth_type = None
|
||||
|
||||
def __init__(self, auth_len=None):
|
||||
super(BFDAuth, self).__init__()
|
||||
if isinstance(auth_len, int):
|
||||
self.auth_len = auth_len
|
||||
else:
|
||||
self.auth_len = len(self)
|
||||
|
||||
@staticmethod
|
||||
def set_type(subcls, auth_type):
|
||||
assert issubclass(subcls, BFDAuth)
|
||||
subcls.auth_type = auth_type
|
||||
|
||||
@classmethod
|
||||
def parser_hdr(cls, buf):
|
||||
"""
|
||||
Parser for common part of authentication section.
|
||||
"""
|
||||
return struct.unpack_from(cls._PACK_HDR_STR,
|
||||
buf[:cls._PACK_HDR_STR_LEN])
|
||||
|
||||
def serialize_hdr(self):
|
||||
"""
|
||||
Serialization function for common part of authentication section.
|
||||
"""
|
||||
return struct.pack(self._PACK_HDR_STR, self.auth_type, self.auth_len)
|
||||
|
||||
|
||||
@bfd.register_auth_type(BFD_AUTH_SIMPLE_PASS)
|
||||
class SimplePassword(BFDAuth):
|
||||
""" BFD (RFC 5880) Simple Password Authentication Section class
|
||||
|
||||
An instance has the following attributes.
|
||||
Most of them are same to the on-wire counterparts but in host byte order.
|
||||
|
||||
.. tabularcolumns:: |l|L|
|
||||
|
||||
=========== ============================================
|
||||
Attribute Description
|
||||
=========== ============================================
|
||||
auth_type (Fixed) The authentication type in use.
|
||||
auth_key_id The authentication Key ID in use.
|
||||
password The simple password in use on this session.
|
||||
The password is a binary string, and MUST be
|
||||
from 1 to 16 bytes in length.
|
||||
auth_len The length, in bytes, of the authentication
|
||||
section, including the ``auth_type`` and
|
||||
``auth_len`` fields.
|
||||
=========== ============================================
|
||||
"""
|
||||
_PACK_STR = '!B'
|
||||
_PACK_STR_LEN = struct.calcsize(_PACK_STR)
|
||||
|
||||
def __init__(self, auth_key_id, password, auth_len=None):
|
||||
assert len(password) >= 1 and len(password) <= 16
|
||||
self.auth_key_id = auth_key_id
|
||||
self.password = password
|
||||
super(SimplePassword, self).__init__(auth_len)
|
||||
|
||||
def __len__(self):
|
||||
return self._PACK_HDR_STR_LEN + self._PACK_STR_LEN + len(self.password)
|
||||
|
||||
@classmethod
|
||||
def parser(cls, buf):
|
||||
(auth_type, auth_len) = cls.parser_hdr(buf)
|
||||
assert auth_type == cls.auth_type
|
||||
|
||||
(auth_key_id,) = struct.unpack_from(cls._PACK_STR,
|
||||
buf[cls._PACK_HDR_STR_LEN])
|
||||
|
||||
password = buf[cls._PACK_HDR_STR_LEN + cls._PACK_STR_LEN:auth_len]
|
||||
|
||||
msg = cls(auth_key_id, password, auth_len)
|
||||
|
||||
return msg, None, None
|
||||
|
||||
def serialize(self, payload, prev):
|
||||
"""Encode a Simple Password Authentication Section.
|
||||
|
||||
``payload`` is the rest of the packet which will immediately follow
|
||||
this section.
|
||||
|
||||
``prev`` is a ``bfd`` instance for the BFD Control header. It's not
|
||||
necessary for encoding only the Simple Password section.
|
||||
"""
|
||||
return self.serialize_hdr() + \
|
||||
struct.pack(self._PACK_STR, self.auth_key_id) + self.password
|
||||
|
||||
def authenticate(self, prev=None, auth_keys={}):
|
||||
"""Authenticate the password for this packet.
|
||||
|
||||
This method can be invoked only when ``self.password`` is defined.
|
||||
|
||||
Returns a boolean indicates whether the password can be authenticated
|
||||
or not.
|
||||
|
||||
``prev`` is a ``bfd`` instance for the BFD Control header. It's not
|
||||
necessary for authenticating the Simple Password.
|
||||
|
||||
``auth_keys`` is a dictionary of authentication key chain which
|
||||
key is an integer of *Auth Key ID* and value is a string of *Password*.
|
||||
"""
|
||||
assert isinstance(prev, bfd)
|
||||
if self.auth_key_id in auth_keys and \
|
||||
self.password == auth_keys[self.auth_key_id]:
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
|
||||
|
||||
@bfd.register_auth_type(BFD_AUTH_KEYED_MD5)
|
||||
class KeyedMD5(BFDAuth):
|
||||
""" BFD (RFC 5880) Keyed MD5 Authentication Section class
|
||||
|
||||
An instance has the following attributes.
|
||||
Most of them are same to the on-wire counterparts but in host byte order.
|
||||
|
||||
.. tabularcolumns:: |l|L|
|
||||
|
||||
=========== =================================================
|
||||
Attribute Description
|
||||
=========== =================================================
|
||||
auth_type (Fixed) The authentication type in use.
|
||||
auth_key_id The authentication Key ID in use.
|
||||
seq The sequence number for this packet.
|
||||
This value is incremented occasionally.
|
||||
auth_key The shared MD5 key for this packet.
|
||||
digest (Optional) The 16-byte MD5 digest for the packet.
|
||||
auth_len (Fixed) The length of the authentication section
|
||||
is 24 bytes.
|
||||
=========== =================================================
|
||||
"""
|
||||
_PACK_STR = '!BBL16s'
|
||||
_PACK_STR_LEN = struct.calcsize(_PACK_STR)
|
||||
|
||||
def __init__(self, auth_key_id, seq, auth_key=None, digest=None,
|
||||
auth_len=None):
|
||||
self.auth_key_id = auth_key_id
|
||||
self.seq = seq
|
||||
self.auth_key = auth_key
|
||||
self.digest = digest
|
||||
super(KeyedMD5, self).__init__(auth_len)
|
||||
|
||||
def __len__(self):
|
||||
# Defined in RFC5880 Section 4.3.
|
||||
return 24
|
||||
|
||||
@classmethod
|
||||
def parser(cls, buf):
|
||||
(auth_type, auth_len) = cls.parser_hdr(buf)
|
||||
assert auth_type == cls.auth_type
|
||||
assert auth_len == 24
|
||||
|
||||
(auth_key_id, reserved, seq, digest) = \
|
||||
struct.unpack_from(cls._PACK_STR, buf[cls._PACK_HDR_STR_LEN:])
|
||||
assert reserved == 0
|
||||
|
||||
msg = cls(auth_key_id=auth_key_id, seq=seq, auth_key=None,
|
||||
digest=digest)
|
||||
|
||||
return msg, None, None
|
||||
|
||||
def serialize(self, payload, prev):
|
||||
"""Encode a Keyed MD5 Authentication Section.
|
||||
|
||||
This method is used only when encoding an BFD Control packet.
|
||||
|
||||
``payload`` is the rest of the packet which will immediately follow
|
||||
this section.
|
||||
|
||||
``prev`` is a ``bfd`` instance for the BFD Control header which this
|
||||
authentication section belongs to. It's necessary to be assigned
|
||||
because an MD5 digest must be calculated over the entire BFD Control
|
||||
packet.
|
||||
"""
|
||||
assert self.auth_key is not None and len(self.auth_key) <= 16
|
||||
assert isinstance(prev, bfd)
|
||||
|
||||
bfd_bin = prev.pack()
|
||||
auth_hdr_bin = self.serialize_hdr()
|
||||
auth_data_bin = struct.pack(self._PACK_STR, self.auth_key_id, 0,
|
||||
self.seq, self.auth_key +
|
||||
''.join(['\x00' *
|
||||
(len(self.auth_key) - 16)]))
|
||||
|
||||
h = hashlib.md5()
|
||||
h.update(bfd_bin + auth_hdr_bin + auth_data_bin)
|
||||
self.digest = h.digest()
|
||||
|
||||
return auth_hdr_bin + struct.pack(self._PACK_STR, self.auth_key_id, 0,
|
||||
self.seq, self.digest)
|
||||
|
||||
def authenticate(self, prev, auth_keys={}):
|
||||
"""Authenticate the MD5 digest for this packet.
|
||||
|
||||
This method can be invoked only when ``self.digest`` is defined.
|
||||
|
||||
Returns a boolean indicates whether the digest can be authenticated
|
||||
by the correspondent Auth Key or not.
|
||||
|
||||
``prev`` is a ``bfd`` instance for the BFD Control header which this
|
||||
authentication section belongs to. It's necessary to be assigned
|
||||
because an MD5 digest must be calculated over the entire BFD Control
|
||||
packet.
|
||||
|
||||
``auth_keys`` is a dictionary of authentication key chain which
|
||||
key is an integer of *Auth Key ID* and value is a string of *Auth Key*.
|
||||
"""
|
||||
assert isinstance(prev, bfd)
|
||||
|
||||
if self.digest is None:
|
||||
return False
|
||||
|
||||
if self.auth_key_id not in auth_keys:
|
||||
return False
|
||||
|
||||
auth_key = auth_keys[self.auth_key_id]
|
||||
|
||||
bfd_bin = prev.pack()
|
||||
auth_hdr_bin = self.serialize_hdr()
|
||||
auth_data_bin = struct.pack(self._PACK_STR, self.auth_key_id, 0,
|
||||
self.seq, auth_key +
|
||||
''.join(['\x00' * (len(auth_key) - 16)]))
|
||||
|
||||
h = hashlib.md5()
|
||||
h.update(bfd_bin + auth_hdr_bin + auth_data_bin)
|
||||
|
||||
if self.digest == h.digest():
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
|
||||
|
||||
@bfd.register_auth_type(BFD_AUTH_METICULOUS_KEYED_MD5)
|
||||
class MeticulousKeyedMD5(KeyedMD5):
|
||||
""" BFD (RFC 5880) Meticulous Keyed MD5 Authentication Section class
|
||||
|
||||
All methods of this class are inherited from ``KeyedMD5``.
|
||||
|
||||
An instance has the following attributes.
|
||||
Most of them are same to the on-wire counterparts but in host byte order.
|
||||
|
||||
.. tabularcolumns:: |l|L|
|
||||
|
||||
=========== =================================================
|
||||
Attribute Description
|
||||
=========== =================================================
|
||||
auth_type (Fixed) The authentication type in use.
|
||||
auth_key_id The authentication Key ID in use.
|
||||
seq The sequence number for this packet.
|
||||
This value is incremented for each
|
||||
successive packet transmitted for a session.
|
||||
auth_key The shared MD5 key for this packet.
|
||||
digest (Optional) The 16-byte MD5 digest for the packet.
|
||||
auth_len (Fixed) The length of the authentication section
|
||||
is 24 bytes.
|
||||
=========== =================================================
|
||||
"""
|
||||
pass
|
||||
|
||||
|
||||
@bfd.register_auth_type(BFD_AUTH_KEYED_SHA1)
|
||||
class KeyedSHA1(BFDAuth):
|
||||
""" BFD (RFC 5880) Keyed SHA1 Authentication Section class
|
||||
|
||||
An instance has the following attributes.
|
||||
Most of them are same to the on-wire counterparts but in host byte order.
|
||||
|
||||
.. tabularcolumns:: |l|L|
|
||||
|
||||
=========== ================================================
|
||||
Attribute Description
|
||||
=========== ================================================
|
||||
auth_type (Fixed) The authentication type in use.
|
||||
auth_key_id The authentication Key ID in use.
|
||||
seq The sequence number for this packet.
|
||||
This value is incremented occasionally.
|
||||
auth_key The shared SHA1 key for this packet.
|
||||
auth_hash (Optional) The 20-byte SHA1 hash for the packet.
|
||||
auth_len (Fixed) The length of the authentication section
|
||||
is 28 bytes.
|
||||
=========== ================================================
|
||||
"""
|
||||
_PACK_STR = '!BBL20s'
|
||||
_PACK_STR_LEN = struct.calcsize(_PACK_STR)
|
||||
|
||||
def __init__(self, auth_key_id, seq, auth_key=None, auth_hash=None,
|
||||
auth_len=None):
|
||||
self.auth_key_id = auth_key_id
|
||||
self.seq = seq
|
||||
self.auth_key = auth_key
|
||||
self.auth_hash = auth_hash
|
||||
super(KeyedSHA1, self).__init__(auth_len)
|
||||
|
||||
def __len__(self):
|
||||
# Defined in RFC5880 Section 4.4.
|
||||
return 28
|
||||
|
||||
@classmethod
|
||||
def parser(cls, buf):
|
||||
(auth_type, auth_len) = cls.parser_hdr(buf)
|
||||
assert auth_type == cls.auth_type
|
||||
assert auth_len == 28
|
||||
|
||||
(auth_key_id, reserved, seq, auth_hash) = \
|
||||
struct.unpack_from(cls._PACK_STR, buf[cls._PACK_HDR_STR_LEN:])
|
||||
assert reserved == 0
|
||||
|
||||
msg = cls(auth_key_id=auth_key_id, seq=seq, auth_key=None,
|
||||
auth_hash=auth_hash)
|
||||
|
||||
return msg, None, None
|
||||
|
||||
def serialize(self, payload, prev):
|
||||
"""Encode a Keyed SHA1 Authentication Section.
|
||||
|
||||
This method is used only when encoding an BFD Control packet.
|
||||
|
||||
``payload`` is the rest of the packet which will immediately follow
|
||||
this section.
|
||||
|
||||
``prev`` is a ``bfd`` instance for the BFD Control header which this
|
||||
authentication section belongs to. It's necessary to be assigned
|
||||
because an SHA1 hash must be calculated over the entire BFD Control
|
||||
packet.
|
||||
"""
|
||||
assert self.auth_key is not None and len(self.auth_key) <= 20
|
||||
assert isinstance(prev, bfd)
|
||||
|
||||
bfd_bin = prev.pack()
|
||||
auth_hdr_bin = self.serialize_hdr()
|
||||
auth_data_bin = struct.pack(self._PACK_STR, self.auth_key_id, 0,
|
||||
self.seq, self.auth_key +
|
||||
''.join(['\x00' *
|
||||
(len(self.auth_key) - 20)]))
|
||||
|
||||
h = hashlib.sha1()
|
||||
h.update(bfd_bin + auth_hdr_bin + auth_data_bin)
|
||||
self.auth_hash = h.digest()
|
||||
|
||||
return auth_hdr_bin + struct.pack(self._PACK_STR, self.auth_key_id, 0,
|
||||
self.seq, self.auth_hash)
|
||||
|
||||
def authenticate(self, prev, auth_keys={}):
|
||||
"""Authenticate the SHA1 hash for this packet.
|
||||
|
||||
This method can be invoked only when ``self.auth_hash`` is defined.
|
||||
|
||||
Returns a boolean indicates whether the hash can be authenticated
|
||||
by the correspondent Auth Key or not.
|
||||
|
||||
``prev`` is a ``bfd`` instance for the BFD Control header which this
|
||||
authentication section belongs to. It's necessary to be assigned
|
||||
because an SHA1 hash must be calculated over the entire BFD Control
|
||||
packet.
|
||||
|
||||
``auth_keys`` is a dictionary of authentication key chain which
|
||||
key is an integer of *Auth Key ID* and value is a string of *Auth Key*.
|
||||
"""
|
||||
assert isinstance(prev, bfd)
|
||||
|
||||
if self.auth_hash is None:
|
||||
return False
|
||||
|
||||
if self.auth_key_id not in auth_keys:
|
||||
return False
|
||||
|
||||
auth_key = auth_keys[self.auth_key_id]
|
||||
|
||||
bfd_bin = prev.pack()
|
||||
auth_hdr_bin = self.serialize_hdr()
|
||||
auth_data_bin = struct.pack(self._PACK_STR, self.auth_key_id, 0,
|
||||
self.seq, auth_key +
|
||||
''.join(['\x00' * (len(auth_key) - 20)]))
|
||||
|
||||
h = hashlib.sha1()
|
||||
h.update(bfd_bin + auth_hdr_bin + auth_data_bin)
|
||||
|
||||
if self.auth_hash == h.digest():
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
|
||||
|
||||
@bfd.register_auth_type(BFD_AUTH_METICULOUS_KEYED_SHA1)
|
||||
class MeticulousKeyedSHA1(KeyedSHA1):
|
||||
""" BFD (RFC 5880) Meticulous Keyed SHA1 Authentication Section class
|
||||
|
||||
All methods of this class are inherited from ``KeyedSHA1``.
|
||||
|
||||
An instance has the following attributes.
|
||||
Most of them are same to the on-wire counterparts but in host byte order.
|
||||
|
||||
.. tabularcolumns:: |l|L|
|
||||
|
||||
=========== ================================================
|
||||
Attribute Description
|
||||
=========== ================================================
|
||||
auth_type (Fixed) The authentication type in use.
|
||||
auth_key_id The authentication Key ID in use.
|
||||
seq The sequence number for this packet.
|
||||
This value is incremented for each
|
||||
successive packet transmitted for a session.
|
||||
auth_key The shared SHA1 key for this packet.
|
||||
auth_hash (Optional) The 20-byte SHA1 hash for the packet.
|
||||
auth_len (Fixed) The length of the authentication section
|
||||
is 28 bytes.
|
||||
=========== ================================================
|
||||
"""
|
||||
pass
|
||||
|
||||
|
||||
bfd.set_classes(bfd._auth_parsers)
|
Loading…
Reference in New Issue
Block a user