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 }