lwip之IP(二)

1、ip数据包输出
(1)输出ip报文
err_t ip_output(struct pbuf *p, ip_addr_t *src, ip_addr_t *dest,
          u8_t ttl, u8_t tos, u8_t proto)
{
  struct netif *netif;

//查找合适的网卡接口,即ip地址在同一网段内
  if ((netif = ip_route(dest)) == NULL) {
    return ERR_RTE;
  }

  return ip_output_if(p, src, dest, ttl, tos, proto, netif);
}


//填充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;


  if (dest != IP_HDRINCL) {
    u16_t ip_hlen = IP_HLEN;

    if (pbuf_header(p, IP_HLEN)) {
      return ERR_BUF;
    }

    iphdr = (struct ip_hdr *)p->payload;

    IPH_TTL_SET(iphdr, ttl);
    IPH_PROTO_SET(iphdr, proto);
    ip_addr_copy(iphdr->dest, *dest);
    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);
    IPH_ID_SET(iphdr, htons(ip_id));

    ++ip_id;

    if (ip_addr_isany(src)) {
      ip_addr_copy(iphdr->src, netif->ip_addr);
    } else {
      ip_addr_copy(iphdr->src, *src);
    }

    IPH_CHKSUM_SET(iphdr, 0);
    IPH_CHKSUM_SET(iphdr, inet_chksum(iphdr, ip_hlen));

  } else {
    /* IP header already included in p */
    iphdr = (struct ip_hdr *)p->payload;
    ip_addr_copy(dest_addr, iphdr->dest);
    dest = &dest_addr;
  }

#if IP_FRAG
 //判断是否分片
  if (netif->mtu && (p->tot_len > netif->mtu)) {
    return ip_frag(p, netif, dest);
  }
#endif /* IP_FRAG */
  return netif->output(netif, p, dest);
}
(2)ip报文分片机制

分片条件:

 if (netif->mtu && (p->tot_len > netif->mtu)) {
    return ip_frag(p, netif, dest);
  }

分片函数ip_frag

//静态分片数组定义
static u8_t buf[LWIP_MEM_ALIGN_SIZE(IP_FRAG_MAX_MTU + MEM_ALIGNMENT - 1)];


err_t ip_frag(struct pbuf *p, struct netif *netif, ip_addr_t *dest)
{
  struct pbuf *rambuf;
  struct pbuf *header;
  struct ip_hdr *iphdr;
  u16_t nfb;
  u16_t left, cop;
  u16_t mtu = netif->mtu;
  u16_t ofo, omf;
  u16_t last;
  u16_t poff = IP_HLEN;
  u16_t tmp;

//pbuf类型为PBUF_REF
  rambuf = pbuf_alloc(PBUF_LINK, 0, PBUF_REF);
  if (rambuf == NULL) {
    return ERR_MEM;
  }
  rambuf->tot_len = rambuf->len = mtu;
  rambuf->payload = LWIP_MEM_ALIGN((void *)buf);

  iphdr = (struct ip_hdr *)rambuf->payload;
  SMEMCPY(iphdr, p->payload, IP_HLEN);

  tmp = ntohs(IPH_OFFSET(iphdr));
  ofo = tmp & IP_OFFMASK;
  omf = tmp & IP_MF;

  left = p->tot_len - IP_HLEN;
  nfb = (mtu - IP_HLEN) / 8;

  while (left) {
    last = (left <= mtu - IP_HLEN);

    tmp = omf | (IP_OFFMASK & (ofo));
    if (!last) {
      tmp = tmp | IP_MF;
    }

    /* Fill this fragment */
    cop = last ? left : nfb * 8;


    poff += pbuf_copy_partial(p, (u8_t*)iphdr + IP_HLEN, cop, poff);

    /* Correct header */
    IPH_OFFSET_SET(iphdr, htons(tmp));
    IPH_LEN_SET(iphdr, htons(cop + IP_HLEN));
    IPH_CHKSUM_SET(iphdr, 0);
    IPH_CHKSUM_SET(iphdr, inet_chksum(iphdr, IP_HLEN));

    if (last) {
      pbuf_realloc(rambuf, left + IP_HLEN);
    }

//申请一个PBUF_RAM型的pbuf,以便以太网帧头部预留空间
    header = pbuf_alloc(PBUF_LINK, 0, PBUF_RAM);
    if (header != NULL) {
      pbuf_chain(header, rambuf); //连接两个pbuf
      netif->output(netif, header, dest);
      pbuf_free(header);
    } else {
      pbuf_free(rambuf);
      return ERR_MEM;
    }

    left -= cop;
    ofo += nfb;
  }
  pbuf_free(rambuf);
  return ERR_OK;
}
2、ip报文输入
(1)ip_input()

