home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
sustworks.com
/
2014.06.sustworks.com.tar
/
sustworks.com
/
open_source_IPNetSentry_TNKE.dmg
/
IPNetSentry_TNKE.c
< prev
next >
Wrap
C/C++ Source or Header
|
2005-08-22
|
59KB
|
1,951 lines
//
// IPNetSentry_TNKE.c
// Mac OS X Interface Filter NKE
//
// Created by psichel [PAS] on Teus Nov 12 2002.
// Based on SharedIP and TCPLogger provided as Open Source sample code
// from Apple Computer.
//
// See IPNetSentry_TNKE.h for high level design overview.
// You can send comments or suggestions to psichel@sustworks.com
// The companion IPNetSentryX 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 DEBUG_IPK 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 "IPNetSentry_TNKE.h"
#include "IPTypes.h"
#include "kft.h"
#include "kftDelay.h"
#include "kftTrigger.h"
#include "kftConnectionTable.h"
#ifdef IPNetRouter
#include "kftPortMapTable.h"
#include "kftNatTable.h"
#endif
#include "FilterTypes.h"
// tag packets so we can recognize re-injected ones
#define MY_BUNDLE_ID "com.sustworks.kext.IPNetSentry_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 = {
IPNetSentry_TNKE_Handle,
SFLT_PROG,
"com.sustworks.kext.IPNetSentry_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 callback
static void ipk_timeout(void *cookie);
// timer schedule
static void ipk_setTimer();
// send message to controlling socket
void PROJECT_sendMessage(socket_t ctl, ipk_message_t *message);
//
// Global structures and data storage
//
// max size of drop Response
#define KFT_dropResponseMax 1000
// drop Connection response
u_int8_t PROJECT_dropResponseBuffer[KFT_dropResponseMax];
int PROJECT_dropResponseLength;
// time of day info
sopt_timeParam_t PROJECT_timeOfDay;
// flags
u_int32_t PROJECT_flags;
// timer Ref Count (firewall enabled)
int32_t PROJECT_timerRefCount;
// ipk_timeout reschedules itself when PROJECT_timerRefCount>0
int PROJECT_doRateLimit; // packet matched a rate limit rule
// index of failover requested 0=none, 1=default, 2=alternate gateway
int PROJECT_failoverRequest;
// reserve bandwidth info
KFT_reserveInfo_t PROJECT_rReserveInfo;
KFT_reserveInfo_t PROJECT_sReserveInfo;
//
// Module wide variables (not exported)
//
static int32_t ipk_timerPending;
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
//
// ---------------------------------------------------------------------------------
// Ñ IPNetSentry_TNKE_start
// ---------------------------------------------------------------------------------
kern_return_t IPNetSentry_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("IPNetSentry_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("IPNetSentry_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("IPNetSentry_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
lck_mtx_lock(tnke_lock);
do {
// already initialized?
if (ipk_initted) break;
// initialize global storage
PROJECT_timerRefCount = 0;
ipk_timerPending = 0;
PROJECT_timeOfDay.timeStamp = 0;
PROJECT_flags = 0;
PROJECT_dropResponseLength = 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));
}
// setup KFT structures
KFT_init();
// register our NKE
status = sflt_register(&ik_sflt_filter, PF_INET,
SOCK_DGRAM, IPPROTO_UDP);
if (status != 0) {
printf("IPNetSentry_TNKE_start: sflt_register failed %d\n", status);
returnValue = KERN_FAILURE;
} else {
registered++;
ipk_initted = 1;
}
} while (FALSE);
// mutex unlock
lck_mtx_unlock(tnke_lock);
return returnValue;
}
// ---------------------------------------------------------------------------------
// Ñ IPNetSentry_TNKE_stop
// ---------------------------------------------------------------------------------
kern_return_t IPNetSentry_TNKE_stop (kmod_info_t * ki, void * data) {
int returnValue = KERN_SUCCESS;
errno_t status;
int i;
// mutex lock
lck_mtx_lock(tnke_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);
}
}
// release KFT structures
KFT_terminate();
// 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 || ipk_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("IPNetSentry_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("IPNetSentry_TNKE_stop: again\n");
returnValue = KERN_FAILURE;
}
} while (FALSE);
// mutex unlock
lck_mtx_unlock(tnke_lock);
// 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("IPNetSentry_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;
int32_t count;
int valsize, howMany;
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
lck_mtx_lock(tnke_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 DEBUG_IPK
printf("ik_setoption: received SO_ATTACH_LINK\n");
#endif
// copyin option parameter
returnValue = sockopt_copyin(sopt, &attachParam, sizeof(attachParam));
if (returnValue != 0) 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.IPNetSentry_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 DEBUG_IPK
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.IPNetSentry_TNKE";
//iff_filter.iff_protocol = PF_INET;
iff_filter.iff_protocol = 0; // bridge non PF_INET packets
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 DEBUG_IPK
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 DEBUG_IPK
printf("ik_setoption control instance: %d attach instance: %d\n",
controlIndex, attachIndex);
#endif
}
// Everything worked, Yay!
returnValue = EJUSTRETURN;
} while (FALSE);
// mutex unlock
lck_mtx_unlock(tnke_lock);
break;
case SO_DETACH_LINK:
// mutex lock
lck_mtx_lock(tnke_lock);
do {
// detach specified interface from controller socket
struct sopt_attachParam attachParam;
#if DEBUG_IPK
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 DEBUG_IPK
printf("ik_setoption detach all interfaces\n");
#endif
returnValue = ik_detachController(controlIndex);
if (returnValue != 0) break;
returnValue = EJUSTRETURN;
break;
}
// copyin option parameter
returnValue = sockopt_copyin(sopt, &attachParam, sizeof(attachParam));
if (returnValue != 0) 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
lck_mtx_unlock(tnke_lock);
break;
case SO_MONITOR_ON:
do {
#if DEBUG_IPK
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*)&ipk_timerPending);
if (result == 0) { // timer is not running
#if DEBUG_IPK
printf("ik_setoption: start timer\n");
#endif
ipk_timeout((void *)0);
}
}
returnValue = EJUSTRETURN;
} while (FALSE);
break;
case SO_MONITOR_OFF:
do {
#if DEBUG_IPK
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
{
// mutex lock
lck_mtx_lock(tnke_lock);
// do report
KFT_connectionReport();
// mutex unlock
lck_mtx_unlock(tnke_lock);
}
returnValue = EJUSTRETURN;
} while (FALSE);
break;
case SO_FILTER_DOWNLOAD:
// filter table is downloaded as binary array of table entries
// mutex lock
lck_mtx_lock(tnke_lock);
do {
#if DEBUG_IPK
printf("ik_setoption: received SO_FILTER_DOWNLOAD\n");
#endif
// check option length
kft_filterTableD.length = sockopt_valsize(sopt);
if (kft_filterTableD.length >= kft_filterTableD.bufferLength) {
kft_filterTableD.length = kft_filterTableD.bufferLength;
#if DEBUG_IPK
printf("SO_FILTER_DOWNLOAD buffer space exceeded.\n");
#endif
returnValue = EMSGSIZE;
break;
}
// copyin option parameter
returnValue = sockopt_copyin(sopt, kft_filterTableD.bytes, kft_filterTableD.length);
if (returnValue != 0) break;
#if DEBUG_IPK
printf("ik_setoption: sooptcopyin OK\n");
#endif
kft_filterTableD.offset = kft_filterTableD.length/sizeof(KFT_filterEntry_t);
returnValue = EJUSTRETURN;
} while (FALSE);
// mutex unlock
lck_mtx_unlock(tnke_lock);
break;
case SO_KFT_RESET: {
#if DEBUG_IPK
printf("ik_setoption: received SO_KFT_RESET\n");
#endif
// mutex lock
lck_mtx_lock(tnke_lock);
// reset selected state before downloading
KFT_reset();
// mutex unlock
lck_mtx_unlock(tnke_lock);
returnValue = EJUSTRETURN;
break;
}
case SO_FILTER_UPLOAD: {
#if DEBUG_IPK
printf("ik_setoption: received SO_FILTER_UPLOAD\n");
#endif
returnValue = sockopt_copyout(sopt, kft_filterTableD.bytes, kft_filterTableD.length);
if (returnValue != 0) break;
#if DEBUG_IPK
printf("ik_setoption: sooptcopyout OK %d\n",outBuf.length);
#endif
returnValue = EJUSTRETURN;
break;
}
case SO_FILTER_COUNT: {
#if DEBUG_IPK
printf("ik_setoption: received SO_FILTER_COUNT\n");
#endif
count = KFT_filterCount();
returnValue = sockopt_copyout(sopt, &count, sizeof(count));
if (returnValue != 0) break;
returnValue = EJUSTRETURN;
break;
}
case SO_TRIGGER_COUNT: {
#if DEBUG_IPK
printf("ik_setoption: received SO_TRIGGER_COUNT\n");
#endif
count = KFT_triggerCount();
returnValue = sockopt_copyout(sopt, &count, sizeof(count));
if (returnValue != 0) break;
returnValue = EJUSTRETURN;
break;
}
case SO_INTERFACE_COUNT: {
#if DEBUG_IPK
printf("ik_setoption: received SO_INTERFACE_COUNT\n");
#endif
count = ik_attachCount(controlIndex);
returnValue = sockopt_copyout(sopt, &count, sizeof(count));
if (returnValue != 0) break;
returnValue = EJUSTRETURN;
break;
}
#ifdef IPNetRouter
case SO_PORTMAP_COUNT: {
#if DEBUG_IPK
printf("ik_setoption: received SO_PORTMAP_COUNT\n");
#endif
count = KFT_portMapCount();
returnValue = sockopt_copyout(sopt, &count, sizeof(count));
if (returnValue != 0) break;
returnValue = EJUSTRETURN;
break;
}
#endif
case SO_SET_TIME: {
// set time of day info for NKE
#if DEBUG_IPK
printf("ik_setoption: received SO_SET_TIME\n");
#endif
// copyin option parameter
returnValue = sockopt_copyin(sopt, &PROJECT_timeOfDay, sizeof(struct sopt_timeParam));
if (returnValue != 0) break;
returnValue = EJUSTRETURN;
break;
}
case SO_SET_FLAGS: {
// set global NKE flags
sopt_flagsParam_t flagsParam;
// copyin option parameter
returnValue = sockopt_copyin(sopt, &flagsParam, sizeof(struct sopt_flagsParam));
if (returnValue != 0) break;
PROJECT_flags &= ~flagsParam.mask; // clear bits we're going to modify
PROJECT_flags |= flagsParam.flags; // set bits to effect change
returnValue = EJUSTRETURN;
#if DEBUG_IPK
KFT_logText("ik_setoption: received SO_SET_FLAGS ", &PROJECT_flags);
#endif
break;
}
case SO_GET_FLAGS: {
// get global NKE flags
sopt_flagsParam_t flagsParam;
flagsParam.flags = PROJECT_flags;
flagsParam.mask = 0xFFFFFFFF;
// return current value
returnValue = sockopt_copyout(sopt, &flagsParam, sizeof(flagsParam));
if (returnValue != 0) break;
returnValue = EJUSTRETURN;
break;
}
case SO_DROP_RESPONSE: {
#if DEBUG_IPK
printf("ik_setoption: received SO_DROP_RESPONSE\n");
#endif
// check length of option parameter
PROJECT_dropResponseLength = sockopt_valsize(sopt);
if (PROJECT_dropResponseLength < KFT_dropResponseMax-60) {
// copyin option parameter
// leave room for link header, ip header, tcp header
returnValue = sockopt_copyin(sopt, &PROJECT_dropResponseBuffer[60], PROJECT_dropResponseLength);
if (returnValue != 0) break;
}
else {
returnValue = E2BIG;
break;
}
#if DEBUG_IPK
printf("ik_setoption: sooptcopyin OK %d\n",PROJECT_dropResponseLength);
#endif
returnValue = EJUSTRETURN;
break;
}
case SO_TRIGGER_DURATION: {
#if DEBUG_IPK
printf("ik_setoption: received SO_TRIGGER_EXPIRATION\n");
#endif
// copyin option parameter
returnValue = sockopt_copyin(sopt, &count, sizeof(count));
if (returnValue != 0) break;
count = KFT_triggerDuration(count);
// return current value
returnValue = sockopt_copyout(sopt, &count, sizeof(count));
if (returnValue != 0) break;
returnValue = EJUSTRETURN;
break;
}
case SO_TRIGGER_ADDRESS: {
KFT_triggerKey_t value[101];
#if DEBUG_IPK
printf("ik_setoption: received SO_TRIGGER_ADDRESS\n");
#endif
// check parameter size
valsize = sockopt_valsize(sopt);
if (valsize <= sizeof(KFT_triggerKey_t)*100) {
// copyin option parameter
returnValue = sockopt_copyin(sopt, value, valsize);
if (returnValue != 0) break;
}
else {
returnValue = E2BIG;
break;
}
howMany = valsize/sizeof(KFT_triggerKey_t);
// mutex lock
lck_mtx_lock(tnke_lock);
KFT_triggerRemoveByKey(value, howMany);
// mutex unlock
lck_mtx_unlock(tnke_lock);
// are there any more updates to send?
KFT_triggerSendUpdates();
returnValue = EJUSTRETURN;
break;
}
case SO_TRIGGER_UPLOAD: {
#if DEBUG_IPK
printf("ik_setoption: received SO_TRIGGER_UPLOAD\n");
#endif
// mutex lock
lck_mtx_lock(tnke_lock);
count = KFT_triggerUpload();
sockopt_copyout(sopt, &count, sizeof(count));
// mutex unlock
lck_mtx_unlock(tnke_lock);
returnValue = EJUSTRETURN;
break;
}
case SO_INTERFACE_UPLOAD: {
#if DEBUG_IPK
printf("ik_setoption: received SO_INTERFACE_UPLOAD\n");
#endif
// mutex lock
lck_mtx_lock(tnke_lock);
count = KFT_interfaceUpload();
sockopt_copyout(sopt, &count, sizeof(count));
// mutex unlock
lck_mtx_unlock(tnke_lock);
returnValue = EJUSTRETURN;
break;
}
#ifdef IPNetRouter
case SO_PORTMAP_UPLOAD: {
#if DEBUG_IPK
printf("ik_setoption: received SO_PORTMAP_UPLOAD\n");
#endif
// mutex lock
lck_mtx_lock(tnke_lock);
count = KFT_portMapUpload();
sockopt_copyout(sopt, &count, sizeof(count));
// mutex unlock
lck_mtx_unlock(tnke_lock);
returnValue = EJUSTRETURN;
break;
}
case SO_NAT_UPLOAD: {
#if DEBUG_IPK
printf("ik_setoption: received SO_NAT_UPLOAD\n");
#endif
// mutex lock
lck_mtx_lock(tnke_lock);
count = KFT_natUpload();
sockopt_copyout(sopt, &count, sizeof(count));
// mutex unlock
lck_mtx_unlock(tnke_lock);
returnValue = EJUSTRETURN;
break;
}
#endif
case SO_IPK_MESSAGE: {
// accept ipk_message as downstream data
unsigned char buffer[kUpdateBufferSize];
ipk_message_t* message;
message = (ipk_message_t*)&buffer[0];
#if DEBUG_IPK
printf("ik_setoption: received SO_IPK_MESSAGE\n");
#endif
// check parameter size
valsize = sockopt_valsize(sopt);
if (valsize <= kUpdateBufferSize) {
// copyin option parameter
returnValue = sockopt_copyin(sopt, &buffer[0], valsize);
if (returnValue != 0) break;
}
else {
returnValue = E2BIG;
break;
}
if (valsize < sizeof(ipk_message_t)) break;
// pass along to KFT for processing
KFT_receiveMessage(message);
returnValue = EJUSTRETURN;
break;
}
default: {
#if DEBUG_IPK
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 sopt)
{
errno_t returnValue = 0; // continue normall processing (pass it on)
//errno_t status;
int controlIndex=0;
//int attachIndex=0;
int32_t count;
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_FILTER_UPLOAD: {
#if DEBUG_IPK
printf("ik_setoption: received SO_FILTER_UPLOAD\n");
#endif
returnValue = sockopt_copyout(sopt, kft_filterTableD.bytes, kft_filterTableD.length);
if (returnValue != 0) break;
#if DEBUG_IPK
printf("ik_setoption: sooptcopyout OK %d\n",outBuf.length);
#endif
returnValue = EJUSTRETURN;
break;
}
case SO_FILTER_COUNT: {
#if DEBUG_IPK
printf("ik_setoption: received SO_FILTER_COUNT\n");
#endif
count = KFT_filterCount();
returnValue = sockopt_copyout(sopt, &count, sizeof(count));
if (returnValue != 0) break;
returnValue = EJUSTRETURN;
break;
}
case SO_TRIGGER_COUNT: {
#if DEBUG_IPK
printf("ik_setoption: received SO_TRIGGER_COUNT\n");
#endif
count = KFT_triggerCount();
returnValue = sockopt_copyout(sopt, &count, sizeof(count));
if (returnValue != 0) break;
returnValue = EJUSTRETURN;
break;
}
case SO_INTERFACE_COUNT: {
#if DEBUG_IPK
printf("ik_setoption: received SO_INTERFACE_COUNT\n");
#endif
count = ik_attachCount(controlIndex);
returnValue = sockopt_copyout(sopt, &count, sizeof(count));
if (returnValue != 0) break;
returnValue = EJUSTRETURN;
break;
}
#ifdef IPNetRouter
case SO_PORTMAP_COUNT: {
#if DEBUG_IPK
printf("ik_setoption: received SO_PORTMAP_COUNT\n");
#endif
count = KFT_portMapCount();
returnValue = sockopt_copyout(sopt, &count, sizeof(count));
if (returnValue != 0) break;
returnValue = EJUSTRETURN;
break;
}
#endif
default: {
#if DEBUG_IPK
printf("ik_getoption: 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_getoption returnValue: %d\n", returnValue);
#endif
return returnValue;
}
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
// ---------------------------------------------------------------------------------
// Ñ ipk_ipf_input_func
// ---------------------------------------------------------------------------------
// Notice we are called to filter IP packets so we assume the mbuf contains a PKTHDR
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;
// IPNetRouterX does not use protocol filter NKEs at this time
#if 0
KFT_packetData_t packet;
bzero(&packet, sizeof(packet));
packet.ipOffset = 0;
packet.direction = 1;
packet.myAttach = myAttach;
returnValue = KFT_filterPacket(&packet);
#endif
// count receive traffic
len = mbuf_pkthdr_len(*data);
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;
// Notice we are called to filter IP packets so we assume the mbuf contains a PKTHDR
//mbuf_flags_t flags = mbuf_flags(*data);
//if (flags & MBUF_PKTHDR)
// IPNetSentryX does not use protocol filter NKEs at this time
#if 0
KFT_packetData_t packet;
bzero(&packet, sizeof(packet));
packet.ipOffset = 0;
packet.direction = 0;
packet.myAttach = myAttach;
returnValue = KFT_filterPacket(&packet);
#endif
// 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;
// Notice we are called to filter IP packets so we assume the mbuf_t contains a PKTHDR
//mbuf_flags_t flags = mbuf_flags(*data);
//if (flags & MBUF_PKTHDR)
// count receive traffic
len = mbuf_pkthdr_len(*data);
// myAttach->receiveCount += len;
OSAddAtomic(len, (SInt32*)&myAttach->receiveCount);
// IP filtering
if (PROJECT_timerRefCount) {
// mutex lock
lck_mtx_lock(tnke_lock);
KFT_packetData_t packet;
bzero(&packet, sizeof(packet));
// passed in
packet.ifnet_ref = interface;
packet.protocol = protocol;
packet.mbuf_ptr = data;
packet.frame_ptr = frame_ptr;
packet.myAttach = myAttach; // cookie
// link layer info
packet.ifType = ifnet_type(interface);
packet.ifHeaderLen = ifnet_hdrlen(interface);
// check frame header for IPv4
if (packet.ifType == IFT_ETHER) {
u_int16_t* dp16;
if (*frame_ptr) { // defensive
dp16 = (u_int16_t*)(*frame_ptr);
if (dp16[6] != 0x0800) packet.bridgeNonIP = 1;
}
}
// packet info
packet.ipOffset = 0;
packet.direction = 1;
// pass to filter
returnValue = KFT_filterPacket(&packet);
// mutex unlock
lck_mtx_unlock(tnke_lock);
}
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;
// 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;
// Notice we are called to filter IP packets so we assume the mbuf_t contains a PKTHDR
//mbuf_flags_t flags = mbuf_flags(*data);
//if (flags & MBUF_PKTHDR)
// count receive traffic
len = mbuf_pkthdr_len(*data) - ifnet_hdrlen(interface);
// myAttach->sendCount += len;
OSAddAtomic(len, (SInt32*)&myAttach->sendCount);
// IP filtering
if (PROJECT_timerRefCount) {
// mutex lock
lck_mtx_lock(tnke_lock);
KFT_packetData_t packet;
bzero(&packet, sizeof(packet));
// passed in
packet.ifnet_ref = interface;
packet.protocol = protocol;
packet.mbuf_ptr = data;
packet.frame_ptr = 0;
packet.myAttach = myAttach;
// link layer info
packet.ifType = ifnet_type(interface);
packet.ifHeaderLen = ifnet_hdrlen(interface);
// fix header to work around PPP bug
if (packet.ifType == 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[packet.ifHeaderLen-2] & 0xF0) == 0x40) packet.ifHeaderLen -= 2;
}
// check frame header for IP
if (packet.ifType == IFT_ETHER) {
u_int16_t* dp16;
dp16 = (u_int16_t*)mbuf_data(*data);
if (dp16[6] != 0x0800) packet.bridgeNonIP = 1;
}
// packet info
packet.ipOffset = packet.ifHeaderLen; // output packets preceeded by frame header
packet.direction = 0;
// pass to filter
returnValue = KFT_filterPacket(&packet);
// mutex unlock
lck_mtx_unlock(tnke_lock);
}
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 DEBUG_IPK
printf("ik_connect\n");
#endif
// mutex lock
lck_mtx_lock(tnke_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;
// we don't retain the socket since we want to be notified when it closes.
returnValue = EJUSTRETURN;
} while (FALSE);
// mutex unlock
lck_mtx_unlock(tnke_lock);
return returnValue;
}
// ---------------------------------------------------------------------------------
// Ñ ik_disconnect
// ---------------------------------------------------------------------------------
static void ik_disconnect(socket_t cso)
{
int controlIndex;
#if DEBUG_IPK
printf("ik_disconnect\n");
#endif
// mutex lock
lck_mtx_lock(tnke_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
lck_mtx_unlock(tnke_lock);
}
// ---------------------------------------------------------------------------------
// Ñ ipk_timeout
// ---------------------------------------------------------------------------------
// one second timer used for monitoring
// reschedules itself to be called each second when timerRefCount > 0
static void ipk_timeout(void *cookie)
{
// lock to allow one thread at a time
lck_mtx_lock(tnke_lock);
KFT_filterPeriodical();
ipk_setTimer();
// unlock
lck_mtx_unlock(tnke_lock);
}
#pragma mark no mutex lock (caller must have it)
// ---------------------------------------------------------------------------------
// Ñ ipk_setTimer
// ---------------------------------------------------------------------------------
// reschedules timer to be called each second when timerRefCount > 0
static void ipk_setTimer()
{
struct timespec ts;
ts.tv_sec = 1; // one second interval
ts.tv_nsec = 0;
// reschedule ourself if anyone needs us
// timeout(void (*func)(), void *cookie, int ticks);
if (PROJECT_timerRefCount > 0) {
ipk_timerPending = TRUE;
bsd_timeout(ipk_timeout, (void *)0, &ts);
}
else {
ipk_timerPending = FALSE;
#if DEBUG_IPK
printf("ipk_timeout: stop timer\n");
#endif
}
}
// ---------------------------------------------------------------------------------
// Ñ 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_modifyReadyPacket
// ---------------------------------------------------------------------------------
// Tiger: prepare packet to be modified by finalizing and setting appropriate mbuf flags, return 0
// Panther: return 1 if outbound hardware TCP checksum is enabled so calculation should be skipped
// (no easy way to "finalize" in Panther)
int PROJECT_modifyReadyPacket(KFT_packetData_t* packet)
{
int returnValue = 0;
mbuf_t mbuf_ref;
#if TIGER
if (packet->modifyReady == 0) {
mbuf_ref = *(packet->mbuf_ptr);
if (packet->direction == kDirectionInbound) {
mbuf_inbound_modified(mbuf_ref);
}
else {
mbuf_outbound_finalize(mbuf_ref, AF_INET, packet->ipOffset);
#if 1
mbuf_csum_request_flags_t request;
u_int32_t value;
mbuf_get_csum_requested(mbuf_ref, &request, &value);
// work around bug in 10.4.2
if (request & 0x1000) returnValue = 1;
if (request & MBUF_CSUM_REQ_TCP) returnValue = 1;
//KFT_logText("csum_requested ", &request);
#endif
}
// remember what we did
packet->modifyReady = 1;
}
#else
mbuf_ref = *(packet->mbuf_ptr);
if (packet->direction == kDirectionInbound) {
// invalidate HW generated checksum flags
mbuf_ref->m_pkthdr.csum_data = 0;
mbuf_ref->m_pkthdr.csum_flags = 0;
//if ((mbuf_ref->m_pkthdr.csum_flags & CSUM_TCP_SUM16) &&
// (mbuf_ref->m_pkthdr.csum_flags & CSUM_DATA_VALID)) returnValue = 1;
}
else {
// Notice for outgoing packets the tcp checksum may not include the TCP header yet
if (mbuf_ref->m_pkthdr.csum_flags & CSUM_TCP_SUM16) returnValue = 1;
}
#endif
return returnValue;
}
// ---------------------------------------------------------------------------------
// Ñ PROJECT_mtag
// ---------------------------------------------------------------------------------
// Tag this packet with "tag_value" so we'll know if we have seen it.
// tag_value will normally 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, or simply IP Forwarded back out another interface,
// 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 = 0;
}
//if (status == 0) *tag_ref = tag_value;
if (status == 0) {
if (tag_value < 3) *tag_ref &= ~3; // clear previous TAG_IN or TAG_OUT
*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;
}