r24942 MediaWiki - Code Review archive

Repository:MediaWiki
Revision:r24941‎ | r24942 | r24943 >
Date:13:05, 20 August 2007
Author:mark
Status:old
Tags:
Comment:
Reformat ip addresses for radix
Modified paths:
  • /trunk/routing/twistedbgp/src/bgp.py (modified) (history)
  • /trunk/routing/twistedbgp/src/test.py (modified) (history)
  • /trunk/routing/twistedbgp/src/twisted/protocols/bgp.py (deleted) (history)

Diff [purge]

Index: trunk/routing/twistedbgp/src/bgp.py
@@ -177,7 +177,8 @@
178178 # TODO: IPv6
179179
180180 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
182183
183184 def __eq__(self, other):
184185 # 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 @@
3737 # Add internal attribute 'last update'
3838 attrSet.add(bgp.LastUpdateIntAttribute((0, bgp.ATTR_TYPE_INT_LAST_UPDATE, datetime.datetime.now())))
3939
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."
4546
4647 for prefix in nlri:
4748 #self.prefixes[prefix] = attrSet
4849 p = self.prefixes.add(str(prefix))
4950 p.data["attributes"] = attrSet
50 - #print p.prefix
 51+ #print prefix, p.prefix
5152
5253 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)
5455
5556 #p = bgp.IPPrefix('145.97.32/20')
5657 try:
@@ -60,6 +61,7 @@
6162 print "\t", a.name, a
6263 except: pass
6364
 65+
6466 #bgpprot = bgp.BGP(myASN=14907, bgpId=1)
6567
6668 #print [ord(i) for i in bgpprot.constructOpen()]

Status & tagging log