Commit 6299201d by Őry Máté

firewall: add sensible default and help_text to Vlan.ipv6_template

closes #360
parent 8f0735c7
...@@ -19,6 +19,7 @@ ...@@ -19,6 +19,7 @@
from string import ascii_letters from string import ascii_letters
from itertools import islice, ifilter, chain from itertools import islice, ifilter, chain
from math import ceil
import logging import logging
import random import random
...@@ -35,7 +36,7 @@ from django.core.validators import MinValueValidator, MaxValueValidator ...@@ -35,7 +36,7 @@ from django.core.validators import MinValueValidator, MaxValueValidator
import django.conf import django.conf
from django.db.models.signals import post_save, post_delete from django.db.models.signals import post_save, post_delete
from celery.exceptions import TimeoutError from celery.exceptions import TimeoutError
from netaddr import IPSet, EUI, IPNetwork, IPAddress from netaddr import IPSet, EUI, IPNetwork, IPAddress, ipv6_full
from common.models import method_cache, WorkerNotFound, HumanSortField from common.models import method_cache, WorkerNotFound, HumanSortField
from firewall.tasks.local_tasks import reloadtask from firewall.tasks.local_tasks import reloadtask
...@@ -363,9 +364,20 @@ class Vlan(AclBase, models.Model): ...@@ -363,9 +364,20 @@ class Vlan(AclBase, models.Model):
'address is: "%(d)d.%(c)d.%(b)d.%(a)d.in-addr.arpa".'), 'address is: "%(d)d.%(c)d.%(b)d.%(a)d.in-addr.arpa".'),
default="%(d)d.%(c)d.%(b)d.%(a)d.in-addr.arpa") default="%(d)d.%(c)d.%(b)d.%(a)d.in-addr.arpa")
ipv6_template = models.TextField( ipv6_template = models.TextField(
validators=[val_ipv6_template], blank=True,
verbose_name=_('ipv6 template'), help_text=_('Template for translating IPv4 addresses to IPv6. '
default="2001:738:2001:4031:%(b)d:%(c)d:%(d)d:0") 'Automatically generated hosts in dual-stack networks '
'will get this address. The template '
'can contain four tokens: "%(a)d", "%(b)d", '
'"%(c)d", and "%(d)d", representing the four bytes '
'of the IPv4 address, respectively, in decimal notation. '
'Moreover you can use any standard printf format '
'specification like %(a)02x to get the first byte as two '
'hexadecimal digits. Usual choices for mapping '
'198.51.100.0/24 to 2001:0DB8:1:1::/64 would be '
'"2001:0DB8:51:1:1:%(d)d::" and '
'"2001:0DB8:51:1:1:%(d)x00::".'),
validators=[val_ipv6_template], verbose_name=_('ipv6 template'))
dhcp_pool = models.TextField(blank=True, verbose_name=_('DHCP pool'), dhcp_pool = models.TextField(blank=True, verbose_name=_('DHCP pool'),
help_text=_( help_text=_(
'The address range of the DHCP pool: ' 'The address range of the DHCP pool: '
...@@ -380,6 +392,52 @@ class Vlan(AclBase, models.Model): ...@@ -380,6 +392,52 @@ class Vlan(AclBase, models.Model):
modified_at = models.DateTimeField(auto_now=True, modified_at = models.DateTimeField(auto_now=True,
verbose_name=_('modified at')) verbose_name=_('modified at'))
def clean(self):
super(Vlan, self).clean()
if self.ipv6_template:
if not self.network6:
raise ValidationError(
_("You cannot specify an IPv6 template if there is no "
"IPv6 network set."))
for i in (self.network4[1], self.network4[-1]):
i6 = self.convert_ipv4_to_ipv6(i)
if i6 not in self.network6:
raise ValidationError(
_("%(ip6)s (translated from %(ip4)s) is outside of "
"the IPv6 network.") % {"ip4": i, "ip6": i6})
if not self.ipv6_template and self.network6:
# come up with some sensible default
self.ipv6_template = self._magic_ipv6_template()
host4_bytes = int(ceil((32 - self.network4.prefixlen) / 8))
host6_bytes = int(ceil((128 - self.network6.prefixlen) / 8))
if host4_bytes > host6_bytes:
raise ValidationError(
_("IPv6 network is too small to map IPv4 addresses to it."))
def _magic_ipv6_template(self):
host4_bytes = int(ceil((32 - self.network4.prefixlen) / 8))
host6_bytes = int(ceil((128 - self.network6.prefixlen) / 8))
letters = ascii_letters[4-host4_bytes:4]
remove = host6_bytes // 2
ipstr = self.network6.network.format(ipv6_full)
s = ipstr.split(":")[0:-remove]
if 2 * (host4_bytes + 1) < host6_bytes: # use verbose format
for i in letters:
s.append("%({})d".format(i))
else: # use short format
remain = host6_bytes
for i in letters:
if remain % 2 == 1: # can use last half word
if s[-1].endswith("00"):
s[-1] = s[-1][:-2]
s[-1] += "%({})02x".format(i)
else:
s.append("%({})02x00".format(i))
remain -= 1
if host6_bytes > host4_bytes:
s.append(":")
return ":".join(s)
def __unicode__(self): def __unicode__(self):
return "%s - %s" % ("managed" if self.managed else "unmanaged", return "%s - %s" % ("managed" if self.managed else "unmanaged",
self.name) self.name)
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or sign in to comment