Following is a complete example that can be compiled and loaded into the kernel.
Use the following commands to compile this code into a working kernel module on a 64–bit system:
# gcc -D_KERNEL -m64 -c full.c # ld -dy -Nmisc/neti -Nmisc/hook -r full.o -o full |
/* * This file is a test module written to test the netinfo APIs in OpenSolaris. * It is being published to demonstrate how the APIs can be used. */ #include <sys/param.h> #include <sys/sunddi.h> #include <sys/modctl.h> #include <sys/ddi.h> #include "neti.h" /* * Module linkage information for the kernel. */ static struct modldrv modlmisc = { &mod_miscops, /* drv_modops */ "neti test module", /* drv_linkinfo */ }; static struct modlinkage modlinkage = { MODREV_1, /* ml_rev */ &modlmisc, /* ml_linkage */ NULL }; typedef struct scratch_s { int sentinel_1; netid_t id; int sentinel_2; int event_notify; int sentinel_3; int v4_event_notify; int sentinel_4; int v6_event_notify; int sentinel_5; int arp_event_notify; int sentinel_6; int v4_hook_notify; int sentinel_7; int v6_hook_notify; int sentinel_8; int arp_hook_notify; int sentinel_9; hook_t *v4_h_in; int sentinel_10; hook_t *v6_h_in; int sentinel_11; hook_t *arp_h_in; int sentinel_12; net_handle_t v4; int sentinel_13; net_handle_t v6; int sentinel_14; net_handle_t arp; int sentinel_15; } scratch_t; #define MAX_RECALL_DOLOG 10000 char recall_myname[10]; net_instance_t *recall_global; int recall_inited = 0; int recall_doing[MAX_RECALL_DOLOG]; int recall_doidx = 0; kmutex_t recall_lock; int recall_continue = 1; timeout_id_t recall_timeout; int recall_steps = 0; int recall_alloced = 0; void *recall_alloclog[MAX_RECALL_DOLOG]; int recall_freed = 0; void *recall_freelog[MAX_RECALL_DOLOG]; static int recall_init(void); static void recall_fini(void); static void *recall_create(const netid_t id); static void recall_shutdown(const netid_t id, void *arg); static void recall_destroy(const netid_t id, void *arg); static int recall_newproto(hook_notify_cmd_t cmd, void *arg, const char *parent, const char *event, const char *hook); static int recall_newevent(hook_notify_cmd_t cmd, void *arg, const char *parent, const char *event, const char *hook); static int recall_newhook(hook_notify_cmd_t cmd, void *arg, const char *parent, const char *event, const char *hook); static void recall_expire(void *arg); static void recall_strfree(char *); static char *recall_strdup(char *, int); static void recall_add_do(int mydo) { mutex_enter(&recall_lock); recall_doing[recall_doidx] = mydo; recall_doidx++; recall_steps++; if ((recall_steps % 1000000) == 0) printf("stamp %d %d\n", recall_steps, recall_doidx); if (recall_doidx == MAX_RECALL_DOLOG) recall_doidx = 0; mutex_exit(&recall_lock); } static void *recall_alloc(size_t len, int wait) { int i; mutex_enter(&recall_lock); i = recall_alloced++; if (recall_alloced == MAX_RECALL_DOLOG) recall_alloced = 0; mutex_exit(&recall_lock); recall_alloclog[i] = kmem_alloc(len, wait); return recall_alloclog[i]; } static void recall_free(void *ptr, size_t len) { int i; mutex_enter(&recall_lock); i = recall_freed++; if (recall_freed == MAX_RECALL_DOLOG) recall_freed = 0; mutex_exit(&recall_lock); recall_freelog[i] = ptr; kmem_free(ptr, len); } static void recall_assert(scratch_t *s) { ASSERT(s->sentinel_1 == 0); ASSERT(s->sentinel_2 == 0); ASSERT(s->sentinel_3 == 0); ASSERT(s->sentinel_4 == 0); ASSERT(s->sentinel_5 == 0); ASSERT(s->sentinel_6 == 0); ASSERT(s->sentinel_7 == 0); ASSERT(s->sentinel_8 == 0); ASSERT(s->sentinel_9 == 0); ASSERT(s->sentinel_10 == 0); ASSERT(s->sentinel_11 == 0); ASSERT(s->sentinel_12 == 0); ASSERT(s->sentinel_13 == 0); ASSERT(s->sentinel_14 == 0); ASSERT(s->sentinel_15 == 0); } int _init(void) { int error; bzero(recall_doing, sizeof(recall_doing)); mutex_init(&recall_lock, NULL, MUTEX_DRIVER, NULL); error = recall_init(); if (error == DDI_SUCCESS) { error = mod_install(&modlinkage); if (error != 0) recall_fini(); } recall_timeout = timeout(recall_expire, NULL, drv_usectohz(500000)); return (error); } int _fini(void) { int error; recall_continue = 0; if (recall_timeout != NULL) { untimeout(recall_timeout); recall_timeout = NULL; } error = mod_remove(&modlinkage); if (error == 0) { recall_fini(); delay(drv_usectohz(500000)); /* .5 seconds */ mutex_destroy(&recall_lock); ASSERT(recall_inited == 0); } return (error); } int _info(struct modinfo *info) { return(0); } static int recall_init() { recall_global = net_instance_alloc(NETINFO_VERSION); strcpy(recall_myname, "full_"); bcopy(((char *)&recall_global) + 4, recall_myname + 5, 4); recall_myname[5] = (recall_myname[5] & 0x7f) | 0x20; recall_myname[6] = (recall_myname[6] & 0x7f) | 0x20; recall_myname[7] = (recall_myname[7] & 0x7f) | 0x20; recall_myname[8] = (recall_myname[8] & 0x7f) | 0x20; recall_myname[9] = '\0'; recall_global->nin_create = recall_create; recall_global->nin_shutdown = recall_shutdown; recall_global->nin_destroy = recall_destroy; recall_global->nin_name = recall_myname; if (net_instance_register(recall_global) != 0) return (DDI_FAILURE); return (DDI_SUCCESS); } static void recall_fini() { if (recall_global != NULL) { net_instance_unregister(recall_global); net_instance_free(recall_global); recall_global = NULL; } } static void recall_expire(void *arg) { if (!recall_continue) return; recall_fini(); if (!recall_continue) return; delay(drv_usectohz(5000)); /* .005 seconds */ if (!recall_continue) return; if (recall_init() == DDI_SUCCESS) recall_timeout = timeout(recall_expire, NULL, drv_usectohz(5000)); /* .005 seconds */ } static void * recall_create(const netid_t id) { scratch_t *s = kmem_zalloc(sizeof(*s), KM_SLEEP); if (s == NULL) return (NULL); recall_inited++; s->id = id; net_instance_notify_register(id, recall_newproto, s); return s; } static void recall_shutdown(const netid_t id, void *arg) { scratch_t *s = arg; ASSERT(s != NULL); recall_add_do(__LINE__); net_instance_notify_unregister(id, recall_newproto); if (s->v4 != NULL) { if (s->v4_h_in != NULL) { net_hook_unregister(s->v4, NH_PHYSICAL_IN, s->v4_h_in); recall_strfree(s->v4_h_in->h_name); hook_free(s->v4_h_in); s->v4_h_in = NULL; } if (net_protocol_notify_unregister(s->v4, recall_newevent)) cmn_err(CE_WARN, "v4:net_protocol_notify_unregister(%p) failed", s->v4); net_protocol_release(s->v4); s->v4 = NULL; } if (s->v6 != NULL) { if (s->v6_h_in != NULL) { net_hook_unregister(s->v6, NH_PHYSICAL_IN, s->v6_h_in); recall_strfree(s->v6_h_in->h_name); hook_free(s->v6_h_in); s->v6_h_in = NULL; } if (net_protocol_notify_unregister(s->v6, recall_newevent)) cmn_err(CE_WARN, "v6:net_protocol_notify_unregister(%p) failed", s->v6); net_protocol_release(s->v6); s->v6 = NULL; } if (s->arp != NULL) { if (s->arp_h_in != NULL) { net_hook_unregister(s->arp, NH_PHYSICAL_IN, s->arp_h_in); recall_strfree(s->arp_h_in->h_name); hook_free(s->arp_h_in); s->arp_h_in = NULL; } if (net_protocol_notify_unregister(s->arp, recall_newevent)) cmn_err(CE_WARN, "arp:net_protocol_notify_unregister(%p) failed", s->arp); net_protocol_release(s->arp); s->arp = NULL; } } static void recall_destroy(const netid_t id, void *arg) { scratch_t *s = arg; ASSERT(s != NULL); recall_assert(s); ASSERT(s->v4 == NULL); ASSERT(s->v6 == NULL); ASSERT(s->arp == NULL); ASSERT(s->v4_h_in == NULL); ASSERT(s->v6_h_in == NULL); ASSERT(s->arp_h_in == NULL); kmem_free(s, sizeof(*s)); ASSERT(recall_inited > 0); recall_inited--; } static int recall_newproto(hook_notify_cmd_t cmd, void *arg, const char *parent, const char *event, const char *hook) { scratch_t *s = arg; s->event_notify++; recall_assert(s); switch (cmd) { case HN_REGISTER : if (strcmp(parent, NHF_INET) == 0) { s->v4 = net_protocol_lookup(s->id, parent); net_protocol_notify_register(s->v4, recall_newevent, s); } else if (strcmp(parent, NHF_INET6) == 0) { s->v6 = net_protocol_lookup(s->id, parent); net_protocol_notify_register(s->v6, recall_newevent, s); } else if (strcmp(parent, NHF_ARP) == 0) { s->arp = net_protocol_lookup(s->id, parent); net_protocol_notify_register(s->arp,recall_newevent, s); } break; case HN_UNREGISTER : case HN_NONE : break; } return 0; } static int recall_do_event(hook_event_token_t tok, hook_data_t data, void *ctx) { scratch_t *s = ctx; recall_assert(s); return (0); } static int recall_newevent(hook_notify_cmd_t cmd, void *arg, const char *parent, const char *event, const char *hook) { scratch_t *s = arg; char buffer[32]; hook_t *h; recall_assert(s); if (strcmp(event, NH_PHYSICAL_IN) == 0) { sprintf(buffer, "%s_%s_%s", recall_myname, parent, event); h = hook_alloc(HOOK_VERSION); h->h_hint = HH_NONE; h->h_arg = s; h->h_name = recall_strdup(buffer, KM_SLEEP); h->h_func = recall_do_event; } else { h = NULL; } if (strcmp(parent, NHF_INET) == 0) { s->v4_event_notify++; if (h != NULL) { s->v4_h_in = h; net_hook_register(s->v4, (char *)event, h); } net_event_notify_register(s->v4, (char *)event, recall_newhook, s); } else if (strcmp(parent, NHF_INET6) == 0) { s->v6_event_notify++; if (h != NULL) { s->v6_h_in = h; net_hook_register(s->v6, (char *)event, h); } net_event_notify_register(s->v6, (char *)event, recall_newhook, s); } else if (strcmp(parent, NHF_ARP) == 0) { s->arp_event_notify++; if (h != NULL) { s->arp_h_in = h; net_hook_register(s->arp, (char *)event, h); } net_event_notify_register(s->arp, (char *)event, recall_newhook, s); } recall_assert(s); return (0); } static int recall_newhook(hook_notify_cmd_t cmd, void *arg, const char *parent, const char *event, const char *hook) { scratch_t *s = arg; recall_assert(s); if (strcmp(parent, NHF_INET) == 0) { s->v4_hook_notify++; } else if (strcmp(parent, NHF_INET6) == 0) { s->v6_hook_notify++; } else if (strcmp(parent, NHF_ARP) == 0) { s->arp_hook_notify++; } recall_assert(s); return (0); } static void recall_strfree(char *str) { int len; if (str != NULL) { len = strlen(str); recall_free(str, len + 1); } } static char* recall_strdup(char *str, int wait) { char *newstr; int len; len = strlen(str); newstr = recall_alloc(len, wait); if (newstr != NULL) strcpy(newstr, str); return (newstr); }