1 原理概述
先来看一下IP协议所处的位置:
IP协议的功能:
IP数据包的格式:
2 代码实现
先来看一下工程组织结构:
xnet_tiny.h中添加如下代码:
#pragma pack(1)
typedef struct _xip_hdr_t {
uint8_t hdr_len : 4; // 首部长, 4字节为单位
uint8_t version : 4; // 版本号
uint8_t tos; // 服务类型
uint16_t total_len; // 总长度
uint16_t id; // 标识符
uint16_t flags_fragment; // 标志与分段
uint8_t ttl; // 存活时间
uint8_t protocol; // 上层协议
uint16_t hdr_checksum; // 首部校验和
uint8_t src_ip[XNET_IPV4_ADDR_SIZE]; // 源IP
uint8_t dest_ip[XNET_IPV4_ADDR_SIZE]; // 目标IP
}xip_hdr_t;
#pragma pack()
#define XNET_VERSION_IPV4 4 // IPV4
void xip_init(void);
void xip_in(xnet_packet_t * packet);
xnet_tiny.c中添加如下代码:
/**
* 以太网数据帧输入输出
* @param packet 待处理的包
*/
static void ethernet_in (xnet_packet_t * packet) {
// 至少要比头部数据大
if (packet->size <= sizeof(xether_hdr_t)) {
return;
}
// 往上分解到各个协议处理
xether_hdr_t* hdr = (xether_hdr_t*)packet->data;
switch (swap_order16(hdr->protocol)) {
case XNET_PROTOCOL_ARP:
remove_header(packet, sizeof(xether_hdr_t));
xarp_in(packet);
break;
case XNET_PROTOCOL_IP: {
// 以下代码是从IP包头中提取IP地址,以及从以太网包头中提取mac地址
// 然后用其更新ARP表
#if 0
xip_hdr_t *iphdr = (xip_hdr_t *) (packet->data + sizeof(xether_hdr_t));
if (packet->size >= sizeof(xether_hdr_t) + sizeof(xip_hdr_t)) {
if (memcmp(iphdr->dest_ip, &netif_ipaddr.array, XNET_IPV4_ADDR_SIZE) == 0) {
update_arp_entry(iphdr->src_ip, hdr->src);
}
}
#endif
remove_header(packet, sizeof(xether_hdr_t));
xip_in(packet);
break;
}
}
}
/**
* 校验和计算
* @param buf 校验数据区的起始地址
* @param len 数据区的长度,以字节为单位
* @param pre_sum 累加的之前的值,用于多次调用checksum对不同的的数据区计算出一个校验和
* @param complement 是否对累加和的结果进行取反
* @return 校验和结果
*/
static uint16_t checksum16(uint16_t * buf, uint16_t len, uint16_t pre_sum, int complement) {
uint32_t checksum = pre_sum;
uint16_t high;
while (len > 1) {
checksum += *buf++;
len -= 2;
}
if (len > 0) {
checksum += *(uint8_t *)buf;
}
// 注意,这里要不断累加。不然结果在某些情况下计算不正确
while ((high = checksum >> 16) != 0) {
checksum = high + (checksum & 0xffff);
}
return complement ? (uint16_t)~checksum : (uint16_t)checksum;
}
/**
* IP层的初始化
*/
void xip_init(void) {}
/**
* IP层的输入处理
* @param packet 输入的IP数据包
*/
void xip_in(xnet_packet_t * packet) {
xip_hdr_t* iphdr = (xip_hdr_t*)packet->data;
uint32_t total_size, header_size;
uint16_t pre_checksum;
xipaddr_t src_ip;
// 进行一些必要性的检查:版本号要求
if (iphdr->version != XNET_VERSION_IPV4) {
return;
}
// 长度要求检查
header_size = iphdr->hdr_len * 4;
total_size = swap_order16(iphdr->total_len);
if ((header_size < sizeof(xip_hdr_t)) || ((total_size < header_size) || (packet->size < total_size))) {
return;
}
// 校验和要求检查
pre_checksum = iphdr->hdr_checksum;
iphdr->hdr_checksum = 0;
if (pre_checksum != checksum16((uint16_t*)iphdr, header_size, 0, 1)) {
return;
}
// 只处理目标IP为自己的数据包,其它广播之类的IP全部丢掉
if (!xipaddr_is_equal_buf(&netif_ipaddr, iphdr->dest_ip)) {
return;
}
// 多跟复用,分别交由ICMP、UDP、TCP处理
switch(iphdr->protocol) {
default:
break;
}
}
/**
* 协议栈的初始化
*/
void xnet_init (void) {
ethernet_init();
xarp_init();
xip_init();
}