4ee8f15df1fa4f94d44159ee24011d1d86dc10df
[vpp.git] / vnet / vnet / ip / icmp4.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/icmp4.c: ipv4 icmp
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 <vlib/vlib.h>
41 #include <vnet/ip/ip.h>
42 #include <vnet/pg/pg.h>
43
44 static u8 * format_ip4_icmp_type_and_code (u8 * s, va_list * args)
45 {
46   icmp4_type_t type = va_arg (*args, int);
47   u8 code = va_arg (*args, int);
48   char * t = 0;
49
50 #define _(n,f) case n: t = #f; break;
51
52   switch (type)
53     {
54       foreach_icmp4_type;
55
56     default:
57       break;
58     }
59
60 #undef _
61
62   if (! t)
63     return format (s, "unknown 0x%x", type);
64
65   s = format (s, "%s", t);
66
67   t = 0;
68   switch ((type << 8) | code)
69     {
70 #define _(a,n,f) case (ICMP4_##a << 8) | (n): t = #f; break;
71
72       foreach_icmp4_code;
73
74 #undef _
75     }
76
77   if (t)
78     s = format (s, " %s", t);
79
80   return s;
81 }
82
83 static u8 * format_ip4_icmp_header (u8 * s, va_list * args)
84 {
85   icmp46_header_t * icmp = va_arg (*args, icmp46_header_t *);
86   u32 max_header_bytes = va_arg (*args, u32);
87
88   /* Nothing to do. */
89   if (max_header_bytes < sizeof (icmp[0]))
90     return format (s, "ICMP header truncated");
91
92   s = format (s, "ICMP %U checksum 0x%x",
93               format_ip4_icmp_type_and_code, icmp->type, icmp->code,
94               clib_net_to_host_u16 (icmp->checksum));
95
96   return s;
97 }
98
99 typedef struct {
100   u8 packet_data[64];
101 } icmp_input_trace_t;
102
103 static u8 * format_icmp_input_trace (u8 * s, va_list * va)
104 {
105   CLIB_UNUSED (vlib_main_t * vm) = va_arg (*va, vlib_main_t *);
106   CLIB_UNUSED (vlib_node_t * node) = va_arg (*va, vlib_node_t *);
107   icmp_input_trace_t * t = va_arg (*va, icmp_input_trace_t *);
108
109   s = format (s, "%U",
110               format_ip4_header,
111               t->packet_data, sizeof (t->packet_data));
112
113   return s;
114 }
115
116 typedef enum {
117   ICMP4_ERROR_UNKNOWN_TYPE,
118   ICMP4_ERROR_ECHO_REPLIES_SENT,
119   ICMP4_ERROR_TTL_EXPIRE_RESP_SENT,
120   ICMP4_ERROR_TTL_EXPIRE_RESP_DROP,
121 } icmp_error_t;
122
123 static char * icmp_error_strings[] = {
124   [ICMP4_ERROR_UNKNOWN_TYPE] = "unknown type",
125   [ICMP4_ERROR_ECHO_REPLIES_SENT] = "echo replies sent",
126   [ICMP4_ERROR_TTL_EXPIRE_RESP_SENT] = "TTL time exceeded response sent",
127   [ICMP4_ERROR_TTL_EXPIRE_RESP_DROP] = "TTL time exceeded response dropped",
128 };
129
130 typedef enum {
131   ICMP_INPUT_NEXT_ERROR,
132   ICMP_INPUT_N_NEXT,
133 } icmp_input_next_t;
134
135 typedef struct {
136   uword * type_and_code_by_name;
137
138   uword * type_by_name;
139
140   /* Vector dispatch table indexed by [icmp type]. */
141   u8 ip4_input_next_index_by_type[256];
142 } icmp4_main_t;
143
144 icmp4_main_t icmp4_main;
145
146 static uword
147 ip4_icmp_input (vlib_main_t * vm,
148                 vlib_node_runtime_t * node,
149                 vlib_frame_t * frame)
150 {
151   icmp4_main_t * im = &icmp4_main;
152   uword n_packets = frame->n_vectors;
153   u32 * from, * to_next;
154   u32 n_left_from, n_left_to_next, next;
155
156   from = vlib_frame_vector_args (frame);
157   n_left_from = n_packets;
158   next = node->cached_next_index;
159   
160   if (node->flags & VLIB_NODE_FLAG_TRACE)
161     vlib_trace_frame_buffers_only (vm, node, from, frame->n_vectors,
162                                    /* stride */ 1,
163                                    sizeof (icmp_input_trace_t));
164
165   while (n_left_from > 0)
166     {
167       vlib_get_next_frame (vm, node, next, to_next, n_left_to_next);
168
169       while (n_left_from > 0 && n_left_to_next > 0)
170         {
171           vlib_buffer_t * p0;
172           ip4_header_t * ip0;
173           icmp46_header_t * icmp0;
174           icmp4_type_t type0;
175           u32 bi0, next0;
176       
177           if (PREDICT_TRUE (n_left_from > 2))
178             {
179               vlib_prefetch_buffer_with_index (vm, from[2], LOAD);
180               p0 = vlib_get_buffer (vm, from[1]);
181               ip0 = vlib_buffer_get_current (p0);
182               CLIB_PREFETCH(ip0, CLIB_CACHE_LINE_BYTES, LOAD);
183             }
184     
185           bi0 = to_next[0] = from[0];
186
187           from += 1;
188           n_left_from -= 1;
189           to_next += 1;
190           n_left_to_next -= 1;
191       
192           p0 = vlib_get_buffer (vm, bi0);
193           ip0 = vlib_buffer_get_current (p0);
194           icmp0 = ip4_next_header (ip0);
195           type0 = icmp0->type;
196           next0 = im->ip4_input_next_index_by_type[type0];
197
198           p0->error = node->errors[ICMP4_ERROR_UNKNOWN_TYPE];
199           if (PREDICT_FALSE (next0 != next))
200             {
201               vlib_put_next_frame (vm, node, next, n_left_to_next + 1);
202               next = next0;
203               vlib_get_next_frame (vm, node, next, to_next, n_left_to_next);
204               to_next[0] = bi0;
205               to_next += 1;
206               n_left_to_next -= 1;
207             }
208         }
209   
210       vlib_put_next_frame (vm, node, next, n_left_to_next);
211     }
212
213   return frame->n_vectors;
214 }
215
216 VLIB_REGISTER_NODE (ip4_icmp_input_node,static) = {
217   .function = ip4_icmp_input,
218   .name = "ip4-icmp-input",
219
220   .vector_size = sizeof (u32),
221
222   .format_trace = format_icmp_input_trace,
223
224   .n_errors = ARRAY_LEN (icmp_error_strings),
225   .error_strings = icmp_error_strings,
226
227   .n_next_nodes = 1,
228   .next_nodes = {
229     [ICMP_INPUT_NEXT_ERROR] = "error-punt",
230   },
231 };
232
233 static uword
234 ip4_icmp_echo_request (vlib_main_t * vm,
235                        vlib_node_runtime_t * node,
236                        vlib_frame_t * frame)
237 {
238   uword n_packets = frame->n_vectors;
239   u32 * from, * to_next;
240   u32 n_left_from, n_left_to_next, next;
241   ip4_main_t * i4m = &ip4_main;
242   u16 * fragment_ids, * fid;
243   u8 host_config_ttl = i4m->host_config.ttl;
244
245   from = vlib_frame_vector_args (frame);
246   n_left_from = n_packets;
247   next = node->cached_next_index;
248   
249   if (node->flags & VLIB_NODE_FLAG_TRACE)
250     vlib_trace_frame_buffers_only (vm, node, from, frame->n_vectors,
251                                    /* stride */ 1,
252                                    sizeof (icmp_input_trace_t));
253
254   /* Get random fragment IDs for replies. */
255   fid = fragment_ids = clib_random_buffer_get_data (&vm->random_buffer,
256                                                     n_packets * sizeof (fragment_ids[0]));
257
258   while (n_left_from > 0)
259     {
260       vlib_get_next_frame (vm, node, next, to_next, n_left_to_next);
261
262       while (n_left_from > 2 && n_left_to_next > 2)
263         {
264           vlib_buffer_t * p0, * p1;
265           ip4_header_t * ip0, * ip1;
266           icmp46_header_t * icmp0, * icmp1;
267           u32 bi0, src0, dst0;
268           u32 bi1, src1, dst1;
269           ip_csum_t sum0, sum1;
270       
271           bi0 = to_next[0] = from[0];
272           bi1 = to_next[1] = from[1];
273
274           from += 2;
275           n_left_from -= 2;
276           to_next += 2;
277           n_left_to_next -= 2;
278       
279           p0 = vlib_get_buffer (vm, bi0);
280           p1 = vlib_get_buffer (vm, bi1);
281           ip0 = vlib_buffer_get_current (p0);
282           ip1 = vlib_buffer_get_current (p1);
283           icmp0 = ip4_next_header (ip0);
284           icmp1 = ip4_next_header (ip1);
285
286           vnet_buffer (p0)->sw_if_index[VLIB_RX] = vnet_main.local_interface_sw_if_index;
287           vnet_buffer (p1)->sw_if_index[VLIB_RX] = vnet_main.local_interface_sw_if_index;
288
289           /* Update ICMP checksum. */
290           sum0 = icmp0->checksum;
291           sum1 = icmp1->checksum;
292
293           ASSERT (icmp0->type == ICMP4_echo_request);
294           ASSERT (icmp1->type == ICMP4_echo_request);
295           sum0 = ip_csum_update (sum0, ICMP4_echo_request, ICMP4_echo_reply,
296                                  icmp46_header_t, type);
297           sum1 = ip_csum_update (sum1, ICMP4_echo_request, ICMP4_echo_reply,
298                                  icmp46_header_t, type);
299           icmp0->type = ICMP4_echo_reply;
300           icmp1->type = ICMP4_echo_reply;
301
302           icmp0->checksum = ip_csum_fold (sum0);
303           icmp1->checksum = ip_csum_fold (sum1);
304
305           src0 = ip0->src_address.data_u32;
306           src1 = ip1->src_address.data_u32;
307           dst0 = ip0->dst_address.data_u32;
308           dst1 = ip1->dst_address.data_u32;
309
310           /* Swap source and destination address.
311              Does not change checksum. */
312           ip0->src_address.data_u32 = dst0;
313           ip1->src_address.data_u32 = dst1;
314           ip0->dst_address.data_u32 = src0;
315           ip1->dst_address.data_u32 = src1;
316
317           /* Update IP checksum. */
318           sum0 = ip0->checksum;
319           sum1 = ip1->checksum;
320
321           sum0 = ip_csum_update (sum0, ip0->ttl, host_config_ttl,
322                                  ip4_header_t, ttl);
323           sum1 = ip_csum_update (sum1, ip1->ttl, host_config_ttl,
324                                  ip4_header_t, ttl);
325           ip0->ttl = host_config_ttl;
326           ip1->ttl = host_config_ttl;
327
328           /* New fragment id. */
329           sum0 = ip_csum_update (sum0, ip0->fragment_id, fid[0],
330                                  ip4_header_t, fragment_id);
331           sum1 = ip_csum_update (sum1, ip1->fragment_id, fid[1],
332                                  ip4_header_t, fragment_id);
333           ip0->fragment_id = fid[0];
334           ip1->fragment_id = fid[1];
335           fid += 2;
336
337           ip0->checksum = ip_csum_fold (sum0);
338           ip1->checksum = ip_csum_fold (sum1);
339
340           ASSERT (ip0->checksum == ip4_header_checksum (ip0));
341           ASSERT (ip1->checksum == ip4_header_checksum (ip1));
342         }
343   
344       while (n_left_from > 0 && n_left_to_next > 0)
345         {
346           vlib_buffer_t * p0;
347           ip4_header_t * ip0;
348           icmp46_header_t * icmp0;
349           u32 bi0, src0, dst0;
350           ip_csum_t sum0;
351       
352           bi0 = to_next[0] = from[0];
353
354           from += 1;
355           n_left_from -= 1;
356           to_next += 1;
357           n_left_to_next -= 1;
358       
359           p0 = vlib_get_buffer (vm, bi0);
360           ip0 = vlib_buffer_get_current (p0);
361           icmp0 = ip4_next_header (ip0);
362
363           vnet_buffer (p0)->sw_if_index[VLIB_RX] = vnet_main.local_interface_sw_if_index;
364
365           /* Update ICMP checksum. */
366           sum0 = icmp0->checksum;
367
368           ASSERT (icmp0->type == ICMP4_echo_request);
369           sum0 = ip_csum_update (sum0, ICMP4_echo_request, ICMP4_echo_reply,
370                                  icmp46_header_t, type);
371           icmp0->type = ICMP4_echo_reply;
372           icmp0->checksum = ip_csum_fold (sum0);
373
374           src0 = ip0->src_address.data_u32;
375           dst0 = ip0->dst_address.data_u32;
376           ip0->src_address.data_u32 = dst0;
377           ip0->dst_address.data_u32 = src0;
378
379           /* Update IP checksum. */
380           sum0 = ip0->checksum;
381
382           sum0 = ip_csum_update (sum0, ip0->ttl, host_config_ttl,
383                                  ip4_header_t, ttl);
384           ip0->ttl = host_config_ttl;
385
386           sum0 = ip_csum_update (sum0, ip0->fragment_id, fid[0],
387                                  ip4_header_t, fragment_id);
388           ip0->fragment_id = fid[0];
389           fid += 1;
390
391           ip0->checksum = ip_csum_fold (sum0);
392
393           ASSERT (ip0->checksum == ip4_header_checksum (ip0));
394         }
395   
396       vlib_put_next_frame (vm, node, next, n_left_to_next);
397     }
398
399   vlib_error_count (vm, ip4_icmp_input_node.index,
400                     ICMP4_ERROR_ECHO_REPLIES_SENT,
401                     frame->n_vectors);
402
403   return frame->n_vectors;
404 }
405
406 VLIB_REGISTER_NODE (ip4_icmp_echo_request_node,static) = {
407   .function = ip4_icmp_echo_request,
408   .name = "ip4-icmp-echo-request",
409
410   .vector_size = sizeof (u32),
411
412   .format_trace = format_icmp_input_trace,
413
414   .n_next_nodes = 1,
415   .next_nodes = {
416     [0] = "ip4-rewrite-local",
417   },
418 };
419
420 typedef enum {
421   ICMP4_TTL_EXPIRE_NEXT_DROP,
422   ICMP4_TTL_EXPIRE_NEXT_LOOKUP,
423   ICMP4_TTL_EXPIRE_N_NEXT,
424 } icmp_ttl_expire_next_t;
425
426 static uword
427 ip4_icmp_ttl_expire (vlib_main_t * vm,
428                      vlib_node_runtime_t * node,
429                      vlib_frame_t * frame)
430 {
431   u32 * from, * to_next;
432   uword n_left_from, n_left_to_next;
433   icmp_ttl_expire_next_t next_index;
434   ip4_main_t *im = &ip4_main;
435   ip_lookup_main_t * lm = &im->lookup_main;
436
437   from = vlib_frame_vector_args(frame);
438   n_left_from = frame->n_vectors;
439   next_index = node->cached_next_index;
440
441   if (node->flags & VLIB_NODE_FLAG_TRACE)
442     vlib_trace_frame_buffers_only (vm, node, from, frame->n_vectors,
443                                    /* stride */ 1, sizeof (icmp_input_trace_t));
444
445   while (n_left_from > 0)
446     {
447       vlib_get_next_frame(vm, node, next_index, to_next, n_left_to_next);
448
449       while (n_left_from > 0 && n_left_to_next > 0)
450         {
451           u32 pi0 = from[0];
452           u32 next0 = ICMP4_TTL_EXPIRE_NEXT_LOOKUP;
453           u8 error0 = ICMP4_ERROR_TTL_EXPIRE_RESP_SENT;
454           u32 len0, new_len0;
455           vlib_buffer_t * p0;
456           ip4_header_t * ip0, * out_ip0;
457           icmp46_header_t * icmp0;
458           ip_csum_t sum;
459           u32 sw_if_index0, if_add_index0; 
460
461           /* Speculatively enqueue p0 to the current next frame */
462           to_next[0] = pi0;
463           from += 1;
464           to_next += 1;
465           n_left_from -= 1;
466           n_left_to_next -= 1;
467
468           p0 = vlib_get_buffer(vm, pi0);
469           ip0 = vlib_buffer_get_current(p0);
470           len0 = vlib_buffer_length_in_chain (vm, p0);
471           sw_if_index0 = vnet_buffer(p0)->sw_if_index[VLIB_RX];
472
473           /* Cut payload to just IP header plus first 8 bytes */
474           new_len0 = (ip0->ip_version_and_header_length &0xf)*4 + 8;
475           if (len0 > new_len0)
476             {
477               p0->current_length = new_len0; /* should fit in 1st buffer */
478               if (PREDICT_FALSE(p0->total_length_not_including_first_buffer))
479                 { /* clear current_length of all other buffers in chain */
480                   vlib_buffer_t *b = p0;
481                   p0->total_length_not_including_first_buffer = 0;
482                   while (b->flags & VLIB_BUFFER_NEXT_PRESENT)
483                     {
484                       b = vlib_get_buffer (vm, b->next_buffer);
485                       b->current_length = 0;
486                     }                  
487                 }
488             }
489
490           /* Add IP header and ICMP header including a 4 byte unused field */
491           vlib_buffer_advance(p0, 
492                               -sizeof(ip4_header_t)-sizeof(icmp46_header_t)-4);
493           out_ip0 = vlib_buffer_get_current(p0);
494           icmp0 = (icmp46_header_t *) &out_ip0[1];
495
496           /* Fill ip header fields */
497           out_ip0->ip_version_and_header_length = 0x45;
498           out_ip0->tos = 0;
499           out_ip0->length = clib_host_to_net_u16(p0->current_length);
500           out_ip0->fragment_id = 0;
501           out_ip0->ttl = 0xff;
502           out_ip0->protocol = IP_PROTOCOL_ICMP;
503           out_ip0->dst_address = ip0->src_address;
504           if_add_index0 = 
505               lm->if_address_pool_index_by_sw_if_index[sw_if_index0];
506           if (PREDICT_TRUE(if_add_index0 != ~0)) 
507             {
508               ip_interface_address_t *if_add = 
509                   pool_elt_at_index(lm->if_address_pool, if_add_index0);
510               ip4_address_t *if_ip = 
511                   ip_interface_address_get_address(lm, if_add);
512               out_ip0->src_address = *if_ip;
513               vlib_error_count (vm, node->node_index, error0, 1);
514             } 
515           else   /* interface has no IP4 address - should not happen */
516             {
517               next0 = ICMP4_TTL_EXPIRE_NEXT_DROP;
518               error0 = ICMP4_ERROR_TTL_EXPIRE_RESP_DROP;
519             }
520           out_ip0->checksum = ip4_header_checksum(out_ip0);
521
522           /* Fill icmp header fields */
523           icmp0->type = ICMP4_time_exceeded;
524           icmp0->code = ICMP4_time_exceeded_ttl_exceeded_in_transit;
525           icmp0->checksum = 0;
526           sum = ip_incremental_checksum(
527               0, icmp0, p0->current_length - sizeof(ip4_header_t));
528           icmp0->checksum = ~ip_csum_fold(sum);
529
530           /* Update error status */
531           p0->error = node->errors[error0];
532
533           /* Verify speculative enqueue, maybe switch current next frame */
534           vlib_validate_buffer_enqueue_x1(vm, node, next_index,
535                                           to_next, n_left_to_next,
536                                           pi0, next0);
537         }
538       vlib_put_next_frame(vm, node, next_index, n_left_to_next);
539     }
540
541   return frame->n_vectors;
542 }
543
544 VLIB_REGISTER_NODE (ip4_icmp_ttl_expire_node) = {
545   .function = ip4_icmp_ttl_expire,
546   .name = "ip4-icmp-ttl-expire",
547   .vector_size = sizeof (u32),
548
549   .n_errors = ARRAY_LEN (icmp_error_strings),
550   .error_strings = icmp_error_strings,
551
552   .n_next_nodes = ICMP4_TTL_EXPIRE_N_NEXT,
553   .next_nodes = {
554     [ICMP4_TTL_EXPIRE_NEXT_DROP] = "error-drop",
555     [ICMP4_TTL_EXPIRE_NEXT_LOOKUP] = "ip4-lookup",
556   },
557
558   .format_trace = format_icmp_input_trace,
559 };
560
561
562 static uword unformat_icmp_type_and_code (unformat_input_t * input, va_list * args)
563 {
564   icmp46_header_t * h = va_arg (*args, icmp46_header_t *);
565   icmp4_main_t * cm = &icmp4_main;
566   u32 i;
567
568   if (unformat_user (input, unformat_vlib_number_by_name,
569                      cm->type_and_code_by_name, &i))
570     {
571       h->type = (i >> 8) & 0xff;
572       h->code = (i >> 0) & 0xff;
573     }
574   else if (unformat_user (input, unformat_vlib_number_by_name,
575                           cm->type_by_name, &i))
576     {
577       h->type = i;
578       h->code = 0;
579     }
580   else
581     return 0;
582
583   return 1;
584 }
585
586 static void
587 icmp4_pg_edit_function (pg_main_t * pg,
588                         pg_stream_t * s,
589                         pg_edit_group_t * g,
590                         u32 * packets,
591                         u32 n_packets)
592 {
593   vlib_main_t * vm = pg->vlib_main;
594   u32 ip_offset, icmp_offset;
595
596   icmp_offset = g->start_byte_offset;
597   ip_offset = (g-1)->start_byte_offset;
598
599   while (n_packets >= 1)
600     {
601       vlib_buffer_t * p0;
602       ip4_header_t * ip0;
603       icmp46_header_t * icmp0;
604       u32 len0;
605
606       p0 = vlib_get_buffer (vm, packets[0]);
607       n_packets -= 1;
608       packets += 1;
609
610       ASSERT (p0->current_data == 0);
611       ip0 = (void *) (p0->data + ip_offset);
612       icmp0 = (void *) (p0->data + icmp_offset);
613       len0 = clib_net_to_host_u16 (ip0->length) - ip4_header_bytes (ip0);
614       icmp0->checksum = ~ ip_csum_fold (ip_incremental_checksum (0, icmp0, len0));
615     }
616 }
617
618 typedef struct {
619   pg_edit_t type, code;
620   pg_edit_t checksum;
621 } pg_icmp46_header_t;
622
623 always_inline void
624 pg_icmp_header_init (pg_icmp46_header_t * p)
625 {
626   /* Initialize fields that are not bit fields in the IP header. */
627 #define _(f) pg_edit_init (&p->f, icmp46_header_t, f);
628   _ (type);
629   _ (code);
630   _ (checksum);
631 #undef _
632 }
633
634 static uword
635 unformat_pg_icmp_header (unformat_input_t * input, va_list * args)
636 {
637   pg_stream_t * s = va_arg (*args, pg_stream_t *);
638   pg_icmp46_header_t * p;
639   u32 group_index;
640   
641   p = pg_create_edit_group (s, sizeof (p[0]), sizeof (icmp46_header_t),
642                             &group_index);
643   pg_icmp_header_init (p);
644
645   p->checksum.type = PG_EDIT_UNSPECIFIED;
646
647   {
648     icmp46_header_t tmp;
649
650     if (! unformat (input, "ICMP %U", unformat_icmp_type_and_code, &tmp))
651       goto error;
652
653     pg_edit_set_fixed (&p->type, tmp.type);
654     pg_edit_set_fixed (&p->code, tmp.code);
655   }
656
657   /* Parse options. */
658   while (1)
659     {
660       if (unformat (input, "checksum %U",
661                     unformat_pg_edit,
662                     unformat_pg_number, &p->checksum))
663         ;
664
665       /* Can't parse input: try next protocol level. */
666       else
667         break;
668     }
669
670   if (! unformat_user (input, unformat_pg_payload, s))
671     goto error;
672
673   if (p->checksum.type == PG_EDIT_UNSPECIFIED)
674     {
675       pg_edit_group_t * g = pg_stream_get_group (s, group_index);
676       g->edit_function = icmp4_pg_edit_function;
677       g->edit_function_opaque = 0;
678     }
679
680   return 1;
681
682  error:
683   /* Free up any edits we may have added. */
684   pg_free_edit_group (s);
685   return 0;
686 }
687
688 void ip4_icmp_register_type (vlib_main_t * vm, icmp4_type_t type, 
689                              u32 node_index)
690 {
691   icmp4_main_t * im = &icmp4_main;
692
693   ASSERT ((int)type < ARRAY_LEN (im->ip4_input_next_index_by_type));
694   im->ip4_input_next_index_by_type[type]
695     = vlib_node_add_next (vm, ip4_icmp_input_node.index, node_index);
696 }
697
698 static clib_error_t *
699 icmp4_init (vlib_main_t * vm)
700 {
701   ip_main_t * im = &ip_main;
702   ip_protocol_info_t * pi;
703   icmp4_main_t * cm = &icmp4_main;
704   clib_error_t * error;
705
706   error = vlib_call_init_function (vm, ip_main_init);
707
708   if (error)
709     return error;
710
711   pi = ip_get_protocol_info (im, IP_PROTOCOL_ICMP);
712   pi->format_header = format_ip4_icmp_header;
713   pi->unformat_pg_edit = unformat_pg_icmp_header;
714
715   cm->type_by_name = hash_create_string (0, sizeof (uword));
716 #define _(n,t) hash_set_mem (cm->type_by_name, #t, (n));
717   foreach_icmp4_type;
718 #undef _
719
720   cm->type_and_code_by_name = hash_create_string (0, sizeof (uword));
721 #define _(a,n,t) hash_set_mem (cm->type_by_name, #t, (n) | (ICMP4_##a << 8));
722   foreach_icmp4_code;
723 #undef _
724
725   memset (cm->ip4_input_next_index_by_type,
726           ICMP_INPUT_NEXT_ERROR,
727           sizeof (cm->ip4_input_next_index_by_type));
728
729   ip4_icmp_register_type (vm, ICMP4_echo_request, ip4_icmp_echo_request_node.index);
730
731   return 0;
732 }
733
734 VLIB_INIT_FUNCTION (icmp4_init);