def setUp(self): self.negotiated = {} for asn4 in (True, False): neighbor = FakeNeighbor() neighbor.asn4 = asn4 capa = Capabilities().new(neighbor, False) capa[Capability.CODE.MULTIPROTOCOL] = neighbor.families() # path = {} # for f in NLRI.known_families(): # if neighbor.add_path: # path[f] = neighbor.add_path # capa[Capability.CODE.ADD_PATH] = path o1 = Open(Version(4), ASN(neighbor.local_as), HoldTime(180), RouterID(neighbor.local_address.top()), capa) o2 = Open(Version(4), ASN(neighbor.peer_as), HoldTime(180), RouterID(neighbor.peer_address.top()), capa) negotiated = Negotiated(neighbor) negotiated.sent(o1) negotiated.received(o2) self.negotiated[asn4] = negotiated
def setUp(self): self.negotiated = {} for asn4 in (True, False): neighbor = FakeNeighbor() neighbor.asn4 = asn4 capa = Capabilities().new(neighbor, False) capa[Capability.CODE.MULTIPROTOCOL] = neighbor.families() # path = {} # for f in NLRI.known_families(): # if neighbor.add_path: # path[f] = neighbor.add_path # capa[Capability.CODE.ADD_PATH] = path routerid_1 = str(neighbor.router_id) routerid_2 = '.'.join( str((int(_) + 1) % 250) for _ in str(neighbor.router_id).split('.', -1)) o1 = Open(Version(4), ASN(neighbor.local_as), HoldTime(180), RouterID(routerid_1), capa) o2 = Open(Version(4), ASN(neighbor.peer_as), HoldTime(180), RouterID(routerid_2), capa) negotiated = Negotiated(neighbor) negotiated.sent(o1) negotiated.received(o2) self.negotiated[asn4] = negotiated
def __init__(self): self.update({ 'description': 'a test neighbor', 'router-id': RouterID('127.0.0.1'), 'local-address': IPv4('127.0.0.1'), 'peer-address': IPv4('127.0.0.1'), 'host-name': 'localhost', 'domain-name': 'localdomain', 'peer-as': ASN('65500'), 'local-as': ASN('65500'), 'hold-time': HoldTime(180), 'capability': { 'asn4': False, 'add-path': 0, 'extended_message': False, 'nexthop': None, 'route-refresh': False, 'graceful-restart': False, 'multi-session': None, 'add-path': None, 'aigp': None, 'operational': None, 'extended-message': True, }, })
class Open (Message): ID = Message.CODE.OPEN TYPE = chr(Message.CODE.OPEN) def __init__ (self, version, asn, router_id, capabilities, hold_time): self.version = Version(version) self.asn = ASN(asn) self.hold_time = HoldTime(hold_time) self.router_id = RouterID(router_id) self.capabilities = capabilities def message (self): return self._message("%s%s%s%s%s" % ( self.version.pack(), self.asn.trans(), self.hold_time.pack(), self.router_id.pack(), self.capabilities.pack() )) def __str__ (self): return "OPEN version=%d asn=%d hold_time=%s router_id=%s capabilities=[%s]" % (self.version, self.asn, self.hold_time, self.router_id,self.capabilities) @classmethod def unpack_message (cls, data, _): version = ord(data[0]) if version != 4: # Only version 4 is supported nowdays.. raise Notify(2,1,data[0]) asn = unpack('!H',data[1:3])[0] hold_time = unpack('!H',data[3:5])[0] numeric = unpack('!L',data[5:9])[0] router_id = "%d.%d.%d.%d" % (numeric >> 24,(numeric >> 16) & 0xFF,(numeric >> 8) & 0xFF,numeric & 0xFF) capabilities = Capabilities.unpack(data[9:]) return cls(version,asn,router_id,capabilities,hold_time)
class FakeNeighbor(object): description = 'a test neighbor' router_id = RouterID('127.0.0.1') local_address = IPv4('127.0.0.1') peer_address = IPv4('127.0.0.1') host_name = 'localhost' domain_name = 'localdomain' peer_as = ASN('65500') local_as = ASN('65500') hold_time = HoldTime(180) asn4 = False add_path = 0 extended_message = False nexthop = None # capability route_refresh = False graceful_restart = False multisession = None add_path = None aigp = None operational = None @staticmethod def families(): return NLRI.known_families()
def __init__(self): # self.logger should not be used here as long as we do use deepcopy as it contains a Lock self.description = '' self.router_id = None self.local_address = None self.peer_address = None self.peer_as = None self.local_as = None self.hold_time = HoldTime(180) self.asn4 = None self.add_path = 0 self.md5 = None self.ttl = None self.group_updates = None self.api = APIOptions() self.passive = False # capability self.route_refresh = False self.graceful_restart = False self.multisession = None self.add_path = None self._families = [] self.rib = None self.operational = None self.asm = dict() self.messages = deque()
class Open(Message): ID = Message.CODE.OPEN TYPE = chr(Message.CODE.OPEN) def __init__(self, version, asn, router_id, capabilities, hold_time): self.version = Version(version) self.asn = ASN(asn) self.hold_time = HoldTime(hold_time) self.router_id = RouterID(router_id) self.capabilities = capabilities def message(self, negotiated=None): return self._message( "%s%s%s%s%s" % (self.version.pack(), self.asn.trans(), self.hold_time.pack(), self.router_id.pack(), self.capabilities.pack())) def __str__(self): return "OPEN version=%d asn=%d hold_time=%s router_id=%s capabilities=[%s]" % ( self.version, self.asn, self.hold_time, self.router_id, self.capabilities) @classmethod def unpack_message(cls, data, _=None): version = ord(data[0]) if version != 4: # Only version 4 is supported nowdays.. raise Notify(2, 1, data[0]) asn = unpack('!H', data[1:3])[0] hold_time = unpack('!H', data[3:5])[0] numeric = unpack('!L', data[5:9])[0] router_id = "%d.%d.%d.%d" % (numeric >> 24, (numeric >> 16) & 0xFF, (numeric >> 8) & 0xFF, numeric & 0xFF) capabilities = Capabilities.unpack(data[9:]) return cls(version, asn, router_id, capabilities, hold_time)
def _negociate(self): sent_capa = self.sent_open.capabilities recv_capa = self.received_open.capabilities self.holdtime = HoldTime( min(self.sent_open.hold_time, self.received_open.hold_time)) self.addpath.setup(self.sent_open, self.received_open) self.asn4 = sent_capa.announced( CID.FOUR_BYTES_ASN) and recv_capa.announced(CID.FOUR_BYTES_ASN) self.operational = sent_capa.announced( CID.OPERATIONAL) and recv_capa.announced(CID.OPERATIONAL) self.local_as = self.sent_open.asn self.peer_as = self.received_open.asn if self.received_open.asn == AS_TRANS: self.peer_as = recv_capa[CID.FOUR_BYTES_ASN] self.families = [] if recv_capa.announced(CID.MULTIPROTOCOL_EXTENSIONS) \ and sent_capa.announced(CID.MULTIPROTOCOL_EXTENSIONS): for family in recv_capa[CID.MULTIPROTOCOL_EXTENSIONS]: if family in sent_capa[CID.MULTIPROTOCOL_EXTENSIONS]: self.families.append(family) self.multisession = sent_capa.announced( CID.MULTISESSION_BGP) and recv_capa.announced(CID.MULTISESSION_BGP) if self.multisession: sent_ms_capa = set(sent_capa[CID.MULTISESSION_BGP]) recv_ms_capa = set(recv_capa[CID.MULTISESSION_BGP]) if sent_ms_capa == set([]): sent_ms_capa = set([CID.MULTIPROTOCOL_EXTENSIONS]) if recv_ms_capa == set([]): recv_ms_capa = set([CID.MULTIPROTOCOL_EXTENSIONS]) if sent_ms_capa != recv_ms_capa: self.multisession = ( 2, 8, 'multisession, our peer did not reply with the same sessionid' ) # The way we implement MS-BGP, we only send one MP per session # therefore we can not collide due to the way we generate the configuration for capa in sent_ms_capa: # no need to check that the capability exists, we generated it # checked it is what we sent and only send MULTIPROTOCOL_EXTENSIONS if sent_capa[capa] != recv_capa[capa]: self.multisession = ( 2, 8, 'when checking session id, capability %s did not match' % str(capa)) break elif sent_capa.announced(CID.MULTISESSION_BGP): self.multisession = (2, 9, 'multisession is mandatory with this peer')
def _negotiate (self): sent_capa = self.sent_open.capabilities recv_capa = self.received_open.capabilities self.holdtime = HoldTime(min(self.sent_open.hold_time,self.received_open.hold_time)) self.addpath.setup(self.sent_open,self.received_open) self.asn4 = sent_capa.announced(Capability.CODE.FOUR_BYTES_ASN) and recv_capa.announced(Capability.CODE.FOUR_BYTES_ASN) self.operational = sent_capa.announced(Capability.CODE.OPERATIONAL) and recv_capa.announced(Capability.CODE.OPERATIONAL) self.local_as = self.sent_open.asn self.peer_as = self.received_open.asn if self.received_open.asn == AS_TRANS and self.asn4: self.peer_as = recv_capa.get(Capability.CODE.FOUR_BYTES_ASN,self.peer_as) self.families = [] if \ recv_capa.announced(Capability.CODE.MULTIPROTOCOL) and \ sent_capa.announced(Capability.CODE.MULTIPROTOCOL): for family in recv_capa[Capability.CODE.MULTIPROTOCOL]: if family in sent_capa[Capability.CODE.MULTIPROTOCOL]: self.families.append(family) if recv_capa.announced(Capability.CODE.ENHANCED_ROUTE_REFRESH) and sent_capa.announced(Capability.CODE.ENHANCED_ROUTE_REFRESH): self.refresh = REFRESH.ENHANCED # pylint: disable=E1101 elif recv_capa.announced(Capability.CODE.ROUTE_REFRESH) and sent_capa.announced(Capability.CODE.ROUTE_REFRESH): self.refresh = REFRESH.NORMAL # pylint: disable=E1101 if recv_capa.announced(Capability.CODE.EXTENDED_MESSAGE) and sent_capa.announced(Capability.CODE.EXTENDED_MESSAGE): self.msg_size = ExtendedMessage.EXTENDED_SIZE self.multisession = sent_capa.announced(Capability.CODE.MULTISESSION) and recv_capa.announced(Capability.CODE.MULTISESSION) self.multisession |= sent_capa.announced(Capability.CODE.MULTISESSION_CISCO) and recv_capa.announced(Capability.CODE.MULTISESSION_CISCO) if self.multisession: sent_ms_capa = set(sent_capa[Capability.CODE.MULTISESSION]) recv_ms_capa = set(recv_capa[Capability.CODE.MULTISESSION]) if sent_ms_capa == set([]): sent_ms_capa = set([Capability.CODE.MULTIPROTOCOL]) if recv_ms_capa == set([]): recv_ms_capa = set([Capability.CODE.MULTIPROTOCOL]) if sent_ms_capa != recv_ms_capa: self.multisession = (2,8,'multisession, our peer did not reply with the same sessionid') # The way we implement MS-BGP, we only send one MP per session # therefore we can not collide due to the way we generate the configuration for capa in sent_ms_capa: # no need to check that the capability exists, we generated it # checked it is what we sent and only send MULTIPROTOCOL if sent_capa[capa] != recv_capa[capa]: self.multisession = (2,8,'when checking session id, capability %s did not match' % str(capa)) break elif sent_capa.announced(Capability.CODE.MULTISESSION): self.multisession = (2,9,'multisession is mandatory with this peer')
def unpack_message(cls, data, _=None): version = data[0] if version != 4: # Only version 4 is supported nowdays.. raise Notify(2, 1, bytes(data[0], 'ascii')) asn = unpack('!H', data[1:3])[0] hold_time = unpack('!H', data[3:5])[0] numeric = unpack('!L', data[5:9])[0] router_id = "%d.%d.%d.%d" % (numeric >> 24, (numeric >> 16) & 0xFF, (numeric >> 8) & 0xFF, numeric & 0xFF) return cls(Version(version), ASN(asn), HoldTime(hold_time), RouterID(router_id), Capabilities.unpack(data[9:]))
def __init__ (self): # self.logger should not be used here as long as we do use deepcopy as it contains a Lock self.description = '' self.router_id = None self.local_address = None self.peer_address = None self.peer_as = None self.local_as = None self.hold_time = HoldTime(180) self.asn4 = None self.add_path = 0 self.md5 = None self.ttl = None self.group_updates = None self.flush = None self.adjribout = None self.manual_eor = False self.api = APIOptions() # passive indicate that we do not establish outgoing connections self.passive = False # the port to listen on ( zero mean that we do not listen ) self.listen = 0 # capability self.route_refresh = False self.graceful_restart = False self.multisession = None self.add_path = None self.aigp = None self._families = [] self.rib = None # The routes we have parsed from the configuration self.changes = [] # On signal update, the previous routes so we can compare what changed self.backup_changes = [] self.operational = None self.eor = deque() self.asm = dict() self.messages = deque() self.refresh = deque() self.counter = Counter() self.ski = [] self.bgpsec_openssl_lib = [] self.bgpsec_crypto_init = [] self.bgpsec_pre_asns = [] self.bgpsec_pre_skis = []
def hold_time (tokeniser): value = tokeniser() try: holdtime = HoldTime(int(value)) except ValueError: raise ValueError ('"%s" is an invalid hold-time' % value) if holdtime < 3 and holdtime != 0: raise ValueError('holdtime must be zero or at least three seconds') if holdtime > HoldTime.MAX: raise ValueError('holdtime must be smaller or equal to %d' % HoldTime.MAX) return holdtime
def __init__ (self): # self.logger should not be used here as long as we do use deepcopy as it contains a Lock self.description = '' self.router_id = None self.host_name = 'localhost'.encode('utf-8') self.domain_name = 'localdomain'.encode('utf-8') self.local_address = None self.peer_address = None self.peer_as = None self.local_as = None self.hold_time = HoldTime(180) self.asn4 = None self.add_path = 0 self.md5 = None self.ttl = None self.group_updates = None self.flush = None self.adjribout = None self.api = APIOptions() # passive indicate that we do not establish outgoing connections self.passive = False # the port to listen on ( zero mean that we do not listen ) self.listen = 0 # capability self.route_refresh = False self.graceful_restart = False self.multisession = None self.add_path = None self.aigp = None self._families = [] self.rib = None # The routes we have parsed from the configuration self.changes = [] # On signal update, the previous routes so we can compare what changed self.backup_changes = [] self.operational = None self.eor = deque() self.asm = dict() self.messages = deque() self.refresh = deque() self.counter = Counter() # It is possible to : # - have multiple exabgp toward one peer on the same host ( use of pid ) # - have more than once connection toward a peer # - each connection has it own neihgbor (hence why identificator is not in Protocol) self.uid = '%d-%s' % (os.getpid(),uuid.uuid1())
def __init__(self): self.sent_open = None self.received_open = None self.holdtime = HoldTime(0) self.local_as = ASN(0) self.peer_as = ASN(0) self.families = [] self.asn4 = False self.addpath = RequirePath() self.multisession = False self.msg_size = 4096 - 19 self.operational = False
def holdtime(tokeniser): value = tokeniser() try: hold_time = HoldTime(value) except ValueError: raise ValueError('"%s" is an invalid hold-time' % value) if hold_time < 3 and hold_time != 0: raise ValueError('holdtime must be zero or at least three seconds') # XXX: FIXME: add HoldTime.MAX and reference it ( pow -1 ) if hold_time >= pow(2, 16): raise ValueError('holdtime must be smaller than %d' % pow(2, 16)) return hold_time
def __init__(self, neighbor): self.neighbor = neighbor self.sent_open = None self.received_open = None self.holdtime = HoldTime(0) self.local_as = ASN(0) self.peer_as = ASN(0) self.families = [] self.asn4 = False self.addpath = RequirePath() self.multisession = False self.msg_size = 4096 self.operational = False self.refresh = REFRESH.absent self.aigp = None
def __init__(self, neighbor): self.neighbor = neighbor self.sent_open = None self.received_open = None self.holdtime = HoldTime(0) self.local_as = ASN(0) self.peer_as = ASN(0) self.families = [] self.asn4 = False self.addpath = RequirePath() self.multisession = False self.msg_size = self.MAX_SIZE self.operational = False self.refresh = REFRESH.ABSENT # pylint: disable=E1101 self.aigp = None
def __init__ (self): # self.logger should not be used here as long as we do use deepcopy as it contains a Lock self.description = '' self.router_id = None self.local_address = None self.peer_address = None self.peer_as = None self.local_as = None self.hold_time = HoldTime(180) self.asn4 = None self.add_path = 0 self.md5 = None self.ttl = None self.group_updates = None self.flush = None self.adjribout = None self.api = APIOptions() self.passive = False # capability self.route_refresh = False self.graceful_restart = False self.multisession = None self.add_path = None self.aigp = None self._families = [] self.rib = None # The routes we have parsed from the configuration self.changes = [] # On signal update, the previous routes so we can compare what changed self.backup_changes = [] self.operational = None self.eor = deque() self.asm = dict() self.messages = deque() self.refresh = deque() self.counter = Counter()
def __init__(self, neighbor): self.neighbor = neighbor self.sent_open = None self.received_open = None self.holdtime = HoldTime(0) self.local_as = ASN(0) self.peer_as = ASN(0) self.families = [] self.nexthop = [] self.asn4 = False self.addpath = RequirePath() self.multisession = False self.msg_size = ExtendedMessage.INITIAL_SIZE self.operational = False self.refresh = REFRESH.ABSENT # pylint: disable=E1101 self.aigp = neighbor['capability']['aigp'] self.mismatch = []
def holdtime(self, scope, name, command, tokens): if not len(tokens) == 1: return self.error.set('hold-time required') try: holdtime = HoldTime(tokens[0]) except ValueError: return self.error.set('"%s" is an invalid hold-time' % ' '.join(tokens)) if holdtime < 3 and holdtime != 0: return self.error.set( 'holdtime must be zero or at least three seconds') if holdtime >= pow(2, 16): return self.error.set('holdtime must be smaller than %d' % pow(2, 16)) scope[-1][command] = holdtime return True
class Neighbor: description = 'a test neighbor' router_id = RouterID('127.0.0.1') local_address = IPv4('127.0.0.1') peer_address = IPv4('127.0.0.1') peer_as = ASN('65500') local_as = ASN('65500') hold_time = HoldTime(180) asn4 = False add_path = 0 # capability route_refresh = False graceful_restart = False multisession = None add_path = None aigp = None @staticmethod def families(): return known_families()
class Open (Message): TYPE = chr(Message.Type.OPEN) def __init__ (self,version,asn,router_id,capabilities,hold_time): self.version = Version(version) self.asn = ASN(asn) self.hold_time = HoldTime(hold_time) self.router_id = RouterID(router_id) self.capabilities = capabilities def message (self): return self._message("%s%s%s%s%s" % ( self.version.pack(), self.asn.trans(), self.hold_time.pack(), self.router_id.pack(), self.capabilities.pack() )) def __str__ (self): return "OPEN version=%d asn=%d hold_time=%s router_id=%s capabilities=[%s]" % (self.version, self.asn, self.hold_time, self.router_id,self.capabilities)
class Neighbor(dict): class Capability(dict): defaults = { 'asn4': True, 'extended-message': True, 'graceful-restart': False, 'multi-session': False, 'operational': False, 'add-path': 0, 'route-refresh': 0, 'nexthop': None, 'aigp': None, } defaults = { # Those are the field from the configuration 'description': '', 'router-id': None, 'local-address': None, 'peer-address': None, 'local-as': None, 'peer-as': None, # passive indicate that we do not establish outgoing connections 'passive': False, # the port to listen on ( zero mean that we do not listen ) 'listen': 0, # the port to connect to 'connect': 0, 'hold-time': HoldTime(180), 'rate-limit': 0, 'host-name': host(), 'domain-name': domain(), 'group-updates': True, 'auto-flush': True, 'adj-rib-in': True, 'adj-rib-out': True, 'manual-eor': False, # XXX: this should be under an MD5 sub-dict/object ? 'md5-password': None, 'md5-base64': False, 'md5-ip': None, 'outgoing-ttl': None, 'incoming-ttl': None, } _GLOBAL = {'uid': 1} def __init__(self): # super init self.update(self.defaults) # Those are subconf self.api = None # XXX: not scriptable - is replaced outside the class # internal or calculated field self['capability'] = self.Capability.defaults.copy() # local_address uses auto discovery self.auto_discovery = False self.range_size = 1 # was this Neighbor generated from a range self.generated = False self._families = [] self._nexthop = [] self._addpath = [] self.rib = None # The routes we have parsed from the configuration self.changes = [] # On signal update, the previous routes so we can compare what changed self.backup_changes = [] self.eor = deque() self.asm = dict() self.messages = deque() self.refresh = deque() self.counter = Counter() # It is possible to : # - have multiple exabgp toward one peer on the same host ( use of pid ) # - have more than once connection toward a peer # - each connection has it own neihgbor (hence why identificator is not in Protocol) self.uid = '%d' % self._GLOBAL['uid'] self._GLOBAL['uid'] += 1 def missing(self): if self['local-as'] is None: return 'incomplete neighbor, missing local-address' if self['local-as'] is None: return 'incomplete neighbor, missing local-as' if self['peer-as'] is None: return 'incomplete neighbor, missing peer-as' return '' def infer(self): if self['md5-ip'] is None: self['md5-ip'] = self['local-address'] if self['capability']['graceful-restart'] == 0: self['capability']['graceful-restart'] = int(self['hold-time']) def id(self): return 'neighbor-%s' % self.uid # This set must be unique between peer, not full draft-ietf-idr-bgp-multisession-07 def index(self): if self['listen'] != 0: return 'peer-ip %s listen %d' % (self['peer-address'], self['listen']) return self.name() def make_rib(self): self.rib = RIB(self.name(), self['adj-rib-in'], self['adj-rib-out'], self._families) # will resend all the routes once we reconnect def reset_rib(self): self.rib.reset() self.messages = deque() self.refresh = deque() # back to square one, all the routes are removed def clear_rib(self): self.rib.clear() self.messages = deque() self.refresh = deque() def name(self): if self['capability']['multi-session']: session = '/'.join("%s-%s" % (afi.name(), safi.name()) for (afi, safi) in self.families()) else: session = 'in-open' return "neighbor %s local-ip %s local-as %s peer-as %s router-id %s family-allowed %s" % ( self['peer-address'], self['local-address'] if self['peer-address'] is not None else 'auto', self['local-as'] if self['local-as'] is not None else 'auto', self['peer-as'] if self['peer-as'] is not None else 'auto', self['router-id'], session, ) def families(self): # this list() is important .. as we use the function to modify self._families return list(self._families) def nexthops(self): # this list() is important .. as we use the function to modify self._nexthop return list(self._nexthop) def addpaths(self): # this list() is important .. as we use the function to modify self._add_path return list(self._addpath) def add_family(self, family): # the families MUST be sorted for neighbor indexing name to be predictable for API users # this list() is important .. as we use the function to modify self._families if family not in self.families(): afi, safi = family d = dict() d[afi] = [ safi, ] for afi, safi in self._families: d.setdefault(afi, []).append(safi) self._families = [(afi, safi) for afi in sorted(d) for safi in sorted(d[afi])] def add_nexthop(self, afi, safi, nhafi): if (afi, safi, nhafi) not in self._nexthop: self._nexthop.append((afi, safi, nhafi)) def add_addpath(self, family): # the families MUST be sorted for neighbor indexing name to be predictable for API users # this list() is important .. as we use the function to modify self._add_path if family not in self.addpaths(): afi, safi = family d = dict() d[afi] = [ safi, ] for afi, safi in self._addpath: d.setdefault(afi, []).append(safi) self._addpath = [(afi, safi) for afi in sorted(d) for safi in sorted(d[afi])] def remove_family(self, family): if family in self.families(): self._families.remove(family) def remove_nexthop(self, afi, safi, nhafi): if (afi, safi, nhafi) in self.nexthops(): self._nexthop.remove((afi, safi, nhafi)) def remove_addpath(self, family): if family in self.addpaths(): self._addpath.remove(family) def missing(self): if self['local-address'] is None and not self.auto_discovery: return 'local-address' if self['listen'] > 0 and self.auto_discovery: return 'local-address' if self['peer-address'] is None: return 'peer-address' if self.auto_discovery and not self['router-id']: return 'router-id' if self['peer-address'].afi == AFI.ipv6 and not self['router-id']: return 'router-id' return '' # This function only compares the neighbor BUT NOT ITS ROUTES def __eq__(self, other): # Comparing local_address is skipped in the case where either # peer is configured to auto discover its local address. In # this case it can happen that one local_address is None and # the other one will be set to the auto disocvered IP address. auto_discovery = self.auto_discovery or other.auto_discovery return (self['router-id'] == other['router-id'] and self['local-as'] == other['local-as'] and self['peer-address'] == other['peer-address'] and self['peer-as'] == other['peer-as'] and self['passive'] == other['passive'] and self['listen'] == other['listen'] and self['connect'] == other['connect'] and self['hold-time'] == other['hold-time'] and self['rate-limit'] == other['rate-limit'] and self['host-name'] == other['host-name'] and self['domain-name'] == other['domain-name'] and self['md5-password'] == other['md5-password'] and self['md5-ip'] == other['md5-ip'] and self['incoming-ttl'] == other['incoming-ttl'] and self['outgoing-ttl'] == other['outgoing-ttl'] and self['group-updates'] == other['group-updates'] and self['auto-flush'] == other['auto-flush'] and self['adj-rib-in'] == other['adj-rib-in'] and self['adj-rib-out'] == other['adj-rib-out'] and (auto_discovery or self['local-address'] == other['local-address']) and self['capability'] == other['capability'] and self.auto_discovery == other.auto_discovery and self.families() == other.families()) def __ne__(self, other): return not self.__eq__(other) def string(self, with_changes=True): changes = '' if with_changes: changes += '\nstatic { ' for change in self.rib.outgoing.queued_changes(): changes += '\n\t\t%s' % change.extensive() changes += '\n}' families = '' for afi, safi in self.families(): families += '\n\t\t%s %s;' % (afi.name(), safi.name()) nexthops = '' for afi, safi, nexthop in self.nexthops(): nexthops += '\n\t\t%s %s %s;' % (afi.name(), safi.name(), nexthop.name()) addpaths = '' for afi, safi in self.addpaths(): addpaths += '\n\t\t%s %s;' % (afi.name(), safi.name()) codes = Message.CODE _extension_global = { 'neighbor-changes': 'neighbor-changes', 'negotiated': 'negotiated', 'fsm': 'fsm', 'signal': 'signal', } _extension_receive = { 'receive-packets': 'packets', 'receive-parsed': 'parsed', 'receive-consolidate': 'consolidate', 'receive-%s' % codes.NOTIFICATION.SHORT: 'notification', 'receive-%s' % codes.OPEN.SHORT: 'open', 'receive-%s' % codes.KEEPALIVE.SHORT: 'keepalive', 'receive-%s' % codes.UPDATE.SHORT: 'update', 'receive-%s' % codes.ROUTE_REFRESH.SHORT: 'refresh', 'receive-%s' % codes.OPERATIONAL.SHORT: 'operational', } _extension_send = { 'send-packets': 'packets', 'send-parsed': 'parsed', 'send-consolidate': 'consolidate', 'send-%s' % codes.NOTIFICATION.SHORT: 'notification', 'send-%s' % codes.OPEN.SHORT: 'open', 'send-%s' % codes.KEEPALIVE.SHORT: 'keepalive', 'send-%s' % codes.UPDATE.SHORT: 'update', 'send-%s' % codes.ROUTE_REFRESH.SHORT: 'refresh', 'send-%s' % codes.OPERATIONAL.SHORT: 'operational', } apis = '' for process in self.api.get('processes', []): _global = [] _receive = [] _send = [] for api, name in _extension_global.items(): _global.extend([ '\t\t%s;\n' % name, ] if process in self.api[api] else []) for api, name in _extension_receive.items(): _receive.extend([ '\t\t\t%s;\n' % name, ] if process in self.api[api] else []) for api, name in _extension_send.items(): _send.extend([ '\t\t\t%s;\n' % name, ] if process in self.api[api] else []) _api = '\tapi {\n' _api += '\t\tprocesses [ %s ];\n' % process _api += ''.join(_global) if _receive: _api += '\t\treceive {\n' _api += ''.join(_receive) _api += '\t\t}\n' if _send: _api += '\t\tsend {\n' _api += ''.join(_send) _api += '\t\t}\n' _api += '\t}\n' apis += _api returned = ( 'neighbor %s {\n' '\tdescription "%s";\n' '\trouter-id %s;\n' '\thost-name %s;\n' '\tdomain-name %s;\n' '\tlocal-address %s;\n' '\tlocal-as %s;\n' '\tpeer-as %s;\n' '\thold-time %s;\n' '\trate-limit %s;\n' '\tmanual-eor %s;\n' '%s%s%s%s%s%s%s%s%s%s%s\n' '\tcapability {\n' '%s%s%s%s%s%s%s%s%s\t}\n' '\tfamily {%s\n' '\t}\n' '\tnexthop {%s\n' '\t}\n' '\tadd-path {%s\n' '\t}\n' '%s' '%s' '}' % ( self['peer-address'], self['description'], self['router-id'], self['host-name'], self['domain-name'], self['local-address'] if not self.auto_discovery else 'auto', self['local-as'], self['peer-as'], self['hold-time'], 'disable' if self['rate-limit'] == 0 else self['rate-limit'], 'true' if self['manual-eor'] else 'false', '\n\tpassive %s;\n' % ('true' if self['passive'] else 'false'), '\n\tlisten %d;\n' % self['listen'] if self['listen'] else '', '\n\tconnect %d;\n' % self['connect'] if self['connect'] else '', '\tgroup-updates %s;\n' % ('true' if self['group-updates'] else 'false'), '\tauto-flush %s;\n' % ('true' if self['auto-flush'] else 'false'), '\tadj-rib-in %s;\n' % ('true' if self['adj-rib-in'] else 'false'), '\tadj-rib-out %s;\n' % ('true' if self['adj-rib-out'] else 'false'), '\tmd5-password "%s";\n' % self['md5-password'] if self['md5-password'] else '', '\tmd5-base64 %s;\n' % ('true' if self['md5-base64'] is True else 'false' if self['md5-base64'] is False else 'auto'), '\tmd5-ip "%s";\n' % self['md5-ip'] if not self.auto_discovery else '', '\toutgoing-ttl %s;\n' % self['outgoing-ttl'] if self['outgoing-ttl'] else '', '\tincoming-ttl %s;\n' % self['incoming-ttl'] if self['incoming-ttl'] else '', '\t\tasn4 %s;\n' % ('enable' if self['capability']['asn4'] else 'disable'), '\t\troute-refresh %s;\n' % ('enable' if self['capability']['route-refresh'] else 'disable'), '\t\tgraceful-restart %s;\n' % (self['capability']['graceful-restart'] if self['capability']['graceful-restart'] else 'disable'), '\t\tnexthop %s;\n' % ('enable' if self['capability']['nexthop'] else 'disable'), '\t\tadd-path %s;\n' % (AddPath.string[self['capability']['add-path']] if self['capability']['add-path'] else 'disable'), '\t\tmulti-session %s;\n' % ('enable' if self['capability']['multi-session'] else 'disable'), '\t\toperational %s;\n' % ('enable' if self['capability']['operational'] else 'disable'), '\t\taigp %s;\n' % ('enable' if self['capability']['aigp'] else 'disable'), families, nexthops, addpaths, apis, changes, )) # '\t\treceive {\n%s\t\t}\n' % receive if receive else '', # '\t\tsend {\n%s\t\t}\n' % send if send else '', return returned.replace('\t', ' ') def __str__(self): return self.string(False)
def post(self): for inherit in self.scope.pop('inherit', []): data = self.scope.template('neighbor', inherit) self.scope.inherit(data) local = self.scope.get() neighbor = Neighbor() # XXX: use the right class for the data type # XXX: we can use the scope.nlri interface ( and rename it ) to set some values neighbor.router_id = local.get('router-id', None) neighbor.peer_address = local.get('peer-address', None) neighbor.local_address = local.get('local-address', None) neighbor.local_as = local.get('local-as', None) neighbor.peer_as = local.get('peer-as', None) neighbor.passive = local.get('passive', False) neighbor.listen = local.get('listen', 0) neighbor.connect = local.get('connect', 0) neighbor.hold_time = local.get('hold-time', HoldTime(180)) neighbor.host_name = local.get('host-name', host()) neighbor.domain_name = local.get('domain-name', domain()) neighbor.md5_password = local.get('md5-password', None) neighbor.md5_base64 = local.get('md5-base64', None) neighbor.md5_ip = local.get('md5-ip', neighbor.local_address) neighbor.description = local.get('description', '') neighbor.flush = local.get('auto-flush', True) neighbor.adj_rib_out = local.get('adj-rib-out', True) neighbor.adj_rib_in = local.get('adj-rib-in', True) neighbor.aigp = local.get('aigp', None) neighbor.ttl_out = local.get('outgoing-ttl', None) neighbor.ttl_in = local.get('incoming-ttl', None) neighbor.group_updates = local.get('group-updates', True) neighbor.manual_eor = local.get('manual-eor', False) capability = local.get('capability', {}) neighbor.add_path = capability.get('add-path', 0) neighbor.asn4 = capability.get('asn4', True) neighbor.extended_message = capability.get('extended-message', True) neighbor.multisession = capability.get('multi-session', False) neighbor.operational = capability.get('operational', False) neighbor.route_refresh = capability.get('route-refresh', 0) if capability.get('graceful-restart', False) is not False: neighbor.graceful_restart = capability.get( 'graceful-restart', 0) or int(neighbor.hold_time) neighbor.api = ParseAPI.flatten(local.pop('api', {})) families = [] for family in ParseFamily.convert: for pair in local.get('family', {}).get(family, []): families.append(pair) families = families or NLRI.known_families() for family in families: neighbor.add_family(family) if neighbor.add_path: add_path = local.get('add-path', {}) if add_path: for family in ParseAddPath.convert: for pair in add_path.get(family, []): if pair not in families: self.logger.debug( 'skipping add-path family %s as it is not negotiated' % pair, 'configuration') continue neighbor.add_addpath(pair) else: for family in families: neighbor.add_addpath(family) neighbor.changes = [] neighbor.changes.extend(self.scope.pop_routes()) # old format for section in ('static', 'l2vpn', 'flow'): routes = local.get(section, {}).get('routes', []) for route in routes: route.nlri.action = OUT.ANNOUNCE neighbor.changes.extend(routes) routes = local.get('routes', []) for route in routes: route.nlri.action = OUT.ANNOUNCE neighbor.changes.extend(routes) messages = local.get('operational', {}).get('routes', []) if neighbor.local_address is None: neighbor.auto_discovery = True neighbor.local_address = None neighbor.md5_ip = None if not neighbor.router_id and neighbor.peer_address.afi == AFI.ipv4 and not neighbor.auto_discovery: neighbor.router_id = neighbor.local_address if neighbor.route_refresh: if neighbor.adj_rib_out: self.logger.debug( 'route-refresh requested, enabling adj-rib-out', 'configuration') missing = neighbor.missing() if missing: return self.error.set('incomplete neighbor, missing %s' % missing) if not neighbor.auto_discovery and neighbor.local_address.afi != neighbor.peer_address.afi: return self.error.set( 'local-address and peer-address must be of the same family') neighbor.range_size = neighbor.peer_address.mask.size() if neighbor.range_size > 1 and not neighbor.passive: return self.error.set( 'can only use ip ranges for the peer address with passive neighbors' ) if neighbor.peer_address.top() in self._neighbors: return self.error.set('duplicate peer definition %s' % neighbor.peer_address.top()) self._neighbors.append(neighbor.peer_address.top()) if neighbor.md5_password: try: md5 = base64.b64decode( neighbor.md5_password ) if neighbor.md5_base64 else neighbor.md5_password except TypeError as e: return self.error.set( "Invalid base64 encoding of MD5 password.") else: if len(md5) > 80: return self.error.set( 'MD5 password must be no larger than 80 characters') # check we are not trying to announce routes without the right MP announcement for change in neighbor.changes: family = change.nlri.family() if family not in families and family != (AFI.ipv4, SAFI.unicast): return self.error.set( 'Trying to announce a route of type %s,%s when we are not announcing the family to our peer' % change.nlri.family()) def _init_neighbor(neighbor): families = neighbor.families() for change in neighbor.changes: if change.nlri.family() in families: # This add the family to neighbor.families() neighbor.rib.outgoing.add_to_rib_watchdog(change) for message in messages: if message.family() in families: if message.name == 'ASM': neighbor.asm[message.family()] = message else: neighbor.messages.append(message) self.neighbors[neighbor.name()] = neighbor # create one neighbor object per family for multisession if neighbor.multisession and len(neighbor.families()) > 1: for family in neighbor.families(): # XXX: FIXME: Ok, it works but it takes LOTS of memory .. m_neighbor = deepcopy(neighbor) m_neighbor.make_rib() m_neighbor.rib.outgoing.families = [family] _init_neighbor(m_neighbor) else: neighbor.make_rib() _init_neighbor(neighbor) return True
def post(self): local = self.scope.pop_context(self.name) neighbor = Neighbor() # XXX: use the right class for the data type # XXX: we can use the scope.nlri interface ( and rename it ) to set some values neighbor.router_id = local.get('router-id', None) neighbor.peer_address = local.get('peer-address', None) neighbor.local_address = local.get('local-address', None) neighbor.local_as = local.get('local-as', None) neighbor.peer_as = local.get('peer-as', None) neighbor.passive = local.get('passive', False) neighbor.listen = local.get('listen', 0) neighbor.hold_time = local.get('hold-time', HoldTime(180)) neighbor.host_name = local.get('host-name', _hostname()) neighbor.domain_name = local.get('domain-name', _domainname()) neighbor.md5 = local.get('md5', None) neighbor.description = local.get('description', '') neighbor.flush = local.get('auto-flush', True) neighbor.adjribout = local.get('adj-rib-out', True) neighbor.aigp = local.get('aigp', None) neighbor.ttl = local.get('ttl-security', None) neighbor.group_updates = local.get('group-updates', True) neighbor.manual_eor = local.get('manual-eor', False) neighbor.api = ParseAPI.extract() # capabilities capability = local.get('capability', {}) neighbor.add_path = capability.get('add-path', 0) neighbor.asn4 = capability.get('asn4', True) neighbor.multisession = capability.get('multi-session', False) neighbor.operational = capability.get('operational', False) neighbor.route_refresh = capability.get('route-refresh', 0) if capability.get('graceful-restart', False) is not False: neighbor.graceful_restart = capability.get( 'graceful-restart', 0) or int(neighbor.hold_time) families = [] for family in ParseFamily.convert.keys(): for pair in local.get('family', {}).get(family, []): families.append(pair) families = families or NLRI.known_families() if (AFI.ipv4, SAFI.unicast) not in families: families.append((AFI(AFI.ipv4), SAFI(SAFI.unicast))) for family in families: neighbor.add_family(family) neighbor.changes = [] for section in ('static', 'l2vpn', 'flow'): routes = local.get(section, {}).get('routes', []) for route in routes: route.nlri.action = OUT.ANNOUNCE neighbor.changes.extend(routes) messages = local.get('operational', {}).get('routes', []) if not neighbor.router_id: neighbor.router_id = neighbor.local_address if neighbor.route_refresh: if neighbor.adjribout: self.logger.configuration( 'route-refresh requested, enabling adj-rib-out') missing = neighbor.missing() if missing: return self.error.set('incomplete neighbor, missing %s' % missing) if neighbor.local_address.afi != neighbor.peer_address.afi: return self.error.set( 'local-address and peer-address must be of the same family') if neighbor.peer_address.top() in self._neighbors: return self.error.set('duplicate peer definition %s' % neighbor.peer_address.top()) self._neighbors.append(neighbor.peer_address.top()) # check we are not trying to announce routes without the right MP announcement for change in neighbor.changes: family = change.nlri.family() if family not in families and family != (AFI.ipv4, SAFI.unicast): return self.error.set( 'Trying to announce a route of type %s,%s when we are not announcing the family to our peer' % change.nlri.family()) def _init_neighbor(neighbor): families = neighbor.families() for change in neighbor.changes: if change.nlri.family() in families: # This add the family to neighbor.families() neighbor.rib.outgoing.insert_announced_watchdog(change) for message in messages: if message.family() in families: if message.name == 'ASM': neighbor.asm[message.family()] = message else: neighbor.messages.append(message) self.neighbors[neighbor.name()] = neighbor # create one neighbor object per family for multisession if neighbor.multisession and len(neighbor.families()) > 1: for family in neighbor.families(): # XXX: FIXME: Ok, it works but it takes LOTS of memory .. m_neighbor = deepcopy(neighbor) m_neighbor.make_rib() m_neighbor.rib.outgoing.families = [family] _init_neighbor(m_neighbor) else: neighbor.make_rib() _init_neighbor(neighbor) return True
def __init__ (self, version, asn, router_id, capabilities, hold_time): self.version = Version(version) self.asn = ASN(asn) self.hold_time = HoldTime(hold_time) self.router_id = RouterID(router_id) self.capabilities = capabilities
def __init__(self, version, asn, router_id, capabilities, hold_time): self.version = Version(version) self.asn = ASN(asn) self.hold_time = HoldTime(hold_time) self.router_id = RouterID(router_id) self.capabilities = capabilities