Completed
Push — master ( 0517dc...c6929c )
by Thomas
24:49 queued 09:50
created

exabgp.protocol.family._SAFI.__repr__()   A

Complexity

Conditions 1

Size

Total Lines 2
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 2
nop 1
dl 0
loc 2
rs 10
c 0
b 0
f 0
1
# encoding: utf-8
2
"""
3
family.py
4
5
Created by Thomas Mangin on 2010-01-19.
6
Copyright (c) 2009-2017 Exa Networks. All rights reserved.
7
License: 3-clause BSD. (See the COPYRIGHT file)
8
"""
9
10
from struct import pack
11
from struct import unpack
12
13
from exabgp.util import character
14
from exabgp.util import ordinal
15
from exabgp.protocol.resource import Resource
16
17
18
# ======================================================================== AFI
19
# https://www.iana.org/assignments/address-family-numbers/
20
21
22
class _AFI(int):
23
    UNDEFINED = 0x00  # internal
24
    IPv4 = 0x01
25
    IPv6 = 0x02
26
    L2VPN = 0x19
27
    BGPLS = 0x4004
28
29
    _names = {
30
        UNDEFINED: 'undefined',
31
        IPv4: 'ipv4',
32
        IPv6: 'ipv6',
33
        L2VPN: 'l2vpn',
34
        BGPLS: 'bgp-ls',
35
    }
36
37
    _masks = {
38
        IPv4: 32,
39
        IPv6: 128,
40
    }
41
42
    def pack(self):
43
        return pack('!H', self)
44
45
    def name(self):
46
        return self._names.get(self, 'unknown-afi-0x%s' % hex(self))
47
48
    def mask(self):
49
        return self._masks.get(self, 'invalid request for this family')
50
51
    def __repr__(self):
52
        return self.name()
53
54
    def __str__(self):
55
        return self.name()
56
57
58
class AFI(Resource):
59
    undefined = _AFI(_AFI.UNDEFINED)
60
    ipv4 = _AFI(_AFI.IPv4)
61
    ipv6 = _AFI(_AFI.IPv6)
62
    l2vpn = _AFI(_AFI.L2VPN)
63
    bgpls = _AFI(_AFI.BGPLS)
64
65
    common = {
66
        undefined.pack(): undefined,
67
        ipv4.pack(): ipv4,
68
        ipv6.pack(): ipv6,
69
        l2vpn.pack(): l2vpn,
70
        bgpls.pack(): bgpls,
71
    }
72
73
    codes = dict(
74
        (k.lower().replace('_', '-'), v)
75
        for (k, v) in {'ipv4': ipv4, 'ipv6': ipv6, 'l2vpn': l2vpn, 'bgp-ls': bgpls,}.items()
76
    )
77
78
    cache = dict([(r, r) for (l, r) in codes.items()])
79
    names = dict([(r, l) for (l, r) in codes.items()])
80
81
    inet_names = dict([(r, l.replace('ipv', 'inet')) for (l, r) in codes.items()])
82
83
    def name(self):
84
        return self.inet_names.get(self, "unknown afi")
85
86
    @staticmethod
87
    def unpack(data):
88
        return AFI.common.get(data, _AFI(unpack('!H', data)[0]))
89
90
    @classmethod
91
    def value(cls, name):
92
        return cls.codes.get(name, None)
93
94
    @staticmethod
95
    def implemented_safi(afi):
96
        if afi == 'ipv4':
97
            return ['unicast', 'multicast', 'nlri-mpls', 'mpls-vpn', 'flow', 'flow-vpn']
98
        if afi == 'ipv6':
99
            return ['unicast', 'mpls-vpn', 'flow', 'flow-vpn']
100
        if afi == 'l2vpn':
101
            return ['vpls', 'evpn']
102
        if afi == 'bgp-ls':
103
            return ['bgp-ls', 'bgp-ls-vpn']
104
        return []
105
106
    @classmethod
107
    def fromString(cls, string):
108
        return cls.codes.get(string, cls.undefined)
109
110
    @classmethod
111
    def create(cls, value):
112
        return cls.cache.get(value, _AFI(value))
113
114
115
# ======================================================================= SAFI
116
# https://www.iana.org/assignments/safi-namespace
117
118
119
class _SAFI(int):
120
    UNDEFINED = 0  # internal
121
    UNICAST = 1  # [RFC4760]
122
    MULTICAST = 2  # [RFC4760]
123
    NLRI_MPLS = 4  # [RFC3107]
