iptables.go raw
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 // +checkescape
339 func (it *IPTables) CheckPrerouting(pkt *PacketBuffer, addressEP AddressableEndpoint, inNicName string) bool {
340 tables := [...]checkTable{ // escapes: on arm this causes an allocation.
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 // +checkescape
377 func (it *IPTables) CheckInput(pkt *PacketBuffer, inNicName string) bool {
378 tables := [...]checkTable{ // escapes: on arm this causes an allocation.
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 // +checkescape
417 func (it *IPTables) CheckForward(pkt *PacketBuffer, inNicName, outNicName string) bool {
418 tables := [...]checkTable{ // escapes: on arm this causes an allocation.
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 // +checkescape
449 func (it *IPTables) CheckOutput(pkt *PacketBuffer, r *Route, outNicName string) bool {
450 tables := [...]checkTable{ // escapes: on arm this causes an allocation.
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 // +checkescape
493 func (it *IPTables) CheckPostrouting(pkt *PacketBuffer, r *Route, addressEP AddressableEndpoint, outNicName string) bool {
494 tables := [...]checkTable{ // escapes: on arm this causes an allocation.
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 }
718