---
title: 'Access Points and Controllers'
source: 'https://academia.sh/en/courses/wireless-and-security/access-points-and-controllers'
course: 'Wireless Networks and Network Security'
language: en
updated: '2026-08-17T18:07:22+00:00'
license: 'CC BY-SA 4.0'
---

# Access Points and Controllers

Whether the decision is made at the access point or at the controller is measured: three points running on separately given budgets associate 25 clients and produce 2 false admits, a shared policy associates 23 clients and leaks zero, and the clients running at full rate rise from 5 to 9.

The previous lesson counted the decision a single access point makes on its own: it
associates the client if it hears it above the threshold, and does not if it does not. 23 of
the forty clients associated, 4 inside the cell could not, and no one leaked in from outside.

In a building, though, there is no single access point. There are dozens of points, and each
one's signal budget, channel, and threshold is a given value — set somewhere, at some time, by
someone. This lesson's question is: **who gives that value, and where is the decision made?**
If the same client can get two different answers from two access points, the owner of the
answer is not the client's condition but which one it happens to land on.

## Association Is an Exchange

A client "seeing" the network and connecting to it are separate matters. A frame exchange
stands between the two, and the decision is made at a specific step of that exchange.

```text
# taught management frame sequence , fictional and not executed

1. access point -> everyone     beacon
                                  network: measurement.test
                                  channel: 4 , band: N
                                  supported tiers: 6 4 2 1

2. client -> everyone           probe request
                                  network: measurement.test

3. access point -> client       probe response
                                  measured signal strength: 26

4. client -> access point       association request
                                  requested tier: 4

5. access point -> client       association response
                                  result: accept , granted tier: 2
                                  <-- THE DECISION IS MADE AT THIS STEP
```

The fifth step is where the decision is made, and it says two things at once: was the client
accepted, and at which rate tier. A "no" answer to the first question means, in the client's
eyes, that the network does not exist; the answer to the second question shows up in no
indicator at all.

The critical point is this: **who writes** the fifth step? The access point sends the frame,
but the rule that determines the answer's content may be defined somewhere else entirely.

## Two Management Models

An **independent access point** carries its own configuration. Its channel, signal budget, and
threshold live in its own memory; it makes the association decision by looking only at the
signal strength it itself measures. It does not know which channel its neighbor is on, what
budget it broadcasts with, or which client it rejected.

The **controller-based model** splits the decision in two. The **control plane** is
centralized: channel assignments, signal budgets, thresholds, and policies are held in a single
place and distributed to the points. The **data plane** can stay local, or it can be tunnelled
to the controller; that is a separate choice, and not what this lesson measures.

What the controller really carries is a **shared view**. When every point's measurements are
collected in a single place, things a point could never know on its own become knowable: where
a channel is being reused, how many points hear a given client, where one point's budget cuts
into its neighbor's coverage.

This resembles the Switching and Routing course's axis, but it is not the same. There, the
question asked was whether copies of a shared truth agreed with each other; here, the question
asked is whether copies **exist at all**. Independent points do not carry stale copies of a
shared truth — there is no shared truth; each point carries its own value alone.

The controller does not have to be a device, either. It can be dedicated hardware, a server on
the network, or a role taken on by one of the points; nothing changes from the measurement's
point of view. What is measured is not the box itself but **whether the decision comes from a
single place**.

## Where the Data Plane Flows

Centralizing the control plane does not centralize the data plane; that is a second, separate
choice.

With **local switching**, the client's frame leaves the access point and enters the local
network directly. The controller only supplies the rule; it never sees the traffic. The path is
short, traffic in flight is unaffected if the controller is cut off, but enforcing the policy
depends on the points being correctly configured.

With a **tunnelled data plane**, the frame is carried to the controller first, and exits to the
network from there. What this buys is a policy enforced from a single point: all wireless
traffic passes through the same place and hits the same rule. What it costs is the path — even
two clients standing side by side have their frames travel all the way to the controller and
back — and the controller becoming a bottleneck.

The choice is not binary either: some traffic can be left local while other traffic is
tunnelled. This lesson's measurement changes only the control plane; where the data plane flows
does not enter the association decision and does not mix into the measurement.

## Configuration Drift

The independent model's countable flaw is **configuration drift**. Points are not set up at
the same time. The first point is raised in response to a coverage complaint, the second stays
as installed, the third is cut back in response to an interference complaint. Every step is
reasonable on its own; none of them leaves a record, and no one sees the total.

The inevitability of drift is a scale problem. The interaction of two points can be held in
mind; the forty-five pairwise interactions of ten points cannot. Raising one point's budget does
not only grow that point's coverage; it also changes where its neighbors' coverage gets cut
off. Deciding point by point means there is no place at all that can see this interaction.

The result is three different boundaries drawn within the same network. The measurement sets
this up with three budgets: one point at 58, one at 52, one at 46. Set against it, the
controller regime distributes a single value, 52, to all three points. The only thing that
changes is **where the decision is made**; the clients, the distances, the interference, and
the intent all stay the same.

