Retired Document
Important: This sample code may not represent best practices for current development. The project may use deprecated symbols and illustrate technologies and techniques that are no longer recommended.
ARPerations.c
/* |
File: ARPerations.c |
Contains: Standard high-level ARP operations. |
Written by: Quinn "The Eskimo!" |
Copyright: Copyright © 1997-1999 by Apple Computer, Inc., All Rights Reserved. |
You may incorporate this Apple sample source code into your program(s) without |
restriction. This Apple sample source code has been provided "AS IS" and the |
responsibility for its operation is yours. You are not permitted to redistribute |
this Apple sample source code as "Apple sample source code" after having made |
changes. If you're going to re-distribute the source, we require that you make |
it clear in the source that the code was descended from Apple sample source |
code, but that you've made changes. |
Change History (most recent first): |
7/21/1999 Karl Groethe Updated for Metrowerks Codewarror Pro 2.1 |
fixed output problem by changing '\r' to '\n' |
OSStatus ARPGetCacheReport(Handle cacheReport) |
*/ |
// Lots of standard OT constructs. |
#include <OpenTransport.h> |
#include <OpenTptClient.h> |
/////////////////////////////////////////////////////////////////// |
// Pick up the names of the standard OT modules. |
#include <modnames.h> |
/////////////////////////////////////////////////////////////////// |
// Pick up types and constants for I_STR ioctls. |
#include <stropts.h> |
/////////////////////////////////////////////////////////////////// |
// Pick up handle operations for ARPGetCacheReport. |
#include <Memory.h> |
/////////////////////////////////////////////////////////////////// |
// Standard C string operations. |
#include <string.h> |
/////////////////////////////////////////////////////////////////// |
// Constants and types for ARP module messages. |
#include "OTARPModule.h" |
/////////////////////////////////////////////////////////////////// |
// Our own prototypes. |
#include "ARPerations.h" |
/////////////////////////////////////////////////////////////////// |
// The following equates are actually exported by <miioccom.h>, but |
// they are commented out for some reason )-: So instead of including |
// <miioccom.h> we just define them again here. |
#define MIOC_ND 'c' /* ioctl's for Mentat's nd device */ |
// The following equates define the two "Name Dispatch" ioctls |
// for setting and getting OT internal parameters. |
#define ND_GET MIOC_CMD(MIOC_ND, 0) /* Get a value */ |
#define ND_SET MIOC_CMD(MIOC_ND, 1) /* Set a value */ |
// The name of the Name Dispatch variable you have to get in |
// order to get a dump of the ARP cache. |
#define ARP_ND_CACHE_REPORT "arp_cache_report" |
enum { |
kARP_ND_CACHE_REPORT_Length = 17 // strlen(ARP_ND_CACHE_REPORT) + 1 |
}; |
OSStatus ARPGetCacheReport(Handle cacheReport) |
// See comment in header file. |
{ |
OSStatus err; |
StreamRef arpStream; |
struct strioctl nddIOCommand; |
SInt32 i; |
char ndCommandBuffer[kARP_ND_CACHE_REPORT_Length]; |
UInt32 realDataSize; |
SInt8 cacheReportHandleState; |
// First open up a raw STREAMS connection to the ARP module. |
arpStream = OTStreamOpen(MI_ARP_NAME, 0, &err); |
if (err == noErr) { |
// Switch the stream in sync/blocking mode. To make the |
// code easier, we're going to do this synchronously. |
(void) OTStreamSetBlocking(arpStream); |
(void) OTStreamSetSynchronous(arpStream); |
// Copy the name of the ND variable we're trying |
// to get into our buffer. |
OTStrCopy(ndCommandBuffer, ARP_ND_CACHE_REPORT); |
// The ND_GET ioctl returns a value and sets ic_len. A negative |
// value is an error and you can give up now (-: The rule for |
// positive values is a bit weirder. ic_len is always set |
// to the amount of data that is actually returned. If the |
// data available exceeds the available buffer space (as |
// defined by the ic_len on input), the ioctl returns |
// a positive number that is the amount of buffer space |
// needed. So we first call it with a minimal buffer |
// then give it the buffer space it requires. Obviously |
// there's a concurrency race here, which we conveniently |
// ignore in this sample by return kEAGAINErr. |
// First get the size of data buffer we need to allocate by |
// doing the ioctl with a small buffer and examining the result. |
nddIOCommand.ic_cmd = ND_GET; |
nddIOCommand.ic_timout = 0; |
nddIOCommand.ic_len = kARP_ND_CACHE_REPORT_Length; // Length of ND name including... |
nddIOCommand.ic_dp = ndCommandBuffer; // ...the zero terminator. |
err = OTStreamIoctl(arpStream, I_STR, &nddIOCommand); |
if (err >= noErr) { |
if (err > noErr) { |
// The first ioctl returned a positive number telling |
// us how big the data returned was. We turn around |
// around make the ioctl again, this time passing |
// in an appropriately sized buffer. |
realDataSize = err; |
SetHandleSize(cacheReport, realDataSize); |
err = MemError(); |
if (err == noErr) { |
cacheReportHandleState = HGetState(cacheReport); |
HLock(cacheReport); |
OTStrCopy(*cacheReport, ARP_ND_CACHE_REPORT); |
nddIOCommand.ic_cmd = ND_GET; |
nddIOCommand.ic_timout = 0; |
nddIOCommand.ic_len = realDataSize; |
nddIOCommand.ic_dp = *cacheReport; |
err = OTStreamIoctl(arpStream, I_STR, &nddIOCommand); |
HSetState(cacheReport, cacheReportHandleState); |
} |
} |
if (err == noErr) { |
// Everything is cool, we have the ARP report. Now |
// all we have remaining is a trivial format |
// conversion. The format returned is straight |
// text with zero characters as the line terminator. |
// We mutate the line terminators into standard EOL '\n' |
for (i = 0; i < realDataSize; i++) { |
if ( (*cacheReport)[i] == '\0' ) { |
(*cacheReport)[i] = '\n'; |
} |
} |
} else { |
// Whoah, ARP table changed size! |
err = kEAGAINErr; |
} |
} |
} |
// Clean up. |
if (arpStream != nil) { |
(void) OTStreamClose(arpStream); |
} |
return (err); |
} |
/////////////////////////////////////////////////////////////////// |
// The following routines are used to copy variable length data structures |
// in the ARP command block that we're assembling. |
static void CopyIntoCommandBuffer(char *buffer, UInt32 *currentOffset, void *data, UInt32 dataLength) |
// Copy dataLength bytes from data into an ARP command buffer. |
// buffer is a pointer to an ARP command block. |
// currentOffset comes in as the number of bytes that are currently being |
// used in the block, and is adjusted to represent the number of bytes we copied in. |
// data is the address of the bytes to copy in. |
// dataLength is the number of bytes to copy in. |
{ |
OTMemcpy(&buffer[*currentOffset], data, dataLength); |
(*currentOffset) += dataLength; |
} |
static void CopyInterfaceIntoCommandBuffer(char *buffer, UInt32 *currentOffset, char *interfaceName) |
// Copy an interface name into an ARP command buffer. |
// buffer is a pointer to an ARP command block. This is assumed to point |
// to at least an arc_t, although the other ARP command structures all |
// begin with an arc_t, so it works for the other structures as well. |
// currentOffset comes in as the number of bytes that are currently being |
// used in the block, and is adjusted to represent the number of bytes we copied in. |
// interfaceName is the address of a C string. |
{ |
arc_t *arpCommandPtr; |
UInt32 interfaceNameLength; |
interfaceNameLength = OTStrLength(interfaceName) + 1; // copy the string and the final null |
arpCommandPtr = (arc_t *) buffer; |
arpCommandPtr->arc_name_offset = *currentOffset; |
arpCommandPtr->arc_name_length = interfaceNameLength; |
CopyIntoCommandBuffer(buffer, currentOffset, interfaceName, interfaceNameLength); |
} |
static void CopyProtoIntoCommandBuffer(char *buffer, UInt32 *currentOffset, |
void *protoAddress, UInt32 protoSize) |
// Copy an protocol address into an ARP command buffer. |
// buffer is a pointer to an ARP command block. This is assumed to point |
// to at least an arc_t, although the other ARP command structures all |
// begin with an arc_t, so it works for the other structures as well. |
// currentOffset comes in as the number of bytes that are currently being |
// used in the block, and is adjusted to represent the number of bytes we copied in. |
// protoAddress is the address of the bytes of the protocol address to copy in. |
// protoSize is the number of bytes to copy in. |
{ |
arc_t *arpCommandPtr; |
arpCommandPtr = (arc_t *) buffer; |
arpCommandPtr->arc_proto_addr_offset = *currentOffset; |
arpCommandPtr->arc_proto_addr_length = protoSize; |
CopyIntoCommandBuffer(buffer, currentOffset, protoAddress, protoSize); |
} |
/////////////////////////////////////////////////////////////////// |
OSStatus ARPAddEntry(char *interfaceName, |
UInt32 arpProto, |
UInt32 flags, |
void *protoAddress, UInt32 protoSize, void *protoMask, |
void *hardwareAddress, UInt32 hardwareSize) |
// See comment in header file. |
{ |
OSStatus err; |
StreamRef arpStream; |
char arpCommandBuffer[256]; |
area_t *arpAddCommandPtr; |
UInt32 currentOffset; |
struct strioctl arpIOControl; |
// First up, open a simple stream to the ARP as a driver. |
arpStream = OTStreamOpen(MI_ARP_NAME, 0, &err); |
if (err == noErr) { |
(void) OTStreamSetBlocking(arpStream); |
(void) OTStreamSetSynchronous(arpStream); |
// Initialise the command buffer for this command. |
arpAddCommandPtr = (area_t *) &arpCommandBuffer[0]; |
currentOffset = sizeof(area_t); |
arpAddCommandPtr->area_arc.arc_cmd = AR_ENTRY_ADD; |
CopyInterfaceIntoCommandBuffer(arpCommandBuffer, ¤tOffset, interfaceName); |
arpAddCommandPtr->area_arc.arc_proto = arpProto; |
CopyProtoIntoCommandBuffer(arpCommandBuffer, ¤tOffset, protoAddress, protoSize); |
arpAddCommandPtr->area_arc.arc_flags = flags; |
arpAddCommandPtr->area_arc.arc_client_q = nil; // ignored by ARP for this command |
arpAddCommandPtr->area_proto_mask_offset = currentOffset; |
CopyIntoCommandBuffer(arpCommandBuffer, ¤tOffset, protoMask, protoSize); |
arpAddCommandPtr->area_hw_addr_offset = currentOffset; |
arpAddCommandPtr->area_hw_addr_length = hardwareSize; |
CopyIntoCommandBuffer(arpCommandBuffer, ¤tOffset, hardwareAddress, hardwareSize); |
// Initialise the I_STR ioctl structure. |
arpIOControl.ic_cmd = AR_ENTRY_ADD; |
arpIOControl.ic_timout = 0; |
arpIOControl.ic_len = currentOffset; |
arpIOControl.ic_dp = &arpCommandBuffer[0]; |
// Send the ioctl. |
err = OTStreamIoctl(arpStream, I_STR, &arpIOControl); |
} |
// Clean up. |
if (arpStream != nil) { |
(void) OTStreamClose(arpStream); |
} |
return (err); |
} |
/////////////////////////////////////////////////////////////////// |
OSStatus ARPDeleteEntry(char *interfaceName, |
UInt32 arpProto, |
void *protoAddress, UInt32 protoSize) |
// See comment in header file. |
{ |
OSStatus err; |
StreamRef arpStream; |
char arpCommandBuffer[256]; |
arc_t *arpDeleteCommandPtr; |
UInt32 currentOffset; |
struct strioctl arpIOControl; |
// First up, open a simple stream to the ARP as a driver. |
arpStream = OTStreamOpen(MI_ARP_NAME, 0, &err); |
if (err == noErr) { |
(void) OTStreamSetBlocking(arpStream); |
(void) OTStreamSetSynchronous(arpStream); |
// Initialise the command buffer for this command. |
arpDeleteCommandPtr = (arc_t *) &arpCommandBuffer[0]; |
currentOffset = sizeof(arc_t); |
arpDeleteCommandPtr->arc_cmd = AR_ENTRY_DELETE; |
CopyInterfaceIntoCommandBuffer(arpCommandBuffer, ¤tOffset, interfaceName); |
arpDeleteCommandPtr->arc_proto = arpProto; |
CopyProtoIntoCommandBuffer(arpCommandBuffer, ¤tOffset, protoAddress, protoSize); |
arpDeleteCommandPtr->arc_flags = 0; // ignored by ARP for this command |
arpDeleteCommandPtr->arc_client_q = nil; // ignored by ARP for this command |
// Initialise the I_STR ioctl structure. |
arpIOControl.ic_cmd = AR_ENTRY_DELETE; |
arpIOControl.ic_timout = 0; |
arpIOControl.ic_len = currentOffset; |
arpIOControl.ic_dp = &arpCommandBuffer[0]; |
// Send the ioctl. |
err = OTStreamIoctl(arpStream, I_STR, &arpIOControl); |
} |
// Clean up. |
if (arpStream != nil) { |
(void) OTStreamClose(arpStream); |
} |
return (err); |
} |
/////////////////////////////////////////////////////////////////// |
OSStatus ARPCacheQuery(char *interfaceName, |
UInt32 arpProto, |
void *protoAddress, UInt32 protoSize, |
void *hardwareAddress, UInt32 hardwareSize, UInt32 *flags) |
// See comment in header file. |
{ |
OSStatus err; |
StreamRef arpStream; |
char arpCommandBuffer[256]; |
area_t *arpQueryCommandPtr; |
UInt32 currentOffset; |
struct strioctl arpIOControl; |
// First up, open a simple stream to the ARP as a driver. |
arpStream = OTStreamOpen(MI_ARP_NAME, 0, &err); |
if (err == noErr) { |
(void) OTStreamSetBlocking(arpStream); |
(void) OTStreamSetSynchronous(arpStream); |
// Initialise the command buffer for this command. |
arpQueryCommandPtr = (area_t *) &arpCommandBuffer[0]; |
currentOffset = sizeof(area_t); |
arpQueryCommandPtr->area_arc.arc_cmd = AR_ENTRY_SQUERY; |
CopyInterfaceIntoCommandBuffer(arpCommandBuffer, ¤tOffset, interfaceName); |
arpQueryCommandPtr->area_arc.arc_proto = arpProto; |
CopyProtoIntoCommandBuffer(arpCommandBuffer, ¤tOffset, protoAddress, protoSize); |
arpQueryCommandPtr->area_arc.arc_flags = 0; // return value |
arpQueryCommandPtr->area_arc.arc_client_q = nil; // ignored by ARP for this command |
arpQueryCommandPtr->area_proto_mask_offset = 0; // ignored by ARP for this command |
arpQueryCommandPtr->area_hw_addr_offset = currentOffset; |
arpQueryCommandPtr->area_hw_addr_length = hardwareSize; |
currentOffset += hardwareSize; // space for return value |
// Initialise the I_STR ioctl structure. |
arpIOControl.ic_cmd = AR_ENTRY_SQUERY; |
arpIOControl.ic_timout = 0; |
arpIOControl.ic_len = currentOffset; |
arpIOControl.ic_dp = &arpCommandBuffer[0]; |
// Send the ioctl. |
err = OTStreamIoctl(arpStream, I_STR, &arpIOControl); |
} |
if (err == noErr) { |
// Copy results out to client buffer. |
OTMemcpy( hardwareAddress, // dest |
&arpCommandBuffer[arpQueryCommandPtr->area_hw_addr_offset], // source |
hardwareSize); // length |
*flags = arpQueryCommandPtr->area_arc.arc_flags; |
} |
// Clean up. |
if (arpStream != nil) { |
(void) OTStreamClose(arpStream); |
} |
return (err); |
} |
/////////////////////////////////////////////////////////////////// |
// The ARPInterfaceInfo structure is used to record all the information this |
// module stores about an interface that it brings up. When |
// you call ARPInterfaceUp, the interfaceCookie result is really |
// a pointer to one of these structures. |
struct ARPInterfaceInfo { |
StreamRef interfaceStream; // The ARP stream for the interface. |
char interfaceName[256]; // The interface name, as calculated by GetARPInterfaceName. |
}; |
typedef struct ARPInterfaceInfo ARPInterfaceInfo, *ARPInterfaceInfoPtr; |
static OSStatus GetARPInterfaceName(ARPInterfaceInfoPtr interfaceInfo) |
// Get the name for an interface on which we're about to bring up |
// ARP. ARP forms the interface name by taking the name of the module |
// and appending a number, starting at 0, to make it unique. Unfortunately |
// we have no way of determining which interfaces are already in use |
// by ARP, so we can't tell what number it will assign. Therefore |
// this sample just takes a guess, assuming 0. |
// The routine gets the name of the module using the I_LIST ioctl, |
// which returns a list of the module names on a stream, and looking |
// for the last module name in the stream, ie the driver. There |
// really shouldn't be more than one module in the stream, so the |
// array of 10 str_mlist we allocate for the return result should be |
// more than enough. |
{ |
OSStatus err; |
struct str_list streamList; |
struct str_mlist streamListNames[10]; |
streamList.sl_nmods = 10; |
streamList.sl_modlist = streamListNames; |
err = OTStreamIoctl(interfaceInfo->interfaceStream, I_LIST, &streamList); |
if (err >= noErr) { |
if ( streamList.sl_nmods == 0 ) { |
err = -5; |
} else { |
OTStrCopy(interfaceInfo->interfaceName, streamListNames[ streamList.sl_nmods-1 ].l_name ); |
OTStrCat(interfaceInfo->interfaceName, "0"); |
} |
} |
return (err); |
} |
static OSStatus SendARPInterfaceUpDown(ARPInterfaceInfoPtr interfaceInfo, UInt32 arpCommand) |
// Send an AR_INTERFACE_UP or AR_INTERFACE_DOWN command to the |
// ARP interface we just created. The name of the interface |
// and the controlling stream for the interface are contained |
// in the interfaceInfo pointer. arpCommand is the actual |
// command to send, ie either AR_INTERFACE_UP or AR_INTERFACE_DOWN. |
{ |
OSStatus err; |
char arpCommandBuffer[256]; |
arc_t *arpCommandPtr; |
UInt32 currentOffset; |
struct strioctl arpIOControl; |
// Initialise the command buffer for this command. |
arpCommandPtr = (arc_t *) &arpCommandBuffer[0]; |
currentOffset = sizeof(arc_t); |
arpCommandPtr->arc_cmd = arpCommand; |
CopyInterfaceIntoCommandBuffer(arpCommandBuffer, ¤tOffset, interfaceInfo->interfaceName); |
arpCommandPtr->arc_proto = 0; // ignored by ARP for this command |
arpCommandPtr->arc_proto_addr_offset = 0; // ignored by ARP for this command |
arpCommandPtr->arc_proto_addr_length = 0; // ignored by ARP for this command |
arpCommandPtr->arc_flags = 0; // ignored by ARP for this command |
arpCommandPtr->arc_client_q = nil; // ignored by ARP for this command |
// Initialise the I_STR ioctl structure. |
arpIOControl.ic_cmd = arpCommand; |
arpIOControl.ic_timout = 0; |
arpIOControl.ic_len = currentOffset; |
arpIOControl.ic_dp = &arpCommandBuffer[0]; |
// Send the ioctl. |
err = OTStreamIoctl(interfaceInfo->interfaceStream, I_STR, &arpIOControl); |
return (err); |
} |
OSStatus ARPInterfaceUp(char *configString, UInt32 *interfaceCookie) |
// See comment in header file. |
{ |
OSStatus err; |
ARPInterfaceInfoPtr interfaceInfo; |
OTConfiguration *arpConfig; |
// Prepare for failure. |
arpConfig = nil; |
// Create a record which we use to hold all the information |
// about this interface. The address of this record is passed |
// back to the client as the interfaceCookie. |
err = noErr; |
interfaceInfo = (ARPInterfaceInfoPtr) OTAllocMem(sizeof(ARPInterfaceInfo)); |
if ( interfaceInfo == nil) { |
err = kENOMEMErr; |
} else { |
OTMemzero(interfaceInfo, sizeof(ARPInterfaceInfo)); |
} |
// Now create a configuration from the supplied configString... |
if (err == noErr) { |
arpConfig = OTCreateConfiguration(configString); |
if (arpConfig == kOTNoMemoryConfigurationPtr) { |
err = kENOMEMErr; |
} else if (arpConfig == kOTInvalidConfigurationPtr) { |
err = kENXIOErr; |
} |
} |
// ... and push the ARP module on top of this configuration. |
// Don't ask me why you have to do this or I'll start to |
// whimper. |
if (err == noErr) { |
(void) OTCfigPushParent(arpConfig, MI_ARPM_NAME, &err); |
} |
// Now create a stream to device driver over which we'll |
// be running ARP. Note the use OTCreateStream rather than |
// OTStreamOpen. This is because the device driver might |
// have extra plumbing underneath it (eg an ATM LANE emulation |
// driver running over an ATM hardware driver), and just |
// opening the driver would not give the driver's configurator |
// chance to set up this plumbing. |
if (err == noErr) { |
interfaceInfo->interfaceStream = OTCreateStream(arpConfig, 0, &err); |
if (err != noErr) { |
// Most OT create/open routines seem to return nil if they fail |
// with an error, however it seems that OTCreateSteam is not |
// as friendly. So if we get an error we nil out our record |
// of the stream to avoid trying to OTStreamClose a bogus stream |
// as we clean up. |
interfaceInfo->interfaceStream = nil; |
} |
arpConfig = nil; |
} |
// Get the name of the interface that ARP will create when |
// we start running it over our newly created stream. |
if (err == noErr) { |
(void) OTStreamSetBlocking(interfaceInfo->interfaceStream); |
(void) OTStreamSetSynchronous(interfaceInfo->interfaceStream); |
err = GetARPInterfaceName(interfaceInfo); |
} |
// Push ARP on top of the driver stream and then tell it that |
// the corresponding interface is up. ARP will then consider |
// the interface to be active. It will snarf ARP responses |
// as they go across the network, and you can use the |
// cache query, add and remove commands on that interface. |
if (err == noErr) { |
err = OTStreamIoctl(interfaceInfo->interfaceStream, I_PUSH, MI_ARPM_NAME); |
} |
if (err == noErr) { |
err = SendARPInterfaceUpDown(interfaceInfo, AR_INTERFACE_UP); |
} |
// Clean up after failure. |
if (err != noErr) { |
if (interfaceInfo != nil) { |
if ( interfaceInfo->interfaceStream != nil ) { |
OTStreamClose( interfaceInfo->interfaceStream ); |
} |
OTFreeMem(interfaceInfo); |
interfaceInfo = nil; |
} |
} |
// General clean up. |
if (arpConfig != nil) { |
OTDestroyConfiguration(arpConfig); |
} |
// Regardless of error result, copy interfaceInfo out to client buffer. |
// This ensures that the client gets a nil cookie when we get an error. |
*interfaceCookie = (UInt32) interfaceInfo; |
return (err); |
} |
/////////////////////////////////////////////////////////////////// |
OSStatus ARPInterfaceDown(UInt32 interfaceCookie) |
// See comment in header file. |
{ |
OSStatus err; |
ARPInterfaceInfoPtr interfaceInfo; |
interfaceInfo = (ARPInterfaceInfoPtr) interfaceCookie; |
// First tell ARP we're about to bring the interface down. |
err = SendARPInterfaceUpDown(interfaceInfo, AR_INTERFACE_DOWN); |
// Now close the stream that we opened to run the interface |
// over. |
if (err == noErr) { |
(void) OTStreamClose( interfaceInfo->interfaceStream ); |
OTFreeMem(interfaceInfo); |
} |
return (err); |
} |
Copyright © 2003 Apple Computer, Inc. All Rights Reserved. Terms of Use | Privacy Policy | Updated: 2003-07-22