06530926120fc145d9d1ed5af63e39b9d3a56605
[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 #include <vppinfra/warnings.h>  /* for WARN_OFF/WARN_ON macro */
47
48 /* IP4 address which can be accessed either as 4 bytes
49    or as a 32-bit number. */
50 typedef union
51 {
52   u8 data[4];
53   u32 data_u32;
54   /* Aliases. */
55   u8 as_u8[4];
56   u16 as_u16[2];
57   u32 as_u32;
58 } ip4_address_t;
59
60 typedef struct
61 {
62   /* IP address must be first for ip_interface_address_get_address() to work */
63   ip4_address_t ip4_addr;
64   u32 fib_index;
65 } ip4_address_fib_t;
66
67 always_inline void
68 ip4_addr_fib_init (ip4_address_fib_t * addr_fib,
69                    const ip4_address_t * address, u32 fib_index)
70 {
71   clib_memcpy_fast (&addr_fib->ip4_addr, address,
72                     sizeof (addr_fib->ip4_addr));
73   addr_fib->fib_index = fib_index;
74 }
75
76 /* (src,dst) pair of addresses as found in packet header. */
77 typedef struct
78 {
79   ip4_address_t src, dst;
80 } ip4_address_pair_t;
81
82 typedef struct
83 {
84   ip4_address_t addr, mask;
85 } ip4_address_and_mask_t;
86
87 typedef union
88 {
89   struct
90   {
91     /* 4 bit packet length (in 32bit units) and version VVVVLLLL.
92        e.g. for packets w/ no options ip_version_and_header_length == 0x45. */
93     u8 ip_version_and_header_length;
94
95     /* Type of service. */
96     ip_dscp_t tos;
97
98     /* Total layer 3 packet length including this header. */
99     u16 length;
100
101     /* Fragmentation ID. */
102     u16 fragment_id;
103
104     /* 3 bits of flags and 13 bits of fragment offset (in units
105        of 8 byte quantities). */
106     u16 flags_and_fragment_offset;
107 #define IP4_HEADER_FLAG_MORE_FRAGMENTS (1 << 13)
108 #define IP4_HEADER_FLAG_DONT_FRAGMENT (1 << 14)
109 #define IP4_HEADER_FLAG_CONGESTION (1 << 15)
110
111     /* Time to live decremented by router at each hop. */
112     u8 ttl;
113
114     /* Next level protocol packet. */
115     u8 protocol;
116
117     /* Checksum. */
118     u16 checksum;
119
120     /* Source and destination address. */
121     union
122     {
123       struct
124       {
125         ip4_address_t src_address, dst_address;
126       };
127       ip4_address_pair_t address_pair;
128     };
129   };
130
131   /* For checksumming we'll want to access IP header in word sized chunks. */
132   /* For 64 bit machines. */
133   /* *INDENT-OFF* */
134   CLIB_PACKED (struct {
135     u64 checksum_data_64[2];
136     u32 checksum_data_64_32[1];
137   });
138   /* *INDENT-ON* */
139
140   /* For 32 bit machines. */
141   /* *INDENT-OFF* */
142   CLIB_PACKED (struct {
143     u32 checksum_data_32[5];
144   });
145   /* *INDENT-ON* */
146 } ip4_header_t;
147
148 /* Value of ip_version_and_header_length for packets w/o options. */
149 #define IP4_VERSION_AND_HEADER_LENGTH_NO_OPTIONS \
150   ((4 << 4) | (sizeof (ip4_header_t) / sizeof (u32)))
151
152 #define IP4_ROUTER_ALERT_OPTION 20
153
154 always_inline u16
155 ip4_get_fragment_offset (const ip4_header_t * i)
156 {
157   return clib_net_to_host_u16 (i->flags_and_fragment_offset) & 0x1fff;
158 }
159
160 always_inline u16
161 ip4_get_fragment_more (const ip4_header_t * i)
162 {
163   return clib_net_to_host_u16 (i->flags_and_fragment_offset) &
164     IP4_HEADER_FLAG_MORE_FRAGMENTS;
165 }
166
167 always_inline int
168 ip4_is_fragment (const ip4_header_t * i)
169 {
170   return (i->flags_and_fragment_offset &
171           clib_net_to_host_u16 (0x1fff | IP4_HEADER_FLAG_MORE_FRAGMENTS));
172 }
173
174 always_inline int
175 ip4_is_first_fragment (const ip4_header_t * i)
176 {
177   return (i->flags_and_fragment_offset &
178           clib_net_to_host_u16 (0x1fff | IP4_HEADER_FLAG_MORE_FRAGMENTS)) ==
179     clib_net_to_host_u16 (IP4_HEADER_FLAG_MORE_FRAGMENTS);
180 }
181
182 /* Fragment offset in bytes. */
183 always_inline int
184 ip4_get_fragment_offset_bytes (const ip4_header_t * i)
185 {
186   return 8 * ip4_get_fragment_offset (i);
187 }
188
189 always_inline int
190 ip4_header_bytes (const ip4_header_t * i)
191 {
192   return sizeof (u32) * (i->ip_version_and_header_length & 0xf);
193 }
194
195 always_inline void *
196 ip4_next_header (ip4_header_t * i)
197 {
198   return (void *) i + ip4_header_bytes (i);
199 }
200
201 /* Turn off array bounds check due to ip4_header_t
202    option field operations. */
203
204 /* *INDENT-OFF* */
205 WARN_OFF(array-bounds)
206 /* *INDENT-ON* */
207
208 static_always_inline u16
209 ip4_header_checksum_inline (ip4_header_t * i, int with_checksum)
210 {
211   int option_len = (i->ip_version_and_header_length & 0xf) - 5;
212   uword sum = 0;
213 #if uword_bits == 64
214   u32 *iphdr = (u32 *) i;
215
216   sum += iphdr[0];
217   sum += iphdr[1];
218   sum += with_checksum ? iphdr[2] : *(u16 *) (iphdr + 2);
219   /* skip checksum */
220   sum += iphdr[3];
221   sum += iphdr[4];
222
223   if (PREDICT_FALSE (option_len > 0))
224     switch (option_len)
225       {
226       case 10:
227         sum += iphdr[14];
228       case 9:
229         sum += iphdr[13];
230       case 8:
231         sum += iphdr[12];
232       case 7:
233         sum += iphdr[11];
234       case 6:
235         sum += iphdr[10];
236       case 5:
237         sum += iphdr[9];
238       case 4:
239         sum += iphdr[8];
240       case 3:
241         sum += iphdr[7];
242       case 2:
243         sum += iphdr[6];
244       case 1:
245         sum += iphdr[5];
246       default:
247         break;
248       }
249
250   sum = ((u32) sum) + (sum >> 32);
251 #else
252   u16 *iphdr = (u16 *) i;
253
254   sum += iphdr[0];
255   sum += iphdr[1];
256   sum += iphdr[2];
257   sum += iphdr[3];
258   sum += iphdr[4];
259   if (with_checksum)
260     sum += iphdr[5];
261   sum += iphdr[6];
262   sum += iphdr[7];
263   sum += iphdr[8];
264   sum += iphdr[9];
265
266   if (PREDICT_FALSE (option_len > 0))
267     switch (option_len)
268       {
269       case 10:
270         sum += iphdr[28];
271         sum += iphdr[29];
272       case 9:
273         sum += iphdr[26];
274         sum += iphdr[27];
275       case 8:
276         sum += iphdr[24];
277         sum += iphdr[25];
278       case 7:
279         sum += iphdr[22];
280         sum += iphdr[23];
281       case 6:
282         sum += iphdr[20];
283         sum += iphdr[21];
284       case 5:
285         sum += iphdr[18];
286         sum += iphdr[19];
287       case 4:
288         sum += iphdr[16];
289         sum += iphdr[17];
290       case 3:
291         sum += iphdr[14];
292         sum += iphdr[15];
293       case 2:
294         sum += iphdr[12];
295         sum += iphdr[13];
296       case 1:
297         sum += iphdr[10];
298         sum += iphdr[11];
299       default:
300         break;
301       }
302 #endif
303
304   sum = ((u16) sum) + (sum >> 16);
305   sum = ((u16) sum) + (sum >> 16);
306   return ~((u16) sum);
307 }
308
309 /* *INDENT-OFF* */
310 WARN_ON(array-bounds)
311 /* *INDENT-ON* */
312
313 always_inline u16
314 ip4_header_checksum (ip4_header_t * i)
315 {
316   return ip4_header_checksum_inline (i, /* with_checksum */ 0);
317 }
318
319 always_inline void
320 ip4_header_set_dscp (ip4_header_t * ip4, ip_dscp_t dscp)
321 {
322   ip4->tos &= ~0xfc;
323   /* not masking the dscp value to save th instruction
324    * it shouldn't b necessary since the argument is an enum
325    * whose range is therefore constrained in the CP. in the
326    * DP it will have been taken from another packet, so again
327    * constrained in  value */
328   ip4->tos |= dscp << IP_PACKET_TC_FIELD_DSCP_BIT_SHIFT;
329 }
330
331 always_inline void
332 ip4_header_set_ecn (ip4_header_t * ip4, ip_ecn_t ecn)
333 {
334   ip4->tos &= ~IP_PACKET_TC_FIELD_ECN_MASK;
335   ip4->tos |= ecn;
336 }
337
338 always_inline void
339 ip4_header_set_ecn_w_chksum (ip4_header_t * ip4, ip_ecn_t ecn)
340 {
341   ip_csum_t sum = ip4->checksum;
342   u8 old = ip4->tos;
343   u8 new = (old & ~IP_PACKET_TC_FIELD_ECN_MASK) | ecn;
344
345   sum = ip_csum_update (sum, old, new, ip4_header_t, tos);
346   ip4->checksum = ip_csum_fold (sum);
347   ip4->tos = new;
348 }
349
350 always_inline ip_dscp_t
351 ip4_header_get_dscp (const ip4_header_t * ip4)
352 {
353   return (ip4->tos >> IP_PACKET_TC_FIELD_DSCP_BIT_SHIFT);
354 }
355
356 always_inline ip_ecn_t
357 ip4_header_get_ecn (const ip4_header_t * ip4)
358 {
359   return (ip4->tos & IP_PACKET_TC_FIELD_ECN_MASK);
360 }
361
362 always_inline void
363 ip4_header_set_df (ip4_header_t * ip4)
364 {
365   ip4->flags_and_fragment_offset |=
366     clib_host_to_net_u16 (IP4_HEADER_FLAG_DONT_FRAGMENT);
367 }
368
369 always_inline void
370 ip4_header_clear_df (ip4_header_t * ip4)
371 {
372   ip4->flags_and_fragment_offset &=
373     ~clib_host_to_net_u16 (IP4_HEADER_FLAG_DONT_FRAGMENT);
374 }
375
376 always_inline u8
377 ip4_header_get_df (const ip4_header_t * ip4)
378 {
379   return (! !(ip4->flags_and_fragment_offset &
380               clib_host_to_net_u16 (IP4_HEADER_FLAG_DONT_FRAGMENT)));
381 }
382
383 static inline uword
384 ip4_header_checksum_is_valid (ip4_header_t * i)
385 {
386   return ip4_header_checksum_inline (i, /* with_checksum */ 1) == 0;
387 }
388
389 #define ip4_partial_header_checksum_x1(ip0,sum0)                        \
390 do {                                                                    \
391   if (BITS (ip_csum_t) > 32)                                            \
392     {                                                                   \
393       sum0 = ip0->checksum_data_64[0];                                  \
394       sum0 = ip_csum_with_carry (sum0, ip0->checksum_data_64[1]);       \
395       sum0 = ip_csum_with_carry (sum0, ip0->checksum_data_64_32[0]);    \
396     }                                                                   \
397   else                                                                  \
398     {                                                                   \
399       sum0 = ip0->checksum_data_32[0];                                  \
400       sum0 = ip_csum_with_carry (sum0, ip0->checksum_data_32[1]);       \
401       sum0 = ip_csum_with_carry (sum0, ip0->checksum_data_32[2]);       \
402       sum0 = ip_csum_with_carry (sum0, ip0->checksum_data_32[3]);       \
403       sum0 = ip_csum_with_carry (sum0, ip0->checksum_data_32[4]);       \
404     }                                                                   \
405 } while (0)
406
407 #define ip4_partial_header_checksum_x2(ip0,ip1,sum0,sum1)               \
408 do {                                                                    \
409   if (BITS (ip_csum_t) > 32)                                            \
410     {                                                                   \
411       sum0 = ip0->checksum_data_64[0];                                  \
412       sum1 = ip1->checksum_data_64[0];                                  \
413       sum0 = ip_csum_with_carry (sum0, ip0->checksum_data_64[1]);       \
414       sum1 = ip_csum_with_carry (sum1, ip1->checksum_data_64[1]);       \
415       sum0 = ip_csum_with_carry (sum0, ip0->checksum_data_64_32[0]);    \
416       sum1 = ip_csum_with_carry (sum1, ip1->checksum_data_64_32[0]);    \
417     }                                                                   \
418   else                                                                  \
419     {                                                                   \
420       sum0 = ip0->checksum_data_32[0];                                  \
421       sum1 = ip1->checksum_data_32[0];                                  \
422       sum0 = ip_csum_with_carry (sum0, ip0->checksum_data_32[1]);       \
423       sum1 = ip_csum_with_carry (sum1, ip1->checksum_data_32[1]);       \
424       sum0 = ip_csum_with_carry (sum0, ip0->checksum_data_32[2]);       \
425       sum1 = ip_csum_with_carry (sum1, ip1->checksum_data_32[2]);       \
426       sum0 = ip_csum_with_carry (sum0, ip0->checksum_data_32[3]);       \
427       sum1 = ip_csum_with_carry (sum1, ip1->checksum_data_32[3]);       \
428       sum0 = ip_csum_with_carry (sum0, ip0->checksum_data_32[4]);       \
429       sum1 = ip_csum_with_carry (sum1, ip1->checksum_data_32[4]);       \
430     }                                                                   \
431 } while (0)
432
433 always_inline uword
434 ip4_address_is_multicast (const ip4_address_t * a)
435 {
436   return (a->data[0] & 0xf0) == 0xe0;
437 }
438
439 always_inline uword
440 ip4_address_is_global_broadcast (const ip4_address_t * a)
441 {
442   return (a->as_u32) == 0xffffffff;
443 }
444
445 always_inline void
446 ip4_multicast_address_set_for_group (ip4_address_t * a,
447                                      ip_multicast_group_t g)
448 {
449   ASSERT ((u32) g < (1 << 28));
450   a->as_u32 = clib_host_to_net_u32 ((0xe << 28) + g);
451 }
452
453 always_inline void
454 ip4_multicast_ethernet_address (u8 * ethernet_address,
455                                 const ip4_address_t * a)
456 {
457   const u8 *d = a->as_u8;
458
459   ethernet_address[0] = 0x01;
460   ethernet_address[1] = 0x00;
461   ethernet_address[2] = 0x5e;
462   ethernet_address[3] = d[1] & 0x7f;
463   ethernet_address[4] = d[2];
464   ethernet_address[5] = d[3];
465 }
466
467 always_inline void
468 ip4_tcp_reply_x1 (ip4_header_t * ip0, tcp_header_t * tcp0)
469 {
470   u32 src0, dst0;
471
472   src0 = ip0->src_address.data_u32;
473   dst0 = ip0->dst_address.data_u32;
474   ip0->src_address.data_u32 = dst0;
475   ip0->dst_address.data_u32 = src0;
476
477   src0 = tcp0->src;
478   dst0 = tcp0->dst;
479   tcp0->src = dst0;
480   tcp0->dst = src0;
481 }
482
483 always_inline void
484 ip4_tcp_reply_x2 (ip4_header_t * ip0, ip4_header_t * ip1,
485                   tcp_header_t * tcp0, tcp_header_t * tcp1)
486 {
487   u32 src0, dst0, src1, dst1;
488
489   src0 = ip0->src_address.data_u32;
490   src1 = ip1->src_address.data_u32;
491   dst0 = ip0->dst_address.data_u32;
492   dst1 = ip1->dst_address.data_u32;
493   ip0->src_address.data_u32 = dst0;
494   ip1->src_address.data_u32 = dst1;
495   ip0->dst_address.data_u32 = src0;
496   ip1->dst_address.data_u32 = src1;
497
498   src0 = tcp0->src;
499   src1 = tcp1->src;
500   dst0 = tcp0->dst;
501   dst1 = tcp1->dst;
502   tcp0->src = dst0;
503   tcp1->src = dst1;
504   tcp0->dst = src0;
505   tcp1->dst = src1;
506 }
507
508 #endif /* included_ip4_packet_h */
509
510 /*
511  * fd.io coding-style-patch-verification: ON
512  *
513  * Local Variables:
514  * eval: (c-set-style "gnu")
515  * End:
516  */