Beispiel #1
0
class CidrBlock(models.Model):

    cidr_block = CidrAddressField()
    netmask = models.GenericIPAddressField(protocol='IPv4',
                                           blank=True,
                                           null=True,
                                           default=None)
Beispiel #2
0
class NetGroupRegexPermission(models.Model):
    group = models.CharField(max_length=80)
    range = CidrAddressField()
    regex = models.CharField(max_length=250, validators=[validate_regex])

    objects = NetManager()

    class Meta:
        db_table = 'perm_net_group_regex'
        unique_together = (
            'group',
            'range',
            'regex',
        )

    def __str__(self):
        return f"group {self.group}, range {self.range}, regex {self.regex}"

    @staticmethod
    def find_perm(groups, hostname, ips):
        if not isinstance(hostname, str):
            raise ValueError(f'hostname is invalid type ({type(hostname)})')
        if isinstance(groups, str):
            groups = [groups]
        if not isinstance(groups, (list, tuple)):
            raise ValueError(f'groups on invalid type ({type(groups)})')
        if isinstance(ips, str):
            ips = [ips]
        if not isinstance(ips, (list, tuple)):
            raise ValueError(f'ips on invalid type ({type(ips)})')
        qs = NetGroupRegexPermission.objects.filter(group__in=groups).extra(
            where=["%s ~ regex"], params=[str(hostname)]).filter(
                reduce(lambda x, y: x | y,
                       [Q(range__net_contains=ip) for ip in ips]))
        return qs
class IP4RTestModel(Model):
    field = CidrAddressField()
    objects = NetManager()

    class Meta:
        # Table name can't be type name?
        db_table = 'ip4rtest'
Beispiel #4
0
class AggregateTestChildModel(Model):
    parent = ForeignKey(
        'AggregateTestModel',
        related_name='children',
        on_delete=CASCADE,
    )
    network = CidrAddressField()
    inet = InetAddressField()
Beispiel #5
0
class NetworkRange(models.Model):
    range = CidrAddressField(unique=True)

    def __str__(self):
        return "%s" % self.range

    class Meta:
        db_table = "network_ranges"
Beispiel #6
0
class Token(ExportModelOperationsMixin('Token'),
            rest_framework.authtoken.models.Token):
    @staticmethod
    def _allowed_subnets_default():
        return [
            ipaddress.IPv4Network('0.0.0.0/0'),
            ipaddress.IPv6Network('::/0')
        ]

    id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
    key = models.CharField("Key", max_length=128, db_index=True, unique=True)
    user = models.ForeignKey(User,
                             related_name='auth_tokens',
                             on_delete=models.CASCADE,
                             verbose_name="User")
    name = models.CharField('Name', blank=True, max_length=64)
    last_used = models.DateTimeField(null=True, blank=True)
    perm_manage_tokens = models.BooleanField(default=False)
    allowed_subnets = ArrayField(CidrAddressField(),
                                 default=_allowed_subnets_default.__func__)
    max_age = models.DurationField(
        null=True, default=None, validators=[MinValueValidator(timedelta(0))])
    max_unused_period = models.DurationField(
        null=True, default=None, validators=[MinValueValidator(timedelta(0))])

    plain = None
    objects = NetManager()

    @property
    def is_valid(self):
        now = timezone.now()

        # Check max age
        try:
            if self.created + self.max_age < now:
                return False
        except TypeError:
            pass

        # Check regular usage requirement
        try:
            if (self.last_used or self.created) + self.max_unused_period < now:
                return False
        except TypeError:
            pass

        return True

    def generate_key(self):
        self.plain = secrets.token_urlsafe(21)
        self.key = Token.make_hash(self.plain)
        return self.key

    @staticmethod
    def make_hash(plain):
        return make_password(plain,
                             salt='static',
                             hasher='pbkdf2_sha256_iter1')
Beispiel #7
0
class IPRule(Contribution):
    #ip = models.GenericIPAddressField()
    ip = CidrAddressField()
    georesult = models.CharField(max_length=256)
    canonical_georesult = models.CharField(max_length=256,
                                           blank=True,
                                           null=True)
    #granularity = models.CharField(max_length=1,  choices=GRANULARITIES ) default city for now
    lat = models.FloatField(blank=True, null=True)
    lon = models.FloatField(blank=True, null=True)

    objects = NetManager()

    #objects = IPRuleManager()

    def save(self, *args, **kwargs):
        if self.georesult:
            loc = openipmap.geoutils.loc_resolve(self.georesult)
            if loc and loc.raw['lat'] and loc.raw['lng']:
                self.lat = loc.raw['lat']
                self.lon = loc.raw['lng']
                cityname = ''
                try:
                    cityname = loc.raw['name']
                except:
                    pass
                regionname = ''
                try:
                    regionname = loc.raw['adminName1']
                except:
                    pass
                countrycode = ''
                try:
                    countrycode = loc.raw['countryCode']
                except:
                    pass
                self.canonical_georesult = "%s,%s,%s" % (cityname, regionname,
                                                         countrycode)
        super(IPRule, self).save(*args, **kwargs)

    @classmethod
    def get_crowdsourced(cls, ip, max_results=10):
        '''
        returns 'max_results' number of results for this particular IP from the IPRules tables
        '''
        results = []
        ipr = IPRule.objects.filter(ip__net_contains_or_equals=ip)
        for rule in ipr:
            results.append({
                'kind': 'ip',
                'granularity': 'city',
                'lat': rule.lat,
                'lon': rule.lon,
                'georesult': rule.georesult,
                'canonical_georesult': rule.canonical_georesult,
                'confidence': rule.confidence,
            })
        return results
Beispiel #8
0
class Lab(models.Model):
    name = models.CharField(max_length=30)
    building = models.ForeignKey(Building, on_delete=models.CASCADE, related_name='labs')
    comment = models.CharField(max_length=30, null=True, blank=True)
    network = CidrAddressField(null=True, blank=True)
    objects = NetManager()

    def __str__(self):
        return self.building.code + '/' + self.name
