Passed
Push — develop ( bce914...037e9d )
by Портнов
04:55
created

Network::azureProvisioning()   B

Complexity

Conditions 7
Paths 14

Size

Total Lines 97
Code Lines 67

Duplication

Lines 0
Ratio 0 %

Importance

Changes 2
Bugs 0 Features 0
Metric Value
eloc 67
c 2
b 0
f 0
dl 0
loc 97
rs 7.7866
cc 7
nc 14
nop 0

How to fix   Long Method   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
/*
3
 * MikoPBX - free phone system for small business
4
 * Copyright (C) 2017-2020 Alexey Portnov and Nikolay Beketov
5
 *
6
 * This program is free software: you can redistribute it and/or modify
7
 * it under the terms of the GNU General Public License as published by
8
 * the Free Software Foundation; either version 3 of the License, or
9
 * (at your option) any later version.
10
 *
11
 * This program is distributed in the hope that it will be useful,
12
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14
 * GNU General Public License for more details.
15
 *
16
 * You should have received a copy of the GNU General Public License along with this program.
17
 * If not, see <https://www.gnu.org/licenses/>.
18
 */
19
20
namespace MikoPBX\Core\System;
21
22
use MikoPBX\Common\Models\{LanInterfaces, PbxSettings};
23
use MikoPBX\Core\System\Configs\IptablesConf;
24
use MikoPBX\Core\Utilities\SubnetCalculator;
25
use Phalcon\Di\Injectable;
26
use Phalcon\Mvc\Model;
27
use Throwable;
28
29
/**
30
 * Class Network
31
 */
