github.com/aporeto-inc/trireme-lib@v10.358.0+incompatible/controller/internal/enforcer/nfqdatapath/autoport.go (about)

     1  package nfqdatapath
     2  
     3  import (
     4  	"path/filepath"
     5  	"sync"
     6  	"time"
     7  
     8  	"go.aporeto.io/enforcerd/trireme-lib/controller/pkg/ipsetmanager"
     9  	"go.aporeto.io/enforcerd/trireme-lib/controller/pkg/pucontext"
    10  	"go.aporeto.io/enforcerd/trireme-lib/utils/portspec"
    11  	"go.uber.org/zap"
    12  )
    13  
    14  type readSystemFiles interface {
    15  	readOpenSockFD(pid string) []string
    16  	readProcNetTCP() (inodeMap map[string]string, userMap map[string]map[string]bool, err error)
    17  	getCgroupList() []string
    18  	listCgroupProcesses(cgroupname string) ([]string, error)
    19  }
    20  
    21  type defaultRead struct{}
    22  
    23  var readFiles readSystemFiles
    24  var d *defaultRead
    25  var lock sync.RWMutex
    26  
    27  func init() {
    28  	lock.Lock()
    29  	readFiles = d
    30  	lock.Unlock()
    31  }
    32  
    33  func (d *Datapath) autoPortDiscovery() {
    34  	for {
    35  		d.findPorts()
    36  		time.Sleep(2 * time.Second)
    37  	}
    38  }
    39  
    40  // resync adds new port for the PU and removes the stale ports
    41  func (d *Datapath) resync(newPortMap map[string]map[string]bool) {
    42  
    43  	for k, vs := range d.puToPortsMap {
    44  		m := newPortMap[k]
    45  
    46  		for v := range vs {
    47  			if m == nil || !m[v] {
    48  				err := ipsetmanager.V4().DeletePortFromServerPortSet(k, v)
    49  				if err != nil {
    50  					zap.L().Debug("autoPortDiscovery: Delete port set returned error", zap.Error(err))
    51  				}
    52  				err = ipsetmanager.V6().DeletePortFromServerPortSet(k, v)
    53  				if err != nil {
    54  					zap.L().Debug("autoPortDiscovery: Delete port set returned error", zap.Error(err))
    55  				}
    56  				// delete the port from contextIDFromTCPPort cache
    57  				err = d.contextIDFromTCPPort.RemoveStringPorts(v)
    58  				if err != nil {
    59  					zap.L().Debug("autoPortDiscovery: can not remove port from cache", zap.Error(err))
    60  				}
    61  			}
    62  		}
    63  	}
    64  
    65  	for k, vs := range newPortMap {
    66  		m := d.puToPortsMap[k]
    67  		for v := range vs {
    68  			if m == nil || !m[v] {
    69  				portSpec, err := portspec.NewPortSpecFromString(v, k)
    70  				if err != nil {
    71  					continue
    72  				}
    73  				d.contextIDFromTCPPort.AddPortSpec(portSpec)
    74  				err = ipsetmanager.V4().AddPortToServerPortSet(k, v)
    75  				if err != nil {
    76  					zap.L().Error("autoPortDiscovery: Failed to add port to portset", zap.String("context", k), zap.String("port", v))
    77  				}
    78  				err = ipsetmanager.V6().AddPortToServerPortSet(k, v)
    79  				if err != nil {
    80  					zap.L().Error("autoPortDiscovery: Failed to add port to portset", zap.String("context", k), zap.String("port", v))
    81  				}
    82  			}
    83  		}
    84  	}
    85  
    86  	d.puToPortsMap = newPortMap
    87  }
    88  
    89  var lastRun time.Time
    90  
    91  func (d *Datapath) findPorts() {
    92  	lock.Lock()
    93  	defer lock.Unlock()
    94  
    95  	// Rate limit this function to run every 5 milliseconds
    96  	if time.Since(lastRun) <= 5*time.Millisecond {
    97  		return
    98  	}
    99  
   100  	lastRun = time.Now()
   101  
   102  	cgroupList := readFiles.getCgroupList()
   103  
   104  	newPUToPortsMap := map[string]map[string]bool{}
   105  	inodeMap, _, err := readFiles.readProcNetTCP()
   106  	if err != nil {
   107  		zap.L().Error("autoPortDiscovery: /proc/net/tcp read failed with error", zap.Error(err))
   108  		return
   109  	}
   110  
   111  	for _, cgroupPath := range cgroupList {
   112  		/* cgroup is also the contextID */
   113  		newMap := map[string]bool{}
   114  
   115  		cgroup := filepath.Base(cgroupPath)
   116  
   117  		// check if a PU exists with that contextID and is marked with auto port
   118  		pu, err := d.puFromContextID.Get(cgroup)
   119  		if err != nil {
   120  			continue
   121  		}
   122  		p := pu.(*pucontext.PUContext)
   123  
   124  		// we skip AutoPort discovery if it is not enabled
   125  		if !p.Autoport() {
   126  			continue
   127  		}
   128  
   129  		procs, err := readFiles.listCgroupProcesses(cgroupPath)
   130  		if err != nil {
   131  			zap.L().Warn("autoPortDiscovery: Cgroup processes could not be retrieved", zap.String("cgroupPath", cgroupPath), zap.String("cgroup", cgroup), zap.Error(err))
   132  			continue
   133  		}
   134  		zap.L().Debug("autoPortDiscovery: processes for cgroup detected", zap.String("cgroupPath", cgroupPath), zap.String("cgroup", cgroup), zap.String("id", p.ID()), zap.Strings("procs", procs))
   135  
   136  		for _, proc := range procs {
   137  			openSockFDs := readFiles.readOpenSockFD(proc)
   138  			for _, sock := range openSockFDs {
   139  				if inodeMap[sock] != "" {
   140  					newMap[inodeMap[sock]] = true
   141  				}
   142  			}
   143  		}
   144  
   145  		newPUToPortsMap[cgroup] = newMap
   146  	}
   147  
   148  	d.resync(newPUToPortsMap)
   149  }