Project

General

Profile

Download (112 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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    
452
}
453

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

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

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

    
478

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    
858
END
859
;
860
}
861

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

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

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

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

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

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

    
924
        close (TEMP1);
925
#    }
926

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    
1207
            } 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
                        $ports = "--";
1240
                        last;
1241
                    }
1242
                }
1243
            }
1244
            if ($ports ne "--") {
1245
                $ports = join(',', @portslist);
1246
            }
1247
            if ($systems ne $regnet->{'systems'}) {
1248
                my $regsystems = $regnet->{'systems'};
1249
                unless (tie(%sysreg,'Tie::DBI', Hash::Merge::merge({table=>'systems'}, $Stabile::dbopts)) ) {$res .= qq|{"status": "Error": "message": "Unable to access systems register"}|; return $res;};
1250

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

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

    
1300
        } else {
1301
        	$postreply = "Status=OK Network $uuid ($id) unchanged\n";
1302
        }
1303

    
1304
        if ($params{'PUTDATA'}) {
1305
            my %jitem = %{$register{$uuid}};
1306
            my $json_text = to_json(\%jitem);
1307
            $json_text =~ s/null/"--"/g;
1308
            $json_text =~ s/""/"--"/g;
1309
            $postreply = $json_text;
1310
            $postmsg = $postmsg || "OK, updated network $name";
1311
        }
1312

    
1313
        return $postreply;
1314

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

    
1340
}
1341

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

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

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

    
1421
            # Also export storage pools to user's network
1422
            my @spl = split(/,\s*/, $storagepools);
1423
            my $reloadnfs;
1424
            my $uid = `id -u irigo-$user`; chomp $uid;
1425
            $uid = `id -u nobody` unless ($uid =~ /\d+/); chomp $uid;
1426
            my $gid = `id -g irigo-$user`; chomp $gid;
1427
            $gid = `id -g nobody` unless ($gid =~ /\d+/); chomp $gid;
1428

    
1429
            # We are dealing with multiple upstream routes - configure local routing
1430
            if ($proxynic && $proxynic ne $extnic) {
1431
                if (-e "/etc/iproute2/rt_tables" && !grep(/1 proxyarp/, `cat /etc/iproute2/rt_tables`)) {
1432
                    `/bin/echo "1 proxyarp" >> /etc/iproute2/rt_tables`;
1433
                }
1434
                if (!grep(/$datanic\.$id/, `/sbin/ip route show table proxyarp`)) {
1435
                    `/sbin/ip route add "10.$idleft.$idright.0/24" dev $datanic.$id table proxyarp`;
1436
                }
1437
            }
1438

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

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

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

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

    
1571
                # When receiving packet from client, if it's been routed, and outgoing interface is the external interface, SNAT.
1572
                unless ($Stabile::disablesnat) {
1573
                    eval {`/sbin/iptables -A POSTROUTING -t nat -s $internalip ! -d 10.$idleft.$idright.0/24 -j SNAT --to-source $externalip`; 1; }
1574
                        or do {$e=4; $postreply .= "Status=ERROR Problem setting up routing $@\n";};
1575
                #    eval {`/sbin/iptables -A POSTROUTING -t nat -s $internalip -j SNAT --to-source $externalip`; 1; }
1576
                #        or do {$e=4; $postreply .= "Status=ERROR Problem setting up routing $@\n";};
1577
                    eval {`/sbin/iptables -I INPUT -t nat -s $internalip ! -d 10.$idleft.$idright.0/24 -j SNAT --to-source $externalip`; 1; }
1578
                        or do {$e=4; $postreply .= "Status=ERROR Problem setting up routing $@\n";};
1579
                #    eval {`/sbin/iptables -I INPUT -t nat -s $internalip -j SNAT --to-source $externalip`; 1; }
1580
                #        or do {$e=4; $postreply .= "Status=ERROR Problem setting up routing $@\n";};
1581
                }
1582
                if ($e) {
1583
                    $main::syslogit->($user, 'info', "Problem $action network $uuid ($name, $id): $@");
1584
                } else {
1585
                    $astatus = "up"
1586
                }
1587
            }
1588
        } elsif ($type eq "remoteip") {
1589
            if ($enginelinked && $remoteipenabled && -e "/home/irigo-$engineuser/.ssh/id_rsa") {
1590
                my $cmd = qq|ssh -fN -i /home/irigo-$engineuser/.ssh/id_rsa -o "StrictHostKeyChecking no" -o "UserKnownHostsFile=/dev/null" -o "ExitOnForwardFailure=yes" -R $externalip:10001:$internalip:10001 -R $externalip:80:$internalip:80 -R $externalip:443:$internalip:443 $remoteipprovider|;
1591
                eval {
1592
                    my $daemon = Proc::Daemon->new(
1593
                        work_dir => '/home/irigo-o@origo.io',
1594
                        exec_command => "$cmd"
1595
                    ) or do {$postreply .= "Status=ERROR $@\n";};
1596
                    my $pid = $daemon->Init();
1597
                    $main::syslogit->($user, "info", "Activating $cmd remote ip $externalip at $remoteipprovider for $engineuser, pid=$pid");
1598
                    1;
1599
                } or do {$e=4; $postreply .= "Status=ERROR Problem activating remote ip $@\n";};
1600
                if ($e) {
1601
                    $main::syslogit->($user, 'info', "Problem $action network $uuid ($name, $id): $@");
1602
                } else {
1603
                    $astatus = "up"
1604
                }
1605
            }
1606
        } elsif ($type eq "externalip") {
1607
            my $route = `/sbin/ip route`;
1608
            my $tables = `/sbin/iptables -L -n`;
1609

    
1610
            # Allow external IP send packets out
1611
            `/sbin/iptables -D FORWARD --in-interface br$id -s $externalip -j RETURN`;
1612
            `/sbin/iptables -I FORWARD --in-interface br$id -s $externalip -j RETURN`;
1613

    
1614
            # We are dealing with multiple upstream routes - configure local routing
1615
            if ($proxynic && ($proxynic ne $extnic)) {
1616
                if (-e "/etc/iproute2/rt_tables" && !grep(/1 proxyarp/, `cat /etc/iproute2/rt_tables`)) {
1617
                    `/bin/echo "1 proxyarp" >> /etc/iproute2/rt_tables`;
1618
                }
1619
                if (!grep(/$proxygw/, `/sbin/ip route show table proxyarp`)) {
1620
                    `/sbin/ip route del default dev $proxynic table proxyarp`; # delete first in case proxygw has changed
1621
                    `/sbin/ip route add default via $proxygw dev $proxynic table proxyarp`;
1622
                }
1623
                if (!grep(/proxyarp/, `/sbin/ip rule show`)) {
1624
                    `/sbin/ip rule add to $proxygw/$proxysubnet table main`;
1625
                    `/sbin/ip rule add from $proxygw/$proxysubnet table proxyarp`;
1626
                }
1627
                my $proxyroute = `/sbin/ip route show table proxyarp`;
1628
#                `/sbin/ip route add $externalip/32 dev $datanic.$id:proxy src $proxyip table proxyarp` unless ($proxyroute =~ /$externalip/);
1629
                `/sbin/ip route add $externalip/32 dev br$id:proxy src $proxyip table proxyarp` unless ($proxyroute =~ /$externalip/);
1630
            }
1631
            eval {`/bin/echo 1 > /proc/sys/net/ipv4/conf/$datanic.$id/proxy_arp`; 1;}
1632
                or do {$e=1; $postreply .= "Status=ERROR Problem setting up proxy arp $@\n";};
1633
            eval {`/bin/echo 1 > /proc/sys/net/ipv4/conf/$proxynic/proxy_arp`; 1;}
1634
                or do {$e=1; $postreply .= "Status=ERROR Problem setting up proxy arp $@\n";};
1635
            eval {`/sbin/ip route add $externalip/32 dev br$id:proxy src $proxyip` unless ($route =~ /$externalip/); 1;}
1636
                or do {$e=1; $postreply .= "Status=ERROR Problem setting up proxy arp $@\n";};
1637

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

    
1643

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

    
1648
            if ($ports && $ports ne "--") {
1649
                my @portslist = split(/, ?| /, $ports);
1650
                foreach $port (@portslist) {
1651
                    my $ipfilter;
1652
                    if ($port =~ /(\d+)\.(\d+)\.(\d+)\.(\d+)(\/\d+)?:(\d+)/) {
1653
                        my $portip = "$1.$2.$3.$4$5";
1654
                        $port = $6;
1655
                        $ipfilter = "-s $portip";
1656
                    } else {
1657
                        $port = 0 unless ($port =~ /\d+/);
1658
                    }
1659
                    if ($port<1 || $port>65535) {
1660
                        $postreply .= "Status=ERROR Invalid port mapping for $name\n";
1661
                        $ports = "--";
1662
                        last;
1663
                    }
1664

    
1665
                    if ($port>1 && $port<65535 && $port!=67) { # Disallow setting up a dhcp server
1666
                        eval {`/sbin/iptables -A FORWARD -p tcp -i $proxynic $portfilter -d $externalip --dport $port -j RETURN`; 1;}
1667
                            or do {$e=1; $postreply .= "Status=ERROR Problem setting up routing $@\n";};
1668
                        eval {`/sbin/iptables -A FORWARD -p udp -i $proxynic $portfilter -d $externalip --dport $port -j RETURN`; 1;}
1669
                            or do {$e=1; $postreply .= "Status=ERROR Problem setting up routing $@\n";};
1670
                    }
1671
                }
1672
                eval {`/sbin/iptables -D FORWARD -i $proxynic -d $externalip -j REJECT`; 1;} # Drop traffic to all other ports
1673
                    or do {$e=1; $postreply .= "Status=ERROR Problem setting up routing $@\n";};
1674
                eval {`/sbin/iptables -A FORWARD -i $proxynic -d $externalip -j REJECT`; 1;} # Drop traffic to all other ports
1675
                    or do {$e=1; $postreply .= "Status=ERROR Problem setting up routing $@\n";};
1676
            } else {
1677
                # First allow everything else to this ip
1678
                eval {`/sbin/iptables -D FORWARD -i $proxynic -d $externalip -j RETURN`; 1;}
1679
                    or do {$e=1; $postreply .= "Status=ERROR Problem setting up routing $@\n";};
1680
                eval {`/sbin/iptables -A FORWARD -i $proxynic -d $externalip -j RETURN`; 1;}
1681
                    or do {$e=1; $postreply .= "Status=ERROR Problem setting up routing $@\n";};
1682
                # Then disallow setting up a dhcp server
1683
                eval {`/sbin/iptables -D FORWARD -p udp -i $proxynic -d $externalip --dport 67 -j REJECT`; 1;}
1684
                    or do {$e=1; $postreply .= "Status=ERROR Problem setting up routing $@\n";};
1685
                eval {`/sbin/iptables -A FORWARD -p udp -i $proxynic -d $externalip --dport 67 -j REJECT`; 1;}
1686
                    or do {$e=1; $postreply .= "Status=ERROR Problem setting up routing $@\n";};
1687
            }
1688
        }
