...

Source file src/k8s.io/kubernetes/cmd/kube-proxy/app/conntrack.go

Documentation: k8s.io/kubernetes/cmd/kube-proxy/app

     1  /*
     2  Copyright 2015 The Kubernetes Authors.
     3  
     4  Licensed under the Apache License, Version 2.0 (the "License");
     5  you may not use this file except in compliance with the License.
     6  You may obtain a copy of the License at
     7  
     8      http://www.apache.org/licenses/LICENSE-2.0
     9  
    10  Unless required by applicable law or agreed to in writing, software
    11  distributed under the License is distributed on an "AS IS" BASIS,
    12  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13  See the License for the specific language governing permissions and
    14  limitations under the License.
    15  */
    16  
    17  package app
    18  
    19  import (
    20  	"errors"
    21  	"os"
    22  	"strconv"
    23  	"strings"
    24  
    25  	"k8s.io/component-helpers/node/util/sysctl"
    26  	"k8s.io/klog/v2"
    27  	"k8s.io/mount-utils"
    28  )
    29  
    30  // Conntracker is an interface to the global sysctl. Descriptions of the various
    31  // sysctl fields can be found here:
    32  //
    33  // https://www.kernel.org/doc/Documentation/networking/nf_conntrack-sysctl.txt
    34  type Conntracker interface {
    35  	// SetMax adjusts nf_conntrack_max.
    36  	SetMax(max int) error
    37  	// SetTCPEstablishedTimeout adjusts nf_conntrack_tcp_timeout_established.
    38  	SetTCPEstablishedTimeout(seconds int) error
    39  	// SetTCPCloseWaitTimeout adjusts nf_conntrack_tcp_timeout_close_wait.
    40  	SetTCPCloseWaitTimeout(seconds int) error
    41  	// SetTCPBeLiberal adjusts nf_conntrack_tcp_be_liberal.
    42  	SetTCPBeLiberal(value int) error
    43  	// SetUDPTimeout adjusts nf_conntrack_udp_timeout.
    44  	SetUDPTimeout(seconds int) error
    45  	// SetUDPStreamTimeout adjusts nf_conntrack_udp_timeout_stream.
    46  	SetUDPStreamTimeout(seconds int) error
    47  }
    48  
    49  type realConntracker struct {
    50  	logger klog.Logger
    51  }
    52  
    53  var errReadOnlySysFS = errors.New("readOnlySysFS")
    54  
    55  func (rct realConntracker) SetMax(max int) error {
    56  	if err := rct.setIntSysCtl("nf_conntrack_max", max); err != nil {
    57  		return err
    58  	}
    59  	rct.logger.Info("Setting nf_conntrack_max", "nfConntrackMax", max)
    60  
    61  	// Linux does not support writing to /sys/module/nf_conntrack/parameters/hashsize
    62  	// when the writer process is not in the initial network namespace
    63  	// (https://github.com/torvalds/linux/blob/v4.10/net/netfilter/nf_conntrack_core.c#L1795-L1796).
    64  	// Usually that's fine. But in some configurations such as with github.com/kinvolk/kubeadm-nspawn,
    65  	// kube-proxy is in another netns.
    66  	// Therefore, check if writing in hashsize is necessary and skip the writing if not.
    67  	hashsize, err := readIntStringFile("/sys/module/nf_conntrack/parameters/hashsize")
    68  	if err != nil {
    69  		return err
    70  	}
    71  	if hashsize >= (max / 4) {
    72  		return nil
    73  	}
    74  
    75  	// sysfs is expected to be mounted as 'rw'. However, it may be
    76  	// unexpectedly mounted as 'ro' by docker because of a known docker
    77  	// issue (https://github.com/docker/docker/issues/24000). Setting
    78  	// conntrack will fail when sysfs is readonly. When that happens, we
    79  	// don't set conntrack hashsize and return a special error
    80  	// errReadOnlySysFS here. The caller should deal with
    81  	// errReadOnlySysFS differently.
    82  	writable, err := rct.isSysFSWritable()
    83  	if err != nil {
    84  		return err
    85  	}
    86  	if !writable {
    87  		return errReadOnlySysFS
    88  	}
    89  	// TODO: generify this and sysctl to a new sysfs.WriteInt()
    90  	rct.logger.Info("Setting conntrack hashsize", "conntrackHashsize", max/4)
    91  	return writeIntStringFile("/sys/module/nf_conntrack/parameters/hashsize", max/4)
    92  }
    93  
    94  func (rct realConntracker) SetTCPEstablishedTimeout(seconds int) error {
    95  	return rct.setIntSysCtl("nf_conntrack_tcp_timeout_established", seconds)
    96  }
    97  
    98  func (rct realConntracker) SetTCPCloseWaitTimeout(seconds int) error {
    99  	return rct.setIntSysCtl("nf_conntrack_tcp_timeout_close_wait", seconds)
   100  }
   101  
   102  func (rct realConntracker) SetTCPBeLiberal(value int) error {
   103  	return rct.setIntSysCtl("nf_conntrack_tcp_be_liberal", value)
   104  }
   105  
   106  func (rct realConntracker) SetUDPTimeout(seconds int) error {
   107  	return rct.setIntSysCtl("nf_conntrack_udp_timeout", seconds)
   108  }
   109  
   110  func (rct realConntracker) SetUDPStreamTimeout(seconds int) error {
   111  	return rct.setIntSysCtl("nf_conntrack_udp_timeout_stream", seconds)
   112  }
   113  
   114  func (rct realConntracker) setIntSysCtl(name string, value int) error {
   115  	entry := "net/netfilter/" + name
   116  
   117  	sys := sysctl.New()
   118  	if val, _ := sys.GetSysctl(entry); val != value {
   119  		rct.logger.Info("Set sysctl", "entry", entry, "value", value)
   120  		if err := sys.SetSysctl(entry, value); err != nil {
   121  			return err
   122  		}
   123  	}
   124  	return nil
   125  }
   126  
   127  // isSysFSWritable checks /proc/mounts to see whether sysfs is 'rw' or not.
   128  func (rct realConntracker) isSysFSWritable() (bool, error) {
   129  	const permWritable = "rw"
   130  	const sysfsDevice = "sysfs"
   131  	m := mount.New("" /* default mount path */)
   132  	mountPoints, err := m.List()
   133  	if err != nil {
   134  		rct.logger.Error(err, "Failed to list mount points")
   135  		return false, err
   136  	}
   137  
   138  	for _, mountPoint := range mountPoints {
   139  		if mountPoint.Type != sysfsDevice {
   140  			continue
   141  		}
   142  		// Check whether sysfs is 'rw'
   143  		if len(mountPoint.Opts) > 0 && mountPoint.Opts[0] == permWritable {
   144  			return true, nil
   145  		}
   146  		rct.logger.Error(nil, "Sysfs is not writable", "mountPoint", mountPoint, "mountOptions", mountPoint.Opts)
   147  		return false, errReadOnlySysFS
   148  	}
   149  
   150  	return false, errors.New("no sysfs mounted")
   151  }
   152  
   153  func readIntStringFile(filename string) (int, error) {
   154  	b, err := os.ReadFile(filename)
   155  	if err != nil {
   156  		return -1, err
   157  	}
   158  	return strconv.Atoi(strings.TrimSpace(string(b)))
   159  }
   160  
   161  func writeIntStringFile(filename string, value int) error {
   162  	return os.WriteFile(filename, []byte(strconv.Itoa(value)), 0640)
   163  }
   164  

View as plain text