diff options
Diffstat (limited to 'pkg')
-rw-r--r-- | pkg/tcpip/header/ipv4.go | 45 | ||||
-rw-r--r-- | pkg/tcpip/network/ipv4/ipv4.go | 19 | ||||
-rw-r--r-- | pkg/tcpip/stack/registration.go | 19 |
3 files changed, 69 insertions, 14 deletions
diff --git a/pkg/tcpip/header/ipv4.go b/pkg/tcpip/header/ipv4.go index 961b77628..7e32b31b4 100644 --- a/pkg/tcpip/header/ipv4.go +++ b/pkg/tcpip/header/ipv4.go @@ -56,12 +56,9 @@ const ( ) // IPv4Fields contains the fields of an IPv4 packet. It is used to describe the -// fields of a packet that needs to be encoded. +// fields of a packet that needs to be encoded. The IHL field is not here as +// it is totally defined by the size of the options. type IPv4Fields struct { - // IHL is the "internet header length" field of an IPv4 packet. The value - // is in bytes. - IHL uint8 - // TOS is the "type of service" field of an IPv4 packet. TOS uint8 @@ -91,6 +88,9 @@ type IPv4Fields struct { // DstAddr is the "destination ip address" of an IPv4 packet. DstAddr tcpip.Address + + // Options is between 0 and 40 bytes or nil if empty. + Options IPv4Options } // IPv4 is an IPv4 header. @@ -118,7 +118,7 @@ const ( // Linux limits this to 65,515 octets (the max IP datagram size - the IPv4 // header size). But RFC 791 section 3.2 discusses the design of the IPv4 // fragment "allows 2**13 = 8192 fragments of 8 octets each for a total of - // 65,536 octets. Note that this is consistent with the the datagram total + // 65,536 octets. Note that this is consistent with the datagram total // length field (of course, the header is counted in the total length and not // in the fragments)." IPv4MaximumPayloadSize = 65536 @@ -275,10 +275,22 @@ func (b IPv4) DestinationAddress() tcpip.Address { // IPv4Options is a buffer that holds all the raw IP options. type IPv4Options []byte -// Options returns a buffer holding the options. +// AllocationSize implements stack.NetOptions. +// It reports the size to allocate for the Options. RFC 791 page 23 (end of +// section 3.1) says of the padding at the end of the options: +// The internet header padding is used to ensure that the internet +// header ends on a 32 bit boundary. +func (o IPv4Options) AllocationSize() int { + return (len(o) + IPv4IHLStride - 1) & ^(IPv4IHLStride - 1) +} + +// Options returns a buffer holding the options or nil. func (b IPv4) Options() IPv4Options { hdrLen := b.HeaderLength() - return IPv4Options(b[options:hdrLen:hdrLen]) + if hdrLen > IPv4MinimumSize { + return IPv4Options(b[options:hdrLen:hdrLen]) + } + return nil } // TransportProtocol implements Network.TransportProtocol. @@ -351,7 +363,22 @@ func (b IPv4) CalculateChecksum() uint16 { // Encode encodes all the fields of the IPv4 header. func (b IPv4) Encode(i *IPv4Fields) { - b.SetHeaderLength(i.IHL) + // The size of the options defines the size of the whole header and thus the + // IHL field. Options are rare and this is a heavily used function so it is + // worth a bit of optimisation here to keep the copy out of the fast path. + hdrLen := IPv4MinimumSize + if len(i.Options) != 0 { + // AllocationSize is always >= len(i.Options). + aLen := i.Options.AllocationSize() + hdrLen += aLen + if hdrLen > len(b) { + panic(fmt.Sprintf("encode received %d bytes, wanted >= %d", len(b), hdrLen)) + } + if aLen != copy(b[options:], i.Options) { + _ = copy(b[options+len(i.Options):options+aLen], []byte{0, 0, 0, 0}) + } + } + b.SetHeaderLength(uint8(hdrLen)) b[tos] = i.TOS b.SetTotalLength(i.TotalLength) binary.BigEndian.PutUint16(b[id:], i.ID) diff --git a/pkg/tcpip/network/ipv4/ipv4.go b/pkg/tcpip/network/ipv4/ipv4.go index cfd0c505a..a376cb8ec 100644 --- a/pkg/tcpip/network/ipv4/ipv4.go +++ b/pkg/tcpip/network/ipv4/ipv4.go @@ -199,14 +199,28 @@ func (e *endpoint) NetworkProtocolNumber() tcpip.NetworkProtocolNumber { } func (e *endpoint) addIPHeader(r *stack.Route, pkt *stack.PacketBuffer, params stack.NetworkHeaderParams) { - ip := header.IPv4(pkt.NetworkHeader().Push(header.IPv4MinimumSize)) + hdrLen := header.IPv4MinimumSize + var opts header.IPv4Options + if params.Options != nil { + var ok bool + if opts, ok = params.Options.(header.IPv4Options); !ok { + panic(fmt.Sprintf("want IPv4Options, got %T", params.Options)) + } + hdrLen += opts.AllocationSize() + if hdrLen > header.IPv4MaximumHeaderSize { + // Since we have no way to report an error we must either panic or create + // a packet which is different to what was requested. Choose panic as this + // would be a programming error that should be caught in testing. + panic(fmt.Sprintf("IPv4 Options %d bytes, Max %d", params.Options.AllocationSize(), header.IPv4MaximumOptionsSize)) + } + } + ip := header.IPv4(pkt.NetworkHeader().Push(hdrLen)) length := uint16(pkt.Size()) // 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 := atomic.AddUint32(&e.protocol.ids[hashRoute(r, params.Protocol, e.protocol.hashIV)%buckets], 1) ip.Encode(&header.IPv4Fields{ - IHL: header.IPv4MinimumSize, TotalLength: length, ID: uint16(id), TTL: params.TTL, @@ -214,6 +228,7 @@ func (e *endpoint) addIPHeader(r *stack.Route, pkt *stack.PacketBuffer, params s Protocol: uint8(params.Protocol), SrcAddr: r.LocalAddress, DstAddr: r.RemoteAddress, + Options: opts, }) ip.SetChecksum(^ip.CalculateChecksum()) pkt.NetworkProtocolNumber = ProtocolNumber diff --git a/pkg/tcpip/stack/registration.go b/pkg/tcpip/stack/registration.go index b8f333057..00e9a82ae 100644 --- a/pkg/tcpip/stack/registration.go +++ b/pkg/tcpip/stack/registration.go @@ -138,7 +138,7 @@ type PacketEndpoint interface { HandlePacket(nicID tcpip.NICID, addr tcpip.LinkAddress, netProto tcpip.NetworkProtocolNumber, pkt *PacketBuffer) } -// UnknownDestinationPacketDisposition enumerates the possible return vaues from +// UnknownDestinationPacketDisposition enumerates the possible return values from // HandleUnknownDestinationPacket(). type UnknownDestinationPacketDisposition int @@ -263,6 +263,15 @@ const ( PacketLoop ) +// NetOptions is an interface that allows us to pass network protocol specific +// options through the Stack layer code. +type NetOptions interface { + // AllocationSize returns the amount of memory that must be allocated to + // hold the options given that the value must be rounded up to the next + // multiple of 4 bytes. + AllocationSize() int +} + // NetworkHeaderParams are the header parameters given as input by the // transport endpoint to the network. type NetworkHeaderParams struct { @@ -274,6 +283,10 @@ type NetworkHeaderParams struct { // TOS refers to TypeOfService or TrafficClass field of the IP-header. TOS uint8 + + // Options is a set of options to add to a network header (or nil). + // It will be protocol specific opaque information from higher layers. + Options NetOptions } // GroupAddressableEndpoint is an endpoint that supports group addressing. @@ -281,7 +294,7 @@ type NetworkHeaderParams struct { // An endpoint is considered to support group addressing when one or more // endpoints may associate themselves with the same identifier (group address). type GroupAddressableEndpoint interface { - // JoinGroup joins the spcified group. + // JoinGroup joins the specified group. // // Returns true if the group was newly joined. JoinGroup(group tcpip.Address) (bool, *tcpip.Error) @@ -378,7 +391,7 @@ type AddressEndpoint interface { SetDeprecated(bool) } -// AddressKind is the kind of of an address. +// AddressKind is the kind of an address. // // See the values of AddressKind for more details. type AddressKind int |