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
This commit is contained in:
parent
0ca90ed42a
commit
1bcd230f95
@ -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
|
||||
|
10
sys/net/if.c
10
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:
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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();
|
||||
}
|
||||
|
||||
|
@ -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,
|
||||
|
@ -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_ */
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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))
|
||||
|
@ -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);
|
||||
|
Loading…
Reference in New Issue
Block a user