Add functions to get/set link flags, MAC address, and MTU using netlink
RTM_GETLINK/RTM_SETLINK messages instead of ioctl.

These will be used in the next commits for a more robust solution that
does not rely on interface names.

Signed-off-by: Robin Jarry <[email protected]>
---
 drivers/net/tap/tap_netlink.c | 291 ++++++++++++++++++++++++++++++++++
 drivers/net/tap/tap_netlink.h |  10 +-
 2 files changed, 299 insertions(+), 2 deletions(-)

diff --git a/drivers/net/tap/tap_netlink.c b/drivers/net/tap/tap_netlink.c
index 5ff60f41d426..20bdbe5f7df8 100644
--- a/drivers/net/tap/tap_netlink.c
+++ b/drivers/net/tap/tap_netlink.c
@@ -6,6 +6,7 @@
 #include <errno.h>
 #include <inttypes.h>
 #include <linux/netlink.h>
+#include <net/if.h>
 #include <string.h>
 #include <sys/socket.h>
 #include <unistd.h>
@@ -411,3 +412,293 @@ tap_nlattr_nested_finish(struct tap_nlmsg *msg)
 
        rte_free(tail);
 }
+
+/**
+ * Helper structure to pass data between netlink request and callback
+ */
+struct link_info_ctx {
+       struct ifinfomsg *info;
+       struct rte_ether_addr *mac;
+       unsigned int *flags;
+       unsigned int ifindex;
+       int found;
+};
+
+/**
+ * Callback to extract link information from RTM_GETLINK response
+ */
+static int
+tap_nl_link_cb(struct nlmsghdr *nh, void *arg)
+{
+       struct link_info_ctx *ctx = arg;
+       struct ifinfomsg *ifi = NLMSG_DATA(nh);
+       struct rtattr *rta;
+       int rta_len;
+
+       if (nh->nlmsg_type != RTM_NEWLINK)
+               return 0;
+
+       /* Check if this is the interface we're looking for */
+       if (ifi->ifi_index != (int)ctx->ifindex)
+               return 0;
+
+       ctx->found = 1;
+
+       /* Copy basic info if requested */
+       if (ctx->info)
+               *ctx->info = *ifi;
+
+       /* Extract flags if requested */
+       if (ctx->flags)
+               *ctx->flags = ifi->ifi_flags;
+
+       /* Parse attributes for MAC address if requested */
+       if (ctx->mac) {
+               rta = IFLA_RTA(ifi);
+               rta_len = IFLA_PAYLOAD(nh);
+
+               for (; RTA_OK(rta, rta_len); rta = RTA_NEXT(rta, rta_len)) {
+                       if (rta->rta_type == IFLA_ADDRESS) {
+                               if (RTA_PAYLOAD(rta) >= RTE_ETHER_ADDR_LEN)
+                                       memcpy(ctx->mac, RTA_DATA(rta),
+                                              RTE_ETHER_ADDR_LEN);
+                               break;
+                       }
+               }
+       }
+
+       return 0;
+}
+
+/**
+ * Get interface flags by ifindex
+ *
+ * @param nlsk_fd
+ *   Netlink socket file descriptor
+ * @param ifindex
+ *   Interface index
+ * @param flags
+ *   Pointer to store interface flags
+ *
+ * @return
+ *   0 on success, -1 on error
+ */
+int
+tap_nl_get_link_flags(int nlsk_fd, unsigned int ifindex, unsigned int *flags)
+{
+       struct {
+               struct nlmsghdr nh;
+               struct ifinfomsg ifi;
+       } req = {
+               .nh = {
+                       .nlmsg_len = NLMSG_LENGTH(sizeof(struct ifinfomsg)),
+                       .nlmsg_type = RTM_GETLINK,
+                       .nlmsg_flags = NLM_F_REQUEST,
+               },
+               .ifi = {
+                       .ifi_family = AF_UNSPEC,
+                       .ifi_index = ifindex,
+               },
+       };
+       struct link_info_ctx ctx = {
+               .flags = flags,
+               .ifindex = ifindex,
+               .found = 0,
+       };
+
+       if (tap_nl_send(nlsk_fd, &req.nh) < 0)
+               return -1;
+
+       if (tap_nl_recv(nlsk_fd, tap_nl_link_cb, &ctx) < 0)
+               return -1;
+
+       if (!ctx.found) {
+               errno = ENODEV;
+               return -1;
+       }
+
+       return 0;
+}
+
+/**
+ * Set interface flags by ifindex
+ *
+ * @param nlsk_fd
+ *   Netlink socket file descriptor
+ * @param ifindex
+ *   Interface index
+ * @param flags
+ *   Flags to set/unset
+ * @param set
+ *   1 to set flags, 0 to unset them
+ *
+ * @return
+ *   0 on success, -1 on error
+ */
+int
+tap_nl_set_link_flags(int nlsk_fd, unsigned int ifindex, unsigned int flags, 
int set)
+{
+       struct {
+               struct nlmsghdr nh;
+               struct ifinfomsg ifi;
+       } req = {
+               .nh = {
+                       .nlmsg_len = NLMSG_LENGTH(sizeof(struct ifinfomsg)),
+                       .nlmsg_type = RTM_SETLINK,
+                       .nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK,
+               },
+               .ifi = {
+                       .ifi_family = AF_UNSPEC,
+                       .ifi_index = ifindex,
+                       .ifi_flags = set ? flags : 0,
+                       .ifi_change = flags,  /* mask of flags to change */
+               },
+       };
+
+       if (tap_nl_send(nlsk_fd, &req.nh) < 0)
+               return -1;
+
+       return tap_nl_recv_ack(nlsk_fd);
+}
+
+/**
+ * Set interface MTU by ifindex
+ *
+ * @param nlsk_fd
+ *   Netlink socket file descriptor
+ * @param ifindex
+ *   Interface index
+ * @param mtu
+ *   New MTU value
+ *
+ * @return
+ *   0 on success, -1 on error
+ */
+int
+tap_nl_set_link_mtu(int nlsk_fd, unsigned int ifindex, unsigned int mtu)
+{
+       struct {
+               struct nlmsghdr nh;
+               struct ifinfomsg ifi;
+               char buf[64];
+       } req = {
+               .nh = {
+                       .nlmsg_len = NLMSG_LENGTH(sizeof(struct ifinfomsg)),
+                       .nlmsg_type = RTM_SETLINK,
+                       .nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK,
+               },
+               .ifi = {
+                       .ifi_family = AF_UNSPEC,
+                       .ifi_index = ifindex,
+               },
+       };
+       struct rtattr *rta;
+
+       /* Add MTU attribute */
+       rta = (struct rtattr *)((char *)&req + NLMSG_ALIGN(req.nh.nlmsg_len));
+       rta->rta_type = IFLA_MTU;
+       rta->rta_len = RTA_LENGTH(sizeof(mtu));
+       memcpy(RTA_DATA(rta), &mtu, sizeof(mtu));
+       req.nh.nlmsg_len = NLMSG_ALIGN(req.nh.nlmsg_len) + 
RTA_ALIGN(rta->rta_len);
+
+       if (tap_nl_send(nlsk_fd, &req.nh) < 0)
+               return -1;
+
+       return tap_nl_recv_ack(nlsk_fd);
+}
+
+/**
+ * Get interface MAC address by ifindex
+ *
+ * @param nlsk_fd
+ *   Netlink socket file descriptor
+ * @param ifindex
+ *   Interface index
+ * @param mac
+ *   Pointer to store MAC address
+ *
+ * @return
+ *   0 on success, -1 on error
+ */
+int
+tap_nl_get_link_mac(int nlsk_fd, unsigned int ifindex, struct rte_ether_addr 
*mac)
+{
+       struct {
+               struct nlmsghdr nh;
+               struct ifinfomsg ifi;
+       } req = {
+               .nh = {
+                       .nlmsg_len = NLMSG_LENGTH(sizeof(struct ifinfomsg)),
+                       .nlmsg_type = RTM_GETLINK,
+                       .nlmsg_flags = NLM_F_REQUEST,
+               },
+               .ifi = {
+                       .ifi_family = AF_UNSPEC,
+                       .ifi_index = ifindex,
+               },
+       };
+       struct link_info_ctx ctx = {
+               .mac = mac,
+               .ifindex = ifindex,
+               .found = 0,
+       };
+
+       if (tap_nl_send(nlsk_fd, &req.nh) < 0)
+               return -1;
+
+       if (tap_nl_recv(nlsk_fd, tap_nl_link_cb, &ctx) < 0)
+               return -1;
+
+       if (!ctx.found) {
+               errno = ENODEV;
+               return -1;
+       }
+
+       return 0;
+}
+
+/**
+ * Set interface MAC address by ifindex
+ *
+ * @param nlsk_fd
+ *   Netlink socket file descriptor
+ * @param ifindex
+ *   Interface index
+ * @param mac
+ *   New MAC address
+ *
+ * @return
+ *   0 on success, -1 on error
+ */
+int
+tap_nl_set_link_mac(int nlsk_fd, unsigned int ifindex, const struct 
rte_ether_addr *mac)
+{
+       struct {
+               struct nlmsghdr nh;
+               struct ifinfomsg ifi;
+               char buf[64];
+       } req = {
+               .nh = {
+                       .nlmsg_len = NLMSG_LENGTH(sizeof(struct ifinfomsg)),
+                       .nlmsg_type = RTM_SETLINK,
+                       .nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK,
+               },
+               .ifi = {
+                       .ifi_family = AF_UNSPEC,
+                       .ifi_index = ifindex,
+               },
+       };
+       struct rtattr *rta;
+
+       /* Add MAC address attribute */
+       rta = (struct rtattr *)((char *)&req + NLMSG_ALIGN(req.nh.nlmsg_len));
+       rta->rta_type = IFLA_ADDRESS;
+       rta->rta_len = RTA_LENGTH(RTE_ETHER_ADDR_LEN);
+       memcpy(RTA_DATA(rta), mac, RTE_ETHER_ADDR_LEN);
+       req.nh.nlmsg_len = NLMSG_ALIGN(req.nh.nlmsg_len) + 
RTA_ALIGN(rta->rta_len);
+
+       if (tap_nl_send(nlsk_fd, &req.nh) < 0)
+               return -1;
+
+       return tap_nl_recv_ack(nlsk_fd);
+}
diff --git a/drivers/net/tap/tap_netlink.h b/drivers/net/tap/tap_netlink.h
index 5eff6edbb1cd..e9c9e5dce553 100644
--- a/drivers/net/tap/tap_netlink.h
+++ b/drivers/net/tap/tap_netlink.h
@@ -6,12 +6,11 @@
 #ifndef _TAP_NETLINK_H_
 #define _TAP_NETLINK_H_
 
