[PATCHv5 net-next 1/4] rtnetlink: add new helper rtnl_configure_link_notify()

Hangbin Liu
2022-09-30T09:45:36Z

This patch update rtnl_configure_link() to rtnl_configure_link_nlh() by
adding parameter netlink message header and port id so we can notify the
userspace about the new link info if NLM_F_ECHO flag is set.

The rtnl_configure_link() will be a wrapper of the new function. The new
call chain looks like:

- rtnl_configure_link_notify()
- __dev_notify_flags()
- rtmsg_ifinfo_nlh()
- rtmsg_ifinfo_event()
- rtmsg_ifinfo_build_skb()
- rtmsg_ifinfo_send()

All the functions in this call chain will add parameter nlh and pid, so
we can use them in the last call rtnl_notify().

Signed-off-by: Hangbin Liu <liuhangbin@gmail.com>
---
include/linux/netdevice.h | 2 +-
include/linux/rtnetlink.h | 6 ++++--
net/core/dev.c | 14 ++++++-------
net/core/rtnetlink.c | 41 ++++++++++++++++++++++++++-------------
4 files changed, 40 insertions(+), 23 deletions(-)

diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h
index eddf8ee270e7..a71d378945e3 100644
--- a/include/linux/netdevice.h
+++ b/include/linux/netdevice.h
@@ -3856,7 +3856,7 @@ int __dev_change_flags(struct net_device *dev, unsigned int flags,
int dev_change_flags(struct net_device *dev, unsigned int flags,
struct netlink_ext_ack *extack);
void __dev_notify_flags(struct net_device *, unsigned int old_flags,
- unsigned int gchanges);
+ unsigned int gchanges, u32 pid, struct nlmsghdr *nlh);
int dev_set_alias(struct net_device *, const char *, size_t);
int dev_get_alias(const struct net_device *, char *, size_t);
int __dev_change_net_namespace(struct net_device *dev, struct net *net,
diff --git a/include/linux/rtnetlink.h b/include/linux/rtnetlink.h
index ae2c6a3cec5d..ef703b4484a7 100644
--- a/include/linux/rtnetlink.h
+++ b/include/linux/rtnetlink.h
@@ -21,12 +21,14 @@ extern int rtnl_put_cacheinfo(struct sk_buff *skb, struct dst_entry *dst,
void rtmsg_ifinfo(int type, struct net_device *dev, unsigned change, gfp_t flags);
void rtmsg_ifinfo_newnet(int type, struct net_device *dev, unsigned int change,
gfp_t flags, int *new_nsid, int new_ifindex);
+void rtmsg_ifinfo_nlh(int type, struct net_device *dev, unsigned int change,
+ gfp_t flags, u32 pid, struct nlmsghdr *nlh);
struct sk_buff *rtmsg_ifinfo_build_skb(int type, struct net_device *dev,
unsigned change, u32 event,
gfp_t flags, int *new_nsid,
- int new_ifindex);
+ int new_ifindex, u32 pid, u32 seq);
void rtmsg_ifinfo_send(struct sk_buff *skb, struct net_device *dev,
- gfp_t flags);
+ gfp_t flags, u32 pid, struct nlmsghdr *nlh);

/* RTNL is used as a global lock for all changes to network configuration */
diff --git a/net/core/dev.c b/net/core/dev.c
index fa53830d0683..89cf082317dd 100644
--- a/net/core/dev.c
+++ b/net/core/dev.c
@@ -8347,7 +8347,7 @@ static int __dev_set_promiscuity(struct net_device *dev, int inc, bool notify)
dev_change_rx_flags(dev, IFF_PROMISC);
}
if (notify)
- __dev_notify_flags(dev, old_flags, IFF_PROMISC);
+ __dev_notify_flags(dev, old_flags, IFF_PROMISC, 0, NULL);
return 0;
}

