github.com/blixtra/rkt@v0.8.1-0.20160204105720-ab0d1add1a43/networking/portfwd.go (about)

     1  // Copyright 2015 The rkt Authors
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //     http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  package networking
    16  
    17  import (
    18  	"fmt"
    19  	"net"
    20  	"strconv"
    21  
    22  	"github.com/coreos/go-iptables/iptables"
    23  )
    24  
    25  func (e *podEnv) forwardPorts(fps []ForwardedPort, defIP net.IP) error {
    26  	if len(fps) == 0 {
    27  		return nil
    28  	}
    29  
    30  	ipt, err := iptables.New()
    31  	if err != nil {
    32  		return err
    33  	}
    34  
    35  	// Create a separate chain for this pod. This helps with debugging
    36  	// and makes it easier to cleanup
    37  	chainDNAT := e.portFwdChain("DNAT")
    38  	chainSNAT := e.portFwdChain("SNAT")
    39  
    40  	if err = ipt.NewChain("nat", chainDNAT); err != nil {
    41  		return err
    42  	}
    43  
    44  	if err = ipt.NewChain("nat", chainSNAT); err != nil {
    45  		return err
    46  	}
    47  
    48  	chainRuleDNAT := e.portFwdChainRuleSpec(chainDNAT, "DNAT")
    49  	chainRuleSNAT := e.portFwdChainRuleSpec(chainSNAT, "SNAT")
    50  
    51  	for _, entry := range []struct {
    52  		chain           string
    53  		customChainRule []string
    54  	}{
    55  		{"POSTROUTING", chainRuleSNAT}, // traffic originating from this host
    56  		{"PREROUTING", chainRuleDNAT},  // outside traffic hitting this host
    57  		{"OUTPUT", chainRuleDNAT},      // traffic originating from this host
    58  	} {
    59  		exists, err := ipt.Exists("nat", entry.chain, entry.customChainRule...)
    60  		if err != nil {
    61  			return err
    62  		}
    63  		if !exists {
    64  			err = ipt.Insert("nat", entry.chain, 1, entry.customChainRule...)
    65  			if err != nil {
    66  				return err
    67  			}
    68  		}
    69  	}
    70  
    71  	for _, p := range fps {
    72  
    73  		dst := fmt.Sprintf("%v:%v", defIP, p.PodPort)
    74  		dstIP := fmt.Sprintf("%v", defIP)
    75  		dport := strconv.Itoa(int(p.HostPort))
    76  
    77  		for _, r := range []struct {
    78  			chain string
    79  			rule  []string
    80  		}{
    81  			{ // Rewrite the destination
    82  				chainDNAT,
    83  				[]string{
    84  					"-p", p.Protocol,
    85  					"--dport", dport,
    86  					"-j", "DNAT",
    87  					"--to-destination", dst,
    88  				},
    89  			},
    90  			{ // Rewrite the source for connections to localhost on the host
    91  				chainSNAT,
    92  				[]string{
    93  					"-p", p.Protocol,
    94  					"-s", "127.0.0.1",
    95  					"-d", dstIP,
    96  					"--dport", dport,
    97  					"-j", "MASQUERADE",
    98  				},
    99  			},
   100  		} {
   101  			if err := ipt.AppendUnique("nat", r.chain, r.rule...); err != nil {
   102  				return err
   103  			}
   104  		}
   105  	}
   106  	return nil
   107  }
   108  
   109  func (e *podEnv) unforwardPorts() error {
   110  	ipt, err := iptables.New()
   111  	if err != nil {
   112  		return err
   113  	}
   114  
   115  	chainDNAT := e.portFwdChain("DNAT")
   116  	chainSNAT := e.portFwdChain("SNAT")
   117  
   118  	chainRuleDNAT := e.portFwdChainRuleSpec(chainDNAT, "DNAT")
   119  	chainRuleSNAT := e.portFwdChainRuleSpec(chainSNAT, "SNAT")
   120  
   121  	// There's no clean way now to test if a chain exists or
   122  	// even if a rule exists if the chain is not present.
   123  	// So we swallow the errors for now :(
   124  	// TODO(eyakubovich): move to using libiptc for iptable
   125  	// manipulation
   126  
   127  	for _, entry := range []struct {
   128  		chain           string
   129  		customChainRule []string
   130  	}{
   131  		{"POSTROUTING", chainRuleSNAT}, // traffic originating on this host
   132  		{"PREROUTING", chainRuleDNAT},  // outside traffic hitting this host
   133  		{"OUTPUT", chainRuleDNAT},      // traffic originating on this host
   134  	} {
   135  		ipt.Delete("nat", entry.chain, entry.customChainRule...)
   136  	}
   137  
   138  	for _, entry := range []string{chainDNAT, chainSNAT} {
   139  		ipt.ClearChain("nat", entry)
   140  		ipt.DeleteChain("nat", entry)
   141  	}
   142  	return nil
   143  }
   144  
   145  func (e *podEnv) portFwdChain(name string) string {
   146  	return fmt.Sprintf("RKT-PFWD-%s-%s", name, e.podID.String()[0:8])
   147  }
   148  
   149  func (e *podEnv) portFwdChainRuleSpec(chain string, name string) []string {
   150  	switch name {
   151  	case "SNAT":
   152  		return []string{"-s", "127.0.0.1", "!", "-d", "127.0.0.1", "-j", chain}
   153  	case "DNAT":
   154  		return []string{"-m", "addrtype", "--dst-type", "LOCAL", "-j", chain}
   155  	default:
   156  		return nil
   157  	}
   158  }