summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
-rw-r--r--ryu/lib/packet/bfd.py744
1 files changed, 744 insertions, 0 deletions
diff --git a/ryu/lib/packet/bfd.py b/ryu/lib/packet/bfd.py
new file mode 100644
index 00000000..05b0217c
--- /dev/null
+++ b/ryu/lib/packet/bfd.py
@@ -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)