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 fullExample 9-1 Packet Filtering Hooks Example Program
* Copyright (c) 2012, Oracle and/or its affiliates. All rights reserved. */ /* * This file is a test module written to test the netinfo APIs in Oracle Solaris 11. * 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) { sn printf(buffer, sizeof(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); }Example 9-2 net_inject Example Program
* Copyright (c) 2012, Oracle and/or its affiliates. All rights reserved. */ * PAMP driver - Ping Amplifier enables Solaris to send two ICMP echo * responses for every ICMP request. * This example provides a test module of the Oracle Solaris PF-hooks * (netinfo(9f)) API.This example discovers ICMP echo * implementation by intercepting inbound packets using * physical-in` event hook. * If the intercepted packet happens to be a ICMPv4 echo request, * the module will generate a corresponding ICMP echo response * which will then be sent to the network interface card using * the net_inject(9f) function. The original ICMPv4 echo request will be * allowed to enter the the IP stack so that the request can be * processed by the destination IP stack. * The destination stack in turn will send its own ICMPv4 echo response. * Therefore there will be two ICMPv4 echo responses for a single * ICMPv4 echo request. * * The following example code demonstrates two key functions of netinfo(9f) API: * * Packet Interception * * Packet Injection * * In order to be able to talk to netinfo(9f), the driver must allocate and * register its own net_instance_t - `pamp_ninst`. This happens in the * pamp_attach() function, which imlements `ddi_attach` driver operation.The * net_instance_t registers three callbacks with netinfo(9f) module: * _create * _shutdown * _destroy * The netinfo(9f) command uses these functions to request the driver to * create, shutdown, or destroy the driver context bound to a particular IP instance. * This will enable the driver to handle packets for every IP stack found in * the Oracle Solaris kernel. For purposes of this example, the driver is always * implicitly bound to every IP instance. */ /* Use the following makefile to build the driver:: /* Begin Makefile */ ALL = pamp_drv pamp_drv.conf pamp_drv = pamp_drv.o pamp_drv.conf: pamp_drv echo 'name="pamp_drv" parent="pseudo" instance=0;' > pamp_drv.conf pamp_drv: pamp_drv.o ld -dy -r -Ndrv/ip -Nmisc/neti -Nmsic/hook -o pamp_drv pamp_drv.o pamp_drv.o: pamp_drv.c cc -m64 -xmodel=kernel -D_KERNEL -c -o $@ $< install: cp pamp_drv /usr/kernel/drv/`isainfo -k`/pamp_drv cp pamp_drv.conf /usr/kernel/drv/pamp_drv.conf uninstall: rm -rf /usr/kernel/drv/`isainfo -k`/pamp_drv rm -rf /usr/kernel/drv/pamp_drv.conf clean: rm -f pamp_drv.o pamp_drv pamp_drv.conf *End Makefile */ * * The Makefile shown above will build a pamp_drv driver binary * and pamp_drv.conf file for driver configuration. If you are * building on a test machine, use `make install` to place * driver and configuration files in the specified location. * Otherwise copy the pamp_drv binary and the pamp_drv.conf files to your test machine manually. * * Run the following command to load the driver to kernel: add_drv pam_drv * Run the following command to unload the driver to kernel: rem_drv pamp_drv * * To check if your driver is working you need to use a snoop * and `ping` which will be running * on a remote host. Start snoop on your network interface: snoop -d netX icmp * Run a ping on a remote host: ping -ns <test.box> * test.box refers to the system where the driver is installed. * * The snoop should show there are two ICMP echo replies for every ICMP echo * request. The expected output should be similar to the snoop output shown below: * 172.16.1.2 -> 172.16.1.100 ICMP Echo request (ID: 16652 Sequence number: 0) * 172.16.1.100 -> 172.16.1.2 ICMP Echo reply (ID: 16652 Sequence number: 0) * 172.16.1.100 -> 172.16.1.2 ICMP Echo reply (ID: 16652 Sequence number: 0) * 172.16.1.2 -> 172.16.1.100 ICMP Echo request (ID: 16652 Sequence number: 1) * 172.16.1.100 -> 172.16.1.2 ICMP Echo reply (ID: 16652 Sequence number: 1) * 172.16.1.100 -> 172.16.1.2 ICMP Echo reply (ID: 16652 Sequence number: 1) * 172.16.1.2 -> 172.16.1.100 ICMP Echo request (ID: 16652 Sequence number: 2) * 172.16.1.100 -> 172.16.1.2 ICMP Echo reply (ID: 16652 Sequence number: 2) * 172.16.1.100 -> 172.16.1.2 ICMP Echo reply (ID: 16652 Sequence number: 2) */ #include <sys/atomic.h> #include <sys/ksynch.h> #include <sys/ddi.h> #include <sys/modctl.h> #include <sys/random.h> #include <sys/sunddi.h> #include <sys/stream.h> #include <sys/devops.h> #include <sys/stat.h> #include <sys/modctl.h> #include <sys/neti.h> #include <sys/hook.h> #include <sys/hook_event.h> #include <sys/synch.h> #include <inet/ip.h> #include <netinet/in_systm.h> #include <netinet/in.h> #include <netinet/ip.h #include <netinet/ip_icmp.h> /* * This is a context for the driver. The context is allocated by * pamp_nin_create() callback for every IP instance found in kernel. */ typedef struct pamp_ipstack { hook_t *pamp_phyin; int pamp_hook_ok; net_handle_t pamp_ipv4; } pamp_ipstack_t; static kmutex_t pamp_stcksmx; /* * The netinstance, which passes driver callbacks to netinfo module. */ static net_instance_t *pamp_ninst = NULL; /* * Solaris kernel driver APIs. */ static int pamp_getinfo(dev_info_t *, ddi_info_cmd_t, void *, void **); static int pamp_attach(dev_info_t *, ddi_attach_cmd_t); static int pamp_detach(dev_info_t *, ddi_detach_cmd_t);static dev_info_t *pamp_dev_info = NULL; /* * Driver does not support any device operations. */ extern struct cb_ops no_cb_ops; static struct dev_ops pamp_ops = { DEVO_REV, 0, pamp_getinfo, nulldev, nulldev, pamp_attach, pamp_detach, nodev, &no_cb_ops, NULL, NULL, ddi_quiesce_not_needed, /* quiesce */ }; static struct modldrv pamp_module = { &mod_driverops, "ECHO_1", &pamp_ops }; static struct modlinkage pamp_modlink = { MODREV_1, &pamp_module, NULL }; /* * Netinfo stack instance create/destroy/shutdown routines. */ static void *pamp_nin_create(const netid_t); static void pamp_nin_destroy(const netid_t, void *); static void pamp_nin_shutdown(const netid_t, void *); /* * Callback to process intercepted packets delivered by hook event */ static int pamp_pkt_in(hook_event_token_t, hook_data_t, void *); /* * Kernel driver getinfo operation */ static int pamp_getinfo(dev_info_t *dip, ddi_info_cmd_t cmd, void * arg, void **resultp) { int e; switch (cmd) { case DDI_INFO_DEVT2DEVINFO: *resultp = pamp_dev_info; e = DDI_SUCCESS; break; case DDI_INFO_DEVT2INSTANCE: *resultp = NULL; e = DDI_SUCCESS; break; default: e = DDI_FAILURE; } return (e); } /* * Kernel driver attach operation. The job of the driver is to create a net * instance for our driver and register it with netinfo(9f) */ static int pamp_attach(dev_info_t *dip, ddi_attach_cmd_t cmd) { int rc; #define RETURN(_x_) do { mutex_exit(&pamp_stcksmx); return (_x_); } while (0) /* * Fail for all commands except DDI_ATTACH. */ if (cmd != DDI_ATTACH) { return (DDI_FAILURE); } mutex_enter(&pamp_stcksmx); /* * It is an error to apply attach operation on a driver which is already * attached. */ if (pamp_ninst != NULL) { RETURN(DDI_FAILURE); } /* * At most one driver instance is allowed (instance 0). */ if (ddi_get_instance(dip) != 0) { RETURN(DDI_FAILURE); } rc = ddi_create_minor_node(dip, "pamp", S_IFCHR, 0, DDI_PSEUDO, 0); if (rc != DDI_SUCCESS) { ddi_remove_minor_node(dip, NULL); RETURN(DDI_FAILURE); } /* * Create and register pamp net instance. Note we are assigning * callbacks _create, _destroy, _shutdown. These callbacks will ask * our driver to create/destroy/shutdown our IP driver instances. */ pamp_ninst = net_instance_alloc(NETINFO_VERSION); if (pamp_ninst == NULL) { ddi_remove_minor_node(dip, NULL); RETURN(DDI_FAILURE); } pamp_ninst->nin_name = "pamp"; pamp_ninst->nin_create = pamp_nin_create; pamp_ninst->nin_destroy = pamp_nin_destroy; pamp_ninst->nin_shutdown = pamp_nin_shutdown; pamp_dev_info = dip; mutex_exit(&pamp_stcksmx); /* * Although it is not shown in the following example, it is * recommended that all mutexes/exclusive locks be released before * * calling net_instance_register(9F) to avoid a recursive lock * entry. As soon as pamp_ninst is registered, the * net_instance_register(9f) will call pamp_nin_create() callback. * The callback will run in the same context as the one in which * pamp_attach() is running. If pamp_nin_create() grabs the same * lock held already by pamp_attach(), then such a lock is being * operated on recursively. */ (void) net_instance_register(pamp_ninst); return (DDI_SUCCESS); #undef RETURN } /* * The detach function will unregister and destroy our driver netinstance. The same rules * for exclusive locks/mutexes introduced for attach operation apply to detach. * The netinfo will take care to call the shutdown()/destroy() callbacks for * every IP stack instance. */ static int pamp_detach(dev_info_t *dip, ddi_detach_cmd_t cmd) { pamp_ipstack_t *pamp_ipstack; net_instance_t *ninst = NULL; /* * It is an error to apply detach operation on driver, when another * detach operation is running (in progress), or when detach operation * is complete (pamp_ninst). */ mutex_enter(&pamp_stcksmx); if (pamp_ninst == NULL) { mutex_exit(&pamp_stcksmx); return (DDI_FAILURE); } ninst = pamp_ninst; pamp_ninst = NULL; mutex_exit(&pamp_stcksmx); /* * Calling net_instance_unregister(9f) will invoke pamp_nin_destroy() * for every pamp_ipstack instance created so far. Therefore it is advisable * to not hold any mutexes, because it might get grabbed by pamp_nin_destroy() function. */ net_instance_unregister(ninst); net_instance_free(ninst); (void) ddi_get_instance(dip); ddi_remove_minor_node(dip, NULL); return (DDI_SUCCESS); } /* * Netinfo callback, which is supposed to create an IP stack context for our * ICMP echo server. * * NOTE: NULL return value is not interpreted as a failure here. The * pamp_nin_shutdown()/pamp_nin_destroy() will receive NULL pointer for IP stack * instance with given `netid` id. * */ static void * pamp_nin_create(const netid_t netid) { pamp_ipstack_t *pamp_ipstack; pamp_ipstack = (pamp_ipstack_t *)kmem_zalloc( sizeof (pamp_ipstack_t), KM_NOSLEEP); if (pamp_ipstack == NULL) { return (NULL); } HOOK_INIT(pamp_ipstack->pamp_phyin, pamp_pkt_in, "pkt_in", pamp_ipstack); pamp_ipstack->pamp_ipv4 = net_protocol_lookup(netid, NHF_INET); if (pamp_ipstack->pamp_ipv4 == NULL) { kmem_free(pamp_ipstack, sizeof (pamp_ipstack_t)); return (NULL); } pamp_ipstack->pamp_hook_ok = net_hook_register( pamp_ipstack->pamp_ipv4, NH_PHYSICAL_IN, pamp_ipstack->pamp_phyin); if (pamp_ipstack->pamp_hook_ok != 0) { net_protocol_release(pamp_ipstack->pamp_ipv4); hook_free(pamp_ipstack->pamp_phyin); kmem_free(pamp_ipstack, sizeof (pamp_ipstack_t)); return (NULL); } return (pamp_ipstack); } /* * This event is delivered right before the particular stack instance is * destroyed. */ static void pamp_nin_shutdown(const netid_t netid, void *stack) { return; } /* * Important to note here that the netinfo(9f) module ensures that no * no pamp_pkt_in() is "running" when the stack it is bound to is being destroyed. */ static void pamp_nin_destroy(const netid_t netid, void *stack) { pamp_ipstack_t *pamp_ipstack = (pamp_ipstack_t *)stack; /* * Remember stack can be NULL! The pamp_nin_create() function returns * NULL on failure. The return value of pamp_nin_create() function will * be `kept` in netinfo module as a driver context for particular IP * instance. As soon as the instance is destroyed the NULL value * will appear here in pamp_nin_destroy(). Same applies to * pamp_nin_shutdown(). Therefore our driver must be able to handle * NULL here. */ if (pamp_ipstack == NULL) return; /* * If driver has managed to initialize packet hook, then it has to be * unhooked here. */ if (pamp_ipstack->pamp_hook_ok != -1) { (void) net_hook_unregister(pamp_ipstack->pamp_ipv4, NH_PHYSICAL_IN, pamp_ipstack->pamp_phyin); hook_free(pamp_ipstack->pamp_phyin); (void) net_protocol_release(pamp_ipstack->pamp_ipv4); } kmem_free(pamp_ipstack, sizeof (pamp_ipstack_t)); } /* * Packet hook handler * * Function receives intercepted IPv4 packets coming from NIC to IP stack. If * inbound packet is ICMP ehco request, then function will generate ICMP echo * response and use net_inject() to send it to network. Function will also let * ICMP echo request in, so it will be still processed by destination IP stack, * which should also generate its own ICMP echo response. The snoop should show * you there will be two ICMP echo responses leaving the system where the pamp * driver is installed */ static int pamp_pkt_in(hook_event_token_t ev, hook_data_t info, void *arg) { hook_pkt_event_t *hpe = (hook_pkt_event_t *)info; phy_if_t phyif; struct ip *ip; /* * Since our pamp_pkt_in callback is hooked to PHYSICAL_IN hook pkt. * event only, the physical interface index will always be passed as * hpe_ifp member. * * If our hook processes PHYSICAL_OUT hook pkt event, then * the physical interface index will be passed as hpe_ofp member. */ phyif = hpe->hpe_ifp; ip = hpe->hpe_hdr; if (ip->ip_p == IPPROTO_ICMP) { mblk_t *mb; /* * All packets are copied/placed into a continuous buffer to make * parsing easier. */ if ((mb = msgpullup(hpe->hpe_mb, -1)) != NULL) { struct icmp *icmp; pamp_ipstack_t *pamp_ipstack = (pamp_ipstack_t *)arg; ip = (struct ip *)mb->b_rptr; icmp = (struct icmp *)(mb->b_rptr + IPH_HDR_LENGTH(ip)); if (icmp->icmp_type == ICMP_ECHO) { struct in_addr addr; uint32_t sum; mblk_t *echo_resp = copymsg(mb); net_inject_t ninj; /* * We need to make copy of packet, since we are * going to turn it into ICMP echo response. */ if (echo_resp == NULL) { return (0); } ip = (struct ip *)echo_resp->b_rptr; addr = ip->ip_src; ip->ip_src = ip->ip_dst; ip->ip_dst = addr; icmp = (struct icmp *) (echo_resp->b_rptr + IPH_HDR_LENGTH(ip)); icmp->icmp_type = ICMP_ECHO_REPLY; sum = ~ntohs(icmp->icmp_cksum) & 0xffff; sum += (ICMP_ECHO_REQUEST - ICMP_ECHO_REPLY); icmp->icmp_cksum = htons(~((sum >> 16) + (sum & 0xffff))); /* * Now we have assembled an ICMP response with * correct chksum. It's time to send it out. * We have to initialize command for * net_inject(9f) -- ninj. */ ninj.ni_packet = echo_resp; ninj.ni_physical = phyif; /* * As we are going use NI_QUEUE_OUT to send * our ICMP response, we don't need to set up * .ni_addr, which is required for NI_DIRECT_OUT * injection path only. In such case packet * bypasses IP stack routing and is pushed * directly to physical device queue. Therefore * net_inject(9f) requires as to specify * next-hop IP address. * * Using NI_QUEUE_OUT is more convenient for us * since IP stack will take care of routing * process and will find out `ni_addr` * (next-hop) address on its own. */ (void) net_inject(pamp_ipstack->pamp_ipv4, NI_QUEUE_OUT, &ninj); } } } /* * 0 as return value will let packet in. */ return (0); } /* * Kernel module handling. */ int init() { mutex_init(&pamp_stcksmx, "pamp_mutex", MUTEX_DRIVER, NULL); return (mod_install(&pamp_modlink)); } int fini() { int rv; rv = mod_remove(&pamp_modlink); return (rv); } int info(struct modinfo *modinfop) { return (mod_info(&pamp_modlink, modinfop)); }