Project

General

Profile

Download (105 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
            if ($filter || $statusfilter || $uuidfilter) { # List filtered networks
279
                my $fmatch;
280
                my $smatch;
281
                my $umatch;
282
                $fmatch = 1 if (!$filter || $val{'name'}=~/$filter/i);
283
                $smatch = 1 if (!$statusfilter || $statusfilter eq 'all'
284
                        || $statusfilter eq $val{'status'}
285
                        );
286
                $umatch = 1 if ($val{'uuid'} eq $uuidfilter);
287
                if ($fmatch && $smatch && !$uuidfilter) {
288
                    push @curregvalues,\%val;
289
                } elsif ($umatch) {
290
                    push @curregvalues,\%val;
291
                    last;
292
                }
293

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

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

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

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

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

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

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

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

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

    
451
}
452

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

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

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

    
477

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    
855
END
856
;
857
}
858

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

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

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

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

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

    
910
        print TEMP1 <<END;
911
tag:external,option:router,$proxygw
912
tag:external,option:netmask,$proxymask
913
tag:external,option:dns-server,$proxyip
914
tag:internal,option:router,$gateway
915
tag:internal,option:netmask,255.255.255.0
916
tag:internal,option:dns-server,$gateway
917
option:dns-server,1.1.1.1
918
END
919

    
920
        close (TEMP1);
921
#    }
922

    
923
    if ($running) {
924
        $main::syslogit->($user, 'info', "HUPing dnsmasq 1: $id");
925
        eval {`/usr/bin/pkill -HUP -f "stabile-$id.pid"`; 1;} or do {$error .= "Status=ERROR Problem configuring dhcp for $name $@\n";};
926
    } else {
927
        eval {`/usr/sbin/dnsmasq $options`;1;} or do {$error .= "Status=ERROR Problem configuring dhcp for $name $@\n";};
928
    }
929

    
930
    return $error?$error:"OK";
931
}
932

    
933
sub removeDHCPAddress {
934
    my $id = shift;
935
    my $uuid = shift;
936
    my $dhcpip = shift;
937
    my $mac;
938
    $mac = lc $domreg{$uuid}->{'nicmac1'} if ($domreg{$uuid});
939
    my $isexternal = ($dhcpip =~ /^10\./);
940
    my $running;
941
    my $error;
942
    my $psid;
943
    return "Status=ERROR Empty mac or ip when configuring dhcp for $name" unless ($mac || $dhcpip);
944

    
945
    eval {
946
        $psid = `/bin/cat /var/run/stabile-$id.pid` if (-e "/var/run/stabile-$id.pid");
947
        chomp $psid;
948
        $running = -e "/proc/$psid" if ($psid);
949
        1;
950
    } or do {$error .= "Status=ERROR Problem deconfiguring dhcp for $name $@\n";};
951

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

    
977
    if ($keepup) {
978
        if ($running) {
979
            $main::syslogit->($user, 'info', "HUPing dnsmasq 2: $id");
980
            eval {`/usr/bin/pkill -HUP -f "stabile-$id.pid"`; 1;} or do {$error .= "Status=ERROR Problem configuring dhcp for $name $@\n";};
981
        }
982
    } else {
983
        unlink "$etcpath/dhcp-options-$id" if (-e "$etcpath/dhcp-options-$id");
984
        if ($running) {
985
            # Take down dhcp server
986
            $main::syslogit->($user, 'info', "Killing dnsmasq 3: $id");
987
            eval {`/usr/bin/pkill -f "stabile-$id.pid"`; 1;} or do {$error .= "Status=ERROR Problem configuring dhcp for $name $@\n";};
988
        }
989
    }
990

    
991
    return $error?$error:"OK";
992
}
993

    
994
# Helper function
995
sub save {
996
    my ($id, $uuid, $name, $status, $type, $internalip, $externalip, $ports, $buildsystem, $username) = @_;
997
    my $obj = {
998
        id => $id,
999
        uuid => $uuid,
1000
        name => $name,
1001
        status => $status,
1002
        type => $type,
1003
        internalip => $internalip,
1004
        externalip => $externalip,
1005
        ports => $ports,
1006
        buildsystem => $buildsystem,
1007
        username => $username
1008
    };
1009
    return Save($uuid, 'save', $obj);
1010
}
1011

    
1012
sub Save {
1013
    my ($uuid, $action, $obj) = @_;
1014
    if ($help) {
1015
        return <<END
1016
POST:uuid, id, name, internalip, externalip, ports, type, systems, activate:
1017
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.
1018
Depending on your privileges not all changes are permitted. If you save without specifying a uuid, a new network is created.
1019
For now, [activate] only has effect when creating a new connection with a linked system/server.
1020
END
1021
    }
1022
    $uuid = $obj->{'uuid'} if ($obj->{'uuid'});
1023
    my $id = $obj->{id};
1024
    my $name = $obj->{name};
1025
    my $status = $obj->{status};
1026
    my $type = $obj->{type};
1027
    my $internalip = $obj->{internalip};
1028
    my $externalip = $obj->{externalip};
1029
    my $ports = $obj->{ports};
1030
    my $buildsystem = $obj->{buildsystem};
1031
    my $username = $obj->{username};
1032
    my $systems = $obj->{systems}; # Optionally link this network to a system
1033

    
1034
    $postreply = "" if ($buildsystem);
1035
	$username = $user unless ($username);
1036

    
1037
    my $regnet = $register{$uuid};
1038
    $status = $regnet->{'status'} || $status; # Trust db status if it exists
1039
    if ((!$uuid && $uuid ne '0') && $status eq 'new') {
1040
        my $ug = new Data::UUID;
1041
        $uuid = $ug->create_str();
1042
    };
1043
    if ($status eq 'new') {
1044
        $name  = 'New Connection' unless ($name);
1045
    }
1046
    unless ($uuid && length $uuid == 36) {
1047
        $postreply .= "Status=Error Invalid uuid $uuid\n";
1048
        return $postreply;
1049
    }
1050
    my $systemnames = $regnet->{'systemnames'};
1051

    
1052
    my $dbid = 0+$regnet->{'id'};
1053
    if ($status eq 'new' || !$dbid) {
1054
        $id = getNextId($id) ;
1055
    } else {
1056
        $id = $dbid;
1057
    }
1058
    if ($id > 4095 || $id < 0 || ($id==0 && $uuid!=0 && $isadmin) || ($id==1 && $uuid!=1 && $isadmin)) {
1059
        $postreply .= "Status=ERROR Invalid network id $id\n";
1060
        return $postreply;
1061
    }
1062
    $name = $name || $regnet->{'name'};
1063
    $internalip = $internalip || $regnet->{'internalip'};
1064
    if (!($internalip =~ /\d+\.\d+\.\d+\.\d+/)) {$internalip = ''};
1065
    $externalip = $externalip || $regnet->{'externalip'};
1066
    $ports = $ports || $regnet->{'ports'};
1067
    my $reguser = $regnet->{'user'};
1068
    # Sanity checks
1069
    if (
1070
        ($name && length $name > 255)
1071
        || ($ports && length $ports > 255)
1072
        || ($type && !($type =~ /gateway|ipmapping|internalip|externalip/))
1073
    ) {
1074
        $postreply .= "Stroke=ERROR Bad data: $name, $ports, $type\n";
1075
        return $postreply;
1076
    }
1077
    # Security check
1078
    if (($reguser && $username ne $reguser && !$isadmin ) ||
1079
        ($reguser && $status eq "new"))
1080
    {
1081
        $postreply .= "Status=Error Bad user: $username ($status)\n";
1082
        return $postreply;
1083
    }
1084

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

    
1127
            } elsif ($type eq "ipmapping") {
1128
                $externalip = getNextExternalIP($externalip, $uuid);
1129
                if (!$externalip) {
1130
                    $postreply .= "Status=ERROR Unable to allocate external IP for $name\n";
1131
                    $externalip = "--";
1132
                    $type = "internalip";
1133
                } else {
1134
                    $postreply .= "Status=OK Allocated external IP: $externalip\n" unless ($regnet->{'externalip'} eq $externalip);
1135
                    if ($dodns) {
1136
                        $postreply .= "Status=OK Trying to register DNS ";
1137
                        $main::dnsCreate->($engineid, $externalip, $externalip, 'A', $user);
1138
                    }
1139
                }
1140
                $internalip = getNextInternalIP($internalip, $uuid, $id);
1141
                if (!$internalip) {
1142
                    $postreply .= "Status=ERROR Unable to allocate internal IP for $name\n";
1143
                    $internalip = "--";
1144
                    $type = "gateway";
1145
                } else {
1146
                    $postreply .= "Status=OK Allocated internal IP: $internalip for $name\n" unless ($regnet->{'internalip'} eq $internalip);
1147
                }
1148

    
1149
            } elsif ($type eq "internalip") {
1150
                $externalip = "--";
1151
                $ports = "--";
1152
                my $ointip = $internalip;
1153
                $internalip = getNextInternalIP($internalip, $uuid, $id);
1154
                if (!$internalip) {
1155
                    $postreply .= "Status=ERROR Unable to allocate internal IP $internalip ($id, $uuid, $ointip) for $name\n";
1156
                    $internalip = "--";
1157
                    $type = "gateway";
1158
                } else {
1159
                    $postreply .= "Status=OK Allocated internal IP: $internalip for $name\n" unless ($regnet->{'internalip'} eq $internalip);
1160
                }
1161

    
1162
            } elsif ($type eq "gateway") {
1163
            #    $internalip = "--";
1164
            #    $externalip = "--";
1165
            #    $ports = "--";
1166
            } else {
1167
                $postreply .= "Status=ERROR Network must have a valid type\n";
1168
                return $postreply;
1169
            }
