skaiuijing

IP协议是一种无连接且不可靠的协议,只提供尽力而为的服务。

IP报头结构

IP报头结构如下:

字段 长度(字节)
版本/头部长度 1
服务类型 1
总长度 2
标识 2
片偏移字段 2
生存时间(TTL) 1
协议 1
校验和 2
源IP地址 4
目的IP地址 4

字段

校验和

IP协议的校验和原理同UDP协议,都是采用原码与反码之和作为校验判断的。为了能够使用16位字保存校验和,计算校验和时采取了循环进位的方式。

如果数据二进制反码的求和大于16位,那么让超出16位的部分继续与低16位相加,直到校验和为16位。

IPv4

IPv6

分片

IP转发

lwip实现

结构体

lwip中,IP首部结构体定义如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
struct ip_hdr {
/* version / header length */
PACK_STRUCT_FLD_8(u8_t _v_hl);
/* type of service */
PACK_STRUCT_FLD_8(u8_t _tos);
/* total length */
PACK_STRUCT_FIELD(u16_t _len);
/* identification */
PACK_STRUCT_FIELD(u16_t _id);
/* fragment offset field */
PACK_STRUCT_FIELD(u16_t _offset);
#define IP_RF 0x8000U /* reserved fragment flag */
#define IP_DF 0x4000U /* don't fragment flag */
#define IP_MF 0x2000U /* more fragments flag */
#define IP_OFFMASK 0x1fffU /* mask for fragmenting bits */
/* time to live */
PACK_STRUCT_FLD_8(u8_t _ttl);
/* protocol*/
PACK_STRUCT_FLD_8(u8_t _proto);
/* checksum */
PACK_STRUCT_FIELD(u16_t _chksum);
/* source and destination IP addresses */
PACK_STRUCT_FLD_S(ip4_addr_p_t src);
PACK_STRUCT_FLD_S(ip4_addr_p_t dest);
} PACK_STRUCT_STRUCT;

发送(IPv4)

发送源码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
/**
* Same as ip_output_if() but 'src' address is not replaced by netif address
* when it is 'any'.
*/
err_t
ip4_output_if_src(struct pbuf *p, const ip4_addr_t *src, const ip4_addr_t *dest,
u8_t ttl, u8_t tos,
u8_t proto, struct netif *netif)
{
#if IP_OPTIONS_SEND
return ip4_output_if_opt_src(p, src, dest, ttl, tos, proto, netif, NULL, 0);
}

