Implement IFLA_IFNAME which specifies an interface's name.

IFLA_IFNAME is used to set an interface's name when being used
with RTM_NEWLINK and RTM_SETLINK. RTM_NEWLINK is the preferred way, it
is backwards compatible with RTM_SETLINK. For gVisor, when RTM_SETLINK is
introduced, it shall just share the implementation.

PiperOrigin-RevId: 647833657
This commit is contained in:
Jing Chen
2024-06-28 17:02:31 -07:00
committed by gVisor bot
parent a5573312e0
commit 59adcc9b1e
4 changed files with 93 additions and 9 deletions
+11 -9
View File
@@ -123,15 +123,13 @@ func (s *Stack) SetInterface(ctx context.Context, msg *nlmsg.Message) *syserr.Er
if len(value) < 1 {
return syserr.ErrInvalidArgument
}
if ifinfomsg.Index != 0 {
// Device name changing isn't supported yet.
return syserr.ErrNotSupported
}
ifname = value.String()
for idx, ifa := range s.Interfaces() {
if ifname == ifa.Name {
ifinfomsg.Index = idx
break
if ifinfomsg.Index == 0 {
ifname = value.String()
for idx, ifa := range s.Interfaces() {
if ifname == ifa.Name {
ifinfomsg.Index = idx
break
}
}
}
case linux.IFLA_MASTER:
@@ -190,6 +188,10 @@ func (s *Stack) setLink(id tcpip.NICID, linkAttrs map[uint16]nlmsg.BytesView) *s
if err := s.Stack.SetNICAddress(id, addr); err != nil {
return syserr.TranslateNetstackError(err)
}
case linux.IFLA_IFNAME:
if err := s.Stack.SetNICName(id, v.String()); err != nil {
return syserr.TranslateNetstackError(err)
}
}
}
return nil
+13
View File
@@ -1057,6 +1057,19 @@ func (s *Stack) SetNICAddress(id tcpip.NICID, addr tcpip.LinkAddress) tcpip.Erro
return nil
}
// SetNICName sets a NIC's name.
func (s *Stack) SetNICName(id tcpip.NICID, name string) tcpip.Error {
s.mu.Lock()
defer s.mu.Unlock()
nic, ok := s.nics[id]
if !ok {
return &tcpip.ErrUnknownNICID{}
}
nic.name = name
return nil
}
// NICInfo captures the name and addresses assigned to a NIC.
type NICInfo struct {
Name string
+11
View File
@@ -30,6 +30,17 @@ if ! wait_for ! ip link show test_veth02; then
exit 1
fi
# Create a new veth pair in the current namespace and rename the link.
ip link add name test_veth01 type veth peer name test_veth02
ip link set test_veth01 name test_veth03
ip link show test_veth03
ip link del name test_veth03
# Check that test_veth02 has been destroyed.
if ! wait_for ! ip link show test_veth02; then
fail "test_veth02 hasn't been destroyed"
exit 1
fi
ip netns add test
ip link add test_veth01 type veth peer name test_veth02 netns test
ip link show test_veth01
@@ -200,6 +200,64 @@ TEST(NetlinkRouteTest, GetLinkByIndex) {
EXPECT_TRUE(found) << "Netlink response does not contain any links.";
}
TEST(NetlinkRouteTest, ChangeLinkName) {
SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_NET_ADMIN)));
SKIP_IF(IsRunningWithHostinet());
// Hosts that run with old kernel allow renaming only when
// the interface is down. The restriction has been removed.
SKIP_IF(!IsRunningOnGvisor());
Link loopback_link = ASSERT_NO_ERRNO_AND_VALUE(LoopbackLink());
FileDescriptor fd =
ASSERT_NO_ERRNO_AND_VALUE(NetlinkBoundSocket(NETLINK_ROUTE));
struct request {
struct nlmsghdr hdr;
struct ifinfomsg ifm;
struct rtattr rtattr;
char ifname[IFNAMSIZ];
char pad[NLMSG_ALIGNTO + RTA_ALIGNTO];
};
const std::string new_linkname = "notloopback";
// Change the link name.
struct request req = {};
req.hdr.nlmsg_len = sizeof(req);
req.hdr.nlmsg_type = RTM_NEWLINK;
req.hdr.nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK;
req.hdr.nlmsg_seq = kSeq;
req.ifm.ifi_family = AF_UNSPEC;
req.ifm.ifi_index = loopback_link.index;
req.rtattr.rta_type = IFLA_IFNAME;
req.rtattr.rta_len = RTA_LENGTH(new_linkname.size() + 1);
strncpy(req.ifname, new_linkname.c_str(), sizeof(req.ifname));
req.hdr.nlmsg_len =
NLMSG_LENGTH(sizeof(req.ifm)) + NLMSG_ALIGN(req.rtattr.rta_len);
EXPECT_NO_ERRNO(NetlinkRequestAckOrError(fd, kSeq, &req, sizeof(req)));
// Search the link by the new name.
loopback_link.name = new_linkname;
req.hdr.nlmsg_type = RTM_GETLINK;
req.ifm.ifi_index = 0;
req.hdr.nlmsg_flags = NLM_F_REQUEST;
req.rtattr.rta_type = IFLA_IFNAME;
req.rtattr.rta_len = RTA_LENGTH(new_linkname.size() + 1);
strncpy(req.ifname, new_linkname.c_str(), sizeof(req.ifname));
req.hdr.nlmsg_len =
NLMSG_LENGTH(sizeof(req.ifm)) + NLMSG_ALIGN(req.rtattr.rta_len);
bool found = false;
ASSERT_NO_ERRNO(NetlinkRequestResponse(
fd, &req, sizeof(req),
[&](const struct nlmsghdr* hdr) {
CheckLinkMsg(hdr, loopback_link);
found = true;
},
false));
EXPECT_TRUE(found) << "Netlink response does not contain any links.";
}
TEST(NetlinkRouteTest, LinkUp) {
SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_NET_ADMIN)));
SKIP_IF(IsRunningWithHostinet());