1170
            # Validate ports
1171
            my @portslist = split(/, ?| /, $ports);
1172
            if ($ports ne "--") {
1173
                foreach my $port (@portslist) {
1174
                    my $p = $port; # Make a copy of var
1175
                    if ($p =~ /(\d+\.\d+\.\d+\.\d+):(\d+)/) {
1176
                        $p = $2;
1177
                    };
1178
                    $p = 0 unless ($p =~ /\d+/);
1179
                    if ($p<1 || $p>65535) {
1180
                        $postreply .= "Status=ERROR Invalid port mapping for $name\n";
1181
                        $ports = "--";
1182
                        last;
1183
                    }
1184
                }
1185
            }
1186
            if ($ports ne "--") {
1187
                $ports = join(',', @portslist);
1188
            }
1189
            if ($systems ne $regnet->{'systems'}) {
1190
                my $regsystems = $regnet->{'systems'};
1191
                unless (tie(%sysreg,'Tie::DBI', Hash::Merge::merge({table=>'systems'}, $Stabile::dbopts)) ) {$res .= qq|{"status": "Error": "message": "Unable to access systems register"}|; return $res;};
1192

    
1193
                # Remove existing link to system
1194
                if ($sysreg{$regsystems}) {
1195
                    $sysreg{$regsystems}->{'networkuuids'} =~ s/$uuid,? ?//;
1196
                    $sysreg{$regsystems}->{'networknames'} = s/$regnet->{'name'},? ?//;
1197
                } elsif ($domreg{$regsystems}) {
1198
                    $domreg{$regsystems}->{'networkuuids'} =~ s/$uuid,? ?//;
1199
                    $domreg{$regsystems}->{'networknames'} = s/$regnet->{'name'},? ?//;
1200
                }
1201
                if ($systems) {
1202
                    if ($sysreg{$systems}) { # Add new link to system
1203
                        $sysreg{$systems}->{'networkuuids'} .= (($sysreg{$systems}->{'networkuuids'}) ? ',' : '') . $uuid;
1204
                        $sysreg{$systems}->{'networknames'} .= (($sysreg{$systems}->{'networknames'}) ? ',' : '') . $name;
1205
                        $systemnames = $sysreg{$systems}->{'name'};
1206
                    } elsif ($domreg{$systems}) {
1207
                        $domreg{$systems}->{'networkuuids'} .= (($domreg{$systems}->{'networkuuids'}) ? ',' : '') . $uuid;
1208
                        $domreg{$systems}->{'networknames'} .= (($domreg{$systems}->{'networknames'}) ? ',' : '') . $name;
1209
                        $systemnames = $domreg{$systems}->{'name'};
1210
                    } else {
1211
                        $systems = '';
1212
                    }
1213
                }
1214
                tied(%sysreg)->commit;
1215
                untie(%sysreg);
1216
            }
1217
            $register{$uuid} = {
1218
                uuid=>$uuid,
1219
                user=>$username,
1220
                id=>$id,
1221
                name=>$name,
1222
                internalip=>$internalip,
1223
                externalip=>$externalip,
1224
                ports=>$ports,
1225
                type=>$type,
1226
                systems=>$systems,
1227
                systemnames=>$systemnames,
1228
                action=>""
1229
            };
1230
            my $res = tied(%register)->commit;
1231
            my $obj = $register{$uuid};
1232
            $postreply .= "Status=OK Network $register{$uuid}->{'name'} saved: $uuid\n";
1233
            $postreply .= "Status=OK uuid: $uuid\n" if ($console && $status eq 'new');
1234
            if ($status eq 'new') {
1235
                validateStatus($register{$uuid});
1236
                $postmsg = "Created connection $name";
1237
                $uiupdatetype = "update";
1238
            }
1239
            updateBilling("allocate $externalip") if (($type eq "ipmapping" || $type eq "externalip") && $externalip && $externalip ne "--");
1240

    
1241
        } else {
1242
        	$postreply = "Status=OK Network $uuid ($id) unchanged\n";
1243
        }
1244

    
1245
        if ($params{'PUTDATA'}) {
1246
            my %jitem = %{$register{$uuid}};
1247
            my $json_text = to_json(\%jitem);
1248
            $json_text =~ s/null/"--"/g;
1249
            $json_text =~ s/""/"--"/g;
1250
            $postreply = $json_text;
1251
            $postmsg = $postmsg || "OK, updated network $name";
1252
        }
1253

    
1254
        return $postreply;
1255

    
1256
    } else {
1257
        if ($id ne $regnet->{'id'} ||
1258
        $internalip ne $regnet->{'internalip'} || $externalip ne $regnet->{'externalip'}) {
1259
            return "Status=ERROR Cannot modify active network: $uuid\n";
1260
        } elsif ($name ne $regnet->{'name'}) {
1261
            $register{$uuid}->{'name'} = $name;
1262
            $postreply .= "Status=OK Network \"$register{$uuid}->{'name'}\" saved: $uuid\n";
1263
            if ($params{'PUTDATA'}) {
1264
                my %jitem = %{$register{$uuid}};
1265
                my $json_text = to_json(\%jitem);
1266
                $json_text =~ s/null/"--"/g;
1267
                $postreply = $json_text;
1268
                $postmsg = "OK, updated network $name";
1269
            }
1270
        } else {
1271
            $postreply .= "Status=OK Nothing to save\n";
1272
            if ($params{'PUTDATA'}) {
1273
                my %jitem = %{$register{$uuid}};
1274
                my $json_text = to_json(\%jitem);
1275
                $json_text =~ s/null/"--"/g;
1276
                $postreply = $json_text;
1277
            }
1278
        }
1279
    }
