ethmacaddr函数
A. 如何在delphi中使用winpcap的例子
winpcap的几个函数相信你已经知道了,那么剩下的就自己看看学学吧:
procere recvThread(myconfig:pconfigini);stdcall;
const
cStartPacket:array[0..63] of Byte=($01, $80, $c2, $00, $00, $03,
$ff, $ff, $ff, $ff, $ff, $ff,
$88, $8e, $01, $01, $00, $00,
$a5, $a5, $a5, $a5, $a5, $a5,
$a5, $a5, $a5, $a5, $a5, $a5,
$a5, $a5, $a5, $a5, $a5, $a5,
$a5, $a5, $a5, $a5, $a5, $a5,
$a5, $a5, $a5, $a5, $a5, $a5,
$a5, $a5, $a5, $a5, $a5, $a5,
$a5, $a5, $a5, $a5, $a5, $a5,
$a5, $a5, $a5, $a5);
cuserPacket :array[0..63] of Byte = ($01, $80, $c2, $00, $00, $03,
$ff, $ff, $ff, $ff, $ff, $ff,
$88, $8e, $01, $00, $00, $ff,
$02, $01, $00, $ff, $01, $a5,
$a5, $a5, $a5, $a5, $a5, $a5,
$a5, $a5, $a5, $a5, $a5, $a5,
$a5, $a5, $a5, $a5, $a5, $a5,
$a5, $a5, $a5, $a5, $a5, $a5,
$a5, $a5, $a5, $a5, $a5, $a5,
$a5, $a5, $a5, $a5, $a5, $a5,
$a5, $a5, $a5, $a5);
cResponseMD5 :array[0..63] of Byte = ($01, $80, $c2, $00, $00, $03,
$ff, $ff, $ff, $ff, $ff, $ff,
$88, $8e, $01, $00, $00, $ff,
$02, $02, $00, $ff, $04, $10,
$a5, $a5, $a5, $a5, $a5, $a5,
$a5, $a5, $a5, $a5, $a5, $a5,
$a5, $a5, $a5, $a5, $a5, $a5,
$a5, $a5, $a5, $a5, $a5, $a5,
$a5, $a5, $a5, $a5, $a5, $a5,
$a5, $a5, $a5, $a5, $a5, $a5,
$a5, $a5, $a5, $a5);
cLogoffPacket :array[0..63] of Byte = ($01, $80, $c2, $00, $00, $03,
$ff, $ff, $ff, $ff, $ff, $ff,
$88, $8e, $01, $02, $00, $00,
$a5, $a5, $a5, $a5, $a5, $a5,
$a5, $a5, $a5, $a5, $a5, $a5,
$a5, $a5, $a5, $a5, $a5, $a5,
$a5, $a5, $a5, $a5, $a5, $a5,
$a5, $a5, $a5, $a5, $a5, $a5,
$a5, $a5, $a5, $a5, $a5, $a5,
$a5, $a5, $a5, $a5, $a5, $a5,
$a5, $a5, $a5, $a5);
var
outnetbyte,innetbyte:int64;
StartPacket,userPacket,ResponseMD5,LogoffPacket:array[0..63] of Byte;
szMd5Buffer: Md5Buffer;
nLen: Integer;
md5Ctx: MD5Context;
md5Dig: MD5Digest;
mmcount:string;
pcap:ppcap;
mac:TMacAddr;//物理网卡地址
user:string;
ErrStr:string;
errbuf:array[0..PCAP_ERRBUF_SIZE] of Char;
userlength,i:integer;
buftoint : array[0..3] of byte;
bufint:integer;
headlen2:integer;
pRecvBuf :array of Byte;
pRecvHeaderBuf :ppcap_pkthdr;
q_MD5Source:array[0..15] of byte;
f:TextFile;
textstring:string;
begin
assignfile(f,'test.txt');
for i:=0 to 63 do
begin
StartPacket[i]:=cStartPacket[i];
userPacket[i]:=cuserPacket[i];
ResponseMD5[i]:=cResponseMD5[i];
LogoffPacket[i]:=cLogoffPacket[i];
end;
buftoint[2]:=0;
buftoint[3]:=0;
try
pcap := pcap_open_live(PChar(myconfig^.ethname),
65535, PCAP_OPENFLAG_PROMISCUOUS, 1, errbuf);
except
showmessage('打开网卡失败');
isloop:=false;
exit;
end;
// end;
if pcap<>nil then
begin
mac:=Pcap_GetMacAddress (Pcap, ErrStr) ;
end;
//填写mac地址
for i:=0 to 5 do
begin
StartPacket[i+6] := mac[i];
userPacket[i+6] := mac[i];
ResponseMD5[i+6] := mac[i];
LogoffPacket[i+6] := mac[i];
end;
//填写账号
user:=myconfig^.username;
userlength:=5+length(user);
userPacket[17]:=byte(userlength);
userPacket[21]:=byte(userlength);
for i:= 23 to length(user)+22 do
begin
userpacket[i] := Str_HexToInt(InttoHex( ord(user[i-22]) ,2));
end;
//发送开始认证包
pcap_sendpacket(pcap, @StartPacket, SizeOf(StartPacket)) ;
mmcount:=formatdatetime('ss',now());
while isloop=true do
begin
//
if (formatdatetime('ss',now()) <>mmcount) then
begin
try
PostMessage(myconfig^.formhandle, WM_THREAD_MSG, 4, outnetbyte);
PostMessage(myconfig^.formhandle, WM_THREAD_MSG, 5, innetbyte);
except
showmessage('子线程发送流量数据出错');
end;
mmcount:=formatdatetime('ss',now());
end;
//开始抓包
try
i:= pcap_next_ex(pcap, @pRecvHeaderBuf, @pRecvBuf);
except
showmessage('抓包函数出错');
end;
if( i>0 ) then
begin
if (pRecvBuf[12] = $88) and (pRecvBuf[13] = $8e) then
begin
case precvbuf[18] of
$01:
begin
if (pRecvBuf[22] = $01) then //Request Identity
begin
//form1.Lab_login_state.Caption:='发送账号包';
try
pcap_sendpacket(pcap, @userpacket, SizeOf(userpacket)) ;
except
showmessage('发送账号包出错');
end;
end
else
begin
for i:=0 to 15 do
begin
q_MD5Source[i] := pRecvBuf[i + 24];
end;
PostMessage(myconfig^.formhandle, WM_THREAD_MSG, 0, 0);
szMd5Buffer[0] := $02;
nLen := 1;
CopyMemory(@szMd5Buffer[nLen], PChar(Trim(myconfig^.userpass)), Length(Trim(myconfig^.userpass))); // 用户密码
nLen := nLen + Length(Trim(myconfig^.userpass));
CopyMemory(@szMd5Buffer[nLen], @q_MD5Source, 16); // 服务器返回密钥
nLen := nLen + 16;
for i:=0 to nlen-1 do
begin
textstring:=textstring+inttohex(szMd5Buffer[i],2)+' ';
end;
textstring:=textstring+#13#10;
MD5Init(md5Ctx);
MD5Update(md5Ctx, @szMd5Buffer, nLen);
MD5Final(md5Ctx, md5Dig);
for i:= 24 to 39 do
begin
ResponseMD5[i] := md5dig[i-24];
textstring:=textstring+inttohex(md5dig[i-24],2)+' ';
end;
textstring:=textstring+#13#10;
rewrite(f);
writeln(f,textstring);
responsemd5[17]:=byte(length(myconfig^.username)+22);
responsemd5[21]:=byte(length(myconfig^.username)+22);
for i:= 40 to length(user)+39 do
begin
ResponseMD5[i] := Str_HexToInt(InttoHex( ord(user[i-39]) ,2));
end;
pcap_sendpacket(pcap, @ResponseMD5, SizeOf(ResponseMD5)) ;
end;
end;
$03:
begin
PostMessage(myconfig^.formhandle, WM_THREAD_MSG, 1, 0);
end;
$04:
begin
PostMessage(myconfig^.formhandle, WM_THREAD_MSG, 3, 0);
isloop:=false;
end;
end;
end;
if (pRecvBuf[12] = $08) and (pRecvBuf[13] = $00) then
begin
if (pRecvBuf[23] = $06) then //tcp包
begin
try
buftoint[0]:= pRecvBuf[17];
buftoint[1]:= pRecvBuf[16];
Bufint := integer(buftoint);
except
showmessage('tcp字节转化错误');
end;
headlen2:=20;
case pRecvBuf[46] of
$50:headlen2:=20;
$60:headlen2:=24;
$70:headlen2:=28;
$80:headlen2:=32;
$90:headlen2:=36;
$a0:headlen2:=40;
$b0:headlen2:=44;
$c0:headlen2:=48;
end;
//if totalbyte<3000000000 then totalbyte:=totalbyte+abuf^ - 20 -headlen2;
if ((pRecvBuf[26] = $c0) or (pRecvBuf[26] = $ac) ) and ((pRecvBuf[30] = $c0) or (pRecvBuf[30] = $ac) ) then
begin
try
innetbyte:=innetbyte+ bufint - 20 -headlen2;
except
showmessage('tcp计算出错in');
end;
end
else
begin
try
outnetbyte:=outnetbyte+ bufint - 20 -headlen2;
except
showmessage('tcp计算出错out');
end;
end;
//if (pRecvBuf[26] = $c0) and ( pRecvBuf[30] <> $c0 ) and (pRecvBuf[30] <> $ac) then outnetbyte:=outnetbyte+ abuf^ - 20 -headlen2;
//if (pRecvBuf[30] = $c0) and ( pRecvBuf[26] <> $c0 ) and (pRecvBuf[26] <> $ac) then outnetbyte:=outnetbyte+ abuf^ - 20 -headlen2;
//if (pRecvBuf[26] = $ac) and ( pRecvBuf[30] <> $c0 ) and (pRecvBuf[30] <> $ac) then outnetbyte:=outnetbyte+ abuf^ - 20 -headlen2;
//if (pRecvBuf[30] = $ac) and ( pRecvBuf[26] <> $c0 ) and (pRecvBuf[26] <> $ac) then outnetbyte:=outnetbyte+ abuf^ - 20 -headlen2;
end;
if (pRecvBuf[23] = $11) then //udp包
begin
try
buftoint[0]:= pRecvBuf[17];
buftoint[1]:= pRecvBuf[16];
Bufint := integer(buftoint);
except
showmessage('udp字节转化错误');
end;
if ((pRecvBuf[26] = $c0) or (pRecvBuf[26] = $ac) ) and ((pRecvBuf[30] = $c0) or (pRecvBuf[30] = $ac) ) then
begin
try
innetbyte:=innetbyte+ bufint - 28;
except
showmessage('udp计算出错in');
end;
end
else
begin
try
outnetbyte:=outnetbyte+ bufint - 28;
except
showmessage('udp计算出错out');
end;
end;
end;
end;
end;
end;
pcap_sendpacket(pcap, @LogoffPacket, SizeOf(LogoffPacket)) ;
end;
转载
B. C++中用哪个函数可以获得当前网卡的物理地址
//如果是Windows
//
//.
(
_In_ULONGFamily,
_In_ULONGFlags,
_In_PVOIDReserved,
_Inout_PIP_ADAPTER_ADDRESSESAdapterAddresses,
_Inout_PULONGSizePointer
);
/*
Family[in]
.这个参数必须是以下值之一.
AF_UNSPEC返回开启IPv4和IPv6的适配器的IPv4和IPv6地址.
AF_INET返回开启IPv4的适配器的IPv4地址.
AF_INET6只返回开启IPv6的适配器的IPv6地址.
------------------------------------------------------------------------------
Flags[in]
需要得到的地址类型.可能值在头文件Iptypes.h中定义.注意到Iptypes.h自动包含在Iphlpapi.h里面,应该永远不要直接使用它.这个参数可以是以下值的组合.如果此参数是0,那么单播、任播、多播IP地址都将会返回.
------------------------------------------------------------------------------
Reserved[in]
这个参数现在未使用,但是是保留为了将来系统使用,在这个调用应该给予NULL.
------------------------------------------------------------------------------
AdapterAddresses[in,out]
一个指针指向缓冲区,函数成功返回时,用来保存IP_ADAPTER_ADDRESSES结构的链表.
------------------------------------------------------------------------------
SizePointer[in,out]
一个指针,指向"表明AdapterAddress指向的缓冲区大小的变量".
--------------------------------------------------------------------------
返回值
如果函数成功,返回值为ERROR_SUCCESS(definedtothesamevalueasNO_ERROR).
如果函数失败,返回值是以下错误值之一.
*/
//C++示例代码
//来源于MSDN
#include<winsock2.h>
#include<iphlpapi.h>
#include<stdio.h>
#include<stdlib.h>
//LinkwithIphlpapi.lib
#pragmacomment(lib,"IPHLPAPI.lib")
#defineWORKING_BUFFER_SIZE15000
#defineMAX_TRIES3
#defineMALLOC(x)HeapAlloc(GetProcessHeap(),0,(x))
#defineFREE(x)HeapFree(GetProcessHeap(),0,(x))
/*Note:couldalsousemalloc()andfree()*/
int__cdeclmain(intargc,char**argv)
{
/*Declareandinitializevariables*/
DWORDdwSize=0;
DWORDdwRetVal=0;
unsignedinti=0;
//
ULONGflags=GAA_FLAG_INCLUDE_PREFIX;
//(both)
ULONGfamily=AF_UNSPEC;
LPVOIDlpMsgBuf=NULL;
PIP_ADAPTER_ADDRESSESpAddresses=NULL;
ULONGoutBufLen=0;
ULONGIterations=0;
PIP_ADAPTER_ADDRESSESpCurrAddresses=NULL;
PIP_ADAPTER_UNICAST_ADDRESSpUnicast=NULL;
PIP_ADAPTER_ANYCAST_ADDRESSpAnycast=NULL;
PIP_ADAPTER_MULTICAST_ADDRESSpMulticast=NULL;
IP_ADAPTER_DNS_SERVER_ADDRESS*pDnServer=NULL;
IP_ADAPTER_PREFIX*pPrefix=NULL;
if(argc!=2){
printf("Usage:getadapteraddressesfamily ");
printf("getadapteraddresses4(forIPv4) ");
printf("getadapteraddresses6(forIPv6) ");
printf("getadapteraddressesA(forbothIPv4andIPv6) ");
exit(1);
}
if(atoi(argv[1])==4)
family=AF_INET;
elseif(atoi(argv[1])==6)
family=AF_INET6;
printf("=");
if(family==AF_INET)
printf("AF_INET ");
if(family==AF_INET6)
printf("AF_INET6 ");
if(family==AF_UNSPEC)
printf("AF_UNSPEC ");
//.
outBufLen=WORKING_BUFFER_SIZE;
do{
pAddresses=(IP_ADAPTER_ADDRESSES*)MALLOC(outBufLen);
if(pAddresses==NULL){
printf
("MemoryallocationfailedforIP_ADAPTER_ADDRESSESstruct ");
exit(1);
}
dwRetVal=
GetAdaptersAddresses(family,flags,NULL,pAddresses,&outBufLen);
if(dwRetVal==ERROR_BUFFER_OVERFLOW){
FREE(pAddresses);
pAddresses=NULL;
}else{
break;
}
Iterations++;
}while((dwRetVal==ERROR_BUFFER_OVERFLOW)&&(Iterations<MAX_TRIES));
if(dwRetVal==NO_ERROR){
//Ifsuccessful,
pCurrAddresses=pAddresses;
while(pCurrAddresses){
printf(" LengthoftheIP_ADAPTER_ADDRESSstruct:%ld ",
pCurrAddresses->Length);
printf(" IfIndex(IPv4interface):%u ",pCurrAddresses->IfIndex);
printf(" Adaptername:%s ",pCurrAddresses->AdapterName);
pUnicast=pCurrAddresses->FirstUnicastAddress;
if(pUnicast!=NULL){
for(i=0;pUnicast!=NULL;i++)
pUnicast=pUnicast->Next;
printf(" NumberofUnicastAddresses:%d ",i);
}else
printf(" NoUnicastAddresses ");
pAnycast=pCurrAddresses->FirstAnycastAddress;
if(pAnycast){
for(i=0;pAnycast!=NULL;i++)
pAnycast=pAnycast->Next;
printf(" NumberofAnycastAddresses:%d ",i);
}else
printf(" NoAnycastAddresses ");
pMulticast=pCurrAddresses->FirstMulticastAddress;
if(pMulticast){
for(i=0;pMulticast!=NULL;i++)
pMulticast=pMulticast->Next;
printf(" NumberofMulticastAddresses:%d ",i);
}else
printf(" NoMulticastAddresses ");
pDnServer=pCurrAddresses->FirstDnsServerAddress;
if(pDnServer){
for(i=0;pDnServer!=NULL;i++)
pDnServer=pDnServer->Next;
printf(" NumberofDNSServerAddresses:%d ",i);
}else
printf(" NoDNSServerAddresses ");
printf(" DNSSuffix:%wS ",pCurrAddresses->DnsSuffix);
printf(" Description:%wS ",pCurrAddresses->Description);
printf(" Friendlyname:%wS ",pCurrAddresses->FriendlyName);
if(pCurrAddresses->PhysicalAddressLength!=0){
printf(" Physicaladdress:");
for(i=0;i<(int)pCurrAddresses->PhysicalAddressLength;
i++){
if(i==(pCurrAddresses->PhysicalAddressLength-1))
printf("%.2X ",
(int)pCurrAddresses->PhysicalAddress[i]);
else
printf("%.2X-",
(int)pCurrAddresses->PhysicalAddress[i]);
}
}
printf(" Flags:%ld ",pCurrAddresses->Flags);
printf(" Mtu:%lu ",pCurrAddresses->Mtu);
printf(" IfType:%ld ",pCurrAddresses->IfType);
printf(" OperStatus:%ld ",pCurrAddresses->OperStatus);
printf(" Ipv6IfIndex(IPv6interface):%u ",
pCurrAddresses->Ipv6IfIndex);
printf(" ZoneIndices(hex):");
for(i=0;i<16;i++)
printf("%lx",pCurrAddresses->ZoneIndices[i]);
printf(" ");
printf(" Transmitlinkspeed:%I64u ",pCurrAddresses->TransmitLinkSpeed);
printf(" Receivelinkspeed:%I64u ",pCurrAddresses->ReceiveLinkSpeed);
pPrefix=pCurrAddresses->FirstPrefix;
if(pPrefix){
for(i=0;pPrefix!=NULL;i++)
pPrefix=pPrefix->Next;
printf(":%d ",i);
}else
printf(":0 ");
printf(" ");
pCurrAddresses=pCurrAddresses->Next;
}
}else{
printf(":%d ",
dwRetVal);
if(dwRetVal==ERROR_NO_DATA)
printf("\n");
else{
if(FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER|
FORMAT_MESSAGE_FROM_SYSTEM|FORMAT_MESSAGE_IGNORE_INSERTS,
NULL,dwRetVal,MAKELANGID(LANG_NEUTRAL,SUBLANG_DEFAULT),
//Defaultlanguage
(LPTSTR)&lpMsgBuf,0,NULL)){
printf(" Error:%s",lpMsgBuf);
LocalFree(lpMsgBuf);
if(pAddresses)
FREE(pAddresses);
exit(1);
}
}
}
if(pAddresses){
FREE(pAddresses);
}
return0;
}
C. ethtool原理介绍和解决网卡丢包排查思路
之前记录过处理因为LVS网卡流量负载过高导致软中断发生丢包的问题, RPS和RFS网卡多队列性能调优实践 ,对一般人来说压力不大的情况下其实碰见的概率并不高。这次想分享的话题是比较常见服务器网卡丢包现象排查思路,如果你是想了解点对点的丢包解决思路涉及面可能就比较广,不妨先参考之前的文章 如何使用MTR诊断网络问题 ,对于Linux常用的网卡丢包分析工具自然是ethtool。
2020年06月22日 - 初稿
阅读原文 - https://wsgzao.github.io/post/ethtool/
ethtool - utility for controlling network drivers and hardware
ethtool is the standard Linux utility for controlling network drivers and hardware, particularly for wired Ethernet devices. It can be used to:
Most features are dependent on support in the specific driver. See the manual page for full information.
ethtool 用于查看和修改网络设备(尤其是有线以太网设备)的驱动参数和硬件设置。你可以根据需要更改以太网卡的参数,包括自动协商、速度、双工和局域网唤醒等参数。通过对以太网卡的配置,你的计算机可以通过网络有效地进行通信。该工具提供了许多关于接驳到你的 Linux 系统的以太网设备的信息。
接收数据包是一个复杂的过程,涉及很多底层的技术细节,但大致需要以下几个步骤:
NIC 在接收到数据包之后,首先需要将数据同步到内核中,这中间的桥梁是 rx ring buffer 。它是由 NIC 和驱动程序共享的一片区域,事实上, rx ring buffer 存储的并不是实际的 packet 数据,而是一个描述符,这个描述符指向了它真正的存储地址,具体流程如下:
当驱动处理速度跟不上网卡收包速度时,驱动来不及分配缓冲区,NIC 接收到的数据包无法及时写到 sk_buffer ,就会产生堆积,当 NIC 内部缓冲区写满后,就会丢弃部分数据,引起丢包。这部分丢包为 rx_fifo_errors ,在 /proc/net/dev 中体现为 fifo 字段增长,在 ifconfig 中体现为 overruns 指标增长。
这个时候,数据包已经被转移到了 sk_buffer 中。前文提到,这是驱动程序在内存中分配的一片缓冲区,并且是通过 DMA 写入的,这种方式不依赖 CPU 直接将数据写到了内存中,意味着对内核来说,其实并不知道已经有新数据到了内存中。那么如何让内核知道有新数据进来了呢?答案就是中断,通过中断告诉内核有新数据进来了,并需要进行后续处理。
提到中断,就涉及到硬中断和软中断,首先需要简单了解一下它们的区别:
当 NIC 把数据包通过 DMA 复制到内核缓冲区 sk_buffer 后,NIC 立即发起一个硬件中断。CPU 接收后,首先进入上半部分,网卡中断对应的中断处理程序是网卡驱动程序的一部分,之后由它发起软中断,进入下半部分,开始消费 sk_buffer 中的数据,交给内核协议栈处理。
通过中断,能够快速及时地响应网卡数据请求,但如果数据量大,那么会产生大量中断请求,CPU 大部分时间都忙于处理中断,效率很低。为了解决这个问题,现在的内核及驱动都采用一种叫 NAPI(new API)的方式进行数据处理,其原理可以简单理解为 中断 + 轮询,在数据量大时,一次中断后通过轮询接收一定数量包再返回,避免产生多次中断。
(1) RX errors
表示总的收包的错误数量,这包括 too-long-frames 错误,Ring Buffer 溢出错误,crc 校验错误,帧同步错误,fifo overruns 以及 missed pkg 等等。
(2) RX dropped
表示数据包已经进入了 Ring Buffer,但是由于内存不够等系统原因,导致在拷贝到内存的过程中被丢弃。
(3) RX overruns
表示了 fifo 的 overruns,这是由于 Ring Buffer(aka Driver Queue) 传输的 IO 大于 kernel 能够处理的 IO 导致的,而 Ring Buffer 则是指在发起 IRQ 请求之前的那块 buffer。很明显,overruns 的增大意味着数据包没到 Ring Buffer 就被网卡物理层给丢弃了,而 CPU 无法即使的处理中断是造成 Ring Buffer 满的原因之一,上面那台有问题的机器就是因为 interruprs 分布的不均匀(都压在 core0),没有做 affinity 而造成的丢包。
(4) RX frame
表示 misaligned 的 frames。
网线上的packet首先被网卡获取,网卡会检查packet的CRC校验,保证完整性,然后将packet头去掉,得到frame。网卡会检查MAC包内的目的MAC地址,如果和本网卡的MAC地址不一样则丢弃(混杂模式除外)。
网卡将frame拷贝到网卡内部的FIFO缓冲区,触发硬件中断。(如有ring buffer的网卡,好像frame可以先存在ring buffer里再触发软件中断(下篇文章将详细解释Linux中frame的走向),ring buffer是网卡和驱动程序共享,是设备里的内存,但是对操作系统是可见的,因为看到linux内核源码里网卡驱动程序是使用kcalloc来分配的空间,所以ring buffer一般都有上限,另外这个ring buffer size,表示的应该是能存储的frame的个数,而不是字节大小。另外有些系统的 ethtool 命令 并不能改变ring parameters来设置ring buffer的大小,暂时不知道为什么,可能是驱动不支持。)
网卡驱动程序通过硬中断处理函数,构建sk_buff,把frame从网卡FIFO拷贝到内存skb中,接下来交给内核处理。(支持napi的网卡应该是直接放在ring buffer,不触发硬中断,直接使用软中断,拷贝ring buffer里的数据,直接输送给上层处理,每个网卡在一次软中断处理过程能处理weight个frame)
过程中,网卡芯片对frame进行了MAC过滤,以减小系统负荷。(除了混杂模式)
网卡驱动程序将IP包添加14字节的MAC头,构成frame(暂无CRC)。Frame(暂无CRC)中含有发送端和接收端的MAC地址,由于是驱动程序创建MAC头,所以可以随便输入地址,也可以进行主机伪装。
驱动程序将frame(暂无CRC)拷贝到网卡芯片内部的缓冲区,由网卡处理。
网卡芯片将未完全完成的frame(缺CRC)再次封装为可以发送的packet,也就是添加头部同步信息和CRC校验,然后丢到网线上,就完成一个IP报的发送了,所有接到网线上的网卡都可以看到该packet。
产生中断的每个设备都有一个相应的中断处理程序,是设备驱动程序的一部分。每个网卡都有一个中断处理程序,用于通知网卡该中断已经被接收了,以及把网卡缓冲区的数据包拷贝到内存中。
当网卡接收来自网络的数据包时,需要通知内核数据包到了。网卡立即发出中断。内核通过执行网卡已注册的中断处理函数来做出应答。中断处理程序开始执行,通知硬件,拷贝最新的网络数据包到内存,然后读取网卡更多的数据包。
这些都是重要、紧迫而又与硬件相关的工作。内核通常需要快速的拷贝网络数据包到系统内存,因为网卡上接收网络数据包的缓存大小固定,而且相比系统内存也要小得多。所以上述拷贝动作一旦被延迟,必然造成网卡FIFO缓存溢出 - 进入的数据包占满了网卡的缓存,后续的包只能被丢弃,这也应该就是ifconfig里的overrun的来源。
当网络数据包被拷贝到系统内存后,中断的任务算是完成了,这时它把控制权交还给被系统中断前运行的程序。
网卡的内核缓冲区,是在PC内存中,由内核控制,而网卡会有FIFO缓冲区,或者ring buffer,这应该将两者区分开。FIFO比较小,里面有数据便会尽量将数据存在内核缓冲中。
网卡中的缓冲区既不属于内核空间,也不属于用户空间。它属于硬件缓冲,允许网卡与操作系统之间有个缓冲;
内核缓冲区在内核空间,在内存中,用于内核程序,做为读自或写往硬件的数据缓冲区;
用户缓冲区在用户空间,在内存中,用于用户程序,做为读自或写往硬件的数据缓冲区;
另外,为了加快数据的交互,可以将内核缓冲区映射到用户空间,这样,内核程序和用户程序就可以同时访问这一区间了。
对于有ring buffer的网卡,ring buffer是由驱动与网卡共享的,所以内核可以直接访问ring buffer,一般拷贝frames的副本到自己的内核空间进行处理(deliver到上层协议,之后的一个个skb就是按skb的指针传递方式传递,直到用户获得数据,所以,对于ring buffer网卡,大量拷贝发生在frame从ring buffer传递到内核控制的计算机内存里)。
网卡工作在数据链路层,数据量链路层,会做一些校验,封装成帧。我们可以查看校验是否出错,确定传输是否存在问题。然后从软件层面,是否因为缓冲区太小丢包。
一台机器经常收到丢包的报警,先看看最底层的有没有问题:
(1) 查看工作模式是否正常
(2) 查看检验是否正常
Speed,Duplex,CRC 之类的都没问题,基本可以排除物理层面的干扰。
Why rx_crc_errors incrementing in the receive counter of ethtool -S output?
Check ethtool -S output and find where are the drops and errors.
Check the numbers corresponding to rx_crc_errors .
显示了p1p1 的接口类型,连接模式,速率等等信息,以及当前是否连接了网线(如果是网线Supported ports 就是TP,如果是光纤则显示Fiber),这里例举下3个重要关键词
Supported ports: [ FIBRE ]
Speed: 10000Mb/s
Link detected: yes
ethtool
Counters Troubleshooting for Linux Driver
Why do I see rx_crc_errors in ethtool output?
ping请求错误分析
ifconfig 命令详解
ethtool 命令详解
ethtool 解决网卡丢包严重和网卡原理
D. 如何修改mac地址让它一直生效
若是你是使用 RHEL 这类版本的话,请考虑先看一下 /etc/sysconfig/network-scripts/ifup 看一下:
# this isn't the same as the MAC in the configuration filename. It is
# available as a configuration option in the config file, forcing the kernel
# to think an ethernet card has a different MAC address than it really has.
if [ -n "${MACADDR}" ]; then
ip link set dev ${DEVICE} address ${MACADDR}
fi
if [ -n "${MTU}" ]; then
ip link set dev ${DEVICE} mtu ${MTU}
fi
可以发现到目前都是流行使用 ip 程式,已经逐渐不使用 ifconfig 程式了。
而你网路卡配置 /etc/sysconfig/network-scripts/ifcfg-eth0 应该就会使用像是:
DEVICE=eth0
IPADDR=192.168.1.1
NETMASK=255.255.255.0
MACADDR=00:11:22:33:44:55
其中 MACADDR 应该就是你预期要改的的项目。把预期的网路卡卡号放进去就可以。
改好执行 /etc/init.d/network stop ; /etc/init.d/network start
若是您是使用 SUSE Linux 的话,SuSE 本身就已经给您一个很明确清楚的样板档案,档案名称为 /etc/sysconfig/network/ifcfg.template,该档案里面有提到配置说明。
比方你目前使用中的网路卡的 mac addr 是 00:11:22:33:44:55,那您的系统应该就会有 /etc/sysconfig/network/ifcfg-eth-id-00:11:22:33:44:55 档案,该档案内容会像是:
IPADDR=192.168.1.1
NETMASK=255.255.255.0
你要换网路卡卡号的话,就是多 LLADDR 该叙述。
IPADDR=192.168.1.1
NETMASK=255.255.255.0
LLADDR=00:48:54:11:22:33
改好后执行 rcnetwork stop ; rcnetwork start 应该就可以了 (用 ifup/ifdown 也可以)。
另外补充的是,使用 ip 程式配置网路介面 mac 组态的话,该配置设定实际底层会是:
ip link set eth0 address 00:48:54:11:22:33
提供给你参考。
E. 在Android机顶盒上 怎样获取有线网卡MAC地址
在Android机顶盒上 获取有线网卡MAC地址方法:
(1) 调用android 的API: NetworkInterface. getHardwareAddress ()
该API的level为9,只有android 2.3以上才有该接口
(2) 调用java 的方法: nbtstat/arp
一般android不支持这两个命令
(3) 调用Android的API: WifiManager
权限:
1 <uses-permission android:name="android.permission.ACCESS_WIFI_STATE"></uses- permission>
代码:
12345 WifiManager wifi = (WifiManager) getSystemService(Context.WIFI_SERVICE); WifiInfo info = wifi.getConnectionInfo(); return info.getMacAddress();
这个是设备开通Wifi连接,获取到网卡的MAC地址
(4) 调用Linux的busybox
/* ***************************************************************** * 子函数:获得本地MAC地址 ***************************************************************** */ public String getMacAddress(){ String result = ""; String Mac = ""; result = callCmd("busybox ifconfig","HWaddr"); //如果返回的result == null,则说明网络不可取 if(result==null){ return "网络出错,请检查网络"; } //对该行数据进行解析 //例如:eth0 Link encap:Ethernet HWaddr 00:16:E8:3E:DF:67 if(result.length()>0 && result.contains("HWaddr")==true){ Mac = result.substring(result.indexOf("HWaddr")+6, result.length()-1); Log.i("test","Mac:"+Mac+" Mac.length: "+Mac.length()); if(Mac.length()>1){ Mac = Mac.replaceAll(" ", ""); result = ""; String[] tmp = Mac.split(":"); for(int i = 0;i<tmp.length;++i){ result +=tmp[i]; } } Log.i("test",result+" result.length: "+result.length()); } return result; } public String callCmd(String cmd,String filter) { String result = ""; String line = ""; try { Process proc = Runtime.getRuntime().exec(cmd); InputStreamReader is = new InputStreamReader(proc.getInputStream()); BufferedReader br = new BufferedReader (is); //执行命令cmd,只取结果中含有filter的这一行 while ((line = br.readLine ()) != null && line.contains(filter)== false) { //result += line; Log.i("test","line: "+line); } result = line; Log.i("test","result: "+result); } catch(Exception e) { e.printStackTrace(); } return result; }
这个需要设备支持busybox工具
(5)查询记录了MAC地址的文件“/proc/net/arp”
需要有这个文件,并且记录了相应的内容
F. uip协议栈怎么设置mac地址
这个就得从TCP/IP协议栈的分层说起了。通信协议一般分7层,不过习惯上把TCP/IP协议分成5层:
应用层(Application);传输层(Transport);网络层(MAC);数据链路层;物理层(PHY);DM9000只实现了最低层的两层,就是物理层和数据链路层,说白了就是电平的转换,但是按照协议的组包分包都是需要单片机编程实现,程序是跑在单片机上。
也有硬件的TCP/IP协议栈芯片,如W5100,这个芯片TCP/IP协议栈是直接跑在芯片上的。
G. 现在,我怀疑局域网有些人的网卡处于混杂模式,可是我怎样才能确定我的观点(300分)
你可以用ARP探测网络中的混杂模式节点
根据sniffer的基本工作原理,其核心就是设置网卡模式为 promiscuous(混杂模式),如果能够检测到网络有是混杂模式的网卡,那么就可以判断可能存在一个sniffer。ARP协议在深入嗅探中很有作用,同时也可以用于进行嗅探器的侦测。
在混杂模式中,网卡进行包过滤不同于普通模式。本来在普通模式下,只有本地地址的数据包或者广播(多播等)才会被网卡提交给系统核心,否则的话,这些数据包就直接被网卡抛弃。现在,混合模式让所有经过的数据包都传递给系统核心,然后被sniffer等程序利用。因此,如果能利用中间的“系统核心”,就能有效地进行是否混杂模式的检测。系统核心也会对一些数据包进行过滤,但是,和网卡的标准不一样的是。
以Windows系统为例:
FF-FF-FF-FF-FF-FF:这个是一个正规的广播地址,不管是正常模式还是其他模式,都会被网卡接收并传递给系统核心。
FF-FF-FF-FF-FF-00:这个地址对于网卡来说,不是一个广播地址,在正常模式下会被网卡抛弃,但是系统核心是认为这个地址同FF-FF-FF-FF-FF-FF是完全一样的。如果处于混杂模式,将被系统核心接收,并认为是一个广播地址。所有的Windows操作系统都是如此。
FF-FF-00-00-00-00:Windows核心只对前面两字节作判断,核心认为这是一个同FF-FF-FF-FF-FF-FF一样的广播地址。这就是为什么FF-FF-FF-FF-FF-00也是广播地址的原因。
FF-00-00-00-00-00:对于Win9x或WinME,则是检查前面的一个字节。因此会认为这个是一个广播地址。
而对于LINUX内核,我则不清楚,不过从一些资料得到会判断一个group bit,不清楚具体什么意思,但是基本上就是认为FF-00-00-00-00-00,是FF-FF-FF-FF-FF-FF一个类别的吧。(望熟悉LINUX者指点)
所以,目的就要让正常模式的网卡抛弃掉探测包,而让混杂模式的系统核心能够处理探测。发送一个目的地址为:FF-FF-FF-FF-FF-FE(系统会认为属于广播地址)的ARP请求,对于普通模式(广播等)的网卡,这个地址不是广播地址,就会直接抛弃,而如果处于混杂模式,那么ARP请求就会被系统核心当作广播地址处理,然后提交给sniffer程序。系统核心就会应答这个ARP请求。
antisniffer也采用了这样的策略进行检测。
下面这个例子就是FF-FF-FF-FF-FF-FE的ARP请求,可以对网络中的每个节点都发送这样的ARP请求。如果有一般的sniffer存在,并设置网卡为混杂模式,那么系统核心就会作出应答,可以判断这些节点是否存在嗅探器了。这种检测办法也是有局限的,对于那些修改内核的sniffer,就没有办法了,不过这种Sniffer毕竟属于少数还有就是Win2k中一些动态加载的包捕获驱动(WinPcap就是),可能会让没有在混杂模式的网卡也作出响应。
///////////////////////////////////////////////////////////////////////////////
//
// Detect Promiscuous Node In Network
//
// Author: suchasplus
// Email: [email protected]
// Home Page: spaces.msn.com/suchasplus
//
// 2006/02/02
//
////////////////////////////////////////////////////////////////////////////////
#include "stdafx.h"
#include "Mac.h" //GetMacAddr(),我写的把字符串转换为MAC地址的函数,就不列在这里了
#include <stdio.h>
#include <conio.h>
#include <Packet32.h>
#include <Winsock2.h>
#include <process.h>
#include <ntddndis.h>
#pragma comment (lib, "packet.lib")
#pragma comment (lib, "ws2_32.lib")
#define EPT_IP 0x0800 /* type: IP */
#define EPT_ARP 0x0806 /* type: ARP */
#define EPT_RARP 0x8035 /* type: RARP */
#define ARP_HARDWARE 0x0001 /* Dummy type for 802.3 frames */
#define ARP_REQUEST 0x0001 /* ARP request */
#define ARP_REPLY 0x0002 /* ARP reply */
#define Max_Num_Adapter 10
#pragma pack(push, 1)
typedef struct ehhdr
{
unsigned char eh_dst[6]; /* destination ethernet addrress */
unsigned char eh_src[6]; /* source ethernet addresss */
unsigned short eh_type; /* ethernet pachet type */
}EHHDR, *PEHHDR;
typedef struct arphdr
{
unsigned short arp_hrd; /* format of hardware address */
unsigned short arp_pro; /* format of protocol address */
unsigned char arp_hln; /* length of hardware address */
unsigned char arp_pln; /* length of protocol address */
unsigned short arp_op; /* ARP/RARP operation */
unsigned char arp_sha[6]; /* sender hardware address */
unsigned long arp_spa; /* sender protocol address */
unsigned char arp_tha[6]; /* target hardware address */
unsigned long arp_tpa; /* target protocol address */
}ARPHDR, *PARPHDR;
typedef struct arpPacket
{
EHHDR ehhdr;
ARPHDR arphdr;
} ARPPACKET, *PARPPACKET;
#pragma pack(pop)
//the thread for listening
void ListenThread(void* Adapter);
//the function of sending packet
void SendARPPacket(void* Adapter);
BOOL DetectIsSniffer(LPPACKET lpPacket);
char g_szMyMacAddr[] = "AAAAAAAAAAAA";
char g_szMyIP[] = "192.168.1.1";
char g_szTargetIP[] = "192.168.1.2";
int main(int argc, char* argv[])
{
static char AdapterList[Max_Num_Adapter][1024];
LPADAPTER lpAdapter;
WCHAR AdapterName[2048];
WCHAR *temp,*temp1;
ULONG AdapterLength = 1024;
int AdapterNum = 0;
int nRetCode, i;
//Get The list of Adapter
if(PacketGetAdapterNames((char*)AdapterName, &AdapterLength) == FALSE)
{
printf("Unable to retrieve the list of the adapters!\n");
return 0;
}
temp = AdapterName;
temp1 = AdapterName;
i = 0;
while ((*temp != '\0')||(*(temp-1) != '\0'))
{
if (*temp == '\0')
{
memcpy(AdapterList[i],temp1,(temp-temp1)*2);
temp1 = temp+1;
i++;
}
temp++;
}
AdapterNum = i;
for (i = 0; i < AdapterNum; i++)
wprintf(L"\n%d- %s\n", i+1, AdapterList[i]);
printf("\n");
//Default open the 0
lpAdapter = (LPADAPTER) PacketOpenAdapter((LPTSTR) AdapterList[0]);
if (!lpAdapter || (lpAdapter->hFile == INVALID_HANDLE_VALUE))
{
nRetCode = GetLastError();
printf("Unable to open the driver, Error Code : %lx\n", nRetCode);
return 0;
}
//begin listening
_beginthread(ListenThread, 0, (void*) lpAdapter);
Sleep(500);
//send the packet
_beginthread(SendARPPacket, 0, (void*) lpAdapter);
Sleep(2000);
printf ("\n\nDetecting end.\n");
// close the adapter and exit
PacketCloseAdapter(lpAdapter);
return 0;
}
void SendARPPacket(void* Adapter)
{
char MacAddr[6];
char szPacketBuf[600];
LPADAPTER lpAdapter = (LPADAPTER) Adapter;
LPPACKET lpPacket;
ARPPACKET ARPPacket;
lpPacket = PacketAllocatePacket();
if(lpPacket == NULL)
{
printf("\nError:failed to allocate the LPPACKET structure.\n");
return;
}
ZeroMemory(szPacketBuf, sizeof(szPacketBuf));
// the fake mac of multicast
if (!GetMacAddr("FFFFFFFFFFFE", MacAddr))
{
printf ("Get Mac address error!\n");
goto Exit0;
}
memcpy(ARPPacket.ehhdr.eh_dst, MacAddr, 6);
//the MAC of sender
if (!GetMacAddr(g_szMyMacAddr, MacAddr))
{
printf ("Get Mac address error!\n");
goto Exit0;
}
memcpy(ARPPacket.ehhdr.eh_src, MacAddr, 6);
ARPPacket.ehhdr.eh_type = htons(EPT_ARP);
//arp header
ARPPacket.arphdr.arp_hrd = htons(ARP_HARDWARE);
ARPPacket.arphdr.arp_pro = htons(EPT_IP);
ARPPacket.arphdr.arp_hln = 6;
ARPPacket.arphdr.arp_pln = 4;
ARPPacket.arphdr.arp_op = htons(ARP_REQUEST);
if (!GetMacAddr("00E04C6A21DF", MacAddr))
{
printf ("Get Mac address error!\n");
goto Exit0;
}
memcpy(ARPPacket.arphdr.arp_sha, MacAddr, 6);
ARPPacket.arphdr.arp_spa = inet_addr(g_szMyIP);
if (!GetMacAddr("000000000000", MacAddr))
{
printf ("Get Mac address error!\n");
goto Exit0;
}
memcpy(ARPPacket.arphdr.arp_tha , MacAddr, 6);
ARPPacket.arphdr.arp_tpa = inet_addr(g_szTargetIP);
memcpy(szPacketBuf, (char*)&ARPPacket, sizeof(ARPPacket));
PacketInitPacket(lpPacket, szPacketBuf, 60);
if(PacketSetNumWrites(lpAdapter, 1)==FALSE)
{
printf("warning: Unable to send more than one packet in a single write!\n");
}
if(PacketSendPacket(lpAdapter, lpPacket, TRUE)==FALSE)
{
printf("Error sending the packets!\n");
goto Exit0;
}
printf ("Send ok!\n\n");
Exit0:
PacketFreePacket(lpPacket);
_endthread();
}
void ListenThread(void* Adapter)
{
LPPACKET lpPacket;
LPADAPTER lpAdapter = (LPADAPTER) Adapter;
char buffer[256000];
if((lpPacket = PacketAllocatePacket())==NULL){
printf("\nError: failed to allocate the LPPACKET structure.");
return;
}
PacketInitPacket(lpPacket,(char*)buffer,256000);
// set the network adapter in promiscuous mode
if(PacketSetHwFilter(lpAdapter, NDIS_PACKET_TYPE_DIRECTED)==FALSE){
printf("Warning: unable to set promiscuous mode!\n");
}
// set buffer in the driver
if(PacketSetBuff(lpAdapter,512000)==FALSE){
printf("Unable to set the kernel buffer!\n");
return;
}
// set second read timeout
if(PacketSetReadTimeout(lpAdapter, 200)==FALSE){
printf("Warning: unable to set the read tiemout!\n");
}
//main capture loop
printf("Listen....\n");
while(true)
{
// capture the packets
if(PacketReceivePacket(lpAdapter, lpPacket, TRUE)==FALSE){
printf("Error: PacketReceivePacket failed");
return ;
}
//
DetectIsSniffer(lpPacket);
}
PacketFreePacket(lpPacket);
// close the adapter and exit
PacketCloseAdapter(lpAdapter);
_endthread();
}
BOOL DetectIsSniffer(LPPACKET lpPacket)
{
BOOL bFlag = FALSE;
PARPHDR pARPHeader;
PARPPACKET pARPPacket;
char MacAddr[6];
GetMacAddr(g_szMyMacAddr, MacAddr);
pARPPacket = (PARPPACKET) ((char*)lpPacket->Buffer + 20);
if (pARPPacket->ehhdr.eh_type == htons(EPT_IP))
return FALSE;
if (strcmp((char*)(pARPPacket->ehhdr.eh_dst), MacAddr) == 0
&& pARPPacket->ehhdr.eh_type == htons(EPT_ARP))
{
char szTemp[10];
pARPHeader = (PARPHDR)((char*)lpPacket->Buffer + 20 + sizeof(EHHDR));
memcpy(szTemp, &pARPHeader->arp_spa, sizeof(pARPHeader->arp_spa));
printf ("A PROMISCUOUS NODE EXISTS!!\n");
printf ("\tIP:%s\n\n", inet_ntoa(*((struct in_addr *)(szTemp))));
return TRUE;
}
return FALSE;
}
拿vc6.0调试通过
H. 有谁知道linux系统环境下,怎样在后台才能获取到mac地址
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
/**
* 与系统相关的一些常用工具方法.
*
* @author stephen
* @version 1.0.0
*/
public class SystemTool {
/**
* 获取当前操作系统名称.
* return 操作系统名称 例如:windows xp,linux 等.
*/
public static String getOSName() {
return System.getProperty("os.name").toLowerCase();
}
/**
* 获取unix网卡的mac地址.
* 非windows的系统默认调用本方法获取.如果有特殊系统请继续扩充新的取mac地址方法.
* @return mac地址
*/
public static String getUnixMACAddress() {
String mac = null;
BufferedReader bufferedReader = null;
Process process = null;
try {
process = Runtime.getRuntime().exec("ifconfig eth0");// linux下的命令,一般取eth0作为本地主网卡 显示信息中包含有mac地址信息
bufferedReader = new BufferedReader(new InputStreamReader(process
.getInputStream()));
String line = null;
int index = -1;
while ((line = bufferedReader.readLine()) != null) {
index = line.toLowerCase().indexOf("hwaddr");// 寻找标示字符串[hwaddr]
if (index >= 0) {// 找到了
mac = line.substring(index +"hwaddr".length()+ 1).trim();// 取出mac地址并去除2边空格
break;
}
}
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
if (bufferedReader != null) {
bufferedReader.close();
}
} catch (IOException e1) {
e1.printStackTrace();
}
bufferedReader = null;
process = null;
}
return mac;
}
/**
* 获取widnows网卡的mac地址.
* @return mac地址
*/
public static String getWindowsMACAddress() {
String mac = null;
BufferedReader bufferedReader = null;
Process process = null;
try {
process = Runtime.getRuntime().exec("ipconfig /all");// windows下的命令,显示信息中包含有mac地址信息
bufferedReader = new BufferedReader(new InputStreamReader(process
.getInputStream()));
String line = null;
int index = -1;
while ((line = bufferedReader.readLine()) != null) {
index = line.toLowerCase().indexOf("physical address");// 寻找标示字符串[physical address]
if (index >= 0) {// 找到了
index = line.indexOf(":");// 寻找":"的位置
if (index>=0) {
mac = line.substring(index + 1).trim();// 取出mac地址并去除2边空格
}
break;
}
}
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
if (bufferedReader != null) {
bufferedReader.close();
}
} catch (IOException e1) {
e1.printStackTrace();
}
bufferedReader = null;
process = null;
}
return mac;
}
/**
* 测试用的main方法.
*
* @param argc
* 运行参数.
*/
public static void main(String[] argc) {
String os = getOSName();
System.out.println(os);
if(os.startsWith("windows")){
//本地是windows
String mac = getWindowsMACAddress();
System.out.println(mac);
}else{
//本地是非windows系统 一般就是unix
String mac = getUnixMACAddress();
System.out.println(mac);
}
}
}
-------------------------------------------------------------------------
本程序可以正确获得本机IP地址和网卡"eth0"的MAC地址,已经在windowsXP和ubuntu-Linux上测试过
(注意:如果有多块网卡,可能出错)
下面给出代码:
import java.net.*;import java.util.*;
public class Test { public static void main(String[] args) { Test t = new Test(); System.out.println(t.getLocalIP()); System.out.println(t.getMacAddr()); }
public String getMacAddr() { String MacAddr = ""; String str = ""; try { NetworkInterface NIC = NetworkInterface.getByName("eth0"); byte[] buf = NIC.getHardwareAddress(); for (int i = 0; i < buf.length; i++) { str = str + byteHEX(buf[i]); } MacAddr = str.toUpperCase(); } catch (SocketException e) { e.printStackTrace(); System.exit(-1); } return MacAddr; }
public String getLocalIP() { String ip = ""; try { Enumeration<?> e1 = (Enumeration<?>) NetworkInterface .getNetworkInterfaces(); while (e1.hasMoreElements()) { NetworkInterface ni = (NetworkInterface) e1.nextElement(); if (!ni.getName().equals("eth0")) { continue; } else { Enumeration<?> e2 = ni.getInetAddresses(); while (e2.hasMoreElements()) { InetAddress ia = (InetAddress) e2.nextElement(); if (ia instanceof Inet6Address) continue; ip = ia.getHostAddress(); } break; } } } catch (SocketException e) { e.printStackTrace(); System.exit(-1); } return ip; }
/* 一个将字节转化为十六进制ASSIC码的函数 */ public static String byteHEX(byte ib) { char[] Digit = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' }; char[] ob = new char[2]; ob[0] = Digit[(ib >>> 4) & 0X0F]; ob[1] = Digit[ib & 0X0F]; String s = new String(ob); return s; }}
I. 如何在delphi中使用winpcap的例子
记得当初能够开始写802.1x的港湾登陆器纯粹是因为loser自己改好了pcap.pas,所以我才能继续的把pcap的代码写下去。最近继续研究delphi和winpcap是因为现在rad平台是做的越来越漂亮了,其实用delphi的控件等等来做我们的界面,而用vc来写我们的主要功能模块不失为一个好的办法。
但是虽然delphi简单快速,可是我还是费了不少时间的,在vc中你很少去处理错误类,而在delphi中try expect 这些错误捕获的代码很多很多,从简单的理解来看,我认为是指针的多次释放,或者销毁对象的多次销毁而引起的,但是不管怎么说,原来我写了这个登陆器的时候,他还是不稳定的,所以虽然引起轰动,但是很快就没了,最后不得已而转向vc开发。
winpcap的几个函数相信你已经知道了,pcap.pas在我的博客中也找的到代码,那么剩下的就自己看看学学吧:
procere recvThread(myconfig:pconfigini);stdcall;
const
cStartPacket:array[0..63] of Byte=($01, $80, $c2, $00, $00, $03,
$ff, $ff, $ff, $ff, $ff, $ff,
$88, $8e, $01, $01, $00, $00,
$a5, $a5, $a5, $a5, $a5, $a5,
$a5, $a5, $a5, $a5, $a5, $a5,
$a5, $a5, $a5, $a5, $a5, $a5,
$a5, $a5, $a5, $a5, $a5, $a5,
$a5, $a5, $a5, $a5, $a5, $a5,
$a5, $a5, $a5, $a5, $a5, $a5,
$a5, $a5, $a5, $a5, $a5, $a5,
$a5, $a5, $a5, $a5);
cuserPacket :array[0..63] of Byte = ($01, $80, $c2, $00, $00, $03,
$ff, $ff, $ff, $ff, $ff, $ff,
$88, $8e, $01, $00, $00, $ff,
$02, $01, $00, $ff, $01, $a5,
$a5, $a5, $a5, $a5, $a5, $a5,
$a5, $a5, $a5, $a5, $a5, $a5,
$a5, $a5, $a5, $a5, $a5, $a5,
$a5, $a5, $a5, $a5, $a5, $a5,
$a5, $a5, $a5, $a5, $a5, $a5,
$a5, $a5, $a5, $a5, $a5, $a5,
$a5, $a5, $a5, $a5);
cResponseMD5 :array[0..63] of Byte = ($01, $80, $c2, $00, $00, $03,
$ff, $ff, $ff, $ff, $ff, $ff,
$88, $8e, $01, $00, $00, $ff,
$02, $02, $00, $ff, $04, $10,
$a5, $a5, $a5, $a5, $a5, $a5,
$a5, $a5, $a5, $a5, $a5, $a5,
$a5, $a5, $a5, $a5, $a5, $a5,
$a5, $a5, $a5, $a5, $a5, $a5,
$a5, $a5, $a5, $a5, $a5, $a5,
$a5, $a5, $a5, $a5, $a5, $a5,
$a5, $a5, $a5, $a5);
cLogoffPacket :array[0..63] of Byte = ($01, $80, $c2, $00, $00, $03,
$ff, $ff, $ff, $ff, $ff, $ff,
$88, $8e, $01, $02, $00, $00,
$a5, $a5, $a5, $a5, $a5, $a5,
$a5, $a5, $a5, $a5, $a5, $a5,
$a5, $a5, $a5, $a5, $a5, $a5,
$a5, $a5, $a5, $a5, $a5, $a5,
$a5, $a5, $a5, $a5, $a5, $a5,
$a5, $a5, $a5, $a5, $a5, $a5,
$a5, $a5, $a5, $a5, $a5, $a5,
$a5, $a5, $a5, $a5);
var
outnetbyte,innetbyte:int64;
StartPacket,userPacket,ResponseMD5,LogoffPacket:array[0..63] of Byte;
szMd5Buffer: Md5Buffer;
nLen: Integer;
md5Ctx: MD5Context;
md5Dig: MD5Digest;
mmcount:string;
pcap:ppcap;
mac:TMacAddr;//物理网卡地址
user:string;
ErrStr:string;
errbuf:array[0..PCAP_ERRBUF_SIZE] of Char;
userlength,i:integer;
buftoint : array[0..3] of byte;
bufint:integer;
headlen2:integer;
pRecvBuf :array of Byte;
pRecvHeaderBuf :ppcap_pkthdr;
q_MD5Source:array[0..15] of byte;
f:TextFile;
textstring:string;
begin
assignfile(f,'test.txt');
for i:=0 to 63 do
begin
StartPacket[i]:=cStartPacket[i];
userPacket[i]:=cuserPacket[i];
ResponseMD5[i]:=cResponseMD5[i];
LogoffPacket[i]:=cLogoffPacket[i];
end;
buftoint[2]:=0;
buftoint[3]:=0;
try
pcap := pcap_open_live(PChar(myconfig^.ethname),
65535, PCAP_OPENFLAG_PROMISCUOUS, 1, errbuf);
except
showmessage('打开网卡失败');
isloop:=false;
exit;
end;
// end;
if pcap<>nil then
begin
mac:=Pcap_GetMacAddress (Pcap, ErrStr) ;
end;
//填写mac地址
for i:=0 to 5 do
begin
StartPacket[i+6] := mac[i];
userPacket[i+6] := mac[i];
ResponseMD5[i+6] := mac[i];
LogoffPacket[i+6] := mac[i];
end;
//填写账号
user:=myconfig^.username;
userlength:=5+length(user);
userPacket[17]:=byte(userlength);
userPacket[21]:=byte(userlength);
for i:= 23 to length(user)+22 do
begin
userpacket[i] := Str_HexToInt(InttoHex( ord(user[i-22]) ,2));
end;
//发送开始认证包
pcap_sendpacket(pcap, @StartPacket, SizeOf(StartPacket)) ;
mmcount:=formatdatetime('ss',now());
while isloop=true do
begin
//
if (formatdatetime('ss',now()) <>mmcount) then
begin
try
PostMessage(myconfig^.formhandle, WM_THREAD_MSG, 4, outnetbyte);
PostMessage(myconfig^.formhandle, WM_THREAD_MSG, 5, innetbyte);
except
showmessage('子线程发送流量数据出错');
end;
mmcount:=formatdatetime('ss',now());
end;
//开始抓包
try
i:= pcap_next_ex(pcap, @pRecvHeaderBuf, @pRecvBuf);
except
showmessage('抓包函数出错');
end;
if( i>0 ) then
begin
if (pRecvBuf[12] = $88) and (pRecvBuf[13] = $8e) then
begin
case precvbuf[18] of
$01:
begin
if (pRecvBuf[22] = $01) then //Request Identity
begin
//form1.Lab_login_state.Caption:='发送账号包';
try
pcap_sendpacket(pcap, @userpacket, SizeOf(userpacket)) ;
except
showmessage('发送账号包出错');
end;
end
else
begin
for i:=0 to 15 do
begin
q_MD5Source[i] := pRecvBuf[i + 24];
end;
PostMessage(myconfig^.formhandle, WM_THREAD_MSG, 0, 0);
szMd5Buffer[0] := $02;
nLen := 1;
CopyMemory(@szMd5Buffer[nLen], PChar(Trim(myconfig^.userpass)), Length(Trim(myconfig^.userpass))); // 用户密码
nLen := nLen + Length(Trim(myconfig^.userpass));
CopyMemory(@szMd5Buffer[nLen], @q_MD5Source, 16); // 服务器返回密钥
nLen := nLen + 16;
for i:=0 to nlen-1 do
begin
textstring:=textstring+inttohex(szMd5Buffer[i],2)+' ';
end;
textstring:=textstring+#13#10;
MD5Init(md5Ctx);
MD5Update(md5Ctx, @szMd5Buffer, nLen);
MD5Final(md5Ctx, md5Dig);
for i:= 24 to 39 do
begin
ResponseMD5[i] := md5dig[i-24];
textstring:=textstring+inttohex(md5dig[i-24],2)+' ';
end;
textstring:=textstring+#13#10;
rewrite(f);
writeln(f,textstring);
responsemd5[17]:=byte(length(myconfig^.username)+22);
responsemd5[21]:=byte(length(myconfig^.username)+22);
for i:= 40 to length(user)+39 do
begin
ResponseMD5[i] := Str_HexToInt(InttoHex( ord(user[i-39]) ,2));
end;
pcap_sendpacket(pcap, @ResponseMD5, SizeOf(ResponseMD5)) ;
end;
end;
$03:
begin
PostMessage(myconfig^.formhandle, WM_THREAD_MSG, 1, 0);
end;
$04:
begin
PostMessage(myconfig^.formhandle, WM_THREAD_MSG, 3, 0);
isloop:=false;
end;
end;
end;
if (pRecvBuf[12] = $08) and (pRecvBuf[13] = $00) then
begin
if (pRecvBuf[23] = $06) then //tcp包
begin
try
buftoint[0]:= pRecvBuf[17];
buftoint[1]:= pRecvBuf[16];
Bufint := integer(buftoint);
except
showmessage('tcp字节转化错误');
end;
headlen2:=20;
case pRecvBuf[46] of
$50:headlen2:=20;
$60:headlen2:=24;
$70:headlen2:=28;
$80:headlen2:=32;
$90:headlen2:=36;
$a0:headlen2:=40;
$b0:headlen2:=44;
$c0:headlen2:=48;
end;
//if totalbyte<3000000000 then totalbyte:=totalbyte+abuf^ - 20 -headlen2;
if ((pRecvBuf[26] = $c0) or (pRecvBuf[26] = $ac) ) and ((pRecvBuf[30] = $c0) or (pRecvBuf[30] = $ac) ) then
begin
try
innetbyte:=innetbyte+ bufint - 20 -headlen2;
except
showmessage('tcp计算出错in');
end;
end
else
begin
try
outnetbyte:=outnetbyte+ bufint - 20 -headlen2;
except
showmessage('tcp计算出错out');
end;
end;
//if (pRecvBuf[26] = $c0) and ( pRecvBuf[30] <> $c0 ) and (pRecvBuf[30] <> $ac) then outnetbyte:=outnetbyte+ abuf^ - 20 -headlen2;
//if (pRecvBuf[30] = $c0) and ( pRecvBuf[26] <> $c0 ) and (pRecvBuf[26] <> $ac) then outnetbyte:=outnetbyte+ abuf^ - 20 -headlen2;
//if (pRecvBuf[26] = $ac) and ( pRecvBuf[30] <> $c0 ) and (pRecvBuf[30] <> $ac) then outnetbyte:=outnetbyte+ abuf^ - 20 -headlen2;
//if (pRecvBuf[30] = $ac) and ( pRecvBuf[26] <> $c0 ) and (pRecvBuf[26] <> $ac) then outnetbyte:=outnetbyte+ abuf^ - 20 -headlen2;
end;
if (pRecvBuf[23] = $11) then //udp包
begin
try
buftoint[0]:= pRecvBuf[17];
buftoint[1]:= pRecvBuf[16];
Bufint := integer(buftoint);
except
showmessage('udp字节转化错误');
end;
if ((pRecvBuf[26] = $c0) or (pRecvBuf[26] = $ac) ) and ((pRecvBuf[30] = $c0) or (pRecvBuf[30] = $ac) ) then
begin
try
innetbyte:=innetbyte+ bufint - 28;
except
showmessage('udp计算出错in');
end;
end
else
begin
try
outnetbyte:=outnetbyte+ bufint - 28;
except
showmessage('udp计算出错out');
end;
end;
end;
end;
end;
end;
pcap_sendpacket(pcap, @LogoffPacket, SizeOf(LogoffPacket)) ;
end;
转载
J. 以太帧是怎么发送
在二层交换网中应用最广泛的是采用IEEE 802.3标准的以太网(Ethernet)。目前,全世界的局域网90%以上是采用以太网技术组网的。随着以太网技术的发展,该技术已经进入接入网和城域网领域。在本讲中,笔者提出了以太网交换技术中存在虚电路的新观点。
1 以太网的分类
以太网的特点是多个数据终端共享传输总线。以太网按其总线的传输速率可划分为10 Mbit/s以太网、100 Mbit/s以太网、1 000 Mbit/s(吉比特)以太网以及10 Gbit/s以太网等;以太网按其总线的传输介质可划分为同轴电缆以太网、双绞线以太网以及光纤(多模、单模)以太网。
2 载波侦听多路访问/冲突检测(CSMA/CD)协议
共享式以太网的核心思想是多个主机共享公共传输通道。在电话通信中采用了时分、频分或码分等方法,使多个用户终端共享公共传输通道。但在数据通信中,数据是突发性的,若占用固定时隙、频段或信道进行数据通信,会造成资源上的浪费。
若多个主机共享公共传输通道(总线)而不采取任何措施,必然会产生碰撞与冲突。CSMA/CD协议正是为解决多个主机争用公共传输通道而制定的。
(1) 载波侦听多路访问(CSMA)
每个以太网帧(MAC帧)均有源主机和宿主机的物理地址(MAC地址)。当网上某台主机要发送MAC帧时,应先监听信道。如果信道空闲,则发送;如果发现信道上有载波(指基带信号),则不发送,等信道空闲时立即发送或延迟一个随机时间再发送,从而大大减少碰撞的次数。
(2) 碰撞检测(CD)
对于碰撞检测,在一般情况下,当总线上的信号摆动超过正常值时,即认为发生冲突。这种检测方法容易出错,因为信号在线路上传播时存在衰耗,当两个主机相距很远时,另一台主机的信号到达时已经很弱,与本地主机发送的信号叠加时,达不到冲突检测的幅度,就会出错。为此,IEEE 802?郾3标准中限制了线缆的长度。目前,应用较多的冲突检测方法是主机的发送器把数据发送到线缆上,该主机的接收机又把数据接收回来,然后与发送数据相比,判别是否一致。若一致,则无冲突发生;若不一致,则表示有冲突发生。
3 MAC帧格式
每一帧以7个字节的前导码开始,前导码为“1010”交替码,其作用是使目的主机接收器时钟与源主机发送器时钟同步。紧接着是帧开始分界符字节“10101011”,用于指示帧的开始。
帧包括两个地址:目的地址和源地址。目的地址最高位如为“0”,则表示普通地址;如为“1”,则表示组地址。地址的次高位用于区分是局部地址还是全局地址。局部地址由局部网络管理者分配,离开这个局部网,该地址就毫无意义。全局地址由IEEE统一分配,以保证全世界没有两个主机具有相同的全局地址。允许大约有7×1013个全局地址。全局地址可用于全球性的MAC帧寻址。
数据域长度给出数据域中存在多少个字节的数据,其值为0~1 500。数据域长度为“0”是合法的,但太短的帧在传送过程中可能会产生问题,其中一个原因就是:当主机检测到冲突时,便停止发送,这时一部分数据已经发送到线缆上,而目的主机却无法简单区分这是正确帧还是垃圾帧。为此,IEEE规定:正确长度必须大于64字节,如果小于64字节,那么必须用填充字段填充到帧的最小长度。
4 以太网的互联
根据OSI 7层模型,以太网可以在低3层和高3层上互联。实现互联的网元设备有中继器、集线器、网桥、路由器、交换机和网关。
4.1 中继器
中继器工作在OSI 7层模型的物理层。因为数字脉冲信号经过一定距离的传输后,会产生衰耗和波形失真,在接收端引起误码。中继器的作用是再生(均衡放大、整形)通过网络传输的数据信号,扩展局域网的范围。
中继器工作在物理层,对高层协议是完全透明的。用中继器相联的两个网络,对链路层而言相当于一个网络,中继器仅起到扩展距离的作用,而不能提供隔离和扩展有效带宽的作用。
4.2 集线器(Hub)
集线器就像一个星型结构的多端口转发器,每个端口都具有发送与接收数据的能力。当某个端口收到连在该端口上的主机发来的数据时,就转发至其它端口。在数据转发之前,每个端口都对它进行再生、整形,并重新定时。
集线器可以互相串联,形成多级星型结构,但相隔最远的两个主机受最大传输延时的限制,因此只能串联几级。当连接的主机数过多时,总线负载很重,冲突将频频发生,导致网络利用率下降。
与中继器一样,集线器工作在OSI 7层模型的物理层,不能提供隔离作用,相当于一个多端口的中继器。
4.3 网桥
网桥工作在OSI 7层模型的链路层(MAC层)。当一个以太网帧通过网桥时,网桥检查该帧的源和目的MAC地址。如果这两个地址分别属于不同的网络,则网桥将该MAC帧转发到另一个网络上,反之不转发。所以,网桥具有过滤与转发MAC帧的功能,能起到网络间的隔离作用。对共享型网络而言,网络间的隔离意味着提高了网络的有效带宽。
网桥最简单的形式是连接两个局域网的两端口网桥。在多个局域网互联时,为不降低网络的有效带宽,可以采用多端口网桥或以太网交换机。但采用这些工作在链路层的设备联网,存在以下缺点:
(1) 多端口网桥或以太网交换机只有简单的路由表,当某一端口收到一个数据包,若设备根据其目的地址找不到对应的输出端口时,即对所有端口广播这个包,当网络较大时易引起广播风暴;
(2) 多端口网桥或以太网交换机无链路层协议转换功能,因此不能做到不同协议网络的互联,例如以太网与X.25、FR、N-ISDN和ATM等网络的互联。
4.4 路由器
在路由器中存放有庞大而复杂的路由表,并能根据网络拓扑、负荷的改变及时维护该路由表。当路由器找不到某一端口输入的数据包对应的输出端口时,即删除该包。因为路由器废除了广播机制,所以可以抑制广播风暴。
4.5 网关
网关工作在OSI 7层模型的高3层,即对话层、表示层和应用层。网关用于两个完全不同网络的互联,其特点是具有高层协议的转换功能。网关最典型的应用是IP电话网关。IP电话网关将时分复用的64 kbit/s编码话音和No?郾7共路信令转换为IP包,送入Internet进行传输,从而使PSTN和Internet两个完全不同的网络可以互联互通。
5 以太网交换机
5.1 以太网交换机的基本原理
大型网络为了提高网络的效率,需要将网络在链路层上进行分段,以提高网络的有效带宽。对于小型网络,可以利用网桥对网络进行分段;对于大型网络,往往采用以太网交换机对网络进行分段,即利用以太网交换机将一个共享型以太网分割成若干个网段。分段后的网络称为交换型以太网。在交换型以太网中,工作在每一网段中的主机对介质的争用仍采用CSMA/CD机制,而联接各网段的交换机则采用路由机制。若某一共享型以太网带宽为M,共带有N台主机,则每台主机平均带宽为M/N。若在该网内引入一台8端口的以太网交换机,将该网分割为8个网段,则每一网段带宽仍为M,而总带宽则拓宽至8M。
目前,大中型以太网中引入了多台交换机的级联工作方式。处在用户级的交换机一般可做到1个端口接1台主机,则该主机可享用所连接端口的全部带宽,无需竞争网络资源。
在以太网中引入交换机将网络分段后,是否能使网络容量无限扩大?答案是否定的。因为在以太网交换机中对MAC帧的寻址采用了广播方式,网络太大时易引起广播风暴。这就需要有路由器对网络在网络层上进行分段。路由器将计算机网分割成若干个子网,从而缩小了其底层以太网的广播域,抑制了广播风暴。
5.2 以太网交换机的路由方式
当该交换机中的某一个端口接收到一个MAC帧时,交换机的首要任务是根据该MAC帧的目的地址寻找输出端口,然后向该输出端口转发这个MAC帧。
通常情况下,在以太网交换机中存有一张路由表,该表根据所接收MAC帧的目的地址,为每个MAC帧选择输出端口。
(1) 固定路由
固定路由是指交换机有一张人工配置的路由表,表上标明各端口及其所对应的目的地址。固定路由虽然不失为一种路由方式,但如果网络规模过大,则配置路由表将变成一项很繁重的工作,再加上交换机所处的网络经常会变更网络配置或增删主机,网络管理员很难使路由表及时更新来适应拓扑结构的变化。
(2) 自学习路由
在实际应用中,通常通过自学习方法来建立一张动态路由表,以自动适应网络拓扑结构的变化。该动态路由表可在人工建立的路由表的基础上,通过自学习过程不断修改而得到。
所谓自学习,即是根据到达每一端口MAC帧的源地址来建立或刷新路由表。假设交换机从X端口收到一个MAC帧,检查该MAC帧的源地址为A地址,则说明凡是目的地址为A地址的MAC帧,应该通过X端口转发。从X端口收到源地址为A地址的MAC帧后,交换机控制部分检查路由表。若路由表中目的地址一项无A地址,则在X端口对应的目的地址项中增加A地址内容;若表中目的地址一项有A地址,但其对应端口为Y端口,则需修改路由表。
由上可见,以太网交换机利用广播帧和自学习的方法来建立路由表,一旦配置好路由表,后续的以太帧根据目的MAC地址(未使用标记)和路由表选择路由,从而形成一条从源主机到目的 主机的虚电路。