/**
* Same as ip_output_if_opt() but 'src' address is not replaced by netif address
* when it is 'any'.
*/
err_t
ip4_output_if_opt_src(struct pbuf *p, const ip4_addr_t *src, const ip4_addr_t *dest,
u8_t ttl, u8_t tos, u8_t proto, struct netif *netif, void *ip_options,
u16_t optlen)
{
#endif /* IP_OPTIONS_SEND */
struct ip_hdr *iphdr;
ip4_addr_t dest_addr;
#if CHECKSUM_GEN_IP_INLINE
u32_t chk_sum = 0;
#endif /* CHECKSUM_GEN_IP_INLINE */

LWIP_ASSERT_CORE_LOCKED();
LWIP_IP_CHECK_PBUF_REF_COUNT_FOR_TX(p);

MIB2_STATS_INC(mib2.ipoutrequests);

/* Should the IP header be generated or is it already included in p? */
if (dest != LWIP_IP_HDRINCL) {
u16_t ip_hlen = IP_HLEN;
#if IP_OPTIONS_SEND
u16_t optlen_aligned = 0;
if (optlen != 0) {
#if CHECKSUM_GEN_IP_INLINE
int i;
#endif /* CHECKSUM_GEN_IP_INLINE */
if (optlen > (IP_HLEN_MAX - IP_HLEN)) {
/* optlen too long */
LWIP_DEBUGF(IP_DEBUG | LWIP_DBG_LEVEL_SERIOUS, ("ip4_output_if_opt: optlen too long\n"));
IP_STATS_INC(ip.err);
MIB2_STATS_INC(mib2.ipoutdiscards);
return ERR_VAL;
}
/* round up to a multiple of 4 */
optlen_aligned = (u16_t)((optlen + 3) & ~3);
ip_hlen = (u16_t)(ip_hlen + optlen_aligned);
/* First write in the IP options */
if (pbuf_add_header(p, optlen_aligned)) {
LWIP_DEBUGF(IP_DEBUG | LWIP_DBG_LEVEL_SERIOUS, ("ip4_output_if_opt: not enough room for IP options in pbuf\n"));
IP_STATS_INC(ip.err);
MIB2_STATS_INC(mib2.ipoutdiscards);
return ERR_BUF;
}
MEMCPY(p->payload, ip_options, optlen);
if (optlen < optlen_aligned) {
/* zero the remaining bytes */
memset(((char *)p->payload) + optlen, 0, (size_t)(optlen_aligned - optlen));
}
#if CHECKSUM_GEN_IP_INLINE
for (i = 0; i < optlen_aligned / 2; i++) {
chk_sum += ((u16_t *)p->payload)[i];
}
#endif /* CHECKSUM_GEN_IP_INLINE */
}
#endif /* IP_OPTIONS_SEND */
/* generate IP header */
if (pbuf_add_header(p, IP_HLEN)) {
LWIP_DEBUGF(IP_DEBUG | LWIP_DBG_LEVEL_SERIOUS, ("ip4_output: not enough room for IP header in pbuf\n"));

IP_STATS_INC(ip.err);
MIB2_STATS_INC(mib2.ipoutdiscards);
return ERR_BUF;
}

iphdr = (struct ip_hdr *)p->payload;
LWIP_ASSERT("check that first pbuf can hold struct ip_hdr",
(p->len >= sizeof(struct ip_hdr)));

IPH_TTL_SET(iphdr, ttl);
IPH_PROTO_SET(iphdr, proto);
#if CHECKSUM_GEN_IP_INLINE
chk_sum += PP_NTOHS(proto | (ttl << 8));
#endif /* CHECKSUM_GEN_IP_INLINE */

/* dest cannot be NULL here */
ip4_addr_copy(iphdr->dest, *dest);
#if CHECKSUM_GEN_IP_INLINE
chk_sum += ip4_addr_get_u32(&iphdr->dest) & 0xFFFF;
chk_sum += ip4_addr_get_u32(&iphdr->dest) >> 16;
#endif /* CHECKSUM_GEN_IP_INLINE */

IPH_VHL_SET(iphdr, 4, ip_hlen / 4);
IPH_TOS_SET(iphdr, tos);
#if CHECKSUM_GEN_IP_INLINE
chk_sum += PP_NTOHS(tos | (iphdr->_v_hl << 8));
#endif /* CHECKSUM_GEN_IP_INLINE */
IPH_LEN_SET(iphdr, lwip_htons(p->tot_len));
#if CHECKSUM_GEN_IP_INLINE
chk_sum += iphdr->_len;
#endif /* CHECKSUM_GEN_IP_INLINE */
IPH_OFFSET_SET(iphdr, 0);
IPH_ID_SET(iphdr, lwip_htons(ip_id));
#if CHECKSUM_GEN_IP_INLINE
chk_sum += iphdr->_id;
#endif /* CHECKSUM_GEN_IP_INLINE */
++ip_id;

if (src == NULL) {
ip4_addr_copy(iphdr->src, *IP4_ADDR_ANY4);
} else {
/* src cannot be NULL here */
ip4_addr_copy(iphdr->src, *src);
}

#if CHECKSUM_GEN_IP_INLINE
chk_sum += ip4_addr_get_u32(&iphdr->src) & 0xFFFF;
chk_sum += ip4_addr_get_u32(&iphdr->src) >> 16;
chk_sum = (chk_sum >> 16) + (chk_sum & 0xFFFF);
chk_sum = (chk_sum >> 16) + chk_sum;
chk_sum = ~chk_sum;
IF__NETIF_CHECKSUM_ENABLED(netif, NETIF_CHECKSUM_GEN_IP) {
iphdr->_chksum = (u16_t)chk_sum; /* network order */
}
#if LWIP_CHECKSUM_CTRL_PER_NETIF
else {
IPH_CHKSUM_SET(iphdr, 0);
}
#endif /* LWIP_CHECKSUM_CTRL_PER_NETIF*/
#else /* CHECKSUM_GEN_IP_INLINE */
IPH_CHKSUM_SET(iphdr, 0);
#if CHECKSUM_GEN_IP
IF__NETIF_CHECKSUM_ENABLED(netif, NETIF_CHECKSUM_GEN_IP) {
IPH_CHKSUM_SET(iphdr, inet_chksum(iphdr, ip_hlen));
}
#endif /* CHECKSUM_GEN_IP */
#endif /* CHECKSUM_GEN_IP_INLINE */
} else {
/* IP header already included in p */
if (p->len < IP_HLEN) {
LWIP_DEBUGF(IP_DEBUG | LWIP_DBG_LEVEL_SERIOUS, ("ip4_output: LWIP_IP_HDRINCL but pbuf is too short\n"));
IP_STATS_INC(ip.err);
MIB2_STATS_INC(mib2.ipoutdiscards);
return ERR_BUF;
}
iphdr = (struct ip_hdr *)p->payload;
ip4_addr_copy(dest_addr, iphdr->dest);
dest = &dest_addr;
}

IP_STATS_INC(ip.xmit);

LWIP_DEBUGF(IP_DEBUG, ("ip4_output_if: %c%c%"U16_F"\n", netif->name[0], netif->name[1], (u16_t)netif->num));
ip4_debug_print(p);

#if ENABLE_LOOPBACK
if (ip4_addr_eq(dest, netif_ip4_addr(netif))
#if !LWIP_HAVE_LOOPIF
|| ip4_addr_isloopback(dest)
#endif /* !LWIP_HAVE_LOOPIF */
) {
/* Packet to self, enqueue it for loopback */
LWIP_DEBUGF(IP_DEBUG, ("netif_loop_output()\n"));
return netif_loop_output(netif, p);
}
#if LWIP_MULTICAST_TX_OPTIONS
if ((p->flags & PBUF_FLAG_MCASTLOOP) != 0) {
netif_loop_output(netif, p);
}
#endif /* LWIP_MULTICAST_TX_OPTIONS */
#endif /* ENABLE_LOOPBACK */
#if IP_FRAG
/* don't fragment if interface has mtu set to 0 [loopif] */
if (netif->mtu && (p->tot_len > netif->mtu)) {
return ip4_frag(p, netif, dest);
}
#endif /* IP_FRAG */

LWIP_DEBUGF(IP_DEBUG, ("ip4_output_if: call netif->output()\n"));
return netif->output(netif, p, dest);
}

BSD实现