1689
    }
1690

    
1691
    # Allow all inter-VLAN communication
1692
    `iptables -D FORWARD --in-interface br$id --out-interface br$id -j RETURN 2>/dev/null`;
1693
    `iptables -I FORWARD --in-interface br$id --out-interface br$id -j RETURN`;
1694
    # Disallow any access to vlan except mapped from external NIC i.e. ipmappings
1695
    `iptables -D FORWARD ! --in-interface $extnic --out-interface br$id -j DROP 2>/dev/null`;
1696
    `iptables -A FORWARD ! --in-interface $extnic --out-interface br$id -j DROP`;
1697

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

    
1704
    # Enable nat'ing
1705
    eval {
1706
        #my $masq = `/sbin/iptables -L -n -t nat`;
1707
        #        if (!($masq =~ "MASQUERADE.+all.+--.+0\.0\.0\.0/0")) {
1708
        `/sbin/iptables -D POSTROUTING -t nat --out-interface $extnic -s 10.0.0.0/8 -j MASQUERADE`;
1709
        `/sbin/iptables -A POSTROUTING -t nat --out-interface $extnic -s 10.0.0.0/8 -j MASQUERADE`;
1710
        # Christian's dev environment
1711
        #            my $interfaces = `/sbin/ifconfig`;
1712
        #            if ($interfaces =~ m/ppp0/) {
1713
        #                `/sbin/iptables --table nat --append POSTROUTING --out-interface ppp0 -s 10.0.0.0/8 -j MASQUERADE`;
1714
        #            }
1715
        #        };
1716
        1;
1717
    } or do {print "Unable to enable masquerading: $@\n";};
1718

    
1719
    $uistatus = ($e)?"":validateStatus($register{$uuid});
1720
    if ($uistatus && $uistatus ne 'down') {
1721
        $uiuuid = $uuid;
1722
        $postreply .= "Status=$uistatus OK $action $type $name\n";
1723
    } else {
1724
        $postreply .= "Status=ERROR Cannot $action $type $name ($uistatus)\n";
1725
    }
1726
    $main::syslogit->($user, 'info', "$action network $uuid ($name, $id) -> $uistatus");
1727
    updateBilling("$uistatus $uuid ($id)");
1728
    # $main::updateUI->({tab=>"networks", user=>$user, uuid=>$uiuuid, status=>$uistatus}) if ($uistatus);
1729
    return $postreply;