该函数的作用是首先判别ip地址是否给本地网口,其次判别该ip报文是否为分片报文,最后将该报文传输至上次协议。

err_t ip_input(struct pbuf *p, struct netif *inp)
{
  struct ip_hdr *iphdr;
  struct netif *netif;
  u16_t iphdr_hlen;
  u16_t iphdr_len;

//检查IP报文的合法性
  iphdr = (struct ip_hdr *)p->payload;
  if (IPH_V(iphdr) != 4) {
    pbuf_free(p);
    return ERR_OK;
  }

  iphdr_hlen = IPH_HL(iphdr);  
  iphdr_hlen *= 4;         //ip报文的首部长度
  iphdr_len = ntohs(IPH_LEN(iphdr));  //ip报文的总长度

//检查pbuf的长度是否小于IP报文的长度,pbuf链表第一个buf长度是否大于ip首部长度
  if ((iphdr_hlen > p->len) || (iphdr_len > p->tot_len)) {
    pbuf_free(p);
    return ERR_OK;
  }
//检查ip报文校验和
  if (inet_chksum(iphdr, iphdr_hlen) != 0) {
    return ERR_OK;
  }

  //删除多余的填充字节
  pbuf_realloc(p, iphdr_len);

  ip_addr_copy(current_iphdr_dest, iphdr->dest);
  ip_addr_copy(current_iphdr_src, iphdr->src);

  {
  //检查网络接口ip是否与ip地址ip相同
    int first = 1;
    netif = inp;
    do {
      if ((netif_is_up(netif)) && (!ip_addr_isany(&(netif->ip_addr)))) {

        if (ip_addr_cmp(&current_iphdr_dest, &(netif->ip_addr)) ||
            ip_addr_isbroadcast(&current_iphdr_dest, netif)) {
          break;
        }
      }
      if (first) {
        first = 0;
        netif = netif_list;
      } else {
        netif = netif->next;
      }
      if (netif == inp) {
        netif = netif->next;
      }
    } while(netif != NULL);
  }

//检查ip地址是否为多播包和广播包,若是则丢弃
  {  if ((ip_addr_isbroadcast(&current_iphdr_src, inp)) ||
         (ip_addr_ismulticast(&current_iphdr_src))) {
      pbuf_free(p);
      return ERR_OK;
    }
  }

//ip地址与本地网络接口ip不相同,则转发出去
  if (netif == NULL) {

    if (!ip_addr_isbroadcast(&current_iphdr_dest, inp)) {
      ip_forward(p, iphdr, inp);  //转发数据包
    } else
    {
      snmp_inc_ipinaddrerrors();
      snmp_inc_ipindiscards();
    }
    pbuf_free(p);
    return ERR_OK;
  }
  //检查IP报文的偏移量是否为0,不为0则是分片报文
  if ((IPH_OFFSET(iphdr) & PP_HTONS(IP_OFFMASK | IP_MF)) != 0) {
    p = ip_reass(p);     //重装分片ip报文
    if (p == NULL) {
      return ERR_OK;
    }

    iphdr = (struct ip_hdr *)p->payload;
  }


  current_netif = inp;
  current_header = iphdr;

  //根据上层协议不同,传输报文至上层协议
    switch (IPH_PROTO(iphdr)) {
#if LWIP_UDP
    case IP_PROTO_UDP:
      udp_input(p, inp);
      break;
#endif /* LWIP_UDP */
#if LWIP_TCP
    case IP_PROTO_TCP:
      snmp_inc_ipindelivers();
      tcp_input(p, inp);
      break;
#endif /* LWIP_TCP */
#if LWIP_ICMP
    case IP_PROTO_ICMP:
      snmp_inc_ipindelivers();
      icmp_input(p, inp);
      break;
#endif /* LWIP_ICMP */
#if LWIP_IGMP
    case IP_PROTO_IGMP:
      igmp_input(p, inp, &current_iphdr_dest);
      break;
#endif /* LWIP_IGMP */
    default:
#if LWIP_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);
      }
#endif /* LWIP_ICMP */
      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;
}
(2)ip_reass函数

