summaryrefslogtreecommitdiffstats
path: root/src/plugins/gtpu/gtpu_decap.c
blob: e3bc476ed6d6ce63c558f4d98dd41e32985d0b00 (plain)
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
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443

@media only all and (prefers-color-scheme: dark) {
.highlight .hll { background-color: #49483e }
.highlight .c { color: #75715e } /* Comment */
.highlight .err { color: #960050; background-color: #1e0010 } /* Error */
.highlight .k { color: #66d9ef } /* Keyword */
.highlight .l { color: #ae81ff } /* Literal */
.highlight .n { color: #f8f8f2 } /* Name */
.highlight .o { color: #f92672 } /* Operator */
.highlight .p { color: #f8f8f2 } /* Punctuation */
.highlight .ch { color: #75715e } /* Comment.Hashbang */
.highlight .cm { color: #75715e } /* Comment.Multiline */
.highlight .cp { color: #75715e } /* Comment.Preproc */
.highlight .cpf { color: #75715e } /* Comment.PreprocFile */
.highlight .c1 { color: #75715e } /* Comment.Single */
.highlight .cs { color: #75715e } /* Comment.Special */
.highlight .gd { color: #f92672 } /* Generic.Deleted */
.highlight .ge { font-style: italic } /* Generic.Emph */
.highlight .gi { color: #a6e22e } /* Generic.Inserted */
.highlight .gs { font-weight: bold } /* Generic.Strong */
.highlight .gu { color: #75715e } /* Generic.Subheading */
.highlight .kc { color: #66d9ef } /* Keyword.Constant */
.highlight .kd { color: #66d9ef } /* Keyword.Declaration */
.highlight .kn { color: #f92672 } /* Keyword.Namespace */
.highlight .kp { color: #66d9ef } /* Keyword.Pseudo */
.highlight .kr { color: #66d9ef } /* Keyword.Reserved */
.highlight .kt { color: #66d9ef } /* Keyword.Type */
.highlight .ld { color: #e6db74 } /* Literal.Date */
.highlight .m { color: #ae81ff } /* Literal.Number */
.highlight .s { color: #e6db74 } /* Literal.String */
.highlight .na { color: #a6e22e } /* Name.Attribute */
.highlight .nb { color: #f8f8f2 } /* Name.Builtin */
.highlight .nc { color: #a6e22e } /* Name.Class */
.highlight .no { color: #66d9ef } /* Name.Constant */
.highlight .nd { color: #a6e22e } /* Name.Decorator */
.highlight .ni { color: #f8f8f2 } /* Name.Entity */
.highlight .ne { color: #a6e22e } /* Name.Exception */
.highlight .nf { color: #a6e22e } /* Name.Function */
.highlight .nl { color: #f8f8f2 } /* Name.Label */
.highlight .nn { color: #f8f8f2 } /* Name.Namespace */
.highlight .nx { color: #a6e22e } /* Name.Other */
.highlight .py { color: #f8f8f2 } /* Name.Property */
.highlight .nt { color: #f92672 } /* Name.Tag */
.highlight .nv { color: #f8f8f2 } /* Name.Variable */
.highlight .ow { color: #f92672 } /* Operator.Word */
.highlight .w { color: #f8f8f2 } /* Text.Whitespace */
.highlight .mb { color: #ae81ff } /* Literal.Number.Bin */
.highlight .mf { color: #ae81ff } /* Literal.Number.Float */
.highlight .mh { color: #ae81ff } /* Literal.Number.Hex */
.highlight .mi { color: #ae81ff } /* Literal.Number.Integer */
.highlight .mo { color: #ae81ff } /* Literal.Number.Oct */
.highlight .sa { color: #e6db74 } /* Literal.String.Affix */
.highlight .sb { color: #e6db74 } /* Literal.String.Backtick */
.highlight .sc { color: #e6db74 } /* Literal.String.Char */
.highlight .dl { color: #e6db74 } /* Literal.String.Delimiter */
.highlight .sd { color: #e6db74 } /* Literal.String.Doc */
.highlight .s2 { color: #e6db74 } /* Literal.String.Double */
.highlight .se { color: #ae81ff } /* Literal.String.Escape */
.highlight .sh { color: #e6db74 } /* Literal.String.Heredoc */
.highlight .si { color: #e6db74 } /* Literal.String.Interpol */
.highlight .sx { color: #e6db74 } /* Literal.String.Other */
.highlight .sr { color: #e6db74 } /* Literal.String.Regex */
.highlight .s1 { color: #e6db74 } /* Literal.String.Single */
.highlight .ss { color: #e6db74 } /* Literal.String.Symbol */
.highlight .bp { color: #f8f8f2 } /* Name.Builtin.Pseudo */
.highlight .fm { color: #a6e22e } /* Name.Function.Magic */
.highlight .vc { color: #f8f8f2 } /* Name.Variable.Class */
.highlight .vg { color: #f8f8f2 } /* Name.Variable.Global */
.highlight .vi { color: #f8f8f2 } /* Name.Variable.Instance */
.highlight .vm { color: #f8f8f2 } /* Name.Variable.Magic */
.highlight .il { color: #ae81ff } /* Literal.Number.Integer.Long */
}
@media (prefers-color-scheme: light) {
.highlight .hll { background-color: #ffffcc }
.highlight .c { color: #888888 } /* Comment */
.highlight .err { color: #a61717; background-color: #e3d2d2 } /* Error */
.highlight .k { color: #008800; font-weight: bold } /* Keyword */
.highlight .ch { color: #888888 } /* Comment.Hashbang */
.highlight .cm { color: #888888 } /* Comment.Multiline */
.highlight .cp { color: #cc0000; font-weight: bold } /* Comment.Preproc */
.highlight .cpf { color: #888888 } /* Comment.PreprocFile */
.highlight .c1 { color: #888888 } /* Comment.Single */
.highlight .cs { color: #cc0000; font-weight: bold; background-color: #fff0f0 } /* Comment.Special */
.highlight .gd { color: #000000; background-color: #ffdddd } /* Generic.Deleted */
.highlight .ge { font-style: italic } /* Generic.Emph */
.highlight .gr { color: #aa0000 } /* Generic.Error */
.highlight .gh { color: #333333 } /* Generic.Heading */
.highlight .gi { color: #000000; background-color: #ddffdd } /* Generic.Inserted */
.highlight .go { color: #888888 } /* Generic.Output */
.highlight .gp { color: #555555 } /* Generic.Prompt */
.highlight .gs { font-weight: bold } /* Generic.Strong */
.highlight .gu { color: #666666 } /* Generic.Subheading */
.highlight .gt { color: #aa0000 } /* Generic.Traceback */
.highlight .kc { color: #008800; font-weight: bold } /* Keyword.Constant */
.highlight .kd { color: #008800; font-weight: bold } /* Keyword.Declaration */
.highlight .kn { color: #008800; font-weight: bold } /* Keyword.Namespace */
.highlight .kp { color: #008800 } /* Keyword.Pseudo */
.highlight .kr { color: #008800; font-weight: bold } /* Keyword.Reserved */
.highlight .kt { color: #888888; font-weight: bold } /* Keyword.Type */
.highlight .m { color: #0000DD; font-weight: bold } /* Literal.Number */
.highlight .s { color: #dd2200; background-color: #fff0f0 } /* Literal.String */
.highlight .na { color: #336699 } /* Name.Attribute */
.highlight .nb { color: #003388 } /* Name.Builtin */
.highlight .nc { color: #bb0066; font-weight: bold } /* Name.Class */
.highlight .no { color: #003366; font-weight: bold } /* Name.Constant */
.highlight .nd { color: #555555 } /* Name.Decorator */
.highlight .ne { color: #bb0066; font-weight: bold } /* Name.Exception */
.highlight .nf { color: #0066bb; font-weight: bold } /* Name.Function */
.highlight .nl { color: #336699; font-style: italic } /* Name.Label */
.highlight .nn { color: #bb0066; font-weight: bold } /* Name.Namespace */
.highlight .py { color: #336699; font-weight: bold } /* Name.Property */
.highlight .nt { color: #bb0066; font-weight: bold } /* Name.Tag */
.highlight .nv { color: #336699 } /* Name.Variable */
.highlight .ow { color: #008800 } /* Operator.Word */
.highlight .w { color: #bbbbbb } /* Text.Whitespace */
.highlight .mb { color: #0000DD; font-weight: bold } /* Literal.Number.Bin */
.highlight .mf { color: #0000DD; font-weight: bold } /* Literal.Number.Float */
.highlight .mh { color: #0000DD; font-weight: bold } /* Literal.Number.Hex */
.highlight .mi { color: #0000DD; font-weight: bold } /* Literal.Number.Integer */
.highlight .mo { color: #0000DD; font-weight: bold } /* Literal.Number.Oct */
.highlight .sa { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Affix */
.highlight .sb { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Backtick */
.highlight .sc { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Char */
.highlight .dl { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Delimiter */
.highlight .sd { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Doc */
.highlight .s2 { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Double */
.highlight .se { color: #0044dd; background-color: #fff0f0 } /* Literal.String.Escape */
.highlight .sh { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Heredoc */
.highlight .si { color: #3333bb; background-color: #fff0f0 } /* Literal.String.Interpol */
.highlight .sx { color: #22bb22; background-color: #f0fff0 } /* Literal.String.Other */
.highlight .sr { color: #008800; background-color: #fff0ff } /* Literal.String.Regex */
.highlight .s1 { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Single */
.highlight .ss { color: #aa6600; background-color: #fff0f0 } /* Literal.String.Symbol */
.highlight .bp { color: #003388 } /* Name.Builtin.Pseudo */
.highlight .fm { color: #0066bb; font-weight: bold } /* Name.Function.Magic */
.highlight .vc { color: #336699 } /* Name.Variable.Class */
.highlight .vg { color: #dd7700 } /* Name.Variable.Global */
.highlight .vi { color: #3333bb } /* Name.Variable.Instance */
.highlight .vm { color: #336699 } /* Name.Variable.Magic */
.highlight .il { color: #0000DD; font-weight: bold } /* Literal.Number.Integer.Long */
}
/*
 * Copyright (c) 2016 Cisco and/or its affiliates.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at:
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#ifndef VNET_CONTROL_H_
#define VNET_CONTROL_H_

#include <vnet/vnet.h>
#include <vnet/lisp-cp/gid_dictionary.h>
#include <vnet/lisp-cp/lisp_types.h>
#include <vppinfra/timing_wheel.h>

#define NUMBER_OF_RETRIES                   1
#define PENDING_MREQ_EXPIRATION_TIME        3.0	/* seconds */
#define PENDING_MREQ_QUEUE_LEN              5

#define RLOC_PROBING_INTERVAL               60.0

/* when map-registration is enabled "quick registration" takes place first.
   In this mode ETR sends map-register messages at an increased frequency
   until specified message count is reached */
#define QUICK_MAP_REGISTER_MSG_COUNT        5
#define QUICK_MAP_REGISTER_INTERVAL         3.0

/* normal map-register period */
#define MAP_REGISTER_INTERVAL               60.0

/* how many tries until next map-server election */
#define MAX_EXPIRED_MAP_REGISTERS_DEFAULT   3

#define PENDING_MREG_EXPIRATION_TIME        3.0	/* seconds */

/* 24 hours */
#define MAP_REGISTER_DEFAULT_TTL            86400

typedef struct
{
  gid_address_t src;
  gid_address_t dst;
  u32 retries_num;
  f64 time_to_expire;
  u8 is_smr_invoked;
  u64 *nonces;
  u8 to_be_removed;
} pending_map_request_t;

typedef struct
{
  f64 time_to_expire;
} pending_map_register_t;

typedef struct
{
  gid_address_t leid;
  gid_address_t reid;
  u8 is_src_dst;
  locator_pair_t *locator_pairs;
} fwd_entry_t;

typedef struct
{
  gid_address_t leid;
  gid_address_t reid;
} lisp_adjacency_t;

typedef enum
{
  IP4_MISS_PACKET,
  IP6_MISS_PACKET
} miss_packet_type_t;

/* map-server/map-resolver structure */
typedef struct
{
  u8 is_down;
  f64 last_update;
  ip_address_t address;
  char *key;
} lisp_msmr_t;

typedef struct
{
  /* headers */
  u8 data[100];
  u32 length;
  miss_packet_type_t type;
} miss_packet_t;

typedef struct
{
  u8 mac[6];
  u32 ip4;
} lisp_api_l2_arp_entry_t;

typedef struct
{
  u8 mac[6];
  u8 ip6[16];
} lisp_api_ndp_entry_t;

typedef enum
{
  MR_MODE_DST_ONLY = 0,
  MR_MODE_SRC_DST,
  _MR_MODE_MAX
} map_request_mode_t;

#define foreach_lisp_flag_bit       \
  _(USE_PETR, "Use Proxy-ETR")                    \
  _(XTR_MODE, "ITR/ETR mode")                     \
  _(PETR_MODE, "Proxy-ETR mode")                   \
  _(PITR_MODE, "Proxy-ITR mode")                  \
  _(STATS_ENABLED, "Statistics enabled")

typedef enum lisp_flag_bits
{
#define _(sym, str) LISP_FLAG_BIT_##sym,
  foreach_lisp_flag_bit
#undef _
} lisp_flag_bits_e;

typedef enum lisp_flags
{
#define _(sym, str) LISP_FLAG_##sym = 1 << LISP_FLAG_BIT_##sym,
  foreach_lisp_flag_bit
#undef _
} lisp_flags_e;

typedef struct
{
  ip_address_t addr;
  u32 bd;
} lisp_l2_arp_key_t;

typedef enum
{
  LISP_TRANSPORT_PROTOCOL_UDP = 1,
  LISP_TRANSPORT_PROTOCOL_API
} lisp_transport_protocol_t;

typedef struct
{
  u64 nonce;
  u8 is_rloc_probe;
  mapping_t *mappings;
  volatile u8 is_free;
} map_records_arg_t;

typedef struct
{
  u32 flags;

  /* LISP feature status */
  u8 is_enabled;

  /* eid table */
  gid_dictionary_t mapping_index_by_gid;

  /* pool of mappings */
  mapping_t *mapping_pool;

  /* hash map of secret keys by mapping index */
  u8 *key_by_mapping_index;

  /* pool of locators */
  locator_t *locator_pool;

  /* pool of locator-sets */
  locator_set_t *locator_set_pool;

  /* vector of locator-set vectors composed of and indexed by locator index */
  u32 **locator_to_locator_sets;

  /* hash map of locators by name */
  uword *locator_set_index_by_name;

  /* vector of eid index vectors supported and indexed by locator-set index */
  u32 **locator_set_to_eids;

  /* vectors of indexes for local locator-sets and mappings */
  u32 *local_mappings_indexes;
  u32 *local_locator_set_indexes;

  /* hash map of forwarding entries by mapping index */
  u32 *fwd_entry_by_mapping_index;

  /* pool of vectors of rmts per lcl mapping in adjacencies */
  u32 **lcl_to_rmt_adjacencies;

  /* hash of pool positions of vectors of rmts by lcl mapping index */
  u32 *lcl_to_rmt_adjs_by_lcl_idx;

  /* forwarding entries pool */
  fwd_entry_t *fwd_entry_pool;

  /* hash map keyed by nonce of pending map-requests */
  uword *pending_map_requests_by_nonce;

  /* pool of pending map requests */
  pending_map_request_t *pending_map_requests_pool;

  /* pool of pending map registers */
  pending_map_register_t *pending_map_registers_pool;

  /* hash map of sent map register messages */
  uword *map_register_messages_by_nonce;

  /* vector of map-resolvers */
  lisp_msmr_t *map_resolvers;

  /* vector of map-servers */
  lisp_msmr_t *map_servers;

  /* map resolver address currently being used for sending requests.
   * This has to be an actual address and not an index to map_resolvers vector
   * since the vector may be modified during request resend/retry procedure
   * and break things :-) */
  ip_address_t active_map_resolver;
  ip_address_t active_map_server;

  u8 do_map_resolver_election;
  u8 do_map_server_election;

  /* map-request  locator set index */
  u32 mreq_itr_rlocs;

  /* vni to vrf hash tables */
  uword *table_id_by_vni;
  uword *vni_by_table_id;

  /* vni to bd-index hash tables */
  uword *bd_id_by_vni;
  uword *vni_by_bd_id;

  /* track l2 and l3 interfaces that have been created for vni */
  uword *l2_dp_intf_by_vni;

  /* Proxy ITR map index */
  u32 pitr_map_index;

  /** Proxy ETR map index used for 'use-petr'.
   *  Not related to PETR tunnel mode  */
  u32 petr_map_index;

  /* mapping index for NSH */
  u32 nsh_map_index;

  /* map request mode */
  u8 map_request_mode;

  /* enable/disable map registering */
  u8 map_registering;

  /* enable/disable rloc-probing */
  u8 rloc_probing;

  /* timing wheel for mapping timeouts */
  timing_wheel_t wheel;

  /** Per thread pool of records shared with thread0 */
  map_records_arg_t **map_records_args_pool;

  /* TTL used for all mappings when registering */
  u32 map_register_ttl;

  /* control variables for map server election */
  u32 max_expired_map_registers;
  u32 expired_map_registers;

  /** either UDP based or binary API. Default is UDP */
  lisp_transport_protocol_t transport_protocol;

  /* commodity */
  ip4_main_t *im4;
  ip6_main_t *im6;
  vlib_main_t *vlib_main;
  vnet_main_t *vnet_main;
} lisp_cp_main_t;

/* lisp-gpe control plane */
extern lisp_cp_main_t lisp_control_main;

extern vlib_node_registration_t lisp_cp_input_node;
extern vlib_node_registration_t lisp_cp_lookup_ip4_node;
extern vlib_node_registration_t lisp_cp_lookup_ip6_node;

clib_error_t *lisp_cp_init ();

always_inline lisp_cp_main_t *
vnet_lisp_cp_get_main ()
{
  return &lisp_control_main;
}

void
get_src_and_dst_eids_from_buffer (lisp_cp_main_t * lcm, vlib_buffer_t * b,
				  gid_address_t * src, gid_address_t * dst,
				  u16 type);

typedef struct
{
  u8 is_add;
  union
  {
    u8 *name;
    u32 index;
  };
  locator_t *locators;
  u8 local;
} vnet_lisp_add_del_locator_set_args_t;

int
vnet_lisp_add_del_locator_set (vnet_lisp_add_del_locator_set_args_t * a,
			       u32 * ls_index);
int
vnet_lisp_add_del_locator (vnet_lisp_add_del_locator_set_args_t * a,
			   locator_set_t * ls, u32 * ls_index);

typedef struct
{
  u8 is_add;
  gid_address_t eid;
  u32 locator_set_index;

  u32 ttl;
  u8 action;
  u8 authoritative;

  u8 local;
  u8 is_static;
  u8 *key;
  u8 key_id;
} vnet_lisp_add_del_mapping_args_t;

int
vnet_lisp_map_cache_add_del (vnet_lisp_add_del_mapping_args_t * a,
			     u32 * map_index);
int
vnet_lisp_add_del_local_mapping (vnet_lisp_add_del_mapping_args_t * a,
				 u32 * map_index_result);

int
vnet_lisp_add_mapping (vnet_lisp_add_del_mapping_args_t * a,
		       locator_t * rlocs, u32 * res_map_index,
		       u8 * is_changed);

int vnet_lisp_del_mapping (gid_address_t * eid, u32 * res_map_index);

typedef struct
{
  gid_address_t reid;
  gid_address_t leid;
  u8 is_add;
} vnet_lisp_add_del_adjacency_args_t;

int vnet_lisp_add_del_adjacency (vnet_lisp_add_del_adjacency_args_t * a);

typedef struct
{
  u8 is_add;
  ip_address_t address;
} vnet_lisp_add_del_map_resolver_args_t;

int
vnet_lisp_add_del_map_resolver (vnet_lisp_add_del_map_resolver_args_t * a);
int vnet_lisp_add_del_map_server (ip_address_t * addr, u8 is_add);

clib_error_t *vnet_lisp_enable_disable (u8 is_enabled);
u8 vnet_lisp_enable_disable_status (void);

int vnet_lisp_pitr_set_locator_set (u8 * locator_set_name, u8 is_add);
int vnet_lisp_use_petr (ip_address_t * ip, u8 is_add);

typedef struct
{
  u8 is_add;
  u8 *locator_set_name;
} vnet_lisp_add_del_mreq_itr_rloc_args_t;

int
vnet_lisp_add_del_mreq_itr_rlocs (vnet_lisp_add_del_mreq_itr_rloc_args_t * a);

int vnet_lisp_clear_all_remote_adjacencies (void);

int vnet_lisp_eid_table_map (u32 vni, u32 vrf, u8 is_l2, u8 is_add);
int vnet_lisp_add_del_map_table_key (gid_address_t * eid, char *key,
				     u8 is_add);
int vnet_lisp_set_map_request_mode (u8 mode);
u8 vnet_lisp_get_map_request_mode (void);
lisp_adjacency_t *vnet_lisp_adjacencies_get_by_vni (u32 vni);
int vnet_lisp_rloc_probe_enable_disable (u8 is_enable);
int vnet_lisp_map_register_enable_disable (u8 is_enable);
u8 vnet_lisp_map_register_state_get (void);
u8 vnet_lisp_rloc_probe_state_get (void);
int vnet_lisp_add_del_l2_arp_ndp_entry (gid_address_t * key, u8 * mac,
					u8 is_add);
u32 *vnet_lisp_l2_arp_bds_get (void);
lisp_api_l2_arp_entry_t *vnet_lisp_l2_arp_entries_get_by_bd (u32 bd);
int vnet_lisp_nsh_set_locator_set (u8 * locator_set_name, u8 is_add);
int vnet_lisp_map_register_set_ttl (u32 ttl);
u32 vnet_lisp_map_register_get_ttl (void);
int vnet_lisp_map_register_fallback_threshold_set (u32 value);
u32 vnet_lisp_map_register_fallback_threshold_get (void);
u32 *vnet_lisp_ndp_bds_get (void);
lisp_api_ndp_entry_t *vnet_lisp_ndp_entries_get_by_bd (u32 bd);
u32 vnet_lisp_set_transport_protocol (u8 protocol);
lisp_transport_protocol_t vnet_lisp_get_transport_protocol (void);

extern int vnet_lisp_enable_disable_xtr_mode (u8 is_enabled);
extern int vnet_lisp_enable_disable_pitr_mode (u8 is_enabled);
extern int vnet_lisp_enable_disable_petr_mode (u8 is_enabled);
extern u8 vnet_lisp_get_xtr_mode (void);
extern u8 vnet_lisp_get_pitr_mode (void);
extern u8 vnet_lisp_get_petr_mode (void);

map_records_arg_t *parse_map_reply (vlib_buffer_t * b);

always_inline mapping_t *
lisp_get_petr_mapping (lisp_cp_main_t * lcm)
{
  return pool_elt_at_index (lcm->mapping_pool, lcm->petr_map_index);
}

#endif /* VNET_CONTROL_H_ */

/*
 * fd.io coding-style-patch-verification: ON
 *
 * Local Variables:
 * eval: (c-set-style "gnu")
 * End:
 */
id='n1701' href='#n1701'>1701 1702 1703 1704 1705 1706 1707 1708 1709 1710 1711 1712 1713 1714 1715 1716 1717 1718 1719 1720 1721 1722 1723 1724 1725 1726 1727 1728 1729 1730 1731 1732 1733 1734 1735 1736 1737 1738 1739 1740 1741 1742 1743 1744 1745 1746 1747 1748 1749 1750 1751 1752 1753 1754 1755 1756 1757 1758 1759 1760 1761
/*
 * decap.c: gtpu tunnel decap packet processing
 *
 * Copyright (c) 2017 Intel and/or its affiliates.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at:
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include <vlib/vlib.h>
#include <gtpu/gtpu.h>

extern vlib_node_registration_t gtpu4_input_node;
extern vlib_node_registration_t gtpu6_input_node;

typedef struct {
  u32 next_index;
  u32 tunnel_index;
  u32 error;
  u32 teid;
} gtpu_rx_trace_t;

static u8 * format_gtpu_rx_trace (u8 * s, va_list * args)
{
  CLIB_UNUSED (vlib_main_t * vm) = va_arg (*args, vlib_main_t *);
  CLIB_UNUSED (vlib_node_t * node) = va_arg (*args, vlib_node_t *);
  gtpu_rx_trace_t * t = va_arg (*args, gtpu_rx_trace_t *);

  if (t->tunnel_index != ~0)
    {
      s = format (s, "GTPU decap from gtpu_tunnel%d teid %d next %d error %d",
                  t->tunnel_index, t->teid, t->next_index, t->error);
    }
  else
    {
      s = format (s, "GTPU decap error - tunnel for teid %d does not exist",
		  t->teid);
    }
  return s;
}

always_inline u32
validate_gtpu_fib (vlib_buffer_t *b, gtpu_tunnel_t *t, u32 is_ip4)
{
  return t->encap_fib_index == vlib_buffer_get_ip_fib_index (b, is_ip4);
}

always_inline uword
gtpu_input (vlib_main_t * vm,
             vlib_node_runtime_t * node,
             vlib_frame_t * from_frame,
             u32 is_ip4)
{
  u32 n_left_from, next_index, * from, * to_next;
  gtpu_main_t * gtm = &gtpu_main;
  vnet_main_t * vnm = gtm->vnet_main;
  vnet_interface_main_t * im = &vnm->interface_main;
  u32 last_tunnel_index = ~0;
  gtpu4_tunnel_key_t last_key4;
  gtpu6_tunnel_key_t last_key6;
  u32 pkts_decapsulated = 0;
  u32 thread_index = vlib_get_thread_index();
  u32 stats_sw_if_index, stats_n_packets, stats_n_bytes;

  if (is_ip4)
    last_key4.as_u64 = ~0;
  else
    clib_memset (&last_key6, 0xff, sizeof (last_key6));

  from = vlib_frame_vector_args (from_frame);
  n_left_from = from_frame->n_vectors;

  next_index = node->cached_next_index;
  stats_sw_if_index = node->runtime_data[0];
  stats_n_packets = stats_n_bytes = 0;

  while (n_left_from > 0)
    {
      u32 n_left_to_next;

      vlib_get_next_frame (vm, node, next_index,
			   to_next, n_left_to_next);
      while (n_left_from >= 4 && n_left_to_next >= 2)
	{
          u32 bi0, bi1;
	  vlib_buffer_t * b0, * b1;
	  u32 next0, next1;
          ip4_header_t * ip4_0, * ip4_1;
          ip6_header_t * ip6_0, * ip6_1;
          gtpu_header_t * gtpu0, * gtpu1;
          u32 gtpu_hdr_len0, gtpu_hdr_len1;
	  uword * p0, * p1;
          u32 tunnel_index0, tunnel_index1;
          gtpu_tunnel_t * t0, * t1, * mt0 = NULL, * mt1 = NULL;
          gtpu4_tunnel_key_t key4_0, key4_1;
          gtpu6_tunnel_key_t key6_0, key6_1;
          u32 error0, error1;
	  u32 sw_if_index0, sw_if_index1, len0, len1;
          u8 has_space0, has_space1;
          u8 ver0, ver1;

	  /* Prefetch next iteration. */
	  {
	    vlib_buffer_t * p2, * p3;

	    p2 = vlib_get_buffer (vm, from[2]);
	    p3 = vlib_get_buffer (vm, from[3]);

	    vlib_prefetch_buffer_header (p2, LOAD);
	    vlib_prefetch_buffer_header (p3, LOAD);

	    CLIB_PREFETCH (p2->data, 2*CLIB_CACHE_LINE_BYTES, LOAD);
	    CLIB_PREFETCH (p3->data, 2*CLIB_CACHE_LINE_BYTES, LOAD);
	  }

	  bi0 = from[0];
	  bi1 = from[1];
	  to_next[0] = bi0;
	  to_next[1] = bi1;
	  from += 2;
	  to_next += 2;
	  n_left_to_next -= 2;
	  n_left_from -= 2;

	  b0 = vlib_get_buffer (vm, bi0);
	  b1 = vlib_get_buffer (vm, bi1);

          /* udp leaves current_data pointing at the gtpu header */
          gtpu0 = vlib_buffer_get_current (b0);
          gtpu1 = vlib_buffer_get_current (b1);
          if (is_ip4)
            {
              ip4_0 = (void *)((u8*)gtpu0 - sizeof(udp_header_t) - sizeof(ip4_header_t));
              ip4_1 = (void *)((u8*)gtpu1 - sizeof(udp_header_t) - sizeof(ip4_header_t));
            }
          else
            {
              ip6_0 = (void *)((u8*)gtpu0 - sizeof(udp_header_t) - sizeof(ip6_header_t));
              ip6_1 = (void *)((u8*)gtpu1 - sizeof(udp_header_t) - sizeof(ip6_header_t));
            }

          tunnel_index0 = ~0;
          error0 = 0;

          tunnel_index1 = ~0;
          error1 = 0;

          /* speculatively load gtp header version field */
          ver0 = gtpu0->ver_flags;
          ver1 = gtpu1->ver_flags;

	  /*
           * Manipulate gtpu header
           * TBD: Manipulate Sequence Number and N-PDU Number
           * TBD: Manipulate Next Extension Header
           */
          gtpu_hdr_len0 = sizeof(gtpu_header_t) - (((ver0 & GTPU_E_S_PN_BIT) == 0) * 4);
          gtpu_hdr_len1 = sizeof(gtpu_header_t) - (((ver1 & GTPU_E_S_PN_BIT) == 0) * 4);

          has_space0 = vlib_buffer_has_space (b0, gtpu_hdr_len0);
          has_space1 = vlib_buffer_has_space (b1, gtpu_hdr_len1);

	  if (PREDICT_FALSE (((ver0 & GTPU_VER_MASK) != GTPU_V1_VER) | (!has_space0)))
	    {
	      error0 = has_space0 ? GTPU_ERROR_BAD_VER : GTPU_ERROR_TOO_SMALL;
	      next0 = GTPU_INPUT_NEXT_DROP;
	      goto trace0;
	    }

	  /* Manipulate packet 0 */
          if (is_ip4) {
            key4_0.src = ip4_0->src_address.as_u32;
            key4_0.teid = gtpu0->teid;

 	    /* Make sure GTPU tunnel exist according to packet SIP and teid
 	     * SIP identify a GTPU path, and teid identify a tunnel in a given GTPU path */
           if (PREDICT_FALSE (key4_0.as_u64 != last_key4.as_u64))
              {
                p0 = hash_get (gtm->gtpu4_tunnel_by_key, key4_0.as_u64);
                if (PREDICT_FALSE (p0 == NULL))
                  {
                    error0 = GTPU_ERROR_NO_SUCH_TUNNEL;
                    next0 = GTPU_INPUT_NEXT_DROP;
                    goto trace0;
                  }
                last_key4.as_u64 = key4_0.as_u64;
                tunnel_index0 = last_tunnel_index = p0[0];
              }
            else
              tunnel_index0 = last_tunnel_index;
	    t0 = pool_elt_at_index (gtm->tunnels, tunnel_index0);

	    /* Validate GTPU tunnel encap-fib index against packet */
	    if (PREDICT_FALSE (validate_gtpu_fib (b0, t0, is_ip4) == 0))
	      {
		error0 = GTPU_ERROR_NO_SUCH_TUNNEL;
		next0 = GTPU_INPUT_NEXT_DROP;
		goto trace0;
	      }

	    /* Validate GTPU tunnel SIP against packet DIP */
	    if (PREDICT_TRUE (ip4_0->dst_address.as_u32 == t0->src.ip4.as_u32))
	      goto next0; /* valid packet */
	    if (PREDICT_FALSE (ip4_address_is_multicast (&ip4_0->dst_address)))
	      {
		key4_0.src = ip4_0->dst_address.as_u32;
		key4_0.teid = gtpu0->teid;
		/* Make sure mcast GTPU tunnel exist by packet DIP and teid */
		p0 = hash_get (gtm->gtpu4_tunnel_by_key, key4_0.as_u64);
		if (PREDICT_TRUE (p0 != NULL))
		  {
		    mt0 = pool_elt_at_index (gtm->tunnels, p0[0]);
		    goto next0; /* valid packet */
		  }
	      }
	    error0 = GTPU_ERROR_NO_SUCH_TUNNEL;
	    next0 = GTPU_INPUT_NEXT_DROP;
	    goto trace0;

         } else /* !is_ip4 */ {
            key6_0.src.as_u64[0] = ip6_0->src_address.as_u64[0];
            key6_0.src.as_u64[1] = ip6_0->src_address.as_u64[1];
            key6_0.teid = gtpu0->teid;

 	    /* Make sure GTPU tunnel exist according to packet SIP and teid
 	     * SIP identify a GTPU path, and teid identify a tunnel in a given GTPU path */
            if (PREDICT_FALSE (memcmp(&key6_0, &last_key6, sizeof(last_key6)) != 0))
              {
                p0 = hash_get_mem (gtm->gtpu6_tunnel_by_key, &key6_0);
                if (PREDICT_FALSE (p0 == NULL))
                  {
                    error0 = GTPU_ERROR_NO_SUCH_TUNNEL;
                    next0 = GTPU_INPUT_NEXT_DROP;
                    goto trace0;
                  }
                clib_memcpy_fast (&last_key6, &key6_0, sizeof(key6_0));
                tunnel_index0 = last_tunnel_index = p0[0];
              }
            else
              tunnel_index0 = last_tunnel_index;
	    t0 = pool_elt_at_index (gtm->tunnels, tunnel_index0);

	    /* Validate GTPU tunnel encap-fib index against packet */
	    if (PREDICT_FALSE (validate_gtpu_fib (b0, t0, is_ip4) == 0))
	      {
		error0 = GTPU_ERROR_NO_SUCH_TUNNEL;
		next0 = GTPU_INPUT_NEXT_DROP;
		goto trace0;
	      }

	    /* Validate GTPU tunnel SIP against packet DIP */
	    if (PREDICT_TRUE (ip6_address_is_equal (&ip6_0->dst_address,
						    &t0->src.ip6)))
		goto next0; /* valid packet */
	    if (PREDICT_FALSE (ip6_address_is_multicast (&ip6_0->dst_address)))
	      {
		key6_0.src.as_u64[0] = ip6_0->dst_address.as_u64[0];
		key6_0.src.as_u64[1] = ip6_0->dst_address.as_u64[1];
		key6_0.teid = gtpu0->teid;
		p0 = hash_get_mem (gtm->gtpu6_tunnel_by_key, &key6_0);
		if (PREDICT_TRUE (p0 != NULL))
		  {
		    mt0 = pool_elt_at_index (gtm->tunnels, p0[0]);
		    goto next0; /* valid packet */
		  }
	      }
	    error0 = GTPU_ERROR_NO_SUCH_TUNNEL;
	    next0 = GTPU_INPUT_NEXT_DROP;
	    goto trace0;
          }

	next0:
	  /* Pop gtpu header */
	  vlib_buffer_advance (b0, gtpu_hdr_len0);

          next0 = t0->decap_next_index;
          sw_if_index0 = t0->sw_if_index;
          len0 = vlib_buffer_length_in_chain (vm, b0);

          /* Required to make the l2 tag push / pop code work on l2 subifs */
          if (PREDICT_TRUE(next0 == GTPU_INPUT_NEXT_L2_INPUT))
            vnet_update_l2_len (b0);

          /* Set packet input sw_if_index to unicast GTPU tunnel for learning */
          vnet_buffer(b0)->sw_if_index[VLIB_RX] = sw_if_index0;
	  sw_if_index0 = (mt0) ? mt0->sw_if_index : sw_if_index0;

          pkts_decapsulated ++;
          stats_n_packets += 1;
          stats_n_bytes += len0;

	  /* Batch stats increment on the same gtpu tunnel so counter
	     is not incremented per packet */
	  if (PREDICT_FALSE (sw_if_index0 != stats_sw_if_index))
	    {
	      stats_n_packets -= 1;
	      stats_n_bytes -= len0;
	      if (stats_n_packets)
		vlib_increment_combined_counter
		  (im->combined_sw_if_counters + VNET_INTERFACE_COUNTER_RX,
		   thread_index, stats_sw_if_index,
		   stats_n_packets, stats_n_bytes);
	      stats_n_packets = 1;
	      stats_n_bytes = len0;
	      stats_sw_if_index = sw_if_index0;
	    }

        trace0:
          b0->error = error0 ? node->errors[error0] : 0;

          if (PREDICT_FALSE(b0->flags & VLIB_BUFFER_IS_TRACED))
            {
              gtpu_rx_trace_t *tr
                = vlib_add_trace (vm, node, b0, sizeof (*tr));
              tr->next_index = next0;
              tr->error = error0;
              tr->tunnel_index = tunnel_index0;
              tr->teid = has_space0 ? clib_net_to_host_u32(gtpu0->teid) : ~0;
            }

	  if (PREDICT_FALSE (((ver1 & GTPU_VER_MASK) != GTPU_V1_VER) | (!has_space1)))
	    {
	      error1 = has_space1 ? GTPU_ERROR_BAD_VER : GTPU_ERROR_TOO_SMALL;
	      next1 = GTPU_INPUT_NEXT_DROP;
	      goto trace1;
	    }

          /* Manipulate packet 1 */
          if (is_ip4) {
            key4_1.src = ip4_1->src_address.as_u32;
            key4_1.teid = gtpu1->teid;

 	    /* Make sure GTPU tunnel exist according to packet SIP and teid
 	     * SIP identify a GTPU path, and teid identify a tunnel in a given GTPU path */
	    if (PREDICT_FALSE (key4_1.as_u64 != last_key4.as_u64))
              {
                p1 = hash_get (gtm->gtpu4_tunnel_by_key, key4_1.as_u64);
                if (PREDICT_FALSE (p1 == NULL))
                  {
                    error1 = GTPU_ERROR_NO_SUCH_TUNNEL;
                    next1 = GTPU_INPUT_NEXT_DROP;
                    goto trace1;
                  }
                last_key4.as_u64 = key4_1.as_u64;
                tunnel_index1 = last_tunnel_index = p1[0];
              }
            else
              tunnel_index1 = last_tunnel_index;
 	    t1 = pool_elt_at_index (gtm->tunnels, tunnel_index1);

	    /* Validate GTPU tunnel encap-fib index against packet */
	    if (PREDICT_FALSE (validate_gtpu_fib (b1, t1, is_ip4) == 0))
	      {
		error1 = GTPU_ERROR_NO_SUCH_TUNNEL;
		next1 = GTPU_INPUT_NEXT_DROP;
		goto trace1;
	      }

	    /* Validate GTPU tunnel SIP against packet DIP */
	    if (PREDICT_TRUE (ip4_1->dst_address.as_u32 == t1->src.ip4.as_u32))
	      goto next1; /* valid packet */
	    if (PREDICT_FALSE (ip4_address_is_multicast (&ip4_1->dst_address)))
	      {
		key4_1.src = ip4_1->dst_address.as_u32;
		key4_1.teid = gtpu1->teid;
		/* Make sure mcast GTPU tunnel exist by packet DIP and teid */
		p1 = hash_get (gtm->gtpu4_tunnel_by_key, key4_1.as_u64);
		if (PREDICT_TRUE (p1 != NULL))
		  {
		    mt1 = pool_elt_at_index (gtm->tunnels, p1[0]);
		    goto next1; /* valid packet */
		  }
	      }
	    error1 = GTPU_ERROR_NO_SUCH_TUNNEL;
	    next1 = GTPU_INPUT_NEXT_DROP;
	    goto trace1;

         } else /* !is_ip4 */ {
            key6_1.src.as_u64[0] = ip6_1->src_address.as_u64[0];
            key6_1.src.as_u64[1] = ip6_1->src_address.as_u64[1];
            key6_1.teid = gtpu1->teid;

 	    /* Make sure GTPU tunnel exist according to packet SIP and teid
 	     * SIP identify a GTPU path, and teid identify a tunnel in a given GTPU path */
            if (PREDICT_FALSE (memcmp(&key6_1, &last_key6, sizeof(last_key6)) != 0))
              {
                p1 = hash_get_mem (gtm->gtpu6_tunnel_by_key, &key6_1);

                if (PREDICT_FALSE (p1 == NULL))
                  {
                    error1 = GTPU_ERROR_NO_SUCH_TUNNEL;
                    next1 = GTPU_INPUT_NEXT_DROP;
                    goto trace1;
                  }

                clib_memcpy_fast (&last_key6, &key6_1, sizeof(key6_1));
                tunnel_index1 = last_tunnel_index = p1[0];
              }
            else
              tunnel_index1 = last_tunnel_index;
 	    t1 = pool_elt_at_index (gtm->tunnels, tunnel_index1);

	    /* Validate GTPU tunnel encap-fib index against packet */
	    if (PREDICT_FALSE (validate_gtpu_fib (b1, t1, is_ip4) == 0))
	      {
		error1 = GTPU_ERROR_NO_SUCH_TUNNEL;
		next1 = GTPU_INPUT_NEXT_DROP;
		goto trace1;
	      }

	    /* Validate GTPU tunnel SIP against packet DIP */
	    if (PREDICT_TRUE (ip6_address_is_equal (&ip6_1->dst_address,
						    &t1->src.ip6)))
		goto next1; /* valid packet */
	    if (PREDICT_FALSE (ip6_address_is_multicast (&ip6_1->dst_address)))
	      {
		key6_1.src.as_u64[0] = ip6_1->dst_address.as_u64[0];
		key6_1.src.as_u64[1] = ip6_1->dst_address.as_u64[1];
		key6_1.teid = gtpu1->teid;
		p1 = hash_get_mem (gtm->gtpu6_tunnel_by_key, &key6_1);
		if (PREDICT_TRUE (p1 != NULL))
		  {
		    mt1 = pool_elt_at_index (gtm->tunnels, p1[0]);
		    goto next1; /* valid packet */
		  }
	      }
	    error1 = GTPU_ERROR_NO_SUCH_TUNNEL;
	    next1 = GTPU_INPUT_NEXT_DROP;
	    goto trace1;
	  }

	next1:
	  /* Pop gtpu header */
	  vlib_buffer_advance (b1, gtpu_hdr_len1);

          next1 = t1->decap_next_index;
          sw_if_index1 = t1->sw_if_index;
          len1 = vlib_buffer_length_in_chain (vm, b1);

          /* Required to make the l2 tag push / pop code work on l2 subifs */
          if (PREDICT_TRUE(next1 == GTPU_INPUT_NEXT_L2_INPUT))
            vnet_update_l2_len (b1);

          /* Set packet input sw_if_index to unicast GTPU tunnel for learning */
          vnet_buffer(b1)->sw_if_index[VLIB_RX] = sw_if_index1;
	  sw_if_index1 = (mt1) ? mt1->sw_if_index : sw_if_index1;

          pkts_decapsulated ++;
          stats_n_packets += 1;
          stats_n_bytes += len1;

	  /* Batch stats increment on the same gtpu tunnel so counter
	     is not incremented per packet */
	  if (PREDICT_FALSE (sw_if_index1 != stats_sw_if_index))
	    {
	      stats_n_packets -= 1;
	      stats_n_bytes -= len1;
	      if (stats_n_packets)
		vlib_increment_combined_counter
		  (im->combined_sw_if_counters + VNET_INTERFACE_COUNTER_RX,
		   thread_index, stats_sw_if_index,
		   stats_n_packets, stats_n_bytes);
	      stats_n_packets = 1;
	      stats_n_bytes = len1;
	      stats_sw_if_index = sw_if_index1;
	    }

        trace1:
          b1->error = error1 ? node->errors[error1] : 0;

          if (PREDICT_FALSE(b1->flags & VLIB_BUFFER_IS_TRACED))
            {
              gtpu_rx_trace_t *tr
                = vlib_add_trace (vm, node, b1, sizeof (*tr));
              tr->next_index = next1;
              tr->error = error1;
              tr->tunnel_index = tunnel_index1;
              tr->teid = has_space1 ? clib_net_to_host_u32(gtpu1->teid) : ~0;
            }

	  vlib_validate_buffer_enqueue_x2 (vm, node, next_index,
					   to_next, n_left_to_next,
					   bi0, bi1, next0, next1);
	}

      while (n_left_from > 0 && n_left_to_next > 0)
	{
	  u32 bi0;
	  vlib_buffer_t * b0;
	  u32 next0;
          ip4_header_t * ip4_0;
          ip6_header_t * ip6_0;
          gtpu_header_t * gtpu0;
          u32 gtpu_hdr_len0;
	  uword * p0;
          u32 tunnel_index0;
          gtpu_tunnel_t * t0, * mt0 = NULL;
          gtpu4_tunnel_key_t key4_0;
          gtpu6_tunnel_key_t key6_0;
          u32 error0;
	  u32 sw_if_index0, len0;
          u8 has_space0;
          u8 ver0;

	  bi0 = from[0];
	  to_next[0] = bi0;
	  from += 1;
	  to_next += 1;
	  n_left_from -= 1;
	  n_left_to_next -= 1;

	  b0 = vlib_get_buffer (vm, bi0);

          /* udp leaves current_data pointing at the gtpu header */
          gtpu0 = vlib_buffer_get_current (b0);
          if (is_ip4) {
            ip4_0 = (void *)((u8*)gtpu0 - sizeof(udp_header_t) - sizeof(ip4_header_t));
          } else {
            ip6_0 = (void *)((u8*)gtpu0 - sizeof(udp_header_t) - sizeof(ip6_header_t));
          }

          tunnel_index0 = ~0;
          error0 = 0;

          /* speculatively load gtp header version field */
          ver0 = gtpu0->ver_flags;

	  /*
           * Manipulate gtpu header
           * TBD: Manipulate Sequence Number and N-PDU Number
           * TBD: Manipulate Next Extension Header
           */
          gtpu_hdr_len0 = sizeof(gtpu_header_t) - (((ver0 & GTPU_E_S_PN_BIT) == 0) * 4);

          has_space0 = vlib_buffer_has_space (b0, gtpu_hdr_len0);

	  if (PREDICT_FALSE (((ver0 & GTPU_VER_MASK) != GTPU_V1_VER) | (!has_space0)))
            {
	      error0 = has_space0 ? GTPU_ERROR_BAD_VER : GTPU_ERROR_TOO_SMALL;
              next0 = GTPU_INPUT_NEXT_DROP;
              goto trace00;
            }

          if (is_ip4) {
            key4_0.src = ip4_0->src_address.as_u32;
            key4_0.teid = gtpu0->teid;

 	    /* Make sure GTPU tunnel exist according to packet SIP and teid
 	     * SIP identify a GTPU path, and teid identify a tunnel in a given GTPU path */
            if (PREDICT_FALSE (key4_0.as_u64 != last_key4.as_u64))
              {
                p0 = hash_get (gtm->gtpu4_tunnel_by_key, key4_0.as_u64);
                if (PREDICT_FALSE (p0 == NULL))
                  {
                    error0 = GTPU_ERROR_NO_SUCH_TUNNEL;
                    next0 = GTPU_INPUT_NEXT_DROP;
                    goto trace00;
                  }
                last_key4.as_u64 = key4_0.as_u64;
                tunnel_index0 = last_tunnel_index = p0[0];
              }
            else
              tunnel_index0 = last_tunnel_index;
	    t0 = pool_elt_at_index (gtm->tunnels, tunnel_index0);

	    /* Validate GTPU tunnel encap-fib index against packet */
	    if (PREDICT_FALSE (validate_gtpu_fib (b0, t0, is_ip4) == 0))
	      {
		error0 = GTPU_ERROR_NO_SUCH_TUNNEL;
		next0 = GTPU_INPUT_NEXT_DROP;
		goto trace00;
	      }

	    /* Validate GTPU tunnel SIP against packet DIP */
	    if (PREDICT_TRUE (ip4_0->dst_address.as_u32 == t0->src.ip4.as_u32))
	      goto next00; /* valid packet */
	    if (PREDICT_FALSE (ip4_address_is_multicast (&ip4_0->dst_address)))
	      {
		key4_0.src = ip4_0->dst_address.as_u32;
		key4_0.teid = gtpu0->teid;
		/* Make sure mcast GTPU tunnel exist by packet DIP and teid */
		p0 = hash_get (gtm->gtpu4_tunnel_by_key, key4_0.as_u64);
		if (PREDICT_TRUE (p0 != NULL))
		  {
		    mt0 = pool_elt_at_index (gtm->tunnels, p0[0]);
		    goto next00; /* valid packet */
		  }
	      }
	    error0 = GTPU_ERROR_NO_SUCH_TUNNEL;
	    next0 = GTPU_INPUT_NEXT_DROP;
	    goto trace00;

          } else /* !is_ip4 */ {
            key6_0.src.as_u64[0] = ip6_0->src_address.as_u64[0];
            key6_0.src.as_u64[1] = ip6_0->src_address.as_u64[1];
            key6_0.teid = gtpu0->teid;

 	    /* Make sure GTPU tunnel exist according to packet SIP and teid
 	     * SIP identify a GTPU path, and teid identify a tunnel in a given GTPU path */
            if (PREDICT_FALSE (memcmp(&key6_0, &last_key6, sizeof(last_key6)) != 0))
              {
                p0 = hash_get_mem (gtm->gtpu6_tunnel_by_key, &key6_0);
                if (PREDICT_FALSE (p0 == NULL))
                  {
                    error0 = GTPU_ERROR_NO_SUCH_TUNNEL;
                    next0 = GTPU_INPUT_NEXT_DROP;
                    goto trace00;
                  }
                clib_memcpy_fast (&last_key6, &key6_0, sizeof(key6_0));
                tunnel_index0 = last_tunnel_index = p0[0];
              }
            else
              tunnel_index0 = last_tunnel_index;
	    t0 = pool_elt_at_index (gtm->tunnels, tunnel_index0);

	    /* Validate GTPU tunnel encap-fib index against packet */
	    if (PREDICT_FALSE (validate_gtpu_fib (b0, t0, is_ip4) == 0))
	      {
		error0 = GTPU_ERROR_NO_SUCH_TUNNEL;
		next0 = GTPU_INPUT_NEXT_DROP;
		goto trace00;
	      }

	    /* Validate GTPU tunnel SIP against packet DIP */
	    if (PREDICT_TRUE (ip6_address_is_equal (&ip6_0->dst_address,
						    &t0->src.ip6)))
		goto next00; /* valid packet */
	    if (PREDICT_FALSE (ip6_address_is_multicast (&ip6_0->dst_address)))
	      {
		key6_0.src.as_u64[0] = ip6_0->dst_address.as_u64[0];
		key6_0.src.as_u64[1] = ip6_0->dst_address.as_u64[1];
		key6_0.teid = gtpu0->teid;
		p0 = hash_get_mem (gtm->gtpu6_tunnel_by_key, &key6_0);
		if (PREDICT_TRUE (p0 != NULL))
		  {
		    mt0 = pool_elt_at_index (gtm->tunnels, p0[0]);
		    goto next00; /* valid packet */
		  }
	      }
	    error0 = GTPU_ERROR_NO_SUCH_TUNNEL;
	    next0 = GTPU_INPUT_NEXT_DROP;
	    goto trace00;
          }

	next00:
	  /* Pop gtpu header */
	  vlib_buffer_advance (b0, gtpu_hdr_len0);

	  next0 = t0->decap_next_index;
	  sw_if_index0 = t0->sw_if_index;
	  len0 = vlib_buffer_length_in_chain (vm, b0);

          /* Required to make the l2 tag push / pop code work on l2 subifs */
          if (PREDICT_TRUE(next0 == GTPU_INPUT_NEXT_L2_INPUT))
            vnet_update_l2_len (b0);

          /* Set packet input sw_if_index to unicast GTPU tunnel for learning */
          vnet_buffer(b0)->sw_if_index[VLIB_RX] = sw_if_index0;
	  sw_if_index0 = (mt0) ? mt0->sw_if_index : sw_if_index0;

          pkts_decapsulated ++;
          stats_n_packets += 1;
          stats_n_bytes += len0;

	  /* Batch stats increment on the same gtpu tunnel so counter
	     is not incremented per packet */
	  if (PREDICT_FALSE (sw_if_index0 != stats_sw_if_index))
	    {
	      stats_n_packets -= 1;
	      stats_n_bytes -= len0;
	      if (stats_n_packets)
		vlib_increment_combined_counter
		  (im->combined_sw_if_counters + VNET_INTERFACE_COUNTER_RX,
		   thread_index, stats_sw_if_index,
		   stats_n_packets, stats_n_bytes);
	      stats_n_packets = 1;
	      stats_n_bytes = len0;
	      stats_sw_if_index = sw_if_index0;
	    }

        trace00:
          b0->error = error0 ? node->errors[error0] : 0;

          if (PREDICT_FALSE(b0->flags & VLIB_BUFFER_IS_TRACED))
            {
              gtpu_rx_trace_t *tr
                = vlib_add_trace (vm, node, b0, sizeof (*tr));
              tr->next_index = next0;
              tr->error = error0;
              tr->tunnel_index = tunnel_index0;
              tr->teid = has_space0 ? clib_net_to_host_u32(gtpu0->teid) : ~0;
            }
	  vlib_validate_buffer_enqueue_x1 (vm, node, next_index,
					   to_next, n_left_to_next,
					   bi0, next0);
	}

      vlib_put_next_frame (vm, node, next_index, n_left_to_next);
    }
  /* Do we still need this now that tunnel tx stats is kept? */
  vlib_node_increment_counter (vm, is_ip4?
			       gtpu4_input_node.index:gtpu6_input_node.index,
                               GTPU_ERROR_DECAPSULATED,
                               pkts_decapsulated);

  /* Increment any remaining batch stats */
  if (stats_n_packets)
    {
      vlib_increment_combined_counter
	(im->combined_sw_if_counters + VNET_INTERFACE_COUNTER_RX,
	 thread_index, stats_sw_if_index, stats_n_packets, stats_n_bytes);
      node->runtime_data[0] = stats_sw_if_index;
    }

  return from_frame->n_vectors;
}

VLIB_NODE_FN (gtpu4_input_node) (vlib_main_t * vm,
             vlib_node_runtime_t * node,
             vlib_frame_t * from_frame)
{
	return gtpu_input(vm, node, from_frame, /* is_ip4 */ 1);
}

VLIB_NODE_FN (gtpu6_input_node) (vlib_main_t * vm,
             vlib_node_runtime_t * node,
             vlib_frame_t * from_frame)
{
	return gtpu_input(vm, node, from_frame, /* is_ip4 */ 0);
}

static char * gtpu_error_strings[] = {
#define gtpu_error(n,s) s,
#include <gtpu/gtpu_error.def>
#undef gtpu_error
#undef _
};

VLIB_REGISTER_NODE (gtpu4_input_node) = {
  .name = "gtpu4-input",
  /* Takes a vector of packets. */
  .vector_size = sizeof (u32),

  .n_errors = GTPU_N_ERROR,
  .error_strings = gtpu_error_strings,

  .n_next_nodes = GTPU_INPUT_N_NEXT,
  .next_nodes = {
#define _(s,n) [GTPU_INPUT_NEXT_##s] = n,
    foreach_gtpu_input_next
#undef _
  },

//temp  .format_buffer = format_gtpu_header,
  .format_trace = format_gtpu_rx_trace,
  // $$$$ .unformat_buffer = unformat_gtpu_header,
};

VLIB_REGISTER_NODE (gtpu6_input_node) = {
  .name = "gtpu6-input",
  /* Takes a vector of packets. */
  .vector_size = sizeof (u32),

  .n_errors = GTPU_N_ERROR,
  .error_strings = gtpu_error_strings,

  .n_next_nodes = GTPU_INPUT_N_NEXT,
  .next_nodes = {
#define _(s,n) [GTPU_INPUT_NEXT_##s] = n,
    foreach_gtpu_input_next
#undef _
  },

//temp  .format_buffer = format_gtpu_header,
  .format_trace = format_gtpu_rx_trace,
  // $$$$ .unformat_buffer = unformat_gtpu_header,
};

typedef enum {
  IP_GTPU_BYPASS_NEXT_DROP,
  IP_GTPU_BYPASS_NEXT_GTPU,
  IP_GTPU_BYPASS_N_NEXT,
} ip_vxan_bypass_next_t;

always_inline uword
ip_gtpu_bypass_inline (vlib_main_t * vm,
			vlib_node_runtime_t * node,
			vlib_frame_t * frame,
			u32 is_ip4)
{
  gtpu_main_t * gtm = &gtpu_main;
  u32 * from, * to_next, n_left_from, n_left_to_next, next_index;
  vlib_node_runtime_t * error_node = vlib_node_get_runtime (vm, ip4_input_node.index);
  vtep4_key_t last_vtep4;	/* last IPv4 address / fib index
				   matching a local VTEP address */
  vtep6_key_t last_vtep6;	/* last IPv6 address / fib index
				   matching a local VTEP address */
  vlib_buffer_t *bufs[VLIB_FRAME_SIZE], **b = bufs;
#ifdef CLIB_HAVE_VEC512
  vtep4_cache_t vtep4_u512;
  clib_memset (&vtep4_u512, 0, sizeof (vtep4_u512));
#endif

  from = vlib_frame_vector_args (frame);
  n_left_from = frame->n_vectors;
  next_index = node->cached_next_index;
  vlib_get_buffers (vm, from, bufs, n_left_from);

  if (node->flags & VLIB_NODE_FLAG_TRACE)
    ip4_forward_next_trace (vm, node, frame, VLIB_TX);

  if (is_ip4)
    vtep4_key_init (&last_vtep4);
  else
    vtep6_key_init (&last_vtep6);

  while (n_left_from > 0)
    {
      vlib_get_next_frame (vm, node, next_index, to_next, n_left_to_next);

      while (n_left_from >= 4 && n_left_to_next >= 2)
      	{
      	  vlib_buffer_t * b0, * b1;
      	  ip4_header_t * ip40, * ip41;
      	  ip6_header_t * ip60, * ip61;
      	  udp_header_t * udp0, * udp1;
      	  u32 bi0, ip_len0, udp_len0, flags0, next0;
      	  u32 bi1, ip_len1, udp_len1, flags1, next1;
      	  i32 len_diff0, len_diff1;
      	  u8 error0, good_udp0, proto0;
      	  u8 error1, good_udp1, proto1;

	  /* Prefetch next iteration. */
	  {
	    vlib_prefetch_buffer_header (b[2], LOAD);
	    vlib_prefetch_buffer_header (b[3], LOAD);

	    CLIB_PREFETCH (b[2]->data, 2*CLIB_CACHE_LINE_BYTES, LOAD);
	    CLIB_PREFETCH (b[3]->data, 2*CLIB_CACHE_LINE_BYTES, LOAD);
	  }

      	  bi0 = to_next[0] = from[0];
      	  bi1 = to_next[1] = from[1];
      	  from += 2;
      	  n_left_from -= 2;
      	  to_next += 2;
      	  n_left_to_next -= 2;

	  b0 = b[0];
	  b1 = b[1];
	  b += 2;
	  if (is_ip4)
	    {
	      ip40 = vlib_buffer_get_current (b0);
	      ip41 = vlib_buffer_get_current (b1);
	    }
	  else
	    {
	      ip60 = vlib_buffer_get_current (b0);
	      ip61 = vlib_buffer_get_current (b1);
	    }

	  /* Setup packet for next IP feature */
	  vnet_feature_next(&next0, b0);
	  vnet_feature_next(&next1, b1);

	  if (is_ip4)
	    {
	      /* Treat IP frag packets as "experimental" protocol for now
		 until support of IP frag reassembly is implemented */
	      proto0 = ip4_is_fragment(ip40) ? 0xfe : ip40->protocol;
	      proto1 = ip4_is_fragment(ip41) ? 0xfe : ip41->protocol;
	    }
	  else
	    {
	      proto0 = ip60->protocol;
	      proto1 = ip61->protocol;
	    }

	  /* Process packet 0 */
	  if (proto0 != IP_PROTOCOL_UDP)
	    goto exit0; /* not UDP packet */

	  if (is_ip4)
	    udp0 = ip4_next_header (ip40);
	  else
	    udp0 = ip6_next_header (ip60);

	  if (udp0->dst_port != clib_host_to_net_u16 (UDP_DST_PORT_GTPU))
	    goto exit0; /* not GTPU packet */

	  /* Validate DIP against VTEPs*/
	  if (is_ip4)
	    {
#ifdef CLIB_HAVE_VEC512
             if (!vtep4_check_vector
                 (&gtm->vtep_table, b0, ip40, &last_vtep4, &vtep4_u512))
#else
	      if (!vtep4_check (&gtm->vtep_table, b0, ip40, &last_vtep4))
#endif
		goto exit0;	/* no local VTEP for GTPU packet */
	    }
	  else
	    {
	      if (!vtep6_check (&gtm->vtep_table, b0, ip60, &last_vtep6))
		goto exit0;	/* no local VTEP for GTPU packet */
	    }

	  flags0 = b0->flags;
	  good_udp0 = (flags0 & VNET_BUFFER_F_L4_CHECKSUM_CORRECT) != 0;

	  /* Don't verify UDP checksum for packets with explicit zero checksum. */
	  good_udp0 |= udp0->checksum == 0;

	  /* Verify UDP length */
	  if (is_ip4)
	    ip_len0 = clib_net_to_host_u16 (ip40->length);
	  else
	    ip_len0 = clib_net_to_host_u16 (ip60->payload_length);
	  udp_len0 = clib_net_to_host_u16 (udp0->length);
	  len_diff0 = ip_len0 - udp_len0;

	  /* Verify UDP checksum */
	  if (PREDICT_FALSE (!good_udp0))
	    {
	      if ((flags0 & VNET_BUFFER_F_L4_CHECKSUM_COMPUTED) == 0)
	        {
		  if (is_ip4)
		    flags0 = ip4_tcp_udp_validate_checksum (vm, b0);
		  else
		    flags0 = ip6_tcp_udp_icmp_validate_checksum (vm, b0);
		  good_udp0 =
		    (flags0 & VNET_BUFFER_F_L4_CHECKSUM_CORRECT) != 0;
	        }
	    }

	  if (is_ip4)
	    {
	      error0 = good_udp0 ? 0 : IP4_ERROR_UDP_CHECKSUM;
	      error0 = (len_diff0 >= 0) ? error0 : IP4_ERROR_UDP_LENGTH;
	    }
	  else
	    {
	      error0 = good_udp0 ? 0 : IP6_ERROR_UDP_CHECKSUM;
	      error0 = (len_diff0 >= 0) ? error0 : IP6_ERROR_UDP_LENGTH;
	    }

	  next0 = error0 ?
	    IP_GTPU_BYPASS_NEXT_DROP : IP_GTPU_BYPASS_NEXT_GTPU;
	  b0->error = error0 ? error_node->errors[error0] : 0;

	  /* gtpu-input node expect current at GTPU header */
	  if (is_ip4)
	    vlib_buffer_advance (b0, sizeof(ip4_header_t)+sizeof(udp_header_t));
	  else
	    vlib_buffer_advance (b0, sizeof(ip6_header_t)+sizeof(udp_header_t));

	exit0:
	  /* Process packet 1 */
	  if (proto1 != IP_PROTOCOL_UDP)
	    goto exit1; /* not UDP packet */

	  if (is_ip4)
	    udp1 = ip4_next_header (ip41);
	  else
	    udp1 = ip6_next_header (ip61);

	  if (udp1->dst_port != clib_host_to_net_u16 (UDP_DST_PORT_GTPU))
	    goto exit1; /* not GTPU packet */

	  /* Validate DIP against VTEPs*/
	  if (is_ip4)
	    {
#ifdef CLIB_HAVE_VEC512
             if (!vtep4_check_vector
                 (&gtm->vtep_table, b1, ip41, &last_vtep4, &vtep4_u512))
#else
              if (!vtep4_check (&gtm->vtep_table, b1, ip41, &last_vtep4))
#endif
                goto exit1;	/* no local VTEP for GTPU packet */
	    }
	  else
	    {
              if (!vtep6_check (&gtm->vtep_table, b1, ip61, &last_vtep6))
                goto exit1;	/* no local VTEP for GTPU packet */
	    }

	  flags1 = b1->flags;
	  good_udp1 = (flags1 & VNET_BUFFER_F_L4_CHECKSUM_CORRECT) != 0;

	  /* Don't verify UDP checksum for packets with explicit zero checksum. */
	  good_udp1 |= udp1->checksum == 0;

	  /* Verify UDP length */
	  if (is_ip4)
	    ip_len1 = clib_net_to_host_u16 (ip41->length);
	  else
	    ip_len1 = clib_net_to_host_u16 (ip61->payload_length);
	  udp_len1 = clib_net_to_host_u16 (udp1->length);
	  len_diff1 = ip_len1 - udp_len1;

	  /* Verify UDP checksum */
	  if (PREDICT_FALSE (!good_udp1))
	    {
	      if ((flags1 & VNET_BUFFER_F_L4_CHECKSUM_COMPUTED) == 0)
	        {
		  if (is_ip4)
		    flags1 = ip4_tcp_udp_validate_checksum (vm, b1);
		  else
		    flags1 = ip6_tcp_udp_icmp_validate_checksum (vm, b1);
		  good_udp1 =
		    (flags1 & VNET_BUFFER_F_L4_CHECKSUM_CORRECT) != 0;
	        }
	    }

	  if (is_ip4)
	    {
	      error1 = good_udp1 ? 0 : IP4_ERROR_UDP_CHECKSUM;
	      error1 = (len_diff1 >= 0) ? error1 : IP4_ERROR_UDP_LENGTH;
	    }
	  else
	    {
	      error1 = good_udp1 ? 0 : IP6_ERROR_UDP_CHECKSUM;
	      error1 = (len_diff1 >= 0) ? error1 : IP6_ERROR_UDP_LENGTH;
	    }

	  next1 = error1 ?
	    IP_GTPU_BYPASS_NEXT_DROP : IP_GTPU_BYPASS_NEXT_GTPU;
	  b1->error = error1 ? error_node->errors[error1] : 0;

	  /* gtpu-input node expect current at GTPU header */
	  if (is_ip4)
	    vlib_buffer_advance (b1, sizeof(ip4_header_t)+sizeof(udp_header_t));
	  else
	    vlib_buffer_advance (b1, sizeof(ip6_header_t)+sizeof(udp_header_t));

	exit1:
	  vlib_validate_buffer_enqueue_x2 (vm, node, next_index,
					   to_next, n_left_to_next,
					   bi0, bi1, next0, next1);
	}

      while (n_left_from > 0 && n_left_to_next > 0)
	{
	  vlib_buffer_t * b0;
	  ip4_header_t * ip40;
	  ip6_header_t * ip60;
	  udp_header_t * udp0;
      	  u32 bi0, ip_len0, udp_len0, flags0, next0;
	  i32 len_diff0;
	  u8 error0, good_udp0, proto0;

	  bi0 = to_next[0] = from[0];
	  from += 1;
	  n_left_from -= 1;
	  to_next += 1;
	  n_left_to_next -= 1;

	  b0 = b[0];
	  b++;
	  if (is_ip4)
	    ip40 = vlib_buffer_get_current (b0);
	  else
	    ip60 = vlib_buffer_get_current (b0);

	  /* Setup packet for next IP feature */
	  vnet_feature_next(&next0, b0);

	  if (is_ip4)
	    /* Treat IP4 frag packets as "experimental" protocol for now
	       until support of IP frag reassembly is implemented */
	    proto0 = ip4_is_fragment(ip40) ? 0xfe : ip40->protocol;
	  else
	    proto0 = ip60->protocol;

	  if (proto0 != IP_PROTOCOL_UDP)
	    goto exit; /* not UDP packet */

	  if (is_ip4)
	    udp0 = ip4_next_header (ip40);
	  else
	    udp0 = ip6_next_header (ip60);

	  if (udp0->dst_port != clib_host_to_net_u16 (UDP_DST_PORT_GTPU))
	    goto exit; /* not GTPU packet */

	  /* Validate DIP against VTEPs*/
	  if (is_ip4)
	    {
#ifdef CLIB_HAVE_VEC512
             if (!vtep4_check_vector
                 (&gtm->vtep_table, b0, ip40, &last_vtep4, &vtep4_u512))
#else
              if (!vtep4_check (&gtm->vtep_table, b0, ip40, &last_vtep4))
#endif
                goto exit;	/* no local VTEP for GTPU packet */
	    }
	  else
	    {
              if (!vtep6_check (&gtm->vtep_table, b0, ip60, &last_vtep6))
                goto exit;	/* no local VTEP for GTPU packet */
	    }

	  flags0 = b0->flags;
	  good_udp0 = (flags0 & VNET_BUFFER_F_L4_CHECKSUM_CORRECT) != 0;

	  /* Don't verify UDP checksum for packets with explicit zero checksum. */
	  good_udp0 |= udp0->checksum == 0;

	  /* Verify UDP length */
	  if (is_ip4)
	    ip_len0 = clib_net_to_host_u16 (ip40->length);
	  else
	    ip_len0 = clib_net_to_host_u16 (ip60->payload_length);
	  udp_len0 = clib_net_to_host_u16 (udp0->length);
	  len_diff0 = ip_len0 - udp_len0;

	  /* Verify UDP checksum */
	  if (PREDICT_FALSE (!good_udp0))
	    {
	      if ((flags0 & VNET_BUFFER_F_L4_CHECKSUM_COMPUTED) == 0)
	        {
		  if (is_ip4)
		    flags0 = ip4_tcp_udp_validate_checksum (vm, b0);
		  else
		    flags0 = ip6_tcp_udp_icmp_validate_checksum (vm, b0);
		  good_udp0 =
		    (flags0 & VNET_BUFFER_F_L4_CHECKSUM_CORRECT) != 0;
	        }
	    }

	  if (is_ip4)
	    {
	      error0 = good_udp0 ? 0 : IP4_ERROR_UDP_CHECKSUM;
	      error0 = (len_diff0 >= 0) ? error0 : IP4_ERROR_UDP_LENGTH;
	    }
	  else
	    {
	      error0 = good_udp0 ? 0 : IP6_ERROR_UDP_CHECKSUM;
	      error0 = (len_diff0 >= 0) ? error0 : IP6_ERROR_UDP_LENGTH;
	    }

	  next0 = error0 ?
	    IP_GTPU_BYPASS_NEXT_DROP : IP_GTPU_BYPASS_NEXT_GTPU;
	  b0->error = error0 ? error_node->errors[error0] : 0;

	  /* gtpu-input node expect current at GTPU header */
	  if (is_ip4)
	    vlib_buffer_advance (b0, sizeof(ip4_header_t)+sizeof(udp_header_t));
	  else
	    vlib_buffer_advance (b0, sizeof(ip6_header_t)+sizeof(udp_header_t));

	exit:
	  vlib_validate_buffer_enqueue_x1 (vm, node, next_index,
					   to_next, n_left_to_next,
					   bi0, next0);
	}

      vlib_put_next_frame (vm, node, next_index, n_left_to_next);
    }

  return frame->n_vectors;
}

VLIB_NODE_FN (ip4_gtpu_bypass_node) (vlib_main_t * vm,
		  vlib_node_runtime_t * node,
		  vlib_frame_t * frame)
{
  return ip_gtpu_bypass_inline (vm, node, frame, /* is_ip4 */ 1);
}

VLIB_REGISTER_NODE (ip4_gtpu_bypass_node) = {
  .name = "ip4-gtpu-bypass",
  .vector_size = sizeof (u32),

  .n_next_nodes = IP_GTPU_BYPASS_N_NEXT,
  .next_nodes = {
    [IP_GTPU_BYPASS_NEXT_DROP] = "error-drop",
    [IP_GTPU_BYPASS_NEXT_GTPU] = "gtpu4-input",
  },

  .format_buffer = format_ip4_header,
  .format_trace = format_ip4_forward_next_trace,
};

#ifndef CLIB_MARCH_VARIANT
/* Dummy init function to get us linked in. */
clib_error_t * ip4_gtpu_bypass_init (vlib_main_t * vm)
{ return 0; }

VLIB_INIT_FUNCTION (ip4_gtpu_bypass_init);
#endif /* CLIB_MARCH_VARIANT */

VLIB_NODE_FN (ip6_gtpu_bypass_node) (vlib_main_t * vm,
		  vlib_node_runtime_t * node,
		  vlib_frame_t * frame)
{
  return ip_gtpu_bypass_inline (vm, node, frame, /* is_ip4 */ 0);
}

VLIB_REGISTER_NODE (ip6_gtpu_bypass_node) = {
  .name = "ip6-gtpu-bypass",
  .vector_size = sizeof (u32),

  .n_next_nodes = IP_GTPU_BYPASS_N_NEXT,
  .next_nodes = {
    [IP_GTPU_BYPASS_NEXT_DROP] = "error-drop",
    [IP_GTPU_BYPASS_NEXT_GTPU] = "gtpu6-input",
  },

  .format_buffer = format_ip6_header,
  .format_trace = format_ip6_forward_next_trace,
};

#ifndef CLIB_MARCH_VARIANT
/* Dummy init function to get us linked in. */
clib_error_t * ip6_gtpu_bypass_init (vlib_main_t * vm)
{ return 0; }

VLIB_INIT_FUNCTION (ip6_gtpu_bypass_init);

#define foreach_gtpu_flow_error					\
  _(NONE, "no error")							\
  _(PAYLOAD_ERROR, "Payload type errors")							\
  _(IP_CHECKSUM_ERROR, "Rx ip checksum errors")				\
  _(IP_HEADER_ERROR, "Rx ip header errors")				\
  _(UDP_CHECKSUM_ERROR, "Rx udp checksum errors")				\
  _(UDP_LENGTH_ERROR, "Rx udp length errors")

typedef enum
{
#define _(f,s) GTPU_FLOW_ERROR_##f,
  foreach_gtpu_flow_error
#undef _
#define gtpu_error(n,s) GTPU_FLOW_ERROR_##n,
#include <gtpu/gtpu_error.def>
#undef gtpu_error
    GTPU_FLOW_N_ERROR,
} gtpu_flow_error_t;

static char *gtpu_flow_error_strings[] = {
#define _(n,s) s,
  foreach_gtpu_flow_error
#undef _
#define gtpu_error(n,s) s,
#include <gtpu/gtpu_error.def>
#undef gtpu_error
#undef _

};

#define gtpu_local_need_csum_check(_b) 			\
    (!(_b->flags & VNET_BUFFER_F_L4_CHECKSUM_COMPUTED 	\
	|| _b->flags & VNET_BUFFER_F_OFFLOAD_UDP_CKSUM))

#define gtpu_local_csum_is_valid(_b)  \
    ((_b->flags & VNET_BUFFER_F_L4_CHECKSUM_CORRECT \
	|| _b->flags & VNET_BUFFER_F_OFFLOAD_UDP_CKSUM) != 0)

static_always_inline u8
gtpu_validate_udp_csum (vlib_main_t * vm, vlib_buffer_t *b)
{
  u32 flags = b->flags;
  enum { offset = sizeof(ip4_header_t) + sizeof(udp_header_t)};

  /* Verify UDP checksum */
  if ((flags & VNET_BUFFER_F_L4_CHECKSUM_COMPUTED) == 0)
  {
    vlib_buffer_advance (b, -offset);
    flags = ip4_tcp_udp_validate_checksum (vm, b);
    vlib_buffer_advance (b, offset);
  }

  return (flags & VNET_BUFFER_F_L4_CHECKSUM_CORRECT) != 0;
}

static_always_inline u8
gtpu_check_ip (vlib_buffer_t *b, u16 payload_len)
{
  ip4_header_t * ip4_hdr = vlib_buffer_get_current(b) - 
      sizeof(ip4_header_t) - sizeof(udp_header_t);
  u16 ip_len = clib_net_to_host_u16 (ip4_hdr->length);
  u16 expected = payload_len + sizeof(ip4_header_t) + sizeof(udp_header_t);
  return ip_len > expected || ip4_hdr->ttl == 0 || ip4_hdr->ip_version_and_header_length != 0x45;
}

static_always_inline u8
gtpu_check_ip_udp_len (vlib_buffer_t *b)
{
  ip4_header_t * ip4_hdr = vlib_buffer_get_current(b) - 
      sizeof(ip4_header_t) - sizeof(udp_header_t);
  udp_header_t * udp_hdr = vlib_buffer_get_current(b) - sizeof(udp_header_t);
  u16 ip_len = clib_net_to_host_u16 (ip4_hdr->length);
  u16 udp_len = clib_net_to_host_u16 (udp_hdr->length);
  return udp_len > ip_len;
}

static_always_inline u8
gtpu_err_code (u8 ip_err0, u8 udp_err0, u8 csum_err0)
{
  u8 error0 = GTPU_FLOW_ERROR_NONE;
  if (ip_err0)
    error0 =  GTPU_FLOW_ERROR_IP_HEADER_ERROR;
  if (udp_err0)
    error0 =  GTPU_FLOW_ERROR_UDP_LENGTH_ERROR;
  if (csum_err0)
    error0 =  GTPU_FLOW_ERROR_UDP_CHECKSUM_ERROR;
  return error0;
}


always_inline uword
gtpu_flow_input (vlib_main_t * vm,
             vlib_node_runtime_t * node,
             vlib_frame_t * from_frame)
{
  u32 n_left_from, next_index, * from, * to_next;
  gtpu_main_t * gtm = &gtpu_main;
  vnet_main_t * vnm = gtm->vnet_main;
  vnet_interface_main_t * im = &vnm->interface_main;
  u32 pkts_decapsulated = 0;
  u32 thread_index = vlib_get_thread_index();
  u32 stats_sw_if_index, stats_n_packets, stats_n_bytes;
  u8 ip_err0, ip_err1, udp_err0, udp_err1, csum_err0, csum_err1;

  from = vlib_frame_vector_args (from_frame);
  n_left_from = from_frame->n_vectors;

  next_index = node->cached_next_index;
  stats_sw_if_index = node->runtime_data[0];
  stats_n_packets = stats_n_bytes = 0;

  while (n_left_from > 0)
    {
      u32 n_left_to_next;

      vlib_get_next_frame (vm, node, next_index,
			   to_next, n_left_to_next);

      while (n_left_from >= 4 && n_left_to_next >= 2)
        {
          u32 bi0, bi1;
      	  vlib_buffer_t * b0, * b1;
      	  u32 next0, next1;
          gtpu_header_t * gtpu0, * gtpu1;
          u32 gtpu_hdr_len0, gtpu_hdr_len1;
          u32 tunnel_index0, tunnel_index1;
          gtpu_tunnel_t * t0, * t1;
          u32 error0, error1;
	        u32 sw_if_index0, sw_if_index1, len0, len1;
          u8 has_space0 = 0, has_space1 = 0;
          u8 ver0, ver1;

      	  /* Prefetch next iteration. */
      	  {
      	    vlib_buffer_t * p2, * p3;

      	    p2 = vlib_get_buffer (vm, from[2]);
      	    p3 = vlib_get_buffer (vm, from[3]);

      	    vlib_prefetch_buffer_header (p2, LOAD);
      	    vlib_prefetch_buffer_header (p3, LOAD);

      	    CLIB_PREFETCH (p2->data, 2*CLIB_CACHE_LINE_BYTES, LOAD);
      	    CLIB_PREFETCH (p3->data, 2*CLIB_CACHE_LINE_BYTES, LOAD);
      	  }

      	  bi0 = from[0];
      	  bi1 = from[1];
      	  to_next[0] = bi0;
      	  to_next[1] = bi1;
      	  from += 2;
      	  to_next += 2;
      	  n_left_to_next -= 2;
      	  n_left_from -= 2;

      	  b0 = vlib_get_buffer (vm, bi0);
      	  b1 = vlib_get_buffer (vm, bi1);

          /* udp leaves current_data pointing at the gtpu header */
          gtpu0 = vlib_buffer_get_current (b0);
          gtpu1 = vlib_buffer_get_current (b1);

          len0 = vlib_buffer_length_in_chain (vm, b0);
          len1 = vlib_buffer_length_in_chain (vm, b1);

          tunnel_index0 = ~0;
          error0 = 0;

          tunnel_index1 = ~0;
          error1 = 0;

      	  ip_err0 = gtpu_check_ip (b0, len0);
      	  udp_err0 = gtpu_check_ip_udp_len (b0);
      	  ip_err1 = gtpu_check_ip (b1, len1);
      	  udp_err1 = gtpu_check_ip_udp_len (b1);

          if (PREDICT_FALSE (gtpu_local_need_csum_check (b0)))
            csum_err0 = !gtpu_validate_udp_csum (vm, b0);
          else
            csum_err0 = !gtpu_local_csum_is_valid (b0);
          if (PREDICT_FALSE (gtpu_local_need_csum_check (b1)))
            csum_err1 = !gtpu_validate_udp_csum (vm, b1);
          else
            csum_err1 = !gtpu_local_csum_is_valid (b1);

      	  if (ip_err0 || udp_err0 || csum_err0)
      	    {
      	      next0 = GTPU_INPUT_NEXT_DROP;
      	      error0 = gtpu_err_code (ip_err0, udp_err0, csum_err0);
      	      goto trace0;
      	    }

          /* speculatively load gtp header version field */
          ver0 = gtpu0->ver_flags;

	       /*
           * Manipulate gtpu header
           * TBD: Manipulate Sequence Number and N-PDU Number
           * TBD: Manipulate Next Extension Header
           */
          gtpu_hdr_len0 = sizeof(gtpu_header_t) - (((ver0 & GTPU_E_S_PN_BIT) == 0) * 4);

          has_space0 = vlib_buffer_has_space (b0, gtpu_hdr_len0);
      	  if (PREDICT_FALSE (((ver0 & GTPU_VER_MASK) != GTPU_V1_VER) | (!has_space0)))
      	    {
      	      error0 = has_space0 ? GTPU_ERROR_BAD_VER : GTPU_ERROR_TOO_SMALL;
      	      next0 = GTPU_INPUT_NEXT_DROP;
      	      goto trace0;
      	    }

	        /* Manipulate packet 0 */
          ASSERT (b0->flow_id != 0);
          tunnel_index0 = b0->flow_id - gtm->flow_id_start;
          t0 = pool_elt_at_index (gtm->tunnels, tunnel_index0);
  	      b0->flow_id = 0;

      	  /* Pop gtpu header */
      	  vlib_buffer_advance (b0, gtpu_hdr_len0);

          /* assign the next node */
          if (PREDICT_FALSE (t0->decap_next_index != GTPU_INPUT_NEXT_IP4_INPUT) &&
              (t0->decap_next_index != GTPU_INPUT_NEXT_IP6_INPUT))
          {
            error0 = GTPU_FLOW_ERROR_PAYLOAD_ERROR;
            next0 = GTPU_INPUT_NEXT_DROP;
            goto trace0;
          }
          next0 = t0->decap_next_index;

          sw_if_index0 = t0->sw_if_index;

          /* Set packet input sw_if_index to unicast GTPU tunnel for learning */
          vnet_buffer(b0)->sw_if_index[VLIB_RX] = sw_if_index0;

          pkts_decapsulated ++;
          stats_n_packets += 1;
          stats_n_bytes += len0;

          /* Batch stats increment on the same gtpu tunnel so counter
              is not incremented per packet */
          if (PREDICT_FALSE (sw_if_index0 != stats_sw_if_index))
            {
              stats_n_packets -= 1;
              stats_n_bytes -= len0;
              if (stats_n_packets)
        	    vlib_increment_combined_counter
        	     (im->combined_sw_if_counters + VNET_INTERFACE_COUNTER_RX,
        	       thread_index, stats_sw_if_index,
        	       stats_n_packets, stats_n_bytes);
              stats_n_packets = 1;
              stats_n_bytes = len0;
              stats_sw_if_index = sw_if_index0;
            }

trace0:
          b0->error = error0 ? node->errors[error0] : 0;

          if (PREDICT_FALSE(b0->flags & VLIB_BUFFER_IS_TRACED))
            {
              gtpu_rx_trace_t *tr
                = vlib_add_trace (vm, node, b0, sizeof (*tr));
              tr->next_index = next0;
              tr->error = error0;
              tr->tunnel_index = tunnel_index0;
              tr->teid = has_space0 ? clib_net_to_host_u32(gtpu0->teid) : ~0;
            }

      	  if (ip_err1 || udp_err1 || csum_err1)
      	    {
      	      next1 = GTPU_INPUT_NEXT_DROP;
      	      error1 = gtpu_err_code (ip_err1, udp_err1, csum_err1);
      	      goto trace1;
      	    }

          /* speculatively load gtp header version field */
      	  ver1 = gtpu1->ver_flags;

          /*
           * Manipulate gtpu header
           * TBD: Manipulate Sequence Number and N-PDU Number
           * TBD: Manipulate Next Extension Header
           */
          gtpu_hdr_len1 = sizeof(gtpu_header_t) - (((ver1 & GTPU_E_S_PN_BIT) == 0) * 4);
          has_space1 = vlib_buffer_has_space (b1, gtpu_hdr_len1);
	        if (PREDICT_FALSE (((ver1 & GTPU_VER_MASK) != GTPU_V1_VER) | (!has_space1)))
	          {
  	          error1 = has_space1 ? GTPU_ERROR_BAD_VER : GTPU_ERROR_TOO_SMALL;
  	          next1 = GTPU_INPUT_NEXT_DROP;
  	          goto trace1;
            }

          /* Manipulate packet 1 */
          ASSERT (b1->flow_id != 0);
          tunnel_index1 = b1->flow_id - gtm->flow_id_start;
          t1 = pool_elt_at_index (gtm->tunnels, tunnel_index1);
          b1->flow_id = 0;

      	  /* Pop gtpu header */
      	  vlib_buffer_advance (b1, gtpu_hdr_len1);

          /* assign the next node */
          if (PREDICT_FALSE (t1->decap_next_index != GTPU_INPUT_NEXT_IP4_INPUT) &&
            (t1->decap_next_index != GTPU_INPUT_NEXT_IP6_INPUT))
          {
            error1 = GTPU_FLOW_ERROR_PAYLOAD_ERROR;
            next1 = GTPU_INPUT_NEXT_DROP;
            goto trace1;
          }
          next1 = t1->decap_next_index;

          sw_if_index1 = t1->sw_if_index;

          /* Required to make the l2 tag push / pop code work on l2 subifs */
          /* This won't happen in current implementation as only 
              ipv4/udp/gtpu/IPV4 type packets can be matched */
          if (PREDICT_FALSE(next1 == GTPU_INPUT_NEXT_L2_INPUT))
            vnet_update_l2_len (b1);

          /* Set packet input sw_if_index to unicast GTPU tunnel for learning */
          vnet_buffer(b1)->sw_if_index[VLIB_RX] = sw_if_index1;

          pkts_decapsulated ++;
          stats_n_packets += 1;
          stats_n_bytes += len1;

      	  /* Batch stats increment on the same gtpu tunnel so counter
      	     is not incremented per packet */
    	    if (PREDICT_FALSE (sw_if_index1 != stats_sw_if_index))
      	    {
      	      stats_n_packets -= 1;
      	      stats_n_bytes -= len1;
      	      if (stats_n_packets)
      		      vlib_increment_combined_counter
      		       (im->combined_sw_if_counters + VNET_INTERFACE_COUNTER_RX,
      		        thread_index, stats_sw_if_index,
      		        stats_n_packets, stats_n_bytes);
      	      stats_n_packets = 1;
      	      stats_n_bytes = len1;
      	      stats_sw_if_index = sw_if_index1;
      	    }

trace1:
          b1->error = error1 ? node->errors[error1] : 0;

          if (PREDICT_FALSE(b1->flags & VLIB_BUFFER_IS_TRACED))
            {
              gtpu_rx_trace_t *tr
                = vlib_add_trace (vm, node, b1, sizeof (*tr));
              tr->next_index = next1;
              tr->error = error1;
              tr->tunnel_index = tunnel_index1;
              tr->teid = has_space1 ? clib_net_to_host_u32(gtpu1->teid) : ~0;
            }

    	    vlib_validate_buffer_enqueue_x2 (vm, node, next_index,
    					   to_next, n_left_to_next,
    					   bi0, bi1, next0, next1);
        }

      while (n_left_from > 0 && n_left_to_next > 0)
        {
          u32 bi0;
          vlib_buffer_t * b0;
          u32 next0;
          gtpu_header_t * gtpu0;
          u32 gtpu_hdr_len0;
          u32 error0;
          u32 tunnel_index0;
          gtpu_tunnel_t * t0;
          u32 sw_if_index0, len0;
          u8 has_space0 = 0;
          u8 ver0;

          bi0 = from[0];
          to_next[0] = bi0;
          from += 1;
          to_next += 1;
          n_left_from -= 1;
          n_left_to_next -= 1;

          b0 = vlib_get_buffer (vm, bi0);
          len0 = vlib_buffer_length_in_chain (vm, b0);

          tunnel_index0 = ~0;
          error0 = 0;

      	  ip_err0 = gtpu_check_ip (b0, len0);
      	  udp_err0 = gtpu_check_ip_udp_len (b0);
          if (PREDICT_FALSE (gtpu_local_need_csum_check (b0)))
            csum_err0 = !gtpu_validate_udp_csum (vm, b0);
          else
            csum_err0 = !gtpu_local_csum_is_valid (b0);

      	  if (ip_err0 || udp_err0 || csum_err0)
      	    {
      	      next0 = GTPU_INPUT_NEXT_DROP;
      	      error0 = gtpu_err_code (ip_err0, udp_err0, csum_err0);
      	      goto trace00;
      	    }

          /* udp leaves current_data pointing at the gtpu header */
          gtpu0 = vlib_buffer_get_current (b0);

          /* speculatively load gtp header version field */
          ver0 = gtpu0->ver_flags;

          /*
           * Manipulate gtpu header
           * TBD: Manipulate Sequence Number and N-PDU Number
           * TBD: Manipulate Next Extension Header
           */
          gtpu_hdr_len0 = sizeof(gtpu_header_t) - (((ver0 & GTPU_E_S_PN_BIT) == 0) * 4);

          has_space0 = vlib_buffer_has_space (b0, gtpu_hdr_len0);
          if (PREDICT_FALSE (((ver0 & GTPU_VER_MASK) != GTPU_V1_VER) | (!has_space0)))
            {
               error0 = has_space0 ? GTPU_ERROR_BAD_VER : GTPU_ERROR_TOO_SMALL;
               next0 = GTPU_INPUT_NEXT_DROP;
               goto trace00;
            }

          ASSERT (b0->flow_id != 0);
          tunnel_index0 = b0->flow_id - gtm->flow_id_start;
          t0 = pool_elt_at_index (gtm->tunnels, tunnel_index0);
  	      b0->flow_id = 0;

      	  /* Pop gtpu header */
      	  vlib_buffer_advance (b0, gtpu_hdr_len0);

          /* assign the next node */
          if (PREDICT_FALSE (t0->decap_next_index != GTPU_INPUT_NEXT_IP4_INPUT) &&
              (t0->decap_next_index != GTPU_INPUT_NEXT_IP6_INPUT))
          {
            error0 = GTPU_FLOW_ERROR_PAYLOAD_ERROR;
            next0 = GTPU_INPUT_NEXT_DROP;
            goto trace00;
          }
          next0 = t0->decap_next_index;

          sw_if_index0 = t0->sw_if_index;

          /* Set packet input sw_if_index to unicast GTPU tunnel for learning */
          vnet_buffer(b0)->sw_if_index[VLIB_RX] = sw_if_index0;

          pkts_decapsulated ++;
          stats_n_packets += 1;
          stats_n_bytes += len0;

         /* Batch stats increment on the same gtpu tunnel so counter
            is not incremented per packet */
          if (PREDICT_FALSE (sw_if_index0 != stats_sw_if_index))
            {
              stats_n_packets -= 1;
              stats_n_bytes -= len0;
              if (stats_n_packets)
        	    vlib_increment_combined_counter
            	  (im->combined_sw_if_counters + VNET_INTERFACE_COUNTER_RX,
            	   thread_index, stats_sw_if_index,
            	   stats_n_packets, stats_n_bytes);
              stats_n_packets = 1;
              stats_n_bytes = len0;
              stats_sw_if_index = sw_if_index0;
            }
  trace00:
            b0->error = error0 ? node->errors[error0] : 0;

            if (PREDICT_FALSE(b0->flags & VLIB_BUFFER_IS_TRACED))
              {
                gtpu_rx_trace_t *tr
                  = vlib_add_trace (vm, node, b0, sizeof (*tr));
                tr->next_index = next0;
                tr->error = error0;
                tr->tunnel_index = tunnel_index0;
                tr->teid = has_space0 ? clib_net_to_host_u32(gtpu0->teid) : ~0;
              }
      	    vlib_validate_buffer_enqueue_x1 (vm, node, next_index,
      					   to_next, n_left_to_next,
      					   bi0, next0);
      	}

      vlib_put_next_frame (vm, node, next_index, n_left_to_next);
    }

    /* Do we still need this now that tunnel tx stats is kept? */
    vlib_node_increment_counter (vm, gtpu4_flow_input_node.index,
                               GTPU_ERROR_DECAPSULATED,
                               pkts_decapsulated);

    /* Increment any remaining batch stats */
    if (stats_n_packets)
      {
        vlib_increment_combined_counter
    	(im->combined_sw_if_counters + VNET_INTERFACE_COUNTER_RX,
    	 thread_index, stats_sw_if_index, stats_n_packets, stats_n_bytes);
        node->runtime_data[0] = stats_sw_if_index;
      }

    return from_frame->n_vectors;
}

VLIB_NODE_FN (gtpu4_flow_input_node) (vlib_main_t * vm,
             vlib_node_runtime_t * node,
             vlib_frame_t * from_frame)
{
	return gtpu_flow_input(vm, node, from_frame);
}


/* *INDENT-OFF* */
#ifndef CLIB_MULTIARCH_VARIANT
VLIB_REGISTER_NODE (gtpu4_flow_input_node) = {
  .name = "gtpu4-flow-input",
  .type = VLIB_NODE_TYPE_INTERNAL,
  .vector_size = sizeof (u32),

  .format_trace = format_gtpu_rx_trace,

  .n_errors = GTPU_FLOW_N_ERROR,
  .error_strings = gtpu_flow_error_strings,

  .n_next_nodes = GTPU_INPUT_N_NEXT,
  .next_nodes = {
#define _(s,n) [GTPU_INPUT_NEXT_##s] = n,
    foreach_gtpu_input_next
#undef _

  },
};
#endif
/* *INDENT-ON* */

#endif /* CLIB_MARCH_VARIANT */