Skip to content

Commit 93ca1a5

Browse files
committed
Output iface name when --output-meta is set
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]>
1 parent e98de68 commit 93ca1a5

File tree

1 file changed

+123
-1
lines changed

1 file changed

+123
-1
lines changed

internal/pwru/output.go

Lines changed: 123 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,18 +5,24 @@
55
package pwru
66

77
import (
8+
"errors"
89
"fmt"
910
"io"
1011
"log"
1112
"net"
1213
"os"
14+
"path/filepath"
1315
"runtime"
16+
"strconv"
1417
"syscall"
1518
"time"
1619

1720
"github.com/cilium/ebpf"
1821
"github.com/cilium/ebpf/btf"
22+
"github.com/jsimonetti/rtnetlink"
1923
ps "github.com/mitchellh/go-ps"
24+
"github.com/vishvananda/netns"
25+
"golang.org/x/sys/unix"
2026

2127
"github.com/cilium/pwru/internal/byteorder"
2228
)
@@ -32,6 +38,7 @@ type output struct {
3238
writer io.Writer
3339
kprobeMulti bool
3440
kfreeReasons map[uint64]string
41+
ifaceCache map[uint64]map[uint32]string
3542
}
3643

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

62+
var ifs map[uint64]map[uint32]string
63+
if flags.OutputMeta {
64+
ifs, err = getIfaces()
65+
if err != nil {
66+
log.Printf("Failed to retrieve all ifaces from all network namespaces: %v. Some iface names might be not shown.", err)
67+
}
68+
}
69+
5570
return &output{
5671
flags: flags,
5772
lastSeenSkb: map[uint64]uint64{},
@@ -61,6 +76,7 @@ func NewOutput(flags *Flags, printSkbMap *ebpf.Map, printStackMap *ebpf.Map,
6176
writer: writer,
6277
kprobeMulti: kprobeMulti,
6378
kfreeReasons: reasons,
79+
ifaceCache: ifs,
6480
}, nil
6581
}
6682

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

134150
if o.flags.OutputMeta {
135-
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)
151+
fmt.Fprintf(o.writer, " netns=%d mark=0x%x iface=%s proto=%x mtu=%d len=%d",
152+
event.Meta.Netns, event.Meta.Mark,
153+
o.getIfaceName(event.Meta.Netns, event.Meta.Ifindex),
154+
event.Meta.Proto, event.Meta.MTU, event.Meta.Len)
136155
}
137156

138157
if o.flags.OutputTuple {
@@ -165,6 +184,15 @@ func (o *output) Print(event *Event) {
165184
fmt.Fprintln(o.writer)
166185
}
167186

187+
func (o *output) getIfaceName(netnsInode, ifindex uint32) string {
188+
if ifaces, ok := o.ifaceCache[uint64(netnsInode)]; ok {
189+
if name, ok := ifaces[ifindex]; ok {
190+
return fmt.Sprintf("%d(%s)", ifindex, name)
191+
}
192+
}
193+
return fmt.Sprintf("%d", ifindex)
194+
}
195+
168196
func protoToStr(proto uint8) string {
169197
switch proto {
170198
case syscall.IPPROTO_TCP:
@@ -212,3 +240,97 @@ func getKFreeSKBReasons(spec *btf.Spec) (map[uint64]string, error) {
212240

213241
return ret, nil
214242
}
243+
244+
func getIfaces() (map[uint64]map[uint32]string, error) {
245+
var err error
246+
procPath := "/proc"
247+
248+
ifaceCache := make(map[uint64]map[uint32]string)
249+
250+
dirs, err := os.ReadDir(procPath)
251+
if err != nil {
252+
return nil, err
253+
}
254+
255+
for _, d := range dirs {
256+
if !d.IsDir() {
257+
continue
258+
}
259+
260+
// skip non-process dirs
261+
if _, err := strconv.Atoi(d.Name()); err != nil {
262+
continue
263+
}
264+
265+
// get inode of netns
266+
path := filepath.Join(procPath, d.Name(), "ns", "net")
267+
fd, err0 := os.Open(path)
268+
if err0 != nil {
269+
err = errors.Join(err, err0)
270+
continue
271+
}
272+
var stat unix.Stat_t
273+
if err0 := unix.Fstat(int(fd.Fd()), &stat); err != nil {
274+
err = errors.Join(err, err0)
275+
continue
276+
}
277+
inode := stat.Ino
278+
279+
if _, exists := ifaceCache[inode]; exists {
280+
continue // we already checked that netns
281+
} else {
282+
ifaceCache[inode] = make(map[uint32]string)
283+
}
284+
285+
ifaces, err0 := getIfacesInNetNs(path)
286+
if err0 != nil {
287+
err = errors.Join(err, err0)
288+
continue
289+
}
290+
291+
ifaceCache[inode] = ifaces
292+
293+
}
294+
295+
return ifaceCache, err
296+
297+
}
298+
299+
func getIfacesInNetNs(path string) (map[uint32]string, error) {
300+
current, err := netns.Get()
301+
if err != nil {
302+
return nil, err
303+
}
304+
305+
remote, err := netns.GetFromPath(path)
306+
if err != nil {
307+
return nil, err
308+
}
309+
310+
runtime.LockOSThread()
311+
defer runtime.UnlockOSThread()
312+
313+
if err := netns.Set(remote); err != nil {
314+
return nil, err
315+
}
316+
317+
defer netns.Set(current)
318+
319+
conn, err := rtnetlink.Dial(nil)
320+
if err != nil {
321+
return nil, err
322+
}
323+
defer conn.Close()
324+
325+
msg, err := conn.Link.List()
326+
if err != nil {
327+
return nil, err
328+
}
329+
330+
ifaces := make(map[uint32]string)
331+
for _, link := range msg {
332+
ifaces[link.Index] = link.Attributes.Name
333+
}
334+
335+
return ifaces, nil
336+
}

0 commit comments

Comments
 (0)