| /* |
| Copyright 2016 The Kubernetes Authors. |
| |
| Licensed under the Apache License, Version 2.0 (the "License"); |
| you may not use this file except in compliance with the License. |
| You may obtain a copy of the License at |
| |
| http://www.apache.org/licenses/LICENSE-2.0 |
| |
| Unless required by applicable law or agreed to in writing, software |
| distributed under the License is distributed on an "AS IS" BASIS, |
| WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| See the License for the specific language governing permissions and |
| limitations under the License. |
| */ |
| |
| package hostport |
| |
| import ( |
| "bytes" |
| "fmt" |
| "net" |
| "strings" |
| |
| "k8s.io/apimachinery/pkg/util/sets" |
| utiliptables "k8s.io/kubernetes/pkg/util/iptables" |
| ) |
| |
| type fakeChain struct { |
| name utiliptables.Chain |
| rules []string |
| } |
| |
| type fakeTable struct { |
| name utiliptables.Table |
| chains map[string]*fakeChain |
| } |
| |
| type fakeIPTables struct { |
| tables map[string]*fakeTable |
| builtinChains map[string]sets.String |
| } |
| |
| func NewFakeIPTables() *fakeIPTables { |
| return &fakeIPTables{ |
| tables: make(map[string]*fakeTable, 0), |
| builtinChains: map[string]sets.String{ |
| string(utiliptables.TableFilter): sets.NewString("INPUT", "FORWARD", "OUTPUT"), |
| string(utiliptables.TableNAT): sets.NewString("PREROUTING", "INPUT", "OUTPUT", "POSTROUTING"), |
| string(utiliptables.TableMangle): sets.NewString("PREROUTING", "INPUT", "FORWARD", "OUTPUT", "POSTROUTING"), |
| }, |
| } |
| } |
| |
| func (f *fakeIPTables) GetVersion() (string, error) { |
| return "1.4.21", nil |
| } |
| |
| func (f *fakeIPTables) getTable(tableName utiliptables.Table) (*fakeTable, error) { |
| table, ok := f.tables[string(tableName)] |
| if !ok { |
| return nil, fmt.Errorf("Table %s does not exist", tableName) |
| } |
| return table, nil |
| } |
| |
| func (f *fakeIPTables) getChain(tableName utiliptables.Table, chainName utiliptables.Chain) (*fakeTable, *fakeChain, error) { |
| table, err := f.getTable(tableName) |
| if err != nil { |
| return nil, nil, err |
| } |
| |
| chain, ok := table.chains[string(chainName)] |
| if !ok { |
| return table, nil, fmt.Errorf("Chain %s/%s does not exist", tableName, chainName) |
| } |
| |
| return table, chain, nil |
| } |
| |
| func (f *fakeIPTables) ensureChain(tableName utiliptables.Table, chainName utiliptables.Chain) (bool, *fakeChain) { |
| table, chain, err := f.getChain(tableName, chainName) |
| if err != nil { |
| // either table or table+chain don't exist yet |
| if table == nil { |
| table = &fakeTable{ |
| name: tableName, |
| chains: make(map[string]*fakeChain), |
| } |
| f.tables[string(tableName)] = table |
| } |
| chain := &fakeChain{ |
| name: chainName, |
| rules: make([]string, 0), |
| } |
| table.chains[string(chainName)] = chain |
| return false, chain |
| } |
| return true, chain |
| } |
| |
| func (f *fakeIPTables) EnsureChain(tableName utiliptables.Table, chainName utiliptables.Chain) (bool, error) { |
| existed, _ := f.ensureChain(tableName, chainName) |
| return existed, nil |
| } |
| |
| func (f *fakeIPTables) FlushChain(tableName utiliptables.Table, chainName utiliptables.Chain) error { |
| _, chain, err := f.getChain(tableName, chainName) |
| if err != nil { |
| return err |
| } |
| chain.rules = make([]string, 0) |
| return nil |
| } |
| |
| func (f *fakeIPTables) DeleteChain(tableName utiliptables.Table, chainName utiliptables.Chain) error { |
| table, _, err := f.getChain(tableName, chainName) |
| if err != nil { |
| return err |
| } |
| delete(table.chains, string(chainName)) |
| return nil |
| } |
| |
| // Returns index of rule in array; < 0 if rule is not found |
| func findRule(chain *fakeChain, rule string) int { |
| for i, candidate := range chain.rules { |
| if rule == candidate { |
| return i |
| } |
| } |
| return -1 |
| } |
| |
| func (f *fakeIPTables) ensureRule(position utiliptables.RulePosition, tableName utiliptables.Table, chainName utiliptables.Chain, rule string) (bool, error) { |
| _, chain, err := f.getChain(tableName, chainName) |
| if err != nil { |
| _, chain = f.ensureChain(tableName, chainName) |
| } |
| |
| rule, err = normalizeRule(rule) |
| if err != nil { |
| return false, err |
| } |
| ruleIdx := findRule(chain, rule) |
| if ruleIdx >= 0 { |
| return true, nil |
| } |
| |
| if position == utiliptables.Prepend { |
| chain.rules = append([]string{rule}, chain.rules...) |
| } else if position == utiliptables.Append { |
| chain.rules = append(chain.rules, rule) |
| } else { |
| return false, fmt.Errorf("Unknown position argument %q", position) |
| } |
| |
| return false, nil |
| } |
| |
| func normalizeRule(rule string) (string, error) { |
| normalized := "" |
| remaining := strings.TrimSpace(rule) |
| for { |
| var end int |
| |
| if strings.HasPrefix(remaining, "--to-destination=") { |
| remaining = strings.Replace(remaining, "=", " ", 1) |
| } |
| |
| if remaining[0] == '"' { |
| end = strings.Index(remaining[1:], "\"") |
| if end < 0 { |
| return "", fmt.Errorf("Invalid rule syntax: mismatched quotes") |
| } |
| end += 2 |
| } else { |
| end = strings.Index(remaining, " ") |
| if end < 0 { |
| end = len(remaining) |
| } |
| } |
| arg := remaining[:end] |
| |
| // Normalize un-prefixed IP addresses like iptables does |
| if net.ParseIP(arg) != nil { |
| arg = arg + "/32" |
| } |
| |
| if len(normalized) > 0 { |
| normalized += " " |
| } |
| normalized += strings.TrimSpace(arg) |
| if len(remaining) == end { |
| break |
| } |
| remaining = remaining[end+1:] |
| } |
| return normalized, nil |
| } |
| |
| func (f *fakeIPTables) EnsureRule(position utiliptables.RulePosition, tableName utiliptables.Table, chainName utiliptables.Chain, args ...string) (bool, error) { |
| ruleArgs := make([]string, 0) |
| for _, arg := range args { |
| // quote args with internal spaces (like comments) |
| if strings.Index(arg, " ") >= 0 { |
| arg = fmt.Sprintf("\"%s\"", arg) |
| } |
| ruleArgs = append(ruleArgs, arg) |
| } |
| return f.ensureRule(position, tableName, chainName, strings.Join(ruleArgs, " ")) |
| } |
| |
| func (f *fakeIPTables) DeleteRule(tableName utiliptables.Table, chainName utiliptables.Chain, args ...string) error { |
| _, chain, err := f.getChain(tableName, chainName) |
| if err == nil { |
| rule := strings.Join(args, " ") |
| ruleIdx := findRule(chain, rule) |
| if ruleIdx < 0 { |
| return nil |
| } |
| chain.rules = append(chain.rules[:ruleIdx], chain.rules[ruleIdx+1:]...) |
| } |
| return nil |
| } |
| |
| func (f *fakeIPTables) IsIpv6() bool { |
| return false |
| } |
| |
| func saveChain(chain *fakeChain, data *bytes.Buffer) { |
| for _, rule := range chain.rules { |
| data.WriteString(fmt.Sprintf("-A %s %s\n", chain.name, rule)) |
| } |
| } |
| |
| func (f *fakeIPTables) SaveInto(tableName utiliptables.Table, buffer *bytes.Buffer) error { |
| table, err := f.getTable(tableName) |
| if err != nil { |
| return err |
| } |
| |
| buffer.WriteString(fmt.Sprintf("*%s\n", table.name)) |
| |
| rules := bytes.NewBuffer(nil) |
| for _, chain := range table.chains { |
| buffer.WriteString(fmt.Sprintf(":%s - [0:0]\n", string(chain.name))) |
| saveChain(chain, rules) |
| } |
| buffer.Write(rules.Bytes()) |
| buffer.WriteString("COMMIT\n") |
| return nil |
| } |
| |
| func (f *fakeIPTables) restore(restoreTableName utiliptables.Table, data []byte, flush utiliptables.FlushFlag) error { |
| allLines := string(data) |
| buf := bytes.NewBuffer(data) |
| var tableName utiliptables.Table |
| for { |
| line, err := buf.ReadString('\n') |
| if err != nil { |
| break |
| } |
| if line[0] == '#' { |
| continue |
| } |
| |
| line = strings.TrimSuffix(line, "\n") |
| if strings.HasPrefix(line, "*") { |
| tableName = utiliptables.Table(line[1:]) |
| } |
| if tableName != "" { |
| if restoreTableName != "" && restoreTableName != tableName { |
| continue |
| } |
| if strings.HasPrefix(line, ":") { |
| chainName := utiliptables.Chain(strings.Split(line[1:], " ")[0]) |
| if flush == utiliptables.FlushTables { |
| table, chain, _ := f.getChain(tableName, chainName) |
| if chain != nil { |
| delete(table.chains, string(chainName)) |
| } |
| } |
| _, _ = f.ensureChain(tableName, chainName) |
| // The --noflush option for iptables-restore doesn't work for user-defined chains, only builtin chains. |
| // We should flush user-defined chains if the chain is not to be deleted |
| if !f.isBuiltinChain(tableName, chainName) && !strings.Contains(allLines, "-X "+string(chainName)) { |
| if err := f.FlushChain(tableName, chainName); err != nil { |
| return err |
| } |
| } |
| } else if strings.HasPrefix(line, "-A") { |
| parts := strings.Split(line, " ") |
| if len(parts) < 3 { |
| return fmt.Errorf("Invalid iptables rule '%s'", line) |
| } |
| chainName := utiliptables.Chain(parts[1]) |
| rule := strings.TrimPrefix(line, fmt.Sprintf("-A %s ", chainName)) |
| _, err := f.ensureRule(utiliptables.Append, tableName, chainName, rule) |
| if err != nil { |
| return err |
| } |
| } else if strings.HasPrefix(line, "-I") { |
| parts := strings.Split(line, " ") |
| if len(parts) < 3 { |
| return fmt.Errorf("Invalid iptables rule '%s'", line) |
| } |
| chainName := utiliptables.Chain(parts[1]) |
| rule := strings.TrimPrefix(line, fmt.Sprintf("-I %s ", chainName)) |
| _, err := f.ensureRule(utiliptables.Prepend, tableName, chainName, rule) |
| if err != nil { |
| return err |
| } |
| } else if strings.HasPrefix(line, "-X") { |
| parts := strings.Split(line, " ") |
| if len(parts) < 2 { |
| return fmt.Errorf("Invalid iptables rule '%s'", line) |
| } |
| if err := f.DeleteChain(tableName, utiliptables.Chain(parts[1])); err != nil { |
| return err |
| } |
| } else if line == "COMMIT" { |
| if restoreTableName == tableName { |
| return nil |
| } |
| tableName = "" |
| } |
| } |
| } |
| |
| return nil |
| } |
| |
| func (f *fakeIPTables) Restore(tableName utiliptables.Table, data []byte, flush utiliptables.FlushFlag, counters utiliptables.RestoreCountersFlag) error { |
| return f.restore(tableName, data, flush) |
| } |
| |
| func (f *fakeIPTables) RestoreAll(data []byte, flush utiliptables.FlushFlag, counters utiliptables.RestoreCountersFlag) error { |
| return f.restore("", data, flush) |
| } |
| |
| func (f *fakeIPTables) AddReloadFunc(reloadFunc func()) { |
| } |
| |
| func (f *fakeIPTables) Destroy() { |
| } |
| |
| func (f *fakeIPTables) isBuiltinChain(tableName utiliptables.Table, chainName utiliptables.Chain) bool { |
| if builtinChains, ok := f.builtinChains[string(tableName)]; ok && builtinChains.Has(string(chainName)) { |
| return true |
| } |
| return false |
| } |