Beispiel #9
0
class DefaultPool(models.Model):
    pool = models.ForeignKey(
        "Pool", related_name="pool_defaults", blank=True, null=True
    )
    cidr = CidrAddressField(unique=True)

    objects = DefaultPoolManager()

    def __str__(self):
        return "%s - %s" % (self.pool, self.cidr)

    class Meta:
        db_table = "default_pools"
Beispiel #10
0
class UserIpAddress(models.Model):
    ip = CidrAddressField(unique=True, db_index=True)
    user = models.ForeignKey(User, related_name='user')
    objects = NetManager()
    
    class Meta:
        app_label = 'api'
        db_table = "useripaddress"
        verbose_name = "User IP Address"
        verbose_name_plural = "User IP Addresses"
    
    def __unicode__(self):
        return "%s - %s" % (self.ip, self.user)
Beispiel #11
0
class Prefix(models.Model):
    protocol = models.CharField(max_length=8)
    prefix = CidrAddressField()
    ixlan_id = models.PositiveIntegerField()

    objects = NetManager()

    class Meta:
        ordering = ["prefix"]
        verbose_name = "IX Prefix"
        verbose_name_plural = "IX Prefixes"

    def __str__(self):
        return "{} - {}".format(self.protocol, self.prefix)
Beispiel #12
0
class PrefixInfo(models.Model):
    objects = NetManager()
    TYPE_CHOICES = ((1, "RIR"), (2, "PRIVATE"))

    asn = models.PositiveIntegerField(blank=True, null=True)
    prefix = CidrAddressField(unique=True)
    name = models.CharField(max_length=32)
    description = models.CharField(max_length=64)
    country_code = models.CharField(max_length=3)
    rir = models.CharField(max_length=16, blank=True, null=True)
    type = models.PositiveSmallIntegerField(choices=TYPE_CHOICES)
    updated_at = models.DateTimeField(auto_now=True)

    def __str__(self):
        return str(self.prefix)
class IXLanPrefix(models.Model):
    notes = models.CharField(max_length=255, blank=True)
    protocol = models.CharField(max_length=64, choices=Protocol.choices)
    prefix = CidrAddressField(unique=True)
    in_dfz = models.BooleanField(default=False)
    ixlan = models.ForeignKey(
        IXLan,
        default=0,
        related_name="ixpfx_set",
        verbose_name="Internet Exchange LAN",
        on_delete=models.CASCADE,
    )

    class Meta:
        verbose_name = "Internet Exchange LAN prefix"
        verbose_name_plural = "Internet Exchange LAN prefixes"
Beispiel #14
0
class Network(models.Model):
    network = CidrAddressField(primary_key=True)
    name = models.CharField(max_length=255, blank=True, null=True)
    gateway = InetAddressField(blank=True, null=True)
    description = models.TextField(blank=True, null=True)
    vlans = models.ManyToManyField(
        "Vlan", through="NetworkToVlan", related_name="vlan_networks"
    )
    dhcp_group = models.ForeignKey(
        "DhcpGroup", db_column="dhcp_group", blank=True, null=True
    )
    shared_network = models.ForeignKey(
        "SharedNetwork", db_column="shared_network", blank=True, null=True
    )
    changed = models.DateTimeField(auto_now=True)
    changed_by = models.ForeignKey(settings.AUTH_USER_MODEL, db_column="changed_by")

    search_index = VectorField()

    # objects = NetworkQuerySet.as_manager()
    objects = NetworkManager.from_queryset(NetworkQuerySet)()

    searcher = SearchManager(
        fields=("name", "description"),
        config="pg_catalog.english",  # this is default
        search_field="search_index",  # this is default
        auto_update_search_field=True,
    )

    tags = TaggableManager(through=TaggedNetworks, blank=True)

    # Forcing pk as string
    @property
    def pk(self):
        return str(self.network)

    def __str__(self):
        return "%s" % self.network

    class Meta:
        db_table = "networks"
        permissions = (
            ("is_owner_network", "Is owner"),
            ("add_records_to_network", "Can add records to"),
        )
        default_permissions = ("add", "change", "delete", "view")
        ordering = ("network",)
Beispiel #15
0
        def getStartAddress(net: CidrAddressField, size: int) -> int:
            """Calculate the next possible start address of a network with requested size

            Arguments:
                net {CidrAddressField} -- Net to start with
                size {int} -- subnet size

            Returns:
                int -- The actual distance
            """
            if size >= net.prefixlen:
                startAddress = int(net.broadcast_address) + 1
            else:
                sizeDiff = net.prefixlen - size
                supernet = net.supernet(sizeDiff)
                startAddress = int(supernet.network_address) + 2**(
                    supernet.max_prefixlen - supernet.prefixlen)
            return startAddress
Beispiel #16
0
class NetGroupRegexPermission(BaseModel):
    group = models.CharField(max_length=80)
    range = CidrAddressField()
    regex = models.CharField(max_length=250, validators=[validate_regex])
    labels = models.ManyToManyField(Label,
                                    blank=True,
                                    related_name='permissions')

    objects = NetManager()

    class Meta:
        db_table = 'perm_net_group_regex'
        unique_together = (
            'group',
            'range',
            'regex',
        )

    def __str__(self):
        return f"group {self.group}, range {self.range}, regex {self.regex}"

    @classmethod
    def find_perm(cls, groups, hostname, ips, require_ip=True):
        if not isinstance(hostname, str):
            raise ValueError(f'hostname is invalid type ({type(hostname)})')
        if isinstance(groups, str):
            groups = [groups]
        if not isinstance(groups, (list, tuple)):
            raise ValueError(f'groups on invalid type ({type(groups)})')
        if isinstance(ips, str):
            ips = [ips]
        if not isinstance(ips, (list, tuple)):
            raise ValueError(f'ips on invalid type ({type(ips)})')
        if require_ip and not ips:
            return cls.objects.none()
        if not all([groups, hostname]):
            return cls.objects.none()
        qs = cls.objects.filter(group__in=groups).extra(where=["%s ~ regex"],
                                                        params=[str(hostname)])
        if require_ip:
            qs = qs.filter(
                reduce(lambda x, y: x | y,
                       [Q(range__net_contains=ip) for ip in ips]))
        return qs
