def writer(self, data): if not self.io: # XXX: FIXME: Make sure it does not hold the cleanup during the closing of the peering session yield True return while not self.writing(): yield False self.logger.debug(LazyFormat('sending TCP payload', data), self.session()) # The first while is here to setup the try/catch block once as it is very expensive while True: try: while True: if self.defensive and random.randint(0, 2): raise socket.error(errno.EAGAIN, 'raising network error on purpose') # we can not use sendall as in case of network buffer filling # it does raise and does not let you know how much was sent number = self.io.send(data) if not number: self.close() self.logger.warning( '%s %s lost TCP connection with peer' % (self.name(), self.peer), self.session()) raise LostConnection('lost the TCP connection') data = data[number:] if not data: yield True return yield False except socket.error as exc: if exc.args[0] in error.block: self.logger.debug( '%s %s blocking io problem mid-way through writing a message %s, trying to complete' % (self.name(), self.peer, errstr(exc)), self.session()) yield False elif exc.errno == errno.EPIPE: # The TCP connection is gone. self.close() raise NetworkError('Broken TCP connection') elif exc.args[0] in error.fatal: self.close() self.logger.critical( '%s %s problem sending message (%s)' % (self.name(), self.peer, errstr(exc)), self.session()) raise NetworkError( 'Problem while writing data to the network (%s)' % errstr(exc)) # what error could it be ! else: self.logger.critical( '%s %s undefined error writing on socket' % (self.name(), self.peer), self.session()) yield False
def writer (self,data): if not self.io: # XXX: FIXME: Make sure it does not hold the cleanup during the closing of the peering session yield True return if not self.writing(): yield False return self.logger.wire(LazyFormat("%s %-32s SENDING " % (self.name(),'%s / %s' % (self.local,self.peer)),od,data)) # The first while is here to setup the try/catch block once as it is very expensive while True: try: while True: if self._writing is None: self._writing = time.time() elif time.time() > self._writing + self.read_timeout: self.close() self.logger.wire("%s %s peer is too slow" % (self.name(),self.peer)) raise TooSlowError('Waited to write for data on a socket for more than %d second(s)' % self.read_timeout) if self.defensive and random.randint(0,2): raise socket.error(errno.EAGAIN,'raising network error in purpose') # we can not use sendall as in case of network buffer filling # it does raise and does not let you know how much was sent nb = self.io.send(data) if not nb: self.close() self.logger.wire("%s %s lost TCP connection with peer" % (self.name(),self.peer)) raise LostConnection('lost the TCP connection') data = data[nb:] if not data: self._writing = None yield True return yield False except socket.error,e: if e.args[0] in error.block: self.logger.wire("%s %s blocking io problem mid-way through writing a message %s, trying to complete" % (self.name(),self.peer,errstr(e)),'debug') elif e.errno == errno.EPIPE: # The TCP connection is gone. self.close() raise NetworkError('Broken TCP connection') elif e.args[0] in error.fatal: self.close() self.logger.wire("%s %s problem sending message (%s)" % (self.name(),self.peer,errstr(e))) raise NetworkError('Problem while writing data to the network (%s)' % errstr(e)) # what error could it be ! else: self.logger.wire("%s %s undefined error writing on socket" % (self.name(),self.peer)) yield False
def _reader (self, number): # The function must not be called if it does not return with no data with a smaller size as parameter if not self.io: self.close() raise NotConnected('Trying to read on a closed TCP connection') if number == 0: yield b'' return while not self.reading(): yield b'' data = b'' reported = '' while True: try: while True: if self.defensive and random.randint(0,2): raise socket.error(errno.EAGAIN,'raising network error on purpose') read = self.io.recv(number) if not read: self.close() self.logger.wire("%s %s lost TCP session with peer" % (self.name(),self.peer),source=self.session()) raise LostConnection('the TCP connection was closed by the remote end') data += read number -= len(read) if not number: self.logger.wire( LazyFormat( "%s %-32s RECEIVED " % ( self.name(), '%s / %s' % (self.local,self.peer) ), read ), source=self.session() ) yield data return yield b'' except socket.timeout as exc: self.close() self.logger.wire("%s %s peer is too slow" % (self.name(),self.peer),source=self.session()) raise TooSlowError('Timeout while reading data from the network (%s)' % errstr(exc)) except socket.error as exc: if exc.args[0] in error.block: message = "%s %s blocking io problem mid-way through reading a message %s, trying to complete" % (self.name(),self.peer,errstr(exc)) if message != reported: reported = message self.logger.wire(message,'debug',self.session()) yield b'' elif exc.args[0] in error.fatal: self.close() raise LostConnection('issue reading on the socket: %s' % errstr(exc)) # what error could it be ! else: self.logger.wire("%s %s undefined error reading on socket" % (self.name(),self.peer),source=self.session()) raise NetworkError('Problem while reading data from the network (%s)' % errstr(exc))
def listen (self, local_ip, peer_ip, local_port, md5): self.serving = True for sock,(local,port,peer,md) in self._sockets.items(): if local_ip.top() != local: continue if local_port != port: continue if md5: MD5(sock,peer_ip.top(),0,md5) return try: sock = self._new_socket(local_ip) if md5: # MD5 must match the peer side of the TCP, not the local one MD5(sock,peer_ip.top(),0,md5) try: sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) except (socket.error,AttributeError): pass sock.setblocking(0) # s.settimeout(0.0) sock.bind((local_ip.top(),local_port)) sock.listen(self._backlog) self._sockets[sock] = (local_ip.top(),local_port,peer_ip.top(),md5) except socket.error,exc: if exc.args[0] == errno.EADDRINUSE: raise BindingError('could not listen on %s:%d, the port already in use by another application' % (local_ip,local_port)) elif exc.args[0] == errno.EADDRNOTAVAIL: raise BindingError('could not listen on %s:%d, this is an invalid address' % (local_ip,local_port)) raise NetworkError(str(exc))
def _new_socket(self, ip): if ip.afi == AFI.ipv6: return socket.socket(socket.AF_INET6, socket.SOCK_STREAM, socket.IPPROTO_TCP) if ip.afi == AFI.ipv4: return socket.socket(socket.AF_INET, socket.SOCK_STREAM, socket.IPPROTO_TCP) raise NetworkError( 'Can not create socket for listening, family of IP %s is unknown' % ip)
def writing (self): while True: try: _,w,_ = select.select([],[self.io,],[],0) except select.error,exc: if exc.args[0] not in error.block: self.close() self.logger.wire("%s %s errno %s on socket" % (self.name(),self.peer,errno.errorcode[exc.args[0]])) raise NetworkError('errno %s on socket' % errno.errorcode[exc.args[0]]) return False return w != []
def reading (self): while True: try: r,_,_ = select.select([self.io,],[],[],0) except select.error as exc: if exc.args[0] not in error.block: self.close() self.logger.wire("%s %s errno %s on socket" % (self.name(),self.peer,errno.errorcode[exc.args[0]]),source=self.session()) raise NetworkError('errno %s on socket' % errno.errorcode[exc.args[0]]) return False return r != []
def _reader (self,number): # The function must not be called if it does not return with no data with a smaller size as parameter if not self.io: self.close() raise NotConnected('Trying to read on a close TCP conncetion') if number == 0: yield '' return # XXX: one of the socket option is to recover the size of the buffer # XXX: we could use it to not have to put together the string with multiple reads # XXX: and get rid of the self.read_timeout option while not self.reading(): yield '' data = '' while True: try: while True: if self._reading is None: self._reading = time.time() elif time.time() > self._reading + self.read_timeout: self.close() self.logger.wire("%s %s peer is too slow (we were told there was data on the socket but we can not read up to what should be there)" % (self.name(),self.peer)) raise TooSlowError('Waited to read for data on a socket for more than %d second(s)' % self.read_timeout) if self.defensive and random.randint(0,2): raise socket.error(errno.EAGAIN,'raising network error in purpose') read = self.io.recv(number) if not read: self.close() self.logger.wire("%s %s lost TCP session with peer" % (self.name(),self.peer)) raise LostConnection('the TCP connection was closed by the remote end') data += read number -= len(read) if not number: self.logger.wire(LazyFormat("%s %-32s RECEIVED " % (self.name(),'%s / %s' % (self.local,self.peer)),od,read)) self._reading = None yield data return except socket.timeout,e: self.close() self.logger.wire("%s %s peer is too slow" % (self.name(),self.peer)) raise TooSlowError('Timeout while reading data from the network (%s)' % errstr(e)) except socket.error,e: if e.args[0] in error.block: self.logger.wire("%s %s blocking io problem mid-way through reading a message %s, trying to complete" % (self.name(),self.peer,errstr(e)),'debug') elif e.args[0] in error.fatal: self.close() raise LostConnection('issue reading on the socket: %s' % errstr(e)) # what error could it be ! else: self.logger.wire("%s %s undefined error reading on socket" % (self.name(),self.peer)) raise NetworkError('Problem while reading data from the network (%s)' % errstr(e))
def reading (self): while True: try: r,_,_ = select.select([self.io,],[],[],0) except select.error,e: if e.args[0] not in error.block: self.close() self.logger.wire("%s %s errno %s on socket" % (self.name(),self.peer,errno.errorcode[e.args[0]])) raise NetworkError('errno %s on socket' % errno.errorcode[e.args[0]]) return False if r: self._reading = time.time() return r != []
def _listen(self, local_ip, peer_ip, local_port, md5, md5_base64, ttl_in): self.serving = True for sock, (local, port, peer, md) in self._sockets.items(): if local_ip.top() != local: continue if local_port != port: continue MD5(sock, peer_ip.top(), 0, md5, md5_base64) if ttl_in: MIN_TTL(sock, peer_ip, ttl_in) return try: sock = self._new_socket(local_ip) # MD5 must match the peer side of the TCP, not the local one MD5(sock, peer_ip.top(), 0, md5, md5_base64) if ttl_in: MIN_TTL(sock, peer_ip, ttl_in) try: sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) if local_ip.ipv6(): sock.setsockopt(socket.IPPROTO_IPV6, socket.IPV6_V6ONLY, 1) except (socket.error, AttributeError): pass sock.setblocking(0) # s.settimeout(0.0) sock.bind((local_ip.top(), local_port)) sock.listen(self._backlog) self._sockets[sock] = (local_ip.top(), local_port, peer_ip.top(), md5) except socket.error as exc: if exc.args[0] == errno.EADDRINUSE: raise BindingError( 'could not listen on %s:%d, the port may already be in use by another application' % (local_ip, local_port)) elif exc.args[0] == errno.EADDRNOTAVAIL: raise BindingError( 'could not listen on %s:%d, this is an invalid address' % (local_ip, local_port)) raise NetworkError(str(exc)) except NetworkError as exc: self.logger.critical(str(exc), 'network') raise exc
def _main (self, direction): """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) proto = self._[direction]['proto'] # Announce to the process BGP is up self.logger.network('Connected to peer %s (%s)' % (self.neighbor.name(),direction)) if self.neighbor.api['neighbor-changes']: try: self.reactor.processes.up(self) 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 proto.negotiated.refresh == REFRESH.ENHANCED else False self.send_ka = KA(self.me,proto) while not self._teardown: for message in proto.read_message(): self.recv_timer.check_ka(message) if self.send_ka() is not False: # we need and will send a keepalive while self.send_ka() is None: yield ACTION.NOW # Received update if message.TYPE == Update.TYPE: number += 1 self.logger.routes(LazyFormat(self.me('<< UPDATE (%d)' % number),message.attributes,lambda _: "%s%s" % (' attributes' if _ else '',_))) for nlri in message.nlris: self.neighbor.rib.incoming.insert_received(Change(nlri,message.attributes)) self.logger.routes(LazyFormat(self.me('<< UPDATE (%d) nlri ' % number),nlri,str)) 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 = proto.new_operational(new_operational,proto.negotiated) if operational: try: operational.next() except StopIteration: operational = None # 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 = proto.new_refresh(new_refresh) if refresh: try: refresh.next() 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 = [] self._have_routes = True # 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) self._have_routes = True send_families = [] # Need to send update if self._have_routes and not new_routes: self._have_routes = False # XXX: in proto really. hum to think about ? new_routes = proto.new_update() if new_routes: try: count = 20 while count: # This can raise a NetworkError new_routes.next() count -= 1 except StopIteration: new_routes = None elif send_eor: send_eor = False for _ in proto.new_eors(): yield ACTION.NOW self.logger.message(self.me('>> EOR(s)')) # 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 = proto.new_eors(new_eor.afi,new_eor.safi) if command_eor: try: command_eor.next() 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 proto.negotiated.sent_open.capabilities.announced(Capability.CODE.GRACEFUL_RESTART): self.logger.network('Closing the session without notification','error') 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 _main (self,direction): """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) proto = self._[direction]['proto'] # Announce to the process BGP is up self.logger.network('Connected to peer %s (%s)' % (self.neighbor.name(),direction)) if self.neighbor.api['neighbor-changes']: try: self.reactor.processes.up(self) 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 = True 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]) counter = Counter(self.logger,self.me) operational = None refresh = None number = 0 self.send_ka = KA(self.me,proto) while not self._teardown: for message in proto.read_message(): self.recv_timer.check_ka(message) if self.send_ka() is not False: # we need and will send a keepalive while self.send_ka() is None: yield ACTION.immediate # Give information on the number of routes seen counter.display() # Received update if message.TYPE == Update.TYPE: counter.increment(len(message.nlris)) number += 1 self.logger.routes(LazyFormat(self.me('<< UPDATE (%d)' % number),lambda _: "%s%s" % (' attributes' if _ else '',_),message.attributes)) for nlri in message.nlris: self.neighbor.rib.incoming.insert_received(Change(nlri,message.attributes)) self.logger.routes(LazyFormat(self.me('<< UPDATE (%d) nlri ' % number),str,nlri)) 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 = proto.new_operational(new_operational,proto.negotiated) if operational: try: operational.next() except StopIteration: operational = None # 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: enhanced_negotiated = True if proto.negotiated.refresh == REFRESH.enhanced else False refresh = proto.new_refresh(new_refresh,enhanced_negotiated) if refresh: try: refresh.next() except StopIteration: refresh = None # Take the routes already sent to that peer and resend them if self._resend_routes != SEND.done: enhanced_refresh = True if self._resend_routes == SEND.refresh and proto.negotiated.refresh == REFRESH.enhanced else False self._resend_routes = SEND.done self.neighbor.rib.outgoing.resend(send_families,enhanced_refresh) self._have_routes = True send_families = [] # Need to send update if self._have_routes and not new_routes: self._have_routes = False # XXX: in proto really. hum to think about ? new_routes = proto.new_update() if new_routes: try: count = 20 while count: # This can raise a NetworkError new_routes.next() count -= 1 except StopIteration: new_routes = None elif send_eor: send_eor = False for eor in proto.new_eors(): yield ACTION.immediate self.logger.message(self.me('>> EOR(s)')) # Go to other Peers yield ACTION.immediate if new_routes or message.TYPE != NOP.TYPE or self.neighbor.messages else 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 proto.negotiated.sent_open.capabilities.announced(Capability.ID.GRACEFUL_RESTART): self.logger.network('Closing the session without notification','error') 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 _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 log.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 log.debug('<< UPDATE #%d' % number, self.id()) for nlri in message.nlris: self.neighbor.rib.incoming.update_cache( Change(nlri, message.attributes)) log.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: 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: 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: count = 1 if self.neighbor.rate_limit > 0 else 25 try: for _ in range(count): # This can raise a NetworkError 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 log.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: 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): log.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 _main(self, direction): "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) proto = self._[direction]['proto'] # Announce to the process BGP is up self.logger.network('Connected to peer %s (%s)' % (self.neighbor.name(), direction)) if self.neighbor.api.neighbor_changes: try: self.reactor.processes.up(self.neighbor.peer_address) 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 = True new_routes = None self._resend_routes = True # 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]) counter = Counter(self.logger, self._log(direction)) need_keepalive = False keepalive = None operational = None while not self._teardown: for message in proto.read_message(): # Update timer self.timer.tick(message) # Give information on the number of routes seen counter.display() # Received update if message.TYPE == Update.TYPE: counter.increment(len(message.nlris)) for nlri in message.nlris: self.neighbor.rib.incoming.insert_received( Change(nlri, message.attributes)) self.logger.routes(LazyFormat(self.me(''), str, nlri)) # SEND KEEPALIVES need_keepalive |= self.timer.keepalive() if need_keepalive and not keepalive: keepalive = proto.new_keepalive() need_keepalive = False if keepalive: try: keepalive.next() except StopIteration: keepalive = None # 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 = proto.new_operational( new_operational) if operational: try: operational.next() except StopIteration: operational = None # Take the routes already sent to that peer and resend them if self._resend_routes: self._resend_routes = False self.neighbor.rib.outgoing.resend_known() self._have_routes = True # Need to send update if self._have_routes and not new_routes: self._have_routes = False # XXX: in proto really. hum to think about ? new_routes = proto.new_update() if new_routes: try: count = 20 while count: # This can raise a NetworkError new_routes.next() count -= 1 except StopIteration: new_routes = None elif send_eor: send_eor = False for eor in proto.new_eors(): yield ACTION.immediate self.logger.message(self.me('>> EOR(s)')) # Go to other Peers yield ACTION.immediate if new_routes or message.TYPE != NOP.TYPE else 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 proto.negotiated.sent_open.capabilities.announced( CapabilityID.GRACEFUL_RESTART): self.logger.network('Closing the session without notification', 'error') proto.close( 'graceful restarted negotiated, closing without sending any notification' ) raise NetworkError('closing') # notify our peer of the shutdown raise Notify(6, self._teardown)