👨‍🏫 Tutorial Openwrt realtime rate with dhcp leases

sUFF3R

Forum Expert
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
  • 565
    Views
  • 1
    Participants
Last reply from:
sUFF3R

Trending Topics

Online now

Members online
1,189
Guests online
11,754
Total visitors
12,943

Forum statistics

Threads
2,334,989
Posts
29,281,176
Members
1,135,767
Latest member
mark2203
Back
Top