diff options
Diffstat (limited to 'pkg/tcpip/header/ndp_options.go')
-rw-r--r-- | pkg/tcpip/header/ndp_options.go | 172 |
1 files changed, 172 insertions, 0 deletions
diff --git a/pkg/tcpip/header/ndp_options.go b/pkg/tcpip/header/ndp_options.go new file mode 100644 index 000000000..b28bde15b --- /dev/null +++ b/pkg/tcpip/header/ndp_options.go @@ -0,0 +1,172 @@ +// 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) +} |