パケットフィルタリングフックインタフェースにより、セキュリティ (パケットフィルタリングやファイアウォール) ソリューション、ネットワークアドレス変換 (Network Address Translation、NAT) ソリューションなど、カーネルレベルの付加価値ネットワークソリューションの開発が容易になります。
パケットフィルタリングフックインタフェースは、次の機能を提供します。
フックポイントのいずれかにパケットが到達するたびの通知
IP の排他インスタンスを必要とする新しいゾーンブートをサポートするために IP の新しいインスタンスが作成されるたびの通知
インタフェースの名前やアドレスなど、その他の基本的なネットワークインタフェース情報へのカーネルのアクセス
ループバックインタフェースでのパケット傍受
ループバックパケット傍受では、IP の共有インスタンスを使用しているゾーン間でパケットが移動するときに、それらのパケットにアクセスすることもできます。これはデフォルトのモデルです。
パケットフィルタリングフックインタフェースには、カーネル関数とデータ型の定義が含まれます。
パケットフィルタリングフックのカーネル関数は、パケットフィルタリングをサポートする misc/neti カーネルモジュールおよび misc/hook カーネルモジュールからエクスポートされます。これらの関数を使用するためには、カーネルモジュールを -Nmisc/neti と -Nmisc/hook にリンクして、関数がカーネルによって正しく読み込まれるようにします。
hook_t データ構造体を割り当てます。
hook_alloc によって最初に割り当てられた hook_t() 構造体を解放します。
指定されたイベントに対する変更が発生したときに呼び出される関数を登録します。
指定されたコールバック関数の呼び出しによる、指定されたイベントに対する変更の通知をこれ以上受け取らないことを示します。
指定のネットワークインタフェースに指定された名前を取得します。
指定された各論理インタフェースのネットワークアドレス情報を取得します。
指定されたネットワークインタフェースの現在の MTU に関する情報を取得します。
指定されたネットワークプロトコルに対してパス MTU (Path MTU、PMTU) 検出が有効かどうかを示します。
指定されたネットワークプロトコルに属するイベントにコールバックを登録できるようにするフックを追加します。
net_hook_register() によって登録されたコールバックフックを無効にします。
ネットワーク層のパケットをカーネルまたはネットワークに配信します。
net_inject_t 構造体を割り当てます。
net_inject_alloc によって最初に割り当てられた net_inject_t() 構造体を解放します。
net_instance_t 構造体を割り当てます。
net_instance_alloc によって最初に割り当てられた net_instance_t() 構造体を解放します。
指定のネットワークインスタンスに対して新しいインスタンスが追加または削除されたときに呼び出される指定の関数を登録します。
指定されたコールバック関数の呼び出しによる、指定されたインスタンスに対する変更の通知をこれ以上受け取らないことを示します。
IP インスタンスの保守に関連するイベントが発生するときに呼び出される関数のセットを記録します。
net_instance_register() によって以前に登録されたインスタンスのセットを削除します。
指定されたパケットに、部分チェックサム値のみを持つヘッダーが含まれるかどうかを示します。
指定されたパケットのレイヤー 3 チェックサム、および場合によってはレイヤー 4 チェックサムを検査します。
IP の指定されたインスタンスの新しい kstat(9S) 構造体を割り当てて初期化します。
IP の指定されたインスタンスの kstat をシステムから削除します。
物理ネットワークインタフェースに関連付けられているすべての論理インタフェースを検索します。
ネットワークプロトコルが「所有」するすべてのネットワークインタフェースを検索します。
ネットワークプロトコルの指定されたインタフェース名の取得を試行します。
ネットワーク層プロトコルの実装を検出します。
指定のプロトコルに対する変更が発生するときに呼び出される指定の関数を登録します。
呼び出す関数のリストから、指定された関数を削除します。
指定されたネットワークプロトコルへの参照が必要なくなったことを示します。
送信されるネットワークインタフェースパケットを示します。
次の型が前述の関数をサポートしています。
ネットワークイベントに挿入されるコールバック。
発生した、ネットワークインタフェースに属するイベント。
フックに渡されるパケットイベント構造体。
パケットの転送方法に関する情報。
関連イベントが IP 内で発生するときに呼び出されるインスタンスのコレクション。
この API では IP スタックの複数のインスタンスを同じカーネルで同時に実行できるので、パケットフィルタリングフックインタフェースを操作するためには、ある程度の量のプログラミングが必要になります。IP スタックでは、ゾーンに対してその IP スタック自体の複数のインスタンスを使用でき、フレームワークの複数のインスタンスは、IP でのパケット傍受をサポートしています。
この節では、パケットフィルタリングフック API を使用してインバウンド IPv4 パケットを受信するコード例を示します。
この API を使用する場合は、まず、IP の複数のインスタンスを 1 つのカーネルで実行できるようにするか、大域ゾーンとやりとりするだけにするかを決定する必要があります。
IP インスタンスの存在がわかるように、インスタンスの作成、破棄、および終了時に起動されるコールバック関数を登録します。これら 3 つの関数ポインタを格納する net_instance_t() パケットイベント構造体を割り当てるには、net_instance_alloc を使用します。コールバックや構造体が必要なくなったときにリソースを解放するには、net_instance_free() を使用します。構造体のインスタンスに名前を指定するには、nin_name を指定します。少なくとも、nin_create() コールバックと nin_destroy() コールバックを指定します。IP の新しいインスタンスが作成されると nin_create() 関数が呼び出され、IP のインスタンスが破棄されると nin_destroy() 関数が呼び出されます。
nin_shutdown() の指定は、kstat に情報をエクスポートする場合を除いて任意です。インスタンス単位で kstat を使用するには、作成コールバック時に net_kstat_create() を使用します。kstat 情報のクリーンアップは、破棄コールバックではなく、終了コールバック時に行います。kstat 情報をクリーンアップするには、net_kstat_delete() を使用します。
extern void *mycreate(const netid_t); net_instance_t *n; n = net_instance_alloc(NETINFO_VERSION); if (n != NULL) { n->nin_create = mycreate; n->nin_destroy = mydestroy; n->nin_name = "my module"; if (net_instance_register(n) != 0) net_instance_free(n); }
net_instance_alloc() が呼び出される場合に IP のインスタンスが 1 つ以上あるときは、現在有効なインスタンスごとに作成コールバックが呼び出されます。コールバックをサポートするこのフレームワークでは、特定のインスタンスに対して一度に有効になるのは、作成関数、破棄関数、または終了関数のいずれか 1 つだけです。また、作成コールバックが呼び出されると、作成コールバックが完了するまで終了コールバックは呼び出されません。同様に、破棄コールバックは、終了コールバックが完了するまで開始されません。
次の例の mycreate() 関数は、作成コールバックの簡単な例です。mycreate() 関数は、ネットワークインスタンス識別子を独自の非公開のコンテキスト構造体に記録し、新しいプロトコル (IPv4 や IPv6 など) がこのフレームワークに登録されるときに呼び出される新しいコールバックを登録します。
ゾーンが実行されていないために大域ゾーン以外のインスタンスがない場合、net_instance_register() を呼び出すと、大域ゾーンに対して作成コールバックが実行されます。あとで net_instance_unregister() が呼び出されるように、破棄コールバックを指定してください。nin_create() フィールドまたは nin_destroy フィールドを NULL に設定して net_instance_register を呼び出すと、失敗します。
void * mycreate(const netid_t id) { mytype_t *ctx; ctx = kmem_alloc(sizeof(*ctx), KM_SLEEP); ctx->instance_id = id; net_instance_notify_register(id, mynewproto, ctx); return (ctx); }
mynewproto() 関数は、ネットワークインスタンスに対してネットワークプロトコルが追加または削除されるたびに呼び出されることになります。登録したネットワークプロトコルが特定のインスタンス内ですでに動作中の場合、作成コールバックは、すでに存在するプロトコルごとに呼び出されます。
このコールバックでは、proto 引数のみ呼び出し元によって指定されます。この時点では、有意なイベント名もフック名も指定できません。この例の関数では、IPv4 プロトコルの登録を通知するイベントのみ検索されます。
この関数では、次に、net_protocol_notify_register() インタフェースを使用して mynewevent() 関数を登録することによって IPv4 プロトコルにイベントが追加されたことを検出します。
static int mynewproto(hook_notify_cmd_t cmd, void *arg, const char *proto, const char *event, const char *hook) { mytype_t *ctx = arg; if (strcmp(proto, NHF_INET) != 0) return (0); switch (cmd) { case HN_REGISTER : ctx->inet = net_protocol_lookup(s->id, proto); net_protocol_notify_register(s->inet, mynewevent, ctx); break; case HN_UNREGISTER : case HN_NONE : break; } return (0); }
次の表に、mynewproto() コールバックで使用されることがある 3 つのプロトコルを示します。今後、新しいプロトコルが追加される可能性があるので、不明なプロトコルは確実にエラー (戻り値 0) にしてください。
プログラミング記号 |
プロトコル |
---|---|
NHF_INET |
IPv4 |
NHF_INET6 |
IPv6 |
NHF_ARP |
ARP |
インスタンスやプロトコルの処理が動的であるのと同様に、各プロトコル下で行われるイベントの処理も動的です。この API では、ネットワークインタフェースイベントとパケットイベントの 2 種類のイベントがサポートされています。
次の関数では、IPv4 のインバウンドパケットのイベントが存在することの通知についてチェックされます。通知があると、hook_t 構造体が割り当てられ、インバウンド IPv4 パケットごとに呼び出される関数が記述されます。
static int mynewevent(hook_notify_cmd_t cmd, void *arg, const char *parent, const char *event, const char *hook) { mytype_t *ctx = arg; char buffer[32]; hook_t *h; if ((strcmp(event, NH_PHYSICAL_IN) == 0) && (strcmp(parent, NHF_INET) == 0)) { sprintf(buffer, "mypkthook_%s_%s", parent, event); h = hook_alloc(HOOK_VERSION); h->h_hint = HH_NONE; h->h_arg = s; h->h_name = strdup(buffer); h->h_func = mypkthook; s->hook_in = h; net_hook_register(ctx->inet, (char *)event, h); } else { h = NULL; } return (0); }
mynewevent() 関数は、追加および削除されるイベントごとに呼び出されます。次のイベントを使用できます。
イベント名 |
データ構造体 |
コメント |
---|---|---|
NH_PHYSICAL_IN |
hook_pkt_event_t |
このイベントは、ネットワークプロトコルに到達し、ネットワークインタフェースドライバから受信されたパケットごとに生成されます。 |
NH_PHYSICAL_OUT |
hook_pkt_event_t |
このイベントは、ネットワークプロトコル層から送信するために、ネットワークインタフェースドライバに配信する前に、パケットごとに生成されます。 |
NH_FORWARDING |
hook_pkt_event_t |
このイベントは、システムで受信されて別のネットワークインタフェースに送信されるすべてのパケットに対して生成されます。このイベントが発生するのは NH_PHYSICAL_IN の後と NH_PHYSICAL_OUT の前です。 |
NH_LOOPBACK_IN |
hook_pkt_event_t |
このイベントは、ループバックインタフェースで受信されるパケット、またはネットワークインスタンスを大域ゾーンと共有しているゾーンで受信されるパケットに対して生成されます。 |
NH_LOOPBACK_OUT |
hook_pkt_event_t |
このイベントは、ループバックインタフェースで送信されるパケット、またはネットワークインスタンスを大域ゾーンと共有しているゾーンで送信されているパケットに対して生成されます。 |
NH_NIC_EVENTS |
hook_nic_event_t |
このイベントは、ネットワークインタフェースの状態の特定の変更に対して生成されます。 |
パケットイベントの場合、IP スタックの特定のポイントごとに固有のイベントが 1 つあります。これは、パケットのフローにおいてパケットを傍受する正確な位置を選択できるようにするためであり、カーネル内で発生するすべてのパケットイベントを検査して過負荷状態になるのを避けることができます。ネットワークインタフェースイベントの場合、モデルは異なります。これは、1 つにはイベントの量が少ないためであり、また、必要とされるイベントが 1 つではなく複数であることが多いためです。
ネットワークインタフェースイベントは、次のイベントのいずれかを通知します。
インタフェースが作成 (NE_PLUMB) または破棄 (NE_UNPLUMB) されます。
インタフェースの状態がアップ (NE_UP) またはダウン (NE_DOWN) に変更します。
インタフェースにアドレスの変更 (NE_ADDRESS_CHANGE) があります。
今後、新しいネットワークインタフェースイベントが追加される可能性があるので、コールバック関数が受信した不明なイベントや認識できないイベントに対しては、常に 0 を返してください。
パケットフック関数は、パケットが受信されると呼び出されます。この場合、mypkthook() 関数は、物理ネットワークインタフェースからカーネルに受信するインバウンドパケットごとに呼び出されることになります。共有 IP インスタンスモデルを使用するゾーン間またはループバックインタフェース上を流れる、内部的に生成されたパケットは、対象になりません。
パケットを受け取ることと、パケットのドロップに必要なものを関数が正常に返すようにすることとの違いを示すために、次のコードでは、パケット 100 個ごとの発信元アドレスと宛先アドレスを出力し、パケットをドロップして、パケットロスを 1% にします。
static int mypkthook(hook_event_token_t tok, hook_data_t data, void *arg) { static int counter = 0; mytupe_t *ctx = arg; hook_pkt_event_t *pkt = (hook_pkt_event_t)data; struct ip *ip; size_t bytes; bytes = msgdsize(pkt->hpe_mb); ip = (struct ip *)pkt->hpe_hdr; counter++; if (counter == 100) { printf("drop %d bytes received from %x to %x\n", bytes, ntohl(ip->ip_src.s_addr), ntohl(ip->ip_dst.s_addr)); counter = 0; freemsg(*pkt->hpe_mp); *pkt->hpe_mp = NULL; pkt->hpe_mb = NULL; pkt->hpe_hdr = NULL; return (1); } return (0); }
この関数で受信されたパケットと、パケットイベントからコールバックとして呼び出されるすべての要素は、1 つずつ受信されます。パケットとこのインタフェースは連鎖していないので、呼び出しごとにパケットは 1 個だけであり、b_next は常に NULL になります。ほかのパケットはありませんが、1 個のパケットが、b_cont と連鎖した複数の mblk_t 構造体で構成されることがあります。
コンパイルしてカーネルに読み込むことができる完全な例を次に示します。
64 ビットシステムで動作中のカーネルモジュールにこのコードをコンパイルするには、次のコマンドを使用します。
# 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); }