github.com/cilium/cilium@v1.16.2/pkg/datapath/iptables/custom_chain.go (about)

     1  // SPDX-License-Identifier: Apache-2.0
     2  // Copyright Authors of Cilium
     3  
     4  package iptables
     5  
     6  import (
     7  	"fmt"
     8  	"strings"
     9  )
    10  
    11  type customChain struct {
    12  	name  string
    13  	table string
    14  	hook  string
    15  	ipv6  bool // ip6tables chain in addition to iptables chain
    16  }
    17  
    18  // ciliumChains is the list of custom iptables chain used by Cilium. Custom
    19  // chains are used to allow for simple replacements of all rules.
    20  //
    21  // WARNING: If you change or remove any of the feeder rules you have to ensure
    22  // that the old feeder rules is also removed on agent start, otherwise,
    23  // flushing and removing the custom chains will fail.
    24  var ciliumChains = []customChain{
    25  	{
    26  		name:  ciliumInputChain,
    27  		table: "filter",
    28  		hook:  "INPUT",
    29  		ipv6:  true,
    30  	},
    31  	{
    32  		name:  ciliumOutputChain,
    33  		table: "filter",
    34  		hook:  "OUTPUT",
    35  		ipv6:  true,
    36  	},
    37  	{
    38  		name:  ciliumOutputRawChain,
    39  		table: "raw",
    40  		hook:  "OUTPUT",
    41  		ipv6:  true,
    42  	},
    43  	{
    44  		name:  ciliumPostNatChain,
    45  		table: "nat",
    46  		hook:  "POSTROUTING",
    47  		ipv6:  true,
    48  	},
    49  	{
    50  		name:  ciliumOutputNatChain,
    51  		table: "nat",
    52  		hook:  "OUTPUT",
    53  	},
    54  	{
    55  		name:  ciliumPreNatChain,
    56  		table: "nat",
    57  		hook:  "PREROUTING",
    58  	},
    59  	{
    60  		name:  ciliumPostMangleChain,
    61  		table: "mangle",
    62  		hook:  "POSTROUTING",
    63  	},
    64  	{
    65  		name:  ciliumPreMangleChain,
    66  		table: "mangle",
    67  		hook:  "PREROUTING",
    68  		ipv6:  true,
    69  	},
    70  	{
    71  		name:  ciliumPreRawChain,
    72  		table: "raw",
    73  		hook:  "PREROUTING",
    74  		ipv6:  true,
    75  	},
    76  	{
    77  		name:  ciliumForwardChain,
    78  		table: "filter",
    79  		hook:  "FORWARD",
    80  		ipv6:  true,
    81  	},
    82  }
    83  
    84  func (c *customChain) exists(prog runnable) (bool, error) {
    85  	args := []string{"-t", c.table, "-S", c.name}
    86  
    87  	output, err := prog.runProgOutput(args)
    88  	if err != nil {
    89  		if strings.Contains(err.Error(), "No chain/target/match by that name.") {
    90  			return false, nil
    91  		}
    92  
    93  		// with iptables-nft >= 1.8.7, when we try to list the rules of a non existing
    94  		// chain, the command will return an error in the format:
    95  		//
    96  		//     chain `$chain' in table `$chain' is incompatible, use 'nft' tool.
    97  		//
    98  		// rather than the usual one:
    99  		//
   100  		//     No chain/target/match by that name.
   101  		if strings.Contains(err.Error(), fmt.Sprintf("chain `%s' in table `%s' is incompatible, use 'nft' tool.", c.name, c.table)) {
   102  			return false, nil
   103  		}
   104  
   105  		return false, fmt.Errorf("unable to list %s chain: %s (%w)", c.name, string(output), err)
   106  	}
   107  
   108  	return true, nil
   109  }
   110  
   111  func (c *customChain) doAdd(prog runnable) error {
   112  	args := []string{"-t", c.table, "-N", c.name}
   113  
   114  	output, err := prog.runProgOutput(args)
   115  	if err != nil {
   116  		return fmt.Errorf("unable to add %s chain: %s (%w)", c.name, string(output), err)
   117  	}
   118  
   119  	return nil
   120  }
   121  
   122  func (c *customChain) add(ipv4, ipv6 bool) error {
   123  	if ipv4 {
   124  		if err := c.doAdd(ip4tables); err != nil {
   125  			return err
   126  		}
   127  	}
   128  	if ipv6 && c.ipv6 {
   129  		if err := c.doAdd(ip6tables); err != nil {
   130  			return err
   131  		}
   132  	}
   133  
   134  	return nil
   135  }
   136  
   137  func (c *customChain) doRename(prog runnable, newName string) error {
   138  	if exists, err := c.exists(prog); err != nil {
   139  		return err
   140  	} else if !exists {
   141  		return nil
   142  	}
   143  
   144  	args := []string{"-t", c.table, "-E", c.name, newName}
   145  
   146  	output, err := prog.runProgOutput(args)
   147  	if err != nil {
   148  		return fmt.Errorf("unable to rename %s chain to %s: %s (%w)", c.name, newName, string(output), err)
   149  	}
   150  
   151  	return nil
   152  }
   153  
   154  func (c *customChain) rename(ipv4, ipv6 bool, name string) error {
   155  	if ipv4 {
   156  		if err := c.doRename(ip4tables, name); err != nil {
   157  			return err
   158  		}
   159  	}
   160  	if ipv6 && c.ipv6 {
   161  		if err := c.doRename(ip6tables, name); err != nil {
   162  			return nil
   163  		}
   164  	}
   165  
   166  	return nil
   167  }
   168  
   169  func (c *customChain) doRemove(prog iptablesInterface) error {
   170  	if exists, err := c.exists(prog); err != nil {
   171  		return err
   172  	} else if !exists {
   173  		return nil
   174  	}
   175  
   176  	args := []string{"-t", c.table, "-F", c.name}
   177  
   178  	output, err := prog.runProgOutput(args)
   179  	if err != nil {
   180  		return fmt.Errorf("unable to flush %s chain: %s (%w)", c.name, string(output), err)
   181  	}
   182  
   183  	args = []string{"-t", c.table, "-X", c.name}
   184  
   185  	output, err = prog.runProgOutput(args)
   186  	if err != nil {
   187  		return fmt.Errorf("unable to remove %s chain: %s (%w)", c.name, string(output), err)
   188  	}
   189  
   190  	return nil
   191  }
   192  
   193  func (c *customChain) remove(ipv4, ipv6 bool) error {
   194  	if ipv4 {
   195  		if err := c.doRemove(ip4tables); err != nil {
   196  			return err
   197  		}
   198  	}
   199  	if ipv6 && c.ipv6 {
   200  		if err := c.doRemove(ip6tables); err != nil {
   201  			return err
   202  		}
   203  	}
   204  
   205  	return nil
   206  }
   207  
   208  func (c *customChain) doInstallFeeder(prog iptablesInterface, prepend bool) error {
   209  	installMode := "-A"
   210  	if prepend {
   211  		installMode = "-I"
   212  	}
   213  
   214  	feedRule := []string{"-m", "comment", "--comment", feederDescription + " " + c.name, "-j", c.name}
   215  	args := append([]string{"-t", c.table, installMode, c.hook}, feedRule...)
   216  
   217  	output, err := prog.runProgOutput(args)
   218  	if err != nil {
   219  		return fmt.Errorf("unable to install feeder rule for %s chain: %s (%w)", c.name, string(output), err)
   220  	}
   221  
   222  	return nil
   223  }
   224  
   225  func (c *customChain) installFeeder(ipv4, ipv6, prepend bool) error {
   226  	if ipv4 {
   227  		if err := c.doInstallFeeder(ip4tables, prepend); err != nil {
   228  			return err
   229  		}
   230  	}
   231  	if ipv6 && c.ipv6 {
   232  		if err := c.doInstallFeeder(ip6tables, prepend); err != nil {
   233  			return err
   234  		}
   235  	}
   236  	return nil
   237  }