mirror of
https://github.com/netbirdio/gvisor.git
synced 2026-05-22 17:12:49 -07:00
Add UDP matchers.
This commit is contained in:
@@ -340,3 +340,95 @@ func goString(cstring []byte) string {
|
||||
}
|
||||
return string(cstring)
|
||||
}
|
||||
|
||||
// XTTCP holds data for matching TCP packets. It corresponds to struct xt_tcp
|
||||
// in include/uapi/linux/netfilter/xt_tcpudp.h.
|
||||
type XTTCP struct {
|
||||
// SourcePortStart specifies the inclusive start of the range of source
|
||||
// ports to which the matcher applies.
|
||||
SourcePortStart uint16
|
||||
|
||||
// SourcePortEnd specifies the inclusive end of the range of source ports
|
||||
// to which the matcher applies.
|
||||
SourcePortEnd uint16
|
||||
|
||||
// DestinationPortStart specifies the start of the destination port
|
||||
// range to which the matcher applies.
|
||||
DestinationPortStart uint16
|
||||
|
||||
// DestinationPortEnd specifies the start of the destination port
|
||||
// range to which the matcher applies.
|
||||
DestinationPortEnd uint16
|
||||
|
||||
// Option specifies that a particular TCP option must be set.
|
||||
Option uint8
|
||||
|
||||
// FlagMask masks the FlagCompare byte when comparing to the TCP flag
|
||||
// fields.
|
||||
FlagMask uint8
|
||||
|
||||
// FlagCompare is binary and-ed with the TCP flag fields.
|
||||
FlagCompare uint8
|
||||
|
||||
// InverseFlags flips the meaning of certain fields. See the
|
||||
// TX_TCP_INV_* flags.
|
||||
InverseFlags uint8
|
||||
}
|
||||
|
||||
// SizeOfXTTCP is the size of an XTTCP.
|
||||
const SizeOfXTTCP = 12
|
||||
|
||||
// Flags in XTTCP.InverseFlags. Corresponding constants are in
|
||||
// include/uapi/linux/netfilter/xt_tcpudp.h.
|
||||
const (
|
||||
// Invert the meaning of SourcePortStart/End.
|
||||
XT_TCP_INV_SRCPT = 0x01
|
||||
// Invert the meaning of DestinationPortStart/End.
|
||||
XT_TCP_INV_DSTPT = 0x02
|
||||
// Invert the meaning of FlagCompare.
|
||||
XT_TCP_INV_FLAGS = 0x04
|
||||
// Invert the meaning of Option.
|
||||
XT_TCP_INV_OPTION = 0x08
|
||||
// Enable all flags.
|
||||
XT_TCP_INV_MASK = 0x0F
|
||||
)
|
||||
|
||||
// XTUDP holds data for matching UDP packets. It corresponds to struct xt_udp
|
||||
// in include/uapi/linux/netfilter/xt_tcpudp.h.
|
||||
type XTUDP struct {
|
||||
// SourcePortStart specifies the inclusive start of the range of source
|
||||
// ports to which the matcher applies.
|
||||
SourcePortStart uint16
|
||||
|
||||
// SourcePortEnd specifies the inclusive end of the range of source ports
|
||||
// to which the matcher applies.
|
||||
SourcePortEnd uint16
|
||||
|
||||
// DestinationPortStart specifies the start of the destination port
|
||||
// range to which the matcher applies.
|
||||
DestinationPortStart uint16
|
||||
|
||||
// DestinationPortEnd specifies the start of the destination port
|
||||
// range to which the matcher applies.
|
||||
DestinationPortEnd uint16
|
||||
|
||||
// InverseFlags flips the meaning of certain fields. See the
|
||||
// TX_UDP_INV_* flags.
|
||||
InverseFlags uint8
|
||||
|
||||
_ uint8
|
||||
}
|
||||
|
||||
// SizeOfXTUDP is the size of an XTUDP.
|
||||
const SizeOfXTUDP = 10
|
||||
|
||||
// Flags in XTUDP.InverseFlags. Corresponding constants are in
|
||||
// include/uapi/linux/netfilter/xt_tcpudp.h.
|
||||
const (
|
||||
// Invert the meaning of SourcePortStart/End.
|
||||
XT_UDP_INV_SRCPT = 0x01
|
||||
// Invert the meaning of DestinationPortStart/End.
|
||||
XT_UDP_INV_DSTPT = 0x02
|
||||
// Enable all flags.
|
||||
XT_UDP_INV_MASK = 0x03
|
||||
)
|
||||
|
||||
@@ -131,6 +131,7 @@ func FillDefaultIPTables(stack *stack.Stack) {
|
||||
stack.SetIPTables(ipt)
|
||||
}
|
||||
|
||||
// TODO: Return proto.
|
||||
// convertNetstackToBinary converts the iptables as stored in netstack to the
|
||||
// format expected by the iptables tool. Linux stores each table as a binary
|
||||
// blob that can only be traversed by parsing a bit, reading some offsets,
|
||||
@@ -318,10 +319,12 @@ func SetEntries(stack *stack.Stack, optVal []byte) *syserr.Error {
|
||||
}
|
||||
var entry linux.IPTEntry
|
||||
buf := optVal[:linux.SizeOfIPTEntry]
|
||||
optVal = optVal[linux.SizeOfIPTEntry:]
|
||||
binary.Unmarshal(buf, usermem.ByteOrder, &entry)
|
||||
if entry.TargetOffset != linux.SizeOfIPTEntry {
|
||||
// TODO(gvisor.dev/issue/170): Support matchers.
|
||||
initialOptValLen := len(optVal)
|
||||
optVal = optVal[linux.SizeOfIPTEntry:]
|
||||
|
||||
if entry.TargetOffset < linux.SizeOfIPTEntry {
|
||||
log.Warningf("netfilter: entry has too-small target offset %d", entry.TargetOffset)
|
||||
return syserr.ErrInvalidArgument
|
||||
}
|
||||
|
||||
@@ -332,19 +335,41 @@ func SetEntries(stack *stack.Stack, optVal []byte) *syserr.Error {
|
||||
return err
|
||||
}
|
||||
|
||||
// TODO: Matchers (and maybe targets) can specify that they only work for certiain protocols, hooks, tables.
|
||||
// Get matchers.
|
||||
matchersSize := entry.TargetOffset - linux.SizeOfIPTEntry
|
||||
if len(optVal) < int(matchersSize) {
|
||||
log.Warningf("netfilter: entry doesn't have enough room for its matchers (only %d bytes remain)", len(optVal))
|
||||
}
|
||||
matchers, err := parseMatchers(filter, optVal[:matchersSize])
|
||||
if err != nil {
|
||||
log.Warningf("netfilter: failed to parse matchers: %v", err)
|
||||
return err
|
||||
}
|
||||
optVal = optVal[matchersSize:]
|
||||
|
||||
// Get the target of the rule.
|
||||
target, consumed, err := parseTarget(optVal)
|
||||
targetSize := entry.NextOffset - entry.TargetOffset
|
||||
if len(optVal) < int(targetSize) {
|
||||
log.Warningf("netfilter: entry doesn't have enough room for its target (only %d bytes remain)", len(optVal))
|
||||
}
|
||||
target, err := parseTarget(optVal[:targetSize])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
optVal = optVal[consumed:]
|
||||
optVal = optVal[targetSize:]
|
||||
|
||||
table.Rules = append(table.Rules, iptables.Rule{
|
||||
Filter: filter,
|
||||
Target: target,
|
||||
Filter: filter,
|
||||
Target: target,
|
||||
Matchers: matchers,
|
||||
})
|
||||
offsets = append(offsets, offset)
|
||||
offset += linux.SizeOfIPTEntry + consumed
|
||||
offset += uint32(entry.NextOffset)
|
||||
|
||||
if initialOptValLen-len(optVal) != int(entry.NextOffset) {
|
||||
log.Warningf("netfilter: entry NextOffset is %d, but entry took up %d bytes", entry.NextOffset, initialOptValLen-len(optVal))
|
||||
}
|
||||
}
|
||||
|
||||
// Go through the list of supported hooks for this table and, for each
|
||||
@@ -401,12 +426,105 @@ func SetEntries(stack *stack.Stack, optVal []byte) *syserr.Error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// parseTarget parses a target from the start of optVal and returns the target
|
||||
// along with the number of bytes it occupies in optVal.
|
||||
func parseTarget(optVal []byte) (iptables.Target, uint32, *syserr.Error) {
|
||||
// parseMatchers parses 0 or more matchers from optVal. optVal should contain
|
||||
// only the matchers.
|
||||
func parseMatchers(filter iptables.IPHeaderFilter, optVal []byte) ([]iptables.Matcher, *syserr.Error) {
|
||||
var matchers []iptables.Matcher
|
||||
for len(optVal) > 0 {
|
||||
log.Infof("parseMatchers: optVal has len %d", len(optVal))
|
||||
// Get the XTEntryMatch.
|
||||
if len(optVal) < linux.SizeOfXTEntryMatch {
|
||||
log.Warningf("netfilter: optVal has insufficient size for entry match: %d", len(optVal))
|
||||
return nil, syserr.ErrInvalidArgument
|
||||
}
|
||||
var match linux.XTEntryMatch
|
||||
buf := optVal[:linux.SizeOfXTEntryMatch]
|
||||
binary.Unmarshal(buf, usermem.ByteOrder, &match)
|
||||
log.Infof("parseMatchers: parsed entry match %q: %+v", match.Name.String(), match)
|
||||
|
||||
// Check some invariants.
|
||||
if match.MatchSize < linux.SizeOfXTEntryMatch {
|
||||
log.Warningf("netfilter: match size is too small, must be at least %d", linux.SizeOfXTEntryMatch)
|
||||
return nil, syserr.ErrInvalidArgument
|
||||
}
|
||||
if len(optVal) < int(match.MatchSize) {
|
||||
log.Warningf("netfilter: optVal has insufficient size for match: %d", len(optVal))
|
||||
return nil, syserr.ErrInvalidArgument
|
||||
}
|
||||
|
||||
buf = optVal[linux.SizeOfXTEntryMatch:match.MatchSize]
|
||||
var matcher iptables.Matcher
|
||||
var err error
|
||||
switch match.Name.String() {
|
||||
case "tcp":
|
||||
if len(buf) < linux.SizeOfXTTCP {
|
||||
log.Warningf("netfilter: optVal has insufficient size for TCP match: %d", len(optVal))
|
||||
return nil, syserr.ErrInvalidArgument
|
||||
}
|
||||
var matchData linux.XTTCP
|
||||
// For alignment reasons, the match's total size may exceed what's
|
||||
// strictly necessary to hold matchData.
|
||||
binary.Unmarshal(buf[:linux.SizeOfXTUDP], usermem.ByteOrder, &matchData)
|
||||
log.Infof("parseMatchers: parsed XTTCP: %+v", matchData)
|
||||
matcher, err = iptables.NewTCPMatcher(filter, iptables.TCPMatcherData{
|
||||
SourcePortStart: matchData.SourcePortStart,
|
||||
SourcePortEnd: matchData.SourcePortEnd,
|
||||
DestinationPortStart: matchData.DestinationPortStart,
|
||||
DestinationPortEnd: matchData.DestinationPortEnd,
|
||||
Option: matchData.Option,
|
||||
FlagMask: matchData.FlagMask,
|
||||
FlagCompare: matchData.FlagCompare,
|
||||
InverseFlags: matchData.InverseFlags,
|
||||
})
|
||||
if err != nil {
|
||||
log.Warningf("netfilter: failed to create TCP matcher: %v", err)
|
||||
return nil, syserr.ErrInvalidArgument
|
||||
}
|
||||
|
||||
case "udp":
|
||||
if len(buf) < linux.SizeOfXTUDP {
|
||||
log.Warningf("netfilter: optVal has insufficient size for UDP match: %d", len(optVal))
|
||||
return nil, syserr.ErrInvalidArgument
|
||||
}
|
||||
var matchData linux.XTUDP
|
||||
// For alignment reasons, the match's total size may exceed what's
|
||||
// strictly necessary to hold matchData.
|
||||
binary.Unmarshal(buf[:linux.SizeOfXTUDP], usermem.ByteOrder, &matchData)
|
||||
log.Infof("parseMatchers: parsed XTUDP: %+v", matchData)
|
||||
matcher, err = iptables.NewUDPMatcher(filter, iptables.UDPMatcherData{
|
||||
SourcePortStart: matchData.SourcePortStart,
|
||||
SourcePortEnd: matchData.SourcePortEnd,
|
||||
DestinationPortStart: matchData.DestinationPortStart,
|
||||
DestinationPortEnd: matchData.DestinationPortEnd,
|
||||
InverseFlags: matchData.InverseFlags,
|
||||
})
|
||||
if err != nil {
|
||||
log.Warningf("netfilter: failed to create UDP matcher: %v", err)
|
||||
return nil, syserr.ErrInvalidArgument
|
||||
}
|
||||
|
||||
default:
|
||||
log.Warningf("netfilter: unsupported matcher with name %q", match.Name.String())
|
||||
return nil, syserr.ErrInvalidArgument
|
||||
}
|
||||
|
||||
matchers = append(matchers, matcher)
|
||||
|
||||
// TODO: Support revision.
|
||||
// TODO: Support proto -- matchers usually specify which proto(s) they work with.
|
||||
optVal = optVal[match.MatchSize:]
|
||||
}
|
||||
|
||||
// TODO: Check that optVal is exhausted.
|
||||
return matchers, nil
|
||||
}
|
||||
|
||||
// parseTarget parses a target from optVal. optVal should contain only the
|
||||
// target.
|
||||
func parseTarget(optVal []byte) (iptables.Target, *syserr.Error) {
|
||||
if len(optVal) < linux.SizeOfXTEntryTarget {
|
||||
log.Warningf("netfilter: optVal has insufficient size for entry target %d", len(optVal))
|
||||
return nil, 0, syserr.ErrInvalidArgument
|
||||
return nil, syserr.ErrInvalidArgument
|
||||
}
|
||||
var target linux.XTEntryTarget
|
||||
buf := optVal[:linux.SizeOfXTEntryTarget]
|
||||
@@ -414,9 +532,9 @@ func parseTarget(optVal []byte) (iptables.Target, uint32, *syserr.Error) {
|
||||
switch target.Name.String() {
|
||||
case "":
|
||||
// Standard target.
|
||||
if len(optVal) < linux.SizeOfXTStandardTarget {
|
||||
log.Warningf("netfilter.SetEntries: optVal has insufficient size for standard target %d", len(optVal))
|
||||
return nil, 0, syserr.ErrInvalidArgument
|
||||
if len(optVal) != linux.SizeOfXTStandardTarget {
|
||||
log.Warningf("netfilter.SetEntries: optVal has wrong size for standard target %d", len(optVal))
|
||||
return nil, syserr.ErrInvalidArgument
|
||||
}
|
||||
var standardTarget linux.XTStandardTarget
|
||||
buf = optVal[:linux.SizeOfXTStandardTarget]
|
||||
@@ -424,22 +542,22 @@ func parseTarget(optVal []byte) (iptables.Target, uint32, *syserr.Error) {
|
||||
|
||||
verdict, err := translateToStandardVerdict(standardTarget.Verdict)
|
||||
if err != nil {
|
||||
return nil, 0, err
|
||||
return nil, err
|
||||
}
|
||||
switch verdict {
|
||||
case iptables.Accept:
|
||||
return iptables.UnconditionalAcceptTarget{}, linux.SizeOfXTStandardTarget, nil
|
||||
return iptables.UnconditionalAcceptTarget{}, nil
|
||||
case iptables.Drop:
|
||||
return iptables.UnconditionalDropTarget{}, linux.SizeOfXTStandardTarget, nil
|
||||
return iptables.UnconditionalDropTarget{}, nil
|
||||
default:
|
||||
panic(fmt.Sprintf("Unknown verdict: %v", verdict))
|
||||
}
|
||||
|
||||
case errorTargetName:
|
||||
// Error target.
|
||||
if len(optVal) < linux.SizeOfXTErrorTarget {
|
||||
if len(optVal) != linux.SizeOfXTErrorTarget {
|
||||
log.Infof("netfilter.SetEntries: optVal has insufficient size for error target %d", len(optVal))
|
||||
return nil, 0, syserr.ErrInvalidArgument
|
||||
return nil, syserr.ErrInvalidArgument
|
||||
}
|
||||
var errorTarget linux.XTErrorTarget
|
||||
buf = optVal[:linux.SizeOfXTErrorTarget]
|
||||
@@ -454,16 +572,16 @@ func parseTarget(optVal []byte) (iptables.Target, uint32, *syserr.Error) {
|
||||
// rules have an error with the name of the chain.
|
||||
switch errorTarget.Name.String() {
|
||||
case errorTargetName:
|
||||
return iptables.ErrorTarget{}, linux.SizeOfXTErrorTarget, nil
|
||||
return iptables.ErrorTarget{}, nil
|
||||
default:
|
||||
log.Infof("Unknown error target %q doesn't exist or isn't supported yet.", errorTarget.Name.String())
|
||||
return nil, 0, syserr.ErrInvalidArgument
|
||||
return nil, syserr.ErrInvalidArgument
|
||||
}
|
||||
}
|
||||
|
||||
// Unknown target.
|
||||
log.Infof("Unknown target %q doesn't exist or isn't supported yet.", target.Name.String())
|
||||
return nil, 0, syserr.ErrInvalidArgument
|
||||
return nil, syserr.ErrInvalidArgument
|
||||
}
|
||||
|
||||
func filterFromIPTIP(iptip linux.IPTIP) (iptables.IPHeaderFilter, *syserr.Error) {
|
||||
|
||||
@@ -7,7 +7,9 @@ go_library(
|
||||
srcs = [
|
||||
"iptables.go",
|
||||
"targets.go",
|
||||
"tcp_matcher.go",
|
||||
"types.go",
|
||||
"udp_matcher.go",
|
||||
],
|
||||
importpath = "gvisor.dev/gvisor/pkg/tcpip/iptables",
|
||||
visibility = ["//visibility:public"],
|
||||
|
||||
@@ -138,6 +138,8 @@ func EmptyFilterTable() Table {
|
||||
// Check runs pkt through the rules for hook. It returns true when the packet
|
||||
// should continue traversing the network stack and false when it should be
|
||||
// dropped.
|
||||
//
|
||||
// Precondition: pkt.NetworkHeader is set.
|
||||
func (it *IPTables) Check(hook Hook, pkt tcpip.PacketBuffer) bool {
|
||||
// TODO(gvisor.dev/issue/170): A lot of this is uncomplicated because
|
||||
// we're missing features. Jumps, the call stack, etc. aren't checked
|
||||
@@ -163,6 +165,7 @@ func (it *IPTables) Check(hook Hook, pkt tcpip.PacketBuffer) bool {
|
||||
return true
|
||||
}
|
||||
|
||||
// Precondition: pkt.NetworkHeader is set.
|
||||
func (it *IPTables) checkTable(hook Hook, pkt tcpip.PacketBuffer, tablename string) Verdict {
|
||||
// Start from ruleIdx and walk the list of rules until a rule gives us
|
||||
// a verdict.
|
||||
|
||||
@@ -0,0 +1,122 @@
|
||||
// Copyright 2020 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 iptables
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"gvisor.dev/gvisor/pkg/log"
|
||||
"gvisor.dev/gvisor/pkg/tcpip"
|
||||
"gvisor.dev/gvisor/pkg/tcpip/header"
|
||||
)
|
||||
|
||||
type TCPMatcher struct {
|
||||
data TCPMatcherData
|
||||
|
||||
// tablename string
|
||||
// unsigned int matchsize;
|
||||
// unsigned int usersize;
|
||||
// #ifdef CONFIG_COMPAT
|
||||
// unsigned int compatsize;
|
||||
// #endif
|
||||
// unsigned int hooks;
|
||||
// unsigned short proto;
|
||||
// unsigned short family;
|
||||
}
|
||||
|
||||
// TODO: Delete?
|
||||
// MatchCheckEntryParams
|
||||
|
||||
type TCPMatcherData struct {
|
||||
// Filter IPHeaderFilter
|
||||
|
||||
SourcePortStart uint16
|
||||
SourcePortEnd uint16
|
||||
DestinationPortStart uint16
|
||||
DestinationPortEnd uint16
|
||||
Option uint8
|
||||
FlagMask uint8
|
||||
FlagCompare uint8
|
||||
InverseFlags uint8
|
||||
}
|
||||
|
||||
func NewTCPMatcher(filter IPHeaderFilter, data TCPMatcherData) (Matcher, error) {
|
||||
// TODO: We currently only support source port and destination port.
|
||||
log.Infof("Adding rule with TCPMatcherData: %+v", data)
|
||||
|
||||
if data.Option != 0 ||
|
||||
data.FlagMask != 0 ||
|
||||
data.FlagCompare != 0 ||
|
||||
data.InverseFlags != 0 {
|
||||
return nil, fmt.Errorf("unsupported TCP matcher flags set")
|
||||
}
|
||||
|
||||
if filter.Protocol != header.TCPProtocolNumber {
|
||||
log.Warningf("TCP matching is only valid for protocol %d.", header.TCPProtocolNumber)
|
||||
}
|
||||
|
||||
return &TCPMatcher{data: data}, nil
|
||||
}
|
||||
|
||||
// TODO: Check xt_tcpudp.c. Need to check for same things (e.g. fragments).
|
||||
func (tm *TCPMatcher) Match(hook Hook, pkt tcpip.PacketBuffer, interfaceName string) (bool, bool) {
|
||||
netHeader := header.IPv4(pkt.NetworkHeader)
|
||||
|
||||
// TODO: Do we check proto here or elsewhere? I think elsewhere (check
|
||||
// codesearch).
|
||||
if netHeader.TransportProtocol() != header.TCPProtocolNumber {
|
||||
return false, false
|
||||
}
|
||||
|
||||
// We dont't match fragments.
|
||||
if frag := netHeader.FragmentOffset(); frag != 0 {
|
||||
if frag == 1 {
|
||||
log.Warningf("Dropping TCP packet: malicious packet with fragment with fragment offest of 1.")
|
||||
return false, true
|
||||
}
|
||||
return false, false
|
||||
}
|
||||
|
||||
// Now we need the transport header. However, this may not have been set
|
||||
// yet.
|
||||
// TODO
|
||||
var tcpHeader header.TCP
|
||||
if pkt.TransportHeader != nil {
|
||||
tcpHeader = header.TCP(pkt.TransportHeader)
|
||||
} else {
|
||||
// The TCP header hasn't been parsed yet. We have to do it here.
|
||||
if len(pkt.Data.First()) < header.TCPMinimumSize {
|
||||
// There's no valid TCP header here, so we hotdrop the
|
||||
// packet.
|
||||
// TODO: Stats.
|
||||
log.Warningf("Dropping TCP packet: size to small.")
|
||||
return false, true
|
||||
}
|
||||
tcpHeader = header.TCP(pkt.Data.First())
|
||||
}
|
||||
|
||||
// Check whether the source and destination ports are within the
|
||||
// matching range.
|
||||
sourcePort := tcpHeader.SourcePort()
|
||||
destinationPort := tcpHeader.DestinationPort()
|
||||
if sourcePort < tm.data.SourcePortStart || tm.data.SourcePortEnd < sourcePort {
|
||||
return false, false
|
||||
}
|
||||
if destinationPort < tm.data.DestinationPortStart || tm.data.DestinationPortEnd < destinationPort {
|
||||
return false, false
|
||||
}
|
||||
|
||||
return true, false
|
||||
}
|
||||
@@ -169,12 +169,29 @@ type IPHeaderFilter struct {
|
||||
Protocol tcpip.TransportProtocolNumber
|
||||
}
|
||||
|
||||
// TODO: Should these be able to marshal/unmarshal themselves?
|
||||
// TODO: Something has to map the name to the matcher.
|
||||
// A Matcher is the interface for matching packets.
|
||||
type Matcher interface {
|
||||
// Match returns whether the packet matches and whether the packet
|
||||
// should be "hotdropped", i.e. dropped immediately. This is usually
|
||||
// used for suspicious packets.
|
||||
//
|
||||
// Precondition: packet.NetworkHeader is set.
|
||||
Match(hook Hook, packet tcpip.PacketBuffer, interfaceName string) (matches bool, hotdrop bool)
|
||||
|
||||
// TODO: Make this typesafe by having each Matcher have their own, typed CheckEntry?
|
||||
// CheckEntry(params MatchCheckEntryParams) bool
|
||||
}
|
||||
|
||||
// TODO: Unused?
|
||||
type MatchCheckEntryParams struct {
|
||||
Table string // TODO: Tables should be an enum...
|
||||
Filter IPHeaderFilter
|
||||
Info interface{} // TODO: Type unsafe.
|
||||
// HookMask uint8
|
||||
// Family uint8
|
||||
// NFTCompat bool
|
||||
}
|
||||
|
||||
// A Target is the interface for taking an action for a packet.
|
||||
|
||||
@@ -0,0 +1,127 @@
|
||||
// Copyright 2020 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 iptables
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"runtime/debug"
|
||||
|
||||
"gvisor.dev/gvisor/pkg/log"
|
||||
"gvisor.dev/gvisor/pkg/tcpip"
|
||||
"gvisor.dev/gvisor/pkg/tcpip/header"
|
||||
)
|
||||
|
||||
type UDPMatcher struct {
|
||||
data UDPMatcherData
|
||||
|
||||
// tablename string
|
||||
// unsigned int matchsize;
|
||||
// unsigned int usersize;
|
||||
// #ifdef CONFIG_COMPAT
|
||||
// unsigned int compatsize;
|
||||
// #endif
|
||||
// unsigned int hooks;
|
||||
// unsigned short proto;
|
||||
// unsigned short family;
|
||||
}
|
||||
|
||||
// TODO: Delete?
|
||||
// MatchCheckEntryParams
|
||||
|
||||
type UDPMatcherData struct {
|
||||
// Filter IPHeaderFilter
|
||||
|
||||
SourcePortStart uint16
|
||||
SourcePortEnd uint16
|
||||
DestinationPortStart uint16
|
||||
DestinationPortEnd uint16
|
||||
InverseFlags uint8
|
||||
}
|
||||
|
||||
func NewUDPMatcher(filter IPHeaderFilter, data UDPMatcherData) (Matcher, error) {
|
||||
// TODO: We currently only support source port and destination port.
|
||||
log.Infof("Adding rule with UDPMatcherData: %+v", data)
|
||||
|
||||
if data.InverseFlags != 0 {
|
||||
return nil, fmt.Errorf("unsupported UDP matcher flags set")
|
||||
}
|
||||
|
||||
if filter.Protocol != header.UDPProtocolNumber {
|
||||
log.Warningf("UDP matching is only valid for protocol %d.", header.UDPProtocolNumber)
|
||||
}
|
||||
|
||||
return &UDPMatcher{data: data}, nil
|
||||
}
|
||||
|
||||
// TODO: Check xt_tcpudp.c. Need to check for same things (e.g. fragments).
|
||||
func (tm *UDPMatcher) Match(hook Hook, pkt tcpip.PacketBuffer, interfaceName string) (bool, bool) {
|
||||
log.Infof("UDPMatcher called from: %s", string(debug.Stack()))
|
||||
netHeader := header.IPv4(pkt.NetworkHeader)
|
||||
|
||||
// TODO: Do we check proto here or elsewhere? I think elsewhere (check
|
||||
// codesearch).
|
||||
if netHeader.TransportProtocol() != header.UDPProtocolNumber {
|
||||
log.Infof("UDPMatcher: wrong protocol number")
|
||||
return false, false
|
||||
}
|
||||
|
||||
// We dont't match fragments.
|
||||
if frag := netHeader.FragmentOffset(); frag != 0 {
|
||||
log.Infof("UDPMatcher: it's a fragment")
|
||||
if frag == 1 {
|
||||
return false, true
|
||||
}
|
||||
log.Warningf("Dropping UDP packet: malicious fragmented packet.")
|
||||
return false, false
|
||||
}
|
||||
|
||||
// Now we need the transport header. However, this may not have been set
|
||||
// yet.
|
||||
// TODO
|
||||
var udpHeader header.UDP
|
||||
if pkt.TransportHeader != nil {
|
||||
log.Infof("UDPMatcher: transport header is not nil")
|
||||
udpHeader = header.UDP(pkt.TransportHeader)
|
||||
} else {
|
||||
log.Infof("UDPMatcher: transport header is nil")
|
||||
log.Infof("UDPMatcher: is network header nil: %t", pkt.NetworkHeader == nil)
|
||||
// The UDP header hasn't been parsed yet. We have to do it here.
|
||||
if len(pkt.Data.First()) < header.UDPMinimumSize {
|
||||
// There's no valid UDP header here, so we hotdrop the
|
||||
// packet.
|
||||
// TODO: Stats.
|
||||
log.Warningf("Dropping UDP packet: size to small.")
|
||||
return false, true
|
||||
}
|
||||
udpHeader = header.UDP(pkt.Data.First())
|
||||
}
|
||||
|
||||
// Check whether the source and destination ports are within the
|
||||
// matching range.
|
||||
sourcePort := udpHeader.SourcePort()
|
||||
destinationPort := udpHeader.DestinationPort()
|
||||
log.Infof("UDPMatcher: sport and dport are %d and %d. sports and dport start and end are (%d, %d) and (%d, %d)",
|
||||
udpHeader.SourcePort(), udpHeader.DestinationPort(),
|
||||
tm.data.SourcePortStart, tm.data.SourcePortEnd,
|
||||
tm.data.DestinationPortStart, tm.data.DestinationPortEnd)
|
||||
if sourcePort < tm.data.SourcePortStart || tm.data.SourcePortEnd < sourcePort {
|
||||
return false, false
|
||||
}
|
||||
if destinationPort < tm.data.DestinationPortStart || tm.data.DestinationPortEnd < destinationPort {
|
||||
return false, false
|
||||
}
|
||||
|
||||
return true, false
|
||||
}
|
||||
@@ -353,6 +353,11 @@ func (e *endpoint) HandlePacket(r *stack.Route, pkt tcpip.PacketBuffer) {
|
||||
}
|
||||
pkt.NetworkHeader = headerView[:h.HeaderLength()]
|
||||
|
||||
hlen := int(h.HeaderLength())
|
||||
tlen := int(h.TotalLength())
|
||||
pkt.Data.TrimFront(hlen)
|
||||
pkt.Data.CapLength(tlen - hlen)
|
||||
|
||||
// iptables filtering. All packets that reach here are intended for
|
||||
// this machine and will not be forwarded.
|
||||
ipt := e.stack.IPTables()
|
||||
@@ -361,11 +366,6 @@ func (e *endpoint) HandlePacket(r *stack.Route, pkt tcpip.PacketBuffer) {
|
||||
return
|
||||
}
|
||||
|
||||
hlen := int(h.HeaderLength())
|
||||
tlen := int(h.TotalLength())
|
||||
pkt.Data.TrimFront(hlen)
|
||||
pkt.Data.CapLength(tlen - hlen)
|
||||
|
||||
more := (h.Flags() & header.IPv4FlagMoreFragments) != 0
|
||||
if more || h.FragmentOffset() != 0 {
|
||||
if pkt.Data.Size() == 0 {
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
package iptables
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"net"
|
||||
"time"
|
||||
@@ -248,3 +249,48 @@ func (FilterInputDropAll) ContainerAction(ip net.IP) error {
|
||||
func (FilterInputDropAll) LocalAction(ip net.IP) error {
|
||||
return sendUDPLoop(ip, dropPort, sendloopDuration)
|
||||
}
|
||||
|
||||
// FilterInputMultiUDPRules verifies that multiple UDP rules are applied
|
||||
// correctly. This has the added benefit of testing whether we're serializing
|
||||
// rules correctly -- if we do it incorrectly, the iptables tool will
|
||||
// misunderstand and save the wrong tables.
|
||||
type FilterInputMultiUDPRules struct{}
|
||||
|
||||
func (FilterInputMultiUDPRules) Name() string {
|
||||
return "FilterInputMultiUDPRules"
|
||||
}
|
||||
|
||||
func (FilterInputMultiUDPRules) ContainerAction(ip net.IP) error {
|
||||
if err := filterTable("-A", "INPUT", "-p", "udp", "-m", "udp", "--destination-port", fmt.Sprintf("%d", dropPort), "-j", "DROP"); err != nil {
|
||||
return err
|
||||
}
|
||||
// if err := filterTable("-A", "INPUT", "-p", "udp", "-m", "udp", "--destination-port", fmt.Sprintf("%d", acceptPort), "-j", "ACCEPT"); err != nil {
|
||||
// return err
|
||||
// }
|
||||
return filterTable("-L")
|
||||
}
|
||||
|
||||
func (FilterInputMultiUDPRules) LocalAction(ip net.IP) error {
|
||||
// No-op.
|
||||
return nil
|
||||
}
|
||||
|
||||
// FilterInputRequireProtocolUDP checks that "-m udp" requires "-p udp" to be
|
||||
// specified.
|
||||
type FilterInputRequireProtocolUDP struct{}
|
||||
|
||||
func (FilterInputRequireProtocolUDP) Name() string {
|
||||
return "FilterInputRequireProtocolUDP"
|
||||
}
|
||||
|
||||
func (FilterInputRequireProtocolUDP) ContainerAction(ip net.IP) error {
|
||||
if err := filterTable("-A", "INPUT", "-m", "udp", "--destination-port", fmt.Sprintf("%d", dropPort), "-j", "DROP"); err == nil {
|
||||
return errors.New("expected iptables to fail with out \"-p udp\", but succeeded")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (FilterInputRequireProtocolUDP) LocalAction(ip net.IP) error {
|
||||
// No-op.
|
||||
return nil
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user