From d58eb9ce828fd7c831f30e922e01f1d2b84e462c Mon Sep 17 00:00:00 2001 From: Kevin Krakauer Date: Fri, 31 May 2019 16:14:04 -0700 Subject: Add basic iptables structures to netstack. Change-Id: Ib589906175a59dae315405a28f2d7f525ff8877f --- pkg/tcpip/iptables/BUILD | 20 +++++ pkg/tcpip/iptables/iptables.go | 97 +++++++++++++++++++++ pkg/tcpip/iptables/targets.go | 35 ++++++++ pkg/tcpip/iptables/types.go | 190 +++++++++++++++++++++++++++++++++++++++++ 4 files changed, 342 insertions(+) create mode 100644 pkg/tcpip/iptables/BUILD create mode 100644 pkg/tcpip/iptables/iptables.go create mode 100644 pkg/tcpip/iptables/targets.go create mode 100644 pkg/tcpip/iptables/types.go (limited to 'pkg') diff --git a/pkg/tcpip/iptables/BUILD b/pkg/tcpip/iptables/BUILD new file mode 100644 index 000000000..173f148da --- /dev/null +++ b/pkg/tcpip/iptables/BUILD @@ -0,0 +1,20 @@ +package(licenses = ["notice"]) + +load("//tools/go_stateify:defs.bzl", "go_library", "go_test") + +go_library( + name = "iptables", + srcs = [ + "iptables.go", + "targets.go", + "types.go", + ], + importpath = "gvisor.googlesource.com/gvisor/pkg/tcpip/iptables", + visibility = ["//visibility:public"], + deps = [ + "//pkg/state", + "//pkg/tcpip", + "//pkg/tcpip/buffer", + "//pkg/tcpip/header", + ], +) diff --git a/pkg/tcpip/iptables/iptables.go b/pkg/tcpip/iptables/iptables.go new file mode 100644 index 000000000..ee1ed4666 --- /dev/null +++ b/pkg/tcpip/iptables/iptables.go @@ -0,0 +1,97 @@ +// Copyright 2019 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 supports packet filtering and manipulation via the iptables +// tool. +package iptables + +const ( + tablenameNat = "nat" + tablenameMangle = "mangle" +) + +// Chain names as defined by net/ipv4/netfilter/ip_tables.c. +const ( + chainNamePrerouting = "PREROUTING" + chainNameInput = "INPUT" + chainNameForward = "FORWARD" + chainNameOutput = "OUTPUT" + chainNamePostrouting = "POSTROUTING" +) + +// DefaultTables returns a default set of tables. Each chain is set to accept +// all packets. +func DefaultTables() *IPTables { + tables := IPTables{ + Tables: map[string]*Table{ + tablenameNat: &Table{ + BuiltinChains: map[Hook]*Chain{ + Prerouting: unconditionalAcceptChain(chainNamePrerouting), + Input: unconditionalAcceptChain(chainNameInput), + Output: unconditionalAcceptChain(chainNameOutput), + Postrouting: unconditionalAcceptChain(chainNamePostrouting), + }, + DefaultTargets: map[Hook]Target{ + Prerouting: UnconditionalAcceptTarget{}, + Input: UnconditionalAcceptTarget{}, + Output: UnconditionalAcceptTarget{}, + Postrouting: UnconditionalAcceptTarget{}, + }, + UserChains: map[string]*Chain{}, + }, + tablenameMangle: &Table{ + BuiltinChains: map[Hook]*Chain{ + Prerouting: unconditionalAcceptChain(chainNamePrerouting), + Output: unconditionalAcceptChain(chainNameOutput), + }, + DefaultTargets: map[Hook]Target{ + Prerouting: UnconditionalAcceptTarget{}, + Output: UnconditionalAcceptTarget{}, + }, + UserChains: map[string]*Chain{}, + }, + }, + Priorities: map[Hook][]string{ + Prerouting: []string{tablenameMangle, tablenameNat}, + Output: []string{tablenameMangle, tablenameNat}, + }, + } + + // Initialize each table's Chains field. + tables.Tables[tablenameNat].Chains = map[string]*Chain{ + chainNamePrerouting: tables.Tables[tablenameNat].BuiltinChains[Prerouting], + chainNameInput: tables.Tables[tablenameNat].BuiltinChains[Input], + chainNameOutput: tables.Tables[tablenameNat].BuiltinChains[Output], + chainNamePostrouting: tables.Tables[tablenameNat].BuiltinChains[Postrouting], + } + tables.Tables[tablenameMangle].Chains = map[string]*Chain{ + chainNamePrerouting: tables.Tables[tablenameMangle].BuiltinChains[Prerouting], + chainNameInput: tables.Tables[tablenameMangle].BuiltinChains[Input], + chainNameOutput: tables.Tables[tablenameMangle].BuiltinChains[Output], + chainNamePostrouting: tables.Tables[tablenameMangle].BuiltinChains[Postrouting], + } + + return &tables +} + +func unconditionalAcceptChain(name string) *Chain { + return &Chain{ + Name: name, + Rules: []*Rule{ + &Rule{ + Target: UnconditionalAcceptTarget{}, + }, + }, + } +} diff --git a/pkg/tcpip/iptables/targets.go b/pkg/tcpip/iptables/targets.go new file mode 100644 index 000000000..028978e3a --- /dev/null +++ b/pkg/tcpip/iptables/targets.go @@ -0,0 +1,35 @@ +// Copyright 2019 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 "gvisor.googlesource.com/gvisor/pkg/tcpip/buffer" + +// This file contains various Targets. + +// UnconditionalAcceptTarget accepts all packets. +type UnconditionalAcceptTarget struct{} + +// Action implements Target.Action. +func (_ UnconditionalAcceptTarget) Action(packet buffer.VectorisedView) (Verdict, string) { + return Accept, "" +} + +// UnconditionalDropTarget denies all packets. +type UnconditionalDropTarget struct{} + +// Action implements Target.Action. +func (_ UnconditionalDropTarget) Action(packet buffer.VectorisedView) (Verdict, string) { + return Drop, "" +} diff --git a/pkg/tcpip/iptables/types.go b/pkg/tcpip/iptables/types.go new file mode 100644 index 000000000..65bfc7b1d --- /dev/null +++ b/pkg/tcpip/iptables/types.go @@ -0,0 +1,190 @@ +// Copyright 2019 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 ( + "sync" + + "gvisor.googlesource.com/gvisor/pkg/tcpip" + "gvisor.googlesource.com/gvisor/pkg/tcpip/buffer" +) + +// Hook specifies one of the hooks built into the network stack. +// +// Userspace app Userspace app +// ^ | +// | v +// [Input] [Output] +// ^ | +// | v +// | routing +// | | +// | v +// ----->[Prerouting]----->routing----->[Forward]---------[Postrouting]-----> +type Hook uint + +// These values correspond to values in include/uapi/linux/netfilter.h. +const ( + // Prerouting happens before a packet is routed to applications or to + // be forwarded. + Prerouting Hook = iota + + // Input happens before a packet reaches an application. + Input + + // Forward happens once it's decided that a packet should be forwarded + // to another host. + Forward + + // Output happens after a packet is written by an application to be + // sent out. + Output + + // Postrouting happens just before a packet goes out on the wire. + Postrouting + + // The total number of hooks. + NumHooks +) + +// Verdict is returned by a rule's target to indicate how traversal of rules +// should (or should not) continue. +type Verdict int + +const ( + // Accept indicates the packet should continue traversing netstack as + // normal. + Accept Verdict = iota + + // Drop inicates the packet should be dropped, stopping traversing + // netstack. + Drop + + // Stolen indicates the packet was co-opted by the target and should + // stop traversing netstack. + Stolen + + // Queue indicates the packet should be queued for userspace processing. + Queue + + // Repeat indicates the packet should re-traverse the chains for the + // current hook. + Repeat + + // None indicates no verdict was reached. + None + + // Jump indicates a jump to another chain. + Jump + + // Continue indicates that traversal should continue at the next rule. + Continue + + // Return indicates that traversal should return to the calling chain. + Return +) + +// IPTables holds all the tables for a netstack. +type IPTables struct { + // mu protects the entire struct. + mu sync.RWMutex + + // Tables maps table names to tables. User tables have arbitrary names. + Tables map[string]*Table + + // Priorities maps each hook to a list of table names. The order of the + // list is the order in which each table should be visited for that + // hook. + Priorities map[Hook][]string +} + +// Table defines a set of chains and hooks into the network stack. The +// currently supported tables are: +// * nat +// * mangle +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 + + // DefaultTargets holds a target for each hook that will be executed if + // chain traversal doesn't yield a verdict. + DefaultTargets map[Hook]Target + + // 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 and + // UserChains, and its purpose is to make looking up tables by name + // fast. + Chains map[string]*Chain +} + +// ValidHooks returns a bitmap of the builtin hooks for the given table. +// +// Precondition: IPTables.mu must be locked for reading. +func (table *Table) ValidHooks() (uint32, *tcpip.Error) { + hooks := uint32(0) + for hook, _ := range table.BuiltinChains { + hooks |= 1 << hook + } + return hooks, nil +} + +// 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 +} + +// 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 +// applies to any packet. +type Rule struct { + // Matchers is the list of matchers for this rule. + Matchers []Matcher + + // Target is the action to invoke if all the matchers match the packet. + Target Target +} + +// 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. + Match(hook Hook, packet buffer.VectorisedView, interfaceName string) (matches bool, hotdrop bool) +} + +// Target is the interface for taking an action for a packet. +type Target interface { + // Action takes an action on the packet and returns a verdict on how + // traversal should (or should not) continue. If the return value is + // Jump, it also returns the name of the chain to jump to. + Action(packet buffer.VectorisedView) (Verdict, string) +} -- cgit v1.2.3 From 8afbd974da2483d8f81e3abde5c9d689719263cb Mon Sep 17 00:00:00 2001 From: Kevin Krakauer Date: Fri, 7 Jun 2019 12:54:53 -0700 Subject: Address Ian's comments. Change-Id: I7445033b1970cbba3f2ed0682fe520dce02d8fad --- pkg/tcpip/iptables/iptables.go | 36 +++++++++++------------------------- pkg/tcpip/iptables/types.go | 12 ++++++------ 2 files changed, 17 insertions(+), 31 deletions(-) (limited to 'pkg') diff --git a/pkg/tcpip/iptables/iptables.go b/pkg/tcpip/iptables/iptables.go index ee1ed4666..bd54ef5a6 100644 --- a/pkg/tcpip/iptables/iptables.go +++ b/pkg/tcpip/iptables/iptables.go @@ -34,9 +34,9 @@ const ( // all packets. func DefaultTables() *IPTables { tables := IPTables{ - Tables: map[string]*Table{ - tablenameNat: &Table{ - BuiltinChains: map[Hook]*Chain{ + Tables: map[string]Table{ + tablenameNat: Table{ + BuiltinChains: map[Hook]Chain{ Prerouting: unconditionalAcceptChain(chainNamePrerouting), Input: unconditionalAcceptChain(chainNameInput), Output: unconditionalAcceptChain(chainNameOutput), @@ -48,10 +48,10 @@ func DefaultTables() *IPTables { Output: UnconditionalAcceptTarget{}, Postrouting: UnconditionalAcceptTarget{}, }, - UserChains: map[string]*Chain{}, + UserChains: map[string]Chain{}, }, - tablenameMangle: &Table{ - BuiltinChains: map[Hook]*Chain{ + tablenameMangle: Table{ + BuiltinChains: map[Hook]Chain{ Prerouting: unconditionalAcceptChain(chainNamePrerouting), Output: unconditionalAcceptChain(chainNameOutput), }, @@ -59,7 +59,7 @@ func DefaultTables() *IPTables { Prerouting: UnconditionalAcceptTarget{}, Output: UnconditionalAcceptTarget{}, }, - UserChains: map[string]*Chain{}, + UserChains: map[string]Chain{}, }, }, Priorities: map[Hook][]string{ @@ -68,28 +68,14 @@ func DefaultTables() *IPTables { }, } - // Initialize each table's Chains field. - tables.Tables[tablenameNat].Chains = map[string]*Chain{ - chainNamePrerouting: tables.Tables[tablenameNat].BuiltinChains[Prerouting], - chainNameInput: tables.Tables[tablenameNat].BuiltinChains[Input], - chainNameOutput: tables.Tables[tablenameNat].BuiltinChains[Output], - chainNamePostrouting: tables.Tables[tablenameNat].BuiltinChains[Postrouting], - } - tables.Tables[tablenameMangle].Chains = map[string]*Chain{ - chainNamePrerouting: tables.Tables[tablenameMangle].BuiltinChains[Prerouting], - chainNameInput: tables.Tables[tablenameMangle].BuiltinChains[Input], - chainNameOutput: tables.Tables[tablenameMangle].BuiltinChains[Output], - chainNamePostrouting: tables.Tables[tablenameMangle].BuiltinChains[Postrouting], - } - return &tables } -func unconditionalAcceptChain(name string) *Chain { - return &Chain{ +func unconditionalAcceptChain(name string) Chain { + return Chain{ Name: name, - Rules: []*Rule{ - &Rule{ + Rules: []Rule{ + Rule{ Target: UnconditionalAcceptTarget{}, }, }, diff --git a/pkg/tcpip/iptables/types.go b/pkg/tcpip/iptables/types.go index 65bfc7b1d..cdfb6ba28 100644 --- a/pkg/tcpip/iptables/types.go +++ b/pkg/tcpip/iptables/types.go @@ -98,11 +98,11 @@ const ( // IPTables holds all the tables for a netstack. type IPTables struct { - // mu protects the entire struct. - mu sync.RWMutex + // Mu protects the entire struct. + Mu sync.RWMutex // Tables maps table names to tables. User tables have arbitrary names. - Tables map[string]*Table + Tables map[string]Table // Priorities maps each hook to a list of table names. The order of the // list is the order in which each table should be visited for that @@ -118,7 +118,7 @@ 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 + BuiltinChains map[Hook]Chain // DefaultTargets holds a target for each hook that will be executed if // chain traversal doesn't yield a verdict. @@ -126,7 +126,7 @@ type Table struct { // UserChains holds user-defined chains for the keyed by name. Users // can give their chains arbitrary names. - UserChains map[string]*Chain + 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 and @@ -158,7 +158,7 @@ type Chain struct { Name string // Rules is the list of rules to traverse. - Rules []*Rule + Rules []Rule } // Rule is a packet processing rule. It consists of two pieces. First it -- cgit v1.2.3 From 06a83df533244dc2b3b8adfc1bf0608d3753c1d9 Mon Sep 17 00:00:00 2001 From: Kevin Krakauer Date: Mon, 10 Jun 2019 12:43:54 -0700 Subject: Address more comments. Change-Id: I83ae1079f3dcba6b018f59ab7898decab5c211d2 --- pkg/tcpip/iptables/iptables.go | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) (limited to 'pkg') diff --git a/pkg/tcpip/iptables/iptables.go b/pkg/tcpip/iptables/iptables.go index bd54ef5a6..f1e1d1fad 100644 --- a/pkg/tcpip/iptables/iptables.go +++ b/pkg/tcpip/iptables/iptables.go @@ -33,7 +33,7 @@ const ( // DefaultTables returns a default set of tables. Each chain is set to accept // all packets. func DefaultTables() *IPTables { - tables := IPTables{ + return &IPTables{ Tables: map[string]Table{ tablenameNat: Table{ BuiltinChains: map[Hook]Chain{ @@ -67,8 +67,6 @@ func DefaultTables() *IPTables { Output: []string{tablenameMangle, tablenameNat}, }, } - - return &tables } func unconditionalAcceptChain(name string) Chain { -- cgit v1.2.3