summaryrefslogtreecommitdiffhomepage
path: root/pkg
diff options
context:
space:
mode:
Diffstat (limited to 'pkg')
-rw-r--r--pkg/tcpip/header/ipv4.go45
-rw-r--r--pkg/tcpip/network/ipv4/ipv4.go19
-rw-r--r--pkg/tcpip/stack/registration.go19
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