conntrack.go 4.4 KB

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