1280

    
1281
}
1282

    
1283
sub Activate {
1284
    my ($uuid, $action, $obj) = @_;
1285
    if ($help) {
1286
        return <<END
1287
GET:uuid:
1288
Activate a network which must be in status down or nat.
1289
END
1290
    }
1291
    $uuid = $obj->{'uuid'} if ($obj->{'uuid'});
1292
    $action = 'activate' || $action;
1293
    my $regnet = $register{$uuid};
1294
    my $id = $regnet->{'id'};
1295
    my $name = $regnet->{'name'};
1296
    my $type = $regnet->{'type'};
1297
    my $status = $regnet->{'status'};
1298
    my $domains = $regnet->{'domains'};
1299
    my $systems = $regnet->{'systems'};
1300
    my $internalip = $regnet->{'internalip'};
1301
    my $externalip = $regnet->{'externalip'};
1302
    my $ports = $regnet->{'ports'};
1303
    my $idleft = ($id>99)?(substr $id,0,-2)+0 : 0;
1304
    my $idright = (substr $id,-2) + 0;
1305
    my $interfaces = `/sbin/ifconfig`;
1306
    my $dom = $domreg{$domains};
1307
    my $nicindex = ($dom->{'networkuuid1'} eq $uuid)?1:
1308
            ($dom->{'networkuuid2'} eq $uuid)?2:
1309
            ($dom->{'networkuuid3'} eq $uuid)?3:
1310
            0;
1311
    my $nicmac = $dom->{"nicmac$nicindex"};
1312
    my $e;
1313

    
1314
	if (!$id || $id==0 || $id==1 || $id>4095) {
1315
        $postreply .= "Status=ERROR Invalid ID activating $type\n";
1316
	    return $postreply;
1317
	} elsif (overQuotas()) { # Enforce quotas
1318
        $postreply .= "Status=ERROR Over quota activating $type " . overQuotas() . "\n";
1319
        return $postreply;
1320
    } elsif (($status ne 'down' && $status ne 'nat')) {
1321
        $postreply .= "Status=ERROR Cannot activate $type $name (current status is: $status)\n";
1322
        return $postreply;
1323
    }
1324

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

    
1362
            # Also export storage pools to user's network
1363
            my @spl = split(/,\s*/, $storagepools);
1364
            my $reloadnfs;
1365
            my $uid = `id -u irigo-$user`; chomp $uid;
1366
            $uid = `id -u nobody` unless ($uid =~ /\d+/); chomp $uid;
1367
            my $gid = `id -g irigo-$user`; chomp $gid;
1368
            $gid = `id -g nobody` unless ($gid =~ /\d+/); chomp $gid;
1369

    
1370
            # We are dealing with multiple upstream routes - configure local routing
1371
            if ($proxynic && $proxynic ne $extnic) {
1372
                if (-e "/etc/iproute2/rt_tables" && !grep(/1 proxyarp/, `cat /etc/iproute2/rt_tables`)) {
1373
                    `/bin/echo "1 proxyarp" >> /etc/iproute2/rt_tables`;
1374
                }
1375
                if (!grep(/$datanic\.$id/, `/sbin/ip route show table proxyarp`)) {
1376
                    `/sbin/ip route add "10.$idleft.$idright.0/24" dev $datanic.$id table proxyarp`;
1377
                }
1378
            }
1379

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

    
1410
        } elsif ($type eq "externalip") {
1411
            # A proxy is needed to route traffic, don't go any further if not configured
1412
            if ($proxyip) {
1413
                # Set up proxy
1414
                if (!($interfaces =~ m/$proxyip/ && $interfaces =~ m/br$id:proxy/)) {
1415
                    eval {`/sbin/ifconfig br$id:proxy $proxyip/$proxysubnet up`; 1;}
1416
                        or do {$e=1; $postreply .= "Status=ERROR Problem setting up proxy arp gw $datanic.$id $@\n";};
1417
                    eval {`/sbin/ifconfig $proxynic:proxy $proxyip/$proxysubnet up`; 1;}
1418
                        or do {$e=1; $postreply .= "Status=ERROR Problem setting up proxy arp gw $proxynic $@\n";};
1419
                }
1420
                my $result = "OK";
1421
                # Configure dhcp server
1422
                if ($domains) {
1423
                    $result = addDHCPAddress($id, $domains, $externalip, "10.$idleft.$idright.1", $nicmac) if ($domains);
1424
                    if ($result eq "OK") {
1425
                        ;
1426
                    } else {
1427
                        $e = 1;
1428
                        $postreply .= "$result\n";
1429
                    }
1430
                }
1431
            } else {
1432
                $postreply .= "Status=ERROR Cannot set up external IP without Proxy ARP gateway\n";
1433
            }
1434
        }
1435

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

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

    
1511
                # When receiving packet from client, if it's been routed, and outgoing interface is the external interface, SNAT.
1512
                unless ($Stabile::disablesnat) {
1513
                    eval {`/sbin/iptables -A POSTROUTING -t nat -s $internalip ! -d 10.$idleft.$idright.0/24 -j SNAT --to-source $externalip`; 1; }
1514
                        or do {$e=4; $postreply .= "Status=ERROR Problem setting up routing $@\n";};
1515
                #    eval {`/sbin/iptables -A POSTROUTING -t nat -s $internalip -j SNAT --to-source $externalip`; 1; }
1516
                #        or do {$e=4; $postreply .= "Status=ERROR Problem setting up routing $@\n";};
1517
                    eval {`/sbin/iptables -I INPUT -t nat -s $internalip ! -d 10.$idleft.$idright.0/24 -j SNAT --to-source $externalip`; 1; }
1518
                        or do {$e=4; $postreply .= "Status=ERROR Problem setting up routing $@\n";};
1519
                #    eval {`/sbin/iptables -I INPUT -t nat -s $internalip -j SNAT --to-source $externalip`; 1; }
1520
                #        or do {$e=4; $postreply .= "Status=ERROR Problem setting up routing $@\n";};
1521
                }
1522

    
1523
                if ($e) {
1524
                    $main::syslogit->($user, 'info', "Problem $action network $uuid ($name, $id): $@");
1525
                } else {
1526
                    $astatus = "up"
1527
                }
1528
            }
