173 lines
5.1 KiB
Go
173 lines
5.1 KiB
Go
|
// Copyright 2019 The gVisor Authors.
|
||
|
//
|
||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||
|
// you may not use this file except in compliance with the License.
|
||
|
// You may obtain a copy of the License at
|
||
|
//
|
||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||
|
//
|
||
|
// Unless required by applicable law or agreed to in writing, software
|
||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||
|
// See the License for the specific language governing permissions and
|
||
|
// limitations under the License.
|
||
|
|
||
|
package header
|
||
|
|
||
|
import (
|
||
|
"gvisor.dev/gvisor/pkg/tcpip"
|
||
|
)
|
||
|
|
||
|
const (
|
||
|
// NDPTargetLinkLayerAddressOptionType is the type of the Target
|
||
|
// Link-Layer Address option, as per RFC 4861 section 4.6.1.
|
||
|
NDPTargetLinkLayerAddressOptionType = 2
|
||
|
|
||
|
// ndpTargetEthernetLinkLayerAddressSize is the size of a Target
|
||
|
// Link Layer Option for an Ethernet address.
|
||
|
ndpTargetEthernetLinkLayerAddressSize = 8
|
||
|
|
||
|
// lengthByteUnits is the multiplier factor for the Length field of an
|
||
|
// NDP option. That is, the length field for NDP options is in units of
|
||
|
// 8 octets, as per RFC 4861 section 4.6.
|
||
|
lengthByteUnits = 8
|
||
|
)
|
||
|
|
||
|
// NDPOptions is a buffer of NDP options as defined by RFC 4861 section 4.6.
|
||
|
type NDPOptions []byte
|
||
|
|
||
|
// Serialize serializes the provided list of NDP options into o.
|
||
|
//
|
||
|
// Note, b must be of sufficient size to hold all the options in s. See
|
||
|
// NDPOptionsSerializer.Length for details on the getting the total size
|
||
|
// of a serialized NDPOptionsSerializer.
|
||
|
//
|
||
|
// Serialize may panic if b is not of sufficient size to hold all the options
|
||
|
// in s.
|
||
|
func (b NDPOptions) Serialize(s NDPOptionsSerializer) int {
|
||
|
done := 0
|
||
|
|
||
|
for _, o := range s {
|
||
|
l := paddedLength(o)
|
||
|
|
||
|
if l == 0 {
|
||
|
continue
|
||
|
}
|
||
|
|
||
|
b[0] = o.Type()
|
||
|
|
||
|
// We know this safe because paddedLength would have returned
|
||
|
// 0 if o had an invalid length (> 255 * lengthByteUnits).
|
||
|
b[1] = uint8(l / lengthByteUnits)
|
||
|
|
||
|
// Serialize NDP option body.
|
||
|
used := o.serializeInto(b[2:])
|
||
|
|
||
|
// Zero out remaining (padding) bytes, if any exists.
|
||
|
for i := used + 2; i < l; i++ {
|
||
|
b[i] = 0
|
||
|
}
|
||
|
|
||
|
b = b[l:]
|
||
|
done += l
|
||
|
}
|
||
|
|
||
|
return done
|
||
|
}
|
||
|
|
||
|
// ndpOption is the set of functions to be implemented by all NDP option types.
|
||
|
type ndpOption interface {
|
||
|
// Type returns the type of this ndpOption.
|
||
|
Type() uint8
|
||
|
|
||
|
// Length returns the length of the body of this ndpOption, in bytes.
|
||
|
Length() int
|
||
|
|
||
|
// serializeInto serializes this ndpOption into the provided byte
|
||
|
// buffer.
|
||
|
//
|
||
|
// Note, the caller MUST provide a byte buffer with size of at least
|
||
|
// Length. Implementers of this function may assume that the byte buffer
|
||
|
// is of sufficient size. serializeInto MAY panic if the provided byte
|
||
|
// buffer is not of sufficient size.
|
||
|
//
|
||
|
// serializeInto will return the number of bytes that was used to
|
||
|
// serialize this ndpOption. Implementers must only use the number of
|
||
|
// bytes required to serialize this ndpOption. Callers MAY provide a
|
||
|
// larger buffer than required to serialize into.
|
||
|
serializeInto([]byte) int
|
||
|
}
|
||
|
|
||
|
// paddedLength returns the length of o, in bytes, with any padding bytes, if
|
||
|
// required.
|
||
|
func paddedLength(o ndpOption) int {
|
||
|
l := o.Length()
|
||
|
|
||
|
if l == 0 {
|
||
|
return 0
|
||
|
}
|
||
|
|
||
|
// Length excludes the 2 Type and Length bytes.
|
||
|
l += 2
|
||
|
|
||
|
// Add extra bytes if needed to make sure the option is
|
||
|
// lengthByteUnits-byte aligned. We do this by adding lengthByteUnits-1
|
||
|
// to l and then stripping off the last few LSBits from l. This will
|
||
|
// make sure that l is rounded up to the nearest unit of
|
||
|
// lengthByteUnits. This works since lengthByteUnits is a power of 2
|
||
|
// (= 8).
|
||
|
mask := lengthByteUnits - 1
|
||
|
l += mask
|
||
|
l &^= mask
|
||
|
|
||
|
if l/lengthByteUnits > 255 {
|
||
|
// Should never happen because an option can only have a max
|
||
|
// value of 255 for its Length field, so just return 0 so this
|
||
|
// option does not get serialized.
|
||
|
//
|
||
|
// Returning 0 here will make sure that this option does not get
|
||
|
// serialized when NDPOptions.Serialize is called with the
|
||
|
// NDPOptionsSerializer that holds this option, effectively
|
||
|
// skipping this option during serialization. Also note that
|
||
|
// a value of zero for the Length field in an NDP option is
|
||
|
// invalid so this is another sign to the caller that this NDP
|
||
|
// option is malformed, as per RFC 4861 section 4.6.
|
||
|
return 0
|
||
|
}
|
||
|
|
||
|
return l
|
||
|
}
|
||
|
|
||
|
// NDPOptionsSerializer is a serializer for NDP options.
|
||
|
type NDPOptionsSerializer []ndpOption
|
||
|
|
||
|
// Length returns the total number of bytes required to serialize.
|
||
|
func (b NDPOptionsSerializer) Length() int {
|
||
|
l := 0
|
||
|
|
||
|
for _, o := range b {
|
||
|
l += paddedLength(o)
|
||
|
}
|
||
|
|
||
|
return l
|
||
|
}
|
||
|
|
||
|
// NDPTargetLinkLayerAddressOption is the NDP Target Link Layer Option
|
||
|
// as defined by RFC 4861 section 4.6.1.
|
||
|
type NDPTargetLinkLayerAddressOption tcpip.LinkAddress
|
||
|
|
||
|
// Type implements ndpOption.Type.
|
||
|
func (o NDPTargetLinkLayerAddressOption) Type() uint8 {
|
||
|
return NDPTargetLinkLayerAddressOptionType
|
||
|
}
|
||
|
|
||
|
// Length implements ndpOption.Length.
|
||
|
func (o NDPTargetLinkLayerAddressOption) Length() int {
|
||
|
return len(o)
|
||
|
}
|
||
|
|
||
|
// serializeInto implements ndpOption.serializeInto.
|
||
|
func (o NDPTargetLinkLayerAddressOption) serializeInto(b []byte) int {
|
||
|
return copy(b, o)
|
||
|
}
|