Merge pull request #1528 from kevinGC:iptables-write

PiperOrigin-RevId: 289479774
This commit is contained in:
gVisor bot
2020-01-13 11:26:26 -08:00
9 changed files with 519 additions and 199 deletions
+56 -33
View File
@@ -42,6 +42,15 @@ const (
NF_RETURN = -NF_REPEAT - 1
)
// VerdictStrings maps int verdicts to the strings they represent. It is used
// for debugging.
var VerdictStrings = map[int32]string{
-NF_DROP - 1: "DROP",
-NF_ACCEPT - 1: "ACCEPT",
-NF_QUEUE - 1: "QUEUE",
NF_RETURN: "RETURN",
}
// Socket options. These correspond to values in
// include/uapi/linux/netfilter_ipv4/ip_tables.h.
const (
@@ -179,7 +188,7 @@ const SizeOfXTCounters = 16
// the user data.
type XTEntryMatch struct {
MatchSize uint16
Name [XT_EXTENSION_MAXNAMELEN]byte
Name ExtensionName
Revision uint8
// Data is omitted here because it would cause XTEntryMatch to be an
// extra byte larger (see http://www.catb.org/esr/structure-packing/).
@@ -199,7 +208,7 @@ const SizeOfXTEntryMatch = 32
// the user data.
type XTEntryTarget struct {
TargetSize uint16
Name [XT_EXTENSION_MAXNAMELEN]byte
Name ExtensionName
Revision uint8
// Data is omitted here because it would cause XTEntryTarget to be an
// extra byte larger (see http://www.catb.org/esr/structure-packing/).
@@ -226,9 +235,9 @@ const SizeOfXTStandardTarget = 40
// ErrorName. It corresponds to struct xt_error_target in
// include/uapi/linux/netfilter/x_tables.h.
type XTErrorTarget struct {
Target XTEntryTarget
ErrorName [XT_FUNCTION_MAXNAMELEN]byte
_ [2]byte
Target XTEntryTarget
Name ErrorName
_ [2]byte
}
// SizeOfXTErrorTarget is the size of an XTErrorTarget.
@@ -237,7 +246,7 @@ const SizeOfXTErrorTarget = 64
// IPTGetinfo is the argument for the IPT_SO_GET_INFO sockopt. It corresponds
// to struct ipt_getinfo in include/uapi/linux/netfilter_ipv4/ip_tables.h.
type IPTGetinfo struct {
Name [XT_TABLE_MAXNAMELEN]byte
Name TableName
ValidHooks uint32
HookEntry [NF_INET_NUMHOOKS]uint32
Underflow [NF_INET_NUMHOOKS]uint32
@@ -248,16 +257,11 @@ type IPTGetinfo struct {
// SizeOfIPTGetinfo is the size of an IPTGetinfo.
const SizeOfIPTGetinfo = 84
// TableName returns the table name.
func (info *IPTGetinfo) TableName() string {
return tableName(info.Name[:])
}
// IPTGetEntries is the argument for the IPT_SO_GET_ENTRIES sockopt. It
// corresponds to struct ipt_get_entries in
// include/uapi/linux/netfilter_ipv4/ip_tables.h.
type IPTGetEntries struct {
Name [XT_TABLE_MAXNAMELEN]byte
Name TableName
Size uint32
_ [4]byte
// Entrytable is omitted here because it would cause IPTGetEntries to
@@ -266,34 +270,22 @@ type IPTGetEntries struct {
// Entrytable [0]IPTEntry
}
// TableName returns the entries' table name.
func (entries *IPTGetEntries) TableName() string {
return tableName(entries.Name[:])
}
// SizeOfIPTGetEntries is the size of an IPTGetEntries.
const SizeOfIPTGetEntries = 40
// KernelIPTGetEntries is identical to IPTEntry, but includes the Elems field.
// This struct marshaled via the binary package to write an KernelIPTGetEntries
// to userspace.
// KernelIPTGetEntries is identical to IPTGetEntries, but includes the
// Entrytable field. This struct marshaled via the binary package to write an
// KernelIPTGetEntries to userspace.
type KernelIPTGetEntries struct {
Name [XT_TABLE_MAXNAMELEN]byte
Size uint32
_ [4]byte
IPTGetEntries
Entrytable []KernelIPTEntry
}
// TableName returns the entries' table name.
func (entries *KernelIPTGetEntries) TableName() string {
return tableName(entries.Name[:])
}
// IPTReplace is the argument for the IPT_SO_SET_REPLACE sockopt. It
// corresponds to struct ipt_replace in
// include/uapi/linux/netfilter_ipv4/ip_tables.h.
type IPTReplace struct {
Name [XT_TABLE_MAXNAMELEN]byte
Name TableName
ValidHooks uint32
NumEntries uint32
Size uint32
@@ -306,14 +298,45 @@ type IPTReplace struct {
// Entries [0]IPTEntry
}
// KernelIPTReplace is identical to IPTReplace, but includes the Entries field.
type KernelIPTReplace struct {
IPTReplace
Entries [0]IPTEntry
}
// SizeOfIPTReplace is the size of an IPTReplace.
const SizeOfIPTReplace = 96
func tableName(name []byte) string {
for i, c := range name {
// ExtensionName holds the name of a netfilter extension.
type ExtensionName [XT_EXTENSION_MAXNAMELEN]byte
// String implements fmt.Stringer.
func (en ExtensionName) String() string {
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 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 (en ErrorName) String() string {
return goString(en[:])
}
func goString(cstring []byte) string {
for i, c := range cstring {
if c == 0 {
return string(name[:i])
return string(cstring[:i])
}
}
return string(name)
return string(cstring)
}
+1
View File
@@ -14,6 +14,7 @@ go_library(
deps = [
"//pkg/abi/linux",
"//pkg/binary",
"//pkg/log",
"//pkg/sentry/kernel",
"//pkg/sentry/usermem",
"//pkg/syserr",
File diff suppressed because it is too large Load Diff
+20
View File
@@ -1377,6 +1377,26 @@ func (s *SocketOperations) SetSockOpt(t *kernel.Task, level int, name int, optVa
return nil
}
if s.skType == linux.SOCK_RAW && level == linux.IPPROTO_IP {
switch name {
case linux.IPT_SO_SET_REPLACE:
if len(optVal) < linux.SizeOfIPTReplace {
return syserr.ErrInvalidArgument
}
stack := inet.StackFromContext(t)
if stack == nil {
return syserr.ErrNoDevice
}
// Stack must be a netstack stack.
return netfilter.SetEntries(stack.(*Stack).Stack, optVal)
case linux.IPT_SO_SET_ADD_COUNTERS:
// TODO(gvisor.dev/issue/170): Counter support.
return nil
}
}
return SetSockOpt(t, s, s.Endpoint, level, name, optVal)
}
+1 -1
View File
@@ -41,7 +41,7 @@ const maxListenBacklog = 1024
const maxAddrLen = 200
// maxOptLen is the maximum sockopt parameter length we're willing to accept.
const maxOptLen = 1024
const maxOptLen = 1024 * 8
// maxControlLen is the maximum length of the msghdr.msg_control buffer we're
// willing to accept. Note that this limit is smaller than Linux, which allows
+4 -1
View File
@@ -11,5 +11,8 @@ go_library(
],
importpath = "gvisor.dev/gvisor/pkg/tcpip/iptables",
visibility = ["//visibility:public"],
deps = ["//pkg/tcpip/buffer"],
deps = [
"//pkg/log",
"//pkg/tcpip/buffer",
],
)
+84 -36
View File
@@ -16,66 +16,114 @@
// tool.
package iptables
// Table names.
const (
tablenameNat = "nat"
tablenameMangle = "mangle"
TablenameNat = "nat"
TablenameMangle = "mangle"
TablenameFilter = "filter"
)
// Chain names as defined by net/ipv4/netfilter/ip_tables.c.
const (
chainNamePrerouting = "PREROUTING"
chainNameInput = "INPUT"
chainNameForward = "FORWARD"
chainNameOutput = "OUTPUT"
chainNamePostrouting = "POSTROUTING"
ChainNamePrerouting = "PREROUTING"
ChainNameInput = "INPUT"
ChainNameForward = "FORWARD"
ChainNameOutput = "OUTPUT"
ChainNamePostrouting = "POSTROUTING"
)
// HookUnset indicates that there is no hook set for an entrypoint or
// underflow.
const HookUnset = -1
// DefaultTables returns a default set of tables. Each chain is set to accept
// all packets.
func DefaultTables() IPTables {
// TODO(gvisor.dev/issue/170): We may be able to swap out some strings for
// iotas.
return IPTables{
Tables: map[string]Table{
tablenameNat: Table{
BuiltinChains: map[Hook]Chain{
Prerouting: unconditionalAcceptChain(chainNamePrerouting),
Input: unconditionalAcceptChain(chainNameInput),
Output: unconditionalAcceptChain(chainNameOutput),
Postrouting: unconditionalAcceptChain(chainNamePostrouting),
TablenameNat: Table{
Rules: []Rule{
Rule{Target: UnconditionalAcceptTarget{}},
Rule{Target: UnconditionalAcceptTarget{}},
Rule{Target: UnconditionalAcceptTarget{}},
Rule{Target: UnconditionalAcceptTarget{}},
Rule{Target: ErrorTarget{}},
},
DefaultTargets: map[Hook]Target{
Prerouting: UnconditionalAcceptTarget{},
Input: UnconditionalAcceptTarget{},
Output: UnconditionalAcceptTarget{},
Postrouting: UnconditionalAcceptTarget{},
BuiltinChains: map[Hook]int{
Prerouting: 0,
Input: 1,
Output: 2,
Postrouting: 3,
},
UserChains: map[string]Chain{},
Underflows: map[Hook]int{
Prerouting: 0,
Input: 1,
Output: 2,
Postrouting: 3,
},
UserChains: map[string]int{},
},
tablenameMangle: Table{
BuiltinChains: map[Hook]Chain{
Prerouting: unconditionalAcceptChain(chainNamePrerouting),
Output: unconditionalAcceptChain(chainNameOutput),
TablenameMangle: Table{
Rules: []Rule{
Rule{Target: UnconditionalAcceptTarget{}},
Rule{Target: UnconditionalAcceptTarget{}},
Rule{Target: ErrorTarget{}},
},
DefaultTargets: map[Hook]Target{
Prerouting: UnconditionalAcceptTarget{},
Output: UnconditionalAcceptTarget{},
BuiltinChains: map[Hook]int{
Prerouting: 0,
Output: 1,
},
UserChains: map[string]Chain{},
Underflows: map[Hook]int{
Prerouting: 0,
Output: 1,
},
UserChains: map[string]int{},
},
TablenameFilter: Table{
Rules: []Rule{
Rule{Target: UnconditionalAcceptTarget{}},
Rule{Target: UnconditionalAcceptTarget{}},
Rule{Target: UnconditionalAcceptTarget{}},
Rule{Target: ErrorTarget{}},
},
BuiltinChains: map[Hook]int{
Input: 0,
Forward: 1,
Output: 2,
},
Underflows: map[Hook]int{
Input: 0,
Forward: 1,
Output: 2,
},
UserChains: map[string]int{},
},
},
Priorities: map[Hook][]string{
Prerouting: []string{tablenameMangle, tablenameNat},
Output: []string{tablenameMangle, tablenameNat},
Input: []string{TablenameNat, TablenameFilter},
Prerouting: []string{TablenameMangle, TablenameNat},
Output: []string{TablenameMangle, TablenameNat, TablenameFilter},
},
}
}
func unconditionalAcceptChain(name string) Chain {
return Chain{
Name: name,
Rules: []Rule{
Rule{
Target: UnconditionalAcceptTarget{},
},
// EmptyFilterTable returns a Table with no rules and the filter table chains
// mapped to HookUnset.
func EmptyFilterTable() Table {
return Table{
Rules: []Rule{},
BuiltinChains: map[Hook]int{
Input: HookUnset,
Forward: HookUnset,
Output: HookUnset,
},
Underflows: map[Hook]int{
Input: HookUnset,
Forward: HookUnset,
Output: HookUnset,
},
UserChains: map[string]int{},
}
}
+15 -1
View File
@@ -16,7 +16,10 @@
package iptables
import "gvisor.dev/gvisor/pkg/tcpip/buffer"
import (
"gvisor.dev/gvisor/pkg/log"
"gvisor.dev/gvisor/pkg/tcpip/buffer"
)
// UnconditionalAcceptTarget accepts all packets.
type UnconditionalAcceptTarget struct{}
@@ -33,3 +36,14 @@ type UnconditionalDropTarget struct{}
func (UnconditionalDropTarget) Action(packet buffer.VectorisedView) (Verdict, string) {
return Drop, ""
}
// ErrorTarget logs an error and drops the packet. It represents a target that
// should be unreachable.
type ErrorTarget struct{}
// Action implements Target.Action.
func (ErrorTarget) Action(packet buffer.VectorisedView) (Verdict, string) {
log.Warningf("ErrorTarget triggered.")
return Drop, ""
}
+15 -34
View File
@@ -61,9 +61,12 @@ const (
type Verdict int
const (
// Invalid indicates an unkonwn or erroneous verdict.
Invalid Verdict = iota
// Accept indicates the packet should continue traversing netstack as
// normal.
Accept Verdict = iota
Accept
// Drop inicates the packet should be dropped, stopping traversing
// netstack.
@@ -104,29 +107,22 @@ type IPTables struct {
Priorities map[Hook][]string
}
// A Table defines a set of chains and hooks into the network stack. The
// currently supported tables are:
// * nat
// * mangle
// A Table defines a set of chains and hooks into the network stack. It is
// really just a list of rules with some metadata for entrypoints and such.
type Table struct {
// BuiltinChains holds the un-deletable chains built into netstack. If
// a hook isn't present in the map, this table doesn't utilize that
// hook.
BuiltinChains map[Hook]Chain
// Rules holds the rules that make up the table.
Rules []Rule
// DefaultTargets holds a target for each hook that will be executed if
// chain traversal doesn't yield a verdict.
DefaultTargets map[Hook]Target
// BuiltinChains maps builtin chains to their entrypoint rule in Rules.
BuiltinChains map[Hook]int
// Underflows maps builtin chains to their underflow rule in Rules
// (i.e. the rule to execute if the chain returns without a verdict).
Underflows map[Hook]int
// UserChains holds user-defined chains for the keyed by name. Users
// can give their chains arbitrary names.
UserChains map[string]Chain
// Chains maps names to chains for both builtin and user-defined chains.
// Its entries point to Chains already either in BuiltinChains or
// UserChains, and its purpose is to make looking up tables by name
// fast.
Chains map[string]*Chain
UserChains map[string]int
// Metadata holds information about the Table that is useful to users
// of IPTables, but not to the netstack IPTables code itself.
@@ -152,21 +148,6 @@ func (table *Table) SetMetadata(metadata interface{}) {
table.metadata = metadata
}
// A Chain defines a list of rules for packet processing. When a packet
// traverses a chain, it is checked against each rule until either a rule
// returns a verdict or the chain ends.
//
// By convention, builtin chains end with a rule that matches everything and
// returns either Accept or Drop. User-defined chains end with Return. These
// aren't strictly necessary here, but the iptables tool writes tables this way.
type Chain struct {
// Name is the chain name.
Name string
// Rules is the list of rules to traverse.
Rules []Rule
}
// A Rule is a packet processing rule. It consists of two pieces. First it
// contains zero or more matchers, each of which is a specification of which
// packets this rule applies to. If there are no matchers in the rule, it