k8s.io/kubernetes@v1.29.3/pkg/util/iptables/testing/fake.go (about)

     1  /*
     2  Copyright 2015 The Kubernetes Authors.
     3  
     4  Licensed under the Apache License, Version 2.0 (the "License");
     5  you may not use this file except in compliance with the License.
     6  You may obtain a copy of the License at
     7  
     8      http://www.apache.org/licenses/LICENSE-2.0
     9  
    10  Unless required by applicable law or agreed to in writing, software
    11  distributed under the License is distributed on an "AS IS" BASIS,
    12  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13  See the License for the specific language governing permissions and
    14  limitations under the License.
    15  */
    16  
    17  package testing
    18  
    19  import (
    20  	"bytes"
    21  	"fmt"
    22  	"strings"
    23  	"time"
    24  
    25  	"k8s.io/apimachinery/pkg/util/sets"
    26  	"k8s.io/kubernetes/pkg/util/iptables"
    27  )
    28  
    29  // FakeIPTables is no-op implementation of iptables Interface.
    30  type FakeIPTables struct {
    31  	hasRandomFully bool
    32  	protocol       iptables.Protocol
    33  
    34  	Dump *IPTablesDump
    35  }
    36  
    37  // NewFake returns a no-op iptables.Interface
    38  func NewFake() *FakeIPTables {
    39  	f := &FakeIPTables{
    40  		protocol: iptables.ProtocolIPv4,
    41  		Dump: &IPTablesDump{
    42  			Tables: []Table{
    43  				{
    44  					Name: iptables.TableNAT,
    45  					Chains: []Chain{
    46  						{Name: iptables.ChainPrerouting},
    47  						{Name: iptables.ChainInput},
    48  						{Name: iptables.ChainOutput},
    49  						{Name: iptables.ChainPostrouting},
    50  					},
    51  				},
    52  				{
    53  					Name: iptables.TableFilter,
    54  					Chains: []Chain{
    55  						{Name: iptables.ChainInput},
    56  						{Name: iptables.ChainForward},
    57  						{Name: iptables.ChainOutput},
    58  					},
    59  				},
    60  				{
    61  					Name:   iptables.TableMangle,
    62  					Chains: []Chain{},
    63  				},
    64  			},
    65  		},
    66  	}
    67  
    68  	return f
    69  }
    70  
    71  // NewIPv6Fake returns a no-op iptables.Interface with IsIPv6() == true
    72  func NewIPv6Fake() *FakeIPTables {
    73  	f := NewFake()
    74  	f.protocol = iptables.ProtocolIPv6
    75  	return f
    76  }
    77  
    78  // SetHasRandomFully sets f's return value for HasRandomFully()
    79  func (f *FakeIPTables) SetHasRandomFully(can bool) *FakeIPTables {
    80  	f.hasRandomFully = can
    81  	return f
    82  }
    83  
    84  // EnsureChain is part of iptables.Interface
    85  func (f *FakeIPTables) EnsureChain(table iptables.Table, chain iptables.Chain) (bool, error) {
    86  	t, err := f.Dump.GetTable(table)
    87  	if err != nil {
    88  		return false, err
    89  	}
    90  	if c, _ := f.Dump.GetChain(table, chain); c != nil {
    91  		return true, nil
    92  	}
    93  	t.Chains = append(t.Chains, Chain{Name: chain})
    94  	return false, nil
    95  }
    96  
    97  // FlushChain is part of iptables.Interface
    98  func (f *FakeIPTables) FlushChain(table iptables.Table, chain iptables.Chain) error {
    99  	if c, _ := f.Dump.GetChain(table, chain); c != nil {
   100  		c.Rules = nil
   101  	}
   102  	return nil
   103  }
   104  
   105  // DeleteChain is part of iptables.Interface
   106  func (f *FakeIPTables) DeleteChain(table iptables.Table, chain iptables.Chain) error {
   107  	t, err := f.Dump.GetTable(table)
   108  	if err != nil {
   109  		return err
   110  	}
   111  	for i := range t.Chains {
   112  		if t.Chains[i].Name == chain {
   113  			t.Chains = append(t.Chains[:i], t.Chains[i+1:]...)
   114  			return nil
   115  		}
   116  	}
   117  	return nil
   118  }
   119  
   120  // ChainExists is part of iptables.Interface
   121  func (f *FakeIPTables) ChainExists(table iptables.Table, chain iptables.Chain) (bool, error) {
   122  	if _, err := f.Dump.GetTable(table); err != nil {
   123  		return false, err
   124  	}
   125  	if c, _ := f.Dump.GetChain(table, chain); c != nil {
   126  		return true, nil
   127  	}
   128  	return false, nil
   129  }
   130  
   131  // EnsureRule is part of iptables.Interface
   132  func (f *FakeIPTables) EnsureRule(position iptables.RulePosition, table iptables.Table, chain iptables.Chain, args ...string) (bool, error) {
   133  	c, err := f.Dump.GetChain(table, chain)
   134  	if err != nil {
   135  		return false, err
   136  	}
   137  
   138  	rule := "-A " + string(chain) + " " + strings.Join(args, " ")
   139  	for _, r := range c.Rules {
   140  		if r.Raw == rule {
   141  			return true, nil
   142  		}
   143  	}
   144  
   145  	parsed, err := ParseRule(rule, false)
   146  	if err != nil {
   147  		return false, err
   148  	}
   149  
   150  	if position == iptables.Append {
   151  		c.Rules = append(c.Rules, parsed)
   152  	} else {
   153  		c.Rules = append([]*Rule{parsed}, c.Rules...)
   154  	}
   155  	return false, nil
   156  }
   157  
   158  // DeleteRule is part of iptables.Interface
   159  func (f *FakeIPTables) DeleteRule(table iptables.Table, chain iptables.Chain, args ...string) error {
   160  	c, err := f.Dump.GetChain(table, chain)
   161  	if err != nil {
   162  		return err
   163  	}
   164  
   165  	rule := "-A " + string(chain) + " " + strings.Join(args, " ")
   166  	for i, r := range c.Rules {
   167  		if r.Raw == rule {
   168  			c.Rules = append(c.Rules[:i], c.Rules[i+1:]...)
   169  			break
   170  		}
   171  	}
   172  	return nil
   173  }
   174  
   175  // IsIPv6 is part of iptables.Interface
   176  func (f *FakeIPTables) IsIPv6() bool {
   177  	return f.protocol == iptables.ProtocolIPv6
   178  }
   179  
   180  // Protocol is part of iptables.Interface
   181  func (f *FakeIPTables) Protocol() iptables.Protocol {
   182  	return f.protocol
   183  }
   184  
   185  func (f *FakeIPTables) saveTable(table iptables.Table, buffer *bytes.Buffer) error {
   186  	t, err := f.Dump.GetTable(table)
   187  	if err != nil {
   188  		return err
   189  	}
   190  
   191  	fmt.Fprintf(buffer, "*%s\n", table)
   192  	for _, c := range t.Chains {
   193  		fmt.Fprintf(buffer, ":%s - [%d:%d]\n", c.Name, c.Packets, c.Bytes)
   194  	}
   195  	for _, c := range t.Chains {
   196  		for _, r := range c.Rules {
   197  			fmt.Fprintf(buffer, "%s\n", r.Raw)
   198  		}
   199  	}
   200  	fmt.Fprintf(buffer, "COMMIT\n")
   201  	return nil
   202  }
   203  
   204  // SaveInto is part of iptables.Interface
   205  func (f *FakeIPTables) SaveInto(table iptables.Table, buffer *bytes.Buffer) error {
   206  	if table == "" {
   207  		// As a secret extension to the API, FakeIPTables treats table="" as
   208  		// meaning "all tables"
   209  		for i := range f.Dump.Tables {
   210  			err := f.saveTable(f.Dump.Tables[i].Name, buffer)
   211  			if err != nil {
   212  				return err
   213  			}
   214  		}
   215  		return nil
   216  	}
   217  
   218  	return f.saveTable(table, buffer)
   219  }
   220  
   221  // This is not a complete list but it's enough to pass the unit tests
   222  var builtinTargets = sets.NewString("ACCEPT", "DROP", "RETURN", "REJECT", "DNAT", "SNAT", "MASQUERADE", "MARK")
   223  
   224  func (f *FakeIPTables) restoreTable(newDump *IPTablesDump, newTable *Table, flush iptables.FlushFlag, counters iptables.RestoreCountersFlag) error {
   225  	oldTable, err := f.Dump.GetTable(newTable.Name)
   226  	if err != nil {
   227  		return err
   228  	}
   229  
   230  	backupChains := make([]Chain, len(oldTable.Chains))
   231  	copy(backupChains, oldTable.Chains)
   232  
   233  	// Update internal state
   234  	if flush == iptables.FlushTables {
   235  		oldTable.Chains = make([]Chain, 0, len(newTable.Chains))
   236  	}
   237  	for _, newChain := range newTable.Chains {
   238  		oldChain, _ := f.Dump.GetChain(newTable.Name, newChain.Name)
   239  		switch {
   240  		case oldChain == nil && newChain.Deleted:
   241  			// no-op
   242  		case oldChain == nil && !newChain.Deleted:
   243  			oldTable.Chains = append(oldTable.Chains, newChain)
   244  		case oldChain != nil && newChain.Deleted:
   245  			_ = f.DeleteChain(newTable.Name, newChain.Name)
   246  		case oldChain != nil && !newChain.Deleted:
   247  			// replace old data with new
   248  			oldChain.Rules = newChain.Rules
   249  			if counters == iptables.RestoreCounters {
   250  				oldChain.Packets = newChain.Packets
   251  				oldChain.Bytes = newChain.Bytes
   252  			}
   253  		}
   254  	}
   255  
   256  	// Now check that all old/new jumps are valid
   257  	for _, chain := range oldTable.Chains {
   258  		for _, rule := range chain.Rules {
   259  			if rule.Jump == nil {
   260  				continue
   261  			}
   262  			if builtinTargets.Has(rule.Jump.Value) {
   263  				continue
   264  			}
   265  
   266  			jumpedChain, _ := f.Dump.GetChain(oldTable.Name, iptables.Chain(rule.Jump.Value))
   267  			if jumpedChain == nil {
   268  				newChain, _ := newDump.GetChain(oldTable.Name, iptables.Chain(rule.Jump.Value))
   269  				if newChain != nil {
   270  					// rule is an old rule that jumped to a chain which
   271  					// was deleted by newDump.
   272  					oldTable.Chains = backupChains
   273  					return fmt.Errorf("deleted chain %q is referenced by existing rules", newChain.Name)
   274  				} else {
   275  					// rule is a new rule that jumped to a chain that was
   276  					// neither created nor pre-existing
   277  					oldTable.Chains = backupChains
   278  					return fmt.Errorf("rule %q jumps to a non-existent chain", rule.Raw)
   279  				}
   280  			}
   281  		}
   282  	}
   283  
   284  	return nil
   285  }
   286  
   287  // Restore is part of iptables.Interface
   288  func (f *FakeIPTables) Restore(table iptables.Table, data []byte, flush iptables.FlushFlag, counters iptables.RestoreCountersFlag) error {
   289  	dump, err := ParseIPTablesDump(string(data))
   290  	if err != nil {
   291  		return err
   292  	}
   293  
   294  	newTable, err := dump.GetTable(table)
   295  	if err != nil {
   296  		return err
   297  	}
   298  
   299  	return f.restoreTable(dump, newTable, flush, counters)
   300  }
   301  
   302  // RestoreAll is part of iptables.Interface
   303  func (f *FakeIPTables) RestoreAll(data []byte, flush iptables.FlushFlag, counters iptables.RestoreCountersFlag) error {
   304  	dump, err := ParseIPTablesDump(string(data))
   305  	if err != nil {
   306  		return err
   307  	}
   308  
   309  	for i := range dump.Tables {
   310  		err = f.restoreTable(dump, &dump.Tables[i], flush, counters)
   311  		if err != nil {
   312  			return err
   313  		}
   314  	}
   315  	return nil
   316  }
   317  
   318  // Monitor is part of iptables.Interface
   319  func (f *FakeIPTables) Monitor(canary iptables.Chain, tables []iptables.Table, reloadFunc func(), interval time.Duration, stopCh <-chan struct{}) {
   320  }
   321  
   322  // HasRandomFully is part of iptables.Interface
   323  func (f *FakeIPTables) HasRandomFully() bool {
   324  	return f.hasRandomFully
   325  }
   326  
   327  func (f *FakeIPTables) Present() bool {
   328  	return true
   329  }
   330  
   331  var _ = iptables.Interface(&FakeIPTables{})