ipset: implement delete API, improve parameter verification
[pve-firewall.git] / src / PVE / API2 / Firewall / IPSet.pm
1 package PVE::API2::Firewall::IPSetBase;
2
3 use strict;
4 use warnings;
5 use PVE::Exception qw(raise raise_param_exc);
6 use PVE::JSONSchema qw(get_standard_option);
7
8 use PVE::Firewall;
9
10 use base qw(PVE::RESTHandler);
11
12 my $api_properties = { 
13     cidr => {
14         description => "Network/IP specification in CIDR format.",
15         type => 'string', format => 'IPv4orCIDR',
16     },
17     name => {
18         description => "IP set name.",
19         type => 'string',
20     },
21     comment => {
22         type => 'string',
23         optional => 1,
24     },
25     nomatch => {
26         type => 'boolean',
27         optional => 1,
28     },
29 };
30
31 sub load_config {
32     my ($class, $param) = @_;
33
34     die "implement this in subclass";
35
36     #return ($fw_conf, $rules);
37 }
38
39 sub save_rules {
40     my ($class, $param, $fw_conf, $rules) = @_;
41
42     die "implement this in subclass";
43 }
44
45 my $additional_param_hash = {};
46
47 sub additional_parameters {
48     my ($class, $new_value) = @_;
49
50     if (defined($new_value)) {
51         $additional_param_hash->{$class} = $new_value;
52     }
53
54     # return a copy
55     my $copy = {};
56     my $org = $additional_param_hash->{$class} || {};
57     foreach my $p (keys %$org) { $copy->{$p} = $org->{$p}; }
58     return $copy;
59 }
60
61 sub register_get_ipset {
62     my ($class) = @_;
63
64     my $properties = $class->additional_parameters();
65
66     $properties->{name} = $api_properties->{name};
67
68     $class->register_method({
69         name => 'get_ipset',
70         path => '',
71         method => 'GET',
72         description => "List IPSet content",
73         parameters => {
74             additionalProperties => 0,
75             properties => $properties,
76         },
77         returns => {
78             type => 'array',
79             items => {
80                 type => "object",
81                 properties => {
82                     cidr => {
83                         type => 'string',
84                     },
85                     comment => {
86                         type => 'string',
87                         optional => 1,
88                     },
89                     nomatch => {
90                         type => 'boolean',
91                         optional => 1,
92                     },                  
93                 },
94             },
95             links => [ { rel => 'child', href => "{cidr}" } ],
96         },
97         code => sub {
98             my ($param) = @_;
99
100             my ($fw_conf, $ipset) = $class->load_config($param);
101
102             return $ipset;
103         }});
104 }
105
106 sub register_add_ip {
107     my ($class) = @_;
108
109     my $properties = $class->additional_parameters();
110
111     $properties->{name} = $api_properties->{name};
112     $properties->{cidr} = $api_properties->{cidr};
113     $properties->{nomatch} = $api_properties->{nomatch};
114     $properties->{comment} = $api_properties->{comment};
115     
116     $class->register_method({
117         name => 'add_ip',
118         path => '',
119         method => 'POST',
120         description => "Add IP or Network to IPSet.",
121         protected => 1,
122         parameters => {
123             additionalProperties => 0,
124             properties => $properties,
125         },
126         returns => { type => "null" },
127         code => sub {
128             my ($param) = @_;
129
130             my ($fw_conf, $ipset) = $class->load_config($param);
131
132             my $cidr = $param->{cidr};
133             
134             foreach my $entry (@$ipset) {
135                 raise_param_exc({ cidr => "address '$cidr' already exists" }) 
136                     if $entry->{cidr} eq $cidr;
137             }
138
139             my $data = { cidr => $cidr };
140             $data->{nomatch} = 1 if $param->{nomatch};
141             $data->{comment} = $param->{comment} if $param->{comment};
142
143             unshift @$ipset, $data;
144
145             $class->save_ipset($param, $fw_conf, $ipset);
146
147             return undef;
148         }});
149 }
150
151 sub register_remove_ip {
152     my ($class) = @_;
153
154     my $properties = $class->additional_parameters();
155
156     $properties->{name} = $api_properties->{name};
157     $properties->{cidr} = $api_properties->{cidr};
158     
159     $class->register_method({
160         name => 'remove_ip',
161         path => '{cidr}',
162         method => 'DELETE',
163         description => "Remove IP or Network from IPSet.",
164         protected => 1,
165         parameters => {
166             additionalProperties => 0,
167             properties => $properties,
168         },
169         returns => { type => "null" },
170         code => sub {
171             my ($param) = @_;
172
173             my ($fw_conf, $ipset) = $class->load_config($param);
174
175             my $new = [];
176    
177             foreach my $entry (@$ipset) {
178                 push @$new, $entry if $entry->{cidr} ne $param->{cidr};
179             }
180
181             $class->save_ipset($param, $fw_conf, $new);
182             
183             return undef;
184         }});
185 }
186
187 sub register_handlers {
188     my ($class) = @_;
189
190     $class->register_get_ipset();
191     $class->register_add_ip();
192     $class->register_remove_ip();
193 }
194
195 package PVE::API2::Firewall::ClusterIPset;
196
197 use strict;
198 use warnings;
199
200 use base qw(PVE::API2::Firewall::IPSetBase);
201
202 sub load_config {
203     my ($class, $param) = @_;
204
205     my $fw_conf = PVE::Firewall::load_clusterfw_conf();
206     my $ipset = $fw_conf->{ipset}->{$param->{name}};
207     die "no such IPSet '$param->{name}'\n" if !defined($ipset);
208
209     return ($fw_conf, $ipset);
210 }
211
212 sub save_ipset {
213     my ($class, $param, $fw_conf, $ipset) = @_;
214
215     $fw_conf->{ipset}->{$param->{name}} = $ipset;
216     PVE::Firewall::save_clusterfw_conf($fw_conf);
217 }
218
219 __PACKAGE__->register_handlers();
220
221 1;