Project

General

Profile

Download (107 KB) Statistics
| Branch: | Revision:
1 95b003ff Origo
#!/usr/bin/perl
2
3
# All rights reserved and Copyright (c) 2020 Origo Systems ApS.
4
# This file is provided with no warranty, and is subject to the terms and conditions defined in the license file LICENSE.md.
5
# The license file is part of this source code package and its content is also available at:
6
# https://www.origo.io/info/stabiledocs/licensing/stabile-open-source-license
7
8
package Stabile::Networks;
9
10
use Error qw(:try);
11
use Data::Dumper;
12
use Time::Local;
13
use Time::HiRes qw( time );
14
use Data::UUID;
15
use Net::Netmask;
16
use Net::Ping;
17
use File::Basename;
18
use List::Util qw(shuffle);
19
use lib dirname (__FILE__);
20
use Stabile;
21
22 2a63870a Christian Orellana
($datanic, $extnic) = $main::getNics->();
23 95b003ff Origo
$extsubnet = $Stabile::config->get('EXTERNAL_SUBNET_SIZE');
24
$proxynic = $Stabile::config->get('PROXY_NIC') || $extnic;
25
$proxyip = $Stabile::config->get('PROXY_IP');
26
$proxygw = $Stabile::config->get('PROXY_GW') || $proxyip;
27
$proxysubnet = $Stabile::config->get('PROXY_SUBNET_SIZE');
28
my $engineid = $Stabile::config->get('ENGINEID') || "";
29
$dodns = $Stabile::config->get('DO_DNS') || "";
30 71b897d3 hq
$enginelinked = $Stabile::config->get('ENGINE_LINKED') || "";
31 95b003ff Origo
32
my $tenders = $Stabile::config->get('STORAGE_POOLS_ADDRESS_PATHS');
33
@tenderlist = split(/,\s*/, $tenders);
34
my $tenderpaths = $Stabile::config->get('STORAGE_POOLS_LOCAL_PATHS') || "/mnt/stabile/images";
35
@tenderpathslist = split(/,\s*/, $tenderpaths);
36
my $tendernames = $Stabile::config->get('STORAGE_POOLS_NAMES') || "Standard storage";
37
@tendernameslist = split(/,\s*/, $tendernames);
38
$storagepools = $Stabile::config->get('STORAGE_POOLS_DEFAULTS') || "0";
39
40
$uiuuid;
41
$uistatus;
42
$help = 0; # If this is set, functions output help
43
44
#our %options=();
45
# -a action -h help -u uuid -m match pattern -f full list, i.e. all users
46
# -v verbose, include HTTP headers -s impersonate subaccount -t target [uuid or image]
47
# -g args to gearman task
48
#Getopt::Std::getopts("a:hfu:g:m:vs:t:", \%options);
49
50
try {
51
    Init(); # Perform various initalization tasks
52
    process() if ($package);
53
54
} catch Error with {
55
	my $ex = shift;
56
    print header('text/html', '500 Internal Server Error') unless ($console);
57
	if ($ex->{-text}) {
58
        print "Got error: ", $ex->{-text}, " on line ", $ex->{-line}, "\n";
59
	} else {
60
	    print "Status=ERROR\n";	    
61
	}
62
} finally {
63
};
64
65
1;
66
67
sub getObj {
68
    my %h = %{@_[0]};
69
    $console = 1 if $h{"console"};
70
    $api = 1 if $h{"api"};
71
    my $uuid = $h{"uuid"};
72 eb31fb38 hq
    my $obj;
73
    $action = $action || $h{'action'};
74
    if (
75
        $action =~ /^dns/
76
    ) {
77
        $obj = \%h;
78
        return $obj;
79
    }
80 95b003ff Origo
    $uuid = $curuuid if ($uuid eq 'this');
81 d3d1a2d4 Origo
    if ($uuid =~ /(\d+\.\d+\.\d+\.\d+)/) { # ip addresses are unique across networks so we allow this
82
        foreach my $val (values %register) {
83
            if ($val->{'internalip'} eq $uuid || $val->{'externalip'} eq $uuid) {
84
                $uuid = $val->{'uuid'};
85
                last;
86
            }
87
        }
88
    }
89 95b003ff Origo
    my $dbobj = $register{$uuid} || {};
90
    my $status = $dbobj->{'status'} || $h{"status"}; # Trust db status if it exists
91 c899e439 Origo
    if ((!$uuid && $uuid ne '0') && (!$status || $status eq 'new') && ($action eq 'save')) {
92 95b003ff Origo
        my $ug = new Data::UUID;
93
        $uuid = $ug->create_str();
94
        $status = 'new';
95
    };
96
    return 0 unless ($uuid && length $uuid == 36);
97
98
    $uiuuid = $uuid;
99
    $uistatus = $dbobj->{'status'};
100
101
    my $id = $h{"id"};
102
    my $dbid = 0+$dbobj->{'id'};
103
    if ($status eq 'new' || !$dbid) {
104
        $id = getNextId($id) ;
105
    } else {
106
        $id = $dbid;
107
    }
108
109
    if ($id > 4095 || $id < 0 || ($id==0 && $uuid!=0) || ($id==1 && $uuid!=1)) {
110
        $postreply .= "Status=ERROR Invalid new network id $id\n";
111
        return;
112
    }
113
    my $name = $h{"name"} || $dbobj->{'name'};
114
    my $internalip = $h{"internalip"} || $dbobj->{'internalip'};
115
    if (!($internalip =~ /\d+\.\d+\.\d+\.\d+/)) {$internalip = ""};
116
    my $externalip = $h{"externalip"} || $dbobj->{'externalip'};
117
    my $ports = $h{"ports"} || $dbobj->{'ports'};
118
    my $type = $h{"type"} || $dbobj->{'type'};
119 d3d1a2d4 Origo
    my $systems = $h{"systems"} || $dbobj->{'systems'};
120
    my $force = $h{"force"};
121 95b003ff Origo
    my $reguser = $dbobj->{'user'};
122
    # Sanity checks
123
    if (
124
        ($name && length $name > 255)
125
        || ($ports && length $ports > 255)
126
        || ($type && !($type =~ /gateway|ipmapping|internalip|externalip/))
127
    ) {
128
         $postreply .= "Stroke=ERROR Bad network data: $name\n";
129
         return;
130
     }
131
     # Security check
132 d24d9a01 hq
     if (($user ne $reguser && index($privileges,"a")==-1 && $action ne 'save' ) ||
133 95b003ff Origo
         ($reguser && $status eq "new"))
134
     {
135 d24d9a01 hq
         $postreply .= "Stroke=ERROR Bad user: $user, $action\n";
136 95b003ff Origo
         return;
137
     }
138
139
    if (!$type ||($type ne 'gateway' && $type ne 'internalip' && $type ne 'ipmapping' && $type ne 'externalip')) {
140
         $type = "gateway";
141
         if ($internalip && $internalip ne "--" && $externalip && $externalip ne "--") {$type = "ipmapping";}
142
         elsif (($internalip && $internalip ne "--") || $status eq 'new') {$type = "internalip";}
143
         elsif (($externalip && $externalip ne "--") || $status eq 'new') {$type = "externalip";}
144 d3d1a2d4 Origo
    }
145 95b003ff Origo
146
    my $obj = {
147
        uuid => $uuid,
148
        id => $id,
149
        name => $name,
150
        status => $status,
151
        type => $type,
152
        internalip => $internalip,
153
        externalip => $externalip,
154
        ports => $ports,
155 d3d1a2d4 Origo
        systems => $systems,
156
        force => $force,
157 95b003ff Origo
        action => $h{"action"}
158
    };
159
    return $obj;
160
}
161
162
sub Init {
163
164
    # Tie database tables to hashes
165
    unless ( tie(%register,'Tie::DBI', Hash::Merge::merge({table=>'networks'}, $Stabile::dbopts)) ) {return "Unable to access network register"};
166
    unless ( tie(%domreg,'Tie::DBI', Hash::Merge::merge({table=>'domains'}, $Stabile::dbopts)) ) {return "Unable to access domain register"};
167
168
    # simplify globals initialized in Stabile.pm
169
    $tktuser = $tktuser || $Stabile::tktuser;
170
    $user = $user || $Stabile::user;
171
172
    # Create aliases of functions
173
    *header = \&CGI::header;
174
175
    *Natall = \&Deactivateall;
176
    *Stopall = \&Deactivateall;
177
    *Restoreall = \&Activateall;
178
179
    *do_save = \&Save;
180
    *do_tablelist = \&do_list;
181
    *do_jsonlist = \&do_list;
182
    *do_listnetworks = \&do_list;
183
    *do_this = \&do_list;
184
    *do_help = \&action;
185
    *do_remove = \&action;
186
187
    *do_restoreall = \&privileged_action;
188
    *do_activateall = \&privileged_action;
189
    *do_deactivateall = \&privileged_action;
190
    *do_natall = \&privileged_action;
191
    *do_stopall = \&privileged_action;
192
    *do_stop = \&privileged_action;
193
    *do_activate = \&privileged_action;
194
    *do_deactivate = \&privileged_action;
195
196
    *do_gear_activate = \&do_gear_action;
197
    *do_gear_deactivate = \&do_gear_action;
198
    *do_gear_stop = \&do_gear_action;
199
    *do_gear_activateall = \&do_gear_action;
200
    *do_gear_restoreall = \&do_gear_action;
201
    *do_gear_deactivateall = \&do_gear_action;
202
    *do_gear_stopall = \&do_gear_action;
203
    *do_gear_natall = \&do_gear_action;
204
205
    $rx; # Global rx count in bytes
206
    $tx; # Global tx count in bytes
207
    $etcpath = "/etc/stabile/networks";
208
209
}
210
211
sub do_list {
212 d3d1a2d4 Origo
    my ($uuid, $action, $obj) = @_;
213 95b003ff Origo
    if ($help) {
214
        return <<END
215 d3d1a2d4 Origo
GET:uuid:
216 95b003ff Origo
List networks current user has access to.
217
END
218
    }
219
220
    my $res;
221
    my $filter;
222
    my $statusfilter;
223
    my $uuidfilter;
224 d3d1a2d4 Origo
    $uuid = $obj->{'uuid'} if ($obj->{'uuid'});
225 95b003ff Origo
226
    if ($curuuid && ($isadmin || $register{$curuuid}->{'user'} eq $user) && $uripath =~ /networks(\.cgi)?\/(\?|)(this)/) {
227
        $uuidfilter = $curuuid;
228
    } elsif ($uripath =~ /networks(\.cgi)?\/(\?|)(name|status)/) {
229
        $filter = $3 if ($uripath =~ /networks(\.cgi)?\/.*name(:|=)(.+)/);
230
        $statusfilter = $3 if ($uripath =~ /networks(\.cgi)?\/.*status(:|=)(\w+)/);
231
    } elsif ($uripath =~ /networks(\.cgi)?\/(\w{8}-\w{4}-\w{4}-\w{4}-\w{12})/) {
232 d3d1a2d4 Origo
        $uuidfilter = $2;
233
    } elsif ($uuid) {
234
        $uuidfilter = $uuid;
235 95b003ff Origo
    }
236
    $uuidfilter = $options{u} unless $uuidfilter;
237
    $filter = $1 if ($filter =~ /(.*)\*/);
238
    $statusfilter = '' if ($statusfilter eq '*');
239
240
    my $curnetwork = URI::Escape::uri_unescape($params{'network'});
241
    my $curnetwork1 = URI::Escape::uri_unescape($params{'network1'});
242
243
    my $sysuuid;
244
    if ($params{'system'}) {
245
        $sysuuid = $params{'system'};
246
        $sysuuid = $cursysuuid || $curdomuuid if ($params{'system'} eq 'this');
247
    }
248
249
    $res .= header('application/json') unless ($console || $action eq 'tablelist');
250
    my @curregvalues;
251
252 54401133 hq
#    updateBilling();
253 95b003ff Origo
    my @regkeys;
254
    if ($fulllist) {
255
        @regkeys = keys %register;
256
    } elsif ($uuidfilter && $isadmin) {
257
        @regkeys = (tied %register)->select_where("uuid = '$uuidfilter'");
258
    } else {
259
        @regkeys = (tied %register)->select_where("user = '$user' OR user = 'common'");
260
    }
261
262
    foreach my $k (@regkeys) {
263
        my $valref = $register{$k};
264
        my $uuid = $valref->{'uuid'};
265
        my $dbuser = $valref->{'user'};
266
        my $type = $valref->{'type'};
267
        my $id = $valref->{'id'};
268
    # Only list networks belonging to current user
269
        if ($dbuser eq "common" || $user eq $dbuser || $fulllist || ($uuidfilter && $isadmin)) {
270
            my $dom = $domreg{$valref->{'domains'}};
271
            next unless (!$sysuuid || $dom->{'system'} eq $sysuuid || $valref->{'domains'} eq $sysuuid);
272
            validateStatus($valref);
273
274
            my %val = %{$valref}; # Deference and assign to new ass array, effectively cloning object
275
            $val{'id'} += 0;
276
            $val{'rx'} = $rx;
277
            $val{'tx'} = $tx;
278 04c16f26 hq
            $val{'domainnames'} = decode('utf8', $val{'domainnames'});
279 95b003ff Origo
            if ($filter || $statusfilter || $uuidfilter) { # List filtered networks
280
                my $fmatch;
281
                my $smatch;
282
                my $umatch;
283
                $fmatch = 1 if (!$filter || $val{'name'}=~/$filter/i);
284
                $smatch = 1 if (!$statusfilter || $statusfilter eq 'all'
285
                        || $statusfilter eq $val{'status'}
286
                        );
287
                $umatch = 1 if ($val{'uuid'} eq $uuidfilter);
288
                if ($fmatch && $smatch && !$uuidfilter) {
289
                    push @curregvalues,\%val;
290
                } elsif ($umatch) {
291
                    push @curregvalues,\%val;
292
                    last;
293
                }
294
295
            } elsif ($action eq "listnetworks") { # List available networks
296 c899e439 Origo
                if (($id>0 || index($privileges,"a")!=-1) && ((!$valref->{'domains'} && !$valref->{'systems'}) || $type eq 'gateway' || ($curnetwork eq $uuid && !$curnetwork1) || $curnetwork1 eq $uuid)) {
297 95b003ff Origo
                    push @curregvalues,\%val;
298
                }
299
            } else {
300
                push @curregvalues,\%val if ($id>0 || index($privileges,"a")!=-1);
301
            }
302
        }
303
    }
304
305
    # Sort @curregvalues
306
    my $sort = 'status';
307
    $sort = $2 if ($uripath =~ /sort\((\+|\-)(\S+)\)/);
308
    my $reverse;
309
    $reverse = 1 if ($1 eq '-');
310
    if ($reverse) { # sort reverse
311
        if ($sort =~ /id/) {
312
            @curregvalues = (sort {$b->{$sort} <=> $a->{$sort}} @curregvalues); # Sort as number
313
        } else {
314
            @curregvalues = (sort {$b->{$sort} cmp $a->{$sort}} @curregvalues); # Sort as string
315
        }
316
    } else {
317
        if ($sort =~ /id/) {
318
            @curregvalues = (sort {$a->{$sort} <=> $b->{$sort}} @curregvalues); # Sort as number
319
        } else {
320
            @curregvalues = (sort {$a->{$sort} cmp $b->{$sort}} @curregvalues); # Sort as string
321
        }
322
    }
323
324
    my %val = ("uuid", "--", "name", "--");
325
    if ($curnetwork1) {
326
        push @curregvalues, \%val;
327
    }
328
    if ($action eq 'tablelist') {
329
        $res .= header("text/plain") unless ($console);
330
        my $t2 = Text::SimpleTable->new(36,20,10,5,10,14,14,7);
331
        $t2->row('uuid', 'name', 'type', 'id', 'internalip', 'externalip', 'user', 'status');
332
        $t2->hr;
333
        my $pattern = $options{m};
334
        foreach $rowref (@curregvalues){
335
            if ($pattern) {
336
                my $rowtext = $rowref->{'uuid'} . " " . $rowref->{'name'} . " " . $rowref->{'type'} . " " . $rowref->{'id'}
337
                   . " " .  $rowref->{'internalip'} . " " . $rowref->{'externalip'} . " " . $rowref->{'user'} . " " . $rowref->{'status'};
338
                $rowtext .= " " . $rowref->{'mac'} if ($isadmin);
339
                next unless ($rowtext =~ /$pattern/i);
340
            }
341
            $t2->row($rowref->{'uuid'}, $rowref->{'name'}||'--', $rowref->{'type'}, $rowref->{'id'},
342
            $rowref->{'internalip'}||'--', $rowref->{'externalip'}||'--', $rowref->{'user'}, $rowref->{'status'});
343
        }
344
        $res .= $t2->draw;
345
    } elsif ($console && !$uuidfilter && $action ne 'jsonlist') {
346
        $res .= Dumper(\@curregvalues);
347
    } else {
348
        my $json_text;
349
        if ($uuidfilter) {
350
            $json_text = to_json($curregvalues[0], {pretty => 1}) if (@curregvalues);
351
        } else {
352
            $json_text = to_json(\@curregvalues, {pretty => 1}) if (@curregvalues);
353
        }
354
        $json_text = "[]" unless $json_text;
355
        $json_text =~ s/""/"--"/g;
356
        $json_text =~ s/null/"--"/g;
357
        $json_text =~ s/undef/"--"/g;
358
        $json_text =~ s/\x/ /g;
359
        $res .= qq|{"action": "$action", "identifier": "uuid", "label": "name", "items": | if ($action && $action ne 'jsonlist' && $action ne 'list' && !$uuidfilter);
360
        $res .= $json_text;
361
        $res .= qq|}| if ($action && $action ne 'jsonlist' && $action ne 'list'  && !$uuidfilter);
362
#        $res .= "JSON" if (action eq 'jsonlist');
363
    }
364
    return $res;
365
}
366
367
sub do_uuidlookup {
368
    if ($help) {
369
        return <<END
370
GET:uuid:
371
Simple action for looking up a uuid or part of a uuid and returning the complete uuid.
372
END
373
    }
374
375
    my $res;
376
    $res .= header('text/plain') unless $console;
377
    my $u = $options{u};
378
    $u = $curuuid unless ($u || $u eq '0');
379
    my $ruuid;
380
    if ($u || $u eq '0') {
381
        foreach my $uuid (keys %register) {
382
            if (($register{$uuid}->{'user'} eq $user || $register{$uuid}->{'user'} eq 'common' || $fulllist)
383
                && ($uuid =~ /^$u/ || $register{$uuid}->{'name'} =~ /^$u/)) {
384
                $ruuid = $uuid;
385
                last;
386
            }
387
        }
388
        if (!$ruuid && $isadmin) { # If no match and user is admin, do comprehensive lookup
389
            foreach $uuid (keys %register) {
390
                if ($uuid =~ /^$u/ || $register{$uuid}->{'name'} =~ /^$u/) {
391
                    $ruuid = $uuid;
392
                    last;
393
                }
394
            }
395
        }
396
    }
397
    $res .= "$ruuid\n" if ($ruuid);
398
    return $res;
399
}
400
401
sub do_uuidshow {
402
    if ($help) {
403
        return <<END
404
GET:uuid:
405
Simple action for showing a single network.
406
END
407
    }
408
409
    my $res;
410
    $res .= header('application/json') unless $console;
411
    my $u = $options{u};
412
    $u = $curuuid unless ($u || $u eq '0');
413
    if ($u || $u eq '0') {
414
        foreach my $uuid (keys %register) {
415
            if (($register{$uuid}->{'user'} eq $user || $register{$uuid}->{'user'} eq 'common' || index($privileges,"a")!=-1)
416
                && $uuid =~ /^$u/) {
417
                my %hash = %{$register{$uuid}};
418
                delete $hash{'action'};
419
                delete $hash{'nextid'};
420
#                my $dump = Dumper(\%hash);
421
                my $dump = to_json(\%hash, {pretty=>1});
422
                $dump =~ s/undef/"--"/g;
423
                $res .= $dump;
424
                last;
425
            }
426
        }
427
    }
428
    return $res;
429
}
430
431
sub do_updateui {
432
    my ($uuid, $action) = @_;
433
    if ($help) {
434
        return <<END
435
GET:uuid:
436
Update the web UI for the given uuid (if user has web UI loaded).
437
END
438
    }
439
440
    my $res;
441
    $res .= header('text/plain') unless $console;
442
    if ($register{$uuid}) {
443
        my $uistatus = $register{$uuid}->{'status'};
444
        $main::updateUI->({tab=>"networks", user=>$user, uuid=>$uuid, status=>$uistatus});
445
        $res .= "Status=OK Updated UI for $register{$uuid}->{'type'} $register{$uuid}->{'name'}: $uistatus";
446
    } else {
447
        $main::updateUI->({tab=>"networks", user=>$user});
448
        $res .= "Status=OK Updated networks UI for $user";
449
    }
450
    return $res;
451
452
}
453
454 eb31fb38 hq
sub do_dnslist {
455 95b003ff Origo
    my ($uuid, $action) = @_;
456
    if ($help) {
457
        return <<END
458 eb31fb38 hq
GET:domain:
459
Lists entries in [domain] or if not specified, the default zone: $dnsdomain.
460 95b003ff Origo
END
461
    }
462
463 eb31fb38 hq
    my $res = $main::dnsList->($engineid, $user, $params{'domain'});
464 95b003ff Origo
    return $res;
465
}
466
467 705b5366 hq
sub do_envdump {
468
    my ($uuid, $action) = @_;
469
    if ($help) {
470
        return <<END
471
GET::
472
Dump environment variables
473
END
474
    }
475
    return to_json(\%ENV, {pretty=>1});
476
}
477
478
479 eb31fb38 hq
sub do_dnscreate {
480 48fcda6b Origo
    my ($uuid, $action) = @_;
481
    if ($help) {
482
        return <<END
483 eb31fb38 hq
GET:name, value, type:
484
Create a DNS record in the the subdomain belonging to the user's default DNS domain.
485
<b>name</b> is a domain name in the Engine's zone. <b>value</b> is either an IP address for A records or a domain name for other. <b>[type]</b> is A, CNAME, TXT or MX.
486 48fcda6b Origo
END
487
    }
488
489 eb31fb38 hq
    my $res = $main::dnsCreate->($engineid, $params{'name'}, $params{'value'}, $params{'type'}, $user);
490 48fcda6b Origo
    return $res;
491
}
492
493 eb31fb38 hq
sub do_dnsupdate {
494
    my ($uuid, $action, $obj) = @_;
495 e9af6c24 Origo
    if ($help) {
496
        return <<END
497 eb31fb38 hq
GET:name,value,type,oldname,oldvalue:
498
Updates CNAME records pointing to a A record with value 'value', to point to the new 'name' in the the default DNS domain.
499 e9af6c24 Origo
END
500
    }
501
502 eb31fb38 hq
    my $res = $main::dnsUpdate->($engineid, $obj->{'name'}, $obj->{'value'}, $obj->{'type'}, $obj->{'oldname'}, $obj->{'oldvalue'}, $user);
503 e9af6c24 Origo
    return $res;
504
}
505
506
sub do_dnsclean {
507
    my ($uuid, $action) = @_;
508
    if ($help) {
509
        return <<END
510
GET::
511
Remove this engines entries in $dnsdomain zone.
512
END
513
    }
514
515
    my $res;
516
    $res .= header('text/plain') unless $console;
517
    $res .= $main::dnsClean->($engineid, $user);
518
    return $res;
519
}
520
521 95b003ff Origo
sub do_dnscheck {
522
    my ($uuid, $action) = @_;
523
    if ($help) {
524
        return <<END
525
GET:name:
526
Checks if a domain name (name[.subdomain]) is available, i.e. not registered,
527
where subdomain is the subdomain belonging to the the registering engine.
528
END
529
    }
530
531
    my $res;
532
    $res .= header('text/plain') unless $console;
533
    my $name = $params{'name'};
534
    $name = $1 if ($name =~ /(.+)\.$dnsdomain$/);
535 48fcda6b Origo
    if (!$enginelinked) {
536
        $res .= "Status=ERROR You cannot create DNS records - your engine is not linked.\n";
537
    } elsif ($name =~ /^\S+$/ && !(`host $name.$dnsdomain authns1.cabocomm.dk` =~ /has address/)
538 95b003ff Origo
        && $name ne 'www'
539
        && $name ne 'mail'
540
        && $name ne 'info'
541
        && $name ne 'admin'
542
        && $name ne 'work'
543
        && $name ne 'io'
544
        && $name ne 'cloud'
545
        && $name ne 'compute'
546
        && $name ne 'sso'
547
        && $name !~ /valve/
548
    ) {
549
        $res .= "Status=OK $name.$dnsdomain is available\n";
550
    } else {
551
        $res .= "Status=ERROR $name.$dnsdomain is not available\n";
552
    }
553
    return $res;
554
}
555
556
sub do_dnsdelete {
557
    my ($uuid, $action) = @_;
558
    if ($help) {
559
        return <<END
560 ca937547 hq
GET:name, value, type:
561 95b003ff Origo
Delete a DNS record in the configured zone.
562
END
563
    }
564
565 ca937547 hq
    my $res = $main::dnsDelete->($engineid, $params{'name'}, $params{'value'}, $params{'type'}, $user);
566 95b003ff Origo
    return $res;
567
}
568
569
sub do_getappstoreurl {
570
    my ($uuid, $action) = @_;
571
    if ($help) {
572
        return <<END
573
GET::
574 45cc3024 hq
Get URL to the app store belonging to engine or user (uverrides engine default).
575 95b003ff Origo
END
576
    }
577
578
    my $res;
579
    # $res .= header('application/json') unless $console;
580
    # $res .= qq|{"url": "$appstoreurl"}\n|;
581
    $res .= "$appstoreurl\n";
582
    return $res;
583
}
584
585 eb31fb38 hq
sub do_listdnsdomains {
586
    my ($uuid, $action) = @_;
587
    if ($help) {
588
        return <<END
589
GET::
590
Get the DNS domains current user has access to.
591
END
592
    }
593
    unless ( tie(%userreg,'Tie::DBI', Hash::Merge::merge({table=>'users', key=>'username', CLOBBER=>1}, $Stabile::dbopts)) ) {return 0};
594
    my $billto = $userreg{$user}->{'billto'};
595
    my $bdomains = ($userreg{$billto})?$userreg{$billto}->{'dnsdomains'}:'';
596
    my $domains = ($enginelinked)?($userreg{$user}->{'dnsdomains'} || $bdomains || $dnsdomain) :'';
597
    untie %userreg;
598
    my @doms = split(/, ?/, $domains);
599
    my $subdomain = ($enginelinked)?substr($engineid, 0, 8):'';
600
    my $linked = ($enginelinked)?'true':'false';
601
    my $res;
602
    $res .= header('application/json') unless $console;
603
    $res .= qq|{"domains": | . to_json(\@doms) . qq|, "subdomain": "$subdomain", "enginelinked": "$linked", "billto": "$billto", "user": "$user"}|;
604
    return $res;
605
}
606
607 95b003ff Origo
sub do_getdnsdomain {
608
    my ($uuid, $action) = @_;
609
    if ($help) {
610
        return <<END
611
GET::
612 eb31fb38 hq
Get the default DNS domain and the subdomain this Engine registers entries in.
613 95b003ff Origo
END
614
    }
615 e9af6c24 Origo
    my $domain = ($enginelinked)?$dnsdomain:'';
616
    my $subdomain = ($enginelinked)?substr($engineid, 0, 8):'';
617
    my $linked = ($enginelinked)?'true':'false';
618 95b003ff Origo
    my $res;
619 e9af6c24 Origo
    $res .= header('application/json') unless $console;
620
    $res .= qq|{"domain": "$domain", "subdomain": "$subdomain", "enginelinked": "$linked"}|;
621 95b003ff Origo
    return $res;
622
}
623
624
sub xmppsend {
625
    my ($uuid, $action) = @_;
626
    if ($help) {
627
        return <<END
628
GET:to, msg:
629
Send out an xmpp alert.
630
END
631
    }
632
    if ($help) {
633
        return <<END
634
Send out an xmpp alert
635
END
636
    }
637
638
    my $res;
639
    $res .= header('text/plain') unless $console;
640
    $res .= $main::xmppSend->($params{'to'}, $params{'msg'}, $engineid);
641
    return $res;
642
}
643
644
# List available network types. Possibly limited by exhausted IP ranges.
645
sub do_listnetworktypes {
646
    if ($help) {
647
        return <<END
648
GET::
649
List available network types. Possibly limited by exhausted IP ranges.
650
END
651
    }
652
653
    my $res;
654
    $res .= header('application/json') unless $console;
655
    # Check if we have exhausted our IP ranges
656
    my $intipavail = getNextInternalIP();
657
    my $extipavail = getNextExternalIP();
658
    my $arpipavail = getNextExternalIP('','',1);
659
    my $json_text;
660
    $json_text .= '{"type": "gateway", "name": "Gateway"}, ';
661
    $json_text .= '{"type": "internalip", "name": "Internal IP"}, ' if ($intipavail);
662
    unless (overQuotas()) {
663
        $json_text .= '{"type": "ipmapping", "name": "IP mapping"}, ' if ($intipavail && $extipavail);
664
        $json_text .= '{"type": "externalip", "name": "External IP"}, 'if ($arpipavail);
665
    }
666
    $json_text = substr($json_text,0,-2);
667
    $res .= '{"identifier": "type", "label": "name", "items": [' . $json_text  . ']}';
668
    return $res;
669
}
670
671
# Simple action for removing all networks belonging to a user
672
sub do_removeusernetworks {
673
    my ($uuid, $action) = @_;
674
675
    if ($help) {
676
        return <<END
677
GET::
678
Remove all networks belonging to a user.
679
END
680
    }
681
682
    my $res;
683
    $res .= header('text/plain') unless $console;
684
    if ($readonly) {
685
        $postreply .= "Status=ERROR Not allowed\n";
686
    } else {
687
        Removeusernetworks($user);
688
    }
689
    $res .= $postreply || "Status=OK Nothing to remove\n";
690
    return $res;
691
}
692
693
# Activate all networks. If restoreall (e.g. after reboot) is called, we only activate networks which have entries in /etc/stabile/network
694
sub Activateall {
695
    my ($nouuid, $action) = @_;
696
    if ($help) {
697
        return <<END
698
GET::
699
Tries to activate all networks. If called as restoreall by an admin, will try to restore all user's networks to saved state, e.g. after a reboot.
700
END
701
    }
702
    my @regkeys;
703
    if (($action eq "restoreall" || $fulllist) && index($privileges,"a")!=-1) { # Only an administrator is allowed to do this
704
        @regkeys = keys %register;
705
    } else {
706
        @regkeys = (tied %register)->select_where("user='$user'");
707
    }
708
    my $i = 0;
709
    if (!$isreadonly) {
710
    	foreach my $key (@regkeys) {
711
            my $valref = $register{$key};
712
    		my $uuid = $valref->{'uuid'};
713
    		my $type = $valref->{'type'};
714
    		my $id = $valref->{'id'};
715
    		my $name = $valref->{'name'};
716
    		my $internalip = $valref->{'internalip'};
717
    		my $externalip = $valref->{'externalip'};
718
    		if ($id!=0 && $id!=1 && $id<4095) {
719
                my $caction = "nat";
720
    			if (-e "$etcpath/dhcp-hosts-$id") {
721
    				if ($action eq "restoreall" && $isadmin) { # If restoring, only activate previously active networks
722
                        my $hosts;
723
                        $hosts = lc `/bin/cat $etcpath/dhcp-hosts-$id` if (-e "$etcpath/dhcp-hosts-$id");
724
                        $caction = "activate" if ($hosts =~ /($internalip|$externalip)/);
725
    			    } elsif ($action eq "activateall") {
726
    				    $caction = "activate";
727
        			}
728 48fcda6b Origo
                    # TODO: investigate why this is necessary - if we don't do it, networks are not activated
729
                    $user = $valref->{'user'};
730
                    do_list($uuid, 'list');
731
732 95b003ff Origo
                    my $res = Activate($uuid, $caction);
733
                    if ($res =~ /\w+=(\w+) / ) {
734
                        $register{$uuid}->{'status'} = $1 unless (uc $1 eq 'ERROR');
735
                        $i ++ unless (uc $1 eq 'ERROR');
736
                    } else {
737
                        $postreply .= "Status=ERROR Cannot $caction $type $name $uuid: $res\n";
738
                    }
739
    		    }
740
            } else {
741
                $postreply .= "Status=ERROR Cannot $action $type $name\n" unless ($id==0 || $id==1);
742
        	}
743
        }
744
    } else {
745
        $postreply .= "Status=ERROR Problem activating all networks\n";
746
    }
747
    if ($postreply =~/Status=ERROR /) {
748
        $postreply = header('text/plain', '500 Internal Server Error') . $postreply unless $console;
749
    }
750
    $postreply .= "Status=OK activated $i networks\n";
751
    $main::updateUI->({tab=>"networks", user=>$user});
752
    updateBilling("$action $user");
753
    return $postreply;
754
}
755
756
# Deactivate all networks
757
sub Deactivateall {
758
    my ($nouuid, $action) = @_;
759
    if ($help) {
760
        return <<END
761
GET::
762
Tries to deactivate all networks. May also be called as natall or stopall.
763
END
764
    }
765
766
    my @regkeys;
767
    if ($fulllist && index($privileges,"a")!=-1) { # Only an administrator is allowed to do this
768
        @regkeys = keys %register;
769
    } else {
770
        @regkeys = (tied %register)->select_where("user='$user'");
771
    }
772
    if (!$isreadonly) {
773
		my %ids;
774
		foreach my $key (@regkeys) {
775
            my $valref = $register{$key};
776
			my $uuid = $valref->{'uuid'};
777
			my $type = $valref->{'type'};
778
			my $id = $valref->{'id'};
779
			my $name = $valref->{'name'};
780
			if ($id!=0 && $id!=1 && $id<4095) {
781
				if (-e "$etcpath/dhcp-hosts-$id") {
782
					my $caction = "deactivate";
783
					my $result;
784
					if ($action eq "stopall") {
785
						$caction = "stop";
786
						# Stop also deactivates all networks with same id, so only do this once for each id
787
						if ($ids{$id}) {
788
							$result = $valref->{'status'};
789
						} else {
790
							$result = Stop($id, $caction);
791
						}
792
						$ids{$id} = 1;
793
					} else {
794
                        my $res = Deactivate($uuid, $caction);
795
                        if ($res =~ /\w+=(\w+) /) {
796
                            $register{$uuid}->{'status'} = $1;
797
                        } else {
798
                            $postreply .= "Status=ERROR Cannot $caction $type $name $uuid: $res\n";
799
                        }
800
					}
801
					if ($result =~ /\w+=(.\w+) /) {
802
                        $register{$uuid}->{'status'} = $uistatus = $1;
803
						$uiuuid = $uuid;
804
						$postreply .= "Status=OK $caction $type $name $uuid\n";
805
						$main::syslogit->($user, "info", "$caction network $uuid ($id) ");
806
					}
807
				}
808
			} else {
809
				$postreply .= "Status=ERROR Cannot $action $type $name\n" unless ($id==0 || $id==1);
810
			}
811
		}
812
	} else {
813
		$postreply .= "Status=ERROR Problem deactivating all networks\n";
814
	}
815
    if ($postreply =~/Status=ERROR /) {
816
        $res = header('text/plain', '500 Internal Server Error') unless $console;
817
    } else {
818
        $res = header('text/plain') unless $console;
819
    }
820
	$main::updateUI->({tab=>"networks", user=>$user});
821
	updateBilling("$action $user");
822
	return $postreply;
823
}
824
825
sub do_updatebilling {
826
    my ($uuid, $action) = @_;
827
    if ($help) {
828
        return <<END
829
GET:uuid:
830
Update network billing for current user.
831
END
832
    }
833
834
    my $res;
835
    $res .= header('text/plain') unless $console;
836
    if ($isreadonly) {
837
        $res .= "Status=ERROR Not updating network billing for $user\n";
838
    } else {
839
        updateBilling("updatebilling $user");
840
        $res .= "Status=OK Updated network billing for $user\n";
841
    }
842
    return $res;
843
}
844
845
# Print list of available actions on objects
846
sub do_plainhelp {
847
    my $res;
848
    $res .= header('text/plain') unless $console;
849
    $res .= <<END
850
* new [type="ipmapping|internalip|externalip|gateway", name="name"]: Creates a new network
851
* activate: Activates a network. If gateway is down it is brought up.
852
* stop: Stops the gateway, effectively stopping network communcation with the outside.
853
* deactivate: Deactivates a network. Removes the associated internal IP address from the DHCP service.
854
* delete: Deletes a network. Use with care. Network can not be in use.
855
856
END
857
;
858
}
859
860
sub addDHCPAddress {
861
    my $id = shift;
862
    my $uuid = shift;
863
    my $dhcpip = shift;
864
    my $gateway = shift;
865
    my $mac = lc shift;
866
    my $isexternal = !($dhcpip =~ /^10\./);
867
    my $options;
868
    my $interface = "br$id"; #,$extnic.$id
869
    $options = "--strict-order --bind-interfaces --except-interface=lo --interface=$interface " .
870
    ($proxyip?"--dhcp-range=tag:external,$proxyip,static ":"") .
871
    "--pid-file=/var/run/stabile-$id.pid --dhcp-hostsfile=$etcpath/dhcp-hosts-$id --dhcp-range=tag:internal,$gateway,static " .
872
    "--dhcp-optsfile=$etcpath/dhcp-options-$id --port=0 --log-dhcp";
873
874
    my $running;
875
    my $error;
876
    my $psid;
877
    return "Status=ERROR Empty mac or ip when configuing dhcp for $name" unless ($mac && $dhcpip);
878
879
    eval {
880
        $psid = `/bin/cat /var/run/stabile-$id.pid` if (-e "/var/run/stabile-$id.pid");
881
        chomp $psid;
882
        $running = -e "/proc/$psid" if ($psid);
883
        # `/bin/ps p $psid` =~ /$psid/
884
        # `/bin/ps ax | /bin/grep stabile-$id.pid | /usr/bin/wc -l`; 1;} or do
885
        1;
886
    } or do {$error .= "Status=ERROR Problem configuring dhcp for $name $@\n";};
887
888
    if (-e "$etcpath/dhcp-hosts-$id") {
889
        open(TEMP1, "<$etcpath/dhcp-hosts-$id") || ($error .= "Status=ERROR Problem reading dhcp hosts\n");
890
        open(TEMP2, ">$etcpath/dhcp-hosts-$id.new") || ($error .= "Status=ERROR Problem writing dhcp hosts $etcpath/dhcp-hosts-$id.new\n");
891
        while (<TEMP1>) {
892
            my $line = $_;
893
            print TEMP2 $line unless (($mac && $line =~ /^$mac/i) || ($line & $line =~ /.+,$dhcpip/));
894
        }
895
        print TEMP2 "$mac," . (($isexternal)?"set:external,":"set:internal,") . "$dhcpip\n";
896
        close(TEMP1);
897
        close(TEMP2);
898
        rename("$etcpath/dhcp-hosts-$id", "$etcpath/dhcp-hosts-$id.old") || ($error .= "Status=ERROR Problem writing dhcp hosts\n");
899
        rename("$etcpath/dhcp-hosts-$id.new", "$etcpath/dhcp-hosts-$id") || ($error .= "Status=ERROR Problem writing dhcp hosts\n");
900
    } else {
901
        open(TEMP1, ">$etcpath/dhcp-hosts-$id") || ($error .= "Status=ERROR Problem writing dhcp options\n");
902
        print TEMP1 "$mac,$dhcpip\n";
903
        close (TEMP1);
904
    }
905
906
#    unless (-e "$etcpath/dhcp-options-$id") {
907
        my $block = new Net::Netmask("$proxygw/$proxysubnet");
908
        my $proxymask = $block->mask();
909
        open(TEMP1, ">$etcpath/dhcp-options-$id") || ($error .= "Status=ERROR Problem writing dhcp options\n");
910
911 e837d785 hq
# Turns out the VM's gateway has to be $proxyip and not $proxygw in our proxyarp setup
912 95b003ff Origo
        print TEMP1 <<END;
913 e837d785 hq
tag:external,option:router,$proxyip
914 95b003ff Origo
tag:external,option:netmask,$proxymask
915
tag:external,option:dns-server,$proxyip
916
tag:internal,option:router,$gateway
917
tag:internal,option:netmask,255.255.255.0
918
tag:internal,option:dns-server,$gateway
919
option:dns-server,1.1.1.1
920
END
921
922
        close (TEMP1);
923
#    }
924
925
    if ($running) {
926 48fcda6b Origo
        $main::syslogit->($user, 'info', "HUPing dnsmasq 1: $id");
927 95b003ff Origo
        eval {`/usr/bin/pkill -HUP -f "stabile-$id.pid"`; 1;} or do {$error .= "Status=ERROR Problem configuring dhcp for $name $@\n";};
928
    } else {
929
        eval {`/usr/sbin/dnsmasq $options`;1;} or do {$error .= "Status=ERROR Problem configuring dhcp for $name $@\n";};
930
    }
931 e5789be5 hq
    # Allow access to DHCP service
932
    `iptables -D INPUT -i br$id -p udp -m udp --dport 67 -j ACCEPT`;
933
    `iptables -I INPUT -i br$id -p udp -m udp --dport 67 -j ACCEPT`;
934
    # Allow access to DNS service
935
    `iptables -D INPUT -i br$id -p udp -m udp --dport 53 -j ACCEPT`;
936
    `iptables -I INPUT -i br$id -p udp -m udp --dport 53 -j ACCEPT`;
937
    `iptables -D INPUT -i br$id -p tcp -m tcp --dport 53 -j ACCEPT`;
938
    `iptables -I INPUT -i br$id -p tcp -m tcp --dport 53 -j ACCEPT`;
939 95b003ff Origo
940
    return $error?$error:"OK";
941
}
942
943
sub removeDHCPAddress {
944
    my $id = shift;
945
    my $uuid = shift;
946
    my $dhcpip = shift;
947
    my $mac;
948
    $mac = lc $domreg{$uuid}->{'nicmac1'} if ($domreg{$uuid});
949
    my $isexternal = ($dhcpip =~ /^10\./);
950
    my $running;
951
    my $error;
952
    my $psid;
953
    return "Status=ERROR Empty mac or ip when configuring dhcp for $name" unless ($mac || $dhcpip);
954
955
    eval {
956
        $psid = `/bin/cat /var/run/stabile-$id.pid` if (-e "/var/run/stabile-$id.pid");
957
        chomp $psid;
958
        $running = -e "/proc/$psid" if ($psid);
959
        1;
960 d3d1a2d4 Origo
    } or do {$error .= "Status=ERROR Problem deconfiguring dhcp for $name $@\n";};
961 95b003ff Origo
962
    my $keepup;
963
    if (-e "$etcpath/dhcp-hosts-$id") {
964
        open(TEMP1, "<$etcpath/dhcp-hosts-$id") || ($error .= "Status=ERROR Problem reading dhcp hosts\n");
965
        open(TEMP2, ">$etcpath/dhcp-hosts-$id.new") || ($error .= "Status=ERROR Problem writing dhcp hosts\n");
966
        while (<TEMP1>) {
967
            my $line = $_; chomp $line;
968
            if ($line && $line =~ /(.+),.+,($dhcpip)/) { # Release and remove this mac/ip from lease file
969 80e0b3f5 hq
                $main::syslogit->($user, 'info', "Releasing dhcp lease: br$id $dhcpip $1");
970
                `/usr/bin/dhcp_release br$id $dhcpip $1`;
971 95b003ff Origo
            } elsif ($mac && $line =~ /^$mac/i) {
972
                # If we find a stale assigment to the mac we are removing, remove this also
973 80e0b3f5 hq
                $main::syslogit->($user, 'info', "Releasing stale dhcp lease: br$id $dhcpip $mac");
974
                `/usr/bin/dhcp_release br$id $dhcpip $mac`;
975 95b003ff Origo
            } else {
976
                # Keep all other leases, and keep up the daemon if any leases found
977
                print TEMP2 "$line\n";
978
                $keepup = 1 if $line;
979
            }
980
        }
981
        close(TEMP1);
982
        close(TEMP2);
983
        rename("$etcpath/dhcp-hosts-$id", "$etcpath/dhcp-hosts-$id.old") || ($error .= "Status=ERROR Problem writing dhcp hosts\n");
984
        rename("$etcpath/dhcp-hosts-$id.new", "$etcpath/dhcp-hosts-$id") || ($error .= "Status=ERROR Problem writing dhcp hosts\n");
985
    }
986
987
    if ($keepup) {
988
        if ($running) {
989 48fcda6b Origo
            $main::syslogit->($user, 'info', "HUPing dnsmasq 2: $id");
990 95b003ff Origo
            eval {`/usr/bin/pkill -HUP -f "stabile-$id.pid"`; 1;} or do {$error .= "Status=ERROR Problem configuring dhcp for $name $@\n";};
991
        }
992
    } else {
993
        unlink "$etcpath/dhcp-options-$id" if (-e "$etcpath/dhcp-options-$id");
994
        if ($running) {
995 e5789be5 hq
            # Disallow access to DHCP service
996
            `iptables -D INPUT -i br$id -p udp -m udp --dport 67 -j ACCEPT`;
997
            # Disallow access to DNS service
998
            `iptables -D INPUT -i br$id -p udp -m udp --dport 53 -j ACCEPT`;
999
            `iptables -D INPUT -i br$id -p tcp -m tcp --dport 53 -j ACCEPT`;
1000 95b003ff Origo
            # Take down dhcp server
1001
            $main::syslogit->($user, 'info', "Killing dnsmasq 3: $id");
1002
            eval {`/usr/bin/pkill -f "stabile-$id.pid"`; 1;} or do {$error .= "Status=ERROR Problem configuring dhcp for $name $@\n";};
1003
        }
1004
    }
1005
1006
    return $error?$error:"OK";
1007
}
1008
1009
# Helper function
1010
sub save {
1011
    my ($id, $uuid, $name, $status, $type, $internalip, $externalip, $ports, $buildsystem, $username) = @_;
1012
    my $obj = {
1013
        id => $id,
1014
        uuid => $uuid,
1015
        name => $name,
1016
        status => $status,
1017
        type => $type,
1018
        internalip => $internalip,
1019
        externalip => $externalip,
1020
        ports => $ports,
1021
        buildsystem => $buildsystem,
1022
        username => $username
1023
    };
1024
    return Save($uuid, 'save', $obj);
1025
}
1026
1027
sub Save {
1028
    my ($uuid, $action, $obj) = @_;
1029
    if ($help) {
1030
        return <<END
1031 d3d1a2d4 Origo
POST:uuid, id, name, internalip, externalip, ports, type, systems, activate:
1032 95b003ff Origo
To save a collection of networks you either PUT or POST a JSON array to the main endpoint with objects representing the networks with the changes you want.
1033
Depending on your privileges not all changes are permitted. If you save without specifying a uuid, a new network is created.
1034 d3d1a2d4 Origo
For now, [activate] only has effect when creating a new connection with a linked system/server.
1035 95b003ff Origo
END
1036
    }
1037 d3d1a2d4 Origo
    $uuid = $obj->{'uuid'} if ($obj->{'uuid'});
1038 04c16f26 hq
    my $regnet = $register{$uuid};
1039 95b003ff Origo
    my $id = $obj->{id};
1040
    my $name = $obj->{name};
1041
    my $status = $obj->{status};
1042 04c16f26 hq
    my $type = $obj->{type} || $regnet->{type};
1043 95b003ff Origo
    my $internalip = $obj->{internalip};
1044
    my $externalip = $obj->{externalip};
1045
    my $ports = $obj->{ports};
1046
    my $buildsystem = $obj->{buildsystem};
1047
    my $username = $obj->{username};
1048 d3d1a2d4 Origo
    my $systems = $obj->{systems}; # Optionally link this network to a system
1049 95b003ff Origo
1050
    $postreply = "" if ($buildsystem);
1051
	$username = $user unless ($username);
1052
1053
    $status = $regnet->{'status'} || $status; # Trust db status if it exists
1054
    if ((!$uuid && $uuid ne '0') && $status eq 'new') {
1055
        my $ug = new Data::UUID;
1056
        $uuid = $ug->create_str();
1057
    };
1058
    if ($status eq 'new') {
1059
        $name  = 'New Connection' unless ($name);
1060
    }
1061
    unless ($uuid && length $uuid == 36) {
1062
        $postreply .= "Status=Error Invalid uuid $uuid\n";
1063
        return $postreply;
1064
    }
1065 d3d1a2d4 Origo
    my $systemnames = $regnet->{'systemnames'};
1066 95b003ff Origo
1067
    my $dbid = 0+$regnet->{'id'};
1068
    if ($status eq 'new' || !$dbid) {
1069
        $id = getNextId($id) ;
1070
    } else {
1071
        $id = $dbid;
1072
    }
1073
    if ($id > 4095 || $id < 0 || ($id==0 && $uuid!=0 && $isadmin) || ($id==1 && $uuid!=1 && $isadmin)) {
1074
        $postreply .= "Status=ERROR Invalid network id $id\n";
1075
        return $postreply;
1076
    }
1077
    $name = $name || $regnet->{'name'};
1078
    $internalip = $internalip || $regnet->{'internalip'};
1079
    if (!($internalip =~ /\d+\.\d+\.\d+\.\d+/)) {$internalip = ''};
1080
    $externalip = $externalip || $regnet->{'externalip'};
1081
    $ports = $ports || $regnet->{'ports'};
1082
    my $reguser = $regnet->{'user'};
1083
    # Sanity checks
1084
    if (
1085
        ($name && length $name > 255)
1086
        || ($ports && length $ports > 255)
1087
        || ($type && !($type =~ /gateway|ipmapping|internalip|externalip/))
1088
    ) {
1089
        $postreply .= "Stroke=ERROR Bad data: $name, $ports, $type\n";
1090
        return $postreply;
1091
    }
1092
    # Security check
1093
    if (($reguser && $username ne $reguser && !$isadmin ) ||
1094
        ($reguser && $status eq "new"))
1095
    {
1096
        $postreply .= "Status=Error Bad user: $username ($status)\n";
1097
        return $postreply;
1098
    }
1099
1100
    my $hit = 0;
1101
# Check if user is allowed to use network
1102
    my @regvalues = values %register;
1103
    foreach my $val (@regvalues) {
1104
        $dbid = $val->{"id"};
1105
        $dbuser = $val->{"user"};
1106
        if ($dbid == $id && $username ne $dbuser && $dbuser ne "common") {
1107
            $hit = 1;
1108
            last;
1109
        }
1110
    }
1111
    if ($hit && !$isadmin) { # Network is nogo (unless you are an admin)
1112
        $postreply .= "Status=ERROR Network id $id not available\n";
1113
        return $postreply;
1114
    } elsif (!$type) {
1115
        $postreply .= "Status=ERROR Network must have a type\n";
1116
        return $postreply;
1117
    } elsif ($status eq 'down' || $status eq 'new' || $status eq 'nat') {
1118
        # Check if network has been modified or is new
1119
        if ($regnet->{'id'} ne $id ||
1120
            $regnet->{'name'} ne $name ||
1121
            $regnet->{'type'} ne $type ||
1122
            $regnet->{'internalip'} ne $internalip ||
1123
            $regnet->{'externalip'} ne $externalip ||
1124 d3d1a2d4 Origo
            $regnet->{'systems'} ne $systems ||
1125 95b003ff Origo
            $regnet->{'ports'} ne $ports)
1126
        {
1127
            if ($type eq "externalip") {
1128
                $internalip = "--";
1129
                $externalip = getNextExternalIP($externalip, $uuid, 1);
1130
                if (!$externalip) {
1131
                    $postreply .= "Status=ERROR Unable to allocate external proxy IP for $name\n";
1132
                    $externalip = "--";
1133
                    $internalip = getNextInternalIP($internalip, $uuid, $id);
1134
                    $type = "internalip";
1135
                } else {
1136
                    $postreply .= "Status=OK Allocated external IP: $externalip\n" unless ($regnet->{'externalip'} eq $externalip);
1137
                    if ($dodns) {
1138 e9af6c24 Origo
                        $main::dnsCreate->($engineid, $externalip, $externalip, 'A', $user);
1139 95b003ff Origo
                    }
1140
                }
1141
1142
            } elsif ($type eq "ipmapping") {
1143
                $externalip = getNextExternalIP($externalip, $uuid);
1144
                if (!$externalip) {
1145
                    $postreply .= "Status=ERROR Unable to allocate external IP for $name\n";
1146
                    $externalip = "--";
1147
                    $type = "internalip";
1148
                } else {
1149
                    $postreply .= "Status=OK Allocated external IP: $externalip\n" unless ($regnet->{'externalip'} eq $externalip);
1150
                    if ($dodns) {
1151 eb31fb38 hq
                        $postreply .= "Status=OK Trying to register DNS ";
1152
                        $main::dnsCreate->($engineid, $externalip, $externalip, 'A', $user);
1153 95b003ff Origo
                    }
1154
                }
1155
                $internalip = getNextInternalIP($internalip, $uuid, $id);
1156
                if (!$internalip) {
1157
                    $postreply .= "Status=ERROR Unable to allocate internal IP for $name\n";
1158
                    $internalip = "--";
1159
                    $type = "gateway";
1160
                } else {
1161
                    $postreply .= "Status=OK Allocated internal IP: $internalip for $name\n" unless ($regnet->{'internalip'} eq $internalip);
1162
                }
1163
1164
            } elsif ($type eq "internalip") {
1165
                $externalip = "--";
1166
                $ports = "--";
1167
                my $ointip = $internalip;
1168
                $internalip = getNextInternalIP($internalip, $uuid, $id);
1169
                if (!$internalip) {
1170
                    $postreply .= "Status=ERROR Unable to allocate internal IP $internalip ($id, $uuid, $ointip) for $name\n";
1171
                    $internalip = "--";
1172
                    $type = "gateway";
1173
                } else {
1174 d3d1a2d4 Origo
                    $postreply .= "Status=OK Allocated internal IP: $internalip for $name\n" unless ($regnet->{'internalip'} eq $internalip);
1175 95b003ff Origo
                }
1176
1177
            } elsif ($type eq "gateway") {
1178
            #    $internalip = "--";
1179
            #    $externalip = "--";
1180
            #    $ports = "--";
1181
            } else {
1182
                $postreply .= "Status=ERROR Network must have a valid type\n";
1183
                return $postreply;
1184
            }
1185
            # Validate ports
1186
            my @portslist = split(/, ?| /, $ports);
1187
            if ($ports ne "--") {
1188
                foreach my $port (@portslist) {
1189
                    my $p = $port; # Make a copy of var
1190
                    if ($p =~ /(\d+\.\d+\.\d+\.\d+):(\d+)/) {
1191
                        $p = $2;
1192
                    };
1193
                    $p = 0 unless ($p =~ /\d+/);
1194
                    if ($p<1 || $p>65535) {
1195
                        $postreply .= "Status=ERROR Invalid port mapping for $name\n";
1196
                        $ports = "--";
1197
                        last;
1198
                    }
1199
                }
1200
            }
1201
            if ($ports ne "--") {
1202
                $ports = join(',', @portslist);
1203
            }
1204 d3d1a2d4 Origo
            if ($systems ne $regnet->{'systems'}) {
1205
                my $regsystems = $regnet->{'systems'};
1206
                unless (tie(%sysreg,'Tie::DBI', Hash::Merge::merge({table=>'systems'}, $Stabile::dbopts)) ) {$res .= qq|{"status": "Error": "message": "Unable to access systems register"}|; return $res;};
1207
1208
                # Remove existing link to system
1209
                if ($sysreg{$regsystems}) {
1210
                    $sysreg{$regsystems}->{'networkuuids'} =~ s/$uuid,? ?//;
1211
                    $sysreg{$regsystems}->{'networknames'} = s/$regnet->{'name'},? ?//;
1212
                } elsif ($domreg{$regsystems}) {
1213
                    $domreg{$regsystems}->{'networkuuids'} =~ s/$uuid,? ?//;
1214
                    $domreg{$regsystems}->{'networknames'} = s/$regnet->{'name'},? ?//;
1215
                }
1216
                if ($systems) {
1217
                    if ($sysreg{$systems}) { # Add new link to system
1218
                        $sysreg{$systems}->{'networkuuids'} .= (($sysreg{$systems}->{'networkuuids'}) ? ',' : '') . $uuid;
1219
                        $sysreg{$systems}->{'networknames'} .= (($sysreg{$systems}->{'networknames'}) ? ',' : '') . $name;
1220
                        $systemnames = $sysreg{$systems}->{'name'};
1221
                    } elsif ($domreg{$systems}) {
1222
                        $domreg{$systems}->{'networkuuids'} .= (($domreg{$systems}->{'networkuuids'}) ? ',' : '') . $uuid;
1223
                        $domreg{$systems}->{'networknames'} .= (($domreg{$systems}->{'networknames'}) ? ',' : '') . $name;
1224
                        $systemnames = $domreg{$systems}->{'name'};
1225
                    } else {
1226
                        $systems = '';
1227
                    }
1228
                }
1229
                tied(%sysreg)->commit;
1230
                untie(%sysreg);
1231
            }
1232 95b003ff Origo
            $register{$uuid} = {
1233
                uuid=>$uuid,
1234
                user=>$username,
1235
                id=>$id,
1236
                name=>$name,
1237
                internalip=>$internalip,
1238
                externalip=>$externalip,
1239
                ports=>$ports,
1240
                type=>$type,
1241 d3d1a2d4 Origo
                systems=>$systems,
1242
                systemnames=>$systemnames,
1243 95b003ff Origo
                action=>""
1244
            };
1245 6fdc8676 hq
            my $res = tied(%register)->commit;
1246
            my $obj = $register{$uuid};
1247 95b003ff Origo
            $postreply .= "Status=OK Network $register{$uuid}->{'name'} saved: $uuid\n";
1248
            $postreply .= "Status=OK uuid: $uuid\n" if ($console && $status eq 'new');
1249
            if ($status eq 'new') {
1250
                validateStatus($register{$uuid});
1251 d3d1a2d4 Origo
                $postmsg = "Created connection $name";
1252
                $uiupdatetype = "update";
1253 95b003ff Origo
            }
1254
            updateBilling("allocate $externalip") if (($type eq "ipmapping" || $type eq "externalip") && $externalip && $externalip ne "--");
1255
1256
        } else {
1257
        	$postreply = "Status=OK Network $uuid ($id) unchanged\n";
1258
        }
1259
1260
        if ($params{'PUTDATA'}) {
1261
            my %jitem = %{$register{$uuid}};
1262
            my $json_text = to_json(\%jitem);
1263
            $json_text =~ s/null/"--"/g;
1264
            $json_text =~ s/""/"--"/g;
1265
            $postreply = $json_text;
1266 d3d1a2d4 Origo
            $postmsg = $postmsg || "OK, updated network $name";
1267 95b003ff Origo
        }
1268
1269
        return $postreply;
1270
1271
    } else {
1272
        if ($id ne $regnet->{'id'} ||
1273
        $internalip ne $regnet->{'internalip'} || $externalip ne $regnet->{'externalip'}) {
1274
            return "Status=ERROR Cannot modify active network: $uuid\n";
1275
        } elsif ($name ne $regnet->{'name'}) {
1276
            $register{$uuid}->{'name'} = $name;
1277
            $postreply .= "Status=OK Network \"$register{$uuid}->{'name'}\" saved: $uuid\n";
1278
            if ($params{'PUTDATA'}) {
1279
                my %jitem = %{$register{$uuid}};
1280
                my $json_text = to_json(\%jitem);
1281
                $json_text =~ s/null/"--"/g;
1282
                $postreply = $json_text;
1283 d3d1a2d4 Origo
                $postmsg = "OK, updated network $name";
1284 95b003ff Origo
            }
1285
        } else {
1286
            $postreply .= "Status=OK Nothing to save\n";
1287
            if ($params{'PUTDATA'}) {
1288
                my %jitem = %{$register{$uuid}};
1289
                my $json_text = to_json(\%jitem);
1290
                $json_text =~ s/null/"--"/g;
1291
                $postreply = $json_text;
1292
            }
1293
        }
1294
    }
1295
1296
}
1297
1298
sub Activate {
1299 d3d1a2d4 Origo
    my ($uuid, $action, $obj) = @_;
1300 95b003ff Origo
    if ($help) {
1301
        return <<END
1302
GET:uuid:
1303
Activate a network which must be in status down or nat.
1304
END
1305
    }
1306 d3d1a2d4 Origo
    $uuid = $obj->{'uuid'} if ($obj->{'uuid'});
1307 95b003ff Origo
    $action = 'activate' || $action;
1308 d3d1a2d4 Origo
    my $regnet = $register{$uuid};
1309
    my $id = $regnet->{'id'};
1310
    my $name = $regnet->{'name'};
1311
    my $type = $regnet->{'type'};
1312
    my $status = $regnet->{'status'};
1313
    my $domains = $regnet->{'domains'};
1314
    my $systems = $regnet->{'systems'};
1315
    my $internalip = $regnet->{'internalip'};
1316
    my $externalip = $regnet->{'externalip'};
1317
    my $ports = $regnet->{'ports'};
1318 95b003ff Origo
    my $idleft = ($id>99)?(substr $id,0,-2)+0 : 0;
1319
    my $idright = (substr $id,-2) + 0;
1320
    my $interfaces = `/sbin/ifconfig`;
1321
    my $dom = $domreg{$domains};
1322
    my $nicindex = ($dom->{'networkuuid1'} eq $uuid)?1:
1323
            ($dom->{'networkuuid2'} eq $uuid)?2:
1324
            ($dom->{'networkuuid3'} eq $uuid)?3:
1325
            0;
1326
    my $nicmac = $dom->{"nicmac$nicindex"};
1327
    my $e;
1328
1329
	if (!$id || $id==0 || $id==1 || $id>4095) {
1330
        $postreply .= "Status=ERROR Invalid ID activating $type\n";
1331
	    return $postreply;
1332
	} elsif (overQuotas()) { # Enforce quotas
1333
        $postreply .= "Status=ERROR Over quota activating $type " . overQuotas() . "\n";
1334
        return $postreply;
1335
    } elsif (($status ne 'down' && $status ne 'nat')) {
1336
        $postreply .= "Status=ERROR Cannot activate $type $name (current status is: $status)\n";
1337
        return $postreply;
1338
    }
1339
1340
    # Check if vlan with $id is created and doing nat, if not create it and create the gateway
1341
    unless (-e "/proc/net/vlan/$datanic.$id") {
1342
        eval {`/sbin/vconfig add $datanic $id`;} or do {$e=1; $postreply .= "Status=ERROR Problem adding vlan $datanic.$id $@\n"; return $postreply;};
1343
        eval {`/sbin/ifconfig $datanic.$id up`;}# or do {$e=1; $postreply .= "Status=ERROR Problem activating vlan $datanic.$id $@\n"; return $postreply;};
1344
    }
1345
#    if (!($interfaces =~ m/$datanic\.$id /)) {
1346
    if (!($interfaces =~ m/br$id /)) {
1347
        # check if gw is created locally
1348
        unless (`arping -C1 -c2 -D -I $datanic.$id 10.$idleft.$idright.1` =~ /reply from/) { # check if gw is created on another engine
1349
            # Create gw
1350
#            eval {`/sbin/ifconfig $datanic.$id 10.$idleft.$idright.1 netmask 255.255.255.0 broadcast 10.$idleft.$idright.255 up`; 1;} or do {
1351
#                $e=1; $postreply .= "Status=ERROR $@\n"; return $postreply;
1352
            #            };
1353
            # To support local instances on valve, gw is now created as a bridge
1354
            eval {`/sbin/brctl addbr br$id`; 1;} or do {$e=1; $postreply .= "Status=ERROR $@\n"; return $postreply; };
1355
            eval {`/sbin/brctl addif br$id $datanic.$id`; 1;} or do {$e=1; $postreply .= "Status=ERROR $@\n"; return $postreply; };
1356
            eval {`/sbin/ifconfig br$id 10.$idleft.$idright.1/24 up`; 1;} or do {
1357
                $e=1; $postreply .= "Status=ERROR $@\n"; return $postreply; }
1358
        } else {
1359
            $postreply .= "Status=OK GW is active on another Engine, assuming this is OK\n";
1360
        }
1361
    }
1362
    my $astatus = "nat" unless ($e);
1363
    `/usr/bin/touch $etcpath/dhcp-hosts-$id` unless (-e "$etcpath/dhcp-hosts-$id");
1364 d3d1a2d4 Origo
    if ($action eq "activate") { #} && $domains) {
1365 95b003ff Origo
        if ($type eq "internalip" || $type eq "ipmapping") {
1366 d3d1a2d4 Origo
            # Configure internal dhcp server
1367
            if ($domains) {
1368
                my $result = addDHCPAddress($id, $domains, $internalip, "10.$idleft.$idright.1", $nicmac);
1369
                if ($result eq "OK") {
1370
                    $astatus = "up" if ($type eq "internalip");
1371
                } else {
1372
                    $e = 1;
1373
                    $postreply .= "$result\n";
1374
                }
1375 95b003ff Origo
            }
1376
1377
            # Also export storage pools to user's network
1378
            my @spl = split(/,\s*/, $storagepools);
1379
            my $reloadnfs;
1380
            my $uid = `id -u irigo-$user`; chomp $uid;
1381
            $uid = `id -u nobody` unless ($uid =~ /\d+/); chomp $uid;
1382
            my $gid = `id -g irigo-$user`; chomp $gid;
1383
            $gid = `id -g nobody` unless ($gid =~ /\d+/); chomp $gid;
1384
1385
            # We are dealing with multiple upstream routes - configure local routing
1386
            if ($proxynic && $proxynic ne $extnic) {
1387
                if (-e "/etc/iproute2/rt_tables" && !grep(/1 proxyarp/, `cat /etc/iproute2/rt_tables`)) {
1388
                    `/bin/echo "1 proxyarp" >> /etc/iproute2/rt_tables`;
1389
                }
1390
                if (!grep(/$datanic\.$id/, `/sbin/ip route show table proxyarp`)) {
1391
                    `/sbin/ip route add "10.$idleft.$idright.0/24" dev $datanic.$id table proxyarp`;
1392
                }
1393
            }
1394
1395 d24d9a01 hq
            # Manuipulate NFS exports and related disk quotas
1396 95b003ff Origo
            foreach my $p (@spl) {
1397
                if ($tenderlist[$p] && $tenderpathslist[$p]) {
1398
                    my $fuelpath = $tenderpathslist[$p] . "/$user/fuel";
1399
                    unless (-e $fuelpath) {
1400 1a56bdde Origo
                        if ($tenderlist[$p] eq 'local') { # We only support fuel on local tender for now
1401
                            `mkdir "$fuelpath"`;
1402
                            `chmod 777 "$fuelpath"`;
1403
                        }
1404 95b003ff Origo
                    }
1405
                    if ($tenderlist[$p] eq "local") {
1406
                        `chown irigo-$user:irigo-$user "$fuelpath"`;
1407
                        my $mpoint = `df -P "$fuelpath" | tail -1 | cut -d' ' -f 1`;
1408
                        chomp $mpoint;
1409
                        my $storagequota = $Stabile::userstoragequota;
1410
                        if (!$storagequota) {
1411
                            $storagequota = $Stabile::config->get('STORAGE_QUOTA');
1412
                        }
1413
                        my $nfsquota = $storagequota * 1024 ; # quota is in MB
1414
                        $nfsquota = 0 if ($nfsquota < 0); # quota of -1 means no limit
1415 d24d9a01 hq
                        `setquota -u irigo-$user $nfsquota $nfsquota 0 0 "$mpoint"` if (-e "$mntpoint");
1416
                        if (!(`grep "$fuelpath 10\.$idleft\.$idright" /etc/exports`) && -e $fuelpath) {
1417 95b003ff Origo
                            `echo "$fuelpath 10.$idleft.$idright.0/255.255.255.0(sync,no_subtree_check,all_squash,rw,anonuid=$uid,anongid=$gid)" >> /etc/exports`;
1418
                            $reloadnfs = 1;
1419
                        }
1420
                    }
1421
                }
1422
            }
1423
            `/usr/sbin/exportfs -r` if ($reloadnfs); #Reexport nfs shares
1424
1425
        } elsif ($type eq "externalip") {
1426 d24d9a01 hq
            # A proxy is needed to route traffic, don't go any further if not configured
1427 95b003ff Origo
            if ($proxyip) {
1428 d24d9a01 hq
                # Set up proxy
1429 95b003ff Origo
                if (!($interfaces =~ m/$proxyip/ && $interfaces =~ m/br$id:proxy/)) {
1430
                    eval {`/sbin/ifconfig br$id:proxy $proxyip/$proxysubnet up`; 1;}
1431 e837d785 hq
                        or do {$e=1; $postreply .= "Status=ERROR Problem setting up proxy arp gw $proxyip on br$id:proxy $@\n";};
1432 95b003ff Origo
                    eval {`/sbin/ifconfig $proxynic:proxy $proxyip/$proxysubnet up`; 1;}
1433
                        or do {$e=1; $postreply .= "Status=ERROR Problem setting up proxy arp gw $proxynic $@\n";};
1434
                }
1435 d3d1a2d4 Origo
                my $result = "OK";
1436 d24d9a01 hq
                # Configure dhcp server
1437 d3d1a2d4 Origo
                if ($domains) {
1438
                    $result = addDHCPAddress($id, $domains, $externalip, "10.$idleft.$idright.1", $nicmac) if ($domains);
1439
                    if ($result eq "OK") {
1440
                        ;
1441
                    } else {
1442
                        $e = 1;
1443
                        $postreply .= "$result\n";
1444
                    }
1445 95b003ff Origo
                }
1446
            } else {
1447
                $postreply .= "Status=ERROR Cannot set up external IP without Proxy ARP gateway\n";
1448
            }
1449
        }
1450
1451 d24d9a01 hq
        # Handle routing with Iptables
1452
        if ($type eq "ipmapping" || $type eq "internalip") {
1453
            `iptables -I FORWARD -d $internalip -m state --state ESTABLISHED,RELATED -j RETURN`;
1454
        }
1455 95b003ff Origo
        # Check if external ip exists and routing configured, if not create and configure it
1456
        if ($type eq "ipmapping") {
1457 2a63870a Christian Orellana
            if ($internalip && $internalip ne "--" && $externalip && $externalip ne "--" && !($interfaces =~ m/$externalip /g)) { # the space is important
1458 64c667ea hq
                $externalip =~ /\d+\.\d+\.(\d+)\.(\d+)/;
1459
                my $ipend = "$1$2"; # Linux NIC names are limited to 15 chars - we will have to find a way to support long NIC names and bigger than /24 subnets
1460
                $ipend = $2 if (length("$extnic:$id-$ipend")>15);
1461 95b003ff Origo
                eval {`/sbin/ifconfig $extnic:$id-$ipend $externalip/$extsubnet up`; 1;}
1462 d3d1a2d4 Origo
                    or do {$e=1; $postreply .= "Status=ERROR Problem adding interface $extnic:$id-$ipend $@\n";};
1463 48fcda6b Origo
                unless (`ip addr show dev $extnic` =~ /$externalip/) {
1464
                    $e=10;
1465 d3d1a2d4 Origo
                    $postreply .= "Status=ERROR Problem adding interface $extnic:$id-$ipend\n";
1466 48fcda6b Origo
                }
1467 d24d9a01 hq
                # `/sbin/iptables -A POSTROUTING -t nat -s $internalip -j LOG --log-prefix "SNAT-POST"`;
1468
                # `/sbin/iptables -A INPUT -t nat -s $internalip -j LOG --log-prefix "SNAT-INPUT"`;
1469
                # `/sbin/iptables -A OUTPUT -t nat -s $internalip -j LOG --log-prefix "SNAT-OUTPUT"`;
1470
                # `/sbin/iptables -A PREROUTING -t nat -s $internalip -j LOG --log-prefix "SNAT-PRE"`;
1471 95b003ff Origo
                if ($ports && $ports ne "--") { # Port mapping is defined
1472
                    my @portslist = split(/, ?| /, $ports);
1473
                    foreach $port (@portslist) {
1474
                        my $ipfilter;
1475
                        if ($port =~ /(\d+)\.(\d+)\.(\d+)\.(\d+)(\/\d+)?:(\d+)/) {
1476
                            my $portip = "$1.$2.$3.$4$5";
1477
                            $port = $6;
1478
                            $ipfilter = "-s $portip";
1479
                        } else {
1480
                            $port = 0 unless ($port =~ /\d+/);
1481
                        }
1482
                        if ($port<1 || $port>65535) {
1483
                            $postreply .= "Status=ERROR Invalid port mapping for $name\n";
1484
                            $ports = "--";
1485
                            last;
1486
                        }
1487
                        if ($port>1 || $port<65535) {
1488 d24d9a01 hq
                            # DNAT externalip -> internalip
1489 95b003ff Origo
                            eval {`/sbin/iptables -A PREROUTING -t nat -p tcp $ipfilter -d $externalip --dport $port -j DNAT --to $internalip`; 1;}
1490 d24d9a01 hq
                               or do {$e=2; $postreply .= "Status=ERROR Problem setting up routing $@\n";};
1491 95b003ff Origo
                            eval {`/sbin/iptables -A PREROUTING -t nat -p udp $ipfilter -d $externalip --dport $port -j DNAT --to $internalip`; 1;}
1492 d24d9a01 hq
                               or do {$e=3; $postreply .= "Status=ERROR Problem setting up routing $@\n";};
1493 2a63870a Christian Orellana
                            # PREROUTING is not parsed for packets coming from local host...
1494
                            eval {`/sbin/iptables -A OUTPUT -t nat -p tcp $ipfilter -d $externalip --dport $port -j DNAT --to $internalip`; 1;}
1495
                                or do {$e=2; $postreply .= "Status=ERROR Problem setting up routing $@\n";};
1496
                            eval {`/sbin/iptables -A OUTPUT -t nat -p udp $ipfilter -d $externalip --dport $port -j DNAT --to $internalip`; 1;}
1497
                                or do {$e=3; $postreply .= "Status=ERROR Problem setting up routing $@\n";};
1498 d24d9a01 hq
                            # Allow access to ipmapped internal ip on $port
1499
                            `iptables -I FORWARD -d $internalip -p tcp --dport $port -j RETURN`;
1500
                            `iptables -I FORWARD -d $internalip -p udp --dport $port -j RETURN`;
1501 95b003ff Origo
                        }
1502
                    }
1503
                    eval {`/sbin/iptables -D INPUT -d $externalip -j DROP`; 1;} # Drop traffic to all other ports
1504 48fcda6b Origo
                        or do {$e=5; $postreply .= "Status=ERROR Problem setting up routing $@\n";};
1505 95b003ff Origo
                    eval {`/sbin/iptables -A INPUT -d $externalip -j DROP`; 1;} # Drop traffic to all other ports
1506 48fcda6b Origo
                        or do {$e=6; $postreply .= "Status=ERROR Problem setting up routing $@\n";};
1507 95b003ff Origo
                } else {
1508 d24d9a01 hq
                    # DNAT externalip -> internalip coming from outside , --in-interface $extnic
1509 95b003ff Origo
                    eval {`/sbin/iptables -A PREROUTING -t nat -d $externalip -j DNAT --to $internalip`; 1;}
1510 48fcda6b Origo
                        or do {$e=7; $postreply .= "Status=ERROR Problem setting up routing $@\n";};
1511 d24d9a01 hq
                    # PREROUTING is not parsed for packets coming from local host...
1512 2a63870a Christian Orellana
                    eval {`/sbin/iptables -A OUTPUT -t nat -d $externalip -j DNAT --to $internalip`; 1;}
1513
                        or do {$e=7; $postreply .= "Status=ERROR Problem setting up routing $@\n";};
1514 d24d9a01 hq
                    # Allow blanket access to ipmapped internal ip
1515
                    `iptables -I FORWARD -d $internalip -j RETURN`;
1516
                }
1517
                # We masquerade packets going to internalip from externalip to avoid confusion
1518
                #eval {`/sbin/iptables -A POSTROUTING -t nat --out-interface br$id -s $externalip -j MASQUERADE`; 1;}
1519
                #    or do {$e=3; $postreply .= "Status=ERROR Problem setting up routing $@\n";};
1520 6fdc8676 hq
1521
                # Masquerade packets from internal ip's not going to our own subnet
1522
                # `/sbin/iptables -D POSTROUTING -t nat --out-interface br$id ! -d 10.$idleft.$idright.0/24 -j MASQUERADE`;
1523
                #eval {`/sbin/iptables -A POSTROUTING -t nat --out-interface br$id ! -d 10.$idleft.$idright.0/24 -j MASQUERADE`; 1;}
1524
                #    or do {$e=3; $postreply .= "Status=ERROR Problem setting up routing $@\n";};
1525
1526 d24d9a01 hq
                # When receiving packet from client, if it's been routed, and outgoing interface is the external interface, SNAT.
1527
                unless ($Stabile::disablesnat) {
1528
                    eval {`/sbin/iptables -A POSTROUTING -t nat -s $internalip ! -d 10.$idleft.$idright.0/24 -j SNAT --to-source $externalip`; 1; }
1529
                        or do {$e=4; $postreply .= "Status=ERROR Problem setting up routing $@\n";};
1530
                #    eval {`/sbin/iptables -A POSTROUTING -t nat -s $internalip -j SNAT --to-source $externalip`; 1; }
1531
                #        or do {$e=4; $postreply .= "Status=ERROR Problem setting up routing $@\n";};
1532
                    eval {`/sbin/iptables -I INPUT -t nat -s $internalip ! -d 10.$idleft.$idright.0/24 -j SNAT --to-source $externalip`; 1; }
1533
                        or do {$e=4; $postreply .= "Status=ERROR Problem setting up routing $@\n";};
1534
                #    eval {`/sbin/iptables -I INPUT -t nat -s $internalip -j SNAT --to-source $externalip`; 1; }
1535
                #        or do {$e=4; $postreply .= "Status=ERROR Problem setting up routing $@\n";};
1536 95b003ff Origo
                }
1537 d24d9a01 hq
1538 48fcda6b Origo
                if ($e) {
1539
                    $main::syslogit->($user, 'info', "Problem $action network $uuid ($name, $id): $@");
1540
                } else {
1541
                    $astatus = "up"
1542
                }
1543 95b003ff Origo
            }
1544
        } elsif ($type eq "externalip") {
1545
            my $route = `/sbin/ip route`;
1546
            my $tables = `/sbin/iptables -L -n`;
1547
1548 d24d9a01 hq
            # Allow external IP send packets out
1549
            `/sbin/iptables -D FORWARD --in-interface br$id -s $externalip -j RETURN`;
1550
            `/sbin/iptables -I FORWARD --in-interface br$id -s $externalip -j RETURN`;
1551
1552 95b003ff Origo
            # We are dealing with multiple upstream routes - configure local routing
1553 e837d785 hq
            if ($proxynic && ($proxynic ne $extnic)) {
1554 95b003ff Origo
                if (-e "/etc/iproute2/rt_tables" && !grep(/1 proxyarp/, `cat /etc/iproute2/rt_tables`)) {
1555
                    `/bin/echo "1 proxyarp" >> /etc/iproute2/rt_tables`;
1556
                }
1557
                if (!grep(/$proxygw/, `/sbin/ip route show table proxyarp`)) {
1558 e837d785 hq
                    `/sbin/ip route del default dev $proxynic table proxyarp`; # delete first in case proxygw has changed
1559 95b003ff Origo
                    `/sbin/ip route add default via $proxygw dev $proxynic table proxyarp`;
1560
                }
1561
                if (!grep(/proxyarp/, `/sbin/ip rule show`)) {
1562
                    `/sbin/ip rule add to $proxygw/$proxysubnet table main`;
1563
                    `/sbin/ip rule add from $proxygw/$proxysubnet table proxyarp`;
1564
                }
1565
                my $proxyroute = `/sbin/ip route show table proxyarp`;
1566
#                `/sbin/ip route add $externalip/32 dev $datanic.$id:proxy src $proxyip table proxyarp` unless ($proxyroute =~ /$externalip/);
1567
                `/sbin/ip route add $externalip/32 dev br$id:proxy src $proxyip table proxyarp` unless ($proxyroute =~ /$externalip/);
1568
            }
1569
            eval {`/bin/echo 1 > /proc/sys/net/ipv4/conf/$datanic.$id/proxy_arp`; 1;}
1570
                or do {$e=1; $postreply .= "Status=ERROR Problem setting up proxy arp $@\n";};
1571
            eval {`/bin/echo 1 > /proc/sys/net/ipv4/conf/$proxynic/proxy_arp`; 1;}
1572
                or do {$e=1; $postreply .= "Status=ERROR Problem setting up proxy arp $@\n";};
1573
            eval {`/sbin/ip route add $externalip/32 dev br$id:proxy src $proxyip` unless ($route =~ /$externalip/); 1;}
1574
                or do {$e=1; $postreply .= "Status=ERROR Problem setting up proxy arp $@\n";};
1575
1576 d24d9a01 hq
            eval {`/sbin/iptables -D FORWARD -i $proxynic -d $externalip -m state --state ESTABLISHED,RELATED -j RETURN`; 1;}
1577 95b003ff Origo
                or do {$e=1; $postreply .= "Status=ERROR Problem setting up routing $@\n";};
1578 d24d9a01 hq
            eval {`/sbin/iptables -A FORWARD -i $proxynic -d $externalip -m state --state ESTABLISHED,RELATED -j RETURN`; 1;}
1579 95b003ff Origo
                or do {$e=1; $postreply .= "Status=ERROR Problem setting up routing $@\n";};
1580
1581
1582
            eval {`/sbin/iptables -D FORWARD -i $proxynic -d $externalip -j REJECT` if
1583
                ($tables =~ /REJECT .+ all .+ $externalip/); 1;}
1584
                or do {$e=1; $postreply .= "Status=ERROR Problem setting up routing $@\n";};
1585
1586
            if ($ports && $ports ne "--") {
1587
                my @portslist = split(/, ?| /, $ports);
1588
                foreach $port (@portslist) {
1589
                    my $ipfilter;
1590
                    if ($port =~ /(\d+)\.(\d+)\.(\d+)\.(\d+)(\/\d+)?:(\d+)/) {
1591
                        my $portip = "$1.$2.$3.$4$5";
1592
                        $port = $6;
1593
                        $ipfilter = "-s $portip";
1594
                    } else {
1595
                        $port = 0 unless ($port =~ /\d+/);
1596
                    }
1597
                    if ($port<1 || $port>65535) {
1598
                        $postreply .= "Status=ERROR Invalid port mapping for $name\n";
1599
                        $ports = "--";
1600
                        last;
1601
                    }
1602
1603
                    if ($port>1 && $port<65535 && $port!=67) { # Disallow setting up a dhcp server
1604 d24d9a01 hq
                        eval {`/sbin/iptables -A FORWARD -p tcp -i $proxynic $portfilter -d $externalip --dport $port -j RETURN`; 1;}
1605 95b003ff Origo
                            or do {$e=1; $postreply .= "Status=ERROR Problem setting up routing $@\n";};
1606 d24d9a01 hq
                        eval {`/sbin/iptables -A FORWARD -p udp -i $proxynic $portfilter -d $externalip --dport $port -j RETURN`; 1;}
1607 95b003ff Origo
                            or do {$e=1; $postreply .= "Status=ERROR Problem setting up routing $@\n";};
1608
                    }
1609
                }
1610 d24d9a01 hq
                eval {`/sbin/iptables -D FORWARD -i $proxynic -d $externalip -j REJECT`; 1;} # Drop traffic to all other ports
1611 95b003ff Origo
                    or do {$e=1; $postreply .= "Status=ERROR Problem setting up routing $@\n";};
1612 d24d9a01 hq
                eval {`/sbin/iptables -A FORWARD -i $proxynic -d $externalip -j REJECT`; 1;} # Drop traffic to all other ports
1613 95b003ff Origo
                    or do {$e=1; $postreply .= "Status=ERROR Problem setting up routing $@\n";};
1614
            } else {
1615 d24d9a01 hq
                # First allow everything else to this ip
1616
                eval {`/sbin/iptables -D FORWARD -i $proxynic -d $externalip -j RETURN`; 1;}
1617 95b003ff Origo
                    or do {$e=1; $postreply .= "Status=ERROR Problem setting up routing $@\n";};
1618 d24d9a01 hq
                eval {`/sbin/iptables -A FORWARD -i $proxynic -d $externalip -j RETURN`; 1;}
1619 95b003ff Origo
                    or do {$e=1; $postreply .= "Status=ERROR Problem setting up routing $@\n";};
1620 d24d9a01 hq
                # Then disallow setting up a dhcp server
1621
                eval {`/sbin/iptables -D FORWARD -p udp -i $proxynic -d $externalip --dport 67 -j REJECT`; 1;}
1622 95b003ff Origo
                    or do {$e=1; $postreply .= "Status=ERROR Problem setting up routing $@\n";};
1623 d24d9a01 hq
                eval {`/sbin/iptables -A FORWARD -p udp -i $proxynic -d $externalip --dport 67 -j REJECT`; 1;}
1624 95b003ff Origo
                    or do {$e=1; $postreply .= "Status=ERROR Problem setting up routing $@\n";};
1625
            }
1626
        }
1627
    }
1628
1629 d24d9a01 hq
    # Allow all inter-VLAN communication
1630
    `iptables -D FORWARD --in-interface br$id --out-interface br$id -j RETURN 2>/dev/null`;
1631
    `iptables -I FORWARD --in-interface br$id --out-interface br$id -j RETURN`;
1632
    # Disallow any access to vlan except mapped from external NIC i.e. ipmappings
1633
    `iptables -D FORWARD ! --in-interface $extnic --out-interface br$id -j DROP 2>/dev/null`;
1634
    `iptables -A FORWARD ! --in-interface $extnic --out-interface br$id -j DROP`;
1635
1636 95b003ff Origo
    # Only forward packets coming from subnet assigned to vlan unless we are setting up a gateway on the proxy nic and the proxy nic is on a vlan
1637 d24d9a01 hq
#    `/sbin/iptables --delete FORWARD --in-interface $datanic.$id ! -s 10.$idleft.$idright.0/24 -j DROP`;
1638 95b003ff Origo
    unless ($proxynic eq "$datanic.$id") {
1639 d24d9a01 hq
#        `/sbin/iptables --append FORWARD --in-interface $datanic.$id ! -s 10.$idleft.$idright.0/24 -j DROP`;
1640 95b003ff Origo
    }
1641
1642 a439a9c4 hq
    # Enable nat'ing
1643
    eval {
1644 64c667ea hq
        #my $masq = `/sbin/iptables -L -n -t nat`;
1645 a439a9c4 hq
        #        if (!($masq =~ "MASQUERADE.+all.+--.+0\.0\.0\.0/0")) {
1646
        `/sbin/iptables -D POSTROUTING -t nat --out-interface $extnic -s 10.0.0.0/8 -j MASQUERADE`;
1647
        `/sbin/iptables -A POSTROUTING -t nat --out-interface $extnic -s 10.0.0.0/8 -j MASQUERADE`;
1648
        # Christian's dev environment
1649
        #            my $interfaces = `/sbin/ifconfig`;
1650
        #            if ($interfaces =~ m/ppp0/) {
1651
        #                `/sbin/iptables --table nat --append POSTROUTING --out-interface ppp0 -s 10.0.0.0/8 -j MASQUERADE`;
1652
        #            }
1653
        #        };
1654
        1;
1655
    } or do {print "Unable to enable masquerading: $@\n";};
1656
1657 95b003ff Origo
    $uistatus = ($e)?"":validateStatus($register{$uuid});
1658
    if ($uistatus && $uistatus ne 'down') {
1659
        $uiuuid = $uuid;
1660
        $postreply .= "Status=$uistatus OK $action $type $name\n";
1661
    } else {
1662
        $postreply .= "Status=ERROR Cannot $action $type $name ($uistatus)\n";
1663
    }
1664
    $main::syslogit->($user, 'info', "$action network $uuid ($name, $id) -> $uistatus");
1665
    updateBilling("$uistatus $uuid ($id)");
1666 d24d9a01 hq
    # $main::updateUI->({tab=>"networks", user=>$user, uuid=>$uiuuid, status=>$uistatus}) if ($uistatus);
1667 95b003ff Origo
    return $postreply;
1668
}
1669
1670
sub Removeusernetworks {
1671
    my $username = shift;
1672
    return unless (($isadmin || $user eq $username) && !$isreadonly);
1673
    $user = $username;
1674
    foreach my $uuid (keys %register) {
1675
        if ($register{$uuid}->{'user'} eq $user) {
1676
            $postreply .=  "Removing network $register{$path}->{'name'}, $uuid" . ($console?'':'<br>') . "\n";
1677
            Deactivate($uuid);
1678
            Remove('remove', $uuid);
1679
        }
1680
    }
1681
}
1682
1683
sub Remove {
1684 d3d1a2d4 Origo
    my ($uuid, $action, $obj) = @_;
1685 95b003ff Origo
    if ($help) {
1686
        return <<END
1687 d3d1a2d4 Origo
DELETE:uuid,force:
1688
Delete a network which must be in status down or nat and should not be used by any servers, or linked to any stacks.
1689 95b003ff Origo
May also be called with endpoints "/stabile/[uuid]" or "/stabile?uuid=[uuid]"
1690 d3d1a2d4 Origo
Set [force] to remove even if linked to a system.
1691 95b003ff Origo
END
1692
    }
1693 d3d1a2d4 Origo
    $uuid = $obj->{'uuid'} if ($curuuid && $obj->{'uuid'}); # we are called from a VM with an ip address as target
1694
    my $force = $obj->{'force'};
1695 95b003ff Origo
    ( my $domains, my $domainnames ) = getDomains($uuid);
1696 d3d1a2d4 Origo
    ( my $systems, my $systemnames ) = getSystems($uuid);
1697 95b003ff Origo
1698
    if ($register{$uuid}) {
1699
        my $id = $register{$uuid}->{'id'};
1700
        my $name = $register{$uuid}->{'name'};
1701
        utf8::decode($name);
1702
        my $status = $register{$uuid}->{'status'};
1703
        my $type = $register{$uuid}->{'type'};
1704
        my $internalip = $register{$uuid}->{'internalip'};
1705
        my $externalip = $register{$uuid}->{'externalip'};
1706
1707
        my @regvalues = values %register;
1708 d3d1a2d4 Origo
        if (
1709
            $id!=0 && $id!=1 && (!$domains || $domains eq '--')
1710 2a63870a Christian Orellana
                && ((!$systems || $systems eq '--' || $force)
1711 d3d1a2d4 Origo
                # allow internalip's to be removed if active and only linked, i.e. not providing dhcp
1712 2a63870a Christian Orellana
                || ($status eq 'down' || $status eq 'new' || $status eq 'nat' || ($type eq 'internalip' && $systems && $systems ne '--')))
1713 d3d1a2d4 Origo
        ) {
1714 95b003ff Origo
            # Deconfigure internal dhcp server and DNS
1715
            if ($type eq "internalip") {
1716
                my $result =  removeDHCPAddress($id, $domains, $internalip);
1717
                $postreply .= "$result\n" unless $result eq "OK";
1718
            } elsif ($type eq "ipmapping") {
1719
                my $result =  removeDHCPAddress($id, $domains, $internalip);
1720
                $postreply .= "$result\n" unless $result eq "OK";
1721
                if ($dodns) {
1722 e9af6c24 Origo
                    $main::dnsDelete->($engineid, $externalip) if ($enginelinked);
1723 95b003ff Origo
                }
1724
            } elsif ($type eq "externalip") {
1725
                my $result =  removeDHCPAddress($id, $domains, $externalip);
1726
                $postreply .= "$result\n" unless $result eq "OK";
1727
                if ($dodns) {
1728 e9af6c24 Origo
                    $main::dnsDelete->($engineid, $externalip) if ($enginelinked);
1729 95b003ff Origo
                }
1730
            }
1731
            if ($status eq 'nat') {
1732
                # Check if last network in vlan. If so take it down
1733
                my $notlast;
1734
                foreach my $val (@regvalues) {
1735
                    if ($val->{'user'} eq $user && $val->{'id'} == $id) {
1736
                        $notlast = 1;
1737
                    }
1738
                }
1739
                if (!$notlast) {
1740
                    eval {`/sbin/ifconfig $datanic.$id down`; 1;} or do {;};
1741
                    eval {`/sbin/vconfig rem $datanic.$id`; 1;} or do {;};
1742
                }
1743
            }
1744 d3d1a2d4 Origo
1745
            unless (tie(%sysreg,'Tie::DBI', Hash::Merge::merge({table=>'systems'}, $Stabile::dbopts)) ) {$res .= qq|{"status": "Error": "message": "Unable to access systems register"}|; return $res;};
1746
            if ($sysreg{$systems}) { # Remove existing link to system
1747
                $sysreg{$systems}->{'networkuuids'} =~ s/$uuid,?//;
1748
                $sysreg{$systems}->{'networknames'} = s/$name,?//;
1749
            }
1750
            tied(%sysreg)->commit;
1751
            untie(%sysreg);
1752
1753
1754 95b003ff Origo
            delete $register{$uuid};
1755
            tied(%register)->commit;
1756
            updateBilling("delete $val->{'externalip'}") if ($type eq "ipmapping");
1757
            $main::syslogit->($user, "info", "Deleted network $uuid ($id)");
1758 d3d1a2d4 Origo
            $postreply = "[]" || $postreply;
1759
            $main::updateUI->({tab=>"networks", user=>$user, type=>"update"});
1760 95b003ff Origo
        } else {
1761 d3d1a2d4 Origo
            $postreply .= "Status=ERROR Cannot remove $uuid which is $status. Cannot delete network 0,1 or a network which is active or in use.\n";
1762
            $main::updateUI->({tab=>"networks", user=>$user, message=>"Cannot remove a network which is active, linked or in use."});
1763 95b003ff Origo
        }
1764
    } else {
1765 d3d1a2d4 Origo
        $postreply .= "Status=ERROR Network $uuid $ipaddress not found\n";
1766 95b003ff Origo
    }
1767
    return $postreply;
1768
}
1769
1770
sub Deactivate {
1771 d3d1a2d4 Origo
    my ($uuid, $action, $obj) = @_;
1772 95b003ff Origo
1773
    if ($help) {
1774
        return <<END
1775
GET:uuid:
1776
Deactivate a network which must be in status up.
1777
END
1778
    }
1779 d3d1a2d4 Origo
    $uuid = $obj->{'uuid'} if ($obj->{'uuid'});
1780
1781
    unless ($register{$uuid}) {
1782
        $postreply .= "Status=ERROR Connection with uuid $uuid not found\n";
1783
        return $postreply;
1784
    }
1785
    my $regnet = $register{$uuid};
1786 95b003ff Origo
1787
    $action = $action || 'deactivate';
1788
    ( my $domains, my $domainnames ) = getDomains($uuid);
1789
    my $interfaces = `/sbin/ifconfig`;
1790
1791 d3d1a2d4 Origo
    my $id = $regnet->{'id'};
1792
    my $name = $regnet->{'name'};
1793
    my $type = $regnet->{'type'};
1794
    my $internalip = $regnet->{'internalip'};
1795
    my $externalip = $regnet->{'externalip'};
1796
    my $ports = $regnet->{'ports'};
1797 95b003ff Origo
1798
    if ($id!=0 && $id!=1 && $status ne 'down') {
1799
    # If gateway is created, take it down along with all user's networks
1800
        if ($action eq "stop") {
1801
            my $res = Stop($id, $action);
1802
            if ($res) {
1803
                unlink "$etcpath/dhcp-hosts-$id" if (-e "$etcpath/dhcp-hosts-$id");
1804
            };
1805
        }
1806
    } else {
1807
        $postreply .= "Status=ERROR Cannot $action network $name\n";
1808
        return $postreply;
1809
    }
1810
1811 2a63870a Christian Orellana
    my $idleft = ($id>99)?(substr $id,0,-2)+0 : 0;
1812
    my $idright = (substr $id,-2) + 0;
1813 95b003ff Origo
    my $e = 0;
1814 2a63870a Christian Orellana
    my $duprules = 0;
1815 d24d9a01 hq
1816
    if ($type eq "ipmapping" || $type eq "internalip") {
1817
        `iptables -D FORWARD -d $internalip -m state --state ESTABLISHED,RELATED -j RETURN`;
1818
    }
1819 95b003ff Origo
    if ($type eq "ipmapping") {
1820 d24d9a01 hq
        # Check if external ip exists and take it down if so
1821 95b003ff Origo
        if ($internalip && $internalip ne "--" && $externalip && $externalip ne "--" && ($interfaces =~ m/$externalip/g)) {
1822 64c667ea hq
            $externalip =~ /\d+\.\d+\.(\d+)\.(\d+)/;
1823
            my $ipend = "$1$2"; # Linux NIC names are limited to 15 chars - we will have to find a way to support long NIC names and bigger than /24 subnets
1824
            $ipend = $2 if (length("$extnic:$id-$ipend")>15);
1825 95b003ff Origo
            eval {`/sbin/ifconfig $extnic:$id-$ipend down`; 1;} or do {$e=1; $postreply .= "Status=ERROR $@\n";};
1826
1827
            if ($ports && $ports ne "--") { # Port mapping is defined
1828
                my @portslist = split(/, ?| /, $ports);
1829 2a63870a Christian Orellana
                foreach my $port (@portslist) {
1830 95b003ff Origo
                    my $ipfilter;
1831
                    if ($port =~ /(\d+)\.(\d+)\.(\d+)\.(\d+)(\/\d+)?:(\d+)/) {
1832
                        my $portip = "$1.$2.$3.$4$5";
1833
                        $port = $6;
1834
                        $ipfilter = "-s $portip";
1835
                    } else {
1836
                        $port = 0 unless ($port =~ /\d+/);
1837
                    }
1838
                    if ($port<1 || $port>65535) {
1839
                        $postreply .= "Status=ERROR Invalid port mapping for $name\n";
1840
                        $ports = "--";
1841
                        last;
1842
                    }
1843 d24d9a01 hq
                    # Remove DNAT rules
1844 95b003ff Origo
                    if ($port>1 || $port<65535) {
1845
                        # repeat for good measure
1846 2a63870a Christian Orellana
                        for (my $di=0; $di < 10; $di++) {
1847
                            $duprules = 0;
1848
                            eval {$duprules++ if (`/sbin/iptables -D PREROUTING -t nat -p tcp $ipfilter -d $externalip --dport $port -j DNAT --to $internalip`); 1;}
1849 95b003ff Origo
                                or do {$postreply .= "Status=ERROR $@\n"; $e=1};
1850 2a63870a Christian Orellana
                            eval {$duprules++ if (`/sbin/iptables -D PREROUTING -t nat -p udp $ipfilter -d $externalip --dport $port -j DNAT --to $internalip`); 1;}
1851 95b003ff Origo
                                or do {$postreply .= "Status=ERROR $@\n"; $e=1};
1852 2a63870a Christian Orellana
                            eval {$duprules++ if (`/sbin/iptables -D OUTPUT -t nat -p tcp $ipfilter -d $externalip --dport $port -j DNAT --to $internalip`); 1;}
1853
                                or do {$postreply .= "Status=ERROR $@\n"; $e=1};
1854
                            eval {$duprules++ if (`/sbin/iptables -D OUTPUT -t nat -p udp $ipfilter -d $externalip --dport $port -j DNAT --to $internalip`); 1;}
1855
                                or do {$postreply .= "Status=ERROR $@\n"; $e=1};
1856
                            eval {$duprules++ if (`/sbin/iptables -D POSTROUTING -t nat --out-interface br$id -s $externalip -j MASQUERADE`); 1;}
1857
                                or do {$e=3; $postreply .= "Status=ERROR Problem setting up routing $@\n";};
1858 d24d9a01 hq
                            # Remove access to ipmapped internal ip on $port
1859
                            eval {$duprules++ if (`/sbin/iptables -D FORWARD -d $internalip -p udp --dport $port -j RETURN`); 1;}
1860
                                or do {$e=3; $postreply .= "Status=ERROR Problem setting up routing $@\n";};
1861
                            eval {$duprules++ if (`/sbin/iptables -D FORWARD -d $internalip -p tcp --dport $port -j RETURN`); 1;}
1862
                                or do {$e=3; $postreply .= "Status=ERROR Problem setting up routing $@\n";};
1863
                            last if ($duprules >6);
1864 95b003ff Origo
                        }
1865
                    }
1866
                }
1867 d24d9a01 hq
                # Remove SNAT rules
1868 95b003ff Origo
                # repeat for good measure
1869 2a63870a Christian Orellana
                for (my $di=0; $di < 10; $di++) {
1870
                    $duprules = 0;
1871
                    eval {$duprules++ if (`/sbin/iptables -D POSTROUTING -t nat -s $internalip ! -d 10.$idleft.$idright.0/24 -j SNAT --to-source $externalip`); 1; }
1872 95b003ff Origo
                        or do {$e=1; $postreply .= "Status=ERROR Problem setting up routing $@\n";};
1873 2a63870a Christian Orellana
                    last if ($duprules);
1874 95b003ff Origo
                }
1875 d24d9a01 hq
                # Remove rule to drop traffic to all other ports
1876
                eval {`/sbin/iptables -D INPUT -d $externalip -j DROP`; 1;}
1877 95b003ff Origo
                    or do {$e=1; $postreply .= "Status=ERROR Problem setting up routing $@\n";};
1878
            } else {
1879 d24d9a01 hq
                # Remove DNAT rules
1880 95b003ff Origo
                # repeat for good measure
1881 2a63870a Christian Orellana
                for (my $di=0; $di < 10; $di++) {
1882
                    $duprules = 0;
1883
                    eval {$duprules++ if (`/sbin/iptables -D PREROUTING -t nat -d $externalip -j DNAT --to $internalip`); 1;}
1884 95b003ff Origo
                        or do {$postreply .= "Status=ERROR $@\n"; $e=1};
1885 2a63870a Christian Orellana
                    eval {$duprules++ if (`/sbin/iptables -D OUTPUT -t nat -d $externalip -j DNAT --to $internalip`); 1;}
1886
                        or do {$postreply .= "Status=ERROR $@\n"; $e=1};
1887 d24d9a01 hq
                    last if ($duprules >1);
1888 95b003ff Origo
                }
1889 d24d9a01 hq
                # Remove blanket access to ipmapped internal ip
1890
                `iptables -D FORWARD -d $internalip -j RETURN`;
1891
            }
1892
            # Remove SNAT and MASQUERADE rules
1893
            # repeat for good measure
1894
            for (my $di=0; $di < 10; $di++) {
1895
                $duprules = 0;
1896
            #    eval {$duprules++ if (`/sbin/iptables -D POSTROUTING -t nat --out-interface br$id -s $externalip -j MASQUERADE`); 1;}
1897
            #        or do {$e=3; $postreply .= "Status=ERROR Problem setting up routing $@\n";};
1898 6fdc8676 hq
                eval {$duprules++ if (`/sbin/iptables -D POSTROUTING -t nat --out-interface br$id ! -d 10.$idleft.$idright.0/24 -j MASQUERADE`); 1;}
1899 d24d9a01 hq
                    or do {$e=3; $postreply .= "Status=ERROR Problem setting up routing $@\n";};
1900
1901
                eval {$duprules++ if (`/sbin/iptables -D POSTROUTING -t nat -s $internalip ! -d 10.$idleft.$idright.0/24 -j SNAT --to-source $externalip`); 1; }
1902
                    or do {$e=1; $postreply .= "Status=ERROR Problem setting up routing $@\n";};
1903
            #    eval {$duprules++ if (`/sbin/iptables -D POSTROUTING -t nat -s $internalip -j SNAT --to-source $externalip`); 1; }
1904
            #        or do {$e=1; $postreply .= "Status=ERROR Problem setting up routing $@\n";};
1905
                eval {$duprules++ if (`/sbin/iptables -D INPUT -t nat -s $internalip ! -d 10.$idleft.$idright.0/24 -j SNAT --to-source $externalip`); 1; }
1906
                    or do {$e=1; $postreply .= "Status=ERROR Problem setting up routing $@\n";};
1907
            #    eval {$duprules++ if (`/sbin/iptables -D INPUT -t nat -s $internalip -j SNAT --to-source $externalip`); 1; }
1908
            #        or do {$e=1; $postreply .= "Status=ERROR Problem setting up routing $@\n";};
1909
            #    eval {$duprules++ if (`/sbin/iptables -D INPUT -t nat -s $internalip ! -d 10.$idleft.$idright.0/24 -j SNAT --to-source $externalip`); 1; }
1910
            #        or do {$e=1; $postreply .= "Status=ERROR Problem setting up routing $@\n";};
1911
            #    eval {$duprules++ if (`/sbin/iptables -D INPUT -t nat -s $internalip -j SNAT --to-source $externalip`); 1; }
1912
            #        or do {$e=1; $postreply .= "Status=ERROR Problem setting up routing $@\n";};
1913
                last if ($duprules >1);
1914 95b003ff Origo
            }
1915 d24d9a01 hq
            # `/sbin/iptables -D POSTROUTING -t nat -s $internalip -j LOG --log-prefix "SNAT-POST"`;
1916
            # `/sbin/iptables -D INPUT -t nat -s $internalip -j LOG --log-prefix "SNAT-INPUT"`;
1917
            # `/sbin/iptables -D OUTPUT -t nat -s $internalip -j LOG --log-prefix "SNAT-OUTPUT"`;
1918
            # `/sbin/iptables -D PREROUTING -t nat -s $internalip -j LOG --log-prefix "SNAT-PRE"`;
1919 95b003ff Origo
        }
1920
    } elsif ($type eq "externalip") {
1921
        if ($externalip && $externalip ne "--") {
1922
            # We are dealing with multiple upstream routes - configure local routing
1923
            if ($proxynic && $proxynic ne $extnic) {
1924
                my $proxyroute = `/sbin/ip route show table proxyarp`;
1925
                `/sbin/ip route del $externalip/32 dev br$id:proxy src $proxyip table proxyarp` if ($proxyroute =~ /$externalip/);
1926
            }
1927
1928
            eval {`/sbin/ip route del $externalip/32 dev br$id:proxy`; 1;}
1929
                or do {$e=1; $postreply .= "Status=ERROR Problem deconfiguring proxy arp $@\n";};
1930
1931
            if ($ports && $ports ne "--") {
1932
                my @portslist = split(/, ?| /, $ports);
1933 2a63870a Christian Orellana
                foreach my $port (@portslist) {
1934 95b003ff Origo
                    my $ipfilter;
1935
                    if ($port =~ /(\d+)\.(\d+)\.(\d+)\.(\d+)(\/\d+)?:(\d+)/) {
1936
                        my $portip = "$1.$2.$3.$4$5";
1937
                        $port = $6;
1938
                        $ipfilter = "-s $portip";
1939
                    } else {
1940
                        $port = 0 unless ($port =~ /\d+/);
1941
                    }
1942
                    if ($port<1 || $port>65535) {
1943
                        $postreply .= "Status=ERROR Invalid port mapping for $name\n";
1944
                        $ports = "--";
1945
                        last;
1946
                    }
1947
1948
                    if ($port>1 || $port<65535) {
1949
                        # repeat for good measure
1950 2a63870a Christian Orellana
                        for (my $di=0; $di < 10; $di++) {
1951
                            $duprules = 0;
1952 d24d9a01 hq
                            eval {$duprules++ if (`/sbin/iptables -D FORWARD -p tcp -i $proxynic $ipfilter -d $externalip --dport $port -j RETURN`); 1;}
1953 95b003ff Origo
                                or do {$e=1; $postreply .= "Status=ERROR Problem setting up routing $@\n";};
1954 d24d9a01 hq
                            eval {$duprules++ if (`/sbin/iptables -D FORWARD -p udp -i $proxynic $ipfilter -d $externalip --dport $port -j RETURN`); 1;}
1955 95b003ff Origo
                                or do {$e=1; $postreply .= "Status=ERROR Problem setting up routing $@\n";};
1956 2a63870a Christian Orellana
                            last if ($duprules > 1);
1957
                        }
1958 95b003ff Origo
                    }
1959
                }
1960
            }
1961 2a63870a Christian Orellana
            # Remove rule to allow forwarding from $externalip
1962 d24d9a01 hq
	        `/sbin/iptables --delete FORWARD --in-interface br$id -s $externalip -j RETURN`;
1963 95b003ff Origo
            # Remove rule to disallow setting up a dhcp server
1964
            eval {`/sbin/iptables -D FORWARD -p udp -i $proxynic -d $externalip --dport 67 -j REJECT`; 1;}
1965
                or do {$e=1; $postreply .= "Status=ERROR Problem setting up routing $@\n";};
1966
            # Leave outgoing connectivity - not
1967 d24d9a01 hq
            eval {`/sbin/iptables -D FORWARD -i $proxynic -d $externalip -m state --state ESTABLISHED,RELATED -j RETURN`; 1;}
1968 95b003ff Origo
                or do {$e=1; $postreply .= "Status=ERROR Problem setting up routing $@\n";};
1969 d24d9a01 hq
            eval {`/sbin/iptables -D FORWARD -i $proxynic -d $externalip -j RETURN`; 1;}
1970 95b003ff Origo
                or do {$e=1; $postreply .= "Status=ERROR Problem setting up routing $@\n";};
1971
            # No need to reject - we reject all per default to the subnet
1972
            eval {`/sbin/iptables -D FORWARD -i $proxynic -d $externalip -j REJECT`; 1;}
1973
                or do {$e=1; $postreply .= "Status=ERROR Problem setting up routing $@\n";};
1974
        }
1975
    }
1976
    # Deconfigure internal dhcp server
1977
    if ($type eq "internalip" || $type eq "ipmapping") {
1978
        my $result =  removeDHCPAddress($id, $domains, $internalip);
1979
        if ($result ne "OK") {
1980
            $e=1;
1981
            $postreply .= "$result\n";
1982
        }
1983 d3d1a2d4 Origo
    } elsif ($type eq "externalip" && $domains) {
1984 95b003ff Origo
        my $result =  removeDHCPAddress($id, $domains, $externalip);
1985
        if ($result ne "OK") {
1986
            $e=1;
1987
            $postreply .= "$result\n";
1988
        }
1989
    }
1990
    $uistatus = ($e)?"":validateStatus($register{$uuid});
1991
    if ($uistatus) {
1992
        $uiuuid = $uuid;
1993
        $postreply .= "Status=$uistatus OK $action $type $name: $uistatus\n";
1994
    } else {
1995
        $postreply .= "Status=ERROR Cannot $action $type $name: $uistatus\n";
1996
    }
1997
    $main::syslogit->($user, 'info', "$action network $uuid ($name, $id) -> $uistatus");
1998
    updateBilling("$uistatus $uuid ($id)");
1999 d24d9a01 hq
    # $main::updateUI->({tab=>"networks", user=>$user, uuid=>$uiuuid, status=>$uistatus}) if ($uistatus);
2000 95b003ff Origo
    return $postreply;
2001
}
2002
2003
sub Stop {
2004
    my ($id, $action) = @_;
2005
    # Check if we were passed a uuid
2006
    if ($id =~ /\-/ && $register{$id} && ($register{$id}->{'user'} eq $user || $isadmin)) {
2007
        $id = $register{$id}->{'id'}
2008
    }
2009
    if ($help) {
2010
        return <<END
2011
GET:uuid:
2012
Stops a network by removing gateway. Network must be in status up or nat.
2013
END
2014
    }
2015
2016
    my $idleft = ($id>99)?(substr $id,0,-2)+0 : 0;
2017
    my $idright = (substr $id,-2) + 0;
2018
    my $e = 0;
2019
    # First deactivate all user's networks with same id
2020
    my @regkeys = (tied %register)->select_where("user = '$user'");
2021
    foreach my $key (@regkeys) {
2022
        my $valref = $register{$key};
2023
        my $cuuid = $valref->{'uuid'};
2024
        my $ctype = $valref->{'type'};
2025
        my $cdbuser = $valref->{'user'};
2026
        my $cid = $valref->{'id'};
2027
    # Only list networks belonging to current user
2028
        if ($user eq $cdbuser && $id eq $cid && $ctype ne "gateway") {
2029
            if ($ctype eq "internalip" || $ctype eq "ipmapping" || $ctype eq "externalip") {
2030
                my $result = Deactivate($cuuid, 'deactivate');
2031
                if ($result =~ /\w+=ERROR (.+)/i) {
2032
                    $e = $1;
2033
                }
2034
            }
2035
        }
2036
     }
2037
    my $interfaces = `/sbin/ifconfig br$id`;
2038
     # Only take down interface and vlan if gateway IP is active on interface
2039
    if ($e) {
2040
        $postreply .= "Status=Error Not taking down gateway, got an error: $e\n"
2041
#    } elsif ($interfaces =~ /^$datanic\.$id.+\n.+inet .+10\.$idleft\.$idright\.1/
2042
    } elsif ($interfaces =~ /10\.$idleft\.$idright\.1/
2043
            && !$e) {
2044
        eval {`/sbin/brctl delif br$id $datanic.$id`; 1;} or do {$e=1;};
2045
        eval {`/sbin/ifconfig br$id down`; 1;} or do {$e=1;};
2046
        eval {`/sbin/ifconfig $datanic.$id down`; 1;} or do {$e=1;};
2047
        eval {`/sbin/vconfig rem $datanic.$id`; 1;} or do {$e=1;};
2048
    } else {
2049
        $postreply .= "Status=Error Not taking down interface, gateway 10.$idleft.$idright.1 is not active on interface br$id - $interfaces.\n"
2050
    }
2051
    # Remove rule to only forward packets coming from subnet assigned to vlan
2052 d24d9a01 hq
#    `/sbin/iptables --delete FORWARD --in-interface $datanic.$id ! -s 10.$idleft.$idright.0/24 -j DROP`;
2053 95b003ff Origo
2054
    $uistatus = ($e)?$uistatus:"down";
2055
    if ($uistatus eq 'down') {
2056
        $uiuuid = $uuid;
2057
        $postreply .= "Status=$uistatus OK $action gateway: $uistatus\n";
2058
    } else {
2059
        $postreply .= "Status=Error Cannot $action $type $name: $uistatus\n";
2060
    }
2061
    return $postreply;
2062
}
2063
2064
sub getDomains {
2065
    my $uuid = shift;
2066
    my $domains;
2067
    my $domainnames;
2068
    my @domregvalues = values %domreg;
2069
    foreach my $domval (@domregvalues) {
2070
        if (($domval->{'networkuuid1'} eq $uuid || $domval->{'networkuuid2'} eq $uuid || $domval->{'networkuuid3'} eq $uuid)
2071
                && $domval->{'user'} eq $user) {
2072
            $domains .= $domval->{'uuid'} . ", ";
2073
            $domainnames .= $domval->{'name'} . ", ";
2074
        }
2075
    }
2076
    $domains = substr $domains, 0, -2;
2077
    $domainnames = substr $domainnames, 0, -2;
2078
    return ($domains, $domainnames); 
2079
}
2080
2081 d3d1a2d4 Origo
sub getSystems {
2082
    my $uuid = shift;
2083
    my $systems;
2084
    my $systemnames;
2085
    unless (tie(%sysreg,'Tie::DBI', Hash::Merge::merge({table=>'systems'}, $Stabile::dbopts)) ) {$res .= qq|{"status": "Error": "message": "Unable to access systems register"}|; return $res;};
2086
    my @sysregvalues = values %sysreg;
2087
    foreach my $sysval (@sysregvalues) {
2088
        my $networkuuids = $sysval->{'networkuuids'};
2089
        if ($networkuuids =~ /$uuid/ && $sysval->{'user'} eq $user) {
2090
            $systems = $sysval->{'uuid'};
2091
            $systemnames = $sysval->{'name'};
2092
            last;
2093
        }
2094
    }
2095
    unless ($systems) {
2096
        my @sysregvalues = values %domreg;
2097
        foreach my $sysval (@sysregvalues) {
2098
            my $networkuuids = $sysval->{'networkuuids'};
2099
            if ($networkuuids =~ /$uuid/ && $sysval->{'user'} eq $user) {
2100
                $systems = $sysval->{'uuid'};
2101
                $systemnames = $sysval->{'name'};
2102
                last;
2103
            }
2104
        }
2105
    }
2106
    return ($systems, $systemnames);
2107
}
2108
2109 95b003ff Origo
sub getNextId {
2110
	# Find the next available vlan id
2111
	my $reqid = shift;
2112
	my $username = shift;
2113
	$username = $user unless ($username);
2114
    my $nextid = 1;
2115
	my $vlanstart = $Stabile::config->get('VLAN_RANGE_START');
2116
	my $vlanend = $Stabile::config->get('VLAN_RANGE_END');
2117
2118
    if ($reqid eq 0 || $reqid == 1) {
2119
        return $requid;
2120
    } elsif ($reqid && ($reqid > $vlanend || $reqid < $vlanstart)) {
2121
        return -1 unless ($isadmin);
2122
    }
2123
2124
	$reqid = $reqid + 0;
2125
2126
    my %ids;
2127
    # First check if the user has an existing vlan, if so use the first we find as default value
2128
    my @regvalues = values %register;
2129
    @regvalues = (sort {$a->{id} <=> $b->{id}} @regvalues);
2130
    foreach my $val (@regvalues) { # Traverse all id's in use
2131
        my $id = 0 + $val->{'id'};
2132
        my $dbuser = $val->{'user'};
2133
        if ($id > 1) {
2134
            if ($username eq $dbuser) { # If a specific id was requested map all id's
2135
                if (!$reqid) {# If no specific id was asked for, stop now, and use the user's first one
2136
                    $nextid = $id;
2137
                    last;
2138
                }
2139
            } else {
2140
                $ids{$id} = 1; # Mark this id as used (by another user)
2141
            }
2142
        }
2143
    }
2144
    if ($nextid>1) {
2145
        return $nextid;
2146
    } elsif ($reqid) {
2147
        if (!$ids{$reqid} || $isadmin) { # If an admin is requesting id used by another, assume he knows what he is doing
2148
            $nextid = $reqid; # Safe to use
2149
        } else {
2150
            $nextid = -1; # Id already in use by another
2151
        }
2152
    } elsif ($nextid == 1) { # This user is not currently using any vlan's, find the first free one
2153
        for ($n=$vlanstart; $n<$vlanend; $n++) {
2154
            if (!$ids{$n}) { # Don't return an id used (by another user)
2155
                $nextid = $n;
2156
                last;
2157
            }
2158
        }
2159
    }
2160
	return $nextid;
2161
}
2162
2163
sub getNextExternalIP {
2164
	# Find the next available IP
2165
	my $extip = shift;
2166
	my $extuuid = shift;
2167
	my $proxyarp = shift; # Are we trying to assign a proxy arp's external IP?
2168
	$extip="" if ($extip eq "--");
2169
2170
	my $extipstart;
2171
	my $extipend;
2172
2173
    if ($proxyarp) {
2174
        $extipstart = $Stabile::config->get('PROXY_IP_RANGE_START');
2175
        $extipend = $Stabile::config->get('PROXY_IP_RANGE_END');
2176
    } else {
2177
        $extipstart = $Stabile::config->get('EXTERNAL_IP_RANGE_START');
2178
        $extipend = $Stabile::config->get('EXTERNAL_IP_RANGE_END');
2179
    }
2180
2181
	return "" unless ($extipstart && $extipend);
2182
2183
	my $interfaces = `/sbin/ifconfig`;
2184
#	$interfaces =~ m/eth0 .+\n.+inet addr:(\d+\.\d+\.\d+)\.(\d+)/;
2185
	$extipstart =~  m/(\d+\.\d+\.\d+)\.(\d+)/;
2186
	my $bnet1 = $1;
2187
	my $bhost1 = $2+0;
2188
	$extipend =~  m/(\d+\.\d+\.\d+)\.(\d+)/;
2189
	my $bnet2 = $1;
2190
	my $bhost2 = $2+0;
2191
	my $nextip = "";
2192
	if ($bnet1 ne $bnet2) {
2193
		print "Status=ERROR Only 1 class C subnet is supported for $name\n";
2194
		return "";
2195
	}
2196
	my %ids;
2197
	# First create map of IP's reserved by other servers in DB
2198
	my @regvalues = values %register;
2199
	foreach my $val (@regvalues) {
2200
		my $ip = $val->{'externalip'};
2201
		# $ip =~ m/(\d+\.\d+\.\d+)\.(\d+)/;
2202
		# my $id = $2;
2203
		$ids{$ip} = $val->{'uuid'} unless ($extuuid eq $val->{'uuid'});
2204
	}
2205 54401133 hq
    my $oc = overQuotas(1);
2206
	if ($oc) { # Enforce quotas
2207 95b003ff Origo
        $postreply .= "Status=ERROR Over quota allocating external IP\n";
2208
	} elsif ($extip && $extip =~  m/($bnet1)\.(\d+)/ && $2>=$bhost1 && $2<$bhost2) {
2209
	# An external ip was supplied - check if it's free and ok
2210
		if (!$ids{$extip} && !($interfaces =~ m/$extip.+\n.+inet addr:$extip/) && $extip=~/$bnet$\.(\d)/) {
2211
			$nextip = $extip;
2212
		}
2213
	} else {
2214
	# Find random IP not reserved, and check it is not in use (for other purposes)
2215
	    my @bhosts = ($bhost1..$bhost2);
2216
        my @rbhosts = shuffle @bhosts;
2217
		for ($n=0; $n<$bhost2-$bhost1; $n++) {
2218
		    my $nb = $rbhosts[$n];
2219
			if (!$ids{"$bnet1.$nb"}) {
2220
				if (!($interfaces =~ m/$extip.+\n.+inet addr:$bnet1\.$nb/)) {
2221
					$nextip = "$bnet1.$nb";
2222
					last;
2223
				}
2224
			}
2225
		}
2226
	}
2227 54401133 hq
	$postreply .= "Status=ERROR No more ($oc) external IPs available\n" unless ($nextip);
2228 95b003ff Origo
	return $nextip;
2229
}
2230
2231
sub ip2domain {
2232
    my $ip = shift;
2233
    my $ruuid;
2234
    if ($ip) {
2235
        my @regkeys = (tied %register)->select_where("internalip = '$ip' OR externalip = '$ip'");
2236
        foreach my $k (@regkeys) {
2237
            my $valref = $register{$k};
2238
            if ($valref->{'internalip'} eq $ip || $valref->{'externalip'} eq $ip) {
2239
                $ruuid = $valref->{'domains'};
2240
                last;
2241
            }
2242
        }
2243
    }
2244
    return $ruuid;
2245
}
2246
2247
sub getNextInternalIP {
2248
	my $intip = shift;
2249
	my $uuid = shift;
2250
	my $id = shift;
2251
	my $username = shift;
2252
	$username = $user unless ($username);
2253
	my $nextip = "";
2254
	my $intipnum;
2255
	my $subnet;
2256
	my %ids;
2257
    my $ping = Net::Ping->new();
2258
2259
    $id = getNextId() unless ($id);
2260
    my $idleft = ($id>99)?(substr $id,0,-2)+0 : 0;
2261
    my $idright = (substr $id,-2) + 0;
2262
    $intip = "10.$idleft.$idright.0" if (!$intip || $intip eq '--');
2263
    
2264
    return '' unless ($intip =~ m/(\d+\.\d+\.\d+)\.(\d+)/ );
2265
    $subnet = $1;
2266
    $intipnum = $2;
2267
2268
	# First create hash of IP's reserved by other servers in DB
2269
	my @regvalues = values %register;
2270
	foreach my $val (@regvalues) {
2271
    	if ($val->{'user'} eq $username) {
2272
            my $ip = $val->{'internalip'} ;
2273
            $ids{$ip} = $val->{'uuid'};
2274
		}
2275
	}
2276
2277
	if ($intipnum && $intipnum>1 && $intipnum<255) {
2278
	# An internal ip was supplied - check if it's free, if not keep the ip already registered in the db
2279
        if (!$ids{$intip}
2280
#            && !($ping->ping($intip, 0.1)) # 0.1 secs timeout, check if ip is in use, possibly on another engine
2281
            && !(`arping -C1 -c2 -D -I $datanic.$id $intip` =~ /reply from/)  # check if ip is created on another engine
2282
        ) {
2283
            $nextip = $intip;
2284
        } else {
2285
            $nextip = $register{$uuid}->{'internalip'}
2286
        }
2287
	} else {
2288
	# Find first IP not reserved
2289
		for ($n=2; $n<255; $n++) {
2290
			if (!$ids{"$subnet.$n"}
2291
# TODO: The arping check takes too long - two networks created by the same user can too easily be assigned the same IP's
2292
#                && !(`arping -f -c2 -D -I $datanic.$id $subnet.$n` =~ /reply from/)  # check if ip is created on another engine
2293
			) {
2294
                $nextip = "$subnet.$n";
2295
                last;
2296
			}
2297
		}
2298
	}
2299
	$postreply .= "Status=ERROR No more internal IPs available\n" if (!$nextip);
2300
	return $nextip;
2301
}
2302
2303
sub validateStatus {
2304
    my $valref = shift;
2305
2306
    my $interfaces = `/sbin/ifconfig`;
2307
    my $uuid = $valref->{'uuid'};
2308
    my $type = $valref->{'type'};
2309
    my $id = $valref->{'id'};
2310
    my $idleft = ($id>99)?(substr $id,0,-2)+0 : 0;
2311
    my $idright = (substr $id,-2) + 0;
2312
2313
    ( $valref->{'domains'}, $valref->{'domainnames'} ) = getDomains($uuid);
2314 d3d1a2d4 Origo
    my ( $systems, $systemnames ) = getSystems($uuid);
2315 95b003ff Origo
    my $extip = $valref->{'externalip'};
2316
    my $intip = $valref->{'internalip'};
2317
2318
    if ($type eq "gateway") {
2319
        $valref->{'internalip'} = "10.$idleft.$idright.1" if ($id>1);
2320
    } else {
2321
        $type = "gateway";
2322
        if ($intip && $intip ne "--" && $extip && $extip ne "--") {
2323
            $type = "ipmapping";
2324
        } elsif ($intip && $intip ne "--") {
2325
            $type = "internalip";
2326
        } elsif ($extip && $extip ne "--") {
2327
            $type = "externalip";
2328
        }
2329
        $valref->{'type'} = $type;
2330
    }
2331
2332
    $valref->{'status'} = "down";
2333
    my $nat;
2334
    if ($id == 0 || $id == 1) {
2335
        $valref->{'status'} = "nat";
2336
    # Check if vlan $id is created (and doing nat)
2337
#    } elsif ($interfaces =~ m/$datanic\.$id.+\n.+10\.$idleft\.$idright\.1/) {
2338
    } elsif (-e "/proc/net/vlan/$datanic.$id") {
2339
        $nat = 1;
2340
    }
2341 d24d9a01 hq
2342 95b003ff Origo
    if (($type eq "internalip" || $type eq "ipmapping")) { # && $val->{'domains'}) {
2343
        $valref->{'status'} = "nat" if ($nat);
2344
        my $dhcprunning;
2345
        my $dhcpconfigured;
2346
        eval {
2347
            my $psid;
2348
            $psid = `/bin/cat /var/run/stabile-$id.pid` if (-e "/var/run/stabile-$id.pid");
2349
            chomp $psid;
2350
            $dhcprunning = -e "/proc/$psid" if ($psid);
2351
            my $dhcphosts;
2352
            $dhcphosts = lc `/bin/cat $etcpath/dhcp-hosts-$id` if (-e "$etcpath/dhcp-hosts-$id");
2353
            $dhcpconfigured = ($dhcphosts =~ /$intip/);
2354
            1;
2355
        } or do {;};
2356
2357
        if ($type eq "internalip") {
2358
        # Check if external ip has been created and dhcp is ok
2359 d3d1a2d4 Origo
            if ($nat && (($dhcprunning && $dhcpconfigured) || $systems)) {
2360 95b003ff Origo
                $valref->{'status'} = "up";
2361
            }
2362
        } elsif ($type eq "ipmapping") {
2363
        # Check if external ip has been created, dhcp is ok and vlan interface is created
2364 d3d1a2d4 Origo
        # An ipmapping linked to a system is considered up if external interface exists
2365
            if ($nat && $interfaces =~ m/$extip/ && (($dhcprunning && $dhcpconfigured) || $systems)) {
2366 95b003ff Origo
                $valref->{'status'} = "up";
2367
            }
2368
        }
2369
2370
    } elsif ($type eq "externalip") {
2371
        my $dhcprunning;
2372
        my $dhcpconfigured;
2373
        eval {
2374
            my $psid;
2375
            $psid = `/bin/cat /var/run/stabile-$id.pid` if (-e "/var/run/stabile-$id.pid");
2376
            chomp $psid;
2377
            $dhcprunning = -e "/proc/$psid" if ($psid);
2378
            my $dhcphosts;
2379
            $dhcphosts = `/bin/cat $etcpath/dhcp-hosts-$id` if (-e "$etcpath/dhcp-hosts-$id");
2380
            $dhcpconfigured = ($dhcphosts =~ /$extip/);
2381
            1;
2382
        } or do {;};
2383
2384
        my $vproxy = `/bin/cat /proc/sys/net/ipv4/conf/$datanic.$id/proxy_arp`; chomp $vproxy;
2385
        my $eproxy = `/bin/cat /proc/sys/net/ipv4/conf/$proxynic/proxy_arp`; chomp $eproxy;
2386
        my $proute = `/sbin/ip route | grep "$extip dev"`; chomp $proute;
2387 d3d1a2d4 Origo
        if ($vproxy && $eproxy && $proute) {
2388
            if ((($dhcprunning && $dhcpconfigured) || $systems)) {
2389
                $valref->{'status'} = "up";
2390
            } elsif (!$valref->{'domains'}) {
2391
                $valref->{'status'} = "nat";
2392
            }
2393 95b003ff Origo
        } else {
2394
            #print "$vproxy && $eproxy && $proute && $dhcprunning && $dhcpconfigured :: $extip\n";        
2395
        }
2396
2397
    } elsif ($type eq "gateway") {
2398
        if ($nat || $id == 0 || $id == 1) {$valref->{'status'} = "up";}
2399
    }
2400
    return $valref->{'status'};
2401
}
2402
2403
sub trim{
2404
   my $string = shift;
2405
   $string =~ s/^\s+|\s+$//g;
2406
   return $string;
2407
}
2408
2409
sub overQuotas {
2410
    my $reqips = shift; # number of new ip's we are asking for
2411
	my $usedexternalips = 0;
2412
	my $overquota = 0;
2413
    return $overquota if ($Stabile::userprivileges =~ /a/); # Don't enforce quotas for admins
2414
2415 54401133 hq
	my $externalipquota = $Stabile::userexternalipquota;
2416 95b003ff Origo
	if (!$externalipquota) {
2417
        $externalipquota = $Stabile::config->get('EXTERNAL_IP_QUOTA');
2418
    }
2419
2420 54401133 hq
	my $rxquota = $Stabile::userrxquota;
2421 95b003ff Origo
	if (!$rxquota) {
2422
        $rxquota = $Stabile::config->get('RX_QUOTA');
2423
    }
2424
2425 54401133 hq
	my $txquota = $Stabile::usertxquota;
2426 95b003ff Origo
	if (!$txquota) {
2427
        $txquota = $Stabile::config->get('TX_QUOTA');
2428
    }
2429
2430
    my @regkeys = (tied %register)->select_where("user = '$user'");
2431
	foreach my $k (@regkeys) {
2432
	    my $val = $register{$k};
2433
		if ($val->{'user'} eq $user && $val->{'externalip'} && $val->{'externalip'} ne "--" ) {
2434
		    $usedexternalips += 1;
2435
		}
2436
	}
2437 54401133 hq
	if ((($usedexternalips + $reqips) > $externalipquota) && $externalipquota > 0) { # -1 means no quota
2438 95b003ff Origo
	    $overquota = $usedexternalips;
2439
	} elsif ($rx > $rxquota*1024 && $rxquota > 0) {
2440
	    $overquota = -1;
2441
	} elsif ($tx > $txquota*1024 && $txquota > 0) {
2442
	    $overquota = -2;
2443
	}
2444
	return $overquota;
2445
}
2446
2447
sub updateBilling {
2448
    my $event = shift;
2449
    my %billing;
2450
    my @regkeys = (tied %register)->select_where("user = '$user' or user = 'common'") unless ($fulllist);
2451
    foreach my $k (@regkeys) {
2452
        my $valref = $register{$k};
2453
        my %val = %{$valref}; # Deference and assign to new array, effectively cloning object
2454
        if ($val{'user'} eq $user && ($val{'type'} eq 'ipmapping' || $val{'type'} eq 'externalip') && $val{'externalip'} ne '--') {
2455
            $billing{$val{'id'}}->{'externalip'} += 1;
2456
        }
2457
    }
2458
2459
    my %billingreg;
2460
    my $monthtimestamp = timelocal(0,0,0,1,$mon,$year); #$sec,$min,$hour,$mday,$mon,$year
2461
2462
    unless ( tie(%billingreg,'Tie::DBI', Hash::Merge::merge({table=>'billing_networks', key=>'useridtime'}, $Stabile::dbopts)) ) {return "Unable to access billing register"};
2463
2464
    my $rx_bytes_total = 0;
2465
    my $tx_bytes_total = 0;
2466
2467
    my $prevmonth = $month-1;
2468
    my $prevyear = $year;
2469
    if ($prevmonth == 0) {$prevmonth=12; $prevyear--;};
2470
    $prevmonth = substr("0" . $prevmonth, -2);
2471
    my $prev_rx_bytes_total = 0;
2472
    my $prev_tx_bytes_total = 0;
2473
2474
    foreach my $id (keys %billing) {
2475
        my $b = $billing{$id};
2476
        my $externalip = $b->{'externalip'};
2477
        my $externalipavg = 0;
2478
        my $startexternalipavg = 0;
2479
        my $starttimestamp = $current_time;
2480
        my $rx_bytes = 0;
2481
        my $tx_bytes = 0;
2482
        my $rx_stats = "/sys/class/net/$datanic.$id/statistics/rx_bytes";
2483
        my $tx_stats = "/sys/class/net/$datanic.$id/statistics/tx_bytes";
2484
        $rx_bytes = `/bin/cat $rx_stats` if (-e $rx_stats);
2485
        chomp $rx_bytes;
2486
        $tx_bytes = `/bin/cat $tx_stats` if (-e $tx_stats);
2487
        chomp $tx_bytes;
2488
2489
        if ($current_time - $monthtimestamp < 4*3600) {
2490
            $starttimestamp = $monthtimestamp;
2491
            $externalipavg = $externalip;
2492
            $startexternalipavg = $externalip;
2493
        }
2494
2495
        my $bill = $billingreg{"$user-$id-$year-$month"};
2496
        my $regrx_bytes = $bill->{'rx'};
2497
        my $regtx_bytes = $bill->{'tx'};
2498
        $rx_bytes += $regrx_bytes if ($regrx_bytes > $rx_bytes); # Network interface was reloaded
2499
        $tx_bytes += $regtx_bytes if ($regtx_bytes > $tx_bytes); # Network interface was reloaded
2500
2501
        # Update timestamp and averages on existing row
2502
        if ($billingreg{"$user-$id-$year-$month"}) {
2503
            $startexternalipavg = $bill->{'startexternalipavg'};
2504
            $starttimestamp = $bill->{'starttimestamp'};
2505
2506
            $externalipavg = ($startexternalipavg*($starttimestamp - $monthtimestamp) + $externalip*($current_time - $starttimestamp)) /
2507
                            ($current_time - $monthtimestamp);
2508
2509
            $billingreg{"$user-$id-$year-$month"}->{'externalip'} = $externalip;
2510
            $billingreg{"$user-$id-$year-$month"}->{'externalipavg'} = $externalipavg;
2511
            $billingreg{"$user-$id-$year-$month"}->{'timestamp'} = $current_time;
2512
            $billingreg{"$user-$id-$year-$month"}->{'rx'} = $rx_bytes;
2513
            $billingreg{"$user-$id-$year-$month"}->{'tx'} = $tx_bytes;
2514
        }
2515
2516
        # No row found or something happened which justifies writing a new row
2517
        if (!$billingreg{"$user-$id-$year-$month"}
2518
        || ($b->{'externalip'} != $bill->{'externalip'})
2519
        ) {
2520
2521
            my $inc = 0;
2522
            if ($billingreg{"$user-$id-$year-$month"}) {
2523
                $startexternalipavg = $externalipavg;
2524
                $starttimestamp = $current_time;
2525
                $inc = $bill->{'inc'};
2526
            }
2527
            # Write a new row
2528
            $billingreg{"$user-$id-$year-$month"} = {
2529
                externalip=>$externalip+0,
2530
                externalipavg=>$externalipavg,
2531
                startexternalipavg=>$startexternalipavg,
2532
                timestamp=>$current_time,
2533
                starttimestamp=>$starttimestamp,
2534
                event=>$event,
2535
                inc=>$inc+1,
2536
                rx=>$rx_bytes,
2537
                tx=>$tx_bytes
2538
            };
2539
        }
2540
2541
        $rx_bytes_total += $rx_bytes;
2542
        $tx_bytes_total += $tx_bytes;
2543
        my $prevbill = $billingreg{"$user-$id-$prevyear-$prevmonth"};
2544
        $prev_rx_bytes_total += $prevbill->{'rx'};
2545
        $prev_tx_bytes_total += $prevbill->{'tx'};
2546
    }
2547
    untie %billingreg;
2548
    $rx = ($rx_bytes_total>$prev_rx_bytes_total)?$rx_bytes_total - $prev_rx_bytes_total:$rx_bytes_total;
2549
    $tx = ($tx_bytes_total>$prev_tx_bytes_total)?$tx_bytes_total - $prev_tx_bytes_total:$tx_bytes_total;
2550
    my $oq = overQuotas();
2551 54401133 hq
    if ($oq && $oq<0) {
2552 95b003ff Origo
        foreach my $id (keys %billing) {
2553
            $main::syslogit->($user, 'info', "$user over rx/tx quota ($oq) stopping network $id");
2554
            Stop($id, 'stop');
2555
        }
2556
    }
2557
}
2558
2559
sub Bit2netmask {
2560
	my $netbit = shift;
2561
	my $_bit         = ( 2 ** (32 - $netbit) ) - 1;
2562
	my ($full_mask)  = unpack( "N", pack( "C4", split(/./, '255.255.255.255') ) );
2563
	my $netmask      = join( '.', unpack( "C4", pack( "N", ( $full_mask ^ $_bit ) ) ) );
2564
	return $netmask;
2565
}