IP Pool Management
ProxPanel takes IP allocation away from RouterOS and does it itself. The reason is simple: when two systems both think they own a pool, duplicates are inevitable. Left alone, RouterOS happily hands <subscriber-ip> to two PPPoE sessions if the timing aligns — and the customer-facing symptom is Destination host unreachable half the time.
This page covers the import process, the ip_pool_assignments tracking table, the three-source duplicate check, and how to recover from a panel that fell out of sync with the router.
Configuration flow
Section titled “Configuration flow”Step 1: Define IP pools on the MikroTik (/ip pool add) ↓Step 2: Add the MikroTik to ProxPanel WITH API credentials ↓ (auto)Step 3: ProxPanel imports each pool's range into ip_pool_assignments ↓ (auto)Step 4: ProxPanel reads /ppp/active to mark in-use IPs ↓Step 5: PPP profile on the router is set to remote-address=none ↓Step 6: New PPPoE sessions get IPs from ProxPanel, not from MikroTikHow auto-import works
Section titled “How auto-import works”When you add a NAS in NAS / Routers with API credentials filled in, ProxPanel automatically:
- Connects to RouterOS via the API.
- Runs
/ip/pool/printto list every pool. - For each pool, expands the range(s) into individual IPs and inserts rows into
ip_pool_assignmentswithstatus = available. - Runs
/ppp/active/printto list current sessions. - For each active session, marks the IP as
status = in_use, associates the username and session ID.
The relevant code lives in:
internal/services/ip_pool_service.go— orchestration (ImportPoolsFromMikrotik,SyncActiveSessionsFromMikrotik)internal/ippool/pool.go— allocation and release primitives (AllocateIP,ReleaseIPForUser)internal/mikrotik/client.go— RouterOS API calls (GetIPPools,GetAllActiveSessions)
A <subscriber-ip>/24 pool produces 254 rows. At 30 K subscribers across several pools, the table runs to ~75 K rows — fast to scan, no problem to index.
The ip_pool_assignments table
Section titled “The ip_pool_assignments table”CREATE TABLE ip_pool_assignments ( id SERIAL PRIMARY KEY, ip_address VARCHAR(15) NOT NULL UNIQUE, pool_name VARCHAR(64) NOT NULL, status VARCHAR(20) DEFAULT 'available', username VARCHAR(100), subscriber_id INTEGER, nas_id INTEGER, assigned_at TIMESTAMP, created_at TIMESTAMP DEFAULT NOW(), updated_at TIMESTAMP DEFAULT NOW());The UNIQUE (ip_address) constraint is what makes this safe — at the database level it’s impossible to mark the same IP in-use twice. Inserts that violate it are caught and the allocation retries with a different IP.
Statuses:
| Status | Meaning |
|---|---|
available | Free for allocation. |
in_use | A live or recently-active PPPoE session holds this IP. |
reserved | Manually held back by the operator (e.g. the gateway). Not returned from AllocateIP. |
The three-source duplicate check (v1.0.179)
Section titled “The three-source duplicate check (v1.0.179)”Before v1.0.179, AllocateIP only checked ip_pool_assignments for availability. That table can fall out of sync with radreply if a subscriber has a manually-assigned Framed-IP-Address — the allocation logic would happily hand the same IP to a new connection.
The v1.0.179 fix queries three sources in a single SQL statement and excludes any IP that shows up in any of them:
SELECT ipa.* FROM ip_pool_assignments ipaWHERE ipa.pool_name = ? AND ipa.status = 'available' AND NOT EXISTS ( SELECT 1 FROM radreply r WHERE r.attribute = 'Framed-IP-Address' AND r.value = ipa.ip_address AND r.username != ? )LIMIT 1FOR UPDATE SKIP LOCKED;| Source | What it means |
|---|---|
ip_pool_assignments.status | The pool tracker says this IP is free. |
radreply.Framed-IP-Address | No other subscriber has a static-IP reservation for this address. |
subscribers.is_online + subscribers.ip_address | (checked separately in the RADIUS server’s findAvailableIP) — no online subscriber actually holds the IP. |
The combination of these checks closed the last known duplicate-IP path. The FOR UPDATE SKIP LOCKED ensures concurrent allocations from two parallel auth requests can’t both claim the same row.
How an IP gets allocated at PPPoE auth
Section titled “How an IP gets allocated at PPPoE auth”- Subscriber dials PPPoE. MikroTik forwards Access-Request to ProxPanel.
- The RADIUS server (
server.go: handleAuth) authenticates the subscriber. - It looks at the subscriber’s service:
- If the service has
pool_nameset → callippool.AllocateIP(pool_name, username, ...). - If the subscriber has
static_ipset → use that. - If neither, build the reply without
Framed-IP-Address(router falls back to its own pool — works only ifremote-addressis set on the PPP profile, which is not the recommended config).
- If the service has
- The allocated IP is written to
radreplyasFramed-IP-Address(for next reconnect) and inserted into the Access-Accept. ip_pool_assignmentsrow is updated:status = in_use,username = ...,assigned_at = NOW().- MikroTik receives Access-Accept, sets up the queue, the PPPoE session comes up.
Why remote-address=none matters
Section titled “Why remote-address=none matters”If the MikroTik’s PPP profile has remote-address=<pool-name>, RouterOS ignores the Framed-IP-Address ProxPanel sent and assigns from its own pool instead. The panel thinks subscriber A has <subscriber-ip>; the router actually gave them <subscriber-ip>. Sooner or later, the panel hands <subscriber-ip> to subscriber B — duplicate IP, dropped traffic, support call.
The PPP profile must have remote-address=none for ProxPanel-driven allocation to work. See MikroTik Integration → PPP profile.
Per-NAS pool isolation
Section titled “Per-NAS pool isolation”ip_pool_assignments.nas_id ties each IP to a specific MikroTik. Two routers with overlapping pool names (e.g. both have a pool called 2M) get separate rows in the assignments table; allocations are scoped to the requesting NAS. Pool names don’t have to be unique across the fleet.
This matters when you have multiple BNGs covering different geographies or product tiers.
Common workflows
Section titled “Common workflows”Re-import all pools after a NAS configuration change
Section titled “Re-import all pools after a NAS configuration change”Sometimes the MikroTik admin adds a new pool, or expands an existing range. The panel won’t know about new IPs until the import runs again.
- Go to NAS / Routers, click the affected NAS.
- Click Re-import pools (the small refresh icon next to the pool list).
- Watch the panel logs:
IPPoolService: Imported N IPs from <pool>. - Optionally run Sync active sessions to refresh which IPs are currently in use.
A re-import is safe — existing in_use rows are preserved; only available rows are recreated for new IPs.
Recover from “wrong-pool” assignments
Section titled “Recover from “wrong-pool” assignments”If you discover users have been assigned IPs from a pool that doesn’t match their service plan (the cause is usually old data with mis-tagged pool_name), the fix is:
- Identify affected users: query
ip_pool_assignmentsjoined tosubscribersandservices, looking forpool_name != service.pool_name. - For each, delete the
Framed-IP-Addressrow fromradreplyso the next reconnect re-allocates. - Mark the offending
ip_pool_assignmentsrow asavailableand clearusername/subscriber_id. - Force a reconnect (CoA Disconnect — see CoA & Disconnect). The fresh auth allocates from the correct pool.
This is the procedure used in v1.0.226 on a 295-subscriber site that had 20 wrong-pool users.
”All my IPs show in-use but I have 200 online subscribers”
Section titled “”All my IPs show in-use but I have 200 online subscribers””The radacct table has 1000 open rows because MikroTik didn’t send STOP packets after a reboot. Every “open” radacct row marks an IP as in-use.
- Wait 5 minutes — the Stale Session Cleanup sweeper will close radacct rows with no interim update for 30+ minutes.
- After cleanup, run Sync active sessions on the NAS to mark the freed IPs back to
available. - If the sweeper didn’t catch them all (
acctstarttime > 30 min agobut noacctupdatetime), close manually:UPDATE radacct SET acctstoptime = NOW() WHERE acctstoptime IS NULL AND acctupdatetime < NOW() - INTERVAL '30 minutes';
Operator-only feature: ProISP IP Management
Section titled “Operator-only feature: ProISP IP Management”The setting proisp_ip_management (Settings → System) controls whether ProxPanel takes over allocation at all. When off, the panel falls back to the legacy “let MikroTik handle it” mode — useful only if you’re migrating an existing install and don’t want any allocation surprises during the cutover. Fresh installs enable it automatically.
Related pages
Section titled “Related pages”- Static IP Assignment —
Framed-IP-Addressper subscriber, conflict resolver. - MikroTik Integration — the PPP profile fix that makes pool allocation work.
- RADIUS Server Setup — where
Framed-IP-AddressandFramed-Poolcome from in the reply. - Stale Session Cleanup — why a fresh install can show “all IPs in use” until the sweeper runs.