Skip to content
Snippets Groups Projects
Ifconfig.php 5.43 KiB
Newer Older
Tibo's avatar
Tibo committed
<?php

namespace App\Sensor;

Tibo's avatar
Tibo committed
use App\Sensor;
Tibo's avatar
Tibo committed
use App\Status;
use App\Report;
Tibo's avatar
Tibo committed

use Illuminate\Database\Eloquent\Collection;
Tibo's avatar
Tibo committed

/**
 * Description of MemInfo
 *
 * @author tibo
 */
Tibo's avatar
Tibo committed
class Ifconfig implements Sensor
Tibo's avatar
Tibo committed
{
    public function config(): SensorConfig
        return new SensorConfig(
Thibault Debatty's avatar
Thibault Debatty committed
            "ifconfig",
            "ifconfig",
            "Use ifconfig command to graph network traffic"
        );
    public function analyze(Record $record): Report
Tibo's avatar
Tibo committed
    {
        $report = (new Report())->setTitle("Network : Traffic");

        $interfaces = $this->parseIfconfigRecord($record);
Tibo's avatar
Tibo committed
        return $report->setStatus(Status::ok())
                ->setHTML(view("agent.ifconfig", ["interfaces" => $interfaces]));
Tibo's avatar
Tibo committed
    }

Tibo's avatar
Tibo committed
    public function points(Collection $records)
Tibo's avatar
Tibo committed
    {
Tibo's avatar
Tibo committed
        // Compute the time ordered list of arrays of interfaces
        $interfaces = [];
        foreach ($records as $record) {
            $interfaces[] = $this->parseIfconfigRecord($record);
        }

        // Foreach interface, compute the array of points
        $dataset = [];
        $current_value = [];
        foreach ($interfaces[0] as $interface) {
            $iname = $interface->name;
Tibo's avatar
Tibo committed
            $dataset[$iname . "/TX"] = [
                "name" => $iname . "/TX",
                "points" => []
            ];
Tibo's avatar
Tibo committed

            $dataset[$iname . "/RX"] = [
                "name" => $iname . "/RX",
Tibo's avatar
Tibo committed
                "points" => []
            ];
            $current_value[$interface->name] = $interface;
        }

        for ($i = 1; $i < count($interfaces); $i++) {
            foreach ($interfaces[$i] as $interface) {
                $iname = $interface->name;
Tibo's avatar
Tibo committed
                $previous_value = $current_value[$iname];
                $delta_time = $interface->time - $previous_value->time;

                // RX
                $delta = $interface->rx - $previous_value->rx;
Tibo's avatar
Tibo committed
                if ($delta < 0) {
                    // Can happen after a reboot...
                    $delta = 0;
                }
Tibo's avatar
Tibo committed
                $dataset[$iname . "/RX"]["points"][] = new Point(
Tibo's avatar
Tibo committed
                    $interface->time * 1000,
                    round(8 / 1024 * $delta / $delta_time)
                );
Tibo's avatar
Tibo committed

                // TX
                $delta = $interface->tx - $previous_value->tx;
Tibo's avatar
Tibo committed
                if ($delta < 0) {
                    // Can happen after a reboot...
                    $delta = 0;
                }
Tibo's avatar
Tibo committed
                $dataset[$iname . "/TX"]["points"][] = new Point(
Tibo's avatar
Tibo committed
                    $interface->time * 1000,
                    round(8 / 1024 * $delta / $delta_time)
                );
Tibo's avatar
Tibo committed

                // Keep current value for next record
                $current_value[$iname] = $interface;
Tibo's avatar
Tibo committed
        return array_values($dataset);
    const IFNAME = '/^(?|(\S+)\s+Link encap:|(\S+): flags)/m';
    const IPV4 = '/^\s+inet (?>addr:)?(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})/m';
    const RXTX = '/^\s+RX bytes:(\d+) .*TX bytes:(\d+)/m';
    const RX = '/^\s+RX packets (?>\d+)  bytes (\d+)/m';
    const TX = '/^\s+TX packets (?>\d+)  bytes (\d+)/m';
Tibo's avatar
Tibo committed

    public function parseIfconfigRecord(Record $record)
Tibo's avatar
Tibo committed
    {
        $interfaces = $this->parseIfconfig($record->data);
        foreach ($interfaces as $interface) {
            $interface->time = $record->time;
        }

        return $interfaces;
    }

Tibo's avatar
Tibo committed
    /**
Tibo's avatar
Tibo committed
     * Parse the result of the ifconfig command, skipping every virtual
Tibo's avatar
Tibo committed
     * interfaces (docker, br, lo) and return an array of NetworkInterface
Tibo's avatar
Tibo committed
     * @param string $string
Tibo's avatar
Tibo committed
     * @return \App\Sensor\NetworkInterface[]
     */
    public function parseIfconfig(string $string) : array
Tibo's avatar
Tibo committed
    {
        $allowed_prefixes = [
            "en", "eth", "wl", "venet", "igb", "ax", "tun",
            "bge", "ovpns", "igc"];
Tibo's avatar
Tibo committed

Tibo's avatar
Tibo committed
        if ($string == null) {
            return [];
        }

        $interfaces = [];
        $lines = explode("\n", $string);
        $if = null;
        foreach ($lines as $line) {
            $name = $this->pregMatchOne(self::IFNAME, $line);
Tibo's avatar
Tibo committed
            if ($name !== false) {
                // Starting the section of a new interface
                $if = new NetworkInterface();
                $interfaces[] = $if;
                $if->name = $name;
                continue;
            }

            $ip = $this->pregMatchOne(self::IPV4, $line);
            if ($ip !== false) {
                $if->address = $ip;
                continue;
            }

Tibo's avatar
Tibo committed
            if (preg_match(self::RXTX, $line, $matches) === 1) {
                $if->rx = $matches[1];
                $if->tx = $matches[2];
                continue;
            }

            $rx = $this->pregMatchOne(self::RX, $line);
            if ($rx !== false) {
                $if->rx = $rx;
            }

            $tx = $this->pregMatchOne(self::TX, $line);
            if ($tx !== false) {
                $if->tx = $tx;
            }
Tibo's avatar
Tibo committed
        }
Tibo's avatar
Tibo committed

Tibo's avatar
Tibo committed
        // filter out uninteresting interfaces
        $filtered = [];
        foreach ($interfaces as $interface) {
            if (\starts_with($interface->name, $allowed_prefixes)) {
                $filtered[] = $interface;
            }
Tibo's avatar
Tibo committed
        }
Tibo's avatar
Tibo committed

        return $filtered;
Tibo's avatar
Tibo committed
    }

    public function pregMatchOne(string $pattern, string $string, int $match_group = 1)
Tibo's avatar
Tibo committed
    {
Tibo's avatar
Tibo committed
        if (preg_match($pattern, $string, $matches) === 1) {
            return $matches[$match_group];
Tibo's avatar
Tibo committed
        }

        return false;
    }
}