在ip.h
中
web
当ip上层好比tcp、udp等传输层要发送数据时,将数据组织在pbuf中,payload指向协议首部,调用ip_output发送数据
ip.c
中
ip_output_if主要是填写ip首部信息(若是已填写则没必要再填),最后分3种状况发送数据报数组
/** * Sends an IP packet on a network interface. This function constructs * the IP header and calculates the IP header checksum. If the source * IP address is NULL, the IP address of the outgoing network * interface is filled in as source address. * If the destination IP address is IP_HDRINCL, p is assumed to already * include an IP header and p->payload points to it instead of the data. * * @param p the packet to send (p->payload points to the data, e.g. next protocol header; if dest == IP_HDRINCL, p already includes an IP header and p->payload points to that IP header) * @param src the source IP address to send from (if src == IP_ADDR_ANY, the * IP address of the netif used to send is used as source address) * @param dest the destination IP address to send the packet to * @param ttl the TTL value to be set in the IP header * @param tos the TOS value to be set in the IP header * @param proto the PROTOCOL to be set in the IP header * @param netif the netif on which to send this packet * @return ERR_OK if the packet was sent OK * ERR_BUF if p doesn't have enough space for IP/LINK headers * returns errors returned by netif->output * * @note ip_id: RFC791 "some host may be able to simply use * unique identifiers independent of destination" *///填写ip首部各字段并发送数据包报 err_t ip_output_if(struct pbuf *p, ip_addr_t *src, ip_addr_t *dest, u8_t ttl, u8_t tos, u8_t proto, struct netif *netif) { struct ip_hdr *iphdr; ip_addr_t dest_addr; /* Should the IP header be generated or is it already included in p? */ if (dest != IP_HDRINCL) {//dest 不为IP_HDRINCL,说明pbuf未填写ip首部,则须要填写 u16_t ip_hlen = IP_HLEN;//IP_HLEN,ip首部长度20 /* generate IP header */ if (pbuf_header(p, IP_HLEN)) {//payload指向ip首部 return ERR_BUF; } iphdr = (struct ip_hdr *)p->payload;//iphdr指向ip首部 IPH_TTL_SET(iphdr, ttl); //填写ttl字段 IPH_PROTO_SET(iphdr, proto); //填写协议字段 /* dest cannot be NULL here */ ip_addr_copy(iphdr->dest, *dest); //填写目的ip IPH_VHL_SET(iphdr, 4, ip_hlen / 4); //填写版本号和首部长度 IPH_TOS_SET(iphdr, tos); //填写服务类型 IPH_LEN_SET(iphdr, htons(p->tot_len));//填写数据报总长度 IPH_OFFSET_SET(iphdr, 0); //填写标志位和片偏移 0 IPH_ID_SET(iphdr, htons(ip_id)); //填写标识字段 ++ip_id; //数据报编号+1,这是个全局变量 if (ip_addr_isany(src)) {//若src为0则源ip为netif接口ip,不然源ip为src ip_addr_copy(iphdr->src, netif->ip_addr); } else { /* src cannot be NULL here */ ip_addr_copy(iphdr->src, *src); } IPH_CHKSUM_SET(iphdr, 0);//清ip首部校验和 IPH_CHKSUM_SET(iphdr, inet_chksum(iphdr, ip_hlen));//计算ip首部校验和 } else { //若是pbuf已经填写ip首部则不需再填写 /* IP header already included in p */ iphdr = (struct ip_hdr *)p->payload; ip_addr_copy(dest_addr, iphdr->dest); dest = &dest_addr; } //下面分3种状况 if (ip_addr_cmp(dest, &netif->ip_addr)) {//若是目的ip dest是netif接口ip则调用回环输入 return netif_loop_output(netif, p, dest); } /* don't fragment if interface has mtu set to 0 [loopif] */ if (netif->mtu && (p->tot_len > netif->mtu)) {//若是发送数据大于mtu则需分片发送 return ip_frag(p, netif, dest); } return netif->output(netif, p, dest);//不然调用注册output发送(etharp_output) }
ip_frag分片发生函数在ip_frag.c
中定义
ip_frag.c
中会定义一个全局数组,大小是mtu值,ip_frag函数把数据报分片的拷贝到这个全局数组中,再调用netif->output发送
数据结构
当arp层收到数据后交给ip层就是传给ip_input这个函数(ethernet_input中被调用)
在ip.c
中并发
/** * This function is called by the network 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 checked. * * Finally, the packet is sent to the upper layer protocol input function. * * @param p the received IP packet (p->payload points to IP header) * @param inp the netif on which this packet was received * @return ERR_OK if the packet was processed (could return ERR_* if it wasn't * processed, but currently always returns ERR_OK) */ err_t ip_input(struct pbuf *p, struct netif *inp) { //前面是作些合法性判断和是否要转发数据报。。。 /* packet consists of multiple fragments? *///若是有更多分片位置位或片偏移不为0则为分片包 if ((IPH_OFFSET(iphdr) & PP_HTONS(IP_OFFMASK | IP_MF)) != 0) { /* reassemble the packet*/ p = ip_reass(p);//重装数据报,若是数据报重装完成则返回整个重装好的数据报p,不然p为null /* packet not fully reassembled yet? */ if (p == NULL) {//未重装好直接返回 return ERR_OK; } iphdr = (struct ip_hdr *)p->payload;//到这里必定是完整的数据报 } if (iphdr_hlen > IP_HLEN) {//不处理带首选项的ip数据报 return ERR_OK; } current_netif = inp; current_header = iphdr; /* raw input did not eat the packet? */ if (raw_input(p, inp) == 0) //为用户直接与ip数据报交互预留接口 { switch (IPH_PROTO(iphdr)) {//根据ip首部协议字段判断向上递交给哪一个协议 case IP_PROTO_UDP: //udp协议 #if LWIP_UDPLITE case IP_PROTO_UDPLITE: #endif /* LWIP_UDPLITE */ udp_input(p, inp); break; case IP_PROTO_TCP: //tcp协议 tcp_input(p, inp); break; case IP_PROTO_ICMP: //icmp协议 icmp_input(p, inp); break; case IP_PROTO_IGMP: //igmp协议 igmp_input(p, inp, ¤t_iphdr_dest); break; default: //都不是的话直接丢掉数据报并返回主机不可达的icmp包 /* send ICMP destination protocol unreachable unless is was a broadcast */ if (!ip_addr_isbroadcast(¤t_iphdr_dest, inp) && !ip_addr_ismulticast(¤t_iphdr_dest)) { p->payload = iphdr; icmp_dest_unreach(p, ICMP_DUR_PROTO); } pbuf_free(p); } } current_netif = NULL; current_header = NULL; ip_addr_set_any(¤t_iphdr_src); ip_addr_set_any(¤t_iphdr_dest); return ERR_OK; }
重装函数是ip_reass,重装数据报,若是数据报重装完成则返回整个重装好的数据报p,不然p为null,实如今ip_frag.c
中。less