def check_ka (self, message=_NOP,ignore=_NOP.TYPE): if message.TYPE != ignore: self.last_read = time.time() if self.holdtime: left = int(self.last_read + self.holdtime - time.time()) if self.last_print != left: self.logger.timers('Receive Timer %d second(s) left' % left,source=self.session()) self.last_print = left if left <= 0: raise Notify(self.code,self.subcode,self.message) elif message.TYPE == KeepAlive.TYPE: raise Notify(2,6,'Negotiated holdtime was zero, it was invalid to send us a keepalive messages')
def read_message(self): # This will always be defined by the loop but scope leaking upset scrutinizer/pylint msg_id = None packets = self.neighbor.api['receive-packets'] consolidate = self.neighbor.api['receive-consolidate'] parsed = self.neighbor.api['receive-parsed'] body, header = '', '' # just because pylint/pylama are getting more clever for length, msg_id, header, body, notify in self.connection.reader(): if notify: if self.neighbor.api['receive-%s' % Message.CODE.NOTIFICATION.SHORT]: if packets and not consolidate: self.peer.reactor.processes.packets( self.peer.neighbor, 'receive', msg_id, header, body) if not packets or consolidate: header = '' body = '' self.peer.reactor.processes.notification( self.peer.neighbor, 'receive', notify.code, notify.subcode, str(notify), header, body) # XXX: is notify not already Notify class ? raise Notify(notify.code, notify.subcode, str(notify)) if not length: yield _NOP if packets and not consolidate: self.peer.reactor.processes.packets(self.peer.neighbor, 'receive', msg_id, header, body) if msg_id == Message.CODE.UPDATE: if not parsed and not self.log_routes: yield _UPDATE return self.logger.message(self.me('<< %s' % Message.CODE.name(msg_id))) try: message = Message.unpack(msg_id, body, self.negotiated) except (KeyboardInterrupt, SystemExit, Notify): raise except Exception, exc: self.logger.message( self.me('Could not decode message "%d"' % msg_id)) self.logger.message(self.me('%s' % str(exc))) self.logger.message(traceback.format_exc()) raise Notify(1, 0, 'can not decode update message of type "%d"' % msg_id)
def _keepalive(self, proto): need_ka = False generator = None while True: # SEND KEEPALIVES need_ka |= self.send_timer.need_ka() if need_ka: if not generator: generator = proto.new_keepalive() need_ka = False if not generator: yield False continue try: # try to close the generator and raise a StopIteration in one call generator.next() generator.next() # still running yield True except NetworkError: raise Notify( 4, 0, 'problem with network while trying to send keepalive') except StopIteration: generator = None yield False
def __call__(self): # True if we need or are trying # False if we do not need to send one try: return self._generator.next() except StopIteration: raise Notify(4, 0, 'could not send keepalive')
def validate_open(self): error = self.negotiated.validate(self.neighbor) if error is not None: raise Notify(*error) if self.neighbor.api['negotiated']: self.peer.reactor.processes.negotiated(self.peer.neighbor, self.negotiated) if self.negotiated.mismatch: log.warning( '--------------------------------------------------------------------', self.connection.session()) log.warning( 'the connection can not carry the following family/families', self.connection.session()) for reason, (afi, safi) in self.negotiated.mismatch: log.warning( ' - %s is not configured for %s/%s' % (reason, afi, safi), self.connection.session()) log.warning( 'therefore no routes of this kind can be announced on the connection', self.connection.session()) log.warning( '--------------------------------------------------------------------', self.connection.session())
def notification(self, code, subcode, message): try: notification = Notify(code, subcode, message).message() for boolean in self.writer(notification): yield False self.close() except NetworkError: pass # This is only be used when closing session due to unconfigured peers - so issues do not matter
def check_ka(self, message=_NOP, ignore=_NOP.TYPE): if self.check_ka_timer(message, ignore): return if self.single: raise Notify( 2, 6, 'Negotiated holdtime was zero, it was invalid to send us a keepalive messages' ) self.single = True
def notification (self, code, subcode, message): try: notification = Notify(code,subcode,message).message() for boolean in self.writer(notification): yield False # self.logger.message(self.me('>> NOTIFICATION (%d,%d,"%s")' % (notification.code,notification.subcode,notification.data)),'error') yield True except NetworkError: pass # This is only be used when closing session due to unconfigured peers - so issues do not matter
def read_keepalive(self): for message in self.read_message(): if message.TYPE == NOP.TYPE: yield message else: break if message.TYPE != KeepAlive.TYPE: raise Notify(5, 2) yield message
def read_open (self, ip): for received_open in self.read_message(): if received_open.TYPE == NOP.TYPE: yield received_open else: break if received_open.TYPE != Open.TYPE: raise Notify(5,1,'The first packet received is not an open message (%s)' % received_open) self.logger.message(self.me('<< %s' % received_open)) yield received_open
def read_open(self, ip): for received_open in self.read_message(): if received_open.TYPE == NOP.TYPE: yield received_open else: break if received_open.TYPE != Open.TYPE: raise Notify(5, 1, 'The first packet received is not an open message (%s)' % received_open) log.debug('<< %s' % received_open, self.connection.session()) yield received_open
def check_ka_timer(self, message=_NOP, ignore=_NOP.TYPE): if message.TYPE != ignore: self.last_read = time.time() if self.holdtime: left = int(self.last_read + self.holdtime - time.time()) if self.last_print != left: self.logger.debug('receive-timer %d second(s) left' % left, source='ka-' + self.session()) self.last_print = left if left <= 0: raise Notify(self.code, self.subcode, self.message) return message.TYPE != KeepAlive.TYPE return False
def check_ka_timer(self, message=_NOP, ignore=_NOP.TYPE): if self.holdtime == 0: return message.TYPE != KeepAlive.TYPE now = int(time.time()) if message.TYPE != ignore: self.last_read = now elapsed = now - self.last_read if elapsed > self.holdtime: raise Notify(self.code, self.subcode, self.message) if self.last_print != now: left = self.holdtime - elapsed log.debug('receive-timer %d second(s) left' % left, source='ka-' + self.session()) self.last_print = now return True
def _main(self): """yield True if we want to come back to it asap, None if nothing urgent, and False if stopped""" if self._teardown: raise Notify(6, 3) self.neighbor.rib.incoming.clear() include_withdraw = False # Announce to the process BGP is up self.logger.notice( 'connected to %s with %s' % (self.id(), self.proto.connection.name()), 'reactor') self.stats['up'] = self.stats.get('up', 0) + 1 if self.neighbor.api['neighbor-changes']: try: self.reactor.processes.up(self.neighbor) except ProcessError: # Can not find any better error code than 6,0 ! # XXX: We can not restart the program so this will come back again and again - FIX # XXX: In the main loop we do exit on this kind of error raise Notify(6, 0, 'ExaBGP Internal error, sorry.') send_eor = not self.neighbor.manual_eor new_routes = None self._resend_routes = SEND.NORMAL send_families = [] # Every last asm message should be re-announced on restart for family in self.neighbor.asm: if family in self.neighbor.families(): self.neighbor.messages.appendleft(self.neighbor.asm[family]) operational = None refresh = None command_eor = None number = 0 refresh_enhanced = True if self.proto.negotiated.refresh == REFRESH.ENHANCED else False send_ka = KA(self.proto.connection.session, self.proto) while not self._teardown: for message in self.proto.read_message(): self.recv_timer.check_ka(message) if send_ka() is not False: # we need and will send a keepalive while send_ka() is None: yield ACTION.NOW # Received update if message.TYPE == Update.TYPE: number += 1 self.logger.debug('<< UPDATE #%d' % number, self.id()) for nlri in message.nlris: self.neighbor.rib.incoming.update_cache( Change(nlri, message.attributes)) self.logger.debug( LazyFormat(' UPDATE #%d nlri ' % number, nlri, str), self.id()) elif message.TYPE == RouteRefresh.TYPE: if message.reserved == RouteRefresh.request: self._resend_routes = SEND.REFRESH send_families.append((message.afi, message.safi)) # SEND OPERATIONAL if self.neighbor.operational: if not operational: new_operational = self.neighbor.messages.popleft( ) if self.neighbor.messages else None if new_operational: operational = self.proto.new_operational( new_operational, self.proto.negotiated) if operational: try: six.next(operational) except StopIteration: operational = None # make sure that if some operational message are received via the API # that we do not eat memory for nothing elif self.neighbor.messages: self.neighbor.messages.popleft() # SEND REFRESH if self.neighbor.route_refresh: if not refresh: new_refresh = self.neighbor.refresh.popleft( ) if self.neighbor.refresh else None if new_refresh: refresh = self.proto.new_refresh(new_refresh) if refresh: try: six.next(refresh) except StopIteration: refresh = None # Take the routes already sent to that peer and resend them if self._reconfigure: self._reconfigure = False # we are here following a configuration change if self._neighbor: # see what changed in the configuration self.neighbor.rib.outgoing.replace( self._neighbor.backup_changes, self._neighbor.changes) # do not keep the previous routes in memory as they are not useful anymore self._neighbor.backup_changes = [] # Take the routes already sent to that peer and resend them if self._resend_routes != SEND.DONE: enhanced = True if refresh_enhanced and self._resend_routes == SEND.REFRESH else False self._resend_routes = SEND.DONE self.neighbor.rib.outgoing.resend(send_families, enhanced) send_families = [] # Need to send update if not new_routes and self.neighbor.rib.outgoing.pending(): # XXX: in proto really. hum to think about ? new_routes = self.proto.new_update(include_withdraw) if new_routes: try: for _ in range(25): # This can raise a NetworkError six.next(new_routes) except StopIteration: new_routes = None include_withdraw = True elif send_eor: send_eor = False for _ in self.proto.new_eors(): yield ACTION.NOW self.logger.debug('>> EOR(s)', self.id()) # SEND MANUAL KEEPALIVE (only if we have no more routes to send) elif not command_eor and self.neighbor.eor: new_eor = self.neighbor.eor.popleft() command_eor = self.proto.new_eors(new_eor.afi, new_eor.safi) if command_eor: try: six.next(command_eor) except StopIteration: command_eor = None if new_routes or message.TYPE != NOP.TYPE: yield ACTION.NOW elif self.neighbor.messages or operational: yield ACTION.NOW elif self.neighbor.eor or command_eor: yield ACTION.NOW else: yield ACTION.LATER # read_message will loop until new message arrives with NOP if self._teardown: break # If graceful restart, silent shutdown if self.neighbor.graceful_restart and self.proto.negotiated.sent_open.capabilities.announced( Capability.CODE.GRACEFUL_RESTART): self.logger.error('closing the session without notification', self.id()) self.proto.close( 'graceful restarted negotiated, closing without sending any notification' ) raise NetworkError('closing') # notify our peer of the shutdown raise Notify(6, self._teardown)
def read_message(self): # This will always be defined by the loop but scope leaking upset scrutinizer/pylint msg_id = None packets = self.neighbor.api['receive-packets'] consolidate = self.neighbor.api['receive-consolidate'] parsed = self.neighbor.api['receive-parsed'] body, header = '', '' # just because pylint/pylama are getting more clever for length, msg_id, header, body, notify in self.connection.reader(): # internal issue if notify: if self.neighbor.api.get( 'send-%s' % Message.CODE.NOTIFICATION.SHORT, False): if consolidate: self.peer.reactor.processes.notification( self.peer.neighbor, 'send', notify.code, notify.subcode, str(notify), header, body) elif parsed: self.peer.reactor.processes.notification( self.peer.neighbor, 'send', notify.code, notify.subcode, str(notify), '', '') elif packets: self.peer.reactor.processes.packets( self.peer.neighbor, 'send', msg_id, header, body) # XXX: is notify not already Notify class ? raise Notify(notify.code, notify.subcode, str(notify)) if not length: yield _NOP continue self.logger.message(self.me('<< %s' % Message.CODE.name(msg_id))) for_api = self.neighbor.api.get( 'receive-%s' % Message.CODE.short(msg_id), False) if for_api and packets and not consolidate: self.peer.reactor.processes.packets(self.peer.neighbor, 'receive', msg_id, header, body) if msg_id == Message.CODE.UPDATE: if not (for_api or self.log_routes) and not (parsed or consolidate): yield _UPDATE return try: message = Message.unpack(msg_id, body, self.negotiated) except (KeyboardInterrupt, SystemExit, Notify): raise except Exception, exc: self.logger.message( self.me('Could not decode message "%d"' % msg_id)) self.logger.message(self.me('%s' % str(exc))) self.logger.message(traceback.format_exc()) raise Notify( 1, 0, 'can not decode update message of type "%d"' % msg_id) # raise Notify(5,0,'unknown message received') if for_api: if consolidate: self.peer.reactor.processes.message( msg_id, self.neighbor, 'receive', message, header, body) elif parsed: self.peer.reactor.processes.message( msg_id, self.neighbor, 'receive', message, '', '') if message.TYPE == Notification.TYPE: raise message yield message
def read_message(self): # This will always be defined by the loop but scope leaking upset scrutinizer/pylint msg_id = None packets = self.neighbor.api['receive-packets'] consolidate = self.neighbor.api['receive-consolidate'] parsed = self.neighbor.api['receive-parsed'] body, header = b'', b'' # just because pylint/pylama are getting more clever for length, msg_id, header, body, notify in self.connection.reader(): # internal issue if notify: code = 'receive-%s' % Message.CODE.NOTIFICATION.SHORT if self.neighbor.api.get(code, False): if consolidate: self.peer.reactor.processes.notification( self.peer.neighbor, 'receive', notify.code, notify.subcode, str(notify), None, header, body) elif parsed: self.peer.reactor.processes.notification( self.peer.neighbor, 'receive', notify.code, notify.subcode, str(notify), None, b'', b'') elif packets: self.peer.reactor.processes.packets( self.peer.neighbor, 'receive', msg_id, None, header, body) # XXX: is notify not already Notify class ? raise Notify(notify.code, notify.subcode, str(notify)) if not length: yield _NOP continue log.debug('<< message of type %s' % Message.CODE.name(msg_id), self.connection.session()) code = 'receive-%s' % Message.CODE.short(msg_id) self.peer.stats[code] = self.peer.stats.get(code, 0) + 1 for_api = self.neighbor.api.get(code, False) if for_api and packets and not consolidate: negotiated = self.negotiated if self.neighbor.api.get( 'negotiated', False) else None self.peer.reactor.processes.packets(self.peer.neighbor, 'receive', msg_id, negotiated, header, body) if msg_id == Message.CODE.UPDATE: if not self.neighbor['adj-rib-in'] and not ( for_api or self.log_routes) and not (parsed or consolidate): yield _UPDATE return try: message = Message.unpack(msg_id, body, Direction.IN, self.negotiated) except (KeyboardInterrupt, SystemExit, Notify): raise except Exception as exc: log.debug('could not decode message "%d"' % msg_id, self.connection.session()) log.debug('%s' % str(exc), self.connection.session()) log.debug(traceback.format_exc(), self.connection.session()) raise Notify( 1, 0, 'can not decode update message of type "%d"' % msg_id) # raise Notify(5,0,'unknown message received') if message.TYPE == Update.TYPE: if Attribute.CODE.INTERNAL_TREAT_AS_WITHDRAW in message.attributes: for nlri in message.nlris: nlri.action = IN.WITHDRAWN if for_api: negotiated = self.negotiated if self.neighbor.api.get( 'negotiated', False) else None if consolidate: self.peer.reactor.processes.message( msg_id, self.neighbor, 'receive', message, negotiated, header, body) elif parsed: self.peer.reactor.processes.message( msg_id, self.neighbor, 'receive', message, negotiated, b'', b'') if message.TYPE == Notification.TYPE: raise message if message.TYPE == Update.TYPE and Attribute.CODE.INTERNAL_DISCARD in message.attributes: yield _NOP else: yield message
def validate_open(self): error = self.negotiated.validate(self.neighbor) if error is not None: raise Notify(*error)