Skip site navigation (1)Skip section navigation (2)
Date:      Fri, 9 Dec 2022 11:22:37 GMT
From:      "Alexander V. Chernikov" <melifaro@FreeBSD.org>
To:        src-committers@FreeBSD.org, dev-commits-src-all@FreeBSD.org, dev-commits-src-main@FreeBSD.org
Subject:   git: 1bcd230f9508 - main - netlink: add interface notification on link status / flags change.
Message-ID:  <202212091122.2B9BMbua014344@gitrepo.freebsd.org>

next in thread | raw e-mail | index | archive | help
The branch main has been updated by melifaro:

URL: https://cgit.FreeBSD.org/src/commit/?id=1bcd230f9508b3c917f26be4b905e4b5141decea

commit 1bcd230f9508b3c917f26be4b905e4b5141decea
Author:     Alexander V. Chernikov <melifaro@FreeBSD.org>
AuthorDate: 2022-12-03 17:10:50 +0000
Commit:     Alexander V. Chernikov <melifaro@FreeBSD.org>
CommitDate: 2022-12-09 11:20:07 +0000

    netlink: add interface notification on link status / flags change.
    
    * Add link-state change notifications by subscribing to ifnet_link_event.
     In the Linux netlink model, link state is reported in 2 places: first is
     the IFLA_OPERSTATE, which stores state per RFC2863.
     The second is an IFF_LOWER_UP interface flag. As many applications rely
     on the latter, reserve 1 bit from if_flags, named as IFF_NETLINK_1.
     This flag is mapped to IFF_LOWER_UP in the netlink headers. This is done
     to avoid making applications think this flag is actually
     supported / presented in non-netlink outputs.
    * Add flag change notifications, by hooking into rt_ifmsg().
     In the netlink model, notification should include the bitmask for the
     change flags. Update rt_ifmsg() to include such bitmask.
    
    Differential Revision: https://reviews.freebsd.org/D37597
---
 sys/dev/usb/usb_pf.c             |  2 +-
 sys/net/if.c                     | 10 +++----
 sys/net/if.h                     |  1 +
 sys/net/route.c                  | 18 +++++++++++-
 sys/net/route.h                  |  2 +-
 sys/net/route/route_ctl.h        |  2 ++
 sys/net/rtsock.c                 | 10 +++++--
 sys/net80211/ieee80211_freebsd.c |  4 +--
 sys/net80211/ieee80211_proto.c   |  4 +--
 sys/net80211/ieee80211_proto.h   |  2 +-
 sys/netlink/netlink_route.c      |  5 +++-
 sys/netlink/route/iface.c        | 59 ++++++++++++++++++++++++++--------------
 sys/netlink/route/interface.h    |  3 ++
 sys/netlink/route/route_var.h    |  1 +
 14 files changed, 86 insertions(+), 37 deletions(-)

