Project

General

Profile

Download (107 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 File::Basename;
18
use List::Util qw(shuffle);
19
use lib dirname (__FILE__);
20
use Stabile;
21

    
22
($datanic, $extnic) = $main::getNics->();
23
$extsubnet = $Stabile::config->get('EXTERNAL_SUBNET_SIZE');
24
$proxynic = $Stabile::config->get('PROXY_NIC') || $extnic;
25
$proxyip = $Stabile::config->get('PROXY_IP');
26
$proxygw = $Stabile::config->get('PROXY_GW') || $proxyip;
27
$proxysubnet = $Stabile::config->get('PROXY_SUBNET_SIZE');
28
my $engineid = $Stabile::config->get('ENGINEID') || "";
29
$dodns = $Stabile::config->get('DO_DNS') || "";
30
$enginelinked = $Stabile::config->get('ENGINE_LINKED') || "";
31

    
32
my $tenders = $Stabile::config->get('STORAGE_POOLS_ADDRESS_PATHS');
33
@tenderlist = split(/,\s*/, $tenders);
34
my $tenderpaths = $Stabile::config->get('STORAGE_POOLS_LOCAL_PATHS') || "/mnt/stabile/images";
35
@tenderpathslist = split(/,\s*/, $tenderpaths);
36
my $tendernames = $Stabile::config->get('STORAGE_POOLS_NAMES') || "Standard storage";
37
@tendernameslist = split(/,\s*/, $tendernames);
38
$storagepools = $Stabile::config->get('STORAGE_POOLS_DEFAULTS') || "0";
39

    
40
$uiuuid;
41
$uistatus;
42
$help = 0; # If this is set, functions output help
43

    
44
#our %options=();
45
# -a action -h help -u uuid -m match pattern -f full list, i.e. all users
46
# -v verbose, include HTTP headers -s impersonate subaccount -t target [uuid or image]
47
# -g args to gearman task
48
#Getopt::Std::getopts("a:hfu:g:m:vs:t:", \%options);
49

    
50
try {
51
    Init(); # Perform various initalization tasks
52
    process() if ($package);
53

    
54
} catch Error with {
55
	my $ex = shift;
56
    print header('text/html', '500 Internal Server Error') unless ($console);
57
	if ($ex->{-text}) {
58
        print "Got error: ", $ex->{-text}, " on line ", $ex->{-line}, "\n";
59
	} else {
60
	    print "Status=ERROR\n";	    
61
	}
62
} finally {
63
};
64

    
65
1;
66

    
67
sub getObj {
68
    my %h = %{@_[0]};
69
    $console = 1 if $h{"console"};
70
    $api = 1 if $h{"api"};
71
    my $uuid = $h{"uuid"};
72
    my $obj;
73
    $action = $action || $h{'action'};
74
    if (
75
        $action =~ /^dns/
76
    ) {
77
        $obj = \%h;
78
        return $obj;
79
    }
80
    $uuid = $curuuid if ($uuid eq 'this');
81
    if ($uuid =~ /(\d+\.\d+\.\d+\.\d+)/) { # ip addresses are unique across networks so we allow this
82
        foreach my $val (values %register) {
83
            if ($val->{'internalip'} eq $uuid || $val->{'externalip'} eq $uuid) {
84
                $uuid = $val->{'uuid'};
85
                last;
86
            }
87
        }
88
    }
89
    my $dbobj = $register{$uuid} || {};
90
    my $status = $dbobj->{'status'} || $h{"status"}; # Trust db status if it exists
91
    if ((!$uuid && $uuid ne '0') && (!$status || $status eq 'new') && ($action eq 'save')) {
92
        my $ug = new Data::UUID;
93
        $uuid = $ug->create_str();
94
        $status = 'new';
95
    };
96
    return 0 unless ($uuid && length $uuid == 36);
97

    
98
    $uiuuid = $uuid;
99
    $uistatus = $dbobj->{'status'};
100

    
101
    my $id = $h{"id"};
102
    my $dbid = 0+$dbobj->{'id'};
103
    if ($status eq 'new' || !$dbid) {
104
        $id = getNextId($id) ;
105
    } else {
106
        $id = $dbid;
107
    }
108

    
109
    if ($id > 4095 || $id < 0 || ($id==0 && $uuid!=0) || ($id==1 && $uuid!=1)) {
110
        $postreply .= "Status=ERROR Invalid new network id $id\n";
111
        return;
112
    }
113
    my $name = $h{"name"} || $dbobj->{'name'};
114
    my $internalip = $h{"internalip"} || $dbobj->{'internalip'};
115
    if (!($internalip =~ /\d+\.\d+\.\d+\.\d+/)) {$internalip = ""};
116
    my $externalip = $h{"externalip"} || $dbobj->{'externalip'};
117
    my $ports = $h{"ports"} || $dbobj->{'ports'};
118
    my $type = $h{"type"} || $dbobj->{'type'};
119
    my $systems = $h{"systems"} || $dbobj->{'systems'};
120
    my $force = $h{"force"};
121
    my $reguser = $dbobj->{'user'};
122
    # Sanity checks
123
    if (
124
        ($name && length $name > 255)
125
        || ($ports && length $ports > 255)
126
        || ($type && !($type =~ /gateway|ipmapping|internalip|externalip/))
127
    ) {
128
         $postreply .= "Stroke=ERROR Bad network data: $name\n";
129
         return;
130
     }
131
     # Security check
132
     if (($user ne $reguser && index($privileges,"a")==-1 && $action ne 'save' ) ||
133
         ($reguser && $status eq "new"))
134
     {
135
         $postreply .= "Stroke=ERROR Bad user: $user, $action\n";
136
         return;
137
     }
138

    
139
    if (!$type ||($type ne 'gateway' && $type ne 'internalip' && $type ne 'ipmapping' && $type ne 'externalip')) {
140
         $type = "gateway";
141
         if ($internalip && $internalip ne "--" && $externalip && $externalip ne "--") {$type = "ipmapping";}
142
         elsif (($internalip && $internalip ne "--") || $status eq 'new') {$type = "internalip";}
143
         elsif (($externalip && $externalip ne "--") || $status eq 'new') {$type = "externalip";}
144
    }
145

    
146
    my $obj = {
147
        uuid => $uuid,
148
        id => $id,
149
        name => $name,
150
        status => $status,
151
        type => $type,
152
        internalip => $internalip,
153
        externalip => $externalip,
154
        ports => $ports,
155
        systems => $systems,
156
        force => $force,
157
        action => $h{"action"}
158
    };
159
    return $obj;
160
}
161

    
162
sub Init {
163

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

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

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

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

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

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

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

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

    
209
}
210

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

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

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

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

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

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

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

    
262
    foreach my $k (@regkeys) {
263
        my $valref = $register{$k};
264
        my $uuid = $valref->{'uuid'};
265
        my $dbuser = $valref->{'user'};
266
        my $type = $valref->{'type'};
267
        my $id = $valref->{'id'};
268
    # Only list networks belonging to current user
269
        if ($dbuser eq "common" || $user eq $dbuser || $fulllist || ($uuidfilter && $isadmin)) {
270
            my $dom = $domreg{$valref->{'domains'}};
271
            next unless (!$sysuuid || $dom->{'system'} eq $sysuuid || $valref->{'domains'} eq $sysuuid);
272
            validateStatus($valref);
273

    
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
    }
666
    $json_text = substr($json_text,0,-2);
667
    $res .= '{"identifier": "type", "label": "name", "items": [' . $json_text  . ']}';
668
    return $res;
669
}
670

    
671
# Simple action for removing all networks belonging to a user
672
sub do_removeusernetworks {
673
    my ($uuid, $action) = @_;
674

    
675
    if ($help) {
676
        return <<END
677
GET::
678
Remove all networks belonging to a user.
679
END
680
    }
681

    
682
    my $res;
683
    $res .= header('text/plain') unless $console;
684
    if ($readonly) {
685
        $postreply .= "Status=ERROR Not allowed\n";
686
    } else {
687
        Removeusernetworks($user);
688
    }
689
    $res .= $postreply || "Status=OK Nothing to remove\n";
690
    return $res;
691
}
692

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

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

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

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

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

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

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

    
856
END
857
;
858
}
859

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

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

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

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

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

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

    
922
        close (TEMP1);
923
#    }
924

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

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

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

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

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

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

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

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

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

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

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

    
1067
    my $dbid = 0+$regnet->{'id'};
1068
    if ($status eq 'new' || !$dbid) {
1069
        $id = getNextId($id) ;
1070
    } else {
1071
        $id = $dbid;
1072
    }
1073
    if ($id > 4095 || $id < 0 || ($id==0 && $uuid!=0 && $isadmin) || ($id==1 && $uuid!=1 && $isadmin)) {
1074
        $postreply .= "Status=ERROR Invalid network id $id\n";
1075
        return $postreply;
1076
    }
1077
    $name = $name || $regnet->{'name'};
1078
    $internalip = $internalip || $regnet->{'internalip'};
1079
    if (!($internalip =~ /\d+\.\d+\.\d+\.\d+/)) {$internalip = ''};
1080
    $externalip = $externalip || $regnet->{'externalip'};
1081
    $ports = $ports || $regnet->{'ports'};
1082
    my $reguser = $regnet->{'user'};
1083
    # Sanity checks
1084
    if (
1085
        ($name && length $name > 255)
1086
        || ($ports && length $ports > 255)
1087
        || ($type && !($type =~ /gateway|ipmapping|internalip|externalip/))
1088
    ) {
1089
        $postreply .= "Stroke=ERROR Bad data: $name, $ports, $type\n";
1090
        return $postreply;
1091
    }
1092
    # Security check
1093
    if (($reguser && $username ne $reguser && !$isadmin ) ||
1094
        ($reguser && $status eq "new"))
1095
    {
1096
        $postreply .= "Status=Error Bad user: $username ($status)\n";
1097
        return $postreply;
1098
    }
1099

    
1100
    my $hit = 0;
1101
# Check if user is allowed to use network
1102
    my @regvalues = values %register;
1103
    foreach my $val (@regvalues) {
1104
        $dbid = $val->{"id"};
1105
        $dbuser = $val->{"user"};
1106
        if ($dbid == $id && $username ne $dbuser && $dbuser ne "common") {
1107
            $hit = 1;
1108
            last;
1109
        }
1110
    }
