Project

General

Profile

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