Subject: Alternative approach for interface events
To: None <tech-net@NetBSD.org>
From: Peter Postma <peter@pointless.nl>
List: tech-net
Date: 09/19/2004 12:31:56
I'm not really happy with the changes to pfil(9) to support interface
events (attach, detach, addresses). IMHO, the changes made the pfil(9) API
more ugly (e.g. extra conditionals in pfil_run_hooks) and I don't think that
the pfil(9) API should be used for handling such things (pfil is for packet
filtering, not watching interface events). I've created a small patch that
implements the interface events differently (patch applies to 2.0 beta).

Pros: no hacks to pfil(9), suits better and cleaner.
Cons: yet another interface that registers functions to be executed, more?

Comments?


Index: net/if.c
===================================================================
RCS file: /cvsroot/src/sys/net/if.c,v
retrieving revision 1.139.2.1
diff -u -r1.139.2.1 if.c
--- net/if.c	28 May 2004 07:24:37 -0000	1.139.2.1
+++ net/if.c	8 Sep 2004 10:13:12 -0000
@@ -160,6 +160,9 @@
 LIST_HEAD(, if_clone) if_cloners = LIST_HEAD_INITIALIZER(if_cloners);
 int if_cloners_count;
 
+LIST_HEAD(, if_event) if_events = LIST_HEAD_INITIALIZER(if_events);
+int if_event_enabled = 0;
+
 #if defined(INET) || defined(INET6) || defined(NETATALK) || defined(NS) || \
     defined(ISO) || defined(CCITT) || defined(NATM)
 static void if_detach_queues __P((struct ifnet *, struct ifqueue *));
@@ -469,6 +472,8 @@
 	if (domains)
 		if_attachdomain1(ifp);
 
+	if_event_invoke(IF_EVENT_ATTACH_IF, ifp);
+
 	/* Announce the interface. */
 	rt_ifannouncemsg(ifp, IFAN_ARRIVAL);
 }
@@ -577,6 +582,8 @@
 	(void) pfil_head_unregister(&ifp->if_pfil);
 #endif
 
+	if_event_invoke(IF_EVENT_DETACH_IF, ifp);
+
 	/*
 	 * Rip all the addresses off the interface.  This should make
 	 * all of the routes go away.
@@ -846,6 +853,7 @@
 if_clone_attach(ifc)
 	struct if_clone *ifc;
 {
+	if_event_invoke(IF_EVENT_ATTACH_CLONE, ifc);
 
 	LIST_INSERT_HEAD(&if_cloners, ifc, ifc_list);
 	if_cloners_count++;
@@ -858,6 +866,7 @@
 if_clone_detach(ifc)
 	struct if_clone *ifc;
 {
+	if_event_invoke(IF_EVENT_DETACH_CLONE, ifc);
 
 	LIST_REMOVE(ifc, ifc_list);
 	if_cloners_count--;
@@ -899,6 +908,75 @@
 }
 
 /*
+ * Search for a function in the interface events list.
+ */
+static struct if_event *
+if_event_lookup(void (*func)(int, void *), int type)
+{
+	struct if_event *p;
+
+	LIST_FOREACH(p, &if_events, link)
+		if (p->type == type && p->func == func)
+			return (p);
+
+	return (NULL);
+}
+
+/*
+ * Add a function to the interface events list.
+ */
+int
+if_event_add(void (*func)(int, void *), int type)
+{
+	struct if_event *p;
+
+	if (if_event_lookup(func, type) != NULL)
+		return (EEXIST);
+
+	p = malloc(sizeof(struct if_event), M_IFADDR, M_WAITOK);
+	p->func = func;
+	p->type = type;
+
+	LIST_INSERT_HEAD(&if_events, p, link);
+
+	return (0);
+}
+
+/*
+ * Remove a function from the interface events list.
+ */
+int
+if_event_remove(void (*func)(int, void *), int type)
+{
+	struct if_event *p;
+
+	if ((p = if_event_lookup(func, type)) == NULL)
+		return (ENOENT);
+
+	free(p, M_IFADDR);
+	LIST_REMOVE(p, link);
+
+	return (0);
+}
+
+/*
+ * Run the interface events with the specified event type.
+ */
+void
+if_event_invoke(int type, void *ifp)
+{
+	struct if_event *p;
+
+	if (!if_event_enabled)
+		return;
+
+	LIST_FOREACH(p, &if_events, link) {
+		if ((p->type & type) && p->func != NULL)
+			(*p->func)(type, ifp);
+	}
+}
+
+/*
  * Locate an interface based on a complete address.
  */
 /*ARGSUSED*/
@@ -1699,6 +1777,24 @@
 	return (error);
 }
 