Beispiel #17
0
class Ip(BaseAccessLevel):
    """ IP Address Model """
    interface = models.ForeignKey('net.Interface', verbose_name=_('interface'))
    address = InetAddressField(verbose_name=_('ip address'),
                               unique=True,
                               db_index=True)
    protocol = models.CharField(_('IP Protocol Version'),
                                max_length=4,
                                choices=IP_PROTOCOLS,
                                default=IP_PROTOCOLS[0][0],
                                blank=True)
    netmask = CidrAddressField(_('netmask (CIDR, eg: 10.40.0.0/24)'))

    objects = NetManager()

    class Meta:
        app_label = 'net'
        permissions = (('can_view_ip', 'Can view ip'), )
        verbose_name = _('ip address')
        verbose_name_plural = _('ip addresses')

    def __unicode__(self):
        return '%s: %s' % (self.protocol, self.address)

    def full_clean(self, *args, **kwargs):
        """ TODO """
        pass

    def save(self, *args, **kwargs):
        """ Determines ip protocol version automatically """
        self.protocol = 'ipv%d' % self.address.version
        # save
        super(Ip, self).save(*args, **kwargs)

    if 'grappelli' in settings.INSTALLED_APPS:

        @staticmethod
        def autocomplete_search_fields():
            return ('address__icontains', )
Beispiel #18
0
class Subnet(models.Model):
    """
    A Subnet represents an IPv4 or IPv6 network, including mask length. Subnets can
    be assigned to Spaces and VRFs.
    A Subnet must be assigned a status and may optionally be assigned a used-defined
    Role. A Subnet can also be assigned to a VLAN where appropriate.
    """

    objects = SubnetManager()

    name = models.CharField(max_length=50)
    cidr = CidrAddressField(unique=True)

    supernet = models.ForeignKey('self',
                                 related_name='children',
                                 null=True,
                                 db_index=True,
                                 editable=False)

    version = models.PositiveSmallIntegerField(choices=AV_CHOICES,
                                               editable=False)

    vrf = models.ForeignKey('ipam.VRF', blank=True, null=True)

    space = models.ForeignKey('ipam.Space', default=1)
    vlan = models.ForeignKey('ipam.VLAN', blank=True, null=True)

    role = models.ManyToManyField('ipam.Role', blank=True)

    description = models.CharField(max_length=100, blank=True)
    notes = models.TextField(blank=True)

    def get_direct_children_subnets_from_cidr(self):
        descendants = Subnet.objects.subnet_descendants(
            cidr=self.cidr).order_by('cidr')

        if not descendants:
            return descendants

        roots = [descendants.first()]
        root = roots[0]

        for subnet in descendants[1:]:
            if not subnet.cidr.overlaps(root.cidr):
                roots.append(subnet)
                root = subnet

        return Subnet.objects.filter(pk__in=[root.pk for root in roots])

    def save(self, *args, **kwargs):
        if cache.get('display_with_ancestors'):
            cache.delete('display_with_ancestors')

        if isinstance(self.cidr, str):
            self.cidr = ip_network(self.cidr)

        self.version = self.cidr.version

        if not self.pk:  # this is a new instance
            self.supernet = self.find_parent()

            with transaction.atomic():
                super(Subnet, self).save(*args, **kwargs)
                self.get_direct_children_subnets_from_cidr().update(
                    supernet=self)

        else:  # instance is being updated
            old_parent = self.supernet
            self.supernet = self.find_parent()

            with transaction.atomic():
                self.children.update(supernet=old_parent)
                super(Subnet, self).save(*args, **kwargs)
                self.get_direct_children_subnets_from_cidr().update(
                    supernet=self)

    def delete(self, *args, **kwargs):
        if self.children.count(
        ) > 0:  # if we need to repair the tree, let SubnetManager deal
            # with it
            Subnet.objects.cleanup_and_delete(self)
        else:
            super(Subnet, self).delete(*args, **kwargs)

    def __str__(self):
        return str(self.cidr)

    def find_parent(self):
        enclosing_subnets = Subnet.objects.filter(
            cidr__net_contains=self.cidr).order_by('-cidr')
        return enclosing_subnets.first()

    @classmethod
    def _update_display_with_ancestors_cache(cls):
        results = dict()
        for subnet in Subnet.objects.all():
            results[subnet.id] = subnet._display_with_ancestors()

        cache.set('display_with_ancestors', results, timeout=None)

    def display_with_ancestors(self):
        cached_values = cache.get('display_with_ancestors')
        if cached_values:
            return cached_values[self.id]
        else:
            Subnet._update_display_with_ancestors_cache()
            return cache.get('display_with_ancestors')[self.id]

    def _display_with_ancestors(self):
        s = list()
        parent = self.supernet
        while parent is not None:
            s.append(str(parent))
            parent = parent.supernet
        s.reverse()
        s.append(str(self))
        return ' > '.join(s)
