summaryrefslogtreecommitdiffhomepage
path: root/pkg/tcpip/header
diff options
context:
space:
mode:
Diffstat (limited to 'pkg/tcpip/header')
-rw-r--r--pkg/tcpip/header/ipv4.go45
-rw-r--r--pkg/tcpip/header/ipversion_test.go2
2 files changed, 37 insertions, 10 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/header/ipversion_test.go b/pkg/tcpip/header/ipversion_test.go
index 17a49d4fa..b5540bf66 100644
--- a/pkg/tcpip/header/ipversion_test.go
+++ b/pkg/tcpip/header/ipversion_test.go
@@ -22,7 +22,7 @@ import (
func TestIPv4(t *testing.T) {
b := header.IPv4(make([]byte, header.IPv4MinimumSize))
- b.Encode(&header.IPv4Fields{IHL: header.IPv4MinimumSize})
+ b.Encode(&header.IPv4Fields{})
const want = header.IPv4Version
if v := header.IPVersion(b); v != want {