Project

General

Profile

Download (116 KB) Statistics
| Branch: | Revision:
1
#!/usr/bin/perl
2

    
3
# All rights reserved and Copyright (c) 2020 Origo Systems ApS.
4
# This file is provided with no warranty, and is subject to the terms and conditions defined in the license file LICENSE.md.
5
# The license file is part of this source code package and its content is also available at:
6
# https://www.origo.io/info/stabiledocs/licensing/stabile-open-source-license
7

    
8
package Stabile::Networks;
9

    
10
use Error qw(:try);
11
use Data::Dumper;
12
use Time::Local;
13
use Time::HiRes qw( time );
14
use Data::UUID;
15
use Net::Netmask;
16
use Net::Ping;
17
use Proc::Daemon;
18
use File::Basename;
19
use List::Util qw(shuffle);
20
use lib dirname (__FILE__);
21
use Stabile;
22

    
23
($datanic, $extnic) = $main::getNics->();
24
$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
$enginelinked = $Stabile::config->get('ENGINE_LINKED') || "";
32

    
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
    my $obj;
74
    $action = $action || $h{'action'};
75
    if (
76
        $action =~ /^dns/
77
    ) {
78
        $obj = \%h;
79
        return $obj;
80
    }
81
    $uuid = $curuuid if ($uuid eq 'this');
82
    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
    my $dbobj = $register{$uuid} || {};
91
    my $status = $dbobj->{'status'} || $h{"status"}; # Trust db status if it exists
92
    if ((!$uuid && $uuid ne '0') && (!$status || $status eq 'new') && ($action eq 'save')) {
93
        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
    my $systems = $h{"systems"} || $dbobj->{'systems'};
121
    my $force = $h{"force"};
122
    my $reguser = $dbobj->{'user'};
123
    # Sanity checks
124
    if (
125
        ($name && length $name > 255)
126
        || ($ports && length $ports > 255)
127
        || ($type && !($type =~ /gateway|ipmapping|internalip|externalip|remoteip/))
128
    ) {
129
         $postreply .= "Stroke=ERROR Bad network data: $name\n";
130
         return;
131
     }
132
     # Security check
133
     if (($user ne $reguser && index($privileges,"a")==-1 && $action ne 'save' ) ||
134
         ($reguser && $status eq "new"))
135
     {
136
         $postreply .= "Stroke=ERROR Bad user: $user, $action\n";
137
         return;
138
     }
139

    
140
    if (!$type ||($type ne 'gateway' && $type ne 'internalip' && $type ne 'ipmapping' && $type ne 'externalip' && $type ne 'remoteip')) {
141
         $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
    }
146

    
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
        systems => $systems,
157
        force => $force,
158
        action => $h{"action"}
159
    };
160
    return $obj;
