Шукати в цьому блозі

вівторок, 18 лютого 2025 р.

Обмеження швидкості на етапі видачі ip-адреси DHCP-сервером в MikroTIK

Час від часу було необхідно обмежити швидкість тим чи іншим клієнтам локальної мережі, які отримують адресу по DHCP.

Що робили?

По-перше, створювали чергу для локальної мережі, наприклад так:

/queue simple add burst-limit=200M/200M burst-threshold=2M/2M burst-time=5s/5s max-limit=198M/198M name=lan queue=pcq-upload-src/pcq-download-dst target=bridge-local total-queue=pcq-dst-src

По-друге, фіксували за mac-адресою ту чи іншу ip-адресу. По-третє, створювали чергу, яку через parent підв'язували до створеної черги локальної мережі і обмежували швидкість для саме цієї ip-адреси.

Така схема робоча, але не дуже зручна, коли треба обмежити швидкість, скажемо так, масово.

Припустимо купуємо у провайдера 100 мегабіт. У нас 10 робочих місць. Задача - кожному роздати по 10 мегабіт.

Звісно можна зафіксувати адреси, а можна…

Варіант 1. Старе рішення.

Перший варіант, який приходить на думку: обійти таблицю leases і під кожну виділену ip-адресу створити відповідне правило в черзі. Це вирішується якось так (позичено і перероблено з форуму MikroTIK):

/ip dhcp-server lease
:foreach x in=[find] do={
 
    # grab variables for use below
    :local leaseaddr ([get $x address])
    :local leasemacaddr [get $x mac-address]
    :local leasehostname [get $x host-name]
    :local leasename [get $x comment]
    :local queuecomment

    :local minlimit 192.168.100.20
    :local maxlimit 192.168.100.100
 
    :local leaseinqueue false
 
    /queue simple
    :foreach y in=[find] do={
 
        #grab variables for use below
        :local queuetargetaddr [get $y target]
        :set queuecomment [get $y comment]
 
        # Isolate information  from the comment field (MAC, Hostname)
        :local queuemac [:pick $queuecomment 4 21]
        :local queuehostname [:pick $queuecomment 22 [:len $queuecomment]]
 
        # If MAC from lease matches the queue MAC then refresh the queue item
        :if ($queuemac = $leasemacaddr) do={
            # build a comment field
            :set queuecomment ("dtq," . $leasemacaddr . "," . $leasehostname)

            set $y target=$leaseaddr comment=$queuecomment
            :if ($leasename != "") do= {
                set $y name=($leasename . " (" . $leasemacaddr . ")")
            } else= {
                :if ($leasehostname != "") do= {
                    set $y name=($leasehostname . " (" . $leasemacaddr . ")")
                } else= {
                    set $y name=$leasemacaddr
                }
            }
            :set leaseinqueue true
        } else= {
            # if ip exists for this lease but mac is different then update mac/hostname and reset counter
            :if ($queuetargetaddr = $leaseaddr) do={
                # build a comment field
                :set queuecomment ("dtq," . $leasemacaddr . "," . $leasehostname)
 
                set $y comment=$queuecomment
                reset-counters $y
                :if ($leasename != "") do= {
                    set $y name=($leasename . " (" . $leasemacaddr . ")")
                } else= {
                    :if ($leasehostname != "") do= {
                        set $y name=($leasehostname . " (" . $leasemacaddr . ")")
                    } else= {
                        set $y name=$leasemacaddr
                    }
                }
                :set leaseinqueue true
            }
        }
    }
 
    # There was not an existing entry so add one for this lease
    :if ($leaseinqueue = false) do={
        # build a comment field
        :set queuecomment ("dtq," . $leasemacaddr . "," . $leasehostname)
        # build command
        :local cmd "/queue simple add parent=lan target=$leaseaddr max-limit=100M/100M comment=$queuecomment"
        :if ($leasename != "") do={ 
            :set cmd "$cmd name=\"$leasename ($leasemacaddr)\"" 
        } else= {
            :if ($leasehostname != "") do={
                :set cmd "$cmd name=\"$leasehostname ($leasemacaddr)\""
            } else= {
                :set cmd "$cmd name=\"$leasemacaddr\""
            }
        }
 
        # If within the range is executed
        :if ($leaseaddr >= $minlimit && $leaseaddr <= $maxlimit) do={
            :execute $cmd
        }
    }
}
 
# Cleanup Routine - remove dynamic entries that no longer exist in the lease table
/queue simple
:foreach z in=[find] do={
    :local queuecomment [get $z comment]
    :local queue1stpart [:pick $queuecomment 0 3]
    :local queue2ndpart [:pick $queuecomment 4 21]
    :if ( $queue1stpart = "dtq") do={
        :if ( [/ip dhcp-server lease find mac-address=$queue2ndpart] = "") do={
            :log info ("DTQ: Removing stale entry for MAC Address - " . $queue2ndpart)
            remove $z
        }
    }
}

Це дійсно працює. Цей скрипт можна додати в Scheduler і насолоджуватися його роботою. Звісно за необхідності варто змінити на свій смак встановлення локальної змінної cmd.

Цей метод працює на будь-яких версіях RouterOS (правда на дуже старих не перевіряв, але має працювати, бо тут нічого складного і специфічного немає.

Явним недоліком є саме необхідність використання Scheduler, щоб підтримувати черги в актуальному стані.

Про те є інший, більш елегантний метод. Більш легкий метод.

Варіант 2. Lease Script.

В WinBox (або в WebFig, кому як подобається) переходимо в IP → DHCP Server. Відкриваємо налаштування свого DHCP. Переходимо у вкладку Script. Тут у нас велике поле для Lease Script, який буде виглядати приблизно так:

:local queueName "lan-$leaseActIP-$leaseActMAC";
:if ($leaseBound = "1") do={
    :log info "DHCP Lease START: $queueName"
    :local hostName [/ip dhcp-server lease get [find where active-mac-address=$leaseActMAC && active-address=$leaseActIP] host-name]
    /queue simple add burst-limit=50M/50M burst-threshold=1M/1M burst-time=5s/5s max-limit=49M/49M total-burst-limit=50M total-burst-threshold=1M total-burst-time=5s total-limit-at=48M total-max-limit=49M total-queue=pcq-dst-src name=$queueName parent=lan target=($leaseActIP . "/32") comment="$leaseActIP [ $leaseActMAC ] $hostName";
} else={
    :log info "DHCP Lease STOP: $queueName"
    /queue simple remove $queueName
}

І все. Скрипт викликається DHCP-сервером коли він «видає» або «забирає» ip-адресу. Відповідним чином встановлюється змінн $leaseBound, коли сервер видає адресу вона дорівнює "1".

Звісно, що тут можна додати й інші умови, інші виключення, тощо. Можна зробити й іншу структуру самих черг. Але, сподіваюся, що все й без того прозоро й зрозуміло. Користуймося.