Add support of listening to multiple netns (#418)

* multiple netns listen - initial commit

* multiple netns listen - actual work

* remove redundant log line

* map /proc of host to tapper

* changing kubernetes provider again after big conflict

* revert node-sass version back to 5.0.0

* Rename host_source to hostSource

Co-authored-by: gadotroee <55343099+gadotroee@users.noreply.github.com>

* PR fixes - adding comment + typos + naming conventions

* go fmt + making procfs read only

* setns back to the original value after packet source initialized

Co-authored-by: gadotroee <55343099+gadotroee@users.noreply.github.com>
This commit is contained in:
David Levanon
2021-11-07 16:00:59 +02:00
committed by GitHub
parent a866576cfc
commit 8a90f02161
7 changed files with 861 additions and 31 deletions

View File

@@ -0,0 +1,154 @@
package source
import (
"fmt"
"runtime"
"strconv"
"strings"
"github.com/up9inc/mizu/shared/logger"
"github.com/vishvananda/netns"
)
type PacketSourceManager struct {
sources []*tcpPacketSource
}
func NewPacketSourceManager(procfs string, pids string, filename string, interfaceName string,
behaviour TcpPacketSourceBehaviour) (*PacketSourceManager, error) {
sources := make([]*tcpPacketSource, 0)
hostSource, err := newHostPacketSource(filename, interfaceName, behaviour)
if err != nil {
return nil, err
}
sources = append(sources, hostSource)
if pids != "" {
netnsSources := newNetnsPacketSources(procfs, pids, interfaceName, behaviour)
sources = append(sources, netnsSources...)
}
return &PacketSourceManager{
sources: sources,
}, nil
}
func newHostPacketSource(filename string, interfaceName string,
behaviour TcpPacketSourceBehaviour) (*tcpPacketSource, error) {
var name string
if filename == "" {
name = fmt.Sprintf("host-%v", interfaceName)
} else {
name = fmt.Sprintf("file-%v", filename)
}
source, err := newTcpPacketSource(name, filename, interfaceName, behaviour)
if err != nil {
return nil, err
}
return source, nil
}
func newNetnsPacketSources(procfs string, pids string, interfaceName string,
behaviour TcpPacketSourceBehaviour) []*tcpPacketSource {
result := make([]*tcpPacketSource, 0)
for _, pidstr := range strings.Split(pids, ",") {
pid, err := strconv.Atoi(pidstr)
if err != nil {
logger.Log.Errorf("Invalid PID: %v - %v", pid, err)
continue
}
nsh, err := netns.GetFromPath(fmt.Sprintf("%v/%v/ns/net", procfs, pid))
if err != nil {
logger.Log.Errorf("Unable to get netns of pid %v - %v", pid, err)
continue
}
src, err := newNetnsPacketSource(pid, nsh, interfaceName, behaviour)
if err != nil {
logger.Log.Errorf("Error starting netns packet source for %v - %v", pid, err)
continue
}
result = append(result, src)
}
return result
}
func newNetnsPacketSource(pid int, nsh netns.NsHandle, interfaceName string,
behaviour TcpPacketSourceBehaviour) (*tcpPacketSource, error) {
done := make(chan *tcpPacketSource)
errors := make(chan error)
go func(done chan<- *tcpPacketSource) {
// Setting a netns should be done from a dedicated OS thread.
//
// goroutines are not really OS threads, we try to mimic the issue by
// locking the OS thread to this goroutine
//
runtime.LockOSThread()
defer runtime.UnlockOSThread()
oldnetns, err := netns.Get()
if err != nil {
logger.Log.Errorf("Unable to get netns of current thread %v", err)
errors <- err
return
}
if err := netns.Set(nsh); err != nil {
logger.Log.Errorf("Unable to set netns of pid %v - %v", pid, err)
errors <- err
return
}
name := fmt.Sprintf("netns-%v-%v", pid, interfaceName)
src, err := newTcpPacketSource(name, "", interfaceName, behaviour)
if err != nil {
logger.Log.Errorf("Error listening to PID %v - %v", pid, err)
errors <- err
return
}
if err := netns.Set(oldnetns); err != nil {
logger.Log.Errorf("Unable to set back netns of current thread %v", err)
errors <- err
return
}
done <- src
}(done)
select {
case err := <-errors:
return nil, err
case source := <-done:
return source, nil
}
}
func (m *PacketSourceManager) ReadPackets(ipdefrag bool, packets chan<- TcpPacketInfo) {
for _, src := range m.sources {
go src.readPackets(ipdefrag, packets)
}
}
func (m *PacketSourceManager) Close() {
for _, src := range m.sources {
src.close()
}
}

View File

@@ -13,11 +13,12 @@ import (
"github.com/up9inc/mizu/tap/diagnose"
)
type TcpPacketSource struct {
type tcpPacketSource struct {
source *gopacket.PacketSource
handle *pcap.Handle
defragger *ip4defrag.IPv4Defragmenter
Behaviour *TcpPacketSourceBehaviour
name string
}
type TcpPacketSourceBehaviour struct {
@@ -31,14 +32,15 @@ type TcpPacketSourceBehaviour struct {
type TcpPacketInfo struct {
Packet gopacket.Packet
Source *TcpPacketSource
Source *tcpPacketSource
}
func NewTcpPacketSource(filename string, interfaceName string,
behaviour TcpPacketSourceBehaviour) (*TcpPacketSource, error) {
func newTcpPacketSource(name, filename string, interfaceName string,
behaviour TcpPacketSourceBehaviour) (*tcpPacketSource, error) {
var err error
result := &TcpPacketSource{
result := &tcpPacketSource{
name: name,
defragger: ip4defrag.NewIPv4Defragmenter(),
Behaviour: &behaviour,
}
@@ -96,21 +98,24 @@ func NewTcpPacketSource(filename string, interfaceName string,
return result, nil
}
func (source *TcpPacketSource) Close() {
func (source *tcpPacketSource) close() {
if source.handle != nil {
source.handle.Close()
}
}
func (source *TcpPacketSource) ReadPackets(ipdefrag bool, packets chan<- TcpPacketInfo) error {
func (source *tcpPacketSource) readPackets(ipdefrag bool, packets chan<- TcpPacketInfo) {
logger.Log.Infof("Start reading packets from %v", source.name)
for {
packet, err := source.source.NextPacket()
if err == io.EOF {
return err
logger.Log.Infof("Got EOF while reading packets from %v", source.name)
return
} else if err != nil {
if err.Error() != "Timeout Expired" {
logger.Log.Debugf("Error: %T", err)
logger.Log.Debugf("Error while reading from %v - %v", source.name, err)
}
continue
}