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 51e32e00 hq
        # Adding VLANs on wifi NICs does not seem to work. Disabling for now until we figure out what is going on.
1406
            unless ($datanic =~ /^wl/) {
1407
                eval {`/sbin/brctl addif br$id $datanic.$id`; 1;} or do {$e=1; $postreply .= "Status=ERROR $@\n"; return $postreply; };
1408
            }
1409 95b003ff Origo
            eval {`/sbin/ifconfig br$id 10.$idleft.$idright.1/24 up`; 1;} or do {
1410
                $e=1; $postreply .= "Status=ERROR $@\n"; return $postreply; }
1411
        } else {
1412
            $postreply .= "Status=OK GW is active on another Engine, assuming this is OK\n";
1413
        }
1414
    }
1415
    my $astatus = "nat" unless ($e);
1416
    `/usr/bin/touch $etcpath/dhcp-hosts-$id` unless (-e "$etcpath/dhcp-hosts-$id");
1417 d3d1a2d4 Origo
    if ($action eq "activate") { #} && $domains) {
1418 6372a66e hq
        if ($type eq "internalip" || $type eq "ipmapping" || $type eq "remoteip") {
1419 d3d1a2d4 Origo
            # Configure internal dhcp server
1420
            if ($domains) {
1421
                my $result = addDHCPAddress($id, $domains, $internalip, "10.$idleft.$idright.1", $nicmac);
1422
                if ($result eq "OK") {
1423
                    $astatus = "up" if ($type eq "internalip");
1424
                } else {
1425
                    $e = 1;
1426
                    $postreply .= "$result\n";
1427
                }
1428 95b003ff Origo
            }
1429
1430
            # Also export storage pools to user's network
1431
            my @spl = split(/,\s*/, $storagepools);
1432
            my $reloadnfs;
1433
            my $uid = `id -u irigo-$user`; chomp $uid;
1434
            $uid = `id -u nobody` unless ($uid =~ /\d+/); chomp $uid;
1435
            my $gid = `id -g irigo-$user`; chomp $gid;
1436
            $gid = `id -g nobody` unless ($gid =~ /\d+/); chomp $gid;
1437
1438
            # We are dealing with multiple upstream routes - configure local routing
1439
            if ($proxynic && $proxynic ne $extnic) {
1440
                if (-e "/etc/iproute2/rt_tables" && !grep(/1 proxyarp/, `cat /etc/iproute2/rt_tables`)) {
1441
                    `/bin/echo "1 proxyarp" >> /etc/iproute2/rt_tables`;
1442
                }
1443
                if (!grep(/$datanic\.$id/, `/sbin/ip route show table proxyarp`)) {
1444
                    `/sbin/ip route add "10.$idleft.$idright.0/24" dev $datanic.$id table proxyarp`;
1445
                }
1446
            }
1447
1448 6372a66e hq
            # Manuipulate NFS exports and related disk quotas.
1449
            # Not needed for externalip's since they dont have access to the internal 10.x.x.x address space
1450 95b003ff Origo
            foreach my $p (@spl) {
1451
                if ($tenderlist[$p] && $tenderpathslist[$p]) {
1452
                    my $fuelpath = $tenderpathslist[$p] . "/$user/fuel";
1453
                    unless (-e $fuelpath) {
1454 1a56bdde Origo
                        if ($tenderlist[$p] eq 'local') { # We only support fuel on local tender for now
1455
                            `mkdir "$fuelpath"`;
1456
                            `chmod 777 "$fuelpath"`;
1457
                        }
1458 95b003ff Origo
                    }
1459
                    if ($tenderlist[$p] eq "local") {
1460
                        `chown irigo-$user:irigo-$user "$fuelpath"`;
1461
                        my $mpoint = `df -P "$fuelpath" | tail -1 | cut -d' ' -f 1`;
1462
                        chomp $mpoint;
1463
                        my $storagequota = $Stabile::userstoragequota;
1464
                        if (!$storagequota) {
1465
                            $storagequota = $Stabile::config->get('STORAGE_QUOTA');
1466
                        }
1467
                        my $nfsquota = $storagequota * 1024 ; # quota is in MB
1468
                        $nfsquota = 0 if ($nfsquota < 0); # quota of -1 means no limit
1469 d24d9a01 hq
                        `setquota -u irigo-$user $nfsquota $nfsquota 0 0 "$mpoint"` if (-e "$mntpoint");
1470
                        if (!(`grep "$fuelpath 10\.$idleft\.$idright" /etc/exports`) && -e $fuelpath) {
1471 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`;
1472
                            $reloadnfs = 1;
1473
                        }
1474
                    }
1475
                }
1476
            }
1477
            `/usr/sbin/exportfs -r` if ($reloadnfs); #Reexport nfs shares
1478
1479
        } elsif ($type eq "externalip") {
1480 d24d9a01 hq
            # A proxy is needed to route traffic, don't go any further if not configured
1481 95b003ff Origo
            if ($proxyip) {
1482 d24d9a01 hq
                # Set up proxy
1483 95b003ff Origo
                if (!($interfaces =~ m/$proxyip/ && $interfaces =~ m/br$id:proxy/)) {
1484
                    eval {`/sbin/ifconfig br$id:proxy $proxyip/$proxysubnet up`; 1;}
1485 e837d785 hq
                        or do {$e=1; $postreply .= "Status=ERROR Problem setting up proxy arp gw $proxyip on br$id:proxy $@\n";};
1486 95b003ff Origo
                    eval {`/sbin/ifconfig $proxynic:proxy $proxyip/$proxysubnet up`; 1;}
1487
                        or do {$e=1; $postreply .= "Status=ERROR Problem setting up proxy arp gw $proxynic $@\n";};
1488
                }
1489 d3d1a2d4 Origo
                my $result = "OK";
1490 d24d9a01 hq
                # Configure dhcp server
1491 d3d1a2d4 Origo
                if ($domains) {
1492
                    $result = addDHCPAddress($id, $domains, $externalip, "10.$idleft.$idright.1", $nicmac) if ($domains);
1493
                    if ($result eq "OK") {
1494
                        ;
1495
                    } else {
1496
                        $e = 1;
1497
                        $postreply .= "$result\n";
1498
                    }
1499 95b003ff Origo
                }
1500
            } else {
1501
                $postreply .= "Status=ERROR Cannot set up external IP without Proxy ARP gateway\n";
1502
            }
1503
        }
1504
1505 d24d9a01 hq
        # Handle routing with Iptables
1506 6372a66e hq
        if ($type eq "ipmapping" || $type eq "internalip" || $type eq "remoteip") {
1507 d24d9a01 hq
            `iptables -I FORWARD -d $internalip -m state --state ESTABLISHED,RELATED -j RETURN`;
1508
        }
1509 95b003ff Origo
        # Check if external ip exists and routing configured, if not create and configure it
1510
        if ($type eq "ipmapping") {
1511 2a63870a Christian Orellana
            if ($internalip && $internalip ne "--" && $externalip && $externalip ne "--" && !($interfaces =~ m/$externalip /g)) { # the space is important
1512 64c667ea hq
                $externalip =~ /\d+\.\d+\.(\d+)\.(\d+)/;
1513
                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
1514
                $ipend = $2 if (length("$extnic:$id-$ipend")>15);
1515 95b003ff Origo
                eval {`/sbin/ifconfig $extnic:$id-$ipend $externalip/$extsubnet up`; 1;}
1516 d3d1a2d4 Origo
                    or do {$e=1; $postreply .= "Status=ERROR Problem adding interface $extnic:$id-$ipend $@\n";};
1517 48fcda6b Origo
                unless (`ip addr show dev $extnic` =~ /$externalip/) {
1518
                    $e=10;
1519 d3d1a2d4 Origo
                    $postreply .= "Status=ERROR Problem adding interface $extnic:$id-$ipend\n";
1520 48fcda6b Origo
                }
1521 d24d9a01 hq
                # `/sbin/iptables -A POSTROUTING -t nat -s $internalip -j LOG --log-prefix "SNAT-POST"`;
1522
                # `/sbin/iptables -A INPUT -t nat -s $internalip -j LOG --log-prefix "SNAT-INPUT"`;
1523
                # `/sbin/iptables -A OUTPUT -t nat -s $internalip -j LOG --log-prefix "SNAT-OUTPUT"`;
1524
                # `/sbin/iptables -A PREROUTING -t nat -s $internalip -j LOG --log-prefix "SNAT-PRE"`;
1525 95b003ff Origo
                if ($ports && $ports ne "--") { # Port mapping is defined
1526
                    my @portslist = split(/, ?| /, $ports);
1527 a2e0bc7e hq
                    foreach my $port (@portslist) {
1528 95b003ff Origo
                        my $ipfilter;
1529
                        if ($port =~ /(\d+)\.(\d+)\.(\d+)\.(\d+)(\/\d+)?:(\d+)/) {
1530
                            my $portip = "$1.$2.$3.$4$5";
1531
                            $port = $6;
1532
                            $ipfilter = "-s $portip";
1533
                        } else {
1534
                            $port = 0 unless ($port =~ /\d+/);
1535
                        }
1536
                        if ($port<1 || $port>65535) {
1537
                            $postreply .= "Status=ERROR Invalid port mapping for $name\n";
1538
                            $ports = "--";
1539
                            last;
1540
                        }
1541
                        if ($port>1 || $port<65535) {
1542 d24d9a01 hq
                            # DNAT externalip -> internalip
1543 95b003ff Origo
                            eval {`/sbin/iptables -A PREROUTING -t nat -p tcp $ipfilter -d $externalip --dport $port -j DNAT --to $internalip`; 1;}
1544 d24d9a01 hq
                               or do {$e=2; $postreply .= "Status=ERROR Problem setting up routing $@\n";};
1545 95b003ff Origo
                            eval {`/sbin/iptables -A PREROUTING -t nat -p udp $ipfilter -d $externalip --dport $port -j DNAT --to $internalip`; 1;}
1546 d24d9a01 hq
                               or do {$e=3; $postreply .= "Status=ERROR Problem setting up routing $@\n";};
1547 2a63870a Christian Orellana
                            # PREROUTING is not parsed for packets coming from local host...
1548
                            eval {`/sbin/iptables -A OUTPUT -t nat -p tcp $ipfilter -d $externalip --dport $port -j DNAT --to $internalip`; 1;}
1549
                                or do {$e=2; $postreply .= "Status=ERROR Problem setting up routing $@\n";};
1550
                            eval {`/sbin/iptables -A OUTPUT -t nat -p udp $ipfilter -d $externalip --dport $port -j DNAT --to $internalip`; 1;}
1551
                                or do {$e=3; $postreply .= "Status=ERROR Problem setting up routing $@\n";};
1552 d24d9a01 hq
                            # Allow access to ipmapped internal ip on $port
1553
                            `iptables -I FORWARD -d $internalip -p tcp --dport $port -j RETURN`;
1554
                            `iptables -I FORWARD -d $internalip -p udp --dport $port -j RETURN`;
1555 95b003ff Origo
                        }
1556
                    }
1557
                    eval {`/sbin/iptables -D INPUT -d $externalip -j DROP`; 1;} # Drop traffic to all other ports
1558 48fcda6b Origo
                        or do {$e=5; $postreply .= "Status=ERROR Problem setting up routing $@\n";};
1559 95b003ff Origo
                    eval {`/sbin/iptables -A INPUT -d $externalip -j DROP`; 1;} # Drop traffic to all other ports
1560 48fcda6b Origo
                        or do {$e=6; $postreply .= "Status=ERROR Problem setting up routing $@\n";};
1561 95b003ff Origo
                } else {
1562 d24d9a01 hq
                    # DNAT externalip -> internalip coming from outside , --in-interface $extnic
1563 95b003ff Origo
                    eval {`/sbin/iptables -A PREROUTING -t nat -d $externalip -j DNAT --to $internalip`; 1;}
1564 48fcda6b Origo
                        or do {$e=7; $postreply .= "Status=ERROR Problem setting up routing $@\n";};
1565 d24d9a01 hq
                    # PREROUTING is not parsed for packets coming from local host...
1566 2a63870a Christian Orellana
                    eval {`/sbin/iptables -A OUTPUT -t nat -d $externalip -j DNAT --to $internalip`; 1;}
1567
                        or do {$e=7; $postreply .= "Status=ERROR Problem setting up routing $@\n";};
1568 d24d9a01 hq
                    # Allow blanket access to ipmapped internal ip
1569
                    `iptables -I FORWARD -d $internalip -j RETURN`;
1570
                }
1571
                # We masquerade packets going to internalip from externalip to avoid confusion
1572
                #eval {`/sbin/iptables -A POSTROUTING -t nat --out-interface br$id -s $externalip -j MASQUERADE`; 1;}
1573
                #    or do {$e=3; $postreply .= "Status=ERROR Problem setting up routing $@\n";};
1574 6fdc8676 hq
1575
                # Masquerade packets from internal ip's not going to our own subnet
1576
                # `/sbin/iptables -D POSTROUTING -t nat --out-interface br$id ! -d 10.$idleft.$idright.0/24 -j MASQUERADE`;
1577
                #eval {`/sbin/iptables -A POSTROUTING -t nat --out-interface br$id ! -d 10.$idleft.$idright.0/24 -j MASQUERADE`; 1;}
1578
                #    or do {$e=3; $postreply .= "Status=ERROR Problem setting up routing $@\n";};
1579
1580 d24d9a01 hq
                # When receiving packet from client, if it's been routed, and outgoing interface is the external interface, SNAT.
1581
                unless ($Stabile::disablesnat) {
1582
                    eval {`/sbin/iptables -A POSTROUTING -t nat -s $internalip ! -d 10.$idleft.$idright.0/24 -j SNAT --to-source $externalip`; 1; }
1583
                        or do {$e=4; $postreply .= "Status=ERROR Problem setting up routing $@\n";};
1584
                #    eval {`/sbin/iptables -A POSTROUTING -t nat -s $internalip -j SNAT --to-source $externalip`; 1; }
1585
                #        or do {$e=4; $postreply .= "Status=ERROR Problem setting up routing $@\n";};
1586
                    eval {`/sbin/iptables -I INPUT -t nat -s $internalip ! -d 10.$idleft.$idright.0/24 -j SNAT --to-source $externalip`; 1; }
1587
                        or do {$e=4; $postreply .= "Status=ERROR Problem setting up routing $@\n";};
1588
                #    eval {`/sbin/iptables -I INPUT -t nat -s $internalip -j SNAT --to-source $externalip`; 1; }
1589
                #        or do {$e=4; $postreply .= "Status=ERROR Problem setting up routing $@\n";};
1590 95b003ff Origo
                }
1591 6372a66e hq
                if ($e) {
1592
                    $main::syslogit->($user, 'info', "Problem $action network $uuid ($name, $id): $@");
1593
                } else {
1594
                    $astatus = "up"
1595
                }
1596
            }
1597
        } elsif ($type eq "remoteip") {
1598 a2e0bc7e hq
            if ($Stabile::remoteipenabled && -e "/home/irigo-$Stabile::engineuser/.ssh/id_rsa") {
1599
                # First activate the ip on remoteipprovider
1600
                my $res = $main::postToOrigo->($engineid, 'activateremoteip', "$externalip:$internalip", 'remotelocalip');
1601
                my $res_obj = JSON::from_json($res);
1602
                my $pid = '--';
1603
                my @remoteports = (80, 443, 10001);
1604
                my $rports;
1605
                if ($ports && $ports ne "--") {
1606
                    # Port mapping is defined
1607
                    my @portslist = split(/, ?| /, $ports);
1608
                    @remoteports = ();
1609
                    foreach my $port (@portslist) {
1610
                        if ($port =~ /(\d+)\.(\d+)\.(\d+)\.(\d+)(\/\d+)?:(\d+)/) {
1611
                            my $portip = "$1.$2.$3.$4$5";
1612
                            $port = $6;
1613
                        } else {
1614
                            $port = 0 unless ($port =~ /\d+/);
1615
                        }
1616
                        if ($port < 1 || $port > 65535) {
1617
                            $postreply .= "Status=ERROR Invalid port mapping for $name\n";
1618
                            $ports = "--";
1619
                            last;
1620
                        }
1621
                        if ($port > 1 || $port < 65535) {
1622
                            push @remoteports, $port;
1623
                        }
1624
                    }
1625
                }
1626
                foreach my $port (@remoteports) {
1627
                    $rports .= "-R $externalip:$port:$internalip:$port ";
1628
                }
1629
                if ($res_obj->{status} eq 'OK') {
1630
#                    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|;
1631
                    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|;
1632
                    eval {
1633
                        my $daemon = Proc::Daemon->new(
1634
                            work_dir => '/home/irigo-o@origo.io',
1635
                            exec_command => "$cmd"
1636
                        ) or do {$postreply .= "Status=ERROR $@";};
1637
                        $pid = $daemon->Init();
1638
                        $main::syslogit->($user, "info", "Activating remote ip $externalip at $Stabile::remoteipprovider for $Stabile::engineuser, pid=$pid");
1639
                        1;
1640
                    } or do {$e=4; $postreply .= "Status=ERROR Problem activating remote ip $@\n";};
1641
#                    sleep 1;
1642
                } else {
1643
                    $postreply .= "Status=Error $res_obj->{message}\n";
1644
                }
1645
                if ($e || !(-e "/proc/$pid")) {
1646
                    $main::syslogit->($user, 'info', "Problem $action network $uuid ($e, $name, $id): $@");
1647
                    $astatus = $status;
1648
                    $postreply .= "Status=OK Waiting to establish remote connetion\n";
1649 48fcda6b Origo
                } else {
1650
                    $astatus = "up"
1651
                }
1652 95b003ff Origo
            }
1653
        } elsif ($type eq "externalip") {
1654
            my $route = `/sbin/ip route`;
1655
            my $tables = `/sbin/iptables -L -n`;
1656
1657 d24d9a01 hq
            # Allow external IP send packets out
1658
            `/sbin/iptables -D FORWARD --in-interface br$id -s $externalip -j RETURN`;
1659
            `/sbin/iptables -I FORWARD --in-interface br$id -s $externalip -j RETURN`;
1660
1661 95b003ff Origo
            # We are dealing with multiple upstream routes - configure local routing
1662 e837d785 hq
            if ($proxynic && ($proxynic ne $extnic)) {
1663 95b003ff Origo
                if (-e "/etc/iproute2/rt_tables" && !grep(/1 proxyarp/, `cat /etc/iproute2/rt_tables`)) {
1664
                    `/bin/echo "1 proxyarp" >> /etc/iproute2/rt_tables`;
1665
                }
1666
                if (!grep(/$proxygw/, `/sbin/ip route show table proxyarp`)) {
1667 e837d785 hq
                    `/sbin/ip route del default dev $proxynic table proxyarp`; # delete first in case proxygw has changed
1668 95b003ff Origo
                    `/sbin/ip route add default via $proxygw dev $proxynic table proxyarp`;
1669
                }
1670
                if (!grep(/proxyarp/, `/sbin/ip rule show`)) {
1671
                    `/sbin/ip rule add to $proxygw/$proxysubnet table main`;
1672
                    `/sbin/ip rule add from $proxygw/$proxysubnet table proxyarp`;
1673
                }
1674
                my $proxyroute = `/sbin/ip route show table proxyarp`;
1675
#                `/sbin/ip route add $externalip/32 dev $datanic.$id:proxy src $proxyip table proxyarp` unless ($proxyroute =~ /$externalip/);
1676
                `/sbin/ip route add $externalip/32 dev br$id:proxy src $proxyip table proxyarp` unless ($proxyroute =~ /$externalip/);
1677
            }
1678
            eval {`/bin/echo 1 > /proc/sys/net/ipv4/conf/$datanic.$id/proxy_arp`; 1;}
1679
                or do {$e=1; $postreply .= "Status=ERROR Problem setting up proxy arp $@\n";};
1680
            eval {`/bin/echo 1 > /proc/sys/net/ipv4/conf/$proxynic/proxy_arp`; 1;}
1681
                or do {$e=1; $postreply .= "Status=ERROR Problem setting up proxy arp $@\n";};
1682
            eval {`/sbin/ip route add $externalip/32 dev br$id:proxy src $proxyip` unless ($route =~ /$externalip/); 1;}
1683
                or do {$e=1; $postreply .= "Status=ERROR Problem setting up proxy arp $@\n";};
1684
1685 d24d9a01 hq
            eval {`/sbin/iptables -D FORWARD -i $proxynic -d $externalip -m state --state ESTABLISHED,RELATED -j RETURN`; 1;}
1686 95b003ff Origo
                or do {$e=1; $postreply .= "Status=ERROR Problem setting up routing $@\n";};
1687 d24d9a01 hq
            eval {`/sbin/iptables -A FORWARD -i $proxynic -d $externalip -m state --state ESTABLISHED,RELATED -j RETURN`; 1;}
1688 95b003ff Origo
                or do {$e=1; $postreply .= "Status=ERROR Problem setting up routing $@\n";};
1689
1690
1691
            eval {`/sbin/iptables -D FORWARD -i $proxynic -d $externalip -j REJECT` if
1692
                ($tables =~ /REJECT .+ all .+ $externalip/); 1;}
1693
                or do {$e=1; $postreply .= "Status=ERROR Problem setting up routing $@\n";};
1694
1695
            if ($ports && $ports ne "--") {
1696
                my @portslist = split(/, ?| /, $ports);
1697
                foreach $port (@portslist) {
1698
                    my $ipfilter;
1699
                    if ($port =~ /(\d+)\.(\d+)\.(\d+)\.(\d+)(\/\d+)?:(\d+)/) {
1700
                        my $portip = "$1.$2.$3.$4$5";
1701
                        $port = $6;
1702
                        $ipfilter = "-s $portip";
1703
                    } else {
1704
                        $port = 0 unless ($port =~ /\d+/);
1705
                    }
1706
                    if ($port<1 || $port>65535) {
1707
                        $postreply .= "Status=ERROR Invalid port mapping for $name\n";
1708
                        $ports = "--";
1709
                        last;
1710
                    }
1711
1712
                    if ($port>1 && $port<65535 && $port!=67) { # Disallow setting up a dhcp server
1713 d24d9a01 hq
                        eval {`/sbin/iptables -A FORWARD -p tcp -i $proxynic $portfilter -d $externalip --dport $port -j RETURN`; 1;}
1714 95b003ff Origo
                            or do {$e=1; $postreply .= "Status=ERROR Problem setting up routing $@\n";};
1715 d24d9a01 hq
                        eval {`/sbin/iptables -A FORWARD -p udp -i $proxynic $portfilter -d $externalip --dport $port -j RETURN`; 1;}
1716 95b003ff Origo
                            or do {$e=1; $postreply .= "Status=ERROR Problem setting up routing $@\n";};
1717
                    }
1718
                }
1719 d24d9a01 hq
                eval {`/sbin/iptables -D FORWARD -i $proxynic -d $externalip -j REJECT`; 1;} # Drop traffic to all other ports
1720 95b003ff Origo
                    or do {$e=1; $postreply .= "Status=ERROR Problem setting up routing $@\n";};
1721 d24d9a01 hq
                eval {`/sbin/iptables -A FORWARD -i $proxynic -d $externalip -j REJECT`; 1;} # Drop traffic to all other ports
1722 95b003ff Origo
                    or do {$e=1; $postreply .= "Status=ERROR Problem setting up routing $@\n";};
1723
            } else {
1724 d24d9a01 hq
                # First allow everything else to this ip
1725
                eval {`/sbin/iptables -D FORWARD -i $proxynic -d $externalip -j RETURN`; 1;}
1726 95b003ff Origo
                    or do {$e=1; $postreply .= "Status=ERROR Problem setting up routing $@\n";};
1727 d24d9a01 hq
                eval {`/sbin/iptables -A FORWARD -i $proxynic -d $externalip -j RETURN`; 1;}
1728 95b003ff Origo
                    or do {$e=1; $postreply .= "Status=ERROR Problem setting up routing $@\n";};
1729 d24d9a01 hq
                # Then disallow setting up a dhcp server
1730
                eval {`/sbin/iptables -D FORWARD -p udp -i $proxynic -d $externalip --dport 67 -j REJECT`; 1;}
1731 95b003ff Origo
                    or do {$e=1; $postreply .= "Status=ERROR Problem setting up routing $@\n";};
1732 d24d9a01 hq
                eval {`/sbin/iptables -A FORWARD -p udp -i $proxynic -d $externalip --dport 67 -j REJECT`; 1;}
1733 95b003ff Origo
                    or do {$e=1; $postreply .= "Status=ERROR Problem setting up routing $@\n";};
1734
            }
1735
        }
1736
    }
1737
1738 d24d9a01 hq
    # Allow all inter-VLAN communication
1739
    `iptables -D FORWARD --in-interface br$id --out-interface br$id -j RETURN 2>/dev/null`;
1740
    `iptables -I FORWARD --in-interface br$id --out-interface br$id -j RETURN`;
1741
    # Disallow any access to vlan except mapped from external NIC i.e. ipmappings
1742
    `iptables -D FORWARD ! --in-interface $extnic --out-interface br$id -j DROP 2>/dev/null`;
1743
    `iptables -A FORWARD ! --in-interface $extnic --out-interface br$id -j DROP`;
1744
1745 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
1746 d24d9a01 hq
#    `/sbin/iptables --delete FORWARD --in-interface $datanic.$id ! -s 10.$idleft.$idright.0/24 -j DROP`;
1747 95b003ff Origo
    unless ($proxynic eq "$datanic.$id") {
1748 d24d9a01 hq
#        `/sbin/iptables --append FORWARD --in-interface $datanic.$id ! -s 10.$idleft.$idright.0/24 -j DROP`;
1749 95b003ff Origo
    }
1750
1751 a439a9c4 hq
    # Enable nat'ing
1752
    eval {
1753 64c667ea hq
        #my $masq = `/sbin/iptables -L -n -t nat`;
1754 a439a9c4 hq
        #        if (!($masq =~ "MASQUERADE.+all.+--.+0\.0\.0\.0/0")) {
1755
        `/sbin/iptables -D POSTROUTING -t nat --out-interface $extnic -s 10.0.0.0/8 -j MASQUERADE`;
1756
        `/sbin/iptables -A POSTROUTING -t nat --out-interface $extnic -s 10.0.0.0/8 -j MASQUERADE`;
1757
        # Christian's dev environment
1758
        #            my $interfaces = `/sbin/ifconfig`;
1759
        #            if ($interfaces =~ m/ppp0/) {
1760
        #                `/sbin/iptables --table nat --append POSTROUTING --out-interface ppp0 -s 10.0.0.0/8 -j MASQUERADE`;
1761
        #            }
1762
        #        };
1763
        1;
1764
    } or do {print "Unable to enable masquerading: $@\n";};
1765
1766 95b003ff Origo
    $uistatus = ($e)?"":validateStatus($register{$uuid});
1767 f222b89c hq
    if ($uistatus && $uistatus ne 'down' # && $uistatus ne 'nat'
1768
        ) {
1769 95b003ff Origo
        $uiuuid = $uuid;
1770
        $postreply .= "Status=$uistatus OK $action $type $name\n";
1771
    } else {
1772
        $postreply .= "Status=ERROR Cannot $action $type $name ($uistatus)\n";
1773
    }
1774
    $main::syslogit->($user, 'info', "$action network $uuid ($name, $id) -> $uistatus");
1775
    updateBilling("$uistatus $uuid ($id)");
1776 d24d9a01 hq
    # $main::updateUI->({tab=>"networks", user=>$user, uuid=>$uiuuid, status=>$uistatus}) if ($uistatus);
1777 95b003ff Origo
    return $postreply;
1778
}
1779
1780
sub Removeusernetworks {
1781
    my $username = shift;
1782
    return unless (($isadmin || $user eq $username) && !$isreadonly);
1783
    $user = $username;
1784
    foreach my $uuid (keys %register) {
1785
        if ($register{$uuid}->{'user'} eq $user) {
1786 6372a66e hq
            $postreply .=  "Removing network $register{$uuid}->{'name'}, $uuid" . ($console?'':'<br>') . "\n";
1787 95b003ff Origo
            Deactivate($uuid);
1788 6372a66e hq
            Remove($uuid, 'remove');
1789 95b003ff Origo
        }
1790
    }
1791
}
1792
1793
sub Remove {
1794 d3d1a2d4 Origo
    my ($uuid, $action, $obj) = @_;
1795 95b003ff Origo
    if ($help) {
1796
        return <<END
1797 d3d1a2d4 Origo
DELETE:uuid,force:
1798
Delete a network which must be in status down or nat and should not be used by any servers, or linked to any stacks.
1799 95b003ff Origo
May also be called with endpoints "/stabile/[uuid]" or "/stabile?uuid=[uuid]"
1800 d3d1a2d4 Origo
Set [force] to remove even if linked to a system.
1801 95b003ff Origo
END
1802
    }
1803 d3d1a2d4 Origo
    $uuid = $obj->{'uuid'} if ($curuuid && $obj->{'uuid'}); # we are called from a VM with an ip address as target
1804
    my $force = $obj->{'force'};
1805 95b003ff Origo
    ( my $domains, my $domainnames ) = getDomains($uuid);
1806 d3d1a2d4 Origo
    ( my $systems, my $systemnames ) = getSystems($uuid);
1807 95b003ff Origo
1808
    if ($register{$uuid}) {
1809
        my $id = $register{$uuid}->{'id'};
1810
        my $name = $register{$uuid}->{'name'};
1811
        utf8::decode($name);
1812
        my $status = $register{$uuid}->{'status'};
1813
        my $type = $register{$uuid}->{'type'};
1814
        my $internalip = $register{$uuid}->{'internalip'};
1815
        my $externalip = $register{$uuid}->{'externalip'};
1816
1817
        my @regvalues = values %register;
1818 d3d1a2d4 Origo
        if (
1819
            $id!=0 && $id!=1 && (!$domains || $domains eq '--')
1820 2a63870a Christian Orellana
                && ((!$systems || $systems eq '--' || $force)
1821 d3d1a2d4 Origo
                # allow internalip's to be removed if active and only linked, i.e. not providing dhcp
1822 2a63870a Christian Orellana
                || ($status eq 'down' || $status eq 'new' || $status eq 'nat' || ($type eq 'internalip' && $systems && $systems ne '--')))
1823 d3d1a2d4 Origo
        ) {
1824 95b003ff Origo
            # Deconfigure internal dhcp server and DNS
1825
            if ($type eq "internalip") {
1826
                my $result =  removeDHCPAddress($id, $domains, $internalip);
1827
                $postreply .= "$result\n" unless $result eq "OK";
1828
            } elsif ($type eq "ipmapping") {
1829
                my $result =  removeDHCPAddress($id, $domains, $internalip);
1830
                $postreply .= "$result\n" unless $result eq "OK";
1831
                if ($dodns) {
1832 e9af6c24 Origo
                    $main::dnsDelete->($engineid, $externalip) if ($enginelinked);
1833 95b003ff Origo
                }
1834 a2e0bc7e hq
            } elsif ($type eq "externalip" || $type eq "remoteip") {
1835 95b003ff Origo
                my $result =  removeDHCPAddress($id, $domains, $externalip);
1836
                $postreply .= "$result\n" unless $result eq "OK";
1837
                if ($dodns) {
1838 e9af6c24 Origo
                    $main::dnsDelete->($engineid, $externalip) if ($enginelinked);
1839 95b003ff Origo
                }
1840 a2e0bc7e hq
                # Deactivate the ip on remoteipprovider
1841
                my $res = $main::postToOrigo->($engineid, 'removeremoteip', "$externalip", 'remoteip');
1842
                my $res_obj = JSON::from_json($res);
1843
                if ($res_obj->{status} ne 'OK') {
1844
                    $postreply .= "Status=OK There was a problem removing the remote IP\n";
1845
                }
1846 95b003ff Origo
            }
1847
            if ($status eq 'nat') {
1848
                # Check if last network in vlan. If so take it down
1849
                my $notlast;
1850
                foreach my $val (@regvalues) {
1851
                    if ($val->{'user'} eq $user && $val->{'id'} == $id) {
1852
                        $notlast = 1;
1853
                    }
1854
                }
1855
                if (!$notlast) {
1856
                    eval {`/sbin/ifconfig $datanic.$id down`; 1;} or do {;};
1857
                    eval {`/sbin/vconfig rem $datanic.$id`; 1;} or do {;};
1858
                }
1859
            }
1860 d3d1a2d4 Origo
1861
            unless (tie(%sysreg,'Tie::DBI', Hash::Merge::merge({table=>'systems'}, $Stabile::dbopts)) ) {$res .= qq|{"status": "Error": "message": "Unable to access systems register"}|; return $res;};
1862
            if ($sysreg{$systems}) { # Remove existing link to system
1863
                $sysreg{$systems}->{'networkuuids'} =~ s/$uuid,?//;
1864
                $sysreg{$systems}->{'networknames'} = s/$name,?//;
1865
            }
1866
            tied(%sysreg)->commit;
1867
            untie(%sysreg);
1868
1869
1870 95b003ff Origo
            delete $register{$uuid};
1871
            tied(%register)->commit;
1872
            updateBilling("delete $val->{'externalip'}") if ($type eq "ipmapping");
1873
            $main::syslogit->($user, "info", "Deleted network $uuid ($id)");
1874 d3d1a2d4 Origo
            $postreply = "[]" || $postreply;
1875
            $main::updateUI->({tab=>"networks", user=>$user, type=>"update"});
1876 95b003ff Origo
        } else {
1877 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";
1878
            $main::updateUI->({tab=>"networks", user=>$user, message=>"Cannot remove a network which is active, linked or in use."});
1879 95b003ff Origo
        }
1880
    } else {
1881 d3d1a2d4 Origo
        $postreply .= "Status=ERROR Network $uuid $ipaddress not found\n";
1882 95b003ff Origo
    }
1883
    return $postreply;
1884
}
1885
1886
sub Deactivate {
1887 d3d1a2d4 Origo
    my ($uuid, $action, $obj) = @_;
1888 95b003ff Origo
1889
    if ($help) {
1890
        return <<END
1891
GET:uuid:
1892
Deactivate a network which must be in status up.
1893
END
1894
    }
1895 d3d1a2d4 Origo
    $uuid = $obj->{'uuid'} if ($obj->{'uuid'});
1896
1897
    unless ($register{$uuid}) {
1898
        $postreply .= "Status=ERROR Connection with uuid $uuid not found\n";
1899
        return $postreply;
1900
    }
1901
    my $regnet = $register{$uuid};
1902 95b003ff Origo
1903
    $action = $action || 'deactivate';
1904
    ( my $domains, my $domainnames ) = getDomains($uuid);
1905
    my $interfaces = `/sbin/ifconfig`;
1906
1907 d3d1a2d4 Origo
    my $id = $regnet->{'id'};
1908
    my $name = $regnet->{'name'};
1909
    my $type = $regnet->{'type'};
1910
    my $internalip = $regnet->{'internalip'};
1911
    my $externalip = $regnet->{'externalip'};
1912
    my $ports = $regnet->{'ports'};
1913 95b003ff Origo
1914
    if ($id!=0 && $id!=1 && $status ne 'down') {
1915
    # If gateway is created, take it down along with all user's networks
1916
        if ($action eq "stop") {
1917
            my $res = Stop($id, $action);
1918
            if ($res) {
1919
                unlink "$etcpath/dhcp-hosts-$id" if (-e "$etcpath/dhcp-hosts-$id");
1920
            };
1921
        }
1922
    } else {
1923
        $postreply .= "Status=ERROR Cannot $action network $name\n";
1924
        return $postreply;
1925
    }
1926
1927 2a63870a Christian Orellana
    my $idleft = ($id>99)?(substr $id,0,-2)+0 : 0;
1928
    my $idright = (substr $id,-2) + 0;
1929 95b003ff Origo
    my $e = 0;
1930 2a63870a Christian Orellana
    my $duprules = 0;
1931 d24d9a01 hq
1932 6372a66e hq
    if ($type eq "ipmapping" || $type eq "internalip" || $type eq "remoteip") {
1933 d24d9a01 hq
        `iptables -D FORWARD -d $internalip -m state --state ESTABLISHED,RELATED -j RETURN`;
1934
    }
1935 95b003ff Origo
    if ($type eq "ipmapping") {
1936 d24d9a01 hq
        # Check if external ip exists and take it down if so
1937 95b003ff Origo
        if ($internalip && $internalip ne "--" && $externalip && $externalip ne "--" && ($interfaces =~ m/$externalip/g)) {
1938 64c667ea hq
            $externalip =~ /\d+\.\d+\.(\d+)\.(\d+)/;
1939
            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
1940
            $ipend = $2 if (length("$extnic:$id-$ipend")>15);
1941 95b003ff Origo
            eval {`/sbin/ifconfig $extnic:$id-$ipend down`; 1;} or do {$e=1; $postreply .= "Status=ERROR $@\n";};
1942
1943
            if ($ports && $ports ne "--") { # Port mapping is defined
1944
                my @portslist = split(/, ?| /, $ports);
1945 2a63870a Christian Orellana
                foreach my $port (@portslist) {
1946 95b003ff Origo
                    my $ipfilter;
1947
                    if ($port =~ /(\d+)\.(\d+)\.(\d+)\.(\d+)(\/\d+)?:(\d+)/) {
1948
                        my $portip = "$1.$2.$3.$4$5";
1949
                        $port = $6;
1950
                        $ipfilter = "-s $portip";
1951
                    } else {
1952
                        $port = 0 unless ($port =~ /\d+/);
1953
                    }
1954
                    if ($port<1 || $port>65535) {
1955
                        $postreply .= "Status=ERROR Invalid port mapping for $name\n";
1956
                        $ports = "--";
1957
                        last;
1958
                    }
1959 d24d9a01 hq
                    # Remove DNAT rules
1960 95b003ff Origo
                    if ($port>1 || $port<65535) {
1961
                        # repeat for good measure
1962 2a63870a Christian Orellana
                        for (my $di=0; $di < 10; $di++) {
1963
                            $duprules = 0;
1964
                            eval {$duprules++ if (`/sbin/iptables -D PREROUTING -t nat -p tcp $ipfilter -d $externalip --dport $port -j DNAT --to $internalip`); 1;}
1965 95b003ff Origo
                                or do {$postreply .= "Status=ERROR $@\n"; $e=1};
1966 2a63870a Christian Orellana
                            eval {$duprules++ if (`/sbin/iptables -D PREROUTING -t nat -p udp $ipfilter -d $externalip --dport $port -j DNAT --to $internalip`); 1;}
1967 95b003ff Origo
                                or do {$postreply .= "Status=ERROR $@\n"; $e=1};
1968 2a63870a Christian Orellana
                            eval {$duprules++ if (`/sbin/iptables -D OUTPUT -t nat -p tcp $ipfilter -d $externalip --dport $port -j DNAT --to $internalip`); 1;}
1969
                                or do {$postreply .= "Status=ERROR $@\n"; $e=1};
1970
                            eval {$duprules++ if (`/sbin/iptables -D OUTPUT -t nat -p udp $ipfilter -d $externalip --dport $port -j DNAT --to $internalip`); 1;}
1971
                                or do {$postreply .= "Status=ERROR $@\n"; $e=1};
1972
                            eval {$duprules++ if (`/sbin/iptables -D POSTROUTING -t nat --out-interface br$id -s $externalip -j MASQUERADE`); 1;}
1973
                                or do {$e=3; $postreply .= "Status=ERROR Problem setting up routing $@\n";};
1974 d24d9a01 hq
                            # Remove access to ipmapped internal ip on $port
1975
                            eval {$duprules++ if (`/sbin/iptables -D FORWARD -d $internalip -p udp --dport $port -j RETURN`); 1;}
1976
                                or do {$e=3; $postreply .= "Status=ERROR Problem setting up routing $@\n";};
1977
                            eval {$duprules++ if (`/sbin/iptables -D FORWARD -d $internalip -p tcp --dport $port -j RETURN`); 1;}
1978
                                or do {$e=3; $postreply .= "Status=ERROR Problem setting up routing $@\n";};
1979
                            last if ($duprules >6);
1980 95b003ff Origo
                        }
1981
                    }
1982
                }
1983 d24d9a01 hq
                # Remove SNAT rules
1984 95b003ff Origo
                # repeat for good measure
1985 2a63870a Christian Orellana
                for (my $di=0; $di < 10; $di++) {
1986
                    $duprules = 0;
1987
                    eval {$duprules++ if (`/sbin/iptables -D POSTROUTING -t nat -s $internalip ! -d 10.$idleft.$idright.0/24 -j SNAT --to-source $externalip`); 1; }
1988 95b003ff Origo
                        or do {$e=1; $postreply .= "Status=ERROR Problem setting up routing $@\n";};
1989 2a63870a Christian Orellana
                    last if ($duprules);
1990 95b003ff Origo
                }
1991 d24d9a01 hq
                # Remove rule to drop traffic to all other ports
1992
                eval {`/sbin/iptables -D INPUT -d $externalip -j DROP`; 1;}
1993 95b003ff Origo
                    or do {$e=1; $postreply .= "Status=ERROR Problem setting up routing $@\n";};
1994
            } else {
1995 d24d9a01 hq
                # Remove DNAT rules
1996 95b003ff Origo
                # repeat for good measure
1997 2a63870a Christian Orellana
                for (my $di=0; $di < 10; $di++) {
1998
                    $duprules = 0;
1999
                    eval {$duprules++ if (`/sbin/iptables -D PREROUTING -t nat -d $externalip -j DNAT --to $internalip`); 1;}
2000 95b003ff Origo
                        or do {$postreply .= "Status=ERROR $@\n"; $e=1};
2001 2a63870a Christian Orellana
                    eval {$duprules++ if (`/sbin/iptables -D OUTPUT -t nat -d $externalip -j DNAT --to $internalip`); 1;}
2002
                        or do {$postreply .= "Status=ERROR $@\n"; $e=1};
2003 d24d9a01 hq
                    last if ($duprules >1);
2004 95b003ff Origo
                }
2005 d24d9a01 hq
                # Remove blanket access to ipmapped internal ip
2006
                `iptables -D FORWARD -d $internalip -j RETURN`;
2007
            }
2008
            # Remove SNAT and MASQUERADE rules
2009
            # repeat for good measure
2010
            for (my $di=0; $di < 10; $di++) {
2011
                $duprules = 0;
2012
            #    eval {$duprules++ if (`/sbin/iptables -D POSTROUTING -t nat --out-interface br$id -s $externalip -j MASQUERADE`); 1;}
2013
            #        or do {$e=3; $postreply .= "Status=ERROR Problem setting up routing $@\n";};
2014 6fdc8676 hq
                eval {$duprules++ if (`/sbin/iptables -D POSTROUTING -t nat --out-interface br$id ! -d 10.$idleft.$idright.0/24 -j MASQUERADE`); 1;}
2015 d24d9a01 hq
                    or do {$e=3; $postreply .= "Status=ERROR Problem setting up routing $@\n";};
2016
2017
                eval {$duprules++ if (`/sbin/iptables -D POSTROUTING -t nat -s $internalip ! -d 10.$idleft.$idright.0/24 -j SNAT --to-source $externalip`); 1; }
2018
                    or do {$e=1; $postreply .= "Status=ERROR Problem setting up routing $@\n";};
2019
            #    eval {$duprules++ if (`/sbin/iptables -D POSTROUTING -t nat -s $internalip -j SNAT --to-source $externalip`); 1; }
2020
            #        or do {$e=1; $postreply .= "Status=ERROR Problem setting up routing $@\n";};
2021
                eval {$duprules++ if (`/sbin/iptables -D INPUT -t nat -s $internalip ! -d 10.$idleft.$idright.0/24 -j SNAT --to-source $externalip`); 1; }
2022
                    or do {$e=1; $postreply .= "Status=ERROR Problem setting up routing $@\n";};
2023
            #    eval {$duprules++ if (`/sbin/iptables -D INPUT -t nat -s $internalip -j SNAT --to-source $externalip`); 1; }
2024
            #        or do {$e=1; $postreply .= "Status=ERROR Problem setting up routing $@\n";};
2025
            #    eval {$duprules++ if (`/sbin/iptables -D INPUT -t nat -s $internalip ! -d 10.$idleft.$idright.0/24 -j SNAT --to-source $externalip`); 1; }
2026
            #        or do {$e=1; $postreply .= "Status=ERROR Problem setting up routing $@\n";};
2027
            #    eval {$duprules++ if (`/sbin/iptables -D INPUT -t nat -s $internalip -j SNAT --to-source $externalip`); 1; }
2028
            #        or do {$e=1; $postreply .= "Status=ERROR Problem setting up routing $@\n";};
2029
                last if ($duprules >1);
2030 95b003ff Origo
            }
2031 d24d9a01 hq
            # `/sbin/iptables -D POSTROUTING -t nat -s $internalip -j LOG --log-prefix "SNAT-POST"`;
2032
            # `/sbin/iptables -D INPUT -t nat -s $internalip -j LOG --log-prefix "SNAT-INPUT"`;
2033
            # `/sbin/iptables -D OUTPUT -t nat -s $internalip -j LOG --log-prefix "SNAT-OUTPUT"`;
2034
            # `/sbin/iptables -D PREROUTING -t nat -s $internalip -j LOG --log-prefix "SNAT-PRE"`;
2035 95b003ff Origo
        }
2036 6372a66e hq
    } elsif ($type eq "remoteip") {
2037
        `pkill -f 'R $externalip'`;
2038 a2e0bc7e hq
        # Deactivate the ip on remoteipprovider
2039
        my $res = $main::postToOrigo->($engineid, 'deactivateremoteip', "$externalip", 'remoteip');
2040
        my $res_obj = JSON::from_json($res);
2041
        if ($res_obj->{status} ne 'OK') {
2042
            $postreply .= "Status=OK There was a problem deactivating the remote IP\n";
2043
        }
2044 95b003ff Origo
    } elsif ($type eq "externalip") {
2045
        if ($externalip && $externalip ne "--") {
2046
            # We are dealing with multiple upstream routes - configure local routing
2047
            if ($proxynic && $proxynic ne $extnic) {
2048
                my $proxyroute = `/sbin/ip route show table proxyarp`;
2049
                `/sbin/ip route del $externalip/32 dev br$id:proxy src $proxyip table proxyarp` if ($proxyroute =~ /$externalip/);
2050
            }
2051
2052
            eval {`/sbin/ip route del $externalip/32 dev br$id:proxy`; 1;}
2053
                or do {$e=1; $postreply .= "Status=ERROR Problem deconfiguring proxy arp $@\n";};
2054
2055
            if ($ports && $ports ne "--") {
2056
                my @portslist = split(/, ?| /, $ports);
2057 2a63870a Christian Orellana
                foreach my $port (@portslist) {
2058 95b003ff Origo
                    my $ipfilter;
2059
                    if ($port =~ /(\d+)\.(\d+)\.(\d+)\.(\d+)(\/\d+)?:(\d+)/) {
2060
                        my $portip = "$1.$2.$3.$4$5";
2061
                        $port = $6;
2062
                        $ipfilter = "-s $portip";
2063
                    } else {
2064
                        $port = 0 unless ($port =~ /\d+/);
2065
                    }
2066
                    if ($port<1 || $port>65535) {
2067
                        $postreply .= "Status=ERROR Invalid port mapping for $name\n";
2068
                        $ports = "--";
2069
                        last;
2070
                    }
2071
2072
                    if ($port>1 || $port<65535) {
2073
                        # repeat for good measure
2074 2a63870a Christian Orellana
                        for (my $di=0; $di < 10; $di++) {
2075
                            $duprules = 0;
2076 d24d9a01 hq
                            eval {$duprules++ if (`/sbin/iptables -D FORWARD -p tcp -i $proxynic $ipfilter -d $externalip --dport $port -j RETURN`); 1;}
2077 95b003ff Origo
                                or do {$e=1; $postreply .= "Status=ERROR Problem setting up routing $@\n";};
2078 d24d9a01 hq
                            eval {$duprules++ if (`/sbin/iptables -D FORWARD -p udp -i $proxynic $ipfilter -d $externalip --dport $port -j RETURN`); 1;}
2079 95b003ff Origo
                                or do {$e=1; $postreply .= "Status=ERROR Problem setting up routing $@\n";};
2080 2a63870a Christian Orellana
                            last if ($duprules > 1);
2081
                        }
2082 95b003ff Origo
                    }
2083
                }
2084
            }
2085 2a63870a Christian Orellana
            # Remove rule to allow forwarding from $externalip
2086 d24d9a01 hq
	        `/sbin/iptables --delete FORWARD --in-interface br$id -s $externalip -j RETURN`;
2087 95b003ff Origo
            # Remove rule to disallow setting up a dhcp server
2088
            eval {`/sbin/iptables -D FORWARD -p udp -i $proxynic -d $externalip --dport 67 -j REJECT`; 1;}
2089
                or do {$e=1; $postreply .= "Status=ERROR Problem setting up routing $@\n";};
2090
            # Leave outgoing connectivity - not
2091 d24d9a01 hq
            eval {`/sbin/iptables -D FORWARD -i $proxynic -d $externalip -m state --state ESTABLISHED,RELATED -j RETURN`; 1;}
2092 95b003ff Origo
                or do {$e=1; $postreply .= "Status=ERROR Problem setting up routing $@\n";};
2093 d24d9a01 hq
            eval {`/sbin/iptables -D FORWARD -i $proxynic -d $externalip -j RETURN`; 1;}
2094 95b003ff Origo
                or do {$e=1; $postreply .= "Status=ERROR Problem setting up routing $@\n";};
2095
            # No need to reject - we reject all per default to the subnet
2096
            eval {`/sbin/iptables -D FORWARD -i $proxynic -d $externalip -j REJECT`; 1;}
2097
                or do {$e=1; $postreply .= "Status=ERROR Problem setting up routing $@\n";};
2098
        }
2099
    }
2100
    # Deconfigure internal dhcp server
2101 6372a66e hq
    if ($type eq "internalip" || $type eq "ipmapping" || $type eq "remoteip") {
2102 95b003ff Origo
        my $result =  removeDHCPAddress($id, $domains, $internalip);
2103
        if ($result ne "OK") {
2104
            $e=1;
2105
            $postreply .= "$result\n";
2106
        }
2107 d3d1a2d4 Origo
    } elsif ($type eq "externalip" && $domains) {
2108 95b003ff Origo
        my $result =  removeDHCPAddress($id, $domains, $externalip);
2109
        if ($result ne "OK") {
2110
            $e=1;
2111
            $postreply .= "$result\n";
2112
        }
2113
    }
2114
    $uistatus = ($e)?"":validateStatus($register{$uuid});
2115
    if ($uistatus) {
2116
        $uiuuid = $uuid;
2117
        $postreply .= "Status=$uistatus OK $action $type $name: $uistatus\n";
2118
    } else {
2119
        $postreply .= "Status=ERROR Cannot $action $type $name: $uistatus\n";
2120
    }
2121
    $main::syslogit->($user, 'info', "$action network $uuid ($name, $id) -> $uistatus");
2122
    updateBilling("$uistatus $uuid ($id)");
2123 d24d9a01 hq
    # $main::updateUI->({tab=>"networks", user=>$user, uuid=>$uiuuid, status=>$uistatus}) if ($uistatus);
2124 95b003ff Origo
    return $postreply;
2125
}
2126
2127
sub Stop {
2128
    my ($id, $action) = @_;
2129
    # Check if we were passed a uuid
2130
    if ($id =~ /\-/ && $register{$id} && ($register{$id}->{'user'} eq $user || $isadmin)) {
2131
        $id = $register{$id}->{'id'}
2132
    }
2133
    if ($help) {
2134
        return <<END
2135
GET:uuid:
2136
Stops a network by removing gateway. Network must be in status up or nat.
2137
END
2138
    }
2139
2140
    my $idleft = ($id>99)?(substr $id,0,-2)+0 : 0;
2141
    my $idright = (substr $id,-2) + 0;
2142
    my $e = 0;
2143
    # First deactivate all user's networks with same id
2144
    my @regkeys = (tied %register)->select_where("user = '$user'");
2145
    foreach my $key (@regkeys) {
2146
        my $valref = $register{$key};
2147
        my $cuuid = $valref->{'uuid'};
2148
        my $ctype = $valref->{'type'};
2149
        my $cdbuser = $valref->{'user'};
2150
        my $cid = $valref->{'id'};
2151
    # Only list networks belonging to current user
2152
        if ($user eq $cdbuser && $id eq $cid && $ctype ne "gateway") {
2153
            if ($ctype eq "internalip" || $ctype eq "ipmapping" || $ctype eq "externalip") {
2154
                my $result = Deactivate($cuuid, 'deactivate');
2155
                if ($result =~ /\w+=ERROR (.+)/i) {
2156
                    $e = $1;
2157
                }
2158
            }
2159
        }
2160
     }
2161
    my $interfaces = `/sbin/ifconfig br$id`;
2162
     # Only take down interface and vlan if gateway IP is active on interface
2163
    if ($e) {
2164
        $postreply .= "Status=Error Not taking down gateway, got an error: $e\n"
2165
#    } elsif ($interfaces =~ /^$datanic\.$id.+\n.+inet .+10\.$idleft\.$idright\.1/
2166 f222b89c hq
    } elsif ($interfaces =~ /10\.$idleft\.$idright\.1/) {
2167 95b003ff Origo
        eval {`/sbin/brctl delif br$id $datanic.$id`; 1;} or do {$e=1;};
2168
        eval {`/sbin/ifconfig br$id down`; 1;} or do {$e=1;};
2169
        eval {`/sbin/ifconfig $datanic.$id down`; 1;} or do {$e=1;};
2170
        eval {`/sbin/vconfig rem $datanic.$id`; 1;} or do {$e=1;};
2171 f222b89c hq
        eval {`/sbin/brctl delbr br$id`; 1;} or do {$e=1;};
2172 95b003ff Origo
    } else {
2173
        $postreply .= "Status=Error Not taking down interface, gateway 10.$idleft.$idright.1 is not active on interface br$id - $interfaces.\n"
2174
    }
2175
    # Remove rule to only forward packets coming from subnet assigned to vlan
2176 d24d9a01 hq
#    `/sbin/iptables --delete FORWARD --in-interface $datanic.$id ! -s 10.$idleft.$idright.0/24 -j DROP`;
2177 95b003ff Origo
2178
    $uistatus = ($e)?$uistatus:"down";
2179
    if ($uistatus eq 'down') {
2180
        $uiuuid = $uuid;
2181
        $postreply .= "Status=$uistatus OK $action gateway: $uistatus\n";
2182
    } else {
2183
        $postreply .= "Status=Error Cannot $action $type $name: $uistatus\n";
2184
    }
2185
    return $postreply;
2186
}
2187
2188
sub getDomains {
2189
    my $uuid = shift;
2190
    my $domains;
2191
    my $domainnames;
2192
    my @domregvalues = values %domreg;
2193
    foreach my $domval (@domregvalues) {
2194
        if (($domval->{'networkuuid1'} eq $uuid || $domval->{'networkuuid2'} eq $uuid || $domval->{'networkuuid3'} eq $uuid)
2195
                && $domval->{'user'} eq $user) {
2196
            $domains .= $domval->{'uuid'} . ", ";
2197
            $domainnames .= $domval->{'name'} . ", ";
2198
        }
2199
    }
2200
    $domains = substr $domains, 0, -2;
2201
    $domainnames = substr $domainnames, 0, -2;
2202
    return ($domains, $domainnames); 
2203
}
2204
2205 d3d1a2d4 Origo
sub getSystems {
2206
    my $uuid = shift;
2207
    my $systems;
2208
    my $systemnames;
2209
    unless (tie(%sysreg,'Tie::DBI', Hash::Merge::merge({table=>'systems'}, $Stabile::dbopts)) ) {$res .= qq|{"status": "Error": "message": "Unable to access systems register"}|; return $res;};
2210
    my @sysregvalues = values %sysreg;
2211
    foreach my $sysval (@sysregvalues) {
2212
        my $networkuuids = $sysval->{'networkuuids'};
2213
        if ($networkuuids =~ /$uuid/ && $sysval->{'user'} eq $user) {
2214
            $systems = $sysval->{'uuid'};
2215
            $systemnames = $sysval->{'name'};
2216
            last;
2217
        }
2218
    }
2219
    unless ($systems) {
2220
        my @sysregvalues = values %domreg;
2221
        foreach my $sysval (@sysregvalues) {
2222
            my $networkuuids = $sysval->{'networkuuids'};
2223
            if ($networkuuids =~ /$uuid/ && $sysval->{'user'} eq $user) {
2224
                $systems = $sysval->{'uuid'};
2225
                $systemnames = $sysval->{'name'};
2226
                last;
2227
            }
2228
        }
2229
    }
2230
    return ($systems, $systemnames);
2231
}
2232
2233 95b003ff Origo
sub getNextId {
2234
	# Find the next available vlan id
2235
	my $reqid = shift;
2236
	my $username = shift;
2237
	$username = $user unless ($username);
2238
    my $nextid = 1;
2239
	my $vlanstart = $Stabile::config->get('VLAN_RANGE_START');
2240
	my $vlanend = $Stabile::config->get('VLAN_RANGE_END');
2241
2242
    if ($reqid eq 0 || $reqid == 1) {
2243
        return $requid;
2244
    } elsif ($reqid && ($reqid > $vlanend || $reqid < $vlanstart)) {
2245
        return -1 unless ($isadmin);
2246
    }
2247
2248
	$reqid = $reqid + 0;
2249
2250
    my %ids;
2251
    # First check if the user has an existing vlan, if so use the first we find as default value
2252
    my @regvalues = values %register;
2253
    @regvalues = (sort {$a->{id} <=> $b->{id}} @regvalues);
2254
    foreach my $val (@regvalues) { # Traverse all id's in use
2255
        my $id = 0 + $val->{'id'};
2256
        my $dbuser = $val->{'user'};
2257
        if ($id > 1) {
2258
            if ($username eq $dbuser) { # If a specific id was requested map all id's
2259
                if (!$reqid) {# If no specific id was asked for, stop now, and use the user's first one
2260
                    $nextid = $id;
2261
                    last;
2262
                }
2263
            } else {
2264
                $ids{$id} = 1; # Mark this id as used (by another user)
2265
            }
2266
        }
2267
    }
2268
    if ($nextid>1) {
2269
        return $nextid;
2270
    } elsif ($reqid) {
2271
        if (!$ids{$reqid} || $isadmin) { # If an admin is requesting id used by another, assume he knows what he is doing
2272
            $nextid = $reqid; # Safe to use
2273
        } else {
2274
            $nextid = -1; # Id already in use by another
2275
        }
2276
    } elsif ($nextid == 1) { # This user is not currently using any vlan's, find the first free one
2277
        for ($n=$vlanstart; $n<$vlanend; $n++) {
2278
            if (!$ids{$n}) { # Don't return an id used (by another user)
2279
                $nextid = $n;
2280
                last;
2281
            }
2282
        }
2283
    }
2284
	return $nextid;
2285
}
2286
2287 6372a66e hq
sub getNextRemoteIP {
2288 a2e0bc7e hq
    my $internalip = shift;
2289
    my $nextip = "";
2290 6372a66e hq
    my $oc = overQuotas(1);
2291
    if ($oc) { # Enforce quotas
2292
        $postreply .= "Status=ERROR Over quota allocating external IP\n";
2293
    } else {
2294 a2e0bc7e hq
        my $res = $main::postToOrigo->($engineid, 'provisionremoteip', $internalip, 'internalip');
2295
        my $res_obj = JSON::from_json($res);
2296
        $nextip = $res_obj->{remoteip} if ($res_obj->{remoteip});
2297 6372a66e hq
    }
2298
    $postreply .= "Status=ERROR No more ($oc) remote IPs available\n" unless ($nextip);
2299
    return $nextip;
2300
2301
}
2302 95b003ff Origo
sub getNextExternalIP {
2303
	# Find the next available IP
2304
	my $extip = shift;
2305
	my $extuuid = shift;
2306
	my $proxyarp = shift; # Are we trying to assign a proxy arp's external IP?
2307 6372a66e hq
	$extip = "" if ($extip eq "--");
2308 95b003ff Origo
2309
	my $extipstart;
2310
	my $extipend;
2311
2312
    if ($proxyarp) {
2313
        $extipstart = $Stabile::config->get('PROXY_IP_RANGE_START');
2314
        $extipend = $Stabile::config->get('PROXY_IP_RANGE_END');
2315
    } else {
2316
        $extipstart = $Stabile::config->get('EXTERNAL_IP_RANGE_START');
2317
        $extipend = $Stabile::config->get('EXTERNAL_IP_RANGE_END');
2318
    }
2319
2320
	return "" unless ($extipstart && $extipend);
2321
2322
	my $interfaces = `/sbin/ifconfig`;
2323
#	$interfaces =~ m/eth0 .+\n.+inet addr:(\d+\.\d+\.\d+)\.(\d+)/;
2324
	$extipstart =~  m/(\d+\.\d+\.\d+)\.(\d+)/;
2325
	my $bnet1 = $1;
2326
	my $bhost1 = $2+0;
2327
	$extipend =~  m/(\d+\.\d+\.\d+)\.(\d+)/;
2328
	my $bnet2 = $1;
2329
	my $bhost2 = $2+0;
2330
	my $nextip = "";
2331
	if ($bnet1 ne $bnet2) {
2332
		print "Status=ERROR Only 1 class C subnet is supported for $name\n";
2333
		return "";
2334
	}
2335
	my %ids;
2336
	# First create map of IP's reserved by other servers in DB
2337
	my @regvalues = values %register;
2338
	foreach my $val (@regvalues) {
2339
		my $ip = $val->{'externalip'};
2340
		# $ip =~ m/(\d+\.\d+\.\d+)\.(\d+)/;
2341
		# my $id = $2;
2342
		$ids{$ip} = $val->{'uuid'} unless ($extuuid eq $val->{'uuid'});
2343
	}
2344 54401133 hq
    my $oc = overQuotas(1);
2345
	if ($oc) { # Enforce quotas
2346 95b003ff Origo
        $postreply .= "Status=ERROR Over quota allocating external IP\n";
2347
	} elsif ($extip && $extip =~  m/($bnet1)\.(\d+)/ && $2>=$bhost1 && $2<$bhost2) {
2348
	# An external ip was supplied - check if it's free and ok
2349
		if (!$ids{$extip} && !($interfaces =~ m/$extip.+\n.+inet addr:$extip/) && $extip=~/$bnet$\.(\d)/) {
2350
			$nextip = $extip;
2351
		}
2352
	} else {
2353
	# Find random IP not reserved, and check it is not in use (for other purposes)
2354
	    my @bhosts = ($bhost1..$bhost2);
2355
        my @rbhosts = shuffle @bhosts;
2356
		for ($n=0; $n<$bhost2-$bhost1; $n++) {
2357
		    my $nb = $rbhosts[$n];
2358
			if (!$ids{"$bnet1.$nb"}) {
2359
				if (!($interfaces =~ m/$extip.+\n.+inet addr:$bnet1\.$nb/)) {
2360
					$nextip = "$bnet1.$nb";
2361
					last;
2362
				}
2363
			}
2364
		}
2365
	}
2366 54401133 hq
	$postreply .= "Status=ERROR No more ($oc) external IPs available\n" unless ($nextip);
2367 95b003ff Origo
	return $nextip;
2368
}
2369
2370
sub ip2domain {
2371
    my $ip = shift;
2372
    my $ruuid;
2373
    if ($ip) {
2374
        my @regkeys = (tied %register)->select_where("internalip = '$ip' OR externalip = '$ip'");
2375
        foreach my $k (@regkeys) {
2376
            my $valref = $register{$k};
2377
            if ($valref->{'internalip'} eq $ip || $valref->{'externalip'} eq $ip) {
2378
                $ruuid = $valref->{'domains'};
2379
                last;
2380
            }
2381
        }
2382
    }
2383
    return $ruuid;
2384
}
2385
2386
sub getNextInternalIP {
2387
	my $intip = shift;
2388
	my $uuid = shift;
2389
	my $id = shift;
2390
	my $username = shift;
2391
	$username = $user unless ($username);
2392
	my $nextip = "";
2393
	my $intipnum;
2394
	my $subnet;
2395
	my %ids;
2396
    my $ping = Net::Ping->new();
2397
2398
    $id = getNextId() unless ($id);
2399
    my $idleft = ($id>99)?(substr $id,0,-2)+0 : 0;
2400
    my $idright = (substr $id,-2) + 0;
2401
    $intip = "10.$idleft.$idright.0" if (!$intip || $intip eq '--');
2402
    
2403
    return '' unless ($intip =~ m/(\d+\.\d+\.\d+)\.(\d+)/ );
2404
    $subnet = $1;
2405
    $intipnum = $2;
2406
2407
	# First create hash of IP's reserved by other servers in DB
2408
	my @regvalues = values %register;
2409
	foreach my $val (@regvalues) {
2410
    	if ($val->{'user'} eq $username) {
2411
            my $ip = $val->{'internalip'} ;
2412
            $ids{$ip} = $val->{'uuid'};
2413
		}
2414
	}
2415
2416
	if ($intipnum && $intipnum>1 && $intipnum<255) {
2417
	# An internal ip was supplied - check if it's free, if not keep the ip already registered in the db
2418
        if (!$ids{$intip}
2419
#            && !($ping->ping($intip, 0.1)) # 0.1 secs timeout, check if ip is in use, possibly on another engine
2420
            && !(`arping -C1 -c2 -D -I $datanic.$id $intip` =~ /reply from/)  # check if ip is created on another engine
2421
        ) {
2422
            $nextip = $intip;
2423
        } else {
2424
            $nextip = $register{$uuid}->{'internalip'}
2425
        }
2426
	} else {
2427
	# Find first IP not reserved
2428
		for ($n=2; $n<255; $n++) {
2429
			if (!$ids{"$subnet.$n"}
2430
# TODO: The arping check takes too long - two networks created by the same user can too easily be assigned the same IP's
2431
#                && !(`arping -f -c2 -D -I $datanic.$id $subnet.$n` =~ /reply from/)  # check if ip is created on another engine
2432
			) {
2433
                $nextip = "$subnet.$n";
2434
                last;
2435
			}
2436
		}
2437
	}
2438
	$postreply .= "Status=ERROR No more internal IPs available\n" if (!$nextip);
2439
	return $nextip;
2440
}
2441
2442
sub validateStatus {
2443
    my $valref = shift;
2444 f222b89c hq
    my $interfaces = `/sbin/ifconfig -a | grep inet`;
2445 95b003ff Origo
    my $uuid = $valref->{'uuid'};
2446
    my $type = $valref->{'type'};
2447
    my $id = $valref->{'id'};
2448
    my $idleft = ($id>99)?(substr $id,0,-2)+0 : 0;
2449
    my $idright = (substr $id,-2) + 0;
2450
2451
    ( $valref->{'domains'}, $valref->{'domainnames'} ) = getDomains($uuid);
2452 d3d1a2d4 Origo
    my ( $systems, $systemnames ) = getSystems($uuid);
2453 95b003ff Origo
    my $extip = $valref->{'externalip'};
2454
    my $intip = $valref->{'internalip'};
2455
2456
    if ($type eq "gateway") {
2457
        $valref->{'internalip'} = "10.$idleft.$idright.1" if ($id>1);
2458
    } else {
2459
        if ($intip && $intip ne "--" && $extip && $extip ne "--") {
2460 6372a66e hq
            $type = "ipmapping" unless ($type eq 'remoteip');
2461 95b003ff Origo
        } elsif ($intip && $intip ne "--") {
2462
            $type = "internalip";
2463
        } elsif ($extip && $extip ne "--") {
2464
            $type = "externalip";
2465 6372a66e hq
        } else {
2466
            $type = "gateway";
2467 95b003ff Origo
        }
2468
        $valref->{'type'} = $type;
2469
    }
2470
2471
    $valref->{'status'} = "down";
2472
    my $nat;
2473
    if ($id == 0 || $id == 1) {
2474
        $valref->{'status'} = "nat";
2475
    # Check if vlan $id is created (and doing nat)
2476
#    } elsif ($interfaces =~ m/$datanic\.$id.+\n.+10\.$idleft\.$idright\.1/) {
2477
    } elsif (-e "/proc/net/vlan/$datanic.$id") {
2478
        $nat = 1;
2479
    }
2480 d24d9a01 hq
2481 6372a66e hq
    if ($type eq "internalip" || $type eq "ipmapping" || $type eq "remoteip") {
2482 95b003ff Origo
        $valref->{'status'} = "nat" if ($nat);
2483
        my $dhcprunning;
2484
        my $dhcpconfigured;
2485
        eval {
2486
            my $psid;
2487
            $psid = `/bin/cat /var/run/stabile-$id.pid` if (-e "/var/run/stabile-$id.pid");
2488
            chomp $psid;
2489
            $dhcprunning = -e "/proc/$psid" if ($psid);
2490
            my $dhcphosts;
2491
            $dhcphosts = lc `/bin/cat $etcpath/dhcp-hosts-$id` if (-e "$etcpath/dhcp-hosts-$id");
2492
            $dhcpconfigured = ($dhcphosts =~ /$intip/);
2493
            1;
2494
        } or do {;};
2495
2496 6372a66e hq
        if ($type eq "internalip" || $type eq "remoteip") {
2497 95b003ff Origo
        # Check if external ip has been created and dhcp is ok
2498 d3d1a2d4 Origo
            if ($nat && (($dhcprunning && $dhcpconfigured) || $systems)) {
2499 a2e0bc7e hq
                if ($type eq "remoteip") {
2500
                    if (`pgrep -f 'ssh .* $externalip'`) {
2501
                        $valref->{'status'} = "up";
2502
                    }
2503
                } else {
2504
                    $valref->{'status'} = "up";
2505
                }
2506 95b003ff Origo
            }
2507
        } elsif ($type eq "ipmapping") {
2508
        # Check if external ip has been created, dhcp is ok and vlan interface is created
2509 d3d1a2d4 Origo
        # An ipmapping linked to a system is considered up if external interface exists
2510 6372a66e hq
        # Update: It appears that ip addresses on virtual interfaces are periodically lost for some reason
2511
        # the interface however still responds to the ip address if iptables rules referencing this exists
2512
        # so we have relaxed the up requirement
2513
            if ($nat
2514
        #            && $interfaces =~ m/$extip/ # interfaces seem to drop out of sight after while even if still active
2515 f222b89c hq
                    && (($dhcprunning && $dhcpconfigured) || ($systems && $interfaces =~ m/$extip/))
2516
            ) {
2517 95b003ff Origo
                $valref->{'status'} = "up";
2518
            }
2519
        }
2520
2521
    } elsif ($type eq "externalip") {
2522
        my $dhcprunning;
2523
        my $dhcpconfigured;
2524
        eval {
2525
            my $psid;
2526
            $psid = `/bin/cat /var/run/stabile-$id.pid` if (-e "/var/run/stabile-$id.pid");
2527
            chomp $psid;
2528
            $dhcprunning = -e "/proc/$psid" if ($psid);
2529
            my $dhcphosts;
2530
            $dhcphosts = `/bin/cat $etcpath/dhcp-hosts-$id` if (-e "$etcpath/dhcp-hosts-$id");
2531
            $dhcpconfigured = ($dhcphosts =~ /$extip/);
2532
            1;
2533
        } or do {;};
2534
2535
        my $vproxy = `/bin/cat /proc/sys/net/ipv4/conf/$datanic.$id/proxy_arp`; chomp $vproxy;
2536
        my $eproxy = `/bin/cat /proc/sys/net/ipv4/conf/$proxynic/proxy_arp`; chomp $eproxy;
2537
        my $proute = `/sbin/ip route | grep "$extip dev"`; chomp $proute;
2538 d3d1a2d4 Origo
        if ($vproxy && $eproxy && $proute) {
2539
            if ((($dhcprunning && $dhcpconfigured) || $systems)) {
2540
                $valref->{'status'} = "up";
2541
            } elsif (!$valref->{'domains'}) {
2542
                $valref->{'status'} = "nat";
2543
            }
2544 95b003ff Origo
        } else {
2545
            #print "$vproxy && $eproxy && $proute && $dhcprunning && $dhcpconfigured :: $extip\n";        
2546
        }
2547
2548
    } elsif ($type eq "gateway") {
2549
        if ($nat || $id == 0 || $id == 1) {$valref->{'status'} = "up";}
2550
    }
2551
    return $valref->{'status'};
2552
}
2553
2554
sub trim{
2555
   my $string = shift;
2556
   $string =~ s/^\s+|\s+$//g;
2557
   return $string;
2558
}
2559
2560
sub overQuotas {
2561
    my $reqips = shift; # number of new ip's we are asking for
2562
	my $usedexternalips = 0;
2563
	my $overquota = 0;
2564
    return $overquota if ($Stabile::userprivileges =~ /a/); # Don't enforce quotas for admins
2565
2566 54401133 hq
	my $externalipquota = $Stabile::userexternalipquota;
2567 95b003ff Origo
	if (!$externalipquota) {
2568
        $externalipquota = $Stabile::config->get('EXTERNAL_IP_QUOTA');
2569
    }
2570
2571 54401133 hq
	my $rxquota = $Stabile::userrxquota;
2572 95b003ff Origo
	if (!$rxquota) {
2573
        $rxquota = $Stabile::config->get('RX_QUOTA');
2574
    }
2575
2576 54401133 hq
	my $txquota = $Stabile::usertxquota;
2577 95b003ff Origo
	if (!$txquota) {
2578
        $txquota = $Stabile::config->get('TX_QUOTA');
2579
    }
2580
2581
    my @regkeys = (tied %register)->select_where("user = '$user'");
2582
	foreach my $k (@regkeys) {
2583
	    my $val = $register{$k};
2584
		if ($val->{'user'} eq $user && $val->{'externalip'} && $val->{'externalip'} ne "--" ) {
2585
		    $usedexternalips += 1;
2586
		}
2587
	}
2588 54401133 hq
	if ((($usedexternalips + $reqips) > $externalipquota) && $externalipquota > 0) { # -1 means no quota
2589 95b003ff Origo
	    $overquota = $usedexternalips;
2590
	} elsif ($rx > $rxquota*1024 && $rxquota > 0) {
2591
	    $overquota = -1;
2592
	} elsif ($tx > $txquota*1024 && $txquota > 0) {
2593
	    $overquota = -2;
2594
	}
2595
	return $overquota;
2596
}
2597
2598
sub updateBilling {
2599
    my $event = shift;
2600
    my %billing;
2601
    my @regkeys = (tied %register)->select_where("user = '$user' or user = 'common'") unless ($fulllist);
2602
    foreach my $k (@regkeys) {
2603
        my $valref = $register{$k};
2604
        my %val = %{$valref}; # Deference and assign to new array, effectively cloning object
2605
        if ($val{'user'} eq $user && ($val{'type'} eq 'ipmapping' || $val{'type'} eq 'externalip') && $val{'externalip'} ne '--') {
2606
            $billing{$val{'id'}}->{'externalip'} += 1;
2607
        }
2608
    }
2609
2610
    my %billingreg;
2611
    my $monthtimestamp = timelocal(0,0,0,1,$mon,$year); #$sec,$min,$hour,$mday,$mon,$year
2612
2613
    unless ( tie(%billingreg,'Tie::DBI', Hash::Merge::merge({table=>'billing_networks', key=>'useridtime'}, $Stabile::dbopts)) ) {return "Unable to access billing register"};
2614
2615
    my $rx_bytes_total = 0;
2616
    my $tx_bytes_total = 0;
2617
2618
    my $prevmonth = $month-1;
2619
    my $prevyear = $year;
2620
    if ($prevmonth == 0) {$prevmonth=12; $prevyear--;};
2621
    $prevmonth = substr("0" . $prevmonth, -2);
2622
    my $prev_rx_bytes_total = 0;
2623
    my $prev_tx_bytes_total = 0;
2624
2625
    foreach my $id (keys %billing) {
2626
        my $b = $billing{$id};
2627
        my $externalip = $b->{'externalip'};
2628
        my $externalipavg = 0;
2629
        my $startexternalipavg = 0;
2630
        my $starttimestamp = $current_time;
2631
        my $rx_bytes = 0;
2632
        my $tx_bytes = 0;
2633 6372a66e hq
#        my $rx_stats = "/sys/class/net/$datanic.$id/statistics/rx_bytes";
2634
#        my $tx_stats = "/sys/class/net/$datanic.$id/statistics/tx_bytes";
2635
        my $rx_stats = "/sys/class/net/br$id/statistics/rx_bytes";
2636
        my $tx_stats = "/sys/class/net/br$id/statistics/tx_bytes";
2637 95b003ff Origo
        $rx_bytes = `/bin/cat $rx_stats` if (-e $rx_stats);
2638
        chomp $rx_bytes;
2639
        $tx_bytes = `/bin/cat $tx_stats` if (-e $tx_stats);
2640
        chomp $tx_bytes;
2641
2642
        if ($current_time - $monthtimestamp < 4*3600) {
2643
            $starttimestamp = $monthtimestamp;
2644
            $externalipavg = $externalip;
2645
            $startexternalipavg = $externalip;
2646
        }
2647
2648
        my $bill = $billingreg{"$user-$id-$year-$month"};
2649
        my $regrx_bytes = $bill->{'rx'};
2650
        my $regtx_bytes = $bill->{'tx'};
2651
        $rx_bytes += $regrx_bytes if ($regrx_bytes > $rx_bytes); # Network interface was reloaded
2652
        $tx_bytes += $regtx_bytes if ($regtx_bytes > $tx_bytes); # Network interface was reloaded
2653
2654
        # Update timestamp and averages on existing row
2655
        if ($billingreg{"$user-$id-$year-$month"}) {
2656
            $startexternalipavg = $bill->{'startexternalipavg'};
2657
            $starttimestamp = $bill->{'starttimestamp'};
2658
2659
            $externalipavg = ($startexternalipavg*($starttimestamp - $monthtimestamp) + $externalip*($current_time - $starttimestamp)) /
2660
                            ($current_time - $monthtimestamp);
2661
2662
            $billingreg{"$user-$id-$year-$month"}->{'externalip'} = $externalip;
2663
            $billingreg{"$user-$id-$year-$month"}->{'externalipavg'} = $externalipavg;
2664
            $billingreg{"$user-$id-$year-$month"}->{'timestamp'} = $current_time;
2665
            $billingreg{"$user-$id-$year-$month"}->{'rx'} = $rx_bytes;
2666
            $billingreg{"$user-$id-$year-$month"}->{'tx'} = $tx_bytes;
2667
        }
2668
2669
        # No row found or something happened which justifies writing a new row
2670
        if (!$billingreg{"$user-$id-$year-$month"}
2671
        || ($b->{'externalip'} != $bill->{'externalip'})
2672
        ) {
2673
2674
            my $inc = 0;
2675
            if ($billingreg{"$user-$id-$year-$month"}) {
2676
                $startexternalipavg = $externalipavg;
2677
                $starttimestamp = $current_time;
2678
                $inc = $bill->{'inc'};
2679
            }
2680
            # Write a new row
2681
            $billingreg{"$user-$id-$year-$month"} = {
2682
                externalip=>$externalip+0,
2683
                externalipavg=>$externalipavg,
2684
                startexternalipavg=>$startexternalipavg,
2685
                timestamp=>$current_time,
2686
                starttimestamp=>$starttimestamp,
2687
                event=>$event,
2688
                inc=>$inc+1,
2689
                rx=>$rx_bytes,
2690
                tx=>$tx_bytes
2691
            };
2692
        }
2693
2694
        $rx_bytes_total += $rx_bytes;
2695
        $tx_bytes_total += $tx_bytes;
2696
        my $prevbill = $billingreg{"$user-$id-$prevyear-$prevmonth"};
2697
        $prev_rx_bytes_total += $prevbill->{'rx'};
2698
        $prev_tx_bytes_total += $prevbill->{'tx'};
2699
    }
2700
    untie %billingreg;
2701
    $rx = ($rx_bytes_total>$prev_rx_bytes_total)?$rx_bytes_total - $prev_rx_bytes_total:$rx_bytes_total;
2702
    $tx = ($tx_bytes_total>$prev_tx_bytes_total)?$tx_bytes_total - $prev_tx_bytes_total:$tx_bytes_total;
2703
    my $oq = overQuotas();
2704 54401133 hq
    if ($oq && $oq<0) {
2705 95b003ff Origo
        foreach my $id (keys %billing) {
2706
            $main::syslogit->($user, 'info', "$user over rx/tx quota ($oq) stopping network $id");
2707
            Stop($id, 'stop');
2708
        }
2709
    }
2710
}
2711
2712
sub Bit2netmask {
2713
	my $netbit = shift;
2714
	my $_bit         = ( 2 ** (32 - $netbit) ) - 1;
2715
	my ($full_mask)  = unpack( "N", pack( "C4", split(/./, '255.255.255.255') ) );
2716
	my $netmask      = join( '.', unpack( "C4", pack( "N", ( $full_mask ^ $_bit ) ) ) );
2717
	return $netmask;
2718
}