Skip to content

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.

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 MikroTik

When you add a NAS in NAS / Routers with API credentials filled in, ProxPanel automatically:

  1. Connects to RouterOS via the API.
  2. Runs /ip/pool/print to list every pool.
  3. For each pool, expands the range(s) into individual IPs and inserts rows into ip_pool_assignments with status = available.
  4. Runs /ppp/active/print to list current sessions.
  5. 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.

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:

StatusMeaning
availableFree for allocation.
in_useA live or recently-active PPPoE session holds this IP.
reservedManually 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 ipa
WHERE 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 1
FOR UPDATE SKIP LOCKED;
SourceWhat it means
ip_pool_assignments.statusThe pool tracker says this IP is free.
radreply.Framed-IP-AddressNo 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.

  1. Subscriber dials PPPoE. MikroTik forwards Access-Request to ProxPanel.
  2. The RADIUS server (server.go: handleAuth) authenticates the subscriber.
  3. It looks at the subscriber’s service:
    • If the service has pool_name set → call ippool.AllocateIP(pool_name, username, ...).
    • If the subscriber has static_ip set → use that.
    • If neither, build the reply without Framed-IP-Address (router falls back to its own pool — works only if remote-address is set on the PPP profile, which is not the recommended config).
  4. The allocated IP is written to radreply as Framed-IP-Address (for next reconnect) and inserted into the Access-Accept.
  5. ip_pool_assignments row is updated: status = in_use, username = ..., assigned_at = NOW().
  6. MikroTik receives Access-Accept, sets up the queue, the PPPoE session comes up.

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.

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.

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.

  1. Go to NAS / Routers, click the affected NAS.
  2. Click Re-import pools (the small refresh icon next to the pool list).
  3. Watch the panel logs: IPPoolService: Imported N IPs from <pool>.
  4. 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.

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:

  1. Identify affected users: query ip_pool_assignments joined to subscribers and services, looking for pool_name != service.pool_name.
  2. For each, delete the Framed-IP-Address row from radreply so the next reconnect re-allocates.
  3. Mark the offending ip_pool_assignments row as available and clear username / subscriber_id.
  4. 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.

  1. Wait 5 minutes — the Stale Session Cleanup sweeper will close radacct rows with no interim update for 30+ minutes.
  2. After cleanup, run Sync active sessions on the NAS to mark the freed IPs back to available.
  3. If the sweeper didn’t catch them all (acctstarttime > 30 min ago but no acctupdatetime), 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.