start API for IPSet
authorDietmar Maurer <dietmar@proxmox.com>
Mon, 7 Apr 2014 10:31:45 +0000 (12:31 +0200)
committerDietmar Maurer <dietmar@proxmox.com>
Mon, 7 Apr 2014 10:31:45 +0000 (12:31 +0200)
src/PVE/API2/Firewall/Cluster.pm
src/PVE/API2/Firewall/IPSet.pm [new file with mode: 0644]
src/PVE/API2/Firewall/Makefile
src/PVE/Firewall.pm

index b76c924..b270010 100644 (file)
@@ -8,6 +8,7 @@ use PVE::JSONSchema qw(get_standard_option);
 use PVE::Firewall;
 use PVE::API2::Firewall::Rules;
 use PVE::API2::Firewall::Groups;
+use PVE::API2::Firewall::IPSet;
 
 #fixme: locking?
 
@@ -178,4 +179,45 @@ __PACKAGE__->register_method({
        return $res;
     }});
 
+__PACKAGE__->register_method({
+    name => 'ipset',
+    path => 'ipset',
+    method => 'GET',
+    description => "List IPSets",
+    parameters => {
+       additionalProperties => 0,
+    },
+    returns => {
+       type => 'array',
+       items => {
+           type => "object",
+           properties => { 
+               name => {
+                   description => "IPSet name.",
+                   type => 'string',
+               },
+           },
+       },
+       links => [ { rel => 'child', href => "{name}" } ],
+    },
+    code => sub {
+       my ($param) = @_;
+
+       my $cluster_conf = PVE::Firewall::load_clusterfw_conf();
+
+       my $res = [];
+       foreach my $name (keys %{$cluster_conf->{ipset}}) {
+           push @$res, { name => $name, count => scalar(@{$cluster_conf->{ipset}->{$name}}) };
+       }
+
+       return $res;
+    }});
+
+__PACKAGE__->register_method ({
+    subclass => "PVE::API2::Firewall::ClusterIPset",  
+    path => 'ipset/{name}',
+    # set fragment delimiter (no subdirs) - we need that, because CIDR address contain a slash '/' 
+    fragmentDelimiter => '', 
+});
+
 1;