1、结构体声明

struct ip_reassdata {
  struct ip_reassdata *next;
  struct pbuf *p;  //该数据报的数据链表
  struct ip_hdr iphdr; //该数据报的ip首部
  u16_t datagram_len; //已收到数据报的长度
  u8_t flags;  //是否收到最后一个分片
  u8_t timer;  //设置超时间隔
};

//ip报文pbuf连接帮助结构体
PACK_STRUCT_BEGIN
struct ip_reass_helper {
  PACK_STRUCT_FIELD(struct pbuf *next_pbuf);
  PACK_STRUCT_FIELD(u16_t start);
  PACK_STRUCT_FIELD(u16_t end);
} PACK_STRUCT_STRUCT;
PACK_STRUCT_END

2、ip_reass

struct pbuf *ip_reass(struct pbuf *p)
{
  struct pbuf *r;
  struct ip_hdr *fraghdr;
  struct ip_reassdata *ipr;
  struct ip_reass_helper *iprh;
  u16_t offset, len;
  u8_t clen;
  struct ip_reassdata *ipr_prev = NULL;

  fraghdr = (struct ip_hdr*)p->payload;

  if ((IPH_HL(fraghdr) * 4) != IP_HLEN) {
    goto nullreturn;
  }

  //确定ip报文的偏移字节,分片ip报文的数据长度
  offset = (ntohs(IPH_OFFSET(fraghdr)) & IP_OFFMASK) * 8;
  len = ntohs(IPH_LEN(fraghdr)) - IPH_HL(fraghdr) * 4;

  /* Check if we are allowed to enqueue more datagrams. */
  clen = pbuf_clen(p);
  if ((ip_reass_pbufcount + clen) > IP_REASS_MAX_PBUFS) {
#if IP_REASS_FREE_OLDEST
    if (!ip_reass_remove_oldest_datagram(fraghdr, clen) ||
        ((ip_reass_pbufcount + clen) > IP_REASS_MAX_PBUFS))
#endif /* IP_REASS_FREE_OLDEST */
    {
      goto nullreturn;
    }
  }

 //查找ip_reassdata重装数据包链表,检查该报文属于哪个ip_reassdata
  for (ipr = reassdatagrams; ipr != NULL; ipr = ipr->next) {
     if (IP_ADDRESSES_AND_ID_MATCH(&ipr->iphdr, fraghdr)) {
      break;
    }
    ipr_prev = ipr;
  }

  if (ipr == NULL) {
 //若ip_reassdata链表中无此报文的重装结构体,则新建一个ip_reassdata结构体
    ipr = ip_reass_enqueue_new_datagram(fraghdr, clen);
    if(ipr == NULL) {
      goto nullreturn;
    }
  } else {
  //此分片报文为ip_reassdata结构的第一个pbuf,则复制ip报文头部到找到ipr
    if (((ntohs(IPH_OFFSET(fraghdr)) & IP_OFFMASK) == 0) && 
      ((ntohs(IPH_OFFSET(&ipr->iphdr)) & IP_OFFMASK) != 0)) {
      SMEMCPY(&ipr->iphdr, fraghdr, IP_HLEN);
    }
  }

  //检查分片ip报文为ip_reassdata的最后一个pbuf,即IP_MF = 0
  if ((IPH_OFFSET(fraghdr) & PP_NTOHS(IP_MF)) == 0) {
    ipr->flags |= IP_REASS_FLAG_LASTFRAG;
    ipr->datagram_len = offset + len;//若为最后一个报文,确定整个ip报文的的数据长度
   }