@@ -8402,7 +8402,7 @@ static int __dev_set_allmulti(struct net_device *dev, int inc, bool notify)
dev_set_rx_mode(dev);
if (notify)
__dev_notify_flags(dev, old_flags,
- dev->gflags ^ old_gflags);
+ dev->gflags ^ old_gflags, 0, NULL);
}
return 0;
}
@@ -8565,12 +8565,12 @@ int __dev_change_flags(struct net_device *dev, unsigned int flags,
}

void __dev_notify_flags(struct net_device *dev, unsigned int old_flags,
- unsigned int gchanges)
+ unsigned int gchanges, u32 pid, struct nlmsghdr *nlh)
{
unsigned int changes = dev->flags ^ old_flags;

if (gchanges)
- rtmsg_ifinfo(RTM_NEWLINK, dev, gchanges, GFP_ATOMIC);
+ rtmsg_ifinfo_nlh(RTM_NEWLINK, dev, gchanges, GFP_ATOMIC, pid, nlh);

if (changes & IFF_UP) {
if (dev->flags & IFF_UP)
@@ -8612,7 +8612,7 @@ int dev_change_flags(struct net_device *dev, unsigned int flags,
return ret;

changes = (old_flags ^ dev->flags) | (old_gflags ^ dev->gflags);
- __dev_notify_flags(dev, old_flags, changes);
+ __dev_notify_flags(dev, old_flags, changes, 0, NULL);
return ret;
}
EXPORT_SYMBOL(dev_change_flags);
@@ -10845,7 +10845,7 @@ void unregister_netdevice_many(struct list_head *head)
if (!dev->rtnl_link_ops ||
dev->rtnl_link_state == RTNL_LINK_INITIALIZED)
skb = rtmsg_ifinfo_build_skb(RTM_DELLINK, dev, ~0U, 0,
- GFP_KERNEL, NULL, 0);
+ GFP_KERNEL, NULL, 0, 0, 0);

/*
* Flush the unicast and multicast chains
@@ -10860,7 +10860,7 @@ void unregister_netdevice_many(struct list_head *head)
dev->netdev_ops->ndo_uninit(dev);

if (skb)
- rtmsg_ifinfo_send(skb, dev, GFP_KERNEL);
+ rtmsg_ifinfo_send(skb, dev, GFP_KERNEL, 0, NULL);

/* Notifier chain MUST detach us all upper devices. */
WARN_ON(netdev_has_any_upper_dev(dev));
diff --git a/net/core/rtnetlink.c b/net/core/rtnetlink.c
index 74864dc46a7e..1558921bd4da 100644
--- a/net/core/rtnetlink.c
+++ b/net/core/rtnetlink.c
@@ -3180,7 +3180,8 @@ static int rtnl_dellink(struct sk_buff *skb, struct nlmsghdr *nlh,
return err;
}

-int rtnl_configure_link(struct net_device *dev, const struct ifinfomsg *ifm)
+static int rtnl_configure_link_notify(struct net_device *dev, const struct ifinfomsg *ifm,
+ struct nlmsghdr *nlh, u32 pid)
{
unsigned int old_flags;
int err;
@@ -3194,13 +3195,18 @@ int rtnl_configure_link(struct net_device *dev, const struct ifinfomsg *ifm)
}

if (dev->rtnl_link_state == RTNL_LINK_INITIALIZED) {
- __dev_notify_flags(dev, old_flags, (old_flags ^ dev->flags));
+ __dev_notify_flags(dev, old_flags, (old_flags ^ dev->flags), pid, nlh);
} else {
dev->rtnl_link_state = RTNL_LINK_INITIALIZED;
- __dev_notify_flags(dev, old_flags, ~0U);
+ __dev_notify_flags(dev, old_flags, ~0U, pid, nlh);
}
return 0;
}
+
+int rtnl_configure_link(struct net_device *dev, const struct ifinfomsg *ifm)
+{
+ return rtnl_configure_link_notify(dev, ifm, NULL, 0);
+}
EXPORT_SYMBOL(rtnl_configure_link);

