Merge branch 'iptables-write' into iptables-write-input-drop

This commit is contained in:
Kevin Krakauer
2020-01-08 20:05:02 -08:00
5 changed files with 68 additions and 72 deletions
+10 -5
View File
@@ -298,6 +298,7 @@ type IPTReplace struct {
// Entries [0]IPTEntry
}
// KernelIPTReplace is identical to IPTReplace, but includes the Entries field.
type KernelIPTReplace struct {
IPTReplace
Entries [0]IPTEntry
@@ -306,28 +307,32 @@ type KernelIPTReplace struct {
// SizeOfIPTReplace is the size of an IPTReplace.
const SizeOfIPTReplace = 96
// ExtensionName holds the name of a netfilter extension.
type ExtensionName [XT_EXTENSION_MAXNAMELEN]byte
// String implements fmt.Stringer.
func (en ExtensionName) String() string {
return name(en[:])
return goString(en[:])
}
// TableName holds the name of a netfilter table.
type TableName [XT_TABLE_MAXNAMELEN]byte
// String implements fmt.Stringer.
func (tn TableName) String() string {
return name(tn[:])
return goString(tn[:])
}
// ErrorName holds the name of a netfilter error. These can also hold
// user-defined chains.
type ErrorName [XT_FUNCTION_MAXNAMELEN]byte
// String implements fmt.Stringer.
func (fn ErrorName) String() string {
return name(fn[:])
func (en ErrorName) String() string {
return goString(en[:])
}
func name(cstring []byte) string {
func goString(cstring []byte) string {
for i, c := range cstring {
if c == 0 {
return string(cstring[:i])
+44 -55
View File
@@ -52,7 +52,7 @@ func GetInfo(t *kernel.Task, stack *stack.Stack, outPtr usermem.Addr) (linux.IPT
}
// Find the appropriate table.
table, err := findTable(stack, info.Name.String())
table, err := findTable(ep, info.Name)
if err != nil {
return linux.IPTGetinfo{}, err
}
@@ -83,7 +83,7 @@ func GetEntries(t *kernel.Task, stack *stack.Stack, outPtr usermem.Addr, outLen
}
// Find the appropriate table.
table, err := findTable(stack, userEntries.Name.String())
table, err := findTable(ep, userEntries.Name)
if err != nil {
return linux.KernelIPTGetEntries{}, err
}
@@ -95,15 +95,19 @@ func GetEntries(t *kernel.Task, stack *stack.Stack, outPtr usermem.Addr, outLen
return linux.KernelIPTGetEntries{}, err
}
if binary.Size(entries) > uintptr(outLen) {
log.Infof("Insufficient GetEntries output size: %d", uintptr(outLen))
log.Warningf("Insufficient GetEntries output size: %d", uintptr(outLen))
return linux.KernelIPTGetEntries{}, syserr.ErrInvalidArgument
}
return entries, nil
}
func findTable(stack *stack.Stack, tableName string) (iptables.Table, *syserr.Error) {
table, ok := stack.IPTables().Tables[tableName]
func findTable(ep tcpip.Endpoint, tablename linux.TableName) (iptables.Table, *syserr.Error) {
ipt, err := ep.IPTables()
if err != nil {
return iptables.Table{}, syserr.FromError(err)
}
table, ok := ipt.Tables[tablename.String()]
if !ok {
return iptables.Table{}, syserr.ErrInvalidArgument
}
@@ -133,17 +137,17 @@ func FillDefaultIPTables(stack *stack.Stack) {
// 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,
// jumping to those offsets, parsing again, etc.
func convertNetstackToBinary(name string, table iptables.Table) (linux.KernelIPTGetEntries, metadata, *syserr.Error) {
func convertNetstackToBinary(tablename string, table iptables.Table) (linux.KernelIPTGetEntries, metadata, *syserr.Error) {
// Return values.
var entries linux.KernelIPTGetEntries
var meta metadata
// The table name has to fit in the struct.
if linux.XT_TABLE_MAXNAMELEN < len(name) {
log.Infof("Table name too long.")
if linux.XT_TABLE_MAXNAMELEN < len(tablename) {
log.Warningf("Table name %q too long.", tablename)
return linux.KernelIPTGetEntries{}, metadata{}, syserr.ErrInvalidArgument
}
copy(entries.Name[:], name)
copy(entries.Name[:], tablename)
for ruleIdx, rule := range table.Rules {
// Is this a chain entry point?
@@ -205,8 +209,8 @@ func marshalTarget(target iptables.Target) []byte {
return marshalStandardTarget(iptables.Accept)
case iptables.UnconditionalDropTarget:
return marshalStandardTarget(iptables.Drop)
case iptables.PanicTarget:
return marshalPanicTarget()
case iptables.ErrorTarget:
return marshalErrorTarget()
default:
panic(fmt.Errorf("unknown target of type %T", target))
}
@@ -225,7 +229,7 @@ func marshalStandardTarget(verdict iptables.Verdict) []byte {
return binary.Marshal(ret, usermem.ByteOrder, target)
}
func marshalPanicTarget() []byte {
func marshalErrorTarget() []byte {
// This is an error target named error
target := linux.XTErrorTarget{
Target: linux.XTEntryTarget{
@@ -268,11 +272,12 @@ func translateToStandardVerdict(val int32) (iptables.Verdict, *syserr.Error) {
case -linux.NF_DROP - 1:
return iptables.Drop, nil
case -linux.NF_QUEUE - 1:
log.Infof("Unsupported iptables verdict QUEUE.")
log.Warningf("Unsupported iptables verdict QUEUE.")
case linux.NF_RETURN:
log.Infof("Unsupported iptables verdict RETURN.")
log.Warningf("Unsupported iptables verdict RETURN.")
default:
log.Warningf("Unknown iptables verdict %d.", val)
}
log.Infof("Unknown iptables verdict %d.", val)
return iptables.Invalid, syserr.ErrInvalidArgument
}
@@ -283,7 +288,7 @@ func SetEntries(stack *stack.Stack, optVal []byte) *syserr.Error {
// Get the basic rules data (struct ipt_replace).
if len(optVal) < linux.SizeOfIPTReplace {
log.Infof("netfilter.SetEntries: optVal has insufficient size for replace %d", len(optVal))
log.Warningf("netfilter.SetEntries: optVal has insufficient size for replace %d", len(optVal))
return syserr.ErrInvalidArgument
}
var replace linux.IPTReplace
@@ -297,7 +302,7 @@ func SetEntries(stack *stack.Stack, optVal []byte) *syserr.Error {
case iptables.TablenameFilter:
table = iptables.EmptyFilterTable()
default:
log.Infof(fmt.Sprintf("We don't yet support writing to the %q table (gvisor.dev/issue/170)", replace.Name.String()))
log.Warningf("We don't yet support writing to the %q table (gvisor.dev/issue/170)", replace.Name.String())
return syserr.ErrInvalidArgument
}
@@ -307,7 +312,7 @@ func SetEntries(stack *stack.Stack, optVal []byte) *syserr.Error {
for entryIdx := uint32(0); entryIdx < replace.NumEntries; entryIdx++ {
// Get the struct ipt_entry.
if len(optVal) < linux.SizeOfIPTEntry {
log.Infof("netfilter: optVal has insufficient size for entry %d", len(optVal))
log.Warningf("netfilter: optVal has insufficient size for entry %d", len(optVal))
return syserr.ErrInvalidArgument
}
var entry linux.IPTEntry
@@ -323,7 +328,7 @@ func SetEntries(stack *stack.Stack, optVal []byte) *syserr.Error {
// filtering. We reject any nonzero IPTIP values for now.
emptyIPTIP := linux.IPTIP{}
if entry.IP != emptyIPTIP {
log.Infof("netfilter: non-empty struct iptip found")
log.Warningf("netfilter: non-empty struct iptip found")
return syserr.ErrInvalidArgument
}
@@ -353,11 +358,11 @@ func SetEntries(stack *stack.Stack, optVal []byte) *syserr.Error {
}
}
if ruleIdx := table.BuiltinChains[hk]; ruleIdx == iptables.HookUnset {
log.Infof("Hook %v is unset.", hk)
log.Warningf("Hook %v is unset.", hk)
return syserr.ErrInvalidArgument
}
if ruleIdx := table.Underflows[hk]; ruleIdx == iptables.HookUnset {
log.Infof("Underflow %v is unset.", hk)
log.Warningf("Underflow %v is unset.", hk)
return syserr.ErrInvalidArgument
}
}
@@ -384,7 +389,7 @@ func SetEntries(stack *stack.Stack, optVal []byte) *syserr.Error {
// along with the number of bytes it occupies in optVal.
func parseTarget(optVal []byte) (iptables.Target, uint32, *syserr.Error) {
if len(optVal) < linux.SizeOfXTEntryTarget {
log.Infof("netfilter: optVal has insufficient size for entry target %d", len(optVal))
log.Warningf("netfilter: optVal has insufficient size for entry target %d", len(optVal))
return nil, 0, syserr.ErrInvalidArgument
}
var target linux.XTEntryTarget
@@ -394,14 +399,14 @@ func parseTarget(optVal []byte) (iptables.Target, uint32, *syserr.Error) {
case "":
// Standard target.
if len(optVal) < linux.SizeOfXTStandardTarget {
log.Infof("netfilter.SetEntries: optVal has insufficient size for standard target %d", len(optVal))
log.Warningf("netfilter.SetEntries: optVal has insufficient size for standard target %d", len(optVal))
return nil, 0, syserr.ErrInvalidArgument
}
var target linux.XTStandardTarget
var standardTarget linux.XTStandardTarget
buf = optVal[:linux.SizeOfXTStandardTarget]
binary.Unmarshal(buf, usermem.ByteOrder, &target)
binary.Unmarshal(buf, usermem.ByteOrder, &standardTarget)
verdict, err := translateToStandardVerdict(target.Verdict)
verdict, err := translateToStandardVerdict(standardTarget.Verdict)
if err != nil {
return nil, 0, err
}
@@ -420,9 +425,9 @@ func parseTarget(optVal []byte) (iptables.Target, uint32, *syserr.Error) {
log.Infof("netfilter.SetEntries: optVal has insufficient size for error target %d", len(optVal))
return nil, 0, syserr.ErrInvalidArgument
}
var target linux.XTErrorTarget
var errorTarget linux.XTErrorTarget
buf = optVal[:linux.SizeOfXTErrorTarget]
binary.Unmarshal(buf, usermem.ByteOrder, &target)
binary.Unmarshal(buf, usermem.ByteOrder, &errorTarget)
// Error targets are used in 2 cases:
// * An actual error case. These rules have an error
@@ -431,11 +436,11 @@ func parseTarget(optVal []byte) (iptables.Target, uint32, *syserr.Error) {
// somehow fall through every rule.
// * To mark the start of a user defined chain. These
// rules have an error with the name of the chain.
switch target.Name.String() {
switch errorTarget.Name.String() {
case errorTargetName:
return iptables.PanicTarget{}, linux.SizeOfXTErrorTarget, nil
return iptables.ErrorTarget{}, linux.SizeOfXTErrorTarget, nil
default:
log.Infof("Unknown error target %q doesn't exist or isn't supported yet.", target.Name.String())
log.Infof("Unknown error target %q doesn't exist or isn't supported yet.", errorTarget.Name.String())
return nil, 0, syserr.ErrInvalidArgument
}
}
@@ -445,22 +450,6 @@ func parseTarget(optVal []byte) (iptables.Target, uint32, *syserr.Error) {
return nil, 0, syserr.ErrInvalidArgument
}
func chainNameFromHook(hook int) string {
switch hook {
case linux.NF_INET_PRE_ROUTING:
return iptables.ChainNamePrerouting
case linux.NF_INET_LOCAL_IN:
return iptables.ChainNameInput
case linux.NF_INET_FORWARD:
return iptables.ChainNameForward
case linux.NF_INET_LOCAL_OUT:
return iptables.ChainNameOutput
case linux.NF_INET_POST_ROUTING:
return iptables.ChainNamePostrouting
}
panic(fmt.Sprintf("Unknown hook %d does not correspond to a builtin chain"))
}
func hookFromLinux(hook int) iptables.Hook {
switch hook {
case linux.NF_INET_PRE_ROUTING:
@@ -485,7 +474,7 @@ func printReplace(optVal []byte) {
replaceBuf := optVal[:linux.SizeOfIPTReplace]
optVal = optVal[linux.SizeOfIPTReplace:]
binary.Unmarshal(replaceBuf, usermem.ByteOrder, &replace)
log.Infof("kevin: Replacing table %q: %+v", replace.Name.String(), replace)
log.Infof("Replacing table %q: %+v", replace.Name.String(), replace)
// Read in the list of entries at the end of replace.
var totalOffset uint16
@@ -493,33 +482,33 @@ func printReplace(optVal []byte) {
var entry linux.IPTEntry
entryBuf := optVal[:linux.SizeOfIPTEntry]
binary.Unmarshal(entryBuf, usermem.ByteOrder, &entry)
log.Infof("kevin: Entry %d (total offset %d): %+v", entryIdx, totalOffset, entry)
log.Infof("Entry %d (total offset %d): %+v", entryIdx, totalOffset, entry)
totalOffset += entry.NextOffset
if entry.TargetOffset == linux.SizeOfIPTEntry {
log.Infof("kevin: Entry has no matches.")
log.Infof("Entry has no matches.")
} else {
log.Infof("kevin: Entry has matches.")
log.Infof("Entry has matches.")
}
var target linux.XTEntryTarget
targetBuf := optVal[entry.TargetOffset : entry.TargetOffset+linux.SizeOfXTEntryTarget]
binary.Unmarshal(targetBuf, usermem.ByteOrder, &target)
log.Infof("kevin: Target named %q: %+v", target.Name.String(), target)
log.Infof("Target named %q: %+v", target.Name.String(), target)
switch target.Name.String() {
case "":
var standardTarget linux.XTStandardTarget
stBuf := optVal[entry.TargetOffset : entry.TargetOffset+linux.SizeOfXTStandardTarget]
binary.Unmarshal(stBuf, usermem.ByteOrder, &standardTarget)
log.Infof("kevin: Standard target with verdict %q (%d).", linux.VerdictStrings[standardTarget.Verdict], standardTarget.Verdict)
log.Infof("Standard target with verdict %q (%d).", linux.VerdictStrings[standardTarget.Verdict], standardTarget.Verdict)
case errorTargetName:
var errorTarget linux.XTErrorTarget
etBuf := optVal[entry.TargetOffset : entry.TargetOffset+linux.SizeOfXTErrorTarget]
binary.Unmarshal(etBuf, usermem.ByteOrder, &errorTarget)
log.Infof("kevin: Error target with name %q.", errorTarget.Name.String())
log.Infof("Error target with name %q.", errorTarget.Name.String())
default:
log.Infof("kevin: Unknown target type.")
log.Infof("Unknown target type.")
}
optVal = optVal[entry.NextOffset:]
+1 -4
View File
@@ -1376,10 +1376,7 @@ func (s *SocketOperations) SetSockOpt(t *kernel.Task, level int, name int, optVa
return syserr.ErrNoDevice
}
// Stack must be a netstack stack.
if err := netfilter.SetEntries(stack.(*Stack).Stack, optVal); err != nil {
return err
}
return nil
return netfilter.SetEntries(stack.(*Stack).Stack, optVal)
case linux.IPT_SO_SET_ADD_COUNTERS:
// TODO(gvisor.dev/issue/170): Counter support.
+3 -3
View File
@@ -52,7 +52,7 @@ func DefaultTables() IPTables {
Rule{Target: UnconditionalAcceptTarget{}},
Rule{Target: UnconditionalAcceptTarget{}},
Rule{Target: UnconditionalAcceptTarget{}},
Rule{Target: PanicTarget{}},
Rule{Target: ErrorTarget{}},
},
BuiltinChains: map[Hook]int{
Prerouting: 0,
@@ -72,7 +72,7 @@ func DefaultTables() IPTables {
Rules: []Rule{
Rule{Target: UnconditionalAcceptTarget{}},
Rule{Target: UnconditionalAcceptTarget{}},
Rule{Target: PanicTarget{}},
Rule{Target: ErrorTarget{}},
},
BuiltinChains: map[Hook]int{
Prerouting: 0,
@@ -89,7 +89,7 @@ func DefaultTables() IPTables {
Rule{Target: UnconditionalAcceptTarget{}},
Rule{Target: UnconditionalAcceptTarget{}},
Rule{Target: UnconditionalAcceptTarget{}},
Rule{Target: PanicTarget{}},
Rule{Target: ErrorTarget{}},
},
BuiltinChains: map[Hook]int{
Input: 0,
+10 -5
View File
@@ -16,7 +16,10 @@
package iptables
import "gvisor.dev/gvisor/pkg/tcpip"
import (
"gvisor.dev/gvisor/pkg/log"
"gvisor.dev/gvisor/pkg/tcpip"
)
// UnconditionalAcceptTarget accepts all packets.
type UnconditionalAcceptTarget struct{}
@@ -34,10 +37,12 @@ func (UnconditionalDropTarget) Action(packet tcpip.PacketBuffer) (Verdict, strin
return Drop, ""
}
// PanicTarget just panics. It represents a target that should be unreachable.
type PanicTarget struct{}
// ErrorTarget logs an error and drops the packet. It represents a target that
// should be unreachable.
type ErrorTarget struct{}
// Actions implements Target.Action.
func (PanicTarget) Action(packet tcpip.PacketBuffer) (Verdict, string) {
panic("PanicTarget triggered.")
func (ErrorTarget) Action(packet tcpip.PacketBuffer) (Verdict, string) {
log.Warningf("ErrorTarget triggered.")
return Drop, ""
}