/* |
(c) Copyright 2005 Apple Computer, Inc. All rights reserved. |
IMPORTANT: This Apple software is supplied to you by Apple Computer, Inc. (“Apple”) in |
consideration of your agreement to the following terms, and your use, installation, |
modification or redistribution of this Apple software constitutes acceptance of these |
terms. If you do not agree with these terms, please do not use, install, modify or |
redistribute this Apple software. |
In consideration of your agreement to abide by the following terms, and subject to |
these terms, Apple grants you a personal, non-exclusive license, under Apple’s copyrights |
in this original Apple software (the “Apple Software”), to use, reproduce, modify and |
redistribute the Apple Software, with or without modifications, in source and/or binary |
forms; provided that if you redistribute the Apple Software in its entirety and without |
modifications, you must retain this notice and the following text and disclaimers in all |
such redistributions of the Apple Software. Neither the name, trademarks, service marks |
or logos of Apple Computer, Inc. may be used to endorse or promote products derived |
from the Apple Software without specific prior written permission from Apple. Except |
as expressly stated in this notice, no other rights or licenses, express or implied, |
are granted by Apple herein, including but not limited to any patent rights that may |
be infringed by your derivative works or by other works in which the Apple Software |
may be incorporated. |
The Apple Software is provided by Apple on an "AS IS" basis. APPLE MAKES NO |
WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION THE IMPLIED WARRANTIES |
OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, REGARDING |
THE APPLE SOFTWARE OR ITS USE AND OPERATION ALONE OR IN COMBINATION WITH YOUR PRODUCTS. |
IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL OR |
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE |
GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) ARISING |
IN ANY WAY OUT OF THE USE, REPRODUCTION, MODIFICATION AND/OR DISTRIBUTION OF THE |
APPLE SOFTWARE, HOWEVER CAUSED AND WHETHER UNDER THEORY OF CONTRACT, TORT (INCLUDING |
NEGLIGENCE), STRICT LIABILITY OR OTHERWISE, EVEN IF APPLE HAS BEEN ADVISED OF THE |
POSSIBILITY OF SUCH DAMAGE. |
*/ |
#include <sys/lock.h> |
#include <mach/vm_types.h> |
#include <mach/kmod.h> |
#include <sys/socket.h> |
#include <sys/kpi_mbuf.h> |
#include <net/kpi_interface.h> |
#include <net/kpi_interfacefilter.h> |
#include <sys/syslog.h> |
#include <libkern/OSMalloc.h> |
#include <sys/kernel.h> |
#include <sys/systm.h> |
#include <netinet/in.h> |
#include <kern/locks.h> |
#include <sys/kern_event.h> |
#include <stdarg.h> |
#define SWALLOW_PACKETS 0 // set this define to 1 to demonstrate packet swallowing/re-injection |
// set this define to 0 for for simple filtering of data. |
#define SHOWDEBUGMESSAGES 1 // set to 1 to show debug messages, else set to 0 to disable messages |
#define MYBUNDLEID "com.apple.dts.kext.enetlognke" |
#define kMY_TAG_TYPE 1 |
// values to use with the memory allocated by the tag function |
enum { |
INBOUND_DONE = 1, |
OUTBOUND_DONE |
}; |
// |
#define NUM_HEADER_BYTES_TO_PRINT 18 // defines the number of bytes in the packet header to print |
/* name of built-in ethernet name */ |
static const char *gBuiltinEnetName = "en0"; |
#if SWALLOW_PACKETS |
static OSMallocTag gOSMallocTag; // tag for use with OSMalloc calls which is used to associate memory |
// allocations made with this kext. Preferred to using MALLOC and FREE |
/* Fine grain locking variables |
Protect consistency of our data in the queues where swallowed data is accessed |
*/ |
static lck_mtx_t *g_input_mutex = NULL; // for processing inbound packets |
static lck_mtx_t *g_output_mutex = NULL; // for processing outbound packets |
static lck_grp_t *gmutex_grp = NULL; |
// SwallowPktQueue is the queue structure which describes the inbound and outbound packet queues |
// where swallowed data is held for processing in the respective timer routines. |
struct SwallowPktQueue { |
TAILQ_ENTRY(SwallowPktQueue) q_next; /* queued entries */ |
ifnet_t interface; |
mbuf_t first_mbuf; |
protocol_family_t protocol; |
}; |
typedef struct SwallowPktQueue SwallowPktQueue; |
TAILQ_HEAD(swallow_queue, SwallowPktQueue); |
static struct swallow_queue swallow_queue_in; |
static struct swallow_queue swallow_queue_out; |
#endif // SWALLOW_PACKETS |
/* tag associated with this kext for use in marking packets that have been previously processed. |
Note that even if you don't swallow/re-inject packets, it's a good idea to mark them, unless |
you don't care whether you will see the same packet again. Another interface filter could |
swallow/re-inject the packet and your filter will be called to process the packet again. |
*/ |
static mbuf_tag_id_t gidtag; |
static interface_filter_t gEnetFilter; |
static boolean_t gFilterRegistered = FALSE; |
static boolean_t gUnregisterProc_started = FALSE; |
static boolean_t gUnregisterProc_complete = FALSE; |
/* =================================== */ |
#pragma mark Utility Functions |
static void |
el_printf(const char *fmt, ...) |
{ |
va_list listp; |
char log_buffer[92]; |
#if !SHOWDEBUGMESSAGES |
return; |
#endif |
va_start(listp, fmt); |
vsnprintf(log_buffer, sizeof(log_buffer), fmt, listp); |
printf("%s", log_buffer); |
va_end(listp); |
} |
#if SWALLOW_PACKETS |
/* |
IMPORTANT: Note that in both the data_in_timer and data_out_timer routine, a fine grain |
lock is taken and held to check that there is data in the swallow queue to process, and to remove |
the queued item from the packet swallow list. Before the system call is made, the lock is |
released as it's bad practice to hold a lock across a system call. Within the system call, a process |
might be called which will result in a call to acquire on the mutex and a deadlock could result if the |
lock were already held in the timer routine. |
The lock must be released before leaving the timer functions. If you find that |
Ethernet traffic hangs, check for a mutex deadlock situation. |
*/ |
static void |
data_in_timer(void * unused) |
{ |
register struct SwallowPktQueue *swallow_queue_item; |
errno_t result; |
lck_mtx_lock(g_input_mutex); /* take the lock prior to checking the swallow_queue |
so that it is held while the packet item is removed from the |
queue. |
make sure to release the lock prior to making the system call |
*/ |
while (!TAILQ_EMPTY(&swallow_queue_in)) |
{ |
// get the next item on the input side queue |
swallow_queue_item = TAILQ_FIRST(&swallow_queue_in); |
// remove this item from the queue |
TAILQ_REMOVE(&swallow_queue_in, swallow_queue_item, q_next); |
if (mbuf_pkthdr_header(swallow_queue_item->first_mbuf) == NULL) |
{ |
printf("packet with NULL pkthdr found in timer\n"); |
mbuf_freem(swallow_queue_item->first_mbuf); |
// free the queue element |
OSFree(swallow_queue_item, sizeof(SwallowPktQueue), gOSMallocTag); |
continue; |
} |
lck_mtx_unlock(g_input_mutex); // release the lock prior to making the system call |
result = ifnet_input(swallow_queue_item->interface, swallow_queue_item->first_mbuf, NULL); |
if (result) |
{ |
el_printf("error calling ifnet_input, dropping data - result was %d\n", result); |
mbuf_freem(swallow_queue_item->first_mbuf); |
} |
// free the queue element |
OSFree(swallow_queue_item, sizeof(SwallowPktQueue), gOSMallocTag); |
// we've already tagged the packet so go on to the next packet, if queued |
// but first aquire the lock so that we can check the queue and remove the swallow packet item |
// atomicly. |
lck_mtx_lock(g_input_mutex); |
} |
lck_mtx_unlock(g_input_mutex); // release the lock |
} |
#endif |
#if SWALLOW_PACKETS |
static void |
data_out_timer(void * unused) |
{ |
register struct SwallowPktQueue *swallow_queue_item; |
errno_t result; |
lck_mtx_lock(g_output_mutex); /* take the lock prior to checking the swallow_queue |
so that it is held while the packet item is removed from the |
queue. |
make sure to release the lock prior to making the system call |
*/ |
while (!TAILQ_EMPTY(&swallow_queue_out)) |
{ |
// get the next item on the input side queue |
swallow_queue_item = TAILQ_FIRST(&swallow_queue_out); |
// remove this item from the queue |
TAILQ_REMOVE(&swallow_queue_out, swallow_queue_item, q_next); |
lck_mtx_unlock(g_output_mutex); // release the lock prior to making the system call |
result = ifnet_output_raw(swallow_queue_item->interface, swallow_queue_item->protocol, swallow_queue_item->first_mbuf); |
if (result) |
{ |
el_printf("error calling ifnet_output_raw, dropping data - result was %d\n", result); |
mbuf_freem(swallow_queue_item->first_mbuf); |
} |
// free the queue element |
OSFree(swallow_queue_item, sizeof(SwallowPktQueue), gOSMallocTag); |
// we've already tagged the packet so go on to the next packet, if queued |
// but first aquire the lock so that we can check the queue and remove the swallow packet item |
// atomicly. |
lck_mtx_lock(g_output_mutex); |
} |
lck_mtx_unlock(g_output_mutex); // release the lock |
} |
#endif |
#if SWALLOW_PACKETS |
/* |
alloc_locks - used to allocate the fine grain locks for use in controlling access to the SwallowPktQueue |
structures. |
input - nothing |
output - result of allocation of lock group and lock |
0 - SUCCESS |
ENOMEM - an error in allocation of memory for the attributes or locks. |
*/ |
static errno_t alloc_locks(void) |
{ |
errno_t result = 0; |
lck_grp_attr_t *grp_attributes = NULL; |
lck_attr_t *lck_attributes = NULL; |
/* Allocate a mutex lock group */ |
/* allocate a lock group attribute var */ |
grp_attributes = lck_grp_attr_alloc_init(); |
if (grp_attributes) |
{ |
/* set the default values for the lock group attribute var */ |
lck_grp_attr_setdefault(grp_attributes); |
// for the name, use the reverse dns name associated with this |
// kernel extension |
/* allocate the lock group */ |
gmutex_grp = lck_grp_alloc_init(MYBUNDLEID, grp_attributes); |
if (gmutex_grp == NULL) |
{ |
el_printf("error calling lck_grp_alloc_init\n"); |
result = ENOMEM; |
} |
// can free the attributes once we've allocated the group lock |
lck_grp_attr_free(grp_attributes); |
} |
else |
{ |
el_printf("error calling lck_grp_attr_alloc_init\n"); |
result = ENOMEM; |
} |
if (result == 0) |
{ |
/* allocate a lock attribute var */ |
lck_attributes = lck_attr_alloc_init(); |
if (lck_attributes) |
{ |
/* allocate the lock for use on processing items in the input queue */ |
g_input_mutex = lck_mtx_alloc_init(gmutex_grp, lck_attributes); |
if (g_input_mutex == NULL) |
{ |
el_printf("error calling lck_mtx_alloc_init\n"); |
result = ENOMEM; |
} |
if (result == 0) |
{ |
/* allocate the lock for use on processing items in the output queue */ |
g_output_mutex = lck_mtx_alloc_init(gmutex_grp, lck_attributes); |
if (g_output_mutex == NULL) |
{ |
el_printf("error calling lck_mtx_alloc_init\n"); |
result = ENOMEM; |
} |
} |
// can free the attributes once we've allocated the lock |
lck_attr_free(lck_attributes); |
} |
else |
{ |
el_printf("error calling lck_attr_alloc_init\n"); |
result = ENOMEM; |
} |
} |
return result; // if we make it here, return success |
} |
#endif |
#if SWALLOW_PACKETS |
/* |
free_locks - used to free the fine grain locks for use in controlling access to the SwallowPktQueue |
structures. |
input - nothing |
output - nothing - since all of the kernel calls return void results. |
*/ |
static void free_locks(void) |
{ |
if (g_input_mutex) |
{ |
lck_mtx_free(g_input_mutex, gmutex_grp); |
g_input_mutex = NULL; |
} |
if (g_output_mutex) |
{ |
lck_mtx_free(g_output_mutex, gmutex_grp); |
g_output_mutex = NULL; |
} |
if (gmutex_grp) |
{ |
lck_grp_free(gmutex_grp); |
gmutex_grp = NULL; |
} |
if (gOSMallocTag) |
{ |
OSMalloc_Tagfree(gOSMallocTag); |
gOSMallocTag = NULL; |
} |
} |
#endif |
/* |
CheckTag - see if there is a tag associated with the mbuf_t with the matching bitmap bits set in the |
memory associated with the tag. Use global gidtag as id Tag to look for |
input m - mbuf_t variable on which to search for tag |
value - value to compare in the tag_ref field associated with the tag |
return 1 - success, the bitmap image set in allocated memory associated with tag gidtag has the same bits set |
as does the bitmap |
return 0 - failure, either the mbuf_t is not tagged, or the allocated memory does not have the |
expected value |
*/ |
static int CheckTag(mbuf_t m, int value) |
{ |
errno_t status; |
int *tag_ref; |
size_t len; |
// Check whether we have seen this packet before. |
status = mbuf_tag_find(m, gidtag, kMY_TAG_TYPE, &len, (void**)&tag_ref); |
if ((status == 0) && (*tag_ref & value) && (len == sizeof(int))) |
return 1; |
return 0; |
} |
/* |
SetTag - Set the tag associated with the mbuf_t with the bitmap bits set in bitmap |
input m - mbuf_t variable on which to search for tag |
bitmap - bitmap field to set in allocated memory |
return 0 - success, the tag has been allocated and for the mbuf_t and the bitmap bits has been set in the |
allocated memory. |
anything else - failure |
*/ |
static errno_t SetTag(mbuf_t m, int value) |
{ |
errno_t status; |
int *tag_ref; |
size_t len; |
// look for existing tag |
status = mbuf_tag_find(m, gidtag, kMY_TAG_TYPE, &len, (void*)&tag_ref); |
// allocate tag if needed |
if (status != 0) { |
// note that setting the MBUF_DONTWAIT flag for mbuf_tag memory allocation while within a packet |
// processing call will not deadlock packet processing under OS X 10.4 and greater. |
status = mbuf_tag_allocate(m, gidtag, kMY_TAG_TYPE, sizeof(int), MBUF_DONTWAIT, (void**)&tag_ref); |
if (status == 0) |
*tag_ref = 0; // init tag_ref |
else |
printf("mbuf_tag_allocate failed - result was %d", status); |
} |
else |
{ |
// the tag exists - verify that the length of the tag_ref is the expected size |
// this should not happen |
if (len != sizeof(int)) |
{ |
printf("tag detected at incorrect length - %d\n", len); |
status = EINVAL; // invalid argument detected. |
} |
} |
if (status == 0) |
*tag_ref = value; |
return status; |
} |
/* |
PrintPacketHeader - prints the first N bytes of the Ethernet packet as specified by |
NUM_HEADER_BYTES_TO_PRINT |
input: data - pointer to mbuf_t of the packet |
*/ |
static void |
PrintPacketHeader(mbuf_t *data) |
{ |
int bytesLeftToPrint = NUM_HEADER_BYTES_TO_PRINT; |
int i, j, bytesToPrintNow; |
mbuf_t m = *data; |
char *frame; |
j = 0; |
do |
{ |
bytesToPrintNow = mbuf_len(m); // count the number of bytes in the mbuf |
if (bytesToPrintNow > bytesLeftToPrint) // limit the number of bytes to print |
{ |
bytesToPrintNow = bytesLeftToPrint; |
} |
bytesLeftToPrint -= bytesToPrintNow; // decrement bytesLeftToPrint to what remains |
// to be printed |
frame = mbuf_data(m); |
for (i = 0; i < bytesToPrintNow; i++, j++) |
{ |
el_printf("%02X", (u_int8_t)frame[i]); |
if (j == 5) el_printf(" "); // print space after the destination address |
if (j == 11) el_printf(" "); // print space after the source address |
if (j == 13) el_printf(" "); // print space after the protocol/length field |
} |
m = mbuf_next(m); |
} while ((m != NULL) && (bytesLeftToPrint != 0)); |
} |
/* =================================== */ |
#pragma mark Filter Functions |
/*! |
@typedef iff_input_func |
@discussion iff_input_func is used to filter incoming packets. The |
interface is only valid for the duration of the filter call. If |
you need to keep a reference to the interface, be sure to call |
ifnet_reference and ifnet_release. |
@param cookie The cookie specified when this filter was attached. |
@param interface The interface the packet was recieved on. |
@param protocol The protocol of this packet. If you specified a |
protocol when attaching your filter, the protocol will only ever |
be the protocol you specified. By passing the protocol to your |
filter, you can supply the same function pointer to filters |
registered for different protocols and differetiate the protocol |
using this parameter. |
@param data The inbound packet, may be changed. |
@param frame_ptr A pointer to the pointer to the frame header. |
@result Return: |
0 - The caller will continue with normal processing of the packet. |
EJUSTRETURN - The caller will stop processing the packet, the packet will not be freed. |
Anything Else - The caller will free the packet and stop processing. |
*/ |
static errno_t |
enet_input_func(void* cookie, ifnet_t interface, protocol_family_t protocol, |
mbuf_t *data, char **frame_ptr) |
{ |
#if SWALLOW_PACKETS |
struct SwallowPktQueue *swallow_queue_item; |
struct timespec ts; |
#endif |
mbuf_t m = *data; |
u_int32_t bytes = 0; |
errno_t err; |
/* check whether we have seen this packet previously */ |
if (CheckTag(*data, INBOUND_DONE)) |
{ |
/* we have processed this packet previously since the INBOUND_DONE bit was set. |
bail on further processing |
*/ |
return 0; |
} |
el_printf("enet_input_func - "); |
switch (protocol) |
{ |
case AF_UNSPEC: el_printf("UNSPEC - "); break; |
case AF_INET: el_printf("TCP/IP - "); break; |
case AF_APPLETALK: el_printf("AppleTalk - "); break; |
default: el_printf("proto %d - ", protocol); break; |
} |
PrintPacketHeader(data); |
do |
{ |
bytes += mbuf_len(m); |
m = mbuf_next(m); |
} while (m != NULL); |
el_printf(" %d bytes incoming\n", bytes); |
/* |
If we swallow the packet and later re-inject the packet, we have to be |
prepared to see the packet through this routine once again. In fact, if |
after re-injecting the packet, another nke swallows and re-injects the packet |
we will see the packet an additional time. Rather than cache the mbuf_t reference |
we tag the mbuf_t and search for it's presence which we have already done above |
to decide if we have processed the packet. |
In fact, it's a good idea to tag the packet anyway, since some other filter could |
swallow/re-inject the packet and we'd end up seeing the packet again. |
*/ |
err = SetTag(*data, INBOUND_DONE); |
if (err == 0) |
{ |
#if SWALLOW_PACKETS |
/* allocate a queue entry so that we can store the packet on the ENET inbound process queue. |
NOTE: the OSMalloc_nowait and OSMalloc_noblock variants of OSMalloc, do not need to |
be used while in a packet processing routine. OSMalloc will not deadlock with the packet |
processing call. |
*/ |
swallow_queue_item = (struct SwallowPktQueue *)OSMalloc(sizeof (struct SwallowPktQueue), gOSMallocTag); |
if (swallow_queue_item) |
{ |
// before we swallow the packet, we need to make sure that the very first mbuf |
// has the pkthdr_header field set, else the system will panic when we inject |
// the packet. The input routine is passed the frame_header pointer, so we can use |
// this value to set as the pkthdr_header. |
if (mbuf_pkthdr_header(*data) == NULL) |
{ |
mbuf_pkthdr_setheader(*data, *frame_ptr); |
} |
swallow_queue_item->interface = interface; |
swallow_queue_item->protocol = protocol; |
swallow_queue_item->first_mbuf = *data; |
lck_mtx_lock(g_input_mutex); |
// queue the item into the input queue for processing when the timer fires |
TAILQ_INSERT_TAIL(&swallow_queue_in, swallow_queue_item, q_next); |
lck_mtx_unlock(g_input_mutex); |
// initiate timer action in 10 microsec - for demonstration purposes to show the |
// continued processing of the packet |
ts.tv_sec = 0; |
ts.tv_nsec = 10000; |
bsd_timeout(data_in_timer, NULL, &ts); |
} |
else |
{ |
printf("Error - failed to allocate memory for inbound queue item, dropping packet.\n"); |
return ENOMEM; // stops processing and will cause the mbuf_t to be released |
} |
#endif // #if SWALLOW_PACKETS |
} |
else |
{ |
// mbuf_tag_allocate failed. |
printf("Error - mbuf_tag_allocate returned an error %d\n", err); |
return err; // stops processing and will cause the mbuf_t to be released |
/* |
Note that this portion of code will be executed even in the non-packet |
swallow case. This code returns the error which will cause the packet to |
be dropped. |
*/ |
} |
#if SWALLOW_PACKETS |
return EJUSTRETURN; // we're going to deal with the packet one way or the other |
#else |
return 0; |
#endif |
} |
/*! |
@typedef iff_output_func |
@discussion iff_output_func is used to filter fully formed outbound |
packets. This function is called after the protocol specific |
preoutput function. The interface is only valid for the duration |
of the filter call. If you need to keep a reference to the |
interface, be sure to call ifnet_reference and ifnet_release. |
@param cookie The cookie specified when this filter was attached. |
@param interface The interface the packet is being transmitted on. |
@param data The outbound packet, may be changed. |
@result Return: |
0 - The caller will continue with normal processing of the packet. |
EJUSTRETURN - The caller will stop processing the packet, the packet will not be freed. |
Anything Else - The caller will free the packet and stop processing. |
*/ |
static errno_t |
enet_output_func(void* cookie, ifnet_t interface, protocol_family_t protocol, |
mbuf_t *data) |
{ |
#if SWALLOW_PACKETS |
struct SwallowPktQueue *swallow_queue_item; |
struct timespec ts; |
#endif |
int byteInPacket; |
mbuf_t m = *data; |
errno_t err; |
/* check whether we have seen this packet previously */ |
if (CheckTag(*data, OUTBOUND_DONE)) |
{ |
/* we have processed this packet previously since the OUTBOUND_DONE bit was set. |
bail on further processing |
*/ |
return 0; |
} |
/* |
If we reach this point, then we have not previously seen this packet. |
First lets get some statistics from the packet. |
*/ |
el_printf("enet_output_func - "); |
switch (protocol) |
{ |
case AF_UNSPEC: el_printf("UNSPEC - "); break; |
case AF_INET: el_printf("TCP/IP - "); break; |
case AF_APPLETALK: el_printf("AppleTalk - "); break; |
default: el_printf("proto %d - ", protocol); break; |
} |
// count the number of outgoing bytes |
byteInPacket = 0; |
do |
{ |
byteInPacket += mbuf_len(m); |
m = mbuf_next(m); |
} while (m != NULL); |
PrintPacketHeader(data); |
el_printf(" %d bytes outgoing\n", byteInPacket); |
/* |
If we swallow the packet and later re-inject the packet, we have to be |
prepared to see the packet through this routine once again. In fact, if |
after re-injecting the packet, another nke swallows and re-injects the packet |
we will see the packet an additional time. Rather than cache the mbuf_t reference |
we tag the mbuf_t and search for it's presence which we have already done above |
to decide if we have processed the packet. |
*/ |
err = SetTag(*data, OUTBOUND_DONE); |
if (err == 0) |
{ |
#if SWALLOW_PACKETS |
/* allocate a queue entry so that we can store the packet on the ENET outbound process queue. |
NOTE: the OSMalloc_nowait and OSMalloc_noblock variants of OSMalloc, do not need to |
be used while in a packet processing routine. OSMalloc will not deadlock with the packet |
processing call. |
*/ |
swallow_queue_item = (struct SwallowPktQueue *)OSMalloc(sizeof (struct SwallowPktQueue), gOSMallocTag); |
if (swallow_queue_item) |
{ |
swallow_queue_item->interface = interface; |
swallow_queue_item->protocol = protocol; |
swallow_queue_item->first_mbuf = *data; |
lck_mtx_lock(g_output_mutex); |
// queue the item into the output queue for processing when the timer fires |
TAILQ_INSERT_TAIL(&swallow_queue_out, swallow_queue_item, q_next); |
lck_mtx_unlock(g_output_mutex); |
// initiate timer action in 10 microsec |
ts.tv_sec = 0; |
ts.tv_nsec = 10000; |
bsd_timeout(data_out_timer, NULL, &ts); |
} |
else |
{ |
printf("Error - failed to allocate memory for outbound queue item, dropping packet.\n"); |
mbuf_freem(*data); |
} |
#endif |
} |
else |
{ |
// mbuf_tag_allocate failed. |
printf("Error - mbuf_tag_allocate returned an error %d\n", err); |
return err; // stops processing and will cause the mbuf_t to be released |
/* |
Note that this portion of code will be executed even in the non-packet |
swallow case. This code returns the error which will cause the packet to |
be dropped. |
*/ |
} |
#if SWALLOW_PACKETS |
return EJUSTRETURN; |
#else |
return 0; |
#endif |
} |
/*! |
@typedef iff_event_func |
@discussion iff_event_func is used to filter interface specific |
events. The interface is only valid for the duration of the |
filter call. If you need to keep a reference to the interface, |
be sure to call ifnet_reference and ifnet_release. |
@param cookie The cookie specified when this filter was attached. |
@param interface The interface the packet is being transmitted on. |
@param event_msg The kernel event, may not be changed. |
*/ |
static void |
enet_event_func(void* cookie, ifnet_t interface, protocol_family_t protocol, |
const struct kev_msg *event_msg) |
{ |
el_printf("enet_event_func - vendor %ld, class %ld, subclass %ld, event code %ld\n", |
event_msg->vendor_code, event_msg->kev_class, |
event_msg->kev_subclass, event_msg->event_code); |
return; |
} |
/*! |
@typedef iff_ioctl_func |
@discussion iff_ioctl_func is used to filter ioctls sent to an |
interface. The interface is only valid for the duration of the |
filter call. If you need to keep a reference to the interface, |
be sure to call ifnet_reference and ifnet_release. |
@param cookie The cookie specified when this filter was attached. |
@param interface The interface the packet is being transmitted on. |
@param ioctl_cmd The ioctl command. |
@param ioctl_arg A pointer to the ioctl argument. |
@result Return: |
(NOTE: The following return result description is a correction to the return results presented |
in kpi_interfacefilter.h provided with 10.4. |
EOPNOTSUPP(or ENOTSUP) - indicates that the ioctl was not processed - the caller will |
continue with normal processing of the packet. |
EJUSTRETURN - indicates that no further processing of the ioctl is desired - the caller |
will stop processing the packet, the packet will not be freed. |
0 - indicates that the ioctl was handled, however, the caller will continue processing |
the packet with other filter functions. |
Anything Else - The caller will free the packet and stop processing. |
*/ |
static errno_t |
enet_ioctl_func(void* cookie, ifnet_t interface, protocol_family_t protocol, |
u_long ioctl_cmd, void* ioctl_arg) |
{ |
el_printf("enet_ioctl_func - "); |
switch (protocol) |
{ |
case AF_INET: el_printf("TCP/IP,"); break; |
case AF_APPLETALK: el_printf("AppleTalk,"); break; |
default: el_printf("%d,", protocol); break; |
} |
el_printf(" cmd is 0x%X\n", ioctl_cmd); |
return EOPNOTSUPP; |
} |
/*! |
@typedef iff_detached_func |
@discussion iff_detached_func is called to notify the filter that it |
has been detached from an interface. This is the last call to |
the filter that will be made. A filter may be detached if the |
interface is detached or the detach filter function is called. |
In the case that the interface is being detached, your filter's |
event function will be called with the interface detaching event |
before the your detached function will be called. |
@param cookie The cookie specified when this filter was attached. |
@param interface The interface this filter was detached from. |
*/ |
static void |
enet_detached_func(void* cookie, ifnet_t interface) |
{ |
gUnregisterProc_complete = TRUE; |
gFilterRegistered = FALSE; |
printf("enet_detached_func entered\n"); |
return; |
} |
static struct iff_filter Enet_filter = { |
NULL, /* iff_cookie field */ |
MYBUNDLEID, |
0, /* interested in all protocol packets */ |
enet_input_func, |
enet_output_func, |
enet_event_func, |
enet_ioctl_func, |
enet_detached_func |
}; |
/* =================================== */ |
#pragma mark kext entry points |
kern_return_t com_dts_apple_kext_enetlognke_start (kmod_info_t * ki, void * d) |
{ |
kern_return_t retval; |
ifnet_t enetifnet; |
printf("enetlognke_start entered\n"); |
if (gFilterRegistered == TRUE) |
return KERN_SUCCESS; |
#if SWALLOW_PACKETS |
retval = alloc_locks(); |
if (retval) |
goto error; |
TAILQ_INIT(&swallow_queue_in); // will hold inbound swallowed packets |
TAILQ_INIT(&swallow_queue_out); // will hold outbound swallowed packets |
gOSMallocTag = OSMalloc_Tagalloc(MYBUNDLEID, OSMT_DEFAULT); // don't want the flag set to OSMT_PAGEABLE since |
// it would indicate that the memory was pageable. |
if (gOSMallocTag == NULL) |
goto error; |
#endif |
// set up the tag value associated with this NKE in preparation for swallowing packets and re-injecting them |
retval = mbuf_tag_id_find(MYBUNDLEID , &gidtag); |
if (retval != 0) |
{ |
el_printf("mbuf_tag_id_find returned error %d\n", retval); |
goto error; |
} |
retval = ifnet_find_by_name(gBuiltinEnetName, &enetifnet); |
if (retval == KERN_SUCCESS) |
{ |
retval = iflt_attach(enetifnet, &Enet_filter, &gEnetFilter); |
// release the reference on the interface name |
ifnet_release(enetifnet); |
} |
if (retval == KERN_SUCCESS) |
gFilterRegistered = TRUE; |
else |
goto error; |
return retval; |
error: |
#if SWALLOW_PACKETS |
free_locks(); |
#endif |
return KERN_FAILURE; |
} |
kern_return_t com_dts_apple_kext_enetlognke_stop (kmod_info_t * ki, void * d) |
{ |
kern_return_t retval = KERN_FAILURE; // default result, unless we know that we are |
// detached from the interface. |
if (gFilterRegistered == FALSE) |
return KERN_SUCCESS; |
if (gUnregisterProc_started == FALSE) |
{ |
// only want to start the detach process once. |
iflt_detach(gEnetFilter); |
gUnregisterProc_started = TRUE; |
} |
if (gUnregisterProc_complete) |
{ |
retval = KERN_SUCCESS; |
} |
else |
{ |
el_printf("enetlognke_stop: incomplete\n"); |
} |
if (retval == KERN_SUCCESS) |
{ |
#if SWALLOW_PACKETS |
free_locks(); |
#endif |
} |
return retval; |
} |
Last updated: 2005-08-10