Beispiel #19
0
class Network(BaseModel):
    network = CidrAddressField(unique=True)
    description = models.TextField(blank=True)
    vlan = models.IntegerField(blank=True, null=True)
    dns_delegated = models.BooleanField(default=False)
    category = models.TextField(blank=True)
    location = models.TextField(blank=True)
    frozen = models.BooleanField(default=False)
    reserved = models.PositiveIntegerField(default=3)

    objects = NetManager()

    class Meta:
        db_table = 'network'
        ordering = ('network', )

    def __str__(self):
        return str(self.network)

    def save(self, *args, **kwargs):
        if isinstance(self.network, str):
            network = ipaddress.ip_network(self.network)
        else:
            network = self.network

        if self.reserved > network.num_addresses:
            self.reserved = network.num_addresses
        super().save(*args, **kwargs)

    def get_reserved_ipaddresses(self):
        """ Returns a set with the reserved ip addresses for the network."""
        network = self.network
        ret = set([network.network_address])
        for i, ip in zip(range(self.reserved), network.hosts()):
            ret.add(ip)
        if isinstance(network, ipaddress.IPv4Network):
            ret.add(network.broadcast_address)
        return ret

    def get_excluded_ranges_start_end(self):
        excluded = []
        for start_ip, end_ip in self.excluded_ranges.values_list(
                'start_ip', 'end_ip'):
            start_ip = ipaddress.ip_address(start_ip)
            end_ip = ipaddress.ip_address(end_ip)
            excluded.append((start_ip, end_ip))
        return excluded

    def get_unused_ipaddresses(self, max=MAX_UNUSED_LIST):
        """
        Returns which ipaddresses on the network are unused.
        """
        network_ips = []
        used_or_reserved = self.used_addresses | self.get_reserved_ipaddresses(
        )
        excluded = self.get_excluded_ranges_start_end()
        # Getting all available IPs for a ipv6 prefix can easily cause
        # the webserver to hang due to lots and lots of IPs. Instead limit
        # to the first MAX_UNUSED_LIST hosts.
        found = 0
        ip = next(self.network.hosts())
        while ip in self.network:
            if ip in used_or_reserved:
                ip += 1
                continue
            was_excluded = False
            for start_ip, end_ip in excluded:
                if ip >= start_ip and ip <= end_ip:
                    ip = end_ip + 1
                    was_excluded = True
            if was_excluded:
                continue
            network_ips.append(ip)
            found += 1
            if found == max:
                break
            ip += 1
        return set(network_ips)

    def __used(self, model):
        from_ip = str(self.network.network_address)
        to_ip = str(self.network.broadcast_address)
        return model.objects.filter(ipaddress__range=(from_ip, to_ip))

    @staticmethod
    def __used_ips(qs):
        ips = qs.values_list('ipaddress', flat=True)
        return {ipaddress.ip_address(ip) for ip in ips}

    def _used_ipaddresses(self):
        return self.__used(Ipaddress)

    def _used_ptroverrides(self):
        return self.__used(PtrOverride)

    @property
    def used_ipaddresses(self):
        """
        Returns the used Ipaddress objects on the network.
        """
        return self.__used_ips(self._used_ipaddresses())

    @property
    def used_ptroverrides(self):
        return self.__used_ips(self._used_ptroverrides())

    @property
    def used_addresses(self):
        """
        Returns which ipaddresses on the network are used.

        A combined usage of Ipaddress and PtrOverride.
        """
        return self.used_ipaddresses | self.used_ptroverrides

    @property
    def unused_addresses(self):
        """
        Returns which ipaddresses on the network are unused.
        """
        return self.get_unused_ipaddresses()

    @property
    def unused_count(self):
        """
        Returns the number of unused ipaddresses on the network.
        """
        # start with the number of all adresses defined by the CIDR
        result = self.network.num_addresses
        # subtract excluded ranges
        for i in self.excluded_ranges.all():
            result -= i.num_addresses()
        # subtract used and reserved addresses
        used_or_reserved = self.used_addresses | self.get_reserved_ipaddresses(
        )
        result -= len(used_or_reserved)
        return result

    def get_first_unused(self):
        """
        Return the first unused IP found, if any.
        """
        a = self.get_unused_ipaddresses(1)
        if a:
            return str(next(iter(a)))
        return None

    def get_random_unused(self):
        """
        Return a random unused IP, if any.
        """

        unused = self.unused_addresses
        if unused:
            network = self.network
            if len(
                    unused
            ) == MAX_UNUSED_LIST and network.num_addresses > MAX_UNUSED_LIST:
                # Attempt to use the entire address if encountering a network larger
                # than MAX_UNUSED_LIST. Typically an IPv6 network.
                network_address = int(network.network_address)
                broadcast_address = int(network.broadcast_address)
                used_or_reserved = self.used_addresses | self.get_reserved_ipaddresses(
                )
                excluded = self.get_excluded_ranges_start_end()
                # Limit the number of attempts, as random might be really unlucky.
                for attempts in range(100):
                    choice = random.randint(network_address, broadcast_address)
                    if network.version == 6:
                        randomip = ipaddress.IPv6Address(choice)
                    else:
                        randomip = ipaddress.IPv4Address(choice)
                    if randomip in used_or_reserved:
                        continue
                    was_excluded = False
                    for start_ip, end_ip in excluded:
                        if randomip >= start_ip and randomip <= end_ip:
                            was_excluded = True
                            break
                    if was_excluded:
                        continue
                    return str(randomip)

            return str(random.choice(tuple(unused)))

        return None
