一 导读
ip层是lwip代码的核心领域,它负责将以太网数据传递上来的数据整理并传递给传输层,或者将数据转发到其他网络。其中涉及ICMP,IGMP,DHCP等辅助协议。这章先介绍ip层的输入函数,搞清楚以太网将数据传递给ip层后,ip层如何将数据整理或转发。
二,输入代码
1,接口函数
网络接口接收到数据后,调用以下函数将数据传递给ip层。该函数将根据ipv4 or ipv6 调用真正的处理函数。
//以太网调用该函数
err_t
ip_input(struct pbuf *p, struct netif *inp)
{
if (p != NULL) {
if (IP_HDR_GET_VERSION(p->payload) == 6) {
return ip6_input(p, inp);
}
return ip4_input(p, inp);
}
return ERR_VAL;
}
2,ipv4 输入
该函数首先检查输入ip数据报的首部各项是否正确,再判断是否给本地的数据,最后将数据交给传输层。详细的逻辑看代码注释
err_t
//pbuf的payload指向ip首部,inp:数据传入的接口
ip4_input(struct pbuf *p, struct netif *inp)
{
struct ip_hdr *iphdr; //输入分组的首部
struct netif *netif;
u16_t iphdr_hlen; //首部长度
u16_t iphdr_len; //总长度
int check_ip_src = 1; //是否检查ip数据源地址
iphdr = (struct ip_hdr *)p->payload; //获取ip首部
//判断ip首部是否是ipv4
if (IPH_V(iphdr) != 4) {
pbuf_free(p);
return ERR_OK;
}
iphdr_hlen = IPH_HL(iphdr); //获取首部长度(字为单位)
iphdr_hlen *= 4; //将ip首部转换为字节单位
iphdr_len = lwip_ntohs(IPH_LEN(iphdr)); //获取ip总字节数
//若pbuf的总长度大于ip首部标志的总长度,则修剪pbuf的长度(因为其中有ip的填充数据)
if (iphdr_len < p->tot_len) {
pbuf_realloc(p, iphdr_len);
}
//检查ip首部是否错误:1,ip首部的数据必须放在一个pbuf中。2,ip首部长度符合?
if ((iphdr_hlen > p->len) || (iphdr_len > p->tot_len) || (iphdr_hlen < IP_HLEN)) {
pbuf_free(p);
return ERR_OK;
}
#if CHECKSUM_CHECK_IP
//检验ip分组的校验和
IF__NETIF_CHECKSUM_ENABLED(inp, NETIF_CHECKSUM_CHECK_IP) {
if (inet_chksum(iphdr, iphdr_hlen) != 0) {
pbuf_free(p);
return ERR_OK;
}
}
#endif
//将输入数据的ip源和目的地址复制到全局变量中,方便处理
ip_addr_copy_from_ip4(ip_data.current_iphdr_dest, iphdr->dest);
ip_addr_copy_from_ip4(ip_data.current_iphdr_src, iphdr->src);
//接下来确定网络接口netif
if (ip4_addr_ismulticast(ip4_current_dest_addr())) {
//ip分组的目的地址是多播地址
//TODO inp接口开启了IGMP且加入了该组播,则该分组就是给inp的
if ((inp->flags & NETIF_FLAG_IGMP) && (igmp_lookfor_group(inp, ip4_current_dest_addr()))) {
ip4_addr_t allsystems;
IP4_ADDR(&allsystems, 224, 0, 0, 1); //224.0.0.1代表本地子网所有主机;
if (ip4_addr_cmp(ip4_current_dest_addr(), &allsystems) &&
ip4_addr_isany(ip4_current_src_addr()))
{
//报文dest = 224.0.0.1 ;src =0.0.0.0 说明是网络中的路由器发送的数据
check_ip_src = 0;
}
netif = inp; //inp就是ip分组对应的接口
} else {
netif = NULL;
}
} else {
//不是多播地址
int first = 1;
//先判断接收到该分组的接口是否就是分组的目的地址,再从netif链表中遍历,确定netif
netif = inp;
do {
//接口已经使能且配置完成?
if ((netif_is_up(netif)) && (!ip4_addr_isany_val(*netif_ip4_addr(netif)))){
//若分组目的地址是 单播给该接口 或 广播地址 或是netif中的广播地址 则确定就是该netif接口
if (ip4_addr_cmp(ip4_current_dest_addr(), netif_ip4_addr(netif)) ||
ip4_addr_isbroadcast(ip4_current_dest_addr(), netif)
) {
break; //netif就是对应的接口,跳出循环
}
#if LWIP_AUTOIP
/* connections to link-local addresses must persist after changing
the netif's address (RFC3927 ch. 1.9) */
if (autoip_accept_packet(netif, ip4_current_dest_addr())) {
LWIP_DEBUGF(IP_DEBUG, ("ip4_input: LLA packet accepted on interface %c%c\n",
netif->name[0], netif->name[1]));
/* break out of for loop */
break;
}
#endif /* LWIP_AUTOIP */
}
if (first) {
//开始遍历netif链表
#if !LWIP_NETIF_LOOPBACK || LWIP_HAVE_LOOPIF
/* Packets sent to the loopback address must not be accepted on an
* interface that does not have the loopback address assigned to it,
* unless a non-loopback interface is used for loopback traffic. */
//对于loopback 特殊处理
if (ip4_addr_isloopback(ip4_current_dest_addr())) {
netif = NULL;
break;
}
#endif
first = 0;
netif = netif_list; //获取netif_list的第一个接口
} else {
netif = netif->next; //匹配下一个接口
}
if (netif == inp) {
netif = netif->next; //跳过inp,因为之前已经匹配失败
}
} while (netif != NULL);
}
#if IP_ACCEPT_LINK_LAYER_ADDRESSING
//dhcp消息:使用udp传输,dhcp服务端口67,客户端口68
//之前的步骤匹配不到对应接口,判断是否是dhcp服务器发送的dhcp消息
if (netif == NULL) {
//该分组是udp数据
if (IPH_PROTO(iphdr) == IP_PROTO_UDP) {
struct udp_hdr *udphdr = (struct udp_hdr *)((u8_t *)iphdr + iphdr_hlen); //获取udp首部
//且udp目的端口是68,说明这时服务器发送的dhcp消息
if (IP_ACCEPT_LINK_LAYER_ADDRESSED_PORT(udphdr->dest)) {
netif = inp;
check_ip_src = 0; //不检查分组的源地址
}
}
}
#endif
//检查源地址合法性
if (check_ip_src && !ip4_addr_isany_val(*ip4_current_src_addr())) //源地址不是0.0.0.0;正常的ip数据
{
//若数据源地址是广播数据,不合法
if ((ip4_addr_isbroadcast(ip4_current_src_addr(), inp)) ||
(ip4_addr_ismulticast(ip4_current_src_addr()))) {
pbuf_free(p);
return ERR_OK;
}
}
//没有对应的接口,分组不是给本地的,转发
if (netif == NULL) {
//不是广播的数据才能转发
if (!ip4_addr_isbroadcast(ip4_current_dest_addr(), inp)) {
ip4_forward(p, iphdr, inp); //转发分组
}
pbuf_free(p); //释放内存
return ERR_OK;
}
//到这,说明数据报是给本地的
//数据包是否是一个分片?(根据MF标志和分片偏移判断)
if ((IPH_OFFSET(iphdr) & PP_HTONS(IP_OFFMASK | IP_MF)) != 0) {
//!嵌入式互联网中的数据量较小,一般不会有数据分片
#if IP_REASSEMBLY /* packet fragment reassembly code present? */
p = ip4_reass(p); //重组ip分片
if (p == NULL) {
return ERR_OK;
}
iphdr = (struct ip_hdr *)p->payload;
#else
pbuf_free(p); //不重装ip分组
return ERR_OK;
#endif
}
//更新全局变量
ip_data.current_netif = netif;
ip_data.current_input_netif = inp;
ip_data.current_ip4_header = iphdr;
ip_data.current_ip_header_tot_len = IPH_HL(iphdr) * 4;
#if LWIP_RAW
/* raw input did not eat the packet? */
if (raw_input(p, inp) == 0)
#endif /* LWIP_RAW */
{
//移动payload后移到上层数据首部
pbuf_header(p, -(s16_t)iphdr_hlen); /* Move to payload, no check necessary. */
//传递给上层协议
switch (IPH_PROTO(iphdr)) {
#if LWIP_UDP
case IP_PROTO_UDP:
#if LWIP_UDPLITE
case IP_PROTO_UDPLITE:
#endif /* LWIP_UDPLITE */
udp_input(p, inp);
break;
#endif /* LWIP_UDP */
#if LWIP_TCP
case IP_PROTO_TCP:
tcp_input(p, inp);
break;
#endif /* LWIP_TCP */
#if LWIP_ICMP
case IP_PROTO_ICMP:
icmp_input(p, inp);
break;
#endif /* LWIP_ICMP */
#if LWIP_IGMP
case IP_PROTO_IGMP:
igmp_input(p, inp, ip4_current_dest_addr());
break;
#endif /* LWIP_IGMP */
default:
#if LWIP_ICMP
//发送icmp协议不可达报文,除非对方是个广播
if (!ip4_addr_isbroadcast(ip4_current_dest_addr(), netif) &&
!ip4_addr_ismulticast(ip4_current_dest_addr())) {
pbuf_header_force(p, iphdr_hlen); //将payload重新指向ip首部
p->payload = iphdr;
icmp_dest_unreach(p, ICMP_DUR_PROTO);//发送协议不可达报文
}
#endif /* LWIP_ICMP */
pbuf_free(p); //最后释放报文
}
}
/* @todo: this is not really necessary... */
ip_data.current_netif = NULL;
ip_data.current_input_netif = NULL;
ip_data.current_ip4_header = NULL;
ip_data.current_ip_header_tot_len = 0;
ip4_addr_set_any(ip4_current_src_addr());
ip4_addr_set_any(ip4_current_dest_addr());
return ERR_OK;
}