55package pwru
66
77import (
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
3744func 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+
168196func 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