home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
sustworks.com
/
2014.06.sustworks.com.tar
/
sustworks.com
/
open_source_IPNetMonitor_TNKE.dmg
/
IPNetMonitor_TNKE.c
< prev
next >
Wrap
C/C++ Source or Header
|
2005-08-22
|
44KB
|
1,416 lines
//
// IPNetMonitor_TNKE.c
// Mac OS X Interface Filter NKE
//
// Created by psichel [PAS] on Wed Mar 27 2002.
// Based on SharedIP and TCPLogger provided as Open Source sample code
// from Apple Computer.
//
// See IPNetMonitor_TNKE.h for high level design overview.
// You can send comments or suggestions to psichel@sustworks.com
// The companion IPNetMonitorX controller application is available from
// http://www.sustworks.com
//
// ---------------------------------------------------------------------------------
// Portions Copyright (c) 1999-2002 Apple Computer, Inc. All Rights Reserved.
//
// This file contains Original Code and/or Modifications of Original Code as defined
// in and that are subject to the Apple Public Source License Version 1.2 (the 'License').
// You may not use this file except in compliance with the License.
// Please obtain a copy of the License at http://www.apple.com/publicsource
// and read it before using this file.
//
// The Original Code and all software distributed under the License are distributed
// on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESS OR IMPLIED,
// AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, INCLUDING WITHOUT LIMITATION,
// ANY WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, QUIET
// ENJOYMENT OR NON-INFRINGEMENT. Please see the License for the specific
// language governing rights and limitations under the License."
// ---------------------------------------------------------------------------------
#define IPK_DEBUG 0
// Tiger kpi (socket filter)
#include <mach/vm_types.h>
#include <mach/kmod.h>
#include <sys/socket.h>
#include <sys/kpi_socketfilter.h>
#include <netinet/in.h>
// Tiger kpi (interface filter)
#include <mach/mach_types.h>
#include <sys/systm.h>
#include <sys/queue.h>
#include <sys/malloc.h>
#include <sys/kpi_mbuf.h>
#define _IP_VHL
#include <netinet/in_systm.h>
//#include <netinet/in.h>
#include <netinet/ip.h>
#include <netinet/ip6.h>
#include <netinet/kpi_ipfilter.h>
#include <net/kpi_interfacefilter.h>
void iflt_detach(interface_filter_t filter_ref);
#include <sys/param.h>
#include <sys/systm.h>
//#include <sys/socket.h>
//#include <sys/protosw.h>
//#include <sys/socketvar.h>
#include <sys/fcntl.h>
//#include <sys/malloc.h>
#include <sys/queue.h>
#include <sys/domain.h>
#include <sys/mbuf.h>
#include <sys/kern_event.h>
#include <net/route.h>
#include <net/if.h>
#include <net/if_types.h>
#include <net/if_dl.h>
#include <net/ndrv.h>
#include <net/kext_net.h>
#include <net/dlil.h>
//#include <netinet/in.h> // Needed for (*&^%$#@ arpcom in if_arp.h
#include <net/ethernet.h>
#include <net/if_arp.h>
#include <machine/spl.h>
#include <kern/thread.h>
#include <libkern/OSAtomic.h>
#include <libkern/OSTypes.h>
#include <string.h>
#include "IPNetMonitor_TNKE.h"
// tag packets so we can recognize re-injected ones
#define MY_BUNDLE_ID "com.sustworks.kext.IPNetMonitor_TNKE"
#define MY_TAG_TYPE 1
// tag associated with this kext for use in marking packets that have been previously processed
static mbuf_tag_id_t gidtag;
int memcmp(const void *, const void *, size_t);
// macro to convert kernel to external socket
//#define sotoextcb(so) (struct kextcb *)(so->so_ext)
//extern void kprintf(const char *, ...);
//
// Forward function declarations
// Use unix style names (words separated by "_") for intercepts and callbacks
// Use Mac style names for internal functions (initial caps)
// Use ipk_ or ik_ prefix to avoid kernel name space conflicts
// ipk_ for PF_NKE and DLIL filter functions, ik_ for socket functions
//
// Tiger NKE socket intercepts
static void ik_unregistered(sflt_handle handle);
static errno_t ik_attach(void **cookie, socket_t so);
static void ik_detach(void *cookie, socket_t so);
static void ik_notify(void *cookie, socket_t so, sflt_event_t event, void *param);
static int ik_getpeername(void *cookie, socket_t so, struct sockaddr **sa);
static int ik_getsockname(void *cookie, socket_t so, struct sockaddr **sa);
static errno_t ik_data_in(void *cookie, socket_t so,
const struct sockaddr *from, mbuf_t *data,
mbuf_t *control, sflt_data_flag_t flags);
static errno_t ik_data_out(void *cookie, socket_t so,
const struct sockaddr *to, mbuf_t *data,
mbuf_t *control, sflt_data_flag_t flags);
static errno_t ik_connect_in(void *cookie, socket_t so, const struct sockaddr *from);
static errno_t ik_connect_out(void *cookie, socket_t so, const struct sockaddr *to);
static errno_t ik_bind(void *cookie, socket_t so, const struct sockaddr *to);
static errno_t ik_setoption(void *cookie, socket_t so, sockopt_t opt);
static errno_t ik_getoption(void *cookie, socket_t so, sockopt_t opt);
static errno_t ik_listen(void *cookie, socket_t so);
static errno_t ik_ioctl(void *cookie, socket_t so, u_int32_t request, const char* argp);
// Tiger socket filter module wide state
static int registered = 0;
static int unregistered_started = 0;
static int unregistered_complete = 0;
// Dispatch vector for Tiger socket intercepts
static struct sflt_filter ik_sflt_filter = {
IPNetMonitor_TNKE_Handle,
SFLT_PROG,
"com.sustworks.kext.IPNetMonitor_TNKE",
ik_unregistered,
ik_attach,
ik_detach,
ik_notify,
ik_getpeername,
ik_getsockname,
ik_data_in,
ik_data_out,
ik_connect_in,
ik_connect_out,
ik_bind,
ik_setoption,
ik_getoption,
ik_listen,
ik_ioctl
};
// mutex lock for synchronizeing NKE data between kernel threads
static lck_mtx_t *tnke_lock;
static lck_grp_t *tnke_lock_grp = 0;
// Tiger protocol filter intercpets
static errno_t ipk_ipf_input_func(caddr_t cookie, mbuf_t *data, int hlen, u_int8_t protocol);
static errno_t ipk_ipf_output_func(caddr_t cookie, mbuf_t *data, ipf_pktopts_t options);
static void ipk_ipf_detach_func(caddr_t cookie);
// Tiger interface filter intercepts
static void ipk_iff_detached_func(
void* cookie,
ifnet_t interface);
static void ipk_iff_event_func(
void* cookie,
ifnet_t interface,
protocol_family_t protocol,
const struct kev_msg *event_msg);
static errno_t ipk_iff_input_func(
void*cookie,
ifnet_t interface,
protocol_family_t protocol,
mbuf_t *data,
char **frame_ptr);
static errno_t ipk_iff_ioctl_func(
void*cookie,
ifnet_t interface,
protocol_family_t protocol,
u_long ioctl_cmd,
void*ioctl_arg);
static errno_t ipk_iff_output_func(
void*cookie,
ifnet_t interface,
protocol_family_t protocol,
mbuf_t *data);
// internal support functions
static int ik_connect(socket_t cso);
static void ik_disconnect(socket_t cso);
static int ik_attachCount(int controlIndex);
static int ik_controlCount(int attachIndex);
static int ik_controlIndexForSocket(socket_t so);
static int ik_emptyControlIndex();
static int ik_attachIndexForName(char *inName);
static int ik_emptyAttachIndex();
static int ik_detachController(int controlIndex);
static int ik_detachInterface(int attachIndex);
// timer callbacks
static void ipk_timeout(void *cookie);
static void ipk_periodical();
// send message to controlling socket
void PROJECT_sendMessage(socket_t ctl, ipk_message_t *message);
//
// Global structures and data storage
//
// timer Ref Count (firewall enabled)
int32_t PROJECT_timerRefCount;
// ipk_timeout reschedules itself when PROJECT_timerRefCount>0
int32_t PROJECT_timerPending;
//
// Module wide variables (not exported)
//
static int ipk_initted = 0;
//
// Setup per instance structures and storage
//
// maximum number of controllers and DLIL attachments use index values 1..n
// 0 is reserved for "not found"
//#define kMaxControl 8
//#define kMaxAttach 8
// Array of controller instances
control_t PROJECT_control[kMaxControl+1];
// Global Array of DLIL attach instances
attach_t PROJECT_attach[kMaxAttach+1];
#pragma mark -- Load and unload kernel extension
//
// Load and unload kernel extension
//
// ---------------------------------------------------------------------------------
// Ñ IPNetMonitor_TNKE_start
// ---------------------------------------------------------------------------------
kern_return_t IPNetMonitor_TNKE_start (kmod_info_t * ki, void * data)
{
int returnValue = KERN_SUCCESS;
errno_t status;
lck_grp_attr_t *grp_attributes = 0;
lck_attr_t *lck_attributes = 0;
int i;
printf("IPNetMonitor_TNKE has loaded!\n");
// set up the tag value associated with this NKE to mark previously seen packets
status = mbuf_tag_id_find(MY_BUNDLE_ID, &gidtag);
if (status != 0) {
printf("IPNetRouter_TNKE_start: mbuf_tag_id_find failed %d\n", status);
return KERN_FAILURE;
}
/* Allocate a mutex lock */
grp_attributes = lck_grp_attr_alloc_init();
lck_grp_attr_setdefault(grp_attributes);
tnke_lock_grp = lck_grp_alloc_init("IPNetMonitor_TNKE", grp_attributes);
lck_grp_attr_free(grp_attributes);
grp_attributes = 0;
lck_attributes = lck_attr_alloc_init();
lck_attr_setdefault(lck_attributes);
lck_attr_setdebug(lck_attributes);
tnke_lock = lck_mtx_alloc_init(tnke_lock_grp, lck_attributes);
lck_attr_free(lck_attributes);
lck_attributes = 0;
// mutex lock
PROJECT_lock();
do {
// already initialized?
if (ipk_initted) break;
// initialize global storage
PROJECT_timerRefCount = 0;
PROJECT_timerPending = 0;
// initialize our instance storage
for (i=0; i<=kMaxControl; i++) {
bzero(&PROJECT_control[i], sizeof(control_t));
}
for (i=0; i<=kMaxAttach; i++) {
bzero(&PROJECT_attach[i], sizeof(attach_t));
}
// register our NKE
status = sflt_register(&ik_sflt_filter, PF_INET,
SOCK_DGRAM, IPPROTO_UDP);
if (status != 0) {
printf("IPNetMonitor_TNKE_start: sflt_register failed %d\n", status);
returnValue = KERN_FAILURE;
} else {
registered++;
ipk_initted = 1;
}
} while (FALSE);
// mutex unlock
PROJECT_unlock();
return returnValue;
}
// ---------------------------------------------------------------------------------
// Ñ IPNetMonitor_TNKE_stop
// ---------------------------------------------------------------------------------
kern_return_t IPNetMonitor_TNKE_stop (kmod_info_t * ki, void * data) {
int returnValue = KERN_SUCCESS;
errno_t status;
int i;
// mutex lock
PROJECT_lock();
do {
// if not initted
if (!ipk_initted) break;
// unlink controllers and remove any DLIL attach instances
for (i=1; i<=kMaxControl; i++) {
if (PROJECT_control[i].ctl != 0) {
ik_detachController(i);
// tell any clients the socket has gone away
// not sure how to do this, so just fail for now
returnValue = KERN_FAILURE; // fail until all sockets are disconnected
break;
}
}
if (returnValue != 0) break;
// remove any remaining attach instances (defensive)
for (i=1; i<=kMaxAttach; i++) {
if (PROJECT_attach[i].ifFilterRef || PROJECT_attach[i].ipFilterRef) {
ik_detachInterface(i);
}
}
// need to cancel any pending kernel callbacks (timeout)
// fortunately we don't normally unload and can warn the user
// not to unload while any tools are running
// force timer ref count to zero
if (PROJECT_timerRefCount || PROJECT_timerPending) {
bsd_untimeout(ipk_timeout, (void *)0);
PROJECT_timerRefCount = 0;
}
// unplug our socket filter
if (unregistered_started == 0) {
status = sflt_unregister(ik_sflt_filter.sf_handle);
if (status != 0) {
printf("IPNetMonitor_TNKE_stop: sflt_unregister failed %d\n", status);
returnValue = KERN_FAILURE;
}
else {
unregistered_started++;
}
} else if (unregistered_complete) {
returnValue = KERN_SUCCESS;
ipk_initted = 0;
} else {
printf("pass_sock_filt_stop: again\n");
returnValue = KERN_FAILURE;
}
} while (FALSE);
// mutex unlock
PROJECT_unlock();
// release mutex lock
if (returnValue == KERN_SUCCESS) {
if (tnke_lock)
lck_mtx_free(tnke_lock, tnke_lock_grp);
if (tnke_lock_grp)
lck_grp_free(tnke_lock_grp);
tnke_lock = 0;
tnke_lock_grp = 0;
}
if (returnValue == 0) printf("IPNetMonitor_TNKE has unloaded!\n");
return returnValue;
}
#pragma mark -- Tiger socket intercept functions --
static void
ik_unregistered(sflt_handle handle)
{
unregistered_complete++;
return;
}
static errno_t
ik_attach(void **cookie, socket_t so)
{
printf("ik_attach: %x\n", so);
return 0;
}
static void
ik_detach(void *cookie, socket_t so)
{
printf("ik_detach: %x\n", so);
return;
}
// this is where we get notified that a socket is closing
static void ik_notify(void *cookie, socket_t so,
sflt_event_t event, void *param)
{
//printf("ik_notify: %x event: %lu\n", so, event);
if (event == sock_evt_closing) {
// socket is being closed:
// disconnect, turn off monitoring, and detach from interface if no more references.
// - Any open sockets will be closed by the kernel when the process quits,
// so this is our big chance to clean up properly regardless of how the
// companion application died.
ik_disconnect(so);
}
return;
}
static int
ik_getpeername(void *cookie, socket_t so,
struct sockaddr **sa)
{
return 0;
}
static int
ik_getsockname(void *cookie, socket_t so,
struct sockaddr **sa)
{
return 0;
}
static errno_t
ik_data_in(void *cookie, socket_t so,
const struct sockaddr *from, mbuf_t *data,
mbuf_t *control, sflt_data_flag_t flags)
{
return 0;
}
static errno_t
ik_data_out(void *cookie, socket_t so,
const struct sockaddr *to, mbuf_t *data,
mbuf_t *control, sflt_data_flag_t flags)
{
return 0;
}
static errno_t
ik_connect_in(void *cookie, socket_t so,
const struct sockaddr *from)
{
printf("ik_connect_in: %x\n", so);
return 0;
}
static errno_t
ik_connect_out(void *cookie, socket_t so,
const struct sockaddr *to)
{
printf("ik_connect_in: %x\n", so);
return 0;
}
static errno_t
ik_bind(void *cookie, socket_t so,
const struct sockaddr *to)
{
return 0;
}
// ---------------------------------------------------------------------------------
// Ñ ik_setoption
// ---------------------------------------------------------------------------------
// intercept socket options (soset and soget)
static errno_t
ik_setoption(void *cookie, socket_t so, sockopt_t sopt)
{
errno_t returnValue = 0; // continue normall processing (pass it on)
errno_t status;
int controlIndex=0;
int attachIndex=0;
do {
// find our controller instance
controlIndex = ik_controlIndexForSocket(so);
if (!controlIndex) {
// not yet connected, try to connect
returnValue = ik_connect(so);
if (returnValue != EJUSTRETURN) break;
controlIndex = ik_controlIndexForSocket(so);
if (!controlIndex) {
returnValue = ENOTCONN;
break;
}
}
switch(sockopt_name(sopt)) {
case SO_ATTACH_LINK:
// mutex lock
PROJECT_lock();
do {
// attach a controller socket to an IP interface (data link)
struct sopt_attachParam attachParam;
// Reference: copyin and copyout option parameters
//errno_t sockopt_copyin(sockopt_t sopt, void *data, size_t length);
//errno_t sockopt_copyout(sockopt_t sopt, void *data, size_t length);
#if IPK_DEBUG
printf("ik_setoption: received SO_ATTACH_LINK\n");
#endif
// copyin option parameter
returnValue = sockopt_copyin(sopt, &attachParam, sizeof(attachParam));
if (returnValue) break;
// look for existing DLIL attach by bsdName
attachIndex = ik_attachIndexForName(attachParam.bsdName);
if (attachIndex) {
// found existing DLIL attach
// associate this controller with DLIL attach
PROJECT_control[controlIndex].attachMap[attachIndex] = 1;
returnValue = EJUSTRETURN;
break;
}
// did not find previous attachment
// create a new DLIL attach instance for requested interface
// find slot for attach instance
attachIndex = ik_emptyAttachIndex();
if (!attachIndex) {
returnValue = EBUSY;
break;
}
bzero(&PROJECT_attach[attachIndex], sizeof(attach_t));
// remember our own index for convenience
PROJECT_attach[attachIndex].attachIndex = attachIndex;
if (attachParam.protocolFilter) {
// Register IPv4 protocol filter
struct ipf_filter ipf_filter;
ipfilter_t ipFilterRef;
// set cookie to point to this attach instance
ipf_filter.cookie = (caddr_t)&PROJECT_attach[attachIndex];
ipf_filter.name = "com.sustworks.nke.IPNetMonitor_TNKE";
ipf_filter.ipf_input = (ipf_input_func)ipk_ipf_input_func;
ipf_filter.ipf_output = (ipf_output_func)ipk_ipf_output_func;
ipf_filter.ipf_detach = (ipf_detach_func)ipk_ipf_detach_func;
// attach as IP filter
returnValue = ipf_addv4(&ipf_filter, &ipFilterRef);
PROJECT_attach[attachIndex].ipFilterRef = ipFilterRef;
if (returnValue != 0) {
printf("ik_setoption attach: ipf_addv4 failed %d\n", returnValue);
break;
} else {
#if IPK_DEBUG
printf("ik_setoption: protocol filter on %s\n", attachParam.bsdName);
#endif
}
}
else {
// Register as interface filter
ifnet_t ifnet_ref = NULL;
struct iff_filter iff_filter;
interface_filter_t ifFilterRef;
// find matching ifnet for interface name (must call ifnet_release)
returnValue = ifnet_find_by_name(attachParam.bsdName, &ifnet_ref);
if (returnValue != 0) break;
// set cookie to point to this attach instance
iff_filter.iff_cookie = (caddr_t)&PROJECT_attach[attachIndex];
iff_filter.iff_name = "com.sustworks.nke.IPNetMonitor_TNKE";
iff_filter.iff_protocol = PF_INET;
iff_filter.iff_input = ipk_iff_input_func;
iff_filter.iff_output = ipk_iff_output_func;
iff_filter.iff_event = ipk_iff_event_func;
iff_filter.iff_ioctl = ipk_iff_ioctl_func;
iff_filter.iff_detached = ipk_iff_detached_func;
// attach as interface filter
returnValue = iflt_attach(ifnet_ref, &iff_filter, &ifFilterRef);
PROJECT_attach[attachIndex].ifFilterRef = ifFilterRef;
// remember the ifnet we found (release when detaching)
PROJECT_attach[attachIndex].ifnet_ref = ifnet_ref;
//if (ifnet_ref) ifnet_release(ifnet_ref);
if (returnValue != 0) {
printf("ik_setoption attach: iflt_attached failed %d\n", returnValue);
break;
} else {
#if IPK_DEBUG
printf("ik_setoption: interface filter on %s\n", attachParam.bsdName);
#endif
}
}
// remember other attach info
{
int len;
// default to filter on
PROJECT_attach[attachIndex].kftInterfaceEntry.filterOn = 1;
// remember interface name as CString
len = strlen(attachParam.bsdName);
memcpy(&PROJECT_attach[attachIndex].kftInterfaceEntry.bsdName[0],
&attachParam.bsdName[0], len+1);
// associate this controller with DLIL attach
PROJECT_control[controlIndex].attachMap[attachIndex] = 1;
#if IPK_DEBUG
printf("ik_setoption control instance: %d attach instance: %d\n",
controlIndex, attachIndex);
#endif
}
// Everything worked, Yay!
returnValue = EJUSTRETURN;
} while (FALSE);
// mutex unlock
PROJECT_unlock();
break;
case SO_DETACH_LINK:
// mutex lock
PROJECT_lock();
do {
// detach specified interface from controller socket
struct sopt_attachParam attachParam;
#if IPK_DEBUG
printf("ik_setoption: received SO_DETACH_LINK\n");
#endif
// if name is nil, detach controller and any interface attachments
if (sockopt_valsize(sopt) == 0) {
#if IPK_DEBUG
printf("ik_setoption detach all interfaces\n");
#endif
returnValue = ik_detachController(controlIndex);
if (returnValue) break;
returnValue = EJUSTRETURN;
break;
}
// copyin option parameter
returnValue = sockopt_copyin(sopt, &attachParam, sizeof(attachParam));
if (returnValue) break;
// make sure delay table is empty before detaching from any interfaces
//KFT_delayAge(0);
// look for existing DLIL attach by bsdName
attachIndex = ik_attachIndexForName(attachParam.bsdName);
if (attachIndex) {
// found existing DLIL attach
// dis-associate this controller with attach
PROJECT_control[controlIndex].attachMap[attachIndex] = 0;
// are there any more references?
if (ik_controlCount(attachIndex) == 0) {
// no more references to this attach instance
ik_detachInterface(attachIndex);
}
}
else {
returnValue = ENOENT;
break;
}
returnValue = EJUSTRETURN;
} while (FALSE);
// mutex unlock
PROJECT_unlock();
break;
case SO_MONITOR_ON:
do {
#if IPK_DEBUG
printf("ik_setoption: received SO_MONITOR_ON\n");
#endif
// reset nkeSends to tell NKE we want more
PROJECT_control[controlIndex].nkeSends = 0;
// check for attachment to an interface
if (ik_attachCount(controlIndex) == 0) {
returnValue = ENOENT;
break;
}
// if monitoring is not already on
if (PROJECT_control[controlIndex].monitorOn == 0) {
int32_t result;
PROJECT_control[controlIndex].monitorOn = 1;
// start 1-second timer to send interface stats upstream
//PROJECT_timerRefCount += 1;
result = OSAddAtomic(1, (SInt32*)&PROJECT_timerRefCount);
result = OSAddAtomic(1, (SInt32*)&PROJECT_timerPending);
if (result == 0) { // timer is not running
#if IPK_DEBUG
printf("ik_setoption: start timer\n");
#endif
ipk_timeout((void *)0);
}
}
returnValue = EJUSTRETURN;
} while (FALSE);
break;
case SO_MONITOR_OFF:
do {
#if IPK_DEBUG
printf("ik_setoption: received SO_MONITOR_OFF\n");
#endif
PROJECT_control[controlIndex].monitorOn = 0;
// bump timer ref count
//PROJECT_timerRefCount -= 1;
status = OSAddAtomic(-1, (SInt32*)&PROJECT_timerRefCount);
if (status <= 0) PROJECT_timerRefCount = 0; // defensive
// report current connection table
returnValue = EJUSTRETURN;
} while (FALSE);
break;
default: {
#if IPK_DEBUG
printf("ik_setoption: default unrecognized sopt->sopt_name=%x pass it on\n", sockopt_name(sopt));
#endif
returnValue = 0;
break;
}
} // end switch
} while (FALSE);
#if 0
printf("ik_setoption returnValue: %d\n", returnValue);
#endif
return returnValue;
}
static errno_t
ik_getoption(void *cookie, socket_t so,
sockopt_t opt)
{
return 0;
}
static errno_t
ik_listen(void *cookie, socket_t so)
{
return 0;
}
static errno_t
ik_ioctl(void *cookie, socket_t so,
u_int32_t request, const char* argp)
{
return 0;
}
#pragma mark -- Tiger protocol filter intercept functions
static errno_t
ipk_ipf_input_func(caddr_t cookie, mbuf_t *data, int hlen, u_int8_t protocol)
{
int returnValue = 0;
attach_t* myAttach;
size_t len;
// avoid duplicates
if ( PROJECT_is_mtag(*data, TAG_IN) ) return 0;
returnValue = PROJECT_mtag(*data, TAG_IN);
//if (returnValue != 0) return returnValue; // if allocation failed, get out
// get access to our attach instance
myAttach = (attach_t*)cookie;
// Count whatever we get at this level. Notice VPN might be counted twice.
//if (protocol != IPPROTO_ICMP && protocol != IPPROTO_TCP && protocol != IPPROTO_TCP) return 0;
// count receive traffic
len = mbuf_pkthdr_len(*data);
//myAttach->receiveCount += len;
OSAddAtomic(len, (SInt32*)&myAttach->receiveCount);
return returnValue;
}
static errno_t
ipk_ipf_output_func(caddr_t cookie, mbuf_t *data, ipf_pktopts_t options)
{
int returnValue = 0;
attach_t* myAttach;
size_t len;
// avoid duplicates
if ( PROJECT_is_mtag(*data, TAG_OUT) ) return 0;
returnValue = PROJECT_mtag(*data, TAG_OUT);
//if (returnValue != 0) return returnValue; // if allocation failed, get out
// get access to our attach instance
myAttach = (attach_t*)cookie;
// Count whatever we get at this level. Notice VPN might be counted twice.
//if (protocol != IPPROTO_ICMP && protocol != IPPROTO_TCP && protocol != IPPROTO_TCP) return 0;
// count receive traffic
len = mbuf_pkthdr_len(*data);
//myAttach->sendCount += len;
OSAddAtomic(len, (SInt32*)&myAttach->sendCount);
return returnValue;
}
static void
ipk_ipf_detach_func(caddr_t cookie)
{
//printf("ipk_ipf_detach_func: %x\n", *(ipfilter_t*)cookie);
}
#pragma mark -- Tiger interface filter intercept functions
static void ipk_iff_detached_func(
void *cookie,
ifnet_t interface)
{
}
static void ipk_iff_event_func(
void *cookie,
ifnet_t interface,
protocol_family_t protocol,
const struct kev_msg *event_msg)
{
// check for interface detaching event
if (event_msg->event_code == KEV_DL_IF_DETACHING) {
attach_t* myAttach;
u_int8_t attachIndex;
// get access to our attach instance
myAttach = (attach_t*)cookie;
attachIndex = myAttach->attachIndex;
// make sure interface is detached
if (PROJECT_attach[attachIndex].ifnet_ref) {
PROJECT_lock();
ik_detachInterface(attachIndex);
PROJECT_unlock();
}
}
}
static errno_t ipk_iff_input_func(
void *cookie,
ifnet_t interface,
protocol_family_t protocol,
mbuf_t *data,
char **frame_ptr)
{
int returnValue = 0;
attach_t* myAttach;
int32_t len;
// avoid duplicates
if ( PROJECT_is_mtag(*data, TAG_IN) ) return 0;
returnValue = PROJECT_mtag(*data, TAG_IN);
//if (returnValue != 0) return returnValue; // if allocation failed, get out
// get access to our attach instance
myAttach = (attach_t*)cookie;
// Count whatever we get at this level. Notice VPN might be counted twice.
//if (protocol != IPPROTO_ICMP && protocol != IPPROTO_TCP && protocol != IPPROTO_TCP) return 0;
// count receive traffic
len = mbuf_pkthdr_len(*data);
// myAttach->receiveCount += len;
OSAddAtomic(len, (SInt32*)&myAttach->receiveCount);
return returnValue;
}
static errno_t ipk_iff_ioctl_func(
void *cookie,
ifnet_t interface,
protocol_family_t protocol,
u_long ioctl_cmd,
void* ioctl_arg)
{
return EOPNOTSUPP;
}
static errno_t ipk_iff_output_func(
void *cookie,
ifnet_t interface,
protocol_family_t protocol,
mbuf_t *data)
{
int returnValue = 0;
attach_t* myAttach;
int32_t len;
// avoid duplicates
if ( PROJECT_is_mtag(*data, TAG_OUT) ) return 0;
returnValue = PROJECT_mtag(*data, TAG_OUT);
//if (returnValue != 0) return returnValue; // if allocation failed, get out
// get access to our attach instance
myAttach = (attach_t*)cookie;
int headerLen = ifnet_hdrlen(interface);
// fix header to work around PPP bug
if (ifnet_type(interface) == IFT_PPP) {
// fix header to work around PPP bug
//if ((m->m_data[headerLength-2] & 0xF0) == 0x40) headerLength -= 2;
u_int8_t* dp = mbuf_data(*data);
if ((dp[headerLen-2] & 0xF0) == 0x40) headerLen -= 2;
}
// Count whatever we get at this level.
// count receive traffic
len = mbuf_pkthdr_len(*data) - headerLen;
// myAttach->sendCount += len;
OSAddAtomic(len, (SInt32*)&myAttach->sendCount);
return returnValue;
}
#pragma mark -- NKE support functions --
#pragma mark does mutex lock
// ---------------------------------------------------------------------------------
// Ñ PROJECT_lock
// ---------------------------------------------------------------------------------
void PROJECT_lock()
{
// lock to allow one thread at a time
lck_mtx_lock(tnke_lock);
}
// ---------------------------------------------------------------------------------
// Ñ PROJECT_unlock
// ---------------------------------------------------------------------------------
void PROJECT_unlock()
{
// unlock
lck_mtx_unlock(tnke_lock);
}
// ---------------------------------------------------------------------------------
// Ñ ik_connect
// ---------------------------------------------------------------------------------
// Register connection between controller socket and our NKE
// Called via PF_NKE, ik_setoption (set_SO) and also soconnect (standard UDP)
static int ik_connect(socket_t cso)
{
int returnValue = 0;
int controlIndex;
#if IPK_DEBUG
printf("ik_connect\n");
#endif
// mutex lock
PROJECT_lock();
do {
// check if this controller socket is already connected
controlIndex = ik_controlIndexForSocket(cso);
if (controlIndex) { // found it
returnValue = EISCONN;
break;
}
// find slot for connection instance
controlIndex = ik_emptyControlIndex();
if (!controlIndex) {
returnValue = EBUSY;
break; // all connection slots full
}
// register we have a connection
PROJECT_control[controlIndex].ctl = cso;
returnValue = EJUSTRETURN;
} while (FALSE);
// mutex unlock
PROJECT_unlock();
return returnValue;
}
// ---------------------------------------------------------------------------------
// Ñ ik_disconnect
// ---------------------------------------------------------------------------------
static void ik_disconnect(socket_t cso)
{
int controlIndex;
#if IPK_DEBUG
printf("ik_disconnect\n");
#endif
// mutex lock
PROJECT_lock();
// check if this controller socket is connected and remove it
controlIndex = ik_controlIndexForSocket(cso);
if (controlIndex) { // found it
// remove reference to attach instance and clear control instance
ik_detachController(controlIndex);
}
// mutex unlock
PROJECT_unlock();
}
// ---------------------------------------------------------------------------------
// Ñ ipk_timeout
// ---------------------------------------------------------------------------------
// one second timer used for monitoring
// reschedules itself to be called each second when timerRefCount > 0
static void ipk_timeout(void *cookie)
{
struct timespec ts;
ts.tv_sec = 1; // one second interval
ts.tv_nsec = 0;
// lock to allow one thread at a time
PROJECT_lock();
ipk_periodical();
// reschedule ourself if anyone needs us
// timeout(void (*func)(), void *cookie, int ticks);
if (PROJECT_timerRefCount > 0) {
PROJECT_timerPending = TRUE;
bsd_timeout(ipk_timeout, (void *)0, &ts);
}
else {
PROJECT_timerPending = FALSE;
#if IPK_DEBUG
printf("ipk_timeout: stop timer\n");
#endif
}
// unlock
PROJECT_unlock();
}
#pragma mark no mutex lock (caller must have it)
// ---------------------------------------------------------------------------------
// Ñ ipk_periodical
// ---------------------------------------------------------------------------------
// periodic processing
static void ipk_periodical()
{
int i, j;
// grab stats for each attach instance
for (i=1; i<=kMaxAttach; i++) {
if (PROJECT_attach[i].ipFilterRef || PROJECT_attach[i].ifFilterRef) {
// copy current value
PROJECT_attach[i].sendStamp = PROJECT_attach[i].sendCount;
PROJECT_attach[i].receiveStamp = PROJECT_attach[i].receiveCount;
// reset counter
PROJECT_attach[i].sendCount = 0;
PROJECT_attach[i].receiveCount = 0;
}
}
// send corresponding stats upstream for each monitoring control instance
for (i=1; i<=kMaxControl; i++) {
if (PROJECT_control[i].monitorOn) {
// increment NKE sends since last request so we don't
// flood input queue when no one is listening.
PROJECT_control[i].nkeSends += 1;
if (PROJECT_control[i].nkeSends < 15) { // 15 seconds of data max
// build message to send monitor stats
ipk_monitorStats_t monitorStats;
monitorStats.length = sizeof(ipk_monitorStats_t);
monitorStats.type = kMonitorStats;
for (j=1; j<=kMaxAttach; j++) {
if (PROJECT_control[i].attachMap[j]) {
monitorStats.sendCount = PROJECT_attach[j].sendStamp;
monitorStats.receiveCount = PROJECT_attach[j].receiveStamp;
break;
}
}
// send it
PROJECT_sendMessage(PROJECT_control[i].ctl, (ipk_message_t*)&monitorStats);
}
}
}
}
// ---------------------------------------------------------------------------------
// Ñ PROJECT_sendMessageToAll
// ---------------------------------------------------------------------------------
// send message to each active controller
void PROJECT_sendMessageToAll(ipk_message_t *message)
{
int i;
for (i=1;i<=kMaxControl;i++) {
if (PROJECT_control[i].ctl) {
// increment NKE sends since last request so we don't
// keep flooding input queue when no one is listening.
PROJECT_control[i].nkeSends += 1;
if (PROJECT_control[i].nkeSends < 250) { // max messages allowed in 2.5 seconds
PROJECT_sendMessage(PROJECT_control[i].ctl, message);
}
}
}
}
// ---------------------------------------------------------------------------------
// Ñ PROJECT_sendMessage
// ---------------------------------------------------------------------------------
// send message to controlling socket
// assumes caller has mutex lock
void PROJECT_sendMessage(socket_t ctl, ipk_message_t *message)
{
struct sockaddr from;
mbuf_t mbuf;
errno_t status;
do {
void* dp; // pointer to actual mbuf data
size_t size;
if (!ctl) break; // defensive
// setup from address
bzero(&from, 16);
from.sa_len = 16;
from.sa_family = PF_INET;
// get mbuf with cluster to hold our message
status = mbuf_getpacket(MBUF_DONTWAIT, &mbuf);
if (status != 0) break;
status = mbuf_settype(mbuf, MBUF_TYPE_DATA);
// confirm we have enough space for message
size = mbuf_maxlen(mbuf);
if (message->length > size) {
mbuf_freem(mbuf);
printf("sendMessage: mbuf too small for message\n");
break;
}
// copy data to cluster
dp = mbuf_datastart(mbuf);
bcopy(message, dp, message->length);
// set data and length
status = mbuf_setdata(mbuf, dp, message->length);
if (status != 0) {
mbuf_freem(mbuf);
printf("mbuf_setData: failed %d\n",status);
break;
}
// inject data to socket
status = sock_inject_data_in(
ctl,
&from,
mbuf,
NULL,
sock_data_filt_flag_record);
if (status != 0) {
mbuf_freem(mbuf);
printf("sock_inject_data_in: failed %d\n",status);
break;
}
} while (FALSE);
}
// ---------------------------------------------------------------------------------
// Ñ ik_attachCount
// ---------------------------------------------------------------------------------
// how many attachments does this controller reference
static int ik_attachCount(int controlIndex)
{
int count, i;
count = 0;
// valid controlIndex?
if ((controlIndex > 0) && (controlIndex <= kMaxControl)) {
// for each attach instance
for (i=1; i<=kMaxAttach; i++) {
// does this controller reference it
if (PROJECT_control[controlIndex].attachMap[i]) count += 1;
}
}
return count;
}
// ---------------------------------------------------------------------------------
// Ñ ik_controlCount
// ---------------------------------------------------------------------------------
// how many controllers reference this attachment
static int ik_controlCount(int attachIndex)
{
int count, i;
count = 0;
// valid attachIndex?
if ((attachIndex > 0) && (attachIndex <= kMaxAttach)) {
// for each control instance
for (i=1; i<=kMaxControl; i++) {
// does it reference this attach instance
if (PROJECT_control[i].attachMap[attachIndex]) count += 1;
}
}
return count;
}
// ---------------------------------------------------------------------------------
// Ñ ik_controlIndexForSocket
// ---------------------------------------------------------------------------------
// Find controller instance with corresponding socket if any and pass back it's index.
// Returns 0 for not found
static int ik_controlIndexForSocket(socket_t so)
{
int returnValue = 0; // controller not connected ENOTCONN
int i;
for (i=1; i<=kMaxControl; i++) {
if (PROJECT_control[i].ctl == so) {
returnValue = i;
break;
}
}
return returnValue;
}
// ---------------------------------------------------------------------------------
// Ñ ik_emptyControlIndex
// ---------------------------------------------------------------------------------
// Find an empty controller instance (available slot)
// Returns 0 for not found
static int ik_emptyControlIndex()
{
int returnValue = 0; // all slots in use EBUSY
int i;
for (i=1; i<=kMaxControl; i++) {
if (PROJECT_control[i].ctl == 0) {
returnValue = i;
break;
}
}
return returnValue;
}
// ---------------------------------------------------------------------------------
// Ñ ik_attachIndexForName
// ---------------------------------------------------------------------------------
// Find attach instance with corresponding bsdName.
// Return 0 for not found.
static int ik_attachIndexForName(char *inName)
{
int returnValue = 0; // no such entry ENOENT
int i;
int len;
len = strlen(inName);
for (i=1; i<=kMaxAttach; i++) {
if ( memcmp(inName, &PROJECT_attach[i].kftInterfaceEntry.bsdName[0], len) == 0 ) {
returnValue = i;
break;
}
}
return returnValue;
}
// ---------------------------------------------------------------------------------
// Ñ ik_emptyAttachIndex
// ---------------------------------------------------------------------------------
// Find an empty attach instance (available slot)
// Return 0 for not found
static int ik_emptyAttachIndex()
{
int returnValue = 0; // all slots in use EBUSY
int i;
for (i=1; i<=kMaxAttach; i++) {
if ((PROJECT_attach[i].ipFilterRef == 0) && (PROJECT_attach[i].ifFilterRef == 0)) {
returnValue = i;
break;
}
}
return returnValue;
}
// ---------------------------------------------------------------------------------
// Ñ ik_detachController
// ---------------------------------------------------------------------------------
// detach controller and remove any interface attachments
// that are no longer referenced. Finally, clear the control instance.
//
// return 0 for success or unix error code
static int ik_detachController(int controlIndex)
{
int returnValue=0;
int attachIndex;
int result;
do {
if (!controlIndex) break; // no control instance, we're done
// make sure delay table is empty before detaching from any interfaces
//KFT_delayAge(0);
// if controller was monitoring, turn it off
// - user might quit app without stopping or closing monitor window
if (PROJECT_control[controlIndex].monitorOn) {
PROJECT_control[controlIndex].monitorOn = 0;
// bump timer ref count
//PROJECT_timerRefCount -= 1;
//if (PROJECT_timerRefCount < 0) PROJECT_timerRefCount = 0;
result = OSAddAtomic(-1, (SInt32*)&PROJECT_timerRefCount);
if (result <= 0) PROJECT_timerRefCount = 0; // defensive
}
// get corresponding attach instances
for (attachIndex=1;attachIndex<=kMaxAttach;attachIndex++) {
if (PROJECT_control[controlIndex].attachMap[attachIndex]) {
// remove our reference to attach instance
PROJECT_control[controlIndex].attachMap[attachIndex] = 0;
// are there any more references?
if (ik_controlCount(attachIndex) == 0) {
// no more references to this attach instance
// detach our NKE from this interface and clear instance
if (PROJECT_attach[attachIndex].ifFilterRef || PROJECT_attach[attachIndex].ipFilterRef) {
ik_detachInterface(attachIndex);
}
}
} // end found attach instance
} // end for each attach instance
// clear control instance
bzero(&PROJECT_control[controlIndex], sizeof(control_t));
} while (FALSE);
return returnValue;
}
// ---------------------------------------------------------------------------------
// Ñ ik_detachInterface
// ---------------------------------------------------------------------------------
// detach interface and clear the attach instance
// return 0 for success or unix error code
static int ik_detachInterface(int attachIndex)
{
int returnValue=0;
errno_t status;
int i;
do {
if ((attachIndex == 0) || (attachIndex > kMaxAttach)) break; // no attach instance, we're done
// for each control instance
for (i=1; i<=kMaxControl; i++) {
// dis-associate with this attach instance
PROJECT_control[i].attachMap[attachIndex] = 0;
}
// detach our NKE from this interface and clear instance
// ifnet_ref
// release and set to zero before dlil_detach_filter which calls ipk_ipf_detach_func
if (PROJECT_attach[attachIndex].ifnet_ref) ifnet_release(PROJECT_attach[attachIndex].ifnet_ref);
PROJECT_attach[attachIndex].ifnet_ref = 0;
// ifFilterRef
if (PROJECT_attach[attachIndex].ifFilterRef) iflt_detach(PROJECT_attach[attachIndex].ifFilterRef);
// ipFilterRef
if (PROJECT_attach[attachIndex].ipFilterRef) {
status = ipf_remove(PROJECT_attach[attachIndex].ipFilterRef);
if (status) {
printf("ik_detachInterface: ipf_remove failed %d\n", status);
returnValue = KERN_FAILURE;
}
}
bzero(&PROJECT_attach[attachIndex], sizeof(attach_t));
} while (0);
return returnValue;
}
// ---------------------------------------------------------------------------------
// Ñ PROJECT_mtag
// ---------------------------------------------------------------------------------
// Tag this packet with "tag_value" so we'll know if we have seen it.
// tag_value should be either TAG_IN or TAG_OUT.
//
// Notice if an mbuf is re-used to send a response such as an echo request (ping) that
// is converted to an echo reply, you may want to recognize the redirected mbuf as a new packet.
// Thus we distinguish TAG_IN from TAG_OUT and intentionally replace the
// previous tag value rather than combining. If we re-direct a packet, we need to
// re-tag it accordingly. The code below checks for an existing tag and resets the value.
//
// Finally, an mbuf tag is effectively another mbuf that travels with the packet.
// Allocating an mbuf tag can fail in which case we return the corresponding error code.
// Notice it is the callers responsibility to free the original packet to avoid looping
// or a recursive lock attempt (kernel panic) if a packet without the correct
// tag is re-injected and processed again.
//
errno_t PROJECT_mtag(mbuf_t mbuf_ref, int tag_value)
{
errno_t status;
int *tag_ref;
size_t len;
// look for existing tag
status = mbuf_tag_find(mbuf_ref, gidtag, MY_TAG_TYPE, &len, (void*)&tag_ref);
// allocate tag if needed
if (status != 0) {
status = mbuf_tag_allocate(mbuf_ref, gidtag, MY_TAG_TYPE, sizeof(*tag_ref), MBUF_DONTWAIT, (void**)&tag_ref);
}
if (status == 0) *tag_ref = tag_value;
return status;
}
// ---------------------------------------------------------------------------------
// Ñ PROJECT_is_mtag
// ---------------------------------------------------------------------------------
// Return 1 if packet is tagged with "tag_value", otherwise 0
int PROJECT_is_mtag(mbuf_t mbuf_ref, int tag_value)
{
int returnValue = 0;
errno_t status;
int *tag_ref;
size_t len;
// Check whether we have seen this packet before.
status = mbuf_tag_find(mbuf_ref, gidtag, MY_TAG_TYPE, &len, (void**)&tag_ref);
if ((status == 0) && (*tag_ref & tag_value)) returnValue = 1;
return returnValue;
}