协议栈的详细分析与移植_第1页
协议栈的详细分析与移植_第2页
协议栈的详细分析与移植_第3页
协议栈的详细分析与移植_第4页
协议栈的详细分析与移植_第5页
已阅读5页,还剩10页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

1、Lwip协议栈的分析与移植主要解决两个问题:操作系统仿真层的移植。这个基于uCOS-II的代码太多了。可以借鉴焦海波著的LWIP移植一章。1,设备驱动的移植.驱动的移植主要就是完成ethernetif.c的工作。作者已经给好了驱动的接口。struct netif   struct netif *next;  struct ip_addr ip_addr;  struct ip_addr netmask;  struct ip_addr gw;  err_t (* input)(struct pbuf *p, struct netif *inp)

2、;  err_t (* output)(struct netif *netif, struct pbuf *p,       struct ip_addr *ipaddr);  err_t (* linkoutput)(struct netif *netif, struct pbuf *p);  void *state;#if LWIP_DHCP  struct dhcp *dhcp;#endif  unsigned char hwaddr_len;  unsigned ch

3、ar hwaddrNETIF_MAX_HWADDR_LEN;  u16_t mtu;  char name2;  u8_t num;  u8_t flags;主要就是:    err_t (* input)(struct pbuf *p, struct netif *inp);        这个是被驱动调用的,传递一个数据包给TCP/IP栈。    err_t (* output)(struct netif *netif,

4、struct pbuf *p,struct ip_addr *ipaddr);        这个是被IP模块调用的,向以太网上发送一个数据包,函数要先通过IP地址获得解决硬件地址,然后发包。    err_t (* linkoutput)(struct netif *netif, struct pbuf *p);        这个是直接发送数据包的接口相应的作者在ethernetif.c里面给了几个函数框架,这个文件相当于

5、一个硬件抽象层。static void low_level_init(struct netif *netif)    网卡初始化函数static err_t low_level_output(struct netif *netif, struct pbuf *p)    链路层发送函数,实现err_t (* linkoutput)接口。static struct pbuf *low_level_input(struct netif *netif)    得到一整帧数据static err_t ethern

6、etif_output(struct netif *netif, struct pbuf *p,struct ip_addr *ipaddr)    实现发送线程,实现err_t (* output)接口。static void ethernetif_input(struct netif *netif)    实现接收线程,识别数据包是ARP包还是IP包err_t ethernetif_init(struct netif *netif)    初始化底层接口 其实,写驱动的时候只要自己再建个etherne

7、t.c,实际的网络硬件控制的文件然后提供几个函数比如:void EMACInit( void )    硬件的初始化void EMACPacketSend ( u8_t *buffer, u16_t length )    用来将buffer里面的包复制到网络设备的发送缓冲里面,发送。u16_t EMACPacketReceive ( u8_t *buffer, u16_t max_length  )    用来将网络设备的接收缓冲里面的包数据复制到buffer里面。u16_t EMACPack

8、etLength ( u16_t max_length )    获得包长度    还有其他控制类函数。最后,用ethernet.c里的函数完成ethernetif.c里的框架。这样脉络可能会清楚一点。LWIP的移植主要是参考网络文献和网卡的数据手册。下面从lwip协议栈入手分析lwip协议栈的具体函数。移植LWIP协议栈主要关注的是底层和网卡之间的衔接工作。/*/struct netif *netif_add(struct netif *netif, struct ip_addr *ipaddr, struct ip_addr *n

9、etmask, struct ip_addr *gw, void *state, err_t (* init)(struct netif *netif), err_t (* input)(struct pbuf *p, struct netif *netif)添加一个网络设备:跟物理网卡再配一个IP地址。State域用来和实际的网卡交互。指示网卡的状态信息。Init()函数是初始化网络设备函数,这个函数在添加的时候被调用。Input()函数参数 只是对网络设备注册,这个函数在输入的时候调用。netif_add()函数,配置网络设备的相关信息,初始化对应的网络设备。系统中的网络设备由一个网络设备

10、的链表构成。此函数会传进来一个网络设备的结构体,将此结构体添加入系统网络设备链表中,然后初始化网络设备,最后注册接受函数。需要添加多个网络设备,那么在此处需要添加独立的初始化函数和输入函数。其具体的代码如下:/*/static int netifnum = 0; #if LWIP_DHCP /* netif not under DHCP control by default */ netif->dhcp = NULL;#endif /* remember netif specific state information data */ netif->state = state; n

