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