Signed-off-by: Wei-Li Tang <[email protected]>
---
 ryu/lib/packet/bfd.py | 744 ++++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 744 insertions(+)
 create mode 100644 ryu/lib/packet/bfd.py

diff --git a/ryu/lib/packet/bfd.py b/ryu/lib/packet/bfd.py
new file mode 100644
index 0000000..05b0217
--- /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)
+
+
[email protected]_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
+
+
[email protected]_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
+
+
[email protected]_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
+
+
[email protected]_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
+
+
[email protected]_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)
-- 
1.9.1


------------------------------------------------------------------------------
Meet PCI DSS 3.0 Compliance Requirements with EventLog Analyzer
Achieve PCI DSS 3.0 Compliant Status with Out-of-the-box PCI DSS Reports
Are you Audit-Ready for PCI DSS 3.0 Compliance? Download White paper
Comply to PCI DSS 3.0 Requirement 10 and 11.5 with EventLog Analyzer
http://pubads.g.doubleclick.net/gampad/clk?id=154622311&iu=/4140/ostg.clktrk
_______________________________________________
Ryu-devel mailing list
[email protected]
https://lists.sourceforge.net/lists/listinfo/ryu-devel

Reply via email to