LWIP学习笔记(6)IP协议

ip数据结构

ip.h
在这里插入图片描述web

ip层输出

当ip上层好比tcp、udp等传输层要发送数据时,将数据组织在pbuf中,payload指向协议首部,调用ip_output发送数据
ip.c
在这里插入图片描述
ip_output_if主要是填写ip首部信息(若是已填写则没必要再填),最后分3种状况发送数据报数组

  • dest ip和netif ip一致则回环接口
  • 数据报大于mtu则调用分片ip_frag函数
  • 调用netif->output,其实就是etharp_output
/** * 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发送
在这里插入图片描述数据结构

ip层输入

当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, &current_iphdr_dest);
      break;
    default:             //都不是的话直接丢掉数据报并返回主机不可达的icmp包
      /* send ICMP destination protocol unreachable unless is was a broadcast */
      if (!ip_addr_isbroadcast(&current_iphdr_dest, inp) &&
          !ip_addr_ismulticast(&current_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(&current_iphdr_src);
  ip_addr_set_any(&current_iphdr_dest);

  return ERR_OK;
}

重装函数是ip_reass,重装数据报,若是数据报重装完成则返回整个重装好的数据报p,不然p为null,实如今ip_frag.c中。less