mirror of
https://github.com/netbirdio/gvisor.git
synced 2026-05-22 17:12:49 -07:00
Merge pull request #1528 from kevinGC:iptables-write
PiperOrigin-RevId: 289479774
This commit is contained in:
+56
-33
@@ -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)
|
||||
}
|
||||
|
||||
@@ -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
@@ -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)
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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",
|
||||
],
|
||||
)
|
||||
|
||||
@@ -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{},
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user