From 6262b00330d9bdf03cdd74b5b3082a63c4bfbe8f Mon Sep 17 00:00:00 2001 From: Nayana Bidari Date: Wed, 15 Nov 2023 11:49:05 -0800 Subject: [PATCH] Change IPv4 ID generation algorithm. This addresses an issue discovered by Inon Kaplan (PhD candidate in the Hebrew University School of Computer Science and Engineering), Ron Even (BSc graduate of Bar Ilan University) and Amit Klein (faculty member in the Hebrew University School of Computer Science and Engineering). Details will be provided in their paper, to be presented in a forthcoming academic conference. Update the IPv4 ID generation algorithm by incrementing the hash value with a random number rather than incrementing it by 1 everytime. PiperOrigin-RevId: 582753591 --- pkg/tcpip/network/ipv4/ipv4.go | 28 ++++++++++++++++++++++++++-- pkg/tcpip/tcpip.go | 5 +++++ 2 files changed, 31 insertions(+), 2 deletions(-) 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