124
    VPLS = 65  # [RFC4761]
125
    EVPN = 70  # [draft-ietf-l2vpn-evpn]
126
    BGPLS = 71  # [RFC7752]
127
    BGPLS_VPN = 72  # [RFC7752]
128
    MPLS_VPN = 128  # [RFC4364]
129
    RTC = 132  # [RFC4684]
130
    FLOW_IP = 133  # [RFC5575]
131
    FLOW_VPN = 134  # [RFC5575]
132
    # deprecated = 3            # [RFC4760]
133
    # mcast_vpn = 5             # [draft-ietf-l3vpn-2547bis-mcast-bgp] (TEMPORARY - Expires 2008-06-19)
134
    # pseudowire = 6            # [draft-ietf-pwe3-dynamic-ms-pw] (TEMPORARY - Expires 2008-08-23) Dynamic Placement of Multi-Segment Pseudowires
135
    # encapsulation = 7         # [RFC5512]
136
    # tunel = 64                # [Nalawade]
137
    # bgp_mdt = 66              # [Nalawade]
138
    # bgp_4over6 = 67           # [Cui]
139
    # bgp_6over4 = 67           # [Cui]
140
    # vpn_adi = 69              # [RFC-ietf-l1vpn-bgp-auto-discovery-05.txt]
141
    # mcast_bgp_mpls_vpn = 129  # [RFC2547]
142
    # rt = 132                  # [RFC4684]
143
    # vpn_ad = 140              # [draft-ietf-l3vpn-bgpvpn-auto]
144
    # private = [_ for _ in range(241,254)]   # [RFC4760]
145
    # unassigned = [_ for _ in range(8,64)] + [_ for _ in range(70,128)]
146
    # reverved = [0,3] + [130,131] + [_ for _ in range(135,140)] + [_ for _ in range(141,241)] + [255,]    # [RFC4760]
147
148
    _names = {
149
        UNICAST: 'unicast',
150
        MULTICAST: 'multicast',
151
        NLRI_MPLS: 'nlri-mpls',
152
        VPLS: 'vpls',
153
        EVPN: 'evpn',
154
        BGPLS: 'bgp-ls',
155
        BGPLS_VPN: 'bgp-ls-vpn',
156
        MPLS_VPN: 'mpls-vpn',
157
        RTC: 'rtc',
158
        FLOW_IP: 'flow',
159
        FLOW_VPN: 'flow-vpn',
160
    }
161
162
    def pack(self):
163
        return character(self)
164
165
    def name(self):
166
        return self._names.get(self, 'unknown safi %d' % int(self))
167
168
    def has_label(self):
169
        return self in (SAFI.nlri_mpls, SAFI.mpls_vpn)
170
171
    def has_rd(self):
172
        return self in (SAFI.nlri_mpls, SAFI.mpls_vpn, SAFI.flow_vpn)
173
        # technically self.flow_vpn and self.vpls has an RD but it is not an NLRI
174
175
    def has_path(self):
176
        return self in (SAFI.unicast, SAFI.nlri_mpls)
177
        # technically self.flow_vpn and self.vpls has an RD but it is not an NLRI
178
179
    def __str__(self):
180
        return self.name()
181
182
    def __repr__(self):
183
        return str(self)
184
185
186
class SAFI(Resource):
187
    undefined = _SAFI(_SAFI.UNDEFINED)
188
    unicast = _SAFI(_SAFI.UNICAST)
189
    multicast = _SAFI(_SAFI.MULTICAST)
190
    nlri_mpls = _SAFI(_SAFI.NLRI_MPLS)
191
    vpls = _SAFI(_SAFI.VPLS)
192
    evpn = _SAFI(_SAFI.EVPN)
193
    bgp_ls = _SAFI(_SAFI.BGPLS)
194
    bgp_ls_vpn = _SAFI(_SAFI.BGPLS_VPN)
195
    mpls_vpn = _SAFI(_SAFI.MPLS_VPN)
196
    rtc = _SAFI(_SAFI.RTC)
197
    flow_ip = _SAFI(_SAFI.FLOW_IP)
198
    flow_vpn = _SAFI(_SAFI.FLOW_VPN)
