github.com/sagernet/netlink@v0.0.0-20240612041022-b9a21c07ac6a/netns_linux.go (about)

     1  package netlink
     2  
     3  // Network namespace ID functions
     4  //
     5  // The kernel has a weird concept called the network namespace ID.
     6  // This is different from the file reference in proc (and any bind-mounted
     7  // namespaces, etc.)
     8  //
     9  // Instead, namespaces can be assigned a numeric ID at any time. Once set,
    10  // the ID is fixed. The ID can either be set manually by the user, or
    11  // automatically, triggered by certain kernel actions. The most common kernel
    12  // action that triggers namespace ID creation is moving one end of a veth pair
    13  // in to that namespace.
    14  
    15  import (
    16  	"fmt"
    17  
    18  	"github.com/sagernet/netlink/nl"
    19  	"golang.org/x/sys/unix"
    20  )
    21  
    22  // These can be replaced by the values from sys/unix when it is next released.
    23  const (
    24  	_ = iota
    25  	NETNSA_NSID
    26  	NETNSA_PID
    27  	NETNSA_FD
    28  )
    29  
    30  // GetNetNsIdByPid looks up the network namespace ID for a given pid (really thread id).
    31  // Returns -1 if the namespace does not have an ID set.
    32  func (h *Handle) GetNetNsIdByPid(pid int) (int, error) {
    33  	return h.getNetNsId(NETNSA_PID, uint32(pid))
    34  }
    35  
    36  // GetNetNsIdByPid looks up the network namespace ID for a given pid (really thread id).
    37  // Returns -1 if the namespace does not have an ID set.
    38  func GetNetNsIdByPid(pid int) (int, error) {
    39  	return pkgHandle.GetNetNsIdByPid(pid)
    40  }
    41  
    42  // SetNetNSIdByPid sets the ID of the network namespace for a given pid (really thread id).
    43  // The ID can only be set for namespaces without an ID already set.
    44  func (h *Handle) SetNetNsIdByPid(pid, nsid int) error {
    45  	return h.setNetNsId(NETNSA_PID, uint32(pid), uint32(nsid))
    46  }
    47  
    48  // SetNetNSIdByPid sets the ID of the network namespace for a given pid (really thread id).
    49  // The ID can only be set for namespaces without an ID already set.
    50  func SetNetNsIdByPid(pid, nsid int) error {
    51  	return pkgHandle.SetNetNsIdByPid(pid, nsid)
    52  }
    53  
    54  // GetNetNsIdByFd looks up the network namespace ID for a given fd.
    55  // fd must be an open file descriptor to a namespace file.
    56  // Returns -1 if the namespace does not have an ID set.
    57  func (h *Handle) GetNetNsIdByFd(fd int) (int, error) {
    58  	return h.getNetNsId(NETNSA_FD, uint32(fd))
    59  }
    60  
    61  // GetNetNsIdByFd looks up the network namespace ID for a given fd.
    62  // fd must be an open file descriptor to a namespace file.
    63  // Returns -1 if the namespace does not have an ID set.
    64  func GetNetNsIdByFd(fd int) (int, error) {
    65  	return pkgHandle.GetNetNsIdByFd(fd)
    66  }
    67  
    68  // SetNetNSIdByFd sets the ID of the network namespace for a given fd.
    69  // fd must be an open file descriptor to a namespace file.
    70  // The ID can only be set for namespaces without an ID already set.
    71  func (h *Handle) SetNetNsIdByFd(fd, nsid int) error {
    72  	return h.setNetNsId(NETNSA_FD, uint32(fd), uint32(nsid))
    73  }
    74  
    75  // SetNetNSIdByFd sets the ID of the network namespace for a given fd.
    76  // fd must be an open file descriptor to a namespace file.
    77  // The ID can only be set for namespaces without an ID already set.
    78  func SetNetNsIdByFd(fd, nsid int) error {
    79  	return pkgHandle.SetNetNsIdByFd(fd, nsid)
    80  }
    81  
    82  // getNetNsId requests the netnsid for a given type-val pair
    83  // type should be either NETNSA_PID or NETNSA_FD
    84  func (h *Handle) getNetNsId(attrType int, val uint32) (int, error) {
    85  	req := h.newNetlinkRequest(unix.RTM_GETNSID, unix.NLM_F_REQUEST)
    86  
    87  	rtgen := nl.NewRtGenMsg()
    88  	req.AddData(rtgen)
    89  
    90  	b := make([]byte, 4)
    91  	native.PutUint32(b, val)
    92  	attr := nl.NewRtAttr(attrType, b)
    93  	req.AddData(attr)
    94  
    95  	msgs, err := req.Execute(unix.NETLINK_ROUTE, unix.RTM_NEWNSID)
    96  
    97  	if err != nil {
    98  		return 0, err
    99  	}
   100  
   101  	for _, m := range msgs {
   102  		msg := nl.DeserializeRtGenMsg(m)
   103  
   104  		attrs, err := nl.ParseRouteAttr(m[msg.Len():])
   105  		if err != nil {
   106  			return 0, err
   107  		}
   108  
   109  		for _, attr := range attrs {
   110  			switch attr.Attr.Type {
   111  			case NETNSA_NSID:
   112  				return int(int32(native.Uint32(attr.Value))), nil
   113  			}
   114  		}
   115  	}
   116  
   117  	return 0, fmt.Errorf("unexpected empty result")
   118  }
   119  
   120  // setNetNsId sets the netnsid for a given type-val pair
   121  // type should be either NETNSA_PID or NETNSA_FD
   122  // The ID can only be set for namespaces without an ID already set
   123  func (h *Handle) setNetNsId(attrType int, val uint32, newnsid uint32) error {
   124  	req := h.newNetlinkRequest(unix.RTM_NEWNSID, unix.NLM_F_REQUEST|unix.NLM_F_ACK)
   125  
   126  	rtgen := nl.NewRtGenMsg()
   127  	req.AddData(rtgen)
   128  
   129  	b := make([]byte, 4)
   130  	native.PutUint32(b, val)
   131  	attr := nl.NewRtAttr(attrType, b)
   132  	req.AddData(attr)
   133  
   134  	b1 := make([]byte, 4)
   135  	native.PutUint32(b1, newnsid)
   136  	attr1 := nl.NewRtAttr(NETNSA_NSID, b1)
   137  	req.AddData(attr1)
   138  
   139  	_, err := req.Execute(unix.NETLINK_ROUTE, unix.RTM_NEWNSID)
   140  	return err
   141  }