1730
}
1731

    
1732
sub Removeusernetworks {
1733
    my $username = shift;
1734
    return unless (($isadmin || $user eq $username) && !$isreadonly);
1735
    $user = $username;
1736
    foreach my $uuid (keys %register) {
1737
        if ($register{$uuid}->{'user'} eq $user) {
1738
            $postreply .=  "Removing network $register{$uuid}->{'name'}, $uuid" . ($console?'':'<br>') . "\n";
1739
            Deactivate($uuid);
1740
            Remove($uuid, 'remove');
1741
        }
1742
    }
1743
}
1744

    
1745
sub Remove {
1746
    my ($uuid, $action, $obj) = @_;
1747
    if ($help) {
1748
        return <<END
1749
DELETE:uuid,force:
1750
Delete a network which must be in status down or nat and should not be used by any servers, or linked to any stacks.
1751
May also be called with endpoints "/stabile/[uuid]" or "/stabile?uuid=[uuid]"
1752
Set [force] to remove even if linked to a system.
1753
END
1754
    }
1755
    $uuid = $obj->{'uuid'} if ($curuuid && $obj->{'uuid'}); # we are called from a VM with an ip address as target
1756
    my $force = $obj->{'force'};
1757
    ( my $domains, my $domainnames ) = getDomains($uuid);
1758
    ( my $systems, my $systemnames ) = getSystems($uuid);
1759

    
1760
    if ($register{$uuid}) {
1761
        my $id = $register{$uuid}->{'id'};
1762
        my $name = $register{$uuid}->{'name'};
1763
        utf8::decode($name);
1764
        my $status = $register{$uuid}->{'status'};
1765
        my $type = $register{$uuid}->{'type'};
1766
        my $internalip = $register{$uuid}->{'internalip'};
1767
        my $externalip = $register{$uuid}->{'externalip'};
1768

    
1769
        my @regvalues = values %register;
1770
        if (
1771
            $id!=0 && $id!=1 && (!$domains || $domains eq '--')
1772
                && ((!$systems || $systems eq '--' || $force)
1773
                # allow internalip's to be removed if active and only linked, i.e. not providing dhcp
1774
                || ($status eq 'down' || $status eq 'new' || $status eq 'nat' || ($type eq 'internalip' && $systems && $systems ne '--')))
1775
        ) {
1776
            # Deconfigure internal dhcp server and DNS
1777
            if ($type eq "internalip") {
1778
                my $result =  removeDHCPAddress($id, $domains, $internalip);
1779
                $postreply .= "$result\n" unless $result eq "OK";
1780
            } elsif ($type eq "ipmapping") {
1781
                my $result =  removeDHCPAddress($id, $domains, $internalip);
1782
                $postreply .= "$result\n" unless $result eq "OK";
1783
                if ($dodns) {
1784
                    $main::dnsDelete->($engineid, $externalip) if ($enginelinked);
1785
                }
1786
            } elsif ($type eq "externalip") {
1787
                my $result =  removeDHCPAddress($id, $domains, $externalip);
1788
                $postreply .= "$result\n" unless $result eq "OK";
1789
                if ($dodns) {
1790
                    $main::dnsDelete->($engineid, $externalip) if ($enginelinked);
1791
                }
1792
            }
1793
            if ($status eq 'nat') {
1794
                # Check if last network in vlan. If so take it down
1795
                my $notlast;
1796
                foreach my $val (@regvalues) {
1797
                    if ($val->{'user'} eq $user && $val->{'id'} == $id) {
1798
                        $notlast = 1;
1799
                    }
1800
                }
1801
                if (!$notlast) {
1802
                    eval {`/sbin/ifconfig $datanic.$id down`; 1;} or do {;};
1803
                    eval {`/sbin/vconfig rem $datanic.$id`; 1;} or do {;};
1804
                }
1805
            }
1806

    
1807
            unless (tie(%sysreg,'Tie::DBI', Hash::Merge::merge({table=>'systems'}, $Stabile::dbopts)) ) {$res .= qq|{"status": "Error": "message": "Unable to access systems register"}|; return $res;};
1808
            if ($sysreg{$systems}) { # Remove existing link to system
1809
                $sysreg{$systems}->{'networkuuids'} =~ s/$uuid,?//;
1810
                $sysreg{$systems}->{'networknames'} = s/$name,?//;
1811
            }
1812
            tied(%sysreg)->commit;
1813
            untie(%sysreg);
1814

    
1815

    
1816
            delete $register{$uuid};
1817
            tied(%register)->commit;
1818
            updateBilling("delete $val->{'externalip'}") if ($type eq "ipmapping");
1819
            $main::syslogit->($user, "info", "Deleted network $uuid ($id)");
1820
            $postreply = "[]" || $postreply;
1821
            $main::updateUI->({tab=>"networks", user=>$user, type=>"update"});
1822
        } else {
1823
            $postreply .= "Status=ERROR Cannot remove $uuid which is $status. Cannot delete network 0,1 or a network which is active or in use.\n";
1824
            $main::updateUI->({tab=>"networks", user=>$user, message=>"Cannot remove a network which is active, linked or in use."});
1825
        }
1826
    } else {
1827
        $postreply .= "Status=ERROR Network $uuid $ipaddress not found\n";
1828
    }
1829
    return $postreply;
1830
}
1831

    
1832
sub Deactivate {
1833
    my ($uuid, $action, $obj) = @_;
1834

    
1835
    if ($help) {
1836
        return <<END
1837
GET:uuid:
1838
Deactivate a network which must be in status up.
1839
END
1840
    }
1841
    $uuid = $obj->{'uuid'} if ($obj->{'uuid'});
1842

    
1843
    unless ($register{$uuid}) {
1844
        $postreply .= "Status=ERROR Connection with uuid $uuid not found\n";
1845
        return $postreply;
1846
    }
1847
    my $regnet = $register{$uuid};
1848

    
1849
    $action = $action || 'deactivate';
1850
    ( my $domains, my $domainnames ) = getDomains($uuid);
1851
    my $interfaces = `/sbin/ifconfig`;
1852

    
1853
    my $id = $regnet->{'id'};
1854
    my $name = $regnet->{'name'};
1855
    my $type = $regnet->{'type'};
1856
    my $internalip = $regnet->{'internalip'};
1857
    my $externalip = $regnet->{'externalip'};
1858
    my $ports = $regnet->{'ports'};
1859

    
1860
    if ($id!=0 && $id!=1 && $status ne 'down') {
1861
    # If gateway is created, take it down along with all user's networks
1862
        if ($action eq "stop") {
1863
            my $res = Stop($id, $action);
1864
            if ($res) {
1865
                unlink "$etcpath/dhcp-hosts-$id" if (-e "$etcpath/dhcp-hosts-$id");
1866
            };
1867
        }
1868
    } else {
1869
        $postreply .= "Status=ERROR Cannot $action network $name\n";
1870
        return $postreply;
1871
    }
1872

    
1873
    my $idleft = ($id>99)?(substr $id,0,-2)+0 : 0;
1874
    my $idright = (substr $id,-2) + 0;
1875
    my $e = 0;
1876
    my $duprules = 0;
1877

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

    
1889
            if ($ports && $ports ne "--") { # Port mapping is defined
1890
                my @portslist = split(/, ?| /, $ports);
1891
                foreach my $port (@portslist) {
1892
                    my $ipfilter;
1893
                    if ($port =~ /(\d+)\.(\d+)\.(\d+)\.(\d+)(\/\d+)?:(\d+)/) {
1894
                        my $portip = "$1.$2.$3.$4$5";
1895
                        $port = $6;
1896
                        $ipfilter = "-s $portip";
1897
                    } else {
1898
                        $port = 0 unless ($port =~ /\d+/);
1899
                    }
1900
                    if ($port<1 || $port>65535) {
1901
                        $postreply .= "Status=ERROR Invalid port mapping for $name\n";
1902
                        $ports = "--";
1903
                        last;
1904
                    }
1905
                    # Remove DNAT rules
1906
                    if ($port>1 || $port<65535) {
1907
                        # repeat for good measure
1908
                        for (my $di=0; $di < 10; $di++) {
1909
                            $duprules = 0;
1910
                            eval {$duprules++ if (`/sbin/iptables -D PREROUTING -t nat -p tcp $ipfilter -d $externalip --dport $port -j DNAT --to $internalip`); 1;}
1911
                                or do {$postreply .= "Status=ERROR $@\n"; $e=1};
1912
                            eval {$duprules++ if (`/sbin/iptables -D PREROUTING -t nat -p udp $ipfilter -d $externalip --dport $port -j DNAT --to $internalip`); 1;}
1913
                                or do {$postreply .= "Status=ERROR $@\n"; $e=1};
1914
                            eval {$duprules++ if (`/sbin/iptables -D OUTPUT -t nat -p tcp $ipfilter -d $externalip --dport $port -j DNAT --to $internalip`); 1;}
1915
                                or do {$postreply .= "Status=ERROR $@\n"; $e=1};
1916
                            eval {$duprules++ if (`/sbin/iptables -D OUTPUT -t nat -p udp $ipfilter -d $externalip --dport $port -j DNAT --to $internalip`); 1;}
1917
                                or do {$postreply .= "Status=ERROR $@\n"; $e=1};
1918
                            eval {$duprules++ if (`/sbin/iptables -D POSTROUTING -t nat --out-interface br$id -s $externalip -j MASQUERADE`); 1;}
1919
                                or do {$e=3; $postreply .= "Status=ERROR Problem setting up routing $@\n";};
1920
                            # Remove access to ipmapped internal ip on $port
1921
                            eval {$duprules++ if (`/sbin/iptables -D FORWARD -d $internalip -p udp --dport $port -j RETURN`); 1;}
1922
                                or do {$e=3; $postreply .= "Status=ERROR Problem setting up routing $@\n";};
1923
                            eval {$duprules++ if (`/sbin/iptables -D FORWARD -d $internalip -p tcp --dport $port -j RETURN`); 1;}
1924
                                or do {$e=3; $postreply .= "Status=ERROR Problem setting up routing $@\n";};
1925
                            last if ($duprules >6);
1926
                        }
1927
                    }
1928
                }
1929
                # Remove SNAT rules
1930
                # repeat for good measure
1931
                for (my $di=0; $di < 10; $di++) {
1932
                    $duprules = 0;
1933
                    eval {$duprules++ if (`/sbin/iptables -D POSTROUTING -t nat -s $internalip ! -d 10.$idleft.$idright.0/24 -j SNAT --to-source $externalip`); 1; }
1934
                        or do {$e=1; $postreply .= "Status=ERROR Problem setting up routing $@\n";};
1935
                    last if ($duprules);
1936
                }
1937
                # Remove rule to drop traffic to all other ports
1938
                eval {`/sbin/iptables -D INPUT -d $externalip -j DROP`; 1;}
1939
                    or do {$e=1; $postreply .= "Status=ERROR Problem setting up routing $@\n";};
1940
            } else {
1941
                # Remove DNAT rules
1942
                # repeat for good measure
1943
                for (my $di=0; $di < 10; $di++) {
1944
                    $duprules = 0;
1945
                    eval {$duprules++ if (`/sbin/iptables -D PREROUTING -t nat -d $externalip -j DNAT --to $internalip`); 1;}
1946
                        or do {$postreply .= "Status=ERROR $@\n"; $e=1};
1947
                    eval {$duprules++ if (`/sbin/iptables -D OUTPUT -t nat -d $externalip -j DNAT --to $internalip`); 1;}
1948
                        or do {$postreply .= "Status=ERROR $@\n"; $e=1};
1949
                    last if ($duprules >1);
1950
                }
1951
                # Remove blanket access to ipmapped internal ip
1952
                `iptables -D FORWARD -d $internalip -j RETURN`;
1953
            }
1954
            # Remove SNAT and MASQUERADE rules
1955
            # repeat for good measure
1956
            for (my $di=0; $di < 10; $di++) {
1957
                $duprules = 0;
1958
            #    eval {$duprules++ if (`/sbin/iptables -D POSTROUTING -t nat --out-interface br$id -s $externalip -j MASQUERADE`); 1;}
1959
            #        or do {$e=3; $postreply .= "Status=ERROR Problem setting up routing $@\n";};
1960
                eval {$duprules++ if (`/sbin/iptables -D POSTROUTING -t nat --out-interface br$id ! -d 10.$idleft.$idright.0/24 -j MASQUERADE`); 1;}
1961
                    or do {$e=3; $postreply .= "Status=ERROR Problem setting up routing $@\n";};
1962

    
1963
                eval {$duprules++ if (`/sbin/iptables -D POSTROUTING -t nat -s $internalip ! -d 10.$idleft.$idright.0/24 -j SNAT --to-source $externalip`); 1; }
1964
                    or do {$e=1; $postreply .= "Status=ERROR Problem setting up routing $@\n";};
1965
            #    eval {$duprules++ if (`/sbin/iptables -D POSTROUTING -t nat -s $internalip -j SNAT --to-source $externalip`); 1; }
1966
            #        or do {$e=1; $postreply .= "Status=ERROR Problem setting up routing $@\n";};
1967
                eval {$duprules++ if (`/sbin/iptables -D INPUT -t nat -s $internalip ! -d 10.$idleft.$idright.0/24 -j SNAT --to-source $externalip`); 1; }
1968
                    or do {$e=1; $postreply .= "Status=ERROR Problem setting up routing $@\n";};
1969
            #    eval {$duprules++ if (`/sbin/iptables -D INPUT -t nat -s $internalip -j SNAT --to-source $externalip`); 1; }
1970
            #        or do {$e=1; $postreply .= "Status=ERROR Problem setting up routing $@\n";};
1971
            #    eval {$duprules++ if (`/sbin/iptables -D INPUT -t nat -s $internalip ! -d 10.$idleft.$idright.0/24 -j SNAT --to-source $externalip`); 1; }
1972
            #        or do {$e=1; $postreply .= "Status=ERROR Problem setting up routing $@\n";};
1973
            #    eval {$duprules++ if (`/sbin/iptables -D INPUT -t nat -s $internalip -j SNAT --to-source $externalip`); 1; }
1974
            #        or do {$e=1; $postreply .= "Status=ERROR Problem setting up routing $@\n";};
1975
                last if ($duprules >1);
1976
            }
1977
            # `/sbin/iptables -D POSTROUTING -t nat -s $internalip -j LOG --log-prefix "SNAT-POST"`;
1978
            # `/sbin/iptables -D INPUT -t nat -s $internalip -j LOG --log-prefix "SNAT-INPUT"`;
1979
            # `/sbin/iptables -D OUTPUT -t nat -s $internalip -j LOG --log-prefix "SNAT-OUTPUT"`;
1980
            # `/sbin/iptables -D PREROUTING -t nat -s $internalip -j LOG --log-prefix "SNAT-PRE"`;
1981
        }
