def __init__(self, prefix, etrr, locators, nat_info, control_plane_sockets, data_plane_sockets, want_map_notify=False): assert isinstance(etrr, ETRRegistration) # Extract RTRs rtrs = nat_info and nat_info.rtr_rlocs or set() # Store input self.prefix = prefix self.etrr = etrr self.nat_info = nat_info self.control_plane_sockets = control_plane_sockets self.data_plane_sockets = data_plane_sockets self.want_map_notify = want_map_notify or bool(rtrs) # Generate the map-register self.map_register = MapRegisterMessage(proxy_map_reply=self.etrr.proxy_map_reply, for_rtr=bool(rtrs), want_map_notify=self.want_map_notify, nonce=os.urandom(8), key_id=self.etrr.key_id, records=[MapRegisterRecord(ttl=1440, authoritative=True, eid_prefix=self.prefix, locator_records=locators)], xtr_id=settings.config.XTR_ID, site_id=settings.config.SITE_ID) self.map_register.insert_authentication_data(self.etrr.key) # Remember source and destination of the outgoing register self.sent_from = None self.sent_to = None # Here the notify is stored self.notify = None # This event will be triggered when the notify is received self.notify_received = threading.Event()
class MapRegister(object): def __init__(self, prefix, etrr, locators, nat_info, control_plane_sockets, data_plane_sockets, want_map_notify=False): assert isinstance(etrr, ETRRegistration) # Extract RTRs rtrs = nat_info and nat_info.rtr_rlocs or set() # Store input self.prefix = prefix self.etrr = etrr self.nat_info = nat_info self.control_plane_sockets = control_plane_sockets self.data_plane_sockets = data_plane_sockets self.want_map_notify = want_map_notify or bool(rtrs) # Generate the map-register self.map_register = MapRegisterMessage(proxy_map_reply=self.etrr.proxy_map_reply, for_rtr=bool(rtrs), want_map_notify=self.want_map_notify, nonce=os.urandom(8), key_id=self.etrr.key_id, records=[MapRegisterRecord(ttl=1440, authoritative=True, eid_prefix=self.prefix, locator_records=locators)], xtr_id=settings.config.XTR_ID, site_id=settings.config.SITE_ID) self.map_register.insert_authentication_data(self.etrr.key) # Remember source and destination of the outgoing register self.sent_from = None self.sent_to = None # Here the notify is stored self.notify = None # This event will be triggered when the notify is received self.notify_received = threading.Event() def send(self): if not self.nat_info: # Plain Map_Register logger.debug(u"Sending Map-Register for {0} to Map-Server {1}".format(self.prefix, self.etrr.map_server)) self.sent_from, self.sent_to = send_message(message=self.map_register, my_sockets=self.control_plane_sockets, destinations=[self.etrr.map_server], port=4342) else: logger.debug(u"Sending NATT Map-Register for {0} to Map-Server {1}".format(self.prefix, self.etrr.map_server)) # Create an IP packet if self.etrr.map_server.version != self.nat_info.private_etr_rloc.version: logger.error("Address family mismatch between local RLOC and Map-Server") return if self.etrr.map_server.version == 4: ip_packet = IPv4Packet() else: ip_packet = IPv6Packet() # Fill the packet ip_packet.ttl = 63 ip_packet.source = self.nat_info.private_etr_rloc ip_packet.destination = self.etrr.map_server # UDP payload inner_udp = UDPMessage(source_port=4342, destination_port=4342, payload=self.map_register) inner_udp.checksum = inner_udp.calculate_checksum(source=ip_packet.source, destination=ip_packet.destination) # Put UDP in the packet ip_packet.next_header = inner_udp.header_type ip_packet.payload = inner_udp # Encapsulate it in an ECM ecm = EncapsulatedControlMessage(payload=ip_packet) send_message(message=ecm, my_sockets=self.data_plane_sockets, destinations=self.nat_info.rtr_rlocs, port=4342) def set_notify_if_matches(self, source, notify): assert isinstance(notify, MapNotifyMessage) if source != self.etrr.map_server \ or notify.nonce != self.map_register.nonce: # Not for us return False logger.debug(u"Received a MapNotify from {0}".format(source)) self.notify = notify self.notify_received.set() return True def notify_content_ok(self): if not self.notify_received.is_set(): logger.error("Cannot validate a MapNotify message without actually receiving it") return False if not self.notify.verify_authentication_data(self.etrr.key): logger.debug(u"MapNotify authentication data is not valid") return False if self.notify.nonce != self.map_register.nonce: logger.debug(u"MapNotify nonce {0x} != MapRegister {1x}".format(self.notify.nonce, self.map_register.nonce)) return False if self.notify.key_id != self.map_register.key_id: logger.debug(u"MapNotify key-id {0} != MapRegister {1}".format(self.notify.key_id, self.map_register.key_id)) return False if self.notify.xtr_id != 0 and self.notify.xtr_id != self.map_register.xtr_id: logger.debug(u"MapNotify XTR-id {0x} != MapRegister {1x}".format(self.notify.xtr_id, self.map_register.xtr_id)) return False if self.notify.site_id != 0 and self.notify.site_id != self.map_register.site_id: logger.debug(u"MapNotify Site-id {0x} != MapRegister {1x}".format(self.notify.site_id, self.map_register.site_id)) return False # Validate records if len(self.notify.records) != len(self.map_register.records): logger.debug(u"MapNotify contains {0} records != MapRegister {1}".format(len(self.notify.records), len(self.map_register.records))) return False for reg_record in self.map_register.records: assert isinstance(reg_record, MapRegisterRecord) record_found = False for record in self.notify.records: assert isinstance(record, MapRegisterRecord) if record.eid_prefix != reg_record.eid_prefix \ or record.ttl != reg_record.ttl \ or record.action != reg_record.action \ or record.map_version != reg_record.map_version \ or len(record.locator_records) != len(reg_record.locator_records): # No match continue for reg_locator in reg_record.locator_records: assert isinstance(reg_locator, LocatorRecord) locator_found = False for locator in record.locator_records: assert isinstance(locator, LocatorRecord) if locator.priority == reg_locator.priority \ and locator.weight == reg_locator.weight \ and locator.m_priority == reg_locator.m_priority \ and locator.m_weight == reg_locator.m_weight \ and locator.reachable == reg_locator.reachable \ and locator.address == reg_locator.address: locator_found = True break if not locator_found: logger.debug("Locators for {0} do not match".format(record.eid_prefix)) # That seemed ok record_found = True break if not record_found: logger.debug(u"MapNotify does not contain record for {0} from MapRegister".format(reg_record.eid_prefix)) return False logger.debug(u"MapNotify validated") return True