The measurement's assumptions:

- **WN7.** Forty clients come from the previous lesson's fiction; distance and interference
  are the same. The oracle is again the cell intent: the client inside the 30 m radius cell
  should have been admitted.
- **WN8.** Which access point hears which client comes from a separate generator and is
  independent of distance. The `distance` field is the distance to the point that hears the
  client.
- **WN9.** There are three points, and their budgets in the independent regime are 58, 52, and
  46. These three values are the fiction of configuration drift, not a power sweep; the sweep
  itself is the subject of the next lesson.
- **WN10.** In the controller regime, all three points' budget is 52. This is the only thing
  that changes between the two regimes.
- **WN11.** The controller's distribution is instant and lossless. Distribution delay and the
  controller becoming unreachable are not measured; both are addressed separately in the text.
- **WN12.** The consistency measurement evaluates the same client separately against three
  budgets. This is not a movement fiction; the client stays put, only the evaluating point
  changes. The client actually changing location is the subject of the roaming lesson.
- **WN13.** The set is forty clients; the smallest measurable difference is **1/40 = 0.025**.

## Measurement

```python
"""Where the decision sits: independent access points, or a shared controller."""
SEED = 20260811
PLACEMENT = 20260812     # which access point hears which client
RADIUS = 30
SHARED_POWER = 52        # the single value the controller distributes to every point
POINTS = (("A", 58), ("B", 52), ("C", 46))    # separately given budgets
TIERS = ((34, 6), (28, 4), (22, 2), (18, 1))


def generator(seed):
    d = seed % 2147483646 + 1

    def r(n):
        nonlocal d
        d = (d * 48271) % 2147483647
        return d % n
    return r


def clients(count=40, seed=SEED):
    r, pool = generator(seed), []
    for i in range(count):
        pool.append({"no": i + 1, "distance": 5 + r(41), "interference": r(13)})
    y = generator(PLACEMENT)
    for c in pool:
        c["point"] = y(3)
    return pool


def signal(c, power):
    return power - c["distance"] - c["interference"]


def rate_tier(c, power):
    for threshold, tier in TIERS:
        if signal(c, power) >= threshold:
            return tier
    return 0


def cell_intent(c, radius=RADIUS):
    return c["distance"] <= radius


def medium(c, power):
    return rate_tier(c, power) > 0


def gap(events, intent, mechanism):
    d = {"correct_admit": 0, "correct_deny": 0, "false_admit": 0, "false_deny": 0}
    for e in events:
        n, m = intent(e), mechanism(e)
        if n and m:
            d["correct_admit"] += 1
        elif not n and not m:
            d["correct_deny"] += 1
        elif m:
            d["false_admit"] += 1
        else:
            d["false_deny"] += 1
    return d


def local(c):
    """Decision at the access point: the budget of the point that hears the client applies."""
    return medium(c, POINTS[c["point"]][1])


def central(c):
    """Decision at the controller: one policy across every point."""
    return medium(c, SHARED_POWER)


pool = clients()
print(f"{'point':>5s} {'budget':>6s} {'clients':>8s} {'associated':>10s} "
      f"{'false admit':>12s} {'false deny':>12s}")
for i, (name, power) in enumerate(POINTS):
    k = [c for c in pool if c["point"] == i]
    d = gap(k, cell_intent, lambda c, p=power: medium(c, p))
    print(f"{name:>5s} {power:6d} {len(k):8d} {d['correct_admit'] + d['false_admit']:10d} "
          f"{d['false_admit']:12d} {d['false_deny']:12d}")
print()

print(f"{'regime':<12s} {'associated':>10s} {'full rate':>9s} {'correct':>7s} "
      f"{'false admit':>12s} {'false deny':>12s}")
for name, mechanism, tier_fn in (("independent", local, lambda c: rate_tier(c, POINTS[c["point"]][1])),
                                  ("controller", central, lambda c: rate_tier(c, SHARED_POWER))):
    d = gap(pool, cell_intent, mechanism)
    print(f"{name:<12s} {d['correct_admit'] + d['false_admit']:10d} "
          f"{sum(1 for c in pool if tier_fn(c) == 6):9d} "
          f"{d['correct_admit'] + d['correct_deny']:7d} "
          f"{d['false_admit']:12d} {d['false_deny']:12d}")
print()

diverging = [c for c in pool if len({medium(c, p) for _, p in POINTS}) > 1]
tier_diverging = [c for c in pool if len({rate_tier(c, p) for _, p in POINTS}) > 1]
print(f"clients whose association decision changes point to point: {len(diverging)}")
print(f"clients whose rate tier changes point to point: {len(tier_diverging)}")
print()

print(f"{'point pair':<11s} {'only first':>11s} {'only second':>12s} "
      f"{'tier differs':>13s}")
for label, g1, g2 in (("A / B", 58, 52), ("B / C", 52, 46), ("A / C", 58, 46)):
    print(f"{label:<11s} {sum(1 for c in pool if medium(c, g1) and not medium(c, g2)):11d} "
          f"{sum(1 for c in pool if medium(c, g2) and not medium(c, g1)):12d} "
          f"{sum(1 for c in pool if rate_tier(c, g2) != rate_tier(c, g1)):13d}")
```