199
200
    common = {
201
        undefined.pack(): undefined,
202
        unicast.pack(): unicast,
203
        multicast.pack(): multicast,
204
        nlri_mpls.pack(): nlri_mpls,
205
        vpls.pack(): vpls,
206
        evpn.pack(): evpn,
207
        bgp_ls.pack(): bgp_ls,
208
        bgp_ls_vpn.pack(): bgp_ls_vpn,
209
        mpls_vpn.pack(): mpls_vpn,
210
        rtc.pack(): rtc,
211
        flow_ip.pack(): flow_ip,
212
        flow_vpn.pack(): flow_vpn,
213
    }
214
215
    codes = dict(
216
        (k.lower().replace('_', '-'), v)
217
        for (k, v) in {
218
            'unicast': unicast,
219
            'multicast': multicast,
220
            'nlri-mpls': nlri_mpls,
221
            'vpls': vpls,
222
            'evpn': evpn,
223
            'bgp-ls': bgp_ls,
224
            'bgp-ls-vpn': bgp_ls_vpn,
225
            'mpls-vpn': mpls_vpn,
226
            'rtc': rtc,
227
            'flow': flow_ip,
228
            'flow-vpn': flow_vpn,
229
        }.items()
230
    )
231
232
    names = _SAFI._names
233
234
    cache = dict([(r, r) for (l, r) in codes.items()])
235
236
    @staticmethod
237
    def unpack(data):
238
        return SAFI.common.get(data, _SAFI(ordinal(data)))
239
240
    @classmethod
241
    def value(cls, name):
242
        return cls.codes.get(name, None)
243
244
    @classmethod
245
    def fromString(cls, string):
246
        return cls.codes.get(string, cls.undefined)
247
248
    @classmethod
249
    def create(cls, value):
250
        return cls.cache.get(value, _SAFI(value))
251
252
253
# ===================================================================== FAMILY
254
255
256
class Family(object):
257
    size = {
258
        # family                   next-hop   RD
259
        (AFI.ipv4, SAFI.unicast): ((4,), 0),
260
        (AFI.ipv4, SAFI.multicast): ((4,), 0),
261
        (AFI.ipv4, SAFI.nlri_mpls): ((4,), 0),
262
        (AFI.ipv4, SAFI.mpls_vpn): ((12,), 8),
263
        (AFI.ipv4, SAFI.flow_ip): ((0, 4), 0),
264
        (AFI.ipv4, SAFI.flow_vpn): ((0, 4), 0),
265
        (AFI.ipv4, SAFI.rtc): ((4, 16), 0),
266
        (AFI.ipv6, SAFI.unicast): ((16, 32), 0),
267
        (AFI.ipv6, SAFI.nlri_mpls): ((16, 32), 0),
268
        (AFI.ipv6, SAFI.mpls_vpn): ((24, 40), 8),
269
        (AFI.ipv6, SAFI.flow_ip): ((0, 16, 32), 0),
270
        (AFI.ipv6, SAFI.flow_vpn): ((0, 16, 32), 0),
271
        (AFI.l2vpn, SAFI.vpls): ((4,), 0),
272
        (AFI.l2vpn, SAFI.evpn): ((4,), 0),
273
        (AFI.bgpls, SAFI.bgp_ls): ((4,), 0),
274
    }
275
276
    __slots__ = ['afi', 'safi']
277
278
    def __init__(self, afi, safi):
279
        self.afi = AFI.create(afi)
280
        self.safi = SAFI.create(safi)
281
282
    def has_label(self):
283
        return self.safi.has_label()
284
285
    def has_rd(self):
286
        return self.safi.has_rd()
287
288
    def has_path(self):
289
        return self.safi.has_path()
290
291
    def __eq__(self, other):
292
        return self.afi == other.afi and self.safi == other.safi
293
294
    def __neq__(self, other):
295
        return self.afi != other.afi or self.safi != other.safi
296
297
    def __lt__(self, other):
298
        raise RuntimeError('comparing Family for ordering does not make sense')
299
300
    def __le__(self, other):
301
        raise RuntimeError('comparing Family for ordering does not make sense')
302
303
    def __gt__(self, other):
304
        raise RuntimeError('comparing Family for ordering does not make sense')
305
306
    def __ge__(self, other):
307
        raise RuntimeError('comparing Family for ordering does not make sense')
308
309
    def family(self):
310
        return (self.afi, self.safi)
311
312
    def extensive(self):
313
        return 'afi %s safi %s' % (self.afi, self.safi)
314
315
    def index(self):
316
        return b'%02x%02x' % (self.afi, self.safi)
317
318
    def __repr__(self):
319
        return "%s %s" % (str(self.afi), str(self.safi))
320