The Zephyr Protocol Greg Hudson 2000-02-17 Protocol entities ----------------- Three different kinds of entities participate in the zephyr protocol: client programs, host managers, and servers. Zephyr is a UDP protocol. All Zephyr communications use the same packet format, whether for displayable messages or for control notices internal to the protocol. Displayable messages generally originate from client programs (except for login notices, which originate from servers), are fragmented into multiple packets if necessary, and are sent to the host manager, which forwards them to the server. (The server will reject displayable notices if it receives them from a source port other than the host manager port, 2104.) The server then sends the notice directly to the receiving client programs, without involving the host manager on the receiving end. Control messages may originate from any party. If a client program is sending a control message to its host's server, it sends the packet to the host manager unless it is a status request. (The server will reject control messages other than status requests if they do not come from the host manager port.) The host manager will forward the control message to the server, which will send its acknowledgement, if any, back to the host manager. Packet format ------------- A zephyr packet consists of a header and a body. A header the concatenation of a sequence of zero-terminated fields. Fields come in the following order: version numfields kind uid port authstatus authlen authenticator class instance opcode sender recipient defaultformat checksum multipart multiuid The message body (which may be fragmented across multiple packets) also consists of a sequence of zero-terminated fields. The meaning of those fields is defined by zwgc.desc convention; for a conversational message, the fields are typically "signature" and "body"; for a login notice, the fields are typically "host", "when", and "tty". Some header fields encode binary data in a format called "zephyrascii". The zephyrascii encoding of series of bytes is "0x" followed by two uppercase hexadecimal characters for each byte (the first character for the most significant four bits, the second character for the least significant four bits). For instance, the zephyrascii encoding of the byte sequence { 166, 35, 6 } would be "0xA62306". The zephyrascii encoding of a 16-bit or 32-bit numeric value is "0x" followed by an uppercase hexadecimal character for each four bits of the value, starting with the most significant. For example, the 32-bit value 1 would be encoded as "0x00000001". Following are descriptions of the named header fields: version The string "ZEPH0.2", giving the major and minor number of the packet. The current implementation will fail to interoperate if the major version is not 0, and it ignores the minor version. numfields A 4-byte zephyrascii quantity giving the number of header fields. Typically 17. A numfields value greater than 17 will trigger a bug in sufficiently old Athena client code (prior to release 8.2). kind A 4-byte zephyrascii quantity telling the host manager how to handle a packet. Defined values are: Value Mnemonic Description ----- -------- ----------- 0 UNSAFE Client wants no acks 1 UNACKED Client want just HM ack 2 ACKED Client wants HM+server ack 3 HMACK Host manager ack 4 HMCTL Host manager control 5 SERVACK Server ack 6 SERVNAK Server negative ack 7 CLIENTACK Client ack 8 STAT HM status request (Mnemonics are provided for the purpose of this document and have nothing to do with the wire protocol.) Note that the host manager always receives an ack from the server for packets it forwards, even if the packet kind is UNACKED or UNSAFE. Those kind values just tell the host manager which acks to send to the client program. uid A 12-byte zephyrascii structure consisting of: 4 bytes: Client interface IP address, in network byte order 8 bytes: A per-client nonce (typically the result of gettimeofday() treated as a raw 64-bit value) zwgc abuses the IP address part of the unique ID, using it as the originating IP address of the message. The zephyr server enforces that the IP address matches the IP source address of the packet it received. Acknowledgements contain the same uid as the packet being acknowledged. port A 2-byte zephyrascii quantity used by some types of server control notices. authstatus A 4-byte zephyrascii quantity, with value 1 if the packet is authenticated or 0 if not. authlen A 4-byte zephyrascii quantity giving the length of the raw krb4 authenticator. If an entity other than a client is creating the packet, this field will be set to 0. authenticator The zephyrascii-encoded krb4 authenticator. If an entity other than a client is creating the packet, this field will be set to "0x" (the zephyrascii encoding of the empty string). class The notice class. instance The notice instance. opcode The notice opcode, used for server control messages. sender The notice sender. recipient The notice recipient (with Kerberos realm). defaultformat The notice default format (set by the sender and used by zwgc; the host manager and server don't peek at it). checksum A 4-byte zephyrascii quantty giving the checksum of the packet. This field has to be filled in last. Clients compute the checksum by taking the xor of the des_quad_cksum() values of the three following byte ranges: Header bytes before the checksum field Header bytes after the checksum field Message body where each checksum uses the credential session key as a cryptographic seed. Servers only compute the checksum on the header bytes before the checksum field. multipart Used for fragmenting message bodies across packets. A string "X/Y" where X is a decimal number giving the offset of this fragment within the message body (0 for the first fragment) and Y is a decimal number giving the total length of the message body. Unfragmented notices should fill in this field (with "0/len" where len is the length of the message body). Note that fragmentation is a client-to-client process; the host manager and server just forward fragments around. multiuid The uid of the first fragment of a message. (For unfragmented messages, this has the same value as the uid field.) Also not used by the server or host manager. Other fields may be added to the header by increasing numfields beyond 17 (but see the note about compatibility with buggy clients). Host manager control notices ---------------------------- If the host manager receives a notice from the loopback address of kind STAT, it will respond with a notice of kind HMACK and with a message body containing the following zero-terminated fields (using ASCII decimal format for numbers): Current server name Queue length Client packets received Server packets received Number of server changes since start RCS ID of zhm source file Whether zhm is looking for a new server ("yes" or "no") Number of seconds since start Heap size (well, it used to be; these days just "-1") Machine type If the host manager receives a notice from the loopback address of kind HMCTL, it examines the opcode field. zhm understands the following client control opcodes: "FLUSH" zhm sends a flush notice to its server (see "Server control notices" below), causing the servers to flush all data about the host. zhm will send a boot notice to its server just before forwarding the next packet. "NEWSERV" zhm switches to a different server. If the host manager receives a notice from a non-loopback address of kind HMCTL, it examines the opcode field. zhm understands the following server control opcodes: "SHUTDOWN" zhm switches to a new server. If the message body contains an IP address which reverse-resolves, zhm will switch to that server if it is in zhm's list of servers. "PING" zhm sends the packet back to the server, with the kind changed to HMACK. Server control notices ---------------------- Unless noted otherwise, control messages will be forwarded from the server which originally received the message to the other servers. Unless noted otherwise, control messages are acknowledged by the server. Positive acknowledgements to control messages all follow the same format: the message being acknowledged is returned to the client (through the host manager) with the kind set to SERVACK and the message body sent to the single 0-terminated field "SENT". Negative acknowledgements may have the kind set to SERVNAK and the message body set to one of: "FAIL" (SERVNAK) User not found (for uloc request) "LOST" (SERVNAK) Authorization failed "LOST" (SERVACK) Some other failure If the server receives a packet of kind CLIENTACK, it treats it as an acknowledgement of the packet it sent with the CLIENTACK packet's uid. If the server receives a packet of class "HM_CTL" or a packet of class "ZEPHYR_CTL" and instance "HM", it expects the packet kind to be HMCTL, and examines the opcode field. zephyrd understands the following host manager control opcodes: "FLUSH" zephyrd flushes all information about the host (determined by the source address of the packet). This message is not acknowledged. "BOOT" zephyrd flushes all information about the host. The server which originally received the message will add the host to its list (which is only used to decide who to send shutdown messages to when the server process is killed). "ATTACH" The server which originally received the message will add the host to its list. Other servers will remove it from their list. "DETACH" The server will remove the host from its list. This message is not forwarded to other servers and is not acknowledged. If the server receives a packet of class "ZEPHYR_CTL" and an instance other than "HM", it examines the opcode. zephyrd understands the following control opcodes: "GIMME" The server will acknowledge the request and send directly to the client program (using the port field of the request) a list of currently active subscriptions for the client. The response is not authenticated and may be fragmented. The message body consists of three fields for each subscription, one each for the class, instance, and recipient of the subscription triplet. "GIMMEDEFS" In the same fashion as "GIMME", the server will respond with a list of the default subscriptions it is configured with. "SUBSCRIBE" The message body of the request (which must fit in one fragment) contains three fields for each subscription. The server will add all of the subscriptions to the client's subscription list, plus the default subscriptions the server was configured with. "SUBSCRIBE_NODEFS" Like "SUBSCRIBE", but without the default subscriptions. "UNSUBSCRIBE" Like "SUBSCRIBE_NODEFS", but subscriptions are removed from the client's list instead of added. "CLEARSUB" All of the subscriptions for the client are removed and the location entry for the client is cleared. [XXX unfinished beyond this point] If the server receives a packet of class "LOGIN", "USER_LOGOUT" "USER_FLUSH" "NONE" "OPSTAFF" "REALM-VISIBLE" "REALM-ANNOUNCED" "NET-VISIBLE" "NET-ANNOUNCED" If the server receives a packet of class "USER_LOCATE", "LOCATE" If the server receives a packet of class "ZEPHYR_ADMIN", "STATUS"