inet.af/netstack@v0.0.0-20220214151720-7585b01ddccf/tcpip/stack/iptables_types.go (about) 1 // Copyright 2019 The gVisor Authors. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package stack 16 17 import ( 18 "fmt" 19 "strings" 20 "sync" 21 22 "inet.af/netstack/tcpip" 23 "inet.af/netstack/tcpip/header" 24 ) 25 26 // A Hook specifies one of the hooks built into the network stack. 27 // 28 // Userspace app Userspace app 29 // ^ | 30 // | v 31 // [Input] [Output] 32 // ^ | 33 // | v 34 // | routing 35 // | | 36 // | v 37 // ----->[Prerouting]----->routing----->[Forward]---------[Postrouting]-----> 38 type Hook uint 39 40 const ( 41 // Prerouting happens before a packet is routed to applications or to 42 // be forwarded. 43 Prerouting Hook = iota 44 45 // Input happens before a packet reaches an application. 46 Input 47 48 // Forward happens once it's decided that a packet should be forwarded 49 // to another host. 50 Forward 51 52 // Output happens after a packet is written by an application to be 53 // sent out. 54 Output 55 56 // Postrouting happens just before a packet goes out on the wire. 57 Postrouting 58 59 // NumHooks is the total number of hooks. 60 NumHooks 61 ) 62 63 // A RuleVerdict is what a rule decides should be done with a packet. 64 type RuleVerdict int 65 66 const ( 67 // RuleAccept indicates the packet should continue through netstack. 68 RuleAccept RuleVerdict = iota 69 70 // RuleDrop indicates the packet should be dropped. 71 RuleDrop 72 73 // RuleJump indicates the packet should jump to another chain. 74 RuleJump 75 76 // RuleReturn indicates the packet should return to the previous chain. 77 RuleReturn 78 ) 79 80 // IPTables holds all the tables for a netstack. 81 // 82 // +stateify savable 83 type IPTables struct { 84 connections ConnTrack 85 86 // reaperDone can be signaled to stop the reaper goroutine. 87 reaperDone chan struct{} 88 89 mu sync.RWMutex 90 // v4Tables and v6tables map tableIDs to tables. They hold builtin 91 // tables only, not user tables. 92 // 93 // +checklocks:mu 94 v4Tables [NumTables]Table 95 // +checklocks:mu 96 v6Tables [NumTables]Table 97 // modified is whether tables have been modified at least once. It is 98 // used to elide the iptables performance overhead for workloads that 99 // don't utilize iptables. 100 // 101 // +checklocks:mu 102 modified bool 103 } 104 105 // VisitTargets traverses all the targets of all tables and replaces each with 106 // transform(target). 107 func (it *IPTables) VisitTargets(transform func(Target) Target) { 108 it.mu.Lock() 109 defer it.mu.Unlock() 110 111 for tid := range it.v4Tables { 112 for i, rule := range it.v4Tables[tid].Rules { 113 it.v4Tables[tid].Rules[i].Target = transform(rule.Target) 114 } 115 } 116 for tid := range it.v6Tables { 117 for i, rule := range it.v6Tables[tid].Rules { 118 it.v6Tables[tid].Rules[i].Target = transform(rule.Target) 119 } 120 } 121 } 122 123 // A Table defines a set of chains and hooks into the network stack. 124 // 125 // It is a list of Rules, entry points (BuiltinChains), and error handlers 126 // (Underflows). As packets traverse netstack, they hit hooks. When a packet 127 // hits a hook, iptables compares it to Rules starting from that hook's entry 128 // point. So if a packet hits the Input hook, we look up the corresponding 129 // entry point in BuiltinChains and jump to that point. 130 // 131 // If the Rule doesn't match the packet, iptables continues to the next Rule. 132 // If a Rule does match, it can issue a verdict on the packet (e.g. RuleAccept 133 // or RuleDrop) that causes the packet to stop traversing iptables. It can also 134 // jump to other rules or perform custom actions based on Rule.Target. 135 // 136 // Underflow Rules are invoked when a chain returns without reaching a verdict. 137 // 138 // +stateify savable 139 type Table struct { 140 // Rules holds the rules that make up the table. 141 Rules []Rule 142 143 // BuiltinChains maps builtin chains to their entrypoint rule in Rules. 144 BuiltinChains [NumHooks]int 145 146 // Underflows maps builtin chains to their underflow rule in Rules 147 // (i.e. the rule to execute if the chain returns without a verdict). 148 Underflows [NumHooks]int 149 } 150 151 // ValidHooks returns a bitmap of the builtin hooks for the given table. 152 func (table *Table) ValidHooks() uint32 { 153 hooks := uint32(0) 154 for hook, ruleIdx := range table.BuiltinChains { 155 if ruleIdx != HookUnset { 156 hooks |= 1 << hook 157 } 158 } 159 return hooks 160 } 161 162 // A Rule is a packet processing rule. It consists of two pieces. First it 163 // contains zero or more matchers, each of which is a specification of which 164 // packets this rule applies to. If there are no matchers in the rule, it 165 // applies to any packet. 166 // 167 // +stateify savable 168 type Rule struct { 169 // Filter holds basic IP filtering fields common to every rule. 170 Filter IPHeaderFilter 171 172 // Matchers is the list of matchers for this rule. 173 Matchers []Matcher 174 175 // Target is the action to invoke if all the matchers match the packet. 176 Target Target 177 } 178 179 // IPHeaderFilter performs basic IP header matching common to every rule. 180 // 181 // +stateify savable 182 type IPHeaderFilter struct { 183 // Protocol matches the transport protocol. 184 Protocol tcpip.TransportProtocolNumber 185 186 // CheckProtocol determines whether the Protocol field should be 187 // checked during matching. 188 CheckProtocol bool 189 190 // Dst matches the destination IP address. 191 Dst tcpip.Address 192 193 // DstMask masks bits of the destination IP address when comparing with 194 // Dst. 195 DstMask tcpip.Address 196 197 // DstInvert inverts the meaning of the destination IP check, i.e. when 198 // true the filter will match packets that fail the destination 199 // comparison. 200 DstInvert bool 201 202 // Src matches the source IP address. 203 Src tcpip.Address 204 205 // SrcMask masks bits of the source IP address when comparing with Src. 206 SrcMask tcpip.Address 207 208 // SrcInvert inverts the meaning of the source IP check, i.e. when true the 209 // filter will match packets that fail the source comparison. 210 SrcInvert bool 211 212 // InputInterface matches the name of the incoming interface for the packet. 213 InputInterface string 214 215 // InputInterfaceMask masks the characters of the interface name when 216 // comparing with InputInterface. 217 InputInterfaceMask string 218 219 // InputInterfaceInvert inverts the meaning of incoming interface check, 220 // i.e. when true the filter will match packets that fail the incoming 221 // interface comparison. 222 InputInterfaceInvert bool 223 224 // OutputInterface matches the name of the outgoing interface for the packet. 225 OutputInterface string 226 227 // OutputInterfaceMask masks the characters of the interface name when 228 // comparing with OutputInterface. 229 OutputInterfaceMask string 230 231 // OutputInterfaceInvert inverts the meaning of outgoing interface check, 232 // i.e. when true the filter will match packets that fail the outgoing 233 // interface comparison. 234 OutputInterfaceInvert bool 235 } 236 237 // match returns whether pkt matches the filter. 238 // 239 // Preconditions: pkt.NetworkHeader is set and is at least of the minimal IPv4 240 // or IPv6 header length. 241 func (fl IPHeaderFilter) match(pkt *PacketBuffer, hook Hook, inNicName, outNicName string) bool { 242 // Extract header fields. 243 var ( 244 transProto tcpip.TransportProtocolNumber 245 dstAddr tcpip.Address 246 srcAddr tcpip.Address 247 ) 248 switch proto := pkt.NetworkProtocolNumber; proto { 249 case header.IPv4ProtocolNumber: 250 hdr := header.IPv4(pkt.NetworkHeader().View()) 251 transProto = hdr.TransportProtocol() 252 dstAddr = hdr.DestinationAddress() 253 srcAddr = hdr.SourceAddress() 254 255 case header.IPv6ProtocolNumber: 256 hdr := header.IPv6(pkt.NetworkHeader().View()) 257 transProto = hdr.TransportProtocol() 258 dstAddr = hdr.DestinationAddress() 259 srcAddr = hdr.SourceAddress() 260 261 default: 262 panic(fmt.Sprintf("unknown network protocol with EtherType: %d", proto)) 263 } 264 265 // Check the transport protocol. 266 if fl.CheckProtocol && fl.Protocol != transProto { 267 return false 268 } 269 270 // Check the addresses. 271 if !filterAddress(dstAddr, fl.DstMask, fl.Dst, fl.DstInvert) || 272 !filterAddress(srcAddr, fl.SrcMask, fl.Src, fl.SrcInvert) { 273 return false 274 } 275 276 switch hook { 277 case Prerouting, Input: 278 return matchIfName(inNicName, fl.InputInterface, fl.InputInterfaceInvert) 279 case Output: 280 return matchIfName(outNicName, fl.OutputInterface, fl.OutputInterfaceInvert) 281 case Forward: 282 if !matchIfName(inNicName, fl.InputInterface, fl.InputInterfaceInvert) { 283 return false 284 } 285 286 if !matchIfName(outNicName, fl.OutputInterface, fl.OutputInterfaceInvert) { 287 return false 288 } 289 290 return true 291 case Postrouting: 292 return true 293 default: 294 panic(fmt.Sprintf("unknown hook: %d", hook)) 295 } 296 } 297 298 func matchIfName(nicName string, ifName string, invert bool) bool { 299 n := len(ifName) 300 if n == 0 { 301 // If the interface name is omitted in the filter, any interface will match. 302 return true 303 } 304 // If the interface name ends with '+', any interface which begins with the 305 // name should be matched. 306 var matches bool 307 if strings.HasSuffix(ifName, "+") { 308 matches = strings.HasPrefix(nicName, ifName[:n-1]) 309 } else { 310 matches = nicName == ifName 311 } 312 return matches != invert 313 } 314 315 // NetworkProtocol returns the protocol (IPv4 or IPv6) on to which the header 316 // applies. 317 func (fl IPHeaderFilter) NetworkProtocol() tcpip.NetworkProtocolNumber { 318 switch len(fl.Src) { 319 case header.IPv4AddressSize: 320 return header.IPv4ProtocolNumber 321 case header.IPv6AddressSize: 322 return header.IPv6ProtocolNumber 323 } 324 panic(fmt.Sprintf("invalid address in IPHeaderFilter: %s", fl.Src)) 325 } 326 327 // filterAddress returns whether addr matches the filter. 328 func filterAddress(addr, mask, filterAddr tcpip.Address, invert bool) bool { 329 matches := true 330 for i := range filterAddr { 331 if addr[i]&mask[i] != filterAddr[i] { 332 matches = false 333 break 334 } 335 } 336 return matches != invert 337 } 338 339 // A Matcher is the interface for matching packets. 340 type Matcher interface { 341 // Match returns whether the packet matches and whether the packet 342 // should be "hotdropped", i.e. dropped immediately. This is usually 343 // used for suspicious packets. 344 // 345 // Precondition: packet.NetworkHeader is set. 346 Match(hook Hook, packet *PacketBuffer, inputInterfaceName, outputInterfaceName string) (matches bool, hotdrop bool) 347 } 348 349 // A Target is the interface for taking an action for a packet. 350 type Target interface { 351 // Action takes an action on the packet and returns a verdict on how 352 // traversal should (or should not) continue. If the return value is 353 // Jump, it also returns the index of the rule to jump to. 354 Action(*PacketBuffer, Hook, *Route, AddressableEndpoint) (RuleVerdict, int) 355 }