Skip Navigation Links | |
Exit Print View | |
Programming Interfaces Guide Oracle Solaris 11.1 Information Library |
2. Session Description Protocol API
8. Programming With XTI and TLI
Packet Filtering Hooks Interfaces
Packet Filtering Hooks Kernel Functions
Packet Filtering Hooks Data Types
Using the Packet Filtering Hooks Interfaces
10. Transport Selection and Name-to-Address Mapping
11. Real-time Programming and Administration
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
Example 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) { snprintf(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)); }