1982
    } elsif ($type eq "remoteip") {
1983
        `pkill -f 'R $externalip'`;
1984
    } elsif ($type eq "externalip") {
1985
        if ($externalip && $externalip ne "--") {
1986
            # We are dealing with multiple upstream routes - configure local routing
1987
            if ($proxynic && $proxynic ne $extnic) {
1988
                my $proxyroute = `/sbin/ip route show table proxyarp`;
1989
                `/sbin/ip route del $externalip/32 dev br$id:proxy src $proxyip table proxyarp` if ($proxyroute =~ /$externalip/);
1990
            }
1991

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

    
1995
            if ($ports && $ports ne "--") {
1996
                my @portslist = split(/, ?| /, $ports);
1997
                foreach my $port (@portslist) {
1998
                    my $ipfilter;
1999
                    if ($port =~ /(\d+)\.(\d+)\.(\d+)\.(\d+)(\/\d+)?:(\d+)/) {
2000
                        my $portip = "$1.$2.$3.$4$5";
2001
                        $port = $6;
2002
                        $ipfilter = "-s $portip";
2003
                    } else {
2004
                        $port = 0 unless ($port =~ /\d+/);
2005
                    }
2006
                    if ($port<1 || $port>65535) {
2007
                        $postreply .= "Status=ERROR Invalid port mapping for $name\n";
2008
                        $ports = "--";
2009
                        last;
2010
                    }
2011

    
2012
                    if ($port>1 || $port<65535) {
2013
                        # repeat for good measure
2014
                        for (my $di=0; $di < 10; $di++) {
2015
                            $duprules = 0;
2016
                            eval {$duprules++ if (`/sbin/iptables -D FORWARD -p tcp -i $proxynic $ipfilter -d $externalip --dport $port -j RETURN`); 1;}
2017
                                or do {$e=1; $postreply .= "Status=ERROR Problem setting up routing $@\n";};
2018
                            eval {$duprules++ if (`/sbin/iptables -D FORWARD -p udp -i $proxynic $ipfilter -d $externalip --dport $port -j RETURN`); 1;}
2019
                                or do {$e=1; $postreply .= "Status=ERROR Problem setting up routing $@\n";};
2020
                            last if ($duprules > 1);
2021
                        }
2022
                    }
2023
                }
2024
            }
2025
            # Remove rule to allow forwarding from $externalip
2026
	        `/sbin/iptables --delete FORWARD --in-interface br$id -s $externalip -j RETURN`;
2027
            # Remove rule to disallow setting up a dhcp server
2028
            eval {`/sbin/iptables -D FORWARD -p udp -i $proxynic -d $externalip --dport 67 -j REJECT`; 1;}
2029
                or do {$e=1; $postreply .= "Status=ERROR Problem setting up routing $@\n";};
2030
            # Leave outgoing connectivity - not
2031
            eval {`/sbin/iptables -D FORWARD -i $proxynic -d $externalip -m state --state ESTABLISHED,RELATED -j RETURN`; 1;}
2032
                or do {$e=1; $postreply .= "Status=ERROR Problem setting up routing $@\n";};
2033
            eval {`/sbin/iptables -D FORWARD -i $proxynic -d $externalip -j RETURN`; 1;}
2034
                or do {$e=1; $postreply .= "Status=ERROR Problem setting up routing $@\n";};
2035
            # No need to reject - we reject all per default to the subnet
2036
            eval {`/sbin/iptables -D FORWARD -i $proxynic -d $externalip -j REJECT`; 1;}
2037
                or do {$e=1; $postreply .= "Status=ERROR Problem setting up routing $@\n";};
2038
        }
2039
    }
