👨‍🏫 Tutorial Openwrt realtime rate with dhcp leases

sUFF3R

Elite
You do not have permission to view the full content of this post. Log in or register now.

1710604892079.png


Tried on openwrt 22
Note: back up first before you replace your controller and view files.

Step 1: replace old controller.
nano /usr/lib/lua/luci/controller/nft-qos.lua
copy & paste


Code:
-- /usr/lib/lua/luci/controller/nft-qos.lua
-- Check if the file exists
if not nixio.fs.access("/tmp/dhcp.leases") then
    print("File /tmp/dhcp.leases does not exist.")
    return
end

local dhcp_leases = io.open("/tmp/dhcp.leases", "r")

-- Check if the file was opened successfully
if not dhcp_leases then
    print("Failed to open file /tmp/dhcp.leases.")
    return
end

-- Create a table to map IP addresses to hostnames
local ip_to_hostname = {}

-- Parse the DHCP leases
for line in dhcp_leases:lines() do
    -- Each line is a string like: "1625469943 00:0c:29:8d:9e:9a 192.168.1.2 myhostname 01:00:0c:29:8d:9e:9a"
    local _, _, ip, hostname = line:match("(%d+) (%S+) (%S+) (%S+)")

    -- Add the mapping from IP address to hostname to the table
    ip_to_hostname[ip] = hostname
end

-- Close the DHCP leases file
dhcp_leases:close()

module("luci.controller.nft-qos", package.seeall)

function index()
    if not nixio.fs.access("/etc/config/nft-qos") then
        return
    end

    local e

    e = entry({"admin", "status", "realtime", "rate"}, template("nft-qos/rate"), _("Rate"), 5)
    e.leaf = true
    e.acl_depends = { "luci-app-nft-qos" }

    e = entry({"admin", "status", "realtime", "rate_status"}, call("action_rate"))
    e.leaf = true
    e.acl_depends = { "luci-app-nft-qos" }

    e = entry({"admin", "services", "nft-qos"}, cbi("nft-qos/nft-qos"), _("QoS over Nftables"), 60)
    e.leaf = true
    e.acl_depends = { "luci-app-nft-qos" }
end
function _action_rate(rv, n)
    local c = nixio.fs.access("/proc/net/ipv6_route") and
        io.popen("nft list chain inet nft-qos-monitor " .. n .. " 2>/dev/null") or
        io.popen("nft list chain ip nft-qos-monitor " .. n .. " 2>/dev/null")

    if c then
        for l in c:lines() do
            local _, i, p, b = l:match('^%s+ip ([^%s]+) ([^%s]+) counter packets (%d+) bytes (%d+)')
            if i and p and b then
                -- Replace the IP address with the hostname
                local hostname = ip_to_hostname[i] or i

                -- Use the hostname and IP address in your JSON output
                rv[#rv + 1] = {
                    rule = {
                        family = "inet",
                        table = "nft-qos-monitor",
                        chain = n,
                        handle = 0,
                        expr = {
                            { match = { right = hostname, ip = i } }, -- Add the IP address here
                            { counter = { packets = p, bytes = b } }
                        }
                    }
                }
            end
        end
        c:close()
    end
end

function action_rate()
    luci.http.prepare_content("application/json")
    local data = { nftables = {} }
    _action_rate(data.nftables, "upload")
    _action_rate(data.nftables, "download")
    luci.http.write_json(data)
end
  • Step 2: replace view.htm
nano /usr/lib/lua/luci/view/nft-qos/rate.htm
copy & paste

Code:
<%#
 Copyright 2018 Rosy Song <rosysong@rosinson.com>
 Licensed to the public under the Apache License 2.0.
-%>
<%+header%>

<script type="text/javascript">//<![CDATA[
    var RC = { };
    var em = 0;
    var ec = 1;
