mirror of
https://github.com/netbirdio/gvisor.git
synced 2026-05-22 17:12:49 -07:00
Detect looped-back NDP DAD messages
...as per RFC 7527. If a looped-back DAD message is received, do not fail DAD since our own DAD message does not indicate that a neighbor has the address assigned. Test: ndp_test.TestDADResolveLoopback PiperOrigin-RevId: 363224288
This commit is contained in:
committed by
gVisor bot
parent
ebd7c1b889
commit
68065d1ceb
@@ -1287,6 +1287,13 @@ func ndpOptions(t *testing.T, optsBuf header.NDPOptions, opts []header.NDPOption
|
||||
} else if got, want := gotOpt.EthernetAddress(), wantOpt.EthernetAddress(); got != want {
|
||||
t.Errorf("got EthernetAddress() = %s at index %d, want = %s", got, i, want)
|
||||
}
|
||||
case header.NDPNonceOption:
|
||||
gotOpt, ok := opt.(header.NDPNonceOption)
|
||||
if !ok {
|
||||
t.Errorf("got type = %T at index = %d; want = %T", opt, i, wantOpt)
|
||||
} else if diff := cmp.Diff(wantOpt.Nonce(), gotOpt.Nonce()); diff != "" {
|
||||
t.Errorf("nonce mismatch (-want +got):\n%s", diff)
|
||||
}
|
||||
default:
|
||||
t.Fatalf("checker not implemented for expected NDP option: %T", wantOpt)
|
||||
}
|
||||
|
||||
@@ -42,13 +42,17 @@ const (
|
||||
// option, as per RFC 4861 section 4.6.2.
|
||||
NDPPrefixInformationType NDPOptionIdentifier = 3
|
||||
|
||||
// NDPNonceOptionType is the type of the Nonce option, as per
|
||||
// RFC 3971 section 5.3.2.
|
||||
NDPNonceOptionType NDPOptionIdentifier = 14
|
||||
|
||||
// NDPRecursiveDNSServerOptionType is the type of the Recursive DNS
|
||||
// Server option, as per RFC 8106 section 5.1.
|
||||
NDPRecursiveDNSServerOptionType NDPOptionIdentifier = 25
|
||||
|
||||
// NDPDNSSearchListOptionType is the type of the DNS Search List option,
|
||||
// as per RFC 8106 section 5.2.
|
||||
NDPDNSSearchListOptionType = 31
|
||||
NDPDNSSearchListOptionType NDPOptionIdentifier = 31
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -231,6 +235,9 @@ func (i *NDPOptionIterator) Next() (NDPOption, bool, error) {
|
||||
case NDPTargetLinkLayerAddressOptionType:
|
||||
return NDPTargetLinkLayerAddressOption(body), false, nil
|
||||
|
||||
case NDPNonceOptionType:
|
||||
return NDPNonceOption(body), false, nil
|
||||
|
||||
case NDPPrefixInformationType:
|
||||
// Make sure the length of a Prefix Information option
|
||||
// body is ndpPrefixInformationLength, as per RFC 4861
|
||||
@@ -416,6 +423,37 @@ func (b NDPOptionsSerializer) Length() int {
|
||||
return l
|
||||
}
|
||||
|
||||
// NDPNonceOption is the NDP Nonce Option as defined by RFC 3971 section 5.3.2.
|
||||
//
|
||||
// It is the first X bytes following the NDP option's Type and Length field
|
||||
// where X is the value in Length multiplied by lengthByteUnits - 2 bytes.
|
||||
type NDPNonceOption []byte
|
||||
|
||||
// Type implements NDPOption.
|
||||
func (o NDPNonceOption) Type() NDPOptionIdentifier {
|
||||
return NDPNonceOptionType
|
||||
}
|
||||
|
||||
// Length implements NDPOption.
|
||||
func (o NDPNonceOption) Length() int {
|
||||
return len(o)
|
||||
}
|
||||
|
||||
// serializeInto implements NDPOption.
|
||||
func (o NDPNonceOption) serializeInto(b []byte) int {
|
||||
return copy(b, o)
|
||||
}
|
||||
|
||||
// String implements fmt.Stringer.
|
||||
func (o NDPNonceOption) String() string {
|
||||
return fmt.Sprintf("%T(%x)", o, []byte(o))
|
||||
}
|
||||
|
||||
// Nonce returns the nonce value this option holds.
|
||||
func (o NDPNonceOption) Nonce() []byte {
|
||||
return []byte(o)
|
||||
}
|
||||
|
||||
// NDPSourceLinkLayerAddressOption is the NDP Source Link Layer Option
|
||||
// as defined by RFC 4861 section 4.6.1.
|
||||
//
|
||||
|
||||
+305
-374
File diff suppressed because it is too large
Load Diff
@@ -12,7 +12,7 @@
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
// Code generated by "stringer -type NDPOptionIdentifier ."; DO NOT EDIT.
|
||||
// Code generated by "stringer -type NDPOptionIdentifier"; DO NOT EDIT.
|
||||
|
||||
package header
|
||||
|
||||
@@ -25,12 +25,16 @@ func _() {
|
||||
_ = x[NDPSourceLinkLayerAddressOptionType-1]
|
||||
_ = x[NDPTargetLinkLayerAddressOptionType-2]
|
||||
_ = x[NDPPrefixInformationType-3]
|
||||
_ = x[NDPNonceOptionType-14]
|
||||
_ = x[NDPRecursiveDNSServerOptionType-25]
|
||||
_ = x[NDPDNSSearchListOptionType-31]
|
||||
}
|
||||
|
||||
const (
|
||||
_NDPOptionIdentifier_name_0 = "NDPSourceLinkLayerAddressOptionTypeNDPTargetLinkLayerAddressOptionTypeNDPPrefixInformationType"
|
||||
_NDPOptionIdentifier_name_1 = "NDPRecursiveDNSServerOptionType"
|
||||
_NDPOptionIdentifier_name_1 = "NDPNonceOptionType"
|
||||
_NDPOptionIdentifier_name_2 = "NDPRecursiveDNSServerOptionType"
|
||||
_NDPOptionIdentifier_name_3 = "NDPDNSSearchListOptionType"
|
||||
)
|
||||
|
||||
var (
|
||||
@@ -42,8 +46,12 @@ func (i NDPOptionIdentifier) String() string {
|
||||
case 1 <= i && i <= 3:
|
||||
i -= 1
|
||||
return _NDPOptionIdentifier_name_0[_NDPOptionIdentifier_index_0[i]:_NDPOptionIdentifier_index_0[i+1]]
|
||||
case i == 25:
|
||||
case i == 14:
|
||||
return _NDPOptionIdentifier_name_1
|
||||
case i == 25:
|
||||
return _NDPOptionIdentifier_name_2
|
||||
case i == 31:
|
||||
return _NDPOptionIdentifier_name_3
|
||||
default:
|
||||
return "NDPOptionIdentifier(" + strconv.FormatInt(int64(i), 10) + ")"
|
||||
}
|
||||
|
||||
@@ -38,6 +38,7 @@ const (
|
||||
|
||||
var _ stack.DuplicateAddressDetector = (*endpoint)(nil)
|
||||
var _ stack.LinkAddressResolver = (*endpoint)(nil)
|
||||
var _ ip.DADProtocol = (*endpoint)(nil)
|
||||
|
||||
// ARP endpoints need to implement stack.NetworkEndpoint because the stack
|
||||
// considers the layer above the link-layer a network layer; the only
|
||||
@@ -82,7 +83,8 @@ func (*endpoint) DuplicateAddressProtocol() tcpip.NetworkProtocolNumber {
|
||||
return header.IPv4ProtocolNumber
|
||||
}
|
||||
|
||||
func (e *endpoint) SendDADMessage(addr tcpip.Address) tcpip.Error {
|
||||
// SendDADMessage implements ip.DADProtocol.
|
||||
func (e *endpoint) SendDADMessage(addr tcpip.Address, _ []byte) tcpip.Error {
|
||||
return e.sendARPRequest(header.IPv4Any, addr, header.EthernetBroadcastAddress)
|
||||
}
|
||||
|
||||
@@ -284,9 +286,12 @@ func (p *protocol) NewEndpoint(nic stack.NetworkInterface, dispatcher stack.Tran
|
||||
|
||||
e.mu.Lock()
|
||||
e.mu.dad.Init(&e.mu, p.options.DADConfigs, ip.DADOptions{
|
||||
Clock: p.stack.Clock(),
|
||||
Protocol: e,
|
||||
NICID: nic.ID(),
|
||||
Clock: p.stack.Clock(),
|
||||
SecureRNG: p.stack.SecureRNG(),
|
||||
// ARP does not support sending nonce values.
|
||||
NonceSize: 0,
|
||||
Protocol: e,
|
||||
NICID: nic.ID(),
|
||||
})
|
||||
e.mu.Unlock()
|
||||
|
||||
|
||||
@@ -16,14 +16,27 @@
|
||||
package ip
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io"
|
||||
|
||||
"gvisor.dev/gvisor/pkg/sync"
|
||||
"gvisor.dev/gvisor/pkg/tcpip"
|
||||
"gvisor.dev/gvisor/pkg/tcpip/stack"
|
||||
)
|
||||
|
||||
type extendRequest int
|
||||
|
||||
const (
|
||||
notRequested extendRequest = iota
|
||||
requested
|
||||
extended
|
||||
)
|
||||
|
||||
type dadState struct {
|
||||
nonce []byte
|
||||
extendRequest extendRequest
|
||||
|
||||
done *bool
|
||||
timer tcpip.Timer
|
||||
|
||||
@@ -33,14 +46,17 @@ type dadState struct {
|
||||
// DADProtocol is a protocol whose core state machine can be represented by DAD.
|
||||
type DADProtocol interface {
|
||||
// SendDADMessage attempts to send a DAD probe message.
|
||||
SendDADMessage(tcpip.Address) tcpip.Error
|
||||
SendDADMessage(tcpip.Address, []byte) tcpip.Error
|
||||
}
|
||||
|
||||
// DADOptions holds options for DAD.
|
||||
type DADOptions struct {
|
||||
Clock tcpip.Clock
|
||||
Protocol DADProtocol
|
||||
NICID tcpip.NICID
|
||||
Clock tcpip.Clock
|
||||
SecureRNG io.Reader
|
||||
NonceSize uint8
|
||||
ExtendDADTransmits uint8
|
||||
Protocol DADProtocol
|
||||
NICID tcpip.NICID
|
||||
}
|
||||
|
||||
// DAD performs duplicate address detection for addresses.
|
||||
@@ -63,6 +79,10 @@ func (d *DAD) Init(protocolMU sync.Locker, configs stack.DADConfigurations, opts
|
||||
panic("attempted to initialize DAD state twice")
|
||||
}
|
||||
|
||||
if opts.NonceSize != 0 && opts.ExtendDADTransmits == 0 {
|
||||
panic(fmt.Sprintf("given a non-zero value for NonceSize (%d) but zero for ExtendDADTransmits", opts.NonceSize))
|
||||
}
|
||||
|
||||
*d = DAD{
|
||||
opts: opts,
|
||||
configs: configs,
|
||||
@@ -96,10 +116,55 @@ func (d *DAD) CheckDuplicateAddressLocked(addr tcpip.Address, h stack.DADComplet
|
||||
s = dadState{
|
||||
done: &done,
|
||||
timer: d.opts.Clock.AfterFunc(0, func() {
|
||||
var err tcpip.Error
|
||||
dadDone := remaining == 0
|
||||
|
||||
nonce, earlyReturn := func() ([]byte, bool) {
|
||||
d.protocolMU.Lock()
|
||||
defer d.protocolMU.Unlock()
|
||||
|
||||
if done {
|
||||
return nil, true
|
||||
}
|
||||
|
||||
s, ok := d.addresses[addr]
|
||||
if !ok {
|
||||
panic(fmt.Sprintf("dad: timer fired but missing state for %s on NIC(%d)", addr, d.opts.NICID))
|
||||
}
|
||||
|
||||
// As per RFC 7527 section 4
|
||||
//
|
||||
// If any probe is looped back within RetransTimer milliseconds
|
||||
// after having sent DupAddrDetectTransmits NS(DAD) messages, the
|
||||
// interface continues with another MAX_MULTICAST_SOLICIT number of
|
||||
// NS(DAD) messages transmitted RetransTimer milliseconds apart.
|
||||
if dadDone && s.extendRequest == requested {
|
||||
dadDone = false
|
||||
remaining = d.opts.ExtendDADTransmits
|
||||
s.extendRequest = extended
|
||||
}
|
||||
|
||||
if !dadDone && d.opts.NonceSize != 0 {
|
||||
if s.nonce == nil {
|
||||
s.nonce = make([]byte, d.opts.NonceSize)
|
||||
}
|
||||
|
||||
if n, err := io.ReadFull(d.opts.SecureRNG, s.nonce); err != nil {
|
||||
panic(fmt.Sprintf("SecureRNG.Read(...): %s", err))
|
||||
} else if n != len(s.nonce) {
|
||||
panic(fmt.Sprintf("expected to read %d bytes from secure RNG, only read %d bytes", len(s.nonce), n))
|
||||
}
|
||||
}
|
||||
|
||||
d.addresses[addr] = s
|
||||
return s.nonce, false
|
||||
}()
|
||||
if earlyReturn {
|
||||
return
|
||||
}
|
||||
|
||||
var err tcpip.Error
|
||||
if !dadDone {
|
||||
err = d.opts.Protocol.SendDADMessage(addr)
|
||||
err = d.opts.Protocol.SendDADMessage(addr, nonce)
|
||||
}
|
||||
|
||||
d.protocolMU.Lock()
|
||||
@@ -142,6 +207,68 @@ func (d *DAD) CheckDuplicateAddressLocked(addr tcpip.Address, h stack.DADComplet
|
||||
return ret
|
||||
}
|
||||
|
||||
// ExtendIfNonceEqualLockedDisposition enumerates the possible results from
|
||||
// ExtendIfNonceEqualLocked.
|
||||
type ExtendIfNonceEqualLockedDisposition int
|
||||
|
||||
const (
|
||||
// Extended indicates that the DAD process was extended.
|
||||
Extended ExtendIfNonceEqualLockedDisposition = iota
|
||||
|
||||
// AlreadyExtended indicates that the DAD process was already extended.
|
||||
AlreadyExtended
|
||||
|
||||
// NoDADStateFound indicates that DAD state was not found for the address.
|
||||
NoDADStateFound
|
||||
|
||||
// NonceDisabled indicates that nonce values are not sent with DAD messages.
|
||||
NonceDisabled
|
||||
|
||||
// NonceNotEqual indicates that the nonce value passed and the nonce in the
|
||||
// last send DAD message are not equal.
|
||||
NonceNotEqual
|
||||
)
|
||||
|
||||
// ExtendIfNonceEqualLocked extends the DAD process if the provided nonce is the
|
||||
// same as the nonce sent in the last DAD message.
|
||||
//
|
||||
// Precondition: d.protocolMU must be locked.
|
||||
func (d *DAD) ExtendIfNonceEqualLocked(addr tcpip.Address, nonce []byte) ExtendIfNonceEqualLockedDisposition {
|
||||
s, ok := d.addresses[addr]
|
||||
if !ok {
|
||||
return NoDADStateFound
|
||||
}
|
||||
|
||||
if d.opts.NonceSize == 0 {
|
||||
return NonceDisabled
|
||||
}
|
||||
|
||||
if s.extendRequest != notRequested {
|
||||
return AlreadyExtended
|
||||
}
|
||||
|
||||
// As per RFC 7527 section 4
|
||||
//
|
||||
// If any probe is looped back within RetransTimer milliseconds after having
|
||||
// sent DupAddrDetectTransmits NS(DAD) messages, the interface continues
|
||||
// with another MAX_MULTICAST_SOLICIT number of NS(DAD) messages transmitted
|
||||
// RetransTimer milliseconds apart.
|
||||
//
|
||||
// If a DAD message has already been sent and the nonce value we observed is
|
||||
// the same as the nonce value we last sent, then we assume our probe was
|
||||
// looped back and request an extension to the DAD process.
|
||||
//
|
||||
// Note, the first DAD message is sent asynchronously so we need to make sure
|
||||
// that we sent a DAD message by checking if we have a nonce value set.
|
||||
if s.nonce != nil && bytes.Equal(s.nonce, nonce) {
|
||||
s.extendRequest = requested
|
||||
d.addresses[addr] = s
|
||||
return Extended
|
||||
}
|
||||
|
||||
return NonceNotEqual
|
||||
}
|
||||
|
||||
// StopLocked stops a currently running DAD process.
|
||||
//
|
||||
// Precondition: d.protocolMU must be locked.
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
package ip_test
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
@@ -32,8 +33,8 @@ type mockDADProtocol struct {
|
||||
mu struct {
|
||||
sync.Mutex
|
||||
|
||||
dad ip.DAD
|
||||
sendCount map[tcpip.Address]int
|
||||
dad ip.DAD
|
||||
sentNonces map[tcpip.Address][][]byte
|
||||
}
|
||||
}
|
||||
|
||||
@@ -48,26 +49,30 @@ func (m *mockDADProtocol) init(t *testing.T, c stack.DADConfigurations, opts ip.
|
||||
}
|
||||
|
||||
func (m *mockDADProtocol) initLocked() {
|
||||
m.mu.sendCount = make(map[tcpip.Address]int)
|
||||
m.mu.sentNonces = make(map[tcpip.Address][][]byte)
|
||||
}
|
||||
|
||||
func (m *mockDADProtocol) SendDADMessage(addr tcpip.Address) tcpip.Error {
|
||||
func (m *mockDADProtocol) SendDADMessage(addr tcpip.Address, nonce []byte) tcpip.Error {
|
||||
m.mu.Lock()
|
||||
defer m.mu.Unlock()
|
||||
m.mu.sendCount[addr]++
|
||||
m.mu.sentNonces[addr] = append(m.mu.sentNonces[addr], nonce)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *mockDADProtocol) check(addrs []tcpip.Address) string {
|
||||
sentNonces := make(map[tcpip.Address][][]byte)
|
||||
for _, a := range addrs {
|
||||
sentNonces[a] = append(sentNonces[a], nil)
|
||||
}
|
||||
|
||||
return m.checkWithNonce(sentNonces)
|
||||
}
|
||||
|
||||
func (m *mockDADProtocol) checkWithNonce(expectedSentNonces map[tcpip.Address][][]byte) string {
|
||||
m.mu.Lock()
|
||||
defer m.mu.Unlock()
|
||||
|
||||
sendCount := make(map[tcpip.Address]int)
|
||||
for _, a := range addrs {
|
||||
sendCount[a]++
|
||||
}
|
||||
|
||||
diff := cmp.Diff(sendCount, m.mu.sendCount)
|
||||
diff := cmp.Diff(expectedSentNonces, m.mu.sentNonces)
|
||||
m.initLocked()
|
||||
return diff
|
||||
}
|
||||
@@ -84,6 +89,12 @@ func (m *mockDADProtocol) stop(addr tcpip.Address, reason stack.DADResult) {
|
||||
m.mu.dad.StopLocked(addr, reason)
|
||||
}
|
||||
|
||||
func (m *mockDADProtocol) extendIfNonceEqual(addr tcpip.Address, nonce []byte) ip.ExtendIfNonceEqualLockedDisposition {
|
||||
m.mu.Lock()
|
||||
defer m.mu.Unlock()
|
||||
return m.mu.dad.ExtendIfNonceEqualLocked(addr, nonce)
|
||||
}
|
||||
|
||||
func (m *mockDADProtocol) setConfigs(c stack.DADConfigurations) {
|
||||
m.mu.Lock()
|
||||
defer m.mu.Unlock()
|
||||
@@ -277,3 +288,94 @@ func TestDADStop(t *testing.T) {
|
||||
default:
|
||||
}
|
||||
}
|
||||
|
||||
func TestNonce(t *testing.T) {
|
||||
const (
|
||||
nonceSize = 2
|
||||
|
||||
extendRequestAttempts = 2
|
||||
|
||||
dupAddrDetectTransmits = 2
|
||||
extendTransmits = 5
|
||||
)
|
||||
|
||||
var secureRNGBytes [nonceSize * (dupAddrDetectTransmits + extendTransmits)]byte
|
||||
for i := range secureRNGBytes {
|
||||
secureRNGBytes[i] = byte(i)
|
||||
}
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
mockedReceivedNonce []byte
|
||||
expectedResults [extendRequestAttempts]ip.ExtendIfNonceEqualLockedDisposition
|
||||
expectedTransmits int
|
||||
}{
|
||||
{
|
||||
name: "not matching",
|
||||
mockedReceivedNonce: []byte{0, 0},
|
||||
expectedResults: [extendRequestAttempts]ip.ExtendIfNonceEqualLockedDisposition{ip.NonceNotEqual, ip.NonceNotEqual},
|
||||
expectedTransmits: dupAddrDetectTransmits,
|
||||
},
|
||||
{
|
||||
name: "matching nonce",
|
||||
mockedReceivedNonce: secureRNGBytes[:nonceSize],
|
||||
expectedResults: [extendRequestAttempts]ip.ExtendIfNonceEqualLockedDisposition{ip.Extended, ip.AlreadyExtended},
|
||||
expectedTransmits: dupAddrDetectTransmits + extendTransmits,
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
var dad mockDADProtocol
|
||||
clock := faketime.NewManualClock()
|
||||
dadConfigs := stack.DADConfigurations{
|
||||
DupAddrDetectTransmits: dupAddrDetectTransmits,
|
||||
RetransmitTimer: time.Second,
|
||||
}
|
||||
|
||||
var secureRNG bytes.Reader
|
||||
secureRNG.Reset(secureRNGBytes[:])
|
||||
dad.init(t, dadConfigs, ip.DADOptions{
|
||||
Clock: clock,
|
||||
SecureRNG: &secureRNG,
|
||||
NonceSize: nonceSize,
|
||||
ExtendDADTransmits: extendTransmits,
|
||||
})
|
||||
|
||||
ch := make(chan dadResult, 1)
|
||||
if res := dad.checkDuplicateAddress(addr1, handler(ch, addr1)); res != stack.DADStarting {
|
||||
t.Errorf("got dad.checkDuplicateAddress(%s, _) = %d, want = %d", addr1, res, stack.DADStarting)
|
||||
}
|
||||
|
||||
clock.Advance(0)
|
||||
for i, want := range test.expectedResults {
|
||||
if got := dad.extendIfNonceEqual(addr1, test.mockedReceivedNonce); got != want {
|
||||
t.Errorf("(i=%d) got dad.extendIfNonceEqual(%s, _) = %d, want = %d", i, addr1, got, want)
|
||||
}
|
||||
}
|
||||
|
||||
for i := 0; i < test.expectedTransmits; i++ {
|
||||
if diff := dad.checkWithNonce(map[tcpip.Address][][]byte{
|
||||
addr1: {
|
||||
secureRNGBytes[nonceSize*i:][:nonceSize],
|
||||
},
|
||||
}); diff != "" {
|
||||
t.Errorf("(i=%d) dad check mismatch (-want +got):\n%s", i, diff)
|
||||
}
|
||||
|
||||
clock.Advance(dadConfigs.RetransmitTimer)
|
||||
}
|
||||
|
||||
if diff := cmp.Diff(dadResult{Addr: addr1, R: &stack.DADSucceeded{}}, <-ch); diff != "" {
|
||||
t.Errorf("dad result mismatch (-want +got):\n%s", diff)
|
||||
}
|
||||
|
||||
// Should not have anymore updates.
|
||||
select {
|
||||
case r := <-ch:
|
||||
t.Fatalf("unexpectedly got an extra DAD result; r = %#v", r)
|
||||
default:
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -369,6 +369,18 @@ func (e *endpoint) handleICMP(pkt *stack.PacketBuffer, hasFragmentHeader bool, r
|
||||
return
|
||||
}
|
||||
|
||||
var it header.NDPOptionIterator
|
||||
{
|
||||
var err error
|
||||
it, err = ns.Options().Iter(false /* check */)
|
||||
if err != nil {
|
||||
// Options are not valid as per the wire format, silently drop the
|
||||
// packet.
|
||||
received.invalid.Increment()
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
if e.hasTentativeAddr(targetAddr) {
|
||||
// If the target address is tentative and the source of the packet is a
|
||||
// unicast (specified) address, then the source of the packet is
|
||||
@@ -382,6 +394,22 @@ func (e *endpoint) handleICMP(pkt *stack.PacketBuffer, hasFragmentHeader bool, r
|
||||
// stack know so it can handle such a scenario and do nothing further with
|
||||
// the NS.
|
||||
if srcAddr == header.IPv6Any {
|
||||
var nonce []byte
|
||||
for {
|
||||
opt, done, err := it.Next()
|
||||
if err != nil {
|
||||
received.invalid.Increment()
|
||||
return
|
||||
}
|
||||
if done {
|
||||
break
|
||||
}
|
||||
if n, ok := opt.(header.NDPNonceOption); ok {
|
||||
nonce = n.Nonce()
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
// Since this is a DAD message we know the sender does not actually hold
|
||||
// the target address so there is no "holder".
|
||||
var holderLinkAddress tcpip.LinkAddress
|
||||
@@ -397,7 +425,7 @@ func (e *endpoint) handleICMP(pkt *stack.PacketBuffer, hasFragmentHeader bool, r
|
||||
//
|
||||
// TODO(gvisor.dev/issue/4046): Handle the scenario when a duplicate
|
||||
// address is detected for an assigned address.
|
||||
switch err := e.dupTentativeAddrDetected(targetAddr, holderLinkAddress); err.(type) {
|
||||
switch err := e.dupTentativeAddrDetected(targetAddr, holderLinkAddress, nonce); err.(type) {
|
||||
case nil, *tcpip.ErrBadAddress, *tcpip.ErrInvalidEndpointState:
|
||||
default:
|
||||
panic(fmt.Sprintf("unexpected error handling duplicate tentative address: %s", err))
|
||||
@@ -418,21 +446,10 @@ func (e *endpoint) handleICMP(pkt *stack.PacketBuffer, hasFragmentHeader bool, r
|
||||
return
|
||||
}
|
||||
|
||||
var sourceLinkAddr tcpip.LinkAddress
|
||||
{
|
||||
it, err := ns.Options().Iter(false /* check */)
|
||||
if err != nil {
|
||||
// Options are not valid as per the wire format, silently drop the
|
||||
// packet.
|
||||
received.invalid.Increment()
|
||||
return
|
||||
}
|
||||
|
||||
sourceLinkAddr, ok = getSourceLinkAddr(it)
|
||||
if !ok {
|
||||
received.invalid.Increment()
|
||||
return
|
||||
}
|
||||
sourceLinkAddr, ok := getSourceLinkAddr(it)
|
||||
if !ok {
|
||||
received.invalid.Increment()
|
||||
return
|
||||
}
|
||||
|
||||
// As per RFC 4861 section 4.3, the Source Link-Layer Address Option MUST
|
||||
@@ -586,6 +603,10 @@ func (e *endpoint) handleICMP(pkt *stack.PacketBuffer, hasFragmentHeader bool, r
|
||||
e.dad.mu.Unlock()
|
||||
|
||||
if e.hasTentativeAddr(targetAddr) {
|
||||
// We only send a nonce value in DAD messages to check for loopedback
|
||||
// messages so we use the empty nonce value here.
|
||||
var nonce []byte
|
||||
|
||||
// We just got an NA from a node that owns an address we are performing
|
||||
// DAD on, implying the address is not unique. In this case we let the
|
||||
// stack know so it can handle such a scenario and do nothing furthur with
|
||||
@@ -602,7 +623,7 @@ func (e *endpoint) handleICMP(pkt *stack.PacketBuffer, hasFragmentHeader bool, r
|
||||
//
|
||||
// TODO(gvisor.dev/issue/4046): Handle the scenario when a duplicate
|
||||
// address is detected for an assigned address.
|
||||
switch err := e.dupTentativeAddrDetected(targetAddr, targetLinkAddr); err.(type) {
|
||||
switch err := e.dupTentativeAddrDetected(targetAddr, targetLinkAddr, nonce); err.(type) {
|
||||
case nil, *tcpip.ErrBadAddress, *tcpip.ErrInvalidEndpointState:
|
||||
return
|
||||
default:
|
||||
|
||||
@@ -348,7 +348,7 @@ func (e *endpoint) hasTentativeAddr(addr tcpip.Address) bool {
|
||||
// dupTentativeAddrDetected removes the tentative address if it exists. If the
|
||||
// address was generated via SLAAC, an attempt is made to generate a new
|
||||
// address.
|
||||
func (e *endpoint) dupTentativeAddrDetected(addr tcpip.Address, holderLinkAddr tcpip.LinkAddress) tcpip.Error {
|
||||
func (e *endpoint) dupTentativeAddrDetected(addr tcpip.Address, holderLinkAddr tcpip.LinkAddress, nonce []byte) tcpip.Error {
|
||||
e.mu.Lock()
|
||||
defer e.mu.Unlock()
|
||||
|
||||
@@ -361,27 +361,48 @@ func (e *endpoint) dupTentativeAddrDetected(addr tcpip.Address, holderLinkAddr t
|
||||
return &tcpip.ErrInvalidEndpointState{}
|
||||
}
|
||||
|
||||
// If the address is a SLAAC address, do not invalidate its SLAAC prefix as an
|
||||
// attempt will be made to generate a new address for it.
|
||||
if err := e.removePermanentEndpointLocked(addressEndpoint, false /* allowSLAACInvalidation */, &stack.DADDupAddrDetected{HolderLinkAddress: holderLinkAddr}); err != nil {
|
||||
return err
|
||||
}
|
||||
switch result := e.mu.ndp.dad.ExtendIfNonceEqualLocked(addr, nonce); result {
|
||||
case ip.Extended:
|
||||
// The nonce we got back was the same we sent so we know the message
|
||||
// indicating a duplicate address was likely ours so do not consider
|
||||
// the address duplicate here.
|
||||
return nil
|
||||
case ip.AlreadyExtended:
|
||||
// See Extended.
|
||||
//
|
||||
// Our DAD message was looped back already.
|
||||
return nil
|
||||
case ip.NoDADStateFound:
|
||||
panic(fmt.Sprintf("expected DAD state for tentative address %s", addr))
|
||||
case ip.NonceDisabled:
|
||||
// If nonce is disabled then we have no way to know if the packet was
|
||||
// looped-back so we have to assume it indicates a duplicate address.
|
||||
fallthrough
|
||||
case ip.NonceNotEqual:
|
||||
// If the address is a SLAAC address, do not invalidate its SLAAC prefix as an
|
||||
// attempt will be made to generate a new address for it.
|
||||
if err := e.removePermanentEndpointLocked(addressEndpoint, false /* allowSLAACInvalidation */, &stack.DADDupAddrDetected{HolderLinkAddress: holderLinkAddr}); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
prefix := addressEndpoint.Subnet()
|
||||
prefix := addressEndpoint.Subnet()
|
||||
|
||||
switch t := addressEndpoint.ConfigType(); t {
|
||||
case stack.AddressConfigStatic:
|
||||
case stack.AddressConfigSlaac:
|
||||
e.mu.ndp.regenerateSLAACAddr(prefix)
|
||||
case stack.AddressConfigSlaacTemp:
|
||||
// Do not reset the generation attempts counter for the prefix as the
|
||||
// temporary address is being regenerated in response to a DAD conflict.
|
||||
e.mu.ndp.regenerateTempSLAACAddr(prefix, false /* resetGenAttempts */)
|
||||
switch t := addressEndpoint.ConfigType(); t {
|
||||
case stack.AddressConfigStatic:
|
||||
case stack.AddressConfigSlaac:
|
||||
e.mu.ndp.regenerateSLAACAddr(prefix)
|
||||
case stack.AddressConfigSlaacTemp:
|
||||
// Do not reset the generation attempts counter for the prefix as the
|
||||
// temporary address is being regenerated in response to a DAD conflict.
|
||||
e.mu.ndp.regenerateTempSLAACAddr(prefix, false /* resetGenAttempts */)
|
||||
default:
|
||||
panic(fmt.Sprintf("unrecognized address config type = %d", t))
|
||||
}
|
||||
|
||||
return nil
|
||||
default:
|
||||
panic(fmt.Sprintf("unrecognized address config type = %d", t))
|
||||
panic(fmt.Sprintf("unhandled result = %d", result))
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// transitionForwarding transitions the endpoint's forwarding status to
|
||||
@@ -1797,16 +1818,36 @@ func (p *protocol) NewEndpoint(nic stack.NetworkInterface, dispatcher stack.Tran
|
||||
dispatcher: dispatcher,
|
||||
protocol: p,
|
||||
}
|
||||
|
||||
// NDP options must be 8 octet aligned and the first 2 bytes are used for
|
||||
// the type and length fields leaving 6 octets as the minimum size for a
|
||||
// nonce option without padding.
|
||||
const nonceSize = 6
|
||||
|
||||
// As per RFC 7527 section 4.1,
|
||||
//
|
||||
// If any probe is looped back within RetransTimer milliseconds after
|
||||
// having sent DupAddrDetectTransmits NS(DAD) messages, the interface
|
||||
// continues with another MAX_MULTICAST_SOLICIT number of NS(DAD)
|
||||
// messages transmitted RetransTimer milliseconds apart.
|
||||
//
|
||||
// Value taken from RFC 4861 section 10.
|
||||
const maxMulticastSolicit = 3
|
||||
dadOptions := ip.DADOptions{
|
||||
Clock: p.stack.Clock(),
|
||||
SecureRNG: p.stack.SecureRNG(),
|
||||
NonceSize: nonceSize,
|
||||
ExtendDADTransmits: maxMulticastSolicit,
|
||||
Protocol: &e.mu.ndp,
|
||||
NICID: nic.ID(),
|
||||
}
|
||||
|
||||
e.mu.Lock()
|
||||
e.mu.addressableEndpointState.Init(e)
|
||||
e.mu.ndp.init(e)
|
||||
e.mu.ndp.init(e, dadOptions)
|
||||
e.mu.mld.init(e)
|
||||
e.dad.mu.Lock()
|
||||
e.dad.mu.dad.Init(&e.dad.mu, p.options.DADConfigs, ip.DADOptions{
|
||||
Clock: p.stack.Clock(),
|
||||
Protocol: &e.mu.ndp,
|
||||
NICID: nic.ID(),
|
||||
})
|
||||
e.dad.mu.dad.Init(&e.dad.mu, p.options.DADConfigs, dadOptions)
|
||||
e.dad.mu.Unlock()
|
||||
e.mu.Unlock()
|
||||
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
package ipv6_test
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
@@ -119,11 +120,26 @@ func TestSendQueuedMLDReports(t *testing.T) {
|
||||
},
|
||||
}
|
||||
|
||||
nonce := [...]byte{
|
||||
1, 2, 3, 4, 5, 6,
|
||||
}
|
||||
|
||||
const maxNSMessages = 2
|
||||
secureRNGBytes := make([]byte, len(nonce)*maxNSMessages)
|
||||
for b := secureRNGBytes[:]; len(b) > 0; b = b[len(nonce):] {
|
||||
if n := copy(b, nonce[:]); n != len(nonce) {
|
||||
t.Fatalf("got copy(...) = %d, want = %d", n, len(nonce))
|
||||
}
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
dadResolutionTime := test.retransmitTimer * time.Duration(test.dadTransmits)
|
||||
clock := faketime.NewManualClock()
|
||||
var secureRNG bytes.Reader
|
||||
secureRNG.Reset(secureRNGBytes[:])
|
||||
s := stack.New(stack.Options{
|
||||
SecureRNG: &secureRNG,
|
||||
NetworkProtocols: []stack.NetworkProtocolFactory{ipv6.NewProtocolWithOptions(ipv6.Options{
|
||||
DADConfigs: stack.DADConfigurations{
|
||||
DupAddrDetectTransmits: test.dadTransmits,
|
||||
@@ -154,7 +170,7 @@ func TestSendQueuedMLDReports(t *testing.T) {
|
||||
checker.TTL(header.NDPHopLimit),
|
||||
checker.NDPNS(
|
||||
checker.NDPNSTargetAddress(addr),
|
||||
checker.NDPNSOptions(nil),
|
||||
checker.NDPNSOptions([]header.NDPOption{header.NDPNonceOption(nonce[:])}),
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1789,18 +1789,14 @@ func (ndp *ndpState) stopSolicitingRouters() {
|
||||
ndp.rtrSolicitTimer = timer{}
|
||||
}
|
||||
|
||||
func (ndp *ndpState) init(ep *endpoint) {
|
||||
func (ndp *ndpState) init(ep *endpoint, dadOptions ip.DADOptions) {
|
||||
if ndp.defaultRouters != nil {
|
||||
panic("attempted to initialize NDP state twice")
|
||||
}
|
||||
|
||||
ndp.ep = ep
|
||||
ndp.configs = ep.protocol.options.NDPConfigs
|
||||
ndp.dad.Init(&ndp.ep.mu, ep.protocol.options.DADConfigs, ip.DADOptions{
|
||||
Clock: ep.protocol.stack.Clock(),
|
||||
Protocol: ndp,
|
||||
NICID: ep.nic.ID(),
|
||||
})
|
||||
ndp.dad.Init(&ndp.ep.mu, ep.protocol.options.DADConfigs, dadOptions)
|
||||
ndp.defaultRouters = make(map[tcpip.Address]defaultRouterState)
|
||||
ndp.onLinkPrefixes = make(map[tcpip.Subnet]onLinkPrefixState)
|
||||
ndp.slaacPrefixes = make(map[tcpip.Subnet]slaacPrefixState)
|
||||
@@ -1811,9 +1807,11 @@ func (ndp *ndpState) init(ep *endpoint) {
|
||||
}
|
||||
}
|
||||
|
||||
func (ndp *ndpState) SendDADMessage(addr tcpip.Address) tcpip.Error {
|
||||
func (ndp *ndpState) SendDADMessage(addr tcpip.Address, nonce []byte) tcpip.Error {
|
||||
snmc := header.SolicitedNodeAddr(addr)
|
||||
return ndp.ep.sendNDPNS(header.IPv6Any, snmc, addr, header.EthernetAddressFromMulticastIPv6Address(snmc), nil /* opts */)
|
||||
return ndp.ep.sendNDPNS(header.IPv6Any, snmc, addr, header.EthernetAddressFromMulticastIPv6Address(snmc), header.NDPOptionsSerializer{
|
||||
header.NDPNonceOption(nonce),
|
||||
})
|
||||
}
|
||||
|
||||
func (e *endpoint) sendNDPNS(srcAddr, dstAddr, targetAddr tcpip.Address, remoteLinkAddr tcpip.LinkAddress, opts header.NDPOptionsSerializer) tcpip.Error {
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
package ipv6
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"strings"
|
||||
"testing"
|
||||
@@ -1264,8 +1265,21 @@ func TestCheckDuplicateAddress(t *testing.T) {
|
||||
DupAddrDetectTransmits: 1,
|
||||
RetransmitTimer: time.Second,
|
||||
}
|
||||
|
||||
nonces := [...][]byte{
|
||||
{1, 2, 3, 4, 5, 6},
|
||||
{7, 8, 9, 10, 11, 12},
|
||||
}
|
||||
|
||||
var secureRNGBytes []byte
|
||||
for _, n := range nonces {
|
||||
secureRNGBytes = append(secureRNGBytes, n...)
|
||||
}
|
||||
var secureRNG bytes.Reader
|
||||
secureRNG.Reset(secureRNGBytes[:])
|
||||
s := stack.New(stack.Options{
|
||||
Clock: clock,
|
||||
SecureRNG: &secureRNG,
|
||||
Clock: clock,
|
||||
NetworkProtocols: []stack.NetworkProtocolFactory{NewProtocolWithOptions(Options{
|
||||
DADConfigs: dadConfigs,
|
||||
})},
|
||||
@@ -1278,10 +1292,36 @@ func TestCheckDuplicateAddress(t *testing.T) {
|
||||
t.Fatalf("CreateNIC(%d, _) = %s", nicID, err)
|
||||
}
|
||||
|
||||
dadPacketsSent := 1
|
||||
dadPacketsSent := 0
|
||||
snmc := header.SolicitedNodeAddr(lladdr0)
|
||||
remoteLinkAddr := header.EthernetAddressFromMulticastIPv6Address(snmc)
|
||||
checkDADMsg := func() {
|
||||
p, ok := e.ReadContext(context.Background())
|
||||
if !ok {
|
||||
t.Fatalf("expected %d-th DAD message", dadPacketsSent)
|
||||
}
|
||||
|
||||
if p.Proto != header.IPv6ProtocolNumber {
|
||||
t.Errorf("(i=%d) got p.Proto = %d, want = %d", dadPacketsSent, p.Proto, header.IPv6ProtocolNumber)
|
||||
}
|
||||
|
||||
if p.Route.RemoteLinkAddress != remoteLinkAddr {
|
||||
t.Errorf("(i=%d) got p.Route.RemoteLinkAddress = %s, want = %s", dadPacketsSent, p.Route.RemoteLinkAddress, remoteLinkAddr)
|
||||
}
|
||||
|
||||
checker.IPv6(t, stack.PayloadSince(p.Pkt.NetworkHeader()),
|
||||
checker.SrcAddr(header.IPv6Any),
|
||||
checker.DstAddr(snmc),
|
||||
checker.TTL(header.NDPHopLimit),
|
||||
checker.NDPNS(
|
||||
checker.NDPNSTargetAddress(lladdr0),
|
||||
checker.NDPNSOptions([]header.NDPOption{header.NDPNonceOption(nonces[dadPacketsSent])}),
|
||||
))
|
||||
}
|
||||
if err := s.AddAddress(nicID, ProtocolNumber, lladdr0); err != nil {
|
||||
t.Fatalf("AddAddress(%d, %d, %s) = %s", nicID, ProtocolNumber, lladdr0, err)
|
||||
}
|
||||
checkDADMsg()
|
||||
|
||||
// Start DAD for the address we just added.
|
||||
//
|
||||
@@ -1297,6 +1337,7 @@ func TestCheckDuplicateAddress(t *testing.T) {
|
||||
} else if res != stack.DADStarting {
|
||||
t.Fatalf("got s.CheckDuplicateAddress(%d, %d, %s, _) = %d, want = %d", nicID, ProtocolNumber, lladdr0, res, stack.DADStarting)
|
||||
}
|
||||
checkDADMsg()
|
||||
|
||||
// Remove the address and make sure our DAD request was not stopped.
|
||||
if err := s.RemoveAddress(nicID, lladdr0); err != nil {
|
||||
@@ -1328,33 +1369,6 @@ func TestCheckDuplicateAddress(t *testing.T) {
|
||||
default:
|
||||
}
|
||||
|
||||
snmc := header.SolicitedNodeAddr(lladdr0)
|
||||
remoteLinkAddr := header.EthernetAddressFromMulticastIPv6Address(snmc)
|
||||
|
||||
for i := 0; i < dadPacketsSent; i++ {
|
||||
p, ok := e.Read()
|
||||
if !ok {
|
||||
t.Fatalf("expected %d-th DAD message", i)
|
||||
}
|
||||
|
||||
if p.Proto != header.IPv6ProtocolNumber {
|
||||
t.Errorf("(i=%d) got p.Proto = %d, want = %d", i, p.Proto, header.IPv6ProtocolNumber)
|
||||
}
|
||||
|
||||
if p.Route.RemoteLinkAddress != remoteLinkAddr {
|
||||
t.Errorf("(i=%d) got p.Route.RemoteLinkAddress = %s, want = %s", i, p.Route.RemoteLinkAddress, remoteLinkAddr)
|
||||
}
|
||||
|
||||
checker.IPv6(t, stack.PayloadSince(p.Pkt.NetworkHeader()),
|
||||
checker.SrcAddr(header.IPv6Any),
|
||||
checker.DstAddr(snmc),
|
||||
checker.TTL(header.NDPHopLimit),
|
||||
checker.NDPNS(
|
||||
checker.NDPNSTargetAddress(lladdr0),
|
||||
checker.NDPNSOptions(nil),
|
||||
))
|
||||
}
|
||||
|
||||
// Should have no more packets.
|
||||
if p, ok := e.Read(); ok {
|
||||
t.Errorf("got unexpected packet = %#v", p)
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
package stack_test
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
@@ -29,6 +30,7 @@ import (
|
||||
"gvisor.dev/gvisor/pkg/tcpip/faketime"
|
||||
"gvisor.dev/gvisor/pkg/tcpip/header"
|
||||
"gvisor.dev/gvisor/pkg/tcpip/link/channel"
|
||||
"gvisor.dev/gvisor/pkg/tcpip/link/loopback"
|
||||
"gvisor.dev/gvisor/pkg/tcpip/network/ipv6"
|
||||
"gvisor.dev/gvisor/pkg/tcpip/stack"
|
||||
"gvisor.dev/gvisor/pkg/tcpip/transport/icmp"
|
||||
@@ -358,6 +360,66 @@ func TestDADDisabled(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestDADResolveLoopback(t *testing.T) {
|
||||
const nicID = 1
|
||||
ndpDisp := ndpDispatcher{
|
||||
dadC: make(chan ndpDADEvent, 1),
|
||||
}
|
||||
|
||||
dadConfigs := stack.DADConfigurations{
|
||||
RetransmitTimer: time.Second,
|
||||
DupAddrDetectTransmits: 1,
|
||||
}
|
||||
clock := faketime.NewManualClock()
|
||||
s := stack.New(stack.Options{
|
||||
Clock: clock,
|
||||
NetworkProtocols: []stack.NetworkProtocolFactory{ipv6.NewProtocolWithOptions(ipv6.Options{
|
||||
NDPDisp: &ndpDisp,
|
||||
DADConfigs: dadConfigs,
|
||||
})},
|
||||
})
|
||||
if err := s.CreateNIC(nicID, loopback.New()); err != nil {
|
||||
t.Fatalf("CreateNIC(%d, _) = %s", nicID, err)
|
||||
}
|
||||
|
||||
addrWithPrefix := tcpip.AddressWithPrefix{
|
||||
Address: addr1,
|
||||
PrefixLen: defaultPrefixLen,
|
||||
}
|
||||
if err := s.AddAddressWithPrefix(nicID, header.IPv6ProtocolNumber, addrWithPrefix); err != nil {
|
||||
t.Fatalf("AddAddressWithPrefix(%d, %d, %s) = %s", nicID, header.IPv6ProtocolNumber, addrWithPrefix, err)
|
||||
}
|
||||
|
||||
// Address should not be considered bound to the NIC yet (DAD ongoing).
|
||||
if err := checkGetMainNICAddress(s, nicID, header.IPv6ProtocolNumber, tcpip.AddressWithPrefix{}); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
// DAD should not resolve after the normal resolution time since our DAD
|
||||
// message was looped back - we should extend our DAD process.
|
||||
dadResolutionTime := time.Duration(dadConfigs.DupAddrDetectTransmits) * dadConfigs.RetransmitTimer
|
||||
clock.Advance(dadResolutionTime)
|
||||
if err := checkGetMainNICAddress(s, nicID, header.IPv6ProtocolNumber, tcpip.AddressWithPrefix{}); err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
|
||||
// Make sure the address does not resolve before the extended resolution time
|
||||
// has passed.
|
||||
const delta = time.Nanosecond
|
||||
// DAD will send extra NS probes if an NS message is looped back.
|
||||
const extraTransmits = 3
|
||||
clock.Advance(dadResolutionTime*extraTransmits - delta)
|
||||
if err := checkGetMainNICAddress(s, nicID, header.IPv6ProtocolNumber, tcpip.AddressWithPrefix{}); err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
|
||||
// DAD should now resolve.
|
||||
clock.Advance(delta)
|
||||
if diff := checkDADEvent(<-ndpDisp.dadC, nicID, addr1, &stack.DADSucceeded{}); diff != "" {
|
||||
t.Errorf("DAD event mismatch (-want +got):\n%s", diff)
|
||||
}
|
||||
}
|
||||
|
||||
// TestDADResolve tests that an address successfully resolves after performing
|
||||
// DAD for various values of DupAddrDetectTransmits and RetransmitTimer.
|
||||
// Included in the subtests is a test to make sure that an invalid
|
||||
@@ -404,6 +466,16 @@ func TestDADResolve(t *testing.T) {
|
||||
},
|
||||
}
|
||||
|
||||
nonces := [][]byte{
|
||||
{1, 2, 3, 4, 5, 6},
|
||||
{7, 8, 9, 10, 11, 12},
|
||||
}
|
||||
|
||||
var secureRNGBytes []byte
|
||||
for _, n := range nonces {
|
||||
secureRNGBytes = append(secureRNGBytes, n...)
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
test := test
|
||||
|
||||
@@ -419,7 +491,12 @@ func TestDADResolve(t *testing.T) {
|
||||
headerLength: test.linkHeaderLen,
|
||||
}
|
||||
e.Endpoint.LinkEPCapabilities |= stack.CapabilityResolutionRequired
|
||||
|
||||
var secureRNG bytes.Reader
|
||||
secureRNG.Reset(secureRNGBytes)
|
||||
|
||||
s := stack.New(stack.Options{
|
||||
SecureRNG: &secureRNG,
|
||||
NetworkProtocols: []stack.NetworkProtocolFactory{ipv6.NewProtocolWithOptions(ipv6.Options{
|
||||
NDPDisp: &ndpDisp,
|
||||
DADConfigs: stack.DADConfigurations{
|
||||
@@ -553,7 +630,7 @@ func TestDADResolve(t *testing.T) {
|
||||
checker.TTL(header.NDPHopLimit),
|
||||
checker.NDPNS(
|
||||
checker.NDPNSTargetAddress(addr1),
|
||||
checker.NDPNSOptions(nil),
|
||||
checker.NDPNSOptions([]header.NDPOption{header.NDPNonceOption(nonces[i])}),
|
||||
))
|
||||
|
||||
if l, want := p.Pkt.AvailableHeaderBytes(), int(test.linkHeaderLen); l != want {
|
||||
|
||||
@@ -23,6 +23,7 @@ import (
|
||||
"bytes"
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
"io"
|
||||
mathrand "math/rand"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
@@ -445,6 +446,9 @@ type Stack struct {
|
||||
// used when a random number is required.
|
||||
randomGenerator *mathrand.Rand
|
||||
|
||||
// secureRNG is a cryptographically secure random number generator.
|
||||
secureRNG io.Reader
|
||||
|
||||
// sendBufferSize holds the min/default/max send buffer sizes for
|
||||
// endpoints other than TCP.
|
||||
sendBufferSize tcpip.SendBufferSizeOption
|
||||
@@ -528,6 +532,9 @@ type Options struct {
|
||||
// IPTables are the initial iptables rules. If nil, iptables will allow
|
||||
// all traffic.
|
||||
IPTables *IPTables
|
||||
|
||||
// SecureRNG is a cryptographically secure random number generator.
|
||||
SecureRNG io.Reader
|
||||
}
|
||||
|
||||
// TransportEndpointInfo holds useful information about a transport endpoint
|
||||
@@ -636,6 +643,10 @@ func New(opts Options) *Stack {
|
||||
|
||||
opts.NUDConfigs.resetInvalidFields()
|
||||
|
||||
if opts.SecureRNG == nil {
|
||||
opts.SecureRNG = rand.Reader
|
||||
}
|
||||
|
||||
s := &Stack{
|
||||
transportProtocols: make(map[tcpip.TransportProtocolNumber]*transportProtocolState),
|
||||
networkProtocols: make(map[tcpip.NetworkProtocolNumber]NetworkProtocol),
|
||||
@@ -652,6 +663,7 @@ func New(opts Options) *Stack {
|
||||
uniqueIDGenerator: opts.UniqueID,
|
||||
nudDisp: opts.NUDDisp,
|
||||
randomGenerator: mathrand.New(randSrc),
|
||||
secureRNG: opts.SecureRNG,
|
||||
sendBufferSize: tcpip.SendBufferSizeOption{
|
||||
Min: MinBufferSize,
|
||||
Default: DefaultBufferSize,
|
||||
@@ -2048,6 +2060,12 @@ func (s *Stack) Rand() *mathrand.Rand {
|
||||
return s.randomGenerator
|
||||
}
|
||||
|
||||
// SecureRNG returns the stack's cryptographically secure random number
|
||||
// generator.
|
||||
func (s *Stack) SecureRNG() io.Reader {
|
||||
return s.secureRNG
|
||||
}
|
||||
|
||||
func generateRandUint32() uint32 {
|
||||
b := make([]byte, 4)
|
||||
if _, err := rand.Read(b); err != nil {
|
||||
|
||||
Reference in New Issue
Block a user