github.com/jingruilea/kubeedge@v1.2.0-beta.0.0.20200410162146-4bb8902b3879/edgemesh/pkg/proxy/proxy.go (about)

     1  package proxy
     2  
     3  import (
     4  	"bufio"
     5  	"fmt"
     6  	"io/ioutil"
     7  	"os"
     8  	"strings"
     9  	"time"
    10  
    11  	"github.com/vishvananda/netlink"
    12  	"k8s.io/klog"
    13  	utiliptables "k8s.io/kubernetes/pkg/util/iptables"
    14  	utilexec "k8s.io/utils/exec"
    15  
    16  	"github.com/kubeedge/kubeedge/edgemesh/pkg/config"
    17  )
    18  
    19  // iptables rules
    20  type Proxier struct {
    21  	iptables utiliptables.Interface
    22  	inboundRule  string
    23  	outboundRule string
    24  	dNatRule     string
    25  }
    26  
    27  const (
    28  	meshChain  = "EDGE-MESH"
    29  	hostResolv = "/etc/resolv.conf"
    30  )
    31  
    32  var (
    33  	proxier *Proxier
    34  	route   netlink.Route
    35  )
    36  
    37  func Init() {
    38  	protocol := utiliptables.ProtocolIpv4
    39  	exec := utilexec.New()
    40  	iptInterface := utiliptables.New(exec, protocol)
    41  	proxier = &Proxier{
    42  		iptables:     iptInterface,
    43  		inboundRule:  "-p tcp -d " + config.Config.SubNet + " -i " + config.Config.ListenInterface + " -j " + meshChain,
    44  		outboundRule: "-p tcp -d " + config.Config.SubNet + " -o " + config.Config.ListenInterface + " -j " + meshChain,
    45  		dNatRule:     "-p tcp -j DNAT --to-destination " + config.Config.Listener.Addr().String(),
    46  	}
    47  	// read and clean iptables rules
    48  	proxier.readAndCleanRule()
    49  	// ensure iptables rules
    50  	proxier.ensureRule()
    51  	// add route
    52  	dst, err := netlink.ParseIPNet(config.Config.SubNet)
    53  	if err != nil {
    54  		klog.Errorf("[EdgeMesh] parse subnet error: %v", err)
    55  		return
    56  	}
    57  	gw := config.Config.ListenIP
    58  	route = netlink.Route{
    59  		Dst: dst,
    60  		Gw:  gw,
    61  	}
    62  	err = netlink.RouteAdd(&route)
    63  	if err != nil {
    64  		klog.Warningf("[EdgeMesh] add route err: %v", err)
    65  	}
    66  	// save iptables rules
    67  	proxier.saveRule()
    68  	// ensure resolv.conf
    69  	ensureResolvForHost()
    70  	// sync
    71  	go proxier.sync()
    72  }
    73  
    74  // sync periodically
    75  func (p *Proxier) sync() {
    76  	syncRuleTicker := time.NewTicker(10 * time.Second)
    77  	for {
    78  		<-syncRuleTicker.C
    79  		p.ensureRule()
    80  		ensureResolvForHost()
    81  	}
    82  }
    83  
    84  // ensureRule ensures iptables rules exist
    85  func (p *Proxier) ensureRule() {
    86  	iptInterface := p.iptables
    87  	inboundRule := strings.Split(p.inboundRule, " ")
    88  	outboundRule := strings.Split(p.outboundRule, " ")
    89  	dNatRule := strings.Split(p.dNatRule, " ")
    90  	exist, err := iptInterface.EnsureChain(utiliptables.TableNAT, meshChain)
    91  	if err != nil {
    92  		klog.Errorf("[EdgeMesh] ensure chain %s failed with err: %v", meshChain, err)
    93  	}
    94  	if !exist {
    95  		klog.Infof("[EdgeMesh] chain %s not exists", meshChain)
    96  	}
    97  
    98  	exist, err = iptInterface.EnsureRule(utiliptables.Append, utiliptables.TableNAT, utiliptables.ChainPrerouting, inboundRule...)
    99  	if err != nil {
   100  		klog.Errorf("[EdgeMesh] ensure inbound rule %s failed with err: %v", p.inboundRule, err)
   101  	}
   102  	if !exist {
   103  		klog.Infof("[EdgeMesh] inbound rule %s not exists", p.inboundRule)
   104  	}
   105  
   106  	exist, err = iptInterface.EnsureRule(utiliptables.Append, utiliptables.TableNAT, utiliptables.ChainOutput, outboundRule...)
   107  	if err != nil {
   108  		klog.Errorf("[EdgeMesh] ensure outbound rule %s failed with err: %v", p.outboundRule, err)
   109  	}
   110  	if !exist {
   111  		klog.Infof("[EdgeMesh] outbound rule %s not exists", p.outboundRule)
   112  	}
   113  
   114  	exist, err = iptInterface.EnsureRule(utiliptables.Append, utiliptables.TableNAT, meshChain, dNatRule...)
   115  	if err != nil {
   116  		klog.Errorf("[EdgeMesh] ensure dnat rule %s failed with err: %v", p.dNatRule, err)
   117  	}
   118  	if !exist {
   119  		klog.Infof("[EdgeMesh] dnat rule %s not exists", p.dNatRule)
   120  	}
   121  }
   122  
   123  // saveRule saves iptables rules into file
   124  func (p *Proxier) saveRule() {
   125  	file, err := os.OpenFile("/run/edgemesh-iptables", os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0666)
   126  	if err != nil {
   127  		klog.Errorf("[EdgeMesh] open file /run/edgemesh-iptables err: %v", err)
   128  		return
   129  	}
   130  	// store
   131  	defer file.Close()
   132  	w := bufio.NewWriter(file)
   133  	fmt.Fprintln(w, p.inboundRule)
   134  	fmt.Fprintln(w, p.dNatRule)
   135  	fmt.Fprintln(w, p.outboundRule)
   136  	w.Flush()
   137  }
   138  
   139  // readAndCleanRule reads iptables rules from file and cleans them
   140  func (p *Proxier) readAndCleanRule() {
   141  	file, err := os.OpenFile("/run/edgemesh-iptables", os.O_RDONLY, 0444)
   142  	if err != nil {
   143  		klog.Errorf("[EdgeMesh] open file /run/edgemesh-iptables err: %v", err)
   144  		return
   145  	}
   146  
   147  	defer file.Close()
   148  	scan := bufio.NewScanner(file)
   149  	scan.Split(bufio.ScanLines)
   150  	for scan.Scan() {
   151  		serverString := scan.Text()
   152  		if strings.Contains(serverString, "-o") {
   153  			p.iptables.DeleteRule(utiliptables.TableNAT, utiliptables.ChainOutput, strings.Split(serverString, " ")...)
   154  		} else if strings.Contains(serverString, "-i") {
   155  			p.iptables.DeleteRule(utiliptables.TableNAT, utiliptables.ChainPrerouting, strings.Split(serverString, " ")...)
   156  		}
   157  	}
   158  	p.iptables.FlushChain(utiliptables.TableNAT, meshChain)
   159  	p.iptables.DeleteChain(utiliptables.TableNAT, meshChain)
   160  }
   161  
   162  // ensureResolvForHost adds edgemesh dns server to the head of /etc/resolv.conf
   163  func ensureResolvForHost() {
   164  	bs, err := ioutil.ReadFile(hostResolv)
   165  	if err != nil {
   166  		klog.Errorf("[EdgeMesh] read file %s err: %v", hostResolv, err)
   167  		return
   168  	}
   169  
   170  	resolv := strings.Split(string(bs), "\n")
   171  	if resolv == nil {
   172  		nameserver := "nameserver " + config.Config.ListenIP.String()
   173  		ioutil.WriteFile(hostResolv, []byte(nameserver), 0600)
   174  		return
   175  	}
   176  
   177  	configured := false
   178  	dnsIdx := 0
   179  	startIdx := 0
   180  	for idx, item := range resolv {
   181  		if strings.Contains(item, config.Config.ListenIP.String()) {
   182  			configured = true
   183  			dnsIdx = idx
   184  			break
   185  		}
   186  	}
   187  	for idx, item := range resolv {
   188  		if strings.Contains(item, "nameserver") {
   189  			startIdx = idx
   190  			break
   191  		}
   192  	}
   193  	if configured {
   194  		if dnsIdx != startIdx && dnsIdx > startIdx {
   195  			nameserver := sortNameserver(resolv, dnsIdx, startIdx)
   196  			ioutil.WriteFile(hostResolv, []byte(nameserver), 0600)
   197  		}
   198  		return
   199  	}
   200  
   201  	nameserver := ""
   202  	for idx := 0; idx < len(resolv); {
   203  		if idx == startIdx {
   204  			startIdx = -1
   205  			nameserver = nameserver + "nameserver " + config.Config.ListenIP.String() + "\n"
   206  			continue
   207  		}
   208  		nameserver = nameserver + resolv[idx] + "\n"
   209  		idx++
   210  	}
   211  
   212  	ioutil.WriteFile(hostResolv, []byte(nameserver), 0600)
   213  }
   214  
   215  func sortNameserver(resolv []string, dnsIdx, startIdx int) string {
   216  	nameserver := ""
   217  	idx := 0
   218  	for ; idx < startIdx; idx++ {
   219  		nameserver = nameserver + resolv[idx] + "\n"
   220  	}
   221  	nameserver = nameserver + resolv[dnsIdx] + "\n"
   222  
   223  	for idx = startIdx; idx < len(resolv); idx++ {
   224  		if idx == dnsIdx {
   225  			continue
   226  		}
   227  		nameserver = nameserver + resolv[idx] + "\n"
   228  	}
   229  
   230  	return nameserver
   231  }
   232  
   233  func Clean() {
   234  	proxier.readAndCleanRule()
   235  	netlink.RouteDel(&route)
   236  	bs, err := ioutil.ReadFile(hostResolv)
   237  	if err != nil {
   238  		klog.Warningf("[EdgeMesh] read file %s err: %v", hostResolv, err)
   239  	}
   240  
   241  	resolv := strings.Split(string(bs), "\n")
   242  	if resolv == nil {
   243  		return
   244  	}
   245  	nameserver := ""
   246  	for _, item := range resolv {
   247  		if strings.Contains(item, config.Config.ListenIP.String()) {
   248  			continue
   249  		}
   250  		nameserver = nameserver + item + "\n"
   251  	}
   252  	ioutil.WriteFile(hostResolv, []byte(nameserver), 0600)
   253  }