1111
    if ($hit && !$isadmin) { # Network is nogo (unless you are an admin)
1112
        $postreply .= "Status=ERROR Network id $id not available\n";
1113
        return $postreply;
1114
    } elsif (!$type) {
1115
        $postreply .= "Status=ERROR Network must have a type\n";
1116
        return $postreply;
1117
    } elsif ($status eq 'down' || $status eq 'new' || $status eq 'nat') {
1118
        # Check if network has been modified or is new
1119
        if ($regnet->{'id'} ne $id ||
1120
            $regnet->{'name'} ne $name ||
1121
            $regnet->{'type'} ne $type ||
1122
            $regnet->{'internalip'} ne $internalip ||
1123
            $regnet->{'externalip'} ne $externalip ||
1124
            $regnet->{'systems'} ne $systems ||
1125
            $regnet->{'ports'} ne $ports)
1126
        {
1127
            if ($type eq "externalip") {
1128
                $internalip = "--";
1129
                $externalip = getNextExternalIP($externalip, $uuid, 1);
1130
                if (!$externalip) {
1131
                    $postreply .= "Status=ERROR Unable to allocate external proxy IP for $name\n";
1132
                    $externalip = "--";
1133
                    $internalip = getNextInternalIP($internalip, $uuid, $id);
1134
                    $type = "internalip";
1135
                } else {
1136
                    $postreply .= "Status=OK Allocated external IP: $externalip\n" unless ($regnet->{'externalip'} eq $externalip);
1137
                    if ($dodns) {
1138
                        $main::dnsCreate->($engineid, $externalip, $externalip, 'A', $user);
1139
                    }
1140
                }
1141

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

    
1164
            } elsif ($type eq "internalip") {
1165
                $externalip = "--";
1166
                $ports = "--";
1167
                my $ointip = $internalip;
1168
                $internalip = getNextInternalIP($internalip, $uuid, $id);
1169
                if (!$internalip) {
1170
                    $postreply .= "Status=ERROR Unable to allocate internal IP $internalip ($id, $uuid, $ointip) for $name\n";
1171
                    $internalip = "--";
1172
                    $type = "gateway";
1173
                } else {
1174
                    $postreply .= "Status=OK Allocated internal IP: $internalip for $name\n" unless ($regnet->{'internalip'} eq $internalip);
1175
                }
1176

    
1177
            } elsif ($type eq "gateway") {
1178
            #    $internalip = "--";
1179
            #    $externalip = "--";
1180
            #    $ports = "--";
1181
            } else {
1182
                $postreply .= "Status=ERROR Network must have a valid type\n";
1183
                return $postreply;
1184
            }
1185
            # Validate ports
1186
            my @portslist = split(/, ?| /, $ports);
1187
            if ($ports ne "--") {
1188
                foreach my $port (@portslist) {
1189
                    my $p = $port; # Make a copy of var
1190
                    if ($p =~ /(\d+\.\d+\.\d+\.\d+):(\d+)/) {
1191
                        $p = $2;
1192
                    };
1193
                    $p = 0 unless ($p =~ /\d+/);
1194
                    if ($p<1 || $p>65535) {
1195
                        $postreply .= "Status=ERROR Invalid port mapping for $name\n";
1196
                        $ports = "--";
1197
                        last;
1198
                    }
1199
                }
1200
            }
1201
            if ($ports ne "--") {
1202
                $ports = join(',', @portslist);
1203
            }
1204
            if ($systems ne $regnet->{'systems'}) {
1205
                my $regsystems = $regnet->{'systems'};
1206
                unless (tie(%sysreg,'Tie::DBI', Hash::Merge::merge({table=>'systems'}, $Stabile::dbopts)) ) {$res .= qq|{"status": "Error": "message": "Unable to access systems register"}|; return $res;};
1207

    
1208
                # Remove existing link to system
1209
                if ($sysreg{$regsystems}) {
1210
                    $sysreg{$regsystems}->{'networkuuids'} =~ s/$uuid,? ?//;
1211
                    $sysreg{$regsystems}->{'networknames'} = s/$regnet->{'name'},? ?//;
1212
                } elsif ($domreg{$regsystems}) {
1213
                    $domreg{$regsystems}->{'networkuuids'} =~ s/$uuid,? ?//;
1214
                    $domreg{$regsystems}->{'networknames'} = s/$regnet->{'name'},? ?//;
1215
                }
1216
                if ($systems) {
1217
                    if ($sysreg{$systems}) { # Add new link to system
1218
                        $sysreg{$systems}->{'networkuuids'} .= (($sysreg{$systems}->{'networkuuids'}) ? ',' : '') . $uuid;
1219
                        $sysreg{$systems}->{'networknames'} .= (($sysreg{$systems}->{'networknames'}) ? ',' : '') . $name;
1220
                        $systemnames = $sysreg{$systems}->{'name'};
1221
                    } elsif ($domreg{$systems}) {
1222
                        $domreg{$systems}->{'networkuuids'} .= (($domreg{$systems}->{'networkuuids'}) ? ',' : '') . $uuid;
1223
                        $domreg{$systems}->{'networknames'} .= (($domreg{$systems}->{'networknames'}) ? ',' : '') . $name;
1224
                        $systemnames = $domreg{$systems}->{'name'};
1225
                    } else {
1226
                        $systems = '';
1227
                    }
1228
                }
1229
                tied(%sysreg)->commit;
1230
                untie(%sysreg);
1231
            }
1232
            $register{$uuid} = {
1233
                uuid=>$uuid,
1234
                user=>$username,
1235
                id=>$id,
1236
                name=>$name,
1237
                internalip=>$internalip,
1238
                externalip=>$externalip,
1239
                ports=>$ports,
1240
                type=>$type,
1241
                systems=>$systems,
1242
                systemnames=>$systemnames,
1243
                action=>""
1244
            };
1245
            my $res = tied(%register)->commit;
1246
            my $obj = $register{$uuid};
1247
            $postreply .= "Status=OK Network $register{$uuid}->{'name'} saved: $uuid\n";
1248
            $postreply .= "Status=OK uuid: $uuid\n" if ($console && $status eq 'new');
1249
            if ($status eq 'new') {
1250
                validateStatus($register{$uuid});
1251
                $postmsg = "Created connection $name";
1252
                $uiupdatetype = "update";
1253
            }
1254
            updateBilling("allocate $externalip") if (($type eq "ipmapping" || $type eq "externalip") && $externalip && $externalip ne "--");
1255

    
1256
        } else {
1257
        	$postreply = "Status=OK Network $uuid ($id) unchanged\n";
1258
        }
1259

    
1260
        if ($params{'PUTDATA'}) {
1261
            my %jitem = %{$register{$uuid}};
1262
            my $json_text = to_json(\%jitem);
1263
            $json_text =~ s/null/"--"/g;
1264
            $json_text =~ s/""/"--"/g;
1265
            $postreply = $json_text;
1266
            $postmsg = $postmsg || "OK, updated network $name";
1267
        }
1268

    
1269
        return $postreply;
1270

    
1271
    } else {
1272
        if ($id ne $regnet->{'id'} ||
1273
        $internalip ne $regnet->{'internalip'} || $externalip ne $regnet->{'externalip'}) {
1274
            return "Status=ERROR Cannot modify active network: $uuid\n";
1275
        } elsif ($name ne $regnet->{'name'}) {
1276
            $register{$uuid}->{'name'} = $name;
1277
            $postreply .= "Status=OK Network \"$register{$uuid}->{'name'}\" saved: $uuid\n";
1278
            if ($params{'PUTDATA'}) {
1279
                my %jitem = %{$register{$uuid}};
1280
                my $json_text = to_json(\%jitem);
1281
                $json_text =~ s/null/"--"/g;
1282
                $postreply = $json_text;
1283
                $postmsg = "OK, updated network $name";
1284
            }
1285
        } else {
1286
            $postreply .= "Status=OK Nothing to save\n";
1287
            if ($params{'PUTDATA'}) {
1288
                my %jitem = %{$register{$uuid}};
1289
                my $json_text = to_json(\%jitem);
1290
                $json_text =~ s/null/"--"/g;
1291
                $postreply = $json_text;
1292
            }
1293
        }
1294
    }