161
}
162

    
163
sub Init {
164

    
165
    # Tie database tables to hashes
166
    unless ( tie(%register,'Tie::DBI', Hash::Merge::merge({table=>'networks'}, $Stabile::dbopts)) ) {return "Unable to access network register"};
167
    unless ( tie(%domreg,'Tie::DBI', Hash::Merge::merge({table=>'domains'}, $Stabile::dbopts)) ) {return "Unable to access domain register"};
168

    
169
    # simplify globals initialized in Stabile.pm
170
    $tktuser = $tktuser || $Stabile::tktuser;
171
    $user = $user || $Stabile::user;
172

    
173
    # Create aliases of functions
174
    *header = \&CGI::header;
175

    
176
    *Natall = \&Deactivateall;
177
    *Stopall = \&Deactivateall;
178
    *Restoreall = \&Activateall;
179

    
180
    *do_save = \&Save;
181
    *do_tablelist = \&do_list;
182
    *do_jsonlist = \&do_list;
183
    *do_listnetworks = \&do_list;
184
    *do_this = \&do_list;
185
    *do_help = \&action;
186
    *do_remove = \&action;
187

    
188
    *do_restoreall = \&privileged_action;
189
    *do_activateall = \&privileged_action;
190
    *do_deactivateall = \&privileged_action;
191
    *do_natall = \&privileged_action;
192
    *do_stopall = \&privileged_action;
193
    *do_stop = \&privileged_action;
194
    *do_activate = \&privileged_action;
195
    *do_deactivate = \&privileged_action;
196

    
197
    *do_gear_activate = \&do_gear_action;
198
    *do_gear_deactivate = \&do_gear_action;
199
    *do_gear_stop = \&do_gear_action;
200
    *do_gear_activateall = \&do_gear_action;
201
    *do_gear_restoreall = \&do_gear_action;
202
    *do_gear_deactivateall = \&do_gear_action;
203
    *do_gear_stopall = \&do_gear_action;
204
    *do_gear_natall = \&do_gear_action;
205

    
206
    $rx; # Global rx count in bytes
207
    $tx; # Global tx count in bytes
208
    $etcpath = "/etc/stabile/networks";
209
}
210

    
211
sub do_list {
212
    my ($uuid, $action, $obj) = @_;
213
    if ($help) {
214
        return <<END
215
GET:uuid:
216
List networks current user has access to.
217
END
218
    }
219

    
220
    my $res;
221
    my $filter;
222
    my $statusfilter;
223
    my $uuidfilter;
224
    $uuid = $obj->{'uuid'} if ($obj->{'uuid'});
225

    
226
    if ($curuuid && ($isadmin || $register{$curuuid}->{'user'} eq $user) && $uripath =~ /networks(\.cgi)?\/(\?|)(this)/) {
227
        $uuidfilter = $curuuid;
228
    } elsif ($uripath =~ /networks(\.cgi)?\/(\?|)(name|status)/) {
229
        $filter = $3 if ($uripath =~ /networks(\.cgi)?\/.*name(:|=)(.+)/);
230
        $statusfilter = $3 if ($uripath =~ /networks(\.cgi)?\/.*status(:|=)(\w+)/);
231
    } elsif ($uripath =~ /networks(\.cgi)?\/(\w{8}-\w{4}-\w{4}-\w{4}-\w{12})/) {
232
        $uuidfilter = $2;
233
    } elsif ($uuid) {
234
        $uuidfilter = $uuid;
235
    }
236
    $uuidfilter = $options{u} unless $uuidfilter;
237
    $filter = $1 if ($filter =~ /(.*)\*/);
238
    $statusfilter = '' if ($statusfilter eq '*');
239

    
240
    my $curnetwork = URI::Escape::uri_unescape($params{'network'});
241
    my $curnetwork1 = URI::Escape::uri_unescape($params{'network1'});
242

    
243
    my $sysuuid;
244
    if ($params{'system'}) {
245
        $sysuuid = $params{'system'};
246
        $sysuuid = $cursysuuid || $curdomuuid if ($params{'system'} eq 'this');
247
    }
248

    
249
    $res .= header('application/json') unless ($console || $action eq 'tablelist');
250
    my @curregvalues;
251

    
252
    updateBilling();
253
    my @regkeys;
254
    if ($fulllist) {
255
        @regkeys = keys %register;
256
    } elsif ($uuidfilter && $isadmin) {
257
        @regkeys = (tied %register)->select_where("uuid = '$uuidfilter'");
258
    } else {
259
        @regkeys = (tied %register)->select_where("user = '$user' OR user = 'common'");
260
    }
261

    
262
    foreach my $k (@regkeys) {
263
        my $valref = $register{$k};
264
        my $uuid = $valref->{'uuid'};
265
        my $dbuser = $valref->{'user'};
266
        my $type = $valref->{'type'};
267
        my $id = $valref->{'id'};
268
    # Only list networks belonging to current user
269
        if ($dbuser eq "common" || $user eq $dbuser || $fulllist || ($uuidfilter && $isadmin)) {
270
            my $dom = $domreg{$valref->{'domains'}};
271
            next unless (!$sysuuid || $dom->{'system'} eq $sysuuid || $valref->{'domains'} eq $sysuuid);
272
            validateStatus($valref);
273
            my %val = %{$valref}; # Deference and assign to new ass array, effectively cloning object
274
            $val{'id'} += 0;
275
            $val{'rx'} = $rx;
276
            $val{'tx'} = $tx;
277
            $val{'domainnames'} = decode('utf8', $val{'domainnames'});
278
            if ($filter || $statusfilter || $uuidfilter) { # List filtered networks
279
                my $fmatch;
280
                my $smatch;
281
                my $umatch;
282
                $fmatch = 1 if (!$filter || $val{'name'}=~/$filter/i);
283
                $smatch = 1 if (!$statusfilter || $statusfilter eq 'all'
284
                        || $statusfilter eq $val{'status'}
285
                        );
286
                $umatch = 1 if ($val{'uuid'} eq $uuidfilter);
287
                if ($fmatch && $smatch && !$uuidfilter) {
288
                    push @curregvalues,\%val;
289
                } elsif ($umatch) {
290
                    push @curregvalues,\%val;
291
                    last;
292
                }
293

    
294
            } elsif ($action eq "listnetworks") { # List available networks
295
                if (($id>0 || index($privileges,"a")!=-1) && ((!$valref->{'domains'} && !$valref->{'systems'}) || $type eq 'gateway' || ($curnetwork eq $uuid && !$curnetwork1) || $curnetwork1 eq $uuid)) {
296
                    push @curregvalues,\%val;
297
                }
298
            } else {
299
                push @curregvalues,\%val if ($id>0 || index($privileges,"a")!=-1);
300
            }
301
        }
302
    }
303

    
304
    # Sort @curregvalues
305
    my $sort = 'status';
306
    $sort = $2 if ($uripath =~ /sort\((\+|\-)(\S+)\)/);
307
    my $reverse;
308
    $reverse = 1 if ($1 eq '-');
309
    if ($reverse) { # sort reverse
310
        if ($sort =~ /id/) {
311
            @curregvalues = (sort {$b->{$sort} <=> $a->{$sort}} @curregvalues); # Sort as number
312
        } else {
313
            @curregvalues = (sort {$b->{$sort} cmp $a->{$sort}} @curregvalues); # Sort as string
314
        }
315
    } else {
316
        if ($sort =~ /id/) {
317
            @curregvalues = (sort {$a->{$sort} <=> $b->{$sort}} @curregvalues); # Sort as number
318
        } else {
319
            @curregvalues = (sort {$a->{$sort} cmp $b->{$sort}} @curregvalues); # Sort as string
320
        }
321
    }
322

    
323
    my %val = ("uuid", "--", "name", "--");
324
    if ($curnetwork1) { # allow second network to be empty
325
        push @curregvalues, \%val;
326
    }
327
    if ($action eq 'tablelist') {
328
        $res .= header("text/plain") unless ($console);
329
        my $t2 = Text::SimpleTable->new(36,20,10,5,10,14,14,7);
330
        $t2->row('uuid', 'name', 'type', 'id', 'internalip', 'externalip', 'user', 'status');
331
        $t2->hr;
332
        my $pattern = $options{m};
333
        foreach $rowref (@curregvalues){
334
            if ($pattern) {
335
                my $rowtext = $rowref->{'uuid'} . " " . $rowref->{'name'} . " " . $rowref->{'type'} . " " . $rowref->{'id'}
336
                   . " " .  $rowref->{'internalip'} . " " . $rowref->{'externalip'} . " " . $rowref->{'user'} . " " . $rowref->{'status'};
337
                $rowtext .= " " . $rowref->{'mac'} if ($isadmin);
338
                next unless ($rowtext =~ /$pattern/i);
339
            }
340
            $t2->row($rowref->{'uuid'}, $rowref->{'name'}||'--', $rowref->{'type'}, $rowref->{'id'},
341
            $rowref->{'internalip'}||'--', $rowref->{'externalip'}||'--', $rowref->{'user'}, $rowref->{'status'});
342
        }
343
        $res .= $t2->draw;
344
    } elsif ($console && !$uuidfilter && $action ne 'jsonlist') {
345
        $res .= Dumper(\@curregvalues);
346
    } else {
347
        my $json_text;
348
        if ($uuidfilter) {
349
            $json_text = to_json($curregvalues[0], {pretty => 1}) if (@curregvalues);
350
        } else {
351
            $json_text = to_json(\@curregvalues, {pretty => 1}) if (@curregvalues);
352
        }
353
        $json_text = "[]" unless $json_text;
354
        $json_text =~ s/""/"--"/g;
355
        $json_text =~ s/null/"--"/g;
356
        $json_text =~ s/undef/"--"/g;
357
        $json_text =~ s/\x/ /g;
358
        $res .= qq|{"action": "$action", "identifier": "uuid", "label": "name", "items": | if ($action && $action ne 'jsonlist' && $action ne 'list' && !$uuidfilter);
359
        $res .= $json_text;
360
        $res .= qq|}| if ($action && $action ne 'jsonlist' && $action ne 'list'  && !$uuidfilter);
361
#        $res .= "JSON" if (action eq 'jsonlist');
362
    }
363
    return $res;
364
}
365

    
366
sub do_uuidlookup {
367
    if ($help) {
368
        return <<END
369
GET:uuid:
370
Simple action for looking up a uuid or part of a uuid and returning the complete uuid.
371
END
372
    }
373

    
374
    my $res;
375
    $res .= header('text/plain') unless $console;
376
    my $u = $options{u};
377
    $u = $curuuid unless ($u || $u eq '0');
378
    my $ruuid;
379
    if ($u || $u eq '0') {
380
        foreach my $uuid (keys %register) {
381
            if (($register{$uuid}->{'user'} eq $user || $register{$uuid}->{'user'} eq 'common' || $fulllist)
382
                && ($uuid =~ /^$u/ || $register{$uuid}->{'name'} =~ /^$u/)) {
383
                $ruuid = $uuid;
384
                last;
385
            }
386
        }
387
        if (!$ruuid && $isadmin) { # If no match and user is admin, do comprehensive lookup
388
            foreach $uuid (keys %register) {
389
                if ($uuid =~ /^$u/ || $register{$uuid}->{'name'} =~ /^$u/) {
390
                    $ruuid = $uuid;
391
                    last;
392
                }
393
            }
394
        }
395
    }
396
    $res .= "$ruuid\n" if ($ruuid);
397
    return $res;
398
}
399

    
400
sub do_uuidshow {
401
    if ($help) {
402
        return <<END
403
GET:uuid:
404
Simple action for showing a single network.
405
END
406
    }
407

    
408
    my $res;
409
    $res .= header('application/json') unless $console;
410
    my $u = $options{u};
411
    $u = $curuuid unless ($u || $u eq '0');
412
    if ($u || $u eq '0') {
413
        foreach my $uuid (keys %register) {
414
            if (($register{$uuid}->{'user'} eq $user || $register{$uuid}->{'user'} eq 'common' || index($privileges,"a")!=-1)
415
                && $uuid =~ /^$u/) {
416
                my %hash = %{$register{$uuid}};
417
                delete $hash{'action'};
418
                delete $hash{'nextid'};
419
#                my $dump = Dumper(\%hash);
420
                my $dump = to_json(\%hash, {pretty=>1});
421
                $dump =~ s/undef/"--"/g;
422
                $res .= $dump;
423
                last;
424
            }
425
        }
426
    }
427
    return $res;
428
}
429

    
430
sub do_updateui {
431
    my ($uuid, $action) = @_;
432
    if ($help) {
433
        return <<END
434
GET:uuid:
435
Update the web UI for the given uuid (if user has web UI loaded).
436
END
437
    }
438

    
439
    my $res;
440
    $res .= header('text/plain') unless $console;
441
    if ($register{$uuid}) {
442
        my $uistatus = $register{$uuid}->{'status'};
443
        $main::updateUI->({tab=>"networks", user=>$user, uuid=>$uuid, status=>$uistatus});
444
        $res .= "Status=OK Updated UI for $register{$uuid}->{'type'} $register{$uuid}->{'name'}: $uistatus";
445
    } else {
446
        $main::updateUI->({tab=>"networks", user=>$user});
447
        $res .= "Status=OK Updated networks UI for $user";
448
    }
449
    return $res;
450

    
451
}
452

    
453
sub do_dnslist {
454
    my ($uuid, $action) = @_;
455
    if ($help) {
456
        return <<END
457
GET:domain:
458
Lists entries in [domain] or if not specified, the default zone: $dnsdomain.
459
END
460
    }
461

    
462
    my $res = $main::dnsList->($engineid, $user, $params{'domain'});
463
    return $res;
464
}
465

    
466
sub do_envdump {
467
    my ($uuid, $action) = @_;
468
    if ($help) {
469
        return <<END
470
GET::
471
Dump environment variables
472
END
473
    }
474
    return to_json(\%ENV, {pretty=>1});
475
}
476

    
477

    
478
sub do_dnscreate {
479
    my ($uuid, $action) = @_;
480
    if ($help) {
481
        return <<END
482
GET:name, value, type:
483
Create a DNS record in the the subdomain belonging to the user's default DNS domain.
484
<b>name</b> is a domain name in the Engine's zone. <b>value</b> is either an IP address for A records or a domain name for other. <b>[type]</b> is A, CNAME, TXT or MX.
485
END
486
    }
487

    
488
    my $res = $main::dnsCreate->($engineid, $params{'name'}, $params{'value'}, $params{'type'}, $user);
489
    return $res;
490
}
491

    
492
sub do_dnsupdate {
493
    my ($uuid, $action, $obj) = @_;
494
    if ($help) {
495
        return <<END
496
GET:name,value,type,oldname,oldvalue:
497
Updates CNAME records pointing to a A record with value 'value', to point to the new 'name' in the the default DNS domain.
498
END
499
    }
500

    
501
    my $res = $main::dnsUpdate->($engineid, $obj->{'name'}, $obj->{'value'}, $obj->{'type'}, $obj->{'oldname'}, $obj->{'oldvalue'}, $user);
502
    return $res;
503
}
504

    
505
sub do_dnsclean {
506
    my ($uuid, $action) = @_;
507
    if ($help) {
508
        return <<END
509
GET::
510
Remove this engines entries in $dnsdomain zone.
511
END
512
    }
513

    
514
    my $res;
515
    $res .= header('text/plain') unless $console;
516
    $res .= $main::dnsClean->($engineid, $user);
517
    return $res;
518
}
519

    
520
sub do_dnscheck {
521
    my ($uuid, $action) = @_;
522
    if ($help) {
523
        return <<END
524
GET:name:
525
Checks if a domain name (name[.subdomain]) is available, i.e. not registered,
526
where subdomain is the subdomain belonging to the the registering engine.
527
END
528
    }
529

    
530
    my $res;
531
    $res .= header('text/plain') unless $console;
532
    my $name = $params{'name'};
533
    $name = $1 if ($name =~ /(.+)\.$dnsdomain$/);
534
    if (!$enginelinked) {
535
        $res .= "Status=ERROR You cannot create DNS records - your engine is not linked.\n";
536
    } elsif ($name =~ /^\S+$/ && !(`host $name.$dnsdomain authns1.cabocomm.dk` =~ /has address/)
537
        && $name ne 'www'
538
        && $name ne 'mail'
539
        && $name ne 'info'
540
        && $name ne 'admin'
541
        && $name ne 'work'
542
        && $name ne 'io'
543
        && $name ne 'cloud'
544
        && $name ne 'compute'
545
        && $name ne 'sso'
546
        && $name !~ /valve/
547
    ) {
548
        $res .= "Status=OK $name.$dnsdomain is available\n";
549
    } else {
550
        $res .= "Status=ERROR $name.$dnsdomain is not available\n";
551
    }
552
    return $res;
553
}
554

    
555
sub do_dnsdelete {
556
    my ($uuid, $action) = @_;
557
    if ($help) {
558
        return <<END
559
GET:name, value, type:
560
Delete a DNS record in the configured zone.
561
END
562
    }
563

    
564
    my $res = $main::dnsDelete->($engineid, $params{'name'}, $params{'value'}, $params{'type'}, $user);
565
    return $res;
566
}
567

    
568
sub do_getappstoreurl {
569
    my ($uuid, $action) = @_;
570
    if ($help) {
571
        return <<END
572
GET::
573
Get URL to the app store belonging to engine or user (uverrides engine default).
574
END
575
    }
576

    
577
    my $res;
578
    # $res .= header('application/json') unless $console;
579
    # $res .= qq|{"url": "$appstoreurl"}\n|;
580
    $res .= "$appstoreurl\n";
581
    return $res;
582
}
583

    
584
sub do_listdnsdomains {
585
    my ($uuid, $action) = @_;
586
    if ($help) {
587
        return <<END
588
GET::
589
Get the DNS domains current user has access to.
590
END
591
    }
592
    unless ( tie(%userreg,'Tie::DBI', Hash::Merge::merge({table=>'users', key=>'username', CLOBBER=>1}, $Stabile::dbopts)) ) {return 0};
593
    my $billto = $userreg{$user}->{'billto'};
594
    my $bdomains = ($userreg{$billto})?$userreg{$billto}->{'dnsdomains'}:'';
595
    my $domains = ($enginelinked)?($userreg{$user}->{'dnsdomains'} || $bdomains || $dnsdomain) :'';
596
    untie %userreg;
597
    my @doms = split(/, ?/, $domains);
598
    my $subdomain = ($enginelinked)?substr($engineid, 0, 8):'';
599
    my $linked = ($enginelinked)?'true':'false';
600
    my $res;
601
    $res .= header('application/json') unless $console;
602
    $res .= qq|{"domains": | . to_json(\@doms) . qq|, "subdomain": "$subdomain", "enginelinked": "$linked", "billto": "$billto", "user": "$user"}|;
603
    return $res;
604
}
605

    
606
sub do_getdnsdomain {
607
    my ($uuid, $action) = @_;
608
    if ($help) {
609
        return <<END
610
GET::
611
Get the default DNS domain and the subdomain this Engine registers entries in.
612
END
613
    }
614
    my $domain = ($enginelinked)?$dnsdomain:'';
615
    my $subdomain = ($enginelinked)?substr($engineid, 0, 8):'';
616
    my $linked = ($enginelinked)?'true':'false';
617
    my $res;
618
    $res .= header('application/json') unless $console;
619
    $res .= qq|{"domain": "$domain", "subdomain": "$subdomain", "enginelinked": "$linked"}|;
620
    return $res;
621
}
622

    
623
sub xmppsend {
624
    my ($uuid, $action) = @_;
625
    if ($help) {
626
        return <<END
627
GET:to, msg:
628
Send out an xmpp alert.
629
END
630
    }
631
    if ($help) {
632
        return <<END
633
Send out an xmpp alert
634
END
635
    }
636

    
637
    my $res;
638
    $res .= header('text/plain') unless $console;
639
    $res .= $main::xmppSend->($params{'to'}, $params{'msg'}, $engineid);
640
    return $res;
641
}
642

    
643
# List available network types. Possibly limited by exhausted IP ranges.
644
sub do_listnetworktypes {
645
    if ($help) {
646
        return <<END
647
GET::
648
List available network types. Possibly limited by exhausted IP ranges.
649
END
650
    }
651

    
652
    my $res;
653
    $res .= header('application/json') unless $console;
654
    # Check if we have exhausted our IP ranges
655
    my $intipavail = getNextInternalIP();
656
    my $extipavail = getNextExternalIP();
657
    my $arpipavail = getNextExternalIP('','',1);
658
    my $json_text;
659
    $json_text .= '{"type": "gateway", "name": "Gateway"}, ';
660
    $json_text .= '{"type": "internalip", "name": "Internal IP"}, ' if ($intipavail);
661
    unless (overQuotas()) {
662
        $json_text .= '{"type": "ipmapping", "name": "IP mapping"}, ' if ($intipavail && $extipavail);
663
        $json_text .= '{"type": "externalip", "name": "External IP"}, 'if ($arpipavail);
664
        $json_text .= '{"type": "remoteip", "name": "Remote IP"}, 'if ($Stabile::remoteipenabled);
665
    }
666
    $json_text = substr($json_text,0,-2);
667
    $res .= '{"identifier": "type", "label": "name", "items": [' . $json_text  . ']}';
668
    return $res;
669
}
670

    
671
# Simple action for removing all networks belonging to a user
672
sub do_removeusernetworks {
673
    my ($uuid, $action) = @_;
674
    if ($help) {
675
        return <<END
676
GET:username:
677
Remove all networks belonging to a user.
678
END
679
    }
680
    my $username = shift;
681
    return unless ($username && ($isadmin || $user eq $username) && !$isreadonly);
682
    $user = $username;
683
    my $res;
684
    $res .= header('text/plain') unless $console;
685
    if ($readonly) {
686
        $postreply .= "Status=ERROR Not allowed\n";
687
    } else {
688
        Removeusernetworks($user);
689
    }
690
    $res .= $postreply || "Status=OK Nothing to remove\n";
691
    return $res;
692
}
693

    
694
# Activate all networks. If restoreall (e.g. after reboot) is called, we only activate networks which have entries in /etc/stabile/network
695
sub Activateall {
696
    my ($nouuid, $action) = @_;
697
    if ($help) {
698
        return <<END
699
GET::
700
Tries to activate all networks. If called as restoreall by an admin, will try to restore all user's networks to saved state, e.g. after a reboot.
701
END
702
    }
703
    my @regkeys;
704
    if (($action eq "restoreall" || $fulllist) && index($privileges,"a")!=-1) { # Only an administrator is allowed to do this
705
        @regkeys = keys %register;
706
    } else {
707
        @regkeys = (tied %register)->select_where("user='$user'");
708
    }
709
    my $i = 0;
710
    if (!$isreadonly) {
711
    	foreach my $key (@regkeys) {
712
            my $valref = $register{$key};
713
    		my $uuid = $valref->{'uuid'};
714
    		my $type = $valref->{'type'};
715
    		my $id = $valref->{'id'};
716
    		my $name = $valref->{'name'};
717
    		my $internalip = $valref->{'internalip'};
718
    		my $externalip = $valref->{'externalip'};
719
    		if ($id!=0 && $id!=1 && $id<4095) {
720
                my $caction = "nat";
721
    			if (-e "$etcpath/dhcp-hosts-$id") {
722
    				if ($action eq "restoreall" && $isadmin) { # If restoring, only activate previously active networks
723
                        my $hosts;
724
                        $hosts = lc `/bin/cat $etcpath/dhcp-hosts-$id` if (-e "$etcpath/dhcp-hosts-$id");
725
                        $caction = "activate" if ($hosts =~ /($internalip|$externalip)/);
726
    			    } elsif ($action eq "activateall") {
727
    				    $caction = "activate";
728
        			}
729
                    # TODO: investigate why this is necessary - if we don't do it, networks are not activated
730
                    $user = $valref->{'user'};
731
                    do_list($uuid, 'list');
732

    
733
                    my $res = Activate($uuid, $caction);
734
                    if ($res =~ /\w+=(\w+) / ) {
735
                        $register{$uuid}->{'status'} = $1 unless (uc $1 eq 'ERROR');
736
                        $i ++ unless (uc $1 eq 'ERROR');
737
                    } else {
738
                        $postreply .= "Status=ERROR Cannot $caction $type $name $uuid: $res\n";
739
                    }
740
    		    }
741
            } else {
742
                $postreply .= "Status=ERROR Cannot $action $type $name\n" unless ($id==0 || $id==1);
743
        	}
744
        }
745
    } else {
746
        $postreply .= "Status=ERROR Problem activating all networks\n";
747
    }
748
    if ($postreply =~/Status=ERROR /) {
749
        $postreply = header('text/plain', '500 Internal Server Error') . $postreply unless $console;
750
    }
751
    $postreply .= "Status=OK activated $i networks\n";
752
    $main::updateUI->({tab=>"networks", user=>$user});
753
    updateBilling("$action $user");
754
    return $postreply;
755
}
756

    
757
# Deactivate all networks
758
sub Deactivateall {
759
    my ($nouuid, $action) = @_;
760
    if ($help) {
761
        return <<END
762
GET::
763
Tries to deactivate all networks. May also be called as natall or stopall.
764
END
765
    }
766

    
767
    my @regkeys;
768
    if ($fulllist && index($privileges,"a")!=-1) { # Only an administrator is allowed to do this
769
        @regkeys = keys %register;
770
    } else {
771
        @regkeys = (tied %register)->select_where("user='$user'");
772
    }
773
    if (!$isreadonly) {
774
		my %ids;
775
		foreach my $key (@regkeys) {
776
            my $valref = $register{$key};
777
			my $uuid = $valref->{'uuid'};
778
			my $type = $valref->{'type'};
779
			my $id = $valref->{'id'};
780
			my $name = $valref->{'name'};
781
			if ($id!=0 && $id!=1 && $id<4095) {
782
				if (-e "$etcpath/dhcp-hosts-$id") {
783
					my $caction = "deactivate";
784
					my $result;
785
					if ($action eq "stopall") {
786
						$caction = "stop";
787
						# Stop also deactivates all networks with same id, so only do this once for each id
788
						if ($ids{$id}) {
789
							$result = $valref->{'status'};
790
						} else {
791
							$result = Stop($id, $caction);
792
						}
793
						$ids{$id} = 1;
794
					} else {
795
                        my $res = Deactivate($uuid, $caction);
796
                        if ($res =~ /\w+=(\w+) /) {
797
                            $register{$uuid}->{'status'} = $1;
798
                        } else {
799
                            $postreply .= "Status=ERROR Cannot $caction $type $name $uuid: $res\n";
800
                        }
801
					}
802
					if ($result =~ /\w+=(.\w+) /) {
803
                        $register{$uuid}->{'status'} = $uistatus = $1;
804
						$uiuuid = $uuid;
805
						$postreply .= "Status=OK $caction $type $name $uuid\n";
806
						$main::syslogit->($user, "info", "$caction network $uuid ($id) ");
807
					}
808
				}
809
			} else {
810
				$postreply .= "Status=ERROR Cannot $action $type $name\n" unless ($id==0 || $id==1);
811
			}
812
		}
813
	} else {
814
		$postreply .= "Status=ERROR Problem deactivating all networks\n";
815
	}
816
    if ($postreply =~/Status=ERROR /) {
817
        $res = header('text/plain', '500 Internal Server Error') unless $console;
818
    } else {
819
        $res = header('text/plain') unless $console;
820
    }
821
	$main::updateUI->({tab=>"networks", user=>$user});
822
	updateBilling("$action $user");
823
	return $postreply;
824
}
825

    
826
sub do_updatebilling {
827
    my ($uuid, $action) = @_;
828
    if ($help) {
829
        return <<END
830
GET:uuid:
831
Update network billing for current user.
832
END
833
    }
834

    
835
    my $res;
836
    $res .= header('text/plain') unless $console;
837
    if ($isreadonly) {
838
        $res .= "Status=ERROR Not updating network billing for $user\n";
839
    } else {
840
        updateBilling("updatebilling $user");
841
        $res .= "Status=OK Updated network billing for $user\n";
842
    }
843
    return $res;
844
}
845

    
846
# Print list of available actions on objects
847
sub do_plainhelp {
848
    my $res;
849
    $res .= header('text/plain') unless $console;
850
    $res .= <<END
851
* new [type="ipmapping|internalip|externalip|gateway", name="name"]: Creates a new network
852
* activate: Activates a network. If gateway is down it is brought up.
853
* stop: Stops the gateway, effectively stopping network communcation with the outside.
854
* deactivate: Deactivates a network. Removes the associated internal IP address from the DHCP service.
855
* delete: Deletes a network. Use with care. Network can not be in use.
856

    
857
END
858
;
859
}
860

    
861
sub addDHCPAddress {
862
    my $id = shift;
863
    my $uuid = shift;
864
    my $dhcpip = shift;
865
    my $gateway = shift;
866
    my $mac = lc shift;
867
    my $isexternal = !($dhcpip =~ /^10\./);
868
    my $options;
869
    my $interface = "br$id"; #,$extnic.$id
870
    $options = "--strict-order --bind-interfaces --except-interface=lo --interface=$interface " .
871
    ($proxyip?"--dhcp-range=tag:external,$proxyip,static ":"") .
872
    "--pid-file=/var/run/stabile-$id.pid --dhcp-hostsfile=$etcpath/dhcp-hosts-$id --dhcp-range=tag:internal,$gateway,static " .
873
    "--dhcp-optsfile=$etcpath/dhcp-options-$id --port=0 --log-dhcp";
874

    
875
    my $running;
876
    my $error;
877
    my $psid;
878
    return "Status=ERROR Empty mac or ip when configuing dhcp for $name" unless ($mac && $dhcpip);
879

    
880
    eval {
881
        $psid = `/bin/cat /var/run/stabile-$id.pid` if (-e "/var/run/stabile-$id.pid");
882
        chomp $psid;
883
        $running = -e "/proc/$psid" if ($psid);
884
        # `/bin/ps p $psid` =~ /$psid/
885
        # `/bin/ps ax | /bin/grep stabile-$id.pid | /usr/bin/wc -l`; 1;} or do
886
        1;
887
    } or do {$error .= "Status=ERROR Problem configuring dhcp for $name $@\n";};
888

    
889
    if (-e "$etcpath/dhcp-hosts-$id") {
890
        open(TEMP1, "<$etcpath/dhcp-hosts-$id") || ($error .= "Status=ERROR Problem reading dhcp hosts\n");
891
        open(TEMP2, ">$etcpath/dhcp-hosts-$id.new") || ($error .= "Status=ERROR Problem writing dhcp hosts $etcpath/dhcp-hosts-$id.new\n");
892
        while (<TEMP1>) {
893
            my $line = $_;
894
            print TEMP2 $line unless (($mac && $line =~ /^$mac/i) || ($line & $line =~ /.+,$dhcpip/));
895
        }
896
        print TEMP2 "$mac," . (($isexternal)?"set:external,":"set:internal,") . "$dhcpip\n";
897
        close(TEMP1);
898
        close(TEMP2);
899
        rename("$etcpath/dhcp-hosts-$id", "$etcpath/dhcp-hosts-$id.old") || ($error .= "Status=ERROR Problem writing dhcp hosts\n");
900
        rename("$etcpath/dhcp-hosts-$id.new", "$etcpath/dhcp-hosts-$id") || ($error .= "Status=ERROR Problem writing dhcp hosts\n");
901
    } else {
902
        open(TEMP1, ">$etcpath/dhcp-hosts-$id") || ($error .= "Status=ERROR Problem writing dhcp options\n");
903
        print TEMP1 "$mac,$dhcpip\n";
904
        close (TEMP1);
905
    }
906

    
907
#    unless (-e "$etcpath/dhcp-options-$id") {
908
        my $block = new Net::Netmask("$proxygw/$proxysubnet");
909
        my $proxymask = $block->mask();
910
        open(TEMP1, ">$etcpath/dhcp-options-$id") || ($error .= "Status=ERROR Problem writing dhcp options\n");
911

    
912
# Turns out the VM's gateway has to be $proxyip and not $proxygw in our proxyarp setup
913
        print TEMP1 <<END;
914
tag:external,option:router,$proxyip
915
tag:external,option:netmask,$proxymask
916
tag:external,option:dns-server,$proxyip
917
tag:internal,option:router,$gateway
918
tag:internal,option:netmask,255.255.255.0
919
tag:internal,option:dns-server,$gateway
920
option:dns-server,1.1.1.1
921
END
922

    
923
        close (TEMP1);
924
#    }
925

    
926
    if ($running) {
927
        $main::syslogit->($user, 'info', "HUPing dnsmasq 1: $id");
928
        eval {`/usr/bin/pkill -HUP -f "stabile-$id.pid"`; 1;} or do {$error .= "Status=ERROR Problem configuring dhcp for $name $@\n";};
929
    } else {
930
        eval {`/usr/sbin/dnsmasq $options`;1;} or do {$error .= "Status=ERROR Problem configuring dhcp for $name $@\n";};
931
    }
932
    # Allow access to DHCP service
933
    `iptables -D INPUT -i br$id -p udp -m udp --dport 67 -j ACCEPT`;
934
    `iptables -I INPUT -i br$id -p udp -m udp --dport 67 -j ACCEPT`;
935
    # Allow access to DNS service
936
    `iptables -D INPUT -i br$id -p udp -m udp --dport 53 -j ACCEPT`;
937
    `iptables -I INPUT -i br$id -p udp -m udp --dport 53 -j ACCEPT`;
938
    `iptables -D INPUT -i br$id -p tcp -m tcp --dport 53 -j ACCEPT`;
939
    `iptables -I INPUT -i br$id -p tcp -m tcp --dport 53 -j ACCEPT`;
940

    
941
    return $error?$error:"OK";
942
}
943

    
944
sub removeDHCPAddress {
945
    my $id = shift;
946
    my $uuid = shift;
947
    my $dhcpip = shift;
948
    my $mac;
949
    $mac = lc $domreg{$uuid}->{'nicmac1'} if ($domreg{$uuid});
950
    my $isexternal = ($dhcpip =~ /^10\./);
951
    my $running;
952
    my $error;
953
    my $psid;
954
    return "Status=ERROR Empty mac or ip when configuring dhcp for $name" unless ($mac || $dhcpip);
955

    
956
    eval {
957
        $psid = `/bin/cat /var/run/stabile-$id.pid` if (-e "/var/run/stabile-$id.pid");
958
        chomp $psid;
959
        $running = -e "/proc/$psid" if ($psid);
960
        1;
961
    } or do {$error .= "Status=ERROR Problem deconfiguring dhcp for $name $@\n";};
962

    
963
    my $keepup;
964
    if (-e "$etcpath/dhcp-hosts-$id") {
965
        open(TEMP1, "<$etcpath/dhcp-hosts-$id") || ($error .= "Status=ERROR Problem reading dhcp hosts\n");
966
        open(TEMP2, ">$etcpath/dhcp-hosts-$id.new") || ($error .= "Status=ERROR Problem writing dhcp hosts\n");
967
        while (<TEMP1>) {
968
            my $line = $_; chomp $line;
969
            if ($line && $line =~ /(.+),.+,($dhcpip)/) { # Release and remove this mac/ip from lease file
970
                $main::syslogit->($user, 'info', "Releasing dhcp lease: br$id $dhcpip $1");
971
                `/usr/bin/dhcp_release br$id $dhcpip $1`;
972
            } elsif ($mac && $line =~ /^$mac/i) {
973
                # If we find a stale assigment to the mac we are removing, remove this also
974
                $main::syslogit->($user, 'info', "Releasing stale dhcp lease: br$id $dhcpip $mac");
975
                `/usr/bin/dhcp_release br$id $dhcpip $mac`;
976
            } else {
977
                # Keep all other leases, and keep up the daemon if any leases found
978
                print TEMP2 "$line\n";
979
                $keepup = 1 if $line;
980
            }
981
        }
982
        close(TEMP1);
983
        close(TEMP2);
984
        rename("$etcpath/dhcp-hosts-$id", "$etcpath/dhcp-hosts-$id.old") || ($error .= "Status=ERROR Problem writing dhcp hosts\n");
985
        rename("$etcpath/dhcp-hosts-$id.new", "$etcpath/dhcp-hosts-$id") || ($error .= "Status=ERROR Problem writing dhcp hosts\n");
986
    }
987

    
988
    if ($keepup) {
989
        if ($running) {
990
            $main::syslogit->($user, 'info', "HUPing dnsmasq 2: $id");
991
            eval {`/usr/bin/pkill -HUP -f "stabile-$id.pid"`; 1;} or do {$error .= "Status=ERROR Problem configuring dhcp for $name $@\n";};
992
        }
993
    } else {
994
        unlink "$etcpath/dhcp-options-$id" if (-e "$etcpath/dhcp-options-$id");
995
        if ($running) {
996
            # Disallow access to DHCP service
997
            `iptables -D INPUT -i br$id -p udp -m udp --dport 67 -j ACCEPT`;
998
            # Disallow access to DNS service
999
            `iptables -D INPUT -i br$id -p udp -m udp --dport 53 -j ACCEPT`;
1000
            `iptables -D INPUT -i br$id -p tcp -m tcp --dport 53 -j ACCEPT`;
1001
            # Take down dhcp server
1002
            $main::syslogit->($user, 'info', "Killing dnsmasq 3: $id");
1003
            eval {`/usr/bin/pkill -f "stabile-$id.pid"`; 1;} or do {$error .= "Status=ERROR Problem configuring dhcp for $name $@\n";};
1004
        }
1005
    }
1006

    
1007
    return $error?$error:"OK";
1008
}
1009

    
1010
# Helper function
1011
sub save {
1012
    my ($id, $uuid, $name, $status, $type, $internalip, $externalip, $ports, $buildsystem, $username) = @_;
1013
    my $obj = {
1014
        id => $id,
1015
        uuid => $uuid,
1016
        name => $name,
1017
        status => $status,
1018
        type => $type,
1019
        internalip => $internalip,
1020
        externalip => $externalip,
1021
        ports => $ports,
1022
        buildsystem => $buildsystem,
1023
        username => $username
1024
    };
1025
    return Save($uuid, 'save', $obj);
1026
}
1027

    
1028
sub Save {
1029
    my ($uuid, $action, $obj) = @_;
1030
    if ($help) {
1031
        return <<END
1032
POST:uuid, id, name, internalip, externalip, ports, type, systems, activate:
1033
To save a collection of networks you either PUT or POST a JSON array to the main endpoint with objects representing the networks with the changes you want.
1034
Depending on your privileges not all changes are permitted. If you save without specifying a uuid, a new network is created.
1035
For now, [activate] only has effect when creating a new connection with a linked system/server.
1036
END
1037
    }
1038
    $uuid = $obj->{'uuid'} if ($obj->{'uuid'});
1039
    my $regnet = $register{$uuid};
1040
    my $id = $obj->{id};
1041
    my $name = $obj->{name};
1042
    my $status = $obj->{status};
1043
    my $type = $obj->{type} || $regnet->{type};
1044
    my $internalip = $obj->{internalip};
1045
    my $externalip = $obj->{externalip};
1046
    my $ports = $obj->{ports};
1047
    my $buildsystem = $obj->{buildsystem};
1048
    my $username = $obj->{username};
1049
    my $systems = $obj->{systems}; # Optionally link this network to a system
1050

    
1051
    $postreply = "" if ($buildsystem);
1052
	$username = $user unless ($username);
1053

    
1054
    $status = $regnet->{'status'} || $status; # Trust db status if it exists
1055
    if ((!$uuid && $uuid ne '0') && $status eq 'new') {
1056
        my $ug = new Data::UUID;
1057
        $uuid = $ug->create_str();
1058
    };
1059
    if ($status eq 'new') {
1060
        $name  = 'New Connection' unless ($name);
1061
    }
1062
    unless ($uuid && length $uuid == 36) {
1063
        $postreply .= "Status=Error Invalid uuid $uuid\n";
1064
        return $postreply;
1065
    }
1066
    my $systemnames = $regnet->{'systemnames'};
1067

    
1068
    my $dbid = 0+$regnet->{'id'};
1069
    if ($status eq 'new' || !$dbid) {
1070
        $id = getNextId($id) ;
1071
    } else {
1072
        $id = $dbid;
1073
    }
1074
    if ($id > 4095 || $id < 0 || ($id==0 && $uuid!=0 && $isadmin) || ($id==1 && $uuid!=1 && $isadmin)) {
1075
        $postreply .= "Status=ERROR Invalid network id $id\n";
1076
        return $postreply;
1077
    }
1078
    $name = $name || $regnet->{'name'};
1079
    $internalip = $internalip || $regnet->{'internalip'};
1080
    if (!($internalip =~ /\d+\.\d+\.\d+\.\d+/)) {$internalip = ''};
1081
    $externalip = $externalip || $regnet->{'externalip'};
1082
    $ports = $ports || $regnet->{'ports'};
1083
    my $reguser = $regnet->{'user'};
1084
    # Sanity checks
1085
    if (
1086
        ($name && length $name > 255)
1087
        || ($ports && length $ports > 255)
1088
        || ($type && !($type =~ /gateway|ipmapping|internalip|externalip|remoteip/))
1089
    ) {
1090
        $postreply .= "Stroke=ERROR Bad data: $name, $ports, $type\n";
1091
        return $postreply;
1092
    }
1093
    # Security check
1094
    if (($reguser && $username ne $reguser && !$isadmin ) ||
1095
        ($reguser && $status eq "new"))
1096
    {
1097
        $postreply .= "Status=Error Bad user: $username ($status)\n";
1098
        return $postreply;
1099
    }
1100
    # Check if remoteip is enabled
1101
    if ($type eq 'remoteip' && !$Stabile::remoteipenabled) {
1102
        $postreply .= "Status=Error remoteip is not enabled on this engine\n";
1103
        return $postreply;
1104
    }
1105
    my $hit = 0;
1106
# Check if user is allowed to use network
1107
    my @regvalues = values %register;
1108
    foreach my $val (@regvalues) {
1109
        $dbid = $val->{"id"};
1110
        $dbuser = $val->{"user"};
1111
        if ($dbid == $id && $username ne $dbuser && $dbuser ne "common") {
1112
            $hit = 1;
1113
            last;
1114
        }
1115
    }
1116
    if ($hit && !$isadmin) { # Network is nogo (unless you are an admin)
1117
        $postreply .= "Status=ERROR Network id $id not available\n";
1118
        return $postreply;
1119
    } elsif (!$type) {
1120
        $postreply .= "Status=ERROR Network must have a type\n";
1121
        return $postreply;
1122
    } elsif ($status eq 'down' || $status eq 'new' || $status eq 'nat') {
1123
        # Check if network has been modified or is new
1124
        if ($regnet->{'id'} ne $id ||
1125
            $regnet->{'name'} ne $name ||
1126
            $regnet->{'type'} ne $type ||
1127
            $regnet->{'internalip'} ne $internalip ||
1128
            $regnet->{'externalip'} ne $externalip ||
1129
            $regnet->{'systems'} ne $systems ||
1130
            $regnet->{'ports'} ne $ports)
1131
        {
1132
            if ($type eq "externalip") {
1133
                $internalip = "--";
1134
                $externalip = getNextExternalIP($externalip, $uuid, 1);
1135
                if (!$externalip) {
1136
                    $postreply .= "Status=ERROR Unable to allocate external proxy IP for $name\n";
1137
                    $externalip = "--";
1138
                    $internalip = getNextInternalIP($internalip, $uuid, $id);
1139
                    $type = "internalip";
1140
                } else {
1141
                    $postreply .= "Status=OK Allocated external IP: $externalip UUID: $uuid\n" unless ($regnet->{'externalip'} eq $externalip);
1142
                    if ($dodns) {
1143
                        $main::dnsCreate->($engineid, $externalip, $externalip, 'A', $user);
1144
                    }
1145
                }
1146

    
1147
            } elsif ($type eq "ipmapping") {
1148
                $externalip = getNextExternalIP($externalip, $uuid);
1149
                if (!$externalip) {
1150
                    $postreply .= "Status=ERROR Unable to allocate external IP for $name\n";
1151
                    $externalip = "--";
1152
                    $type = "internalip";
1153
                } else {
1154
                    $postreply .= "Status=OK Allocated external IP: $externalip\n" unless ($regnet->{'externalip'} eq $externalip);
1155
                    if ($dodns) {
1156
                        $postreply .= "Status=OK Trying to register DNS ";
1157
                        $main::dnsCreate->($engineid, $externalip, $externalip, 'A', $user);
1158
                    }
1159
                }
1160
                $internalip = getNextInternalIP($internalip, $uuid, $id);
1161
                if (!$internalip) {
1162
                    $postreply .= "Status=ERROR Unable to allocate internal IP for $name\n";
1163
                    $internalip = "--";
1164
                    $type = "gateway";
1165
                } else {
1166
                    $postreply .= "Status=OK Allocated internal IP: $internalip for $name\n" unless ($regnet->{'internalip'} eq $internalip);
1167
                }
1168

    
1169
            } elsif ($type eq "remoteip") {
1170
                # Check if engine user has been created
1171
                my $uid = `id -u irigo-$Stabile::engineuser`; chomp $uid;
1172
                if (!$uid) {
1173
                    $postreply .= "Status=ERROR Local engine user irigo-$Stabile::engineuser has not been created.\n";
1174
                    $postmsg = "ERROR Local engine user irigo-$Stabile::engineuser has not been created";
1175
                } else {
1176
                    if (!(-e "/home/irigo-$Stabile::engineuser/.ssh/id_rsa.pub")) { # Generate ssh keys if they don't exist
1177
                        `sudo -u irigo-$Stabile::engineuser ssh-keygen -t rsa -b 4096 -N '' -f "/home/irigo-$Stabile::engineuser/.ssh/id_rsa" -C $Stabile::engineuser`;
1178
                        my $pubkey = `cat "/home/irigo-$Stabile::engineuser/.ssh/id_rsa.pub"`;
1179
                        chomp $pubkey;
1180
                        # Upload public key to origo registry
1181
                        $postreply .= $main::postToOrigo->($engineid, 'uploadpubkey', $pubkey, 'pubkey');
1182
                    }
1183
                    $internalip = getNextInternalIP($internalip, $uuid, $id);
1184
                    if (!$internalip) {
1185
                        $postreply .= "Status=ERROR Unable to allocate internal IP for $name\n";
1186
                        $internalip = "--";
1187
                        $type = "gateway";
1188
                    } else {
1189
                        $postreply .= "Status=OK Allocated internal IP: $internalip for $name\n" unless ($regnet->{'internalip'} eq $internalip);
1190
                    }
1191
                    $externalip = getNextRemoteIP($internalip) unless ($externalip && $externalip ne '--' && $regnet->{'externalip'} eq $externalip);
1192
                    if (!$externalip) {
1193
                        $postreply .= "Status=ERROR Unable to allocate remote IP $externalip for $name\n";
1194
                        $postmsg = "Unable to allocate remote IP $externalip for $name";
1195
                        $externalip = "--";
1196
                        $type = "internalip";
1197
                    } else {
1198
                        $postreply .= "Status=OK Acquired remote IP: $externalip\n" unless ($regnet->{'externalip'} eq $externalip);
1199
                        if ($dodns) {
1200
                            $postreply .= "Status=OK Trying to register DNS ";
1201
                            $main::dnsCreate->($engineid, $externalip, $externalip, 'A', $user);
1202
                        }
1203
                    }
1204
                }
1205
                $ports = "80,443,10001" if ($ports eq '--' || $ports eq '');
1206

    
1207
            } 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
                    $postreply .= "Status=OK Allocated internal IP: $internalip for $name\n" unless ($regnet->{'internalip'} eq $internalip);
1218
                }
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
                        $postmsg = "Invalid port mapping";
1240
                        if ($type eq "remoteip") {
1241
                            @portslist = (80,443,10001);
1242
                        } else {
1243
                            $ports = "--";
1244
                        }
1245
                        last;
1246
                    }
1247
                }