1529
        } elsif ($type eq "externalip") {
1530
            my $route = `/sbin/ip route`;
1531
            my $tables = `/sbin/iptables -L -n`;
1532

    
1533
            # Allow external IP send packets out
1534
            `/sbin/iptables -D FORWARD --in-interface br$id -s $externalip -j RETURN`;
1535
            `/sbin/iptables -I FORWARD --in-interface br$id -s $externalip -j RETURN`;
1536

    
1537
            # We are dealing with multiple upstream routes - configure local routing
1538
            if ($proxynic && $proxynic ne $extnic) {
1539
                if (-e "/etc/iproute2/rt_tables" && !grep(/1 proxyarp/, `cat /etc/iproute2/rt_tables`)) {
1540
                    `/bin/echo "1 proxyarp" >> /etc/iproute2/rt_tables`;
1541
                }
1542
                if (!grep(/$proxygw/, `/sbin/ip route show table proxyarp`)) {
1543
                    `/sbin/ip route add default via $proxygw dev $proxynic table proxyarp`;
1544
                }
1545
                if (!grep(/proxyarp/, `/sbin/ip rule show`)) {
1546
                    `/sbin/ip rule add to $proxygw/$proxysubnet table main`;
1547
                    `/sbin/ip rule add from $proxygw/$proxysubnet table proxyarp`;
1548
                }
1549
                my $proxyroute = `/sbin/ip route show table proxyarp`;
1550
#                `/sbin/ip route add $externalip/32 dev $datanic.$id:proxy src $proxyip table proxyarp` unless ($proxyroute =~ /$externalip/);
1551
                `/sbin/ip route add $externalip/32 dev br$id:proxy src $proxyip table proxyarp` unless ($proxyroute =~ /$externalip/);
1552
            }
1553
            eval {`/bin/echo 1 > /proc/sys/net/ipv4/conf/$datanic.$id/proxy_arp`; 1;}
1554
                or do {$e=1; $postreply .= "Status=ERROR Problem setting up proxy arp $@\n";};
1555
            eval {`/bin/echo 1 > /proc/sys/net/ipv4/conf/$proxynic/proxy_arp`; 1;}
1556
                or do {$e=1; $postreply .= "Status=ERROR Problem setting up proxy arp $@\n";};
1557
            eval {`/sbin/ip route add $externalip/32 dev br$id:proxy src $proxyip` unless ($route =~ /$externalip/); 1;}
1558
                or do {$e=1; $postreply .= "Status=ERROR Problem setting up proxy arp $@\n";};
1559

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

    
1565

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

    
1570
            if ($ports && $ports ne "--") {
1571
                my @portslist = split(/, ?| /, $ports);
1572
                foreach $port (@portslist) {
1573
                    my $ipfilter;
1574
                    if ($port =~ /(\d+)\.(\d+)\.(\d+)\.(\d+)(\/\d+)?:(\d+)/) {
1575
                        my $portip = "$1.$2.$3.$4$5";
1576
                        $port = $6;
1577
                        $ipfilter = "-s $portip";
1578
                    } else {
1579
                        $port = 0 unless ($port =~ /\d+/);
1580
                    }
1581
                    if ($port<1 || $port>65535) {
1582
                        $postreply .= "Status=ERROR Invalid port mapping for $name\n";
1583
                        $ports = "--";
1584
                        last;
1585
                    }
1586

    
1587
                    if ($port>1 && $port<65535 && $port!=67) { # Disallow setting up a dhcp server
1588
                        eval {`/sbin/iptables -A FORWARD -p tcp -i $proxynic $portfilter -d $externalip --dport $port -j RETURN`; 1;}
1589
                            or do {$e=1; $postreply .= "Status=ERROR Problem setting up routing $@\n";};
1590
                        eval {`/sbin/iptables -A FORWARD -p udp -i $proxynic $portfilter -d $externalip --dport $port -j RETURN`; 1;}
1591
                            or do {$e=1; $postreply .= "Status=ERROR Problem setting up routing $@\n";};
1592
                    }
1593
                }
1594
                eval {`/sbin/iptables -D FORWARD -i $proxynic -d $externalip -j REJECT`; 1;} # Drop traffic to all other ports
1595
                    or do {$e=1; $postreply .= "Status=ERROR Problem setting up routing $@\n";};
1596
                eval {`/sbin/iptables -A FORWARD -i $proxynic -d $externalip -j REJECT`; 1;} # Drop traffic to all other ports
1597
                    or do {$e=1; $postreply .= "Status=ERROR Problem setting up routing $@\n";};
1598
            } else {
1599
                # First allow everything else to this ip
1600
                eval {`/sbin/iptables -D FORWARD -i $proxynic -d $externalip -j RETURN`; 1;}
1601
                    or do {$e=1; $postreply .= "Status=ERROR Problem setting up routing $@\n";};
1602
                eval {`/sbin/iptables -A FORWARD -i $proxynic -d $externalip -j RETURN`; 1;}
1603
                    or do {$e=1; $postreply .= "Status=ERROR Problem setting up routing $@\n";};
1604
                # Then disallow setting up a dhcp server
1605
                eval {`/sbin/iptables -D FORWARD -p udp -i $proxynic -d $externalip --dport 67 -j REJECT`; 1;}
1606
                    or do {$e=1; $postreply .= "Status=ERROR Problem setting up routing $@\n";};
1607
                eval {`/sbin/iptables -A FORWARD -p udp -i $proxynic -d $externalip --dport 67 -j REJECT`; 1;}
1608
                    or do {$e=1; $postreply .= "Status=ERROR Problem setting up routing $@\n";};
1609
            }
1610
        }
1611
    }
1612

    
1613
    # Allow all inter-VLAN communication
1614
    `iptables -D FORWARD --in-interface br$id --out-interface br$id -j RETURN 2>/dev/null`;
1615
    `iptables -I FORWARD --in-interface br$id --out-interface br$id -j RETURN`;
1616
    # Disallow any access to vlan except mapped from external NIC i.e. ipmappings
1617
    `iptables -D FORWARD ! --in-interface $extnic --out-interface br$id -j DROP 2>/dev/null`;
1618
    `iptables -A FORWARD ! --in-interface $extnic --out-interface br$id -j DROP`;
1619

    
1620
    # 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
1621
#    `/sbin/iptables --delete FORWARD --in-interface $datanic.$id ! -s 10.$idleft.$idright.0/24 -j DROP`;
1622
    unless ($proxynic eq "$datanic.$id") {
1623
#        `/sbin/iptables --append FORWARD --in-interface $datanic.$id ! -s 10.$idleft.$idright.0/24 -j DROP`;
1624
    }
1625

    
1626
    # Enable nat'ing
1627
    eval {
1628
        #my $masq = `/sbin/iptables -L -n -t nat`;
1629
        #        if (!($masq =~ "MASQUERADE.+all.+--.+0\.0\.0\.0/0")) {
1630
        `/sbin/iptables -D POSTROUTING -t nat --out-interface $extnic -s 10.0.0.0/8 -j MASQUERADE`;
1631
        `/sbin/iptables -A POSTROUTING -t nat --out-interface $extnic -s 10.0.0.0/8 -j MASQUERADE`;
1632
        # Christian's dev environment
1633
        #            my $interfaces = `/sbin/ifconfig`;
1634
        #            if ($interfaces =~ m/ppp0/) {
1635
        #                `/sbin/iptables --table nat --append POSTROUTING --out-interface ppp0 -s 10.0.0.0/8 -j MASQUERADE`;
1636
        #            }
1637
        #        };
1638
        1;
1639
    } or do {print "Unable to enable masquerading: $@\n";};
1640

    
1641
    $uistatus = ($e)?"":validateStatus($register{$uuid});
1642
    if ($uistatus && $uistatus ne 'down') {
1643
        $uiuuid = $uuid;
1644
        $postreply .= "Status=$uistatus OK $action $type $name\n";
1645
    } else {
1646
        $postreply .= "Status=ERROR Cannot $action $type $name ($uistatus)\n";
1647
    }
1648
    $main::syslogit->($user, 'info', "$action network $uuid ($name, $id) -> $uistatus");
1649
    updateBilling("$uistatus $uuid ($id)");
1650
    # $main::updateUI->({tab=>"networks", user=>$user, uuid=>$uiuuid, status=>$uistatus}) if ($uistatus);
1651
    return $postreply;
