diff --git a/share/man/man4/ng_hole.4 b/share/man/man4/ng_hole.4 index 65a6a093a656..fd956adf56bf 100644 --- a/share/man/man4/ng_hole.4 +++ b/share/man/man4/ng_hole.4 @@ -35,7 +35,7 @@ .\" $FreeBSD$ .\" $Whistle: ng_hole.8,v 1.4 1999/01/25 23:46:26 archie Exp $ .\" -.Dd January 19, 1999 +.Dd May 19, 2004 .Dt NG_HOLE 4 .Os .Sh NAME @@ -54,8 +54,25 @@ A node accepts any request to connect, regardless of the hook name, as long as the name is unique. .Sh CONTROL MESSAGES -This node type supports only the generic control messages. -Other control messages are silently discarded. +This node type supports the generic control messages, plus the +following: +.Bl -tag -width indent +.It Dv NGM_BPF_GET_STATS +This command takes an +.Tn ASCII +string argument, the hook name, and returns the statistics +associated with the hook as a +.Vt "struct ng_hole_hookstat" . +.It Dv NGM_BPF_CLR_STATS +This command takes an +.Tn ASCII +string argument, the hook name, and clears the statistics +associated with the hook. +.It Dv NGM_BPF_GETCLR_STATS +This command is identical to +.Dv NGM_BPF_GET_STATS , +except that the statistics are also atomically cleared. +.El .Sh SHUTDOWN This node shuts down upon receipt of a .Dv NGM_SHUTDOWN diff --git a/sys/netgraph/ng_hole.c b/sys/netgraph/ng_hole.c index 8b4df2933310..b1fc95d89c31 100644 --- a/sys/netgraph/ng_hole.c +++ b/sys/netgraph/ng_hole.c @@ -51,10 +51,53 @@ #include #include #include +#include #include +/* Per hook private info. */ +struct ng_hole_hookinfo { + struct ng_hole_hookstat stats; +}; +typedef struct ng_hole_hookinfo *hinfo_p; + +/* Parse type for struct ng_hole_hookstat. */ +static const struct ng_parse_struct_field ng_hole_hookstat_type_fields[] = + NG_HOLE_HOOKSTAT_TYPE_INFO; +static const struct ng_parse_type ng_hole_hookstat_type = { + &ng_parse_struct_type, + &ng_hole_hookstat_type_fields +}; + +/* List of commands and how to convert arguments to/from ASCII. */ +static const struct ng_cmdlist ng_hole_cmdlist[] = { + { + NGM_HOLE_COOKIE, + NGM_HOLE_GET_STATS, + "getstats", + &ng_parse_hookbuf_type, + &ng_hole_hookstat_type + }, + { + NGM_HOLE_COOKIE, + NGM_HOLE_CLR_STATS, + "clrstats", + &ng_parse_hookbuf_type, + NULL + }, + { + NGM_HOLE_COOKIE, + NGM_HOLE_GETCLR_STATS, + "getclrstats", + &ng_parse_hookbuf_type, + &ng_hole_hookstat_type + }, + { 0 } +}; + /* Netgraph methods */ static ng_constructor_t ngh_cons; +static ng_rcvmsg_t ngh_rcvmsg; +static ng_newhook_t ngh_newhook; static ng_rcvdata_t ngh_rcvdata; static ng_disconnect_t ngh_disconnect; @@ -63,14 +106,14 @@ static struct ng_type typestruct = { NG_HOLE_NODE_TYPE, NULL, /* modeventhand_t */ ngh_cons, /* ng_constructor_t */ - NULL, /* ng_rcvmsg_t */ + ngh_rcvmsg, /* ng_rcvmsg_t */ NULL, /* ng_shutdown_t */ - NULL, /* ng_newhook_t */ + ngh_newhook, /* ng_newhook_t */ NULL, /* ng_findhook_t */ NULL, /* ng_connect_t */ ngh_rcvdata, /* ng_rcvdata_t */ ngh_disconnect, /* ng_disconnect_t */ - NULL /* ng_cmdlist */ + ng_hole_cmdlist /* ng_cmdlist */ }; NETGRAPH_INIT(hole, &typestruct); @@ -83,12 +126,91 @@ ngh_cons(node_p node) return(0); } +/* + * Add a hook. + */ +static int +ngh_newhook(node_p node, hook_p hook, const char *name) +{ + hinfo_p hip; + + /* Create hook private structure. */ + MALLOC(hip, hinfo_p, sizeof(*hip), M_NETGRAPH, M_NOWAIT | M_ZERO); + if (hip == NULL) + return (ENOMEM); + NG_HOOK_SET_PRIVATE(hook, hip); + return (0); +} + +/* + * Receive a control message. + */ +static int +ngh_rcvmsg(node_p node, item_p item, hook_p lasthook) +{ + struct ng_mesg *msg; + struct ng_mesg *resp = NULL; + int error = 0; + struct ng_hole_hookstat *stats; + hook_p hook; + + NGI_GET_MSG(item, msg); + switch (msg->header.typecookie) { + case NGM_HOLE_COOKIE: + switch (msg->header.cmd) { + case NGM_HOLE_GET_STATS: + case NGM_HOLE_CLR_STATS: + case NGM_HOLE_GETCLR_STATS: + /* Sanity check. */ + if (msg->header.arglen != NG_HOOKLEN + 1) { + error = EINVAL; + break; + } + /* Find hook. */ + hook = ng_findhook(node, (char *)msg->data); + if (hook == NULL) { + error = ENOENT; + break; + } + stats = &((hinfo_p)NG_HOOK_PRIVATE(hook))->stats; + /* Build response (if desired). */ + if (msg->header.cmd != NGM_HOLE_CLR_STATS) { + NG_MKRESPONSE(resp, msg, sizeof(*stats), + M_NOWAIT); + if (resp == NULL) { + error = ENOMEM; + break; + } + bcopy(stats, resp->data, sizeof(*stats)); + } + /* Clear stats (if desired). */ + if (msg->header.cmd != NGM_HOLE_GET_STATS) + bzero(stats, sizeof(*stats)); + break; + default: /* Unknown command. */ + error = EINVAL; + break; + } + break; + default: /* Unknown type cookie. */ + error = EINVAL; + break; + } + NG_RESPOND_MSG(error, node, item, resp); + NG_FREE_MSG(msg); + return (error); +} + /* * Receive data */ static int ngh_rcvdata(hook_p hook, item_p item) { + const hinfo_p hip = NG_HOOK_PRIVATE(hook); + + hip->stats.frames++; + hip->stats.octets += NGI_M(item)->m_pkthdr.len; NG_FREE_ITEM(item); return 0; } @@ -99,6 +221,8 @@ ngh_rcvdata(hook_p hook, item_p item) static int ngh_disconnect(hook_p hook) { + + NG_HOOK_SET_PRIVATE(hook, NULL); if (NG_NODE_NUMHOOKS(NG_HOOK_NODE(hook)) == 0) ng_rmnode_self(NG_HOOK_NODE(hook)); return (0); diff --git a/sys/netgraph/ng_hole.h b/sys/netgraph/ng_hole.h index cff47aa157dd..d2da2d29f09a 100644 --- a/sys/netgraph/ng_hole.h +++ b/sys/netgraph/ng_hole.h @@ -46,5 +46,25 @@ /* Node type name and magic cookie */ #define NG_HOLE_NODE_TYPE "hole" #define NGM_HOLE_COOKIE 915433206 + +/* Statistics structure for one hook. */ +struct ng_hole_hookstat { + uint64_t frames; + uint64_t octets; +}; + +/* Keep this in sync with the above structure definition. */ +#define NG_HOLE_HOOKSTAT_TYPE_INFO { \ + { "frames", &ng_parse_uint64_type }, \ + { "octets", &ng_parse_uint64_type }, \ + { NULL } \ +} + +/* Netgraph commands. */ +enum { + NGM_HOLE_GET_STATS = 1, + NGM_HOLE_CLR_STATS, + NGM_HOLE_GETCLR_STATS, +}; #endif /* _NETGRAPH_NG_HOLE_H_ */