1248
            }
1249
            if ($ports ne "--") {
1250
                $ports = join(',', @portslist);
1251
            }
1252
            if ($systems ne $regnet->{'systems'}) {
1253
                my $regsystems = $regnet->{'systems'};
1254
                unless (tie(%sysreg,'Tie::DBI', Hash::Merge::merge({table=>'systems'}, $Stabile::dbopts)) ) {$res .= qq|{"status": "Error": "message": "Unable to access systems register"}|; return $res;};
1255

    
1256
                # Remove existing link to system
1257
                if ($sysreg{$regsystems}) {
1258
                    $sysreg{$regsystems}->{'networkuuids'} =~ s/$uuid,? ?//;
1259
                    $sysreg{$regsystems}->{'networknames'} = s/$regnet->{'name'},? ?//;
1260
                } elsif ($domreg{$regsystems}) {
1261
                    $domreg{$regsystems}->{'networkuuids'} =~ s/$uuid,? ?//;
1262
                    $domreg{$regsystems}->{'networknames'} = s/$regnet->{'name'},? ?//;
1263
                }
1264
                if ($systems) {
1265
                    if ($sysreg{$systems}) { # Add new link to system
1266
                        $sysreg{$systems}->{'networkuuids'} .= (($sysreg{$systems}->{'networkuuids'}) ? ',' : '') . $uuid;
1267
                        $sysreg{$systems}->{'networknames'} .= (($sysreg{$systems}->{'networknames'}) ? ',' : '') . $name;
1268
                        $systemnames = $sysreg{$systems}->{'name'};
1269
                    } elsif ($domreg{$systems}) {
1270
                        $domreg{$systems}->{'networkuuids'} .= (($domreg{$systems}->{'networkuuids'}) ? ',' : '') . $uuid;
1271
                        $domreg{$systems}->{'networknames'} .= (($domreg{$systems}->{'networknames'}) ? ',' : '') . $name;
1272
                        $systemnames = $domreg{$systems}->{'name'};
1273
                    } else {
1274
                        $systems = '';
1275
                    }
1276
                }
1277
                tied(%sysreg)->commit;
1278
                untie(%sysreg);
1279
            }
