IP協(xié)議的功能
回顧一下前面寫(xiě)的關(guān)于IP協(xié)議的文章:
IP協(xié)議基礎(chǔ)掃盲班
IP地址相關(guān)知識(shí)深入了解~
IP數(shù)據(jù)報(bào)分析
IP數(shù)據(jù)報(bào)結(jié)構(gòu)、IP分片的原理與處理
IP數(shù)據(jù)報(bào)的收發(fā)
回顧一下前面的文章所提及的知識(shí)點(diǎn),總結(jié)一下IP協(xié)議的功能,得到以下結(jié)論:
- 編址(目標(biāo)端的IP地址),數(shù)據(jù)傳輸?shù)倪^(guò)程當(dāng)中就必須表明要發(fā)送目標(biāo)端的IP地址
- 尋址和路由(根據(jù)對(duì)方的IP地址,尋找最佳路徑傳輸信息);
- 數(shù)據(jù)報(bào)的分片和重組。
- 傳遞服務(wù)是不可靠的(IP協(xié)議只是盡自己最大努力去傳輸數(shù)據(jù)包),它也是無(wú)連接的協(xié)議
IP數(shù)據(jù)報(bào)發(fā)送
IP協(xié)議是網(wǎng)絡(luò)層的主要協(xié)議,在上層傳輸協(xié)議(如TCP/UDP)需要發(fā)送數(shù)據(jù)時(shí),會(huì)將數(shù)據(jù)封裝起來(lái),然后傳遞到IP層,IP協(xié)議首先會(huì)根據(jù)上層協(xié)議的目標(biāo)IP地址選擇一個(gè)合適的網(wǎng)卡進(jìn)行發(fā)送數(shù)據(jù)( 路由
),然后IP協(xié)議將再次封裝數(shù)據(jù)形成IP數(shù)據(jù)報(bào),主要的操作就是填寫(xiě)IP數(shù)據(jù)報(bào)首部對(duì)應(yīng)的各個(gè)字段:目標(biāo)IP地址、源IP地址、協(xié)議類(lèi)型、生存時(shí)間等
,最后在IP層通過(guò)回調(diào)函數(shù)netif->output(即etharp_output()函數(shù))將IP數(shù)據(jù)報(bào)投遞給ARP,再調(diào)用網(wǎng)卡底層發(fā)送函數(shù)進(jìn)行發(fā)送,這樣子自上而下的數(shù)據(jù)就發(fā)送出去,IP協(xié)議以目標(biāo)IP地址作為目標(biāo)主機(jī)的身份地址。
/**
* ip_output_if的簡(jiǎn)化版接口。它找到發(fā)送數(shù)據(jù)包的netif網(wǎng)絡(luò)接口并調(diào)用ip_output_if來(lái)完成實(shí)際工作。
*
* @param p 要發(fā)送的數(shù)據(jù)包(p->payload(有效負(fù)載)指向數(shù)據(jù),如果dest == LWIP_IP_HDRINCL,則p已包含IP頭和p->有效負(fù)載指向該IP頭)
* @param src 要發(fā)送的源IP地址(如果src == IP4_ADDR_ANY,則用發(fā)送的netif綁定的IP地址用作源地址)
* @param dest 目的IP地址
* @param ttl 要在IP標(biāo)頭中設(shè)置的TTL值(生存時(shí)間)
* @param tos 用于在IP標(biāo)頭中設(shè)置的TOS值
* @param proto 將在IP頭中設(shè)置對(duì)應(yīng)的上層協(xié)議
* @return ERR_OK 如果數(shù)據(jù)包發(fā)送正常就返回ok,
* 如果p沒(méi)有足夠的空間用于IP /LINK標(biāo)頭,則為ERR_BUF
* 其他則返回netif->output返回的錯(cuò)誤
* @return ERR_RTE如果沒(méi)有找到路線
* 請(qǐng)參閱ip_output_if()以獲取更多返回值
*/
err_t
ip4_output(struct pbuf *p,constip4_addr_t*src,constip4_addr_t*dest,
u8_t ttl,u8_t tos,u8_t proto)
{
struct netif *netif;
LWIP_IP_CHECK_PBUF_REF_COUNT_FOR_TX(p);
//根據(jù)目標(biāo)IP地址找到對(duì)應(yīng)的網(wǎng)卡發(fā)送數(shù)據(jù)
if((netif = ip4_route_src(src, dest))== NULL){
LWIP_DEBUGF(IP_DEBUG,("ip4_output: No route to %"U16_F".%"U16_F".%"U16_F".%"U16_F"\\n",
ip4_addr1_16(dest), ip4_addr2_16(dest), ip4_addr3_16(dest), ip4_addr4_16(dest)));
IP_STATS_INC(ip.rterr);
return ERR_RTE;
}
return ip4_output_if(p, src, dest, ttl, tos, proto, netif);
}
路由過(guò)程的實(shí)現(xiàn)
路由(routing)就是通過(guò)互聯(lián)的網(wǎng)絡(luò)把信息從源地址傳輸?shù)侥康牡刂返幕顒?dòng),發(fā)送端必然需要找到一個(gè)網(wǎng)卡將數(shù)據(jù)報(bào)發(fā)送出去,而實(shí)現(xiàn)這個(gè)過(guò)程的函數(shù)就是 ip4_route_src()
。
其實(shí)lwip對(duì) ip4_route_src()
函數(shù)進(jìn)行了重新定義,實(shí)際上是調(diào)用了 ip4_route()
函數(shù)。這個(gè)函數(shù)的原理就是根據(jù)指定的IP地址找到合適的網(wǎng)卡 netif
,然后返回,前面的文章也提到過(guò),lwip的網(wǎng)卡是通過(guò) netif_list
列表管理的,那么找網(wǎng)卡的操作也必然是遍歷網(wǎng)卡列表 netif_list
,判斷網(wǎng)卡是否已經(jīng)掛載并且IP地址是否有效,如果連網(wǎng)卡都找不到,那就不用發(fā)送數(shù)據(jù)了,返回null。
#define ip4_route_src(src, dest) ip4_route(dest)
/**
*為給定的IP地址查找適當(dāng)?shù)木W(wǎng)絡(luò)接口。
*它搜索網(wǎng)絡(luò)接口列表。找到匹配項(xiàng)
*
*@param dest 要查找路由的目標(biāo)IP地址
*@return 發(fā)送到達(dá)目的地的網(wǎng)卡 netif
*/
struct netif *
ip4_route(constip4_addr_t*dest)
{
#if!LWIP_SINGLE_NETIF
struct netif *netif;
LWIP_ASSERT_CORE_LOCKED();
#if LWIP_MULTICAST_TX_OPTIONS
/*默認(rèn)使用管理選擇的接口進(jìn)行多播*/
if(ip4_addr_ismulticast(dest)&& ip4_default_multicast_netif){
return ip4_default_multicast_netif;
}
#endif /* LWIP_MULTICAST_TX_OPTIONS */
/* bug #54569: in case LWIP_SINGLE_NETIF=1 and LWIP_DEBUGF() disabled, the following loop is optimized away */
LWIP_UNUSED_ARG(dest);
/*遍歷網(wǎng)卡列表netif_list */
NETIF_FOREACH(netif){
/* 如果網(wǎng)卡已經(jīng)掛載并且IP地址是有效的 */
if(netif_is_up(netif)&& netif_is_link_up(netif)&&!ip4_addr_isany_val(*netif_ip4_addr(netif))){
/* 網(wǎng)絡(luò)掩碼匹配? */
if(ip4_addr_netcmp(dest, netif_ip4_addr(netif), netif_ip4_netmask(netif))){
/* 返回找到的網(wǎng)卡netif */
return netif;
}
/* 網(wǎng)關(guān)在非廣播接口上匹配?(即在點(diǎn)對(duì)點(diǎn)接口中對(duì)等) */
if(((netif->flags & NETIF_FLAG_BROADCAST)==0)&& ip4_addr_cmp(dest, netif_ip4_gw(netif))){
/* 返回找到的網(wǎng)卡netif */
return netif;
}
}
}
#if LWIP_NETIF_LOOPBACK &&!LWIP_HAVE_LOOPIF /**如果打開(kāi)環(huán)回地址的宏定義 */
/* loopif is disabled, looopback traffic is passed through any netif */
if(ip4_addr_isloopback(dest)){
/*不檢查環(huán)回流量的鏈接*/
if(netif_default != NULL && netif_is_up(netif_default)){
return netif_default;
}
/*默認(rèn)netif沒(méi)有啟動(dòng),只需使用任何netif進(jìn)行環(huán)回流量*/
NETIF_FOREACH(netif){
if(netif_is_up(netif)){
return netif;
}
}
return NULL;
}
#endif /* LWIP_NETIF_LOOPBACK && !LWIP_HAVE_LOOPIF */
#ifdef LWIP_HOOK_IP4_ROUTE_SRC
netif = LWIP_HOOK_IP4_ROUTE_SRC(NULL, dest);
if(netif != NULL){
return netif;
}
#elif defined(LWIP_HOOK_IP4_ROUTE)
netif = LWIP_HOOK_IP4_ROUTE(dest);
if(netif != NULL){
return netif;
}
#endif
#endif /* !LWIP_SINGLE_NETIF */
if((netif_default == NULL)||!netif_is_up(netif_default)||!netif_is_link_up(netif_default)||
ip4_addr_isany_val(*netif_ip4_addr(netif_default))|| ip4_addr_isloopback(dest)){
/*找不到匹配的netif,默認(rèn)的netif不可用。建議使用LWIP_HOOK_IP4_ROUTE()*/
LWIP_DEBUGF(IP_DEBUG | LWIP_DBG_LEVEL_SERIOUS,("ip4_route: No route to %"U16_F".%"U16_F".%"U16_F".%"U16_F"\\n",
ip4_addr1_16(dest), ip4_addr2_16(dest), ip4_addr3_16(dest), ip4_addr4_16(dest)));
IP_STATS_INC(ip.rterr);
MIB2_STATS_INC(mib2.ipoutnoroutes);
return NULL;
}
return netif_default;
}
ip4_output_if函數(shù)
找到網(wǎng)卡之后就調(diào)用 ip4_output_if()
函數(shù)將數(shù)據(jù)發(fā)送出去,這個(gè)函數(shù)會(huì)指定發(fā)送數(shù)據(jù)的網(wǎng)卡,同時(shí)會(huì)將來(lái)自上層協(xié)議(tcp、udp)的數(shù)據(jù)進(jìn)行封裝,組成IP數(shù)據(jù)報(bào)再發(fā)送,不過(guò)這個(gè)函數(shù)層層調(diào)用,比較麻煩,具體如下:。
/**
* 在網(wǎng)絡(luò)接口上發(fā)送IP數(shù)據(jù)包。這個(gè)函數(shù)構(gòu)造IP數(shù)據(jù)包首部并計(jì)算IP頭校驗(yàn)和,
* 如果源IP地址為NULL,在發(fā)送的時(shí)候就填寫(xiě)發(fā)送網(wǎng)卡的IP地址為源IP地址
* 如果目標(biāo)IP地址是LWIP_IP_HDRINCL,則假定pbuf已經(jīng)存在包括IP頭和有效負(fù)載指向它而不是數(shù)據(jù)。
*
* @param p 要發(fā)送的數(shù)據(jù)包(p->payload(有效負(fù)載)指向數(shù)據(jù),如果dest == LWIP_IP_HDRINCL,則p已包含IP頭和p->有效負(fù)載指向該IP頭)
* @param src 要發(fā)送的源IP地址(如果src == IP4_ADDR_ANY,則用發(fā)送的netif綁定的IP地址用作源地址)
* @param dest 目的IP地址
* @param ttl 要在IP標(biāo)頭中設(shè)置的TTL值(生存時(shí)間)
* @param tos 用于在IP標(biāo)頭中設(shè)置的TOS值
* @param proto 將在IP頭中設(shè)置對(duì)應(yīng)的上層協(xié)議
* @param netif 發(fā)送此數(shù)據(jù)包的netif
* @return ERR_OK 如果數(shù)據(jù)包發(fā)送正常就返回ok,
* 如果p沒(méi)有足夠的空間用于IP /LINK標(biāo)頭,則為ERR_BUF
* 其他則返回netif->output返回的錯(cuò)誤
*
* @note ip_id:RFC791“某些主機(jī)可能只需使用
* 獨(dú)立于目的地的唯一標(biāo)識(shí)符“
*/
err_t
ip4_output_if(struct pbuf *p,constip4_addr_t*src,constip4_addr_t*dest,
u8_t ttl,u8_t tos,
u8_t proto,struct netif *netif)
{
#if IP_OPTIONS_SEND
return ip4_output_if_opt(p, src, dest, ttl, tos, proto, netif, NULL,0);
}
/**
* 與ip_output_if()相同,但可以包含IP選項(xiàng):
*
* @param ip_options指向IP選項(xiàng)的指針,復(fù)制到IP頭中
* @param optlen ip_options的長(zhǎng)度
*/
err_t
ip4_output_if_opt(struct pbuf *p,constip4_addr_t*src,constip4_addr_t*dest,
u8_t ttl,u8_t tos,u8_t proto,struct netif *netif,void*ip_options,
u16_t optlen)
{
#endif /* IP_OPTIONS_SEND */
constip4_addr_t*src_used = src;
if(dest != LWIP_IP_HDRINCL){
if(ip4_addr_isany(src)){
src_used = netif_ip4_addr(netif);
}
}
#if IP_OPTIONS_SEND
return ip4_output_if_opt_src(p, src_used, dest, ttl, tos, proto, netif,
ip_options, optlen);
#else/* IP_OPTIONS_SEND */
return ip4_output_if_src(p, src_used, dest, ttl, tos, proto, netif);
#endif /* IP_OPTIONS_SEND */
}
ip4_output_if_opt_src
首先看看這個(gè)函數(shù)到底做了什么吧:在上層協(xié)議遞交數(shù)據(jù)包后,通過(guò)層層調(diào)用,最終到 ip4_output_if_opt_src()
函數(shù)中處理,它的處理如下:代碼的實(shí)現(xiàn)如下:注釋非常豐富。主要過(guò)程就是:
- 判斷是否填寫(xiě)好IP數(shù)據(jù)報(bào)首部?若目標(biāo)IP地址為L(zhǎng)WIPIPHDRINCL表示已經(jīng)填寫(xiě)好IP數(shù)據(jù)報(bào)首部,且payload指針也指向了IP數(shù)據(jù)報(bào)首部。
- 如果沒(méi)有填寫(xiě)IP數(shù)據(jù)報(bào)首部,調(diào)用
pbuf_add_header()
函數(shù)調(diào)整數(shù)據(jù)區(qū)域指針以指向IP數(shù)據(jù)報(bào)首部。 - 填寫(xiě)IP數(shù)據(jù)報(bào)中的生存時(shí)間、服務(wù)類(lèi)型、上層協(xié)議、目標(biāo)IP地址、版本號(hào)與首部長(zhǎng)度、數(shù)據(jù)報(bào)總長(zhǎng)度、標(biāo)志位和分片偏移量、標(biāo)識(shí)、源IP地址等內(nèi)容,總之就是將IP數(shù)據(jù)報(bào)首部的內(nèi)容該填的都填上。
- 如果目標(biāo)IP地址是自己的網(wǎng)卡IP地址,調(diào)用環(huán)回輸入函數(shù)
netif_loop_output()
發(fā)送IP數(shù)據(jù)報(bào)給自己,這種處理一般是用于測(cè)試代碼。 - 如果IP數(shù)據(jù)報(bào)太大,數(shù)據(jù)報(bào)總長(zhǎng)度大于網(wǎng)卡的MTU,則需要進(jìn)行分片處理,調(diào)用
ip4_frag()
函數(shù)進(jìn)行發(fā)送。 - 直接調(diào)用注冊(cè)的
netif->output
接口傳遞給ARP,實(shí)際上就是調(diào)用etharp_output()
函數(shù),在這里它會(huì)將IP地址解析成對(duì)應(yīng)的MAC
地址,并且調(diào)用網(wǎng)卡發(fā)送函數(shù)進(jìn)行發(fā)送。
/**
* 與ip4_output_if_opt()相同,當(dāng)源地址是'IP4_ADDR_ANY'時(shí),'src'地址不會(huì)被netif地址替換
*/
err_t
ip4_output_if_opt_src(struct pbuf *p,constip4_addr_t*src,constip4_addr_t*dest,
u8_t ttl,u8_t tos,u8_t proto,struct netif *netif,void*ip_options,
u16_t optlen)
{
#endif /* IP_OPTIONS_SEND */
struct ip_hdr *iphdr;
ip4_addr_t dest_addr;
#if CHECKSUM_GEN_IP_INLINE
u32_t chk_sum =0;
#endif /* CHECKSUM_GEN_IP_INLINE */
LWIP_ASSERT_CORE_LOCKED();
LWIP_IP_CHECK_PBUF_REF_COUNT_FOR_TX(p);
MIB2_STATS_INC(mib2.ipoutrequests);
/* 應(yīng)該要構(gòu)建IP首部還是已經(jīng)包含在pbuf中了?如果是要構(gòu)建IP數(shù)據(jù)報(bào)首部 */
if(dest != LWIP_IP_HDRINCL){
u16_t ip_hlen = IP_HLEN;
#if IP_OPTIONS_SEND
u16_t optlen_aligned =0;
if(optlen !=0){
#if CHECKSUM_GEN_IP_INLINE
int i;
#endif /* CHECKSUM_GEN_IP_INLINE */
if(optlen >(IP_HLEN_MAX - IP_HLEN)){
/* 選項(xiàng)字段太長(zhǎng) */
LWIP_DEBUGF(IP_DEBUG | LWIP_DBG_LEVEL_SERIOUS,("ip4_output_if_opt: optlen too long\\n"));
IP_STATS_INC(ip.err);
MIB2_STATS_INC(mib2.ipoutdiscards);
return ERR_VAL;
}
/* 選項(xiàng)字段按照4字節(jié)對(duì)齊 */
optlen_aligned =(u16_t)((optlen +3)&~3);
ip_hlen =(u16_t)(ip_hlen + optlen_aligned);
/* 首先寫(xiě)入IP選項(xiàng)字段 */
if(pbuf_add_header(p, optlen_aligned)){
LWIP_DEBUGF(IP_DEBUG | LWIP_DBG_LEVEL_SERIOUS,("ip4_output_if_opt: not enough room for IP options in pbuf\\n"));
IP_STATS_INC(ip.err);
MIB2_STATS_INC(mib2.ipoutdiscards);
return ERR_BUF;
}
MEMCPY(p->payload, ip_options, optlen);
if(optlen < optlen_aligned){
/* 剩余字節(jié)清零 */
memset(((char*)p->payload)+ optlen,0,(size_t)(optlen_aligned - optlen));
}
#if CHECKSUM_GEN_IP_INLINE
for(i =0; i < optlen_aligned /2; i++){
chk_sum +=((u16_t*)p->payload)[i];
}
#endif /* CHECKSUM_GEN_IP_INLINE */
}
#endif /* IP_OPTIONS_SEND */
/* 生成IP頭 */
if(pbuf_add_header(p, IP_HLEN)){
LWIP_DEBUGF(IP_DEBUG | LWIP_DBG_LEVEL_SERIOUS,("ip4_output: not enough room for IP header in pbuf\\n"));
IP_STATS_INC(ip.err);
MIB2_STATS_INC(mib2.ipoutdiscards);
return ERR_BUF;
}
iphdr =(struct ip_hdr *)p->payload;
(p->len >=sizeof(struct ip_hdr)));
IPH_TTL_SET(iphdr, ttl);
IPH_PROTO_SET(iphdr, proto);
#if CHECKSUM_GEN_IP_INLINE
chk_sum += PP_NTOHS(proto |(ttl <<8));
#endif /* CHECKSUM_GEN_IP_INLINE */
/* 構(gòu)建目的IP地址,此處的目的IP地址不能為NULL */
ip4_addr_copy(iphdr->dest,*dest);
#if CHECKSUM_GEN_IP_INLINE
chk_sum += ip4_addr_get_u32(&iphdr->dest)&0xFFFF;
chk_sum += ip4_addr_get_u32(&iphdr->dest)>>16;
#endif /* CHECKSUM_GEN_IP_INLINE */
IPH_VHL_SET(iphdr,4, ip_hlen /4);
IPH_TOS_SET(iphdr, tos);
#if CHECKSUM_GEN_IP_INLINE
chk_sum += PP_NTOHS(tos |(iphdr->_v_hl <<8));
#endif /* CHECKSUM_GEN_IP_INLINE */
IPH_LEN_SET(iphdr, lwip_htons(p->tot_len));
#if CHECKSUM_GEN_IP_INLINE
chk_sum += iphdr->_len;
#endif /* CHECKSUM_GEN_IP_INLINE */
IPH_OFFSET_SET(iphdr,0);
IPH_ID_SET(iphdr, lwip_htons(ip_id));
#if CHECKSUM_GEN_IP_INLINE
chk_sum += iphdr->_id;
#endif /* CHECKSUM_GEN_IP_INLINE */
++ip_id;
if(src == NULL){
ip4_addr_copy(iphdr->src,*IP4_ADDR_ANY4);/** 構(gòu)建源IP地址 */
}else{
/* 此處的源IP地址不能為NULL */
ip4_addr_copy(iphdr->src,*src);
}
#if CHECKSUM_GEN_IP_INLINE
chk_sum += ip4_addr_get_u32(&iphdr->src)&0xFFFF;
chk_sum += ip4_addr_get_u32(&iphdr->src)>>16;
chk_sum =(chk_sum >>16)+(chk_sum &0xFFFF);
chk_sum =(chk_sum >>16)+ chk_sum;
chk_sum =~chk_sum;
IF__NETIF_CHECKSUM_ENABLED(netif, NETIF_CHECKSUM_GEN_IP){
iphdr->_chksum =(u16_t)chk_sum;/* network order */
}
#if LWIP_CHECKSUM_CTRL_PER_NETIF
else{
IPH_CHKSUM_SET(iphdr,0);
}
#endif /* LWIP_CHECKSUM_CTRL_PER_NETIF*/
#else/* CHECKSUM_GEN_IP_INLINE */
IPH_CHKSUM_SET(iphdr,0);
#if CHECKSUM_GEN_IP
IF__NETIF_CHECKSUM_ENABLED(netif, NETIF_CHECKSUM_GEN_IP){
IPH_CHKSUM_SET(iphdr, inet_chksum(iphdr, ip_hlen));
}
#endif /* CHECKSUM_GEN_IP */
#endif /* CHECKSUM_GEN_IP_INLINE */
}else{
/* IP頭已包含在pbuf中 */
if(p->len < IP_HLEN){
/** pbuf的長(zhǎng)度小于IP數(shù)據(jù)報(bào)首部長(zhǎng)度(20字節(jié)) */
LWIP_DEBUGF(IP_DEBUG | LWIP_DBG_LEVEL_SERIOUS,("ip4_output: LWIP_IP_HDRINCL but pbuf is too short\\n"));
IP_STATS_INC(ip.err);
MIB2_STATS_INC(mib2.ipoutdiscards);
return ERR_BUF;
}
iphdr =(struct ip_hdr *)p->payload;/** 直接從數(shù)據(jù)區(qū)域獲取IP數(shù)據(jù)報(bào)首部 */
ip4_addr_copy(dest_addr, iphdr->dest);/** 獲取目的IP地址 */
dest =&dest_addr;
}
IP_STATS_INC(ip.xmit);
LWIP_DEBUGF(IP_DEBUG,("ip4_output_if: %c%c%"U16_F"\\n", netif->name[0], netif->name[1],(u16_t)netif->num));
ip4_debug_print(p);
#if ENABLE_LOOPBACK /** 換回接口 */
if(ip4_addr_cmp(dest, netif_ip4_addr(netif))
#if!LWIP_HAVE_LOOPIF
|| ip4_addr_isloopback(dest)
#endif /* !LWIP_HAVE_LOOPIF */
){
/* 數(shù)據(jù)包是給自己的,將其放入環(huán)回接口 */
LWIP_DEBUGF(IP_DEBUG,("netif_loop_output()"));
return netif_loop_output(netif, p);
}
#if LWIP_MULTICAST_TX_OPTIONS
if((p->flags & PBUF_FLAG_MCASTLOOP)!=0){
netif_loop_output(netif, p);
}
#endif /* LWIP_MULTICAST_TX_OPTIONS */
#endif /* ENABLE_LOOPBACK */
#if IP_FRAG
/** 要發(fā)送的數(shù)據(jù)報(bào)大于mtu,需要分片,此處的前提是使能了IP_FRAG (IP分片) */
if(netif->mtu &&(p->tot_len > netif->mtu)){
return ip4_frag(p, netif, dest);/** 調(diào)用IP數(shù)據(jù)報(bào)分片函數(shù)將數(shù)據(jù)報(bào)分片發(fā)送出去 */
}
#endif /* IP_FRAG */
LWIP_DEBUGF(IP_DEBUG,("ip4_output_if: call netif->output()\\n"));
return netif->output(netif, p, dest);/** 如果不需要分片就直接通過(guò)網(wǎng)卡發(fā)送出去,netif->output() */
}
最后提個(gè)醒
此外:上層協(xié)議是不會(huì)直接調(diào)用 ip4_output()
函數(shù)的,lwip是通過(guò)宏定義將 ip4_output()
函數(shù)進(jìn)行重新定義:
#define ip_output(p, src, dest, ttl, tos, proto) ip4_output(p, src, dest, ttl, tos, proto
-
IP協(xié)議
+關(guān)注
關(guān)注
3文章
85瀏覽量
21636 -
傳輸協(xié)議
+關(guān)注
關(guān)注
0文章
78瀏覽量
11439 -
網(wǎng)絡(luò)層
+關(guān)注
關(guān)注
0文章
40瀏覽量
10290
發(fā)布評(píng)論請(qǐng)先 登錄
相關(guān)推薦
評(píng)論