5
Created by Thomas Mangin on 2012-07-08.
6
Copyright (c) 2009-2013 Exa Networks. All rights reserved.
11
from struct import pack,unpack
12
from exabgp.protocol.family import AFI,SAFI
13
from exabgp.protocol.ip.inet import Inet
15
from exabgp.protocol.ip.address import Address
16
from exabgp.bgp.message.update.attribute.attributes import Attributes
18
from exabgp.bgp.message.notification import Notify
21
for netmask in range(0,129):
22
mask_to_bytes[netmask] = int(math.ceil(float(netmask)/8))
24
# Take an integer an created it networked packed representation for the right family (ipv4/ipv6)
25
def pack_int (afi,integer,mask):
26
return ''.join([chr((integer>>(offset*8)) & 0xff) for offset in range(NLRI.length[afi]-1,-1,-1)])
29
class PathInfo (object):
30
def __init__ (self,integer=None,ip=None,packed=None):
34
self.value = ''.join([chr(int(_)) for _ in ip.split('.')])
36
self.value = ''.join([chr((integer>>offset) & 0xff) for offset in [24,16,8,0]])
39
#sum(int(a)<<offset for (a,offset) in zip(ip.split('.'), range(24, -8, -8)))
42
return len(self.value)
46
return ' path-information %s' % '.'.join([str(ord(_)) for _ in self.value])
52
return '\x00\x00\x00\x00'
54
_NoPathInfo = PathInfo()
57
class Labels (object):
60
def __init__ (self,labels):
64
# shift to 20 bits of the label to be at the top of three bytes and then truncate.
65
packed.append(pack('!L',label << 4)[1:])
66
# Mark the bottom of stack with the bit
69
packed.append(pack('!L',(label << 4)|1)[1:])
70
self.packed = ''.join(packed)
71
self._len = len(self.packed)
81
return ' label [ %s ]' % ' '.join([str(_) for _ in self.labels])
83
return ' label %s' % self.labels[0]
87
_NoLabels = Labels([])
89
class RouteDistinguisher (object):
90
def __init__ (self,rd):
92
self._len = len(self.rd)
104
t,c1,c2,c3 = unpack('!HHHH',self.rd)
106
rd = '%d:%d' % (c1,(c2<<16)+c3)
108
rd = '%d.%d.%d.%d:%d' % (c1>>8,c1&0xFF,c2>>8,c2&0xFF,c3)
110
rd = '%d:%d' % ((c1<<16)+c2,c3)
115
return ' route-distinguisher %s' % rd
118
_NoRD = RouteDistinguisher('')
120
class BGPPrefix (Inet):
121
# have a .raw for the ip
122
# have a .mask for the mask
123
# have a .bgp with the bgp wire format of the prefix
125
def __init__(self,afi,safi,packed,mask):
126
self.mask = int(mask)
127
Inet.__init__(self,afi,safi,packed)
130
return "%s/%s" % (self.ip,self.mask)
132
# The API requires addpath, but it is irrelevant here.
133
def pack (self,addpath=None):
134
return chr(self.mask) + self.prefix()
137
return self.packed[:mask_to_bytes[self.mask]]
140
return mask_to_bytes[self.mask] + 1
143
class NLRI (BGPPrefix):
144
def __init__(self,afi,safi,packed,mask):
145
self.path_info = _NoPathInfo
146
self.labels = _NoLabels
149
BGPPrefix.__init__(self,afi,safi,packed,mask)
151
def has_label (self):
152
if self.afi == AFI.ipv4 and self.safi in (SAFI.nlri_mpls,SAFI.mpls_vpn):
154
if self.afi == AFI.ipv6 and self.safi == SAFI.mpls_vpn:
159
prefix_len = len(self.path_info) + len(self.labels) + len(self.rd)
160
return 1 + prefix_len + mask_to_bytes[self.mask]
163
return "%s%s%s%s" % (BGPPrefix.__str__(self),str(self.labels),str(self.path_info),str(self.rd))
165
def __eq__ (self,other):
166
return str(self) == str(other)
168
def __ne__ (self,other):
169
return not self.__eq__(other)
172
label = str(self.labels)
173
pinfo = str(self.path_info)
177
if label: r.append('"label": "%s"' % label)
178
if pinfo: r.append('"path-information": "%s"' % pinfo)
179
if rdist: r.append('"route-distinguisher": "%s"' % rdist)
180
return '"%s": { %s }' % (BGPPrefix.__str__(self),", ".join(r))
182
def pack (self,addpath):
184
path_info = self.path_info.pack()
189
length = len(self.labels)*8 + len(self.rd)*8 + self.mask
190
return path_info + chr(length) + self.labels.pack() + self.rd.pack() + self.packed[:mask_to_bytes[self.mask]]
192
return path_info + BGPPrefix.pack(self)
194
# Generate an NLRI from a BGP packet receive
195
def BGPNLRI (afi,safi,bgp,has_multiple_path):
199
if has_multiple_path:
200
path_identifier = bgp[:4]
208
if SAFI(safi).has_label():
209
while bgp and mask >= 8:
210
label = int(unpack('!L',chr(0) + bgp[:3])[0])
212
labels.append(label>>4)
216
# This is a route withdrawal, or next-hop
217
if label == 0x000000 or label == 0x80000:
220
if SAFI(safi).has_rd():
221
mask -= 8*8 # the 8 bytes of the route distinguisher
226
raise Notify(3,10,'invalid length in NLRI prefix')
229
raise Notify(3,10,'not enough data for the mask provided to decode the NLRI')
231
size = mask_to_bytes[mask]
234
raise Notify(3,10,'could not decode route with AFI %d sand SAFI %d' % (afi,safi))
237
# XXX: The padding calculation should really go into the NLRI class
238
padding = '\0'*(NLRI.length[afi]-size)
239
prefix = network + padding
240
nlri = NLRI(afi,safi,prefix,mask)
242
# XXX: Not the best interface but will do for now
244
nlri.safi = SAFI(safi)
247
nlri.path_info = PathInfo(packed=path_identifier)
249
nlri.labels = Labels(labels)
251
nlri.rd = RouteDistinguisher(rd)
255
class Route (object):
256
def __init__ (self,nlri):
258
self.attributes = Attributes()
261
return "route %s%s" % (str(self.nlri),str(self.attributes))
264
return hash(str(self))
266
def __eq__(self, other):
267
return str(self) == str(other)
269
def __ne__ (self,other):
270
return not self.__eq__(other)
272
def extensive (self):
273
return "%s %s%s" % (str(Address(self.nlri.afi,self.nlri.safi)),str(self.nlri),str(self.attributes))
276
return self.nlri.pack(True)+self.nlri.rd.rd
279
class RouteBGP (Route):
280
def __init__ (self,nlri,action):
281
self.action = action # announce, announced, withdraw or withdrawn
282
Route.__init__(self,nlri)
285
return "%s %s" % (self.action,Route.__str__(self))
287
def routeFactory(afi,safi,data,path_info,state):
288
return RouteBGP(BGPNLRI(afi,safi,data,path_info),state)