1280

    
1281
            $register{$uuid} = {
1282
                uuid=>$uuid,
1283
                user=>$username,
1284
                id=>$id,
1285
                name=>$name,
1286
                internalip=>$internalip,
1287
                externalip=>$externalip,
1288
                ports=>$ports,
1289
                type=>$type,
1290
                systems=>$systems,
1291
                systemnames=>$systemnames,
1292
                action=>""
1293
            };
1294
            my $res = tied(%register)->commit;
1295
            my $obj = $register{$uuid};
1296
            $postreply .= "Status=OK Network $register{$uuid}->{'name'} saved: $uuid\n";
1297
            $postreply .= "Status=OK uuid: $uuid\n" if ($console && $status eq 'new');
1298
            if ($status eq 'new') {
1299
                validateStatus($register{$uuid});
1300
                $postmsg = "Created connection $name";
1301
                $uiupdatetype = "update";
1302
            }
1303
            updateBilling("allocate $externalip") if (($type eq "ipmapping" || $type eq "externalip" || $type eq "remoteip") && $externalip && $externalip ne "--");
1304

    
1305
        } else {
1306
        	$postreply = "Status=OK Network $uuid ($id) unchanged\n";
1307
        }
1308

    
1309
        if ($params{'PUTDATA'}) {
1310
            my %jitem = %{$register{$uuid}};
1311
            my $json_text = to_json(\%jitem);
1312
            $json_text =~ s/null/"--"/g;
1313
            $json_text =~ s/""/"--"/g;
1314
            $postreply = $json_text;
1315
            $postmsg = $postmsg || "OK, updated network $name";
1316
        }
1317
        return $postreply;
1318

    
1319
    } else {
1320
        $internalip = '--' unless ($internalip);
1321
        $externalip = '--' unless ($externalip);
1322
        if ($id ne $regnet->{'id'} ||
1323
        $internalip ne $regnet->{'internalip'} || $externalip ne $regnet->{'externalip'}) {
1324
            return "Status=ERROR Cannot modify active network: $uuid\n";
1325
        } elsif ($name ne $regnet->{'name'}) {
1326
            $register{$uuid}->{'name'} = $name;
1327
            $postreply .= "Status=OK Network \"$register{$uuid}->{'name'}\" saved: $uuid\n";
1328
            if ($params{'PUTDATA'}) {
1329
                my %jitem = %{$register{$uuid}};
1330
                my $json_text = to_json(\%jitem);
1331
                $json_text =~ s/null/"--"/g;
1332
                $postreply = $json_text;
1333
                $postmsg = "OK, updated network $name";
1334
            }
1335
        } else {
1336
            $postreply .= "Status=OK Nothing to save\n";
1337
            if ($params{'PUTDATA'}) {
1338
                my %jitem = %{$register{$uuid}};
1339
                my $json_text = to_json(\%jitem);
1340
                $json_text =~ s/null/"--"/g;
1341
                $postreply = $json_text;
1342
            }
1343
        }
1344
    }
1345
    return $postreply;