2040
    # Deconfigure internal dhcp server
2041
    if ($type eq "internalip" || $type eq "ipmapping" || $type eq "remoteip") {
2042
        my $result =  removeDHCPAddress($id, $domains, $internalip);
2043
        if ($result ne "OK") {
2044
            $e=1;
2045
            $postreply .= "$result\n";
2046
        }
2047
    } elsif ($type eq "externalip" && $domains) {
2048
        my $result =  removeDHCPAddress($id, $domains, $externalip);
2049
        if ($result ne "OK") {
2050
            $e=1;
2051
            $postreply .= "$result\n";
2052
        }
2053
    }
2054
    $uistatus = ($e)?"":validateStatus($register{$uuid});
2055
    if ($uistatus) {
2056
        $uiuuid = $uuid;
2057
        $postreply .= "Status=$uistatus OK $action $type $name: $uistatus\n";
2058
    } else {
2059
        $postreply .= "Status=ERROR Cannot $action $type $name: $uistatus\n";
2060
    }
2061
    $main::syslogit->($user, 'info', "$action network $uuid ($name, $id) -> $uistatus");
2062
    updateBilling("$uistatus $uuid ($id)");
2063
    # $main::updateUI->({tab=>"networks", user=>$user, uuid=>$uiuuid, status=>$uistatus}) if ($uistatus);
2064
    return $postreply;
