def alloc_ip_address(self, sid): """ Allocate an IP address from the free list Assumption: one IP per UE and one-to-one mappings between SID and IP. At most one IP address can be allocated for a UE at any given time. Args: sid (string): universal subscriber id Returns: ipaddress.ip_address: IP address allocated Raises: NoAvailableIPError: if run out of available IP addresses DuplicatedIPAllocationError: if an IP has been allocated to a UE with the same IMSI """ with self._lock: # if an IP is reserved for the UE, this IP could be in the state of # ALLOCATED, RELEASED or REAPED. if sid in self._sid_ips_map: old_ip = self._sid_ips_map[sid][0] if self._test_ip_state(old_ip, IPState.ALLOCATED): raise DuplicatedIPAllocationError( "An IP has been allocated for this IMSI") elif self._test_ip_state(old_ip, IPState.RELEASED): ip_desc = self._mark_ip_state(old_ip, IPState.ALLOCATED) ip_desc.sid = sid logging.debug("SID %s IP %s RELEASED => ALLOCATED", sid, old_ip) elif self._test_ip_state(old_ip, IPState.REAPED): ip_desc = self._mark_ip_state(old_ip, IPState.ALLOCATED) ip_desc.sid = sid logging.debug("SID %s IP %s REAPED => ALLOCATED", sid, old_ip) else: assert False, "Unexpected internal state" logging.info("Allocating the same IP %s for sid %s", old_ip, sid) IP_ALLOCATED_TOTAL.inc() return old_ip # if an IP is not yet allocated for the UE, allocate a new IP if self._get_ip_count(IPState.FREE): ip_desc = self._pop_ip_from_state(IPState.FREE) ip_desc.sid = sid ip_desc.state = IPState.ALLOCATED self._add_ip_to_state(ip_desc.ip, ip_desc, IPState.ALLOCATED) self._sid_ips_map[sid].append(ip_desc.ip) assert len(self._sid_ips_map[sid]) == 1, \ "Only one IP per SID is supported" IP_ALLOCATED_TOTAL.inc() return ip_desc.ip else: logging.error("Run out of available IP addresses") raise NoAvailableIPError("No available IP addresses")
def alloc_ip_address(self, sid: str, version: int = IPAddress.IPV4) -> \ Tuple[ip_address, int]: """ Allocate an IP address from the free list Assumption: one-to-one mappings between SID and IP. Args: sid (string): universal subscriber id version (int): version of IP to allocate Returns: ipaddress.ip_address: IP address allocated Raises: NoAvailableIPError: if run out of available IP addresses DuplicatedIPAllocationError: if an IP has been allocated to a UE with the same IMSI """ with self._lock: # if an IP is reserved for the UE, this IP could be in the state of # ALLOCATED, RELEASED or REAPED. if sid in self._store.sid_ips_map: old_ip_desc = self._store.sid_ips_map[sid] if self.is_ip_in_state(old_ip_desc.ip, IPState.ALLOCATED): # MME state went out of sync with mobilityd! # Recover gracefully by allocating the same IP logging.warning( "Re-allocate IP %s for sid %s without " "MME releasing it first ip-state-map", old_ip_desc.ip, sid) # TODO: enable strict checking after root causing the # issue in MME # raise DuplicatedIPAllocationError( # "An IP has been allocated for this IMSI") elif self.is_ip_in_state(old_ip_desc.ip, IPState.RELEASED): ip_desc = self._store.ip_state_map.mark_ip_state( old_ip_desc.ip, IPState.ALLOCATED) ip_desc.sid = sid logging.debug("SID %s IP %s RELEASED => ALLOCATED", sid, old_ip_desc.ip) elif self.is_ip_in_state(old_ip_desc.ip, IPState.REAPED): ip_desc = self._store.ip_state_map.mark_ip_state( old_ip_desc.ip, IPState.ALLOCATED) ip_desc.sid = sid logging.debug("SID %s IP %s REAPED => ALLOCATED", sid, old_ip_desc.ip) else: raise AssertionError("Unexpected internal state") logging.info("Allocating the same IP %s for sid %s", old_ip_desc.ip, sid) IP_ALLOCATED_TOTAL.inc() return old_ip_desc.ip, old_ip_desc.vlan_id # Now try to allocate it from underlying allocator. allocator = self.ip_allocator if version == IPAddress.IPV4 \ else self.ipv6_allocator ip_desc = allocator.alloc_ip_address(sid, 0) existing_sid = self.get_sid_for_ip(ip_desc.ip) if existing_sid: error_msg = "Dup IP: {} for SID: {}, which already is " \ "assigned to SID: {}".format(ip_desc.ip, sid, existing_sid) logging.error(error_msg) raise DuplicateIPAssignmentError(error_msg) self._store.ip_state_map.add_ip_to_state(ip_desc.ip, ip_desc, IPState.ALLOCATED) self._store.sid_ips_map[sid] = ip_desc logging.debug("Allocating New IP: %s", str(ip_desc)) IP_ALLOCATED_TOTAL.inc() return ip_desc.ip, ip_desc.vlan_id
def alloc_ip_address(self, sid): """ Allocate an IP address from the free list Assumption: one-to-one mappings between SID and IP. Args: sid (string): universal subscriber id Returns: ipaddress.ip_address: IP address allocated Raises: NoAvailableIPError: if run out of available IP addresses DuplicatedIPAllocationError: if an IP has been allocated to a UE with the same IMSI """ with self._lock: # if an IP is reserved for the UE, this IP could be in the state of # ALLOCATED, RELEASED or REAPED. if sid in self._sid_ips_map: old_ip = self._sid_ips_map[sid][0] if self._test_ip_state(old_ip, IPState.ALLOCATED): # MME state went out of sync with mobilityd! # Recover gracefully by allocating the same IP logging.warn("Re-allocate IP %s for sid %s without " "MME releasing it first", old_ip, sid) # TODO: enable strict checking after root causing the # issue in MME # raise DuplicatedIPAllocationError( # "An IP has been allocated for this IMSI") elif self._test_ip_state(old_ip, IPState.RELEASED): ip_desc = self._mark_ip_state(old_ip, IPState.ALLOCATED) ip_desc.sid = sid logging.debug("SID %s IP %s RELEASED => ALLOCATED", sid, old_ip) elif self._test_ip_state(old_ip, IPState.REAPED): ip_desc = self._mark_ip_state(old_ip, IPState.ALLOCATED) ip_desc.sid = sid logging.debug("SID %s IP %s REAPED => ALLOCATED", sid, old_ip) else: raise AssertionError("Unexpected internal state") logging.info("Allocating the same IP %s for sid %s", old_ip, sid) IP_ALLOCATED_TOTAL.inc() return old_ip # if an IP is not yet allocated for the UE, allocate a new IP if self._get_ip_count(IPState.FREE): ip_desc = self._pop_ip_from_state(IPState.FREE) ip_desc.sid = sid ip_desc.state = IPState.ALLOCATED self._add_ip_to_state(ip_desc.ip, ip_desc, IPState.ALLOCATED) self._sid_ips_map[sid].append(ip_desc.ip) assert len(self._sid_ips_map[sid]) == 1, \ "Only one IP per SID is supported" IP_ALLOCATED_TOTAL.inc() return ip_desc.ip else: logging.error("Run out of available IP addresses") raise NoAvailableIPError("No available IP addresses")
def alloc_ip_address(self, sid: str) -> ip_address: """ Allocate an IP address from the free list Assumption: one-to-one mappings between SID and IP. Args: sid (string): universal subscriber id Returns: ipaddress.ip_address: IP address allocated Raises: NoAvailableIPError: if run out of available IP addresses DuplicatedIPAllocationError: if an IP has been allocated to a UE with the same IMSI """ with self._lock: # if an IP is reserved for the UE, this IP could be in the state of # ALLOCATED, RELEASED or REAPED. if sid in self.sid_ips_map: old_ip_desc = self.sid_ips_map[sid] if self.ip_state_map.test_ip_state(old_ip_desc.ip, IPState.ALLOCATED): # MME state went out of sync with mobilityd! # Recover gracefully by allocating the same IP logging.warning( "Re-allocate IP %s for sid %s without " "MME releasing it first ip-state-map", old_ip_desc.ip, sid) # TODO: enable strict checking after root causing the # issue in MME # raise DuplicatedIPAllocationError( # "An IP has been allocated for this IMSI") elif self.ip_state_map.test_ip_state(old_ip_desc.ip, IPState.RELEASED): ip_desc = self.ip_state_map.mark_ip_state( old_ip_desc.ip, IPState.ALLOCATED) ip_desc.sid = sid logging.debug("SID %s IP %s RELEASED => ALLOCATED", sid, old_ip_desc.ip) elif self.ip_state_map.test_ip_state(old_ip_desc.ip, IPState.REAPED): ip_desc = self.ip_state_map.mark_ip_state( old_ip_desc.ip, IPState.ALLOCATED) ip_desc.sid = sid logging.debug("SID %s IP %s REAPED => ALLOCATED", sid, old_ip_desc.ip) else: raise AssertionError("Unexpected internal state") logging.info("Allocating the same IP %s for sid %s", old_ip_desc.ip, sid) IP_ALLOCATED_TOTAL.inc() return old_ip_desc.ip # Now try to allocate it from underlying allocator. ip_desc = self.ip_allocator.alloc_ip_address(sid) ip_desc.sid = sid ip_desc.state = IPState.ALLOCATED self.ip_state_map.add_ip_to_state(ip_desc.ip, ip_desc, IPState.ALLOCATED) self.sid_ips_map[sid] = ip_desc IP_ALLOCATED_TOTAL.inc() return ip_desc.ip