/proc/[PID]/fdinfo/[FD] で参照される tap/tun デバイスの Linux ネットワーク名前空間を取得するには?

/proc/[PID]/fdinfo/[FD] で参照される tap/tun デバイスの Linux ネットワーク名前空間を取得するには?

TAP / TUNデバイスを使用しているプロセスに正しく一致させたいのですが。外部ioctl()これらのプロセスはTAP / TUNデバイスを使用します(つまり、プロセス自体内の特定のファイル記述子にアクセスできないため、sを実行できません)。

私は次の質問に対する答えを知っていますタブインターフェイスとそのファイル記述子間の接続を見つけるには?つまり、対応するTAP / TUNネットワークインターフェイスの名前を提供する追加のキーと値の/proc/[PID]/fdinfo/[FD]ペアがあります。iff:

ただし、ネットワークネームスペースには問題があります。特に、ユーザー空間プロセスがネットワーク名前空間に接続された後、TAP / TUNネットワークインタフェースがネットワーク名前空間内を移動する場合に問題があります。これにはtapclient単純なバリエーションがあります。a34729ttunclient.c、タブネットワーク名を受け入れて追加します):

$ sudo ip tuntap add tap123 mode tap
$ sudo tapclient tap123 &
$ sudo ip netns add fooz
$ sudo ip link set tap123 netns fooz
$ PID=$(ps faux | grep tapclient | grep -v -e sudo -e grep | awk '{print $2}')
$ sudo cat /proc/$PID/fdinfo/3

...その後、以下を提供します。iff: tap123- ただし、tap123ネットワークインターフェイスが現在配置されているネットワークネームスペースではありません。

もちろん、tap123すべてのネットワーク名前空間を繰り返して、それらの1つで一致するネットワークインターフェイスを見つけてそれを見つけることができます。残念ながら、tap123最初の名前を上記のネットワークネームスペースに移動したfooz後、ホストネームスペースに別の名前を作成した場合など、重複した名前がある可能性があります。

$ sudo ip tuntap add tap123 mode tap
$ ip link show tap123
$ sudo ip netns exec fooz ip link show tap123

だから私たちは今二つ tap123は別々のネットワークネームスペースにあり、fdinfoあいまいですiff: tap123

残念ながら、Tapclientのネットワークネームスペースを調べることは/proc/$PID/ns/net役に立ちません。現在のネットワーク名前空間と一致しなくなったためですtap123

$ findmnt -t nsfs | grep /run/netns/fooz
$ sudo readlink /proc/$PID/ns/net

たとえば、これはnet:[4026532591]vs。net:[4026531993]

tapclientプロセスが接続されている正しいネットワークインターフェイスインスタンスと明示的に一致する方法はありますか?tap123

答え1

名前の代わりにハードウェアアドレスでtunインターフェースを一致させることができます(ioctl(SIOCGIFHWADDR)tun / tapファイル記述子からアドレスを取得できます)。

これより簡単なことはないと思います。それ以外の場合、最近の変更は次のとおりです。これ(これにより、tun fdを介してインターフェイスのネットワークネームスペースを取得する必要がなくなり、許容される可能性が高くなります。

答え2

残念ながら、user313992のヒントSIOCGSKNSは非常にソケットに便利SIOCGSKNS、TAP/TUNファイル記述子の実装は次のとおりです。奇妙な:TAP / TUNが元々作成されたネットワークネームスペースのfdを返します。現在は該当しませんnetdevネットワークネームスペース。

もっと見る__tun_chr_ioctl実装された場合、SIOCGSKNS非常に有望であることがわかりました。TUNGETDEVNETNSioctlアクション:最終的にTAP / TUNデバイスのネットワーク名前空間を取得して返します。

次のユニットテストコードは、初期ネットワークネームスペースにTAPデバイスを作成し、新しいネットワークネームスペースを作成し、TAP netdevをこの新しいネットワークネームスペースに移動します。その後、ioctlは、TUNGETDEVNETNSTAP netdevが移動された新しいネットワーク名前空間を参照するfdを正しく返します。

package main

import (
    "os"
    "runtime"

    "github.com/thediveo/notwork/link"
    "github.com/thediveo/notwork/netns"
    "github.com/vishvananda/netlink"
    "golang.org/x/sys/unix"

    . "github.com/onsi/ginkgo/v2"
    . "github.com/onsi/gomega"
    . "github.com/thediveo/success"
)

const tapNamePrefix = "tap-"

// Ugly IOCTL stuff; copied from github.com/thediveo/lxkns/ops/ioctl.go
const _IOC_NRBITS = 8
const _IOC_TYPEBITS = 8
const _IOC_SIZEBITS = 14

const _IOC_NRSHIFT = 0
const _IOC_TYPESHIFT = _IOC_NRSHIFT + _IOC_NRBITS
const _IOC_SIZESHIFT = _IOC_TYPESHIFT + _IOC_TYPEBITS
const _IOC_DIRSHIFT = _IOC_SIZESHIFT + _IOC_SIZEBITS

const _IOC_NONE = uint(0)

func _IOC(dir, ioctype, nr, size uint) uint {
    return (dir << _IOC_DIRSHIFT) | (ioctype << _IOC_TYPESHIFT) | (nr << _IOC_NRSHIFT) | (size << _IOC_SIZESHIFT)
}

func _IO(ioctype, nr uint) uint {
    return _IOC(_IOC_NONE, ioctype, nr, 0)
}

func getTapNetdevNetnsFd(fd int) (int, error) {
    return unix.IoctlRetInt(fd, _IO('T', 227))
}

var _ = Describe("TAP/TUN netns", func() {

    It("finds namespace of TAP/TUN netdev", func() {
        runtime.LockOSThread()
        defer runtime.UnlockOSThread()

        By("creating a TAP netdev")
        tt := netlink.Tuntap{
            Mode:   netlink.TUNTAP_MODE_TAP,
            Queues: 1,
        }
        tap := link.NewTransient(&tt, tapNamePrefix).(*netlink.Tuntap)
        Expect(tap.Fds).NotTo(BeEmpty())
        for _, fd := range tap.Fds {
            DeferCleanup(func() { fd.Close() })
        }

        By("creating a new transient network namespace")
        newnetnsfd := netns.NewTransient()

        By("moving the TAP netdev into the new network namespace")
        Expect(netlink.LinkSetNsFd(tap, newnetnsfd)).To(Succeed())
        Expect(netlink.LinkList()).NotTo(ContainElement(
            HaveField("Attrs().Name", tap.Name)))
        nlh := netns.NewNetlinkHandle(newnetnsfd)
        defer func() {
            Expect(nlh.LinkSetNsPid(tap, os.Getpid())).To(Succeed())
            nlh.Close()
        }()
        Expect(nlh.LinkList()).To(ContainElement(
            HaveField("Attrs().Name", tap.Name)))

        By("querying the network namespace of the TAP netdev")
        tapnetnsfd := Successful(getTapNetdevNetnsFd(int(tap.Fds[0].Fd())))
        defer unix.Close(tapnetnsfd)

        Expect(netns.Ino(tapnetnsfd)).NotTo(Equal(netns.CurrentIno()))
        Expect(netns.Ino(tapnetnsfd)).To(Equal(netns.Ino(newnetnsfd)))
    })

})

関連情報