Beispiel #20
0
class ReverseZone(BaseZone):
    name = DnsNameField(unique=True, validators=[validate_reverse_zone_name])
    # network can not be blank, but it will allow full_clean() to pass, even if
    # the network is not set. Will anyway be overridden by update() and save().
    network = CidrAddressField(unique=True, blank=True)

    objects = NetManager()

    class Meta:
        db_table = 'reverse_zone'

    def save(self, *args, **kwargs):
        self.network = get_network_from_zonename(self.name)
        super().save(*args, **kwargs)

    @staticmethod
    def get_zone_by_ip(ip):
        """Search and return a zone which contains an IP address."""
        return ReverseZone.objects.filter(network__net_contains=ip).first()

    def _get_excluded_ranges(self):
        """
        Get ranges which should not be exported in the reverse zone.

        These are addresses used by sub zones or delegations.

        Returned as a list of named tuples.
        """
        excluded_ips = list()
        Range = namedtuple('Range', 'name from_ip to_ip')
        networks = list()
        for i in self.delegations.all():
            networks.append(get_network_from_zonename(i.name))
        for i in ReverseZone.objects.filter(name__endswith="." + self.name):
            networks.append(i.network)

        for network in networks:
            from_ip = str(network.network_address)
            to_ip = str(network.broadcast_address)
            excluded_ips.append(
                Range(name=str(network), from_ip=from_ip, to_ip=to_ip))

        return excluded_ips

    def get_ipaddresses(self):
        """
        Get all ipaddresses used in a reverse zone.

        Will return tuples of (ipaddress, ttl, hostname), sorted by ipaddress.
        """
        network = self.network
        from_ip = str(network.network_address)
        to_ip = str(network.broadcast_address)
        ipaddresses = dict()
        override_ips = dict()
        excluded_ranges = self._get_excluded_ranges()
        for model, data in (
            (Ipaddress, ipaddresses),
            (PtrOverride, override_ips),
        ):
            qs = model.objects.filter(ipaddress__range=(from_ip, to_ip))
            for exclude in excluded_ranges:
                qs = qs.exclude(ipaddress__range=(exclude.from_ip,
                                                  exclude.to_ip))
            for ip, ttl, hostname in qs.values_list('ipaddress', 'host__ttl',
                                                    'host__name'):
                data[ip] = (ttl, hostname)
        # XXX: send signal/mail to hostmaster(?) about issues with multiple_ip_no_ptr
        count = defaultdict(int)
        for i in ipaddresses:
            if i not in override_ips:
                count[i] += 1
        multiple_ip_no_ptr = {i: count[i] for i in count if count[i] > 1}
        ptr_done = set()
        result = []

        def _add_to_result(ip, ttl, hostname):
            # Wildcards are not allowed in reverse zones.
            if "*" in hostname:
                return
            ttl = ttl or ""
            result.append((ipaddress.ip_address(ip), ttl, hostname))

        # Use PtrOverrides when found, but only once. Also skip IPaddresses
        # which have been used multiple times, but lacks a PtrOverride.
        for ip, data in ipaddresses.items():
            if ip in multiple_ip_no_ptr:
                continue
            if ip in override_ips:
                if ip not in ptr_done:
                    ptr_done.add(ip)
                    _add_to_result(ip, *override_ips[ip])
            else:
                _add_to_result(ip, *data)
        # Add PtrOverrides which actually don't override anything,
        # but are only used as PTRs without any Ipaddress object creating
        # forward entries.
        for ptr, data in override_ips.items():
            if ptr not in ptr_done:
                _add_to_result(ptr, *data)

        # Return sorted by IP
        return sorted(result, key=lambda i: i[0])
Beispiel #21
0
class Network(models.Model):
    network = CidrAddressField(unique=True)
    description = models.TextField(blank=True)
    vlan = models.IntegerField(blank=True, null=True)
    dns_delegated = models.BooleanField(default=False)
    category = models.TextField(blank=True)
    location = models.TextField(blank=True)
    frozen = models.BooleanField(default=False)
    reserved = models.PositiveIntegerField(default=3)

    objects = NetManager()

    class Meta:
        db_table = 'network'
        ordering = ('network', )

    def __str__(self):
        return str(self.network)

    def get_reserved_ipaddresses(self):
        """ Returns a set with the reserved ip addresses for the network."""
        network = self.network
        ret = set([network.network_address])
        for i, ip in zip(range(self.reserved), network.hosts()):
            ret.add(ip)
        if isinstance(network, ipaddress.IPv4Network):
            ret.add(network.broadcast_address)
        return ret

    def _get_used_ipaddresses(self):
        from_ip = str(self.network.network_address)
        to_ip = str(self.network.broadcast_address)
        #where_str = "ipaddress BETWEEN '{}' AND '{}'".format(from_ip, to_ip)
        #ips = Ipaddress.objects.extra(where=[where_str])
        return Ipaddress.objects.filter(ipaddress__range=(from_ip, to_ip))

    def get_used_ipaddresses(self):
        """
        Returns the used ipaddress on the network.
        """
        ips = self._get_used_ipaddresses()
        used = {ipaddress.ip_address(i.ipaddress) for i in ips}
        return used

    def get_used_ipaddress_count(self):
        """
        Returns the number of used ipaddreses on the network.
        """
        return self._get_used_ipaddresses().count()

    def get_unused_ipaddresses(self):
        """
        Returns which ip-addresses on the network are unused.
        """
        network_ips = []
        if isinstance(self.network, ipaddress.IPv6Network):
            # Getting all availible IPs for a ipv6 prefix can easily cause
            # the webserver to hang due to lots and lots of IPs. Instead limit
            # to the first 4000 hosts. Should probably be configurable.
            for ip in self.network.hosts():
                if len(network_ips) == 4000:
                    break
                network_ips.append(ip)
        else:
            network_ips = self.network.hosts()

        reserved = self.get_reserved_ipaddresses()
        used = self.get_used_ipaddresses()
        return set(network_ips) - reserved - used

    def get_first_unused(self):
        """
        Return the first unused IP found, if any.
        """

        reserved = self.get_reserved_ipaddresses()
        used = self.get_used_ipaddresses()
        for ip in self.network.hosts():
            if ip in reserved:
                continue
            if ip not in used:
                return str(ip)
        return None
