Index: trunk/routing/twistedbgp/src/bgp.py |
— | — | @@ -177,7 +177,8 @@ |
178 | 178 | # TODO: IPv6 |
179 | 179 | |
180 | 180 | def __str__(self): |
181 | | - return ".".join([str(ord(o)) for o in self.prefix]) + '/%d' % self.prefixlen |
| 181 | + prefix = self.prefix + ('\0\0\0\0'[:4-len(self.prefix)]) |
| 182 | + return ".".join([str(ord(o)) for o in prefix]) + '/%d' % self.prefixlen |
182 | 183 | |
183 | 184 | def __eq__(self, other): |
184 | 185 | # FIXME: masked ips |
Index: trunk/routing/twistedbgp/src/twisted/protocols/bgp.py |
— | — | @@ -1,1524 +0,0 @@ |
2 | | -# bgp.py |
3 | | -# Copyright (c) 2007 by Mark Bergsma <mark@nedworks.org> |
4 | | - |
5 | | -""" |
6 | | -A (partial) implementation of the BGP 4 protocol (RFC4271). |
7 | | -""" |
8 | | - |
9 | | -# System imports |
10 | | -import struct |
11 | | - |
12 | | -# Zope imports |
13 | | -from zope.interface import implements, Interface |
14 | | - |
15 | | -# Twisted imports |
16 | | -from twisted import copyright |
17 | | -from twisted.internet import reactor, protocol, base, interfaces, error, defer |
18 | | - |
19 | | -# Constants |
20 | | -VERSION = 4 |
21 | | -PORT = 179 |
22 | | - |
23 | | -HDR_LEN = 19 |
24 | | -MAX_LEN = 4096 |
25 | | - |
26 | | -# BGP messages |
27 | | -MSG_OPEN = 1 |
28 | | -MSG_UPDATE = 2 |
29 | | -MSG_NOTIFICATION = 3 |
30 | | -MSG_KEEPALIVE = 4 |
31 | | - |
32 | | -# BGP FSM states |
33 | | -ST_IDLE, ST_CONNECT, ST_ACTIVE, ST_OPENSENT, ST_OPENCONFIRM, ST_ESTABLISHED = range(6) |
34 | | - |
35 | | -stateDescr = { |
36 | | - ST_IDLE: "IDLE", |
37 | | - ST_CONNECT: "CONNECT", |
38 | | - ST_ACTIVE: "ACTIVE", |
39 | | - ST_OPENSENT: "OPENSENT", |
40 | | - ST_OPENCONFIRM: "OPENCONFIRM", |
41 | | - ST_ESTABLISHED: "ESTABLISHED" |
42 | | -} |
43 | | - |
44 | | -# Notification error codes |
45 | | -ERR_MSG_HDR = 1 |
46 | | -ERR_MSG_OPEN = 2 |
47 | | -ERR_MSG_UPDATE = 3 |
48 | | -ERR_HOLD_TIMER_EXPIRED = 4 |
49 | | -ERR_FSM = 5 |
50 | | -ERR_CEASE = 6 |
51 | | - |
52 | | -# Notification suberror codes |
53 | | -ERR_MSG_HDR_CONN_NOT_SYNC = 1 |
54 | | -ERR_MSG_HDR_BAD_MSG_LEN = 2 |
55 | | -ERR_MSG_HDR_BAD_MSG_TYPE = 3 |
56 | | - |
57 | | -ERR_MSG_OPEN_UNSUP_VERSION = 1 |
58 | | -ERR_MSG_OPEN_BAD_PEER_AS = 2 |
59 | | -ERR_MSG_OPEN_BAD_BGP_ID = 3 |
60 | | -ERR_MSG_OPEN_UNSUP_OPT_PARAM = 4 |
61 | | -ERR_MSG_OPEN_UNACCPT_HOLD_TIME = 6 |
62 | | - |
63 | | -ERR_MSG_UPDATE_MALFORMED_ATTR_LIST = 1 |
64 | | -ERR_MSG_UPDATE_UNRECOGNIZED_WELLKNOWN_ATTR = 2 |
65 | | -ERR_MSG_UPDATE_MISSING_WELLKNOWN_ATTR = 3 |
66 | | -ERR_MSG_UPDATE_ATTR_FLAGS = 4 |
67 | | -ERR_MSG_UPDATE_ATTR_LEN = 5 |
68 | | -ERR_MSG_UPDATE_INVALID_ORIGIN = 6 |
69 | | -ERR_MSG_UPDATE_INVALID_NEXTHOP = 8 |
70 | | -ERR_MSG_UPDATE_OPTIONAL_ATTR = 9 |
71 | | -ERR_MSG_UPDATE_INVALID_NETWORK_FIELD = 10 |
72 | | -ERR_MSG_UPDATE_MALFORMED_ASPATH = 11 |
73 | | - |
74 | | -# BGP attribute flags |
75 | | -ATTR_OPTIONAL = 1 << 7 |
76 | | -ATTR_TRANSITIVE = 1 << 6 |
77 | | -ATTR_PARTIAL = 1 << 5 |
78 | | -ATTR_EXTENDED_LEN = 1 << 4 |
79 | | - |
80 | | -# BGP attribute types |
81 | | -ATTR_TYPE_ORIGIN = 1 |
82 | | -ATTR_TYPE_AS_PATH = 2 |
83 | | -ATTR_TYPE_NEXT_HOP = 3 |
84 | | -ATTR_TYPE_MULTI_EXIT_DISC = 4 |
85 | | -ATTR_TYPE_LOCAL_PREF = 5 |
86 | | -ATTR_TYPE_ATOMIC_AGGREGATE = 6 |
87 | | -ATTR_TYPE_AGGREGATOR = 7 |
88 | | -ATTR_TYPE_COMMUNITY = 8 |
89 | | - |
90 | | -# Exception classes |
91 | | - |
92 | | -class BGPException(Exception): |
93 | | - def __init__(self, protocol=None): |
94 | | - self.protocol = protocol |
95 | | - |
96 | | -class NotificationSent(BGPException): |
97 | | - def __init__(self, protocol, error, suberror, data): |
98 | | - BGPException.__init__(self, protocol) |
99 | | - |
100 | | - self.error = error |
101 | | - self.suberror = suberror |
102 | | - self.data = data |
103 | | - |
104 | | -class BadMessageLength(BGPException): |
105 | | - pass |
106 | | - |
107 | | -class AttributeException(BGPException): |
108 | | - def __init__(self, suberror, data=''): |
109 | | - BGPException.__init__(self) |
110 | | - |
111 | | - self.error = ERR_MSG_UPDATE |
112 | | - self.suberror = suberror |
113 | | - self.data = data |
114 | | - |
115 | | -# Interfaces |
116 | | - |
117 | | -class IBGPPeering(Interface): |
118 | | - """ |
119 | | - Interface for notifications from the BGP protocol / FSM |
120 | | - """ |
121 | | - |
122 | | - def notificationSent(self, protocol, error, suberror, data): |
123 | | - """ |
124 | | - Called when a BGP Notification message was sent. |
125 | | - """ |
126 | | - |
127 | | - def connectionClosed(self, protocol): |
128 | | - """ |
129 | | - Called when the BGP connection has been closed (in error or not). |
130 | | - """ |
131 | | - |
132 | | - def completeInit(self, protocol): |
133 | | - """ |
134 | | - Called when BGP resources should be initialized. |
135 | | - """ |
136 | | - |
137 | | - def sessionEstablished(self, protocol): |
138 | | - """ |
139 | | - Called when the BGP session has reached the Established state |
140 | | - """ |
141 | | - |
142 | | - def connectRetryEvent(self, protocol): |
143 | | - """ |
144 | | - Called when the connect-retry timer expires. A new connection should |
145 | | - be initiated. |
146 | | - """ |
147 | | - |
148 | | -class IPPrefix(object): |
149 | | - """Class that represents an IP prefix""" |
150 | | - |
151 | | - def __init__(self, ipprefix): |
152 | | - self.prefix = None # packed ip string |
153 | | - |
154 | | - if type(ipprefix) is tuple: |
155 | | - prefix, self.prefixlen = ipprefix |
156 | | - if type(prefix) is str: |
157 | | - # tuple (ipstr, prefixlen) |
158 | | - self.prefix = prefix |
159 | | - elif type(prefix) is int: |
160 | | - # tuple (ipint, prefixlen) |
161 | | - self.prefix = struct.pack('!I', prefix)[0] |
162 | | - else: |
163 | | - # Assume prefix is a sequence of octets |
164 | | - self.prefix = "".join(map(chr, prefix)) |
165 | | - elif type(ipprefix) is str: |
166 | | - # textual form |
167 | | - prefix, prefixlen = ipprefix.split('/') |
168 | | - self.prefix = "".join([chr(int(o)) for o in prefix.split('.')]) |
169 | | - self.prefixlen = int(prefixlen) |
170 | | - # TODO: IPv6 |
171 | | - else: |
172 | | - raise ValueError |
173 | | - |
174 | | - def __repr__(self): |
175 | | - return repr(".".join([str(ord(o)) for o in self.prefix]) + '/%d' % self.prefixlen) |
176 | | - # TODO: IPv6 |
177 | | - |
178 | | -class IPv4IP(IPPrefix): |
179 | | - """Class that represents a single non-prefix IPv4 IP.""" |
180 | | - |
181 | | - def __init__(self, ip): |
182 | | - if type(ip) is str and len(ip) > 4: |
183 | | - super(IPv4IP, self).__init__(ip + '/32') |
184 | | - else: |
185 | | - super(IPv4IP, self).__init__((ip, 32)) |
186 | | - |
187 | | - def __repr__(self): |
188 | | - return repr(".".join([str(ord(o)) for o in self.prefix])) |
189 | | - |
190 | | -class Attribute(object): |
191 | | - """Base class for all BGP attribute classes""" |
192 | | - |
193 | | - typeToClass = {} |
194 | | - name = 'Attribute' |
195 | | - |
196 | | - def __init__(self, attrTuple=None): |
197 | | - super(Attribute, self).__init__() |
198 | | - |
199 | | - self.tuple = attrTuple |
200 | | - |
201 | | - if attrTuple is None: |
202 | | - self.optional = 0 |
203 | | - self.transitive = 0 |
204 | | - self.partial = 0 |
205 | | - self.extendedLength = 0 |
206 | | - |
207 | | - self.value = None |
208 | | - self.typeCode = 0 |
209 | | - else: |
210 | | - flags, typeCode, value = attrTuple |
211 | | - self.optional = (flags & ATTR_OPTIONAL != 0) |
212 | | - self.transitive = (flags & ATTR_TRANSITIVE != 0) |
213 | | - self.partial = (flags & ATTR_PARTIAL != 0) |
214 | | - self.extendedLength = (flags & ATTR_EXTENDED_LEN != 0) |
215 | | - |
216 | | - self.value = value |
217 | | - self.typeCode = typeCode |
218 | | - |
219 | | - if typeCode not in self.typeToClass: |
220 | | - if self.optional and self.transitive: |
221 | | - # Unrecognized optional, transitive attribute, set partial bit |
222 | | - self.partial = 1 |
223 | | - elif not self.optional: |
224 | | - raise AttributeException(ERR_MSG_UPDATE_UNRECOGNIZED_WELLKNOWN_ATTR, attrTuple) |
225 | | - |
226 | | - self.type = self.__class__ |
227 | | - |
228 | | - def __eq__(self, other): |
229 | | - return self is other or \ |
230 | | - (type(self) is type(other) and self.flags == other.flags and self.value == other.value) |
231 | | - |
232 | | - def __ne__(self, other): |
233 | | - return not self.__eq__(other) |
234 | | - |
235 | | - def __repr__(self): |
236 | | - return repr(self.tuple) |
237 | | - |
238 | | - def __str__(self): |
239 | | - return str(self.value) |
240 | | - |
241 | | - def flagsStr(self): |
242 | | - """Returns a string with characters symbolizing the flags |
243 | | - set to True""" |
244 | | - |
245 | | - s = '' |
246 | | - for c, f in [('O', self.optional), ('T', self.transitive), |
247 | | - ('P', self.partial), ('E', self.extendedLength)]: |
248 | | - if f: s += c |
249 | | - return s |
250 | | - |
251 | | - @classmethod |
252 | | - def fromTuple(cls, attrTuple): |
253 | | - """Instantiates an Attribute inheritant of the right type for a |
254 | | - given attribute tuple. |
255 | | - """ |
256 | | - |
257 | | - return cls.typeToClass.get(attrTuple[1], cls)(attrTuple) |
258 | | - |
259 | | -class OriginAttribute(Attribute): |
260 | | - name = 'Origin' |
261 | | - |
262 | | - ORIGIN_IGP = 0 |
263 | | - ORIGIN_EGP = 1 |
264 | | - ORIGIN_INCOMPLETE = 2 |
265 | | - |
266 | | - def __init__(self, attrTuple): |
267 | | - super(OriginAttribute, self).__init__(attrTuple) |
268 | | - |
269 | | - value = attrTuple[2] |
270 | | - |
271 | | - if self.optional or not self.transitive: |
272 | | - raise AttributeException(ERR_MSG_UPDATE_ATTR_FLAGS, attrTuple) |
273 | | - if len(value) != 1: |
274 | | - raise AttributeException(ERR_MSG_UPDATE_ATTR_LEN, attrTuple) |
275 | | - if ord(value) not in (self.ORIGIN_IGP, self.ORIGIN_EGP, self.ORIGIN_INCOMPLETE): |
276 | | - raise AttributeException(ERR_MSG_UPDATE_INVALID_ORIGIN, attrTuple) |
277 | | - |
278 | | - self.value = ord(value) |
279 | | - |
280 | | -class ASPathAttribute(Attribute): |
281 | | - name = 'AS Path' |
282 | | - |
283 | | - def __init__(self, attrTuple): |
284 | | - super(ASPathAttribute, self).__init__(attrTuple) |
285 | | - |
286 | | - value = attrTuple[2] |
287 | | - |
288 | | - if self.optional or not self.transitive: |
289 | | - raise AttributeException(ERR_MSG_UPDATE_ATTR_FLAGS, attrTuple) |
290 | | - if len(value) == 0: |
291 | | - raise AttributeException(ERR_MSG_UPDATE_ATTR_LEN, attrTuple) |
292 | | - |
293 | | - self.value = [] |
294 | | - postfix = value |
295 | | - try: |
296 | | - # Loop over all path segments |
297 | | - while len(postfix) > 0: |
298 | | - type, length = struct.unpack('!BB', postfix[:2]) |
299 | | - asPath = list(struct.unpack('!%dH' % length, postfix[2:2+length*2])) |
300 | | - |
301 | | - postfix = postfix[2+length*2:] |
302 | | - self.value.append( (type, asPath) ) |
303 | | - except: |
304 | | - raise AttributeException(ERR_MSG_UPDATE_MALFORMED_ASPATH) |
305 | | - |
306 | | -class NextHopAttribute(Attribute): |
307 | | - name = 'Next Hop' |
308 | | - |
309 | | - def __init__(self, attrTuple): |
310 | | - super(NextHopAttribute, self).__init__(attrTuple) |
311 | | - |
312 | | - value = attrTuple[2] |
313 | | - |
314 | | - if self.optional or not self.transitive: |
315 | | - raise AttributeException(ERR_MSG_UPDATE_ATTR_FLAGS, attrTuple) |
316 | | - if len(value) != 4: |
317 | | - raise AttributeException(ERR_MSG_UPDATE_ATTR_LEN, attrTuple) |
318 | | - if value in (0, 2**32-1): |
319 | | - raise AttributeException(ERR_MSG_UPDATE_INVALID_NEXTHOP, attrTuple) |
320 | | - |
321 | | - self.value = IPv4IP(value) |
322 | | - |
323 | | -class MEDAttribute(Attribute): |
324 | | - name = 'MED' |
325 | | - |
326 | | - def __init__(self, attrTuple): |
327 | | - super(MEDAttribute, self).__init__(attrTuple) |
328 | | - |
329 | | - value = attrTuple[2] |
330 | | - |
331 | | - if not self.optional or self.transitive: |
332 | | - raise AttributeException(ERR_MSG_UPDATE_ATTR_FLAGS, attrTuple) |
333 | | - if len(value) != 4: |
334 | | - raise AttributeException(ERR_MSG_UPDATE_ATTR_LEN, attrTuple) |
335 | | - |
336 | | - self.value = struct.unpack('!I', value)[0] |
337 | | - |
338 | | -class LocalPrefAttribute(Attribute): |
339 | | - name = 'Local Pref' |
340 | | - |
341 | | - def __init__(self, attrTuple): |
342 | | - super(LocalPrefAttribute, self).__init__(attrTuple) |
343 | | - |
344 | | - value = attrTuple[2] |
345 | | - |
346 | | - if not self.optional or self.transitive: |
347 | | - raise AttributeException(ERR_MSG_UPDATE_ATTR_FLAGS, attrTuple) |
348 | | - if len(value) != 4: |
349 | | - raise AttributeException(ERR_MSG_UPDATE_ATTR_LEN, attrTuple) |
350 | | - |
351 | | - self.value = struct.unpack('!I', value)[0] |
352 | | - |
353 | | -class AtomicAggregateAttribute(Attribute): |
354 | | - name = 'Atomic Aggregate' |
355 | | - |
356 | | - def __init__(self, attrTuple): |
357 | | - super(AtomicAggregateAttribute, self).__init__(attrTuple) |
358 | | - |
359 | | - if self.optional: |
360 | | - raise AttributeException(ERR_MSG_UPDATE_ATTR_FLAGS, attrTuple) |
361 | | - if len(attrTuple[2]) != 0: |
362 | | - raise AttributeException(ERR_MSG_UPDATE_ATTR_LEN, attrTuple) |
363 | | - |
364 | | -class AggregatorAttribute(Attribute): |
365 | | - name = 'Aggregator' |
366 | | - |
367 | | - def __init__(self, attrTuple): |
368 | | - super(AggregatorAttribute, self).__init__(attrTuple) |
369 | | - |
370 | | - value = attrTuple[2] |
371 | | - |
372 | | - if not self.optional or not self.transitive: |
373 | | - raise AttributeException(ERR_MSG_UPDATE_ATTR_FLAGS, attrTuple) |
374 | | - if len(value) != 6: |
375 | | - raise AttributeException(ERR_MSG_UPDATE_ATTR_LEN, attrTuple) |
376 | | - |
377 | | - asn = struct.unpack('!H', value[:2])[0] |
378 | | - aggregator = IPv4IP(value[2:]) # TODO: IPv6 |
379 | | - self.value = (asn, aggregator) |
380 | | - |
381 | | -class CommunityAttribute(Attribute): |
382 | | - name = 'Community' |
383 | | - |
384 | | - def __init__(self, attrTuple): |
385 | | - super(CommunityAttribute, self).__init__(attrTuple) |
386 | | - |
387 | | - value = attrTuple[2] |
388 | | - |
389 | | - if not self.optional or not self.transitive: |
390 | | - raise AttributeException(ERR_MSG_UPDATE_ATTR_FLAGS, attrTuple) |
391 | | - if len(value) % 4 != 0: |
392 | | - raise AttributeException(ERR_MSG_UPDATE_ATTR_LEN, attrTuple) |
393 | | - |
394 | | - length = len(value) / 4 |
395 | | - self.value = list(struct.unpack('!%dI' % length, value)) |
396 | | - |
397 | | - def __str__(self): |
398 | | - return str(["%d:%d" % (c / 2**16, c % 2**16) for c in self.value]) |
399 | | - |
400 | | -Attribute.typeToClass = { |
401 | | - ATTR_TYPE_ORIGIN: OriginAttribute, |
402 | | - ATTR_TYPE_AS_PATH: ASPathAttribute, |
403 | | - ATTR_TYPE_NEXT_HOP: NextHopAttribute, |
404 | | - ATTR_TYPE_MULTI_EXIT_DISC: MEDAttribute, |
405 | | - ATTR_TYPE_LOCAL_PREF: LocalPrefAttribute, |
406 | | - ATTR_TYPE_ATOMIC_AGGREGATE: AtomicAggregateAttribute, |
407 | | - ATTR_TYPE_AGGREGATOR: AggregatorAttribute, |
408 | | - ATTR_TYPE_COMMUNITY: CommunityAttribute |
409 | | -} |
410 | | - |
411 | | -class AttributeSet(set): |
412 | | - """Class that contains a single set of attributes attached to a list of NLRIs""" |
413 | | - |
414 | | - def __init__(self, attributes): |
415 | | - """Expects a sequence of either unparsed attribute tuples, or parsed |
416 | | - Attribute inheritants. |
417 | | - """ |
418 | | - |
419 | | - self.origin, self.asPath, self.nextHop = None, None, None |
420 | | - |
421 | | - for a in attributes: |
422 | | - if type(a) is tuple: |
423 | | - attr = Attribute.fromTuple(a) |
424 | | - elif isinstance(a, Attribute): |
425 | | - attr = a |
426 | | - |
427 | | - self.add(attr) |
428 | | - |
429 | | - # Check whether all mandatory wellknown attributes are present |
430 | | - # FIXME: None.typeCode will of course not work - rewrite |
431 | | - for attr in (self.origin, self.asPath, self.nextHop): |
432 | | - if attr is None: |
433 | | - raise AttributeError(ERR_MSG_UPDATE_MISSING_WELLKNOWN_ATTR, (0, attr.typeCode, None)) |
434 | | - |
435 | | - def add(self, attr): |
436 | | - """Adds attribute attr to the set, raises KeyError if already present""" |
437 | | - |
438 | | - try: |
439 | | - super(AttributeSet, self).add(attr) |
440 | | - |
441 | | - # Add direct references for the mandatory wellknown attributes |
442 | | - if type(attr) is OriginAttribute: |
443 | | - self.origin = attr |
444 | | - elif type(attr) is ASPathAttribute: |
445 | | - self.asPath = attr |
446 | | - elif type(attr) is NextHopAttribute: |
447 | | - self.nextHop = attr |
448 | | - except KeyError: |
449 | | - # Attribute was already present |
450 | | - raise AttributeError(ERR_MSG_UPDATE_MALFORMED_ATTR_LIST) |
451 | | - |
452 | | - # FIXME: check/implement other set methods |
453 | | - |
454 | | -class FSM(object): |
455 | | - class BGPTimer(object): |
456 | | - """ |
457 | | - Timer class with a slightly different Timer interface than the |
458 | | - Twisted DelayedCall interface |
459 | | - """ |
460 | | - |
461 | | - def __init__(self, callable): |
462 | | - self.delayedCall = None |
463 | | - self.callable = callable |
464 | | - |
465 | | - def cancel(self): |
466 | | - """Cancels the timer if it was running, does nothing otherwise""" |
467 | | - |
468 | | - try: |
469 | | - self.delayedCall.cancel() |
470 | | - except (AttributeError, error.AlreadyCancelled): |
471 | | - pass |
472 | | - |
473 | | - def reset(self, secondsFromNow): |
474 | | - """Resets an already running timer, or starts it if it wasn't running.""" |
475 | | - |
476 | | - try: |
477 | | - self.delayedCall.reset(secondsFromNow) |
478 | | - except (AttributeError, error.AlreadyCalled, error.AlreadyCancelled): |
479 | | - self.delayedCall = reactor.callLater(secondsFromNow, self.callable) |
480 | | - |
481 | | - def active(self): |
482 | | - """Returns True if the timer was running, False otherwise.""" |
483 | | - |
484 | | - try: |
485 | | - return self.delayedCall.active() |
486 | | - except AttributeError: |
487 | | - return False |
488 | | - |
489 | | - protocol = None |
490 | | - |
491 | | - state = ST_IDLE |
492 | | - |
493 | | - largeHoldTime = 4*60 |
494 | | - sendNotificationWithoutOpen = True # No bullshit |
495 | | - |
496 | | - def __init__(self, bgpPeering=None, protocol=None): |
497 | | - self.bgpPeering = bgpPeering |
498 | | - self.protocol = protocol |
499 | | - |
500 | | - self.connectRetryCounter = 0 |
501 | | - self.connectRetryTime = 30 |
502 | | - self.connectRetryTimer = FSM.BGPTimer(self.connectRetryTimeEvent) |
503 | | - self.holdTime = 3 * 60 |
504 | | - self.holdTimer = FSM.BGPTimer(self.holdTimeEvent) |
505 | | - self.keepAliveTime = self.holdTime / 3 |
506 | | - self.keepAliveTimer = FSM.BGPTimer(self.keepAliveEvent) |
507 | | - |
508 | | - self.allowAutomaticStart = False |
509 | | - self.allowAutomaticStop = False |
510 | | - self.delayOpen = False |
511 | | - self.delayOpenTime = 30 |
512 | | - self.delayOpenTimer = FSM.BGPTimer(self.delayOpenEvent) |
513 | | - |
514 | | - def manualStart(self): |
515 | | - """ |
516 | | - Should be called when a BGP ManualStart event (event 1) is requested. |
517 | | - Note that a protocol instance does not yet exist at this point, |
518 | | - so this method requires some support from BGPPeering.manualStart(). |
519 | | - """ |
520 | | - |
521 | | - if self.state == ST_IDLE: |
522 | | - self.connectRetryCounter = 0 |
523 | | - self.connectRetryTimer.reset(self.connectRetryTime) |
524 | | - |
525 | | - def manualStop(self): |
526 | | - """Should be called when a BGP ManualStop event (event 2) is requested.""" |
527 | | - |
528 | | - if self.state != ST_IDLE: |
529 | | - self.protocol.sendNotification(ERR_CEASE, 0) |
530 | | - # Stop all timers |
531 | | - for timer in (self.connectRetryTimer, self.holdTimer, self.keepAliveTimer, |
532 | | - self.delayOpenTimer): |
533 | | - timer.cancel() |
534 | | - if self.bgpPeering is not None: self.bgpPeering.releaseResources() |
535 | | - self._errorClose() |
536 | | - self.connectRetryCounter = 0 |
537 | | - raise NotificationSent(self.protocol, ERR_CEASE, 0) |
538 | | - |
539 | | - self.state = ST_IDLE |
540 | | - |
541 | | - def connectionMade(self): |
542 | | - """Should be called when a TCP connection has successfully been |
543 | | - established with the peer. (events 16, 17) |
544 | | - """ |
545 | | - |
546 | | - if self.state in (ST_CONNECT, ST_ACTIVE): |
547 | | - # State Connect, Event 16 or 17 |
548 | | - if self.delayOpen: |
549 | | - self.connectRetryTimer.cancel() |
550 | | - self.delayOpenTimer.reset(self.delayOpenTime) |
551 | | - else: |
552 | | - self.connectRetryTimer.cancel() |
553 | | - if self.bgpPeering: self.bgpPeering.completeInit(self.protocol) |
554 | | - self.protocol.sendOpen() |
555 | | - self.holdTimer.reset(self.largeHoldTime) |
556 | | - self.state = ST_OPENSENT |
557 | | - |
558 | | - def connectionFailed(self): |
559 | | - """Should be called when the associated TCP connection failed, or |
560 | | - was lost. (event 18)""" |
561 | | - |
562 | | - if self.state == ST_CONNECT: |
563 | | - # State Connect, event 18 |
564 | | - if self.delayOpenTimer.active(): |
565 | | - self.connectRetryTimer.reset(self.connectRetryTime) |
566 | | - self.delayOpenTimer.cancel() |
567 | | - self.state = ST_ACTIVE |
568 | | - else: |
569 | | - self.connectRetryTimer.cancel() |
570 | | - self._closeConnection() |
571 | | - if self.bgpPeering: self.bgpPeering.releaseResources(self.protocol) |
572 | | - self.state = ST_IDLE |
573 | | - if self.bgpPeering: self.bgpPeering.connectionClosed(self.protocol) |
574 | | - elif self.state == ST_ACTIVE: |
575 | | - # State Active, event 18 |
576 | | - self.connectRetryTimer.reset(self.connectRetryTime) |
577 | | - self.delayOpenTimer.cancel() |
578 | | - if self.bgpPeering: self.bgpPeering.releaseResources(self.protocol) |
579 | | - self.connectRetryCounter += 1 |
580 | | - # TODO: osc damping |
581 | | - self.state = ST_IDLE |
582 | | - elif self.state == ST_OPENSENT: |
583 | | - # State OpenSent, event 18 |
584 | | - if self.bgpPeering: self.bgpPeering.releaseResources(self.protocol) |
585 | | - self._closeConnection() |
586 | | - self.connectRetryTimer.reset(self.connectRetryTime) |
587 | | - self.state = ST_ACTIVE |
588 | | - if self.bgpPeering: self.bgpPeering.connectionClosed(self.protocol) |
589 | | - elif self.state in (ST_OPENCONFIRM, ST_ESTABLISHED): |
590 | | - self._errorClose() |
591 | | - |
592 | | - |
593 | | - def openReceived(self): |
594 | | - """Should be called when a BGP Open message was received from |
595 | | - the peer. (events 19, 20) |
596 | | - """ |
597 | | - |
598 | | - if self.state in (ST_CONNECT, ST_ACTIVE): |
599 | | - if self.delayOpenTimer.active(): |
600 | | - # State Connect, event 20 |
601 | | - self.connectRetryTimer.cancel() |
602 | | - if self.bgpPeering: self.bgpPeering.completeInit(self.protocol) |
603 | | - self.delayOpenTimer.cancel() |
604 | | - self.protocol.sendOpen() |
605 | | - self.protocol.sendKeepAlive() |
606 | | - if self.holdTime != 0: |
607 | | - self.KeepAliveTimer.reset(self.keepAliveTime) |
608 | | - self.holdTimer.reset(self.holdTimer) |
609 | | - else: # holdTime == 0 |
610 | | - self.keepAliveTimer.cancel() |
611 | | - self.holdTimer.cancel() |
612 | | - |
613 | | - self.state = ST_OPENCONFIRM |
614 | | - else: |
615 | | - # State Connect, event 19 |
616 | | - self._errorClose() |
617 | | - |
618 | | - elif self.state == ST_OPENSENT: |
619 | | - # State OpenSent, events 19, 20 |
620 | | - self.delayOpenTimer.cancel() |
621 | | - self.connectRetryTimer.cancel() |
622 | | - self.protocol.sendKeepAlive() |
623 | | - if self.holdTime > 0: |
624 | | - self.keepAliveTimer.reset(self.keepAliveTime) |
625 | | - self.holdTimer.reset(self.holdTime) |
626 | | - self.state = ST_OPENCONFIRM |
627 | | - |
628 | | - elif self.state == ST_OPENCONFIRM: |
629 | | - # State OpenConfirm, events 19, 20 |
630 | | - # DEBUG |
631 | | - print "Running collision detection" |
632 | | - |
633 | | - # Perform collision detection |
634 | | - self.protocol.collisionDetect() |
635 | | - |
636 | | - elif self.state == ST_ESTABLISHED: |
637 | | - # State Established, event 19 or 20 |
638 | | - self.protocol.sendNotification(ERR_FSM, 0) |
639 | | - self._errorClose() |
640 | | - raise NotificationSent(self.protocol, ERR_FSM, 0) |
641 | | - |
642 | | - def headerError(self, suberror, data=''): |
643 | | - """ |
644 | | - Should be called when an invalid BGP message header was received. |
645 | | - (event 21) |
646 | | - """ |
647 | | - |
648 | | - self.protocol.sendNotification(ERR_MSG_HDR, suberror, data) |
649 | | - # Note: RFC4271 states that we should send ERR_FSM in the |
650 | | - # Established state, which contradicts earlier statements. |
651 | | - self._errorClose() |
652 | | - raise NotificationSent(self.protocol, ERR_MSG_HDR, suberror, data) |
653 | | - |
654 | | - def openMessageError(self, suberror, data=''): |
655 | | - """ |
656 | | - Should be called when an invalid BGP Open message was received. |
657 | | - (event 22) |
658 | | - """ |
659 | | - |
660 | | - self.protocol.sendNotification(ERR_MSG_OPEN, suberror, data) |
661 | | - # Note: RFC4271 states that we should send ERR_FSM in the |
662 | | - # Established state, which contradicts earlier statements. |
663 | | - self._errorClose() |
664 | | - raise NotificationSent(self.protocol, ERR_MSG_OPEN, suberror, data) |
665 | | - |
666 | | - def keepAliveReceived(self): |
667 | | - """Should be called when a BGP KeepAlive packet was received |
668 | | - from the peer. (event 26) |
669 | | - """ |
670 | | - |
671 | | - if self.state == ST_OPENCONFIRM: |
672 | | - # State OpenSent, event 26 |
673 | | - self.holdTimer.reset(self.holdTime) |
674 | | - self.state = ST_ESTABLISHED |
675 | | - self.protocol.deferred.callback(self.protocol) |
676 | | - elif self.state == ST_ESTABLISHED: |
677 | | - # State Established, event 26 |
678 | | - self.holdTimer.reset(self.holdTime) |
679 | | - elif self.state in (ST_CONNECT, ST_ACTIVE): |
680 | | - # States Connect, Active, event 26 |
681 | | - self._errorClose() |
682 | | - |
683 | | - def versionError(self): |
684 | | - """Should be called when a BGP Notification Open Version Error |
685 | | - message was received from the peer. (event 24) |
686 | | - """ |
687 | | - |
688 | | - if self.state in (ST_OPENSENT, ST_OPENCONFIRM): |
689 | | - # State OpenSent, event 24 |
690 | | - self.connectRetryTimer.cancel() |
691 | | - if self.bgpPeering: self.bgpPeering.releaseResources(self.protocol) |
692 | | - self._closeConnection() |
693 | | - self.state = ST_IDLE |
694 | | - elif self.state in (ST_CONNECT, ST_ACTIVE): |
695 | | - # State Connect, event 24 |
696 | | - self._errorClose() |
697 | | - |
698 | | - def notificationReceived(self, error, suberror): |
699 | | - """Should be called when a BGP Notification message was |
700 | | - received from the peer. (events 24, 25) |
701 | | - """ |
702 | | - |
703 | | - if error == ERR_MSG_OPEN and suberror == 1: |
704 | | - # Event 24 |
705 | | - self.versionError() |
706 | | - else: |
707 | | - if self.state != ST_IDLE: |
708 | | - # State != Idle, events 24, 25 |
709 | | - self._errorClose() |
710 | | - |
711 | | - def updateReceived(self): |
712 | | - """Called when a valid BGP Update message was received. (event 27)""" |
713 | | - |
714 | | - if self.state == ST_ESTABLISHED: |
715 | | - # State Established, event 27 |
716 | | - if self.holdTime != 0: |
717 | | - self.holdTimer.reset(self.holdTime) |
718 | | - elif self.state in (ST_ACTIVE, ST_CONNECT): |
719 | | - # States Active, Connect, event 27 |
720 | | - self._errorClose() |
721 | | - elif self.state in (ST_OPENSENT, ST_OPENCONFIRM): |
722 | | - # States OpenSent, OpenConfirm, event 27 |
723 | | - self.protocol.sendNotification(ERR_FSM, 0) |
724 | | - self._errorClose() |
725 | | - raise NotificationSent(self.protocol, ERR_FSM, 0) |
726 | | - |
727 | | - def updateError(self, suberror, data=''): |
728 | | - """Called when an invalid BGP Update message was received. (event 28)""" |
729 | | - |
730 | | - if self.state == ST_ESTABLISHED: |
731 | | - # State Established, event 28 |
732 | | - self.protocol.sendNotification(ERR_MSG_UPDATE, suberror, data) |
733 | | - self._errorClose() |
734 | | - raise NotificationSent(self.protocol, ERR_MSG_UPDATE, suberror, data) |
735 | | - elif self.state in (ST_ACTIVE, ST_CONNECT): |
736 | | - # States Active, Connect, event 28 |
737 | | - self._errorClose() |
738 | | - elif self.state in (ST_OPENSENT, ST_OPENCONFIRM): |
739 | | - # States OpenSent, OpenConfirm, event 28 |
740 | | - self.protocol.sendNotification(self.protocol, ERR_FSM, 0) |
741 | | - self._errorClose() |
742 | | - raise NotificationSent(self.protocol, ERR_FSM, 0) |
743 | | - |
744 | | - def openCollisionDump(self): |
745 | | - """Called when the collision detection algorithm determined |
746 | | - that the associated connection should be dumped. |
747 | | - (event 23) |
748 | | - """ |
749 | | - |
750 | | - # DEBUG |
751 | | - print "Collided, closing." |
752 | | - |
753 | | - if self.state == ST_IDLE: |
754 | | - return |
755 | | - elif self.state in (ST_OPENSENT, ST_OPENCONFIRM, ST_ESTABLISHED): |
756 | | - self.protocol.sendNotification(ERR_CEASE, 0) |
757 | | - |
758 | | - self._errorClose() |
759 | | - raise NotificationSent(self.protocol, ERR_CEASE, 0) |
760 | | - |
761 | | - def delayOpenEvent(self): |
762 | | - """Called when the DelayOpenTimer expires. (event 12)""" |
763 | | - |
764 | | - assert(self.delayOpen) |
765 | | - |
766 | | - # DEBUG |
767 | | - print "Delay Open event" |
768 | | - |
769 | | - if self.state == ST_CONNECT: |
770 | | - # State Connect, event 12 |
771 | | - self.protocol.sendOpen() |
772 | | - self.holdTimer.reset(self.largeHoldTime) |
773 | | - self.state = ST_OPENSENT |
774 | | - elif self.state == ST_ACTIVE: |
775 | | - # State Active, event 12 |
776 | | - self.connectRetryTimer.cancel() |
777 | | - self.delayOpenTimer.cancel() |
778 | | - if self.bgpPeering: self.bgpPeering.completeInit(self.protocol) |
779 | | - self.sendOpen() |
780 | | - self.holdTimer.reset(self.largeHoldTime) |
781 | | - self.state = ST_OPENSENT |
782 | | - elif self.state != ST_IDLE: |
783 | | - # State OpenSent, OpenConfirm, Established, event 12 |
784 | | - self.protocol.sendNotification(ERR_FSM, 0) |
785 | | - self._errorClose() |
786 | | - raise NotificationSent(self.protocol, ERR_FSM, 0) |
787 | | - |
788 | | - def keepAliveEvent(self): |
789 | | - """Called when the KeepAliveTimer expires. (event 11)""" |
790 | | - |
791 | | - # DEBUG |
792 | | - print "KeepAlive event" |
793 | | - |
794 | | - if self.state in (ST_OPENCONFIRM, ST_ESTABLISHED): |
795 | | - # State OpenConfirm, Established, event 11 |
796 | | - self.protocol.sendKeepAlive() |
797 | | - if self.holdTime > 0: |
798 | | - self.keepAliveTimer.reset(self.keepAliveTime) |
799 | | - elif self.state in (ST_CONNECT, ST_ACTIVE): |
800 | | - self._errorClose() |
801 | | - |
802 | | - def holdTimeEvent(self): |
803 | | - """Called when the HoldTimer expires. (event 10)""" |
804 | | - |
805 | | - if self.state in (ST_OPENSENT, ST_OPENCONFIRM, ST_ESTABLISHED): |
806 | | - # States OpenSent, OpenConfirm, Established, event 10 |
807 | | - self.protocol.sendNotification(ERR_HOLD_TIMER_EXPIRED, 0) |
808 | | - self.connectRetryTimer.cancel() |
809 | | - self._errorClose() |
810 | | - self.connectRetryCounter += 1 |
811 | | - # TODO: peer osc damping |
812 | | - self.state = ST_IDLE |
813 | | - |
814 | | - #self.protocol.deferred.errback(HoldTimerExpired(self.protocol)) |
815 | | - elif self.state in (ST_CONNECT, ST_ACTIVE): |
816 | | - self._errorClose() |
817 | | - |
818 | | - def connectRetryTimeEvent(self): |
819 | | - """Called when the ConnectRetryTimer expires. (event 9)""" |
820 | | - |
821 | | - if self.state in (ST_CONNECT, ST_ACTIVE): |
822 | | - # State Connect, event 9 |
823 | | - self._closeConnection() |
824 | | - self.connectRetryTimer.reset(self.connectRetryTime) |
825 | | - self.delayOpenTimer.cancel() |
826 | | - # Initiate TCP connection |
827 | | - if self.bgpPeering: self.bgpPeering.connectRetryEvent(self.protocol) |
828 | | - elif self.state != ST_IDLE: |
829 | | - # State OpenSent, OpenConfirm, Established, event 12 |
830 | | - self.protocol.sendNotification(ERR_FSM, 0) |
831 | | - self._errorClose() |
832 | | - raise NotificationSent(self.protocol, ERR_FSM, 0) |
833 | | - |
834 | | - def _errorClose(self): |
835 | | - """Internal method that closes a connection and returns the state |
836 | | - to IDLE. |
837 | | - """ |
838 | | - |
839 | | - # Stop the timers |
840 | | - for timer in (self.connectRetryTimer, self.delayOpenTimer, self.holdTimer, |
841 | | - self.keepAliveTimer): |
842 | | - timer.cancel() |
843 | | - |
844 | | - # Release BGP resources (routes, etc) |
845 | | - if self.bgpPeering: self.bgpPeering.releaseResources(self.protocol) |
846 | | - |
847 | | - self._closeConnection() |
848 | | - |
849 | | - self.connectRetryCounter += 1 |
850 | | - self.state = ST_IDLE |
851 | | - |
852 | | - if self.bgpPeering: self.bgpPeering.connectionClosed(self.protocol) |
853 | | - |
854 | | - def _closeConnection(self): |
855 | | - """Internal method that close the connection if a valid BGP protocol |
856 | | - instance exists. |
857 | | - """ |
858 | | - |
859 | | - if self.protocol is not None: |
860 | | - self.protocol.closeConnection() |
861 | | - |
862 | | - |
863 | | -class BGP(protocol.Protocol): |
864 | | - """Protocol class for BGP 4""" |
865 | | - |
866 | | - def __init__(self): |
867 | | - self.deferred = defer.Deferred() |
868 | | - self.fsm = None |
869 | | - |
870 | | - self.disconnected = False |
871 | | - self.receiveBuffer = '' |
872 | | - |
873 | | - def connectionMade(self): |
874 | | - """ |
875 | | - Starts the initial negotiation of the protocol |
876 | | - """ |
877 | | - |
878 | | - # Set transport socket options |
879 | | - self.transport.setTcpNoDelay(True) |
880 | | - |
881 | | - # DEBUG |
882 | | - print "Connection established" |
883 | | - |
884 | | - try: |
885 | | - self.fsm.connectionMade() |
886 | | - except NotificationSent, e: |
887 | | - self.deferred.errback(e) |
888 | | - |
889 | | - def connectionLost(self, reason): |
890 | | - """Called when the associated connection was lost.""" |
891 | | - |
892 | | - # Don't do anything if we closed the connection explicitly ourselves |
893 | | - if self.disconnected: return |
894 | | - |
895 | | - # DEBUG |
896 | | - print "Connection lost" |
897 | | - |
898 | | - try: |
899 | | - self.fsm.connectionFailed() |
900 | | - except NotificationSent, e: |
901 | | - self.deferred.errback(e) |
902 | | - |
903 | | - def dataReceived(self, data): |
904 | | - """Appends newly received data to the receive buffer, and |
905 | | - then attempts to parse as many BGP messages as possible. |
906 | | - """ |
907 | | - |
908 | | - # Buffer possibly incomplete data first |
909 | | - self.receiveBuffer += data |
910 | | - |
911 | | - # Attempt to parse as many messages as possible |
912 | | - while(self.parseBuffer()): pass |
913 | | - |
914 | | - def closeConnection(self): |
915 | | - """Close the connection""" |
916 | | - |
917 | | - if self.transport.connected: |
918 | | - self.transport.loseConnection() |
919 | | - self.disconnected = True |
920 | | - |
921 | | - def sendOpen(self): |
922 | | - """Sends a BGP Open message to the peer""" |
923 | | - |
924 | | - # DEBUG |
925 | | - print "Sending Open" |
926 | | - |
927 | | - self.transport.write(self.constructOpen()) |
928 | | - |
929 | | - def sendKeepAlive(self): |
930 | | - """Sends a BGP KeepAlive message to the peer""" |
931 | | - |
932 | | - self.transport.write(self.constructKeepAlive()) |
933 | | - |
934 | | - def sendNotification(self, error, suberror, data=''): |
935 | | - """Sends a BGP Notification message to the peer |
936 | | - """ |
937 | | - |
938 | | - self.transport.write(self.constructNotification(error, suberror, data)) |
939 | | - |
940 | | - def constructHeader(self, message, type): |
941 | | - """Prepends the mandatory header to a constructed BGP message""" |
942 | | - |
943 | | - return struct.pack('!16sHB', |
944 | | - chr(255)*16, |
945 | | - len(message)+19, |
946 | | - type) + message |
947 | | - |
948 | | - def constructOpen(self): |
949 | | - """Constructs a BGP Open message""" |
950 | | - |
951 | | - msg = struct.pack('!BHHIB', |
952 | | - VERSION, |
953 | | - self.factory.myASN, |
954 | | - self.fsm.holdTime, |
955 | | - self.factory.bgpId, |
956 | | - 0) |
957 | | - |
958 | | - # TODO: support optional parameters |
959 | | - |
960 | | - return self.constructHeader(msg, MSG_OPEN) |
961 | | - |
962 | | - def constructKeepAlive(self): |
963 | | - """Constructs a BGP KeepAlive message""" |
964 | | - |
965 | | - return self.constructHeader('', MSG_KEEPALIVE) |
966 | | - |
967 | | - def constructNotification(self, error, suberror=0, data=''): |
968 | | - """Constructs a BGP Notification message""" |
969 | | - |
970 | | - msg = struct.pack('!BB', error, suberror) + data |
971 | | - return self.constructHeader(msg, MSG_NOTIFICATION) |
972 | | - |
973 | | - def parseBuffer(self): |
974 | | - """Parse received data in receiveBuffer""" |
975 | | - |
976 | | - buf = self.receiveBuffer |
977 | | - |
978 | | - if len(buf) < HDR_LEN: |
979 | | - # Every BGP message is at least 19 octets. Maybe the rest |
980 | | - # hasn't arrived yet. |
981 | | - return False |
982 | | - |
983 | | - # Check whether the first 16 octets of the buffer consist of |
984 | | - # the BGP marker (all bits one) |
985 | | - if buf[:16] != chr(255)*16: |
986 | | - self.fsm.headerError(ERR_MSG_HDR_CONN_NOT_SYNC) |
987 | | - |
988 | | - # Parse the header |
989 | | - try: |
990 | | - marker, length, type = struct.unpack('!16sHB', buf[:HDR_LEN]) |
991 | | - except: |
992 | | - self.fsm.headerError(ERR_MSG_HDR_CONN_NOT_SYNC) |
993 | | - |
994 | | - # Check the length of the message |
995 | | - if length < HDR_LEN or length > MAX_LEN: |
996 | | - self.fsm.headerError(ERR_MSG_HDR_BAD_MSG_LEN, struct.pack('!H', length)) |
997 | | - |
998 | | - # Check whether the entire message is already available |
999 | | - if len(buf) < length: return False |
1000 | | - |
1001 | | - message = buf[HDR_LEN:length] |
1002 | | - try: |
1003 | | - try: |
1004 | | - if type == MSG_OPEN: |
1005 | | - self.openReceived(*self.parseOpen(message)) |
1006 | | - elif type == MSG_UPDATE: |
1007 | | - self.updateReceived(*self.parseUpdate(message)) |
1008 | | - elif type == MSG_KEEPALIVE: |
1009 | | - self.parseKeepAlive(message) |
1010 | | - self.keepAliveReceived() |
1011 | | - elif type == MSG_NOTIFICATION: |
1012 | | - self.notificationReceived(*self.parseNotification(message)) |
1013 | | - else: # Unknown message type |
1014 | | - self.fsm.headerError(ERR_MSG_HDR_BAD_MSG_TYPE, chr(type)) |
1015 | | - except BadMessageLength: |
1016 | | - self.fsm.headerError(ERR_MSG_HDR_BAD_MSG_LEN, struct.pack('!H', length)) |
1017 | | - except NotificationSent, e: |
1018 | | - self.deferred.errback(e) |
1019 | | - |
1020 | | - # Message successfully processed, jump to next message |
1021 | | - self.receiveBuffer = self.receiveBuffer[length:] |
1022 | | - return True |
1023 | | - |
1024 | | - def parseOpen(self, message): |
1025 | | - """Parses a BGP Open message""" |
1026 | | - |
1027 | | - try: |
1028 | | - peerVersion, peerASN, peerHoldTime, peerBgpId, paramLen = struct.unpack('!BHHIB', message[:10]) |
1029 | | - except: |
1030 | | - raise BadMessageLength(self) |
1031 | | - |
1032 | | - # Check whether these values are acceptable |
1033 | | - |
1034 | | - if peerVersion != VERSION: |
1035 | | - self.fsm.openMessageError(ERR_MSG_OPEN_UNSUP_VERSION, |
1036 | | - struct.pack('!B', VERSION)) |
1037 | | - |
1038 | | - if peerASN in (0, 2**16-1): |
1039 | | - self.fsm.openMessageError(ERR_MSG_OPEN_BAD_PEER_AS) |
1040 | | - |
1041 | | - # Hold Time is negotiated and/or rejected later |
1042 | | - |
1043 | | - if peerBgpId in (0, 2**32-1, self.bgpPeering.bgpId): |
1044 | | - self.fsm.openMessageError(ERR_MSG_OPEN_BAD_BGP_ID) |
1045 | | - |
1046 | | - # TODO: optional parameters |
1047 | | - |
1048 | | - return peerVersion, peerASN, peerHoldTime, peerBgpId |
1049 | | - |
1050 | | - def parseUpdate(self, message): |
1051 | | - """Parses a BGP Update message""" |
1052 | | - |
1053 | | - try: |
1054 | | - withdrawnLen = struct.unpack('!H', message[:2])[0] |
1055 | | - withdrawnPrefixesData = message[2:withdrawnLen+2] |
1056 | | - attrLen = struct.unpack('!H', message[withdrawnLen+2:withdrawnLen+4])[0] |
1057 | | - attributesData = message[withdrawnLen+4:withdrawnLen+4+attrLen] |
1058 | | - nlriData = message[withdrawnLen+4+attrLen:] |
1059 | | - |
1060 | | - withdrawnPrefixes = BGP.parseEncodedPrefixList(withdrawnPrefixesData) |
1061 | | - attributes = BGP.parseEncodedAttributes(attributesData) |
1062 | | - nlri = BGP.parseEncodedPrefixList(nlriData) |
1063 | | - except BGPException, e: |
1064 | | - if (e.error, e.suberror) == (ERR_MSG_UPDATE, ERR_MSG_UPDATE_INVALID_NETWORK_FIELD): |
1065 | | - self.fsm.updateError(e.suberror) |
1066 | | - else: |
1067 | | - raise |
1068 | | - except: |
1069 | | - # RFC4271 dictates that we send ERR_MSG_UPDATE Malformed Attribute List |
1070 | | - # in this case |
1071 | | - self.fsm.updateError(ERR_MSG_UPDATE_MALFORMED_ATTR_LIST) |
1072 | | - |
1073 | | - return withdrawnPrefixes, attributes, nlri |
1074 | | - |
1075 | | - def parseKeepAlive(self, message): |
1076 | | - """Parses a BGP KeepAlive message""" |
1077 | | - |
1078 | | - # KeepAlive body must be empty |
1079 | | - if len(message) != 0: raise BadMessageLength(self) |
1080 | | - |
1081 | | - def parseNotification(self, message): |
1082 | | - """Parses a BGP Notification message""" |
1083 | | - |
1084 | | - try: |
1085 | | - error, suberror = struct.unpack('!BB', message[:2]) |
1086 | | - except: |
1087 | | - raise BadMessageLength(self) |
1088 | | - |
1089 | | - return error, suberror, message[2:] |
1090 | | - |
1091 | | - def openReceived(self, version, ASN, holdTime, bgpId): |
1092 | | - """Called when a BGP Open message was received.""" |
1093 | | - |
1094 | | - # DEBUG |
1095 | | - print "OPEN: version:", version, "ASN:", ASN, "hold time:", \ |
1096 | | - holdTime, "id:", bgpId |
1097 | | - |
1098 | | - self.peerId = bgpId |
1099 | | - self.bgpPeering.setPeerId(bgpId) |
1100 | | - |
1101 | | - # Perform collision detection |
1102 | | - self.collisionDetect() |
1103 | | - |
1104 | | - self.negotiateHoldTime(holdTime) |
1105 | | - self.fsm.openReceived() |
1106 | | - |
1107 | | - # DEBUG |
1108 | | - print "State is now:", stateDescr[self.fsm.state] |
1109 | | - |
1110 | | - def updateReceived(self, withdrawnPrefixes, attributes, nlri): |
1111 | | - """Called when a BGP Update message was received.""" |
1112 | | - |
1113 | | - try: |
1114 | | - attrSet = AttributeSet(attributes) |
1115 | | - except AttributeException, e: |
1116 | | - if e.suberror in (ERR_MSG_UPDATE_UNRECOGNIZED_WELLKNOWN_ATTR, |
1117 | | - ERR_MSG_UPDATE_MISSING_WELLKNOWN_ATTR): |
1118 | | - # e.data is a typecode |
1119 | | - self.fsm.updateError(e.suberror, chr(e.data)) |
1120 | | - else: |
1121 | | - # e.data is an attribute tuple |
1122 | | - self.fsm.updateError(e.suberror, self.encodeAttribute(e.data)) |
1123 | | - |
1124 | | - # DEBUG |
1125 | | - print "UPDATE:", withdrawnPrefixes, nlri |
1126 | | - for a in attrSet: |
1127 | | - print a.name, a |
1128 | | - |
1129 | | - self.fsm.updateReceived() |
1130 | | - |
1131 | | - def keepAliveReceived(self): |
1132 | | - """Called when a BGP KeepAlive message was received. |
1133 | | - """ |
1134 | | - |
1135 | | - assert self.fsm.holdTimer.active() |
1136 | | - |
1137 | | - # DEBUG |
1138 | | - print "KEEPALIVE" |
1139 | | - |
1140 | | - self.fsm.keepAliveReceived() |
1141 | | - |
1142 | | - # DEBUG |
1143 | | - print "State is now:", stateDescr[self.fsm.state] |
1144 | | - |
1145 | | - def notificationReceived(self, error, suberror, data=''): |
1146 | | - """Called when a BGP Notification message was received. |
1147 | | - """ |
1148 | | - |
1149 | | - # DEBUG |
1150 | | - print "NOTIFICATION:", error, suberror |
1151 | | - |
1152 | | - self.fsm.notificationReceived(error, suberror) |
1153 | | - |
1154 | | - def negotiateHoldTime(self, holdTime): |
1155 | | - """Negotiates the hold time""" |
1156 | | - |
1157 | | - self.fsm.holdTime = min(self.fsm.holdTime, holdTime) |
1158 | | - if self.fsm.holdTime != 0 and self.fsm.holdTime < 3: |
1159 | | - self.fsm.openMessageError(ERR_MSG_OPEN_UNACCPT_HOLD_TIME) |
1160 | | - |
1161 | | - # Derived times |
1162 | | - self.fsm.keepAliveTime = self.fsm.holdTime / 3 |
1163 | | - |
1164 | | - # DEBUG |
1165 | | - print "hold time:", self.fsm.holdTime, "keepalive time:", self.fsm.keepAliveTime |
1166 | | - |
1167 | | - def collisionDetect(self): |
1168 | | - """Performs collision detection. Outsources to factory class BGPPeering.""" |
1169 | | - |
1170 | | - return self.bgpPeering.collisionDetect(self) |
1171 | | - |
1172 | | - def isOutgoing(self): |
1173 | | - """Returns True when this protocol represents an outgoing connection, |
1174 | | - and False otherwise.""" |
1175 | | - |
1176 | | - return (self.transport.getPeer().port == PORT) |
1177 | | - |
1178 | | - @staticmethod |
1179 | | - def parseEncodedPrefixList(data): |
1180 | | - """Parses an RFC4271 encoded blob of BGP prefixes into a list""" |
1181 | | - |
1182 | | - prefixes = [] |
1183 | | - postfix = data |
1184 | | - while len(postfix) > 0: |
1185 | | - prefixLen = ord(postfix[0]) |
1186 | | - if prefixLen > 32: |
1187 | | - raise BGPError(ERR_MSG_UPDATE, ERR_MSG_UPDATE_INVALID_NETWORK_FIELD) |
1188 | | - |
1189 | | - octetLen, remainder = prefixLen / 8, prefixLen % 8 |
1190 | | - if remainder > 0: |
1191 | | - # prefix length doesn't fall on octet boundary |
1192 | | - octetLen += 1 |
1193 | | - |
1194 | | - prefixData = map(ord, postfix[1:octetLen+1]) |
1195 | | - # Zero the remaining bits in the last octet if it didn't fall |
1196 | | - # on an octet boundary |
1197 | | - if remainder > 0: |
1198 | | - prefixData[-1] = prefixData[-1] & (255 << (8-remainder)) |
1199 | | - |
1200 | | - prefixes.append(IPPrefix((prefixData, prefixLen))) |
1201 | | - |
1202 | | - # Next prefix |
1203 | | - postfix = postfix[octetLen+1:] |
1204 | | - |
1205 | | - return prefixes |
1206 | | - |
1207 | | - @staticmethod |
1208 | | - def parseEncodedAttributes(data): |
1209 | | - """Parses an RFC4271 encoded blob of BGP prefixes into a list""" |
1210 | | - |
1211 | | - attributes = [] |
1212 | | - postfix = data |
1213 | | - while len(postfix) > 0: |
1214 | | - flags, typeCode = struct.unpack('!BB', postfix[:2]) |
1215 | | - |
1216 | | - if flags & ATTR_EXTENDED_LEN: |
1217 | | - attrLen = struct.unpack('!H', postfix[2:4])[0] |
1218 | | - value = postfix[4:4+attrLen] |
1219 | | - postfix = postfix[4+attrLen:] # Next attribute |
1220 | | - else: # standard 1-octet length |
1221 | | - attrLen = ord(postfix[2]) |
1222 | | - value = postfix[3:3+attrLen] |
1223 | | - postfix = postfix[3+attrLen:] # Next attribute |
1224 | | - |
1225 | | - attribute = (flags, typeCode, value) |
1226 | | - attributes.append(attribute) |
1227 | | - |
1228 | | - return attributes |
1229 | | - |
1230 | | - @staticmethod |
1231 | | - def encodeAttribute(attrTuple): |
1232 | | - """Encodes a single attribute""" |
1233 | | - |
1234 | | - flags, typeCode, value = attrTuple |
1235 | | - if flags & ATTR_EXTENDED_LEN: |
1236 | | - fmtString = '!BBH' |
1237 | | - else: |
1238 | | - fmtString = '!BBB' |
1239 | | - |
1240 | | - return struct.pack(fmtString, flags, typeCode, len(value)) + value |
1241 | | - |
1242 | | -class BGPFactory(protocol.Factory): |
1243 | | - """Base factory for creating BGP protocol instances""" |
1244 | | - |
1245 | | - protocol = BGP |
1246 | | - FSM = FSM |
1247 | | - |
1248 | | - myASN = None |
1249 | | - bgpId = None |
1250 | | - |
1251 | | - def buildProtocol(self, addr): |
1252 | | - """Builds a BGPProtocol instance""" |
1253 | | - |
1254 | | - assert self.myASN is not None and self.bgpId is not None |
1255 | | - |
1256 | | - return protocol.Factory.buildProtocol(self, addr) |
1257 | | - |
1258 | | - def startedConnecting(self, connector): |
1259 | | - # DEBUG |
1260 | | - print "Started connecting", connector |
1261 | | - |
1262 | | - def clientConnectionLost(self, connector, reason): |
1263 | | - # DEBUG |
1264 | | - print "Client connection lost", connector, reason |
1265 | | - |
1266 | | -class BGPServerFactory(BGPFactory): |
1267 | | - """Class managing the server (listening) side of the BGP |
1268 | | - protocol. Hands over the factory work to a specific BGPPeering |
1269 | | - (factory) instance. |
1270 | | - """ |
1271 | | - |
1272 | | - def __init__(self, peers): |
1273 | | - self.peers = peers |
1274 | | - |
1275 | | - def buildProtocol(self, addr): |
1276 | | - """Builds a BGPProtocol instance by finding an appropriate |
1277 | | - BGPPeering factory instance to hand over to. |
1278 | | - """ |
1279 | | - |
1280 | | - # DEBUG |
1281 | | - print "Connection received from", addr.host |
1282 | | - |
1283 | | - try: |
1284 | | - bgpPeering = self.peers[addr.host] |
1285 | | - except KeyError: |
1286 | | - # This peer is unknown. Reject the incoming connection. |
1287 | | - return None |
1288 | | - |
1289 | | - return bgpPeering.takeServerConnection(addr) |
1290 | | - |
1291 | | - |
1292 | | -class BGPPeering(BGPFactory): |
1293 | | - """Class managing a BGP session with a peer""" |
1294 | | - |
1295 | | - implements(IBGPPeering) |
1296 | | - |
1297 | | - def __init__(self): |
1298 | | - self.peerAddr = None |
1299 | | - self.peerId = None |
1300 | | - self.fsm = BGPFactory.FSM(self) |
1301 | | - self.inConnections = [] |
1302 | | - self.outConnections = [] |
1303 | | - self.estabProtocol = None # reference to the BGPProtocol instance in ESTAB state |
1304 | | - |
1305 | | - def buildProtocol(self, addr): |
1306 | | - """Builds a BGP protocol instance""" |
1307 | | - |
1308 | | - p = BGPFactory.buildProtocol(self, addr) |
1309 | | - if p is not None: |
1310 | | - self._initProtocol(p, addr) |
1311 | | - self.outConnections.append(p) |
1312 | | - |
1313 | | - return p |
1314 | | - |
1315 | | - def takeServerConnection(self, addr): |
1316 | | - """Builds a BGP protocol instance for a server connection""" |
1317 | | - |
1318 | | - p = BGPFactory.buildProtocol(self, addr) |
1319 | | - if p is not None: |
1320 | | - self._initProtocol(p, addr) |
1321 | | - self.inConnections.append(p) |
1322 | | - |
1323 | | - return p |
1324 | | - |
1325 | | - def _initProtocol(self, protocol, addr): |
1326 | | - """Initializes a BGPProtocol instance""" |
1327 | | - |
1328 | | - protocol.bgpPeering = self |
1329 | | - |
1330 | | - # Hand over the FSM |
1331 | | - protocol.fsm = self.fsm |
1332 | | - protocol.fsm.protocol = protocol |
1333 | | - |
1334 | | - # Create a new fsm for internal use for now |
1335 | | - self.fsm = BGPFactory.FSM(self) |
1336 | | - self.fsm.state = protocol.fsm.state |
1337 | | - |
1338 | | - if addr.port == PORT: |
1339 | | - protocol.fsm.state = ST_CONNECT |
1340 | | - else: |
1341 | | - protocol.fsm.state = ST_ACTIVE |
1342 | | - |
1343 | | - # Set up callback and error handlers |
1344 | | - protocol.deferred.addCallbacks(self.sessionEstablished, self.protocolError) |
1345 | | - |
1346 | | - def clientConnectionFailed(self, connector, reason): |
1347 | | - """Called when the outgoing connection failed.""" |
1348 | | - |
1349 | | - # DEBUG |
1350 | | - print "Client connection failed", connector, reason |
1351 | | - |
1352 | | - # There is no protocol instance yet at this point. |
1353 | | - # Catch a possible NotificationException |
1354 | | - try: |
1355 | | - self.fsm.connectionFailed() |
1356 | | - except NotificationSent, e: |
1357 | | - # TODO: error handling |
1358 | | - pass |
1359 | | - |
1360 | | - def manualStart(self): |
1361 | | - """BGP ManualStart event (event 1)""" |
1362 | | - |
1363 | | - if self.fsm.state == ST_IDLE: |
1364 | | - self.fsm.manualStart() |
1365 | | - # Create outbound connection |
1366 | | - self.connect() |
1367 | | - self.fsm.state = ST_CONNECT |
1368 | | - |
1369 | | - def manualStop(self): |
1370 | | - """BGP ManualStop event (event 2)""" |
1371 | | - |
1372 | | - for c in inConnections + outConnections: |
1373 | | - # Catch a possible NotificationSent exception |
1374 | | - try: |
1375 | | - c.fsm.manualStop() |
1376 | | - except NotificationSent, e: |
1377 | | - c.deferred.errback(e) |
1378 | | - |
1379 | | - def releaseResources(self, protocol): |
1380 | | - """ |
1381 | | - Called by FSM when BGP resources (routes etc.) should be released |
1382 | | - prior to ending a session. |
1383 | | - """ |
1384 | | - |
1385 | | - print "Releasing resources" |
1386 | | - |
1387 | | - def connectionClosed(self, protocol): |
1388 | | - """ |
1389 | | - Called by FSM when the BGP connection has been closed. |
1390 | | - """ |
1391 | | - |
1392 | | - print "Connection closed", protocol |
1393 | | - |
1394 | | - if protocol is None: |
1395 | | - # Protocol did not exist yet, connection never succeeded |
1396 | | - # No further cleanup needed. |
1397 | | - return |
1398 | | - |
1399 | | - # Remove the protocol |
1400 | | - if protocol.isOutgoing(): |
1401 | | - self.outConnections.remove(protocol) |
1402 | | - else: |
1403 | | - self.inConnections.remove(protocol) |
1404 | | - |
1405 | | - if protocol is self.estabProtocol: |
1406 | | - self.estabProtocol = None |
1407 | | - # self.fsm should still be valid and set to ST_IDLE |
1408 | | - assert self.fsm.state == ST_IDLE |
1409 | | - |
1410 | | - def completeInit(self, protocol): |
1411 | | - """ |
1412 | | - Called by FSM when BGP resources should be initialized. |
1413 | | - """ |
1414 | | - |
1415 | | - def sessionEstablished(self, protocol): |
1416 | | - """Called when the BGP session was established""" |
1417 | | - |
1418 | | - # The One True protocol |
1419 | | - self.estabProtocol = protocol |
1420 | | - self.fsm = protocol.fsm |
1421 | | - |
1422 | | - # Create a new deferred for later possible errors |
1423 | | - protocol.deferred = defer.Deferred() |
1424 | | - protocol.deferred.addErrback(self.protocolError) |
1425 | | - |
1426 | | - # Kill off all other possibly running protocols |
1427 | | - for p in self.inConnections + self.outConnections: |
1428 | | - if p != protocol: |
1429 | | - p.openCollisionDump() |
1430 | | - |
1431 | | - def connectRetryEvent(self, protocol): |
1432 | | - """Called by FSM when we should reattempt to connect.""" |
1433 | | - |
1434 | | - self.connect() |
1435 | | - |
1436 | | - def protocolError(self, failure): |
1437 | | - failure.trap(BGPException) |
1438 | | - |
1439 | | - print "BGP exception", failure |
1440 | | - |
1441 | | - e = failure.check(NotificationSent) |
1442 | | - try: |
1443 | | - # Raise the original exception |
1444 | | - failure.raiseException() |
1445 | | - except NotificationSent, e: |
1446 | | - if (e.error, e.suberror) == (ERR_MSG_UPDATE, ERR_MSG_UPDATE_ATTR_FLAGS): |
1447 | | - print "exception on flags:", BGP.parseEncodedAttributes(e.data) |
1448 | | - else: |
1449 | | - print e.error, e.suberror, e.data |
1450 | | - |
1451 | | - # FIXME: error handling |
1452 | | - |
1453 | | - def setPeerId(self, bgpId): |
1454 | | - """ |
1455 | | - Should be called when an Open message was received from a peer. |
1456 | | - Sets the BGP identifier of the peer if it wasn't set yet. If the |
1457 | | - new peer id is unequal to the existing one, CEASE all connections. |
1458 | | - """ |
1459 | | - |
1460 | | - if self.peerId is None: |
1461 | | - self.peerId = bgpId |
1462 | | - elif self.peerId != bgpId: |
1463 | | - # Ouch, schizophrenia. The BGP id of the peer is unequal to |
1464 | | - # the ids of current and/or previous sessions. Close all |
1465 | | - # connections. |
1466 | | - self.peerId = None |
1467 | | - for c in inConnections + outConnections: |
1468 | | - try: |
1469 | | - c.fsm.openCollisionDump() |
1470 | | - except NotificationSent, e: |
1471 | | - c.deferred.errback(e) |
1472 | | - |
1473 | | - def collisionDetect(self, protocol): |
1474 | | - """ |
1475 | | - Runs the collision detection algorithm as defined in the RFC. |
1476 | | - Returns True if the requesting protocol has to CEASE |
1477 | | - """ |
1478 | | - |
1479 | | - # Construct a list of other connections to examine |
1480 | | - openConfirmConnections = [c |
1481 | | - for c |
1482 | | - in self.inConnections + self.outConnections |
1483 | | - if c != protocol and c.fsm.state in (ST_OPENCONFIRM, ST_ESTABLISHED)] |
1484 | | - |
1485 | | - # We need at least 1 other connections to have a collision |
1486 | | - if len(openConfirmConnections) < 1: |
1487 | | - return False |
1488 | | - |
1489 | | - # A collision exists at this point. |
1490 | | - |
1491 | | - # If one of the other connections is already in ESTABLISHED state, |
1492 | | - # it wins |
1493 | | - if ST_ESTABLISHED in [c.fsm.state for c in openConfirmConnections]: |
1494 | | - protocol.fsm.openCollisionDump() |
1495 | | - return True |
1496 | | - |
1497 | | - # Break the tie |
1498 | | - assert self.bgpId != protocol.peerId |
1499 | | - if self.bgpId < protocol.peerId: |
1500 | | - dumpList = outConnections |
1501 | | - elif self.bgpId > protocol.peerId: |
1502 | | - dumpList = inConnections |
1503 | | - |
1504 | | - for c in dumpList: |
1505 | | - try: |
1506 | | - c.fsm.openCollisionDump() |
1507 | | - except NotificationSent, e: |
1508 | | - c.deferred.errback(e) |
1509 | | - |
1510 | | - return (protocol in dumpList) |
1511 | | - |
1512 | | - def connect(self): |
1513 | | - """Initiates a TCP connection to the peer. Should only be called from |
1514 | | - BGPPeering or FSM, otherwise use manualStart() instead. |
1515 | | - """ |
1516 | | - |
1517 | | - # DEBUG |
1518 | | - print "(Re)connect to", self.peerAddr |
1519 | | - |
1520 | | - if self.fsm.state != ST_ESTABLISHED: |
1521 | | - reactor.connectTCP(self.peerAddr, PORT, self) |
1522 | | - return True |
1523 | | - else: |
1524 | | - return False |
1525 | | - |
\ No newline at end of file |
Index: trunk/routing/twistedbgp/src/test.py |
— | — | @@ -36,20 +36,21 @@ |
37 | 37 | # Add internal attribute 'last update' |
38 | 38 | attrSet.add(bgp.LastUpdateIntAttribute((0, bgp.ATTR_TYPE_INT_LAST_UPDATE, datetime.datetime.now()))) |
39 | 39 | |
40 | | - #for prefix in withdrawnPrefixes: |
41 | | - # try: |
42 | | - # del self.prefixes[prefix] |
43 | | - # except KeyError: |
44 | | - # print "withdrawn prefix", prefix, "not found." |
| 40 | + for prefix in withdrawnPrefixes: |
| 41 | + try: |
| 42 | + self.prefixes.delete(str(prefix)) |
| 43 | + #del self.prefixes[prefix] |
| 44 | + except KeyError: |
| 45 | + print "withdrawn prefix", prefix, "not found." |
45 | 46 | |
46 | 47 | for prefix in nlri: |
47 | 48 | #self.prefixes[prefix] = attrSet |
48 | 49 | p = self.prefixes.add(str(prefix)) |
49 | 50 | p.data["attributes"] = attrSet |
50 | | - #print p.prefix |
| 51 | + #print prefix, p.prefix |
51 | 52 | |
52 | 53 | def printStats(self): |
53 | | - print "Now %d prefixes in table, %d total nlri, %d withdrawals" % (len(self.prefixes.nodes()), self.nlriCount, self.withdrawnCount) |
| 54 | + print "Now %d prefixes in table, %d total nlri, %d withdrawals" % (len(self.prefixes.prefixes()), self.nlriCount, self.withdrawnCount) |
54 | 55 | |
55 | 56 | #p = bgp.IPPrefix('145.97.32/20') |
56 | 57 | try: |
— | — | @@ -60,6 +61,7 @@ |
61 | 62 | print "\t", a.name, a |
62 | 63 | except: pass |
63 | 64 | |
| 65 | + |
64 | 66 | #bgpprot = bgp.BGP(myASN=14907, bgpId=1) |
65 | 67 | |
66 | 68 | #print [ord(i) for i in bgpprot.constructOpen()] |