diff --git a/pkg/tcpip/network/ipv4/ipv4.go b/pkg/tcpip/network/ipv4/ipv4.go index 2e5ab0264..ef105d95a 100644 --- a/pkg/tcpip/network/ipv4/ipv4.go +++ b/pkg/tcpip/network/ipv4/ipv4.go @@ -437,6 +437,28 @@ func (e *endpoint) NetworkProtocolNumber() tcpip.NetworkProtocolNumber { return e.protocol.Number() } +func (e *endpoint) generateID(srcAddr, dstAddr tcpip.Address, params stack.NetworkHeaderParams) uint32 { + // Get the time difference between the last time 'ids' was accessed and + // now. Update 'idTS' to the current time. + now := e.protocol.stack.Clock().NowMonotonic().Milliseconds() + oldTS := e.protocol.idTS.Load() + diff := now - oldTS + rng := e.protocol.stack.SecureRNG() + e.protocol.idTS.Store(now) + + var counter uint32 + if diff < 1 { + counter = rng.Uint32() + } else { + // Increment ID with a random number in the range [0, diff). + counter = uint32(rng.Int63n(diff)) + } + + // Calculate the hash value. + hash := hashRoute(srcAddr, dstAddr, params.Protocol, e.protocol.hashIV) % buckets + return e.protocol.ids[hash].Add(counter) +} + func (e *endpoint) addIPHeader(srcAddr, dstAddr tcpip.Address, pkt stack.PacketBufferPtr, params stack.NetworkHeaderParams, options header.IPv4OptionsSerializer) tcpip.Error { hdrLen := header.IPv4MinimumSize var optLen int @@ -455,7 +477,7 @@ func (e *endpoint) addIPHeader(srcAddr, dstAddr tcpip.Address, pkt stack.PacketB // RFC 6864 section 4.3 mandates uniqueness of ID values for non-atomic // datagrams. Since the DF bit is never being set here, all datagrams // are non-atomic and need an ID. - id := e.protocol.ids[hashRoute(srcAddr, dstAddr, params.Protocol, e.protocol.hashIV)%buckets].Add(1) + id := e.generateID(srcAddr, dstAddr, params) ipH.Encode(&header.IPv4Fields{ TotalLength: uint16(length), ID: uint16(id), @@ -628,7 +650,7 @@ func (e *endpoint) WriteHeaderIncludedPacket(r *stack.Route, pkt stack.PacketBuf // non-atomic datagrams, so assign an ID to all such datagrams // according to the definition given in RFC 6864 section 4. if ipH.Flags()&header.IPv4FlagDontFragment == 0 || ipH.Flags()&header.IPv4FlagMoreFragments != 0 || ipH.FragmentOffset() > 0 { - ipH.SetID(uint16(e.protocol.ids[hashRoute(r.LocalAddress(), r.RemoteAddress(), 0 /* protocol */, e.protocol.hashIV)%buckets].Add(1))) + ipH.SetID(uint16(e.generateID(r.LocalAddress(), r.RemoteAddress(), stack.NetworkHeaderParams{}))) } } @@ -1514,6 +1536,8 @@ type protocol struct { ids []atomicbitops.Uint32 hashIV uint32 + // idTS is the unix timestamp in milliseconds 'ids' was last accessed. + idTS atomicbitops.Int64 fragmentation *fragmentation.Fragmentation diff --git a/pkg/tcpip/tcpip.go b/pkg/tcpip/tcpip.go index 9ffe1671c..fa8462b60 100644 --- a/pkg/tcpip/tcpip.go +++ b/pkg/tcpip/tcpip.go @@ -112,6 +112,11 @@ func (mt MonotonicTime) Sub(u MonotonicTime) time.Duration { return time.Unix(0, mt.nanoseconds).Sub(time.Unix(0, u.nanoseconds)) } +// Milliseconds returns the time in milliseconds. +func (mt MonotonicTime) Milliseconds() int64 { + return mt.nanoseconds / 1e6 +} + // A Clock provides the current time and schedules work for execution. // // Times returned by a Clock should always be used for application-visible