32
class Network extends Injectable
33
{
34
35
    public static function startSipDump(): void
36
    {
37
        $config = new MikoPBXConfig();
38
        $use    = $config->getGeneralSettings('USE_PCAP_SIP_DUMP');
39
        if ($use !== '1') {
40
            return;
41
        }
42
43
        Processes::killByName('pcapsipdump');
44
        $log_dir = System::getLogDir() . '/pcapsipdump';
45
        Util::mwMkdir($log_dir);
46
47
        $network         = new Network();
48
        $arr_eth         = $network->getInterfacesNames();
49
        $pcapsipdumpPath = Util::which('pcapsipdump');
50
        foreach ($arr_eth as $eth) {
51
            $pid_file = "/var/run/pcapsipdump_{$eth}.pid";
52
            Processes::mwExecBg(
53
                $pcapsipdumpPath . ' -T 120 -P ' . $pid_file . ' -i ' . $eth . ' -m \'^(INVITE|REGISTER)$\' -L ' . $log_dir . '/dump.db'
54
            );
55
        }
56
    }
57
58
    /**
59
     * Имена всех подключенных сетевых интерфейсов.
60
     */
61
    public function getInterfacesNames()
62
    {
63
        // Универсальная команда для получения всех PCI сетевых интерфейсов.
64
        $lsPath   = Util::which('ls');
65
        $grepPath = Util::which('grep');
66
        $awkPath  = Util::which('awk');
67
        Processes::mwExec("{$lsPath} -l /sys/class/net | {$grepPath} devices | {$grepPath} -v virtual | {$awkPath} '{ print $9 }'", $names);
68
69
        return $names;
70
    }
71
72
    /**
73
     * Up loopback.
74
     **/
75
    public function loConfigure():void
76
    {
77
        if (Util::isSystemctl()) {
78
            return;
79
        }
80
        $busyboxPath  = Util::which('busybox');
81
        $ifconfigPath = Util::which('ifconfig');
82
        Processes::mwExec("{$busyboxPath} {$ifconfigPath} lo 127.0.0.1");
83
    }
84
85
    /**
86
     * Generates resolv.conf
87
     **/
88
    public function resolvConfGenerate(): void
89
    {
90
        $resolv_conf   = '';
91
        $data_hostname = self::getHostName();
92
        if (trim($data_hostname['domain']) !== '') {
93
            $resolv_conf .= "domain {$data_hostname['domain']}\n";
94
        }
95
96
        $resolv_conf .= "nameserver 127.0.0.1\n";
97
98
        $named_dns = [];
99
        $dns       = $this->getHostDNS();
100
        foreach ($dns as $ns) {
101
            if (trim($ns) === '') {
102
                continue;
103
            }
104
            $named_dns[] = $ns;
105
            $resolv_conf .= "nameserver {$ns}\n";
106
        }
107
        if (count($dns) === 0) {
108
            $resolv_conf .= "nameserver 4.4.4.4\n";
109
            $named_dns[] .= "8.8.8.8";
110
        }
111
112
        if (Util::isSystemctl()) {
113
            $s_resolv_conf = "[Resolve]\n"
114
                . "DNS=127.0.0.1\n";
115
            if (trim($data_hostname['domain']) !== '') {
116
                $s_resolv_conf .= "Domains={$data_hostname['domain']}\n";
117
            }
118
            file_put_contents('/etc/systemd/resolved.conf', $s_resolv_conf);
119
            $systemctlPath = Util::which('systemctl');
120
            Processes::mwExec("{$systemctlPath} restart systemd-resolved");
121
        } else {
122
            file_put_contents('/etc/resolv.conf', $resolv_conf);
123
        }
124
125
        $this->generatePdnsdConfig($named_dns);
126
    }
127
128
    /**
129
     * Возвращает имя сервера в виде ассоциативного массива.
130
     *
131
     * @return array
132
     */
133
    public static function getHostName(): array
134
    {
135
        $data = [
136
            'hostname' => 'mikopbx',
137
            'domain'   => '',
138
        ];
139
        /** @var LanInterfaces $res */
140
        $res = LanInterfaces::findFirst("internet = '1'");
141
        if (null !== $res) {
142
            $data['hostname'] = $res->hostname;
143
            $data['domain']   = $res->domain;
144
        }
145
        $data['hostname'] = (empty($data['hostname'])) ? 'mikopbx' : $data['hostname'];
146
147
        return $data;
148
    }
149
150
    /**
151
     * Возвращает массив DNS серверов из настроек.
152
     *
153
     * @return array
154
     */
155
    public function getHostDNS(): array
156
    {
157
        $dns = [];
158
        /** @var LanInterfaces $res */
159
        $res = LanInterfaces::findFirst("internet = '1'");
160
        if (null !== $res) {
161
            if ( ! empty($res->primarydns) && '127.0.0.1' !== $res->primarydns) {
162
                $dns[] = $res->primarydns;
163
            }
164
            if ( ! empty($res->secondarydns) && '127.0.0.1' !== $res->secondarydns) {
165
                $dns[] = $res->secondarydns;
166
            }
167
        }
168
169
        return $dns;
170
    }
171
172
    /**
173
     * Настройка кэширующего DNS сервера.
174
     *
175
     * @param $named_dns
176
     */
177
    public function generatePdnsdConfig($named_dns): void
178
    {
179
        $tempDir   = $this->di->getShared('config')->path('core.tempDir');
180
        $cache_dir = $tempDir . '/pdnsd/cache';
181
        Util::mwMkdir($cache_dir);
182
183
        $conf = 'global {' . "\n" .
184
            '	perm_cache=10240;' . "\n" .
185
            '	cache_dir="' . $cache_dir . '";' . "\n" .
186
            '	pid_file = /var/run/pdnsd.pid;' . "\n" .
187
            '	run_as="nobody";' . "\n" .
188
            '	server_ip = 127.0.0.1;' . "\n" .
189
            '	status_ctl = on;' . "\n" .
190
            '	query_method=udp_tcp;' . "\n" .
191
            '	min_ttl=15m;' . "\n" .
192
            '	max_ttl=1w;' . "\n" .
193
            '	timeout=10;' . "\n" .
194
            '	neg_domain_pol=on;' . "\n" .
195
            '	run_as=root;' . "\n" .
196
            '	daemon=on;' . "\n" .
197
            '}' . "\n" .
198
            'server {' . "\n" .
199
            '	label = "main";' . "\n" .
200
            '	ip = ' . implode(', ', $named_dns) . ';' . "\n" .
201
            '	interface=lo;' . "\n" .
202
            '	uptest=if;' . "\n" .
203
            '	interval=10m;' . "\n" .
204
            '	purge_cache=off;' . "\n" .
205
            '}';
206
207
        $pdnsdConfFile  = '/etc/pdnsd.conf';
208
        $savedConf = '';
209
        if(file_exists($pdnsdConfFile)){
210
            $savedConf = file_get_contents($pdnsdConfFile);
211
        }
212
        if($savedConf !== $conf){
213
            file_put_contents($pdnsdConfFile, $conf);
214
        }
215
        $pdnsdPath = Util::which('pdnsd');
216
        $pid       = Processes::getPidOfProcess($pdnsdPath);
217
        if (!empty($pid) && $savedConf === $conf) {
218
            // Выполним дополнительную проверку, работает ли сервер.
219
            $resultResolve = gethostbynamel('lic.miko.ru');
220
            if($resultResolve !== false){
221
                // Ничего делать не нужно. Конфиг не изменился. Рестарт не требуется.
222
                return;
223
            }
224
            // Выполним reload сервера DNS.
225
        }
226
227
        if (!empty($pid)) {
228
            // Завершаем процесс.
229
            $busyboxPath = Util::which('busybox');
230
            $killPath = Util::which('kill');
231
            Processes::mwExec("{$busyboxPath} {$killPath} '$pid'");
232
        }
233
        if (Util::isSystemctl()) {
234
            $systemctlPath = Util::which('systemctl');
235
            Processes::mwExec("{$systemctlPath} restart pdnsd");
236
        } else {
237
            Processes::mwExec("{$pdnsdPath} -c /etc/pdnsd.conf -4");
238
        }
239
    }
240
241
    /**
242
     * Configures LAN interface
243
     *
244
     * @return int
245
     */
246
    public function lanConfigure(): int
247
    {
248
        if (Util::isSystemctl()) {
249
            $this->lanConfigureSystemCtl();
250
            $this->openVpnConfigure();
251
252
            return 0;
253
        }
254
        $busyboxPath = Util::which('busybox');
255
        $vconfigPath = Util::which('vconfig');
256
        $killallPath = Util::which('killall');
257
258
        $networks     = $this->getGeneralNetSettings();
259
        $arr_commands = [];
260
        $arr_commands[] = "{$killallPath} udhcpc";
261
        $eth_mtu = [];
262
        foreach ($networks as $if_data) {
263
            if ($if_data['disabled'] === '1') {
264
                continue;
265
            }
266
267
            $if_name = $if_data['interface'];
268
            $if_name = escapeshellcmd(trim($if_name));
269
            if (empty($if_name)) {
270
                continue;
271
            }
272
273
            $data_hostname = self::getHostName();
274
            $hostname      = $data_hostname['hostname'];
275
276
            if ($if_data['vlanid'] > 0) {
277
                // Переопределяем имя интерфейса.
278
                $arr_commands[] = "{$vconfigPath} set_name_type VLAN_PLUS_VID_NO_PAD";
279
                // Добавляем новый интерфейс.
280
                $arr_commands[] = "{$vconfigPath} add {$if_data['interface_orign']} {$if_data['vlanid']}";
281
            }
282
            // Отключаем интерфейс.
283
            $arr_commands[] = "{$busyboxPath} ifconfig $if_name down";
284
            $arr_commands[] = "{$busyboxPath} ifconfig $if_name 0.0.0.0";
285
286
            $gw_param = '';
287
            if (trim($if_data['dhcp']) === '1') {
288
                /*
289
                 * -t - количество попыток.
290
                 * -T - таймаут попытки.
291
                 * -v - включить отладку.
292
                 * -S - логи в syslog.
293
                 * -q - Exit after obtaining lease
294
                 * -n - Exit if lease is not obtained
295
                 */
296
                $pid_file = "/var/run/udhcpc_{$if_name}";
297
                $pid_pcc  = Processes::getPidOfProcess($pid_file);
298
                if ( ! empty($pid_pcc) && file_exists($pid_file)) {
299
                    // Завершаем старый процесс.
300
                    $killPath = Util::which('kill');
301
                    $catPath  = Util::which('cat');
302
                    system("{$killPath} `{$catPath} {$pid_file}` {$pid_pcc}");
303
                }
304
                $udhcpcPath = Util::which('udhcpc');
305
                $nohupPath  = Util::which('nohup');
306
307
                // Получаем IP и дожидаемся завершения процесса.
308
                $workerPath     = '/etc/rc/udhcpc.configure';
309
                $options        = '-t 6 -T 5 -q -n';
310
                $arr_commands[] = "{$udhcpcPath} {$options} -i {$if_name} -x hostname:{$hostname} -s {$workerPath}";
311
                // Старутем новый процесс udhcpc в  фоне.
312
                $options        = '-t 6 -T 5 -S -b -n';
313
                $arr_commands[] = "{$nohupPath} {$udhcpcPath} {$options} -p {$pid_file} -i {$if_name} -x hostname:{$hostname} -s {$workerPath} 2>&1 &";
314
                /*
315
                    udhcpc  - утилита произведет настройку интерфейса
316
                               - произведет конфигурацию /etc/resolv.conf
317
                    Дальнейшая настройка маршрутов будет произволиться в udhcpcConfigureRenewBound();
318
                    и в udhcpcConfigureDeconfig(). Эти методы будут вызваны скриптом WorkerUdhcpcConfigure.php.
319
                    // man udhcp
320
                    // http://pwet.fr/man/linux/administration_systeme/udhcpc/
321
322
                */
323
            } else {
324
                $ipaddr  = trim($if_data['ipaddr']);
325
                $subnet  = trim($if_data['subnet']);
326
                $gateway = trim($if_data['gateway']);
327
                if (empty($ipaddr)) {
328
                    continue;
329
                }
330
                // Это короткое представление маск /24 /32.
331
                try {
332
                    $calc_subnet = new SubnetCalculator($ipaddr, $subnet);
333
                    $subnet      = $calc_subnet->getSubnetMask();
334
                } catch (Throwable $e) {
335
                    echo "Caught exception: $ipaddr $subnet", $e->getMessage(), "\n";
336
                    continue;
337
                }
338
339
                $ifconfigPath   = Util::which('ifconfig');
340
                $arr_commands[] = "{$busyboxPath} {$ifconfigPath} $if_name $ipaddr netmask $subnet";
341
342
                if ("" !== trim($gateway)) {
343
                    $gw_param = "gw $gateway";
344
                }
345
346
                $routePath      = Util::which('route');
347
                $arr_commands[] = "{$busyboxPath} {$routePath} del default $if_name";
348
349
                /** @var LanInterfaces $if_data */
350
                $if_data = LanInterfaces::findFirst("id = '{$if_data['id']}'");
351
                $is_inet = ($if_data !== null) ? (string)$if_data->internet : '0';
352
                // Добавляем маршруты по умолчанию.
353
                if ($is_inet === '1') {
354
                    // ТОЛЬКО, если этот интерфейс для интернет, создаем дефолтный маршрут.
355
                    $arr_commands[] = "{$busyboxPath} {$routePath} add default $gw_param dev $if_name";
356
                }
357
                // Поднимаем интерфейс.
358
                $arr_commands[] = "{$busyboxPath} {$ifconfigPath} $if_name up";
359
360
                $eth_mtu[] = $if_name;
361
            }
362
        }
363
        $out = null;
364
        Processes::mwExecCommands($arr_commands, $out, 'net');
365
        $this->hostsGenerate();
366
367
        foreach ($eth_mtu as $eth) {
368
            Processes::mwExecBg("/etc/rc/networking.set.mtu '{$eth}'");
369
        }
370
371
        $firewall = new IptablesConf();
372
        $firewall->applyConfig();
373
374
        // Дополнительные "ручные" маршруты.
375
        Util::fileWriteContent('/etc/static-routes', '');
376
        $arr_commands = [];
377
        $out          = [];
378
        $grepPath     = Util::which('grep');
379
        $awkPath      = Util::which('awk');
380
        $catPath      = Util::which('cat');
381
        Processes::mwExec(
382
            "{$catPath} /etc/static-routes | {$grepPath} '^rout' | {$busyboxPath} {$awkPath} -F ';' '{print $1}'",
383
            $arr_commands
384
        );
385
        Processes::mwExecCommands($arr_commands, $out, 'rout');
386
387
        $this->openVpnConfigure();
388
        return 0;
389
    }
390
391
    /**
392
     * For OS systemctl (Debian).
393
     * Configures LAN interface
394
     */
395
    public function lanConfigureSystemCtl(): void
396
    {
397
        $networks      = $this->getGeneralNetSettings();
398
        $busyboxPath   = Util::which('busybox');
399
        $grepPath      = Util::which('grep');
400
        $awkPath       = Util::which('awk');
401
        $catPath       = Util::which('cat');
402
        $systemctlPath = Util::which('systemctl');
403
        $modprobePath  = Util::which('modprobe');
404
        Processes::mwExec("{$systemctlPath} stop networking");
405
        Processes::mwExec("{$modprobePath} 8021q");
406
        foreach ($networks as $if_data) {
407
            $if_name = trim($if_data['interface']);
408
            if ('' === $if_name) {
409
                continue;
410
            }
411
            $conf_file = "/etc/network/interfaces.d/{$if_name}";
412
            if ($if_data['disabled'] === '1') {
413
                $ifdownPath = Util::which('ifdown');
414
                Processes::mwExec("{$ifdownPath} eth0");
415
                if (file_exists($if_name)) {
416
                    unlink($conf_file);
417
                }
418
                continue;
419
            }
420
            $subnet  = trim($if_data['subnet']);
421
            $ipaddr  = trim($if_data['ipaddr']);
422
            $gateway = trim($if_data['gateway']);
423
424
            $result = [''];
425
            if (file_exists('/etc/static-routes')) {
426
                $command = "{$catPath} /etc/static-routes " .
427
                    "| {$grepPath} '^rout' " .
428
                    "| {$busyboxPath} awk -F ';' '{print $1}' " .
429
                    "| {$grepPath} '{$if_name}\$' " .
430
                    "| {$awkPath} -F 'dev {$if_name}' '{ print $1 }'";
431
                Processes::mwExec($command, $result);
432
            }
433
            $routs_add = ltrim(implode("\npost-up ", $result));
434
            $routs_rem = ltrim(implode("\npre-down ", $result));
435
436
437
            if ($if_data['vlanid'] > 0) {
438
                // Пока только статика.
439
                $lan_config = "auto {$if_data['interface_orign']}.{$if_data['vlanid']}\n" .
440
                    "iface {$if_data['interface_orign']}.{$if_data['vlanid']} inet static \n" .
441
                    "address {$ipaddr}\n" .
442
                    "netmask {$subnet}\n" .
443
                    "gateway {$gateway}\n" .
444
                    "dns-nameservers 127.0.0.1\n" .
445
                    "vlan_raw_device {$if_data['interface_orign']}\n" .
446
                    "{$routs_add}\n" .
447
                    "{$routs_rem}\n";
448
            } elseif (trim($if_data['dhcp']) === '1') {
449
                $lan_config = "auto {$if_name}\n" .
450
                    "iface {$if_name} inet dhcp\n" .
451
                    "{$routs_add}\n" .
452
                    "{$routs_rem}\n";
453
            } else {
454
                if (empty($ipaddr)) {
455
                    continue;
456
                }
457
                try {
458
                    $calc_subnet = new SubnetCalculator($ipaddr, $subnet);
459
                    $subnet      = $calc_subnet->getSubnetMask();
460
                } catch (Throwable $e) {
461
                    echo "Caught exception: $ipaddr $subnet", $e->getMessage(), "\n";
462
                    continue;
463
                }
464
                $lan_config = "auto {$if_name}\n" .
465
                    "iface {$if_name} inet static\n" .
466
                    "address {$ipaddr}\n" .
467
                    "netmask {$subnet}\n" .
468
                    "gateway {$gateway}\n" .
469
                    "dns-nameservers 127.0.0.1\n" .
470
                    "{$routs_add}\n" .
471
                    "{$routs_rem}\n";
472
            }
473
            file_put_contents("/etc/network/interfaces.d/{$if_name}", $lan_config);
474
        }
475
        $systemctlPath = Util::which('systemctl');
476
        Processes::mwExec("{$systemctlPath} start networking");
477
        $this->hostsGenerate();
478
479
        $firewall = new IptablesConf();
480
        $firewall->applyConfig();
481
    }
482
483
    /**
484
     * Получение настроек интерфейсов LAN.
485
     *
486
     * @return array
487
     */
488
    public function getGeneralNetSettings(): array
489
    {
490
        // Массив сетевых интерфейсов, которые видит ОС.
491
        $src_array_eth = $this->getInterfacesNames();
492
        // Создаем копию массива сетевых интерфейсов.
493
        $array_eth = $src_array_eth;
494
        $res       = LanInterfaces::find(['order' => 'interface,vlanid']);
495
        $networks  = $res->toArray();
496
        if (count($networks) > 0) {
497
            // Дополнительная обработка данных.
498
            foreach ($networks as &$if_data) {
499
                $if_data['interface_orign'] = $if_data['interface'];
500
                $if_data['interface']       = ($if_data['vlanid'] > 0) ? "vlan{$if_data['vlanid']}" : $if_data['interface'];
501
                $if_data['dhcp']            = ($if_data['vlanid'] > 0) ? 0 : $if_data['dhcp'];
502
503
                if (Verify::isIpAddress($if_data['subnet'])) {
504
                    $if_data['subnet'] = $this->netMaskToCidr($if_data['subnet']);
505
                }
506
507
                $key = array_search($if_data['interface_orign'], $src_array_eth, true);
508
                if ($key !== false) {
509
                    // Интерфейс найден.
510
                    // Удаляем элемент массива, если это не VLAN.
511
                    if ($if_data['vlanid'] === '0') {
512
                        unset($array_eth[$key]);
513
                        $this->enableLanInterface($if_data['interface_orign']);
514
                    }
515
                } else {
516
                    // Интерфейс не существует.
517
                    $this->disableLanInterface($if_data['interface_orign']);
518
                    // Отключаем интерфейс.
519
                    $if_data['disabled'] = 1;
520
                }
521
            }
522
            unset($if_data);
523
        } elseif (count($array_eth) > 0) {
524
            $networks = [];
525
            // Настраиваем основной интерфейс.
526
            $networks[] = $this->addLanInterface($array_eth[0], true);
527
            unset($array_eth[0]);
528
        }
529
        // $array_eth - в массиве останутся только те элементы,
530
        // по которым нет настроек в базе дынных.
531
        // Следует добавить настройки "по умолчанию".
532
        foreach ($array_eth as $eth) {
533
            // Добавляем. Все интерфейсы, отключаем.
534
            $networks[] = $this->addLanInterface($eth, false);
535
        }
536
        $res = LanInterfaces::findFirst("internet = '1' AND disabled='0'");
537
        if (null === $res) {
538
            /** @var LanInterfaces $eth_settings */
539
            $eth_settings = LanInterfaces::findFirst("disabled='0'");
540
            if ($eth_settings !== null) {
541
                $eth_settings->internet = 1;
542
                $eth_settings->save();
543
            }
544
        }
545
546
        return $networks;
547
    }
548
549
    /**
550
     * Преобразует сетевую маску в CIDR представление.
551
     *
552
     * @param $net_mask
553
     *
554
     * @return int
555
     */
556
    public function netMaskToCidr($net_mask): int
557
    {
558
        $bits     = 0;
559
        $net_mask = explode(".", $net_mask);
560
561
        foreach ($net_mask as $oct_ect) {
562
            $bits += strlen(str_replace("0", "", decbin((int)$oct_ect)));
563
        }
564
565
        return $bits;
566
    }
567
568
    /**
569
     * Включаем интерфейс по его имени.
570
     *
571
     * @param $name
572
     */
573
    public function enableLanInterface($name): void
574
    {
575
        $parameters = [
576
            'conditions' => 'interface = :ifName: and disabled = :disabled:',
577
            'bind'       => [
578
                'ifName'   => $name,
579
                'disabled' => 1,
580
            ],
581
        ];
582
583
        $if_data = LanInterfaces::findFirst($parameters);
584
        if ($if_data !== null) {
585
            $if_data->disabled = 0;
586
            $if_data->update();
587
        }
588
    }
589
590
    /**
591
     * Удаляем интерфейс по его имени.
592
     *
593
     * @param $name
594
     */
595
    public function disableLanInterface($name): void
596
    {
597
        $if_data = LanInterfaces::findFirst("interface = '{$name}'");
598
        if ($if_data !== null) {
599
            $if_data->internet = 0;
600
            $if_data->disabled = 1;
601
            $if_data->update();
602
        }
603
    }
604
605
    /**
606
     * Добавляем в базу данных сведения о новом интерфейсе.
607
     *
608
     * @param      $name
609
     * @param bool $general
610
     *
611
     * @return mixed
612
     */
613
    private function addLanInterface($name, $general = false)
614
    {
615
        $data = new LanInterfaces();
616
        $data->name      = $name;
617
        $data->interface = $name;
618
        $data->dhcp      = '1';
619
        $data->internet  = ($general === true) ? '1' : '0';
620
        $data->disabled  = '0';
621
        $data->vlanid    = '0';
622
        $data->hostname  = 'mikopbx';
623
        $data->domain    = '';
624
        $data->topology  = 'private';
625
        $data->primarydns= '';
626
        $data->save();
627
628
        return $data->toArray();
629
    }
630
631
    /**
632
     * Настройка hosts
633
     */
634
    public function hostsGenerate(): void
635
    {
636
        $this->hostnameConfigure();
637
    }
638
639
    /**
640
     *  Setup hostname
641
     **/
642
    public function hostnameConfigure(): void
643
    {
644
        $data       = self::getHostName();
645
        $hosts_conf = "127.0.0.1 localhost\n" .
646
            "127.0.0.1 {$data['hostname']}\n";
647
        if ( ! empty($data['domain'])) {
648
            $hosts_conf .= "127.0.0.1 {$data['hostname']}.{$data['domain']}\n";
649
        }
650
        Util::fileWriteContent('/etc/hosts', $hosts_conf);
651
652
        $hostnamePath = Util::which('hostname');
653
        Processes::mwExec($hostnamePath . ' ' . escapeshellarg($data['hostname']));
654
    }
655
656
    /**
657
     * Настройка OpenVPN. Если в кастомизации системных файлов определн конфиг, то сеть поднимется.
658
     */
659
    public function openVpnConfigure():void
660
    {
661
        $confFile = '/etc/openvpn.ovpn';
662
        Util::fileWriteContent($confFile, '');
663
        $data = file_get_contents($confFile);
664
665
        $pidFile = '/var/run/openvpn.pid';
666
        $pid     = Processes::getPidOfProcess('openvpn');
667
        if ( ! empty($pid)) {
668
            // Завершаем процесс.
669
            $busyboxPath = Util::which('busybox');
670
            Processes::mwExec("{$busyboxPath} kill '$pid'");
671
        }
672
        if ( ! empty($data)) {
673
            $openvpnPath = Util::which('openvpn');
674
            Processes::mwExecBg("{$openvpnPath} --config /etc/openvpn.ovpn --writepid {$pidFile}", '/dev/null', 5);
675
        }
676
    }
677
678
    /**
679
     * Configures LAN interface FROM udhcpc (renew_bound)
680
     */
681
    public function udhcpcConfigureRenewBound(): void
682
    {
683
        if (Util::isSystemctl()) {
684
            $this->udhcpcConfigureRenewBoundSystemCtl();
685
            return;
686
        }
687
        // Инициализация массива переменных.
688
        $env_vars = [
689
            'broadcast' => '',
690
            'interface' => '',
691
            'ip'        => '',
692
            'router'    => '',
693
            'timesvr'   => '',
694
            'namesvr'   => '',
695
            'dns'       => '',
696
            'hostname'  => '',
697
            'subnet'    => '',
698
            'serverid'  => '',
699
            'ipttl'     => '',
700
            'lease'     => '',
701
            'domain'    => '',
702
        ];
703
704
        $debugMode = $this->di->getShared('config')->path('core.debugMode');
705
        // Получаем значения переменных окружения.
706
        foreach ($env_vars as $key => $value) {
707
            $env_vars[$key] = trim(getenv($key));
708
        }
709
        $BROADCAST = ($env_vars['broadcast'] === '') ? "" : "broadcast {$env_vars['broadcast']}";
710
        if($env_vars['subnet'] === '255.255.255.255' || $env_vars['subnet'] === ''){
711
            // support /32 address assignment
712
            // https://forummikrotik.ru/viewtopic.php?f=3&t=6246&start=40
713
            $NET_MASK = '';
714
        }else{
715
            $NET_MASK = "netmask {$env_vars['subnet']}";
716
        }
717
        // Настраиваем интерфейс.
718
        $busyboxPath = Util::which('busybox');
719
        Processes::mwExec("{$busyboxPath} ifconfig {$env_vars['interface']} {$env_vars['ip']} $BROADCAST $NET_MASK");
720
721
        // Удаляем старые маршруты по умолчанию.
722
        while (true) {
723
            $out = [];
724
            Processes::mwExec("route del default gw 0.0.0.0 dev {$env_vars['interface']}", $out);
725
            if (trim(implode('', $out)) !== '') {
726
                // Произошла ошибка, значит все маршруты очищены.
727
                break;
728
            }
729
            if ($debugMode) {
730
                break;
731
            } // Иначе бесконечный цикл.
732
        }
733
        // Добавляем маршруты по умолчанию.
734
        /** @var LanInterfaces $if_data */
735
        $if_data = LanInterfaces::findFirst("interface = '{$env_vars['interface']}'");
736
        $is_inet = ($if_data !== null) ? (int)$if_data->internet : 0;
737
        if ('' !== $env_vars['router'] && $is_inet === 1) {
738
            // ТОЛЬКО, если этот интерфейс для интернет, создаем дефолтный маршрут.
739
            $routers = explode(' ', $env_vars['router']);
740
            foreach ($routers as $router) {
741
                Processes::mwExec("route add default gw {$router} dev {$env_vars['interface']}");
742
            }
743
        }
744
        // Добавляем пользовательские маршруты.
745
        if (file_exists('/etc/static-routes')) {
746
            $busyboxPath = Util::which('busybox');
747
            $grepPath    = Util::which('grep');
748
            $awkPath     = Util::which('awk');
749
            $catPath     = Util::which('cat');
750
            $shPath      = Util::which('sh');
751
            Processes::mwExec(
752
                "{$catPath} /etc/static-routes | {$grepPath} '^rout' | {$busyboxPath} {$awkPath} -F ';' '{print $1}' | {$grepPath} '{$env_vars['interface']}' | {$shPath}"
753
            );
754
        }
755
        $named_dns = [];
756
        if ('' !== $env_vars['dns']) {
757
            $named_dns = explode(' ', $env_vars['dns']);
758
        }
759
        if ($is_inet === 1) {
760
            // ТОЛЬКО, если этот интерфейс для интернет, правим resolv.conf.
761
            // Прописываем основные DNS.
762
            $this->generatePdnsdConfig($named_dns);
763
        }
764
765
        // Сохрании информацию в базу данных.
766
        $data = [
767
            'subnet'  => $env_vars['subnet'],
768
            'ipaddr'  => $env_vars['ip'],
769
            'gateway' => $env_vars['router'],
770
        ];
771
        if (Verify::isIpAddress($env_vars['ip'])) {
772
            $data['subnet'] = $this->netMaskToCidr($env_vars['subnet']);
773
        } else {
774
            $data['subnet'] = '';
775
        }
776
        $this->updateIfSettings($data, $env_vars['interface']);
777
778
        $data = [
779
            'primarydns'   => $named_dns[0] ?? '',
780
            'secondarydns' => $named_dns[1] ?? '',
781
        ];
782
        $this->updateDnsSettings($data, $env_vars['interface']);
783
784
        Processes::mwExecBg("/etc/rc/networking.set.mtu '{$env_vars['interface']}'");
785
    }
786
787
    /**
788
     * For OS systemctl (Debian).
789
     * Configures LAN interface FROM dhcpc (renew_bound).
790
     */
791
    public function udhcpcConfigureRenewBoundSystemCtl(): void
792
    {
793
        // Инициализация массива переменных.
794
        $prefix   = "new_";
795
        $env_vars = [
796
            'broadcast' => 'broadcast_address',
797
            'interface' => 'interface',
798
            'ip'        => 'ip_address',
799
            'router'    => 'routers',
800
            'timesvr'   => '',
801
            'namesvr'   => 'netbios_name_servers',
802
            'dns'       => 'domain_name_servers',
803
            'hostname'  => 'host_name',
804
            'subnet'    => 'subnet_mask',
805
            'serverid'  => '',
806
            'ipttl'     => '',
807
            'lease'     => 'new_dhcp_lease_time',
808
            'domain'    => 'domain_name',
809
        ];
810
811
        // Получаем значения переменных окружения.
812
        foreach ($env_vars as $key => $value) {
813
            $var_name = "{$prefix}{$value}";
814
            if (empty($var_name)) {
815
                continue;
816
            }
817
            $env_vars[$key] = trim(getenv("{$prefix}{$value}"));
818
        }
819
820
        // Добавляем маршруты по умолчанию.
821
        /** @var LanInterfaces $if_data */
822
        $if_data = LanInterfaces::findFirst("interface = '{$env_vars['interface']}'");
823
        $is_inet = ($if_data !== null) ? (string)$if_data->internet : '0';
824
825
        $named_dns = [];
826
        if ('' !== $env_vars['dns']) {
827
            $named_dns = explode(' ', $env_vars['dns']);
828
        }
829
        if ($is_inet === '1') {
830
            // ТОЛЬКО, если этот интерфейс для интернет, правим resolv.conf.
831
            // Прописываем основные DNS.
832
            $this->generatePdnsdConfig($named_dns);
833
        }
834
        // Сохрании информацию в базу данных.
835
        $data = [
836
            'subnet'  => $env_vars['subnet'],
837
            'ipaddr'  => $env_vars['ip'],
838
            'gateway' => $env_vars['router'],
839
        ];
840
        if (Verify::isIpAddress($env_vars['ip'])) {
841
            $data['subnet'] = $this->netMaskToCidr($env_vars['subnet']);
842
        } else {
843
            $data['subnet'] = '';
844
        }
845
        $this->updateIfSettings($data, $env_vars['interface']);
846
        $data = [
847
            'primarydns'   => $named_dns[0] ?? '',
848
            'secondarydns' => $named_dns[1] ?? '',
849
        ];
850
        $this->updateDnsSettings($data, $env_vars['interface']);
851
    }
852
853
    /**
854
     * Сохранение настроек сетевого интерфейса.
855
     *
856
     * @param $data
857
     * @param $name
858
     */
859
    public function updateIfSettings($data, $name): void
860
    {
861
        /** @var LanInterfaces $res */
862
        $res = LanInterfaces::findFirst("interface = '$name' AND vlanid=0");
863
        if ($res === null || !$this->settingsIsChange($data, $res->toArray()) ) {
864
            return;
865
        }
866
        foreach ($data as $key => $value) {
867
            $res->writeAttribute($key, $value);
868
        }
869
        $res->save();
870
    }
871
872
    /**
873
     * Сохранение DNS настроек сетевого интерфейса.
874
     *
875
     * @param $data
876
     * @param $name
877
     */
878
    public function updateDnsSettings($data, $name): void
879
    {
880
        /** @var LanInterfaces $res */
881
        $res = LanInterfaces::findFirst("interface = '$name' AND vlanid=0");
882
        if ($res === null || !$this->settingsIsChange($data, $res->toArray())) {
883
            return;
884
        }
885
        foreach ($data as $key => $value) {
886
            $res->writeAttribute($key, $value);
887
        }
888
        if (empty($res->primarydns) && !empty($res->secondarydns)) {
889
            $res->primarydns    = $res->secondarydns;
890
            $res->secondarydns  = '';
891
        }
892
        $res->save();
893
    }
894
895
    /**
896
     * Compares two array
897
     * @param array $data
898
     * @param array $dbData
899
     * @return bool
900
     */
901
    private function settingsIsChange(array $data, array $dbData):bool{
902
        $isChange = false;
903
        foreach ($dbData as $key => $value){
904
            if(!isset($data[$key]) || (string)$value === (string)$data[$key]){
905
                continue;
906
            } 
907
            Util::sysLogMsg(__METHOD__, "Find new network settings: {$key} changed {$value}=>{$data[$key]}");
908
            $isChange = true;
909
        }
910
        return $isChange;
911
    }
912
913
    /**
914
     * Возвращает имя интерфейса по его id.
915
     *
916
     * @param $id_net
917
     *
918
     * @return string
919
     */
920
    public function getInterfaceNameById($id_net): string
921
    {
922
        $res = LanInterfaces::findFirstById($id_net);
923
        if ($res !== null && $res->interface !== null) {
924
            return $res->interface;
925
        }
926
927
        return '';
928
    }
929
930
    /**
931
     * Возвращает список включеннх веб интерейсов
932
     *
933
     * @return array
934
     */
935
    public function getEnabledLanInterfaces(): array
936
    {
937
        /** @var LanInterfaces $res */
938
        $res = LanInterfaces::find('disabled=0');
939
940
        return $res->toArray();
941
    }
942
943
    /**
944
     * Configures LAN interface FROM udhcpc (deconfig)
945
     */
946
    public function udhcpcConfigureDeconfig(): void
947
    {
948
        // Настройка по умолчанию.
949
        $interface = trim(getenv('interface'));
950
        if ( ! Util::isSystemctl()) {
951
            // Для MIKO LFS Edition.
952
            $busyboxPath = Util::which('busybox');
953
            Processes::mwExec("{$busyboxPath} ifconfig {$interface} up");
954
            Processes::mwExec("{$busyboxPath} ifconfig {$interface} 192.168.2.1 netmask 255.255.255.0");
955
        }
956
957
    }
958
    /**
959
     * Сохранение настроек сетевого интерфейса.
960
     *
961
     * @param $data
962
     */
963
    public function updateNetSettings($data): void
964
    {
965
        $res         = LanInterfaces::findFirst("internet = '1'");
966
        $update_inet = false;
967
        if ($res === null) {
968
            $res         = LanInterfaces::findFirst();
969
            $update_inet = true;
970
        }
971
972
        if ($res !== null) {
973
            foreach ($data as $key => $value) {
974
                $res->$key = $value;
975
            }
976
            if ($update_inet === true) {
977
                $res->internet = 1;
978
            }
979
            $res->save();
980
        }
981
    }
982
983
    /**
984
     * Возвращает массив с информацией по сетевым интерфейсам.
985
     *
986
     * @return array
987
     */
988
    public function getInterfaces(): array
989
    {
990
        // Получим все имена PCI интерфейсов (сеть).
991
        $i_names = $this->getInterfacesNames();
992
        $if_list = [];
993
        foreach ($i_names as $i) {
994
            $if_list[$i] = $this->getInterface($i);
995
        }
996
997
        return $if_list;
998
    }
999
1000
    /**
1001
     * Сбор информации по сетевому интерфейсу.
1002
     *
1003
     * @param $name
1004
     *
1005
     * @return array
1006
     */
1007
    public function getInterface($name): array
1008
    {
1009
        $interface = [];
1010
1011
        // Получаем ifconfig's для interface $name.
1012
        $busyboxPath = Util::which('busybox');
1013
        Processes::mwExec("{$busyboxPath} ifconfig $name 2>/dev/null", $output);
1014
        $output = implode(" ", $output);
1015
1016
        // Парсим mac
1017
        preg_match("/HWaddr (\S+)/", $output, $matches);
1018
        $interface['mac'] = (count($matches) > 0) ? $matches[1] : '';
1019
1020
        // Парсим ip.
1021
        preg_match("/inet addr:(\S+)/", $output, $matches);
1022
        $interface['ipaddr'] = (count($matches) > 0) ? $matches[1] : '';
1023
1024
        // Парсим подсеть.
1025
        preg_match("/Mask:(\S+)/", $output, $matches);
1026
        $subnet              = (count($matches) > 0) ? $this->netMaskToCidr($matches[1]) : '';
1027
        $interface['subnet'] = $subnet;
1028
1029
        // Поднят ли интерфейс?
1030
        preg_match("/\s+(UP)\s+/", $output, $matches);
1031
        $status = (count($matches) > 0) ? $matches[1] : '';
1032
        if ($status === "UP") {
1033
            $interface['up'] = true;
1034
        } else {
1035
            $interface['up'] = false;
1036
        }
1037
        $busyboxPath = Util::which('busybox');
1038
        $grepPath    = Util::which('grep');
1039
        $cutPath     = Util::which('cut');
1040
        $routePath   = Util::which('route');
1041
1042
        Processes::mwExec(
1043
            "{$busyboxPath} {$routePath} -n | {$grepPath} {$name} | {$grepPath} \"^0.0.0.0\" | {$cutPath} -d ' ' -f 10",
1044
            $matches
1045
        );
1046
        $gw = (count($matches) > 0) ? $matches[0] : '';
1047
        if (Verify::isIpAddress($gw)) {
1048
            $interface['gateway'] = $gw;
1049
        }
1050
        $catPath = Util::which('cat');
1051
        Processes::mwExec("{$catPath} /etc/resolv.conf | {$grepPath} nameserver | {$cutPath} -d ' ' -f 2", $dnsout);
1052
1053
        $dnsSrv = [];
1054
        foreach ($dnsout as $line) {
1055
            if (Verify::isIpAddress($line)) {
1056
                $dnsSrv[] = $line;
1057
            }
1058
        }
1059
        $interface['dns'] = $dnsSrv;
1060
1061
        return $interface;
1062
    }
1063
}