typedef ssize_t (*rmib_func_ptr)(struct rmib_call *, struct rmib_node *,
struct rmib_oldp *, struct rmib_newp *);
+/*
+ * Indirect node, used for sparse nodes. Sparse nodes are node-type nodes with
+ * the CTLFLAG_SPARSE flag set. A sparse node points not to an array of child
+ * nodes (using rnode_cptr), but to a array of {id,child pointer} elements
+ * (using rnode_icptr). At the cost of O(n) lookups, sparse nodes save memory.
+ * Currently for presentation reasons only, indirect lists must be sorted
+ * ascending by node identifiers. They may also not have ID duplicates, may not
+ * have NULL node pointers, and may not point to nodes with zero flags fields.
+ */
+#define CTLFLAG_SPARSE CTLFLAG_ROOT /* overloaded NetBSD flag */
+
+struct rmib_indir {
+ unsigned int rindir_id; /* node identifier */
+ struct rmib_node *rindir_node; /* pointer to actual node */
+};
+
/*
* The central structure for remote MIB nodes. This is essentially a somewhat
* cut-down version of the node structure used within the MIB service. See the
union pxfer_rnode_ptr_u {
void *rpu_data; /* struct or string data pointer */
struct rmib_node *rpu_cptr; /* child node array */
+ struct rmib_indir *rpu_icptr; /* indirect child node array */
} rnode_ptr_u;
rmib_func_ptr rnode_func; /* handler function */
const char *rnode_name; /* node name string */
#define rnode_clen rnode_val_u.rvu_clen
#define rnode_data rnode_ptr_u.rpu_data
#define rnode_cptr rnode_ptr_u.rpu_cptr
+#define rnode_icptr rnode_ptr_u.rpu_icptr
/* Various macros to initialize nodes at compile time. */
#define RMIB_NODE(f,t,n,d) { \
.rnode_name = n, \
.rnode_desc = d \
}
+#define RMIB_SNODE(f,t,n,d) { \
+ .rnode_flags = CTLTYPE_NODE | CTLFLAG_READONLY | \
+ CTLFLAG_PERMANENT | CTLFLAG_SPARSE | f, \
+ .rnode_size = 0, \
+ .rnode_icptr = t, \
+ .rnode_name = n, \
+ .rnode_desc = d \
+}
#define RMIB_FUNC(f,s,fp,n,d) { \
.rnode_flags = CTLFLAG_PERMANENT | f, \
.rnode_size = s, \
* though the copy here operates on slightly different data structures in order
* to keep the implementation more lightweight. For clarification on many
* aspects of the source code here, see the source code of the MIB service.
+ * One unique feature here is support for sparse nodes, which is needed for
+ * net.inet/inet6 as those are using subtrees with protocol-based identifiers.
*
* There is no way for this module to get to know about MIB service deaths
* without possibly interfering with the main code of the service this module
memset(&scn, 0, sizeof(scn));
/*
- * The RMIB implementation does not overload flags, so it also need not
+ * We use CTLFLAG_SPARSE internally only. NetBSD uses these flags for
+ * different purposes. Either way, do not expose it to userland.
* hide any of them from the user.
*/
- scn.sysctl_flags = SYSCTL_VERSION | rnode->rnode_flags;
+ scn.sysctl_flags = SYSCTL_VERSION |
+ (rnode->rnode_flags & ~CTLFLAG_SPARSE);
scn.sysctl_num = id;
strlcpy(scn.sysctl_name, rnode->rnode_name, sizeof(scn.sysctl_name));
scn.sysctl_ver = call->call_rootver;
{
struct sysctlnode scn;
struct rmib_node *rnode;
- unsigned int id;
+ unsigned int i, id;
ssize_t r, off;
/* If the user passed in version numbers, check them. */
/* Enumerate the child nodes of the given parent node. */
off = 0;
- for (id = 0; id < rparent->rnode_size; id++) {
- rnode = &rparent->rnode_cptr[id];
+ for (i = 0; i < rparent->rnode_size; i++) {
+ if (rparent->rnode_flags & CTLFLAG_SPARSE) {
+ id = rparent->rnode_icptr[i].rindir_id;
+ rnode = rparent->rnode_icptr[i].rindir_node;
+ } else {
+ id = i;
+ rnode = &rparent->rnode_cptr[i];
- if (rnode->rnode_flags == 0)
- continue;
+ if (rnode->rnode_flags == 0)
+ continue;
+ }
if ((r = rmib_copyout_node(call, oldp, off, id, rnode)) < 0)
return r;
return roundup2(size, sizeof(int32_t));
}
+/*
+ * Look up a child node given a parent node and a child node identifier.
+ * Return a pointer to the child node if found, or NULL otherwise. The lookup
+ * procedure differs based on whether the parent node is sparse or not.
+ */
+static struct rmib_node *
+rmib_lookup(struct rmib_node * rparent, unsigned int id)
+{
+ struct rmib_node *rnode;
+ struct rmib_indir *rindir;
+ unsigned int i;
+
+ if (rparent->rnode_flags & CTLFLAG_SPARSE) {
+ rindir = rparent->rnode_icptr;
+ for (i = 0; i < rparent->rnode_size; i++, rindir++)
+ if (rindir->rindir_id == id)
+ return rindir->rindir_node;
+ } else {
+ if (id >= rparent->rnode_size)
+ return NULL;
+ rnode = &rparent->rnode_cptr[id];
+ if (rnode->rnode_flags != 0)
+ return rnode;
+ }
+
+ return NULL;
+}
+
/*
* Retrieve node descriptions in bulk, or retrieve a particular node's
* description.
{
struct sysctlnode scn;
struct rmib_node *rnode;
- unsigned int id;
+ unsigned int i, id;
ssize_t r, off;
if (newp != NULL) {
return EINVAL;
/* Locate the child node. */
- if ((unsigned int)scn.sysctl_num >= rparent->rnode_size)
- return ENOENT;
- rnode = &rparent->rnode_cptr[scn.sysctl_num];
- if (rnode->rnode_flags == 0)
+ if ((rnode = rmib_lookup(rparent, scn.sysctl_num)) == NULL)
return ENOENT;
/* Descriptions of private nodes are considered private too. */
/* Describe the child nodes of the given parent node. */
off = 0;
- for (id = 0; id < rparent->rnode_size; id++) {
- rnode = &rparent->rnode_cptr[id];
+ for (i = 0; i < rparent->rnode_size; i++) {
+ if (rparent->rnode_flags & CTLFLAG_SPARSE) {
+ id = rparent->rnode_icptr[i].rindir_id;
+ rnode = rparent->rnode_icptr[i].rindir_node;
+ } else {
+ id = i;
+ rnode = &rparent->rnode_cptr[i];
- if (rnode->rnode_flags == 0)
- continue;
+ if (rnode->rnode_flags == 0)
+ continue;
+ }
if ((r = rmib_copyout_desc(call, oldp, off, id, rnode)) < 0)
return r;
}
/* Locate the child node. */
- if ((unsigned int)id >= rparent->rnode_size)
- return ENOENT;
- rnode = &rparent->rnode_cptr[id];
- if (rnode->rnode_flags == 0)
+ if ((rnode = rmib_lookup(rparent, id)) == NULL)
return ENOENT;
/* Check if access is permitted at this level. */
* assigning the proper child length value to each of them.
*/
static void
-rmib_init(struct rmib_node * rnode)
+rmib_init(struct rmib_node * rparent)
{
- struct rmib_node *rchild;
- unsigned int id;
+ struct rmib_node *rnode;
+ unsigned int i;
+
+ for (i = 0; i < rparent->rnode_size; i++) {
+ if (rparent->rnode_flags & CTLFLAG_SPARSE) {
+ /* Indirect lists must be sorted ascending by ID. */
+ assert(i == 0 || rparent->rnode_icptr[i].rindir_id >
+ rparent->rnode_icptr[i - 1].rindir_id);
- rchild = rnode->rnode_cptr;
+ rnode = rparent->rnode_icptr[i].rindir_node;
+ } else {
+ rnode = &rparent->rnode_cptr[i];
- for (id = 0; id < rnode->rnode_size; id++, rchild++) {
- if (rchild->rnode_flags == 0)
- continue;
+ if (rnode->rnode_flags == 0)
+ continue;
+ }
- rnode->rnode_clen++;
+ rparent->rnode_clen++;
- if (SYSCTL_TYPE(rchild->rnode_flags) == CTLTYPE_NODE)
- rmib_init(rchild); /* recurse */
+ if (SYSCTL_TYPE(rnode->rnode_flags) == CTLTYPE_NODE)
+ rmib_init(rnode); /* recurse */
}
}
m.m_type = MIB_REGISTER;
m.m_lsys_mib_register.root_id = id;
m.m_lsys_mib_register.flags = SYSCTL_VERSION |
- rnodes[id].rno_node->rnode_flags;
+ (rnodes[id].rno_node->rnode_flags & ~CTLFLAG_SPARSE);
m.m_lsys_mib_register.csize = rnodes[id].rno_node->rnode_size;
m.m_lsys_mib_register.clen = rnodes[id].rno_node->rnode_clen;
m.m_lsys_mib_register.miblen = rnodes[id].rno_namelen;