ip(); /* * A device that has only just associated may have no IP yet, so UniFi's * client table does not know it. Retry briefly rather than telling a * pupil their device is not connected when it is about to be. * * Bounded on purpose: this runs inside a web request, and a longer wait * would hold a worker per connecting device at exactly the moment a * class of them arrives at once. */ $client = null; for ($attempt = 1; $attempt <= self::ATTEMPTS; $attempt++) { $client = $this->unifi->findClientByIp($ip); if ($client) { break; } if ($attempt < self::ATTEMPTS) { usleep(self::PAUSE_US); } } if (! $client) { // A forwarded header present while the request IP is still the raw // socket address means TrustProxies is not applying, so every lookup // here is doomed for the same reason. Say which failure this is. $forwarded = $request->headers->get('X-Forwarded-For'); $raw = $request->server('REMOTE_ADDR'); $reason = ($forwarded && $ip === $raw) ? 'proxy_untrusted' : 'no_client'; return new ResolvedDevice(ok: false, reason: $reason); } $mac = MacAddress::normalise($client['mac'] ?? null); if (! $mac) { return new ResolvedDevice(ok: false, reason: 'no_client'); } $clientAp = MacAddress::normalise($client['ap_mac'] ?? null); if (! $clientAp || $clientAp !== MacAddress::normalise($apMac)) { return new ResolvedDevice(ok: false, reason: 'ap_mismatch'); } $supplied = MacAddress::normalise($request->input('mac')); $disagreed = $supplied !== null && $supplied !== $mac; if ($disagreed) { Log::warning('unifi.portal: supplied MAC disagreed with the derived one', [ 'derived' => $mac, 'supplied' => $supplied, 'ip' => $ip, 'ap' => $apMac, ]); } return new ResolvedDevice( ok: true, mac: $mac, isRandomized: MacAddress::isRandomized($mac), macParameterDisagreed: $disagreed, client: $client, ); } }