1346
}
1347

    
1348
sub Activate {
1349
    my ($uuid, $action, $obj) = @_;
1350
    if ($help) {
1351
        return <<END
1352
GET:uuid:
1353
Activate a network which must be in status down or nat.
1354
END
1355
    }
1356
    $uuid = $obj->{'uuid'} if ($obj->{'uuid'});
1357
    $action = 'activate' || $action;
1358
    my $regnet = $register{$uuid};
1359
    my $id = $regnet->{'id'};
1360
    my $name = $regnet->{'name'};
1361
    my $type = $regnet->{'type'};
1362
    my $status = $regnet->{'status'};
1363
    my $domains = $regnet->{'domains'};
1364
    my $systems = $regnet->{'systems'};
1365
    my $internalip = $regnet->{'internalip'};
1366
    my $externalip = $regnet->{'externalip'};
1367
    my $ports = $regnet->{'ports'};
1368
    my $idleft = ($id>99)?(substr $id,0,-2)+0 : 0;
1369
    my $idright = (substr $id,-2) + 0;
1370
    my $interfaces = `/sbin/ifconfig`;
1371
    my $dom = $domreg{$domains};
1372
    my $nicindex = ($dom->{'networkuuid1'} eq $uuid)?1:
1373
            ($dom->{'networkuuid2'} eq $uuid)?2:
1374
            ($dom->{'networkuuid3'} eq $uuid)?3:
1375
            0;
1376
    my $nicmac = $dom->{"nicmac$nicindex"};
1377
    my $e;
1378

    
1379
    if (!$id || $id==0 || $id==1 || $id>4095) {
1380
        $postreply .= "Status=ERROR Invalid ID activating $type\n";
1381
	    return $postreply;
1382
	} elsif (overQuotas()) { # Enforce quotas
1383
        $postreply .= "Status=ERROR Over quota activating $type " . overQuotas() . "\n";
1384
        return $postreply;
1385
    } elsif (($status ne 'down' && $status ne 'nat')) {
1386
        $postreply .= "Status=ERROR Cannot activate $type $name (current status is: $status)\n";
1387
        return $postreply;
1388
    }
1389

    
1390
    # Check if vlan with $id is created and doing nat, if not create it and create the gateway
1391
    unless (-e "/proc/net/vlan/$datanic.$id") {
1392
        eval {`/sbin/vconfig add $datanic $id`;}; # or do {$e=1; $postreply .= "Status=ERROR Problem adding vlan $datanic.$id $@\n"; return $postreply;};
1393
        eval {`/sbin/ifconfig $datanic.$id up`;}; # or do {$e=1; $postreply .= "Status=ERROR Problem activating vlan $datanic.$id $@\n"; return $postreply;};
1394
    }
1395
#    if (!($interfaces =~ m/$datanic\.$id /)) {
1396
    if (!($interfaces =~ m/br$id /)) {
1397
        # check if gw is created locally
1398
        unless (`arping -C1 -c2 -D -I $datanic.$id 10.$idleft.$idright.1` =~ /reply from/) { # check if gw is created on another engine
1399
            # Create gw
1400
#            eval {`/sbin/ifconfig $datanic.$id 10.$idleft.$idright.1 netmask 255.255.255.0 broadcast 10.$idleft.$idright.255 up`; 1;} or do {
1401
#                $e=1; $postreply .= "Status=ERROR $@\n"; return $postreply;
1402
            #            };
1403
            # To support local instances on valve, gw is now created as a bridge
1404
            eval {`/sbin/brctl addbr br$id`; 1;} or do {$e=1; $postreply .= "Status=ERROR $@\n"; return $postreply; };
1405
        # Adding VLANs on wifi NICs does not seem to work. Disabling for now until we figure out what is going on.
1406
            unless ($datanic =~ /^wl/) {
1407
                eval {`/sbin/brctl addif br$id $datanic.$id`; 1;} or do {$e=1; $postreply .= "Status=ERROR $@\n"; return $postreply; };
1408
            }
1409
            eval {`/sbin/ifconfig br$id 10.$idleft.$idright.1/24 up`; 1;} or do {
1410
                $e=1; $postreply .= "Status=ERROR $@\n"; return $postreply; }
1411
        } else {
1412
            $postreply .= "Status=OK GW is active on another Engine, assuming this is OK\n";
1413
        }
1414
    }
1415
    my $astatus = "nat" unless ($e);
1416
    `/usr/bin/touch $etcpath/dhcp-hosts-$id` unless (-e "$etcpath/dhcp-hosts-$id");
1417
    if ($action eq "activate") { #} && $domains) {
1418
        if ($type eq "internalip" || $type eq "ipmapping" || $type eq "remoteip") {
1419
            # Configure internal dhcp server
1420
            if ($domains) {
1421
                my $result = addDHCPAddress($id, $domains, $internalip, "10.$idleft.$idright.1", $nicmac);
1422
                if ($result eq "OK") {
1423
                    $astatus = "up" if ($type eq "internalip");
1424
                } else {
1425
                    $e = 1;
1426
                    $postreply .= "$result\n";
1427
                }
1428
            }
1429

    
1430
            # Also export storage pools to user's network
1431
            my @spl = split(/,\s*/, $storagepools);
1432
            my $reloadnfs;
1433
            my $uid = `id -u irigo-$user`; chomp $uid;
1434
            $uid = `id -u nobody` unless ($uid =~ /\d+/); chomp $uid;
1435
            my $gid = `id -g irigo-$user`; chomp $gid;
1436
            $gid = `id -g nobody` unless ($gid =~ /\d+/); chomp $gid;
1437

    
1438
            # We are dealing with multiple upstream routes - configure local routing
1439
            if ($proxynic && $proxynic ne $extnic) {
1440
                if (-e "/etc/iproute2/rt_tables" && !grep(/1 proxyarp/, `cat /etc/iproute2/rt_tables`)) {
1441
                    `/bin/echo "1 proxyarp" >> /etc/iproute2/rt_tables`;
1442
                }
1443
                if (!grep(/$datanic\.$id/, `/sbin/ip route show table proxyarp`)) {
1444
                    `/sbin/ip route add "10.$idleft.$idright.0/24" dev $datanic.$id table proxyarp`;
1445
                }
1446
            }
1447

    
1448
            # Manuipulate NFS exports and related disk quotas.
1449
            # Not needed for externalip's since they dont have access to the internal 10.x.x.x address space
1450
            foreach my $p (@spl) {
1451
                if ($tenderlist[$p] && $tenderpathslist[$p]) {
1452
                    my $fuelpath = $tenderpathslist[$p] . "/$user/fuel";
1453
                    unless (-e $fuelpath) {
1454
                        if ($tenderlist[$p] eq 'local') { # We only support fuel on local tender for now
1455
                            `mkdir "$fuelpath"`;
1456
                            `chmod 777 "$fuelpath"`;
1457
                        }
1458
                    }
1459
                    if ($tenderlist[$p] eq "local") {
1460
                        `chown irigo-$user:irigo-$user "$fuelpath"`;
1461
                        my $mpoint = `df -P "$fuelpath" | tail -1 | cut -d' ' -f 1`;
1462
                        chomp $mpoint;
1463
                        my $storagequota = $Stabile::userstoragequota;
1464
                        if (!$storagequota) {
1465
                            $storagequota = $Stabile::config->get('STORAGE_QUOTA');
1466
                        }
1467
                        my $nfsquota = $storagequota * 1024 ; # quota is in MB
1468
                        $nfsquota = 0 if ($nfsquota < 0); # quota of -1 means no limit
1469
                        `setquota -u irigo-$user $nfsquota $nfsquota 0 0 "$mpoint"` if (-e "$mntpoint");
1470
                        if (!(`grep "$fuelpath 10\.$idleft\.$idright" /etc/exports`) && -e $fuelpath) {
1471
                            `echo "$fuelpath 10.$idleft.$idright.0/255.255.255.0(sync,no_subtree_check,all_squash,rw,anonuid=$uid,anongid=$gid)" >> /etc/exports`;
1472
                            $reloadnfs = 1;
1473
                        }
1474
                    }
1475
                }
1476
            }
1477
            `/usr/sbin/exportfs -r` if ($reloadnfs); #Reexport nfs shares
1478

    
1479
        } elsif ($type eq "externalip") {
1480
            # A proxy is needed to route traffic, don't go any further if not configured
1481
            if ($proxyip) {
1482
                # Set up proxy
1483
                if (!($interfaces =~ m/$proxyip/ && $interfaces =~ m/br$id:proxy/)) {
1484
                    eval {`/sbin/ifconfig br$id:proxy $proxyip/$proxysubnet up`; 1;}
1485
                        or do {$e=1; $postreply .= "Status=ERROR Problem setting up proxy arp gw $proxyip on br$id:proxy $@\n";};
1486
                    eval {`/sbin/ifconfig $proxynic:proxy $proxyip/$proxysubnet up`; 1;}
1487
                        or do {$e=1; $postreply .= "Status=ERROR Problem setting up proxy arp gw $proxynic $@\n";};
1488
                }
1489
                my $result = "OK";
1490
                # Configure dhcp server
1491
                if ($domains) {
1492
                    $result = addDHCPAddress($id, $domains, $externalip, "10.$idleft.$idright.1", $nicmac) if ($domains);
1493
                    if ($result eq "OK") {
1494
                        ;
1495
                    } else {
1496
                        $e = 1;
1497
                        $postreply .= "$result\n";
1498
                    }
1499
                }
1500
            } else {
1501
                $postreply .= "Status=ERROR Cannot set up external IP without Proxy ARP gateway\n";
1502
            }
1503
        }
1504

    
1505
        # Handle routing with Iptables
1506
        if ($type eq "ipmapping" || $type eq "internalip" || $type eq "remoteip") {
1507
            `iptables -I FORWARD -d $internalip -m state --state ESTABLISHED,RELATED -j RETURN`;
1508
        }
1509
        # Check if external ip exists and routing configured, if not create and configure it
1510
        if ($type eq "ipmapping") {
1511
            if ($internalip && $internalip ne "--" && $externalip && $externalip ne "--" && !($interfaces =~ m/$externalip /g)) { # the space is important
1512
                $externalip =~ /\d+\.\d+\.(\d+)\.(\d+)/;
1513
                my $ipend = "$1$2"; # Linux NIC names are limited to 15 chars - we will have to find a way to support long NIC names and bigger than /24 subnets
1514
                $ipend = $2 if (length("$extnic:$id-$ipend")>15);
1515
                eval {`/sbin/ifconfig $extnic:$id-$ipend $externalip/$extsubnet up`; 1;}
1516
                    or do {$e=1; $postreply .= "Status=ERROR Problem adding interface $extnic:$id-$ipend $@\n";};
1517
                unless (`ip addr show dev $extnic` =~ /$externalip/) {
1518
                    $e=10;
1519
                    $postreply .= "Status=ERROR Problem adding interface $extnic:$id-$ipend\n";
1520
                }
1521
                # `/sbin/iptables -A POSTROUTING -t nat -s $internalip -j LOG --log-prefix "SNAT-POST"`;
1522
                # `/sbin/iptables -A INPUT -t nat -s $internalip -j LOG --log-prefix "SNAT-INPUT"`;
1523
                # `/sbin/iptables -A OUTPUT -t nat -s $internalip -j LOG --log-prefix "SNAT-OUTPUT"`;
1524
                # `/sbin/iptables -A PREROUTING -t nat -s $internalip -j LOG --log-prefix "SNAT-PRE"`;
1525
                if ($ports && $ports ne "--") { # Port mapping is defined
1526
                    my @portslist = split(/, ?| /, $ports);
1527
                    foreach my $port (@portslist) {
1528
                        my $ipfilter;
1529
                        if ($port =~ /(\d+)\.(\d+)\.(\d+)\.(\d+)(\/\d+)?:(\d+)/) {
1530
                            my $portip = "$1.$2.$3.$4$5";
1531
                            $port = $6;
1532
                            $ipfilter = "-s $portip";
1533
                        } else {
1534
                            $port = 0 unless ($port =~ /\d+/);
1535
                        }
1536
                        if ($port<1 || $port>65535) {
1537
                            $postreply .= "Status=ERROR Invalid port mapping for $name\n";
1538
                            $ports = "--";
1539
                            last;
1540
                        }
1541
                        if ($port>1 || $port<65535) {
1542
                            # DNAT externalip -> internalip
1543
                            eval {`/sbin/iptables -A PREROUTING -t nat -p tcp $ipfilter -d $externalip --dport $port -j DNAT --to $internalip`; 1;}
1544
                               or do {$e=2; $postreply .= "Status=ERROR Problem setting up routing $@\n";};
1545
                            eval {`/sbin/iptables -A PREROUTING -t nat -p udp $ipfilter -d $externalip --dport $port -j DNAT --to $internalip`; 1;}
1546
                               or do {$e=3; $postreply .= "Status=ERROR Problem setting up routing $@\n";};
1547
                            # PREROUTING is not parsed for packets coming from local host...
1548
                            eval {`/sbin/iptables -A OUTPUT -t nat -p tcp $ipfilter -d $externalip --dport $port -j DNAT --to $internalip`; 1;}
1549
                                or do {$e=2; $postreply .= "Status=ERROR Problem setting up routing $@\n";};
1550
                            eval {`/sbin/iptables -A OUTPUT -t nat -p udp $ipfilter -d $externalip --dport $port -j DNAT --to $internalip`; 1;}
1551
                                or do {$e=3; $postreply .= "Status=ERROR Problem setting up routing $@\n";};
1552
                            # Allow access to ipmapped internal ip on $port
1553
                            `iptables -I FORWARD -d $internalip -p tcp --dport $port -j RETURN`;
1554
                            `iptables -I FORWARD -d $internalip -p udp --dport $port -j RETURN`;
1555
                        }
1556
                    }
1557
                    eval {`/sbin/iptables -D INPUT -d $externalip -j DROP`; 1;} # Drop traffic to all other ports
1558
                        or do {$e=5; $postreply .= "Status=ERROR Problem setting up routing $@\n";};
1559
                    eval {`/sbin/iptables -A INPUT -d $externalip -j DROP`; 1;} # Drop traffic to all other ports
1560
                        or do {$e=6; $postreply .= "Status=ERROR Problem setting up routing $@\n";};
1561
                } else {
1562
                    # DNAT externalip -> internalip coming from outside , --in-interface $extnic
1563
                    eval {`/sbin/iptables -A PREROUTING -t nat -d $externalip -j DNAT --to $internalip`; 1;}
1564
                        or do {$e=7; $postreply .= "Status=ERROR Problem setting up routing $@\n";};
1565
                    # PREROUTING is not parsed for packets coming from local host...
1566
                    eval {`/sbin/iptables -A OUTPUT -t nat -d $externalip -j DNAT --to $internalip`; 1;}
1567
                        or do {$e=7; $postreply .= "Status=ERROR Problem setting up routing $@\n";};
1568
                    # Allow blanket access to ipmapped internal ip
1569
                    `iptables -I FORWARD -d $internalip -j RETURN`;
1570
                }
1571
                # We masquerade packets going to internalip from externalip to avoid confusion
1572
                #eval {`/sbin/iptables -A POSTROUTING -t nat --out-interface br$id -s $externalip -j MASQUERADE`; 1;}
1573
                #    or do {$e=3; $postreply .= "Status=ERROR Problem setting up routing $@\n";};
1574

    
1575
                # Masquerade packets from internal ip's not going to our own subnet
1576
                # `/sbin/iptables -D POSTROUTING -t nat --out-interface br$id ! -d 10.$idleft.$idright.0/24 -j MASQUERADE`;
1577
                #eval {`/sbin/iptables -A POSTROUTING -t nat --out-interface br$id ! -d 10.$idleft.$idright.0/24 -j MASQUERADE`; 1;}
1578
                #    or do {$e=3; $postreply .= "Status=ERROR Problem setting up routing $@\n";};
1579

    
1580
                # When receiving packet from client, if it's been routed, and outgoing interface is the external interface, SNAT.
1581
                unless ($Stabile::disablesnat) {
1582
                    eval {`/sbin/iptables -A POSTROUTING -t nat -s $internalip ! -d 10.$idleft.$idright.0/24 -j SNAT --to-source $externalip`; 1; }
1583
                        or do {$e=4; $postreply .= "Status=ERROR Problem setting up routing $@\n";};
1584
                #    eval {`/sbin/iptables -A POSTROUTING -t nat -s $internalip -j SNAT --to-source $externalip`; 1; }
1585
                #        or do {$e=4; $postreply .= "Status=ERROR Problem setting up routing $@\n";};
1586
                    eval {`/sbin/iptables -I INPUT -t nat -s $internalip ! -d 10.$idleft.$idright.0/24 -j SNAT --to-source $externalip`; 1; }
1587
                        or do {$e=4; $postreply .= "Status=ERROR Problem setting up routing $@\n";};
1588
                #    eval {`/sbin/iptables -I INPUT -t nat -s $internalip -j SNAT --to-source $externalip`; 1; }
1589
                #        or do {$e=4; $postreply .= "Status=ERROR Problem setting up routing $@\n";};
1590
                }
1591
                if ($e) {
1592
                    $main::syslogit->($user, 'info', "Problem $action network $uuid ($name, $id): $@");
1593
                } else {
1594
                    $astatus = "up"
1595
                }
1596
            }
1597
        } elsif ($type eq "remoteip") {
1598
            if ($Stabile::remoteipenabled && -e "/home/irigo-$Stabile::engineuser/.ssh/id_rsa") {
1599
                # First activate the ip on remoteipprovider
1600
                my $res = $main::postToOrigo->($engineid, 'activateremoteip', "$externalip:$internalip", 'remotelocalip');
1601
                my $res_obj = JSON::from_json($res);
1602
                my $pid = '--';
1603
                my @remoteports = (80, 443, 10001);
1604
                my $rports;
1605
                if ($ports && $ports ne "--") {
1606
                    # Port mapping is defined
1607
                    my @portslist = split(/, ?| /, $ports);
1608
                    @remoteports = ();
1609
                    foreach my $port (@portslist) {
1610
                        if ($port =~ /(\d+)\.(\d+)\.(\d+)\.(\d+)(\/\d+)?:(\d+)/) {
1611
                            my $portip = "$1.$2.$3.$4$5";
1612
                            $port = $6;
1613
                        } else {
1614
                            $port = 0 unless ($port =~ /\d+/);
1615
                        }
1616
                        if ($port < 1 || $port > 65535) {
1617
                            $postreply .= "Status=ERROR Invalid port mapping for $name\n";
1618
                            $ports = "--";
1619
                            last;
1620
                        }
1621
                        if ($port > 1 || $port < 65535) {
1622
                            push @remoteports, $port;
1623
                        }
1624
                    }
1625
                }
1626
                foreach my $port (@remoteports) {
1627
                    $rports .= "-R $externalip:$port:$internalip:$port ";
1628
                }
1629
                if ($res_obj->{status} eq 'OK') {
1630
#                    my $cmd = qq|ssh -fN -i /home/irigo-$Stabile::engineuser/.ssh/id_rsa -o "StrictHostKeyChecking=no" -o "UserKnownHostsFile=/dev/null" -o "ExitOnForwardFailure=yes" -R $externalip:10001:$internalip:10001 -R $externalip:80:$internalip:80 -R $externalip:443:$internalip:443 $Stabile::remoteipprovider|;
1631
                    my $cmd = qq|ssh -fN -i /home/irigo-$Stabile::engineuser/.ssh/id_rsa -o "StrictHostKeyChecking=no" -o "UserKnownHostsFile=/dev/null" -o "ExitOnForwardFailure=yes" $rports $Stabile::remoteipprovider|;
1632
                    eval {
1633
                        my $daemon = Proc::Daemon->new(
1634
                            work_dir => '/home/irigo-o@origo.io',
1635
                            exec_command => "$cmd"
1636
                        ) or do {$postreply .= "Status=ERROR $@";};
1637
                        $pid = $daemon->Init();
1638
                        $main::syslogit->($user, "info", "Activating remote ip $externalip at $Stabile::remoteipprovider for $Stabile::engineuser, pid=$pid");
1639
                        1;
1640
                    } or do {$e=4; $postreply .= "Status=ERROR Problem activating remote ip $@\n";};
1641
#                    sleep 1;
1642
                } else {
1643
                    $postreply .= "Status=Error $res_obj->{message}\n";
1644
                }
1645
                if ($e || !(-e "/proc/$pid")) {
1646
                    $main::syslogit->($user, 'info', "Problem $action network $uuid ($e, $name, $id): $@");
1647
                    $astatus = $status;
1648
                    $postreply .= "Status=OK Waiting to establish remote connetion\n";
1649
                } else {
1650
                    $astatus = "up"
1651
                }
1652
            }
1653
        } elsif ($type eq "externalip") {
1654
            my $route = `/sbin/ip route`;
1655
            my $tables = `/sbin/iptables -L -n`;
1656

    
1657
            # Allow external IP send packets out
1658
            `/sbin/iptables -D FORWARD --in-interface br$id -s $externalip -j RETURN`;
1659
            `/sbin/iptables -I FORWARD --in-interface br$id -s $externalip -j RETURN`;
1660

    
1661
            # We are dealing with multiple upstream routes - configure local routing
1662
            if ($proxynic && ($proxynic ne $extnic)) {
1663
                if (-e "/etc/iproute2/rt_tables" && !grep(/1 proxyarp/, `cat /etc/iproute2/rt_tables`)) {
1664
                    `/bin/echo "1 proxyarp" >> /etc/iproute2/rt_tables`;
1665
                }
1666
                if (!grep(/$proxygw/, `/sbin/ip route show table proxyarp`)) {
1667
                    `/sbin/ip route del default dev $proxynic table proxyarp`; # delete first in case proxygw has changed
1668
                    `/sbin/ip route add default via $proxygw dev $proxynic table proxyarp`;
1669
                }
1670
                if (!grep(/proxyarp/, `/sbin/ip rule show`)) {
1671
                    `/sbin/ip rule add to $proxygw/$proxysubnet table main`;
1672
                    `/sbin/ip rule add from $proxygw/$proxysubnet table proxyarp`;
1673
                }
1674
                my $proxyroute = `/sbin/ip route show table proxyarp`;
1675
#                `/sbin/ip route add $externalip/32 dev $datanic.$id:proxy src $proxyip table proxyarp` unless ($proxyroute =~ /$externalip/);
1676
                `/sbin/ip route add $externalip/32 dev br$id:proxy src $proxyip table proxyarp` unless ($proxyroute =~ /$externalip/);
1677
            }
1678
            eval {`/bin/echo 1 > /proc/sys/net/ipv4/conf/$datanic.$id/proxy_arp`; 1;}
1679
                or do {$e=1; $postreply .= "Status=ERROR Problem setting up proxy arp $@\n";};
1680
            eval {`/bin/echo 1 > /proc/sys/net/ipv4/conf/$proxynic/proxy_arp`; 1;}
1681
                or do {$e=1; $postreply .= "Status=ERROR Problem setting up proxy arp $@\n";};
1682
            eval {`/sbin/ip route add $externalip/32 dev br$id:proxy src $proxyip` unless ($route =~ /$externalip/); 1;}
1683
                or do {$e=1; $postreply .= "Status=ERROR Problem setting up proxy arp $@\n";};
1684

    
1685
            eval {`/sbin/iptables -D FORWARD -i $proxynic -d $externalip -m state --state ESTABLISHED,RELATED -j RETURN`; 1;}
1686
                or do {$e=1; $postreply .= "Status=ERROR Problem setting up routing $@\n";};
1687
            eval {`/sbin/iptables -A FORWARD -i $proxynic -d $externalip -m state --state ESTABLISHED,RELATED -j RETURN`; 1;}
1688
                or do {$e=1; $postreply .= "Status=ERROR Problem setting up routing $@\n";};
1689

    
1690

    
1691
            eval {`/sbin/iptables -D FORWARD -i $proxynic -d $externalip -j REJECT` if
1692
                ($tables =~ /REJECT .+ all .+ $externalip/); 1;}
1693
                or do {$e=1; $postreply .= "Status=ERROR Problem setting up routing $@\n";};
1694

    
1695
            if ($ports && $ports ne "--") {
1696
                my @portslist = split(/, ?| /, $ports);
1697
                foreach $port (@portslist) {
1698
                    my $ipfilter;
1699
                    if ($port =~ /(\d+)\.(\d+)\.(\d+)\.(\d+)(\/\d+)?:(\d+)/) {
1700
                        my $portip = "$1.$2.$3.$4$5";
1701
                        $port = $6;
1702
                        $ipfilter = "-s $portip";
1703
                    } else {
1704
                        $port = 0 unless ($port =~ /\d+/);
1705
                    }
1706
                    if ($port<1 || $port>65535) {
1707
                        $postreply .= "Status=ERROR Invalid port mapping for $name\n";
1708
                        $ports = "--";
1709
                        last;
1710
                    }
1711

    
1712
                    if ($port>1 && $port<65535 && $port!=67) { # Disallow setting up a dhcp server
1713
                        eval {`/sbin/iptables -A FORWARD -p tcp -i $proxynic $portfilter -d $externalip --dport $port -j RETURN`; 1;}
1714
                            or do {$e=1; $postreply .= "Status=ERROR Problem setting up routing $@\n";};
1715
                        eval {`/sbin/iptables -A FORWARD -p udp -i $proxynic $portfilter -d $externalip --dport $port -j RETURN`; 1;}
1716
                            or do {$e=1; $postreply .= "Status=ERROR Problem setting up routing $@\n";};
1717
                    }
1718
                }
1719
                eval {`/sbin/iptables -D FORWARD -i $proxynic -d $externalip -j REJECT`; 1;} # Drop traffic to all other ports
1720
                    or do {$e=1; $postreply .= "Status=ERROR Problem setting up routing $@\n";};
1721
                eval {`/sbin/iptables -A FORWARD -i $proxynic -d $externalip -j REJECT`; 1;} # Drop traffic to all other ports
1722
                    or do {$e=1; $postreply .= "Status=ERROR Problem setting up routing $@\n";};
1723
            } else {
1724
                # First allow everything else to this ip
1725
                eval {`/sbin/iptables -D FORWARD -i $proxynic -d $externalip -j RETURN`; 1;}
1726
                    or do {$e=1; $postreply .= "Status=ERROR Problem setting up routing $@\n";};
1727
                eval {`/sbin/iptables -A FORWARD -i $proxynic -d $externalip -j RETURN`; 1;}
1728
                    or do {$e=1; $postreply .= "Status=ERROR Problem setting up routing $@\n";};
1729
                # Then disallow setting up a dhcp server
1730
                eval {`/sbin/iptables -D FORWARD -p udp -i $proxynic -d $externalip --dport 67 -j REJECT`; 1;}
1731
                    or do {$e=1; $postreply .= "Status=ERROR Problem setting up routing $@\n";};
1732
                eval {`/sbin/iptables -A FORWARD -p udp -i $proxynic -d $externalip --dport 67 -j REJECT`; 1;}
1733
                    or do {$e=1; $postreply .= "Status=ERROR Problem setting up routing $@\n";};
1734
            }
1735
        }
1736
    }
1737

    
1738
    # Allow all inter-VLAN communication
1739
    `iptables -D FORWARD --in-interface br$id --out-interface br$id -j RETURN 2>/dev/null`;
1740
    `iptables -I FORWARD --in-interface br$id --out-interface br$id -j RETURN`;
1741
    # Disallow any access to vlan except mapped from external NIC i.e. ipmappings
1742
    `iptables -D FORWARD ! --in-interface $extnic --out-interface br$id -j DROP 2>/dev/null`;
1743
    `iptables -A FORWARD ! --in-interface $extnic --out-interface br$id -j DROP`;
1744

    
1745
    # Only forward packets coming from subnet assigned to vlan unless we are setting up a gateway on the proxy nic and the proxy nic is on a vlan
1746
#    `/sbin/iptables --delete FORWARD --in-interface $datanic.$id ! -s 10.$idleft.$idright.0/24 -j DROP`;
1747
    unless ($proxynic eq "$datanic.$id") {
1748
#        `/sbin/iptables --append FORWARD --in-interface $datanic.$id ! -s 10.$idleft.$idright.0/24 -j DROP`;
1749
    }
1750

    
1751
    # Enable nat'ing
1752
    eval {
1753
        #my $masq = `/sbin/iptables -L -n -t nat`;
1754
        #        if (!($masq =~ "MASQUERADE.+all.+--.+0\.0\.0\.0/0")) {
1755
        `/sbin/iptables -D POSTROUTING -t nat --out-interface $extnic -s 10.0.0.0/8 -j MASQUERADE`;
1756
        `/sbin/iptables -A POSTROUTING -t nat --out-interface $extnic -s 10.0.0.0/8 -j MASQUERADE`;
1757
        # Christian's dev environment
1758
        #            my $interfaces = `/sbin/ifconfig`;
1759
        #            if ($interfaces =~ m/ppp0/) {
1760
        #                `/sbin/iptables --table nat --append POSTROUTING --out-interface ppp0 -s 10.0.0.0/8 -j MASQUERADE`;
1761
        #            }
1762
        #        };
1763
        1;
1764
    } or do {print "Unable to enable masquerading: $@\n";};
1765

    
1766
    $uistatus = ($e)?"":validateStatus($register{$uuid});
1767
    if ($uistatus && $uistatus ne 'down' # && $uistatus ne 'nat'
1768
        ) {
1769
        $uiuuid = $uuid;
1770
        $postreply .= "Status=$uistatus OK $action $type $name\n";
1771
    } else {
1772
        $postreply .= "Status=ERROR Cannot $action $type $name ($uistatus)\n";
1773
    }
1774
    $main::syslogit->($user, 'info', "$action network $uuid ($name, $id) -> $uistatus");
1775
    updateBilling("$uistatus $uuid ($id)");
1776
    # $main::updateUI->({tab=>"networks", user=>$user, uuid=>$uiuuid, status=>$uistatus}) if ($uistatus);
1777
    return $postreply;
1778
}
1779

    
1780
sub Removeusernetworks {
1781
    my $username = shift;
1782
    return unless (($isadmin || $user eq $username) && !$isreadonly);
1783
    $user = $username;
1784
    foreach my $uuid (keys %register) {
1785
        if ($register{$uuid}->{'user'} eq $user) {
1786
            $postreply .=  "Removing network $register{$uuid}->{'name'}, $uuid" . ($console?'':'<br>') . "\n";
1787
            Deactivate($uuid);
1788
            Remove($uuid, 'remove');
1789
        }
1790
    }
1791
}
1792

    
1793
sub Remove {
1794
    my ($uuid, $action, $obj) = @_;
1795
    if ($help) {
1796
        return <<END
1797
DELETE:uuid,force:
1798
Delete a network which must be in status down or nat and should not be used by any servers, or linked to any stacks.
1799
May also be called with endpoints "/stabile/[uuid]" or "/stabile?uuid=[uuid]"
1800
Set [force] to remove even if linked to a system.
1801
END
1802
    }
1803
    $uuid = $obj->{'uuid'} if ($curuuid && $obj->{'uuid'}); # we are called from a VM with an ip address as target
1804
    my $force = $obj->{'force'};
1805
    ( my $domains, my $domainnames ) = getDomains($uuid);
1806
    ( my $systems, my $systemnames ) = getSystems($uuid);
1807

    
1808
    if ($register{$uuid}) {
1809
        my $id = $register{$uuid}->{'id'};
1810
        my $name = $register{$uuid}->{'name'};
1811
        utf8::decode($name);
1812
        my $status = $register{$uuid}->{'status'};
1813
        my $type = $register{$uuid}->{'type'};
1814
        my $internalip = $register{$uuid}->{'internalip'};
1815
        my $externalip = $register{$uuid}->{'externalip'};
1816

    
1817
        my @regvalues = values %register;
1818
        if (
1819
            $id!=0 && $id!=1 && (!$domains || $domains eq '--')
1820
                && ((!$systems || $systems eq '--' || $force)
1821
                # allow internalip's to be removed if active and only linked, i.e. not providing dhcp
1822
                || ($status eq 'down' || $status eq 'new' || $status eq 'nat' || ($type eq 'internalip' && $systems && $systems ne '--')))
1823
        ) {
1824
            # Deconfigure internal dhcp server and DNS
1825
            if ($type eq "internalip") {
1826
                my $result =  removeDHCPAddress($id, $domains, $internalip);
1827
                $postreply .= "$result\n" unless $result eq "OK";
1828
            } elsif ($type eq "ipmapping") {
1829
                my $result =  removeDHCPAddress($id, $domains, $internalip);
1830
                $postreply .= "$result\n" unless $result eq "OK";
1831
                if ($dodns) {
1832
                    $main::dnsDelete->($engineid, $externalip) if ($enginelinked);
1833
                }
1834
            } elsif ($type eq "externalip" || $type eq "remoteip") {
1835
                my $result =  removeDHCPAddress($id, $domains, $externalip);
1836
                $postreply .= "$result\n" unless $result eq "OK";
1837
                if ($dodns) {
1838
                    $main::dnsDelete->($engineid, $externalip) if ($enginelinked);
1839
                }
1840
                # Deactivate the ip on remoteipprovider
1841
                my $res = $main::postToOrigo->($engineid, 'removeremoteip', "$externalip", 'remoteip');
1842
                my $res_obj = JSON::from_json($res);
1843
                if ($res_obj->{status} ne 'OK') {
1844
                    $postreply .= "Status=OK There was a problem removing the remote IP\n";
1845
                }
1846
            }
1847
            if ($status eq 'nat') {
1848
                # Check if last network in vlan. If so take it down
1849
                my $notlast;
1850
                foreach my $val (@regvalues) {
1851
                    if ($val->{'user'} eq $user && $val->{'id'} == $id) {
1852
                        $notlast = 1;
1853
                    }
1854
                }
1855
                if (!$notlast) {
1856
                    eval {`/sbin/ifconfig $datanic.$id down`; 1;} or do {;};
1857
                    eval {`/sbin/vconfig rem $datanic.$id`; 1;} or do {;};
1858
                }
1859
            }
1860

    
1861
            unless (tie(%sysreg,'Tie::DBI', Hash::Merge::merge({table=>'systems'}, $Stabile::dbopts)) ) {$res .= qq|{"status": "Error": "message": "Unable to access systems register"}|; return $res;};
1862
            if ($sysreg{$systems}) { # Remove existing link to system
1863
                $sysreg{$systems}->{'networkuuids'} =~ s/$uuid,?//;
1864
                $sysreg{$systems}->{'networknames'} = s/$name,?//;
1865
            }
1866
            tied(%sysreg)->commit;
1867
            untie(%sysreg);
1868

    
1869

    
1870
            delete $register{$uuid};
1871
            tied(%register)->commit;
1872
            updateBilling("delete $val->{'externalip'}") if ($type eq "ipmapping");
1873
            $main::syslogit->($user, "info", "Deleted network $uuid ($id)");
1874
            $postreply = "[]" || $postreply;
1875
            $main::updateUI->({tab=>"networks", user=>$user, type=>"update"});
1876
        } else {
1877
            $postreply .= "Status=ERROR Cannot remove $uuid which is $status. Cannot delete network 0,1 or a network which is active or in use.\n";
1878
            $main::updateUI->({tab=>"networks", user=>$user, message=>"Cannot remove a network which is active, linked or in use."});
1879
        }
1880
    } else {
1881
        $postreply .= "Status=ERROR Network $uuid $ipaddress not found\n";
1882
    }
1883
    return $postreply;
1884
}
1885

    
1886
sub Deactivate {
1887
    my ($uuid, $action, $obj) = @_;
1888

    
1889
    if ($help) {
1890
        return <<END
1891
GET:uuid:
1892
Deactivate a network which must be in status up.
1893
END
1894
    }
1895
    $uuid = $obj->{'uuid'} if ($obj->{'uuid'});
1896

    
1897
    unless ($register{$uuid}) {
1898
        $postreply .= "Status=ERROR Connection with uuid $uuid not found\n";
1899
        return $postreply;
1900
    }
1901
    my $regnet = $register{$uuid};
1902

    
1903
    $action = $action || 'deactivate';
1904
    ( my $domains, my $domainnames ) = getDomains($uuid);
1905
    my $interfaces = `/sbin/ifconfig`;
1906

    
1907
    my $id = $regnet->{'id'};
1908
    my $name = $regnet->{'name'};
1909
    my $type = $regnet->{'type'};
1910
    my $internalip = $regnet->{'internalip'};
1911
    my $externalip = $regnet->{'externalip'};
1912
    my $ports = $regnet->{'ports'};
1913

    
1914
    if ($id!=0 && $id!=1 && $status ne 'down') {
1915
    # If gateway is created, take it down along with all user's networks
1916
        if ($action eq "stop") {
1917
            my $res = Stop($id, $action);
1918
            if ($res) {
1919
                unlink "$etcpath/dhcp-hosts-$id" if (-e "$etcpath/dhcp-hosts-$id");
1920
            };
1921
        }
1922
    } else {
1923
        $postreply .= "Status=ERROR Cannot $action network $name\n";
1924
        return $postreply;
1925
    }
1926

    
1927
    my $idleft = ($id>99)?(substr $id,0,-2)+0 : 0;
1928
    my $idright = (substr $id,-2) + 0;
1929
    my $e = 0;
1930
    my $duprules = 0;
1931

    
1932
    if ($type eq "ipmapping" || $type eq "internalip" || $type eq "remoteip") {
1933
        `iptables -D FORWARD -d $internalip -m state --state ESTABLISHED,RELATED -j RETURN`;
1934
    }
1935
    if ($type eq "ipmapping") {
1936
        # Check if external ip exists and take it down if so
1937
        if ($internalip && $internalip ne "--" && $externalip && $externalip ne "--" && ($interfaces =~ m/$externalip/g)) {
1938
            $externalip =~ /\d+\.\d+\.(\d+)\.(\d+)/;
1939
            my $ipend = "$1$2"; # Linux NIC names are limited to 15 chars - we will have to find a way to support long NIC names and bigger than /24 subnets
1940
            $ipend = $2 if (length("$extnic:$id-$ipend")>15);
1941
            eval {`/sbin/ifconfig $extnic:$id-$ipend down`; 1;} or do {$e=1; $postreply .= "Status=ERROR $@\n";};
1942

    
1943
            if ($ports && $ports ne "--") { # Port mapping is defined
1944
                my @portslist = split(/, ?| /, $ports);
1945
                foreach my $port (@portslist) {
1946
                    my $ipfilter;
1947
                    if ($port =~ /(\d+)\.(\d+)\.(\d+)\.(\d+)(\/\d+)?:(\d+)/) {
1948
                        my $portip = "$1.$2.$3.$4$5";
1949
                        $port = $6;
1950
                        $ipfilter = "-s $portip";
1951
                    } else {
1952
                        $port = 0 unless ($port =~ /\d+/);
1953
                    }
1954
                    if ($port<1 || $port>65535) {
1955
                        $postreply .= "Status=ERROR Invalid port mapping for $name\n";
1956
                        $ports = "--";
1957
                        last;
1958
                    }
1959
                    # Remove DNAT rules
1960
                    if ($port>1 || $port<65535) {
1961
                        # repeat for good measure
1962
                        for (my $di=0; $di < 10; $di++) {
1963
                            $duprules = 0;
1964
                            eval {$duprules++ if (`/sbin/iptables -D PREROUTING -t nat -p tcp $ipfilter -d $externalip --dport $port -j DNAT --to $internalip`); 1;}
1965
                                or do {$postreply .= "Status=ERROR $@\n"; $e=1};
1966
                            eval {$duprules++ if (`/sbin/iptables -D PREROUTING -t nat -p udp $ipfilter -d $externalip --dport $port -j DNAT --to $internalip`); 1;}
1967
                                or do {$postreply .= "Status=ERROR $@\n"; $e=1};
1968
                            eval {$duprules++ if (`/sbin/iptables -D OUTPUT -t nat -p tcp $ipfilter -d $externalip --dport $port -j DNAT --to $internalip`); 1;}
1969
                                or do {$postreply .= "Status=ERROR $@\n"; $e=1};
1970
                            eval {$duprules++ if (`/sbin/iptables -D OUTPUT -t nat -p udp $ipfilter -d $externalip --dport $port -j DNAT --to $internalip`); 1;}
1971
                                or do {$postreply .= "Status=ERROR $@\n"; $e=1};
1972
                            eval {$duprules++ if (`/sbin/iptables -D POSTROUTING -t nat --out-interface br$id -s $externalip -j MASQUERADE`); 1;}
1973
                                or do {$e=3; $postreply .= "Status=ERROR Problem setting up routing $@\n";};
1974
                            # Remove access to ipmapped internal ip on $port
1975
                            eval {$duprules++ if (`/sbin/iptables -D FORWARD -d $internalip -p udp --dport $port -j RETURN`); 1;}
1976
                                or do {$e=3; $postreply .= "Status=ERROR Problem setting up routing $@\n";};
1977
                            eval {$duprules++ if (`/sbin/iptables -D FORWARD -d $internalip -p tcp --dport $port -j RETURN`); 1;}
1978
                                or do {$e=3; $postreply .= "Status=ERROR Problem setting up routing $@\n";};
1979
                            last if ($duprules >6);
1980
                        }
1981
                    }
1982
                }
1983
                # Remove SNAT rules
1984
                # repeat for good measure
1985
                for (my $di=0; $di < 10; $di++) {
1986
                    $duprules = 0;
1987
                    eval {$duprules++ if (`/sbin/iptables -D POSTROUTING -t nat -s $internalip ! -d 10.$idleft.$idright.0/24 -j SNAT --to-source $externalip`); 1; }
1988
                        or do {$e=1; $postreply .= "Status=ERROR Problem setting up routing $@\n";};
1989
                    last if ($duprules);
1990
                }
1991
                # Remove rule to drop traffic to all other ports
1992
                eval {`/sbin/iptables -D INPUT -d $externalip -j DROP`; 1;}
1993
                    or do {$e=1; $postreply .= "Status=ERROR Problem setting up routing $@\n";};
1994
            } else {
1995
                # Remove DNAT rules
1996
                # repeat for good measure
1997
                for (my $di=0; $di < 10; $di++) {
1998
                    $duprules = 0;
1999
                    eval {$duprules++ if (`/sbin/iptables -D PREROUTING -t nat -d $externalip -j DNAT --to $internalip`); 1;}
2000
                        or do {$postreply .= "Status=ERROR $@\n"; $e=1};
2001
                    eval {$duprules++ if (`/sbin/iptables -D OUTPUT -t nat -d $externalip -j DNAT --to $internalip`); 1;}
2002
                        or do {$postreply .= "Status=ERROR $@\n"; $e=1};
2003
                    last if ($duprules >1);
2004
                }
2005
                # Remove blanket access to ipmapped internal ip
2006
                `iptables -D FORWARD -d $internalip -j RETURN`;
2007
            }
2008
            # Remove SNAT and MASQUERADE rules
2009
            # repeat for good measure
2010
            for (my $di=0; $di < 10; $di++) {
2011
                $duprules = 0;
2012
            #    eval {$duprules++ if (`/sbin/iptables -D POSTROUTING -t nat --out-interface br$id -s $externalip -j MASQUERADE`); 1;}
2013
            #        or do {$e=3; $postreply .= "Status=ERROR Problem setting up routing $@\n";};
2014
                eval {$duprules++ if (`/sbin/iptables -D POSTROUTING -t nat --out-interface br$id ! -d 10.$idleft.$idright.0/24 -j MASQUERADE`); 1;}
2015
                    or do {$e=3; $postreply .= "Status=ERROR Problem setting up routing $@\n";};
2016

    
2017
                eval {$duprules++ if (`/sbin/iptables -D POSTROUTING -t nat -s $internalip ! -d 10.$idleft.$idright.0/24 -j SNAT --to-source $externalip`); 1; }
2018
                    or do {$e=1; $postreply .= "Status=ERROR Problem setting up routing $@\n";};
2019
            #    eval {$duprules++ if (`/sbin/iptables -D POSTROUTING -t nat -s $internalip -j SNAT --to-source $externalip`); 1; }
2020
            #        or do {$e=1; $postreply .= "Status=ERROR Problem setting up routing $@\n";};
2021
                eval {$duprules++ if (`/sbin/iptables -D INPUT -t nat -s $internalip ! -d 10.$idleft.$idright.0/24 -j SNAT --to-source $externalip`); 1; }
2022
                    or do {$e=1; $postreply .= "Status=ERROR Problem setting up routing $@\n";};
2023
            #    eval {$duprules++ if (`/sbin/iptables -D INPUT -t nat -s $internalip -j SNAT --to-source $externalip`); 1; }
2024
            #        or do {$e=1; $postreply .= "Status=ERROR Problem setting up routing $@\n";};
2025
            #    eval {$duprules++ if (`/sbin/iptables -D INPUT -t nat -s $internalip ! -d 10.$idleft.$idright.0/24 -j SNAT --to-source $externalip`); 1; }
2026
            #        or do {$e=1; $postreply .= "Status=ERROR Problem setting up routing $@\n";};
2027
            #    eval {$duprules++ if (`/sbin/iptables -D INPUT -t nat -s $internalip -j SNAT --to-source $externalip`); 1; }
2028
            #        or do {$e=1; $postreply .= "Status=ERROR Problem setting up routing $@\n";};
2029
                last if ($duprules >1);
2030
            }
2031
            # `/sbin/iptables -D POSTROUTING -t nat -s $internalip -j LOG --log-prefix "SNAT-POST"`;
2032
            # `/sbin/iptables -D INPUT -t nat -s $internalip -j LOG --log-prefix "SNAT-INPUT"`;
2033
            # `/sbin/iptables -D OUTPUT -t nat -s $internalip -j LOG --log-prefix "SNAT-OUTPUT"`;
2034
            # `/sbin/iptables -D PREROUTING -t nat -s $internalip -j LOG --log-prefix "SNAT-PRE"`;
2035
        }
2036
    } elsif ($type eq "remoteip") {
2037
        `pkill -f 'R $externalip'`;
2038
        # Deactivate the ip on remoteipprovider
2039
        my $res = $main::postToOrigo->($engineid, 'deactivateremoteip', "$externalip", 'remoteip');
2040
        my $res_obj = JSON::from_json($res);
2041
        if ($res_obj->{status} ne 'OK') {
2042
            $postreply .= "Status=OK There was a problem deactivating the remote IP\n";
2043
        }
2044
    } elsif ($type eq "externalip") {
2045
        if ($externalip && $externalip ne "--") {
2046
            # We are dealing with multiple upstream routes - configure local routing
2047
            if ($proxynic && $proxynic ne $extnic) {
2048
                my $proxyroute = `/sbin/ip route show table proxyarp`;
2049
                `/sbin/ip route del $externalip/32 dev br$id:proxy src $proxyip table proxyarp` if ($proxyroute =~ /$externalip/);
2050
            }
2051

    
2052
            eval {`/sbin/ip route del $externalip/32 dev br$id:proxy`; 1;}
2053
                or do {$e=1; $postreply .= "Status=ERROR Problem deconfiguring proxy arp $@\n";};
2054

    
2055
            if ($ports && $ports ne "--") {
2056
                my @portslist = split(/, ?| /, $ports);
2057
                foreach my $port (@portslist) {
2058
                    my $ipfilter;
2059
                    if ($port =~ /(\d+)\.(\d+)\.(\d+)\.(\d+)(\/\d+)?:(\d+)/) {
2060
                        my $portip = "$1.$2.$3.$4$5";
2061
                        $port = $6;
2062
                        $ipfilter = "-s $portip";
2063
                    } else {
2064
                        $port = 0 unless ($port =~ /\d+/);
2065
                    }
2066
                    if ($port<1 || $port>65535) {
2067
                        $postreply .= "Status=ERROR Invalid port mapping for $name\n";
2068
                        $ports = "--";
2069
                        last;
2070
                    }
2071

    
2072
                    if ($port>1 || $port<65535) {
2073
                        # repeat for good measure
2074
                        for (my $di=0; $di < 10; $di++) {
2075
                            $duprules = 0;
2076
                            eval {$duprules++ if (`/sbin/iptables -D FORWARD -p tcp -i $proxynic $ipfilter -d $externalip --dport $port -j RETURN`); 1;}
2077
                                or do {$e=1; $postreply .= "Status=ERROR Problem setting up routing $@\n";};
2078
                            eval {$duprules++ if (`/sbin/iptables -D FORWARD -p udp -i $proxynic $ipfilter -d $externalip --dport $port -j RETURN`); 1;}
2079
                                or do {$e=1; $postreply .= "Status=ERROR Problem setting up routing $@\n";};
2080
                            last if ($duprules > 1);
2081
                        }
2082
                    }
2083
                }
2084
            }
2085
            # Remove rule to allow forwarding from $externalip
2086
	        `/sbin/iptables --delete FORWARD --in-interface br$id -s $externalip -j RETURN`;
2087
            # Remove rule to disallow setting up a dhcp server
2088
            eval {`/sbin/iptables -D FORWARD -p udp -i $proxynic -d $externalip --dport 67 -j REJECT`; 1;}
2089
                or do {$e=1; $postreply .= "Status=ERROR Problem setting up routing $@\n";};
2090
            # Leave outgoing connectivity - not
2091
            eval {`/sbin/iptables -D FORWARD -i $proxynic -d $externalip -m state --state ESTABLISHED,RELATED -j RETURN`; 1;}
2092
                or do {$e=1; $postreply .= "Status=ERROR Problem setting up routing $@\n";};
2093
            eval {`/sbin/iptables -D FORWARD -i $proxynic -d $externalip -j RETURN`; 1;}
2094
                or do {$e=1; $postreply .= "Status=ERROR Problem setting up routing $@\n";};
2095
            # No need to reject - we reject all per default to the subnet
2096
            eval {`/sbin/iptables -D FORWARD -i $proxynic -d $externalip -j REJECT`; 1;}
2097
                or do {$e=1; $postreply .= "Status=ERROR Problem setting up routing $@\n";};
2098
        }
2099
    }
2100
    # Deconfigure internal dhcp server
2101
    if ($type eq "internalip" || $type eq "ipmapping" || $type eq "remoteip") {
2102
        my $result =  removeDHCPAddress($id, $domains, $internalip);
2103
        if ($result ne "OK") {
2104
            $e=1;
2105
            $postreply .= "$result\n";
2106
        }
2107
    } elsif ($type eq "externalip" && $domains) {
2108
        my $result =  removeDHCPAddress($id, $domains, $externalip);
2109
        if ($result ne "OK") {
2110
            $e=1;
2111
            $postreply .= "$result\n";
2112
        }
2113
    }
2114
    $uistatus = ($e)?"":validateStatus($register{$uuid});
2115
    if ($uistatus) {
2116
        $uiuuid = $uuid;
2117
        $postreply .= "Status=$uistatus OK $action $type $name: $uistatus\n";
2118
    } else {
2119
        $postreply .= "Status=ERROR Cannot $action $type $name: $uistatus\n";
2120
    }
2121
    $main::syslogit->($user, 'info', "$action network $uuid ($name, $id) -> $uistatus");
2122
    updateBilling("$uistatus $uuid ($id)");
2123
    # $main::updateUI->({tab=>"networks", user=>$user, uuid=>$uiuuid, status=>$uistatus}) if ($uistatus);
2124
    return $postreply;
2125
}
2126

    
2127
sub Stop {
2128
    my ($id, $action) = @_;
2129
    # Check if we were passed a uuid
2130
    if ($id =~ /\-/ && $register{$id} && ($register{$id}->{'user'} eq $user || $isadmin)) {
2131
        $id = $register{$id}->{'id'}
2132
    }
2133
    if ($help) {
2134
        return <<END
2135
GET:uuid:
2136
Stops a network by removing gateway. Network must be in status up or nat.
2137
END
2138
    }
2139

    
2140
    my $idleft = ($id>99)?(substr $id,0,-2)+0 : 0;
2141
    my $idright = (substr $id,-2) + 0;
2142
    my $e = 0;
2143
    # First deactivate all user's networks with same id
2144
    my @regkeys = (tied %register)->select_where("user = '$user'");
2145
    foreach my $key (@regkeys) {
2146
        my $valref = $register{$key};
2147
        my $cuuid = $valref->{'uuid'};
2148
        my $ctype = $valref->{'type'};
2149
        my $cdbuser = $valref->{'user'};
2150
        my $cid = $valref->{'id'};
2151
    # Only list networks belonging to current user
2152
        if ($user eq $cdbuser && $id eq $cid && $ctype ne "gateway") {
2153
            if ($ctype eq "internalip" || $ctype eq "ipmapping" || $ctype eq "externalip") {
2154
                my $result = Deactivate($cuuid, 'deactivate');
2155
                if ($result =~ /\w+=ERROR (.+)/i) {
2156
                    $e = $1;
2157
                }
2158
            }
2159
        }
2160
     }
2161
    my $interfaces = `/sbin/ifconfig br$id`;
2162
     # Only take down interface and vlan if gateway IP is active on interface
2163
    if ($e) {
2164
        $postreply .= "Status=Error Not taking down gateway, got an error: $e\n"
2165
#    } elsif ($interfaces =~ /^$datanic\.$id.+\n.+inet .+10\.$idleft\.$idright\.1/
2166
    } elsif ($interfaces =~ /10\.$idleft\.$idright\.1/) {
2167
        eval {`/sbin/brctl delif br$id $datanic.$id`; 1;} or do {$e=1;};
2168
        eval {`/sbin/ifconfig br$id down`; 1;} or do {$e=1;};
2169
        eval {`/sbin/ifconfig $datanic.$id down`; 1;} or do {$e=1;};
2170
        eval {`/sbin/vconfig rem $datanic.$id`; 1;} or do {$e=1;};
2171
        eval {`/sbin/brctl delbr br$id`; 1;} or do {$e=1;};
2172
    } else {
2173
        $postreply .= "Status=Error Not taking down interface, gateway 10.$idleft.$idright.1 is not active on interface br$id - $interfaces.\n"
2174
    }
2175
    # Remove rule to only forward packets coming from subnet assigned to vlan
2176
#    `/sbin/iptables --delete FORWARD --in-interface $datanic.$id ! -s 10.$idleft.$idright.0/24 -j DROP`;
2177

    
2178
    $uistatus = ($e)?$uistatus:"down";
2179
    if ($uistatus eq 'down') {
2180
        $uiuuid = $uuid;
2181
        $postreply .= "Status=$uistatus OK $action gateway: $uistatus\n";
2182
    } else {
2183
        $postreply .= "Status=Error Cannot $action $type $name: $uistatus\n";
2184
    }
2185
    return $postreply;
2186
}
2187

    
2188
sub getDomains {
2189
    my $uuid = shift;
2190
    my $domains;
2191
    my $domainnames;
2192
    my @domregvalues = values %domreg;
2193
    foreach my $domval (@domregvalues) {
2194
        if (($domval->{'networkuuid1'} eq $uuid || $domval->{'networkuuid2'} eq $uuid || $domval->{'networkuuid3'} eq $uuid)
2195
                && $domval->{'user'} eq $user) {
2196
            $domains .= $domval->{'uuid'} . ", ";
2197
            $domainnames .= $domval->{'name'} . ", ";
2198
        }
2199
    }
2200
    $domains = substr $domains, 0, -2;
2201
    $domainnames = substr $domainnames, 0, -2;
2202
    return ($domains, $domainnames); 
2203
}
2204

    
2205
sub getSystems {
2206
    my $uuid = shift;
2207
    my $systems;
2208
    my $systemnames;
2209
    unless (tie(%sysreg,'Tie::DBI', Hash::Merge::merge({table=>'systems'}, $Stabile::dbopts)) ) {$res .= qq|{"status": "Error": "message": "Unable to access systems register"}|; return $res;};
2210
    my @sysregvalues = values %sysreg;
2211
    foreach my $sysval (@sysregvalues) {
2212
        my $networkuuids = $sysval->{'networkuuids'};
2213
        if ($networkuuids =~ /$uuid/ && $sysval->{'user'} eq $user) {
2214
            $systems = $sysval->{'uuid'};
2215
            $systemnames = $sysval->{'name'};
2216
            last;
2217
        }
2218
    }
2219
    unless ($systems) {
2220
        my @sysregvalues = values %domreg;
2221
        foreach my $sysval (@sysregvalues) {
2222
            my $networkuuids = $sysval->{'networkuuids'};
2223
            if ($networkuuids =~ /$uuid/ && $sysval->{'user'} eq $user) {
2224
                $systems = $sysval->{'uuid'};
2225
                $systemnames = $sysval->{'name'};
2226
                last;
2227
            }
2228
        }
2229
    }
2230
    return ($systems, $systemnames);
2231
}
2232

    
2233
sub getNextId {
2234
	# Find the next available vlan id
2235
	my $reqid = shift;
2236
	my $username = shift;
2237
	$username = $user unless ($username);
2238
    my $nextid = 1;
2239
	my $vlanstart = $Stabile::config->get('VLAN_RANGE_START');
2240
	my $vlanend = $Stabile::config->get('VLAN_RANGE_END');
2241

    
2242
    if ($reqid eq 0 || $reqid == 1) {
2243
        return $requid;
2244
    } elsif ($reqid && ($reqid > $vlanend || $reqid < $vlanstart)) {
2245
        return -1 unless ($isadmin);
2246
    }
2247

    
2248
	$reqid = $reqid + 0;
2249

    
2250
    my %ids;
2251
    # First check if the user has an existing vlan, if so use the first we find as default value
2252
    my @regvalues = values %register;
2253
    @regvalues = (sort {$a->{id} <=> $b->{id}} @regvalues);
2254
    foreach my $val (@regvalues) { # Traverse all id's in use
2255
        my $id = 0 + $val->{'id'};
2256
        my $dbuser = $val->{'user'};
2257
        if ($id > 1) {
2258
            if ($username eq $dbuser) { # If a specific id was requested map all id's
2259
                if (!$reqid) {# If no specific id was asked for, stop now, and use the user's first one
2260
                    $nextid = $id;
2261
                    last;
2262
                }
2263
            } else {
2264
                $ids{$id} = 1; # Mark this id as used (by another user)
2265
            }
2266
        }
2267
    }
2268
    if ($nextid>1) {
2269
        return $nextid;
2270
    } elsif ($reqid) {
2271
        if (!$ids{$reqid} || $isadmin) { # If an admin is requesting id used by another, assume he knows what he is doing
2272
            $nextid = $reqid; # Safe to use
2273
        } else {
2274
            $nextid = -1; # Id already in use by another
2275
        }
2276
    } elsif ($nextid == 1) { # This user is not currently using any vlan's, find the first free one
2277
        for ($n=$vlanstart; $n<$vlanend; $n++) {
2278
            if (!$ids{$n}) { # Don't return an id used (by another user)
2279
                $nextid = $n;
2280
                last;
2281
            }
2282
        }
2283
    }
2284
	return $nextid;
2285
}
2286

    
2287
sub getNextRemoteIP {
2288
    my $internalip = shift;
2289
    my $nextip = "";
2290
    my $oc = overQuotas(1);
2291
    if ($oc) { # Enforce quotas
2292
        $postreply .= "Status=ERROR Over quota allocating external IP\n";
2293
    } else {
2294
        my $res = $main::postToOrigo->($engineid, 'provisionremoteip', $internalip, 'internalip');
2295
        my $res_obj = JSON::from_json($res);
2296
        $nextip = $res_obj->{remoteip} if ($res_obj->{remoteip});
2297
    }
2298
    $postreply .= "Status=ERROR No more ($oc) remote IPs available\n" unless ($nextip);
2299
    return $nextip;
2300

    
2301
}
2302
sub getNextExternalIP {
2303
	# Find the next available IP
2304
	my $extip = shift;
2305
	my $extuuid = shift;
2306
	my $proxyarp = shift; # Are we trying to assign a proxy arp's external IP?
2307
	$extip = "" if ($extip eq "--");
2308

    
2309
	my $extipstart;
2310
	my $extipend;
2311

    
2312
    if ($proxyarp) {
2313
        $extipstart = $Stabile::config->get('PROXY_IP_RANGE_START');
2314
        $extipend = $Stabile::config->get('PROXY_IP_RANGE_END');
2315
    } else {
2316
        $extipstart = $Stabile::config->get('EXTERNAL_IP_RANGE_START');
2317
        $extipend = $Stabile::config->get('EXTERNAL_IP_RANGE_END');
2318
    }
2319

    
2320
	return "" unless ($extipstart && $extipend);
2321

    
2322
	my $interfaces = `/sbin/ifconfig`;
2323
#	$interfaces =~ m/eth0 .+\n.+inet addr:(\d+\.\d+\.\d+)\.(\d+)/;
2324
	$extipstart =~  m/(\d+\.\d+\.\d+)\.(\d+)/;
2325
	my $bnet1 = $1;
2326
	my $bhost1 = $2+0;
2327
	$extipend =~  m/(\d+\.\d+\.\d+)\.(\d+)/;
2328
	my $bnet2 = $1;
2329
	my $bhost2 = $2+0;
2330
	my $nextip = "";
2331
	if ($bnet1 ne $bnet2) {
2332
		print "Status=ERROR Only 1 class C subnet is supported for $name\n";
2333
		return "";
2334
	}
2335
	my %ids;
2336
	# First create map of IP's reserved by other servers in DB
2337
	my @regvalues = values %register;
2338
	foreach my $val (@regvalues) {
2339
		my $ip = $val->{'externalip'};
2340
		# $ip =~ m/(\d+\.\d+\.\d+)\.(\d+)/;
2341
		# my $id = $2;
2342
		$ids{$ip} = $val->{'uuid'} unless ($extuuid eq $val->{'uuid'});
2343
	}
2344
    my $oc = overQuotas(1);
2345
	if ($oc) { # Enforce quotas
2346
        $postreply .= "Status=ERROR Over quota allocating external IP\n";
2347
	} elsif ($extip && $extip =~  m/($bnet1)\.(\d+)/ && $2>=$bhost1 && $2<$bhost2) {
2348
	# An external ip was supplied - check if it's free and ok
2349
		if (!$ids{$extip} && !($interfaces =~ m/$extip.+\n.+inet addr:$extip/) && $extip=~/$bnet$\.(\d)/) {
2350
			$nextip = $extip;
2351
		}
2352
	} else {
2353
	# Find random IP not reserved, and check it is not in use (for other purposes)
2354
	    my @bhosts = ($bhost1..$bhost2);
2355
        my @rbhosts = shuffle @bhosts;
2356
		for ($n=0; $n<$bhost2-$bhost1; $n++) {
2357
		    my $nb = $rbhosts[$n];
2358
			if (!$ids{"$bnet1.$nb"}) {
2359
				if (!($interfaces =~ m/$extip.+\n.+inet addr:$bnet1\.$nb/)) {
2360
					$nextip = "$bnet1.$nb";
2361
					last;
2362
				}
2363
			}
2364
		}
2365
	}
2366
	$postreply .= "Status=ERROR No more ($oc) external IPs available\n" unless ($nextip);
2367
	return $nextip;
2368
}
2369

    
2370
sub ip2domain {
2371
    my $ip = shift;
2372
    my $ruuid;
2373
    if ($ip) {
2374
        my @regkeys = (tied %register)->select_where("internalip = '$ip' OR externalip = '$ip'");
2375
        foreach my $k (@regkeys) {
2376
            my $valref = $register{$k};
2377
            if ($valref->{'internalip'} eq $ip || $valref->{'externalip'} eq $ip) {
2378
                $ruuid = $valref->{'domains'};
2379
                last;
2380
            }
2381
        }
2382
    }
2383
    return $ruuid;
2384
}
2385

    
2386
sub getNextInternalIP {
2387
	my $intip = shift;
2388
	my $uuid = shift;
2389
	my $id = shift;
2390
	my $username = shift;
2391
	$username = $user unless ($username);
2392
	my $nextip = "";
2393
	my $intipnum;
2394
	my $subnet;
2395
	my %ids;
2396
    my $ping = Net::Ping->new();
2397

    
2398
    $id = getNextId() unless ($id);
2399
    my $idleft = ($id>99)?(substr $id,0,-2)+0 : 0;
2400
    my $idright = (substr $id,-2) + 0;
2401
    $intip = "10.$idleft.$idright.0" if (!$intip || $intip eq '--');
2402
    
2403
    return '' unless ($intip =~ m/(\d+\.\d+\.\d+)\.(\d+)/ );
2404
    $subnet = $1;
2405
    $intipnum = $2;
2406

    
2407
	# First create hash of IP's reserved by other servers in DB
2408
	my @regvalues = values %register;
2409
	foreach my $val (@regvalues) {
2410
    	if ($val->{'user'} eq $username) {
2411
            my $ip = $val->{'internalip'} ;
2412
            $ids{$ip} = $val->{'uuid'};
2413
		}
2414
	}
2415

    
2416
	if ($intipnum && $intipnum>1 && $intipnum<255) {
2417
	# An internal ip was supplied - check if it's free, if not keep the ip already registered in the db
2418
        if (!$ids{$intip}
2419
#            && !($ping->ping($intip, 0.1)) # 0.1 secs timeout, check if ip is in use, possibly on another engine
2420
            && !(`arping -C1 -c2 -D -I $datanic.$id $intip` =~ /reply from/)  # check if ip is created on another engine
2421
        ) {
2422
            $nextip = $intip;
2423
        } else {
2424
            $nextip = $register{$uuid}->{'internalip'}
2425
        }
2426
	} else {
2427
	# Find first IP not reserved
2428
		for ($n=2; $n<255; $n++) {
2429
			if (!$ids{"$subnet.$n"}
2430
# TODO: The arping check takes too long - two networks created by the same user can too easily be assigned the same IP's
2431
#                && !(`arping -f -c2 -D -I $datanic.$id $subnet.$n` =~ /reply from/)  # check if ip is created on another engine
2432
			) {
2433
                $nextip = "$subnet.$n";
2434
                last;
2435
			}
2436
		}
2437
	}
2438
	$postreply .= "Status=ERROR No more internal IPs available\n" if (!$nextip);
2439
	return $nextip;
2440
}
2441

    
2442
sub validateStatus {
2443
    my $valref = shift;
2444
    my $interfaces = `/sbin/ifconfig -a | grep inet`;
2445
    my $uuid = $valref->{'uuid'};
2446
    my $type = $valref->{'type'};
2447
    my $id = $valref->{'id'};
2448
    my $idleft = ($id>99)?(substr $id,0,-2)+0 : 0;
2449
    my $idright = (substr $id,-2) + 0;
2450

    
2451
    ( $valref->{'domains'}, $valref->{'domainnames'} ) = getDomains($uuid);
2452
    my ( $systems, $systemnames ) = getSystems($uuid);
2453
    my $extip = $valref->{'externalip'};
2454
    my $intip = $valref->{'internalip'};
2455

    
2456
    if ($type eq "gateway") {
2457
        $valref->{'internalip'} = "10.$idleft.$idright.1" if ($id>1);
2458
    } else {
2459
        if ($intip && $intip ne "--" && $extip && $extip ne "--") {
2460
            $type = "ipmapping" unless ($type eq 'remoteip');
2461
        } elsif ($intip && $intip ne "--") {
2462
            $type = "internalip";
2463
        } elsif ($extip && $extip ne "--") {
2464
            $type = "externalip";
2465
        } else {
2466
            $type = "gateway";
2467
        }
2468
        $valref->{'type'} = $type;
2469
    }
2470

    
2471
    $valref->{'status'} = "down";
2472
    my $nat;
2473
    if ($id == 0 || $id == 1) {
2474
        $valref->{'status'} = "nat";
2475
    # Check if vlan $id is created (and doing nat)
2476
#    } elsif ($interfaces =~ m/$datanic\.$id.+\n.+10\.$idleft\.$idright\.1/) {
2477
    } elsif (-e "/proc/net/vlan/$datanic.$id") {
2478
        $nat = 1;
2479
    }
2480

    
2481
    if ($type eq "internalip" || $type eq "ipmapping" || $type eq "remoteip") {
2482
        $valref->{'status'} = "nat" if ($nat);
2483
        my $dhcprunning;
2484
        my $dhcpconfigured;
2485
        eval {
2486
            my $psid;
2487
            $psid = `/bin/cat /var/run/stabile-$id.pid` if (-e "/var/run/stabile-$id.pid");
2488
            chomp $psid;
2489
            $dhcprunning = -e "/proc/$psid" if ($psid);
2490
            my $dhcphosts;
2491
            $dhcphosts = lc `/bin/cat $etcpath/dhcp-hosts-$id` if (-e "$etcpath/dhcp-hosts-$id");
2492
            $dhcpconfigured = ($dhcphosts =~ /$intip/);
2493
            1;
2494
        } or do {;};
2495

    
2496
        if ($type eq "internalip" || $type eq "remoteip") {
2497
        # Check if external ip has been created and dhcp is ok
2498
            if ($nat && (($dhcprunning && $dhcpconfigured) || $systems)) {
2499
                if ($type eq "remoteip") {
2500
                    if (`pgrep -f 'ssh .* $externalip'`) {
2501
                        $valref->{'status'} = "up";
2502
                    }
2503
                } else {
2504
                    $valref->{'status'} = "up";
2505
                }
2506
            }
2507
        } elsif ($type eq "ipmapping") {
2508
        # Check if external ip has been created, dhcp is ok and vlan interface is created
2509
        # An ipmapping linked to a system is considered up if external interface exists
2510
        # Update: It appears that ip addresses on virtual interfaces are periodically lost for some reason
2511
        # the interface however still responds to the ip address if iptables rules referencing this exists
2512
        # so we have relaxed the up requirement
2513
            if ($nat
2514
        #            && $interfaces =~ m/$extip/ # interfaces seem to drop out of sight after while even if still active
2515
                    && (($dhcprunning && $dhcpconfigured) || ($systems && $interfaces =~ m/$extip/))
2516
            ) {
2517
                $valref->{'status'} = "up";
2518
            }
2519
        }
2520

    
2521
    } elsif ($type eq "externalip") {
2522
        my $dhcprunning;
2523
        my $dhcpconfigured;
2524
        eval {
2525
            my $psid;
2526
            $psid = `/bin/cat /var/run/stabile-$id.pid` if (-e "/var/run/stabile-$id.pid");
2527
            chomp $psid;
2528
            $dhcprunning = -e "/proc/$psid" if ($psid);
2529
            my $dhcphosts;
2530
            $dhcphosts = `/bin/cat $etcpath/dhcp-hosts-$id` if (-e "$etcpath/dhcp-hosts-$id");
2531
            $dhcpconfigured = ($dhcphosts =~ /$extip/);
2532
            1;
2533
        } or do {;};
2534

    
2535
        my $vproxy = `/bin/cat /proc/sys/net/ipv4/conf/$datanic.$id/proxy_arp`; chomp $vproxy;
2536
        my $eproxy = `/bin/cat /proc/sys/net/ipv4/conf/$proxynic/proxy_arp`; chomp $eproxy;
2537
        my $proute = `/sbin/ip route | grep "$extip dev"`; chomp $proute;
2538
        if ($vproxy && $eproxy && $proute) {
2539
            if ((($dhcprunning && $dhcpconfigured) || $systems)) {
2540
                $valref->{'status'} = "up";
2541
            } elsif (!$valref->{'domains'}) {
2542
                $valref->{'status'} = "nat";
2543
            }
2544
        } else {
2545
            #print "$vproxy && $eproxy && $proute && $dhcprunning && $dhcpconfigured :: $extip\n";        
2546
        }
2547

    
2548
    } elsif ($type eq "gateway") {
2549
        if ($nat || $id == 0 || $id == 1) {$valref->{'status'} = "up";}
2550
    }
2551
    return $valref->{'status'};
2552
}
2553

    
2554
sub trim{
2555
   my $string = shift;
2556
   $string =~ s/^\s+|\s+$//g;
2557
   return $string;
2558
}
2559

    
2560
sub overQuotas {
2561
    my $reqips = shift; # number of new ip's we are asking for
2562
	my $usedexternalips = 0;
2563
	my $overquota = 0;
2564
    return $overquota if ($Stabile::userprivileges =~ /a/); # Don't enforce quotas for admins
2565

    
2566
	my $externalipquota = $Stabile::userexternalipquota;
2567
	if (!$externalipquota) {
2568
        $externalipquota = $Stabile::config->get('EXTERNAL_IP_QUOTA');
2569
    }
2570

    
2571
	my $rxquota = $Stabile::userrxquota;
2572
	if (!$rxquota) {
2573
        $rxquota = $Stabile::config->get('RX_QUOTA');
2574
    }
2575

    
2576
	my $txquota = $Stabile::usertxquota;
2577
	if (!$txquota) {
2578
        $txquota = $Stabile::config->get('TX_QUOTA');
2579
    }
2580

    
2581
    my @regkeys = (tied %register)->select_where("user = '$user'");
2582
	foreach my $k (@regkeys) {
2583
	    my $val = $register{$k};
2584
		if ($val->{'user'} eq $user && $val->{'externalip'} && $val->{'externalip'} ne "--" ) {
2585
		    $usedexternalips += 1;
2586
		}
2587
	}
2588
	if ((($usedexternalips + $reqips) > $externalipquota) && $externalipquota > 0) { # -1 means no quota
2589
	    $overquota = $usedexternalips;
2590
	} elsif ($rx > $rxquota*1024 && $rxquota > 0) {
2591
	    $overquota = -1;
2592
	} elsif ($tx > $txquota*1024 && $txquota > 0) {
2593
	    $overquota = -2;
2594
	}
2595
	return $overquota;
2596
}
2597

    
2598
sub updateBilling {
2599
    my $event = shift;
2600
    my %billing;
2601
    my @regkeys = (tied %register)->select_where("user = '$user' or user = 'common'") unless ($fulllist);
2602
    foreach my $k (@regkeys) {
2603
        my $valref = $register{$k};
2604
        my %val = %{$valref}; # Deference and assign to new array, effectively cloning object
2605
        if ($val{'user'} eq $user && ($val{'type'} eq 'ipmapping' || $val{'type'} eq 'externalip') && $val{'externalip'} ne '--') {
2606
            $billing{$val{'id'}}->{'externalip'} += 1;
2607
        }
2608
    }
2609

    
2610
    my %billingreg;
2611
    my $monthtimestamp = timelocal(0,0,0,1,$mon,$year); #$sec,$min,$hour,$mday,$mon,$year
2612

    
2613
    unless ( tie(%billingreg,'Tie::DBI', Hash::Merge::merge({table=>'billing_networks', key=>'useridtime'}, $Stabile::dbopts)) ) {return "Unable to access billing register"};
2614

    
2615
    my $rx_bytes_total = 0;
2616
    my $tx_bytes_total = 0;
2617

    
2618
    my $prevmonth = $month-1;
2619
    my $prevyear = $year;
2620
    if ($prevmonth == 0) {$prevmonth=12; $prevyear--;};
2621
    $prevmonth = substr("0" . $prevmonth, -2);
2622
    my $prev_rx_bytes_total = 0;
2623
    my $prev_tx_bytes_total = 0;
2624

    
2625
    foreach my $id (keys %billing) {
2626
        my $b = $billing{$id};
2627
        my $externalip = $b->{'externalip'};
2628
        my $externalipavg = 0;
2629
        my $startexternalipavg = 0;
2630
        my $starttimestamp = $current_time;
2631
        my $rx_bytes = 0;
2632
        my $tx_bytes = 0;
2633
#        my $rx_stats = "/sys/class/net/$datanic.$id/statistics/rx_bytes";
2634
#        my $tx_stats = "/sys/class/net/$datanic.$id/statistics/tx_bytes";
2635
        my $rx_stats = "/sys/class/net/br$id/statistics/rx_bytes";
2636
        my $tx_stats = "/sys/class/net/br$id/statistics/tx_bytes";
2637
        $rx_bytes = `/bin/cat $rx_stats` if (-e $rx_stats);
2638
        chomp $rx_bytes;
2639
        $tx_bytes = `/bin/cat $tx_stats` if (-e $tx_stats);
2640
        chomp $tx_bytes;
2641

    
2642
        if ($current_time - $monthtimestamp < 4*3600) {
2643
            $starttimestamp = $monthtimestamp;
2644
            $externalipavg = $externalip;
2645
            $startexternalipavg = $externalip;
2646
        }
2647

    
2648
        my $bill = $billingreg{"$user-$id-$year-$month"};
2649
        my $regrx_bytes = $bill->{'rx'};
2650
        my $regtx_bytes = $bill->{'tx'};
2651
        $rx_bytes += $regrx_bytes if ($regrx_bytes > $rx_bytes); # Network interface was reloaded
2652
        $tx_bytes += $regtx_bytes if ($regtx_bytes > $tx_bytes); # Network interface was reloaded
2653

    
2654
        # Update timestamp and averages on existing row
2655
        if ($billingreg{"$user-$id-$year-$month"}) {
2656
            $startexternalipavg = $bill->{'startexternalipavg'};
2657
            $starttimestamp = $bill->{'starttimestamp'};
2658

    
2659
            $externalipavg = ($startexternalipavg*($starttimestamp - $monthtimestamp) + $externalip*($current_time - $starttimestamp)) /
2660
                            ($current_time - $monthtimestamp);
2661

    
2662
            $billingreg{"$user-$id-$year-$month"}->{'externalip'} = $externalip;
2663
            $billingreg{"$user-$id-$year-$month"}->{'externalipavg'} = $externalipavg;
2664
            $billingreg{"$user-$id-$year-$month"}->{'timestamp'} = $current_time;
2665
            $billingreg{"$user-$id-$year-$month"}->{'rx'} = $rx_bytes;
2666
            $billingreg{"$user-$id-$year-$month"}->{'tx'} = $tx_bytes;
2667
        }
2668

    
2669
        # No row found or something happened which justifies writing a new row
2670
        if (!$billingreg{"$user-$id-$year-$month"}
2671
        || ($b->{'externalip'} != $bill->{'externalip'})
2672
        ) {
2673

    
2674
            my $inc = 0;
2675
            if ($billingreg{"$user-$id-$year-$month"}) {
2676
                $startexternalipavg = $externalipavg;
2677
                $starttimestamp = $current_time;
2678
                $inc = $bill->{'inc'};
2679
            }
2680
            # Write a new row
2681
            $billingreg{"$user-$id-$year-$month"} = {
2682
                externalip=>$externalip+0,
2683
                externalipavg=>$externalipavg,
2684
                startexternalipavg=>$startexternalipavg,
2685
                timestamp=>$current_time,
2686
                starttimestamp=>$starttimestamp,
2687
                event=>$event,
2688
                inc=>$inc+1,
2689
                rx=>$rx_bytes,
2690
                tx=>$tx_bytes
2691
            };
2692
        }
2693

    
2694
        $rx_bytes_total += $rx_bytes;
2695
        $tx_bytes_total += $tx_bytes;
2696
        my $prevbill = $billingreg{"$user-$id-$prevyear-$prevmonth"};
2697
        $prev_rx_bytes_total += $prevbill->{'rx'};
2698
        $prev_tx_bytes_total += $prevbill->{'tx'};
2699
    }
2700
    untie %billingreg;
2701
    $rx = ($rx_bytes_total>$prev_rx_bytes_total)?$rx_bytes_total - $prev_rx_bytes_total:$rx_bytes_total;
2702
    $tx = ($tx_bytes_total>$prev_tx_bytes_total)?$tx_bytes_total - $prev_tx_bytes_total:$tx_bytes_total;
2703
    my $oq = overQuotas();
2704
    if ($oq && $oq<0) {
2705
        foreach my $id (keys %billing) {
2706
            $main::syslogit->($user, 'info', "$user over rx/tx quota ($oq) stopping network $id");
2707
            Stop($id, 'stop');
2708
        }
2709
    }
2710
}
2711

    
2712
sub Bit2netmask {
2713
	my $netbit = shift;
2714
	my $_bit         = ( 2 ** (32 - $netbit) ) - 1;
2715
	my ($full_mask)  = unpack( "N", pack( "C4", split(/./, '255.255.255.255') ) );
2716
	my $netmask      = join( '.', unpack( "C4", pack( "N", ( $full_mask ^ $_bit ) ) ) );
2717
	return $netmask;
2718
}
(3-3/9)