btd: the advertisement carries the robot's IPv4 address
`duck-btctl scan` connects to nothing, which is what makes it the command to reach for when a robot is unreachable — and it is why a listing could only report what an advertisement carried. So the question a listing is most often read to answer, *where do I ssh?*, had no answer in it: the address is in `net.status`, and reading that costs a connection, a bond and the PIN, per robot. Four bytes of IPv4 now go out in a manufacturer-data field under company id `0xFFFF`, the id the SIG reserves for internal use. `btd::adv` holds the layout and both halves use it, so the encoder on the robot and the decoder on the laptop cannot disagree. The budget is the reason it is four bytes and no more: flags (3), the 128-bit service UUID (18) and this field (8) spend 29 of the 31 bytes a legacy advertisement holds, which is also why the SSID is not here and cannot be — it is up to 32 bytes on its own, and stays a `wifi status` question. A robot with no address advertises `0.0.0.0` rather than dropping the field, so `scan` tells three states apart: an address, a robot on no network, and a robot on a release from before this existed, which broadcasts no field. The last two want different next moves. Three things this had to get right: - `configd` failing to answer keeps the last known address rather than clearing it. Clearing it would deregister and re-register the advertisement every tick for as long as a `configd` restart lasted, with a client watching the address blink; - BlueZ refusing an advertisement carrying the field is retried without it. The arithmetic says it fits, but the byte that overflows is the controller's to count — and an advertisement with no address is a robot someone can still reach, where a refused one is a robot gone dark; - `--name` pins the name, not the advertisement. The reconcile loop used to be skipped entirely under a pinned name, which was the same thing back when the name was all the advertisement carried; a pinned name does not pin a DHCP lease. `0xFFFF` is open to anyone, so the field is never an identity check: it is read only from a device that also advertised the service UUID. Assisted-by: Claude:claude-opus-5[1m]
This commit is contained in:
@@ -599,11 +599,12 @@ bump would rename every robot in the field, and nobody would ever connect the tw
|
||||
Four hex characters is 65 536 possibilities, so three robots in a room collide about once in 22 000
|
||||
times. This is a default meant to be *distinguishable*, not a unique key.
|
||||
|
||||
A name of more than **8 characters** does not fit the 31 bytes of a legacy advertisement once flags
|
||||
and the 128-bit service UUID are counted, so it travels in the scan response — a second exchange a
|
||||
central can miss. `duck-c51b` is 9, one over. Dropping to three hex characters would fit, at 4 096
|
||||
possibilities; whether that trade is worth taking is unmeasured, because it depends on how BlueZ
|
||||
packs the two payloads.
|
||||
The name travels in the **scan response**, not the advertisement, and now always will: flags (3),
|
||||
the 128-bit service UUID (18) and the address field (8, see below) spend 29 of the 31 bytes a legacy
|
||||
advertisement holds. Before the address it was 21, and a name of 8 characters or fewer could have
|
||||
fitted alongside — `duck-c51b` is 9, one over, so in practice it never did. A scan response is a
|
||||
second exchange a central can miss on its own, which is why a device reported with no name and no
|
||||
services is a plausible robot rather than something to filter out.
|
||||
|
||||
#### The advertised name is the one the robot was given
|
||||
|
||||
@@ -630,9 +631,39 @@ problem with a client fix.
|
||||
Reconciled rather than event-driven, deliberately. `btd` forwards `system.setName` without reading
|
||||
the reply — interpreting replies is what this daemon avoids — and re-asking the moment it forwards
|
||||
one races the write it just forwarded. Polling is fewer moving parts and covers renames made through
|
||||
`robotctl`, which never cross `btd` at all. `--name` pins the advertisement for bench work. An
|
||||
unreachable `configd` falls back to the hostname: `btd` is on the recovery path and has to come up
|
||||
when the rest of the robot has not.
|
||||
`robotctl`, which never cross `btd` at all. `--name` pins the *name* for bench work — the reconcile
|
||||
loop still runs, because the address below moves whether or not the name does. An unreachable
|
||||
`configd` falls back to the hostname: `btd` is on the recovery path and has to come up when the rest
|
||||
of the robot has not.
|
||||
|
||||
#### The advertisement carries the robot's IPv4 address
|
||||
|
||||
`duck-btctl scan` connects to nothing, which is what makes it the command to reach for when a robot
|
||||
is unreachable — and it is why a listing can only report what an advertisement carries. So the
|
||||
question a listing is most often read to answer, *where do I ssh?*, had no answer in it: the address
|
||||
is in `net.status`, and reading that costs a connection, a bond and the PIN, per robot.
|
||||
|
||||
Four bytes of IPv4 go in a manufacturer-data field under company id `0xFFFF` — the id the Bluetooth
|
||||
SIG reserves for internal and interoperability testing, which is the right one for a project that
|
||||
has not been assigned one. Anyone may use that id, so **the field is not an identity check**: it is
|
||||
read only from a device that also advertised the service UUID, which is the discriminator.
|
||||
|
||||
**The SSID is not in it and cannot be.** An SSID is up to 32 bytes on its own, against the 6 bytes
|
||||
of payload the budget above leaves. It stays a `wifi status` question, and that is the one to ask
|
||||
when the address in a listing says something surprising.
|
||||
|
||||
A robot with no address advertises `0.0.0.0` rather than dropping the field, so a listing can tell
|
||||
three states apart: an address, a robot with no network, and a robot on a release from before this
|
||||
existed, which broadcasts no field at all. Collapsing the last two would send the reader to check
|
||||
wifi on a robot that needs an update.
|
||||
|
||||
The address is reconciled on the same tick as the name, every few seconds — far faster than a DHCP
|
||||
lease moves, and the point is the other case: whoever just ran `wifi connect` is waiting to read the
|
||||
address it produced. `configd` failing to answer keeps the last known address rather than clearing
|
||||
it, or a `configd` restart would deregister and re-register the advertisement every tick with a
|
||||
client watching the address blink. And if BlueZ ever refuses an advertisement carrying the field,
|
||||
`btd` retries without it: an advertisement with no address is a robot someone can still reach, and a
|
||||
refused one is a robot gone dark.
|
||||
|
||||
#### Provisioning can name a board, and does not have to
|
||||
|
||||
|
||||
Reference in New Issue
Block a user