Skip to content

Commit

Permalink
Output iface name when --output-meta is set
Browse files Browse the repository at this point in the history
This commits makes --output-meta to output not only ifindex, but also
iface name.

To achieve that, we first build a netns_inode => ifindex => iface_name
cache. This is done by entering each /proc/$PID/ns/net, and then quering
ifaces. Then, when a pwru process receives an BPF event, it queries the
cache (netns inode and ifindex are set in the event).

Example:

    ... [curl]  nf_hook_slow netns=4026531840 mark=0x0 iface=3(wlan0)

Signed-off-by: Martynas Pumputis <[email protected]>
  • Loading branch information
brb committed Sep 28, 2023
1 parent e98de68 commit 93ca1a5
Showing 1 changed file with 123 additions and 1 deletion.
124 changes: 123 additions & 1 deletion internal/pwru/output.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,18 +5,24 @@
package pwru

import (
"errors"
"fmt"
"io"
"log"
"net"
"os"
"path/filepath"
"runtime"
"strconv"
"syscall"
"time"

"github.com/cilium/ebpf"
"github.com/cilium/ebpf/btf"
"github.com/jsimonetti/rtnetlink"
ps "github.com/mitchellh/go-ps"
"github.com/vishvananda/netns"
"golang.org/x/sys/unix"

"github.com/cilium/pwru/internal/byteorder"
)
Expand All @@ -32,6 +38,7 @@ type output struct {
writer io.Writer
kprobeMulti bool
kfreeReasons map[uint64]string
ifaceCache map[uint64]map[uint32]string
}

func NewOutput(flags *Flags, printSkbMap *ebpf.Map, printStackMap *ebpf.Map,
Expand All @@ -52,6 +59,14 @@ func NewOutput(flags *Flags, printSkbMap *ebpf.Map, printStackMap *ebpf.Map,
log.Printf("Unable to load packet drop reaons: %v", err)
}

var ifs map[uint64]map[uint32]string
if flags.OutputMeta {
ifs, err = getIfaces()
if err != nil {
log.Printf("Failed to retrieve all ifaces from all network namespaces: %v. Some iface names might be not shown.", err)
}
}

return &output{
flags: flags,
lastSeenSkb: map[uint64]uint64{},
Expand All @@ -61,6 +76,7 @@ func NewOutput(flags *Flags, printSkbMap *ebpf.Map, printStackMap *ebpf.Map,
writer: writer,
kprobeMulti: kprobeMulti,
kfreeReasons: reasons,
ifaceCache: ifs,
}, nil
}

Expand Down Expand Up @@ -132,7 +148,10 @@ func (o *output) Print(event *Event) {
o.lastSeenSkb[event.SAddr] = event.Timestamp

if o.flags.OutputMeta {
fmt.Fprintf(o.writer, " netns=%d mark=0x%x ifindex=%d proto=%x mtu=%d len=%d", event.Meta.Netns, event.Meta.Mark, event.Meta.Ifindex, event.Meta.Proto, event.Meta.MTU, event.Meta.Len)
fmt.Fprintf(o.writer, " netns=%d mark=0x%x iface=%s proto=%x mtu=%d len=%d",
event.Meta.Netns, event.Meta.Mark,
o.getIfaceName(event.Meta.Netns, event.Meta.Ifindex),
event.Meta.Proto, event.Meta.MTU, event.Meta.Len)
}

if o.flags.OutputTuple {
Expand Down Expand Up @@ -165,6 +184,15 @@ func (o *output) Print(event *Event) {
fmt.Fprintln(o.writer)
}

func (o *output) getIfaceName(netnsInode, ifindex uint32) string {
if ifaces, ok := o.ifaceCache[uint64(netnsInode)]; ok {
if name, ok := ifaces[ifindex]; ok {
return fmt.Sprintf("%d(%s)", ifindex, name)
}
}
return fmt.Sprintf("%d", ifindex)
}

func protoToStr(proto uint8) string {
switch proto {
case syscall.IPPROTO_TCP:
Expand Down Expand Up @@ -212,3 +240,97 @@ func getKFreeSKBReasons(spec *btf.Spec) (map[uint64]string, error) {

return ret, nil
}

func getIfaces() (map[uint64]map[uint32]string, error) {
var err error
procPath := "/proc"

ifaceCache := make(map[uint64]map[uint32]string)

dirs, err := os.ReadDir(procPath)
if err != nil {
return nil, err
}

for _, d := range dirs {
if !d.IsDir() {
continue
}

// skip non-process dirs
if _, err := strconv.Atoi(d.Name()); err != nil {
continue
}

// get inode of netns
path := filepath.Join(procPath, d.Name(), "ns", "net")
fd, err0 := os.Open(path)
if err0 != nil {
err = errors.Join(err, err0)
continue
}
var stat unix.Stat_t
if err0 := unix.Fstat(int(fd.Fd()), &stat); err != nil {
err = errors.Join(err, err0)
continue
}
inode := stat.Ino

if _, exists := ifaceCache[inode]; exists {
continue // we already checked that netns
} else {
ifaceCache[inode] = make(map[uint32]string)
}

ifaces, err0 := getIfacesInNetNs(path)
if err0 != nil {
err = errors.Join(err, err0)
continue
}

ifaceCache[inode] = ifaces

}

return ifaceCache, err

}

func getIfacesInNetNs(path string) (map[uint32]string, error) {
current, err := netns.Get()
if err != nil {
return nil, err
}

remote, err := netns.GetFromPath(path)
if err != nil {
return nil, err
}

runtime.LockOSThread()
defer runtime.UnlockOSThread()

if err := netns.Set(remote); err != nil {
return nil, err
}

defer netns.Set(current)

conn, err := rtnetlink.Dial(nil)
if err != nil {
return nil, err
}
defer conn.Close()

msg, err := conn.Link.List()
if err != nil {
return nil, err
}

ifaces := make(map[uint32]string)
for _, link := range msg {
ifaces[link.Index] = link.Attributes.Name
}

return ifaces, nil
}

0 comments on commit 93ca1a5

Please sign in to comment.