import ipaddress from django.conf import settings from django.core.exceptions import ValidationError from django.db import models from django.utils.translation import gettext_lazy as _ class Prefix(models.Model): name = models.SlugField( unique=True, verbose_name=_("name"), ) prefix = models.GenericIPAddressField( protocol='IPv6', unique=True, verbose_name=_("prefix"), ) length = models.PositiveSmallIntegerField( verbose_name=_("length"), ) delegated_length = models.PositiveSmallIntegerField( verbose_name=_("length of delegated prefixes"), ) def network(self): return ipaddress.IPv6Network(f'{self.prefix}/{self.length}') def clean(self): try: self.network() except: raise ValidationError(f"{self.prefix}/{self.length} is not a valid network.") if self.length >= self.delegated_length: raise ValidationError(f"length ({self.length}) must be lower than delegated_length ({self.delegated_length}).") if self.delegated_length > 128: raise ValidationError(f"delegated_length {self.delegated_length} must be lower than or equal to 128.") def __str__(self): return str(self.network()) class Meta: verbose_name = _("Prefix") verbose_name_plural = _("Prefixes") class DelegatedPrefix(models.Model): prefix = models.ForeignKey( Prefix, on_delete=models.PROTECT, related_name='delegated_prefixes', verbose_name=_("prefix"), ) delegated_prefix = models.GenericIPAddressField( protocol='IPv6', unique=True, verbose_name=_("delegated prefix"), ) gateway = models.GenericIPAddressField( protocol='IPv6', verbose_name=_("gateway"), ) owner = models.ForeignKey( settings.PREFIX_DELEGATION_OWNER, on_delete=models.CASCADE, verbose_name=_("owner"), ) def network(self): return ipaddress.IPv6Network(f'{self.delegated_prefix}/{self.prefix.delegated_length}') def clean(self): try: network = self.network() except: raise ValidationError(f"{self.delegated_prefix}/{self.prefix.delegated_length} is not a valid network.") prefix_network = self.prefix.network() if not network.subnet_of(prefix_network): raise ValidationError(f"{network} must be a subnet of {prefix_network}.") def __str__(self): return str(self.network()) class Meta: verbose_name = "Delegated prefix" verbose_name_plural = "Delegated prefixes" class Nameserver(models.Model): name = models.CharField( max_length=253, unique=True, ) def __str__(self): return self.name class Meta: verbose_name = "Name server" verbose_name_plural = "Name servers" class DelegatedReverseDns(models.Model): delegated_prefix = models.OneToOneField( DelegatedPrefix, on_delete=models.CASCADE, related_name='delegated_reverse_dns' ) nameservers = models.ManyToManyField( Nameserver ) def owner(self): return self.delegated_prefix.owner def clean(self): if self.delegated_prefix.prefix.delegated_length % 4: raise ValidationError(f"delegated_length must be a multiple of 4 in order to delegate a reverse DNS.") def __str__(self): network = self.delegated_prefix.network() zone = network.network_address.exploded.replace(':', '')[network.prefixlen//4-1::-1] zone = '.'.join(list(zone)) + '.ip6.arpa' return zone class Meta: verbose_name = "Delegated reverse DNS" verbose_name_plural = "Delegated reverse DNS" class DsRecord(models.Model): delegated_reverse_dns = models.ForeignKey( DelegatedReverseDns, on_delete=models.CASCADE, related_name='ds_records' ) key_tag = models.PositiveIntegerField() algorithm = models.PositiveSmallIntegerField() digest_type = models.PositiveSmallIntegerField() digest = models.TextField() def __str__(self): return f"{self.ket_tag} {self.algorithm} {self.digest_type} {self.digest}" class Meta: verbose_name = "DS record" verbose_name_plural = "DS records"