/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간단한 변형이 있습니다 .a34729t의tunclient.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모든 네트워크 네임스페이스를 반복하고 그 중 하나에서 일치하는 네트워크 인터페이스를 찾아 이를 찾을 수 있습니다. 불행하게도 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)))
    })

})

관련 정보