因公司项目需求,需要使用UDP服务与UDP组播,并且监听多端口数据,最开始没考虑那么多,直接就上了 LWIP 轻量级协议栈,参考正点原子的STM32F4开发板的 LWIP 移植,完成之后,实现单独的 UDP 服务或单独的 UDP 组播,是完全没有问题的,不过问题就在于既需要使用 UDP 多端口服务,又要使用 UDP 多端口组播,没有相对应的使用示例,自己对 LWIP 不是很了解,怕把问题搞复杂了,于是自己参考了 LWIP 的部分读包技巧与公司提供的上一版程序的开发思路,重新自己开发ARP响应与ICMP回包服务,下面是踩过的一些坑,这里记录一下。
经过以上流程以后,ping 功能就算完工啦,感谢各位的观看,为了一些不必要的麻烦,这里就不上图了,代码处理在下面,可以参考,仅提供功能的核心模块逻辑,剩下的需要靠自己去完善啦,大部分思路是参考的 LWIP, 但是 LWIP,无法实现我的功能需求(更大的可能性是我太菜,没有把LWIP玩明白),所以得自己重写。
公共部分
struct eth_hdr {
struct mac_addr dest; /* Destination MAC address */
struct mac_addr src; /* Source MAC address */
uint16_t type; /* Ethernet protocol type */
};
enum
{
ETH_PROTO_ARP = 0,
ETH_PROTO_IP,
ETH_PROTO_NUM
};
u8 ethhdr_pack(u8* buf, u8* dst_mac, u8* src_mac, u8 type) /* 使用前请添加assert()断言数组长度大于13 */
{
u8 eth_offset,
tmp_data[ETHHDR_FRAMETYPE_LEN] = {0};
switch(type)
{
case ETH_PROTO_ARP:
tmp_data[0] = PP_HTONS(ETHTYPE_ARP) & 0xFF;
tmp_data[1] = PP_HTONS(ETHTYPE_ARP) >> 8;
break;
case ETH_PROTO_IP:
tmp_data[0] = PP_HTONS(ETHTYPE_IP) & 0xFF;
tmp_data[1] = PP_HTONS(ETHTYPE_IP) >> 8;
break;
default:
break;
}
eth_offset = 0;
memcpy(buf + eth_offset, dst_mac, ETHHDR_DSTMAC_LEN);
eth_offset += ETHHDR_DSTMAC_LEN;
memcpy(buf + eth_offset, src_mac, ETHHDR_SRCMAC_LEN);
eth_offset += ETHHDR_SRCMAC_LEN;
memcpy(buf + eth_offset, tmp_data, ETHHDR_FRAMETYPE_LEN);
eth_offset += ETHHDR_FRAMETYPE_LEN;
return eth_offset;
}
arp 部分
/** the ARP message, see RFC 826 ("Packet format") */
struct etharp_hdr {
uint16_t hwtype;
uint16_t proto;
uint8_t hwlen;
uint8_t protolen;
uint16_t opcode;
struct mac_addr shwaddr;
struct ip_addr sipaddr;
struct mac_addr dhwaddr;
struct ip_addr dipaddr;
};
void arp_answer(struct etharp_hdr *arphdr)
{
u8 answer_buf[ARP_PACK_LEN],
tmp_data[2];
u32 arp_offset;
memset(answer_buf, 0, ARP_PACK_LEN);
arp_offset = ethhdr_pack(answer_buf, arphdr->shwaddr.addr, mjipdev.mac, ETH_PROTO_ARP);
memcpy(answer_buf + arp_offset, &arphdr->hwtype, ARPHDR_HARDTYPE_LEN);
arp_offset += ARPHDR_HARDTYPE_LEN;
memcpy(answer_buf + arp_offset, &arphdr->proto, ARPHDR_PROTOTYPE_LEN);
arp_offset += ARPHDR_PROTOTYPE_LEN;
memcpy(answer_buf + arp_offset, &arphdr->hwlen, ARPHDR_HARDLEN_LEN);
arp_offset += ARPHDR_HARDLEN_LEN;
memcpy(answer_buf + arp_offset, &arphdr->protolen, ARPHDR_PROTOLEN_LEN);
arp_offset += ARPHDR_PROTOLEN_LEN;
tmp_data[0] = ARP_REPLY >> 8;
tmp_data[1] = ARP_REPLY & 0xFF;
memcpy(answer_buf + arp_offset, tmp_data, ARPHDR_OPTION_LEN);
arp_offset += ARPHDR_OPTION_LEN;
memcpy(answer_buf + arp_offset, mjipdev.mac, ARPHDR_SRCMAC_LEN);
arp_offset += ARPHDR_SRCMAC_LEN;
memcpy(answer_buf + arp_offset, mjipdev.ip, ARPHDR_SRCEIP_LEN);
arp_offset += ARPHDR_SRCEIP_LEN;
memcpy(answer_buf + arp_offset, arphdr->shwaddr.addr, ARPHDR_DSTMAC_LEN);
arp_offset += ARPHDR_DSTMAC_LEN;
memcpy(answer_buf + arp_offset, arphdr->sipaddr.addr, ARPHDR_DSTEIP_LEN);
memcpy(send_buf, answer_buf, ARP_PACK_LEN);
server_send(ARP_PACK_LEN);
icmp 部分
struct ip_hdr {
/* version / header length */
uint8_t _v_hl;
/* type of service */
uint8_t _tos;
/* total length */
uint16_t _len;
/* identification */
uint16_t _id;
/* fragment offset field */
uint16_t _offset;
/* time to live */
uint8_t _ttl;
/* protocol*/
uint8_t _proto;
/* checksum */
uint16_t _chksum;
/* source and destination IP addresses */
struct ip_addr src;
struct ip_addr dest;
};
struct icmp_info
{
uint8_t type;
uint8_t opcode;
uint16_t chksum;
};
uint8_t iphdr_pack(uint8_t* buf, struct ip_hdr* iphdr)
{
u8 tmp_data[2];
u16 chksum;
u32 ip_offset = 0,
chksum_offset;
memcpy(buf + ip_offset, &iphdr->_v_hl, IPHDR_VHDRLEN_LEN);
ip_offset += IPHDR_VHDRLEN_LEN;
memcpy(buf + ip_offset, &iphdr->_tos, IPHDR_TOS_LEN);
ip_offset += IPHDR_TOS_LEN;
memcpy(buf + ip_offset, &iphdr->_len, IPHDR_TOTALLEN_LEN);
ip_offset += IPHDR_TOTALLEN_LEN;
iphdr->_id += 0x0100;
memcpy(buf + ip_offset, &iphdr->_id, IPHDR_ID_LEN);
ip_offset += IPHDR_ID_LEN;
tmp_data[0] = IPHDR_OFFSET_VAL & 0xFF;
tmp_data[1] = IPHDR_OFFSET_VAL >> 8;
memcpy(buf + ip_offset, tmp_data, IPHDR_OFFSET_LEN);
ip_offset += IPHDR_OFFSET_LEN;
tmp_data[0] = IPHDR_TTL_VAL;
memcpy(buf + ip_offset, tmp_data, IPHDR_TTL_LEN);
ip_offset += IPHDR_TTL_LEN;
memcpy(buf + ip_offset, &iphdr->_proto, IPHDR_PROTO_LEN);
ip_offset += IPHDR_PROTO_LEN;
tmp_data[0] = 0x00;
tmp_data[1] = 0x00;
chksum_offset = ip_offset;
memcpy(buf + ip_offset, &tmp_data, IPHDR_CHKSUM_LEN);
ip_offset += IPHDR_CHKSUM_LEN;
memcpy(buf + ip_offset, mjipdev.ip, IPHDR_SRCIP_LEN);
ip_offset += IPHDR_SRCIP_LEN;
memcpy(buf + ip_offset, &iphdr->src.addr, IPHDR_DSTIP_LEN);
ip_offset += IPHDR_DSTIP_LEN;
//IP 校验和
chksum = checksum(buf, IPHDR_MIN_LEN, CHECKSUM_IP);
chksum = PP_HTONS(chksum); //校验反转,否则 PC 端不通过,请求超时
memcpy(buf + chksum_offset, &chksum, IPHDR_CHKSUM_LEN);
return ip_offset + ETHHDR_LEN;
}
void icmp_reply(u8* buf, u32 len)
{
u8 answer_buf[SERVER_TX_BUFSIZE],
tmp_data[2];
u16 chksum;
u32 icmp_offset,
icmp_chksum_offset; //用于存放 ICMP 的 checksum 校验和
struct eth_hdr* ethhdr;
struct ip_hdr* iphdr;
ethhdr = (struct eth_hdr*)buf;
iphdr = (struct ip_hdr*)((u8*)ethhdr + ETHHDR_LEN);
memset(answer_buf, 0, len);
icmp_offset = ethhdr_pack(answer_buf, ethhdr->src.addr, mjipdev.mac, ETH_PROTO_IP);
icmp_offset = iphdr_pack(answer_buf + icmp_offset, iphdr);
tmp_data[0] = ICMP_OPT_REPLY;
tmp_data[1] = ICMP_CODE_REPLY;
memcpy(answer_buf + icmp_offset, tmp_data, ICMP_OPTCODE_LEN);
icmp_offset += ICMP_OPTCODE_LEN;
tmp_data[0] = 0x00;
tmp_data[1] = 0x00;
icmp_chksum_offset = icmp_offset;
memcpy(answer_buf + icmp_offset, &tmp_data, ICMP_CHKSUM_LEN);
icmp_offset += ICMP_CHKSUM_LEN;
memcpy(answer_buf + icmp_offset, buf + icmp_offset, len - icmp_offset);
//ICMP 校验和
chksum = checksum(answer_buf + ETHHDR_LEN + IPHDR_MIN_LEN, len - ETHHDR_LEN - IPHDR_MIN_LEN, CHECKSUM_IP);
chksum = PP_HTONS(chksum); //校验反转,否则 PC 端不通过,请求超时
memcpy(answer_buf + icmp_chksum_offset, &chksum, IPHDR_CHKSUM_LEN);
memcpy(send_buf, answer_buf, len);
server_send(len);
}
学习分享,一起成长!以上为小编的实验分享,若存在不当之处,请批评指正!