def test(): OPEN = ''.join([ chr(int(_, 16)) for _ in "FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF 00 1D 01 04 78 14 00 5A 52 DB 00 45 00" .split() ]) KEEP = ''.join([ chr(int(_, 16)) for _ in "FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF 00 00 04".split() ]) from exabgp.reactor.network.outgoing import Outgoing connection = Outgoing(1, '82.219.0.5', '82.219.212.34') writer = connection._writer(OPEN) while writer() == False: pass writer = connection._writer(KEEP) while writer() == False: pass reader = connection.reader() for size, kind, header, body in reader: if size: print od(header + body) else: sys.stdout.write('-')
def connect(self): # allows to test the protocol code using modified StringIO with a extra 'pending' function if not self.connection: peer = self.neighbor.peer_address local = self.neighbor.local_address md5 = self.neighbor.md5 ttl = self.neighbor.ttl self.connection = Outgoing(peer.afi, peer.ip, local.ip, self.port, md5, ttl) try: generator = self.connection.establish() while True: connected = generator.next() if not connected: yield False continue if self.peer.neighbor.api['neighbor-changes']: self.peer.reactor.processes.connected(self.peer) yield True return except StopIteration: # close called by the caller # self.close('could not connect to remote end') yield False return
def connect(self): # allows to test the protocol code using modified StringIO with a extra 'pending' function if not self.connection: local = self.neighbor.md5_ip.top( ) if not self.neighbor.auto_discovery else None peer = self.neighbor.peer_address.top() afi = self.neighbor.peer_address.afi md5 = self.neighbor.md5_password md5_base64 = self.neighbor.md5_base64 ttl_out = self.neighbor.ttl_out self.connection = Outgoing(afi, peer, local, self.port, md5, md5_base64, ttl_out) if not self.connection.init: yield False return if not local: self.neighbor.local_address = IP.create(self.connection.local) if self.neighbor.router_id is None and self.neighbor.local_address.afi == AFI.ipv4: self.neighbor.router_id = self.neighbor.local_address for connected in self.connection.establish(): if not connected: yield False continue if self.peer.neighbor.api['neighbor-changes']: self.peer.reactor.processes.connected(self.peer.neighbor) yield True return
def connect(self): # allows to test the protocol code using modified StringIO with a extra 'pending' function if not self.connection: local = self.neighbor.md5_ip.top( ) if not self.neighbor.auto_discovery else None peer = self.neighbor.peer_address.top() afi = self.neighbor.peer_address.afi md5 = self.neighbor.md5_password md5_base64 = self.neighbor.md5_base64 ttl_out = self.neighbor.ttl_out self.connection = Outgoing(afi, peer, local, self.port, md5, md5_base64, ttl_out) if not local and self.connection.init: self.neighbor.local_address = IP.create(self.connection.local) if self.neighbor.router_id is None and self.neighbor.local_address.afi == AFI.ipv4: self.neighbor.router_id = self.neighbor.local_address try: generator = self.connection.establish() while True: connected = six.next(generator) if not connected: yield False continue if self.peer.neighbor.api['neighbor-changes']: self.peer.reactor.processes.connected( self.peer.neighbor) yield True return except StopIteration: # close called by the caller # self.close('could not connect to remote end') yield False return
def connect(self): # allows to test the protocol code using modified StringIO with a extra 'pending' function if self.connection: return local = self.neighbor['md5-ip'].top( ) if not self.neighbor.auto_discovery else None peer = self.neighbor['peer-address'].top() afi = self.neighbor['peer-address'].afi md5 = self.neighbor['md5-password'] md5_base64 = self.neighbor['md5-base64'] ttl_out = self.neighbor['outgoing-ttl'] self.connection = Outgoing(afi, peer, local, self.port, md5, md5_base64, ttl_out) for connected in self.connection.establish(): yield False if self.peer.neighbor.api['neighbor-changes']: self.peer.reactor.processes.connected(self.peer.neighbor) if not local: self.neighbor['local-address'] = IP.create(self.connection.local) if self.neighbor['router-id'] is None and self.neighbor[ 'local-address'].afi == AFI.ipv4: self.neighbor['router-id'] = self.neighbor['local-address'] yield True
def connect (self): # allows to test the protocol code using modified StringIO with a extra 'pending' function if not self.connection: local = self.neighbor.md5_ip.top() if not self.neighbor.auto_discovery else None peer = self.neighbor.peer_address.top() afi = self.neighbor.peer_address.afi md5 = self.neighbor.md5_password md5_base64 = self.neighbor.md5_base64 ttl_out = self.neighbor.ttl_out self.connection = Outgoing(afi,peer,local,self.port,md5,md5_base64,ttl_out) if not self.connection.init: yield False return if not local: self.neighbor.local_address = IP.create(self.connection.local) if self.neighbor.router_id is None and self.neighbor.local_address.afi == AFI.ipv4: self.neighbor.router_id = self.neighbor.local_address for connected in self.connection.establish(): if not connected: yield False continue if self.peer.neighbor.api['neighbor-changes']: self.peer.reactor.processes.connected(self.peer.neighbor) yield True return
def connect (self): # allows to test the protocol code using modified StringIO with a extra 'pending' function if not self.connection: peer = self.neighbor.peer_address local = self.neighbor.local_address md5 = self.neighbor.md5 ttl = self.neighbor.ttl self.connection = Outgoing(peer.afi,peer.ip,local.ip,self.port,md5,ttl) try: generator = self.connection.establish() while True: connected = generator.next() if not connected: yield False continue if self.peer.neighbor.api['neighbor-changes']: self.peer.reactor.processes.connected(self.peer) yield True return except StopIteration: # close called by the caller # self.close('could not connect to remote end') yield False return
def connect (self): # allows to test the protocol code using modified StringIO with a extra 'pending' function if not self.connection: local = self.neighbor.md5_ip.top() if not self.neighbor.auto_discovery else None peer = self.neighbor.peer_address.top() afi = self.neighbor.peer_address.afi md5 = self.neighbor.md5_password md5_base64 = self.neighbor.md5_base64 ttl_out = self.neighbor.ttl_out self.connection = Outgoing(afi,peer,local,self.port,md5,md5_base64,ttl_out) if not local and self.connection.init: self.neighbor.local_address = IP.create(self.connection.local) if self.neighbor.router_id is None and self.neighbor.local_address.afi == AFI.ipv4: self.neighbor.router_id = self.neighbor.local_address try: generator = self.connection.establish() while True: connected = six.next(generator) if not connected: yield False continue if self.peer.neighbor.api['neighbor-changes']: self.peer.reactor.processes.connected(self.peer.neighbor) yield True return except StopIteration: # close called by the caller # self.close('could not connect to remote end') yield False return
def test (): OPEN = ''.join([chr(int(_,16)) for _ in "FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF 00 1D 01 04 78 14 00 5A 52 DB 00 45 00".split()]) KEEP = ''.join([chr(int(_,16)) for _ in "FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF 00 00 04".split()]) from exabgp.reactor.network.outgoing import Outgoing connection = Outgoing(1,'82.219.0.69','82.219.212.34') writer = connection.writer(OPEN) while six.next(writer) is False: pass writer = connection.writer(KEEP) while six.next(writer) is False: pass reader = connection.reader() for size,msg,header,body,notification in reader: if size: print(od(header+body)) else: sys.stdout.write('-') reader = connection.reader() for size,msg,header,body,notification in reader: if size: print(od(header+body)) else: sys.stdout.write('+') connection.close()
def test (): OPEN = ''.join([chr(int(_,16)) for _ in "FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF 00 1D 01 04 78 14 00 5A 52 DB 00 45 00".split()]) KEEP = ''.join([chr(int(_,16)) for _ in "FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF 00 00 04".split()]) from exabgp.reactor.network.outgoing import Outgoing connection = Outgoing(1,'82.219.0.5','82.219.212.34') writer=connection._writer(OPEN) while writer() == False: pass writer=connection._writer(KEEP) while writer() == False: pass reader=connection.reader() for size,kind,header,body in reader: if size: print od(header+body) else: sys.stdout.write('-')
def test(): OPEN = ''.join([ chr(int(_, 16)) for _ in "FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF 00 1D 01 04 78 14 00 5A 52 DB 00 45 00" .split() ]) KEEP = ''.join([ chr(int(_, 16)) for _ in "FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF 00 00 04".split() ]) from exabgp.reactor.network.outgoing import Outgoing connection = Outgoing(1, '82.219.0.69', '82.219.212.34') writer = connection.writer(OPEN) while next(writer) is False: pass writer = connection.writer(KEEP) while next(writer) is False: pass reader = connection.reader() for size, msg, header, body, notification in reader: if size: print(od(header + body)) else: sys.stdout.write('-') reader = connection.reader() for size, msg, header, body, notification in reader: if size: print(od(header + body)) else: sys.stdout.write('+') connection.close()
class Protocol(object): decode = True def __init__(self, peer): self.peer = peer self.neighbor = peer.neighbor self.negotiated = Negotiated(self.neighbor) self.connection = None if self.neighbor['connect']: self.port = self.neighbor['connect'] elif os.environ.get('exabgp.tcp.port', '').isdigit(): self.port = int(os.environ.get('exabgp.tcp.port')) elif os.environ.get('exabgp_tcp_port', '').isdigit(): self.port = int(os.environ.get('exabgp_tcp_port')) else: self.port = 179 from exabgp.environment import getenv self.log_routes = peer.neighbor['adj-rib-in'] or getenv().log.routes def fd(self): if self.connection is None: return -1 return self.connection.fd() # XXX: we use self.peer.neighbor['peer-address'] when we could use self.neighbor['peer-address'] def me(self, message): return "%s/%s %s" % (self.peer.neighbor['peer-address'], self.peer.neighbor['peer-as'], message) def accept(self, incoming): self.connection = incoming if self.peer.neighbor.api['neighbor-changes']: self.peer.reactor.processes.connected(self.peer.neighbor) # very important - as we use this function on __init__ return self def connect(self): # allows to test the protocol code using modified StringIO with a extra 'pending' function if self.connection: return local = self.neighbor['md5-ip'].top( ) if not self.neighbor.auto_discovery else None peer = self.neighbor['peer-address'].top() afi = self.neighbor['peer-address'].afi md5 = self.neighbor['md5-password'] md5_base64 = self.neighbor['md5-base64'] ttl_out = self.neighbor['outgoing-ttl'] self.connection = Outgoing(afi, peer, local, self.port, md5, md5_base64, ttl_out) for connected in self.connection.establish(): yield False if self.peer.neighbor.api['neighbor-changes']: self.peer.reactor.processes.connected(self.peer.neighbor) if not local: self.neighbor['local-address'] = IP.create(self.connection.local) if self.neighbor['router-id'] is None and self.neighbor[ 'local-address'].afi == AFI.ipv4: self.neighbor['router-id'] = self.neighbor['local-address'] yield True def close(self, reason='protocol closed, reason unspecified'): if self.connection: log.debug(reason, self.connection.session()) self.peer.stats['down'] = self.peer.stats.get('down', 0) + 1 self.connection.close() self.connection = None def _to_api(self, direction, message, raw): packets = self.neighbor.api['%s-packets' % direction] parsed = self.neighbor.api['%s-parsed' % direction] consolidate = self.neighbor.api['%s-consolidate' % direction] negotiated = self.negotiated if self.neighbor.api[ 'negotiated'] else None if consolidate: if packets: self.peer.reactor.processes.message(message.ID, self.peer.neighbor, direction, message, negotiated, raw[:19], raw[19:]) else: self.peer.reactor.processes.message(message.ID, self.peer.neighbor, direction, message, negotiated, b'', b'') else: if packets: self.peer.reactor.processes.packets(self.peer.neighbor, direction, int(message.ID), negotiated, raw[:19], raw[19:]) if parsed: self.peer.reactor.processes.message(message.ID, self.peer.neighbor, direction, message, negotiated, b'', b'') def write(self, message, negotiated=None): raw = message.message(negotiated) code = 'send-%s' % Message.CODE.short(message.ID) self.peer.stats[code] = self.peer.stats.get(code, 0) + 1 if self.neighbor.api.get(code, False): self._to_api('send', message, raw) for boolean in self.connection.writer(raw): yield boolean def send(self, raw): code = 'send-%s' % Message.CODE.short(raw[18]) self.peer.stats[code] = self.peer.stats.get(code, 0) + 1 if self.neighbor.api.get(code, False): message = Update.unpack_message(raw[19:], Direction.OUT, self.negotiated) self._to_api('send', message, raw) for boolean in self.connection.writer(raw): yield boolean # Read from network ....................................................... 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) 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 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 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 # # Sending message to peer # def new_open(self): if self.neighbor['local-as']: local_as = self.neighbor['local-as'] elif self.negotiated.received_open: local_as = self.negotiated.received_open.asn else: raise RuntimeError('no ASN available for the OPEN message') sent_open = Open( Version(4), local_as, self.neighbor['hold-time'], self.neighbor['router-id'], Capabilities().new(self.neighbor, self.peer._restarted), ) # we do not buffer open message in purpose for _ in self.write(sent_open): yield _NOP log.debug('>> %s' % sent_open, self.connection.session()) yield sent_open def new_keepalive(self, comment=''): keepalive = KeepAlive() for _ in self.write(keepalive): yield _NOP log.debug('>> KEEPALIVE%s' % (' (%s)' % comment if comment else ''), self.connection.session()) yield keepalive def new_notification(self, notification): for _ in self.write(notification): yield _NOP log.debug( '>> NOTIFICATION (%d,%d,"%s")' % (notification.code, notification.subcode, notification.data.decode('utf-8')), self.connection.session(), ) yield notification def new_update(self, include_withdraw): updates = self.neighbor.rib.outgoing.updates( self.neighbor['group-updates']) number = 0 for update in updates: for message in update.messages(self.negotiated, include_withdraw): number += 1 for boolean in self.send(message): # boolean is a transient network error we already announced yield _NOP if number: log.debug('>> %d UPDATE(s)' % number, self.connection.session()) yield _UPDATE def new_eor(self, afi, safi): eor = EOR(afi, safi) for _ in self.write(eor): yield _NOP log.debug('>> EOR %s %s' % (afi, safi), self.connection.session()) yield eor def new_eors(self, afi=AFI.undefined, safi=SAFI.undefined): # Send EOR to let our peer know he can perform a RIB update if self.negotiated.families: families = (self.negotiated.families if (afi, safi) == (AFI.undefined, SAFI.undefined) else [ (afi, safi), ]) for eor_afi, eor_safi in families: for _ in self.new_eor(eor_afi, eor_safi): yield _ else: # If we are not sending an EOR, send a keepalive as soon as when finished # So the other routers knows that we have no (more) routes to send ... # (is that behaviour documented somewhere ??) for eor in self.new_keepalive('EOR'): yield _NOP yield _UPDATE def new_operational(self, operational, negotiated): for _ in self.write(operational, negotiated): yield _NOP log.debug('>> OPERATIONAL %s' % str(operational), self.connection.session()) yield operational def new_refresh(self, refresh): for _ in self.write(refresh, None): yield _NOP log.debug('>> REFRESH %s' % str(refresh), self.connection.session()) yield refresh
class Protocol (object): decode = True def __init__ (self,peer): try: self.logger = Logger() except RuntimeError: self.logger = FakeLogger() self.peer = peer self.neighbor = peer.neighbor self.negotiated = Negotiated(self.neighbor) self.connection = None port = os.environ.get('exabgp.tcp.port','') self.port = int(port) if port.isdigit() else 179 # XXX: FIXME: check the the -19 is correct (but it is harmless) # The message size is the whole BGP message _without_ headers self.message_size = Message.MAX_LEN-Message.HEADER_LEN # XXX: we use self.peer.neighbor.peer_address when we could use self.neighbor.peer_address def __del__ (self): self.close('automatic protocol cleanup') def me (self,message): return "Peer %15s ASN %-7s %s" % (self.peer.neighbor.peer_address,self.peer.neighbor.peer_as,message) def accept (self,incoming): self.connection = incoming if self.peer.neighbor.api.neighbor_changes: self.peer.reactor.processes.connected(self.peer.neighbor.peer_address) # very important - as we use this function on __init__ return self def connect (self): # allows to test the protocol code using modified StringIO with a extra 'pending' function if not self.connection: peer = self.neighbor.peer_address local = self.neighbor.local_address md5 = self.neighbor.md5 ttl = self.neighbor.ttl self.connection = Outgoing(peer.afi,peer.ip,local.ip,self.port,md5,ttl) connected = False try: generator = self.connection.establish() while True: connected = generator.next() if not connected: yield False continue if self.peer.neighbor.api.neighbor_changes: self.peer.reactor.processes.connected(self.peer.neighbor.peer_address) yield True return except StopIteration: # close called by the caller # self.close('could not connect to remote end') yield False return def close (self,reason='protocol closed, reason unspecified'): if self.connection: self.logger.network(self.me(reason)) # must be first otherwise we could have a loop caused by the raise in the below self.connection.close() self.connection = None try: if self.peer.neighbor.api.neighbor_changes: self.peer.reactor.processes.down(self.peer.neighbor.peer_address,reason) except ProcessError: self.logger.message(self.me('could not send notification of neighbor close to API')) def write (self,message): if self.neighbor.api.send_packets: self.peer.reactor.processes.send(self.peer.neighbor.peer_address,ord(message[18]),message[:19],message[19:]) for boolean in self.connection.writer(message): yield boolean # Read from network ....................................................... def read_message (self,comment=''): try: for length,msg,header,body in self.connection.reader(): if not length: yield _NOP except NotifyError,n: raise Notify(n.code,n.subcode,str(n)) if self.neighbor.api.receive_packets: self.peer.reactor.processes.receive(self.peer.neighbor.peer_address,msg,header,body) if msg == Message.Type.UPDATE: self.logger.message(self.me('<< UPDATE')) if length == 30 and body.startswith(EOR.PREFIX): update = EORFactory(body) if self.neighbor.api.receive_routes: self.peer.reactor.processes.update(self.peer.neighbor.peer_address,update) elif self.neighbor.api.receive_routes: update = UpdateFactory(self.negotiated,body) if self.neighbor.api.receive_routes: self.peer.reactor.processes.update(self.peer.neighbor.peer_address,update) else: update = _UPDATE yield update elif msg == Message.Type.KEEPALIVE: self.logger.message(self.me('<< KEEPALIVE%s' % (' (%s)' % comment if comment else ''))) yield KeepAlive() elif msg == Message.Type.NOTIFICATION: self.logger.message(self.me('<< NOTIFICATION')) yield NotificationFactory(body) elif msg == Message.Type.ROUTE_REFRESH: if self.negotiated.refresh != REFRESH.absent: self.logger.message(self.me('<< ROUTE-REFRESH')) refresh = RouteRefreshFactory(body) if self.neighbor.api.receive_routes: if refresh.reserved in (RouteRefresh.start,RouteRefresh.end): self.peer.reactor.processes.refresh(self.peer.neighbor.peer_address,refresh) else: # XXX: FIXME: really should raise, we are too nice self.logger.message(self.me('<< NOP (un-negotiated type %d)' % msg)) refresh = UnknownMessageFactory(body) yield refresh elif msg == Message.Type.OPERATIONAL: if self.peer.neighbor.operational: operational = OperationalFactory(body) what = OperationalGroup[operational.what][0] self.peer.reactor.processes.operational(self.peer.neighbor.peer_address,what,operational) else: operational = _OPERATIONAL yield operational elif msg == Message.Type.OPEN: yield OpenFactory(body) else: # XXX: FIXME: really should raise, we are too nice self.logger.message(self.me('<< NOP (unknow type %d)' % msg)) yield UnknownMessageFactory(msg)
class Protocol(object): decode = True def __init__(self, peer): try: self.logger = Logger() except RuntimeError: self.logger = FakeLogger() self.peer = peer self.neighbor = peer.neighbor self.negotiated = Negotiated(self.neighbor) self.connection = None port = os.environ.get('exabgp.tcp.port', '') or os.environ.get( 'exabgp_tcp_port', '') self.port = int(port) if port.isdigit() else 179 # XXX: FIXME: check the the -19 is correct (but it is harmless) # The message size is the whole BGP message _without_ headers self.message_size = Message.MAX_LEN - Message.HEADER_LEN from exabgp.configuration.environment import environment self.log_routes = environment.settings().log.routes # XXX: we use self.peer.neighbor.peer_address when we could use self.neighbor.peer_address def __del__(self): self.close('automatic protocol cleanup') def me(self, message): return "Peer %15s ASN %-7s %s" % (self.peer.neighbor.peer_address, self.peer.neighbor.peer_as, message) def accept(self, incoming): self.connection = incoming self.peer.reactor.processes.reset(self.peer) if self.peer.neighbor.api['neighbor-changes']: self.peer.reactor.processes.connected(self.peer) # very important - as we use this function on __init__ return self def connect(self): # allows to test the protocol code using modified StringIO with a extra 'pending' function if not self.connection: peer = self.neighbor.peer_address local = self.neighbor.local_address md5 = self.neighbor.md5 ttl = self.neighbor.ttl self.connection = Outgoing(peer.afi, peer.ip, local.ip, self.port, md5, ttl) connected = False try: generator = self.connection.establish() while True: connected = generator.next() if not connected: yield False continue self.peer.reactor.processes.reset(self.peer) if self.peer.neighbor.api['neighbor-changes']: self.peer.reactor.processes.connected(self.peer) yield True return except StopIteration: # close called by the caller # self.close('could not connect to remote end') yield False return def close(self, reason='protocol closed, reason unspecified'): if self.connection: self.logger.network(self.me(reason)) # must be first otherwise we could have a loop caused by the raise in the below self.connection.close() self.connection = None try: if self.peer.neighbor.api['neighbor-changes']: self.peer.reactor.processes.down(self.peer, reason) except ProcessError: self.logger.message( self. me('could not send notification of neighbor close to API')) def write(self, message): if self.neighbor.api[ 'send-packets'] and not self.neighbor.api['consolidate']: self.peer.reactor.processes.send(self.peer, ord(message[18]), message[:19], message[19:]) for boolean in self.connection.writer(message): yield boolean # Read from network ....................................................... def read_message(self, comment=''): for length, msg, header, body, notify in self.connection.reader(): if notify: if self.neighbor.api['receive-packets']: self.peer.reactor.processes.receive( self.peer, msg, header, body) if self.neighbor.api[Message.ID.NOTIFICATION]: self.peer.reactor.processes.notification( self.peer, notify.code, notify.subcode, str(notify)) # XXX: is notify not already Notify class ? raise Notify(notify.code, notify.subcode, str(notify)) if not length: yield _NOP if self.neighbor.api[ 'receive-packets'] and not self.neighbor.api['consolidate']: self.peer.reactor.processes.receive(self.peer, msg, header, body) if msg == Message.ID.UPDATE and not self.neighbor.api[ 'receive-parsed'] and not self.log_routes: yield _UPDATE return message = Message.unpack_message(msg, body, self.negotiated) self.logger.message(self.me('<< %s' % Message.ID.name(msg))) if message.TYPE == Notification.TYPE: raise message if self.neighbor.api[msg]: if self.neighbor.api['receive-parsed']: if self.neighbor.api['consolidate'] and self.neighbor.api[ 'receive-packets']: self.peer.reactor.processes.message( msg, self.peer, message, header, body) else: self.peer.reactor.processes.message( msg, self.peer, message, '', '') yield message return # XXX: FIXME: check it is well 2,4 raise Notify(2, 4, 'unknown message received') # elif msg == Message.ID.ROUTE_REFRESH: # if self.negotiated.refresh != REFRESH.absent: # self.logger.message(self.me('<< ROUTE-REFRESH')) # refresh = RouteRefresh.unpack_message(body,self.negotiated) # if self.neighbor.api.receive_refresh: # if refresh.reserved in (RouteRefresh.start,RouteRefresh.end): # if self.neighbor.api.consolidate: # self.peer.reactor.process.refresh(self.peer,refresh,header,body) # else: # self.peer.reactor.processes.refresh(self.peer,refresh,'','') # else: # # XXX: FIXME: really should raise, we are too nice # self.logger.message(self.me('<< NOP (un-negotiated type %d)' % msg)) # refresh = UnknownMessage.unpack_message(body,self.negotiated) # yield refresh def validate_open(self): error = self.negotiated.validate(self.neighbor) if error is not None: raise Notify(*error) 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 recevied is not an open message (%s)' % received_open) self.logger.message(self.me('<< %s' % received_open)) yield received_open def read_keepalive(self, comment=''): for message in self.read_message(comment): if message.TYPE == NOP.TYPE: yield message else: break if message.TYPE != KeepAlive.TYPE: raise Notify(5, 2) yield message # # Sending message to peer # def new_open(self, restarted): sent_open = Open(4, self.neighbor.local_as, self.neighbor.router_id.ip, Capabilities().new(self.neighbor, restarted), self.neighbor.hold_time) # we do not buffer open message in purpose msg_send = sent_open.message() for _ in self.write(msg_send): yield _NOP self.logger.message(self.me('>> %s' % sent_open)) if self.neighbor.api[Message.ID.OPEN]: if self.neighbor.api['consolidate']: header = msg_send[0:38] body = msg_send[38:] self.peer.reactor.processes.message(Message.ID.OPEN, self.peer, sent_open, header, body, 'sent') else: self.peer.reactor.processes.message(Message.ID.OPEN, self.peer, sent_open, '', '', 'sent') yield sent_open def new_keepalive(self, comment=''): keepalive = KeepAlive() for _ in self.write(keepalive.message()): yield _NOP self.logger.message( self.me('>> KEEPALIVE%s' % (' (%s)' % comment if comment else ''))) yield keepalive def new_notification(self, notification): for _ in self.write(notification.message()): yield _NOP self.logger.message( self.me( '>> NOTIFICATION (%d,%d,"%s")' % (notification.code, notification.subcode, notification.data))) yield notification def new_update(self): updates = self.neighbor.rib.outgoing.updates( self.neighbor.group_updates) number = 0 for update in updates: for message in update.messages(self.negotiated): number += 1 for boolean in self.write(message): # boolean is a transient network error we already announced yield _NOP if number: self.logger.message(self.me('>> %d UPDATE(s)' % number)) yield _UPDATE def new_eors(self): # Send EOR to let our peer know he can perform a RIB update if self.negotiated.families: for afi, safi in self.negotiated.families: eor = EOR(afi, safi).message() for _ in self.write(eor): yield _NOP yield _UPDATE else: # If we are not sending an EOR, send a keepalive as soon as when finished # So the other routers knows that we have no (more) routes to send ... # (is that behaviour documented somewhere ??) for eor in self.new_keepalive('EOR'): yield _NOP yield _UPDATE def new_operational(self, operational, negotiated): for _ in self.write(operational.message(negotiated)): yield _NOP self.logger.message(self.me('>> OPERATIONAL %s' % str(operational))) yield operational def new_refresh(self, refresh, negotiated): for refresh in refresh.messages(negotiated): for _ in self.write(refresh): yield _NOP self.logger.message(self.me('>> REFRESH %s' % str(refresh))) yield refresh
class Protocol (object): decode = True def __init__ (self,peer): try: self.logger = Logger() except RuntimeError: self.logger = FakeLogger() self.peer = peer self.neighbor = peer.neighbor self.negotiated = Negotiated(self.neighbor) self.connection = None port = os.environ.get('exabgp.tcp.port','') self.port = int(port) if port.isdigit() else 179 # XXX: FIXME: check the the -19 is correct (but it is harmless) # The message size is the whole BGP message _without_ headers self.message_size = Message.MAX_LEN-Message.HEADER_LEN # XXX: we use self.peer.neighbor.peer_address when we could use self.neighbor.peer_address def __del__ (self): self.close('automatic protocol cleanup') def me (self,message): return "Peer %15s ASN %-7s %s" % (self.peer.neighbor.peer_address,self.peer.neighbor.peer_as,message) def accept (self,incoming): self.connection = incoming if self.peer.neighbor.api.neighbor_changes: self.peer.reactor.processes.connected(self.peer.neighbor.peer_address) # very important - as we use this function on __init__ return self def connect (self): # allows to test the protocol code using modified StringIO with a extra 'pending' function if not self.connection: peer = self.neighbor.peer_address local = self.neighbor.local_address md5 = self.neighbor.md5 ttl = self.neighbor.ttl self.connection = Outgoing(peer.afi,peer.ip,local.ip,self.port,md5,ttl) connected = False try: generator = self.connection.establish() while True: connected = generator.next() if not connected: yield False continue if self.peer.neighbor.api.neighbor_changes: self.peer.reactor.processes.connected(self.peer.neighbor.peer_address) yield True return except StopIteration: # close called by the caller # self.close('could not connect to remote end') yield False return def close (self,reason='protocol closed, reason unspecified'): if self.connection: self.logger.network(self.me(reason)) # must be first otherwise we could have a loop caused by the raise in the below self.connection.close() self.connection = None try: if self.peer.neighbor.api.neighbor_changes: self.peer.reactor.processes.down(self.peer.neighbor.peer_address,reason) except ProcessError: self.logger.message(self.me('could not send notification of neighbor close to API')) def write (self,message): if self.neighbor.api.send_packets: self.peer.reactor.processes.send(self.peer.neighbor.peer_address,message[18],message[:19],message[19:]) for boolean in self.connection.writer(message): yield boolean # Read from network ....................................................... def read_message (self,comment=''): try: for length,msg,header,body in self.connection.reader(): if not length: yield _NOP except NotifyError,n: raise Notify(n.code,n.subcode,str(n)) if self.neighbor.api.receive_packets: self.peer.reactor.processes.receive(self.peer.neighbor.peer_address,msg,header,body) if msg == Message.Type.UPDATE: self.logger.message(self.me('<< UPDATE')) if length == 30 and body.startswith(EOR.PREFIX): update = EORFactory(body) if self.neighbor.api.receive_routes: self.peer.reactor.processes.update(self.peer.neighbor.peer_address,update) elif self.neighbor.api.receive_routes: update = UpdateFactory(self.negotiated,body) if self.neighbor.api.receive_routes: self.peer.reactor.processes.update(self.peer.neighbor.peer_address,update) else: update = _UPDATE yield update elif msg == Message.Type.KEEPALIVE: self.logger.message(self.me('<< KEEPALIVE%s' % (' (%s)' % comment if comment else ''))) yield KeepAlive() elif msg == Message.Type.NOTIFICATION: self.logger.message(self.me('<< NOTIFICATION')) yield NotificationFactory(body) elif msg == Message.Type.ROUTE_REFRESH: if self.negotiated.refresh != REFRESH.absent: self.logger.message(self.me('<< ROUTE-REFRESH')) refresh = RouteRefreshFactory(body) if self.neighbor.api.receive_routes: if refresh.reserved in (RouteRefresh.start,RouteRefresh.end): self.peer.reactor.processes.refresh(self.peer.neighbor.peer_address,refresh) else: # XXX: FIXME: really should raise, we are too nice self.logger.message(self.me('<< NOP (un-negotiated type %d)' % msg)) refresh = UnknownMessageFactory(body) yield refresh elif msg == Message.Type.OPERATIONAL: if self.peer.neighbor.operational: operational = OperationalFactory(body) what = OperationalGroup[operational.what][0] self.peer.reactor.processes.operational(self.peer.neighbor.peer_address,what,operational) else: operational = _OPERATIONAL yield operational elif msg == Message.Type.OPEN: yield OpenFactory(body) else: # XXX: FIXME: really should raise, we are too nice self.logger.message(self.me('<< NOP (unknow type %d)' % msg)) yield UnknownMessageFactory(msg)
class Protocol (object): decode = True def __init__ (self,peer): try: self.logger = Logger() except RuntimeError: self.logger = FakeLogger() self.peer = peer self.neighbor = peer.neighbor self.negotiated = Negotiated(self.neighbor) self.connection = None port = os.environ.get('exabgp.tcp.port','') or os.environ.get('exabgp_tcp_port','') self.port = int(port) if port.isdigit() else 179 # XXX: FIXME: check the the -19 is correct (but it is harmless) # The message size is the whole BGP message _without_ headers self.message_size = Message.MAX_LEN-Message.HEADER_LEN from exabgp.configuration.environment import environment self.log_routes = environment.settings().log.routes # XXX: we use self.peer.neighbor.peer_address when we could use self.neighbor.peer_address def __del__ (self): self.close('automatic protocol cleanup') def me (self,message): return "Peer %15s ASN %-7s %s" % (self.peer.neighbor.peer_address,self.peer.neighbor.peer_as,message) def accept (self,incoming): self.connection = incoming if self.peer.neighbor.api['neighbor-changes']: self.peer.reactor.processes.connected(self.peer) # very important - as we use this function on __init__ return self def connect (self): # allows to test the protocol code using modified StringIO with a extra 'pending' function if not self.connection: peer = self.neighbor.peer_address local = self.neighbor.local_address md5 = self.neighbor.md5 ttl = self.neighbor.ttl self.connection = Outgoing(peer.afi,peer.ip,local.ip,self.port,md5,ttl) try: generator = self.connection.establish() while True: connected = generator.next() if not connected: yield False continue if self.peer.neighbor.api['neighbor-changes']: self.peer.reactor.processes.connected(self.peer) yield True return except StopIteration: # close called by the caller # self.close('could not connect to remote end') yield False return def close (self,reason='protocol closed, reason unspecified'): if self.connection: self.logger.network(self.me(reason)) # must be first otherwise we could have a loop caused by the raise in the below self.connection.close() self.connection = None try: if self.peer.neighbor.api['neighbor-changes']: self.peer.reactor.processes.down(self.peer,reason) except ProcessError: self.logger.message(self.me('could not send notification of neighbor close to API')) def write (self,message): if self.neighbor.api['send-packets'] and not self.neighbor.api['consolidate']: self.peer.reactor.processes.send(self.peer,ord(message[18]),message[:19],message[19:]) for boolean in self.connection.writer(message): yield boolean # Read from network ....................................................... def read_message (self): for length,msg,header,body,notify in self.connection.reader(): if notify: if self.neighbor.api['receive-packets']: self.peer.reactor.processes.receive(self.peer,msg,header,body) if self.neighbor.api[Message.ID.NOTIFICATION]: self.peer.reactor.processes.notification(self.peer,notify.code,notify.subcode,str(notify)) # XXX: is notify not already Notify class ? raise Notify(notify.code,notify.subcode,str(notify)) if not length: yield _NOP if self.neighbor.api['receive-packets'] and not self.neighbor.api['consolidate']: self.peer.reactor.processes.receive(self.peer,msg,header,body) if msg == Message.ID.UPDATE and not self.neighbor.api['receive-parsed'] and not self.log_routes: yield _UPDATE return self.logger.message(self.me('<< %s' % Message.ID.name(msg))) try: message = Message.unpack_message(msg,body,self.negotiated) except (KeyboardInterrupt,SystemExit,Notify): raise except Exception,e: self.logger.message(self.me('Could not decode message %s' % Capability.hex(msg))) self.logger.message(self.me('%s' % str(e))) raise Notify(2,0,'can not decode update message %s' % Capability.hex(msg)) if message.TYPE == Notification.TYPE: raise message if self.neighbor.api[msg]: if self.neighbor.api['receive-parsed']: if self.neighbor.api['consolidate'] and self.neighbor.api['receive-packets']: self.peer.reactor.processes.message(msg,self.peer,message,header,body) else: self.peer.reactor.processes.message(msg,self.peer,message,'','') yield message return # XXX: FIXME: check it is well 2,4 raise Notify(2,4,'unknown message received')
class Protocol (object): decode = True def __init__ (self,peer): try: self.logger = Logger() except RuntimeError: self.logger = FakeLogger() self.peer = peer self.neighbor = peer.neighbor self.negotiated = Negotiated(self.neighbor) self.connection = None port = os.environ.get('exabgp.tcp.port','') or os.environ.get('exabgp_tcp_port','') self.port = int(port) if port.isdigit() else 179 # XXX: FIXME: check the the -19 is correct (but it is harmless) # The message size is the whole BGP message _without_ headers self.message_size = Message.MAX_LEN-Message.HEADER_LEN from exabgp.configuration.environment import environment self.log_routes = environment.settings().log.routes # XXX: we use self.peer.neighbor.peer_address when we could use self.neighbor.peer_address def __del__ (self): self.close('automatic protocol cleanup') def me (self,message): return "Peer %15s ASN %-7s %s" % (self.peer.neighbor.peer_address,self.peer.neighbor.peer_as,message) def accept (self,incoming): self.connection = incoming self.peer.reactor.processes.reset(self.peer) if self.peer.neighbor.api.neighbor_changes: self.peer.reactor.processes.connected(self.peer) # very important - as we use this function on __init__ return self def connect (self): # allows to test the protocol code using modified StringIO with a extra 'pending' function if not self.connection: peer = self.neighbor.peer_address local = self.neighbor.local_address md5 = self.neighbor.md5 ttl = self.neighbor.ttl self.connection = Outgoing(peer.afi,peer.ip,local.ip,self.port,md5,ttl) connected = False try: generator = self.connection.establish() while True: connected = generator.next() if not connected: yield False continue self.peer.reactor.processes.reset(self.peer) if self.peer.neighbor.api.neighbor_changes: self.peer.reactor.processes.connected(self.peer) yield True return except StopIteration: # close called by the caller # self.close('could not connect to remote end') yield False return def close (self,reason='protocol closed, reason unspecified'): if self.connection: self.logger.network(self.me(reason)) # must be first otherwise we could have a loop caused by the raise in the below self.connection.close() self.connection = None try: if self.peer.neighbor.api.neighbor_changes: self.peer.reactor.processes.down(self.peer,reason) except ProcessError: self.logger.message(self.me('could not send notification of neighbor close to API')) def write (self,message): if self.neighbor.api.send_packets: self.peer.reactor.processes.send(self.peer,ord(message[18]),message[:19],message[19:]) for boolean in self.connection.writer(message): yield boolean # Read from network ....................................................... def read_message (self,comment=''): self.peer.reactor.processes.increase(self.peer) for length,msg,header,body,notify in self.connection.reader(): if notify: if self.neighbor.api.receive_packets: self.peer.reactor.processes.receive(self.peer,msg,header,body) if self.neighbor.api.receive_notifications: self.peer.reactor.processes.notification(self.peer,notify.code,notify.subcode,str(notify)) # XXX: is notify not already Notify class ? raise Notify(notify.code,notify.subcode,str(notify)) if not length: yield _NOP if self.neighbor.api.receive_packets: self.peer.reactor.processes.receive(self.peer,msg,header,body) if msg == Message.Type.UPDATE: self.logger.message(self.me('<< UPDATE')) # This could be speed up massively by changing the order of the IF if length == 23: update = EORFactory() if self.neighbor.api.receive_updates: if self.neighbor.api.consolidate: self.peer.reactor.processes.update(self.peer,update,header,body) else: self.peer.reactor.processes.update(self.peer,update,'','') elif length == 30 and body.startswith(EOR.MP): update = EORFactory(body) if self.neighbor.api.receive_updates: if self.neighbor.api.consolidate: self.peer.reactor.processes.update(self.peer,update,header,body) else: self.peer.reactor.processes.update(self.peer,update,'','') elif self.neighbor.api.receive_updates: update = UpdateFactory(self.negotiated,body) if self.neighbor.api.consolidate: self.peer.reactor.processes.update(self.peer,update,header,body) else: self.peer.reactor.processes.update(self.peer,update,'','') elif self.log_routes: update = UpdateFactory(self.negotiated,body) else: update = _UPDATE yield update elif msg == Message.Type.KEEPALIVE: self.logger.message(self.me('<< KEEPALIVE%s' % (' (%s)' % comment if comment else ''))) if self.neighbor.api.receive_keepalives: if self.neighbor.api.consolidate: self.peer.reactor.processes.keepalive(self.peer,msg,header,body) else: self.peer.reactor.processes.keepalive(self.peer,msg,'','') yield KeepAlive() elif msg == Message.Type.NOTIFICATION: self.logger.message(self.me('<< NOTIFICATION')) yield NotificationFactory(body) elif msg == Message.Type.ROUTE_REFRESH: if self.negotiated.refresh != REFRESH.absent: self.logger.message(self.me('<< ROUTE-REFRESH')) refresh = RouteRefreshFactory(body) if self.neighbor.api.receive_refresh: if refresh.reserved in (RouteRefresh.start,RouteRefresh.end): if self.neighbor.api.consolidate: self.peer.reactor.process.refresh(self.peer,refresh,header,body) else: self.peer.reactor.processes.refresh(self.peer,refresh,'','') else: # XXX: FIXME: really should raise, we are too nice self.logger.message(self.me('<< NOP (un-negotiated type %d)' % msg)) refresh = UnknownMessageFactory(body) yield refresh elif msg == Message.Type.OPERATIONAL: if self.peer.neighbor.operational: operational = OperationalFactory(body) what = OperationalGroup[operational.what][0] if self.neighbor.api.consolidate: self.peer.reactor.processes.operational(self.peer,what,operational,header,body) else: self.peer.reactor.processes.operational(self.peer,what,operational,'','') else: operational = _OPERATIONAL yield operational elif msg == Message.Type.OPEN: if self.neighbor.api.receive_opens: open_message = OpenFactory(body) if self.neighbor.api.consolidate: self.peer.reactor.processes.open(self.peer,'received',open_message,header,body) else: self.peer.reactor.processes.open(self.peer,'received',open_message,'','') yield OpenFactory(body) else: # XXX: FIXME: really should raise, we are too nice self.logger.message(self.me('<< NOP (unknow type %d)' % msg)) yield UnknownMessageFactory(msg) def validate_open (self): error = self.negotiated.validate(self.neighbor) if error is not None: raise Notify(*error) 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 == Notification.TYPE: raise received_open if received_open.TYPE != Open.TYPE: raise Notify(5,1,'The first packet recevied is not an open message (%s)' % received_open) self.logger.message(self.me('<< %s' % received_open)) yield received_open def read_keepalive (self,comment=''): for message in self.read_message(comment): if message.TYPE == NOP.TYPE: yield message else: break if message.TYPE == Notification.TYPE: raise message if message.TYPE != KeepAlive.TYPE: raise Notify(5,2) yield message # # Sending message to peer # def new_open (self,restarted): sent_open = Open( 4, self.neighbor.local_as, self.neighbor.router_id.ip, Capabilities().new(self.neighbor,restarted), self.neighbor.hold_time ) # we do not buffer open message in purpose msg_send = sent_open.message() for _ in self.write(msg_send): yield _NOP self.logger.message(self.me('>> %s' % sent_open)) if self.neighbor.api.receive_opens: if self.neighbor.api.consolidate: header = msg_send[0:38] body = msg_send[38:] self.peer.reactor.processes.open(self.peer,'sent',sent_open,header,body) else: self.peer.reactor.processes.open(self.peer,'sent',sent_open,'','') yield sent_open def new_keepalive (self,comment=''): keepalive = KeepAlive() for _ in self.write(keepalive.message()): yield _NOP self.logger.message(self.me('>> KEEPALIVE%s' % (' (%s)' % comment if comment else ''))) yield keepalive def new_notification (self,notification): for _ in self.write(notification.message()): yield _NOP self.logger.message(self.me('>> NOTIFICATION (%d,%d,"%s")' % (notification.code,notification.subcode,notification.data))) yield notification def new_update (self): updates = self.neighbor.rib.outgoing.updates(self.neighbor.group_updates) number = 0 for update in updates: for message in update.messages(self.negotiated): number += 1 for boolean in self.write(message): # boolean is a transient network error we already announced yield _NOP if number: self.logger.message(self.me('>> %d UPDATE(s)' % number)) yield _UPDATE def new_eors (self): # Send EOR to let our peer know he can perform a RIB update if self.negotiated.families: for afi,safi in self.negotiated.families: eor = EOR(afi,safi).message() for _ in self.write(eor): yield _NOP yield _UPDATE else: # If we are not sending an EOR, send a keepalive as soon as when finished # So the other routers knows that we have no (more) routes to send ... # (is that behaviour documented somewhere ??) for eor in self.new_keepalive('EOR'): yield _NOP yield _UPDATE def new_operational (self,operational,negotiated): for _ in self.write(operational.message(negotiated)): yield _NOP self.logger.message(self.me('>> OPERATIONAL %s' % str(operational))) yield operational def new_refresh (self,refresh,negotiated): for refresh in refresh.messages(negotiated): for _ in self.write(refresh): yield _NOP self.logger.message(self.me('>> REFRESH %s' % str(refresh))) yield refresh
class Protocol (object): decode = True def __init__ (self, peer): try: self.logger = Logger() except RuntimeError: self.logger = FakeLogger() self.peer = peer self.neighbor = peer.neighbor self.negotiated = Negotiated(self.neighbor) self.connection = None if self.neighbor.connect: self.port = self.neighbor.connect elif os.environ.get('exabgp.tcp.port','').isdigit(): self.port = int(os.environ.get('exabgp.tcp.port')) elif os.environ.get('exabgp_tcp_port','').isdigit(): self.port = int(os.environ.get('exabgp_tcp_port')) else: self.port = 179 # XXX: FIXME: check the the -19 is correct (but it is harmless) # The message size is the whole BGP message _without_ headers self.message_size = Message.MAX_LEN-Message.HEADER_LEN from exabgp.configuration.environment import environment self.log_routes = peer.neighbor.adj_rib_in or environment.settings().log.routes # XXX: we use self.peer.neighbor.peer_address when we could use self.neighbor.peer_address def me (self, message): return "%s/%s %s" % (self.peer.neighbor.peer_address,self.peer.neighbor.peer_as,message) def accept (self, incoming): self.connection = incoming if self.peer.neighbor.api['neighbor-changes']: self.peer.reactor.processes.connected(self.peer.neighbor) # very important - as we use this function on __init__ return self def connect (self): # allows to test the protocol code using modified StringIO with a extra 'pending' function if not self.connection: local = self.neighbor.md5_ip.top() if not self.neighbor.auto_discovery else None peer = self.neighbor.peer_address.top() afi = self.neighbor.peer_address.afi md5 = self.neighbor.md5_password md5_base64 = self.neighbor.md5_base64 ttl_out = self.neighbor.ttl_out self.connection = Outgoing(afi,peer,local,self.port,md5,md5_base64,ttl_out) if not local and self.connection.init: self.neighbor.local_address = IP.create(self.connection.local) if self.neighbor.router_id is None and self.neighbor.local_address.afi == AFI.ipv4: self.neighbor.router_id = self.neighbor.local_address try: generator = self.connection.establish() while True: connected = six.next(generator) if not connected: yield False continue if self.peer.neighbor.api['neighbor-changes']: self.peer.reactor.processes.connected(self.peer.neighbor) yield True return except StopIteration: # close called by the caller # self.close('could not connect to remote end') yield False return def close (self, reason='protocol closed, reason unspecified'): if self.connection: self.logger.debug(reason,self.connection.session()) # must be first otherwise we could have a loop caused by the raise in the below self.connection.close() self.connection = None self.peer.stats['down'] = self.peer.stats.get('down',0) + 1 try: if self.peer.neighbor.api['neighbor-changes']: self.peer.reactor.processes.down(self.peer.neighbor,reason) except ProcessError: self.logger.debug('could not send notification of neighbor close to API',self.connection.session()) def _to_api (self,direction,message,raw): packets = self.neighbor.api['%s-packets' % direction] parsed = self.neighbor.api['%s-parsed' % direction] consolidate = self.neighbor.api['%s-consolidate' % direction] negotiated = self.negotiated if self.neighbor.api['negotiated'] else None if consolidate: if packets: self.peer.reactor.processes.message(self.peer.neighbor,direction,message,negotiated,raw[:19],raw[19:]) else: self.peer.reactor.processes.message(self.peer.neighbor,direction,message,negotiated,b'',b'') else: if packets: self.peer.reactor.processes.packets(self.peer.neighbor,direction,int(message.ID),negotiated,raw[:19],raw[19:]) if parsed: self.peer.reactor.processes.message(message.ID,self.peer.neighbor,direction,message,negotiated,b'',b'') def write (self, message, negotiated=None): raw = message.message(negotiated) code = 'send-%s' % Message.CODE.short(message.ID) self.peer.stats[code] = self.peer.stats.get(code,0) + 1 if self.neighbor.api.get(code,False): self._to_api('send',message,raw) for boolean in self.connection.writer(raw): yield boolean def send (self, raw): code = 'send-%s' % Message.CODE.short(ordinal(raw[18])) self.peer.stats[code] = self.peer.stats.get(code,0) + 1 if self.neighbor.api.get(code,False): message = Update.unpack_message(raw[19:],self.negotiated) self._to_api('send',message,raw) for boolean in self.connection.writer(raw): yield boolean # Read from network ....................................................... 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 self.logger.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,self.negotiated) except (KeyboardInterrupt,SystemExit,Notify): raise except Exception as exc: self.logger.debug('could not decode message "%d"' % msg_id,self.connection.session()) self.logger.debug('%s' % str(exc),self.connection.session()) self.logger.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) if self.neighbor.api['negotiated']: self.peer.reactor.processes.negotiated(self.peer.neighbor,self.negotiated) if self.negotiated.mismatch: self.logger.warning('--------------------------------------------------------------------',self.connection.session()) self.logger.warning('the connection can not carry the following family/families',self.connection.session()) for reason,(afi,safi) in self.negotiated.mismatch: self.logger.warning(' - %s is not configured for %s/%s' % (reason,afi,safi),self.connection.session()) self.logger.warning('therefore no routes of this kind can be announced on the connection',self.connection.session()) self.logger.warning('--------------------------------------------------------------------',self.connection.session()) 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.debug('<< %s' % received_open,self.connection.session()) yield received_open 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 # # Sending message to peer # def new_open (self): if self.neighbor.local_as: local_as = self.neighbor.local_as elif self.negotiated.received_open: local_as = self.negotiated.received_open.asn else: raise RuntimeError('no ASN available for the OPEN message') sent_open = Open( Version(4), local_as, self.neighbor.hold_time, self.neighbor.router_id, Capabilities().new(self.neighbor,self.peer._restarted) ) # we do not buffer open message in purpose for _ in self.write(sent_open): yield _NOP self.logger.debug('>> %s' % sent_open,self.connection.session()) yield sent_open def new_keepalive (self, comment=''): keepalive = KeepAlive() for _ in self.write(keepalive): yield _NOP self.logger.debug('>> KEEPALIVE%s' % (' (%s)' % comment if comment else ''),self.connection.session()) yield keepalive def new_notification (self, notification): for _ in self.write(notification): yield _NOP self.logger.debug('>> NOTIFICATION (%d,%d,"%s")' % (notification.code,notification.subcode,notification.data),self.connection.session()) yield notification def new_update (self, include_withdraw): updates = self.neighbor.rib.outgoing.updates(self.neighbor.group_updates) number = 0 for update in updates: for message in update.messages(self.negotiated,include_withdraw): number += 1 for boolean in self.send(message): # boolean is a transient network error we already announced yield _NOP if number: self.logger.debug('>> %d UPDATE(s)' % number,self.connection.session()) yield _UPDATE def new_eor (self, afi, safi): eor = EOR(afi,safi) for _ in self.write(eor): yield _NOP self.logger.debug('>> EOR %s %s' % (afi,safi),self.connection.session()) yield eor def new_eors (self, afi=AFI.undefined,safi=SAFI.undefined): # Send EOR to let our peer know he can perform a RIB update if self.negotiated.families: families = self.negotiated.families if (afi,safi) == (AFI.undefined,SAFI.undefined) else [(afi,safi),] for eor_afi,eor_safi in families: for _ in self.new_eor(eor_afi,eor_safi): yield _ else: # If we are not sending an EOR, send a keepalive as soon as when finished # So the other routers knows that we have no (more) routes to send ... # (is that behaviour documented somewhere ??) for eor in self.new_keepalive('EOR'): yield _NOP yield _UPDATE def new_operational (self, operational, negotiated): for _ in self.write(operational,negotiated): yield _NOP self.logger.debug('>> OPERATIONAL %s' % str(operational),self.connection.session()) yield operational def new_refresh (self, refresh): for _ in self.write(refresh,None): yield _NOP self.logger.debug('>> REFRESH %s' % str(refresh),self.connection.session()) yield refresh
class Protocol (object): decode = True def __init__ (self, peer): try: self.logger = Logger() except RuntimeError: self.logger = FakeLogger() self.peer = peer self.neighbor = peer.neighbor self.negotiated = Negotiated(self.neighbor) self.connection = None if self.neighbor.connect: self.port = self.neighbor.connect elif os.environ.get('exabgp.tcp.port','').isdigit(): self.port = int(os.environ.get('exabgp.tcp.port')) elif os.environ.get('exabgp_tcp_port','').isdigit(): self.port = int(os.environ.get('exabgp_tcp_port')) else: self.port = 179 # XXX: FIXME: check the the -19 is correct (but it is harmless) # The message size is the whole BGP message _without_ headers self.message_size = Message.MAX_LEN-Message.HEADER_LEN from exabgp.configuration.environment import environment self.log_routes = environment.settings().log.routes # XXX: we use self.peer.neighbor.peer_address when we could use self.neighbor.peer_address def __del__ (self): self.close('automatic protocol cleanup') def me (self, message): return "Peer %15s ASN %-7s %s" % (self.peer.neighbor.peer_address,self.peer.neighbor.peer_as,message) def accept (self, incoming): self.connection = incoming if self.peer.neighbor.api['neighbor-changes']: self.peer.reactor.processes.connected(self.peer.neighbor) # very important - as we use this function on __init__ return self def connect (self): # allows to test the protocol code using modified StringIO with a extra 'pending' function if not self.connection: local = self.neighbor.md5_ip peer = self.neighbor.peer_address md5 = self.neighbor.md5_password ttl_out = self.neighbor.ttl_out self.connection = Outgoing(peer.afi,peer.top(),local.top(),self.port,md5,ttl_out) try: generator = self.connection.establish() while True: connected = generator.next() if not connected: yield False continue if self.peer.neighbor.api['neighbor-changes']: self.peer.reactor.processes.connected(self.peer.neighbor) yield True return except StopIteration: # close called by the caller # self.close('could not connect to remote end') yield False return def close (self, reason='protocol closed, reason unspecified'): if self.connection: self.logger.network(self.me(reason)) # must be first otherwise we could have a loop caused by the raise in the below self.connection.close() self.connection = None try: if self.peer.neighbor.api['neighbor-changes']: self.peer.reactor.processes.down(self.peer.neighbor,reason) except ProcessError: self.logger.message(self.me('could not send notification of neighbor close to API')) def _to_api (self,direction,message,raw): packets = self.neighbor.api['%s-packets' % direction] parsed = self.neighbor.api['%s-parsed' % direction] consolidate = self.neighbor.api['%s-consolidate' % direction] if consolidate: if packets: self.peer.reactor.processes.message(self.peer.neighbor,direction,message,raw[:19],raw[19:]) else: self.peer.reactor.processes.message(self.peer.neighbor,direction,message,'','') else: if packets: self.peer.reactor.processes.packets(self.peer.neighbor,direction,int(message.ID),raw[:19],raw[19:]) if parsed: self.peer.reactor.processes.message(message.ID,self.peer.neighbor,direction,message,'','') def write (self, message, negotiated=None): raw = message.message(negotiated) if self.neighbor.api.get('send-%s' % Message.CODE.short(message.ID),False): self._to_api('send',message,raw) for boolean in self.connection.writer(raw): yield boolean def send (self,raw): if self.neighbor.api.get('send-%s' % Message.CODE.short(ord(raw[19])),False): message = Update.unpack_message(raw[19:],self.negotiated) self._to_api('send',message,raw) for boolean in self.connection.writer(raw): yield boolean # Read from network ....................................................... 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,'','') try: if message.TYPE == Notification.TYPE: raise message except: import pdb; pdb.set_trace() pass yield message
class Protocol(object): decode = True def __init__(self, peer): try: self.logger = Logger() except RuntimeError: self.logger = FakeLogger() self.peer = peer self.neighbor = peer.neighbor self.negotiated = Negotiated(self.neighbor) self.connection = None if self.neighbor.connect: self.port = self.neighbor.connect elif os.environ.get('exabgp.tcp.port', '').isdigit(): self.port = int(os.environ.get('exabgp.tcp.port')) elif os.environ.get('exabgp_tcp_port', '').isdigit(): self.port = int(os.environ.get('exabgp_tcp_port')) else: self.port = 179 # XXX: FIXME: check the the -19 is correct (but it is harmless) # The message size is the whole BGP message _without_ headers self.message_size = Message.MAX_LEN - Message.HEADER_LEN from exabgp.configuration.environment import environment self.log_routes = environment.settings().log.routes # XXX: we use self.peer.neighbor.peer_address when we could use self.neighbor.peer_address def __del__(self): self.close('automatic protocol cleanup') def me(self, message): return "Peer %15s ASN %-7s %s" % (self.peer.neighbor.peer_address, self.peer.neighbor.peer_as, message) def accept(self, incoming): self.connection = incoming if self.peer.neighbor.api['neighbor-changes']: self.peer.reactor.processes.connected(self.peer.neighbor) # very important - as we use this function on __init__ return self def connect(self): # allows to test the protocol code using modified StringIO with a extra 'pending' function if not self.connection: local = self.neighbor.md5_ip peer = self.neighbor.peer_address md5 = self.neighbor.md5_password ttl_out = self.neighbor.ttl_out self.connection = Outgoing(peer.afi, peer.top(), local.top(), self.port, md5, ttl_out) try: generator = self.connection.establish() while True: connected = generator.next() if not connected: yield False continue if self.peer.neighbor.api['neighbor-changes']: self.peer.reactor.processes.connected( self.peer.neighbor) yield True return except StopIteration: # close called by the caller # self.close('could not connect to remote end') yield False return def close(self, reason='protocol closed, reason unspecified'): if self.connection: self.logger.network(self.me(reason)) # must be first otherwise we could have a loop caused by the raise in the below self.connection.close() self.connection = None try: if self.peer.neighbor.api['neighbor-changes']: self.peer.reactor.processes.down(self.peer.neighbor, reason) except ProcessError: self.logger.message( self. me('could not send notification of neighbor close to API')) def _to_api(self, direction, message, raw): packets = self.neighbor.api['%s-packets' % direction] parsed = self.neighbor.api['%s-parsed' % direction] consolidate = self.neighbor.api['%s-consolidate' % direction] if consolidate: if packets: self.peer.reactor.processes.message(self.peer.neighbor, direction, message, raw[:19], raw[19:]) else: self.peer.reactor.processes.message(self.peer.neighbor, direction, message, '', '') else: if packets: self.peer.reactor.processes.packets(self.peer.neighbor, direction, int(message.ID), raw[:19], raw[19:]) if parsed: self.peer.reactor.processes.message(message.ID, self.peer.neighbor, direction, message, '', '') def write(self, message, negotiated=None): raw = message.message(negotiated) if self.neighbor.api.get('send-%s' % Message.CODE.short(message.ID), False): self._to_api('send', message, raw) for boolean in self.connection.writer(raw): yield boolean def send(self, raw): if self.neighbor.api.get('send-%s' % Message.CODE.short(ord(raw[19])), False): message = Update.unpack_message(raw[19:], self.negotiated) self._to_api('send', message, raw) for boolean in self.connection.writer(raw): yield boolean # Read from network ....................................................... 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
class Protocol(object): decode = True def __init__(self, peer): try: self.logger = Logger() except RuntimeError: self.logger = FakeLogger() self.peer = peer self.neighbor = peer.neighbor self.negotiated = Negotiated(self.neighbor) self.connection = None port = os.environ.get('exabgp.tcp.port', '') or os.environ.get( 'exabgp_tcp_port', '') self.port = int(port) if port.isdigit() else 179 # XXX: FIXME: check the the -19 is correct (but it is harmless) # The message size is the whole BGP message _without_ headers self.message_size = Message.MAX_LEN - Message.HEADER_LEN from exabgp.configuration.environment import environment self.log_routes = environment.settings().log.routes # XXX: we use self.peer.neighbor.peer_address when we could use self.neighbor.peer_address def __del__(self): self.close('automatic protocol cleanup') def me(self, message): return "Peer %15s ASN %-7s %s" % (self.peer.neighbor.peer_address, self.peer.neighbor.peer_as, message) def accept(self, incoming): self.connection = incoming if self.peer.neighbor.api['neighbor-changes']: self.peer.reactor.processes.connected(self.peer) # very important - as we use this function on __init__ return self def connect(self): # allows to test the protocol code using modified StringIO with a extra 'pending' function if not self.connection: peer = self.neighbor.peer_address local = self.neighbor.local_address md5 = self.neighbor.md5 ttl = self.neighbor.ttl self.connection = Outgoing(peer.afi, peer.ip, local.ip, self.port, md5, ttl) try: generator = self.connection.establish() while True: connected = generator.next() if not connected: yield False continue if self.peer.neighbor.api['neighbor-changes']: self.peer.reactor.processes.connected(self.peer) yield True return except StopIteration: # close called by the caller # self.close('could not connect to remote end') yield False return def close(self, reason='protocol closed, reason unspecified'): if self.connection: self.logger.network(self.me(reason)) # must be first otherwise we could have a loop caused by the raise in the below self.connection.close() self.connection = None try: if self.peer.neighbor.api['neighbor-changes']: self.peer.reactor.processes.down(self.peer, reason) except ProcessError: self.logger.message( self. me('could not send notification of neighbor close to API')) def write(self, message): if self.neighbor.api[ 'send-packets'] and not self.neighbor.api['consolidate']: self.peer.reactor.processes.send(self.peer, ord(message[18]), message[:19], message[19:]) for boolean in self.connection.writer(message): yield boolean # Read from network ....................................................... def read_message(self): # This will always be defined by the loop but scope leaking upset scrutinizer/pylint msg_id = None for length, msg_id, header, body, notify in self.connection.reader(): if notify: if self.neighbor.api['receive-packets']: self.peer.reactor.processes.receive( self.peer, msg_id, header, body) if self.neighbor.api[Message.CODE.NOTIFICATION]: self.peer.reactor.processes.notification( self.peer, notify.code, notify.subcode, str(notify)) # XXX: is notify not already Notify class ? raise Notify(notify.code, notify.subcode, str(notify)) if not length: yield _NOP if self.neighbor.api[ 'receive-packets'] and not self.neighbor.api['consolidate']: self.peer.reactor.processes.receive(self.peer, msg_id, header, body) if msg_id == Message.CODE.UPDATE and not self.neighbor.api[ 'receive-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))) # XXX: TODO: add backtrace here raise Notify(1, 0, 'can not decode update message of type "%d"' % msg_id) # raise Notify(5,0,'unknown message received') if message.TYPE == Notification.TYPE: raise message if self.neighbor.api[msg_id]: if self.neighbor.api['receive-parsed']: if self.neighbor.api['consolidate'] and self.neighbor.api[ 'receive-packets']: self.peer.reactor.processes.message( msg_id, self.peer, message, header, body) else: self.peer.reactor.processes.message( msg_id, self.peer, message, '', '') yield message
class Protocol(object): decode = True def __init__(self, peer): try: self.logger = Logger() except RuntimeError: self.logger = FakeLogger() self.peer = peer self.neighbor = peer.neighbor self.negotiated = Negotiated(self.neighbor) self.connection = None if self.neighbor.connect: self.port = self.neighbor.connect elif os.environ.get('exabgp.tcp.port', '').isdigit(): self.port = int(os.environ.get('exabgp.tcp.port')) elif os.environ.get('exabgp_tcp_port', '').isdigit(): self.port = int(os.environ.get('exabgp_tcp_port')) else: self.port = 179 # XXX: FIXME: check the the -19 is correct (but it is harmless) # The message size is the whole BGP message _without_ headers self.message_size = Message.MAX_LEN - Message.HEADER_LEN from exabgp.configuration.environment import environment self.log_routes = environment.settings().log.routes # XXX: we use self.peer.neighbor.peer_address when we could use self.neighbor.peer_address def me(self, message): return "Peer %15s ASN %-7s %s" % (self.peer.neighbor.peer_address, self.peer.neighbor.peer_as, message) def accept(self, incoming): self.connection = incoming if self.peer.neighbor.api['neighbor-changes']: self.peer.reactor.processes.connected(self.peer.neighbor) # very important - as we use this function on __init__ return self def connect(self): # allows to test the protocol code using modified StringIO with a extra 'pending' function if not self.connection: local = self.neighbor.md5_ip peer = self.neighbor.peer_address md5 = self.neighbor.md5_password ttl_out = self.neighbor.ttl_out self.connection = Outgoing(peer.afi, peer.top(), local.top(), self.port, md5, ttl_out) try: generator = self.connection.establish() while True: connected = six.next(generator) if not connected: yield False continue if self.peer.neighbor.api['neighbor-changes']: self.peer.reactor.processes.connected( self.peer.neighbor) yield True return except StopIteration: # close called by the caller # self.close('could not connect to remote end') yield False return def close(self, reason='protocol closed, reason unspecified'): if self.connection: self.logger.network(self.me(reason)) # must be first otherwise we could have a loop caused by the raise in the below self.connection.close() self.connection = None try: if self.peer.neighbor.api['neighbor-changes']: self.peer.reactor.processes.down(self.peer.neighbor, reason) except ProcessError: self.logger.message( self. me('could not send notification of neighbor close to API')) def _to_api(self, direction, message, raw): packets = self.neighbor.api['%s-packets' % direction] parsed = self.neighbor.api['%s-parsed' % direction] consolidate = self.neighbor.api['%s-consolidate' % direction] if consolidate: if packets: self.peer.reactor.processes.message(self.peer.neighbor, direction, message, raw[:19], raw[19:]) else: self.peer.reactor.processes.message(self.peer.neighbor, direction, message, '', '') else: if packets: self.peer.reactor.processes.packets(self.peer.neighbor, direction, int(message.ID), raw[:19], raw[19:]) if parsed: self.peer.reactor.processes.message(message.ID, self.peer.neighbor, direction, message, '', '') def write(self, message, negotiated=None): raw = message.message(negotiated) if self.neighbor.api.get('send-%s' % Message.CODE.short(message.ID), False): self._to_api('send', message, raw) for boolean in self.connection.writer(raw): yield boolean def send(self, raw): if self.neighbor.api.get('send-%s' % Message.CODE.short(ord(raw[18])), False): message = Update.unpack_message(raw[19:], self.negotiated) self._to_api('send', message, raw) for boolean in self.connection.writer(raw): yield boolean # Read from network ....................................................... 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 as 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 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: 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 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) 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_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 # # Sending message to peer # def new_open(self, restarted): sent_open = Open(Version(4), self.neighbor.local_as, self.neighbor.hold_time, self.neighbor.router_id, Capabilities().new(self.neighbor, restarted)) # we do not buffer open message in purpose for _ in self.write(sent_open): yield _NOP self.logger.message(self.me('>> %s' % sent_open)) yield sent_open def new_keepalive(self, comment=''): keepalive = KeepAlive() for _ in self.write(keepalive): yield _NOP self.logger.message( self.me('>> KEEPALIVE%s' % (' (%s)' % comment if comment else ''))) yield keepalive def new_notification(self, notification): for _ in self.write(notification): yield _NOP self.logger.message( self.me( '>> NOTIFICATION (%d,%d,"%s")' % (notification.code, notification.subcode, notification.data))) yield notification def new_update(self, include_withdraw): updates = self.neighbor.rib.outgoing.updates( self.neighbor.group_updates) number = 0 for update in updates: for message in update.messages(self.negotiated, include_withdraw): number += 1 for boolean in self.send(message): # boolean is a transient network error we already announced yield _NOP if number: self.logger.message(self.me('>> %d UPDATE(s)' % number)) yield _UPDATE def new_eor(self, afi, safi): eor = EOR(afi, safi) for _ in self.write(eor): yield _NOP self.logger.message(self.me('>> EOR %s %s' % (afi, safi))) yield eor def new_eors(self, afi=AFI.undefined, safi=SAFI.undefined): # Send EOR to let our peer know he can perform a RIB update if self.negotiated.families: families = self.negotiated.families if (afi, safi) == ( AFI.undefined, SAFI.undefined) else [ (afi, safi), ] for eor_afi, eor_safi in families: for _ in self.new_eor(eor_afi, eor_safi): yield _ else: # If we are not sending an EOR, send a keepalive as soon as when finished # So the other routers knows that we have no (more) routes to send ... # (is that behaviour documented somewhere ??) for eor in self.new_keepalive('EOR'): yield _NOP yield _UPDATE def new_operational(self, operational, negotiated): for _ in self.write(operational, negotiated): yield _NOP self.logger.message(self.me('>> OPERATIONAL %s' % str(operational))) yield operational def new_refresh(self, refresh): for _ in self.write(refresh, None): yield _NOP self.logger.message(self.me('>> REFRESH %s' % str(refresh))) yield refresh