net.i2p.router.transport.udp
Class PacketBuilder

java.lang.Object
  extended by net.i2p.router.transport.udp.PacketBuilder

 class PacketBuilder
extends Object

Big ol' class to do all our packet formatting. The UDPPackets generated are fully authenticated, encrypted, and configured for delivery to the peer. The following is from udp.html on the website:

All UDP datagrams begin with a 16 byte MAC (Message Authentication Code) and a 16 byte IV (Initialization Vector followed by a variable size payload encrypted with the appropriate key. The MAC used is HMAC-MD5, truncated to 16 bytes, while the key is a full 32 byte AES256 key. The specific construct of the MAC is the first 16 bytes from:

  HMAC-MD5(payload || IV || (payloadLength ^ protocolVersion), macKey)

The protocol version is currently 0.

The payload itself is AES256/CBC encrypted with the IV and the sessionKey, with replay prevention addressed within its body, explained below. The payloadLength in the MAC is a 2 byte unsigned integer in 2s complement.

The protocolVersion is a 2 byte unsigned integer in 2s complement, and currently set to 0. Peers using a different protocol version will not be able to communicate with this peer, though earlier versions not using this flag are.

Payload

Within the AES encrypted payload, there is a minimal common structure to the various messages - a one byte flag and a four byte sending timestamp (*seconds* since the unix epoch). The flag byte contains the following bitfields:

  bits 0-3: payload type
     bit 4: rekey?
     bit 5: extended options included
  bits 6-7: reserved

If the rekey flag is set, 64 bytes of keying material follow the timestamp. If the extended options flag is set, a one byte option size value is appended to, followed by that many extended option bytes, which are currently uninterpreted.

When rekeying, the first 32 bytes of the keying material is fed into a SHA256 to produce the new MAC key, and the next 32 bytes are fed into a SHA256 to produce the new session key, though the keys are not immediately used. The other side should also reply with the rekey flag set and that same keying material. Once both sides have sent and received those values, the new keys should be used and the previous keys discarded. It may be useful to keep the old keys around briefly, to address packet loss and reordering.

NOTE: Rekeying is currently unimplemented.

 Header: 37+ bytes
 +----+----+----+----+----+----+----+----+
 |                  MAC                  |
 |                                       |
 +----+----+----+----+----+----+----+----+
 |                   IV                  |
 |                                       |
 +----+----+----+----+----+----+----+----+
 |flag|        time       | (optionally  |
 +----+----+----+----+----+              |
 | this may have 64 byte keying material |
 | and/or a one+N byte extended options) |
 +---------------------------------------|


Nested Class Summary
static class PacketBuilder.Fragment
          Class for passing multiple fragments to buildPacket()
 
Field Summary
static int ABSOLUTE_MAX_ACKS
          one byte field
static int DATA_HEADER_SIZE
          not including acks.
static int FRAGMENT_HEADER_SIZE
          4 byte msg ID + 3 byte fragment info
static int HEADER_SIZE
          if no extended options or rekey data, which we don't support = 37
static int IP_HEADER_SIZE
          IPv4 only
static int IPV6_HEADER_SIZE
           
static int MIN_DATA_PACKET_OVERHEAD
          74
static int MIN_IPV6_DATA_PACKET_OVERHEAD
          94
(package private) static int TYPE_ACK
           
(package private) static int TYPE_CONF
           
(package private) static int TYPE_CREAT
           
(package private) static int TYPE_FIRST
          For debugging and stats only - does not go out on the wire.
(package private) static int TYPE_INTRO
           
(package private) static int TYPE_PUNCH
           
(package private) static int TYPE_RESP
           
(package private) static int TYPE_RREQ
           
(package private) static int TYPE_SREQ
           
(package private) static int TYPE_TBC
           
(package private) static int TYPE_TCB
           
(package private) static int TYPE_TFA
           
(package private) static int TYPE_TTA
           
static int UDP_HEADER_SIZE
          Same for IPv4 and IPv6
 
Constructor Summary
PacketBuilder(RouterContext ctx, UDPTransport transport)
           
 
Method Summary
 UDPPacket buildACK(PeerState peer, List<ACKBitfield> ackBitfields)
          Build the ack packet.
 UDPPacket buildHolePunch(InetAddress to, int port)
          Creates an empty unauthenticated packet for hole punching.
 UDPPacket buildPacket(byte[] data, InetAddress to, int port)
          TESTING ONLY.
 UDPPacket buildPacket(List<PacketBuilder.Fragment> fragments, PeerState peer, List<Long> ackIdsRemaining, int newAckCount, List<ACKBitfield> partialACKsRemaining)
           
 UDPPacket buildPacket(OutboundMessageState state, int fragment, PeerState peer, List<Long> ackIdsRemaining, int newAckCount, List<ACKBitfield> partialACKsRemaining)
          This builds a data packet (PAYLOAD_TYPE_DATA).
 UDPPacket buildPeerTestFromAlice(InetAddress toIP, int toPort, SessionKey toIntroKey, long nonce, SessionKey aliceIntroKey)
          Build a packet as if we are Alice and we either want Bob to begin a peer test or Charlie to finish a peer test.
 UDPPacket buildPeerTestFromAlice(InetAddress toIP, int toPort, SessionKey toCipherKey, SessionKey toMACKey, long nonce, SessionKey aliceIntroKey)
          Build a packet as if we are Alice and we either want Bob to begin a peer test or Charlie to finish a peer test.
 UDPPacket buildPeerTestToAlice(InetAddress aliceIP, int alicePort, SessionKey aliceIntroKey, SessionKey charlieIntroKey, long nonce)
          Build a packet as if we are either Bob or Charlie and we are helping test Alice.
 UDPPacket buildPeerTestToBob(InetAddress bobIP, int bobPort, InetAddress aliceIP, int alicePort, SessionKey aliceIntroKey, long nonce, SessionKey bobCipherKey, SessionKey bobMACKey)
          Build a packet as if we are Charlie sending Bob a packet verifying that we will help test Alice.
 UDPPacket buildPeerTestToCharlie(InetAddress aliceIP, int alicePort, SessionKey aliceIntroKey, long nonce, InetAddress charlieIP, int charliePort, SessionKey charlieCipherKey, SessionKey charlieMACKey)
          Build a packet as if we are Bob sending Charlie a packet to help test Alice.
 UDPPacket buildPing(PeerState peer)
          An ACK packet with no acks.
(package private)  UDPPacket buildRelayIntro(RemoteHostId alice, PeerState charlie, UDPPacketReader.RelayRequestReader request)
           
 List<UDPPacket> buildRelayRequest(UDPTransport transport, OutboundEstablishState state, SessionKey ourIntroKey)
          build intro packets for each of the published introducers
(package private)  UDPPacket buildRelayResponse(RemoteHostId alice, PeerState charlie, long nonce, SessionKey cipherKey, SessionKey macKey)
           
 UDPPacket[] buildSessionConfirmedPackets(OutboundEstablishState state, RouterIdentity ourIdentity)
          Build a new series of SessionConfirmed packets for the given peer, encrypting it as necessary.
 UDPPacket buildSessionCreatedPacket(InboundEstablishState state, int externalPort, SessionKey ourIntroKey)
          Build a new SessionCreated packet for the given peer, encrypting it as necessary.
 UDPPacket buildSessionDestroyPacket(InboundEstablishState peer)
          Build a destroy packet, which contains a header but no body.
 UDPPacket buildSessionDestroyPacket(OutboundEstablishState peer)
          Build a destroy packet, which contains a header but no body.
 UDPPacket buildSessionDestroyPacket(PeerState peer)
          Build a destroy packet, which contains a header but no body.
 UDPPacket buildSessionRequestPacket(OutboundEstablishState state)
          Build a new SessionRequest packet for the given peer, encrypting it as necessary.
static int getMaxAdditionalFragmentSize(PeerState peer, int numFragments, int curDataSize)
          Will a packet to 'peer' that already has 'numFragments' fragments totalling 'curDataSize' bytes fit another fragment of size 'newFragSize' ?? This doesn't leave anything for acks.
 
Methods inherited from class java.lang.Object
clone, equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait
 

Field Detail

TYPE_FIRST

static final int TYPE_FIRST
For debugging and stats only - does not go out on the wire. These are chosen to be higher than the highest I2NP message type, as a data packet is set to the underlying I2NP message type.

See Also:
Constant Field Values

TYPE_ACK

static final int TYPE_ACK
See Also:
Constant Field Values

TYPE_PUNCH

static final int TYPE_PUNCH
See Also:
Constant Field Values

TYPE_RESP

static final int TYPE_RESP
See Also:
Constant Field Values

TYPE_INTRO

static final int TYPE_INTRO
See Also:
Constant Field Values

TYPE_RREQ

static final int TYPE_RREQ
See Also:
Constant Field Values

TYPE_TCB

static final int TYPE_TCB
See Also:
Constant Field Values

TYPE_TBC

static final int TYPE_TBC
See Also:
Constant Field Values

TYPE_TTA

static final int TYPE_TTA
See Also:
Constant Field Values

TYPE_TFA

static final int TYPE_TFA
See Also:
Constant Field Values

TYPE_CONF

static final int TYPE_CONF
See Also:
Constant Field Values

TYPE_SREQ

static final int TYPE_SREQ
See Also:
Constant Field Values

TYPE_CREAT

static final int TYPE_CREAT
See Also:
Constant Field Values

HEADER_SIZE

public static final int HEADER_SIZE
if no extended options or rekey data, which we don't support = 37

See Also:
Constant Field Values

FRAGMENT_HEADER_SIZE

public static final int FRAGMENT_HEADER_SIZE
4 byte msg ID + 3 byte fragment info

See Also:
Constant Field Values

DATA_HEADER_SIZE

public static final int DATA_HEADER_SIZE
not including acks. 46

See Also:
Constant Field Values

IP_HEADER_SIZE

public static final int IP_HEADER_SIZE
IPv4 only

See Also:
Constant Field Values

UDP_HEADER_SIZE

public static final int UDP_HEADER_SIZE
Same for IPv4 and IPv6

See Also:
Constant Field Values

MIN_DATA_PACKET_OVERHEAD

public static final int MIN_DATA_PACKET_OVERHEAD
74

See Also:
Constant Field Values

IPV6_HEADER_SIZE

public static final int IPV6_HEADER_SIZE
See Also:
Constant Field Values

MIN_IPV6_DATA_PACKET_OVERHEAD

public static final int MIN_IPV6_DATA_PACKET_OVERHEAD
94

See Also:
Constant Field Values

ABSOLUTE_MAX_ACKS

public static final int ABSOLUTE_MAX_ACKS
one byte field

See Also:
Constant Field Values
Constructor Detail

PacketBuilder

public PacketBuilder(RouterContext ctx,
                     UDPTransport transport)
Parameters:
transport - may be null for unit testing only
Method Detail

getMaxAdditionalFragmentSize

public static int getMaxAdditionalFragmentSize(PeerState peer,
                                               int numFragments,
                                               int curDataSize)
Will a packet to 'peer' that already has 'numFragments' fragments totalling 'curDataSize' bytes fit another fragment of size 'newFragSize' ?? This doesn't leave anything for acks.

Parameters:
numFragments - >= 1
Since:
0.9.16

buildPacket

public UDPPacket buildPacket(OutboundMessageState state,
                             int fragment,
                             PeerState peer,
                             List<Long> ackIdsRemaining,
                             int newAckCount,
                             List<ACKBitfield> partialACKsRemaining)
This builds a data packet (PAYLOAD_TYPE_DATA). See the methods below for the other message types. Note that while the UDP message spec allows for more than one fragment in a message, this method writes exactly one fragment. For no fragments use buildAck(). Multiple fragments in a single packet is not supported. Rekeying and extended options are not supported. Packet format:
    16 byte MAC
    16 byte IV
     1 byte flag
     4 byte date
     1 byte flag
     1 byte explicit ack count IF included
   4*n byte explict acks IF included
     1 byte ack bitfield count IF included
   4*n + ?? ack bitfields IF included
     1 byte fragment count (always 1)
     4 byte message ID
     3 byte fragment info
     n byte fragment
  0-15 bytes padding
So ignoring the ack bitfields, and assuming we have explicit acks, it's (47 + 4*explict acks + padding) added to the fragment length.

Parameters:
ackIdsRemaining - list of messageIds (Long) that should be acked by this packet. The list itself is passed by reference, and if a messageId is transmitted it will be removed from the list. Not all message IDs will necessarily be sent, there may not be room. non-null.
newAckCount - the number of ackIdsRemaining entries that are new. These must be the first ones in the list
partialACKsRemaining - list of messageIds (ACKBitfield) that should be acked by this packet. The list itself is passed by reference, and if a messageId is included, it should be removed from the list. Full acks in this list are skipped, they are NOT transmitted. non-null. Not all acks will necessarily be sent, there may not be room.
Returns:
null on error

buildPacket

public UDPPacket buildPacket(List<PacketBuilder.Fragment> fragments,
                             PeerState peer,
                             List<Long> ackIdsRemaining,
                             int newAckCount,
                             List<ACKBitfield> partialACKsRemaining)

buildPing

public UDPPacket buildPing(PeerState peer)
An ACK packet with no acks. We use this for keepalive purposes. It doesn't generate a reply, but that's ok.


buildACK

public UDPPacket buildACK(PeerState peer,
                          List<ACKBitfield> ackBitfields)
Build the ack packet. The list need not be sorted into full and partial; this method will put all fulls before the partials in the outgoing packet. An ack packet is just a data packet with no data. See buildPacket() for format. TODO MTU not enforced. TODO handle huge number of acks better

Parameters:
ackBitfields - list of ACKBitfield instances to either fully or partially ACK

buildSessionCreatedPacket

public UDPPacket buildSessionCreatedPacket(InboundEstablishState state,
                                           int externalPort,
                                           SessionKey ourIntroKey)
Build a new SessionCreated packet for the given peer, encrypting it as necessary.

Returns:
ready to send packet, or null if there was a problem

buildSessionRequestPacket

public UDPPacket buildSessionRequestPacket(OutboundEstablishState state)
Build a new SessionRequest packet for the given peer, encrypting it as necessary.

Returns:
ready to send packet, or null if there was a problem

buildSessionConfirmedPackets

public UDPPacket[] buildSessionConfirmedPackets(OutboundEstablishState state,
                                                RouterIdentity ourIdentity)
Build a new series of SessionConfirmed packets for the given peer, encrypting it as necessary. Note that while a SessionConfirmed could in theory be fragmented, in practice a RouterIdentity is 387 bytes and a single fragment is 512 bytes max, so it will never be fragmented.

Returns:
ready to send packets, or null if there was a problem TODO: doesn't really return null, and caller doesn't handle null return (null SigningPrivateKey should cause this?) Should probably return null if buildSessionConfirmedPacket() returns null for any fragment

buildSessionDestroyPacket

public UDPPacket buildSessionDestroyPacket(PeerState peer)
Build a destroy packet, which contains a header but no body. Session must be established or this will NPE in authenticate(). Unused until 0.8.9.

Since:
0.8.1

buildSessionDestroyPacket

public UDPPacket buildSessionDestroyPacket(OutboundEstablishState peer)
Build a destroy packet, which contains a header but no body. If the keys and ip/port are not yet set, this will return null.

Returns:
packet or null
Since:
0.9.2

buildSessionDestroyPacket

public UDPPacket buildSessionDestroyPacket(InboundEstablishState peer)
Build a destroy packet, which contains a header but no body. If the keys and ip/port are not yet set, this will return null.

Returns:
packet or null
Since:
0.9.2

buildPeerTestFromAlice

public UDPPacket buildPeerTestFromAlice(InetAddress toIP,
                                        int toPort,
                                        SessionKey toIntroKey,
                                        long nonce,
                                        SessionKey aliceIntroKey)
Build a packet as if we are Alice and we either want Bob to begin a peer test or Charlie to finish a peer test.

Returns:
ready to send packet, or null if there was a problem

buildPeerTestFromAlice

public UDPPacket buildPeerTestFromAlice(InetAddress toIP,
                                        int toPort,
                                        SessionKey toCipherKey,
                                        SessionKey toMACKey,
                                        long nonce,
                                        SessionKey aliceIntroKey)
Build a packet as if we are Alice and we either want Bob to begin a peer test or Charlie to finish a peer test.

Returns:
ready to send packet, or null if there was a problem

buildPeerTestToAlice

public UDPPacket buildPeerTestToAlice(InetAddress aliceIP,
                                      int alicePort,
                                      SessionKey aliceIntroKey,
                                      SessionKey charlieIntroKey,
                                      long nonce)
Build a packet as if we are either Bob or Charlie and we are helping test Alice.

Returns:
ready to send packet, or null if there was a problem

buildPeerTestToCharlie

public UDPPacket buildPeerTestToCharlie(InetAddress aliceIP,
                                        int alicePort,
                                        SessionKey aliceIntroKey,
                                        long nonce,
                                        InetAddress charlieIP,
                                        int charliePort,
                                        SessionKey charlieCipherKey,
                                        SessionKey charlieMACKey)
Build a packet as if we are Bob sending Charlie a packet to help test Alice.

Returns:
ready to send packet, or null if there was a problem

buildPeerTestToBob

public UDPPacket buildPeerTestToBob(InetAddress bobIP,
                                    int bobPort,
                                    InetAddress aliceIP,
                                    int alicePort,
                                    SessionKey aliceIntroKey,
                                    long nonce,
                                    SessionKey bobCipherKey,
                                    SessionKey bobMACKey)
Build a packet as if we are Charlie sending Bob a packet verifying that we will help test Alice.

Returns:
ready to send packet, or null if there was a problem

buildRelayRequest

public List<UDPPacket> buildRelayRequest(UDPTransport transport,
                                         OutboundEstablishState state,
                                         SessionKey ourIntroKey)
build intro packets for each of the published introducers

Returns:
empty list on failure

buildRelayIntro

UDPPacket buildRelayIntro(RemoteHostId alice,
                          PeerState charlie,
                          UDPPacketReader.RelayRequestReader request)

buildRelayResponse

UDPPacket buildRelayResponse(RemoteHostId alice,
                             PeerState charlie,
                             long nonce,
                             SessionKey cipherKey,
                             SessionKey macKey)

buildHolePunch

public UDPPacket buildHolePunch(InetAddress to,
                                int port)
Creates an empty unauthenticated packet for hole punching. Parameters must be validated previously.


buildPacket

public UDPPacket buildPacket(byte[] data,
                             InetAddress to,
                             int port)
TESTING ONLY. Creates an arbitrary packet for unit testing. Null transport in constructor OK.

Since:
IPv6