1. 網卡如何與uIP協議交互(包括arp, icmp等)
在我看來,CP2200提供了讀取網絡數據的能力,而UIP提供的是一種如何封裝網路數據的策略。對用戶數
據不斷封裝,最後交給CP2200發送,在UIP協議中有一個uip_buf緩衝用來接收和發送數據。
(轉自:維庫電子開發網>電子通列表 > 協議棧)
ARP請求和應答
在UIP協議中定義了一個ARP的struct。維護了一張緩存表。
ARP請求發送函數:void uip_arp_out(void)
* 爲傳出的IP包添加以太網頭並看是否需要發送ARP請求.
* 此函數應該在發送IP包時調用,它會檢查IP包的目的IP地址,看看以太網應該使用什麼目的MAC地址.
* 如果目的IP地址是在局域網中(由IP地址與子網掩碼的與邏輯決定),函數就會從ARP緩存表中查找有
* 無對應項.若有,就取對應的MAC地址,加上以太網頭,並返回,否則uip_buf[]中的數據包會被替換成一個
* 目的IP在址的ARP請求.原來的IP包會被簡單的仍掉,此函數假設高層協議(如TCP)會最終重傳扔掉的包.
* 如果目標IP地址並非一個局域網IP,則會使用默認路由的IP地址.
* uip_len.函數返回時,uip_buf[]中已經有了一個包,其長度由uip_len指定.
在我看來,CP2200提供了讀取網絡數據的能力,而UIP提供的是一種如何封裝網路數據的策略。對用戶數
據不斷封裝,最後交給CP2200發送,在UIP協議中有一個uip_buf緩衝用來接收和發送數據。
(轉自:維庫電子開發網>電子通列表 > 協議棧)
ARP請求和應答
在UIP協議中定義了一個ARP的struct。維護了一張緩存表。
-
struct arp_entry {
-
u16_t ipaddr[2]; //
保存的是IP地址
-
struct uip_eth_addr ethaddr; // 保存的是mac地址
-
u8_t time; // 緩存更新時間
- };
* 爲傳出的IP包添加以太網頭並看是否需要發送ARP請求.
* 此函數應該在發送IP包時調用,它會檢查IP包的目的IP地址,看看以太網應該使用什麼目的MAC地址.
* 如果目的IP地址是在局域網中(由IP地址與子網掩碼的與邏輯決定),函數就會從ARP緩存表中查找有
* 無對應項.若有,就取對應的MAC地址,加上以太網頭,並返回,否則uip_buf[]中的數據包會被替換成一個
* 目的IP在址的ARP請求.原來的IP包會被簡單的仍掉,此函數假設高層協議(如TCP)會最終重傳扔掉的包.
* 如果目標IP地址並非一個局域網IP,則會使用默認路由的IP地址.
* uip_len.函數返回時,uip_buf[]中已經有了一個包,其長度由uip_len指定.
-
void uip_arp_out(void)
-
{
-
struct arp_entry *tabptr=0;
- // 在ARP表中找到目的IP地址,構成以太網頭.如果目的IP地址不在局域網中,則使用默認路由的IP.
-
// 如果ARP表中找不到,則將原來的IP包替換成一個ARP請求.
- // 首先檢查目標是不是廣播
- if(uip_ipaddr_cmp(IPBUF->destipaddr, broadcast_ipaddr))
-
{
-
memcpy(IPBUF->ethhdr.dest.addr, broadcast_ethaddr.addr, 6);
-
}
- else
-
{
-
/* 檢查目標地址是否在局域網內 */
- if(!uip_ipaddr_maskcmp(IPBUF->destipaddr, uip_hostaddr, uip_netmask))
-
{
-
/* 目的地址不在局域網內,所以保用默認路由器的地址來確在MAC地址 */
-
uip_ipaddr_copy(ipaddr, uip_draddr);
-
}
-
else
-
{
-
/* 否則,使用目標IP地址 */
-
uip_ipaddr_copy(ipaddr, IPBUF->destipaddr);
-
}
- //這裏遍歷表,對比目的IP與ARP緩存表中的IP.
- for(i = 0; i < UIP_ARPTAB_SIZE; ++i)
-
{
-
tabptr = &arp_table[i];
- if(uip_ipaddr_cmp(ipaddr, tabptr->ipaddr))
-
{
-
break;
-
}
-
}
- if(i == UIP_ARPTAB_SIZE)
-
{
-
/* 如果遍歷到頭沒找到,將原IP包替換爲ARP請求並返回 */
-
memset(BUF->ethhdr.dest.addr, 0xff, 6); //
以太網目的地址
- memset(BUF->dhwaddr.addr, 0x00, 6); // 目的以太網地址
-
memcpy(BUF->ethhdr.src.addr, uip_ethaddr.addr, 6);
//
- memcpy(BUF->shwaddr.addr, uip_ethaddr.addr, 6); // 源以太網地址
-
- uip_ipaddr_copy(BUF->dipaddr, ipaddr); // 目的IP地址
-
uip_ipaddr_copy(BUF->sipaddr, uip_hostaddr); //
源IP地址
-
BUF->opcode = HTONS(ARP_REQUEST); // ARP
請求
-
BUF->hwtype = HTONS(ARP_HWTYPE_ETH); //
硬件類型 值爲1
-
BUF->protocol = HTONS(UIP_ETHTYPE_IP); //
協議類型 值爲0x8000表示IP地址
-
BUF->hwlen = 6;
-
BUF->protolen = 4;
- BUF->ethhdr.type = HTONS(UIP_ETHTYPE_ARP);
-
-
uip_appdata = &uip_buf[UIP_TCPIP_HLEN + UIP_LLH_LEN];
-
uip_len = sizeof(struct arp_hdr);
-
return;
-
}
-
-
// 如果是在局域網中,且在ARP緩存中找到了(如果沒找到進行不到這一步,在上面就返回了),則構建以太網頭
-
memcpy(IPBUF->ethhdr.dest.addr, tabptr->ethaddr.addr, 6);
-
}
-
memcpy(IPBUF->ethhdr.src.addr, uip_ethaddr.addr, 6);
-
IPBUF->ethhdr.type = HTONS(UIP_ETHTYPE_IP);
-
uip_len += sizeof(struct uip_eth_hdr);
- }
-
接下來看看UIP如何處理ARP應答的情況,在主循環中一段代碼:
- else if(BUF->type == htons(UIP_ETHTYPE_ARP))
-
{
-
uip_arp_arpin(); //
處理ARP應答
-
/* If the above function invocation
resulted in data that
-
should be sent out on the network, the global variable
- uip_len is set to a value > 0. */
-
// 如果上面的函數返回的結果需要發送到網絡上,那麼uip_len就必須設置 > 0
-
if(uip_len > 0)
-
{
-
network_device_send(); //
迴應ARP包
-
}
-
}
-
這個函數是在設備接收到ARP包時,由驅動程序調用的.如果收到是ARP包是一個對本地主機上次發送的ARP請求的應答,那麼就從包中取得自己想要的主機的MAC地址,加入自己的ARP緩存表中.如果收到是一個ARP請求,那就把自己的MAC地址打包成一個ARP應答,發送給請求的主機.看代碼uip_arp.c的254行:
-
void uip_arp_arpin(void)
-
{
- if(uip_len < sizeof(struct arp_hdr))
-
{
-
uip_len = 0;
-
return;
-
}
-
uip_len = 0;
-
-
switch(BUF->opcode) //
操作碼
-
{
-
case HTONS(ARP_REQUEST):
- // 如果是一個ARP請求,則發送應答
- if(uip_ipaddr_cmp(BUF->dipaddr, uip_hostaddr))
-
{
- // 首先,我們將發送請求的主機註冊到ARP緩存表中,因爲我們很
- // 可能要跟它要有更多的交流
- uip_arp_update(BUF->sipaddr, &BUF->shwaddr);
-
-
// 迴應的操作碼是 2.
-
BUF->opcode = HTONS(2);
-
// 將收到的ARP包的發送端以太網地址,變爲目的以太網地址
-
memcpy(BUF->dhwaddr.addr, BUF->shwaddr.addr, 6);
-
// 將自己的以太網地址,賦值給ARP包的發送端以太網地址
- memcpy(BUF->shwaddr.addr, uip_ethaddr.addr, 6);
-
// 對應以太網源地址
- memcpy(BUF->ethhdr.src.addr, uip_ethaddr.addr, 6);
-
// 對應以太網目的地址
-
memcpy(BUF->ethhdr.dest.addr, BUF->dhwaddr.addr, 6);
-
-
BUF->dipaddr[0] = BUF->sipaddr[0];
-
BUF->dipaddr[1] = BUF->sipaddr[1];
-
BUF->sipaddr[0] = uip_hostaddr[0];
-
BUF->sipaddr[1] = uip_hostaddr[1];
-
-
BUF->ethhdr.type = HTONS(UIP_ETHTYPE_ARP);
-
uip_len = sizeof(struct arp_hdr);
-
}
- break;
- // 如果收到的是一個ARP應答,而且也是我們所要的應答的話,就插件
- // 並更新ARP緩存表
-
case HTONS(ARP_REPLY):
- if(uip_ipaddr_cmp(BUF->dipaddr, uip_hostaddr))
-
{
-
uip_arp_update(BUF->sipaddr, &BUF->shwaddr);
-
}
-
break;
-
}
-
-
return;
- }
// 每10秒運行一次具體的代碼:
if(timer_expired(&arp_timer))
{
timer_reset(&arp_timer);
uip_arp_timer();
}
-
void uip_arp_timer(void)
-
{
-
struct arp_entry *tabptr;
- ++arptime; // 這個是個全局變量,結合uip_arp_update來更新緩存表
- for(i = 0; i < UIP_ARPTAB_SIZE; ++i)
-
{
- tabptr = &arp_table[i];
-
// 把超過20分鐘都沒有更新的項扔掉
-
if((tabptr->ipaddr[0] | tabptr->ipaddr[1]) != 0 &&
- arptime - tabptr->time >= UIP_ARP_MAXAGE)
-
{
-
memset(tabptr->ipaddr, 0, 4);
-
}
-
}
- }