home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
sustworks.com
/
2014.06.sustworks.com.tar
/
sustworks.com
/
open_source_IPNetMonitor_NKE.dmg
/
IPNetMonitor_NKE.c
< prev
next >
Wrap
Text File
|
2005-03-09
|
49KB
|
1,396 lines
//
// IPNetMonitor_NKE.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_NKE.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
#if IPK_DEBUG
#include <sys/syslog.h>
#endif
#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 <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 "IPNetMonitor_NKE.h"
#include <libkern/OSTypes.h>
#include <string.h>
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
//
// PF_NKE socket functions
static int ipk_connect();
static int ipk_read();
static int ipk_write();
static int ipk_get();
static int ipk_put();
static void ipk_disconnect();
// socket interface functions our NKE will intercept
static int ik_close();
static int ik_connect();
static int ik_control();
static int ik_disconnect();
// dlil protocol filter functions our NKE can intercept
static int ipk_filter_dl_input();
static int ipk_filter_dl_output();
// dlil interface filter functions our NKE can intercept
static int ipk_filter_if_input();
static int ipk_filter_if_output();
// timer callbacks
static void ipk_timeout(void *cookie);
static void ipk_periodical();
// send message to controlling socket
void PROJECT_sendMessage(struct socket *ctl, ipk_message_t *message);
// internal support functions
static int ik_attachCount(int controlIndex);
static int ik_controlCount(int attachIndex);
static int ik_controlIndexForSocket(struct socket *so);
static int ik_emptyControlIndex();
static int ik_attachIndexForTag(u_long dl_tag);
static int ik_attachIndexForName(char *inName);
static int ik_emptyAttachIndex();
static int ik_findIFNet(char *inName, struct ifnet **ifnet_ptr);
static int ik_detachController(int controlIndex);
//
// Global structures and data storage
//
// Dispatch vector for IPNetMonitor_NKE socket intercepts
struct sockif IKsockif = {
NULL, // soabort
NULL, // soaccept
NULL, // sobind
ik_close, // soclose
ik_connect, // soconnect
NULL, // soconnect2
ik_control, // soset/getopt
NULL, // socreate
ik_disconnect, // sodisconnect
NULL, // sofree
NULL, // sogetopt
NULL, // sohasoutofband
NULL, // solisten
NULL, // soreceive
NULL, // sorflush
NULL, // sosend
NULL, // sosetopt
NULL, // soshutdown
NULL, // socantrcvmore
NULL, // socantsendmore
NULL, // soisconnected
NULL, // soisconnecting
NULL, // soisdisconnected
NULL, // soisdisconnecting
NULL, // sonewconn1
NULL, // soqinsque
NULL, // soqremque
NULL, // soreserve
NULL, // sowakeup
};
// Dispatch vector for IPNetMonitor_NKE socket buffer functions
struct sockutil IKsockutil = {
NULL, // sb_lock
NULL, // sbappend
NULL, // sbappendaddr
NULL, // sbappendcontrol
NULL, // sbappendrecord
NULL, // sbcompress
NULL, // sbdrop
NULL, // sbdroprecord
NULL, // sbflush
NULL, // sbinsertoob
NULL, // sbrelease
NULL, // sbreserve
NULL, // sbwait
};
// Dispatch vector for PF_NKE socket control functions
struct NFDescriptor IPNetMonitor_NKE = {
{NULL, NULL},
{NULL, NULL},
IPNetMonitor_NKE_Handle,
NFD_PROG|NFD_VISIBLE, // Ask for me by name
// NFD_GLOBAL|NFS_VISIBLE, // only if we want global filtering
ipk_connect,
ipk_disconnect,
ipk_read,
ipk_write,
ipk_get,
ipk_put,
&IKsockif, &IKsockutil
};
// I include code for both DLIL protocol and DLIL interface filters.
// I tried using a protocol filter first and discovered it did not
// see traffic from the classic networking stack. When using an
// interface filter, we need to sort the traffic for different
// protocols ourselves (IP vs AppleTalk...).
// Dispatch vector for DLIL protocol filter intercepts
struct dlil_pr_flt_str IKprotocol_filter = {
NULL, // caddr_t cookie
ipk_filter_dl_input, // filter_dl_input
ipk_filter_dl_output, // filter_dl_output
NULL, // filter_dl_event
NULL, // filter_dl_ioctl
NULL, // filter_detach
};
// Dispatch vector for DLIL interface filter intercepts
struct dlil_if_flt_str IKinterface_filter = {
NULL, // caddr_t cookie
ipk_filter_if_input, // filter_if_input
NULL, // filter_if_event
ipk_filter_if_output, // filter_if_output
NULL, // filter_if_ioctl
NULL, // filter_if_free
NULL, // filter_detach
};
int32_t PROJECT_timerRefCount;
// ipk_timeout reschedules itself when PROJECT_timerRefCount>0
int32_t PROJECT_timerPending;
//
// Module wide variables (not exported)
//
// socket highwater mark for flow control
// make these big enough to hold our report data without starving kernel memory
static int IPK_recvspace = 8192;
static int IPK_sendspace = 8192;
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];
//
// Load and unload kernel extension
//
// ---------------------------------------------------------------------------------
// Ñ IPNetMonitor_NKE_start
// ---------------------------------------------------------------------------------
kern_return_t IPNetMonitor_NKE_start (kmod_info_t * ki, void * d)
{
int returnValue = KERN_SUCCESS;
struct protosw *pp;
int s;
int funnel_state;
int i;
printf("IPNetMonitor_NKE has loaded!\n");
// grab the network funnel and prevent other network threads from pre-empting us
funnel_state = thread_funnel_set(network_flock, TRUE);
s = splnet();
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));
}
// find the protosw we want to sidle up to
// we will use UDP for the control connection to our NKE
pp = pffindproto(AF_INET, IPPROTO_UDP, SOCK_DGRAM);
if (pp == NULL) {
returnValue = EPFNOSUPPORT;
break;
}
// register our NKE
returnValue = register_sockfilter(&IPNetMonitor_NKE, NULL, pp, NFF_AFTER);
if (!returnValue) {
ipk_initted = 1;
returnValue = KERN_SUCCESS;
}
#if IPK_DEBUG
log(LOG_WARNING, "IPNetMonitor_NKE init: %d\n", returnValue);
#endif
} while (FALSE);
// restore previous priority level and thread funnel
splx(s);
thread_funnel_set(network_flock, funnel_state);
return returnValue;
}
// ---------------------------------------------------------------------------------
// Ñ IPNetMonitor_NKE_stop
// ---------------------------------------------------------------------------------
kern_return_t IPNetMonitor_NKE_stop (kmod_info_t * ki, void * d) {
int returnValue = KERN_SUCCESS;
struct protosw *pp;
int s;
int funnel_state;
int i;
u_long filterID;
funnel_state = thread_funnel_set(network_flock, TRUE);
s = splnet();
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) break;
// remove any remaining attach instances (defensive)
for (i=1; i<=kMaxAttach; i++) {
if (filterID = PROJECT_attach[i].filterID != 0) {
returnValue = dlil_detach_filter(PROJECT_attach[i].filterID);
bzero(&PROJECT_attach[i], sizeof(attach_t));
#if IPK_DEBUG
log(LOG_WARNING, "IPNetMonitor_NKE_stop: detach protocol filter: %d\n",filterID);
#endif
}
}
// 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) {
untimeout(ipk_timeout, (void *)0);
PROJECT_timerRefCount = 0;
}
// unplug our socket filter
pp = pffindproto(AF_INET, IPPROTO_UDP, SOCK_DGRAM);
if (pp == NULL) {
returnValue = EPFNOSUPPORT;
break;
}
returnValue = unregister_sockfilter(&IPNetMonitor_NKE, pp, 0);
if (!returnValue) {
ipk_initted = 0; // don't allow multiples
returnValue = KERN_SUCCESS;
}
#if IPK_DEBUG
log(LOG_WARNING, "IPNetMonitor_NKE_stop terminate: %d\n", returnValue);
#endif
} while (FALSE);
splx(s);
thread_funnel_set(network_flock, funnel_state);
if (returnValue == 0) printf("IPNetMonitor_NKE has unloaded!\n");
return returnValue;
}
// ---------------------------------------------------------------------------------
// Ñ ik_control
// ---------------------------------------------------------------------------------
// intercept socket options (soset and soget)
static int ik_control(struct socket *so, struct sockopt *sopt, struct kextcb *kp)
{
int returnValue = 0; // continue normall processing (pass it on)
int result;
int controlIndex=0;
int attachIndex=0;
int s;
int funnel_state;
do {
// find our controller instance
controlIndex = ik_controlIndexForSocket(so);
if (!controlIndex) {
// not yet connected, try to connect
returnValue = ipk_connect(so);
if (returnValue != EJUSTRETURN) break;
controlIndex = ik_controlIndexForSocket(so);
if (!controlIndex) {
returnValue = ENOTCONN;
break;
}
}
switch(sopt->sopt_name) {
case SO_ATTACH_LINK:
// grab the network funnel and prevent other network threads from pre-empting us
funnel_state = thread_funnel_set(network_flock, TRUE);
s = splnet();
do {
// attach a controller socket to an IP interface (data link)
struct sopt_attachParam attachParam;
u_long dl_tag;
u_long filterID;
struct ifnet *ifnet_ptr;
// Reference: copyin and copyout option parameters
//int sooptcopyin __P((struct sockopt *sopt, void *buf, size_t len, size_t minlen));
//int sooptcopyout __P((struct sockopt *sopt, void *buf, size_t len));
#if IPK_DEBUG
log(LOG_WARNING, "ik_control: received SO_ATTACH_LINK\n");
#endif
// copyin option parameter
returnValue = sooptcopyin(sopt, &attachParam, sizeof (struct sopt_attachParam),
sizeof (attachParam));
if (returnValue) break;
// get ifnet_ptr for corresponding interface
returnValue = ik_findIFNet(attachParam.bsdName, &ifnet_ptr);
if (returnValue) break;
// get DLIL attachID we're interested in
returnValue = dlil_find_dltag(ifnet_ptr->if_family, ifnet_ptr->if_unit, PF_INET, &dl_tag);
if (returnValue) break;
// look for existing DLIL attach instance
attachIndex = ik_attachIndexForTag(dl_tag);
if (attachIndex) {
// found existing DLIL attach
// associate this controller with DLIL attach
PROJECT_control[controlIndex].attachMap[attachIndex] = 1;
returnValue = EJUSTRETURN;
break;
}
// defensive, 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;
}
// set dlil dispatch cookie to point to this attach instance
IKprotocol_filter.cookie = (caddr_t)&PROJECT_attach[attachIndex];
IKinterface_filter.cookie = (caddr_t)&PROJECT_attach[attachIndex];
// attach our NKE as a protocol filter using dl_tag or
// an interface filter using ifnet_ptr
if (attachParam.protocolFilter) {
returnValue = dlil_attach_protocol_filter(dl_tag, &IKprotocol_filter,
&filterID, DLIL_LAST_FILTER);
if (returnValue) break;
#if IPK_DEBUG
log(LOG_WARNING, "ik_control: protocol filter on %s\n", attachParam.bsdName);
#endif
}
else {
returnValue = dlil_attach_interface_filter(ifnet_ptr, &IKinterface_filter,
&filterID, DLIL_LAST_FILTER);
if (returnValue) break;
#if IPK_DEBUG
log(LOG_WARNING, "ik_control: interface filter on %s\n", attachParam.bsdName);
#endif
}
// initialize attach instance and remember dlil attach info
{
int len;
bzero(&PROJECT_attach[attachIndex], sizeof(attach_t));
PROJECT_attach[attachIndex].attachID = dl_tag;
PROJECT_attach[attachIndex].filterID = filterID;
// 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
log(LOG_WARNING, "ik_control control instance: %d attach instance: %d attach filterID: %d\n",
controlIndex, attachIndex, filterID);
#endif
}
// Everything worked, Yay!
returnValue = EJUSTRETURN;
} while (FALSE);
// restore previous priority level and thread funnel
splx(s);
thread_funnel_set(network_flock, funnel_state);
break;
case SO_DETACH_LINK:
// grab the network funnel and prevent other network threads from pre-empting us
funnel_state = thread_funnel_set(network_flock, TRUE);
s = splnet();
do {
// detach specified interface from controller socket
struct sopt_attachParam attachParam;
u_long dl_tag;
u_long filterID;
struct ifnet *ifnet_ptr;
#if IPK_DEBUG
log(LOG_WARNING, "ik_control: received SO_DETACH_LINK\n");
#endif
// if name is nil, detach controller and any dlil attachments
if (sopt->sopt_valsize == 0) {
#if IPK_DEBUG
log(LOG_WARNING, "ik_control detach all interfaces\n");
#endif
returnValue = ik_detachController(controlIndex);
if (returnValue) break;
returnValue = EJUSTRETURN;
break;
}
// copyin option parameter
returnValue = sooptcopyin(sopt, &attachParam, sizeof(struct sopt_attachParam),
sopt->sopt_valsize);
if (returnValue) break;
// make sure delay table is empty before detaching from any interfaces
//KFT_delayAge(0);
// get ifnet_ptr for corresponding interface
returnValue = ik_findIFNet(attachParam.bsdName, &ifnet_ptr);
if (returnValue) break;
// get DLIL attachID we're interested in
returnValue = dlil_find_dltag(ifnet_ptr->if_family, ifnet_ptr->if_unit, PF_INET, &dl_tag);
if (returnValue) break;
// look for existing DLIL attach instance
attachIndex = ik_attachIndexForTag(dl_tag);
// defensive, look for existing DLIL attach by bsdName
if (!attachIndex) attachIndex = ik_attachIndexForName(attachParam.bsdName);
if (attachIndex) {
// found existing DLIL attach
// dis-associate this controller with DLIL attach
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
filterID = PROJECT_attach[attachIndex].filterID;
returnValue = dlil_detach_filter(filterID);
bzero(&PROJECT_attach[attachIndex], sizeof(attach_t));
#if IPK_DEBUG
log(LOG_WARNING, "ik_control control instance: %d attach instance: %d detach filterID: %d\n",
controlIndex, attachIndex, filterID);
#endif
}
}
else {
returnValue = ENOENT;
break;
}
returnValue = EJUSTRETURN;
} while (FALSE);
// restore previous priority level and thread funnel
splx(s);
thread_funnel_set(network_flock, funnel_state);
break;
case SO_MONITOR_ON:
do {
#if IPK_DEBUG
log(LOG_WARNING, "ik_control: 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) {
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
log(LOG_WARNING, "ik_control: start timer\n");
#endif
ipk_timeout((void *)0);
}
}
returnValue = EJUSTRETURN;
} while (FALSE);
break;
case SO_MONITOR_OFF:
do {
#if IPK_DEBUG
log(LOG_WARNING, "ik_control: received SO_MONITOR_OFF\n");
#endif
PROJECT_control[controlIndex].monitorOn = 0;
// bump timer ref count
//PROJECT_timerRefCount -= 1;
result = OSAddAtomic(-1, (SInt32*)&PROJECT_timerRefCount);
if (result <= 0) PROJECT_timerRefCount = 0; // defensive
// report current connection table
{
// grab the network funnel and prevent other network threads from pre-empting us
funnel_state = thread_funnel_set(network_flock, TRUE);
s = splnet();
//KFT_connectionReport();
// restore previous priority level and thread funnel
splx(s);
thread_funnel_set(network_flock, funnel_state);
}
returnValue = EJUSTRETURN;
} while (FALSE);
break;
default: {
#if IPK_DEBUG
log(LOG_WARNING, "ik_control: default unrecognized sopt->sopt_name=%x pass it on\n", sopt->sopt_name);
#endif
returnValue = 0;
break;
}
} // end switch
} while (FALSE);
#if 0
log(LOG_WARNING, "ik_control returnValue: %d\n", returnValue);
#endif
return returnValue;
}
// ---------------------------------------------------------------------------------
// Ñ ik_connect
// ---------------------------------------------------------------------------------
// intercept soconnect
static int ik_connect(struct socket *so, struct sockaddr *nam)
{
ipk_connect(so);
return 0;
}
// ---------------------------------------------------------------------------------
// Ñ ik_disconnect
// ---------------------------------------------------------------------------------
// intercept sodisconnect
static int ik_disconnect(struct socket *so, register struct kextcb *kp)
{
ipk_disconnect(so);
return 0;
}
// ---------------------------------------------------------------------------------
// Ñ ik_close
// ---------------------------------------------------------------------------------
// intercept soclose
static int ik_close(struct socket *so, register struct kextcb *kp)
{
// socket is being closed:
// disconnect, turn off monitoring, and detach from dlil 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.
ipk_disconnect(so);
return 0;
}
//
// We have a control (PF_NKE) socket expressing interest.
//
// ---------------------------------------------------------------------------------
// Ñ ipk_connect
// ---------------------------------------------------------------------------------
// Register connection between controller socket and our NKE
// Called via PF_NKE, ik_control (set_SO) and also soconnect (standard UDP)
static int ipk_connect(register struct socket *cso)
{
int returnValue = 0;
int controlIndex;
int s;
int funnel_state;
#if IPK_DEBUG
log(LOG_WARNING, "ipk_connect\n");
#endif
// grab the network funnel and prevent other network threads from pre-empting us
funnel_state = thread_funnel_set(network_flock, TRUE);
s = splnet();
do {
// check if this controller socket is already connected
controlIndex = ik_controlIndexForSocket(cso);
if (controlIndex) { // found it
returnValue = EISCONN;
break;
}
// reserve socket buffer space if needed
if (cso->so_snd.sb_hiwat == 0 || cso->so_rcv.sb_hiwat == 0) {
returnValue = soreserve(cso, IPK_sendspace, IPK_recvspace);
if (returnValue) 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);
// restore previous priority level and thread funnel
splx(s);
thread_funnel_set(network_flock, funnel_state);
return returnValue;
}
// ---------------------------------------------------------------------------------
// Ñ ipk_disconnect
// ---------------------------------------------------------------------------------
static void ipk_disconnect(register struct socket *cso)
{
int controlIndex;
int s;
int funnel_state;
#if IPK_DEBUG
log(LOG_WARNING, "ipk_disconnect\n");
#endif
// grab the network funnel and prevent other network threads from pre-empting us
funnel_state = thread_funnel_set(network_flock, TRUE);
s = splnet();
// check if this controller socket is connected and remove it
controlIndex = ik_controlIndexForSocket(cso);
if (controlIndex) { // found it
// remove reference to dlil attach instance and clear control instance
ik_detachController(controlIndex);
}
// restore previous priority level and thread funnel
splx(s);
thread_funnel_set(network_flock, funnel_state);
}
static int ipk_get()
{
return(0);
}
static int ipk_read()
{
return(0);
}
static int ipk_put()
{
return(0);
}
static int ipk_write()
{
return(0);
}
//
// dlil protocol filter functions our NKE will intercept
//
// ---------------------------------------------------------------------------------
// Ñ ipk_filter_dl_input
// ---------------------------------------------------------------------------------
// protocol filter NKE intercept input datagram
static int ipk_filter_dl_input(caddr_t cookie,
struct mbuf **m,
char **frame_header,
struct ifnet **ifp)
{
int returnValue = 0;
struct mbuf *dg; // mbuf chain for datagram
attach_t* myAttach;
// get access to our attach instance
myAttach = (attach_t*)cookie;
// examine the mbuf chain to determine the data length
// for more information on mbufs, see TCP/IP Illustrated Volume 2,
// "The Implementation" by Wright and Stevens
dg = *m; // first mbuf chain
if (dg) {
if (dg->m_flags & M_PKTHDR) {
// count receive traffic
myAttach->receiveCount += dg->m_pkthdr.len;
// dlil filters execute at splnet so we should be safe here
// need to set splnet to access receiveCount from other contexts
// IP filtering
#if 0
if (0) {
KFT_packetData_t packet;
bzero(&packet, sizeof(packet));
packet.ipOffset = 0;
packet.direction = 1;
packet.myAttach = myAttach;
returnValue = KFT_filterPacket(&packet);
}
#endif
}
}
return returnValue;
}
// ---------------------------------------------------------------------------------
// Ñ ipk_filter_dl_output
// ---------------------------------------------------------------------------------
// protocol filter NKE intercept output datagram
static int ipk_filter_dl_output(caddr_t cookie,
struct mbuf **m,
struct ifnet **ifp,
struct sockaddr **dest,
char *dest_linkaddr,
char *frame_type)
{
int returnValue = 0;
struct mbuf *dg; // mbuf chain for datagram
attach_t* myAttach;
// get access to our attach instance
myAttach = (attach_t*)cookie;
// examine the mbuf chain to determine the data length
dg = *m; // first mbuf chain
if (dg) {
if (dg->m_flags & M_PKTHDR) {
// count send traffic
myAttach->sendCount += dg->m_pkthdr.len;
// dlil filters execute at splnet so we should be safe here
// need to set splnet to access sendCount from other contexts
// IP filtering
#if 0
if (0) {
KFT_packetData_t packet;
bzero(&packet, sizeof(packet));
packet.ipOffset = 0;
packet.direction = 0;
packet.myAttach = myAttach;
returnValue = KFT_filterPacket(&packet);
}
#endif
}
}
return returnValue;
}
//
// dlil interface filter functions our NKE will intercept
// Notice dlil will call us for each packet with m->m_nextpkt == NULL
// ---------------------------------------------------------------------------------
// Ñ ipk_filter_if_input
// ---------------------------------------------------------------------------------
// interface filter NKE intercept input datagram
static int ipk_filter_if_input(caddr_t cookie,
struct ifnet **ifnet_ptr,
struct mbuf **mbuf_ptr,
char **frame_ptr)
{
int returnValue = 0;
struct mbuf *m; // mbuf chain for datagram
attach_t* myAttach;
u_char interfaceType = 0;
u_char headerLength = 0;
// get access to our attach instance
myAttach = (attach_t*)cookie;
// hardware info
if (*ifnet_ptr) { // defensive
interfaceType = (*ifnet_ptr)->if_type;
headerLength = (*ifnet_ptr)->if_hdrlen;
}
// examine the mbuf chain to process IP datagrams
// for more information on mbufs, see TCP/IP Illustrated Volume 2,
// "The Implementation" by Wright and Stevens
do {
if ((m = *mbuf_ptr) == NULL) break;
if (m->m_flags & M_PKTHDR) {
// add to receive count
myAttach->receiveCount += m->m_pkthdr.len;
// dlil filters execute at splnet so we should be safe here
// need to set splnet to access receiveCount from other contexts
#if 0
// check frame header for IP
if ((interfaceType == IFT_ETHER) && (headerLength == 14)) {
u_int16_t* dp16;
if (*frame_ptr) { // defensive
dp16 = (u_int16_t*)(*frame_ptr);
if (dp16[6] != 0x0800) {
#if 0
log(LOG_WARNING, "ipk_filter_if_input non IP frame header\n");
#endif
break;
}
}
}
// IP filtering
if (PROJECT_timerRefCount) {
KFT_packetData_t packet;
bzero(&packet, sizeof(packet));
// passed in
packet.ifnet_ptr = ifnet_ptr;
packet.mbuf_ptr = mbuf_ptr;
packet.frame_ptr = frame_ptr;
packet.myAttach = myAttach; // cookie
// interface info
packet.ifType = interfaceType;
packet.ifHeaderLen = headerLength;
// packet info
packet.ipOffset = 0;
packet.direction = 1;
// pass to filter
returnValue = KFT_filterPacket(&packet);
}
#endif
}
} while (FALSE);
return returnValue;
}
// ---------------------------------------------------------------------------------
// Ñ ipk_filter_if_output
// ---------------------------------------------------------------------------------
// interface filter NKE intercept output datagram
static int ipk_filter_if_output(caddr_t cookie,
struct ifnet **ifnet_ptr,
struct mbuf **mbuf_ptr)
{
int returnValue = 0;
struct mbuf *m;
attach_t* myAttach;
u_char interfaceType = 0;
u_char headerLength = 0;
// get access to our attach instance
myAttach = (attach_t*)cookie;
// hardware info
if (*ifnet_ptr) { // defensive
interfaceType = (*ifnet_ptr)->if_type;
headerLength = (*ifnet_ptr)->if_hdrlen;
}
// examine the mbuf chain to process IP datagragms
do {
if ((m = *mbuf_ptr) == NULL) break;
if (m->m_flags & M_PKTHDR) {
// add to receive count
myAttach->sendCount += (m->m_pkthdr.len - headerLength);
// dlil filters execute at splnet so we should be safe here
// need to set splnet to access sendCount from other contexts
#if 0
// check frame header for IP
if ((interfaceType == IFT_ETHER) && (headerLength == 14)) {
u_int16_t* dp16;
dp16 = (u_int16_t*)m->m_data;
if (dp16[6] != 0x0800) {
#if 0
log(LOG_WARNING, "ipk_filter_if_output non IP frame header\n");
#endif
break;
}
}
if (interfaceType == IFT_PPP) {
// fix header to work around PPP bug
if ((m->m_data[headerLength-2] & 0xF0) == 0x40) headerLength -= 2;
}
// IP filtering
if (PROJECT_timerRefCount) {
KFT_packetData_t packet;
bzero(&packet, sizeof(packet));
// passed in
packet.ifnet_ptr = ifnet_ptr;
packet.mbuf_ptr = mbuf_ptr;
packet.frame_ptr = 0;
packet.myAttach = myAttach;
// interface info
packet.ifType = interfaceType;
packet.ifHeaderLen = headerLength;
// packet info
packet.ipOffset = headerLength; // output packets preceeded by frame header
packet.direction = 0;
// pass to filter
returnValue = KFT_filterPacket(&packet);
}
#endif
}
} while (FALSE);
return returnValue;
}
// ---------------------------------------------------------------------------------
// Ñ ipk_timeout
// ---------------------------------------------------------------------------------
// one second timer used for monitoring
// reschedules itself to be called each second when timerRefCount > 0
static void ipk_timeout(void *cookie)
{
extern int hz; // number of clock ticks that occur in one second
int s;
int funnel_state;
// grab the network funnel and prevent other network threads from pre-empting us
funnel_state = thread_funnel_set(network_flock, TRUE);
s = splnet();
ipk_periodical();
// reschedule ourself if anyone needs us
// timeout(void (*func)(), void *cookie, int ticks);
if (PROJECT_timerRefCount > 0) {
PROJECT_timerPending = TRUE;
timeout(ipk_timeout, (void *)0, hz);
}
else {
PROJECT_timerPending = FALSE;
#if IPK_DEBUG
log(LOG_WARNING, "ipk_timeout: stop timer\n");
#endif
}
// restore previous priority level and thread funnel
splx(s);
thread_funnel_set(network_flock, funnel_state);
}
// ---------------------------------------------------------------------------------
// Ñ 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].attachID) {
// 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
void PROJECT_sendMessage(struct socket *ctl, ipk_message_t *message)
{
struct mbuf *m;
int s;
int funnel_state;
// grab the network funnel and prevent other network threads from pre-empting us
funnel_state = thread_funnel_set(network_flock, TRUE);
s = splnet();
do {
char *p;
if (!ctl) break; // defensive
// make sure we have room
if (sbspace(&ctl->so_rcv) < message->length) break;
// get an mbuf to hold message
// just discard if we can't get a buffer (M_NOWAIT)
// since we're at splnet and holding the network funnel
MGETHDR(m, M_NOWAIT, MT_DATA);
if (m == NULL) break;
// get a cluster to handle messages longer than 100 bytes
if (message->length >= 100) {
MCLGET(m, M_NOWAIT);
// check if we got it
if (!(m->m_flags & M_EXT)) {
m_freem(m);
break;
}
}
// align mbuf data to long word boundary
p = m->m_data;
p = (char *)(((int)p+3)&(~0x3));
m->m_data = (caddr_t)p;
// copy message to our mbuf and set data length
bcopy(message, mtod(m, caddr_t), message->length);
m->m_len = message->length;
// append our mbuf to socket receive queue
//sbappend(&ctl->so_rcv, m);
sbappendrecord(&ctl->so_rcv, m);
// wake any process waiting for read on this socket
sorwakeup(ctl);
// the mbuf we allocated should be released when the socket is read
} while (FALSE);
// restore previous priority level and thread funnel
splx(s);
thread_funnel_set(network_flock, funnel_state);
}
// ---------------------------------------------------------------------------------
// Ñ 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(struct socket *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_attachIndexForTag
// ---------------------------------------------------------------------------------
// Find attach instance with corresponding dl_tag if any and pass back it's index.
// Return 0 for not found.
static int ik_attachIndexForTag(u_long dl_tag)
{
int returnValue = 0; // no such entry ENOENT
int i;
for (i=1; i<=kMaxAttach; i++) {
if (PROJECT_attach[i].attachID == dl_tag) {
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].attachID == 0) {
returnValue = i;
break;
}
}
return returnValue;
}
// ---------------------------------------------------------------------------------
// Ñ ik_findIFNet
// ---------------------------------------------------------------------------------
static int ik_findIFNet(char *inName, struct ifnet **ifnet_ptr)
// return 0 if ifnet was found, otherwise unix error
//
// For insertering our NKE below IP as an interface filter, we use the ifnet
// global variable which points to the list of ifnet structures and scan the
// list to find the desired ifnet structure with matching interface name.
{
int returnValue = ENOENT;
extern struct ifnethead ifnet;
struct ifnet *ifp;
int len, unit, i;
short match;
do {
if (!inName) break; // defensive - name is null
len = strlen(inName) - 1;
unit = inName[len] - '0'; // get unit from end of name of the form "en0"
// walk the ifnet list
for (ifp = ifnet.tqh_first; ifp; ifp = ifp->if_link.tqe_next) {
// testing: list interfaces
//log(LOG_WARNING, "ik_findIFNet interface name: %s unit: %d\n",
// ifp->if_name, ifp->if_unit);
// check name length
if (len != strlen(ifp->if_name)) continue;
// compare base name
match = 1;
for (i=0; i<len; i++) {
if (inName[i] != ifp->if_name[i]) {
match = 0;
break;
}
}
if (!match) continue;
// names match, check unit
if (unit != ifp->if_unit) continue;
// we found a match
*ifnet_ptr = ifp;
returnValue = 0;
break;
}
} while (FALSE);
return returnValue;
}
// ---------------------------------------------------------------------------------
// Ñ ik_detachController
// ---------------------------------------------------------------------------------
// detach controller and remove any dlil 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;
u_long filterID;
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
filterID = PROJECT_attach[attachIndex].filterID;
returnValue = dlil_detach_filter(filterID);
bzero(&PROJECT_attach[attachIndex], sizeof(attach_t));
#if IPK_DEBUG
log(LOG_WARNING, "ik_detachController control instance: %d attach instance: %d detach filterID: %d\n",
controlIndex, attachIndex, filterID);
#endif
}
} // end found attach instance
} // end for each attach instance
// clear control instance
bzero(&PROJECT_control[controlIndex], sizeof(control_t));
} while (FALSE);
return returnValue;
}