diff --git a/src/PVE/API2/Firewall/IPSet.pm b/src/PVE/API2/Firewall/IPSet.pm
new file mode 100644 (file)
index 0000000..e96efc0
--- /dev/null
@@ -0,0 +1,211 @@
+package PVE::API2::Firewall::IPSetBase;
+
+use strict;
+use warnings;
+use PVE::JSONSchema qw(get_standard_option);
+
+use PVE::Firewall;
+
+use base qw(PVE::RESTHandler);
+
+my $api_properties = { 
+    cidr => {
+       description => "Network/IP specification in CIDR format.",
+       type => 'string', format => 'IPv4orCIDR',
+    },
+    name => {
+       description => "IP set name.",
+       type => 'string',
+    },
+    comment => {
+       type => 'string',
+       optional => 1,
+    },
+    nomatch => {
+       type => 'boolean',
+       optional => 1,
+    },
+};
+
+sub load_config {
+    my ($class, $param) = @_;
+
+    die "implement this in subclass";
+
+    #return ($fw_conf, $rules);
+}
+
+sub save_rules {
+    my ($class, $param, $fw_conf, $rules) = @_;
+
+    die "implement this in subclass";
+}
+
+my $additional_param_hash = {};
+
+sub additional_parameters {
+    my ($class, $new_value) = @_;
+
+    if (defined($new_value)) {
+       $additional_param_hash->{$class} = $new_value;
+    }
+
+    # return a copy
+    my $copy = {};
+    my $org = $additional_param_hash->{$class} || {};
+    foreach my $p (keys %$org) { $copy->{$p} = $org->{$p}; }
+    return $copy;
+}
+
+sub register_get_ipset {
+    my ($class) = @_;
+
+    my $properties = $class->additional_parameters();
+
+    $properties->{name} = $api_properties->{name};
+
+    $class->register_method({
+       name => 'get_ipset',
+       path => '',
+       method => 'GET',
+       description => "List IPSet content",
+       parameters => {
+           additionalProperties => 0,
+           properties => $properties,
+       },
+       returns => {
+           type => 'array',
+           items => {
+               type => "object",
+               properties => {
+                   cidr => {
+                       type => 'string',
+                   },
+                   comment => {
+                       type => 'string',
+                       optional => 1,
+                   },
+                   nomatch => {
+                       type => 'boolean',
+                       optional => 1,
+                   },                  
+               },
+           },
+           links => [ { rel => 'child', href => "{cidr}" } ],
+       },
+       code => sub {
+           my ($param) = @_;
+
+           my ($fw_conf, $ipset) = $class->load_config($param);
+
+           return $ipset;
+       }});
+}
+
+sub register_add_ip {
+    my ($class) = @_;
+
+    my $properties = $class->additional_parameters();
+
+    $properties->{name} = $api_properties->{name};
+    $properties->{cidr} = $api_properties->{cidr};
+    $properties->{nomatch} = $api_properties->{nomatch};
+    $properties->{comment} = $api_properties->{comment};
+    
+    $class->register_method({
+       name => 'add_ip',
+       path => '',
+       method => 'POST',
+       description => "Add IP or Network to IPSet.",
+       protected => 1,
+       parameters => {
+           additionalProperties => 0,
+           properties => $properties,
+       },
+       returns => { type => "null" },
+       code => sub {
+           my ($param) = @_;
+
+           my ($fw_conf, $ipset) = $class->load_config($param);
+
+           my $data = { cidr => $param->{cidr} };
+           $data->{nomatch} = 1 if $param->{nomatch};
+           $data->{comment} = $param->{comment} if $param->{comment};
+
+           # fixme: verify
+
+           unshift @$ipset, $data;
+
+           $class->save_ipset($param, $fw_conf, $ipset);
+
+           return undef;
+       }});
+}
+
+sub register_remove_ip {
+    my ($class) = @_;
+
+    my $properties = $class->additional_parameters();
+
+    $properties->{name} = $api_properties->{name};
+    $properties->{cidr} = $api_properties->{cidr};
+    
+    $class->register_method({
+       name => 'remove_ip',
+       path => '{cidr}',
+       method => 'DELETE',
+       description => "Remove IP or Network from IPSet.",
+       protected => 1,
+       parameters => {
+           additionalProperties => 0,
+           properties => $properties,
+       },
+       returns => { type => "null" },
+       code => sub {
+           my ($param) = @_;
+
+           my ($fw_conf, $ipset) = $class->load_config($param);
+
+           die "implement me $param->{cidr}";
+
+           $class->save_ipset($param, $fw_conf, $ipset);
+
+           return undef;
+       }});
+}
+
+sub register_handlers {
+    my ($class) = @_;
+
+    $class->register_get_ipset();
+    $class->register_add_ip();
+    $class->register_remove_ip();
+}
+
+package PVE::API2::Firewall::ClusterIPset;
+
+use strict;
+use warnings;
+
+use base qw(PVE::API2::Firewall::IPSetBase);
+
+sub load_config {
+    my ($class, $param) = @_;
+
+    my $fw_conf = PVE::Firewall::load_clusterfw_conf();
+    my $ipset = $fw_conf->{ipset}->{$param->{name}};
+    die "no such IPSet '$param->{name}'\n" if !defined($ipset);
+
+    return ($fw_conf, $ipset);
+}
+
+sub save_ipset {
+    my ($class, $param, $fw_conf, $ipset) = @_;
+
+    $fw_conf->{ipset}->{$param->{name}} = $ipset;
+    PVE::Firewall::save_clusterfw_conf($fw_conf);
+}
+
+__PACKAGE__->register_handlers();
+
+1;
index cb93029..acbf9a2 100644 (file)
@@ -1,4 +1,5 @@
 LIB_SOURCES=                   \
+       IPSet.pm                \
        Rules.pm                \
        Cluster.pm              \
        Host.pm                 \
index 5168093..a20b995 100644 (file)
@@ -10,7 +10,7 @@ use PVE::Exception qw(raise raise_param_exc);
 use PVE::JSONSchema qw(get_standard_option);
 use PVE::Cluster;
 use PVE::ProcFSTools;
-use PVE::Tools;
+use PVE::Tools qw($IPV4RE);
 use File::Basename;
 use File::Path;
 use IO::File;
@@ -35,6 +35,18 @@ eval {
     $have_pve_manager = 1;
 };
 
+PVE::JSONSchema::register_format('IPv4orCIDR', \&pve_verify_ipv4_or_cidr);
+sub pve_verify_ipv4_or_cidr {
+    my ($cidr, $noerr) = @_;
+
+    if ($cidr =~ m!^(?:$IPV4RE)(/(\d+))?$! && (!$1 || (($2 > 7) && ($2 <= 32)))) {
+       return $cidr;
+    }
+    return undef if $noerr;
+    die "value does not look like a valid IP address or CIDR network\n";
+}
+
+
 my $feature_ipset_nomatch = 0;
 eval  {
     my (undef, undef, $release) = POSIX::uname();