ip: optimize ip4_header_checksum
[vpp.git] / src / vnet / ip / ip4_packet.h
1 /*
2  * Copyright (c) 2015 Cisco and/or its affiliates.
3  * Licensed under the Apache License, Version 2.0 (the "License");
4  * you may not use this file except in compliance with the License.
5  * You may obtain a copy of the License at:
6  *
7  *     http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14  */
15 /*
16  * ip4/packet.h: ip4 packet format
17  *
18  * Copyright (c) 2008 Eliot Dresselhaus
19  *
20  * Permission is hereby granted, free of charge, to any person obtaining
21  * a copy of this software and associated documentation files (the
22  * "Software"), to deal in the Software without restriction, including
23  * without limitation the rights to use, copy, modify, merge, publish,
24  * distribute, sublicense, and/or sell copies of the Software, and to
25  * permit persons to whom the Software is furnished to do so, subject to
26  * the following conditions:
27  *
28  * The above copyright notice and this permission notice shall be
29  * included in all copies or substantial portions of the Software.
30  *
31  *  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
32  *  EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
33  *  MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
34  *  NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
35  *  LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
36  *  OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
37  *  WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
38  */
39
40 #ifndef included_ip4_packet_h
41 #define included_ip4_packet_h
42
43 #include <vnet/ip/ip_packet.h>  /* for ip_csum_t */
44 #include <vnet/tcp/tcp_packet.h>        /* for tcp_header_t */
45 #include <vppinfra/byte_order.h>        /* for clib_net_to_host_u16 */
46
47 /* IP4 address which can be accessed either as 4 bytes
48    or as a 32-bit number. */
49 typedef union
50 {
51   u8 data[4];
52   u32 data_u32;
53   /* Aliases. */
54   u8 as_u8[4];
55   u16 as_u16[2];
56   u32 as_u32;
57 } ip4_address_t;
58
59 typedef struct
60 {
61   /* IP address must be first for ip_interface_address_get_address() to work */
62   ip4_address_t ip4_addr;
63   u32 fib_index;
64 } ip4_address_fib_t;
65
66 always_inline void
67 ip4_addr_fib_init (ip4_address_fib_t * addr_fib,
68                    const ip4_address_t * address, u32 fib_index)
69 {
70   clib_memcpy_fast (&addr_fib->ip4_addr, address,
71                     sizeof (addr_fib->ip4_addr));
72   addr_fib->fib_index = fib_index;
73 }
74
75 /* (src,dst) pair of addresses as found in packet header. */
76 typedef struct
77 {
78   ip4_address_t src, dst;
79 } ip4_address_pair_t;
80
81 typedef struct
82 {
83   ip4_address_t addr, mask;
84 } ip4_address_and_mask_t;
85
86 typedef union
87 {
88   struct
89   {
90     /* 4 bit packet length (in 32bit units) and version VVVVLLLL.
91        e.g. for packets w/ no options ip_version_and_header_length == 0x45. */
92     u8 ip_version_and_header_length;
93
94     /* Type of service. */
95     ip_dscp_t tos;
96
97     /* Total layer 3 packet length including this header. */
98     u16 length;
99
100     /* Fragmentation ID. */
101     u16 fragment_id;
102
103     /* 3 bits of flags and 13 bits of fragment offset (in units
104        of 8 byte quantities). */
105     u16 flags_and_fragment_offset;
106 #define IP4_HEADER_FLAG_MORE_FRAGMENTS (1 << 13)
107 #define IP4_HEADER_FLAG_DONT_FRAGMENT (1 << 14)
108 #define IP4_HEADER_FLAG_CONGESTION (1 << 15)
109
110     /* Time to live decremented by router at each hop. */
111     u8 ttl;
112
113     /* Next level protocol packet. */
114     u8 protocol;
115
116     /* Checksum. */
117     u16 checksum;
118
119     /* Source and destination address. */
120     union
121     {
122       struct
123       {
124         ip4_address_t src_address, dst_address;
125       };
126       ip4_address_pair_t address_pair;
127     };
128   };
129
130   /* For checksumming we'll want to access IP header in word sized chunks. */
131   /* For 64 bit machines. */
132   /* *INDENT-OFF* */
133   CLIB_PACKED (struct {
134     u64 checksum_data_64[2];
135     u32 checksum_data_64_32[1];
136   });
137   /* *INDENT-ON* */
138
139   /* For 32 bit machines. */
140   /* *INDENT-OFF* */
141   CLIB_PACKED (struct {
142     u32 checksum_data_32[5];
143   });
144   /* *INDENT-ON* */
145 } ip4_header_t;
146
147 /* Value of ip_version_and_header_length for packets w/o options. */
148 #define IP4_VERSION_AND_HEADER_LENGTH_NO_OPTIONS \
149   ((4 << 4) | (sizeof (ip4_header_t) / sizeof (u32)))
150
151 #define IP4_ROUTER_ALERT_OPTION 20
152
153 always_inline u16
154 ip4_get_fragment_offset (const ip4_header_t * i)
155 {
156   return clib_net_to_host_u16 (i->flags_and_fragment_offset) & 0x1fff;
157 }
158
159 always_inline u16
160 ip4_get_fragment_more (const ip4_header_t * i)
161 {
162   return clib_net_to_host_u16 (i->flags_and_fragment_offset) &
163     IP4_HEADER_FLAG_MORE_FRAGMENTS;
164 }
165
166 always_inline int
167 ip4_is_fragment (const ip4_header_t * i)
168 {
169   return (i->flags_and_fragment_offset &
170           clib_net_to_host_u16 (0x1fff | IP4_HEADER_FLAG_MORE_FRAGMENTS));
171 }
172
173 always_inline int
174 ip4_is_first_fragment (const ip4_header_t * i)
175 {
176   return (i->flags_and_fragment_offset &
177           clib_net_to_host_u16 (0x1fff | IP4_HEADER_FLAG_MORE_FRAGMENTS)) ==
178     clib_net_to_host_u16 (IP4_HEADER_FLAG_MORE_FRAGMENTS);
179 }
180
181 /* Fragment offset in bytes. */
182 always_inline int
183 ip4_get_fragment_offset_bytes (const ip4_header_t * i)
184 {
185   return 8 * ip4_get_fragment_offset (i);
186 }
187
188 always_inline int
189 ip4_header_bytes (const ip4_header_t * i)
190 {
191   return sizeof (u32) * (i->ip_version_and_header_length & 0xf);
192 }
193
194 always_inline void *
195 ip4_next_header (ip4_header_t * i)
196 {
197   return (void *) i + ip4_header_bytes (i);
198 }
199
200 always_inline u16
201 ip4_header_checksum (ip4_header_t * i)
202 {
203   u16 *iphdr = (u16 *) i;
204   u32 sum = 0;
205   int option_len = (i->ip_version_and_header_length & 0xf) - 5;
206
207   sum += clib_net_to_host_u16 (iphdr[0]);
208   sum += clib_net_to_host_u16 (iphdr[1]);
209   sum += clib_net_to_host_u16 (iphdr[2]);
210   sum += clib_net_to_host_u16 (iphdr[3]);
211   sum += clib_net_to_host_u16 (iphdr[4]);
212
213   sum += clib_net_to_host_u16 (iphdr[6]);
214   sum += clib_net_to_host_u16 (iphdr[7]);
215   sum += clib_net_to_host_u16 (iphdr[8]);
216   sum += clib_net_to_host_u16 (iphdr[9]);
217
218   if (PREDICT_FALSE (option_len > 0))
219     switch (option_len)
220       {
221       case 10:
222         sum += clib_net_to_host_u16 (iphdr[28]);
223         sum += clib_net_to_host_u16 (iphdr[29]);
224       case 9:
225         sum += clib_net_to_host_u16 (iphdr[26]);
226         sum += clib_net_to_host_u16 (iphdr[27]);
227       case 8:
228         sum += clib_net_to_host_u16 (iphdr[24]);
229         sum += clib_net_to_host_u16 (iphdr[25]);
230       case 7:
231         sum += clib_net_to_host_u16 (iphdr[22]);
232         sum += clib_net_to_host_u16 (iphdr[23]);
233       case 6:
234         sum += clib_net_to_host_u16 (iphdr[20]);
235         sum += clib_net_to_host_u16 (iphdr[21]);
236       case 5:
237         sum += clib_net_to_host_u16 (iphdr[18]);
238         sum += clib_net_to_host_u16 (iphdr[19]);
239       case 4:
240         sum += clib_net_to_host_u16 (iphdr[16]);
241         sum += clib_net_to_host_u16 (iphdr[17]);
242       case 3:
243         sum += clib_net_to_host_u16 (iphdr[14]);
244         sum += clib_net_to_host_u16 (iphdr[15]);
245       case 2:
246         sum += clib_net_to_host_u16 (iphdr[12]);
247         sum += clib_net_to_host_u16 (iphdr[13]);
248       case 1:
249         sum += clib_net_to_host_u16 (iphdr[10]);
250         sum += clib_net_to_host_u16 (iphdr[11]);
251       default:
252         break;
253       }
254
255   sum = ((u16) sum) + (sum >> 16);
256   sum = ((u16) sum) + (sum >> 16);
257   return clib_host_to_net_u16 (~((u16) sum));
258 }
259
260 always_inline void
261 ip4_header_set_dscp (ip4_header_t * ip4, ip_dscp_t dscp)
262 {
263   ip4->tos &= ~0xfc;
264   /* not masking the dscp value to save th instruction
265    * it shouldn't b necessary since the argument is an enum
266    * whose range is therefore constrained in the CP. in the
267    * DP it will have been taken from another packet, so again
268    * constrained in  value */
269   ip4->tos |= dscp << IP_PACKET_TC_FIELD_DSCP_BIT_SHIFT;
270 }
271
272 always_inline void
273 ip4_header_set_ecn (ip4_header_t * ip4, ip_ecn_t ecn)
274 {
275   ip4->tos &= ~IP_PACKET_TC_FIELD_ECN_MASK;
276   ip4->tos |= ecn;
277 }
278
279 always_inline void
280 ip4_header_set_ecn_w_chksum (ip4_header_t * ip4, ip_ecn_t ecn)
281 {
282   ip_csum_t sum = ip4->checksum;
283   u8 old = ip4->tos;
284   u8 new = (old & ~IP_PACKET_TC_FIELD_ECN_MASK) | ecn;
285
286   sum = ip_csum_update (sum, old, new, ip4_header_t, tos);
287   ip4->checksum = ip_csum_fold (sum);
288   ip4->tos = new;
289 }
290
291 always_inline ip_dscp_t
292 ip4_header_get_dscp (const ip4_header_t * ip4)
293 {
294   return (ip4->tos >> IP_PACKET_TC_FIELD_DSCP_BIT_SHIFT);
295 }
296
297 always_inline ip_ecn_t
298 ip4_header_get_ecn (const ip4_header_t * ip4)
299 {
300   return (ip4->tos & IP_PACKET_TC_FIELD_ECN_MASK);
301 }
302
303 always_inline void
304 ip4_header_set_df (ip4_header_t * ip4)
305 {
306   ip4->flags_and_fragment_offset |=
307     clib_host_to_net_u16 (IP4_HEADER_FLAG_DONT_FRAGMENT);
308 }
309
310 always_inline void
311 ip4_header_clear_df (ip4_header_t * ip4)
312 {
313   ip4->flags_and_fragment_offset &=
314     ~clib_host_to_net_u16 (IP4_HEADER_FLAG_DONT_FRAGMENT);
315 }
316
317 always_inline u8
318 ip4_header_get_df (const ip4_header_t * ip4)
319 {
320   return (! !(ip4->flags_and_fragment_offset &
321               clib_host_to_net_u16 (IP4_HEADER_FLAG_DONT_FRAGMENT)));
322 }
323
324 static inline uword
325 ip4_header_checksum_is_valid (ip4_header_t * i)
326 {
327   return i->checksum == ip4_header_checksum (i);
328 }
329
330 #define ip4_partial_header_checksum_x1(ip0,sum0)                        \
331 do {                                                                    \
332   if (BITS (ip_csum_t) > 32)                                            \
333     {                                                                   \
334       sum0 = ip0->checksum_data_64[0];                                  \
335       sum0 = ip_csum_with_carry (sum0, ip0->checksum_data_64[1]);       \
336       sum0 = ip_csum_with_carry (sum0, ip0->checksum_data_64_32[0]);    \
337     }                                                                   \
338   else                                                                  \
339     {                                                                   \
340       sum0 = ip0->checksum_data_32[0];                                  \
341       sum0 = ip_csum_with_carry (sum0, ip0->checksum_data_32[1]);       \
342       sum0 = ip_csum_with_carry (sum0, ip0->checksum_data_32[2]);       \
343       sum0 = ip_csum_with_carry (sum0, ip0->checksum_data_32[3]);       \
344       sum0 = ip_csum_with_carry (sum0, ip0->checksum_data_32[4]);       \
345     }                                                                   \
346 } while (0)
347
348 #define ip4_partial_header_checksum_x2(ip0,ip1,sum0,sum1)               \
349 do {                                                                    \
350   if (BITS (ip_csum_t) > 32)                                            \
351     {                                                                   \
352       sum0 = ip0->checksum_data_64[0];                                  \
353       sum1 = ip1->checksum_data_64[0];                                  \
354       sum0 = ip_csum_with_carry (sum0, ip0->checksum_data_64[1]);       \
355       sum1 = ip_csum_with_carry (sum1, ip1->checksum_data_64[1]);       \
356       sum0 = ip_csum_with_carry (sum0, ip0->checksum_data_64_32[0]);    \
357       sum1 = ip_csum_with_carry (sum1, ip1->checksum_data_64_32[0]);    \
358     }                                                                   \
359   else                                                                  \
360     {                                                                   \
361       sum0 = ip0->checksum_data_32[0];                                  \
362       sum1 = ip1->checksum_data_32[0];                                  \
363       sum0 = ip_csum_with_carry (sum0, ip0->checksum_data_32[1]);       \
364       sum1 = ip_csum_with_carry (sum1, ip1->checksum_data_32[1]);       \
365       sum0 = ip_csum_with_carry (sum0, ip0->checksum_data_32[2]);       \
366       sum1 = ip_csum_with_carry (sum1, ip1->checksum_data_32[2]);       \
367       sum0 = ip_csum_with_carry (sum0, ip0->checksum_data_32[3]);       \
368       sum1 = ip_csum_with_carry (sum1, ip1->checksum_data_32[3]);       \
369       sum0 = ip_csum_with_carry (sum0, ip0->checksum_data_32[4]);       \
370       sum1 = ip_csum_with_carry (sum1, ip1->checksum_data_32[4]);       \
371     }                                                                   \
372 } while (0)
373
374 always_inline uword
375 ip4_address_is_multicast (const ip4_address_t * a)
376 {
377   return (a->data[0] & 0xf0) == 0xe0;
378 }
379
380 always_inline uword
381 ip4_address_is_global_broadcast (const ip4_address_t * a)
382 {
383   return (a->as_u32) == 0xffffffff;
384 }
385
386 always_inline void
387 ip4_multicast_address_set_for_group (ip4_address_t * a,
388                                      ip_multicast_group_t g)
389 {
390   ASSERT ((u32) g < (1 << 28));
391   a->as_u32 = clib_host_to_net_u32 ((0xe << 28) + g);
392 }
393
394 always_inline void
395 ip4_multicast_ethernet_address (u8 * ethernet_address,
396                                 const ip4_address_t * a)
397 {
398   const u8 *d = a->as_u8;
399
400   ethernet_address[0] = 0x01;
401   ethernet_address[1] = 0x00;
402   ethernet_address[2] = 0x5e;
403   ethernet_address[3] = d[1] & 0x7f;
404   ethernet_address[4] = d[2];
405   ethernet_address[5] = d[3];
406 }
407
408 always_inline void
409 ip4_tcp_reply_x1 (ip4_header_t * ip0, tcp_header_t * tcp0)
410 {
411   u32 src0, dst0;
412
413   src0 = ip0->src_address.data_u32;
414   dst0 = ip0->dst_address.data_u32;
415   ip0->src_address.data_u32 = dst0;
416   ip0->dst_address.data_u32 = src0;
417
418   src0 = tcp0->src;
419   dst0 = tcp0->dst;
420   tcp0->src = dst0;
421   tcp0->dst = src0;
422 }
423
424 always_inline void
425 ip4_tcp_reply_x2 (ip4_header_t * ip0, ip4_header_t * ip1,
426                   tcp_header_t * tcp0, tcp_header_t * tcp1)
427 {
428   u32 src0, dst0, src1, dst1;
429
430   src0 = ip0->src_address.data_u32;
431   src1 = ip1->src_address.data_u32;
432   dst0 = ip0->dst_address.data_u32;
433   dst1 = ip1->dst_address.data_u32;
434   ip0->src_address.data_u32 = dst0;
435   ip1->src_address.data_u32 = dst1;
436   ip0->dst_address.data_u32 = src0;
437   ip1->dst_address.data_u32 = src1;
438
439   src0 = tcp0->src;
440   src1 = tcp1->src;
441   dst0 = tcp0->dst;
442   dst1 = tcp1->dst;
443   tcp0->src = dst0;
444   tcp1->src = dst1;
445   tcp0->dst = src0;
446   tcp1->dst = src1;
447 }
448
449 #endif /* included_ip4_packet_h */
450
451 /*
452  * fd.io coding-style-patch-verification: ON
453  *
454  * Local Variables:
455  * eval: (c-set-style "gnu")
456  * End:
457  */