Add basic iptables structures to netstack.

Change-Id: Ib589906175a59dae315405a28f2d7f525ff8877f
This commit is contained in:
Kevin Krakauer
2019-05-31 16:14:04 -07:00
parent 6f73d79c32
commit d58eb9ce82
4 changed files with 342 additions and 0 deletions
+20
View File
@@ -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",
],
)
+97
View File
@@ -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{},
},
},
}
}
+35
View File
@@ -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, ""
}
+190
View File
@@ -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)
}