2065
}
2066

    
2067
sub Stop {
2068
    my ($id, $action) = @_;
2069
    # Check if we were passed a uuid
2070
    if ($id =~ /\-/ && $register{$id} && ($register{$id}->{'user'} eq $user || $isadmin)) {
2071
        $id = $register{$id}->{'id'}
2072
    }
2073
    if ($help) {
2074
        return <<END
2075
GET:uuid:
2076
Stops a network by removing gateway. Network must be in status up or nat.
2077
END
2078
    }
2079

    
2080
    my $idleft = ($id>99)?(substr $id,0,-2)+0 : 0;
2081
    my $idright = (substr $id,-2) + 0;
2082
    my $e = 0;
2083
    # First deactivate all user's networks with same id
2084
    my @regkeys = (tied %register)->select_where("user = '$user'");
2085
    foreach my $key (@regkeys) {
2086
        my $valref = $register{$key};
2087
        my $cuuid = $valref->{'uuid'};
2088
        my $ctype = $valref->{'type'};
2089
        my $cdbuser = $valref->{'user'};
2090
        my $cid = $valref->{'id'};
2091
    # Only list networks belonging to current user
2092
        if ($user eq $cdbuser && $id eq $cid && $ctype ne "gateway") {
2093
            if ($ctype eq "internalip" || $ctype eq "ipmapping" || $ctype eq "externalip") {
2094
                my $result = Deactivate($cuuid, 'deactivate');
2095
                if ($result =~ /\w+=ERROR (.+)/i) {
2096
                    $e = $1;
2097
                }
2098
            }
2099
        }
2100
     }
2101
    my $interfaces = `/sbin/ifconfig br$id`;
2102
     # Only take down interface and vlan if gateway IP is active on interface
2103
    if ($e) {
2104
        $postreply .= "Status=Error Not taking down gateway, got an error: $e\n"
2105
#    } elsif ($interfaces =~ /^$datanic\.$id.+\n.+inet .+10\.$idleft\.$idright\.1/
2106
    } elsif ($interfaces =~ /10\.$idleft\.$idright\.1/
2107
            && !$e) {
2108
        eval {`/sbin/brctl delif br$id $datanic.$id`; 1;} or do {$e=1;};
2109
        eval {`/sbin/ifconfig br$id down`; 1;} or do {$e=1;};
2110
        eval {`/sbin/ifconfig $datanic.$id down`; 1;} or do {$e=1;};
2111
        eval {`/sbin/vconfig rem $datanic.$id`; 1;} or do {$e=1;};
2112
    } else {
2113
        $postreply .= "Status=Error Not taking down interface, gateway 10.$idleft.$idright.1 is not active on interface br$id - $interfaces.\n"
2114
    }
2115
    # Remove rule to only forward packets coming from subnet assigned to vlan
2116
#    `/sbin/iptables --delete FORWARD --in-interface $datanic.$id ! -s 10.$idleft.$idright.0/24 -j DROP`;
2117

    
2118
    $uistatus = ($e)?$uistatus:"down";
2119
    if ($uistatus eq 'down') {
2120
        $uiuuid = $uuid;
2121
        $postreply .= "Status=$uistatus OK $action gateway: $uistatus\n";
2122
    } else {
2123
        $postreply .= "Status=Error Cannot $action $type $name: $uistatus\n";
2124
    }
2125
    return $postreply;
2126
}
2127

    
2128
sub getDomains {
2129
    my $uuid = shift;
2130
    my $domains;
2131
    my $domainnames;
2132
    my @domregvalues = values %domreg;
2133
    foreach my $domval (@domregvalues) {
2134
        if (($domval->{'networkuuid1'} eq $uuid || $domval->{'networkuuid2'} eq $uuid || $domval->{'networkuuid3'} eq $uuid)
2135
                && $domval->{'user'} eq $user) {
2136
            $domains .= $domval->{'uuid'} . ", ";
2137
            $domainnames .= $domval->{'name'} . ", ";
2138
        }
2139
    }
2140
    $domains = substr $domains, 0, -2;
2141
    $domainnames = substr $domainnames, 0, -2;
2142
    return ($domains, $domainnames); 
2143
}
2144

    
2145
sub getSystems {
2146
    my $uuid = shift;
2147
    my $systems;
2148
    my $systemnames;
2149
    unless (tie(%sysreg,'Tie::DBI', Hash::Merge::merge({table=>'systems'}, $Stabile::dbopts)) ) {$res .= qq|{"status": "Error": "message": "Unable to access systems register"}|; return $res;};
2150
    my @sysregvalues = values %sysreg;
2151
    foreach my $sysval (@sysregvalues) {
2152
        my $networkuuids = $sysval->{'networkuuids'};
2153
        if ($networkuuids =~ /$uuid/ && $sysval->{'user'} eq $user) {
2154
            $systems = $sysval->{'uuid'};
2155
            $systemnames = $sysval->{'name'};
2156
            last;
2157
        }
2158
    }
2159
    unless ($systems) {
2160
        my @sysregvalues = values %domreg;
2161
        foreach my $sysval (@sysregvalues) {
2162
            my $networkuuids = $sysval->{'networkuuids'};
2163
            if ($networkuuids =~ /$uuid/ && $sysval->{'user'} eq $user) {
2164
                $systems = $sysval->{'uuid'};
2165
                $systemnames = $sysval->{'name'};
2166
                last;
2167
            }
2168
        }
2169
    }
2170
    return ($systems, $systemnames);
2171
}
2172

    
2173
sub getNextId {
2174
	# Find the next available vlan id
2175
	my $reqid = shift;
2176
	my $username = shift;
2177
	$username = $user unless ($username);
2178
    my $nextid = 1;
2179
	my $vlanstart = $Stabile::config->get('VLAN_RANGE_START');
2180
	my $vlanend = $Stabile::config->get('VLAN_RANGE_END');
2181

    
2182
    if ($reqid eq 0 || $reqid == 1) {
2183
        return $requid;
2184
    } elsif ($reqid && ($reqid > $vlanend || $reqid < $vlanstart)) {
2185
        return -1 unless ($isadmin);
2186
    }
2187

    
2188
	$reqid = $reqid + 0;
2189

    
2190
    my %ids;
2191
    # First check if the user has an existing vlan, if so use the first we find as default value
2192
    my @regvalues = values %register;
2193
    @regvalues = (sort {$a->{id} <=> $b->{id}} @regvalues);
2194
    foreach my $val (@regvalues) { # Traverse all id's in use
2195
        my $id = 0 + $val->{'id'};
2196
        my $dbuser = $val->{'user'};
2197
        if ($id > 1) {
2198
            if ($username eq $dbuser) { # If a specific id was requested map all id's
2199
                if (!$reqid) {# If no specific id was asked for, stop now, and use the user's first one
2200
                    $nextid = $id;
2201
                    last;
2202
                }
2203
            } else {
2204
                $ids{$id} = 1; # Mark this id as used (by another user)
2205
            }
2206
        }
2207
    }
2208
    if ($nextid>1) {
2209
        return $nextid;
2210
    } elsif ($reqid) {
2211
        if (!$ids{$reqid} || $isadmin) { # If an admin is requesting id used by another, assume he knows what he is doing
2212
            $nextid = $reqid; # Safe to use
2213
        } else {
2214
            $nextid = -1; # Id already in use by another
2215
        }
2216
    } elsif ($nextid == 1) { # This user is not currently using any vlan's, find the first free one
2217
        for ($n=$vlanstart; $n<$vlanend; $n++) {
2218
            if (!$ids{$n}) { # Don't return an id used (by another user)
2219
                $nextid = $n;
2220
                last;
2221
            }
2222
        }
2223
    }
2224
	return $nextid;
2225
}
2226

    
2227
sub getNextRemoteIP {
2228
    my $nextip = "193.200.44.48";
2229
    my $oc = overQuotas(1);
2230
    if ($oc) { # Enforce quotas
2231
        $postreply .= "Status=ERROR Over quota allocating external IP\n";
2232
    } else {
2233
        my $res = $main::postToOrigo->($engineid, 'provisionremoteip');
2234
        my $res_obj = from_json($res);
2235
        $nextip = $res_obj->{remoteip};
2236
    }
2237
    $postreply .= "Status=ERROR No more ($oc) remote IPs available\n" unless ($nextip);
2238
    return $nextip;
2239

    
2240
}
2241
sub getNextExternalIP {
2242
	# Find the next available IP
2243
	my $extip = shift;
2244
	my $extuuid = shift;
2245
	my $proxyarp = shift; # Are we trying to assign a proxy arp's external IP?
2246
	$extip = "" if ($extip eq "--");
2247

    
2248
	my $extipstart;
2249
	my $extipend;
2250

    
2251
    if ($proxyarp) {
2252
        $extipstart = $Stabile::config->get('PROXY_IP_RANGE_START');
2253
        $extipend = $Stabile::config->get('PROXY_IP_RANGE_END');
2254
    } else {
2255
        $extipstart = $Stabile::config->get('EXTERNAL_IP_RANGE_START');
2256
        $extipend = $Stabile::config->get('EXTERNAL_IP_RANGE_END');
2257
    }
2258

    
2259
	return "" unless ($extipstart && $extipend);
2260

    
2261
	my $interfaces = `/sbin/ifconfig`;
2262
#	$interfaces =~ m/eth0 .+\n.+inet addr:(\d+\.\d+\.\d+)\.(\d+)/;
2263
	$extipstart =~  m/(\d+\.\d+\.\d+)\.(\d+)/;
2264
	my $bnet1 = $1;
2265
	my $bhost1 = $2+0;
2266
	$extipend =~  m/(\d+\.\d+\.\d+)\.(\d+)/;
2267
	my $bnet2 = $1;
2268
	my $bhost2 = $2+0;
2269
	my $nextip = "";
2270
	if ($bnet1 ne $bnet2) {
2271
		print "Status=ERROR Only 1 class C subnet is supported for $name\n";
2272
		return "";
2273
	}
2274
	my %ids;
2275
	# First create map of IP's reserved by other servers in DB
2276
	my @regvalues = values %register;
2277
	foreach my $val (@regvalues) {
2278
		my $ip = $val->{'externalip'};
2279
		# $ip =~ m/(\d+\.\d+\.\d+)\.(\d+)/;
2280
		# my $id = $2;
2281
		$ids{$ip} = $val->{'uuid'} unless ($extuuid eq $val->{'uuid'});
2282
	}
2283
    my $oc = overQuotas(1);
2284
	if ($oc) { # Enforce quotas
2285
        $postreply .= "Status=ERROR Over quota allocating external IP\n";
2286
	} elsif ($extip && $extip =~  m/($bnet1)\.(\d+)/ && $2>=$bhost1 && $2<$bhost2) {
2287
	# An external ip was supplied - check if it's free and ok
2288
		if (!$ids{$extip} && !($interfaces =~ m/$extip.+\n.+inet addr:$extip/) && $extip=~/$bnet$\.(\d)/) {
2289
			$nextip = $extip;
2290
		}
2291
	} else {
2292
	# Find random IP not reserved, and check it is not in use (for other purposes)
2293
	    my @bhosts = ($bhost1..$bhost2);
2294
        my @rbhosts = shuffle @bhosts;
2295
		for ($n=0; $n<$bhost2-$bhost1; $n++) {
2296
		    my $nb = $rbhosts[$n];
2297
			if (!$ids{"$bnet1.$nb"}) {
2298
				if (!($interfaces =~ m/$extip.+\n.+inet addr:$bnet1\.$nb/)) {
2299
					$nextip = "$bnet1.$nb";
2300
					last;
2301
				}
2302
			}
2303
		}
2304
	}
2305
	$postreply .= "Status=ERROR No more ($oc) external IPs available\n" unless ($nextip);
2306
	return $nextip;
2307
}
2308

    
2309
sub ip2domain {
2310
    my $ip = shift;
2311
    my $ruuid;
2312
    if ($ip) {
2313
        my @regkeys = (tied %register)->select_where("internalip = '$ip' OR externalip = '$ip'");
2314
        foreach my $k (@regkeys) {
2315
            my $valref = $register{$k};
2316
            if ($valref->{'internalip'} eq $ip || $valref->{'externalip'} eq $ip) {
2317
                $ruuid = $valref->{'domains'};
2318
                last;
2319
            }
2320
        }
2321
    }
2322
    return $ruuid;
2323
}
2324

    
2325
sub getNextInternalIP {
2326
	my $intip = shift;
2327
	my $uuid = shift;
2328
	my $id = shift;
2329
	my $username = shift;
2330
	$username = $user unless ($username);
2331
	my $nextip = "";
2332
	my $intipnum;
2333
	my $subnet;
2334
	my %ids;
2335
    my $ping = Net::Ping->new();
2336

    
2337
    $id = getNextId() unless ($id);
2338
    my $idleft = ($id>99)?(substr $id,0,-2)+0 : 0;
2339
    my $idright = (substr $id,-2) + 0;
2340
    $intip = "10.$idleft.$idright.0" if (!$intip || $intip eq '--');
2341
    
2342
    return '' unless ($intip =~ m/(\d+\.\d+\.\d+)\.(\d+)/ );
2343
    $subnet = $1;
2344
    $intipnum = $2;
2345

    
2346
	# First create hash of IP's reserved by other servers in DB
2347
	my @regvalues = values %register;
2348
	foreach my $val (@regvalues) {
2349
    	if ($val->{'user'} eq $username) {
2350
            my $ip = $val->{'internalip'} ;
2351
            $ids{$ip} = $val->{'uuid'};
2352
		}
2353
	}
2354

    
2355
	if ($intipnum && $intipnum>1 && $intipnum<255) {
2356
	# An internal ip was supplied - check if it's free, if not keep the ip already registered in the db
2357
        if (!$ids{$intip}
2358
#            && !($ping->ping($intip, 0.1)) # 0.1 secs timeout, check if ip is in use, possibly on another engine
2359
            && !(`arping -C1 -c2 -D -I $datanic.$id $intip` =~ /reply from/)  # check if ip is created on another engine
2360
        ) {
2361
            $nextip = $intip;
2362
        } else {
2363
            $nextip = $register{$uuid}->{'internalip'}
2364
        }
2365
	} else {
2366
	# Find first IP not reserved
2367
		for ($n=2; $n<255; $n++) {
2368
			if (!$ids{"$subnet.$n"}
2369
# TODO: The arping check takes too long - two networks created by the same user can too easily be assigned the same IP's
2370
#                && !(`arping -f -c2 -D -I $datanic.$id $subnet.$n` =~ /reply from/)  # check if ip is created on another engine
2371
			) {
2372
                $nextip = "$subnet.$n";
2373
                last;
2374
			}
2375
		}
2376
	}
2377
	$postreply .= "Status=ERROR No more internal IPs available\n" if (!$nextip);
2378
	return $nextip;
2379
}
2380

    
2381
sub validateStatus {
2382
    my $valref = shift;
2383

    
2384
    # my $interfaces = `/sbin/ifconfig`;
2385
    my $uuid = $valref->{'uuid'};
2386
    my $type = $valref->{'type'};
2387
    my $id = $valref->{'id'};
2388
    my $idleft = ($id>99)?(substr $id,0,-2)+0 : 0;
2389
    my $idright = (substr $id,-2) + 0;
2390

    
2391
    ( $valref->{'domains'}, $valref->{'domainnames'} ) = getDomains($uuid);
2392
    my ( $systems, $systemnames ) = getSystems($uuid);
2393
    my $extip = $valref->{'externalip'};
2394
    my $intip = $valref->{'internalip'};
2395

    
2396
    if ($type eq "gateway") {
2397
        $valref->{'internalip'} = "10.$idleft.$idright.1" if ($id>1);
2398
    } else {
2399
        if ($intip && $intip ne "--" && $extip && $extip ne "--") {
2400
            $type = "ipmapping" unless ($type eq 'remoteip');
2401
        } elsif ($intip && $intip ne "--") {
2402
            $type = "internalip";
2403
        } elsif ($extip && $extip ne "--") {
2404
            $type = "externalip";
2405
        } else {
2406
            $type = "gateway";
2407
        }
2408
        $valref->{'type'} = $type;
2409
    }
2410

    
2411
    $valref->{'status'} = "down";
2412
    my $nat;
2413
    if ($id == 0 || $id == 1) {
2414
        $valref->{'status'} = "nat";
2415
    # Check if vlan $id is created (and doing nat)
2416
#    } elsif ($interfaces =~ m/$datanic\.$id.+\n.+10\.$idleft\.$idright\.1/) {
2417
    } elsif (-e "/proc/net/vlan/$datanic.$id") {
2418
        $nat = 1;
2419
    }
2420

    
2421
    if ($type eq "internalip" || $type eq "ipmapping" || $type eq "remoteip") {
2422
        $valref->{'status'} = "nat" if ($nat);
2423
        my $dhcprunning;
2424
        my $dhcpconfigured;
2425
        eval {
2426
            my $psid;
2427
            $psid = `/bin/cat /var/run/stabile-$id.pid` if (-e "/var/run/stabile-$id.pid");
2428
            chomp $psid;
2429
            $dhcprunning = -e "/proc/$psid" if ($psid);
2430
            my $dhcphosts;
2431
            $dhcphosts = lc `/bin/cat $etcpath/dhcp-hosts-$id` if (-e "$etcpath/dhcp-hosts-$id");
2432
            $dhcpconfigured = ($dhcphosts =~ /$intip/);
2433
            1;
2434
        } or do {;};
2435

    
2436
        if ($type eq "internalip" || $type eq "remoteip") {
2437
        # Check if external ip has been created and dhcp is ok
2438
            if ($nat && (($dhcprunning && $dhcpconfigured) || $systems)) {
2439
                $valref->{'status'} = "up";
2440
            }
2441
        } elsif ($type eq "ipmapping") {
2442
        # Check if external ip has been created, dhcp is ok and vlan interface is created
2443
        # An ipmapping linked to a system is considered up if external interface exists
2444
        # Update: It appears that ip addresses on virtual interfaces are periodically lost for some reason
2445
        # the interface however still responds to the ip address if iptables rules referencing this exists
2446
        # so we have relaxed the up requirement
2447
            if ($nat
2448
        #            && $interfaces =~ m/$extip/ # interfaces seem to drop out of sight after while even if still active
2449
                    && (($dhcprunning && $dhcpconfigured) || $systems)) {
2450
                $valref->{'status'} = "up";
2451
            }
2452
        }
2453

    
2454
    } elsif ($type eq "externalip") {
2455
        my $dhcprunning;
2456
        my $dhcpconfigured;
2457
        eval {
2458
            my $psid;
2459
            $psid = `/bin/cat /var/run/stabile-$id.pid` if (-e "/var/run/stabile-$id.pid");
2460
            chomp $psid;
2461
            $dhcprunning = -e "/proc/$psid" if ($psid);
2462
            my $dhcphosts;
2463
            $dhcphosts = `/bin/cat $etcpath/dhcp-hosts-$id` if (-e "$etcpath/dhcp-hosts-$id");
2464
            $dhcpconfigured = ($dhcphosts =~ /$extip/);
2465
            1;
2466
        } or do {;};
2467

    
2468
        my $vproxy = `/bin/cat /proc/sys/net/ipv4/conf/$datanic.$id/proxy_arp`; chomp $vproxy;
2469
        my $eproxy = `/bin/cat /proc/sys/net/ipv4/conf/$proxynic/proxy_arp`; chomp $eproxy;
2470
        my $proute = `/sbin/ip route | grep "$extip dev"`; chomp $proute;
2471
        if ($vproxy && $eproxy && $proute) {
2472
            if ((($dhcprunning && $dhcpconfigured) || $systems)) {
2473
                $valref->{'status'} = "up";
2474
            } elsif (!$valref->{'domains'}) {
2475
                $valref->{'status'} = "nat";
2476
            }
2477
        } else {
2478
            #print "$vproxy && $eproxy && $proute && $dhcprunning && $dhcpconfigured :: $extip\n";        
2479
        }
2480

    
2481
    } elsif ($type eq "gateway") {
2482
        if ($nat || $id == 0 || $id == 1) {$valref->{'status'} = "up";}
2483
    }
2484
    return $valref->{'status'};
2485
}
2486

    
2487
sub trim{
2488
   my $string = shift;
2489
   $string =~ s/^\s+|\s+$//g;
2490
   return $string;
2491
}
2492

    
2493
sub overQuotas {
2494
    my $reqips = shift; # number of new ip's we are asking for
2495
	my $usedexternalips = 0;
2496
	my $overquota = 0;
2497
    return $overquota if ($Stabile::userprivileges =~ /a/); # Don't enforce quotas for admins
2498

    
2499
	my $externalipquota = $Stabile::userexternalipquota;
2500
	if (!$externalipquota) {
2501
        $externalipquota = $Stabile::config->get('EXTERNAL_IP_QUOTA');
2502
    }
2503

    
2504
	my $rxquota = $Stabile::userrxquota;
2505
	if (!$rxquota) {
2506
        $rxquota = $Stabile::config->get('RX_QUOTA');
2507
    }
2508

    
2509
	my $txquota = $Stabile::usertxquota;
2510
	if (!$txquota) {
2511
        $txquota = $Stabile::config->get('TX_QUOTA');
2512
    }
2513

    
2514
    my @regkeys = (tied %register)->select_where("user = '$user'");
2515
	foreach my $k (@regkeys) {
2516
	    my $val = $register{$k};
2517
		if ($val->{'user'} eq $user && $val->{'externalip'} && $val->{'externalip'} ne "--" ) {
2518
		    $usedexternalips += 1;
2519
		}
2520
	}
2521
	if ((($usedexternalips + $reqips) > $externalipquota) && $externalipquota > 0) { # -1 means no quota
2522
	    $overquota = $usedexternalips;
2523
	} elsif ($rx > $rxquota*1024 && $rxquota > 0) {
2524
	    $overquota = -1;
2525
	} elsif ($tx > $txquota*1024 && $txquota > 0) {
2526
	    $overquota = -2;
2527
	}
2528
	return $overquota;
2529
}
2530

    
2531
sub updateBilling {
2532
    my $event = shift;
2533
    my %billing;
2534
    my @regkeys = (tied %register)->select_where("user = '$user' or user = 'common'") unless ($fulllist);
2535
    foreach my $k (@regkeys) {
2536
        my $valref = $register{$k};
2537
        my %val = %{$valref}; # Deference and assign to new array, effectively cloning object
2538
        if ($val{'user'} eq $user && ($val{'type'} eq 'ipmapping' || $val{'type'} eq 'externalip') && $val{'externalip'} ne '--') {
2539
            $billing{$val{'id'}}->{'externalip'} += 1;
2540
        }
2541
    }
2542

    
2543
    my %billingreg;
2544
    my $monthtimestamp = timelocal(0,0,0,1,$mon,$year); #$sec,$min,$hour,$mday,$mon,$year
2545

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

    
2548
    my $rx_bytes_total = 0;
2549
    my $tx_bytes_total = 0;
2550

    
2551
    my $prevmonth = $month-1;
2552
    my $prevyear = $year;
2553
    if ($prevmonth == 0) {$prevmonth=12; $prevyear--;};
2554
    $prevmonth = substr("0" . $prevmonth, -2);
2555
    my $prev_rx_bytes_total = 0;
2556
    my $prev_tx_bytes_total = 0;
2557

    
2558
    foreach my $id (keys %billing) {
2559
        my $b = $billing{$id};
2560
        my $externalip = $b->{'externalip'};
2561
        my $externalipavg = 0;
2562
        my $startexternalipavg = 0;
2563
        my $starttimestamp = $current_time;
2564
        my $rx_bytes = 0;
2565
        my $tx_bytes = 0;
2566
#        my $rx_stats = "/sys/class/net/$datanic.$id/statistics/rx_bytes";
2567
#        my $tx_stats = "/sys/class/net/$datanic.$id/statistics/tx_bytes";
2568
        my $rx_stats = "/sys/class/net/br$id/statistics/rx_bytes";
2569
        my $tx_stats = "/sys/class/net/br$id/statistics/tx_bytes";
2570
        $rx_bytes = `/bin/cat $rx_stats` if (-e $rx_stats);
2571
        chomp $rx_bytes;
2572
        $tx_bytes = `/bin/cat $tx_stats` if (-e $tx_stats);
2573
        chomp $tx_bytes;
2574

    
2575
        if ($current_time - $monthtimestamp < 4*3600) {
2576
            $starttimestamp = $monthtimestamp;
2577
            $externalipavg = $externalip;
2578
            $startexternalipavg = $externalip;
2579
        }
2580

    
2581
        my $bill = $billingreg{"$user-$id-$year-$month"};
2582
        my $regrx_bytes = $bill->{'rx'};
2583
        my $regtx_bytes = $bill->{'tx'};
2584
        $rx_bytes += $regrx_bytes if ($regrx_bytes > $rx_bytes); # Network interface was reloaded
2585
        $tx_bytes += $regtx_bytes if ($regtx_bytes > $tx_bytes); # Network interface was reloaded
2586

    
2587
        # Update timestamp and averages on existing row
2588
        if ($billingreg{"$user-$id-$year-$month"}) {
2589
            $startexternalipavg = $bill->{'startexternalipavg'};
2590
            $starttimestamp = $bill->{'starttimestamp'};
2591

    
2592
            $externalipavg = ($startexternalipavg*($starttimestamp - $monthtimestamp) + $externalip*($current_time - $starttimestamp)) /
2593
                            ($current_time - $monthtimestamp);
2594

    
2595
            $billingreg{"$user-$id-$year-$month"}->{'externalip'} = $externalip;
2596
            $billingreg{"$user-$id-$year-$month"}->{'externalipavg'} = $externalipavg;
2597
            $billingreg{"$user-$id-$year-$month"}->{'timestamp'} = $current_time;
2598
            $billingreg{"$user-$id-$year-$month"}->{'rx'} = $rx_bytes;
2599
            $billingreg{"$user-$id-$year-$month"}->{'tx'} = $tx_bytes;
2600
        }
2601

    
2602
        # No row found or something happened which justifies writing a new row
2603
        if (!$billingreg{"$user-$id-$year-$month"}
2604
        || ($b->{'externalip'} != $bill->{'externalip'})
2605
        ) {
2606

    
2607
            my $inc = 0;
2608
            if ($billingreg{"$user-$id-$year-$month"}) {
2609
                $startexternalipavg = $externalipavg;
2610
                $starttimestamp = $current_time;
2611
                $inc = $bill->{'inc'};
2612
            }
2613
            # Write a new row
2614
            $billingreg{"$user-$id-$year-$month"} = {
2615
                externalip=>$externalip+0,
2616
                externalipavg=>$externalipavg,
2617
                startexternalipavg=>$startexternalipavg,
2618
                timestamp=>$current_time,
2619
                starttimestamp=>$starttimestamp,
2620
                event=>$event,
2621
                inc=>$inc+1,
2622
                rx=>$rx_bytes,
2623
                tx=>$tx_bytes
2624
            };
2625
        }
2626

    
2627
        $rx_bytes_total += $rx_bytes;
2628
        $tx_bytes_total += $tx_bytes;
2629
        my $prevbill = $billingreg{"$user-$id-$prevyear-$prevmonth"};
2630
        $prev_rx_bytes_total += $prevbill->{'rx'};
2631
        $prev_tx_bytes_total += $prevbill->{'tx'};
2632
    }
2633
    untie %billingreg;
2634
    $rx = ($rx_bytes_total>$prev_rx_bytes_total)?$rx_bytes_total - $prev_rx_bytes_total:$rx_bytes_total;
2635
    $tx = ($tx_bytes_total>$prev_tx_bytes_total)?$tx_bytes_total - $prev_tx_bytes_total:$tx_bytes_total;
2636
    my $oq = overQuotas();
2637
    if ($oq && $oq<0) {
2638
        foreach my $id (keys %billing) {
2639
            $main::syslogit->($user, 'info', "$user over rx/tx quota ($oq) stopping network $id");
2640
            Stop($id, 'stop');
2641
        }
2642
    }
2643
}
2644

    
2645
sub Bit2netmask {
2646
	my $netbit = shift;
2647
	my $_bit         = ( 2 ** (32 - $netbit) ) - 1;
2648
	my ($full_mask)  = unpack( "N", pack( "C4", split(/./, '255.255.255.255') ) );
2649
	my $netmask      = join( '.', unpack( "C4", pack( "N", ( $full_mask ^ $_bit ) ) ) );
2650
	return $netmask;
2651
}
(3-3/9)