gvisor.dev/gvisor@v0.0.0-20240520182842-f9d4d51c7e0f/pkg/tcpip/stack/iptables.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 "context" 19 "fmt" 20 "math/rand" 21 "reflect" 22 "time" 23 24 "gvisor.dev/gvisor/pkg/tcpip" 25 "gvisor.dev/gvisor/pkg/tcpip/header" 26 ) 27 28 // TableID identifies a specific table. 29 type TableID int 30 31 // Each value identifies a specific table. 32 const ( 33 NATID TableID = iota 34 MangleID 35 FilterID 36 NumTables 37 ) 38 39 // HookUnset indicates that there is no hook set for an entrypoint or 40 // underflow. 41 const HookUnset = -1 42 43 // reaperDelay is how long to wait before starting to reap connections. 44 const reaperDelay = 5 * time.Second 45 46 // DefaultTables returns a default set of tables. Each chain is set to accept 47 // all packets. 48 func DefaultTables(clock tcpip.Clock, rand *rand.Rand) *IPTables { 49 return &IPTables{ 50 v4Tables: [NumTables]Table{ 51 NATID: { 52 Rules: []Rule{ 53 {Filter: EmptyFilter4(), Target: &AcceptTarget{NetworkProtocol: header.IPv4ProtocolNumber}}, 54 {Filter: EmptyFilter4(), Target: &AcceptTarget{NetworkProtocol: header.IPv4ProtocolNumber}}, 55 {Filter: EmptyFilter4(), Target: &AcceptTarget{NetworkProtocol: header.IPv4ProtocolNumber}}, 56 {Filter: EmptyFilter4(), Target: &AcceptTarget{NetworkProtocol: header.IPv4ProtocolNumber}}, 57 {Filter: EmptyFilter4(), Target: &ErrorTarget{NetworkProtocol: header.IPv4ProtocolNumber}}, 58 }, 59 BuiltinChains: [NumHooks]int{ 60 Prerouting: 0, 61 Input: 1, 62 Forward: HookUnset, 63 Output: 2, 64 Postrouting: 3, 65 }, 66 Underflows: [NumHooks]int{ 67 Prerouting: 0, 68 Input: 1, 69 Forward: HookUnset, 70 Output: 2, 71 Postrouting: 3, 72 }, 73 }, 74 MangleID: { 75 Rules: []Rule{ 76 {Filter: EmptyFilter4(), Target: &AcceptTarget{NetworkProtocol: header.IPv4ProtocolNumber}}, 77 {Filter: EmptyFilter4(), Target: &AcceptTarget{NetworkProtocol: header.IPv4ProtocolNumber}}, 78 {Filter: EmptyFilter4(), Target: &ErrorTarget{NetworkProtocol: header.IPv4ProtocolNumber}}, 79 }, 80 BuiltinChains: [NumHooks]int{ 81 Prerouting: 0, 82 Output: 1, 83 }, 84 Underflows: [NumHooks]int{ 85 Prerouting: 0, 86 Input: HookUnset, 87 Forward: HookUnset, 88 Output: 1, 89 Postrouting: HookUnset, 90 }, 91 }, 92 FilterID: { 93 Rules: []Rule{ 94 {Filter: EmptyFilter4(), Target: &AcceptTarget{NetworkProtocol: header.IPv4ProtocolNumber}}, 95 {Filter: EmptyFilter4(), Target: &AcceptTarget{NetworkProtocol: header.IPv4ProtocolNumber}}, 96 {Filter: EmptyFilter4(), Target: &AcceptTarget{NetworkProtocol: header.IPv4ProtocolNumber}}, 97 {Filter: EmptyFilter4(), Target: &ErrorTarget{NetworkProtocol: header.IPv4ProtocolNumber}}, 98 }, 99 BuiltinChains: [NumHooks]int{ 100 Prerouting: HookUnset, 101 Input: 0, 102 Forward: 1, 103 Output: 2, 104 Postrouting: HookUnset, 105 }, 106 Underflows: [NumHooks]int{ 107 Prerouting: HookUnset, 108 Input: 0, 109 Forward: 1, 110 Output: 2, 111 Postrouting: HookUnset, 112 }, 113 }, 114 }, 115 v6Tables: [NumTables]Table{ 116 NATID: { 117 Rules: []Rule{ 118 {Filter: EmptyFilter6(), Target: &AcceptTarget{NetworkProtocol: header.IPv6ProtocolNumber}}, 119 {Filter: EmptyFilter6(), Target: &AcceptTarget{NetworkProtocol: header.IPv6ProtocolNumber}}, 120 {Filter: EmptyFilter6(), Target: &AcceptTarget{NetworkProtocol: header.IPv6ProtocolNumber}}, 121 {Filter: EmptyFilter6(), Target: &AcceptTarget{NetworkProtocol: header.IPv6ProtocolNumber}}, 122 {Filter: EmptyFilter6(), Target: &ErrorTarget{NetworkProtocol: header.IPv6ProtocolNumber}}, 123 }, 124 BuiltinChains: [NumHooks]int{ 125 Prerouting: 0, 126 Input: 1, 127 Forward: HookUnset, 128 Output: 2, 129 Postrouting: 3, 130 }, 131 Underflows: [NumHooks]int{ 132 Prerouting: 0, 133 Input: 1, 134 Forward: HookUnset, 135 Output: 2, 136 Postrouting: 3, 137 }, 138 }, 139 MangleID: { 140 Rules: []Rule{ 141 {Filter: EmptyFilter6(), Target: &AcceptTarget{NetworkProtocol: header.IPv6ProtocolNumber}}, 142 {Filter: EmptyFilter6(), Target: &AcceptTarget{NetworkProtocol: header.IPv6ProtocolNumber}}, 143 {Filter: EmptyFilter6(), Target: &ErrorTarget{NetworkProtocol: header.IPv6ProtocolNumber}}, 144 }, 145 BuiltinChains: [NumHooks]int{ 146 Prerouting: 0, 147 Output: 1, 148 }, 149 Underflows: [NumHooks]int{ 150 Prerouting: 0, 151 Input: HookUnset, 152 Forward: HookUnset, 153 Output: 1, 154 Postrouting: HookUnset, 155 }, 156 }, 157 FilterID: { 158 Rules: []Rule{ 159 {Filter: EmptyFilter6(), Target: &AcceptTarget{NetworkProtocol: header.IPv6ProtocolNumber}}, 160 {Filter: EmptyFilter6(), Target: &AcceptTarget{NetworkProtocol: header.IPv6ProtocolNumber}}, 161 {Filter: EmptyFilter6(), Target: &AcceptTarget{NetworkProtocol: header.IPv6ProtocolNumber}}, 162 {Filter: EmptyFilter6(), Target: &ErrorTarget{NetworkProtocol: header.IPv6ProtocolNumber}}, 163 }, 164 BuiltinChains: [NumHooks]int{ 165 Prerouting: HookUnset, 166 Input: 0, 167 Forward: 1, 168 Output: 2, 169 Postrouting: HookUnset, 170 }, 171 Underflows: [NumHooks]int{ 172 Prerouting: HookUnset, 173 Input: 0, 174 Forward: 1, 175 Output: 2, 176 Postrouting: HookUnset, 177 }, 178 }, 179 }, 180 connections: ConnTrack{ 181 seed: rand.Uint32(), 182 clock: clock, 183 rand: rand, 184 }, 185 } 186 } 187 188 // EmptyFilterTable returns a Table with no rules and the filter table chains 189 // mapped to HookUnset. 190 func EmptyFilterTable() Table { 191 return Table{ 192 Rules: []Rule{}, 193 BuiltinChains: [NumHooks]int{ 194 Prerouting: HookUnset, 195 Postrouting: HookUnset, 196 }, 197 Underflows: [NumHooks]int{ 198 Prerouting: HookUnset, 199 Postrouting: HookUnset, 200 }, 201 } 202 } 203 204 // EmptyNATTable returns a Table with no rules and the filter table chains 205 // mapped to HookUnset. 206 func EmptyNATTable() Table { 207 return Table{ 208 Rules: []Rule{}, 209 BuiltinChains: [NumHooks]int{ 210 Forward: HookUnset, 211 }, 212 Underflows: [NumHooks]int{ 213 Forward: HookUnset, 214 }, 215 } 216 } 217 218 // GetTable returns a table with the given id and IP version. It panics when an 219 // invalid id is provided. 220 func (it *IPTables) GetTable(id TableID, ipv6 bool) Table { 221 it.mu.RLock() 222 defer it.mu.RUnlock() 223 return it.getTableRLocked(id, ipv6) 224 } 225 226 // +checklocksread:it.mu 227 func (it *IPTables) getTableRLocked(id TableID, ipv6 bool) Table { 228 if ipv6 { 229 return it.v6Tables[id] 230 } 231 return it.v4Tables[id] 232 } 233 234 // ReplaceTable replaces or inserts table by name. It panics when an invalid id 235 // is provided. 236 func (it *IPTables) ReplaceTable(id TableID, table Table, ipv6 bool) { 237 it.replaceTable(id, table, ipv6, false /* force */) 238 } 239 240 // ForceReplaceTable replaces or inserts table by name. It panics when an invalid id 241 // is provided. It enables iptables even when the inserted table is all 242 // conditionless ACCEPT, skipping our optimization that disables iptables until 243 // they're modified. 244 func (it *IPTables) ForceReplaceTable(id TableID, table Table, ipv6 bool) { 245 it.replaceTable(id, table, ipv6, true /* force */) 246 } 247 248 func (it *IPTables) replaceTable(id TableID, table Table, ipv6, force bool) { 249 it.mu.Lock() 250 defer it.mu.Unlock() 251 252 // If iptables is being enabled, initialize the conntrack table and 253 // reaper. 254 if !it.modified { 255 // Don't do anything if the table is identical. 256 if ((ipv6 && reflect.DeepEqual(table, it.v6Tables[id])) || (!ipv6 && reflect.DeepEqual(table, it.v4Tables[id]))) && !force { 257 return 258 } 259 260 it.connections.init() 261 it.startReaper(reaperDelay) 262 } 263 it.modified = true 264 if ipv6 { 265 it.v6Tables[id] = table 266 } else { 267 it.v4Tables[id] = table 268 } 269 } 270 271 // A chainVerdict is what a table decides should be done with a packet. 272 type chainVerdict int 273 274 const ( 275 // chainAccept indicates the packet should continue through netstack. 276 chainAccept chainVerdict = iota 277 278 // chainDrop indicates the packet should be dropped. 279 chainDrop 280 281 // chainReturn indicates the packet should return to the calling chain 282 // or the underflow rule of a builtin chain. 283 chainReturn 284 ) 285 286 type checkTable struct { 287 fn checkTableFn 288 tableID TableID 289 table Table 290 } 291 292 // shouldSkipOrPopulateTables returns true iff IPTables should be skipped. 293 // 294 // If IPTables should not be skipped, tables will be updated with the 295 // specified table. 296 // 297 // This is called in the hot path even when iptables are disabled, so we ensure 298 // it does not allocate. We check recursively for heap allocations, but not for: 299 // - Stack splitting, which can allocate. 300 // - Calls to interfaces, which can allocate. 301 // - Calls to dynamic functions, which can allocate. 302 // 303 // +checkescape:hard 304 func (it *IPTables) shouldSkipOrPopulateTables(tables []checkTable, pkt *PacketBuffer) bool { 305 switch pkt.NetworkProtocolNumber { 306 case header.IPv4ProtocolNumber, header.IPv6ProtocolNumber: 307 default: 308 // IPTables only supports IPv4/IPv6. 309 return true 310 } 311 312 it.mu.RLock() 313 defer it.mu.RUnlock() 314 315 if !it.modified { 316 // Many users never configure iptables. Spare them the cost of rule 317 // traversal if rules have never been set. 318 return true 319 } 320 321 for i := range tables { 322 table := &tables[i] 323 table.table = it.getTableRLocked(table.tableID, pkt.NetworkProtocolNumber == header.IPv6ProtocolNumber) 324 } 325 return false 326 } 327 328 // CheckPrerouting performs the prerouting hook on the packet. 329 // 330 // Returns true iff the packet may continue traversing the stack; the packet 331 // must be dropped if false is returned. 332 // 333 // Precondition: The packet's network and transport header must be set. 334 // 335 // This is called in the hot path even when iptables are disabled, so we ensure 336 // that it does not allocate. Note that called functions (e.g. 337 // getConnAndUpdate) can allocate. 338 // TODO(b/233951539): checkescape fails on arm sometimes. Fix and re-add. 339 func (it *IPTables) CheckPrerouting(pkt *PacketBuffer, addressEP AddressableEndpoint, inNicName string) bool { 340 tables := [...]checkTable{ 341 { 342 fn: check, 343 tableID: MangleID, 344 }, 345 { 346 fn: checkNAT, 347 tableID: NATID, 348 }, 349 } 350 351 if it.shouldSkipOrPopulateTables(tables[:], pkt) { 352 return true 353 } 354 355 pkt.tuple = it.connections.getConnAndUpdate(pkt, false /* skipChecksumValidation */) 356 357 for _, table := range tables { 358 if !table.fn(it, table.table, Prerouting, pkt, nil /* route */, addressEP, inNicName, "" /* outNicName */) { 359 return false 360 } 361 } 362 363 return true 364 } 365 366 // CheckInput performs the input hook on the packet. 367 // 368 // Returns true iff the packet may continue traversing the stack; the packet 369 // must be dropped if false is returned. 370 // 371 // Precondition: The packet's network and transport header must be set. 372 // 373 // This is called in the hot path even when iptables are disabled, so we ensure 374 // that it does not allocate. Note that called functions (e.g. 375 // getConnAndUpdate) can allocate. 376 // TODO(b/233951539): checkescape fails on arm sometimes. Fix and re-add. 377 func (it *IPTables) CheckInput(pkt *PacketBuffer, inNicName string) bool { 378 tables := [...]checkTable{ 379 { 380 fn: checkNAT, 381 tableID: NATID, 382 }, 383 { 384 fn: check, 385 tableID: FilterID, 386 }, 387 } 388 389 if it.shouldSkipOrPopulateTables(tables[:], pkt) { 390 return true 391 } 392 393 for _, table := range tables { 394 if !table.fn(it, table.table, Input, pkt, nil /* route */, nil /* addressEP */, inNicName, "" /* outNicName */) { 395 return false 396 } 397 } 398 399 if t := pkt.tuple; t != nil { 400 pkt.tuple = nil 401 return t.conn.finalize() 402 } 403 return true 404 } 405 406 // CheckForward performs the forward hook on the packet. 407 // 408 // Returns true iff the packet may continue traversing the stack; the packet 409 // must be dropped if false is returned. 410 // 411 // Precondition: The packet's network and transport header must be set. 412 // 413 // This is called in the hot path even when iptables are disabled, so we ensure 414 // that it does not allocate. Note that called functions (e.g. 415 // getConnAndUpdate) can allocate. 416 // TODO(b/233951539): checkescape fails on arm sometimes. Fix and re-add. 417 func (it *IPTables) CheckForward(pkt *PacketBuffer, inNicName, outNicName string) bool { 418 tables := [...]checkTable{ 419 { 420 fn: check, 421 tableID: FilterID, 422 }, 423 } 424 425 if it.shouldSkipOrPopulateTables(tables[:], pkt) { 426 return true 427 } 428 429 for _, table := range tables { 430 if !table.fn(it, table.table, Forward, pkt, nil /* route */, nil /* addressEP */, inNicName, outNicName) { 431 return false 432 } 433 } 434 435 return true 436 } 437 438 // CheckOutput performs the output hook on the packet. 439 // 440 // Returns true iff the packet may continue traversing the stack; the packet 441 // must be dropped if false is returned. 442 // 443 // Precondition: The packet's network and transport header must be set. 444 // 445 // This is called in the hot path even when iptables are disabled, so we ensure 446 // that it does not allocate. Note that called functions (e.g. 447 // getConnAndUpdate) can allocate. 448 // TODO(b/233951539): checkescape fails on arm sometimes. Fix and re-add. 449 func (it *IPTables) CheckOutput(pkt *PacketBuffer, r *Route, outNicName string) bool { 450 tables := [...]checkTable{ 451 { 452 fn: check, 453 tableID: MangleID, 454 }, 455 { 456 fn: checkNAT, 457 tableID: NATID, 458 }, 459 { 460 fn: check, 461 tableID: FilterID, 462 }, 463 } 464 465 if it.shouldSkipOrPopulateTables(tables[:], pkt) { 466 return true 467 } 468 469 // We don't need to validate the checksum in the Output path: we can assume 470 // we calculate it correctly, plus checksumming may be deferred due to GSO. 471 pkt.tuple = it.connections.getConnAndUpdate(pkt, true /* skipChecksumValidation */) 472 473 for _, table := range tables { 474 if !table.fn(it, table.table, Output, pkt, r, nil /* addressEP */, "" /* inNicName */, outNicName) { 475 return false 476 } 477 } 478 479 return true 480 } 481 482 // CheckPostrouting performs the postrouting hook on the packet. 483 // 484 // Returns true iff the packet may continue traversing the stack; the packet 485 // must be dropped if false is returned. 486 // 487 // Precondition: The packet's network and transport header must be set. 488 // 489 // This is called in the hot path even when iptables are disabled, so we ensure 490 // that it does not allocate. Note that called functions (e.g. 491 // getConnAndUpdate) can allocate. 492 // TODO(b/233951539): checkescape fails on arm sometimes. Fix and re-add. 493 func (it *IPTables) CheckPostrouting(pkt *PacketBuffer, r *Route, addressEP AddressableEndpoint, outNicName string) bool { 494 tables := [...]checkTable{ 495 { 496 fn: check, 497 tableID: MangleID, 498 }, 499 { 500 fn: checkNAT, 501 tableID: NATID, 502 }, 503 } 504 505 if it.shouldSkipOrPopulateTables(tables[:], pkt) { 506 return true 507 } 508 509 for _, table := range tables { 510 if !table.fn(it, table.table, Postrouting, pkt, r, addressEP, "" /* inNicName */, outNicName) { 511 return false 512 } 513 } 514 515 if t := pkt.tuple; t != nil { 516 pkt.tuple = nil 517 return t.conn.finalize() 518 } 519 return true 520 } 521 522 // Note: this used to omit the *IPTables parameter, but doing so caused 523 // unnecessary allocations. 524 type checkTableFn func(it *IPTables, table Table, hook Hook, pkt *PacketBuffer, r *Route, addressEP AddressableEndpoint, inNicName, outNicName string) bool 525 526 func checkNAT(it *IPTables, table Table, hook Hook, pkt *PacketBuffer, r *Route, addressEP AddressableEndpoint, inNicName, outNicName string) bool { 527 return it.checkNAT(table, hook, pkt, r, addressEP, inNicName, outNicName) 528 } 529 530 // checkNAT runs the packet through the NAT table. 531 // 532 // See check. 533 func (it *IPTables) checkNAT(table Table, hook Hook, pkt *PacketBuffer, r *Route, addressEP AddressableEndpoint, inNicName, outNicName string) bool { 534 t := pkt.tuple 535 if t != nil && t.conn.handlePacket(pkt, hook, r) { 536 return true 537 } 538 539 if !it.check(table, hook, pkt, r, addressEP, inNicName, outNicName) { 540 return false 541 } 542 543 if t == nil { 544 return true 545 } 546 547 dnat, natDone := func() (bool, bool) { 548 switch hook { 549 case Prerouting, Output: 550 return true, pkt.dnatDone 551 case Input, Postrouting: 552 return false, pkt.snatDone 553 case Forward: 554 panic("should not attempt NAT in forwarding") 555 default: 556 panic(fmt.Sprintf("unhandled hook = %d", hook)) 557 } 558 }() 559 560 // Make sure the connection is NATed. 561 // 562 // If the packet was already NATed, the connection must be NATed. 563 if !natDone { 564 t.conn.maybePerformNoopNAT(pkt, hook, r, dnat) 565 } 566 567 return true 568 } 569 570 func check(it *IPTables, table Table, hook Hook, pkt *PacketBuffer, r *Route, addressEP AddressableEndpoint, inNicName, outNicName string) bool { 571 return it.check(table, hook, pkt, r, addressEP, inNicName, outNicName) 572 } 573 574 // check runs the packet through the rules in the specified table for the 575 // hook. It returns true if the packet should continue to traverse through the 576 // network stack or tables, or false when it must be dropped. 577 // 578 // Precondition: The packet's network and transport header must be set. 579 func (it *IPTables) check(table Table, hook Hook, pkt *PacketBuffer, r *Route, addressEP AddressableEndpoint, inNicName, outNicName string) bool { 580 ruleIdx := table.BuiltinChains[hook] 581 switch verdict := it.checkChain(hook, pkt, table, ruleIdx, r, addressEP, inNicName, outNicName); verdict { 582 // If the table returns Accept, move on to the next table. 583 case chainAccept: 584 return true 585 // The Drop verdict is final. 586 case chainDrop: 587 return false 588 case chainReturn: 589 // Any Return from a built-in chain means we have to 590 // call the underflow. 591 underflow := table.Rules[table.Underflows[hook]] 592 switch v, _ := underflow.Target.Action(pkt, hook, r, addressEP); v { 593 case RuleAccept: 594 return true 595 case RuleDrop: 596 return false 597 case RuleJump, RuleReturn: 598 panic("Underflows should only return RuleAccept or RuleDrop.") 599 default: 600 panic(fmt.Sprintf("Unknown verdict: %d", v)) 601 } 602 default: 603 panic(fmt.Sprintf("Unknown verdict %v.", verdict)) 604 } 605 } 606 607 // beforeSave is invoked by stateify. 608 func (it *IPTables) beforeSave() { 609 // Ensure the reaper exits cleanly. 610 it.reaper.Stop() 611 // Prevent others from modifying the connection table. 612 it.connections.mu.Lock() 613 } 614 615 // afterLoad is invoked by stateify. 616 func (it *IPTables) afterLoad(context.Context) { 617 it.startReaper(reaperDelay) 618 } 619 620 // startReaper periodically reaps timed out connections. 621 func (it *IPTables) startReaper(interval time.Duration) { 622 bucket := 0 623 it.reaper = it.connections.clock.AfterFunc(interval, func() { 624 bucket, interval = it.connections.reapUnused(bucket, interval) 625 it.reaper.Reset(interval) 626 }) 627 } 628 629 // Preconditions: 630 // - pkt is a IPv4 packet of at least length header.IPv4MinimumSize. 631 // - pkt.NetworkHeader is not nil. 632 func (it *IPTables) checkChain(hook Hook, pkt *PacketBuffer, table Table, ruleIdx int, r *Route, addressEP AddressableEndpoint, inNicName, outNicName string) chainVerdict { 633 // Start from ruleIdx and walk the list of rules until a rule gives us 634 // a verdict. 635 for ruleIdx < len(table.Rules) { 636 switch verdict, jumpTo := it.checkRule(hook, pkt, table, ruleIdx, r, addressEP, inNicName, outNicName); verdict { 637 case RuleAccept: 638 return chainAccept 639 640 case RuleDrop: 641 return chainDrop 642 643 case RuleReturn: 644 return chainReturn 645 646 case RuleJump: 647 // "Jumping" to the next rule just means we're 648 // continuing on down the list. 649 if jumpTo == ruleIdx+1 { 650 ruleIdx++ 651 continue 652 } 653 switch verdict := it.checkChain(hook, pkt, table, jumpTo, r, addressEP, inNicName, outNicName); verdict { 654 case chainAccept: 655 return chainAccept 656 case chainDrop: 657 return chainDrop 658 case chainReturn: 659 ruleIdx++ 660 continue 661 default: 662 panic(fmt.Sprintf("Unknown verdict: %d", verdict)) 663 } 664 665 default: 666 panic(fmt.Sprintf("Unknown verdict: %d", verdict)) 667 } 668 669 } 670 671 // We got through the entire table without a decision. Default to DROP 672 // for safety. 673 return chainDrop 674 } 675 676 // Preconditions: 677 // - pkt is a IPv4 packet of at least length header.IPv4MinimumSize. 678 // - pkt.NetworkHeader is not nil. 679 // 680 // * pkt is a IPv4 packet of at least length header.IPv4MinimumSize. 681 // * pkt.NetworkHeader is not nil. 682 func (it *IPTables) checkRule(hook Hook, pkt *PacketBuffer, table Table, ruleIdx int, r *Route, addressEP AddressableEndpoint, inNicName, outNicName string) (RuleVerdict, int) { 683 rule := table.Rules[ruleIdx] 684 685 // Check whether the packet matches the IP header filter. 686 if !rule.Filter.match(pkt, hook, inNicName, outNicName) { 687 // Continue on to the next rule. 688 return RuleJump, ruleIdx + 1 689 } 690 691 // Go through each rule matcher. If they all match, run 692 // the rule target. 693 for _, matcher := range rule.Matchers { 694 matches, hotdrop := matcher.Match(hook, pkt, inNicName, outNicName) 695 if hotdrop { 696 return RuleDrop, 0 697 } 698 if !matches { 699 // Continue on to the next rule. 700 return RuleJump, ruleIdx + 1 701 } 702 } 703 704 // All the matchers matched, so run the target. 705 return rule.Target.Action(pkt, hook, r, addressEP) 706 } 707 708 // OriginalDst returns the original destination of redirected connections. It 709 // returns an error if the connection doesn't exist or isn't redirected. 710 func (it *IPTables) OriginalDst(epID TransportEndpointID, netProto tcpip.NetworkProtocolNumber, transProto tcpip.TransportProtocolNumber) (tcpip.Address, uint16, tcpip.Error) { 711 it.mu.RLock() 712 defer it.mu.RUnlock() 713 if !it.modified { 714 return tcpip.Address{}, 0, &tcpip.ErrNotConnected{} 715 } 716 return it.connections.originalDst(epID, netProto, transProto) 717 }