-#include <ctype.h>
 #include <inttypes.h>
 #include <linux/rtnetlink.h>
 #include <linux/netlink.h>
-#include <stdio.h>
 
+#include <rte_ether.h>
 #include <rte_log.h>
 
 #define NLMSG_BUF 512
@@ -39,4 +38,11 @@ void tap_nlattr_add32(struct tap_nlmsg *msg, unsigned short 
type, uint32_t data)
 int tap_nlattr_nested_start(struct tap_nlmsg *msg, uint16_t type);
 void tap_nlattr_nested_finish(struct tap_nlmsg *msg);
 
+/* Link management functions using netlink */
+int tap_nl_get_link_flags(int nlsk_fd, unsigned int ifindex, unsigned int 
*flags);
+int tap_nl_set_link_flags(int nlsk_fd, unsigned int ifindex, unsigned int 
flags, int set);
+int tap_nl_set_link_mtu(int nlsk_fd, unsigned int ifindex, unsigned int mtu);
+int tap_nl_set_link_mac(int nlsk_fd, unsigned int ifindex, const struct 
rte_ether_addr *mac);
+int tap_nl_get_link_mac(int nlsk_fd, unsigned int ifindex, struct 
rte_ether_addr *mac);
+
 #endif /* _TAP_NETLINK_H_ */
-- 
2.51.0

Reply via email to