Source-Changes-HG archive
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index][Old Index]
[src/trunk]: src/sys/lkm/net/ethfoo/ethfoo Add a sample utilization of Andrew...
details: https://anonhg.NetBSD.org/src/rev/20f81f77bab4
branches: trunk
changeset: 566549:20f81f77bab4
user: cube <cube%NetBSD.org@localhost>
date: Wed May 12 13:51:16 2004 +0000
description:
Add a sample utilization of Andrew Brown's sysctl framework. See comments
in the code for more details.
Give copyright to TNF.
diffstat:
sys/lkm/net/ethfoo/ethfoo/ethfoo_lkm.c | 287 ++++++++++++++++++++++++++++++++-
1 files changed, 283 insertions(+), 4 deletions(-)
diffs (truncated from 387 to 300 lines):
diff -r da3c1098c1c5 -r 20f81f77bab4 sys/lkm/net/ethfoo/ethfoo/ethfoo_lkm.c
--- a/sys/lkm/net/ethfoo/ethfoo/ethfoo_lkm.c Wed May 12 13:49:01 2004 +0000
+++ b/sys/lkm/net/ethfoo/ethfoo/ethfoo_lkm.c Wed May 12 13:51:16 2004 +0000
@@ -1,7 +1,11 @@
-/* $NetBSD: ethfoo_lkm.c,v 1.1 2003/11/24 21:58:45 cube Exp $ */
+/* $NetBSD: ethfoo_lkm.c,v 1.2 2004/05/12 13:51:16 cube Exp $ */
/*
- * Copyright (c) 2003, Quentin Garnier. All rights reserved.
+ * Copyright (c) 2003, 2004 The NetBSD Foundation.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to the NetBSD Foundation
+ * by Quentin Garnier.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
@@ -11,6 +15,13 @@
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the NetBSD
+ * Foundation, Inc. and its contributors.
+ * 4. Neither the name of The NetBSD Foundation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
@@ -33,6 +44,7 @@
* Second, sample Ethernet driver.
* Third, sample of use of autoconf stuff inside a LKM.
* Fourth, sample clonable interface
+ * Fifth, sample sysctl interface use from a LKM.
*
* XXX Hacks
* 1. NetBSD doesn't offer a way to change an Ethernet address, so I chose
@@ -51,6 +63,7 @@
#endif
#include <sys/lkm.h>
#include <sys/sockio.h>
+#include <sys/sysctl.h>
#include <net/if.h>
#include <net/if_dl.h>
@@ -66,6 +79,25 @@
};
/*
+ * sysctl node management
+ *
+ * It's not really possible to use a SYSCTL_SETUP block with
+ * current LKM implementation, so it is easier to just define
+ * our own function.
+ *
+ * The handler function is a "helper" in Andrew Brown's sysctl
+ * framework terminology. It is used as a gateway for sysctl
+ * requests over the nodes.
+ *
+ * ethfoo_log allows the module to log creations of nodes and
+ * destroy them all at once using sysctl_teardown.
+ */
+static struct sysctlnode *ethfoo_node;
+static struct sysctllog *ethfoo_log;
+static int ethfoo_sysctl_setup();
+static int ethfoo_sysctl_handler(SYSCTLFN_PROTO);
+
+/*
* Since we're an Ethernet device, we need the 3 following
* components: a leading struct device, a struct ethercom,
* and also a struct ifmedia since we don't attach a PHY to
@@ -83,6 +115,7 @@
struct ifmedia sc_im;
struct ethercom sc_ec;
void (*sc_bpf_mtap)(caddr_t, struct mbuf *);
+ int sc_mibnum;
};
/* LKM management routines */
@@ -97,6 +130,11 @@
static void ethfoo_attach(struct device *, struct device *, void *);
static int ethfoo_detach(struct device*, int);
+/* Ethernet address helper functions */
+
+static char *ethfoo_ether_sprintf(char *, const u_char *);
+static void ethfoo_ether_aton(u_char *, char *);
+
CFATTACH_DECL(ethfoo, sizeof(struct ethfoo_softc),
ethfoo_match, ethfoo_attach, ethfoo_detach, NULL);
@@ -189,7 +227,7 @@
}
if_clone_attach(ðfoo_cloners);
-
+ error = ethfoo_sysctl_setup();
out:
return error;
}
@@ -219,6 +257,7 @@
{
int error, i;
+ sysctl_teardown(ðfoo_log);
if_clone_detach(ðfoo_cloners);
for (i = 0; i < ethfoo_cd.cd_ndevs; i++)
@@ -257,8 +296,11 @@
struct ethfoo_softc *sc = (struct ethfoo_softc *)self;
struct ifnet *ifp;
u_int8_t enaddr[ETHER_ADDR_LEN] = { 0xf0, 0x0b, 0xa4, 0xff, 0xff, 0xff };
+ char enaddrstr[18];
unsigned long u;
uint32_t ui;
+ int error;
+ struct sysctlnode *node;
aprint_normal("%s: faking Ethernet device\n",
self->dv_xname);
@@ -272,7 +314,7 @@
memcpy(enaddr+3, (u_int8_t *)&ui, 3);
aprint_normal("%s: Ethernet address %s\n", sc->sc_dev.dv_xname,
- ether_sprintf(enaddr));
+ ethfoo_ether_sprintf(enaddrstr, enaddr));
/* ksyms interface is only available since mid-1.6R, so require
* at least 1.6T
@@ -318,6 +360,28 @@
* being common to all network interface drivers. */
if_attach(ifp);
ether_ifattach(ifp, enaddr);
+
+ /*
+ * Add a sysctl node for that interface.
+ *
+ * The pointer transmitted is not a string, but instead a pointer to the softc
+ * structure, which we can use to build the string value on the fly in the helper
+ * function of the node. See the comments for ethfoo_sysctl_handler for details.
+ *
+ * As in ethfoo_sysctl_setup, we use ethfoo_log. This allows the use of
+ * sysctl_teardown() in ethfoo_lkmunload, which undoes every creation we made in the
+ * module.
+ */
+ if ((error = sysctl_createv(ðfoo_log, 0, ðfoo_node,
+ &node, CTLFLAG_READWRITE,
+ CTLTYPE_STRING, sc->sc_dev.dv_xname, NULL,
+ ethfoo_sysctl_handler, 0, sc, 18,
+ CTL_CREATE, CTL_EOL)) != 0) {
+ sc->sc_mibnum = -1;
+ aprint_error("%s: sysctl_createv returned %d, ignoring\n",
+ sc->sc_dev.dv_xname, error);
+ } else
+ sc->sc_mibnum = node->sysctl_num;
}
/*
@@ -329,7 +393,17 @@
{
struct ethfoo_softc *sc = (struct ethfoo_softc *)self;
struct ifnet *ifp = &sc->sc_ec.ec_if;
+ int error;
+ /*
+ * Destroying a single leaf is a very straightforward operation using
+ * sysctl_destroyv. One should be sure to always end the path with
+ * CTL_EOL.
+ */
+ if (sc->sc_mibnum != -1 && (error = sysctl_destroyv(ethfoo_node, sc->sc_mibnum,
+ CTL_EOL)) != 0)
+ aprint_error("%s: sysctl_destroyv returned %d, ignoring\n",
+ sc->sc_dev.dv_xname, error);
ether_ifdetach(ifp);
if_detach(ifp);
ifmedia_delete_instance(&sc->sc_im, IFM_INST_ANY);
@@ -516,3 +590,208 @@
aprint_error("%s: unable to detach instance\n",
dev->dv_xname);
}
+
+/*
+ * sysctl management routines
+ * You can set the address of an interface through:
+ * net.link.ethfoo.ethfoo<number>
+ *
+ * Note the consistent use of ethfoo_log in order to use
+ * sysctl_teardown at unload time.
+ *
+ * In the kernel you will find a lot of SYSCTL_SETUP blocks. Those
+ * blocks register a function in a special section of the kernel
+ * (called a link set) which is used at init_sysctl() time to cycle
+ * through all those functions to create the kernel's sysctl tree.
+ *
+ * It is not possible to use link sets in a LKM, so the easiest is
+ * to simply call our own setup routine at load time.
+ *
+ * In the SYSCTL_SETUP blocks you find in the kernel, nodes have the
+ * CTLFLAG_PERMANENT flag, meaning they cannot be removed. Once the
+ * whole kernel sysctl tree is built, it is not possible to add any
+ * permanent node. This is fine with us since ethfoo is meant to be
+ * unloaded without leaving anything behind.
+ */
+int
+ethfoo_sysctl_setup(void)
+{
+ int error = 0;
+
+ if ((error = sysctl_createv(ðfoo_log, 0, NULL, NULL, 0,
+ CTLTYPE_NODE, "net", NULL,
+ NULL, 0, NULL, 0,
+ CTL_NET, CTL_EOL)) != 0)
+ return (error);
+
+ if ((error = sysctl_createv(ðfoo_log, 0, NULL, NULL, 0,
+ CTLTYPE_NODE, "link", NULL,
+ NULL, 0, NULL, 0,
+ CTL_NET, PF_LINK, CTL_EOL)) != 0)
+ return (error);
+
+ /*
+ * The first four parameters of sysctl_createv are for management.
+ *
+ * The four that follows, here starting with a '0' for the flags,
+ * describe the node.
+ *
+ * The next series of four set its value, through various possible
+ * means.
+ *
+ * Last but not least, the path to the node is described. That path
+ * is relative to the given root (third argument). Here we're
+ * starting from the root.
+ */
+ error = sysctl_createv(ðfoo_log, 0, NULL, ðfoo_node, 0,
+ CTLTYPE_NODE, "ethfoo", NULL,
+ NULL, 0, NULL, 0,
+ CTL_NET, PF_LINK, CTL_CREATE, CTL_EOL);
+
+ return (error);
+}
+
+/*
+ * The helper functions make Andrew Brown's interface really
+ * shine. It makes possible to create value on the fly whether
+ * the sysctl value is read or written.
+ *
+ * As shown as an example in the man page, the first step is to
+ * create a copy of the node to have sysctl_lookup work on it.
+ *
+ * Here, we have more work to do than just a copy, since we have
+ * to create the string. The first step is to collect the actual
+ * value of the node, which is a convenient pointer to the softc
+ * of the interface. From there we create the string and use it
+ * as the value, but only for the *copy* of the node.
+ *
+ * Then we let sysctl_lookup do the magic, which consists in
+ * setting oldp and newp as required by the operation. When the
+ * value is read, that means that the string will be copied to
+ * the user, and when it is written, the new value will be copied
+ * over in the addr array.
+ *
+ * If newp is NULL, the user was reading the value, so we don't
+ * have anything else to do. If a new value was written, we
+ * have to check it.
+ *
+ * If it is incorrect, we can return an error and leave 'node' as
+ * it is: since it is a copy of the actual node, the change will
+ * be forgotten.
+ *
+ * Upon a correct input, we commit the change to the ifnet
+ * structure of our interface.
+ */
+static int
+ethfoo_sysctl_handler(SYSCTLFN_ARGS)
+{
+ struct sysctlnode node;
+ struct ethfoo_softc *sc;
+ struct ifnet *ifp;
+ int error, i;
+ char addr[18];
+
+ node = *rnode;
+ sc = node.sysctl_data;
+ ifp = &sc->sc_ec.ec_if;
+ (void)ethfoo_ether_sprintf(addr, LLADDR(ifp->if_sadl));
+ node.sysctl_data = addr;
+ error = sysctl_lookup(SYSCTLFN_CALL(&node));
+ if (error || newp == NULL)
+ return (error);
+
+ if (strlen(addr) != 17)
+ return (EINVAL);
+
+ for (i = 0; i < 17; i++)
+ if ((i % 3 == 2 && addr[i] != ':') ||
+ (i % 3 != 2 && !isxdigit(addr[i])))
+ return (EINVAL);
+
Home |
Main Index |
Thread Index |
Old Index