//   console.log(dhcp_leases)
    var rate_table_dl;
    var rate_table_ul;

    function init_bytes(rl, ra) {
        var bytes_pre;
        var obj = { };
        obj.chain = rl.chain;
    obj.device = rl.expr[em].match.right;
        obj.ipaddr = rl.expr[em].match.ip;
        obj.bytes = rl.expr[ec].counter.bytes;
        obj.packets = rl.expr[ec].counter.packets;
        obj.rate = 0;
    //obj.device = deviceMap[obj.ipaddr] || "Unknown device";
        if (RC[obj.chain] && RC[obj.chain][obj.ipaddr])
                bytes_pre = RC[obj.chain][obj.ipaddr];
        else
                bytes_pre = 0;

        obj.rate = (bytes_pre > 0) ? (obj.bytes - bytes_pre) / 3: 0;

        if (!RC[obj.chain])
                RC[obj.chain] = { };
        RC[obj.chain][obj.ipaddr] = obj.bytes;

        if (!ra[obj.chain])
                ra[obj.chain] = [ ];
        ra[obj.chain].push(obj);
    } /* function init_bytes(rl, ra) */

    function bytes_label(bytes) {
        var uby = '<%:kB%>';
        var kby = (bytes / 1024);

        if (kby > 1024) {
            uby = '<%:MB%>';
            kby = (kby / 1024);
        }

        return String.format("%f %s", kby.toFixed(2), uby);
    }

    function print_table(tbl, rs, ra) {
        ra.sort(function(a, b) { return b.rate - a.rate });
        for (var i = 0; i < ra.length; i++) {
            rs.push([
        ra[i].device,
                ra[i].ipaddr,
                bytes_label(ra[i].rate) + '/s',
                bytes_label(ra[i].bytes),
                '%s Pkts.'.format(ra[i].packets),
            ]);
        }
        // cbi_update_table(tbl, rs, '<em><%:No information available%></em>');

        /* BEGIN: Workaround as "cbi_update_table" isn't working */
        rs.forEach((element, i) => {
            var r = document.createElement("TR");
            r.innerHTML = `
                <tr>
                    <td>${element[0]}</th>
                    <td>${element[1]}</td>
                    <td>${element[2]}</td>
                    <td>${element[3]}</td>
             <td>${element[4]}</th>
                </tr>
                    `;
            if (i%2){
                r.classList.add('tr', 'cbi-rowstyle-2');
            } else {
                r.classList.add('tr', 'cbi-rowstyle-1');
            }
       
            tbl.appendChild(r)
            /* END: Workaround as "cbi_update_table" isn't working */
        });
    } /* function print_table(tbl, ra) */

    /* wait for SVG */
    window.setTimeout(
        function() {
            if (!RC)
            {
                window.setTimeout(arguments.callee, 1000);
            }
            else
            {
                rate_table_dl     = document.getElementById('rate_table_dl');
                rate_table_ul     = document.getElementById('rate_table_ul');

                /* render datasets, start update interval */
                L.Request.poll.add(3, '<%=build_url("admin/status/realtime/rate_status")%>', null,
                    function(x, json)
                    {
                        var RA = {};
                        var rows_dl = [];
                        var rows_ul = [];

                        var rules = json.nftables;
                        for (var i = 0; i < rules.length; i++)
                        {
                            if (!rules[i].rule)
                                continue;
                            if (rules[i].rule.table != 'nft-qos-monitor')
                                continue;

                            var rl = rules[i].rule;
                            switch (rl.chain)
                            {
                                case 'download':
                                case 'upload': init_bytes(rl, RA); break;
                            }
                        } /* for (var i = 0; i < rules.length; i++) */

                        /* display the result */
                        if (RA.download) {
                            while (rate_table_dl.firstElementChild !== rate_table_dl.lastElementChild)
                                rate_table_dl.removeChild(rate_table_dl.lastElementChild);
                            print_table(rate_table_dl, rows_dl, RA.download);
                        }
                        if (RA.upload) {
                            while (rate_table_ul.firstElementChild !== rate_table_ul.lastElementChild)
                                rate_table_ul.removeChild(rate_table_ul.lastElementChild);
                            print_table(rate_table_ul, rows_ul, RA.upload);
                        }

                    } /* function(x, json) */
                );

                L.Request.poll.start();
            }
        }, 1000
    );
//]]></script>


<h2 name="content"><%:Realtime Rate%></h2>

<div class="cbi-map-descr"><%:This page gives an overview over currently download/upload rate.%></div>

<fieldset class="cbi-section" id="cbi-table-table">
    <legend><%:Realtime Download Rate%></legend>
    <div class="cbi-section-node">
        <div class="table" id="rate_table_dl">
            <div class="tr table-titles">
        <div class="th col-2 hide-xs"><%:Device%></div>
                <div class="th col-2 hide-xs"><%:Ipaddress%></div>
                <div class="th col-2"><%:Download Rate%></div>
                <div class="th col-7"><%:Bytes Total%></div>
                <div class="th col-7"><%:Packets Total%></div>
            </div>
        </div>
    </div>
</fieldset>

<fieldset class="cbi-section" id="cbi-table-table">
    <legend><%:Realtime Upload Rate%></legend>
    <div class="cbi-section-node">
        <div class="table" id="rate_table_ul">
            <div class="tr table-titles">
        <div class="th col-2 hide-xs"><%:Device%></div>
                <div class="th col-2 hide-xs"><%:Ipaddress%></div>
                <div class="th col-2"><%:Upload Rate%></div>
                <div class="th col-7"><%:Bytes Total%></div>
                <div class="th col-7"><%:Packets Total%></div>
            </div>
        </div>
    </div>
</fieldset>

<%+footer%>
  • Step 4: Restart router to apply changes, if you dont want to restart then...
ls /tmp/
rm -r /tmp/luci-indexcache.xxxxx.xxxx..json
rm -r /tmp/luci-indexcache.xxxxx.lua

/etc/init.d/uhttpd restart


 

About this Thread

  • 0
    Replies
  • 557
    Views
  • 1
    Participants
Last reply from:
sUFF3R

Trending Topics

Online now

Members online
1,078
Guests online
3,814
Total visitors
4,892

Forum statistics

Threads
2,325,451
Posts
29,222,022
Members
1,169,312
Latest member
jaa5i1
Back
Top