+SYSCTL_SETUP(sysctl_net_if_event_setup, "sysctl net.if_events subtree setup")
+{
+	struct sysctlnode *node;
+
+	sysctl_createv(clog, 0, NULL, NULL, CTLFLAG_PERMANENT,
+	    CTLTYPE_NODE, "net", NULL, NULL, 0, NULL, 0, CTL_NET, CTL_EOL);
+
+	sysctl_createv(clog, 0, NULL, &node, CTLFLAG_PERMANENT,
+	    CTLTYPE_NODE, "if_events", SYSCTL_DESCR("if_events options"),
+	    NULL, 0, NULL, 0, CTL_NET, CTL_CREATE, CTL_EOL);
+
+	sysctl_createv(clog, 0, NULL, NULL,
+	    CTLFLAG_PERMANENT|CTLFLAG_READWRITE, CTLTYPE_INT, "enabled",
+	    SYSCTL_DESCR("Enable or disable the interface events"),
+	    NULL, 0, &if_event_enabled, 0, CTL_NET,
+	    node->sysctl_num, CTL_CREATE, CTL_EOL);
+}
+
 #if defined(INET) || defined(INET6)
 static void
 sysctl_net_ifq_setup(struct sysctllog **clog,
Index: net/if.h
===================================================================
RCS file: /cvsroot/src/sys/net/if.h,v
retrieving revision 1.95
diff -u -r1.95 if.h
--- net/if.h	10 Dec 2003 11:46:33 -0000	1.95
+++ net/if.h	8 Sep 2004 10:13:13 -0000
@@ -159,6 +159,22 @@
 };
 
 /*
+ * Structure for the interface events.
+ */
+struct if_event {
+	void (*func)(int, void *);
+	int type;
+	LIST_ENTRY(if_event) link;
+};
+
+#define	IF_EVENT_ATTACH_IF	0x01
+#define	IF_EVENT_DETACH_IF	0x02
+#define	IF_EVENT_ATTACH_CLONE	0x04
+#define	IF_EVENT_DETACH_CLONE	0x08
+#define	IF_EVENT_ADDR_V4	0x10
+#define	IF_EVENT_ADDR_V6	0x20
+
+/*
  * Structure defining statistics and other data kept regarding a network
  * interface.
  */
@@ -773,6 +789,10 @@
 int	if_clone_create __P((const char *));
 int	if_clone_destroy __P((const char *));
 
+int	if_event_add(void (*)(int, void *), int);
+int	if_event_remove(void (*)(int, void *), int);
+void	if_event_invoke(int, void *);
+
 int	loioctl __P((struct ifnet *, u_long, caddr_t));
 void	loopattach __P((int));
 int	looutput __P((struct ifnet *,
Index: netinet/in.c
===================================================================
RCS file: /cvsroot/src/sys/netinet/in.c,v
retrieving revision 1.93.2.1
diff -u -r1.93.2.1 in.c
--- netinet/in.c	10 Jul 2004 12:42:37 -0000	1.93.2.1
+++ netinet/in.c	8 Sep 2004 10:13:13 -0000
@@ -464,6 +464,8 @@
 
 	case SIOCSIFADDR:
 		error = in_ifinit(ifp, ia, satosin(&ifr->ifr_addr), 1);
+		if (!error)
+			if_event_invoke(IF_EVENT_ADDR_V4, ifp);
 		return error;
 
 	case SIOCSIFNETMASK:
@@ -503,6 +505,8 @@
 		if ((ifp->if_flags & IFF_BROADCAST) &&
 		    (ifra->ifra_broadaddr.sin_family == AF_INET))
 			ia->ia_broadaddr = ifra->ifra_broadaddr;
+		if (!error)
+			if_event_invoke(IF_EVENT_ADDR_V4, ifp);
 		return (error);
 
 	case SIOCGIFALIAS:
@@ -520,6 +524,7 @@
 
 	case SIOCDIFADDR:
 		in_purgeaddr(&ia->ia_ifa, ifp);
+		if_event_invoke(IF_EVENT_ADDR_V4, ifp);
 		break;
 
 #ifdef MROUTING
Index: netinet6/in6.c
===================================================================
RCS file: /cvsroot/src/sys/netinet6/in6.c,v
retrieving revision 1.86
diff -u -r1.86 in6.c
--- netinet6/in6.c	28 Mar 2004 08:28:06 -0000	1.86
+++ netinet6/in6.c	8 Sep 2004 10:13:15 -0000
@@ -736,6 +736,7 @@
 		 * that is, this address might make other addresses detached.
 		 */
 		pfxlist_onlink_check();
+		if_event_invoke(IF_EVENT_ADDR_V6, ifp);
 
 		break;
 	}
@@ -777,6 +778,7 @@
 		in6_purgeaddr(&ia->ia_ifa);
 		if (pr && purgeprefix)
 			prelist_remove(pr);
+		if_event_invoke(IF_EVENT_ADDR_V6, ifp);
 		break;
 	}
 

-- 
Peter Postma