分类: WINDOWS
2005-10-17 15:27:32
附录A DLC具体实例
/*
模块名:
SendArp.c
编译时需要的库模块:
dlcapi.lib ( 调用了AcsLan()函数,该库只有此一个函数,其他均为结构 )
ws2_32.lib ( inet_addr(), inet_ntoa(), htons() )
作者:
Gary Yukish (garyy) 15-July-98
摘要:
此程序分析了怎样运用NT的DLC API在一个以太网站来发送和接收ARP查询,得到给定IP地址的MAC地址
乙太网帧和ARP包的格式在W.Richard Stevens先生的书中("TCP/IP详解,1:协议")21-23页,56-57页
给定本地硬件地址,IP地址和一个目的IP地址,他将广播一个需求,目的IP地址将发给他硬件地址
直到收到ARP回答,然后从网络适配器读取帧
此程序使用AcsLan()函数API()来发送DLC命令到DLC协议,基本思想是用此函数建立一CCB块,
包含了你要用的LLC命令,以及该命令要用到的参数及数据缓冲.例如用命令LLC_DIR_OPEN_DIRECT
必须提供一个LLC_DIR_OPEN_DIRECT_PARMS的结构指针.
发出一个DLC命令的标准事例:
1) 用ZeroMemory把LLC_CCB结构清零(初始化).
2) 设置uchDlcCommand(CCB的成员)为你想调用的命令.
3) 用ZeroMemory来清零你将要使用的缓冲.
4) 填充你将传递的CCB结构和其他结构.
5) 设置CCB的hCompletionEvent成员为你预先建立的事件句柄.
6) 用ResetEvent重置hCompletionEvent.
7) 发出AcsLan API调用并传递CCB结构
8) 用WaitForSingleObject函数等待事件.
9) 检查成功以后的返回结果, 或时间溢出,或错误.
注意:你必须安装DLC协议应用程序才能正常工作.
========================================
各命令使用的顺序如下:
1) LLC_DIR_INITIALIZE (初始化网卡)
2) LLC_DIR_OPEN_ADAPTER (打开网卡)
3) LLC_DIR_OPEN_DIRECT (打开指示站点)
4) LLC_BUFFER_CREATE (建立缓冲池,记住是"缓冲池")
5) LLC_BUFFER_GET (Returns the requested buffers.
You can retrieve several buffers
of the same size or an optimal(最佳) set of
buffers for the specified frame size.)
6) LLC_BUFFER_FREE (Returns one or more buffers to the adapter pool. )
7) LLC_RECEIVE (接收数据)
8) LLC_TRANSMIT_DIR_FRAME(传送一帧数据)
LOOP (循环至LLC_READ失败或一个好的数据收到)
9) LLC_READ (读数据)
10) LLC_BUFFER_FREE (给一个缓冲)
UNTIL LLC_READ fails or good data received
11) LLC_RECEIVE_CANCEL (停止读数据)
12) LLC_DIR_CLOSE_DIRECT (关闭指向站点)
13) LLC_DIR_CLOSE_ADAPTER (关闭适配器)
数据帧说明:
MAC Frames:The application provides the complete LAN header and the data field.
The source address in the LAN header is overwritten with the address used by
the adapter.(come from MSDN_NET_DLC)
应用程序提供完整的LAN头和数据区,在LAN头中源地址由本适配器的地址覆盖
----------------------------------------------------------------------------------
I-Frames:The application provides only the data. The information used to build
the headers is provided by the LLC_DLC_OPEN_STATION and LLC_DLC_CONNECT_STATION
commands. (come from MSDN_NET_DLC)
应用程序只提供数据,由LLC_DLC_OPEN_STATION和LLC_DLC_CONNECT_STATION命令
提供建立头
Direct Frames:The application provides the LAN header and the data in its own
buffers. It provides the information to build the headers with one of the
LLC_TRANSMIT commands. The command overwrites the source address in the LAN header
with the address used by the adapter.(come from MSDN_NET_DLC)
应用程序在自己的缓冲区中提供LAN头和数据,用LLC_TRANSMIT命令来建立信息
*/
#include
#include
#include
#define STATION_ID 0
#define ADAPTER_ID 0
//结构必须要2字节对齐
#pragma pack(push, 2)
struct ethernet_hdr
{
char dest[6]; // ETHERNET网要求的包头
char src[6]; // (14 字节)
WORD type; // 由适配器填充
} *PETHER;
//以下为发送的ARP包
typedef struct arp_packet
{
struct ethernet_hdr;
WORD hardType; //硬件地址类型: 1 为 Ethernet
WORD protType; //协议类型: 0x0800 为 IP, 0x0806 为 ARP
BYTE hardSize; //硬件地址大小: 6 为 Ethernet大小
BYTE protSize; //协议地址大小: 4 为 IP 的大小
WORD opCode; //指令: 1=ARP 请求, 2=ARP 应答, 3=RARP 请求, 4=RARP 应答
BYTE srcAddr[6]; //源硬件地址
ULONG srcIP; //源IP地址
BYTE destAddr[6]; //目的硬件地址
ULONG destIP; //目的IP地址
char padding[18]; // 空数据,为了添满最小包
} ARP_PACKET, *PARP_PACKET;
#pragma pack(pop)
//函数声明
//此函数用于把硬件地址从网络字节顺序改为主机字节顺序
void twiddle_bits(LPBYTE, DWORD );
//填充ARP数据包
void FillArpPacket(PARP_PACKET a, LPSTR srcAddr, LPSTR srcIPAddress, LPSTR ipAddr);
void PrintNext(LPBYTE *t, int c);
#define RECEIVE_COMPLETE_FLAG 0x80204030
#define RECEIVE_DATA_FLAG 0x80204040 //唯一标志
#define BUFFSIZE 2000
char BigBuff[20000]; //建立一缓冲来接受从适配器的信息(缓冲池)
void main (int argc, char *argv[])
{
LLC_CCB CCB, *pBadCCB; //定义在执行CHAIN链接命令时出错的第一个CCB块
char headerBuff[255]; //头缓冲
PLLC_XMIT_BUFFER pxmitBuff=(PLLC_XMIT_BUFFER) headerBuff;//在buffer_free,buffer_get,及transmit用到
ACSLAN_STATUS rtrn; //调用ACSLAN函数后的返回值
HANDLE hEvent; //通知事件句柄
LLC_DIR_INITIALIZE_PARMS InitParms; //初始化适配器的参数结构
LLC_DIR_OPEN_ADAPTER_PARMS OpenParms; //打开适配器的参数结构
LLC_DIR_OPEN_DIRECT_PARMS OpenDirectParms; //打开指向站点参数数据结构
LLC_DLC_PARMS DlcParms; //打开适配器参数结构的字段中的pDlcParms
LLC_ADAPTER_OPEN_PARMS AdapterOpenParms; //打开适配器参数结构
LLC_EXTENDED_ADAPTER_PARMS ExtendedAdapterParms;//打开适配器扩展参数结构(要用到乙太网类型)
LLC_TRANSMIT_PARMS TransmitParms;//发送乙太帧的数据结构
LLC_RECEIVE_PARMS ReceiveParms; //接受帧的数据结构
LLC_READ_PARMS ReadParms; //读乙太帧的数据结构
LLC_BUFFER_CREATE_PARMS BufferCreateParms; //建立一个缓冲来接收被指向指定站点的帧,调用后得到缓冲池句柄
LLC_BUFFER_GET_PARMS BufferGetParms; //得到所需求的缓冲
LLC_BUFFER_FREE_PARMS BufferFreeParms;//得到该适配器的缓冲
UCHAR AppID;//应用程序ID,WIN95/98用
HANDLE BufferHandle;//缓冲句柄
LPBYTE BufferPool = (LPBYTE)BigBuff;
DWORD readStatus;
BOOL Done = FALSE;
UCHAR *pStat = &CCB.uchDlcStatus;
char verbose = 0; //命令行参数
ARP_PACKET a; //定义一个ARP包,ARP_PACKET结构前面已经定义
if(argv[argc-1][1]=='v')
verbose=argc--;
if(argc<3)
{
printf("用法: Sendarp 远程IP地址 [/v]
"
"例子: Sendarp 210.35.4.67
"
"
/v 显示接收到的帧.
");
return;
}
hEvent = CreateEvent(NULL, FALSE, FALSE, NULL);//建立通知事件
// 初始化 DLC
ZeroMemory(&CCB, sizeof(CCB));
CCB.uchDlcCommand = LLC_DIR_INITIALIZE;//命令=初始化
ZeroMemory(&InitParms, sizeof(InitParms));//初始化命令参数结构
CCB.u.pParameterTable = (PLLC_PARMS)&InitParms;//把参数表的地址放入CCB块的U结构的pParameterTable中
rtrn = AcsLan(&CCB, &pBadCCB);//调用AcsLan函数
printf("%-40s 返回值 %u 状态: 0x%X
","LLC_DIR_INITIALIZE(适配器初始化)",rtrn,*pStat);
//*pStat为&CCB.uchDlcStatus
if(*pStat==0)
{
// 打开适配器
ZeroMemory(&CCB, sizeof(CCB));
CCB.uchDlcCommand = LLC_DIR_OPEN_ADAPTER; //命令为打开适配器,
ZeroMemory(&OpenParms, sizeof(OpenParms));
CCB.u.pParameterTable = (PLLC_PARMS)&OpenParms;//指定参数表为打开适配器参数表
ZeroMemory(&AdapterOpenParms, sizeof(AdapterOpenParms));
OpenParms.pAdapterParms = &AdapterOpenParms;//参数表的适配器参数字段指向适配器参数结构
ZeroMemory(&ExtendedAdapterParms, sizeof(ExtendedAdapterParms));
ExtendedAdapterParms.LlcEthernetType=LLC_ETHERNET_TYPE_DEFAULT;
OpenParms.pExtendedParms = &ExtendedAdapterParms;
ZeroMemory(&DlcParms, sizeof(DlcParms));
OpenParms.pDlcParms = &DlcParms;
CCB.hCompletionEvent = hEvent; //事件句柄
ResetEvent(hEvent);
rtrn = AcsLan(&CCB, &pBadCCB);
WaitForSingleObject(hEvent, 5000);
//主机方式到网络方式(即IBM Token ring---Ethernet MAC address)
twiddle_bits(AdapterOpenParms.auchNodeAddress, 6); //调用函数后得到硬件地址并转换成主机顺序
printf("%-40s 返回值 %u 状态: 0x%X
", "LLC_DIR_OPEN_ADAPTER(打开适配器)", rtrn, *pStat);
AppID = CCB.uchReserved2; //函数调用后返回应用程序的ID号(95/98有用),以后调用函数时要用到
if(*pStat==0)
{
//LLC_BUFFER_CREATE - 建立一个缓冲来接收被指向指定站点的帧
ResetEvent(hEvent);//清除事件
ZeroMemory(&CCB, sizeof(CCB));//清除CCB块
CCB.uchDlcCommand = LLC_BUFFER_CREATE;
ZeroMemory(&BufferCreateParms, sizeof(BufferCreateParms));
ZeroMemory(&BigBuff, sizeof(BigBuff));//BigBuff有20000字节
BufferCreateParms.pBuffer = BigBuff; //填充LLC_BUFFER_CREATE_PARMS结构
BufferCreateParms.cbBufferSize = sizeof(BigBuff);
BufferCreateParms.cbMinimumSizeThreshold=sizeof(BigBuff) / 4;//最小极限
CCB.u.pParameterTable = (PLLC_PARMS)&BufferCreateParms;
CCB.hCompletionEvent = hEvent;
CCB.uchReserved2 = AppID;//95/98用
rtrn = AcsLan(&CCB, &pBadCCB);
if(WaitForSingleObject(hEvent, 5000) == WAIT_TIMEOUT)
printf("Buffer.Create Timed out.
");
BufferHandle = BufferCreateParms.hBufferPool; //调用后得到缓冲池句柄
printf("%-40s 返回值 %u 状态: 0x%X
", "LLC_BUFFER_CREATE(建立缓冲池)", rtrn, *pStat);
if(*pStat==0)
{
//LLC_BUFFER_GET - 得到被请求的缓冲池
//Returns the requested buffers. You can retrieve
//several buffers of the same size or an optimal
//set of buffers for the specified frame size.
//DESCRIBED BY XIE XIAO RONG
ResetEvent(hEvent);
ZeroMemory(&CCB, sizeof(CCB));
ZeroMemory(&BufferGetParms, sizeof(BufferGetParms));
//The number of free buffer segments in the pool after the command completes. described by xie xiao rong
//在命令完成后,在缓冲池中空闲的缓冲段数在结构的cBuffersLeft中
//The number of buffers to get from the pool即打算从缓冲池中得到的缓冲段数
BufferGetParms.cBuffersToGet = 1;
//The size of the requested buffers. The system rounds this number to
// the next largest segment size (256, 512, 1024, 2048, or 4096). described by xiexiaorong
//请求缓冲的大小
BufferGetParms.cbBufferSize = 256;
CCB.uchDlcCommand = LLC_BUFFER_GET;
CCB.u.pParameterTable = (PLLC_PARMS)&BufferGetParms;
CCB.hCompletionEvent = hEvent;
CCB.uchReserved2 = AppID;
rtrn = AcsLan(&CCB, NULL);
if(WaitForSingleObject(hEvent, 5000) == WAIT_TIMEOUT)
printf("LLC_BUFFER_GET Timed out.
");
printf("%-40s 返回值 %u 状态: 0x%X
", "LLC_BUFFER_GET(得到缓冲段数)", rtrn, *pStat);
if(*pStat==0)
{
//LLC_BUFFER_FREE - Free(释放) the entire(全部) buffer you grabbed earlier(早先夺取的)
//Returns one or more buffers to the adapter pool.
//The buffers must have been obtained(获得) with the LLC_BUFFER_GET command
//DESCRIBED BY XIE XIAO RONG
ResetEvent(hEvent);
ZeroMemory(&CCB, sizeof(CCB));
ZeroMemory(&BufferFreeParms, sizeof(BufferFreeParms));
//在执行GET命令完成时,BufferGetParms.pFirstBuffer指向得到的第一个缓冲
BufferFreeParms.pFirstBuffer = BufferGetParms.pFirstBuffer;
CCB.uchDlcCommand = LLC_BUFFER_FREE;
CCB.u.pParameterTable = (PLLC_PARMS)&BufferFreeParms;
CCB.hCompletionEvent = hEvent;
CCB.uchReserved2 = AppID;
rtrn = AcsLan(&CCB, NULL);
if(WaitForSingleObject(hEvent, 5000) == WAIT_TIMEOUT)
printf("LLC_BUFFER_FREE Timed out.
");
printf("%-40s 返回值 %u 状态: 0x%X
", "LLC_BUFFER_FREE(指向得到的第一个缓冲)", rtrn, *pStat);
if(*pStat==0)
{
// Open the Direct Station(指向站点)
ZeroMemory(&CCB, sizeof(CCB));
CCB.uchDlcCommand = LLC_DIR_OPEN_DIRECT;
ZeroMemory(&OpenDirectParms, sizeof(OpenDirectParms));
CCB.uchReserved2 = AppID;
CCB.u.dlc.usStationId=STATION_ID;
/************************************************************************************/
//One of the most important parts of getting this to work is getting the
//correct Protocol Type and offset established.
//EFFECT ON RECEIVING FRAMES:
//=========================
//The Receive command is going to use these values to decide what frames to pick up(获得).
//For an ARP Packet the protocol type is loaded 2 bytes into the frame itself
//Whenever LLC_DIR_OPEN_DIRECT sees anything other than(除了) the following values:
// LLC_ETHERNET_TYPE_DEFAULT, // use the parameter value set in registry注册表中的
// LLC_ETHERNET_TYPE_AUTO, // automatic header type selction for links
// LLC_ETHERNET_TYPE_802_3, // use always 802.3 lan headers
// LLC_ETHERNET_TYPE_DIX // use always LLC on DIX SNA type.
//it will compare比较 the 2 bytes in the protocol frame offset by the value of
//OpenDirectParms.usProtocolTypeOffset (in this case 2 bytes), AND them together
//and compare the result with OpenDirectParms.ulProtocolTypeMatch.
//FROM THE PLATFORM SDK(平台SDK中的描述):
//The ulProtoTypeMask, ulProtocolTypeMatch, and usProtocolTypeOffset members
//are optional(可选择的).They specify the protocol type mask, match, and
//offset,respectively.They receive frames for a particular subprotocol type or
//socket.That is,the packet is received whenever the following statement is true:
//(* (PULONG)((PUCHAR)pFrame + 14 + offset) & mask) == match
//EFFECT作用 ON SENDING FRAMES:
//========================
//It also uses the OpenDirectParms.usEthernetType to determine how to setup the frame
//to be transmitted.
//In an Ethernet Frame bytes 13 & 14 are the type of frame that is sent, the same bytes
//are used to show the length of an 802.3 frame. No Ethernet types are valid lengths for
//an 802.3 frame. The AcsLan API looks at the proto type located in the ethernet frame offset
//by the value of usProtocolTypeOffset value. If this is a valid frame type then it continues
//otherwise it assumes it is a length value and you'll probably end up transmitting an
//unknown frame type.
OpenDirectParms.usEthernetType=0x0806; //SUB Protocol Type: ARP;子协议类型为ARP
OpenDirectParms.ulProtocolTypeMask=0x0000;
OpenDirectParms.ulProtocolTypeMatch=0x0000;
OpenDirectParms.usProtocolTypeOffset=2;//偏移为2,即6源地址,6目的地址后的2个字节
/************************************************************************************/
CCB.u.pParameterTable = (PLLC_PARMS)&OpenDirectParms;
CCB.hCompletionEvent = hEvent;
ResetEvent(hEvent);
rtrn = AcsLan(&CCB, &pBadCCB);
WaitForSingleObject(hEvent, 5000);
printf("%-40s 返回值 %u 状态: 0x%X
", "LLC_DIR_OPEN_DIRECT(打开指向站点)", rtrn, *pStat);
if(*pStat==0)
{
//LLC_RECEIVE - 设置接收并开始帧听适配器上的数据
ResetEvent(hEvent);
ZeroMemory(&CCB, sizeof(CCB));
ZeroMemory(&ReceiveParms, sizeof(ReceiveParms));
ReceiveParms.usStationId=STATION_ID;//站点标识SAP or link station identifier.
ReceiveParms.uchOptions = LLC_CONTIGUOUS_DATA;//接收帧的类型Only used with non-MAC frames.
ReceiveParms.ulReceiveFlag = RECEIVE_DATA_FLAG;//唯一标识
//The frames are not chained. 该帧没有被链接,必须用LLC_READ命令逐个的去读
//Use an LLC_READ command to retrieve each frame.
// discribed by xie xiao rong at MSDN
ReceiveParms.uchRcvReadOption = LLC_RCV_READ_INDIVIDUAL_FRAMES;
ReceiveParms.pFirstBuffer = BufferCreateParms.hBufferPool;
CCB.uchAdapterNumber = ADAPTER_ID;
CCB.uchDlcCommand = LLC_RECEIVE;
CCB.ulCompletionFlag = RECEIVE_COMPLETE_FLAG;
CCB.u.pParameterTable = (PLLC_PARMS)&ReceiveParms;
CCB.hCompletionEvent = hEvent;
CCB.uchReserved2 = AppID;
rtrn = AcsLan(&CCB, &pBadCCB);
printf("%-40s 返回值 %u 状态: 0x%X
", "LLC_RECEIVE(接收数据)", rtrn, *pStat);
//*pStat为&CCB.uchDlcStatus,0xFF代表LLC_STATUS_PENDING未决状态
if(*pStat==0xFF)//在未决状态时发送一ARP包,在有数据包回应时该状态必为0.这时候用读命令
{
//LLC_TRANSMIT_DIR_FRAME - 广播ARP包当作Direct Frame
//填充一个ARP包
FillArpPacket(&a, AdapterOpenParms.auchNodeAddress, argv[1], argv[2]);
ResetEvent(hEvent);
ZeroMemory(&CCB, sizeof(CCB));
CCB.uchDlcCommand = LLC_TRANSMIT_DIR_FRAME;//命令为发送一个包
CCB.ulCompletionFlag = 0;
ZeroMemory(&headerBuff, sizeof(headerBuff));
pxmitBuff->cbBuffer=14;
//Optional header and transmitted data,auchData[];
CopyMemory(&pxmitBuff->auchData[0],&a,14);
ZeroMemory(&TransmitParms, sizeof(TransmitParms));
TransmitParms.pXmitQueue1=pxmitBuff;
//Specifies the station sending the data,指定发送的站点
TransmitParms.usStationId = STATION_ID;
TransmitParms.cbBuffer1 = sizeof(a)-14;//指示发送的数据长度
TransmitParms.cbBuffer2 = 0;
TransmitParms.pBuffer1 = (LPBYTE)&a.hardType;//指示发送的偏移量
TransmitParms.pBuffer2 = NULL;
TransmitParms.uchXmitReadOption = LLC_COMPLETE_SINGLE_XMIT_FRAME;//发送帧的类型
CCB.u.pParameterTable = (PLLC_PARMS)&TransmitParms;
CCB.hCompletionEvent = hEvent;
CCB.uchReserved2 = AppID;
rtrn = AcsLan(&CCB, &pBadCCB);
if(WaitForSingleObject(hEvent, 5000) == WAIT_TIMEOUT)
printf("LLC_TRANSMIT_DIR_FRAME Timed out.
");
printf("%-40s 返回值 %u 状态: 0x%X
", "LLC_TRANSMIT_DIR_FRAME(发送帧)", rtrn, *pStat);
if(*pStat==0)
{
//LLC_READ - Check to see if we received any data until we read the data
// we want or tried to read 50 (an arbitrary value) times.
int d=50;
readStatus = 0;
while((readStatus == 0) && !Done) //readStatus为0则无错误
{
printf("
=====================
");
//发出一个读指令
ResetEvent(hEvent);
ZeroMemory(&CCB, sizeof(CCB));
ZeroMemory(&ReadParms, sizeof(ReadParms));
ReadParms.usStationId=STATION_ID;
ReadParms.uchOptionIndicator = 0;
ReadParms.uchEventSet = LLC_EVENT_RECEIVE_DATA;
CCB.uchDlcCommand = LLC_READ;
CCB.ulCompletionFlag = RECEIVE_COMPLETE_FLAG;
CCB.u.pParameterTable = (PLLC_PARMS)&ReadParms;
CCB.hCompletionEvent = hEvent;
CCB.uchReserved2 = AppID;
rtrn = AcsLan(&CCB, &pBadCCB);
if(WaitForSingleObject(hEvent, 5000) != WAIT_OBJECT_0)
{
printf("读时间溢出或其他错误出现.
");
readStatus = *pStat;//在*pStat中的直肯定不为0,则结束循环
}
else
{
printf("%-40s 返回值 %u 状态: 0x%X
", "LLC_READ(读到一个帧) ", rtrn, *pStat);
printf("第%d次读到的数据
",51-d);
readStatus = *pStat;
// 组织LLC_READ的结果
// 过滤所有RECEIVE_DATA事件.
if(ReadParms.uchEvent == LLC_EVENT_RECEIVE_DATA)//uchEvent为收到的事件
{
PLLC_BUFFER pFrame = ReadParms.Type.Event.pReceivedFrame;
PARP_PACKET pArp = (PARP_PACKET) ((LPBYTE) pFrame + pFrame->Buffer.Header.offUserData);
DWORD i;
LPBYTE t = (LPBYTE) pFrame + pFrame->Buffer.Header.offUserData;
if(verbose)
{
PrintNext(&t,12);
printf("
|发送方的网卡地址 | 接收方网卡地址 |");
printf("
");
PrintNext(&t,8);
printf(" (网络字节顺序)
|类 型|硬 件|");
printf("
");
PrintNext(&t,2);
printf("
|指令代码|");
printf("
");
PrintNext(&t,6);
printf("
|接收方网卡地址|");
printf("
");
PrintNext(&t,4);
printf("
|接收方IP地址");
printf("
");
PrintNext(&t,6);
printf("
|发送方的网卡地址|");
printf("
");
PrintNext(&t,4);
printf("
|发送方的IP地址");
printf("
");
PrintNext(&t,8);
printf("
");
i=(LPBYTE) pFrame + pFrame->Buffer.Header.offUserData - t;
PrintNext(&t,(LPBYTE) pFrame + pFrame->Buffer.Header.offUserData - t);
}
//比较读到的帧的目的地址,如果和我们提交帧的MAC地址相同,那么此帧是给我们
//的
if(memcmp(pArp->dest,AdapterOpenParms.auchNodeAddress,6) != 0)
{
struct in_addr x;
x.s_addr = pArp->srcIP;
printf("得到数据!
IP 地址为: %s 网卡地址为: ", inet_ntoa(x));
for(i=0;i<5;i++)
printf("%02X-",pArp->srcAddr[i]);
printf("%02X
",pArp->srcAddr[i]);
Done = TRUE;
}
else
{ //data was bound for another MAC address
printf("数据发往: ");
for(i=0;i<5;i++)
printf("%02X-",(BYTE)pArp->dest[i]);
printf("%02X
",(BYTE)pArp->dest[i]);
if(d-- == 0)
Done = TRUE;
}
//LLC_BUFFER_FREE - You must free every buffer that comes
//in, even if it wasn't to your MAC Address, direct station,
//or SAP
ResetEvent(hEvent);
ZeroMemory(&CCB, sizeof(CCB));
ZeroMemory(&BufferFreeParms, sizeof(BufferFreeParms));
BufferFreeParms.pFirstBuffer = (PLLC_XMIT_BUFFER)ReadParms.Type.Event.pReceivedFrame;
CCB.uchDlcCommand = LLC_BUFFER_FREE;
CCB.u.pParameterTable = (PLLC_PARMS)&BufferFreeParms;
CCB.hCompletionEvent = hEvent;
rtrn = AcsLan(&CCB, NULL);
if(WaitForSingleObject(hEvent, 5000) == WAIT_TIMEOUT)
printf("LLC_BUFFER_FREE 时间溢出.
");
printf("%-40s 返回值 %u 状态: 0x%X
", "LLC_BUFFER_FREE", rtrn, *pStat);
printf("可用的缓存数: %u
",BufferFreeParms.cBuffersLeft);
}
else
//Show the type of event you received this should
//never get called since we asked only to get
//LLC_EVENT_RECEIVE_DATA events when we did the LLC_READ
printf("%d
",ReadParms.uchEvent);
}
}
}
printf("
=====================
");
ZeroMemory(&CCB, sizeof(CCB));
ResetEvent(hEvent);
//LLC_RECEIVE_CANCEL
//Release the request to RECEIVE frames on this station
//This isn't really necessary in this APP since the app
//is going away. But normally you should do this anyway.
CCB.uchAdapterNumber = ADAPTER_ID;
CCB.uchDlcCommand = LLC_RECEIVE_CANCEL;
CCB.u.pParameterTable = (PLLC_PARMS)&ReceiveParms;
CCB.hCompletionEvent = hEvent;
CCB.uchReserved2 = AppID;
rtrn = AcsLan(&CCB, &pBadCCB);
printf("%-40s 返回值 %u 状态: 0x%X
", "LLC_RECEIVE_CANCEL", rtrn, *pStat);
}
// Close the Direct Station
ZeroMemory(&CCB, sizeof(CCB));
CCB.uchDlcCommand = LLC_DIR_CLOSE_DIRECT;
CCB.uchReserved2 = AppID;
CCB.hCompletionEvent = hEvent;
ResetEvent(hEvent);
rtrn = AcsLan(&CCB, &pBadCCB);
WaitForSingleObject(hEvent, 5000);
printf("%-40s 返回值 %u 状态: 0x%X
", "LLC_DIR_CLOSE_DIRECT", rtrn, *pStat);
}
}
}
}
// Close the Adapter
ZeroMemory(&CCB, sizeof(CCB));
CCB.uchDlcCommand = LLC_DIR_CLOSE_ADAPTER;
CCB.uchReserved2 = AppID;
CCB.u.pParameterTable = (PLLC_PARMS)&OpenParms;
OpenParms.pAdapterParms = &AdapterOpenParms;
OpenParms.pExtendedParms = &ExtendedAdapterParms;
OpenParms.pDlcParms = &DlcParms;
CCB.hCompletionEvent = hEvent;
ResetEvent(hEvent);
rtrn = AcsLan(&CCB, &pBadCCB);
WaitForSingleObject(hEvent, 5000);
printf("%-40s 返回值 %u 状态: 0x%X
", "LLC_DIR_CLOSE_ADAPTER", rtrn, *pStat);
}
}
return;
}
void FillArpPacket(PARP_PACKET a, LPSTR srcAddr, LPSTR srcIPAddress, LPSTR ipAddr)
{
//建立一个ARP包
// 宽度为16位
// /----------------------------------------------------------------------------------------/
// | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | A | B | C | D | E | F |
// /----------------------------------------------------------------------------------------/
// | 目的地址 | 源地址 | 类型 | 硬件类型 |
// /----------------------------------------------------------------------------------------/
// | Prot Type | HS* | PS* | Oper Code | Sender Enternet Address | Sender IP Address |
// /-----------------------------------------------------------------------------------------/
// | Target Ethernet Address | Target IP Address | 18 Bytes of Padding |
// /-----------------------------------------------------------------------------------------/
// | Padding Continued |
// /-----------------------------------------------------------------------/
//
// *HS = Hardware Address Size, PS = Protocol Address Size
ZeroMemory(a,sizeof(ARP_PACKET));
FillMemory(a->dest,6, 0xFF);
a->type=htons(0x0806); //Network byte order
a->hardType=htons(0x0001); //Network byte order
a->protType=htons(0x0800); //Network byte order
a->hardSize=0x06;
a->protSize=0x04;
a->opCode=0x0100;
CopyMemory(a->srcAddr, srcAddr, 6);
a->srcIP = inet_addr(srcIPAddress);
a->destIP = inet_addr(ipAddr);
}
//把每一个字节从网络顺序改为主机字节顺序
unsigned char swap_bits(unsigned char b)
{
unsigned char bb = 0;
unsigned char mask;
for (mask = 1; mask; mask <<= 1)
{
bb <<= 1;
bb |= ((b & mask) ? 1 : 0);
}
return bb;
}
void twiddle_bits(LPBYTE buffer, DWORD length)
{
//This function is used to format bits from IBM Token ring (BigEndian)
//format to Ethernet MAC address format (LittleEndian)
//此函数用于把硬件地址从网络字节顺序改为主机字节顺序
while (length--)
{
*buffer = swap_bits(*buffer); //调用改每一个字节函数
++buffer;
}
}
void PrintNext(LPBYTE *t, int c)
{
//This function prints c bytes of memory on one line
//with spaces between each byte... like this:
//00 A0 C9 C9 F6 11 00 10 54 42 C0 80 00 00
int i;
printf(" ");
for(i=0;i
printf("%02X ",**t);
(*t)++;
}
//printf("
");
}