struct net_device *rtnl_create_link(struct net *net, const char *ifname,
@@ -3896,7 +3902,7 @@ static int rtnl_dump_all(struct sk_buff *skb, struct netlink_callback *cb)
struct sk_buff *rtmsg_ifinfo_build_skb(int type, struct net_device *dev,
unsigned int change,
u32 event, gfp_t flags, int *new_nsid,
- int new_ifindex)
+ int new_ifindex, u32 pid, u32 seq)
{
struct net *net = dev_net(dev);
struct sk_buff *skb;
@@ -3907,7 +3913,7 @@ struct sk_buff *rtmsg_ifinfo_build_skb(int type, struct net_device *dev,
goto errout;

err = rtnl_fill_ifinfo(skb, dev, dev_net(dev),
- type, 0, 0, change, 0, 0, event,
+ type, pid, seq, change, 0, 0, event,
new_nsid, new_ifindex, -1, flags);
if (err < 0) {
/* -EMSGSIZE implies BUG in if_nlmsg_size() */
@@ -3922,16 +3928,18 @@ struct sk_buff *rtmsg_ifinfo_build_skb(int type, struct net_device *dev,
return NULL;
}

-void rtmsg_ifinfo_send(struct sk_buff *skb, struct net_device *dev, gfp_t flags)
+void rtmsg_ifinfo_send(struct sk_buff *skb, struct net_device *dev, gfp_t flags,
+ u32 pid, struct nlmsghdr *nlh)
{
struct net *net = dev_net(dev);

- rtnl_notify(skb, net, 0, RTNLGRP_LINK, NULL, flags);
+ rtnl_notify(skb, net, pid, RTNLGRP_LINK, nlh, flags);
}

static void rtmsg_ifinfo_event(int type, struct net_device *dev,
unsigned int change, u32 event,
- gfp_t flags, int *new_nsid, int new_ifindex)
+ gfp_t flags, int *new_nsid, int new_ifindex,
+ u32 pid, struct nlmsghdr *nlh)
{
struct sk_buff *skb;

@@ -3939,23 +3947,30 @@ static void rtmsg_ifinfo_event(int type, struct net_device *dev,
return;

skb = rtmsg_ifinfo_build_skb(type, dev, change, event, flags, new_nsid,
- new_ifindex);
+ new_ifindex, pid, nlh ? nlh->nlmsg_seq : 0);
if (skb)
- rtmsg_ifinfo_send(skb, dev, flags);
+ rtmsg_ifinfo_send(skb, dev, flags, pid, nlh);
}

void rtmsg_ifinfo(int type, struct net_device *dev, unsigned int change,
gfp_t flags)
{
rtmsg_ifinfo_event(type, dev, change, rtnl_get_event(0), flags,
- NULL, 0);
+ NULL, 0, 0, NULL);
}

void rtmsg_ifinfo_newnet(int type, struct net_device *dev, unsigned int change,
gfp_t flags, int *new_nsid, int new_ifindex)
{
rtmsg_ifinfo_event(type, dev, change, rtnl_get_event(0), flags,
- new_nsid, new_ifindex);
+ new_nsid, new_ifindex, 0, NULL);
+}
+
+void rtmsg_ifinfo_nlh(int type, struct net_device *dev, unsigned int change,
+ gfp_t flags, u32 pid, struct nlmsghdr *nlh)
+{
+ rtmsg_ifinfo_event(type, dev, change, rtnl_get_event(0), flags,
+ NULL, 0, pid, nlh);
}

static int nlmsg_populate_fdb_fill(struct sk_buff *skb,
@@ -6140,7 +6155,7 @@ static int rtnetlink_event(struct notifier_block *this, unsigned long event, voi
case NETDEV_CHANGELOWERSTATE:
case NETDEV_CHANGE_TX_QUEUE_LEN:
rtmsg_ifinfo_event(RTM_NEWLINK, dev, 0, rtnl_get_event(event),
- GFP_KERNEL, NULL, 0);
+ GFP_KERNEL, NULL, 0, 0, NULL);
break;
default:
break;
--
2.37.2