Pluto packet processing
The document describes the steps that a packet goes through when it is received by pluto. This is not a state machine, but rather a call graph, in general each run through the process causes a single state change.
- read_packet()
The packet is received from the network and the origin and destination address and the ports of the packet are recorded. Based upon the destination port, a particular interface definition is chosen.
The packet is stored into an object called the "msg_digest"
A single common (statically allocated) msg_digest is used to receive all packets, if it needs to be saved (not the common case), it is copied.
- process_packet()
process_packet() is the primary function which parses the incoming packet. In the diagram, this is why all other functions appear to be called from this function.
- de-marshal payloads (in_struct)
The first step is to take the packet apart. This is done by the in_struct() routine, which turns the wire format into a structure, found in md->hdr.
- examine isa_xchg
The first step is to look at the exchange type. Many exchange types are not supported. In general each type will cause a new state to be created when the msgid is different than a different code was done.
- find/create state object
Each type will search for a state object that matches the set of cookies, and the message id. If no state object is found, a new one may be created.
If the state is not found by the message ID, then we look for state with a zero message ID. If that is found, then the state is adjusted to now be for this message ID.
- determine valid transitions
Once we have a state object, it can determined what are valid transitions from this state to another state. The selection is typically based upon what payloads are present in the message. The transition microcode has a bit for each payload type, and based upon this we determine if there would be a valid state transition.
This microcode also then indicates things such as whether or not the message should be encrypted, and whether or not additional options payloads may appear.
- verify state is not suspended
The state may be suspended. This will occur if there is computation (such as a Diffie-Hellman exponentiation) occuring or a network lookup is occuring (DNS). If so, the packet will be dropped. Likely, it is a retransmission anyway.
Retransmissions are also detected --- if we have already replied to this message, then we will have saved the outgoing packet, and it is simply resent.
- decrypt packet if appropriate
The message may have been encrypted. The state transition may be valid only with an encrypted message. These two conditions are compared, and if they match, then the message is decrypted.
The resulting new data will have new payloads, and these will be de-marshalled.
- calculate hash if appropriate
Prior to losing track of the newly decrypted message, we calculate over the plaintext, which may be used later on to authenticate the message.
- enforce ordering of payloads
Some ordering of payloads is mandated, and without this ordering the message may not make sense. This is then checked.
- state specific func
The state transition microcode ("smc") will have designated a particular function handle the state transition. The function will be passed a pointer to the pointer to the message digest, and a pointer to the microcode.
The reason a pointer to a pointer is passed is so that the state specific function can set the message digest to NULL. It will do this in situations where it needs to keep a copy of the message for later examination by a continuation function. Since the pointer will now be NULL, the enclosing read_packet() function will not free the message digest, and a new one will be allocated for the next packet.
State specific functions are named for the exchange type, and message number they expect. For instance, quick_inI1_outR1 is part of a QuickMode (aka "Phase 2") exchange, and is called by the responder when it receives message I1 in order to prepare the R1 message
A typical IKEv1 negotiation looks like this:
[[main_outI1]] ------I1-----> [[main_inI1_outR1]] <-----R1------ [[main_inR1_outI2]] ------I2-----> [[main_inI2_outR2]] <-----R2------ [[main_inR2_outI3]] ------I3-----> [[main_inI3_outR3]] <-----R3------ [[main_inR3]] | PHASE 1 ESTABLISHED | V [[quick_outI1]] ------I1-----> [[quick_inI1_outR1]] <-----R1------ [[quick_inR1_outI2]] | ------I2-----> | [[quick_inI2]] | | V V PHASE 2 ESTABLISHED PHASE 2 ESTABLISHED
Details about each state transition is covered in additional pages above.
- complete_state_transition()
The state specific function is expected to return one of the following returns of type stf_status (State Transition Function Status).
- STF_IGNORE
An ignore value means that this message should not cause any change to the current stat. No further processing is done.
- STF_INLINE
A return of INLINE is a meta value, and means that the state transition has already been performed.
- STF_SUSPEND
A return of SUSPEND means that the state transition function could not (yet) complete the state transition. A continuation function (to be called later) will likely complete the transition.
- STF_FATAL
A return of FATAL means that something is wrong in the configuration of this connection pluto, and likely that it will be unable to continue processing.
- STF_INTERNAL_ERROR
An internal error means that a system wide resource is misconfigured, and it is likely that no connection will succeed.
- STF_FAIL
A simple failure means that the message was not successfully processed. This could be due to inability to authenticate the message, mismatches in policy, etc. It may also be due to malicious corruption of the exchange.
- STF_TOO_MUCH_CRYPTO
The message could not be processed at this time because the system is too busy processing other (more important) messages. The message is dropped, and retransmission will take care of resending it.
- STF_OK
The message was successfully processed, and the state should be advanced. The received message is record (in case it is retransmitted), and if this state requires a response, any response message will then be sent.
- send_packet()
The send_packet() process actually transmits the reply. It uses the remote address and port that has been stored in the state structure, since the NAT-traversal code may have updated these values.
- encrypt packet
The state specific function, if it prepares a reply packet, may need to encrypt it. This is done in a single function.
- continuation
Many state specific functions require that some work be done: this may involve one or more DNS or LDAP lookups to retrieve public keys, or may involve performing lengthy cryptographic operations. In this case, the state specific function will arrange to have the operation started. A continuation structure is created when the operation is started, and the state specific function will return STF_SUSPENDED.
When the asynchronous operation is completed, a continuation function (often called the _tail function) is called, it is provided with the continuation structure, and a reply message is fashioned. Typically, the continuation function will then call complete_state_transition() itself.