1# encoding: utf-8
2"""
3sradjlan.py
4
5Created by Evelio Vila
6Copyright (c) 2014-2017 Exa Networks. All rights reserved.
7"""
8
9import json
10from struct import unpack
11from exabgp.vendoring import six
12from exabgp.util import hexstring
13
14from exabgp.vendoring.bitstring import BitArray
15from exabgp.protocol.iso import ISO
16from exabgp.bgp.message.update.attribute.bgpls.linkstate import LINKSTATE, LsGenericFlags
17from exabgp.bgp.message.notification import Notify
18
19#   0                   1                   2                   3
20#   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
21#  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
22#  |              Type             |            Length             |
23#  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
24#  |     Flags     |     Weight    |            Reserved           |
25#  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
26#
27#   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
28#   |             OSPF Neighbor ID / IS-IS System-ID                |
29#   +                               +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
30#   |                               |
31#   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
32#
33#   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
34#   |                    SID/Label/Index (variable)                 |
35#   +---------------------------------------------------------------+
36# 		draft-gredler-idr-bgp-ls-segment-routing-ext-03
37
38
39@LINKSTATE.register()
40class SrAdjacencyLan(object):
41    TLV = 1100
42
43    def __init__(self, flags, sids, weight, undecoded=[]):
44        self.flags = flags
45        self.sids = sids
46        self.weight = weight
47        self.undecoded = undecoded
48
49    def __repr__(self):
50        return "sr_adj_lan_flags: %s, sids: %s, undecoded_sid: %s" % (self.flags, self.sids, self.undecoded)
51
52    @classmethod
53    def unpack(cls, data, length):
54        # We only support IS-IS flags for now.
55        flags = LsGenericFlags.unpack(data[0:1], LsGenericFlags.ISIS_SR_ADJ_FLAGS)
56        # Parse adj weight
57        weight = six.indexbytes(data, 1)
58        # Move pointer 4 bytes: Flags(1) + Weight(1) + Reserved(2)
59        system_id = ISO.unpack_sysid(data[4:10])
60        data = data[10:]
61        # SID/Index/Label: according to the V and L flags, it contains
62        # either:
63        # *  A 3 octet local label where the 20 rightmost bits are used for
64        # 	 encoding the label value.  In this case the V and L flags MUST
65        # 	 be set.
66        #
67        # *  A 4 octet index defining the offset in the SID/Label space
68        # 	 advertised by this router using the encodings defined in
69        #  	 Section 3.1.  In this case V and L flags MUST be unset.
70        sids = []
71        raw = []
72        while data:
73            # Range Size: 3 octet value indicating the number of labels in
74            # the range.
75            if int(flags.flags['V']) and int(flags.flags['L']):
76                b = BitArray(bytes=data[:3])
77                sid = b.unpack('uintbe:24')[0]
78                data = data[3:]
79                sids.append(sid)
80            elif (not flags.flags['V']) and (not flags.flags['L']):
81                sid = unpack('!I', data[:4])[0]
82                data = data[4:]
83                sids.append(sid)
84            else:
85                raw.append(hexstring(data))
86                break
87
88        return cls(flags=flags, sids=sids, weight=weight, undecoded=raw)
89
90    def json(self, compact=None):
91        return ', '.join(
92            [
93                '"sr-adj-lan-flags": {}'.format(self.flags.json()),
94                '"sids": {}'.format(json.dumps(self.sids)),
95                '"undecoded-sids": {}'.format(json.dumps(self.undecoded)),
96                '"sr-adj-lan-weight": {}'.format(json.dumps(self.weight)),
97            ]
98        )
99