Beispiel #22
0
class ReverseZone(BaseZone):
    name = DnsNameField(unique=True, validators=[validate_reverse_zone_name])
    # network can not be blank, but it will allow full_clean() to pass, even if
    # the network is not set. Will anyway be overridden by update() and save().
    network = CidrAddressField(unique=True, blank=True)

    objects = NetManager()

    class Meta:
        db_table = 'reverse_zone'

    def save(self, *args, **kwargs):
        self.network = get_network_from_zonename(self.name)
        super().save(*args, **kwargs)

    @staticmethod
    def get_zone_by_ip(ip):
        """Search and return a zone which contains an IP address."""
        return ReverseZone.objects.filter(network__net_contains=ip).first()

    def get_ipaddresses(self):
        network = self.network
        from_ip = str(network.network_address)
        to_ip = str(network.broadcast_address)
        ips = Ipaddress.objects.filter(ipaddress__range=(from_ip, to_ip))
        ips = ips.select_related('host')
        override_ips = dict()
        ptrs = PtrOverride.objects.filter(ipaddress__range=(from_ip, to_ip))
        ptrs = ptrs.select_related('host')
        for p in ptrs:
            override_ips[p.ipaddress] = p
        # XXX: send signal/mail to hostmaster(?) about issues with multiple_ip_no_ptr
        count = defaultdict(int)
        for i in ips:
            if i.ipaddress not in override_ips:
                count[i.ipaddress] += 1
        multiple_ip_no_ptr = {i: count[i] for i in count if count[i] > 1}
        ptr_done = set()
        # Use PtrOverrides when found, but only once. Also skip IPaddresses
        # which have been used multiple times, but lacks a PtrOverride.
        result = []

        def _add_to_result(item):
            ttl = item.host.ttl or ""
            result.append(
                (ipaddress.ip_address(item.ipaddress), ttl, item.host.name))

        for i in ips:
            ip = i.ipaddress
            if ip in multiple_ip_no_ptr:
                continue
            if ip in override_ips:
                if ip not in ptr_done:
                    ptr_done.add(ip)
                    _add_to_result(override_ips[ip])
            else:
                _add_to_result(i)
        # Add PtrOverrides which actually don't override anything,
        # but are only used as PTRs without any Ipaddress object creating
        # forward entries.
        for k, v in override_ips.items():
            if k not in ptr_done:
                _add_to_result(v)

        # Return sorted by IP
        return sorted(result, key=lambda i: i[0])
Beispiel #23
0
class NetworkIpPool(BaseAbstractModel):
    network = CidrAddressField(
        verbose_name=_("Ip network address"),
        help_text=_("Ip address of network. For example: " "192.168.1.0 or fde8:6789:1234:1::"),
        unique=True,
    )
    kind = models.PositiveSmallIntegerField(
        _("Kind of network"), choices=NetworkIpPoolKind.choices, default=NetworkIpPoolKind.NETWORK_KIND_NOT_DEFINED
    )
    description = models.CharField(_("Description"), max_length=64)
    groups = models.ManyToManyField(
        Group, verbose_name=_("Member groups"), db_table="networks_ippool_groups", blank=True
    )

    # Usable ip range
    ip_start = models.GenericIPAddressField(_("Start work ip range"))
    ip_end = models.GenericIPAddressField(_("End work ip range"))

    vlan_if = models.ForeignKey(
        VlanIf, verbose_name=_("Vlan interface"), on_delete=models.CASCADE, blank=True, null=True, default=None
    )

    gateway = models.GenericIPAddressField(_("Gateway ip address"))

    is_dynamic = models.BooleanField(_("Is dynamic"), default=False)

    # deprecated: pool_tag is deprecated, remove it
    pool_tag = models.CharField(
        _("Tag"), max_length=32, null=True, blank=True, default=None, validators=[validators.validate_slug]
    )
    sites = models.ManyToManyField(Site, blank=True)

    def __str__(self):
        return f"{self.description}: {self.network}"

    def clean(self):
        errs = {}
        if self.network is None:
            errs["network"] = ValidationError(_("Network is invalid"), code="invalid")

        try:
            net = ip_network("%s" % self.network)
        except ValueError as err:
            errs["network"] = ValidationError(message=str(err), code="invalid")
            raise ValidationError(errs)

        if self.ip_start is None:
            errs["ip_start"] = ValidationError(_("Ip start is invalid"), code="invalid")

        start_ip = ip_address(self.ip_start)
        if start_ip not in net:
            errs["ip_start"] = ValidationError(_("Start ip must be in subnet of specified network"), code="invalid")
        if self.ip_end is None:
            errs["ip_end"] = ValidationError(_("Ip end is invalid"), code="invalid")

        end_ip = ip_address(self.ip_end)
        if end_ip not in net:
            errs["ip_end"] = ValidationError(_("End ip must be in subnet of specified network"), code="invalid")

        gw = ip_address(self.gateway)
        if gw not in net:
            errs["gateway"] = ValidationError(_("Gateway ip must be in subnet of specified network"), code="invalid")
        if start_ip <= gw <= end_ip:
            errs["gateway"] = ValidationError(_("Gateway must not be in the range of allowed ips"), code="invalid")
        if errs:
            raise ValidationError(errs)

        other_nets = NetworkIpPool.objects.exclude(pk=self.pk).only("network").order_by("network")
        if not other_nets.exists():
            return
        for other_net in other_nets.iterator():
            other_net_netw = ip_network("%s" % other_net.network)
            if net.overlaps(other_net_netw):
                errs["network"] = ValidationError(
                    _("Network is overlaps with %(other_network)s"), params={"other_network": str(other_net_netw)}
                )
                raise ValidationError(errs)

    def get_free_ip(self) -> Optional[Union[IPv4Address, IPv6Address]]:
        """
        Finds unused ip
        :return:
        """
        with connection.cursor() as cur:
            cur.execute(
                "SELECT find_new_ip_pool_lease(%s, %s::boolean, 0::smallint, %s::smallint)"
                % (self.pk, 1 if self.is_dynamic else 0, self.kind)
            )
            free_ip = cur.fetchone()
        return ip_address(free_ip[0]) if free_ip and free_ip[0] else None

    @staticmethod
    def find_ip_pool_by_ip(ip_addr: str):
        with connection.cursor() as cur:
            cur.execute("SELECT * FROM find_ip_pool_by_ip(%s::inet)", (ip_addr,))
            res = cur.fetchone()
        if isinstance(res[0], int) and res[0] > 0:
            return NetworkIpPool(
                pk=res[0],
                network=res[1],
                kind=res[2],
                description=res[3],
                ip_start=res[4],
                ip_end=res[5],
                vlan_if_id=res[6],
                gateway=res[7],
                is_dynamic=res[8],
            )
        return None

    class Meta:
        db_table = "networks_ip_pool"
        verbose_name = _("Network ip pool")
        verbose_name_plural = _("Network ip pools")
        ordering = ("network",)
