Initial commit of vpp code.
[vpp.git] / vnet / vnet / ip / ip4_pg.c
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  * ip/ip4_pg: IP v4 packet-generator interface
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 #include <vnet/ip/ip.h>
41 #include <vnet/pg/pg.h>
42
43 #define IP4_PG_EDIT_CHECKSUM (1 << 0)
44 #define IP4_PG_EDIT_LENGTH (1 << 1)
45
46 static_always_inline void
47 compute_length_and_or_checksum (vlib_main_t * vm,
48                                 u32 * packets,
49                                 u32 n_packets,
50                                 u32 ip_header_offset,
51                                 u32 flags)
52 {
53   ASSERT (flags != 0);
54
55   while (n_packets >= 2)
56     {
57       u32 pi0, pi1;
58       vlib_buffer_t * p0, * p1;
59       ip4_header_t * ip0, * ip1;
60       ip_csum_t sum0, sum1;
61
62       pi0 = packets[0];
63       pi1 = packets[1];
64       p0 = vlib_get_buffer (vm, pi0);
65       p1 = vlib_get_buffer (vm, pi1);
66       n_packets -= 2;
67       packets += 2;
68
69       ip0 = (void *) (p0->data + ip_header_offset);
70       ip1 = (void *) (p1->data + ip_header_offset);
71
72       if (flags & IP4_PG_EDIT_LENGTH)
73         {
74           ip0->length = clib_host_to_net_u16 (vlib_buffer_length_in_chain (vm, p0) - ip_header_offset);
75           ip1->length = clib_host_to_net_u16 (vlib_buffer_length_in_chain (vm, p1) - ip_header_offset);
76         }
77
78       if (flags & IP4_PG_EDIT_CHECKSUM)
79         {
80           ASSERT (ip4_header_bytes (ip0) == sizeof (ip0[0]));
81           ASSERT (ip4_header_bytes (ip1) == sizeof (ip1[0]));
82
83           ip0->checksum = 0;
84           ip1->checksum = 0;
85
86           ip4_partial_header_checksum_x2 (ip0, ip1, sum0, sum1);
87           ip0->checksum = ~ ip_csum_fold (sum0);
88           ip1->checksum = ~ ip_csum_fold (sum1);
89
90           ASSERT (ip0->checksum == ip4_header_checksum (ip0));
91           ASSERT (ip1->checksum == ip4_header_checksum (ip1));
92         }
93     }
94
95   while (n_packets >= 1)
96     {
97       u32 pi0;
98       vlib_buffer_t * p0;
99       ip4_header_t * ip0;
100       ip_csum_t sum0;
101
102       pi0 = packets[0];
103       p0 = vlib_get_buffer (vm, pi0);
104       n_packets -= 1;
105       packets += 1;
106
107       ip0 = (void *) (p0->data + ip_header_offset);
108
109       if (flags & IP4_PG_EDIT_LENGTH)
110         ip0->length = clib_host_to_net_u16 (vlib_buffer_length_in_chain (vm, p0) - ip_header_offset);
111
112       if (flags & IP4_PG_EDIT_CHECKSUM)
113         {
114           ASSERT (ip4_header_bytes (ip0) == sizeof (ip0[0]));
115
116           ip0->checksum = 0;
117
118           ip4_partial_header_checksum_x1 (ip0, sum0);
119           ip0->checksum = ~ ip_csum_fold (sum0);
120
121           ASSERT (ip0->checksum == ip4_header_checksum (ip0));
122         }
123     }
124 }
125
126 static void
127 ip4_pg_edit_function (pg_main_t * pg,
128                       pg_stream_t * s,
129                       pg_edit_group_t * g,
130                       u32 * packets,
131                       u32 n_packets)
132 {
133   vlib_main_t * vm = pg->vlib_main;
134   u32 ip_offset;
135
136   ip_offset = g->start_byte_offset;
137
138   switch (g->edit_function_opaque)
139     {
140     case IP4_PG_EDIT_LENGTH:
141       compute_length_and_or_checksum (vm, packets, n_packets, ip_offset,
142                                       IP4_PG_EDIT_LENGTH);
143       break;
144
145     case IP4_PG_EDIT_CHECKSUM:
146       compute_length_and_or_checksum (vm, packets, n_packets, ip_offset,
147                                       IP4_PG_EDIT_CHECKSUM);
148       break;
149
150     case IP4_PG_EDIT_LENGTH | IP4_PG_EDIT_CHECKSUM:
151       compute_length_and_or_checksum (vm, packets, n_packets, ip_offset,
152                                       IP4_PG_EDIT_LENGTH
153                                       | IP4_PG_EDIT_CHECKSUM);
154       break;
155
156     default:
157       ASSERT (0);
158       break;
159     }
160 }
161
162 typedef struct {
163   pg_edit_t ip_version, header_length;
164   pg_edit_t tos;
165   pg_edit_t length;
166
167   pg_edit_t fragment_id, fragment_offset;
168
169   /* Flags together with fragment offset. */
170   pg_edit_t mf_flag, df_flag, ce_flag;
171
172   pg_edit_t ttl;
173
174   pg_edit_t protocol;
175
176   pg_edit_t checksum;
177
178   pg_edit_t src_address, dst_address;
179 } pg_ip4_header_t;
180
181 static inline void
182 pg_ip4_header_init (pg_ip4_header_t * p)
183 {
184   /* Initialize fields that are not bit fields in the IP header. */
185 #define _(f) pg_edit_init (&p->f, ip4_header_t, f);
186   _ (tos);
187   _ (length);
188   _ (fragment_id);
189   _ (ttl);
190   _ (protocol);
191   _ (checksum);
192   _ (src_address);
193   _ (dst_address);
194 #undef _
195
196   /* Initialize bit fields. */
197   pg_edit_init_bitfield (&p->header_length, ip4_header_t,
198                          ip_version_and_header_length,
199                          0, 4);
200   pg_edit_init_bitfield (&p->ip_version, ip4_header_t,
201                          ip_version_and_header_length,
202                          4, 4);
203
204   pg_edit_init_bitfield (&p->fragment_offset, ip4_header_t,
205                          flags_and_fragment_offset,
206                          0, 13);
207   pg_edit_init_bitfield (&p->mf_flag, ip4_header_t,
208                          flags_and_fragment_offset,
209                          13, 1);
210   pg_edit_init_bitfield (&p->df_flag, ip4_header_t,
211                          flags_and_fragment_offset,
212                          14, 1);
213   pg_edit_init_bitfield (&p->ce_flag, ip4_header_t,
214                          flags_and_fragment_offset,
215                          15, 1);
216 }
217
218 uword
219 unformat_pg_ip4_header (unformat_input_t * input, va_list * args)
220 {
221   pg_stream_t * s = va_arg (*args, pg_stream_t *);
222   pg_ip4_header_t * p;
223   u32 group_index;
224   
225   p = pg_create_edit_group (s, sizeof (p[0]), sizeof (ip4_header_t),
226                             &group_index);
227   pg_ip4_header_init (p);
228
229   /* Defaults. */
230   pg_edit_set_fixed (&p->ip_version, 4);
231   pg_edit_set_fixed (&p->header_length,
232                      sizeof (ip4_header_t) / sizeof (u32));
233
234   pg_edit_set_fixed (&p->tos, 0);
235   pg_edit_set_fixed (&p->ttl, 64);
236
237   pg_edit_set_fixed (&p->fragment_id, 0);
238   pg_edit_set_fixed (&p->fragment_offset, 0);
239   pg_edit_set_fixed (&p->mf_flag, 0);
240   pg_edit_set_fixed (&p->df_flag, 0);
241   pg_edit_set_fixed (&p->ce_flag, 0);
242
243   p->length.type = PG_EDIT_UNSPECIFIED;
244   p->checksum.type = PG_EDIT_UNSPECIFIED;
245
246   if (unformat (input, "%U: %U -> %U",
247                 unformat_pg_edit,
248                 unformat_ip_protocol, &p->protocol,
249                 unformat_pg_edit,
250                 unformat_ip4_address, &p->src_address,
251                 unformat_pg_edit,
252                 unformat_ip4_address, &p->dst_address))
253       goto found;
254
255   if (! unformat (input, "%U:",
256                   unformat_pg_edit,
257                   unformat_ip_protocol, &p->protocol))
258       goto error;
259
260 found:
261   /* Parse options. */
262   while (1)
263     {
264       if (unformat (input, "version %U",
265                     unformat_pg_edit,
266                     unformat_pg_number, &p->ip_version))
267         ;
268
269       else if (unformat (input, "header-length %U",
270                          unformat_pg_edit,
271                          unformat_pg_number, &p->header_length))
272         ;
273
274       else if (unformat (input, "tos %U",
275                          unformat_pg_edit,
276                          unformat_pg_number, &p->tos))
277         ;
278
279       else if (unformat (input, "length %U",
280                          unformat_pg_edit,
281                          unformat_pg_number, &p->length))
282         ;
283
284       else if (unformat (input, "checksum %U",
285                          unformat_pg_edit,
286                          unformat_pg_number, &p->checksum))
287         ;
288
289       else if (unformat (input, "ttl %U",
290                          unformat_pg_edit,
291                          unformat_pg_number, &p->ttl))
292         ;
293
294       else if (unformat (input, "fragment id %U offset %U",
295                          unformat_pg_edit,
296                          unformat_pg_number, &p->fragment_id,
297                          unformat_pg_edit,
298                          unformat_pg_number, &p->fragment_offset))
299         {
300           int i;
301           for (i = 0; i< ARRAY_LEN (p->fragment_offset.values); i++)
302             pg_edit_set_value (&p->fragment_offset, i,
303                                pg_edit_get_value (&p->fragment_offset, i) / 8);
304         
305         }
306
307       /* Flags. */
308       else if (unformat (input, "mf") || unformat (input, "MF"))
309         pg_edit_set_fixed (&p->mf_flag, 1);
310
311       else if (unformat (input, "df") || unformat (input, "DF"))
312         pg_edit_set_fixed (&p->df_flag, 1);
313
314       else if (unformat (input, "ce") || unformat (input, "CE"))
315         pg_edit_set_fixed (&p->ce_flag, 1);
316         
317       /* Can't parse input: try next protocol level. */
318       else
319         break;
320     }
321
322   {
323     ip_main_t * im = &ip_main;
324     ip_protocol_t protocol;
325     ip_protocol_info_t * pi;
326
327     pi = 0;
328     if (p->protocol.type == PG_EDIT_FIXED)
329       {
330         protocol = pg_edit_get_value (&p->protocol, PG_EDIT_LO);
331         pi = ip_get_protocol_info (im, protocol);
332       }
333
334     if (pi && pi->unformat_pg_edit
335         && unformat_user (input, pi->unformat_pg_edit, s))
336       ;
337
338     else if (! unformat_user (input, unformat_pg_payload, s))
339       goto error;
340
341     if (p->length.type == PG_EDIT_UNSPECIFIED
342         && s->min_packet_bytes == s->max_packet_bytes
343         && group_index + 1 < vec_len (s->edit_groups))
344       {
345         pg_edit_set_fixed (&p->length,
346                            pg_edit_group_n_bytes (s, group_index));
347       }
348
349     /* Compute IP header checksum if all edits are fixed. */
350     if (p->checksum.type == PG_EDIT_UNSPECIFIED)
351       {
352         ip4_header_t fixed_header, fixed_mask, cmp_mask;
353         
354         /* See if header is all fixed and specified except for
355            checksum field. */
356         memset (&cmp_mask, ~0, sizeof (cmp_mask));
357         cmp_mask.checksum = 0;
358
359         pg_edit_group_get_fixed_packet_data (s, group_index,
360                                              &fixed_header, &fixed_mask);
361         if (! memcmp (&fixed_mask, &cmp_mask, sizeof (cmp_mask)))
362           pg_edit_set_fixed (&p->checksum,
363                              clib_net_to_host_u16 (ip4_header_checksum (&fixed_header)));
364       }
365
366     p = pg_get_edit_group (s, group_index);
367     if (p->length.type == PG_EDIT_UNSPECIFIED
368         || p->checksum.type == PG_EDIT_UNSPECIFIED)
369       {
370         pg_edit_group_t * g = pg_stream_get_group (s, group_index);
371         g->edit_function = ip4_pg_edit_function;
372         g->edit_function_opaque = 0;
373         if (p->length.type == PG_EDIT_UNSPECIFIED)
374           g->edit_function_opaque |= IP4_PG_EDIT_LENGTH;
375         if (p->checksum.type == PG_EDIT_UNSPECIFIED)
376           g->edit_function_opaque |= IP4_PG_EDIT_CHECKSUM;
377       }
378
379     return 1;
380   }
381
382  error:
383   /* Free up any edits we may have added. */
384   pg_free_edit_group (s);
385   return 0;
386 }
387