1# Copyright (C) 2014 Xinguard, Inc. 2# 3# Licensed under the Apache License, Version 2.0 (the "License"); 4# you may not use this file except in compliance with the License. 5# You may obtain a copy of the License at 6# 7# http://www.apache.org/licenses/LICENSE-2.0 8# 9# Unless required by applicable law or agreed to in writing, software 10# distributed under the License is distributed on an "AS IS" BASIS, 11# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 12# implied. 13# See the License for the specific language governing permissions and 14# limitations under the License. 15 16""" 17BFD Control packet parser/serializer 18 19[RFC 5880] BFD Control packet format:: 20 21 0 1 2 3 22 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 23 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 24 |Vers | Diag |Sta|P|F|C|A|D|M| Detect Mult | Length | 25 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 26 | My Discriminator | 27 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 28 | Your Discriminator | 29 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 30 | Desired Min TX Interval | 31 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 32 | Required Min RX Interval | 33 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 34 | Required Min Echo RX Interval | 35 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 36 37An optional Authentication Section MAY be present in the following 38format of types: 39 401. Format of Simple Password Authentication Section:: 41 42 0 1 2 3 43 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 44 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 45 | Auth Type | Auth Len | Auth Key ID | Password... | 46 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 47 | ... | 48 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 49 502. Format of Keyed MD5 and Meticulous Keyed MD5 Authentication Section:: 51 52 0 1 2 3 53 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 54 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 55 | Auth Type | Auth Len | Auth Key ID | Reserved | 56 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 57 | Sequence Number | 58 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 59 | Auth Key/Digest... | 60 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 61 | ... | 62 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 63 643. Format of Keyed SHA1 and Meticulous Keyed SHA1 Authentication Section:: 65 66 0 1 2 3 67 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 68 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 69 | Auth Type | Auth Len | Auth Key ID | Reserved | 70 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 71 | Sequence Number | 72 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 73 | Auth Key/Hash... | 74 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 75 | ... | 76 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 77""" 78import binascii 79import hashlib 80import random 81import six 82import struct 83 84from . import packet_base 85from ryu.lib import addrconv 86from ryu.lib import stringify 87 88BFD_STATE_ADMIN_DOWN = 0 89BFD_STATE_DOWN = 1 90BFD_STATE_INIT = 2 91BFD_STATE_UP = 3 92 93BFD_STATE_NAME = {0: "AdminDown", 94 1: "Down", 95 2: "Init", 96 3: "Up"} 97 98BFD_FLAG_POLL = 1 << 5 99BFD_FLAG_FINAL = 1 << 4 100BFD_FLAG_CTRL_PLANE_INDEP = 1 << 3 101BFD_FLAG_AUTH_PRESENT = 1 << 2 102BFD_FLAG_DEMAND = 1 << 1 103BFD_FLAG_MULTIPOINT = 1 104 105BFD_DIAG_NO_DIAG = 0 106BFD_DIAG_CTRL_DETECT_TIME_EXPIRED = 1 107BFD_DIAG_ECHO_FUNC_FAILED = 2 108BFD_DIAG_NEIG_SIG_SESS_DOWN = 3 109BFD_DIAG_FWD_PLANE_RESET = 4 110BFD_DIAG_PATH_DOWN = 5 111BFD_DIAG_CONCAT_PATH_DOWN = 6 112BFD_DIAG_ADMIN_DOWN = 7 113BFD_DIAG_REV_CONCAT_PATH_DOWN = 8 114 115BFD_DIAG_CODE_NAME = {0: "No Diagnostic", 116 1: "Control Detection Time Expired", 117 2: "Echo Function Failed", 118 3: "Neighbor Signaled Session Down", 119 4: "Forwarding Plane Reset", 120 5: "Path Down", 121 6: "Concatenated Path Down", 122 7: "Administratively Down", 123 8: "Reverse Concatenated Path Down"} 124 125BFD_AUTH_RESERVED = 0 126BFD_AUTH_SIMPLE_PASS = 1 127BFD_AUTH_KEYED_MD5 = 2 128BFD_AUTH_METICULOUS_KEYED_MD5 = 3 129BFD_AUTH_KEYED_SHA1 = 4 130BFD_AUTH_METICULOUS_KEYED_SHA1 = 5 131 132BFD_AUTH_TYPE_NAME = {0: "Reserved", 133 1: "Simple Password", 134 2: "Keyed MD5", 135 3: "Meticulous Keyed MD5", 136 4: "Keyed SHA1", 137 5: "Meticulous Keyed SHA1"} 138 139 140class bfd(packet_base.PacketBase): 141 """BFD (RFC 5880) Control packet encoder/decoder class. 142 143 The serialized packet would looks like the ones described 144 in the following sections. 145 146 * RFC 5880 Generic BFD Control Packet Format 147 148 An instance has the following attributes at least. 149 Most of them are same to the on-wire counterparts but in host byte order. 150 151 __init__ takes the corresponding args in this order. 152 153 .. tabularcolumns:: |l|L| 154 155 ============================== ============================================ 156 Attribute Description 157 ============================== ============================================ 158 ver The version number of the protocol. 159 This class implements protocol version 1. 160 diag A diagnostic code specifying the local 161 system's reason for the last change in 162 session state. 163 state The current BFD session state as seen by 164 the transmitting system. 165 flags Bitmap of the following flags. 166 167 | BFD_FLAG_POLL 168 | BFD_FLAG_FINAL 169 | BFD_FLAG_CTRL_PLANE_INDEP 170 | BFD_FLAG_AUTH_PRESENT 171 | BFD_FLAG_DEMAND 172 | BFD_FLAG_MULTIPOINT 173 detect_mult Detection time multiplier. 174 my_discr My Discriminator. 175 your_discr Your Discriminator. 176 desired_min_tx_interval Desired Min TX Interval. (in microseconds) 177 required_min_rx_interval Required Min RX Interval. (in microseconds) 178 required_min_echo_rx_interval Required Min Echo RX Interval. 179 (in microseconds) 180 auth_cls (Optional) Authentication Section instance. 181 It's defined only when the Authentication 182 Present (A) bit is set in flags. 183 Assign an instance of the following classes: 184 ``SimplePassword``, ``KeyedMD5``, 185 ``MeticulousKeyedMD5``, ``KeyedSHA1``, and 186 ``MeticulousKeyedSHA1``. 187 length (Optional) Length of the BFD Control packet, 188 in bytes. 189 ============================== ============================================ 190 """ 191 192 _PACK_STR = '!BBBBIIIII' 193 _PACK_STR_LEN = struct.calcsize(_PACK_STR) 194 195 _TYPE = { 196 'ascii': [] 197 } 198 199 _auth_parsers = {} 200 201 def __init__(self, ver=1, diag=0, state=0, flags=0, detect_mult=0, 202 my_discr=0, your_discr=0, desired_min_tx_interval=0, 203 required_min_rx_interval=0, required_min_echo_rx_interval=0, 204 auth_cls=None, length=None): 205 super(bfd, self).__init__() 206 207 self.ver = ver 208 self.diag = diag 209 self.state = state 210 self.flags = flags 211 self.detect_mult = detect_mult 212 self.my_discr = my_discr 213 self.your_discr = your_discr 214 self.desired_min_tx_interval = desired_min_tx_interval 215 self.required_min_rx_interval = required_min_rx_interval 216 self.required_min_echo_rx_interval = required_min_echo_rx_interval 217 self.auth_cls = auth_cls 218 if isinstance(length, int): 219 self.length = length 220 else: 221 self.length = len(self) 222 223 def __len__(self): 224 if self.flags & BFD_FLAG_AUTH_PRESENT and self.auth_cls is not None: 225 return self._PACK_STR_LEN + len(self.auth_cls) 226 else: 227 return self._PACK_STR_LEN 228 229 @classmethod 230 def parser(cls, buf): 231 (diag, flags, detect_mult, length, my_discr, your_discr, 232 desired_min_tx_interval, required_min_rx_interval, 233 required_min_echo_rx_interval) = \ 234 struct.unpack_from(cls._PACK_STR, buf[:cls._PACK_STR_LEN]) 235 236 ver = diag >> 5 237 diag = diag & 0x1f 238 state = flags >> 6 239 flags = flags & 0x3f 240 241 if flags & BFD_FLAG_AUTH_PRESENT: 242 auth_type = six.indexbytes(buf, cls._PACK_STR_LEN) 243 auth_cls = cls._auth_parsers[auth_type].\ 244 parser(buf[cls._PACK_STR_LEN:])[0] 245 else: 246 auth_cls = None 247 248 msg = cls(ver, diag, state, flags, detect_mult, 249 my_discr, your_discr, desired_min_tx_interval, 250 required_min_rx_interval, required_min_echo_rx_interval, 251 auth_cls) 252 253 return msg, None, None 254 255 def serialize(self, payload, prev): 256 if self.flags & BFD_FLAG_AUTH_PRESENT and self.auth_cls is not None: 257 return self.pack() + \ 258 self.auth_cls.serialize(payload=None, prev=self) 259 else: 260 return self.pack() 261 262 def pack(self): 263 """ 264 Encode a BFD Control packet without authentication section. 265 """ 266 diag = (self.ver << 5) + self.diag 267 flags = (self.state << 6) + self.flags 268 length = len(self) 269 270 return struct.pack(self._PACK_STR, diag, flags, self.detect_mult, 271 length, self.my_discr, self.your_discr, 272 self.desired_min_tx_interval, 273 self.required_min_rx_interval, 274 self.required_min_echo_rx_interval) 275 276 def authenticate(self, *args, **kwargs): 277 """Authenticate this packet. 278 279 Returns a boolean indicates whether the packet can be authenticated 280 or not. 281 282 Returns ``False`` if the Authentication Present (A) is not set in the 283 flag of this packet. 284 285 Returns ``False`` if the Authentication Section for this packet is not 286 present. 287 288 For the description of the arguemnts of this method, refer to the 289 authentication method of the Authentication Section classes. 290 """ 291 if not self.flags & BFD_FLAG_AUTH_PRESENT or \ 292 not issubclass(self.auth_cls.__class__, BFDAuth): 293 return False 294 295 return self.auth_cls.authenticate(self, *args, **kwargs) 296 297 @classmethod 298 def set_auth_parser(cls, auth_cls): 299 cls._auth_parsers[auth_cls.auth_type] = auth_cls 300 301 @classmethod 302 def register_auth_type(cls, auth_type): 303 def _set_type(auth_cls): 304 auth_cls.set_type(auth_cls, auth_type) 305 cls.set_auth_parser(auth_cls) 306 return auth_cls 307 return _set_type 308 309 310class BFDAuth(stringify.StringifyMixin): 311 """Base class of BFD (RFC 5880) Authentication Section 312 313 An instance has the following attributes at least. 314 Most of them are same to the on-wire counterparts but in host byte order. 315 316 .. tabularcolumns:: |l|L| 317 318 =========== ============================================ 319 Attribute Description 320 =========== ============================================ 321 auth_type The authentication type in use. 322 auth_len The length, in bytes, of the authentication 323 section, including the ``auth_type`` and 324 ``auth_len`` fields. 325 =========== ============================================ 326 """ 327 _PACK_HDR_STR = '!BB' 328 _PACK_HDR_STR_LEN = struct.calcsize(_PACK_HDR_STR) 329 330 auth_type = None 331 332 def __init__(self, auth_len=None): 333 super(BFDAuth, self).__init__() 334 if isinstance(auth_len, int): 335 self.auth_len = auth_len 336 else: 337 self.auth_len = len(self) 338 339 @staticmethod 340 def set_type(subcls, auth_type): 341 assert issubclass(subcls, BFDAuth) 342 subcls.auth_type = auth_type 343 344 @classmethod 345 def parser_hdr(cls, buf): 346 """ 347 Parser for common part of authentication section. 348 """ 349 return struct.unpack_from(cls._PACK_HDR_STR, 350 buf[:cls._PACK_HDR_STR_LEN]) 351 352 def serialize_hdr(self): 353 """ 354 Serialization function for common part of authentication section. 355 """ 356 return struct.pack(self._PACK_HDR_STR, self.auth_type, self.auth_len) 357 358 359@bfd.register_auth_type(BFD_AUTH_SIMPLE_PASS) 360class SimplePassword(BFDAuth): 361 """ BFD (RFC 5880) Simple Password Authentication Section class 362 363 An instance has the following attributes. 364 Most of them are same to the on-wire counterparts but in host byte order. 365 366 .. tabularcolumns:: |l|L| 367 368 =========== ============================================ 369 Attribute Description 370 =========== ============================================ 371 auth_type (Fixed) The authentication type in use. 372 auth_key_id The authentication Key ID in use. 373 password The simple password in use on this session. 374 The password is a binary string, and MUST be 375 from 1 to 16 bytes in length. 376 auth_len The length, in bytes, of the authentication 377 section, including the ``auth_type`` and 378 ``auth_len`` fields. 379 =========== ============================================ 380 """ 381 _PACK_STR = '!B' 382 _PACK_STR_LEN = struct.calcsize(_PACK_STR) 383 384 def __init__(self, auth_key_id, password, auth_len=None): 385 assert len(password) >= 1 and len(password) <= 16 386 self.auth_key_id = auth_key_id 387 self.password = password 388 super(SimplePassword, self).__init__(auth_len) 389 390 def __len__(self): 391 return self._PACK_HDR_STR_LEN + self._PACK_STR_LEN + len(self.password) 392 393 @classmethod 394 def parser(cls, buf): 395 (auth_type, auth_len) = cls.parser_hdr(buf) 396 assert auth_type == cls.auth_type 397 398 auth_key_id = six.indexbytes(buf, cls._PACK_HDR_STR_LEN) 399 400 password = buf[cls._PACK_HDR_STR_LEN + cls._PACK_STR_LEN:auth_len] 401 402 msg = cls(auth_key_id, password, auth_len) 403 404 return msg, None, None 405 406 def serialize(self, payload, prev): 407 """Encode a Simple Password Authentication Section. 408 409 ``payload`` is the rest of the packet which will immediately follow 410 this section. 411 412 ``prev`` is a ``bfd`` instance for the BFD Control header. It's not 413 necessary for encoding only the Simple Password section. 414 """ 415 return self.serialize_hdr() + \ 416 struct.pack(self._PACK_STR, self.auth_key_id) + self.password 417 418 def authenticate(self, prev=None, auth_keys=None): 419 """Authenticate the password for this packet. 420 421 This method can be invoked only when ``self.password`` is defined. 422 423 Returns a boolean indicates whether the password can be authenticated 424 or not. 425 426 ``prev`` is a ``bfd`` instance for the BFD Control header. It's not 427 necessary for authenticating the Simple Password. 428 429 ``auth_keys`` is a dictionary of authentication key chain which 430 key is an integer of *Auth Key ID* and value is a string of *Password*. 431 """ 432 auth_keys = auth_keys if auth_keys else {} 433 assert isinstance(prev, bfd) 434 if self.auth_key_id in auth_keys and \ 435 self.password == auth_keys[self.auth_key_id]: 436 return True 437 else: 438 return False 439 440 441@bfd.register_auth_type(BFD_AUTH_KEYED_MD5) 442class KeyedMD5(BFDAuth): 443 """ BFD (RFC 5880) Keyed MD5 Authentication Section class 444 445 An instance has the following attributes. 446 Most of them are same to the on-wire counterparts but in host byte order. 447 448 .. tabularcolumns:: |l|L| 449 450 =========== ================================================= 451 Attribute Description 452 =========== ================================================= 453 auth_type (Fixed) The authentication type in use. 454 auth_key_id The authentication Key ID in use. 455 seq The sequence number for this packet. 456 This value is incremented occasionally. 457 auth_key The shared MD5 key for this packet. 458 digest (Optional) The 16-byte MD5 digest for the packet. 459 auth_len (Fixed) The length of the authentication section 460 is 24 bytes. 461 =========== ================================================= 462 """ 463 _PACK_STR = '!BBL16s' 464 _PACK_STR_LEN = struct.calcsize(_PACK_STR) 465 466 def __init__(self, auth_key_id, seq, auth_key=None, digest=None, 467 auth_len=None): 468 self.auth_key_id = auth_key_id 469 self.seq = seq 470 self.auth_key = auth_key 471 self.digest = digest 472 super(KeyedMD5, self).__init__(auth_len) 473 474 def __len__(self): 475 # Defined in RFC5880 Section 4.3. 476 return 24 477 478 @classmethod 479 def parser(cls, buf): 480 (auth_type, auth_len) = cls.parser_hdr(buf) 481 assert auth_type == cls.auth_type 482 assert auth_len == 24 483 484 (auth_key_id, reserved, seq, digest) = \ 485 struct.unpack_from(cls._PACK_STR, buf[cls._PACK_HDR_STR_LEN:]) 486 assert reserved == 0 487 488 msg = cls(auth_key_id=auth_key_id, seq=seq, auth_key=None, 489 digest=digest) 490 491 return msg, None, None 492 493 def serialize(self, payload, prev): 494 """Encode a Keyed MD5 Authentication Section. 495 496 This method is used only when encoding an BFD Control packet. 497 498 ``payload`` is the rest of the packet which will immediately follow 499 this section. 500 501 ``prev`` is a ``bfd`` instance for the BFD Control header which this 502 authentication section belongs to. It's necessary to be assigned 503 because an MD5 digest must be calculated over the entire BFD Control 504 packet. 505 """ 506 assert self.auth_key is not None and len(self.auth_key) <= 16 507 assert isinstance(prev, bfd) 508 509 bfd_bin = prev.pack() 510 auth_hdr_bin = self.serialize_hdr() 511 auth_data_bin = struct.pack(self._PACK_STR, self.auth_key_id, 0, 512 self.seq, self.auth_key + 513 (b'\x00' * (len(self.auth_key) - 16))) 514 515 h = hashlib.md5() 516 h.update(bfd_bin + auth_hdr_bin + auth_data_bin) 517 self.digest = h.digest() 518 519 return auth_hdr_bin + struct.pack(self._PACK_STR, self.auth_key_id, 0, 520 self.seq, self.digest) 521 522 def authenticate(self, prev, auth_keys=None): 523 """Authenticate the MD5 digest for this packet. 524 525 This method can be invoked only when ``self.digest`` is defined. 526 527 Returns a boolean indicates whether the digest can be authenticated 528 by the correspondent Auth Key or not. 529 530 ``prev`` is a ``bfd`` instance for the BFD Control header which this 531 authentication section belongs to. It's necessary to be assigned 532 because an MD5 digest must be calculated over the entire BFD Control 533 packet. 534 535 ``auth_keys`` is a dictionary of authentication key chain which 536 key is an integer of *Auth Key ID* and value is a string of *Auth Key*. 537 """ 538 auth_keys = auth_keys if auth_keys else {} 539 assert isinstance(prev, bfd) 540 541 if self.digest is None: 542 return False 543 544 if self.auth_key_id not in auth_keys: 545 return False 546 547 auth_key = auth_keys[self.auth_key_id] 548 549 bfd_bin = prev.pack() 550 auth_hdr_bin = self.serialize_hdr() 551 auth_data_bin = struct.pack(self._PACK_STR, self.auth_key_id, 0, 552 self.seq, auth_key + 553 (b'\x00' * (len(auth_key) - 16))) 554 555 h = hashlib.md5() 556 h.update(bfd_bin + auth_hdr_bin + auth_data_bin) 557 558 if self.digest == h.digest(): 559 return True 560 else: 561 return False 562 563 564@bfd.register_auth_type(BFD_AUTH_METICULOUS_KEYED_MD5) 565class MeticulousKeyedMD5(KeyedMD5): 566 """ BFD (RFC 5880) Meticulous Keyed MD5 Authentication Section class 567 568 All methods of this class are inherited from ``KeyedMD5``. 569 570 An instance has the following attributes. 571 Most of them are same to the on-wire counterparts but in host byte order. 572 573 .. tabularcolumns:: |l|L| 574 575 =========== ================================================= 576 Attribute Description 577 =========== ================================================= 578 auth_type (Fixed) The authentication type in use. 579 auth_key_id The authentication Key ID in use. 580 seq The sequence number for this packet. 581 This value is incremented for each 582 successive packet transmitted for a session. 583 auth_key The shared MD5 key for this packet. 584 digest (Optional) The 16-byte MD5 digest for the packet. 585 auth_len (Fixed) The length of the authentication section 586 is 24 bytes. 587 =========== ================================================= 588 """ 589 pass 590 591 592@bfd.register_auth_type(BFD_AUTH_KEYED_SHA1) 593class KeyedSHA1(BFDAuth): 594 """ BFD (RFC 5880) Keyed SHA1 Authentication Section class 595 596 An instance has the following attributes. 597 Most of them are same to the on-wire counterparts but in host byte order. 598 599 .. tabularcolumns:: |l|L| 600 601 =========== ================================================ 602 Attribute Description 603 =========== ================================================ 604 auth_type (Fixed) The authentication type in use. 605 auth_key_id The authentication Key ID in use. 606 seq The sequence number for this packet. 607 This value is incremented occasionally. 608 auth_key The shared SHA1 key for this packet. 609 auth_hash (Optional) The 20-byte SHA1 hash for the packet. 610 auth_len (Fixed) The length of the authentication section 611 is 28 bytes. 612 =========== ================================================ 613 """ 614 _PACK_STR = '!BBL20s' 615 _PACK_STR_LEN = struct.calcsize(_PACK_STR) 616 617 def __init__(self, auth_key_id, seq, auth_key=None, auth_hash=None, 618 auth_len=None): 619 self.auth_key_id = auth_key_id 620 self.seq = seq 621 self.auth_key = auth_key 622 self.auth_hash = auth_hash 623 super(KeyedSHA1, self).__init__(auth_len) 624 625 def __len__(self): 626 # Defined in RFC5880 Section 4.4. 627 return 28 628 629 @classmethod 630 def parser(cls, buf): 631 (auth_type, auth_len) = cls.parser_hdr(buf) 632 assert auth_type == cls.auth_type 633 assert auth_len == 28 634 635 (auth_key_id, reserved, seq, auth_hash) = \ 636 struct.unpack_from(cls._PACK_STR, buf[cls._PACK_HDR_STR_LEN:]) 637 assert reserved == 0 638 639 msg = cls(auth_key_id=auth_key_id, seq=seq, auth_key=None, 640 auth_hash=auth_hash) 641 642 return msg, None, None 643 644 def serialize(self, payload, prev): 645 """Encode a Keyed SHA1 Authentication Section. 646 647 This method is used only when encoding an BFD Control packet. 648 649 ``payload`` is the rest of the packet which will immediately follow 650 this section. 651 652 ``prev`` is a ``bfd`` instance for the BFD Control header which this 653 authentication section belongs to. It's necessary to be assigned 654 because an SHA1 hash must be calculated over the entire BFD Control 655 packet. 656 """ 657 assert self.auth_key is not None and len(self.auth_key) <= 20 658 assert isinstance(prev, bfd) 659 660 bfd_bin = prev.pack() 661 auth_hdr_bin = self.serialize_hdr() 662 auth_data_bin = struct.pack(self._PACK_STR, self.auth_key_id, 0, 663 self.seq, self.auth_key + 664 (b'\x00' * (len(self.auth_key) - 20))) 665 666 h = hashlib.sha1() 667 h.update(bfd_bin + auth_hdr_bin + auth_data_bin) 668 self.auth_hash = h.digest() 669 670 return auth_hdr_bin + struct.pack(self._PACK_STR, self.auth_key_id, 0, 671 self.seq, self.auth_hash) 672 673 def authenticate(self, prev, auth_keys=None): 674 """Authenticate the SHA1 hash for this packet. 675 676 This method can be invoked only when ``self.auth_hash`` is defined. 677 678 Returns a boolean indicates whether the hash can be authenticated 679 by the correspondent Auth Key or not. 680 681 ``prev`` is a ``bfd`` instance for the BFD Control header which this 682 authentication section belongs to. It's necessary to be assigned 683 because an SHA1 hash must be calculated over the entire BFD Control 684 packet. 685 686 ``auth_keys`` is a dictionary of authentication key chain which 687 key is an integer of *Auth Key ID* and value is a string of *Auth Key*. 688 """ 689 auth_keys = auth_keys if auth_keys else {} 690 assert isinstance(prev, bfd) 691 692 if self.auth_hash is None: 693 return False 694 695 if self.auth_key_id not in auth_keys: 696 return False 697 698 auth_key = auth_keys[self.auth_key_id] 699 700 bfd_bin = prev.pack() 701 auth_hdr_bin = self.serialize_hdr() 702 auth_data_bin = struct.pack(self._PACK_STR, self.auth_key_id, 0, 703 self.seq, auth_key + 704 (b'\x00' * (len(auth_key) - 20))) 705 706 h = hashlib.sha1() 707 h.update(bfd_bin + auth_hdr_bin + auth_data_bin) 708 709 if self.auth_hash == h.digest(): 710 return True 711 else: 712 return False 713 714 715@bfd.register_auth_type(BFD_AUTH_METICULOUS_KEYED_SHA1) 716class MeticulousKeyedSHA1(KeyedSHA1): 717 """ BFD (RFC 5880) Meticulous Keyed SHA1 Authentication Section class 718 719 All methods of this class are inherited from ``KeyedSHA1``. 720 721 An instance has the following attributes. 722 Most of them are same to the on-wire counterparts but in host byte order. 723 724 .. tabularcolumns:: |l|L| 725 726 =========== ================================================ 727 Attribute Description 728 =========== ================================================ 729 auth_type (Fixed) The authentication type in use. 730 auth_key_id The authentication Key ID in use. 731 seq The sequence number for this packet. 732 This value is incremented for each 733 successive packet transmitted for a session. 734 auth_key The shared SHA1 key for this packet. 735 auth_hash (Optional) The 20-byte SHA1 hash for the packet. 736 auth_len (Fixed) The length of the authentication section 737 is 28 bytes. 738 =========== ================================================ 739 """ 740 pass 741 742 743bfd.set_classes(bfd._auth_parsers) 744