class UniqueCidrTestModel(Model):
    field = CidrAddressField(unique=True)
    objects = NetManager()

    class Meta:
        db_table = 'uniquecidr'
class NullCidrTestModel(Model):
    field = CidrAddressField(null=True)
    objects = NetManager()

    class Meta:
        db_table = 'nullcidr'
Beispiel #26
0
class CIDR(MPTTModel, BaseModel):
    id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
    cidr = CidrAddressField(unique=True)
    parent = TreeForeignKey('self',
                            on_delete=models.CASCADE,
                            null=True,
                            blank=True,
                            related_name='children')
    pool = models.ForeignKey('Pool',
                             blank=True,
                             null=True,
                             on_delete=models.DO_NOTHING,
                             related_name='prefixes')
    fqdn = FQDNField(blank=True, null=True)
    description = models.TextField(blank=True, null=True)
    flag = models.CharField(
        blank=False,
        max_length=11,
        choices=[(tag.value, tag.value) for tag in FlagChoices],
        default=FlagChoices.RESERVATION,
    )
    objects = NetManager()

    class Meta:
        ordering = ('cidr', )

    @property
    def supercidr(self) -> 'CIDR':
        """
            :returns: the direct parent of self (by cidr)
        """
        return self.parent

    @property
    def subcidr(self) -> List['CIDR']:
        """
            :returns: the direct subcidr of self (by cidr)
        """
        return list(CIDR.objects.filter(parent=self).order_by('cidr').all())

    def getChildIDs(self) -> List[str]:
        """Get the IDs of every child

        Returns:
            List[str] -- List of child IDs
        """
        return [
            child.id for child in CIDR.objects.filter(
                parent=self).order_by('cidr').all()
        ]

    @property
    def ips(self) -> List['CIDR']:
        """
            :returns: the direct ips allocated under this prefix
        """
        return [
            cidr for cidr in self.get_children()
            if not utilities.subcidr(cidr.cidr)
        ]

    @property
    def version(self) -> IP:
        """Returns the version of this object
        Either IPv4 our IPv6

        Returns:
            IP -- Version (IP.v4 or IP.v6)
        """
        return IP(self.cidr.version)

    @property
    def labelDict(self) -> dict:
        """Get labels as key value pair

        Returns:
            dict -- Labels as key-value pair
        """
        return {label.name: label.value for label in self.labels.all()}

    @transaction.atomic
    def assignLinknet(self,
                      description: str,
                      hostname=None) -> Tuple['CIDR', 'CIDR', 'CIDR']:
        """Assigns a new linknet from the pool to be used with physical nodes

        Arguments:
            description {str} -- Description of the use for this subnet
            hostname {str} -- Hostname to use
        """
        if self.version == IP.v4:
            size = 31
        else:
            size = 127

        net = self.assignNet(size, description)

        # Assign gateway ip manually because network adress = assignment is prohibited by assign net
        gateway_ip = str(net.cidr.network_address) + "/" + str(size + 1)
        gateway_ip = ip_network(gateway_ip)
        gateway = CIDR(cidr=gateway_ip,
                       description='Gateway',
                       flag=FlagChoices.ASSIGNMENT,
                       parent=net)
        gateway.save()

        # Now use assignIP to not do the assign workaround again
        ip = net.assignIP(description, hostname)

        return net, gateway, ip

    def assignIP(self, description: str, hostname=None) -> 'CIDR':
        """Assigns a new single ip to be used for VMs

        Arguments:
            description {str} -- Description of the use for this subnet
            hostname {str} -- Hostname to use
        """
        if self.version == IP.v4:
            size = 32
        else:
            size = 128

        return self.assignNet(size,
                              description,
                              hostname,
                              flag=FlagChoices.HOST)

    def assignNet(self,
                  size: int,
                  description: str,
                  hostname=None,
                  flag=FlagChoices.RESERVATION,
                  offset=1) -> 'CIDR':
        """Assign a subnet of requested size from this network

        Arguments:
            size {int} -- Desired size of the subnet
            description {str} -- Description of the use for this subnet

        Keyword Arguments:
            hostname {[type]} -- Hostname to use (default: {None})
            flag: {FlagChoices} -- Network type (reservation, assignment, host)
            offset: {int} -- Offset from the network address (e.g. to allow network address assignments)
        """
        def getStartAddress(net: CidrAddressField, size: int) -> int:
            """Calculate the next possible start address of a network with requested size

            Arguments:
                net {CidrAddressField} -- Net to start with
                size {int} -- subnet size

            Returns:
                int -- The actual distance
            """
            if size >= net.prefixlen:
                startAddress = int(net.broadcast_address) + 1
            else:
                sizeDiff = net.prefixlen - size
                supernet = net.supernet(sizeDiff)
                startAddress = int(supernet.network_address) + 2**(
                    supernet.max_prefixlen - supernet.prefixlen)
            return startAddress

        if size <= self.cidr.prefixlen:
            raise NotEnoughSpace

        gapSize = 2**(self.cidr.max_prefixlen - size)

        # When the network is empty, take first
        if len(self.subcidr) == 0:
            # Make use of ipaddress functions to get all subnets of requested size
            subnets = self.cidr.subnets(new_prefix=size)
            next(subnets)
            newNet = next(subnets)

            # Instantiate new object and persist
            newCIDR = CIDR(cidr=newNet,
                           description=description,
                           fqdn=hostname,
                           flag=flag,
                           parent=self)
            newCIDR.save()
            return newCIDR

        # Non empty network --> find best place
        smallestGap = None

        # Check whether there is enough space before the first child
        firstChild = self.subcidr[0]
        firstGap = int(
            firstChild.cidr.network_address) - int(self.cidr.network_address +
                                                   offset)
        if firstGap >= gapSize:
            smallestGap = {
                'address': self.cidr.network_address + offset,
                'length': firstGap
            }

        for i, child in enumerate(self.subcidr[:-1]):
            net1 = child.cidr
            net2 = self.subcidr[i + 1].cidr

            startAddress = getStartAddress(net1, size)

            gap = int(net2.network_address) - startAddress
            if gap >= gapSize and (smallestGap is None
                                   or gap < smallestGap['length']):
                smallestGap = {
                    'address': ip_address(startAddress),
                    'length': gap
                }

        # the last gap
        lastBlock = self.subcidr[-1].cidr
        lastGapStart = getStartAddress(lastBlock, size)
        lastGap = int(self.cidr.broadcast_address) - lastGapStart + 1

        if lastGap >= gapSize and (smallestGap is None
                                   or lastGap < smallestGap['length']):
            smallestGap = {
                'address': ip_address(lastGapStart),
                'length': lastGap
            }

        if smallestGap is None:
            raise NotEnoughSpace

        # This is ugly but apparently IPVXNetwork has no way to change the netmask
        newNet = str(smallestGap['address']) + "/" + str(size)
        newNet = ip_network(newNet)

        # Instantiate new object and persist
        newCIDR = CIDR(cidr=newNet,
                       description=description,
                       fqdn=hostname,
                       flag=flag,
                       parent=self)
        newCIDR.save()
        return newCIDR