diff --git a/sys/dev/usb/usb_pf.c b/sys/dev/usb/usb_pf.c
index d0b7f0889cea..54b5b0d55a99 100644
--- a/sys/dev/usb/usb_pf.c
+++ b/sys/dev/usb/usb_pf.c
@@ -209,7 +209,7 @@ usbpf_clone_create(struct if_clone *ifc, char *name, size_t len,
 	ifp->if_ioctl = usbpf_ioctl;
 	if_attach(ifp);
 	ifp->if_flags |= IFF_UP;
-	rt_ifmsg(ifp);
+	rt_ifmsg(ifp, IFF_UP);
 	/*
 	 * XXX According to the specification of DLT_USB, it indicates
 	 * packets beginning with USB setup header. But not sure all
diff --git a/sys/net/if.c b/sys/net/if.c
index 8333c85b5eb1..7cbb750d3ab1 100644
--- a/sys/net/if.c
+++ b/sys/net/if.c
@@ -2138,7 +2138,7 @@ if_unroute(struct ifnet *ifp, int flag, int fam)
 
 	if (ifp->if_carp)
 		(*carp_linkstate_p)(ifp);
-	rt_ifmsg(ifp);
+	rt_ifmsg(ifp, IFF_UP);
 }
 
 /*
@@ -2155,7 +2155,7 @@ if_route(struct ifnet *ifp, int flag, int fam)
 	getmicrotime(&ifp->if_lastchange);
 	if (ifp->if_carp)
 		(*carp_linkstate_p)(ifp);
-	rt_ifmsg(ifp);
+	rt_ifmsg(ifp, IFF_UP);
 #ifdef INET6
 	in6_if_up(ifp);
 #endif
@@ -2199,7 +2199,7 @@ do_link_state_change(void *arg, int pending)
 	link_state = ifp->if_link_state;
 
 	CURVNET_SET(ifp->if_vnet);
-	rt_ifmsg(ifp);
+	rt_ifmsg(ifp, 0);
 	if (ifp->if_vlantrunk != NULL)
 		(*vlan_link_state_p)(ifp);
 
@@ -2845,7 +2845,7 @@ ifhwioctl(u_long cmd, struct ifnet *ifp, caddr_t data, struct thread *td)
 		error = (*ifp->if_ioctl)(ifp, cmd, data);
 		if (error == 0) {
 			getmicrotime(&ifp->if_lastchange);
-			rt_ifmsg(ifp);
+			rt_ifmsg(ifp, 0);
 #ifdef INET
 			DEBUGNET_NOTIFY_MTU(ifp);
 #endif
@@ -3274,7 +3274,7 @@ if_setflag(struct ifnet *ifp, int flag, int pflag, int *refcount, int onswitch)
 	if (error)
 		goto recover;
 	/* Notify userland that interface flags have changed */
-	rt_ifmsg(ifp);
+	rt_ifmsg(ifp, flag);
 	return (0);
 
 recover:
diff --git a/sys/net/if.h b/sys/net/if.h
index a559219d700b..117b0e14ef05 100644
--- a/sys/net/if.h
+++ b/sys/net/if.h
@@ -164,6 +164,7 @@ struct if_data {
 #define	IFF_DYING	0x200000	/* (n) interface is winding down */
 #define	IFF_RENAMING	0x400000	/* (n) interface is being renamed */
 #define	IFF_NOGROUP	0x800000	/* (n) interface is not part of any groups */
+#define	IFF_NETLINK_1	0x1000000	/* (n) used by netlink */
 
 /*
  * Old names for driver flags so that user space tools can continue to use
diff --git a/sys/net/route.c b/sys/net/route.c
index 9773f899f5af..b79e8e6e2a66 100644
--- a/sys/net/route.c
+++ b/sys/net/route.c
@@ -695,12 +695,28 @@ rt_routemsg_info(int cmd, struct rt_addrinfo *info, int fibnum)
 	return (rtsock_routemsg_info(cmd, info, fibnum));
 }
 
+void
+rt_ifmsg(struct ifnet *ifp, int if_flags_mask)
+{
+	rtsock_callback_p->ifmsg_f(ifp, if_flags_mask);
+	netlink_callback_p->ifmsg_f(ifp, if_flags_mask);
+}
+
 /* Netlink-related callbacks needed to glue rtsock, netlink and linuxolator */
 static void
 ignore_route_event(uint32_t fibnum, const struct rib_cmd_info *rc)
 {
 }
-static struct rtbridge ignore_cb = { .route_f = ignore_route_event };
+
+static void
+ignore_ifmsg_event(struct ifnet *ifp, int if_flags_mask)
+{
+}
+
+static struct rtbridge ignore_cb = {
+	.route_f = ignore_route_event,
+	.ifmsg_f = ignore_ifmsg_event,
+};
 
 void *linux_netlink_p = NULL; /* Callback pointer for Linux translator functions */
 struct rtbridge *rtsock_callback_p = &ignore_cb;
diff --git a/sys/net/route.h b/sys/net/route.h
index 87cbbbf1d83d..34a7b0d5c795 100644
--- a/sys/net/route.h
+++ b/sys/net/route.h
@@ -412,7 +412,7 @@ struct ifmultiaddr;
 struct rib_head;
 
 void	 rt_ieee80211msg(struct ifnet *, int, void *, size_t);
-void	 rt_ifmsg(struct ifnet *);
+void	 rt_ifmsg(struct ifnet *, int);
 void	 rt_missmsg(int, struct rt_addrinfo *, int, int);
 void	 rt_missmsg_fib(int, struct rt_addrinfo *, int, int, int);
 int	 rt_addrmsg(int, struct ifaddr *, int);
diff --git a/sys/net/route/route_ctl.h b/sys/net/route/route_ctl.h
index d150da6264d4..e8560e681ddb 100644
--- a/sys/net/route/route_ctl.h
+++ b/sys/net/route/route_ctl.h
@@ -191,8 +191,10 @@ void rib_notify(struct rib_head *rnh, enum rib_subscription_type type,
 
 /* Event bridge */
 typedef void route_event_f(uint32_t fibnum, const struct rib_cmd_info *rc);
+typedef void ifmsg_event_f(struct ifnet *ifp, int if_flags_mask);
 struct rtbridge{
 	route_event_f	*route_f;
+	ifmsg_event_f	*ifmsg_f;
 };
 extern struct rtbridge *rtsock_callback_p;
 extern struct rtbridge *netlink_callback_p;
diff --git a/sys/net/rtsock.c b/sys/net/rtsock.c
index 99d962c972cb..a67643e724db 100644
--- a/sys/net/rtsock.c
+++ b/sys/net/rtsock.c
@@ -220,6 +220,7 @@ static void	send_rtm_reply(struct socket *so, struct rt_msghdr *rtm,
 static bool	can_export_rte(struct ucred *td_ucred, bool rt_is_host,
 			const struct sockaddr *rt_dst);
 static void	rtsock_notify_event(uint32_t fibnum, const struct rib_cmd_info *rc);
+static void	rtsock_ifmsg(struct ifnet *ifp, int if_flags_mask);
 
 static struct netisr_handler rtsock_nh = {
 	.nh_name = "rtsock",
@@ -297,7 +298,10 @@ rts_handle_route_event(uint32_t fibnum, const struct rib_cmd_info *rc)
 #endif
 		report_route_event(rc, (void *)(uintptr_t)fibnum);
 }
-static struct rtbridge rtsbridge = { .route_f = rts_handle_route_event };
+static struct rtbridge rtsbridge = {
+	.route_f = rts_handle_route_event,
+	.ifmsg_f = rtsock_ifmsg,
+};
 static struct rtbridge *rtsbridge_orig_p;
 
 static void
@@ -1916,8 +1920,8 @@ rt_missmsg(int type, struct rt_addrinfo *rtinfo, int flags, int error)
  * This routine is called to generate a message from the routing
  * socket indicating that the status of a network interface has changed.
  */
-void
-rt_ifmsg(struct ifnet *ifp)
+static void
+rtsock_ifmsg(struct ifnet *ifp, int if_flags_mask __unused)
 {
 	struct if_msghdr *ifm;
 	struct mbuf *m;
diff --git a/sys/net80211/ieee80211_freebsd.c b/sys/net80211/ieee80211_freebsd.c
index ec970e217dfd..7158ada291ab 100644
--- a/sys/net80211/ieee80211_freebsd.c
+++ b/sys/net80211/ieee80211_freebsd.c
@@ -1020,7 +1020,7 @@ ieee80211_notify_radio(struct ieee80211com *ic, int state)
 }
 
 void
-ieee80211_notify_ifnet_change(struct ieee80211vap *vap)
+ieee80211_notify_ifnet_change(struct ieee80211vap *vap, int if_flags_mask)
 {
 	struct ifnet *ifp = vap->iv_ifp;
 
@@ -1028,7 +1028,7 @@ ieee80211_notify_ifnet_change(struct ieee80211vap *vap)
 	    "interface state change");
 
 	CURVNET_SET(ifp->if_vnet);
-	rt_ifmsg(ifp);
+	rt_ifmsg(ifp, if_flags_mask);
 	CURVNET_RESTORE();
 }
 
diff --git a/sys/net80211/ieee80211_proto.c b/sys/net80211/ieee80211_proto.c
index 87fed05a281c..7e76a3ae9226 100644
--- a/sys/net80211/ieee80211_proto.c
+++ b/sys/net80211/ieee80211_proto.c
@@ -1993,7 +1993,7 @@ ieee80211_start_locked(struct ieee80211vap *vap)
 		 * back in here and complete the work.
 		 */
 		ifp->if_drv_flags |= IFF_DRV_RUNNING;
-		ieee80211_notify_ifnet_change(vap);
+		ieee80211_notify_ifnet_change(vap, IFF_DRV_RUNNING);
 
 		/*
 		 * We are not running; if this we are the first vap
@@ -2107,7 +2107,7 @@ ieee80211_stop_locked(struct ieee80211vap *vap)
 	ieee80211_new_state_locked(vap, IEEE80211_S_INIT, -1);
 	if (ifp->if_drv_flags & IFF_DRV_RUNNING) {
 		ifp->if_drv_flags &= ~IFF_DRV_RUNNING;	/* mark us stopped */
-		ieee80211_notify_ifnet_change(vap);
+		ieee80211_notify_ifnet_change(vap, IFF_DRV_RUNNING);
 		if (--ic->ic_nrunning == 0) {
 			IEEE80211_DPRINTF(vap,
 			    IEEE80211_MSG_STATE | IEEE80211_MSG_DEBUG,
diff --git a/sys/net80211/ieee80211_proto.h b/sys/net80211/ieee80211_proto.h
index e9b5157716d4..3bbeccfd0547 100644
--- a/sys/net80211/ieee80211_proto.h
+++ b/sys/net80211/ieee80211_proto.h
@@ -462,5 +462,5 @@ void	ieee80211_notify_node_auth(struct ieee80211_node *);
 void	ieee80211_notify_country(struct ieee80211vap *,
 		const uint8_t [IEEE80211_ADDR_LEN], const uint8_t cc[2]);
 void	ieee80211_notify_radio(struct ieee80211com *, int);
-void	ieee80211_notify_ifnet_change(struct ieee80211vap *);
+void	ieee80211_notify_ifnet_change(struct ieee80211vap *, int);
 #endif /* _NET80211_IEEE80211_PROTO_H_ */
diff --git a/sys/netlink/netlink_route.c b/sys/netlink/netlink_route.c
index cc1a0cc6db8d..0622656715c4 100644
--- a/sys/netlink/netlink_route.c
+++ b/sys/netlink/netlink_route.c
@@ -106,7 +106,10 @@ rtnl_handle_message(struct nlmsghdr *hdr, struct nl_pstate *npt)
 	return (error);
 }
 
-static struct rtbridge nlbridge = { .route_f = rtnl_handle_route_event };
+static struct rtbridge nlbridge = {
+	.route_f = rtnl_handle_route_event,
+	.ifmsg_f = rtnl_handle_ifnet_event,
+};
 static struct rtbridge *nlbridge_orig_p;
 
 static void
diff --git a/sys/netlink/route/iface.c b/sys/netlink/route/iface.c
index 5ffe11cc7e80..579869e9662c 100644
--- a/sys/netlink/route/iface.c
+++ b/sys/netlink/route/iface.c
@@ -68,7 +68,7 @@ struct netlink_walkargs {
 	int dumped;
 };
 
-static eventhandler_tag ifdetach_event, ifattach_event, ifaddr_event;
+static eventhandler_tag ifdetach_event, ifattach_event, iflink_event, ifaddr_event;
 
 static SLIST_HEAD(, nl_cloner) nl_cloners = SLIST_HEAD_INITIALIZER(nl_cloners);
 
@@ -234,11 +234,13 @@ dump_sa(struct nl_writer *nw, int attr, const struct sockaddr *sa)
  * @nw: message writer
  * @ifp: target interface
  * @hdr: template header
+ * @if_flags_mask: changed if_[drv]_flags bitmask
  *
  * This function is called without epoch and MAY sleep.
  */
 static bool
-dump_iface(struct nl_writer *nw, struct ifnet *ifp, const struct nlmsghdr *hdr)
+dump_iface(struct nl_writer *nw, struct ifnet *ifp, const struct nlmsghdr *hdr,
+    int if_flags_mask)
 {
         struct ifinfomsg *ifinfo;
 
@@ -253,13 +255,15 @@ dump_iface(struct nl_writer *nw, struct ifnet *ifp, const struct nlmsghdr *hdr)
         ifinfo->ifi_type = ifp->if_type;
         ifinfo->ifi_index = ifp->if_index;
         ifinfo->ifi_flags = ifp_flags_to_netlink(ifp);
-        ifinfo->ifi_change = 0;
-
-        nlattr_add_string(nw, IFLA_IFNAME, if_name(ifp));
+        ifinfo->ifi_change = if_flags_mask;
 
 	struct if_state ifs = {};
 	get_operstate(ifp, &ifs);
 
+	if (ifs.ifla_operstate == IF_OPER_UP)
+		ifinfo->ifi_flags |= IFF_LOWER_UP;
+
+        nlattr_add_string(nw, IFLA_IFNAME, if_name(ifp));
         nlattr_add_u8(nw, IFLA_OPERSTATE, ifs.ifla_operstate);
         nlattr_add_u8(nw, IFLA_CARRIER, ifs.ifla_carrier);
 
@@ -387,7 +391,7 @@ rtnl_handle_getlink(struct nlmsghdr *hdr, struct nlpcb *nlp, struct nl_pstate *n
 		NLP_LOG(LOG_DEBUG3, nlp, "fast track -> searching index %u", attrs.ifi_index);
 		if (ifp != NULL) {
 			if (match_iface(&attrs, ifp)) {
-				if (!dump_iface(wa.nw, ifp, &wa.hdr))
+				if (!dump_iface(wa.nw, ifp, &wa.hdr, 0))
 					error = ENOMEM;
 			} else
 				error = ESRCH;
@@ -439,7 +443,7 @@ rtnl_handle_getlink(struct nlmsghdr *hdr, struct nlpcb *nlp, struct nl_pstate *n
 
 	NL_LOG(LOG_DEBUG2, "Matched %d interface(s), dumping", offset);
 	for (int i = 0; error == 0 && i < offset; i++) {
-		if (!dump_iface(wa.nw, match_array[i], &wa.hdr))
+		if (!dump_iface(wa.nw, match_array[i], &wa.hdr, 0))
 			error = ENOMEM;
 	}
 	for (int i = 0; i < offset; i++)
@@ -767,9 +771,9 @@ rtnl_handle_ifaddr(void *arg __unused, struct ifaddr *ifa, int cmd)
 }
 
 static void
-rtnl_handle_ifattach(void *arg, struct ifnet *ifp)
+rtnl_handle_ifevent(struct ifnet *ifp, int nlmsg_type, int if_flags_mask)
 {
-	struct nlmsghdr hdr = { .nlmsg_type = NL_RTM_NEWLINK };
+	struct nlmsghdr hdr = { .nlmsg_type = nlmsg_type };
 	struct nl_writer nw = {};
 
 	if (!nl_has_listeners(NETLINK_ROUTE, RTNLGRP_LINK))
@@ -779,25 +783,36 @@ rtnl_handle_ifattach(void *arg, struct ifnet *ifp)
 		NL_LOG(LOG_DEBUG, "error allocating mbuf");
 		return;
 	}
-	dump_iface(&nw, ifp, &hdr);
+	dump_iface(&nw, ifp, &hdr, if_flags_mask);
         nlmsg_flush(&nw);
 }
 
+static void
+rtnl_handle_ifattach(void *arg, struct ifnet *ifp)
+{
+	NL_LOG(LOG_DEBUG2, "ifnet %s", if_name(ifp));
+	rtnl_handle_ifevent(ifp, NL_RTM_NEWLINK, 0);
+}
+
 static void
 rtnl_handle_ifdetach(void *arg, struct ifnet *ifp)
 {
-	struct nlmsghdr hdr = { .nlmsg_type = NL_RTM_DELLINK };
-	struct nl_writer nw = {};
+	NL_LOG(LOG_DEBUG2, "ifnet %s", if_name(ifp));
+	rtnl_handle_ifevent(ifp, NL_RTM_DELLINK, 0);
+}
 
-	if (!nl_has_listeners(NETLINK_ROUTE, RTNLGRP_LINK))
-		return;
+static void
+rtnl_handle_iflink(void *arg, struct ifnet *ifp)
+{
+	NL_LOG(LOG_DEBUG2, "ifnet %s", if_name(ifp));
+	rtnl_handle_ifevent(ifp, NL_RTM_NEWLINK, 0);
+}
 
-	if (!nlmsg_get_group_writer(&nw, NLMSG_LARGE, NETLINK_ROUTE, RTNLGRP_LINK)) {
-		NL_LOG(LOG_DEBUG, "error allocating mbuf");
-		return;
-	}
-	dump_iface(&nw, ifp, &hdr);
-        nlmsg_flush(&nw);
+void
+rtnl_handle_ifnet_event(struct ifnet *ifp, int if_flags_mask)
+{
+	NL_LOG(LOG_DEBUG2, "ifnet %s", if_name(ifp));
+	rtnl_handle_ifevent(ifp, NL_RTM_NEWLINK, if_flags_mask);
 }
 
 static const struct rtnl_cmd_handler cmd_handlers[] = {
@@ -867,6 +882,9 @@ rtnl_ifaces_init(void)
 	ifaddr_event = EVENTHANDLER_REGISTER(
 	    rt_addrmsg, rtnl_handle_ifaddr, NULL,
 	    EVENTHANDLER_PRI_ANY);
+	iflink_event = EVENTHANDLER_REGISTER(
+	    ifnet_link_event, rtnl_handle_iflink, NULL,
+	    EVENTHANDLER_PRI_ANY);
 	NL_VERIFY_PARSERS(all_parsers);
 	rtnl_iface_drivers_register();
 	rtnl_register_messages(cmd_handlers, NL_ARRAY_LEN(cmd_handlers));
@@ -878,4 +896,5 @@ rtnl_ifaces_destroy(void)
 	EVENTHANDLER_DEREGISTER(ifnet_arrival_event, ifattach_event);
 	EVENTHANDLER_DEREGISTER(ifnet_departure_event, ifdetach_event);
 	EVENTHANDLER_DEREGISTER(rt_addrmsg, ifaddr_event);
+	EVENTHANDLER_DEREGISTER(ifnet_link_event, iflink_event);
 }
diff --git a/sys/netlink/route/interface.h b/sys/netlink/route/interface.h
index cae763cc4a58..1b8f1cf7b53d 100644
--- a/sys/netlink/route/interface.h
+++ b/sys/netlink/route/interface.h
@@ -42,6 +42,9 @@ struct ifinfomsg {
 	unsigned	ifi_change;	/* IFF_* change mask */
 };
 
+/* Linux-specific link-level state flag */
+#define	IFF_LOWER_UP	IFF_NETLINK_1
+
 #ifndef _KERNEL
 /* Compatilbility helpers */
 #define	_IFINFO_HDRLEN		((int)sizeof(struct ifinfomsg))
diff --git a/sys/netlink/route/route_var.h b/sys/netlink/route/route_var.h
index 7a31a8c896a5..0bcfcc962020 100644
--- a/sys/netlink/route/route_var.h
+++ b/sys/netlink/route/route_var.h
@@ -88,6 +88,7 @@ void rtnl_ifaces_init(void);
 void rtnl_ifaces_destroy(void);
 void rtnl_iface_add_cloner(struct nl_cloner *cloner);
 void rtnl_iface_del_cloner(struct nl_cloner *cloner);
+void rtnl_handle_ifnet_event(struct ifnet *ifp, int if_change_mask);
 
 /* iface_drivers.c */
 void rtnl_iface_drivers_register(void);



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?202212091122.2B9BMbua014344>