1295

    
1296
}
1297

    
1298
sub Activate {
1299
    my ($uuid, $action, $obj) = @_;
1300
    if ($help) {
1301
        return <<END
1302
GET:uuid:
1303
Activate a network which must be in status down or nat.
1304
END
1305
    }
1306
    $uuid = $obj->{'uuid'} if ($obj->{'uuid'});
1307
    $action = 'activate' || $action;
1308
    my $regnet = $register{$uuid};
1309
    my $id = $regnet->{'id'};
1310
    my $name = $regnet->{'name'};
1311
    my $type = $regnet->{'type'};
1312
    my $status = $regnet->{'status'};
1313
    my $domains = $regnet->{'domains'};
1314
    my $systems = $regnet->{'systems'};
1315
    my $internalip = $regnet->{'internalip'};
1316
    my $externalip = $regnet->{'externalip'};
1317
    my $ports = $regnet->{'ports'};
1318
    my $idleft = ($id>99)?(substr $id,0,-2)+0 : 0;
1319
    my $idright = (substr $id,-2) + 0;
1320
    my $interfaces = `/sbin/ifconfig`;
1321
    my $dom = $domreg{$domains};
1322
    my $nicindex = ($dom->{'networkuuid1'} eq $uuid)?1:
1323
            ($dom->{'networkuuid2'} eq $uuid)?2:
1324
            ($dom->{'networkuuid3'} eq $uuid)?3:
1325
            0;
1326
    my $nicmac = $dom->{"nicmac$nicindex"};
1327
    my $e;
1328

    
1329
	if (!$id || $id==0 || $id==1 || $id>4095) {
1330
        $postreply .= "Status=ERROR Invalid ID activating $type\n";
1331
	    return $postreply;
1332
	} elsif (overQuotas()) { # Enforce quotas
1333
        $postreply .= "Status=ERROR Over quota activating $type " . overQuotas() . "\n";
1334
        return $postreply;
1335
    } elsif (($status ne 'down' && $status ne 'nat')) {
1336
        $postreply .= "Status=ERROR Cannot activate $type $name (current status is: $status)\n";
1337
        return $postreply;
1338
    }
1339

    
1340
    # Check if vlan with $id is created and doing nat, if not create it and create the gateway
1341
    unless (-e "/proc/net/vlan/$datanic.$id") {
1342
        eval {`/sbin/vconfig add $datanic $id`;} or do {$e=1; $postreply .= "Status=ERROR Problem adding vlan $datanic.$id $@\n"; return $postreply;};
1343
        eval {`/sbin/ifconfig $datanic.$id up`;}# or do {$e=1; $postreply .= "Status=ERROR Problem activating vlan $datanic.$id $@\n"; return $postreply;};
1344
    }
1345
#    if (!($interfaces =~ m/$datanic\.$id /)) {
1346
    if (!($interfaces =~ m/br$id /)) {
1347
        # check if gw is created locally
1348
        unless (`arping -C1 -c2 -D -I $datanic.$id 10.$idleft.$idright.1` =~ /reply from/) { # check if gw is created on another engine
1349
            # Create gw
1350
#            eval {`/sbin/ifconfig $datanic.$id 10.$idleft.$idright.1 netmask 255.255.255.0 broadcast 10.$idleft.$idright.255 up`; 1;} or do {
1351
#                $e=1; $postreply .= "Status=ERROR $@\n"; return $postreply;
1352
            #            };
1353
            # To support local instances on valve, gw is now created as a bridge
1354
            eval {`/sbin/brctl addbr br$id`; 1;} or do {$e=1; $postreply .= "Status=ERROR $@\n"; return $postreply; };
1355
            eval {`/sbin/brctl addif br$id $datanic.$id`; 1;} or do {$e=1; $postreply .= "Status=ERROR $@\n"; return $postreply; };
1356
            eval {`/sbin/ifconfig br$id 10.$idleft.$idright.1/24 up`; 1;} or do {
1357
                $e=1; $postreply .= "Status=ERROR $@\n"; return $postreply; }
1358
        } else {
1359
            $postreply .= "Status=OK GW is active on another Engine, assuming this is OK\n";
1360
        }
1361
    }
1362
    my $astatus = "nat" unless ($e);
1363
    `/usr/bin/touch $etcpath/dhcp-hosts-$id` unless (-e "$etcpath/dhcp-hosts-$id");
1364
    if ($action eq "activate") { #} && $domains) {
1365
        if ($type eq "internalip" || $type eq "ipmapping") {
1366
            # Configure internal dhcp server
1367
            if ($domains) {
1368
                my $result = addDHCPAddress($id, $domains, $internalip, "10.$idleft.$idright.1", $nicmac);
1369
                if ($result eq "OK") {
1370
                    $astatus = "up" if ($type eq "internalip");
1371
                } else {
1372
                    $e = 1;
1373
                    $postreply .= "$result\n";
1374
                }
1375
            }
1376

    
1377
            # Also export storage pools to user's network
1378
            my @spl = split(/,\s*/, $storagepools);
1379
            my $reloadnfs;
1380
            my $uid = `id -u irigo-$user`; chomp $uid;
1381
            $uid = `id -u nobody` unless ($uid =~ /\d+/); chomp $uid;
1382
            my $gid = `id -g irigo-$user`; chomp $gid;
1383
            $gid = `id -g nobody` unless ($gid =~ /\d+/); chomp $gid;
1384

    
1385
            # We are dealing with multiple upstream routes - configure local routing
1386
            if ($proxynic && $proxynic ne $extnic) {
1387
                if (-e "/etc/iproute2/rt_tables" && !grep(/1 proxyarp/, `cat /etc/iproute2/rt_tables`)) {
1388
                    `/bin/echo "1 proxyarp" >> /etc/iproute2/rt_tables`;
1389
                }
1390
                if (!grep(/$datanic\.$id/, `/sbin/ip route show table proxyarp`)) {
1391
                    `/sbin/ip route add "10.$idleft.$idright.0/24" dev $datanic.$id table proxyarp`;
1392
                }
1393
            }
1394

    
1395
            # Manuipulate NFS exports and related disk quotas
1396
            foreach my $p (@spl) {
1397
                if ($tenderlist[$p] && $tenderpathslist[$p]) {
1398
                    my $fuelpath = $tenderpathslist[$p] . "/$user/fuel";
1399
                    unless (-e $fuelpath) {
1400
                        if ($tenderlist[$p] eq 'local') { # We only support fuel on local tender for now
1401
                            `mkdir "$fuelpath"`;
1402
                            `chmod 777 "$fuelpath"`;
1403
                        }
1404
                    }
1405
                    if ($tenderlist[$p] eq "local") {
1406
                        `chown irigo-$user:irigo-$user "$fuelpath"`;
1407
                        my $mpoint = `df -P "$fuelpath" | tail -1 | cut -d' ' -f 1`;
1408
                        chomp $mpoint;
1409
                        my $storagequota = $Stabile::userstoragequota;
1410
                        if (!$storagequota) {
1411
                            $storagequota = $Stabile::config->get('STORAGE_QUOTA');
1412
                        }
1413
                        my $nfsquota = $storagequota * 1024 ; # quota is in MB
1414
                        $nfsquota = 0 if ($nfsquota < 0); # quota of -1 means no limit
1415
                        `setquota -u irigo-$user $nfsquota $nfsquota 0 0 "$mpoint"` if (-e "$mntpoint");
1416
                        if (!(`grep "$fuelpath 10\.$idleft\.$idright" /etc/exports`) && -e $fuelpath) {
1417
                            `echo "$fuelpath 10.$idleft.$idright.0/255.255.255.0(sync,no_subtree_check,all_squash,rw,anonuid=$uid,anongid=$gid)" >> /etc/exports`;
1418
                            $reloadnfs = 1;
1419
                        }
1420
                    }
1421
                }
1422
            }
1423
            `/usr/sbin/exportfs -r` if ($reloadnfs); #Reexport nfs shares
1424

    
1425
        } elsif ($type eq "externalip") {
1426
            # A proxy is needed to route traffic, don't go any further if not configured
1427
            if ($proxyip) {
1428
                # Set up proxy
1429
                if (!($interfaces =~ m/$proxyip/ && $interfaces =~ m/br$id:proxy/)) {
1430
                    eval {`/sbin/ifconfig br$id:proxy $proxyip/$proxysubnet up`; 1;}
1431
                        or do {$e=1; $postreply .= "Status=ERROR Problem setting up proxy arp gw $proxyip on br$id:proxy $@\n";};
1432
                    eval {`/sbin/ifconfig $proxynic:proxy $proxyip/$proxysubnet up`; 1;}
1433
                        or do {$e=1; $postreply .= "Status=ERROR Problem setting up proxy arp gw $proxynic $@\n";};
1434
                }
1435
                my $result = "OK";
1436
                # Configure dhcp server
1437
                if ($domains) {
1438
                    $result = addDHCPAddress($id, $domains, $externalip, "10.$idleft.$idright.1", $nicmac) if ($domains);
1439
                    if ($result eq "OK") {
1440
                        ;
1441
                    } else {
1442
                        $e = 1;
1443
                        $postreply .= "$result\n";
1444
                    }
1445
                }
1446
            } else {
1447
                $postreply .= "Status=ERROR Cannot set up external IP without Proxy ARP gateway\n";
1448
            }
1449
        }
1450

    
1451
        # Handle routing with Iptables
1452
        if ($type eq "ipmapping" || $type eq "internalip") {
1453
            `iptables -I FORWARD -d $internalip -m state --state ESTABLISHED,RELATED -j RETURN`;
1454
        }
1455
        # Check if external ip exists and routing configured, if not create and configure it
1456
        if ($type eq "ipmapping") {
1457
            if ($internalip && $internalip ne "--" && $externalip && $externalip ne "--" && !($interfaces =~ m/$externalip /g)) { # the space is important
1458
                $externalip =~ /\d+\.\d+\.(\d+)\.(\d+)/;
1459
                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
1460
                $ipend = $2 if (length("$extnic:$id-$ipend")>15);
1461
                eval {`/sbin/ifconfig $extnic:$id-$ipend $externalip/$extsubnet up`; 1;}
1462
                    or do {$e=1; $postreply .= "Status=ERROR Problem adding interface $extnic:$id-$ipend $@\n";};
1463
                unless (`ip addr show dev $extnic` =~ /$externalip/) {
1464
                    $e=10;
1465
                    $postreply .= "Status=ERROR Problem adding interface $extnic:$id-$ipend\n";
1466
                }
1467
                # `/sbin/iptables -A POSTROUTING -t nat -s $internalip -j LOG --log-prefix "SNAT-POST"`;
1468
                # `/sbin/iptables -A INPUT -t nat -s $internalip -j LOG --log-prefix "SNAT-INPUT"`;
1469
                # `/sbin/iptables -A OUTPUT -t nat -s $internalip -j LOG --log-prefix "SNAT-OUTPUT"`;
1470
                # `/sbin/iptables -A PREROUTING -t nat -s $internalip -j LOG --log-prefix "SNAT-PRE"`;
1471
                if ($ports && $ports ne "--") { # Port mapping is defined
1472
                    my @portslist = split(/, ?| /, $ports);
1473
                    foreach $port (@portslist) {
1474
                        my $ipfilter;
1475
                        if ($port =~ /(\d+)\.(\d+)\.(\d+)\.(\d+)(\/\d+)?:(\d+)/) {
1476
                            my $portip = "$1.$2.$3.$4$5";
1477
                            $port = $6;
1478
                            $ipfilter = "-s $portip";
1479
                        } else {
1480
                            $port = 0 unless ($port =~ /\d+/);
1481
                        }
1482
                        if ($port<1 || $port>65535) {
1483
                            $postreply .= "Status=ERROR Invalid port mapping for $name\n";
1484
                            $ports = "--";
1485
                            last;
1486
                        }
1487
                        if ($port>1 || $port<65535) {
1488
                            # DNAT externalip -> internalip
1489
                            eval {`/sbin/iptables -A PREROUTING -t nat -p tcp $ipfilter -d $externalip --dport $port -j DNAT --to $internalip`; 1;}
1490
                               or do {$e=2; $postreply .= "Status=ERROR Problem setting up routing $@\n";};
1491
                            eval {`/sbin/iptables -A PREROUTING -t nat -p udp $ipfilter -d $externalip --dport $port -j DNAT --to $internalip`; 1;}
1492
                               or do {$e=3; $postreply .= "Status=ERROR Problem setting up routing $@\n";};
1493
                            # PREROUTING is not parsed for packets coming from local host...
1494
                            eval {`/sbin/iptables -A OUTPUT -t nat -p tcp $ipfilter -d $externalip --dport $port -j DNAT --to $internalip`; 1;}
1495
                                or do {$e=2; $postreply .= "Status=ERROR Problem setting up routing $@\n";};
1496
                            eval {`/sbin/iptables -A OUTPUT -t nat -p udp $ipfilter -d $externalip --dport $port -j DNAT --to $internalip`; 1;}
1497
                                or do {$e=3; $postreply .= "Status=ERROR Problem setting up routing $@\n";};
1498
                            # Allow access to ipmapped internal ip on $port
1499
                            `iptables -I FORWARD -d $internalip -p tcp --dport $port -j RETURN`;
1500
                            `iptables -I FORWARD -d $internalip -p udp --dport $port -j RETURN`;
1501
                        }
1502
                    }
1503
                    eval {`/sbin/iptables -D INPUT -d $externalip -j DROP`; 1;} # Drop traffic to all other ports
1504
                        or do {$e=5; $postreply .= "Status=ERROR Problem setting up routing $@\n";};
1505
                    eval {`/sbin/iptables -A INPUT -d $externalip -j DROP`; 1;} # Drop traffic to all other ports
1506
                        or do {$e=6; $postreply .= "Status=ERROR Problem setting up routing $@\n";};
1507
                } else {
1508
                    # DNAT externalip -> internalip coming from outside , --in-interface $extnic
1509
                    eval {`/sbin/iptables -A PREROUTING -t nat -d $externalip -j DNAT --to $internalip`; 1;}
1510
                        or do {$e=7; $postreply .= "Status=ERROR Problem setting up routing $@\n";};
1511
                    # PREROUTING is not parsed for packets coming from local host...
1512
                    eval {`/sbin/iptables -A OUTPUT -t nat -d $externalip -j DNAT --to $internalip`; 1;}
1513
                        or do {$e=7; $postreply .= "Status=ERROR Problem setting up routing $@\n";};
1514
                    # Allow blanket access to ipmapped internal ip
1515
                    `iptables -I FORWARD -d $internalip -j RETURN`;
1516
                }
1517
                # We masquerade packets going to internalip from externalip to avoid confusion
1518
                #eval {`/sbin/iptables -A POSTROUTING -t nat --out-interface br$id -s $externalip -j MASQUERADE`; 1;}
1519
                #    or do {$e=3; $postreply .= "Status=ERROR Problem setting up routing $@\n";};
1520

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

    
1526
                # When receiving packet from client, if it's been routed, and outgoing interface is the external interface, SNAT.
1527
                unless ($Stabile::disablesnat) {
1528
                    eval {`/sbin/iptables -A POSTROUTING -t nat -s $internalip ! -d 10.$idleft.$idright.0/24 -j SNAT --to-source $externalip`; 1; }
1529
                        or do {$e=4; $postreply .= "Status=ERROR Problem setting up routing $@\n";};
1530
                #    eval {`/sbin/iptables -A POSTROUTING -t nat -s $internalip -j SNAT --to-source $externalip`; 1; }
1531
                #        or do {$e=4; $postreply .= "Status=ERROR Problem setting up routing $@\n";};
1532
                    eval {`/sbin/iptables -I INPUT -t nat -s $internalip ! -d 10.$idleft.$idright.0/24 -j SNAT --to-source $externalip`; 1; }
1533
                        or do {$e=4; $postreply .= "Status=ERROR Problem setting up routing $@\n";};
1534
                #    eval {`/sbin/iptables -I INPUT -t nat -s $internalip -j SNAT --to-source $externalip`; 1; }
1535
                #        or do {$e=4; $postreply .= "Status=ERROR Problem setting up routing $@\n";};
1536
                }
1537

    
1538
                if ($e) {
1539
                    $main::syslogit->($user, 'info', "Problem $action network $uuid ($name, $id): $@");
1540
                } else {
1541
                    $astatus = "up"
1542
                }
1543
            }
1544
        } elsif ($type eq "externalip") {
1545
            my $route = `/sbin/ip route`;
1546
            my $tables = `/sbin/iptables -L -n`;
1547

    
1548
            # Allow external IP send packets out
1549
            `/sbin/iptables -D FORWARD --in-interface br$id -s $externalip -j RETURN`;
1550
            `/sbin/iptables -I FORWARD --in-interface br$id -s $externalip -j RETURN`;
1551

    
1552
            # We are dealing with multiple upstream routes - configure local routing
1553
            if ($proxynic && ($proxynic ne $extnic)) {
1554
                if (-e "/etc/iproute2/rt_tables" && !grep(/1 proxyarp/, `cat /etc/iproute2/rt_tables`)) {
1555
                    `/bin/echo "1 proxyarp" >> /etc/iproute2/rt_tables`;
1556
                }
1557
                if (!grep(/$proxygw/, `/sbin/ip route show table proxyarp`)) {
1558
                    `/sbin/ip route del default dev $proxynic table proxyarp`; # delete first in case proxygw has changed
1559
                    `/sbin/ip route add default via $proxygw dev $proxynic table proxyarp`;
1560
                }
1561
                if (!grep(/proxyarp/, `/sbin/ip rule show`)) {
1562
                    `/sbin/ip rule add to $proxygw/$proxysubnet table main`;
1563
                    `/sbin/ip rule add from $proxygw/$proxysubnet table proxyarp`;
1564
                }
1565
                my $proxyroute = `/sbin/ip route show table proxyarp`;
1566
#                `/sbin/ip route add $externalip/32 dev $datanic.$id:proxy src $proxyip table proxyarp` unless ($proxyroute =~ /$externalip/);
1567
                `/sbin/ip route add $externalip/32 dev br$id:proxy src $proxyip table proxyarp` unless ($proxyroute =~ /$externalip/);
1568
            }
1569
            eval {`/bin/echo 1 > /proc/sys/net/ipv4/conf/$datanic.$id/proxy_arp`; 1;}
1570
                or do {$e=1; $postreply .= "Status=ERROR Problem setting up proxy arp $@\n";};
1571
            eval {`/bin/echo 1 > /proc/sys/net/ipv4/conf/$proxynic/proxy_arp`; 1;}
1572
                or do {$e=1; $postreply .= "Status=ERROR Problem setting up proxy arp $@\n";};
1573
            eval {`/sbin/ip route add $externalip/32 dev br$id:proxy src $proxyip` unless ($route =~ /$externalip/); 1;}
1574
                or do {$e=1; $postreply .= "Status=ERROR Problem setting up proxy arp $@\n";};
1575

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

    
1581

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

    
1586
            if ($ports && $ports ne "--") {
1587
                my @portslist = split(/, ?| /, $ports);
1588
                foreach $port (@portslist) {
1589
                    my $ipfilter;
1590
                    if ($port =~ /(\d+)\.(\d+)\.(\d+)\.(\d+)(\/\d+)?:(\d+)/) {
1591
                        my $portip = "$1.$2.$3.$4$5";
1592
                        $port = $6;
1593
                        $ipfilter = "-s $portip";
1594
                    } else {
1595
                        $port = 0 unless ($port =~ /\d+/);
1596
                    }
1597
                    if ($port<1 || $port>65535) {
1598
                        $postreply .= "Status=ERROR Invalid port mapping for $name\n";
1599
                        $ports = "--";
1600
                        last;
1601
                    }
1602

    
1603
                    if ($port>1 && $port<65535 && $port!=67) { # Disallow setting up a dhcp server
1604
                        eval {`/sbin/iptables -A FORWARD -p tcp -i $proxynic $portfilter -d $externalip --dport $port -j RETURN`; 1;}
1605
                            or do {$e=1; $postreply .= "Status=ERROR Problem setting up routing $@\n";};
1606
                        eval {`/sbin/iptables -A FORWARD -p udp -i $proxynic $portfilter -d $externalip --dport $port -j RETURN`; 1;}
1607
                            or do {$e=1; $postreply .= "Status=ERROR Problem setting up routing $@\n";};
1608
                    }
1609
                }
1610
                eval {`/sbin/iptables -D FORWARD -i $proxynic -d $externalip -j REJECT`; 1;} # Drop traffic to all other ports
1611
                    or do {$e=1; $postreply .= "Status=ERROR Problem setting up routing $@\n";};
1612
                eval {`/sbin/iptables -A FORWARD -i $proxynic -d $externalip -j REJECT`; 1;} # Drop traffic to all other ports
1613
                    or do {$e=1; $postreply .= "Status=ERROR Problem setting up routing $@\n";};
1614
            } else {
1615
                # First allow everything else to this ip
1616
                eval {`/sbin/iptables -D FORWARD -i $proxynic -d $externalip -j RETURN`; 1;}
1617
                    or do {$e=1; $postreply .= "Status=ERROR Problem setting up routing $@\n";};
1618
                eval {`/sbin/iptables -A FORWARD -i $proxynic -d $externalip -j RETURN`; 1;}
1619
                    or do {$e=1; $postreply .= "Status=ERROR Problem setting up routing $@\n";};
1620
                # Then disallow setting up a dhcp server
1621
                eval {`/sbin/iptables -D FORWARD -p udp -i $proxynic -d $externalip --dport 67 -j REJECT`; 1;}
1622
                    or do {$e=1; $postreply .= "Status=ERROR Problem setting up routing $@\n";};
1623
                eval {`/sbin/iptables -A FORWARD -p udp -i $proxynic -d $externalip --dport 67 -j REJECT`; 1;}
1624
                    or do {$e=1; $postreply .= "Status=ERROR Problem setting up routing $@\n";};
1625
            }
1626
        }
1627
    }
1628

    
1629
    # Allow all inter-VLAN communication
1630
    `iptables -D FORWARD --in-interface br$id --out-interface br$id -j RETURN 2>/dev/null`;
1631
    `iptables -I FORWARD --in-interface br$id --out-interface br$id -j RETURN`;
1632
    # Disallow any access to vlan except mapped from external NIC i.e. ipmappings
1633
    `iptables -D FORWARD ! --in-interface $extnic --out-interface br$id -j DROP 2>/dev/null`;
1634
    `iptables -A FORWARD ! --in-interface $extnic --out-interface br$id -j DROP`;
1635

    
1636
    # 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
1637
#    `/sbin/iptables --delete FORWARD --in-interface $datanic.$id ! -s 10.$idleft.$idright.0/24 -j DROP`;
1638
    unless ($proxynic eq "$datanic.$id") {
1639
#        `/sbin/iptables --append FORWARD --in-interface $datanic.$id ! -s 10.$idleft.$idright.0/24 -j DROP`;
1640
    }
1641

    
1642
    # Enable nat'ing
1643
    eval {
1644
        #my $masq = `/sbin/iptables -L -n -t nat`;
1645
        #        if (!($masq =~ "MASQUERADE.+all.+--.+0\.0\.0\.0/0")) {
1646
        `/sbin/iptables -D POSTROUTING -t nat --out-interface $extnic -s 10.0.0.0/8 -j MASQUERADE`;
1647
        `/sbin/iptables -A POSTROUTING -t nat --out-interface $extnic -s 10.0.0.0/8 -j MASQUERADE`;
1648
        # Christian's dev environment
1649
        #            my $interfaces = `/sbin/ifconfig`;
1650
        #            if ($interfaces =~ m/ppp0/) {
1651
        #                `/sbin/iptables --table nat --append POSTROUTING --out-interface ppp0 -s 10.0.0.0/8 -j MASQUERADE`;
1652
        #            }
1653
        #        };
1654
        1;
1655
    } or do {print "Unable to enable masquerading: $@\n";};
1656

    
1657
    $uistatus = ($e)?"":validateStatus($register{$uuid});
1658
    if ($uistatus && $uistatus ne 'down') {
1659
        $uiuuid = $uuid;
1660
        $postreply .= "Status=$uistatus OK $action $type $name\n";
1661
    } else {
1662
        $postreply .= "Status=ERROR Cannot $action $type $name ($uistatus)\n";
1663
    }
1664
    $main::syslogit->($user, 'info', "$action network $uuid ($name, $id) -> $uistatus");
1665
    updateBilling("$uistatus $uuid ($id)");
1666
    # $main::updateUI->({tab=>"networks", user=>$user, uuid=>$uiuuid, status=>$uistatus}) if ($uistatus);
1667
    return $postreply;
1668
}
1669

    
1670
sub Removeusernetworks {
1671
    my $username = shift;
1672
    return unless (($isadmin || $user eq $username) && !$isreadonly);
1673
    $user = $username;
1674
    foreach my $uuid (keys %register) {
1675
        if ($register{$uuid}->{'user'} eq $user) {
1676
            $postreply .=  "Removing network $register{$path}->{'name'}, $uuid" . ($console?'':'<br>') . "\n";
1677
            Deactivate($uuid);
1678
            Remove('remove', $uuid);
1679
        }
1680
    }
1681
}
1682

    
1683
sub Remove {
1684
    my ($uuid, $action, $obj) = @_;
1685
    if ($help) {
1686
        return <<END
1687
DELETE:uuid,force:
1688
Delete a network which must be in status down or nat and should not be used by any servers, or linked to any stacks.
1689
May also be called with endpoints "/stabile/[uuid]" or "/stabile?uuid=[uuid]"
1690
Set [force] to remove even if linked to a system.
1691
END
1692
    }
1693
    $uuid = $obj->{'uuid'} if ($curuuid && $obj->{'uuid'}); # we are called from a VM with an ip address as target
1694
    my $force = $obj->{'force'};
1695
    ( my $domains, my $domainnames ) = getDomains($uuid);
1696
    ( my $systems, my $systemnames ) = getSystems($uuid);
1697

    
1698
    if ($register{$uuid}) {
1699
        my $id = $register{$uuid}->{'id'};
1700
        my $name = $register{$uuid}->{'name'};
1701
        utf8::decode($name);
1702
        my $status = $register{$uuid}->{'status'};
1703
        my $type = $register{$uuid}->{'type'};
1704
        my $internalip = $register{$uuid}->{'internalip'};
1705
        my $externalip = $register{$uuid}->{'externalip'};
1706

    
1707
        my @regvalues = values %register;
1708
        if (
1709
            $id!=0 && $id!=1 && (!$domains || $domains eq '--')
1710
                && ((!$systems || $systems eq '--' || $force)
1711
                # allow internalip's to be removed if active and only linked, i.e. not providing dhcp
1712
                || ($status eq 'down' || $status eq 'new' || $status eq 'nat' || ($type eq 'internalip' && $systems && $systems ne '--')))
1713
        ) {
1714
            # Deconfigure internal dhcp server and DNS
1715
            if ($type eq "internalip") {
1716
                my $result =  removeDHCPAddress($id, $domains, $internalip);
1717
                $postreply .= "$result\n" unless $result eq "OK";
1718
            } elsif ($type eq "ipmapping") {
1719
                my $result =  removeDHCPAddress($id, $domains, $internalip);
1720
                $postreply .= "$result\n" unless $result eq "OK";
1721
                if ($dodns) {
1722
                    $main::dnsDelete->($engineid, $externalip) if ($enginelinked);
1723
                }
1724
            } elsif ($type eq "externalip") {
1725
                my $result =  removeDHCPAddress($id, $domains, $externalip);
1726
                $postreply .= "$result\n" unless $result eq "OK";
1727
                if ($dodns) {
1728
                    $main::dnsDelete->($engineid, $externalip) if ($enginelinked);
1729
                }
1730
            }
1731
            if ($status eq 'nat') {
1732
                # Check if last network in vlan. If so take it down
1733
                my $notlast;
1734
                foreach my $val (@regvalues) {
1735
                    if ($val->{'user'} eq $user && $val->{'id'} == $id) {
1736
                        $notlast = 1;
1737
                    }
1738
                }
1739
                if (!$notlast) {
1740
                    eval {`/sbin/ifconfig $datanic.$id down`; 1;} or do {;};
1741
                    eval {`/sbin/vconfig rem $datanic.$id`; 1;} or do {;};
1742
                }
1743
            }
1744

    
1745
            unless (tie(%sysreg,'Tie::DBI', Hash::Merge::merge({table=>'systems'}, $Stabile::dbopts)) ) {$res .= qq|{"status": "Error": "message": "Unable to access systems register"}|; return $res;};
1746
            if ($sysreg{$systems}) { # Remove existing link to system
1747
                $sysreg{$systems}->{'networkuuids'} =~ s/$uuid,?//;
1748
                $sysreg{$systems}->{'networknames'} = s/$name,?//;
1749
            }
1750
            tied(%sysreg)->commit;
1751
            untie(%sysreg);
1752

    
1753

    
1754
            delete $register{$uuid};
1755
            tied(%register)->commit;
1756
            updateBilling("delete $val->{'externalip'}") if ($type eq "ipmapping");
1757
            $main::syslogit->($user, "info", "Deleted network $uuid ($id)");
1758
            $postreply = "[]" || $postreply;
1759
            $main::updateUI->({tab=>"networks", user=>$user, type=>"update"});
1760
        } else {
1761
            $postreply .= "Status=ERROR Cannot remove $uuid which is $status. Cannot delete network 0,1 or a network which is active or in use.\n";
1762
            $main::updateUI->({tab=>"networks", user=>$user, message=>"Cannot remove a network which is active, linked or in use."});
1763
        }
1764
    } else {
1765
        $postreply .= "Status=ERROR Network $uuid $ipaddress not found\n";
1766
    }
1767
    return $postreply;
1768
}
1769

    
1770
sub Deactivate {
1771
    my ($uuid, $action, $obj) = @_;
1772

    
1773
    if ($help) {
1774
        return <<END
1775
GET:uuid:
1776
Deactivate a network which must be in status up.
1777
END
1778
    }
1779
    $uuid = $obj->{'uuid'} if ($obj->{'uuid'});
1780

    
1781
    unless ($register{$uuid}) {
1782
        $postreply .= "Status=ERROR Connection with uuid $uuid not found\n";
1783
        return $postreply;
1784
    }
1785
    my $regnet = $register{$uuid};
1786

    
1787
    $action = $action || 'deactivate';
1788
    ( my $domains, my $domainnames ) = getDomains($uuid);
1789
    my $interfaces = `/sbin/ifconfig`;
1790

    
1791
    my $id = $regnet->{'id'};
1792
    my $name = $regnet->{'name'};
1793
    my $type = $regnet->{'type'};
1794
    my $internalip = $regnet->{'internalip'};
1795
    my $externalip = $regnet->{'externalip'};
1796
    my $ports = $regnet->{'ports'};
1797

    
1798
    if ($id!=0 && $id!=1 && $status ne 'down') {
1799
    # If gateway is created, take it down along with all user's networks
1800
        if ($action eq "stop") {
1801
            my $res = Stop($id, $action);
1802
            if ($res) {
1803
                unlink "$etcpath/dhcp-hosts-$id" if (-e "$etcpath/dhcp-hosts-$id");
1804
            };
1805
        }
1806
    } else {
1807
        $postreply .= "Status=ERROR Cannot $action network $name\n";
1808
        return $postreply;
1809
    }
1810

    
1811
    my $idleft = ($id>99)?(substr $id,0,-2)+0 : 0;
1812
    my $idright = (substr $id,-2) + 0;
1813
    my $e = 0;
1814
    my $duprules = 0;
1815

    
1816
    if ($type eq "ipmapping" || $type eq "internalip") {
1817
        `iptables -D FORWARD -d $internalip -m state --state ESTABLISHED,RELATED -j RETURN`;
1818
    }
1819
    if ($type eq "ipmapping") {
1820
        # Check if external ip exists and take it down if so
1821
        if ($internalip && $internalip ne "--" && $externalip && $externalip ne "--" && ($interfaces =~ m/$externalip/g)) {
1822
            $externalip =~ /\d+\.\d+\.(\d+)\.(\d+)/;
1823
            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
1824
            $ipend = $2 if (length("$extnic:$id-$ipend")>15);
1825
            eval {`/sbin/ifconfig $extnic:$id-$ipend down`; 1;} or do {$e=1; $postreply .= "Status=ERROR $@\n";};
1826

    
1827
            if ($ports && $ports ne "--") { # Port mapping is defined
1828
                my @portslist = split(/, ?| /, $ports);
1829
                foreach my $port (@portslist) {
1830
                    my $ipfilter;
1831
                    if ($port =~ /(\d+)\.(\d+)\.(\d+)\.(\d+)(\/\d+)?:(\d+)/) {
1832
                        my $portip = "$1.$2.$3.$4$5";
1833
                        $port = $6;
1834
                        $ipfilter = "-s $portip";
1835
                    } else {
1836
                        $port = 0 unless ($port =~ /\d+/);
1837
                    }
1838
                    if ($port<1 || $port>65535) {
1839
                        $postreply .= "Status=ERROR Invalid port mapping for $name\n";
1840
                        $ports = "--";
1841
                        last;
1842
                    }
1843
                    # Remove DNAT rules
1844
                    if ($port>1 || $port<65535) {
1845
                        # repeat for good measure
1846
                        for (my $di=0; $di < 10; $di++) {
1847
                            $duprules = 0;
1848
                            eval {$duprules++ if (`/sbin/iptables -D PREROUTING -t nat -p tcp $ipfilter -d $externalip --dport $port -j DNAT --to $internalip`); 1;}
1849
                                or do {$postreply .= "Status=ERROR $@\n"; $e=1};
1850
                            eval {$duprules++ if (`/sbin/iptables -D PREROUTING -t nat -p udp $ipfilter -d $externalip --dport $port -j DNAT --to $internalip`); 1;}
1851
                                or do {$postreply .= "Status=ERROR $@\n"; $e=1};
1852
                            eval {$duprules++ if (`/sbin/iptables -D OUTPUT -t nat -p tcp $ipfilter -d $externalip --dport $port -j DNAT --to $internalip`); 1;}
1853
                                or do {$postreply .= "Status=ERROR $@\n"; $e=1};
1854
                            eval {$duprules++ if (`/sbin/iptables -D OUTPUT -t nat -p udp $ipfilter -d $externalip --dport $port -j DNAT --to $internalip`); 1;}
1855
                                or do {$postreply .= "Status=ERROR $@\n"; $e=1};
1856
                            eval {$duprules++ if (`/sbin/iptables -D POSTROUTING -t nat --out-interface br$id -s $externalip -j MASQUERADE`); 1;}
1857
                                or do {$e=3; $postreply .= "Status=ERROR Problem setting up routing $@\n";};
1858
                            # Remove access to ipmapped internal ip on $port
1859
                            eval {$duprules++ if (`/sbin/iptables -D FORWARD -d $internalip -p udp --dport $port -j RETURN`); 1;}
1860
                                or do {$e=3; $postreply .= "Status=ERROR Problem setting up routing $@\n";};
1861
                            eval {$duprules++ if (`/sbin/iptables -D FORWARD -d $internalip -p tcp --dport $port -j RETURN`); 1;}
1862
                                or do {$e=3; $postreply .= "Status=ERROR Problem setting up routing $@\n";};
1863
                            last if ($duprules >6);
1864
                        }
1865
                    }
1866
                }
1867
                # Remove SNAT rules
1868
                # repeat for good measure
1869
                for (my $di=0; $di < 10; $di++) {
1870
                    $duprules = 0;
1871
                    eval {$duprules++ if (`/sbin/iptables -D POSTROUTING -t nat -s $internalip ! -d 10.$idleft.$idright.0/24 -j SNAT --to-source $externalip`); 1; }
1872
                        or do {$e=1; $postreply .= "Status=ERROR Problem setting up routing $@\n";};
1873
                    last if ($duprules);
1874
                }
1875
                # Remove rule to drop traffic to all other ports
1876
                eval {`/sbin/iptables -D INPUT -d $externalip -j DROP`; 1;}
1877
                    or do {$e=1; $postreply .= "Status=ERROR Problem setting up routing $@\n";};
1878
            } else {
1879
                # Remove DNAT rules
1880
                # repeat for good measure
1881
                for (my $di=0; $di < 10; $di++) {
1882
                    $duprules = 0;
1883
                    eval {$duprules++ if (`/sbin/iptables -D PREROUTING -t nat -d $externalip -j DNAT --to $internalip`); 1;}
1884
                        or do {$postreply .= "Status=ERROR $@\n"; $e=1};
1885
                    eval {$duprules++ if (`/sbin/iptables -D OUTPUT -t nat -d $externalip -j DNAT --to $internalip`); 1;}
1886
                        or do {$postreply .= "Status=ERROR $@\n"; $e=1};
1887
                    last if ($duprules >1);
1888
                }
1889
                # Remove blanket access to ipmapped internal ip
1890
                `iptables -D FORWARD -d $internalip -j RETURN`;
1891
            }
1892
            # Remove SNAT and MASQUERADE rules
1893
            # repeat for good measure
1894
            for (my $di=0; $di < 10; $di++) {
1895
                $duprules = 0;
1896
            #    eval {$duprules++ if (`/sbin/iptables -D POSTROUTING -t nat --out-interface br$id -s $externalip -j MASQUERADE`); 1;}
1897
            #        or do {$e=3; $postreply .= "Status=ERROR Problem setting up routing $@\n";};
1898
                eval {$duprules++ if (`/sbin/iptables -D POSTROUTING -t nat --out-interface br$id ! -d 10.$idleft.$idright.0/24 -j MASQUERADE`); 1;}
1899
                    or do {$e=3; $postreply .= "Status=ERROR Problem setting up routing $@\n";};
1900

    
1901
                eval {$duprules++ if (`/sbin/iptables -D POSTROUTING -t nat -s $internalip ! -d 10.$idleft.$idright.0/24 -j SNAT --to-source $externalip`); 1; }
1902
                    or do {$e=1; $postreply .= "Status=ERROR Problem setting up routing $@\n";};
1903
            #    eval {$duprules++ if (`/sbin/iptables -D POSTROUTING -t nat -s $internalip -j SNAT --to-source $externalip`); 1; }
1904
            #        or do {$e=1; $postreply .= "Status=ERROR Problem setting up routing $@\n";};
1905
                eval {$duprules++ if (`/sbin/iptables -D INPUT -t nat -s $internalip ! -d 10.$idleft.$idright.0/24 -j SNAT --to-source $externalip`); 1; }
1906
                    or do {$e=1; $postreply .= "Status=ERROR Problem setting up routing $@\n";};
1907
            #    eval {$duprules++ if (`/sbin/iptables -D INPUT -t nat -s $internalip -j SNAT --to-source $externalip`); 1; }
1908
            #        or do {$e=1; $postreply .= "Status=ERROR Problem setting up routing $@\n";};
1909
            #    eval {$duprules++ if (`/sbin/iptables -D INPUT -t nat -s $internalip ! -d 10.$idleft.$idright.0/24 -j SNAT --to-source $externalip`); 1; }
1910
            #        or do {$e=1; $postreply .= "Status=ERROR Problem setting up routing $@\n";};
1911
            #    eval {$duprules++ if (`/sbin/iptables -D INPUT -t nat -s $internalip -j SNAT --to-source $externalip`); 1; }
1912
            #        or do {$e=1; $postreply .= "Status=ERROR Problem setting up routing $@\n";};
1913
                last if ($duprules >1);
1914
            }
1915
            # `/sbin/iptables -D POSTROUTING -t nat -s $internalip -j LOG --log-prefix "SNAT-POST"`;
1916
            # `/sbin/iptables -D INPUT -t nat -s $internalip -j LOG --log-prefix "SNAT-INPUT"`;
1917
            # `/sbin/iptables -D OUTPUT -t nat -s $internalip -j LOG --log-prefix "SNAT-OUTPUT"`;
1918
            # `/sbin/iptables -D PREROUTING -t nat -s $internalip -j LOG --log-prefix "SNAT-PRE"`;
1919
        }
1920
    } elsif ($type eq "externalip") {
1921
        if ($externalip && $externalip ne "--") {
1922
            # We are dealing with multiple upstream routes - configure local routing
1923
            if ($proxynic && $proxynic ne $extnic) {
1924
                my $proxyroute = `/sbin/ip route show table proxyarp`;
1925
                `/sbin/ip route del $externalip/32 dev br$id:proxy src $proxyip table proxyarp` if ($proxyroute =~ /$externalip/);
1926
            }
1927

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

    
1931
            if ($ports && $ports ne "--") {
1932
                my @portslist = split(/, ?| /, $ports);
1933
                foreach my $port (@portslist) {
1934
                    my $ipfilter;
1935
                    if ($port =~ /(\d+)\.(\d+)\.(\d+)\.(\d+)(\/\d+)?:(\d+)/) {
1936
                        my $portip = "$1.$2.$3.$4$5";
1937
                        $port = $6;
1938
                        $ipfilter = "-s $portip";
1939
                    } else {
1940
                        $port = 0 unless ($port =~ /\d+/);
1941
                    }
1942
                    if ($port<1 || $port>65535) {
1943
                        $postreply .= "Status=ERROR Invalid port mapping for $name\n";
1944
                        $ports = "--";
1945
                        last;
1946
                    }
1947

    
1948
                    if ($port>1 || $port<65535) {
1949
                        # repeat for good measure
1950
                        for (my $di=0; $di < 10; $di++) {
1951
                            $duprules = 0;
1952
                            eval {$duprules++ if (`/sbin/iptables -D FORWARD -p tcp -i $proxynic $ipfilter -d $externalip --dport $port -j RETURN`); 1;}
1953
                                or do {$e=1; $postreply .= "Status=ERROR Problem setting up routing $@\n";};
1954
                            eval {$duprules++ if (`/sbin/iptables -D FORWARD -p udp -i $proxynic $ipfilter -d $externalip --dport $port -j RETURN`); 1;}
1955
                                or do {$e=1; $postreply .= "Status=ERROR Problem setting up routing $@\n";};
1956
                            last if ($duprules > 1);
1957
                        }
1958
                    }
1959
                }
1960
            }
1961
            # Remove rule to allow forwarding from $externalip
1962
	        `/sbin/iptables --delete FORWARD --in-interface br$id -s $externalip -j RETURN`;
1963
            # Remove rule to disallow setting up a dhcp server
1964
            eval {`/sbin/iptables -D FORWARD -p udp -i $proxynic -d $externalip --dport 67 -j REJECT`; 1;}
1965
                or do {$e=1; $postreply .= "Status=ERROR Problem setting up routing $@\n";};
1966
            # Leave outgoing connectivity - not
1967
            eval {`/sbin/iptables -D FORWARD -i $proxynic -d $externalip -m state --state ESTABLISHED,RELATED -j RETURN`; 1;}
1968
                or do {$e=1; $postreply .= "Status=ERROR Problem setting up routing $@\n";};
1969
            eval {`/sbin/iptables -D FORWARD -i $proxynic -d $externalip -j RETURN`; 1;}
1970
                or do {$e=1; $postreply .= "Status=ERROR Problem setting up routing $@\n";};
1971
            # No need to reject - we reject all per default to the subnet
1972
            eval {`/sbin/iptables -D FORWARD -i $proxynic -d $externalip -j REJECT`; 1;}
1973
                or do {$e=1; $postreply .= "Status=ERROR Problem setting up routing $@\n";};
1974
        }
1975
    }
1976
    # Deconfigure internal dhcp server
1977
    if ($type eq "internalip" || $type eq "ipmapping") {
1978
        my $result =  removeDHCPAddress($id, $domains, $internalip);
1979
        if ($result ne "OK") {
1980
            $e=1;
1981
            $postreply .= "$result\n";
1982
        }
1983
    } elsif ($type eq "externalip" && $domains) {
1984
        my $result =  removeDHCPAddress($id, $domains, $externalip);
1985
        if ($result ne "OK") {
1986
            $e=1;
1987
            $postreply .= "$result\n";
1988
        }
1989
    }
1990
    $uistatus = ($e)?"":validateStatus($register{$uuid});
1991
    if ($uistatus) {
1992
        $uiuuid = $uuid;
1993
        $postreply .= "Status=$uistatus OK $action $type $name: $uistatus\n";
1994
    } else {
1995
        $postreply .= "Status=ERROR Cannot $action $type $name: $uistatus\n";
1996
    }
1997
    $main::syslogit->($user, 'info', "$action network $uuid ($name, $id) -> $uistatus");
1998
    updateBilling("$uistatus $uuid ($id)");
1999
    # $main::updateUI->({tab=>"networks", user=>$user, uuid=>$uiuuid, status=>$uistatus}) if ($uistatus);
2000
    return $postreply;
2001
}
2002

    
2003
sub Stop {
2004
    my ($id, $action) = @_;
2005
    # Check if we were passed a uuid
2006
    if ($id =~ /\-/ && $register{$id} && ($register{$id}->{'user'} eq $user || $isadmin)) {
2007
        $id = $register{$id}->{'id'}
2008
    }
2009
    if ($help) {
2010
        return <<END
2011
GET:uuid:
2012
Stops a network by removing gateway. Network must be in status up or nat.
2013
END
2014
    }
2015

    
2016
    my $idleft = ($id>99)?(substr $id,0,-2)+0 : 0;
2017
    my $idright = (substr $id,-2) + 0;
2018
    my $e = 0;
2019
    # First deactivate all user's networks with same id
2020
    my @regkeys = (tied %register)->select_where("user = '$user'");
2021
    foreach my $key (@regkeys) {
2022
        my $valref = $register{$key};
2023
        my $cuuid = $valref->{'uuid'};
2024
        my $ctype = $valref->{'type'};
2025
        my $cdbuser = $valref->{'user'};
2026
        my $cid = $valref->{'id'};
2027
    # Only list networks belonging to current user
2028
        if ($user eq $cdbuser && $id eq $cid && $ctype ne "gateway") {
2029
            if ($ctype eq "internalip" || $ctype eq "ipmapping" || $ctype eq "externalip") {
2030
                my $result = Deactivate($cuuid, 'deactivate');
2031
                if ($result =~ /\w+=ERROR (.+)/i) {
2032
                    $e = $1;
2033
                }
2034
            }
2035
        }
2036
     }
2037
    my $interfaces = `/sbin/ifconfig br$id`;
2038
     # Only take down interface and vlan if gateway IP is active on interface
2039
    if ($e) {
2040
        $postreply .= "Status=Error Not taking down gateway, got an error: $e\n"
2041
#    } elsif ($interfaces =~ /^$datanic\.$id.+\n.+inet .+10\.$idleft\.$idright\.1/
2042
    } elsif ($interfaces =~ /10\.$idleft\.$idright\.1/
2043
            && !$e) {
2044
        eval {`/sbin/brctl delif br$id $datanic.$id`; 1;} or do {$e=1;};
2045
        eval {`/sbin/ifconfig br$id down`; 1;} or do {$e=1;};
2046
        eval {`/sbin/ifconfig $datanic.$id down`; 1;} or do {$e=1;};
2047
        eval {`/sbin/vconfig rem $datanic.$id`; 1;} or do {$e=1;};
2048
    } else {
2049
        $postreply .= "Status=Error Not taking down interface, gateway 10.$idleft.$idright.1 is not active on interface br$id - $interfaces.\n"
2050
    }
2051
    # Remove rule to only forward packets coming from subnet assigned to vlan
2052
#    `/sbin/iptables --delete FORWARD --in-interface $datanic.$id ! -s 10.$idleft.$idright.0/24 -j DROP`;
2053

    
2054
    $uistatus = ($e)?$uistatus:"down";
2055
    if ($uistatus eq 'down') {
2056
        $uiuuid = $uuid;
2057
        $postreply .= "Status=$uistatus OK $action gateway: $uistatus\n";
2058
    } else {
2059
        $postreply .= "Status=Error Cannot $action $type $name: $uistatus\n";
2060
    }
2061
    return $postreply;
2062
}
2063

    
2064
sub getDomains {
2065
    my $uuid = shift;
2066
    my $domains;
2067
    my $domainnames;
2068
    my @domregvalues = values %domreg;
2069
    foreach my $domval (@domregvalues) {
2070
        if (($domval->{'networkuuid1'} eq $uuid || $domval->{'networkuuid2'} eq $uuid || $domval->{'networkuuid3'} eq $uuid)
2071
                && $domval->{'user'} eq $user) {
2072
            $domains .= $domval->{'uuid'} . ", ";
2073
            $domainnames .= $domval->{'name'} . ", ";
2074
        }
2075
    }
2076
    $domains = substr $domains, 0, -2;
2077
    $domainnames = substr $domainnames, 0, -2;
2078
    return ($domains, $domainnames); 
2079
}
2080

    
2081
sub getSystems {
2082
    my $uuid = shift;
2083
    my $systems;
2084
    my $systemnames;
2085
    unless (tie(%sysreg,'Tie::DBI', Hash::Merge::merge({table=>'systems'}, $Stabile::dbopts)) ) {$res .= qq|{"status": "Error": "message": "Unable to access systems register"}|; return $res;};
2086
    my @sysregvalues = values %sysreg;
2087
    foreach my $sysval (@sysregvalues) {
2088
        my $networkuuids = $sysval->{'networkuuids'};
2089
        if ($networkuuids =~ /$uuid/ && $sysval->{'user'} eq $user) {
2090
            $systems = $sysval->{'uuid'};
2091
            $systemnames = $sysval->{'name'};
2092
            last;
2093
        }
2094
    }
2095
    unless ($systems) {
2096
        my @sysregvalues = values %domreg;
2097
        foreach my $sysval (@sysregvalues) {
2098
            my $networkuuids = $sysval->{'networkuuids'};
2099
            if ($networkuuids =~ /$uuid/ && $sysval->{'user'} eq $user) {
2100
                $systems = $sysval->{'uuid'};
2101
                $systemnames = $sysval->{'name'};
2102
                last;
2103
            }
2104
        }
2105
    }
2106
    return ($systems, $systemnames);
2107
}
2108

    
2109
sub getNextId {
2110
	# Find the next available vlan id
2111
	my $reqid = shift;
2112
	my $username = shift;
2113
	$username = $user unless ($username);
2114
    my $nextid = 1;
2115
	my $vlanstart = $Stabile::config->get('VLAN_RANGE_START');
2116
	my $vlanend = $Stabile::config->get('VLAN_RANGE_END');
2117

    
2118
    if ($reqid eq 0 || $reqid == 1) {
2119
        return $requid;
2120
    } elsif ($reqid && ($reqid > $vlanend || $reqid < $vlanstart)) {
2121
        return -1 unless ($isadmin);
2122
    }
2123

    
2124
	$reqid = $reqid + 0;
2125

    
2126
    my %ids;
2127
    # First check if the user has an existing vlan, if so use the first we find as default value
2128
    my @regvalues = values %register;
2129
    @regvalues = (sort {$a->{id} <=> $b->{id}} @regvalues);
2130
    foreach my $val (@regvalues) { # Traverse all id's in use
2131
        my $id = 0 + $val->{'id'};
2132
        my $dbuser = $val->{'user'};
2133
        if ($id > 1) {
2134
            if ($username eq $dbuser) { # If a specific id was requested map all id's
2135
                if (!$reqid) {# If no specific id was asked for, stop now, and use the user's first one
2136
                    $nextid = $id;
2137
                    last;
2138
                }
2139
            } else {
2140
                $ids{$id} = 1; # Mark this id as used (by another user)
2141
            }
2142
        }
2143
    }
2144
    if ($nextid>1) {
2145
        return $nextid;
2146
    } elsif ($reqid) {
2147
        if (!$ids{$reqid} || $isadmin) { # If an admin is requesting id used by another, assume he knows what he is doing
2148
            $nextid = $reqid; # Safe to use
2149
        } else {
2150
            $nextid = -1; # Id already in use by another
2151
        }
2152
    } elsif ($nextid == 1) { # This user is not currently using any vlan's, find the first free one
2153
        for ($n=$vlanstart; $n<$vlanend; $n++) {
2154
            if (!$ids{$n}) { # Don't return an id used (by another user)
2155
                $nextid = $n;
2156
                last;
2157
            }
2158
        }
2159
    }
2160
	return $nextid;
2161
}
2162

    
2163
sub getNextExternalIP {
2164
	# Find the next available IP
2165
	my $extip = shift;
2166
	my $extuuid = shift;
2167
	my $proxyarp = shift; # Are we trying to assign a proxy arp's external IP?
2168
	$extip="" if ($extip eq "--");
2169

    
2170
	my $extipstart;
2171
	my $extipend;
2172

    
2173
    if ($proxyarp) {
2174
        $extipstart = $Stabile::config->get('PROXY_IP_RANGE_START');
2175
        $extipend = $Stabile::config->get('PROXY_IP_RANGE_END');
2176
    } else {
2177
        $extipstart = $Stabile::config->get('EXTERNAL_IP_RANGE_START');
2178
        $extipend = $Stabile::config->get('EXTERNAL_IP_RANGE_END');
2179
    }
2180

    
2181
	return "" unless ($extipstart && $extipend);
2182

    
2183
	my $interfaces = `/sbin/ifconfig`;
2184
#	$interfaces =~ m/eth0 .+\n.+inet addr:(\d+\.\d+\.\d+)\.(\d+)/;
2185
	$extipstart =~  m/(\d+\.\d+\.\d+)\.(\d+)/;
2186
	my $bnet1 = $1;
2187
	my $bhost1 = $2+0;
2188
	$extipend =~  m/(\d+\.\d+\.\d+)\.(\d+)/;
2189
	my $bnet2 = $1;
2190
	my $bhost2 = $2+0;
2191
	my $nextip = "";
2192
	if ($bnet1 ne $bnet2) {
2193
		print "Status=ERROR Only 1 class C subnet is supported for $name\n";
2194
		return "";
2195
	}
2196
	my %ids;
2197
	# First create map of IP's reserved by other servers in DB
2198
	my @regvalues = values %register;
2199
	foreach my $val (@regvalues) {
2200
		my $ip = $val->{'externalip'};
2201
		# $ip =~ m/(\d+\.\d+\.\d+)\.(\d+)/;
2202
		# my $id = $2;
2203
		$ids{$ip} = $val->{'uuid'} unless ($extuuid eq $val->{'uuid'});
2204
	}
2205
    my $oc = overQuotas(1);
2206
	if ($oc) { # Enforce quotas
2207
        $postreply .= "Status=ERROR Over quota allocating external IP\n";
2208
	} elsif ($extip && $extip =~  m/($bnet1)\.(\d+)/ && $2>=$bhost1 && $2<$bhost2) {
2209
	# An external ip was supplied - check if it's free and ok
2210
		if (!$ids{$extip} && !($interfaces =~ m/$extip.+\n.+inet addr:$extip/) && $extip=~/$bnet$\.(\d)/) {
2211
			$nextip = $extip;
2212
		}
2213
	} else {
2214
	# Find random IP not reserved, and check it is not in use (for other purposes)
2215
	    my @bhosts = ($bhost1..$bhost2);
2216
        my @rbhosts = shuffle @bhosts;
2217
		for ($n=0; $n<$bhost2-$bhost1; $n++) {
2218
		    my $nb = $rbhosts[$n];
2219
			if (!$ids{"$bnet1.$nb"}) {
2220
				if (!($interfaces =~ m/$extip.+\n.+inet addr:$bnet1\.$nb/)) {
2221
					$nextip = "$bnet1.$nb";
2222
					last;
2223
				}
2224
			}
2225
		}
2226
	}
2227
	$postreply .= "Status=ERROR No more ($oc) external IPs available\n" unless ($nextip);
2228
	return $nextip;
2229
}
2230

    
2231
sub ip2domain {
2232
    my $ip = shift;
2233
    my $ruuid;
2234
    if ($ip) {
2235
        my @regkeys = (tied %register)->select_where("internalip = '$ip' OR externalip = '$ip'");
2236
        foreach my $k (@regkeys) {
2237
            my $valref = $register{$k};
2238
            if ($valref->{'internalip'} eq $ip || $valref->{'externalip'} eq $ip) {
2239
                $ruuid = $valref->{'domains'};
2240
                last;
2241
            }
2242
        }
2243
    }
2244
    return $ruuid;
2245
}
2246

    
2247
sub getNextInternalIP {
2248
	my $intip = shift;
2249
	my $uuid = shift;
2250
	my $id = shift;
2251
	my $username = shift;
2252
	$username = $user unless ($username);
2253
	my $nextip = "";
2254
	my $intipnum;
2255
	my $subnet;
2256
	my %ids;
2257
    my $ping = Net::Ping->new();
2258

    
2259
    $id = getNextId() unless ($id);
2260
    my $idleft = ($id>99)?(substr $id,0,-2)+0 : 0;
2261
    my $idright = (substr $id,-2) + 0;
2262
    $intip = "10.$idleft.$idright.0" if (!$intip || $intip eq '--');
2263
    
2264
    return '' unless ($intip =~ m/(\d+\.\d+\.\d+)\.(\d+)/ );
2265
    $subnet = $1;
2266
    $intipnum = $2;
2267

    
2268
	# First create hash of IP's reserved by other servers in DB
2269
	my @regvalues = values %register;
2270
	foreach my $val (@regvalues) {
2271
    	if ($val->{'user'} eq $username) {
2272
            my $ip = $val->{'internalip'} ;
2273
            $ids{$ip} = $val->{'uuid'};
2274
		}
2275
	}
2276

    
2277
	if ($intipnum && $intipnum>1 && $intipnum<255) {
2278
	# An internal ip was supplied - check if it's free, if not keep the ip already registered in the db
2279
        if (!$ids{$intip}
2280
#            && !($ping->ping($intip, 0.1)) # 0.1 secs timeout, check if ip is in use, possibly on another engine
2281
            && !(`arping -C1 -c2 -D -I $datanic.$id $intip` =~ /reply from/)  # check if ip is created on another engine
2282
        ) {
2283
            $nextip = $intip;
2284
        } else {
2285
            $nextip = $register{$uuid}->{'internalip'}
2286
        }
2287
	} else {
2288
	# Find first IP not reserved
2289
		for ($n=2; $n<255; $n++) {
2290
			if (!$ids{"$subnet.$n"}
2291
# TODO: The arping check takes too long - two networks created by the same user can too easily be assigned the same IP's
2292
#                && !(`arping -f -c2 -D -I $datanic.$id $subnet.$n` =~ /reply from/)  # check if ip is created on another engine
2293
			) {
2294
                $nextip = "$subnet.$n";
2295
                last;
2296
			}
2297
		}
2298
	}
2299
	$postreply .= "Status=ERROR No more internal IPs available\n" if (!$nextip);
2300
	return $nextip;
2301
}
2302

    
2303
sub validateStatus {
2304
    my $valref = shift;
2305

    
2306
    my $interfaces = `/sbin/ifconfig`;
2307
    my $uuid = $valref->{'uuid'};
2308
    my $type = $valref->{'type'};
2309
    my $id = $valref->{'id'};
2310
    my $idleft = ($id>99)?(substr $id,0,-2)+0 : 0;
2311
    my $idright = (substr $id,-2) + 0;
2312

    
2313
    ( $valref->{'domains'}, $valref->{'domainnames'} ) = getDomains($uuid);
2314
    my ( $systems, $systemnames ) = getSystems($uuid);
2315
    my $extip = $valref->{'externalip'};
2316
    my $intip = $valref->{'internalip'};
2317

    
2318
    if ($type eq "gateway") {
2319
        $valref->{'internalip'} = "10.$idleft.$idright.1" if ($id>1);
2320
    } else {
2321
        $type = "gateway";
2322
        if ($intip && $intip ne "--" && $extip && $extip ne "--") {
2323
            $type = "ipmapping";
2324
        } elsif ($intip && $intip ne "--") {
2325
            $type = "internalip";
2326
        } elsif ($extip && $extip ne "--") {
2327
            $type = "externalip";
2328
        }
2329
        $valref->{'type'} = $type;
2330
    }
2331

    
2332
    $valref->{'status'} = "down";
2333
    my $nat;
2334
    if ($id == 0 || $id == 1) {
2335
        $valref->{'status'} = "nat";
2336
    # Check if vlan $id is created (and doing nat)
2337
#    } elsif ($interfaces =~ m/$datanic\.$id.+\n.+10\.$idleft\.$idright\.1/) {
2338
    } elsif (-e "/proc/net/vlan/$datanic.$id") {
2339
        $nat = 1;
2340
    }
2341

    
2342
    if (($type eq "internalip" || $type eq "ipmapping")) { # && $val->{'domains'}) {
2343
        $valref->{'status'} = "nat" if ($nat);
2344
        my $dhcprunning;
2345
        my $dhcpconfigured;
2346
        eval {
2347
            my $psid;
2348
            $psid = `/bin/cat /var/run/stabile-$id.pid` if (-e "/var/run/stabile-$id.pid");
2349
            chomp $psid;
2350
            $dhcprunning = -e "/proc/$psid" if ($psid);
2351
            my $dhcphosts;
2352
            $dhcphosts = lc `/bin/cat $etcpath/dhcp-hosts-$id` if (-e "$etcpath/dhcp-hosts-$id");
2353
            $dhcpconfigured = ($dhcphosts =~ /$intip/);
2354
            1;
2355
        } or do {;};
2356

    
2357
        if ($type eq "internalip") {
2358
        # Check if external ip has been created and dhcp is ok
2359
            if ($nat && (($dhcprunning && $dhcpconfigured) || $systems)) {
2360
                $valref->{'status'} = "up";
2361
            }
2362
        } elsif ($type eq "ipmapping") {
2363
        # Check if external ip has been created, dhcp is ok and vlan interface is created
2364
        # An ipmapping linked to a system is considered up if external interface exists
2365
            if ($nat && $interfaces =~ m/$extip/ && (($dhcprunning && $dhcpconfigured) || $systems)) {
2366
                $valref->{'status'} = "up";
2367
            }
2368
        }
2369

    
2370
    } elsif ($type eq "externalip") {
2371
        my $dhcprunning;
2372
        my $dhcpconfigured;
2373
        eval {
2374
            my $psid;
2375
            $psid = `/bin/cat /var/run/stabile-$id.pid` if (-e "/var/run/stabile-$id.pid");
2376
            chomp $psid;
2377
            $dhcprunning = -e "/proc/$psid" if ($psid);
2378
            my $dhcphosts;
2379
            $dhcphosts = `/bin/cat $etcpath/dhcp-hosts-$id` if (-e "$etcpath/dhcp-hosts-$id");
2380
            $dhcpconfigured = ($dhcphosts =~ /$extip/);
2381
            1;
2382
        } or do {;};
2383

    
2384
        my $vproxy = `/bin/cat /proc/sys/net/ipv4/conf/$datanic.$id/proxy_arp`; chomp $vproxy;
2385
        my $eproxy = `/bin/cat /proc/sys/net/ipv4/conf/$proxynic/proxy_arp`; chomp $eproxy;
2386
        my $proute = `/sbin/ip route | grep "$extip dev"`; chomp $proute;
2387
        if ($vproxy && $eproxy && $proute) {
2388
            if ((($dhcprunning && $dhcpconfigured) || $systems)) {
2389
                $valref->{'status'} = "up";
2390
            } elsif (!$valref->{'domains'}) {
2391
                $valref->{'status'} = "nat";
2392
            }
2393
        } else {
2394
            #print "$vproxy && $eproxy && $proute && $dhcprunning && $dhcpconfigured :: $extip\n";        
2395
        }
2396

    
2397
    } elsif ($type eq "gateway") {
2398
        if ($nat || $id == 0 || $id == 1) {$valref->{'status'} = "up";}
2399
    }
2400
    return $valref->{'status'};
2401
}
2402

    
2403
sub trim{
2404
   my $string = shift;
2405
   $string =~ s/^\s+|\s+$//g;
2406
   return $string;
2407
}
2408

    
2409
sub overQuotas {
2410
    my $reqips = shift; # number of new ip's we are asking for
2411
	my $usedexternalips = 0;
2412
	my $overquota = 0;
2413
    return $overquota if ($Stabile::userprivileges =~ /a/); # Don't enforce quotas for admins
2414

    
2415
	my $externalipquota = $Stabile::userexternalipquota;
2416
	if (!$externalipquota) {
2417
        $externalipquota = $Stabile::config->get('EXTERNAL_IP_QUOTA');
2418
    }
2419

    
2420
	my $rxquota = $Stabile::userrxquota;
2421
	if (!$rxquota) {
2422
        $rxquota = $Stabile::config->get('RX_QUOTA');
2423
    }
2424

    
2425
	my $txquota = $Stabile::usertxquota;
2426
	if (!$txquota) {
2427
        $txquota = $Stabile::config->get('TX_QUOTA');
2428
    }
2429

    
2430
    my @regkeys = (tied %register)->select_where("user = '$user'");
2431
	foreach my $k (@regkeys) {
2432
	    my $val = $register{$k};
2433
		if ($val->{'user'} eq $user && $val->{'externalip'} && $val->{'externalip'} ne "--" ) {
2434
		    $usedexternalips += 1;
2435
		}
2436
	}
2437
	if ((($usedexternalips + $reqips) > $externalipquota) && $externalipquota > 0) { # -1 means no quota
2438
	    $overquota = $usedexternalips;
2439
	} elsif ($rx > $rxquota*1024 && $rxquota > 0) {
2440
	    $overquota = -1;
2441
	} elsif ($tx > $txquota*1024 && $txquota > 0) {
2442
	    $overquota = -2;
2443
	}
2444
	return $overquota;
2445
}
2446

    
2447
sub updateBilling {
2448
    my $event = shift;
2449
    my %billing;
2450
    my @regkeys = (tied %register)->select_where("user = '$user' or user = 'common'") unless ($fulllist);
2451
    foreach my $k (@regkeys) {
2452
        my $valref = $register{$k};
2453
        my %val = %{$valref}; # Deference and assign to new array, effectively cloning object
2454
        if ($val{'user'} eq $user && ($val{'type'} eq 'ipmapping' || $val{'type'} eq 'externalip') && $val{'externalip'} ne '--') {
2455
            $billing{$val{'id'}}->{'externalip'} += 1;
2456
        }
2457
    }
2458

    
2459
    my %billingreg;
2460
    my $monthtimestamp = timelocal(0,0,0,1,$mon,$year); #$sec,$min,$hour,$mday,$mon,$year
2461

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

    
2464
    my $rx_bytes_total = 0;
2465
    my $tx_bytes_total = 0;
2466

    
2467
    my $prevmonth = $month-1;
2468
    my $prevyear = $year;
2469
    if ($prevmonth == 0) {$prevmonth=12; $prevyear--;};
2470
    $prevmonth = substr("0" . $prevmonth, -2);
2471
    my $prev_rx_bytes_total = 0;
2472
    my $prev_tx_bytes_total = 0;
2473

    
2474
    foreach my $id (keys %billing) {
2475
        my $b = $billing{$id};
2476
        my $externalip = $b->{'externalip'};
2477
        my $externalipavg = 0;
2478
        my $startexternalipavg = 0;
2479
        my $starttimestamp = $current_time;
2480
        my $rx_bytes = 0;
2481
        my $tx_bytes = 0;
2482
        my $rx_stats = "/sys/class/net/$datanic.$id/statistics/rx_bytes";
2483
        my $tx_stats = "/sys/class/net/$datanic.$id/statistics/tx_bytes";
2484
        $rx_bytes = `/bin/cat $rx_stats` if (-e $rx_stats);
2485
        chomp $rx_bytes;
2486
        $tx_bytes = `/bin/cat $tx_stats` if (-e $tx_stats);
2487
        chomp $tx_bytes;
2488

    
2489
        if ($current_time - $monthtimestamp < 4*3600) {
2490
            $starttimestamp = $monthtimestamp;
2491
            $externalipavg = $externalip;
2492
            $startexternalipavg = $externalip;
2493
        }
2494

    
2495
        my $bill = $billingreg{"$user-$id-$year-$month"};
2496
        my $regrx_bytes = $bill->{'rx'};
2497
        my $regtx_bytes = $bill->{'tx'};
2498
        $rx_bytes += $regrx_bytes if ($regrx_bytes > $rx_bytes); # Network interface was reloaded
2499
        $tx_bytes += $regtx_bytes if ($regtx_bytes > $tx_bytes); # Network interface was reloaded
2500

    
2501
        # Update timestamp and averages on existing row
2502
        if ($billingreg{"$user-$id-$year-$month"}) {
2503
            $startexternalipavg = $bill->{'startexternalipavg'};
2504
            $starttimestamp = $bill->{'starttimestamp'};
2505

    
2506
            $externalipavg = ($startexternalipavg*($starttimestamp - $monthtimestamp) + $externalip*($current_time - $starttimestamp)) /
2507
                            ($current_time - $monthtimestamp);
2508

    
2509
            $billingreg{"$user-$id-$year-$month"}->{'externalip'} = $externalip;
2510
            $billingreg{"$user-$id-$year-$month"}->{'externalipavg'} = $externalipavg;
2511
            $billingreg{"$user-$id-$year-$month"}->{'timestamp'} = $current_time;
2512
            $billingreg{"$user-$id-$year-$month"}->{'rx'} = $rx_bytes;
2513
            $billingreg{"$user-$id-$year-$month"}->{'tx'} = $tx_bytes;
2514
        }
2515

    
2516
        # No row found or something happened which justifies writing a new row
2517
        if (!$billingreg{"$user-$id-$year-$month"}
2518
        || ($b->{'externalip'} != $bill->{'externalip'})
2519
        ) {
2520

    
2521
            my $inc = 0;
2522
            if ($billingreg{"$user-$id-$year-$month"}) {
2523
                $startexternalipavg = $externalipavg;
2524
                $starttimestamp = $current_time;
2525
                $inc = $bill->{'inc'};
2526
            }
2527
            # Write a new row
2528
            $billingreg{"$user-$id-$year-$month"} = {
2529
                externalip=>$externalip+0,
2530
                externalipavg=>$externalipavg,
2531
                startexternalipavg=>$startexternalipavg,
2532
                timestamp=>$current_time,
2533
                starttimestamp=>$starttimestamp,
2534
                event=>$event,
2535
                inc=>$inc+1,
2536
                rx=>$rx_bytes,
2537
                tx=>$tx_bytes
2538
            };
2539
        }
2540

    
2541
        $rx_bytes_total += $rx_bytes;
2542
        $tx_bytes_total += $tx_bytes;
2543
        my $prevbill = $billingreg{"$user-$id-$prevyear-$prevmonth"};
2544
        $prev_rx_bytes_total += $prevbill->{'rx'};
2545
        $prev_tx_bytes_total += $prevbill->{'tx'};
2546
    }
2547
    untie %billingreg;
2548
    $rx = ($rx_bytes_total>$prev_rx_bytes_total)?$rx_bytes_total - $prev_rx_bytes_total:$rx_bytes_total;
2549
    $tx = ($tx_bytes_total>$prev_tx_bytes_total)?$tx_bytes_total - $prev_tx_bytes_total:$tx_bytes_total;
2550
    my $oq = overQuotas();
2551
    if ($oq && $oq<0) {
2552
        foreach my $id (keys %billing) {
2553
            $main::syslogit->($user, 'info', "$user over rx/tx quota ($oq) stopping network $id");
2554
            Stop($id, 'stop');
2555
        }
2556
    }
2557
}
2558

    
2559
sub Bit2netmask {
2560
	my $netbit = shift;
2561
	my $_bit         = ( 2 ** (32 - $netbit) ) - 1;
2562
	my ($full_mask)  = unpack( "N", pack( "C4", split(/./, '255.255.255.255') ) );
2563
	my $netmask      = join( '.', unpack( "C4", pack( "N", ( $full_mask ^ $_bit ) ) ) );
2564
	return $netmask;
2565
}
(3-3/9)