1652
}
1653

    
1654
sub Removeusernetworks {
1655
    my $username = shift;
1656
    return unless (($isadmin || $user eq $username) && !$isreadonly);
1657
    $user = $username;
1658
    foreach my $uuid (keys %register) {
1659
        if ($register{$uuid}->{'user'} eq $user) {
1660
            $postreply .=  "Removing network $register{$path}->{'name'}, $uuid" . ($console?'':'<br>') . "\n";
1661
            Deactivate($uuid);
1662
            Remove('remove', $uuid);
1663
        }
1664
    }
1665
}
1666

    
1667
sub Remove {
1668
    my ($uuid, $action, $obj) = @_;
1669
    if ($help) {
1670
        return <<END
1671
DELETE:uuid,force:
1672
Delete a network which must be in status down or nat and should not be used by any servers, or linked to any stacks.
1673
May also be called with endpoints "/stabile/[uuid]" or "/stabile?uuid=[uuid]"
1674
Set [force] to remove even if linked to a system.
1675
END
1676
    }
1677
    $uuid = $obj->{'uuid'} if ($curuuid && $obj->{'uuid'}); # we are called from a VM with an ip address as target
1678
    my $force = $obj->{'force'};
1679
    ( my $domains, my $domainnames ) = getDomains($uuid);
1680
    ( my $systems, my $systemnames ) = getSystems($uuid);
1681

    
1682
    if ($register{$uuid}) {
1683
        my $id = $register{$uuid}->{'id'};
1684
        my $name = $register{$uuid}->{'name'};
1685
        utf8::decode($name);
1686
        my $status = $register{$uuid}->{'status'};
1687
        my $type = $register{$uuid}->{'type'};
1688
        my $internalip = $register{$uuid}->{'internalip'};
1689
        my $externalip = $register{$uuid}->{'externalip'};
1690

    
1691
        my @regvalues = values %register;
1692
        if (
1693
            $id!=0 && $id!=1 && (!$domains || $domains eq '--')
1694
                && ((!$systems || $systems eq '--' || $force)
1695
                # allow internalip's to be removed if active and only linked, i.e. not providing dhcp
1696
                || ($status eq 'down' || $status eq 'new' || $status eq 'nat' || ($type eq 'internalip' && $systems && $systems ne '--')))
1697
        ) {
1698
            # Deconfigure internal dhcp server and DNS
1699
            if ($type eq "internalip") {
1700
                my $result =  removeDHCPAddress($id, $domains, $internalip);
1701
                $postreply .= "$result\n" unless $result eq "OK";
1702
            } elsif ($type eq "ipmapping") {
1703
                my $result =  removeDHCPAddress($id, $domains, $internalip);
1704
                $postreply .= "$result\n" unless $result eq "OK";
1705
                if ($dodns) {
1706
                    $main::dnsDelete->($engineid, $externalip) if ($enginelinked);
1707
                }
1708
            } elsif ($type eq "externalip") {
1709
                my $result =  removeDHCPAddress($id, $domains, $externalip);
1710
                $postreply .= "$result\n" unless $result eq "OK";
1711
                if ($dodns) {
1712
                    $main::dnsDelete->($engineid, $externalip) if ($enginelinked);
1713
                }
1714
            }
1715
            if ($status eq 'nat') {
1716
                # Check if last network in vlan. If so take it down
1717
                my $notlast;
1718
                foreach my $val (@regvalues) {
1719
                    if ($val->{'user'} eq $user && $val->{'id'} == $id) {
1720
                        $notlast = 1;
1721
                    }
1722
                }
1723
                if (!$notlast) {
1724
                    eval {`/sbin/ifconfig $datanic.$id down`; 1;} or do {;};
1725
                    eval {`/sbin/vconfig rem $datanic.$id`; 1;} or do {;};
1726
                }
1727
            }
1728

    
1729
            unless (tie(%sysreg,'Tie::DBI', Hash::Merge::merge({table=>'systems'}, $Stabile::dbopts)) ) {$res .= qq|{"status": "Error": "message": "Unable to access systems register"}|; return $res;};
1730
            if ($sysreg{$systems}) { # Remove existing link to system
1731
                $sysreg{$systems}->{'networkuuids'} =~ s/$uuid,?//;
1732
                $sysreg{$systems}->{'networknames'} = s/$name,?//;
1733
            }
1734
            tied(%sysreg)->commit;
1735
            untie(%sysreg);
1736

    
1737

    
1738
            delete $register{$uuid};
1739
            tied(%register)->commit;
1740
            updateBilling("delete $val->{'externalip'}") if ($type eq "ipmapping");
1741
            $main::syslogit->($user, "info", "Deleted network $uuid ($id)");
1742
            $postreply = "[]" || $postreply;
1743
            $main::updateUI->({tab=>"networks", user=>$user, type=>"update"});
1744
        } else {
1745
            $postreply .= "Status=ERROR Cannot remove $uuid which is $status. Cannot delete network 0,1 or a network which is active or in use.\n";
1746
            $main::updateUI->({tab=>"networks", user=>$user, message=>"Cannot remove a network which is active, linked or in use."});
1747
        }
1748
    } else {
1749
        $postreply .= "Status=ERROR Network $uuid $ipaddress not found\n";
1750
    }
1751
    return $postreply;
1752
}
1753

    
1754
sub Deactivate {
1755
    my ($uuid, $action, $obj) = @_;
1756

    
1757
    if ($help) {
1758
        return <<END
1759
GET:uuid:
1760
Deactivate a network which must be in status up.
1761
END
1762
    }
1763
    $uuid = $obj->{'uuid'} if ($obj->{'uuid'});
1764

    
1765
    unless ($register{$uuid}) {
1766
        $postreply .= "Status=ERROR Connection with uuid $uuid not found\n";
1767
        return $postreply;
1768
    }
1769
    my $regnet = $register{$uuid};
1770

    
1771
    $action = $action || 'deactivate';
1772
    ( my $domains, my $domainnames ) = getDomains($uuid);
1773
    my $interfaces = `/sbin/ifconfig`;
1774

    
1775
    my $id = $regnet->{'id'};
1776
    my $name = $regnet->{'name'};
1777
    my $type = $regnet->{'type'};
1778
    my $internalip = $regnet->{'internalip'};
1779
    my $externalip = $regnet->{'externalip'};
1780
    my $ports = $regnet->{'ports'};
1781

    
1782
    if ($id!=0 && $id!=1 && $status ne 'down') {
1783
    # If gateway is created, take it down along with all user's networks
1784
        if ($action eq "stop") {
1785
            my $res = Stop($id, $action);
1786
            if ($res) {
1787
                unlink "$etcpath/dhcp-hosts-$id" if (-e "$etcpath/dhcp-hosts-$id");
1788
            };
1789
        }
1790
    } else {
1791
        $postreply .= "Status=ERROR Cannot $action network $name\n";
1792
        return $postreply;
1793
    }
1794

    
1795
    my $idleft = ($id>99)?(substr $id,0,-2)+0 : 0;
1796
    my $idright = (substr $id,-2) + 0;
1797
    my $e = 0;
1798
    my $duprules = 0;
1799

    
1800
    if ($type eq "ipmapping" || $type eq "internalip") {
1801
        `iptables -D FORWARD -d $internalip -m state --state ESTABLISHED,RELATED -j RETURN`;
1802
    }
1803
    if ($type eq "ipmapping") {
1804
        # Check if external ip exists and take it down if so
1805
        if ($internalip && $internalip ne "--" && $externalip && $externalip ne "--" && ($interfaces =~ m/$externalip/g)) {
1806
            $externalip =~ /\d+\.\d+\.(\d+)\.(\d+)/;
1807
            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
1808
            $ipend = $2 if (length("$extnic:$id-$ipend")>15);
1809
            eval {`/sbin/ifconfig $extnic:$id-$ipend down`; 1;} or do {$e=1; $postreply .= "Status=ERROR $@\n";};
1810

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

    
1885
                eval {$duprules++ if (`/sbin/iptables -D POSTROUTING -t nat -s $internalip ! -d 10.$idleft.$idright.0/24 -j SNAT --to-source $externalip`); 1; }
1886
                    or do {$e=1; $postreply .= "Status=ERROR Problem setting up routing $@\n";};
1887
            #    eval {$duprules++ if (`/sbin/iptables -D POSTROUTING -t nat -s $internalip -j SNAT --to-source $externalip`); 1; }
1888
            #        or do {$e=1; $postreply .= "Status=ERROR Problem setting up routing $@\n";};
1889
                eval {$duprules++ if (`/sbin/iptables -D INPUT -t nat -s $internalip ! -d 10.$idleft.$idright.0/24 -j SNAT --to-source $externalip`); 1; }
1890
                    or do {$e=1; $postreply .= "Status=ERROR Problem setting up routing $@\n";};
1891
            #    eval {$duprules++ if (`/sbin/iptables -D INPUT -t nat -s $internalip -j SNAT --to-source $externalip`); 1; }
1892
            #        or do {$e=1; $postreply .= "Status=ERROR Problem setting up routing $@\n";};
1893
            #    eval {$duprules++ if (`/sbin/iptables -D INPUT -t nat -s $internalip ! -d 10.$idleft.$idright.0/24 -j SNAT --to-source $externalip`); 1; }
1894
            #        or do {$e=1; $postreply .= "Status=ERROR Problem setting up routing $@\n";};
1895
            #    eval {$duprules++ if (`/sbin/iptables -D INPUT -t nat -s $internalip -j SNAT --to-source $externalip`); 1; }
1896
            #        or do {$e=1; $postreply .= "Status=ERROR Problem setting up routing $@\n";};
1897
                last if ($duprules >1);
1898
            }
1899
            # `/sbin/iptables -D POSTROUTING -t nat -s $internalip -j LOG --log-prefix "SNAT-POST"`;
1900
            # `/sbin/iptables -D INPUT -t nat -s $internalip -j LOG --log-prefix "SNAT-INPUT"`;
1901
            # `/sbin/iptables -D OUTPUT -t nat -s $internalip -j LOG --log-prefix "SNAT-OUTPUT"`;
1902
            # `/sbin/iptables -D PREROUTING -t nat -s $internalip -j LOG --log-prefix "SNAT-PRE"`;
1903
        }
1904
    } elsif ($type eq "externalip") {
1905
        if ($externalip && $externalip ne "--") {
1906
            # We are dealing with multiple upstream routes - configure local routing
1907
            if ($proxynic && $proxynic ne $extnic) {
1908
                my $proxyroute = `/sbin/ip route show table proxyarp`;
1909
                `/sbin/ip route del $externalip/32 dev br$id:proxy src $proxyip table proxyarp` if ($proxyroute =~ /$externalip/);
1910
            }
1911

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

    
1915
            if ($ports && $ports ne "--") {
1916
                my @portslist = split(/, ?| /, $ports);
1917
                foreach my $port (@portslist) {
1918
                    my $ipfilter;
1919
                    if ($port =~ /(\d+)\.(\d+)\.(\d+)\.(\d+)(\/\d+)?:(\d+)/) {
1920
                        my $portip = "$1.$2.$3.$4$5";
1921
                        $port = $6;
1922
                        $ipfilter = "-s $portip";
1923
                    } else {
1924
                        $port = 0 unless ($port =~ /\d+/);
1925
                    }
1926
                    if ($port<1 || $port>65535) {
1927
                        $postreply .= "Status=ERROR Invalid port mapping for $name\n";
1928
                        $ports = "--";
1929
                        last;
1930
                    }
1931

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

    
1987
sub Stop {
1988
    my ($id, $action) = @_;
1989
    # Check if we were passed a uuid
1990
    if ($id =~ /\-/ && $register{$id} && ($register{$id}->{'user'} eq $user || $isadmin)) {
1991
        $id = $register{$id}->{'id'}
1992
    }
1993
    if ($help) {
1994
        return <<END
1995
GET:uuid:
1996
Stops a network by removing gateway. Network must be in status up or nat.
1997
END
1998
    }
1999

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

    
2038
    $uistatus = ($e)?$uistatus:"down";
2039
    if ($uistatus eq 'down') {
2040
        $uiuuid = $uuid;
2041
        $postreply .= "Status=$uistatus OK $action gateway: $uistatus\n";
2042
    } else {
2043
        $postreply .= "Status=Error Cannot $action $type $name: $uistatus\n";
2044
    }
2045
    return $postreply;
2046
}
2047

    
2048
sub getDomains {
2049
    my $uuid = shift;
2050
    my $domains;
2051
    my $domainnames;
2052
    my @domregvalues = values %domreg;
2053
    foreach my $domval (@domregvalues) {
2054
        if (($domval->{'networkuuid1'} eq $uuid || $domval->{'networkuuid2'} eq $uuid || $domval->{'networkuuid3'} eq $uuid)
2055
                && $domval->{'user'} eq $user) {
2056
            $domains .= $domval->{'uuid'} . ", ";
2057
            $domainnames .= $domval->{'name'} . ", ";
2058
        }
2059
    }
2060
    $domains = substr $domains, 0, -2;
2061
    $domainnames = substr $domainnames, 0, -2;
2062
    return ($domains, $domainnames); 
2063
}
2064

    
2065
sub getSystems {
2066
    my $uuid = shift;
2067
    my $systems;
2068
    my $systemnames;
2069
    unless (tie(%sysreg,'Tie::DBI', Hash::Merge::merge({table=>'systems'}, $Stabile::dbopts)) ) {$res .= qq|{"status": "Error": "message": "Unable to access systems register"}|; return $res;};
2070
    my @sysregvalues = values %sysreg;
2071
    foreach my $sysval (@sysregvalues) {
2072
        my $networkuuids = $sysval->{'networkuuids'};
2073
        if ($networkuuids =~ /$uuid/ && $sysval->{'user'} eq $user) {
2074
            $systems = $sysval->{'uuid'};
2075
            $systemnames = $sysval->{'name'};
2076
            last;
2077
        }
2078
    }
2079
    unless ($systems) {
2080
        my @sysregvalues = values %domreg;
2081
        foreach my $sysval (@sysregvalues) {
2082
            my $networkuuids = $sysval->{'networkuuids'};
2083
            if ($networkuuids =~ /$uuid/ && $sysval->{'user'} eq $user) {
2084
                $systems = $sysval->{'uuid'};
2085
                $systemnames = $sysval->{'name'};
2086
                last;
2087
            }
2088
        }
2089
    }
2090
    return ($systems, $systemnames);
2091
}
2092

    
2093
sub getNextId {
2094
	# Find the next available vlan id
2095
	my $reqid = shift;
2096
	my $username = shift;
2097
	$username = $user unless ($username);
2098
    my $nextid = 1;
2099
	my $vlanstart = $Stabile::config->get('VLAN_RANGE_START');
2100
	my $vlanend = $Stabile::config->get('VLAN_RANGE_END');
2101

    
2102
    if ($reqid eq 0 || $reqid == 1) {
2103
        return $requid;
2104
    } elsif ($reqid && ($reqid > $vlanend || $reqid < $vlanstart)) {
2105
        return -1 unless ($isadmin);
2106
    }
2107

    
2108
	$reqid = $reqid + 0;
2109

    
2110
    my %ids;
2111
    # First check if the user has an existing vlan, if so use the first we find as default value
2112
    my @regvalues = values %register;
2113
    @regvalues = (sort {$a->{id} <=> $b->{id}} @regvalues);
2114
    foreach my $val (@regvalues) { # Traverse all id's in use
2115
        my $id = 0 + $val->{'id'};
2116
        my $dbuser = $val->{'user'};
2117
        if ($id > 1) {
2118
            if ($username eq $dbuser) { # If a specific id was requested map all id's
2119
                if (!$reqid) {# If no specific id was asked for, stop now, and use the user's first one
2120
                    $nextid = $id;
2121
                    last;
2122
                }
2123
            } else {
2124
                $ids{$id} = 1; # Mark this id as used (by another user)
2125
            }
2126
        }
2127
    }
2128
    if ($nextid>1) {
2129
        return $nextid;
2130
    } elsif ($reqid) {
2131
        if (!$ids{$reqid} || $isadmin) { # If an admin is requesting id used by another, assume he knows what he is doing
2132
            $nextid = $reqid; # Safe to use
2133
        } else {
2134
            $nextid = -1; # Id already in use by another
2135
        }
2136
    } elsif ($nextid == 1) { # This user is not currently using any vlan's, find the first free one
2137
        for ($n=$vlanstart; $n<$vlanend; $n++) {
2138
            if (!$ids{$n}) { # Don't return an id used (by another user)
2139
                $nextid = $n;
2140
                last;
2141
            }
2142
        }
2143
    }
2144
	return $nextid;
2145
}
2146

    
2147
sub getNextExternalIP {
2148
	# Find the next available IP
2149
	my $extip = shift;
2150
	my $extuuid = shift;
2151
	my $proxyarp = shift; # Are we trying to assign a proxy arp's external IP?
2152
	$extip="" if ($extip eq "--");
2153

    
2154
	my $extipstart;
2155
	my $extipend;
2156

    
2157
    if ($proxyarp) {
2158
        $extipstart = $Stabile::config->get('PROXY_IP_RANGE_START');
2159
        $extipend = $Stabile::config->get('PROXY_IP_RANGE_END');
2160
    } else {
2161
        $extipstart = $Stabile::config->get('EXTERNAL_IP_RANGE_START');
2162
        $extipend = $Stabile::config->get('EXTERNAL_IP_RANGE_END');
2163
    }
2164

    
2165
	return "" unless ($extipstart && $extipend);
2166

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

    
2215
sub ip2domain {
2216
    my $ip = shift;
2217
    my $ruuid;
2218
    if ($ip) {
2219
        my @regkeys = (tied %register)->select_where("internalip = '$ip' OR externalip = '$ip'");
2220
        foreach my $k (@regkeys) {
2221
            my $valref = $register{$k};
2222
            if ($valref->{'internalip'} eq $ip || $valref->{'externalip'} eq $ip) {
2223
                $ruuid = $valref->{'domains'};
2224
                last;
2225
            }
2226
        }
2227
    }
2228
    return $ruuid;
2229
}
2230

    
2231
sub getNextInternalIP {
2232
	my $intip = shift;
2233
	my $uuid = shift;
2234
	my $id = shift;
2235
	my $username = shift;
2236
	$username = $user unless ($username);
2237
	my $nextip = "";
2238
	my $intipnum;
2239
	my $subnet;
2240
	my %ids;
2241
    my $ping = Net::Ping->new();
2242

    
2243
    $id = getNextId() unless ($id);
2244
    my $idleft = ($id>99)?(substr $id,0,-2)+0 : 0;
2245
    my $idright = (substr $id,-2) + 0;
2246
    $intip = "10.$idleft.$idright.0" if (!$intip || $intip eq '--');
2247
    
2248
    return '' unless ($intip =~ m/(\d+\.\d+\.\d+)\.(\d+)/ );
2249
    $subnet = $1;
2250
    $intipnum = $2;
2251

    
2252
	# First create hash of IP's reserved by other servers in DB
2253
	my @regvalues = values %register;
2254
	foreach my $val (@regvalues) {
2255
    	if ($val->{'user'} eq $username) {
2256
            my $ip = $val->{'internalip'} ;
2257
            $ids{$ip} = $val->{'uuid'};
2258
		}
2259
	}
2260

    
2261
	if ($intipnum && $intipnum>1 && $intipnum<255) {
2262
	# An internal ip was supplied - check if it's free, if not keep the ip already registered in the db
2263
        if (!$ids{$intip}
2264
#            && !($ping->ping($intip, 0.1)) # 0.1 secs timeout, check if ip is in use, possibly on another engine
2265
            && !(`arping -C1 -c2 -D -I $datanic.$id $intip` =~ /reply from/)  # check if ip is created on another engine
2266
        ) {
2267
            $nextip = $intip;
2268
        } else {
2269
            $nextip = $register{$uuid}->{'internalip'}
2270
        }
2271
	} else {
2272
	# Find first IP not reserved
2273
		for ($n=2; $n<255; $n++) {
2274
			if (!$ids{"$subnet.$n"}
2275
# TODO: The arping check takes too long - two networks created by the same user can too easily be assigned the same IP's
2276
#                && !(`arping -f -c2 -D -I $datanic.$id $subnet.$n` =~ /reply from/)  # check if ip is created on another engine
2277
			) {
2278
                $nextip = "$subnet.$n";
2279
                last;
2280
			}
2281
		}
2282
	}
2283
	$postreply .= "Status=ERROR No more internal IPs available\n" if (!$nextip);
2284
	return $nextip;
2285
}
2286

    
2287
sub validateStatus {
2288
    my $valref = shift;
2289

    
2290
    my $interfaces = `/sbin/ifconfig`;
2291
    my $uuid = $valref->{'uuid'};
2292
    my $type = $valref->{'type'};
2293
    my $id = $valref->{'id'};
2294
    my $idleft = ($id>99)?(substr $id,0,-2)+0 : 0;
2295
    my $idright = (substr $id,-2) + 0;
2296

    
2297
    ( $valref->{'domains'}, $valref->{'domainnames'} ) = getDomains($uuid);
2298
    my ( $systems, $systemnames ) = getSystems($uuid);
2299
    my $extip = $valref->{'externalip'};
2300
    my $intip = $valref->{'internalip'};
2301

    
2302
    if ($type eq "gateway") {
2303
        $valref->{'internalip'} = "10.$idleft.$idright.1" if ($id>1);
2304
    } else {
2305
        $type = "gateway";
2306
        if ($intip && $intip ne "--" && $extip && $extip ne "--") {
2307
            $type = "ipmapping";
2308
        } elsif ($intip && $intip ne "--") {
2309
            $type = "internalip";
2310
        } elsif ($extip && $extip ne "--") {
2311
            $type = "externalip";
2312
        }
2313
        $valref->{'type'} = $type;
2314
    }
2315

    
2316
    $valref->{'status'} = "down";
2317
    my $nat;
2318
    if ($id == 0 || $id == 1) {
2319
        $valref->{'status'} = "nat";
2320
    # Check if vlan $id is created (and doing nat)
2321
#    } elsif ($interfaces =~ m/$datanic\.$id.+\n.+10\.$idleft\.$idright\.1/) {
2322
    } elsif (-e "/proc/net/vlan/$datanic.$id") {
2323
        $nat = 1;
2324
    }
2325

    
2326
    if (($type eq "internalip" || $type eq "ipmapping")) { # && $val->{'domains'}) {
2327
        $valref->{'status'} = "nat" if ($nat);
2328
        my $dhcprunning;
2329
        my $dhcpconfigured;
2330
        eval {
2331
            my $psid;
2332
            $psid = `/bin/cat /var/run/stabile-$id.pid` if (-e "/var/run/stabile-$id.pid");
2333
            chomp $psid;
2334
            $dhcprunning = -e "/proc/$psid" if ($psid);
2335
            my $dhcphosts;
2336
            $dhcphosts = lc `/bin/cat $etcpath/dhcp-hosts-$id` if (-e "$etcpath/dhcp-hosts-$id");
2337
            $dhcpconfigured = ($dhcphosts =~ /$intip/);
2338
            1;
2339
        } or do {;};
2340

    
2341
        if ($type eq "internalip") {
2342
        # Check if external ip has been created and dhcp is ok
2343
            if ($nat && (($dhcprunning && $dhcpconfigured) || $systems)) {
2344
                $valref->{'status'} = "up";
2345
            }
2346
        } elsif ($type eq "ipmapping") {
2347
        # Check if external ip has been created, dhcp is ok and vlan interface is created
2348
        # An ipmapping linked to a system is considered up if external interface exists
2349
            if ($nat && $interfaces =~ m/$extip/ && (($dhcprunning && $dhcpconfigured) || $systems)) {
2350
                $valref->{'status'} = "up";
2351
            }
2352
        }
2353

    
2354
    } elsif ($type eq "externalip") {
2355
        my $dhcprunning;
2356
        my $dhcpconfigured;
2357
        eval {
2358
            my $psid;
2359
            $psid = `/bin/cat /var/run/stabile-$id.pid` if (-e "/var/run/stabile-$id.pid");
2360
            chomp $psid;
2361
            $dhcprunning = -e "/proc/$psid" if ($psid);
2362
            my $dhcphosts;
2363
            $dhcphosts = `/bin/cat $etcpath/dhcp-hosts-$id` if (-e "$etcpath/dhcp-hosts-$id");
2364
            $dhcpconfigured = ($dhcphosts =~ /$extip/);
2365
            1;
2366
        } or do {;};
2367

    
2368
        my $vproxy = `/bin/cat /proc/sys/net/ipv4/conf/$datanic.$id/proxy_arp`; chomp $vproxy;
2369
        my $eproxy = `/bin/cat /proc/sys/net/ipv4/conf/$proxynic/proxy_arp`; chomp $eproxy;
2370
        my $proute = `/sbin/ip route | grep "$extip dev"`; chomp $proute;
2371
        if ($vproxy && $eproxy && $proute) {
2372
            if ((($dhcprunning && $dhcpconfigured) || $systems)) {
2373
                $valref->{'status'} = "up";
2374
            } elsif (!$valref->{'domains'}) {
2375
                $valref->{'status'} = "nat";
2376
            }
2377
        } else {
2378
            #print "$vproxy && $eproxy && $proute && $dhcprunning && $dhcpconfigured :: $extip\n";        
2379
        }
2380

    
2381
    } elsif ($type eq "gateway") {
2382
        if ($nat || $id == 0 || $id == 1) {$valref->{'status'} = "up";}
2383
    }
2384
    return $valref->{'status'};
2385
}
2386

    
2387
sub trim{
2388
   my $string = shift;
2389
   $string =~ s/^\s+|\s+$//g;
2390
   return $string;
2391
}
2392

    
2393
sub overQuotas {
2394
    my $reqips = shift; # number of new ip's we are asking for
2395
	my $usedexternalips = 0;
2396
	my $overquota = 0;
2397
    return $overquota if ($Stabile::userprivileges =~ /a/); # Don't enforce quotas for admins
2398

    
2399
	my $externalipquota = $Stabile::userexternalipquota;
2400
	if (!$externalipquota) {
2401
        $externalipquota = $Stabile::config->get('EXTERNAL_IP_QUOTA');
2402
    }
2403

    
2404
	my $rxquota = $Stabile::userrxquota;
2405
	if (!$rxquota) {
2406
        $rxquota = $Stabile::config->get('RX_QUOTA');
2407
    }
2408

    
2409
	my $txquota = $Stabile::usertxquota;
2410
	if (!$txquota) {
2411
        $txquota = $Stabile::config->get('TX_QUOTA');
2412
    }
2413

    
2414
    my @regkeys = (tied %register)->select_where("user = '$user'");
2415
	foreach my $k (@regkeys) {
2416
	    my $val = $register{$k};
2417
		if ($val->{'user'} eq $user && $val->{'externalip'} && $val->{'externalip'} ne "--" ) {
2418
		    $usedexternalips += 1;
2419
		}
2420
	}
2421
	if ((($usedexternalips + $reqips) > $externalipquota) && $externalipquota > 0) { # -1 means no quota
2422
	    $overquota = $usedexternalips;
2423
	} elsif ($rx > $rxquota*1024 && $rxquota > 0) {
2424
	    $overquota = -1;
2425
	} elsif ($tx > $txquota*1024 && $txquota > 0) {
2426
	    $overquota = -2;
2427
	}
2428
	return $overquota;
2429
}
2430

    
2431
sub updateBilling {
2432
    my $event = shift;
2433
    my %billing;
2434
    my @regkeys = (tied %register)->select_where("user = '$user' or user = 'common'") unless ($fulllist);
2435
    foreach my $k (@regkeys) {
2436
        my $valref = $register{$k};
2437
        my %val = %{$valref}; # Deference and assign to new array, effectively cloning object
2438
        if ($val{'user'} eq $user && ($val{'type'} eq 'ipmapping' || $val{'type'} eq 'externalip') && $val{'externalip'} ne '--') {
2439
            $billing{$val{'id'}}->{'externalip'} += 1;
2440
        }
2441
    }
2442

    
2443
    my %billingreg;
2444
    my $monthtimestamp = timelocal(0,0,0,1,$mon,$year); #$sec,$min,$hour,$mday,$mon,$year
2445

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

    
2448
    my $rx_bytes_total = 0;
2449
    my $tx_bytes_total = 0;
2450

    
2451
    my $prevmonth = $month-1;
2452
    my $prevyear = $year;
2453
    if ($prevmonth == 0) {$prevmonth=12; $prevyear--;};
2454
    $prevmonth = substr("0" . $prevmonth, -2);
2455
    my $prev_rx_bytes_total = 0;
2456
    my $prev_tx_bytes_total = 0;
2457

    
2458
    foreach my $id (keys %billing) {
2459
        my $b = $billing{$id};
2460
        my $externalip = $b->{'externalip'};
2461
        my $externalipavg = 0;
2462
        my $startexternalipavg = 0;
2463
        my $starttimestamp = $current_time;
2464
        my $rx_bytes = 0;
2465
        my $tx_bytes = 0;
2466
        my $rx_stats = "/sys/class/net/$datanic.$id/statistics/rx_bytes";
2467
        my $tx_stats = "/sys/class/net/$datanic.$id/statistics/tx_bytes";
2468
        $rx_bytes = `/bin/cat $rx_stats` if (-e $rx_stats);
2469
        chomp $rx_bytes;
2470
        $tx_bytes = `/bin/cat $tx_stats` if (-e $tx_stats);
2471
        chomp $tx_bytes;
2472

    
2473
        if ($current_time - $monthtimestamp < 4*3600) {
2474
            $starttimestamp = $monthtimestamp;
2475
            $externalipavg = $externalip;
2476
            $startexternalipavg = $externalip;
2477
        }
2478

    
2479
        my $bill = $billingreg{"$user-$id-$year-$month"};
2480
        my $regrx_bytes = $bill->{'rx'};
2481
        my $regtx_bytes = $bill->{'tx'};
2482
        $rx_bytes += $regrx_bytes if ($regrx_bytes > $rx_bytes); # Network interface was reloaded
2483
        $tx_bytes += $regtx_bytes if ($regtx_bytes > $tx_bytes); # Network interface was reloaded
2484

    
2485
        # Update timestamp and averages on existing row
2486
        if ($billingreg{"$user-$id-$year-$month"}) {
2487
            $startexternalipavg = $bill->{'startexternalipavg'};
2488
            $starttimestamp = $bill->{'starttimestamp'};
2489

    
2490
            $externalipavg = ($startexternalipavg*($starttimestamp - $monthtimestamp) + $externalip*($current_time - $starttimestamp)) /
2491
                            ($current_time - $monthtimestamp);
2492

    
2493
            $billingreg{"$user-$id-$year-$month"}->{'externalip'} = $externalip;
2494
            $billingreg{"$user-$id-$year-$month"}->{'externalipavg'} = $externalipavg;
2495
            $billingreg{"$user-$id-$year-$month"}->{'timestamp'} = $current_time;
2496
            $billingreg{"$user-$id-$year-$month"}->{'rx'} = $rx_bytes;
2497
            $billingreg{"$user-$id-$year-$month"}->{'tx'} = $tx_bytes;
2498
        }
2499

    
2500
        # No row found or something happened which justifies writing a new row
2501
        if (!$billingreg{"$user-$id-$year-$month"}
2502
        || ($b->{'externalip'} != $bill->{'externalip'})
2503
        ) {
2504

    
2505
            my $inc = 0;
2506
            if ($billingreg{"$user-$id-$year-$month"}) {
2507
                $startexternalipavg = $externalipavg;
2508
                $starttimestamp = $current_time;
2509
                $inc = $bill->{'inc'};
2510
            }
2511
            # Write a new row
2512
            $billingreg{"$user-$id-$year-$month"} = {
2513
                externalip=>$externalip+0,
2514
                externalipavg=>$externalipavg,
2515
                startexternalipavg=>$startexternalipavg,
2516
                timestamp=>$current_time,
2517
                starttimestamp=>$starttimestamp,
2518
                event=>$event,
2519
                inc=>$inc+1,
2520
                rx=>$rx_bytes,
2521
                tx=>$tx_bytes
2522
            };
2523
        }
2524

    
2525
        $rx_bytes_total += $rx_bytes;
2526
        $tx_bytes_total += $tx_bytes;
2527
        my $prevbill = $billingreg{"$user-$id-$prevyear-$prevmonth"};
2528
        $prev_rx_bytes_total += $prevbill->{'rx'};
2529
        $prev_tx_bytes_total += $prevbill->{'tx'};
2530
    }
2531
    untie %billingreg;
2532
    $rx = ($rx_bytes_total>$prev_rx_bytes_total)?$rx_bytes_total - $prev_rx_bytes_total:$rx_bytes_total;
2533
    $tx = ($tx_bytes_total>$prev_tx_bytes_total)?$tx_bytes_total - $prev_tx_bytes_total:$tx_bytes_total;
2534
    my $oq = overQuotas();
2535
    if ($oq && $oq<0) {
2536
        foreach my $id (keys %billing) {
2537
            $main::syslogit->($user, 'info', "$user over rx/tx quota ($oq) stopping network $id");
2538
            Stop($id, 'stop');
2539
        }
2540
    }
2541
}
2542

    
2543
sub Bit2netmask {
2544
	my $netbit = shift;
2545
	my $_bit         = ( 2 ** (32 - $netbit) ) - 1;
2546
	my ($full_mask)  = unpack( "N", pack( "C4", split(/./, '255.255.255.255') ) );
2547
	my $netmask      = join( '.', unpack( "C4", pack( "N", ( $full_mask ^ $_bit ) ) ) );
2548
	return $netmask;
2549
}
(3-3/9)