summaryrefslogtreecommitdiffhomepage
path: root/pkg/tcpip/network/ipv4/ipv4.go
diff options
context:
space:
mode:
authorJulian Elischer <jrelis@google.com>2021-02-09 01:00:40 -0800
committergVisor bot <gvisor-bot@google.com>2021-02-09 01:02:43 -0800
commit3f802e7180cfe703ecb97d8f2b6ba32257cf6216 (patch)
treeda741ecec72e36a68b4e4868dbcb5b68a1ac3d5f /pkg/tcpip/network/ipv4/ipv4.go
parent6671a42d605d681e6aa63b610617523ab632e95a (diff)
add IPv4 options processing for forwarding and reassembly
IPv4 forwarding and reassembly needs support for option processing and regular processing also needs options to be processed before being passed to the transport layer. This patch extends option processing to those cases and provides additional testing. A small change to the ICMP error generation API code was required to allow it to know when a packet was being forwarded or not. Updates #4586 PiperOrigin-RevId: 356446681
Diffstat (limited to 'pkg/tcpip/network/ipv4/ipv4.go')
-rw-r--r--pkg/tcpip/network/ipv4/ipv4.go114
1 files changed, 95 insertions, 19 deletions
diff --git a/pkg/tcpip/network/ipv4/ipv4.go b/pkg/tcpip/network/ipv4/ipv4.go
index 7de438fe3..c5e4034ce 100644
--- a/pkg/tcpip/network/ipv4/ipv4.go
+++ b/pkg/tcpip/network/ipv4/ipv4.go
@@ -561,6 +561,34 @@ func (e *endpoint) forwardPacket(pkt *stack.PacketBuffer) tcpip.Error {
return e.protocol.returnError(&icmpReasonTTLExceeded{}, pkt)
}
+ if opts := h.Options(); len(opts) != 0 {
+ newOpts, optProblem := e.processIPOptions(pkt, opts, &optionUsageForward{})
+ if optProblem != nil {
+ if optProblem.NeedICMP {
+ _ = e.protocol.returnError(&icmpReasonParamProblem{
+ pointer: optProblem.Pointer,
+ forwarding: true,
+ }, pkt)
+ e.protocol.stack.Stats().MalformedRcvdPackets.Increment()
+ e.stats.ip.MalformedPacketsReceived.Increment()
+ }
+ return nil // option problems are not reported locally.
+ }
+ copied := copy(opts, newOpts)
+ if copied != len(newOpts) {
+ panic(fmt.Sprintf("copied %d bytes of new options, expected %d bytes", copied, len(newOpts)))
+ }
+ // Since in forwarding we handle all options, including copying those we
+ // do not recognise, the options region should remain the same size which
+ // simplifies processing. As we MAY receive a packet with a lot of padded
+ // bytes after the "end of options list" byte, make sure we copy
+ // them as the legal padding value (0).
+ for i := copied; i < len(opts); i++ {
+ // Pad with 0 (EOL). RFC 791 page 23 says "The padding is zero".
+ opts[i] = byte(header.IPv4OptionListEndType)
+ }
+ }
+
dstAddr := h.DestinationAddress()
// Check if the destination is owned by the stack.
@@ -711,8 +739,9 @@ func (e *endpoint) handlePacket(pkt *stack.PacketBuffer) {
}
}
- // The destination address should be an address we own or a group we joined
- // for us to receive the packet. Otherwise, attempt to forward the packet.
+ // Before we do any processing, note if the packet was received as some
+ // sort of broadcast. The destination address should be an address we own
+ // or a group we joined.
if addressEndpoint := e.AcquireAssignedAddress(dstAddr, e.nic.Promiscuous(), stack.CanBePrimaryEndpoint); addressEndpoint != nil {
subnet := addressEndpoint.AddressWithPrefix().Subnet()
addressEndpoint.DecRef()
@@ -722,7 +751,6 @@ func (e *endpoint) handlePacket(pkt *stack.PacketBuffer) {
stats.ip.InvalidDestinationAddressesReceived.Increment()
return
}
-
_ = e.forwardPacket(pkt)
return
}
@@ -744,6 +772,21 @@ func (e *endpoint) handlePacket(pkt *stack.PacketBuffer) {
stats.ip.MalformedFragmentsReceived.Increment()
return
}
+ if opts := h.Options(); len(opts) != 0 {
+ // If there are options we need to check them before we do assembly
+ // or we could be assembling errant packets. However we do not change the
+ // options as that could lead to double processing later.
+ if _, optProblem := e.processIPOptions(pkt, opts, &optionUsageVerify{}); optProblem != nil {
+ if optProblem.NeedICMP {
+ _ = e.protocol.returnError(&icmpReasonParamProblem{
+ pointer: optProblem.Pointer,
+ }, pkt)
+ e.protocol.stack.Stats().MalformedRcvdPackets.Increment()
+ e.stats.ip.MalformedPacketsReceived.Increment()
+ }
+ return
+ }
+ }
// The packet is a fragment, let's try to reassemble it.
start := h.FragmentOffset()
// Drop the fragment if the size of the reassembled payload would exceed the
@@ -802,17 +845,10 @@ func (e *endpoint) handlePacket(pkt *stack.PacketBuffer) {
e.handleICMP(pkt)
return
}
- if p == header.IGMPProtocolNumber {
- e.mu.Lock()
- e.mu.igmp.handleIGMP(pkt)
- e.mu.Unlock()
- return
- }
+ // ICMP handles options itself but do it here for all remaining destinations.
if opts := h.Options(); len(opts) != 0 {
- // TODO(gvisor.dev/issue/4586):
- // When we add forwarding support we should use the verified options
- // rather than just throwing them away.
- if _, optProblem := e.processIPOptions(pkt, opts, &optionUsageReceive{}); optProblem != nil {
+ newOpts, optProblem := e.processIPOptions(pkt, opts, &optionUsageReceive{})
+ if optProblem != nil {
if optProblem.NeedICMP {
_ = e.protocol.returnError(&icmpReasonParamProblem{
pointer: optProblem.Pointer,
@@ -822,6 +858,20 @@ func (e *endpoint) handlePacket(pkt *stack.PacketBuffer) {
}
return
}
+ copied := copy(opts, newOpts)
+ if copied != len(newOpts) {
+ panic(fmt.Sprintf("copied %d bytes of new options, expected %d bytes", copied, len(newOpts)))
+ }
+ for i := copied; i < len(opts); i++ {
+ // Pad with 0 (EOL). RFC 791 page 23 says "The padding is zero".
+ opts[i] = byte(header.IPv4OptionListEndType)
+ }
+ }
+ if p == header.IGMPProtocolNumber {
+ e.mu.Lock()
+ e.mu.igmp.handleIGMP(pkt)
+ e.mu.Unlock()
+ return
}
switch res := e.dispatcher.DeliverTransportPacket(p, pkt); res {
@@ -1254,23 +1304,49 @@ type optionsUsage interface {
actions() optionActions
}
-// optionUsageReceive implements optionsUsage for received packets.
-type optionUsageReceive struct{}
+// optionUsageVerify implements optionsUsage for when we just want to check
+// fragments. Don't change anything, just check and reject if bad. No
+// replacement options are generated.
+type optionUsageVerify struct{}
// actions implements optionsUsage.
-func (*optionUsageReceive) actions() optionActions {
+func (*optionUsageVerify) actions() optionActions {
return optionActions{
timestamp: optionVerify,
recordRoute: optionVerify,
+ unknown: optionRemove,
+ }
+}
+
+// optionUsageReceive implements optionsUsage for packets we will pass
+// to the transport layer (with the exception of Echo requests).
+type optionUsageReceive struct{}
+
+// actions implements optionsUsage.
+func (*optionUsageReceive) actions() optionActions {
+ return optionActions{
+ timestamp: optionProcess,
+ recordRoute: optionProcess,
unknown: optionPass,
}
}
-// TODO(gvisor.dev/issue/4586): Add an entry here for forwarding when it
-// is enabled (Process, Process, Pass) and for fragmenting (Process, Process,
-// Pass for frag1, but Remove,Remove,Remove for all other frags).
+// optionUsageForward implements optionsUsage for packets about to be forwarded.
+// All options are passed on regardless of whether we recognise them, however
+// we do process the Timestamp and Record Route options.
+type optionUsageForward struct{}
+
+// actions implements optionsUsage.
+func (*optionUsageForward) actions() optionActions {
+ return optionActions{
+ timestamp: optionProcess,
+ recordRoute: optionProcess,
+ unknown: optionPass,
+ }
+}
// optionUsageEcho implements optionsUsage for echo packet processing.
+// Only Timestamp and RecordRoute are processed and sent back.
type optionUsageEcho struct{}
// actions implements optionsUsage.