use PVE::Network::SDN::Subnets;
use PVE::Network::SDN::SubnetPlugin;
use PVE::Network::SDN::Vnets;
+use PVE::Network::SDN::Zones;
use PVE::Network::SDN::Ipams;
use PVE::Network::SDN::Ipams::Plugin;
sub {
my $cfg = PVE::Network::SDN::Subnets::config();
+ my $zone_cfg = PVE::Network::SDN::Zones::config();
+ my $vnet_cfg = PVE::Network::SDN::Vnets::config();
+ my $vnet = $param->{vnet};
+ my $zoneid = $vnet_cfg->{ids}->{$vnet}->{zone};
+ my $zone = $zone_cfg->{ids}->{$zoneid};
+
my $opts = PVE::Network::SDN::SubnetPlugin->check_config($id, $param, 1, 1);
my $scfg = undef;
}
$cfg->{ids}->{$id} = $opts;
- PVE::Network::SDN::SubnetPlugin->on_update_hook($id, $opts);
+ PVE::Network::SDN::SubnetPlugin->on_update_hook($zone, $id, $opts);
PVE::Network::SDN::Subnets::write_config($cfg);
sub {
my $cfg = PVE::Network::SDN::Subnets::config();
+ my $zone_cfg = PVE::Network::SDN::Zones::config();
+ my $vnet_cfg = PVE::Network::SDN::Vnets::config();
+ my $vnet = $param->{vnet};
+ my $zoneid = $vnet_cfg->{ids}->{$vnet}->{zone};
+ my $zone = $zone_cfg->{ids}->{$zoneid};
+
my $scfg = &$api_sdn_subnets_config($cfg, $id);
PVE::SectionConfig::assert_if_modified($cfg, $digest);
raise_param_exc({ ipam => "you can't change ipam"}) if $opts->{ipam} && $scfg->{ipam} && $opts->{ipam} ne $scfg->{ipam};
- PVE::Network::SDN::SubnetPlugin->on_update_hook($id, $opts, $scfg);
+ PVE::Network::SDN::SubnetPlugin->on_update_hook($zone, $id, $opts, $scfg);
PVE::Network::SDN::Subnets::write_config($cfg);
use PVE::Network::SDN;
use PVE::Network::SDN::Vnets;
use PVE::Network::SDN::Zones;
+use PVE::Network::SDN::Dns;
use PVE::Network::SDN::Zones::Plugin;
use PVE::Network::SDN::Zones::VlanPlugin;
use PVE::Network::SDN::Zones::QinQPlugin;
use Storable qw(dclone);
use PVE::JSONSchema qw(get_standard_option);
use PVE::RPCEnvironment;
+use PVE::Exception qw(raise raise_param_exc);
use PVE::RESTHandler;
properties => { zone => { type => 'string'},
type => { type => 'string'},
mtu => { type => 'integer', optional => 1 },
+ dns => { type => 'string', optional => 1},
+ reversedns => { type => 'string', optional => 1},
+ dnszone => { type => 'string', optional => 1},
pending => { optional => 1},
state => { type => 'string', optional => 1},
nodes => { type => 'string', optional => 1},
my $zone_cfg = PVE::Network::SDN::Zones::config();
my $controller_cfg = PVE::Network::SDN::Controllers::config();
+ my $dns_cfg = PVE::Network::SDN::Dns::config();
my $scfg = undef;
if ($scfg = PVE::Network::SDN::Zones::sdn_zones_config($zone_cfg, $id, 1)) {
die "sdn zone object ID '$id' already defined\n";
}
+
+ my $dnsserver = $opts->{dns};
+ my $reversednsserver = $opts->{reversedns};
+ my $dnszone = $opts->{dnszone};
+ raise_param_exc({ dns => "$dnsserver don't exist"}) if $dnsserver && !$dns_cfg->{ids}->{$dnsserver};
+ raise_param_exc({ reversedns => "$reversednsserver don't exist"}) if $reversednsserver && !$dns_cfg->{ids}->{$reversednsserver};
+ raise_param_exc({ dnszone => "missing dns server"}) if $dnszone && !$dnsserver;
$zone_cfg->{ids}->{$id} = $opts;
$plugin->on_update_hook($id, $zone_cfg, $controller_cfg);
my $zone_cfg = PVE::Network::SDN::Zones::config();
my $controller_cfg = PVE::Network::SDN::Controllers::config();
+ my $dns_cfg = PVE::Network::SDN::Dns::config();
PVE::SectionConfig::assert_if_modified($zone_cfg, $digest);
$scfg->{$k} = $opts->{$k};
}
+ my $dnsserver = $opts->{dns};
+ my $reversednsserver = $opts->{reversedns};
+ my $dnszone = $opts->{dnszone};
+ raise_param_exc({ dns => "$dnsserver don't exist"}) if $dnsserver && !$dns_cfg->{ids}->{$dnsserver};
+ raise_param_exc({ reversedns => "$reversednsserver don't exist"}) if $reversednsserver && !$dns_cfg->{ids}->{$reversednsserver};
+ raise_param_exc({ dnszone => "missing dns server"}) if $dnszone && !$dnsserver;
+
$plugin->on_update_hook($id, $zone_cfg, $controller_cfg);
PVE::Network::SDN::Zones::write_config($zone_cfg);
type => 'string', format => 'pve-configid',
},
ttl => { type => 'integer', optional => 1 },
+ reversev6mask => { type => 'integer', optional => 1 },
dns => get_standard_option('pve-sdn-dns-id',
{ completion => \&PVE::Network::SDN::Dns::complete_sdn_dns }),
},
use PVE::Tools;
use JSON;
use Net::IP;
+use NetAddr::IP;
use base('PVE::Network::SDN::Dns::Plugin');
key => {
type => 'string',
},
+ reversemaskv6 => {
+ type => 'integer'
+ },
};
}
url => { optional => 0},
key => { optional => 0 },
ttl => { optional => 1 },
+ reversemaskv6 => { optional => 1, description => "force a different netmask for the ipv6 reverse zone name." },
+
};
}
my $headers = ['Content-Type' => 'application/json; charset=UTF-8', 'X-API-Key' => $key];
$hostname .= ".";
- my $reverseip = join(".", reverse(split(/\./, $ip))).".in-addr.arpa.";
+ my $reverseip = Net::IP->new($ip)->reverse_ip();
+
my $type = "PTR";
my $record = { content => $hostname,
my $key = $plugin_config->{key};
my $headers = ['Content-Type' => 'application/json; charset=UTF-8', 'X-API-Key' => $key];
- my $reverseip = join(".", reverse(split(/\./, $ip))).".in-addr.arpa.";
+ my $reverseip = Net::IP->new($ip)->reverse_ip();
+
my $type = "PTR";
my $rrset = { name => $reverseip,
}
}
+sub get_reversedns_zone {
+ my ($class, $plugin_config, $subnetid, $ip) = @_;
+
+ my ($network, $mask) = split(/-/, $subnetid);
+
+ my $cidr = "$ip/$mask";
+ my $zone = "";
+
+ if (Net::IP::ip_is_ipv4($ip)) {
+ my ($ipblock1, $ipblock2, $ipblock3, $ipblock4) = split(/\./, $ip);
+
+ my $ipv4 = new NetAddr::IP($cidr);
+ #private addresse #powerdns built-in private zone : serve-rfc1918
+ if($ipv4->is_rfc1918()) {
+ if ($ipblock1 == 192) {
+ $zone = "168.192.in-addr.arpa.";
+ } elsif ($ipblock1 == 172) {
+ $zone = "16-31.172.in-addr.arpa.";
+ } elsif ($ipblock1 == 10) {
+ $zone = "10.in-addr.arpa.";
+ }
+
+ } else {
+ #public ipv4 : RIPE,ARIN,AFRNIC
+ #. Delegations can be managed in IPv4 on bit boundaries (/8, /16 or /24s), and IPv6 networks can be managed on nibble boundaries (every 4 bits of the IPv6 address)
+ #One or more /24 type zones need to be created if your address space has a prefix length between /17 and /24.
+ # If your prefix length is between /16 and /9 you will have to request one or more delegations for /16 type zones.
+
+ if ($mask <= 24) {
+ $zone = "$ipblock3.$ipblock2.$ipblock1.in-addr.arpa.";
+ } elsif ($mask <= 16) {
+ $zone = "$ipblock2.$ipblock1.in-addr.arpa.";
+ } elsif ($mask <= 8) {
+ $zone = "$ipblock1.in-addr.arpa.";
+ }
+ }
+ } else {
+ $mask = $plugin_config->{reversemaskv6} if $plugin_config->{reversemaskv6};
+ die "reverse dns zone mask need to be a multiple of 4" if ($mask % 4);
+ my $networkv6 = NetAddr::IP->new($cidr)->network();
+ $zone = Net::IP->new($networkv6)->reverse_ip();
+ }
+
+ return $zone;
+}
+
sub on_update_hook {
my ($class, $plugin_config) = @_;
# type => 'string',
# description => "static routes [network=<network>:gateway=<ip>,network=<network>:gateway=<ip>,... ]",
# },
- dns => {
- type => 'string',
- description => "dns api server",
- },
- reversedns => {
- type => 'string',
- description => "reverse dns api server",
- },
- dnszone => {
- type => 'string', format => 'dns-name',
- description => "dns domain zone ex: mydomain.com",
- },
- reversednszone => {
- type => 'string', format => 'dns-name',
- description => "reverse dns zone ex: 0.168.192.in-addr.arpa",
- },
dnszoneprefix => {
type => 'string', format => 'dns-name',
description => "dns domain zone prefix ex: 'adm' -> <hostname>.adm.mydomain.com",
gateway => { optional => 1 },
# routes => { optional => 1 },
snat => { optional => 1 },
- dns => { optional => 1 },
- reversedns => { optional => 1 },
- dnszone => { optional => 1 },
- reversednszone => { optional => 1 },
dnszoneprefix => { optional => 1 },
ipam => { optional => 0 },
};
}
sub on_update_hook {
- my ($class, $subnetid, $subnet, $old_subnet) = @_;
+ my ($class, $zone, $subnetid, $subnet, $old_subnet) = @_;
my $cidr = $subnetid =~ s/-/\//r;
my $subnet_matcher = subnet_matcher($cidr);
my $vnetid = $subnet->{vnet};
my $gateway = $subnet->{gateway};
my $ipam = $subnet->{ipam};
- my $dns = $subnet->{dns};
- my $dnszone = $subnet->{dnszone};
- my $reversedns = $subnet->{reversedns};
- my $reversednszone = $subnet->{reversednszone};
+ my $dns = $zone->{dns};
+ my $dnszone = $zone->{dnszone};
+ my $reversedns = $zone->{reversedns};
my $old_gateway = $old_subnet->{gateway} if $old_subnet;
#for /32 pointopoint, we allow gateway outside the subnet
raise_param_exc({ gateway => "$gateway is not in subnet $subnetid"}) if $gateway && !$subnet_matcher->($gateway) && $mask != 32;
- raise_param_exc({ dns => "missing dns provider"}) if $dnszone && !$dns;
- raise_param_exc({ dnszone => "missing dns zone"}) if $dns && !$dnszone;
- raise_param_exc({ reversedns => "missing dns provider"}) if $reversednszone && !$reversedns;
- raise_param_exc({ reversednszone => "missing dns zone"}) if $reversedns && !$reversednszone;
- raise_param_exc({ reversedns => "missing forward dns zone"}) if $reversednszone && !$dnszone;
-
if ($ipam) {
my $ipam_cfg = PVE::Network::SDN::Ipams::config();
my $plugin_config = $ipam_cfg->{ids}->{$ipam};
#delete on removal
if (!defined($gateway) && $old_gateway) {
eval {
- PVE::Network::SDN::Subnets::del_ip($subnetid, $old_subnet, $old_gateway);
+ PVE::Network::SDN::Subnets::del_ip($zone, $subnetid, $old_subnet, $old_gateway);
};
warn if $@;
}
if(!$old_gateway || $gateway && $gateway ne $old_gateway) {
- PVE::Network::SDN::Subnets::add_ip($subnetid, $subnet, $gateway);
+ PVE::Network::SDN::Subnets::add_ip($zone, $subnetid, $subnet, $gateway);
}
#delete old ip after update
if($gateway && $old_gateway && $gateway ne $old_gateway) {
eval {
- PVE::Network::SDN::Subnets::del_ip($subnetid, $old_subnet, $old_gateway);
+ PVE::Network::SDN::Subnets::del_ip($zone, $subnetid, $old_subnet, $old_gateway);
};
warn if $@;
}
$plugin->verify_zone($plugin_config, $zone);
};
+my $get_reversedns_zone = sub {
+ my ($subnetid, $dns, $ip) = @_;
+
+ return if !$subnetid || !$dns || !$ip;
+
+ my $dns_cfg = PVE::Network::SDN::Dns::config();
+ my $plugin_config = $dns_cfg->{ids}->{$dns};
+ my $plugin = PVE::Network::SDN::Dns::Plugin->lookup($plugin_config->{type});
+ $plugin->get_reversedns_zone($plugin_config, $subnetid, $ip);
+};
+
my $add_dns_record = sub {
my ($zone, $dns, $hostname, $dnszoneprefix, $ip) = @_;
return if !$zone || !$dns || !$hostname || !$ip;
};
sub next_free_ip {
- my ($subnetid, $subnet, $hostname) = @_;
+ my ($zone, $subnetid, $subnet, $hostname) = @_;
my $cidr = undef;
my $ip = undef;
my $ipamid = $subnet->{ipam};
- my $dns = $subnet->{dns};
- my $dnszone = $subnet->{dnszone};
- my $reversedns = $subnet->{reversedns};
- my $reversednszone = $subnet->{reversednszone};
+ my $dns = $zone->{dns};
+ my $dnszone = $zone->{dnszone};
+ my $reversedns = $zone->{reversedns};
my $dnszoneprefix = $subnet->{dnszoneprefix};
#verify dns zones before ipam
&$verify_dns_zone($dnszone, $dns);
- &$verify_dns_zone($reversednszone, $reversedns);
if($ipamid) {
my $ipam_cfg = PVE::Network::SDN::Ipams::config();
}
eval {
+ my $reversednszone = &$get_reversedns_zone($subnetid, $reversedns, $ip);
+
#add dns
&$add_dns_record($dnszone, $dns, $hostname, $dnszoneprefix, $ip);
#add reverse dns
}
sub add_ip {
- my ($subnetid, $subnet, $ip, $hostname) = @_;
+ my ($zone, $subnetid, $subnet, $ip, $hostname) = @_;
return if !$subnet;
my $ipamid = $subnet->{ipam};
- my $dns = $subnet->{dns};
- my $dnszone = $subnet->{dnszone};
- my $reversedns = $subnet->{reversedns};
- my $reversednszone = $subnet->{reversednszone};
+ my $dns = $zone->{dns};
+ my $dnszone = $zone->{dnszone};
+ my $reversedns = $zone->{reversedns};
+ my $reversednszone = &$get_reversedns_zone($subnetid, $reversedns, $ip);
my $dnszoneprefix = $subnet->{dnszoneprefix};
#verify dns zones before ipam
}
sub del_ip {
- my ($subnetid, $subnet, $ip, $hostname) = @_;
+ my ($zone, $subnetid, $subnet, $ip, $hostname) = @_;
return if !$subnet;
my $ipamid = $subnet->{ipam};
- my $dns = $subnet->{dns};
- my $dnszone = $subnet->{dnszone};
- my $reversedns = $subnet->{reversedns};
- my $reversednszone = $subnet->{reversednszone};
+ my $dns = $zone->{dns};
+ my $dnszone = $zone->{dnszone};
+ my $reversedns = $zone->{reversedns};
+ my $reversednszone = &$get_reversedns_zone($subnetid, $reversedns, $ip);
my $dnszoneprefix = $subnet->{dnszoneprefix};
&$verify_dns_zone($dnszone, $dns);
sub on_update_hook {
my ($class, $vnetid, $vnet_cfg, $subnet_cfg) = @_;
+
+ #fixme : don't allow change zone if subnets are defined
+ #fixme : don't vlanaware change if subnets are defined
+# my $subnets = PVE::Network::SDN::Vnets::get_subnets($vnetid);
+
# verify that tag is not already defined in another vnet
if (defined($vnet_cfg->{ids}->{$vnetid}->{tag})) {
my $tag = $vnet_cfg->{ids}->{$vnetid}->{tag};
use PVE::Cluster qw(cfs_read_file cfs_write_file cfs_lock_file);
use Net::IP;
use PVE::Network::SDN::Subnets;
+use PVE::Network::SDN::Zones;
use PVE::Network::SDN::VnetPlugin;
PVE::Network::SDN::VnetPlugin->register();
sub get_next_free_ip {
my ($vnetid, $hostname, $ipversion) = @_;
+ my $vnet = PVE::Network::SDN::Vnets::get_vnet($vnetid);
+ my $zoneid = $vnet->{zone};
+ my $zone = PVE::Network::SDN::Zones::get_zone($zoneid);
+
$ipversion = 4 if !$ipversion;
my $subnets = PVE::Network::SDN::Vnets::get_subnets($vnetid, 1);
my $ip = undef;
$subnetcount++;
if ($subnet->{ipam}) {
eval {
- $ip = PVE::Network::SDN::Subnets::next_free_ip($subnetid, $subnet, $hostname);
+ $ip = PVE::Network::SDN::Subnets::next_free_ip($zone, $subnetid, $subnet, $hostname);
};
warn $@ if $@;
}
my ($vnetid, $cidr, $hostname) = @_;
my $subnets = PVE::Network::SDN::Vnets::get_subnets($vnetid, 1);
+ my $vnet = PVE::Network::SDN::Vnets::get_vnet($vnetid);
+ my $zoneid = $vnet->{zone};
+ my $zone = PVE::Network::SDN::Zones::get_zone($zoneid);
my ($ip, $mask) = split(/\//, $cidr);
my ($subnetid, $subnet) = PVE::Network::SDN::Subnets::find_ip_subnet($ip, $subnets);
- PVE::Network::SDN::Subnets::add_ip($subnetid, $subnet, $ip, $hostname);
+ PVE::Network::SDN::Subnets::add_ip($zone, $subnetid, $subnet, $ip, $hostname);
}
sub del_ip {
my ($vnetid, $cidr, $hostname) = @_;
my $subnets = PVE::Network::SDN::Vnets::get_subnets($vnetid, 1);
+ my $vnet = PVE::Network::SDN::Vnets::get_vnet($vnetid);
+ my $zoneid = $vnet->{zone};
+ my $zone = PVE::Network::SDN::Zones::get_zone($zoneid);
my ($ip, $mask) = split(/\//, $cidr);
my ($subnetid, $subnet) = PVE::Network::SDN::Subnets::find_ip_subnet($ip, $subnets);
- PVE::Network::SDN::Subnets::del_ip($subnetid, $subnet, $ip, $hostname);
+ PVE::Network::SDN::Subnets::del_ip($zone, $subnetid, $subnet, $ip, $hostname);
}
1;
return $cmdname eq 'add' ? [] : [ PVE::Network::SDN::sdn_zones_ids($cfg) ];
}
+sub get_zone {
+ my ($zoneid, $running) = @_;
+
+ my $cfg = {};
+ if($running) {
+ my $cfg = PVE::Network::SDN::config();
+ $cfg = $cfg->{vnets};
+ } else {
+ $cfg = PVE::Network::SDN::Zones::config();
+ }
+
+ my $zone = PVE::Network::SDN::Zones::sdn_zones_config($cfg, $zoneid, 1);
+
+ return $zone;
+}
+
sub generate_etc_network_config {
'vrf-vxlan' => { optional => 0 },
'controller' => { optional => 0 },
mtu => { optional => 1 },
+ dns => { optional => 1 },
+ reversedns => { optional => 1 },
+ dnszone => { optional => 1 },
};
}
'dp-id' => { optional => 0 },
# 'uplink-id' => { optional => 0 },
'controller' => { optional => 0 },
+ dns => { optional => 1 },
+ reversedns => { optional => 1 },
+ dnszone => { optional => 1 },
};
}
'bridge' => { optional => 0 },
'mtu' => { optional => 1 },
'vlan-protocol' => { optional => 1 },
+ dns => { optional => 1 },
+ reversedns => { optional => 1 },
+ dnszone => { optional => 1 },
};
}
return 'simple';
}
+sub properties {
+ return {
+ dns => {
+ type => 'string',
+ description => "dns api server",
+ },
+ reversedns => {
+ type => 'string',
+ description => "reverse dns api server",
+ },
+ dnszone => {
+ type => 'string', format => 'dns-name',
+ description => "dns domain zone ex: mydomain.com",
+ },
+ };
+}
+
sub options {
return {
nodes => { optional => 1},
- mtu => { optional => 1 }
+ mtu => { optional => 1 },
+ dns => { optional => 1 },
+ reversedns => { optional => 1 },
+ dnszone => { optional => 1 },
};
}
return {
nodes => { optional => 1},
'bridge' => { optional => 0 },
- mtu => { optional => 1 }
+ mtu => { optional => 1 },
+ dns => { optional => 1 },
+ reversedns => { optional => 1 },
+ dnszone => { optional => 1 },
};
}
nodes => { optional => 1},
peers => { optional => 0 },
mtu => { optional => 1 },
+ dns => { optional => 1 },
+ reversedns => { optional => 1 },
+ dnszone => { optional => 1 },
};
}