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