Project

General

Profile

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