Beispiel #27
0
class WhitelistEntry(models.Model):
    cidr = CidrAddressField()
    who = models.ForeignKey(User)
    why = models.TextField()
    added = models.DateTimeField('date added', auto_now_add=True)
Beispiel #28
0
class Block(models.Model):

    FLAG_DIRECTIONS = (
        (FLAG_NONE, 'None'),
        (FLAG_INBOUND, 'Inbound'),
        (FLAG_OUTBOUND, 'Outbound'),
        (FLAG_BOTH, 'Both'),
    )

    cidr = CidrAddressField(db_index=True)
    who  = models.ForeignKey(User)
    source = models.CharField(max_length=30, db_index=True)
    why  = models.TextField()

    added = models.DateTimeField('date added', auto_now_add=True)
    unblock_at = models.DateTimeField('date to be unblocked', null=True, db_index=True)

    flag = models.CharField(max_length=1, choices=FLAG_DIRECTIONS, default=FLAG_NONE)

    skip_whitelist = models.BooleanField(default=False)

    forced_unblock  = models.BooleanField(default=False, db_index=True)
    unblock_why = models.TextField(blank=True)
    unblock_who = models.ForeignKey(User, related_name='+', null=True, blank=True)

    objects = models.Manager()
    current = CurrentBlockManager()
    expected = ExpectedBlockManager()
    pending = PendingBlockManager()
    pending_removal = PendingRemovalBlockManager()
    expired = ExpiredBlockManager()

    def save(self, *args, **kwargs):
        if not self.skip_whitelist and not self.unblock_now:
            wle = is_whitelisted(self.cidr)
            if wle:
                raise WhitelistError(wle.why)
            if is_prefixlen_too_small(self.cidr):
                raise PrefixLenTooSmallError("Prefix length in %s is too small" % self.cidr)
            item = is_source_blacklisted(self.source)
            if item:
                raise SourceBlacklistedError("Source %s is blacklisted: %s: %s" % (self.source, item.who, item.why))
        super(Block, self).save(*args, **kwargs)

    @property
    def is_unblockable(self):
        """Is this block record unblockable?
        This is not the same as if it IS blocked, but more "should this be blocked"
        """
        if self.forced_unblock:
            return False

        if self.unblock_at is None:
            return True

        if self.unblock_at > timezone.now():
            return True

        return False

    @property
    def duration(self):
        if self.unblock_at is None:
            return None
        return self.unblock_at - self.added

    @property
    def age(self):
        if self.unblock_at is None:
            return None
        return timezone.now() - self.unblock_at

    def unblock_now(self, who, why):
        self.forced_unblock = True
        self.unblock_who = who
        self.unblock_why = why
        self.unblock_at = timezone.now()
        self.save()
Beispiel #29
0
class Ip(BaseAccessLevel):
    """ IP Address Model """
    interface = models.ForeignKey('net.Interface', verbose_name=_('interface'))
    address = InetAddressField(verbose_name=_('ip address'),
                               unique=True,
                               db_index=True)
    protocol = models.CharField(_('IP Protocol Version'),
                                max_length=4,
                                choices=IP_PROTOCOLS,
                                default=IP_PROTOCOLS[0][0],
                                blank=True)
    netmask = CidrAddressField(_('netmask (CIDR, eg: 10.40.0.0/24)'),
                               blank=True,
                               null=True)

    objects = NetAccessLevelManager()

    class Meta:
        app_label = 'net'
        permissions = (('can_view_ip', 'Can view ip'), )
        verbose_name = _('ip address')
        verbose_name_plural = _('ip addresses')

    def __unicode__(self):
        return '%s: %s' % (self.protocol, self.address)

    def clean(self, *args, **kwargs):
        """ TODO """
        # netaddr.IPAddress('10.40.2.1') in netaddr.IPNetwork('10.40.0.0/24')
        pass

    def save(self, *args, **kwargs):
        """
        Determines ip protocol version automatically.
        Stores address in interface shortcuts for convenience.
        """
        self.protocol = 'ipv%d' % self.address.version
        # save
        super(Ip, self).save(*args, **kwargs)

        # save shortcut on interfaces
        ip_cached_list = self.interface.ip_addresses
        # if not present in interface shorctus add it to the list
        if str(self.address) not in ip_cached_list:
            # recalculate cached_ip_list
            recalculated_ip_cached_list = []
            for ip in self.interface.ip_set.all():
                recalculated_ip_cached_list.append(str(ip.address))
            # rebuild string in format "<ip_1>, <ip_2>"
            self.interface.ip_addresses = recalculated_ip_cached_list
            self.interface.save()

    @property
    def owner(self):
        return self.interface.owner

    if 'grappelli' in settings.INSTALLED_APPS:

        @staticmethod
        def autocomplete_search_fields():
            return ('address__icontains', )
class CidrTestModel(Model):
    field = CidrAddressField()
    objects = NetManager()

    class Meta:
        db_table = 'cidr'