A Babel node computes the MAC of a Babel packet as follows.
First, the node builds a pseudo-header that will participate in MAC computation but will not be sent. If the packet is carried over IPv6, the pseudo-header has the following format:
0 1 2 3
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| |
+ +
| |
+ Src address +
| |
+ +
| |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Src port | |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +
| |
+ +
| Dest address |
+ +
| |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| | Dest port |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
If the packet is carried over IPv4, the pseudo-header has the following format:
0 1 2 3
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Src address |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Src port | Dest address |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| | Dest port |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
Fields:
-
Src address
-
The source IP address of the packet.
-
Src port
-
The source UDP port number of the packet.
-
Dest address
-
The destination IP address of the packet.
-
Src port
-
The destination UDP port number of the packet.
The node takes the concatenation of the pseudo-header and the Babel packet including the packet header but excluding the packet trailer (from octet 0 inclusive up to (Body Length + 4) exclusive) and computes a MAC with one of the implemented algorithms. Every implementation
MUST implement HMAC-SHA256 as defined in [
RFC 6234] and
Section 2 of
RFC 2104,
SHOULD implement keyed BLAKE2s [
RFC 7693] with 128-bit (16-octet) digests, and
MAY implement other MAC algorithms.
A Babel node might delay actually sending TLVs by a small amount, in order to aggregate multiple TLVs in a single packet up to the interface MTU (
Section 4 of
RFC 8966). For an interface on which MAC protection is configured, the TLV aggregation logic
MUST take into account the overhead due to PC TLVs (one in each packet) and MAC TLVs (one per configured key).
Before sending a packet, the following actions are performed:
-
a PC TLV containing the PC and Index associated with the outgoing interface MUST be appended to the packet body;
-
the PC MUST be incremented by a strictly positive amount (typically just 1);
-
if the PC overflows, a fresh index MUST be generated (as defined in Section 3.1);
a node MUST NOT include multiple PC TLVs in a single packet;
-
for each key configured on the interface, a MAC is computed as specified in Section 4.1 and stored in a MAC TLV that MUST be appended to the packet trailer (see Section 4.2 of RFC 8966).
When a packet is received on an interface that is configured for MAC protection, the following steps are performed before the packet is passed to normal processing:
-
First, the receiver checks whether the trailer of the received packet carries at least one MAC TLV; if not, the packet MUST be immediately dropped and processing stops. Then, for each key configured on the receiving interface, the receiver computes the MAC of the packet. It then compares every generated MAC against every MAC included in the packet; if there is at least one match, the packet passes the MAC test; if there is none, the packet MUST be silently dropped and processing stops at this point. In order to avoid memory exhaustion attacks, an entry in the neighbour table MUST NOT be created before the MAC test has passed successfully. The MAC of the packet MUST NOT be computed for each MAC TLV contained in the packet, but only once for each configured key.
-
If an entry for the sender does not exist in the neighbour table, it MAY be created at this point (or, alternatively, its creation can be delayed until a challenge needs to be sent, see below).
-
The packet body is then parsed a first time. During this "preparse" phase, the packet body is traversed and all TLVs are ignored except PC, Challenge Request, and Challenge Reply TLVs. When a PC TLV is encountered, the enclosed PC and Index are saved for later processing. If multiple PCs are found (which should not happen, see Section 4.2), only the first one is processed, the remaining ones MUST be silently ignored. If a Challenge Request is encountered, a Challenge Reply MUST be scheduled, as described in Section 4.3.1.2. If a Challenge Reply is encountered, it is tested for validity as described in Section 4.3.1.3, and a note is made of the result of the test.
-
The preparse phase above yields two pieces of data: the PC and Index from the first PC TLV, and a bit indicating whether the packet contains a successful Challenge Reply. If the packet does not contain a PC TLV, the packet MUST be dropped, and processing stops at this point. If the packet contains a successful Challenge Reply, then the PC and Index contained in the PC TLV MUST be stored in the neighbour table entry corresponding to the sender (which already exists in this case), and the packet is accepted.
-
Otherwise, if there is no entry in the neighbour table corresponding to the sender, or if such an entry exists but contains no Index, or if the Index it contains is different from the Index contained in the PC TLV, then a challenge MUST be sent as described in Section 4.3.1.1, the packet MUST be dropped, and processing stops at this stage.
-
At this stage, the packet contains no successful Challenge Reply, and the Index contained in the PC TLV is equal to the Index in the neighbour table entry corresponding to the sender. The receiver compares the received PC with the PC contained in the neighbour table; if the received PC is smaller or equal than the PC contained in the neighbour table, the packet MUST be dropped and processing stops (no challenge is sent in this case, since the mismatch might be caused by harmless packet reordering on the link). Otherwise, the PC contained in the neighbour table entry is set to the received PC, and the packet is accepted.
In the algorithm described above, Challenge Requests are processed and challenges are sent before the (Index, PC) pair is verified against the neighbour table. This simplifies the implementation somewhat (the node may simply schedule outgoing requests as it walks the packet during the preparse phase) but relies on the rate limiting described in
Section 4.3.1.1 to avoid sending too many challenges in response to replayed packets. As an optimisation, a node
MAY ignore all Challenge Requests contained in a packet except the last one, and it
MAY ignore a Challenge Request in the case where it is contained in a packet with an Index that matches the one in the neighbour table and a PC that is smaller or equal to the one contained in the neighbour table. Since it is still possible to replay a packet with an obsolete Index, the rate limiting described in
Section 4.3.1.1 is required even if this optimisation is implemented.
The same is true of Challenge Replies. However, since validating a Challenge Reply has minimal additional cost (it is just a bitwise comparison of two strings of octets), a similar optimisation for Challenge Replies is not worthwhile.
After the packet has been accepted, it is processed as normal, except that any PC, Challenge Request, and Challenge Reply TLVs that it contains are silently ignored.
During the preparse stage, the receiver might encounter a mismatched Index, to which it will react by scheduling a Challenge Request. It might encounter a Challenge Request TLV, to which it will reply with a Challenge Reply TLV. Finally, it might encounter a Challenge Reply TLV, which it will attempt to match with a previously sent Challenge Request TLV in order to update the neighbour table entry corresponding to the sender of the packet.
When it encounters a mismatched Index during the preparse phase, a node picks a nonce that it has never used with any of the keys currently configured on the relevant interface, for example, by drawing a sufficiently large random string of bytes or by consulting a strictly monotonic hardware clock. It
MUST then store the nonce in the entry of the neighbour table associated to the neighbour (the entry might need to be created at this stage), initialise the neighbour's challenge expiry timer to 30 seconds, and send a Challenge Request TLV to the unicast address corresponding to the neighbour.
A node
MAY aggregate a Challenge Request with other TLVs; in other words, if it has already buffered TLVs to be sent to the unicast address of the neighbour, it
MAY send the buffered TLVs in the same packet as the Challenge Request. However, it
MUST arrange for the Challenge Request to be sent in a timely manner, as any packets received from that neighbour will be silently ignored until the challenge completes.
A node
MUST impose a rate limitation to the challenges it sends; the limit
SHOULD default to one Challenge Request every 300 ms and
MAY be configurable. This rate limiting serves two purposes. First, since a challenge may be sent in response to a packet replayed by an attacker, it limits the number of challenges that an attacker can cause a node to send. Second, it limits the number of challenges sent when there are multiple packets in flight from a single neighbour.
When it encounters a Challenge Request during the preparse phase, a node constructs a Challenge Reply TLV by copying the Nonce from the Challenge Request into the Challenge Reply. It
MUST then send the Challenge Reply to the unicast address from which the Challenge Request was sent. A challenge sent to a multicast address
MUST be silently ignored.
A node
MAY aggregate a Challenge Reply with other TLVs; in other words, if it has already buffered TLVs to be sent to the unicast address of the sender of the Challenge Request, it
MAY send the buffered TLVs in the same packet as the Challenge Reply. However, it
MUST arrange for the Challenge Reply to be sent in a timely manner (within a few seconds) and
SHOULD NOT send any other packets over the same interface before sending the Challenge Reply, as those would be dropped by the challenger.
Since a Challenge Reply might be caused by a replayed Challenge Request, a node
MUST impose a rate limitation to the Challenge Replies it sends; the limit
SHOULD default to one Challenge Reply for each peer every 300 ms and
MAY be configurable.
When it encounters a Challenge Reply during the preparse phase, a node consults the neighbour table entry corresponding to the neighbour that sent the Challenge Reply. If no challenge is in progress, i.e., if there is no Nonce stored in the neighbour table entry or the challenge timer has expired, the Challenge Reply
MUST be silently ignored, and the challenge has failed.
Otherwise, the node compares the Nonce contained in the Challenge Reply with the Nonce contained in the neighbour table entry. If the two are equal (they have the same length and content), then the challenge has succeeded and the nonce stored in the neighbour table for this neighbour
SHOULD be discarded; otherwise, the challenge has failed (and the nonce is not discarded).
The per-neighbour (Index, PC) pair is maintained in the neighbour table, and is normally discarded when the neighbour table entry expires. Implementations
MUST ensure that an (Index, PC) pair is discarded within a finite time since the last time a packet has been accepted. In particular, unsuccessful challenges
MUST NOT prevent an (Index, PC) pair from being discarded for unbounded periods of time.
A possible implementation strategy for implementations that use a Hello history (Appendix A of [
RFC 8966]) is to discard the (Index, PC) pair whenever the Hello history becomes empty. Another implementation strategy is to use a timer that is reset whenever a packet is accepted and to discard the (Index, PC) pair whenever the timer expires. If the latter strategy is used, the timer
SHOULD default to a value of 5 minutes and
MAY be configurable.