  //分片ip报文插入与检查
  if (ip_reass_chain_frag_into_datagram_and_validate(ipr, p)) {
    ipr->datagram_len += IP_HLEN;

    处理除第一个pbuf以外的pbuf头部
    r = ((struct ip_reass_helper*)ipr->p->payload)->next_pbuf;

    /* copy the original ip header back to the first pbuf */
    fraghdr = (struct ip_hdr*)(ipr->p->payload);
    SMEMCPY(fraghdr, &ipr->iphdr, IP_HLEN);
    IPH_LEN_SET(fraghdr, htons(ipr->datagram_len));
    IPH_OFFSET_SET(fraghdr, 0);
    IPH_CHKSUM_SET(fraghdr, 0);
    IPH_CHKSUM_SET(fraghdr, inet_chksum(fraghdr, IP_HLEN));

    p = ipr->p;

    /* chain together the pbufs contained within the reass_data list. */
    while(r != NULL) {
      iprh = (struct ip_reass_helper*)r->payload;

      /* hide the ip header for every succeding fragment */
      pbuf_header(r, -IP_HLEN);
      pbuf_cat(p, r);
      r = iprh->next_pbuf;
    }

    //删除已经打包好ip报文的ip_reassdata
    ip_reass_dequeue_datagram(ipr, ipr_prev);

//更改全局变量的pbuf个数
    ip_reass_pbufcount -= pbuf_clen(p);

//返回打包好的pbuf
    return p;
  }

  return NULL;

nullreturn:
  pbuf_free(p);
  return NULL;
}

3、分片插入
正对ip_reassdata链表上的某一个ip_reassdata 进行pbuf数据的插入排序和验证ip_reassdata是否均接收到。

static int ip_reass_chain_frag_into_datagram_and_validate(struct ip_reassdata *ipr, struct pbuf *new_p)
{
  struct ip_reass_helper *iprh, *iprh_tmp, *iprh_prev=NULL;
  struct pbuf *q;
  u16_t offset,len;
  struct ip_hdr *fraghdr;
  int valid = 1;

  fraghdr = (struct ip_hdr*)new_p->payload; 
  len = ntohs(IPH_LEN(fraghdr)) - IPH_HL(fraghdr) * 4;
  offset = (ntohs(IPH_OFFSET(fraghdr)) & IP_OFFMASK) * 8;

  iprh = (struct ip_reass_helper*)new_p->payload;
  iprh->next_pbuf = NULL;
  iprh->start = offset;
  iprh->end = offset + len;

 //轮询整个ipr->p链表,并进行插入操作
  for (q = ipr->p; q != NULL;) {
    iprh_tmp = (struct ip_reass_helper*)q->payload;
    if (iprh->start < iprh_tmp->start) {
      iprh->next_pbuf = q;
      if (iprh_prev != NULL) {
        iprh_prev->next_pbuf = new_p;
      } else {
        /* fragment with the lowest offset */
        ipr->p = new_p;
      }
      break;
    } else if(iprh->start == iprh_tmp->start) {
      goto freepbuf;
    } else {
      if (iprh_prev != NULL) {
        if (iprh_prev->end != iprh_tmp->start) {
          valid = 0;
        }
      }
    }
    q = iprh_tmp->next_pbuf;
    iprh_prev = iprh_tmp;
  }


  if (q == NULL) {
    if (iprh_prev != NULL) {
        iprh_prev->next_pbuf = new_p;
      if (iprh_prev->end != iprh->start) {
        valid = 0;
      }
    } else {
      ipr->p = new_p;
    }
  }

  if ((ipr->flags & IP_REASS_FLAG_LASTFRAG) != 0) {
    if (valid) {
      if (((struct ip_reass_helper*)ipr->p->payload)->start != 0) {
        valid = 0;
      } else {
        /* and check that there are no wholes after this datagram */
        iprh_prev = iprh;
        q = iprh->next_pbuf;
        while (q != NULL) {
          iprh = (struct ip_reass_helper*)q->payload;
          if (iprh_prev->end != iprh->start) {
            valid = 0;
            break;
          }
          iprh_prev = iprh;
          q = iprh->next_pbuf;
        }
            }
    }

    return valid;
  }
    return 0; /* not yet valid! */
}
  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值