github.com/adityamillind98/moby@v23.0.0-rc.4+incompatible/libnetwork/iptables/iptables_test.go (about)

     1  //go:build linux
     2  // +build linux
     3  
     4  package iptables
     5  
     6  import (
     7  	"net"
     8  	"os/exec"
     9  	"strconv"
    10  	"strings"
    11  	"testing"
    12  
    13  	"golang.org/x/sync/errgroup"
    14  )
    15  
    16  const (
    17  	chainName  = "DOCKEREST"
    18  	bridgeName = "lo"
    19  )
    20  
    21  func createNewChain(t *testing.T) (*IPTable, *ChainInfo, *ChainInfo) {
    22  	t.Helper()
    23  	iptable := GetIptable(IPv4)
    24  
    25  	natChain, err := iptable.NewChain(chainName, Nat, false)
    26  	if err != nil {
    27  		t.Fatal(err)
    28  	}
    29  	err = iptable.ProgramChain(natChain, bridgeName, false, true)
    30  	if err != nil {
    31  		t.Fatal(err)
    32  	}
    33  
    34  	filterChain, err := iptable.NewChain(chainName, Filter, false)
    35  	if err != nil {
    36  		t.Fatal(err)
    37  	}
    38  	err = iptable.ProgramChain(filterChain, bridgeName, false, true)
    39  	if err != nil {
    40  		t.Fatal(err)
    41  	}
    42  
    43  	return iptable, natChain, filterChain
    44  }
    45  
    46  func TestNewChain(t *testing.T) {
    47  	createNewChain(t)
    48  }
    49  
    50  func TestForward(t *testing.T) {
    51  	iptable, natChain, filterChain := createNewChain(t)
    52  
    53  	ip := net.ParseIP("192.168.1.1")
    54  	port := 1234
    55  	dstAddr := "172.17.0.1"
    56  	dstPort := 4321
    57  	proto := "tcp"
    58  
    59  	err := natChain.Forward(Insert, ip, port, proto, dstAddr, dstPort, bridgeName)
    60  	if err != nil {
    61  		t.Fatal(err)
    62  	}
    63  
    64  	dnatRule := []string{
    65  		"-d", ip.String(),
    66  		"-p", proto,
    67  		"--dport", strconv.Itoa(port),
    68  		"-j", "DNAT",
    69  		"--to-destination", dstAddr + ":" + strconv.Itoa(dstPort),
    70  		"!", "-i", bridgeName,
    71  	}
    72  
    73  	if !iptable.Exists(natChain.Table, natChain.Name, dnatRule...) {
    74  		t.Fatal("DNAT rule does not exist")
    75  	}
    76  
    77  	filterRule := []string{
    78  		"!", "-i", bridgeName,
    79  		"-o", bridgeName,
    80  		"-d", dstAddr,
    81  		"-p", proto,
    82  		"--dport", strconv.Itoa(dstPort),
    83  		"-j", "ACCEPT",
    84  	}
    85  
    86  	if !iptable.Exists(filterChain.Table, filterChain.Name, filterRule...) {
    87  		t.Fatal("filter rule does not exist")
    88  	}
    89  
    90  	masqRule := []string{
    91  		"-d", dstAddr,
    92  		"-s", dstAddr,
    93  		"-p", proto,
    94  		"--dport", strconv.Itoa(dstPort),
    95  		"-j", "MASQUERADE",
    96  	}
    97  
    98  	if !iptable.Exists(natChain.Table, "POSTROUTING", masqRule...) {
    99  		t.Fatal("MASQUERADE rule does not exist")
   100  	}
   101  }
   102  
   103  func TestLink(t *testing.T) {
   104  	iptable, _, filterChain := createNewChain(t)
   105  	ip1 := net.ParseIP("192.168.1.1")
   106  	ip2 := net.ParseIP("192.168.1.2")
   107  	port := 1234
   108  	proto := "tcp"
   109  
   110  	err := filterChain.Link(Append, ip1, ip2, port, proto, bridgeName)
   111  	if err != nil {
   112  		t.Fatal(err)
   113  	}
   114  
   115  	rule1 := []string{
   116  		"-i", bridgeName,
   117  		"-o", bridgeName,
   118  		"-p", proto,
   119  		"-s", ip1.String(),
   120  		"-d", ip2.String(),
   121  		"--dport", strconv.Itoa(port),
   122  		"-j", "ACCEPT"}
   123  
   124  	if !iptable.Exists(filterChain.Table, filterChain.Name, rule1...) {
   125  		t.Fatal("rule1 does not exist")
   126  	}
   127  
   128  	rule2 := []string{
   129  		"-i", bridgeName,
   130  		"-o", bridgeName,
   131  		"-p", proto,
   132  		"-s", ip2.String(),
   133  		"-d", ip1.String(),
   134  		"--sport", strconv.Itoa(port),
   135  		"-j", "ACCEPT"}
   136  
   137  	if !iptable.Exists(filterChain.Table, filterChain.Name, rule2...) {
   138  		t.Fatal("rule2 does not exist")
   139  	}
   140  }
   141  
   142  func TestPrerouting(t *testing.T) {
   143  	iptable, natChain, _ := createNewChain(t)
   144  
   145  	args := []string{
   146  		"-i", "lo",
   147  		"-d", "192.168.1.1"}
   148  
   149  	err := natChain.Prerouting(Insert, args...)
   150  	if err != nil {
   151  		t.Fatal(err)
   152  	}
   153  
   154  	if !iptable.Exists(natChain.Table, "PREROUTING", args...) {
   155  		t.Fatal("rule does not exist")
   156  	}
   157  
   158  	delRule := append([]string{"-D", "PREROUTING", "-t", string(Nat)}, args...)
   159  	if _, err = iptable.Raw(delRule...); err != nil {
   160  		t.Fatal(err)
   161  	}
   162  }
   163  
   164  func TestOutput(t *testing.T) {
   165  	iptable, natChain, _ := createNewChain(t)
   166  
   167  	args := []string{
   168  		"-o", "lo",
   169  		"-d", "192.168.1.1"}
   170  
   171  	err := natChain.Output(Insert, args...)
   172  	if err != nil {
   173  		t.Fatal(err)
   174  	}
   175  
   176  	if !iptable.Exists(natChain.Table, "OUTPUT", args...) {
   177  		t.Fatal("rule does not exist")
   178  	}
   179  
   180  	delRule := append([]string{"-D", "OUTPUT", "-t",
   181  		string(natChain.Table)}, args...)
   182  	if _, err = iptable.Raw(delRule...); err != nil {
   183  		t.Fatal(err)
   184  	}
   185  }
   186  
   187  func TestConcurrencyWithWait(t *testing.T) {
   188  	RunConcurrencyTest(t, true)
   189  }
   190  
   191  func TestConcurrencyNoWait(t *testing.T) {
   192  	RunConcurrencyTest(t, false)
   193  }
   194  
   195  // Runs 10 concurrent rule additions. This will fail if iptables
   196  // is actually invoked simultaneously without --wait.
   197  // Note that if iptables does not support the xtable lock on this
   198  // system, then allowXlock has no effect -- it will always be off.
   199  func RunConcurrencyTest(t *testing.T, allowXlock bool) {
   200  	_, natChain, _ := createNewChain(t)
   201  
   202  	if !allowXlock && supportsXlock {
   203  		supportsXlock = false
   204  		defer func() { supportsXlock = true }()
   205  	}
   206  
   207  	ip := net.ParseIP("192.168.1.1")
   208  	port := 1234
   209  	dstAddr := "172.17.0.1"
   210  	dstPort := 4321
   211  	proto := "tcp"
   212  
   213  	group := new(errgroup.Group)
   214  	for i := 0; i < 10; i++ {
   215  		group.Go(func() error {
   216  			return natChain.Forward(Append, ip, port, proto, dstAddr, dstPort, "lo")
   217  		})
   218  	}
   219  	if err := group.Wait(); err != nil {
   220  		t.Fatal(err)
   221  	}
   222  }
   223  
   224  func TestCleanup(t *testing.T) {
   225  	iptable, _, filterChain := createNewChain(t)
   226  
   227  	var rules []byte
   228  
   229  	// Cleanup filter/FORWARD first otherwise output of iptables-save is dirty
   230  	link := []string{"-t", string(filterChain.Table),
   231  		string(Delete), "FORWARD",
   232  		"-o", bridgeName,
   233  		"-j", filterChain.Name}
   234  
   235  	if _, err := iptable.Raw(link...); err != nil {
   236  		t.Fatal(err)
   237  	}
   238  	filterChain.Remove()
   239  
   240  	err := iptable.RemoveExistingChain(chainName, Nat)
   241  	if err != nil {
   242  		t.Fatal(err)
   243  	}
   244  
   245  	rules, err = exec.Command("iptables-save").Output()
   246  	if err != nil {
   247  		t.Fatal(err)
   248  	}
   249  	if strings.Contains(string(rules), chainName) {
   250  		t.Fatalf("Removing chain failed. %s found in iptables-save", chainName)
   251  	}
   252  }
   253  
   254  func TestExistsRaw(t *testing.T) {
   255  	testChain1 := "ABCD"
   256  	testChain2 := "EFGH"
   257  
   258  	iptable := GetIptable(IPv4)
   259  
   260  	_, err := iptable.NewChain(testChain1, Filter, false)
   261  	if err != nil {
   262  		t.Fatal(err)
   263  	}
   264  	defer func() {
   265  		iptable.RemoveExistingChain(testChain1, Filter)
   266  	}()
   267  
   268  	_, err = iptable.NewChain(testChain2, Filter, false)
   269  	if err != nil {
   270  		t.Fatal(err)
   271  	}
   272  	defer func() {
   273  		iptable.RemoveExistingChain(testChain2, Filter)
   274  	}()
   275  
   276  	// Test detection over full and truncated rule string
   277  	input := []struct{ rule []string }{
   278  		{[]string{"-s", "172.8.9.9/32", "-j", "ACCEPT"}},
   279  		{[]string{"-d", "172.8.9.0/24", "-j", "DROP"}},
   280  		{[]string{"-s", "172.0.3.0/24", "-d", "172.17.0.0/24", "-p", "tcp", "-m", "tcp", "--dport", "80", "-j", testChain2}},
   281  		{[]string{"-j", "RETURN"}},
   282  	}
   283  
   284  	for i, r := range input {
   285  		ruleAdd := append([]string{"-t", string(Filter), "-A", testChain1}, r.rule...)
   286  		err = iptable.RawCombinedOutput(ruleAdd...)
   287  		if err != nil {
   288  			t.Fatalf("i=%d, err: %v", i, err)
   289  		}
   290  		if !iptable.exists(true, Filter, testChain1, r.rule...) {
   291  			t.Fatalf("Failed to detect rule. i=%d", i)
   292  		}
   293  		// Truncate the rule
   294  		trg := r.rule[len(r.rule)-1]
   295  		trg = trg[:len(trg)-2]
   296  		r.rule[len(r.rule)-1] = trg
   297  		if iptable.exists(true, Filter, testChain1, r.rule...) {
   298  			t.Fatalf("Invalid detection. i=%d", i)
   299  		}
   300  	}
   301  }