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地址(未使用標記)和路由表選擇路由,從而形成一條從源主機到目的 主機的虛電路。