11、etif->num = netifnum+; netif->input = input; netif_set_addr(netif, ipaddr, netmask, gw); /* call user specified initialization function for netif */ if (init(netif) != ERR_OK) return NULL; /* add this netif to the list */ netif->next = netif_list; netif_list = netif; LWIP_DEBUGF(NETIF_DEBUG

12、, ("netif: added interface %c%c IP addr ", netif->name0, netif->name1); ip_addr_debug_print(NETIF_DEBUG, ipaddr); LWIP_DEBUGF(NETIF_DEBUG, (" netmask "); ip_addr_debug_print(NETIF_DEBUG, netmask); LWIP_DEBUGF(NETIF_DEBUG, (" gw "); ip_addr_debug_print(NETIF_DEBUG,

13、 gw); LWIP_DEBUGF(NETIF_DEBUG, ("n"); return netif;/*/*/static void ethernetif_input(void *pReserved) 函数的中语句:case ETHTYPE_IP:/* 更新ARP表etharp_ip_input(_pstNetif, _pstPbuf);/* 跳过以太网头部字段pbuf_header(_pstPbuf, -sizeof(struct eth_hdr) ); /* 传递到网络层_pstNetif->input(_pstPbuf, _pstNetif);break;这里

14、的input()函数就是在上面注册的函数。每一个网络设备对应一个输入函数。不过这里就直接将这个传给了一个确定的网络设备。/*/err_t ethernetif_init(struct netif *netif) 此函数就是上面的init()函数,这个函数在netif_add()函数中被调用。也就是说系统的网络设备netif在配置后马上就初始化。输入参数是netif;网路设备。此函数功能就是将此网络设备进行初始化,然后注册发送函数。对于每个网络设备都有自己的初始化函数。当需要添加多个网络设备时,需要添加输出函数,因为输出是交给LTE协议栈的。所以在这里需要添加指定的输出函数,输出的是一个IP数据

15、包。虚拟的网卡设备是没有有些不需要配置,mac地址,当然也可以给定一个mac地址。这个问题现在还不确定。下面是函数的具体代码:/*/static struct ethernetif ethernetif; netif->state = &ethernetif; netif->name0 = IFNAME0; netif->name1 = IFNAME1; netif->output = ethernetif_output; netif->linkoutput = low_level_output; ethernetif.ethaddr = (struct

16、eth_addr *)&(netif->hwaddr0); low_level_init(netif); etharp_init(); sys_timeout(ARP_TMR_INTERVAL, arp_timer, NULL); return ERR_OK;综合前面三个函数的分析,需要添加一个网络设备,主要是对netif结构体进行注册。需要将接受到的第二个网络设备的数据包做处理,可以用一个进程。进程的输入参数就是网络设备。每次将丢给第二个网络设备的数据包,会由netif->output = ethernetif_output; netif->linkoutput =

17、 low_level_output;这两条语句注册的函数发出去,而这两条语句注册的函数的调用又是在IP层调用的。因此要验证最后配置两个网络设备之后数据是否正常发送,那么可以在输出函数中捕获数据包。IP层的输出函数部分/*/下面这个函数是在IP层总领发送的函数,从中可以看到发送一个IP数据包的具体实现。当要发送一个数据包的时候,首先要进行路由选择,注意输入的IP数据包发送函数,是没有网络设备这个参数的,那说明上层协议是不知道底层的IP地址的,那么当一个数据包进来的时候,首先要进行路由选择,找到合适的网络设备,然后将数据包实际发送出去。err_t ip_output(struct pbuf *p,

18、 struct ip_addr *src, struct ip_addr *dest, u8_t ttl, u8_t tos, u8_t proto) struct netif *netif;/进行路由选择,找到合适的网络设备 if (netif = ip_route(dest) = NULL) LWIP_DEBUGF(IP_DEBUG | 2, ("ip_output: No route to 0x%lxn", dest->addr); IP_STATS_INC(ip.rterr); snmp_inc_ipoutdiscards(); return ERR_RTE;

19、 /*如果找到了合适的网络设备,那么将数据发送出去 return ip_output_if(p, src, dest, ttl, tos, proto, netif);/*/static err_t low_level_output(struct netif *pstNetif, struct pbuf *pstPbuf)输入参数:网络设备pstNetif , pbuf结构体pstPbuf。网络的输出数据就存储在pstPbuf中,只要将其拷贝出来即可。/*/下面这个函数就是IP层的发送函数,发送是以网络设备作为参数进行的。首先前面有进行一个路由,路由的作用就是找到适合发生的网络设备,然后调用这

20、个函数发送。这个函数在末尾调用了对应的网络设备的output()函数,将实际的数据发送出去。Output()函数实际就是前面的netif->output = ethernetif_output; netif->linkoutput = low_level_output;这样最后数据就由此发生出去。添加一个网络设备,会增加系统的路由开销,但是当有数据进来的时候,数据会自动的发送给相应的这个网络设备。err_t ip_output_if(struct pbuf *p, struct ip_addr *src, struct ip_addr *dest, u8_t ttl, u8_t t

21、os,u8_t proto, struct netif *netif) struct ip_hdr *iphdr; u16_t ip_id = 0; snmp_inc_ipoutrequests(); if (dest != IP_HDRINCL) if (pbuf_header(p, IP_HLEN) LWIP_DEBUGF(IP_DEBUG | 2, ("ip_output: not enough room for IP header in pbufn"); IP_STATS_INC(ip.err); snmp_inc_ipoutdiscards(); return E

22、RR_BUF; iphdr = p->payload; IPH_TTL_SET(iphdr, ttl); IPH_PROTO_SET(iphdr, proto); ip_addr_set(&(iphdr->dest), dest); IPH_VHLTOS_SET(iphdr, 4, IP_HLEN / 4, tos); IPH_LEN_SET(iphdr, htons(p->tot_len); IPH_OFFSET_SET(iphdr, htons(IP_DF); IPH_ID_SET(iphdr, htons(ip_id); +ip_id; if (ip_addr_

23、isany(src) ip_addr_set(&(iphdr->src), &(netif->ip_addr); else ip_addr_set(&(iphdr->src), src); IPH_CHKSUM_SET(iphdr, 0);#if CHECKSUM_GEN_IP IPH_CHKSUM_SET(iphdr, inet_chksum(iphdr, IP_HLEN);#endif else iphdr = p->payload; dest = &(iphdr->dest); #if IP_FRAG /* don't

24、 fragment if interface has mtu set to 0 loopif */ if (netif->mtu && (p->tot_len > netif->mtu) return ip_frag(p,netif,dest);#endif IP_STATS_INC(ip.xmit); LWIP_DEBUGF(IP_DEBUG, ("ip_output_if: %c%c%un", netif->name0, netif->name1, netif->num); ip_debug_print(p);

25、LWIP_DEBUGF(IP_DEBUG, ("netif->output()");/* 将最后的IP数据报通过网卡输出 return netif->output(netif, p, dest);/*/路由函数,找到合适的网络设备,供ip_output()函数调用。struct netif * ip_route(struct ip_addr *dest) struct netif *netif; /* iterate through netifs */ for(netif = netif_list; netif != NULL; netif = netif-&g

26、t;next) /* network mask matches? */ if (ip_addr_netcmp(dest, &(netif->ip_addr), &(netif->netmask) /* return netif on which to forward IP packet */ return netif; /* no matching netif found, use default netif */ return netif_default;通过以上对IP发送的分析,当如果系统中配置了多个网络设备之后,那么在当上层丢一个数据包下来的时候,首先进行路由

27、选择,然后在调用底层的发送函数,将数据包发送出去。因为发送函数的处理就在这一层,也就是说就是在这个进程中处理,如果这个进程是应用程序创建的进程,那么发送函数就在这个进程中,这个发送函数直接最后将会调用到最为底层的操作网卡的函数。IP层的输入流程/*/首先分析一个函数:这个函数是将输入的IP包提交给上层。输入参数有一个是网络设备,这里并不影响上层协议的转发。无论底层是什么网络都,都将进行这步骤。err_t tcpip_input(struct pbuf *p, struct netif *inp) struct tcpip_msg *msg; msg = memp_malloc(MEMP_TCP

28、IP_MSG); if (msg = NULL) pbuf_free(p); return ERR_MEM; msg->type = TCPIP_MSG_INPUT; msg->msg.inp.p = p; msg->if = inp; sys_mbox_post(mbox, msg); return ERR_OK;上面函数中可以看出,这个输入的数据包的消息被附加上了接收到这个消息的网络设备。/*/后面分析下面的这个函数:ip_forward 输入参数中有一个网络设备。是这个消息的原始接受的网络设备。这个函数在IP层路由中被调用。在当没有找到数据包在本机对应的IP地址的时候,

29、那么将数据包丢给IP路由层去处理。本函数首先是进行路由选择,找到合适的发出去的端口,实际上就是找到一个合适存在的物理层的出口网络设备。这里也是我们要关注的地方,因为当输入到了一个数据包之后,我们可以通过路由将它路由到一个虚拟的网络设备上。下面这个函数检查路由选择是否成功,要是没有找到,那就直接返回,否则查看这个适合的网络设备是不是接受数据包的设备,要是那么直接返回,因为不能转发给相同的目标机。这个时候应该是要转发给网关。最后通过底层的驱动函数将数据包转发出去。static struct netif * ip_forward(struct pbuf *p, struct ip_hdr *iphd

30、r, struct netif *inp) struct netif *netif; PERF_START; /* Find network interface where to forward this IP packet to. */ netif = ip_route(struct ip_addr *)&(iphdr->dest); if (netif = NULL) LWIP_DEBUGF(IP_DEBUG, ("ip_forward: no forwarding route for 0x%lx foundn", iphdr->dest.addr)

31、; snmp_inc_ipnoroutes(); return (struct netif *)NULL; /* Do not forward packets onto the same network interface on which * they arrived. */ if (netif = inp) LWIP_DEBUGF(IP_DEBUG, ("ip_forward: not bouncing packets back on incoming interface.n"); snmp_inc_ipnoroutes(); return (struct netif

32、*)NULL; /* decrement TTL */ IPH_TTL_SET(iphdr, IPH_TTL(iphdr) - 1); /* send ICMP if TTL = 0 */ if (IPH_TTL(iphdr) = 0) /* Don't send ICMP messages in response to ICMP messages */ if (IPH_PROTO(iphdr) != IP_PROTO_ICMP) icmp_time_exceeded(p, ICMP_TE_TTL); snmp_inc_icmpouttimeexcds(); return (struc

33、t netif *)NULL; /* Incrementally update the IP checksum. */ if (IPH_CHKSUM(iphdr) >= htons(0xffff - 0x100) IPH_CHKSUM_SET(iphdr, IPH_CHKSUM(iphdr) + htons(0x100) + 1); else IPH_CHKSUM_SET(iphdr, IPH_CHKSUM(iphdr) + htons(0x100); LWIP_DEBUGF(IP_DEBUG, ("ip_forward: forwarding packet to 0x%lxn

34、", iphdr->dest.addr); IP_STATS_INC(ip.fw); IP_STATS_INC(ip.xmit); snmp_inc_ipforwdatagrams(); PERF_STOP("ip_forward"); /* transmit pbuf on chosen interface */ netif->output(netif, p, (struct ip_addr *)&(iphdr->dest); return netif;/*/* * This function is called by the netw

35、ork interface device driver when * an IP packet is received. The function does the basic checks of the * IP header such as packet size being at least larger than the header * size etc. If the packet was not destined for us, the packet is * forwarded (using ip_forward). The IP checksum is always chec

36、ked.* Finally, the packet is sent to the upper layer protocol input function.*/函数 ip_input是将不同的底层物理链路层数据都输入到此处的接口。输入参数netif *inp,并不会对这里的处理过程有什么影响。输入的这个参数,虽然一直存在,但是关键的使用点并不多,此函数里面会进行一个网络设备的选择,就相当于从各个底层的网络进来数据之后,都会在这里进行汇合,而这种汇合也只是名义上的汇合,因为上层的协议是不知道具体这个数据是由哪一个网络端口发过来的数据。同时它也不关心这个数据从哪里过来,但是这里之所以还是需要用到这个

37、netif *inp,是因为对于过来的数据包,如果不是系统配置的网络端口的数据包,那么需要丢弃,所以这里就用到了netif *inp。而netif *inp在此函数中也只是做了一个验证的工作,当得到的端口存在的时候还需要保证从那个端口进来的数据包,再不能从这个端口出去了。详细的过程查看这个函数的注释部分。err_t ip_input(struct pbuf *p, struct netif *inp) struct ip_hdr *iphdr; struct netif *netif; u16_t iphdrlen; IP_STATS_INC(ip.recv); snmp_inc_ipinre

38、ceives(); /* identify the IP header */ iphdr = p->payload; if (IPH_V(iphdr) != 4) LWIP_DEBUGF(IP_DEBUG | 1, ("IP packet dropped due to bad version number %un", IPH_V(iphdr); ip_debug_print(p); pbuf_free(p); IP_STATS_INC(ip.err); IP_STATS_INC(ip.drop); snmp_inc_ipunknownprotos(); return

39、ERR_OK; /* obtain IP header length in number of 32-bit words */ iphdrlen = IPH_HL(iphdr); /* calculate IP header length in bytes */ iphdrlen *= 4; /* header length exceeds first pbuf length? */ if (iphdrlen > p->len) LWIP_DEBUGF(IP_DEBUG | 2, ("IP header (len %u) does not fit in first pbu

40、f (len %u), IP packet droppped.n", iphdrlen, p->len); /* free (drop) packet pbufs */ pbuf_free(p); IP_STATS_INC(ip.lenerr); IP_STATS_INC(ip.drop); snmp_inc_ipindiscards(); return ERR_OK; /* verify checksum */#if CHECKSUM_CHECK_IP if (inet_chksum(iphdr, iphdrlen) != 0) LWIP_DEBUGF(IP_DEBUG |

41、2, ("Checksum (0x%x) failed, IP packet dropped.n", inet_chksum(iphdr, iphdrlen); ip_debug_print(p); pbuf_free(p); IP_STATS_INC(ip.chkerr); IP_STATS_INC(ip.drop); snmp_inc_ipindiscards(); return ERR_OK; #endif /* Trim pbuf. This should have been done at the netif layer, * but we'll do i

42、t anyway just to be sure that its done. */ pbuf_realloc(p, ntohs(IPH_LEN(iphdr);/* 这里是核心,在这里会将数据包进行一定的处理。/* 在得到的数据包之后,在进行路由,选择合适的网络设备/* netif_list的头是在初始化的时候设定为默认的 /* match packet against an interface, i.e. is this packet for us? */ for (netif = netif_list; netif != NULL; netif = netif->next) LWIP

43、_DEBUGF(IP_DEBUG, ("ip_input: iphdr->dest 0x%lx netif->ip_addr 0x%lx (0x%lx, 0x%lx, 0x%lx)n", iphdr->dest.addr, netif->ip_addr.addr, iphdr->dest.addr & netif->netmask.addr, netif->ip_addr.addr & netif->netmask.addr, iphdr->dest.addr & (netif->netma

44、sk.addr); /* interface is up and configured? */ /* 首先这个网络设备是否开启的,同时检查IP地址是否配置 if (netif_is_up(netif) && (!ip_addr_isany(&(netif->ip_addr) /* unicast to this interface address? */ /*匹配这个IP地址 /*iphdr->dest 输入进来的IP地址,从以太网帧中输入进来的IP地址 if (ip_addr_cmp(&(iphdr->dest), &(netif-&

45、gt;ip_addr) | /* or broadcast on this interface network address? */ ip_addr_isbroadcast(&(iphdr->dest), netif) LWIP_DEBUGF(IP_DEBUG, ("ip_input: packet accepted on interface %c%cn", netif->name0, netif->name1); /*调试添加的 Printf("ip_input: packet accepted on interface %c%cn&

46、quot;,netif->name0, netif->name1); /* break out of for loop */ break; #if LWIP_DHCP /* Pass DHCP messages regardless of destination address. DHCP traffic is addressed * using link layer addressing (such as Ethernet MAC) so we must not filter on IP. * According to RFC 1542 section , referred by

47、 RFC 2131). */ if (netif = NULL) /* remote port is DHCP server? */ if (IPH_PROTO(iphdr) = IP_PROTO_UDP) LWIP_DEBUGF(IP_DEBUG | DBG_TRACE | 1, ("ip_input: UDP packet to DHCP client port %un", ntohs(struct udp_hdr *)(u8_t *)iphdr + iphdrlen)->dest); if (ntohs(struct udp_hdr *)(u8_t *)iphd

48、r + iphdrlen)->dest) = DHCP_CLIENT_PORT) LWIP_DEBUGF(IP_DEBUG | DBG_TRACE | 1, ("ip_input: DHCP packet accepted.n"); netif = inp; #endif /* LWIP_DHCP */*下面首先处理没有找到网络设备的情况。注意这个函数是ip_input(),因此在无论是以太网还是其他的网络输入进来一个/*数据包之后都会首先进行这步。这个函数的触发是由底层的驱动函数得到一个包之后将消息发到上层的IP层后开始的。不管/*这个IP包之前是属于哪个网络设备的

49、,都将会触发这个函数的处理工作。 /* packet not for us? */ if (netif = NULL) /* packet not for us, route or discard */ LWIP_DEBUGF(IP_DEBUG | DBG_TRACE | 1, ("ip_input: packet not for us.n");#if IP_FORWARD/*前提是要允许转发,实际的这个协议栈中是没有打开转发功能的 /* non-broadcast packet? */ if (!ip_addr_isbroadcast(&(iphdr->de

50、st), inp) /* 这里实现IP数据包的路由功能。 /* try to forward IP packet on (other) interfaces */ ip_forward(p, iphdr, inp); else /*如果是不允许转发的话 那么这个数据包直接丢弃。#endif /* IP_FORWARD */ snmp_inc_ipindiscards(); pbuf_free(p); return ERR_OK; /* packet consists of multiple fragments? */ if (IPH_OFFSET(iphdr) & htons(IP_O

51、FFMASK | IP_MF) != 0) #if IP_REASSEMBLY /* packet fragment reassembly code present? */ LWIP_DEBUGF(IP_DEBUG, ("IP packet is a fragment (id=0x%04x tot_len=%u len=%u MF=%u offset=%u), calling ip_reass()n", ntohs(IPH_ID(iphdr), p->tot_len, ntohs(IPH_LEN(iphdr), !(IPH_OFFSET(iphdr) & ht

52、ons(IP_MF), (ntohs(IPH_OFFSET(iphdr) & IP_OFFMASK)*8); /* reassemble the packet*/ p = ip_reass(p); /* packet not fully reassembled yet? */ if (p = NULL) return ERR_OK; iphdr = p->payload;#else /* IP_REASSEMBLY = 0, no packet fragment reassembly code present */ pbuf_free(p); LWIP_DEBUGF(IP_DEB

53、UG | 2, ("IP packet dropped since it was fragmented (0x%x) (while IP_REASSEMBLY = 0).n", ntohs(IPH_OFFSET(iphdr); IP_STATS_INC(ip.opterr); IP_STATS_INC(ip.drop); snmp_inc_ipunknownprotos(); return ERR_OK;#endif /* IP_REASSEMBLY */ #if IP_OPTIONS = 0 /* no support for IP options in the IP header? */ if (iphdrlen > IP_HLEN) LWIP_DEBUGF(IP_DEBUG | 2, ("I

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论