```
point budget  clients associated  false admit   false deny
    A     58       10          8            2            0
    B     52       18          9            0            2
    C     46       12          8            0            2

regime       associated full rate correct  false admit   false deny
independent          25         5      34            2            4
controller           23         9      36            0            4

clients whose association decision changes point to point: 14
clients whose rate tier changes point to point: 29

point pair   only first  only second  tier differs
A / B                 8            0            22
B / C                 6            0            21
A / C                14            0            29
```

## More Connect, Fewer Work

The independent regime associates **25** clients, the controller regime **23**. A comparison
that looked only at this column would declare the independent model the winner: two more
clients connected.

The column next to it upsets this. Clients running at full rate tier are **5** in the
independent regime, **9** in the controller regime. More clients associate and fewer work. The
reason shows up in the table above: point A, pulled up to a budget of 58, gathers its share of
clients from too far away, while point C, cut down to 46, drops its share into lower tiers. The
average is a distribution that loses at both ends.

Reading the two directions separately clarifies the table further. **False admit** is 2 in the
independent regime, 0 in the controller regime. Both leaks come from point A — the table above
shows this directly: A's false admit is 2, the other two's is 0. **False deny**, on the other
hand, is **4 in both regimes**.

This equality is the lesson's most important line. Raising one point's budget **bought two
leaks and did not reduce the shortfall by a single client.** The coverage gain went not to
where it was needed but outside the area already covered. This is the course's second claim:
**growing the apparatus does not bring it closer to intent.**

## Who Owns the Decision

The bottom two lines measure consistency. For **14** of the forty clients the association
decision changes depending on which point evaluates it; for **29** the rate tier changes. So
for roughly two-thirds of the forty clients, the answer to "what does this client get" is
written not in the client's condition but in which one it lands on.

The last table separates the pairs. Between A and C, **14** clients can associate only with A,
and **none** can associate only with C — the asymmetry is total, because a lower budget only
costs. The number of clients whose tier differs is **22** between A and B, **21** between B and
C, **29** between A and C.

These numbers do not look like a coverage problem. A complaining user says "the network is slow
in that corner of the building"; a measurement finds no coverage gap in that corner, because
there is none. What has to be found instead is that the point covering that corner runs on a
different budget.

A direct consequence of the inconsistency is paid in roaming. As a client moves between two
points, it decides "I'm staying here" versus "I'm moving to the other one" based on the signal
strength it itself measures; and the signal it measures depends on the point's budget. A
high-budget point keeps holding a client from far away, and the handover comes late; a
low-budget point lets the client go early. When two budgets stand side by side within the same
network, the handover threshold does not mean the same thing everywhere in the network. What
this lesson measures is only the **separation** of the decision; when the handover itself
should happen, and early versus late handover being two separate errors, is the subject of the
roaming lesson.

## The Controller's Own Flaws

The controller model's measured advantage is not free, and it is paid in two places.

The first is a **single point of failure**. What do the points do when the controller becomes
unreachable? There are two behaviors: either they stop new association and preserve the
policy, or they keep running with the last configuration in hand, **falling back to
standalone**. If the second is chosen and the outage runs long, the network slowly drifts back
to the independent regime — this time with no one noticing. The measurement's independent row
is the state at the end of a long stretch spent without a controller.

The second is a **shared flaw**. In the independent model, a wrong value breaks one point; in
the controller model, a wrong value is distributed to **every** point. The controller regime in
the measurement leaks zero because it distributes 52; had it distributed 58, the leak would have
spread across the whole network. A shared view shares the shared error too.

Both of these flaws are narrowed by **recording** the configuration: if what value was
distributed, when it changed, and which point it reached are written down, drift becomes a
matter of measurement. If it is not written down, drift shows up only through user complaints,
and the complaint points to the wrong place.

## Summary

- Association is a frame exchange, and the decision is made at the last step; that step writes
  both the acceptance and the granted rate tier, and the second shows up in no indicator.
- An independent access point decides with its own budget and does not know its neighbor; the
  controller model builds a shared view by centralizing the control plane.
- Configuration drift is a measurable flaw: three separate budgets associate **25** clients and
  produce **2** false admits, a shared policy associates **23** and leaks **0**.
- **False deny is 4 in both regimes.** Raising one point's budget bought only leakage; it never
  reduced the shortfall.
- For **14** of the forty clients the association decision, and for **29** the rate tier,
  changes with which point they land on; this does not look like a coverage gap.

## Next Step

In this measurement the budgets came from the fiction, and why they were set to those values
was never asked. Yet a wireless network's design is exactly this question: which channel, which
budget goes to each point, and where should the cells touch? The next lesson sweeps the budget
alone — 58, 52, 46, and 40 — and counts both directions at every value. There it will be seen
that lowering the budget zeroes out the leak, and the moment it does, the shortfall it leaves
inside the cell grows silently.
