VPP-323: Add LISP locator set index to reply API message
[vpp.git] / vnet / vnet / lisp-gpe / lisp_gpe.c
1 /*
2  * Copyright (c) 2016 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 #include <vnet/lisp-gpe/lisp_gpe.h>
17 #include <vppinfra/math.h>
18
19 lisp_gpe_main_t lisp_gpe_main;
20
21 static int
22 lisp_gpe_rewrite (lisp_gpe_tunnel_t * t, lisp_gpe_sub_tunnel_t * st,
23                   locator_pair_t * lp)
24 {
25   u8 *rw = 0;
26   lisp_gpe_header_t *lisp0;
27   int len;
28
29   if (ip_addr_version (&lp->lcl_loc) == IP4)
30     {
31       ip4_header_t *ip0;
32       ip4_udp_lisp_gpe_header_t *h0;
33       len = sizeof (*h0);
34
35       vec_validate_aligned (rw, len - 1, CLIB_CACHE_LINE_BYTES);
36
37       h0 = (ip4_udp_lisp_gpe_header_t *) rw;
38
39       /* Fixed portion of the (outer) ip4 header */
40       ip0 = &h0->ip4;
41       ip0->ip_version_and_header_length = 0x45;
42       ip0->ttl = 254;
43       ip0->protocol = IP_PROTOCOL_UDP;
44
45       /* we fix up the ip4 header length and checksum after-the-fact */
46       ip_address_copy_addr (&ip0->src_address, &lp->lcl_loc);
47       ip_address_copy_addr (&ip0->dst_address, &lp->rmt_loc);
48       ip0->checksum = ip4_header_checksum (ip0);
49
50       /* UDP header, randomize src port on something, maybe? */
51       h0->udp.src_port = clib_host_to_net_u16 (4341);
52       h0->udp.dst_port = clib_host_to_net_u16 (UDP_DST_PORT_lisp_gpe);
53
54       /* LISP-gpe header */
55       lisp0 = &h0->lisp;
56     }
57   else
58     {
59       ip6_header_t *ip0;
60       ip6_udp_lisp_gpe_header_t *h0;
61       len = sizeof (*h0);
62
63       vec_validate_aligned (rw, len - 1, CLIB_CACHE_LINE_BYTES);
64
65       h0 = (ip6_udp_lisp_gpe_header_t *) rw;
66
67       /* Fixed portion of the (outer) ip6 header */
68       ip0 = &h0->ip6;
69       ip0->ip_version_traffic_class_and_flow_label =
70         clib_host_to_net_u32 (0x6 << 28);
71       ip0->hop_limit = 254;
72       ip0->protocol = IP_PROTOCOL_UDP;
73
74       /* we fix up the ip6 header length after-the-fact */
75       ip_address_copy_addr (&ip0->src_address, &lp->lcl_loc);
76       ip_address_copy_addr (&ip0->dst_address, &lp->rmt_loc);
77
78       /* UDP header, randomize src port on something, maybe? */
79       h0->udp.src_port = clib_host_to_net_u16 (4341);
80       h0->udp.dst_port = clib_host_to_net_u16 (UDP_DST_PORT_lisp_gpe);
81
82       /* LISP-gpe header */
83       lisp0 = &h0->lisp;
84     }
85
86   lisp0->flags = t->flags;
87   lisp0->ver_res = t->ver_res;
88   lisp0->res = t->res;
89   lisp0->next_protocol = t->next_protocol;
90   lisp0->iid = clib_host_to_net_u32 (t->vni);
91
92   st->is_ip4 = ip_addr_version (&lp->lcl_loc) == IP4;
93   st->rewrite = rw;
94   return 0;
95 }
96
97 static int
98 weight_cmp (normalized_sub_tunnel_weights_t * a,
99             normalized_sub_tunnel_weights_t * b)
100 {
101   int cmp = a->weight - b->weight;
102   return (cmp == 0
103           ? a->sub_tunnel_index - b->sub_tunnel_index : (cmp > 0 ? -1 : 1));
104 }
105
106 /** Computes sub tunnel load balancing vector.
107  * Algorithm is identical to that used for building unequal-cost multipath
108  * adjacencies */
109 static void
110 compute_sub_tunnels_balancing_vector (lisp_gpe_tunnel_t * t)
111 {
112   uword n_sts, i, n_nsts, n_nsts_left;
113   f64 sum_weight, norm, error, tolerance;
114   normalized_sub_tunnel_weights_t *nsts = 0, *stp;
115   lisp_gpe_sub_tunnel_t *sts = t->sub_tunnels;
116   u32 *st_lbv = 0;
117
118   /* Accept 1% error */
119   tolerance = .01;
120
121   n_sts = vec_len (sts);
122   vec_validate (nsts, 2 * n_sts - 1);
123
124   sum_weight = 0;
125   for (i = 0; i < n_sts; i++)
126     {
127       /* Find total weight to normalize weights. */
128       sum_weight += sts[i].weight;
129
130       /* build normalized sub tunnels vector */
131       nsts[i].weight = sts[i].weight;
132       nsts[i].sub_tunnel_index = i;
133     }
134
135   n_nsts = n_sts;
136   if (n_sts == 1)
137     {
138       nsts[0].weight = 1;
139       _vec_len (nsts) = 1;
140       goto build_lbv;
141     }
142
143   /* Sort sub-tunnels by weight */
144   qsort (nsts, n_nsts, sizeof (u32), (void *) weight_cmp);
145
146   /* Save copies of all next hop weights to avoid being overwritten in loop below. */
147   for (i = 0; i < n_nsts; i++)
148     nsts[n_nsts + i].weight = nsts[i].weight;
149
150   /* Try larger and larger power of 2 sized blocks until we
151      find one where traffic flows to within 1% of specified weights. */
152   for (n_nsts = max_pow2 (n_sts);; n_nsts *= 2)
153     {
154       error = 0;
155
156       norm = n_nsts / sum_weight;
157       n_nsts_left = n_nsts;
158       for (i = 0; i < n_sts; i++)
159         {
160           f64 nf = nsts[n_sts + i].weight * norm;
161           word n = flt_round_nearest (nf);
162
163           n = n > n_nsts_left ? n_nsts_left : n;
164           n_nsts_left -= n;
165           error += fabs (nf - n);
166           nsts[i].weight = n;
167         }
168
169       nsts[0].weight += n_nsts_left;
170
171       /* Less than 5% average error per adjacency with this size adjacency block? */
172       if (error <= tolerance * n_nsts)
173         {
174           /* Truncate any next hops with zero weight. */
175           _vec_len (nsts) = i;
176           break;
177         }
178     }
179
180 build_lbv:
181
182   /* build load balancing vector */
183   vec_foreach (stp, nsts)
184   {
185     for (i = 0; i < stp[0].weight; i++)
186       vec_add1 (st_lbv, stp[0].sub_tunnel_index);
187   }
188
189   t->sub_tunnels_lbv = st_lbv;
190   t->sub_tunnels_lbv_count = n_nsts;
191   t->norm_sub_tunnel_weights = nsts;
192 }
193
194 static void
195 create_sub_tunnels (lisp_gpe_main_t * lgm, lisp_gpe_tunnel_t * t)
196 {
197   lisp_gpe_sub_tunnel_t st;
198   locator_pair_t *lp = 0;
199   int i;
200
201   /* create sub-tunnels for all locator pairs */
202   for (i = 0; i < vec_len (t->locator_pairs); i++)
203     {
204       lp = &t->locator_pairs[i];
205       st.locator_pair_index = i;
206       st.parent_index = t - lgm->tunnels;
207       st.weight = lp->weight;
208
209       /* compute rewrite for sub-tunnel */
210       lisp_gpe_rewrite (t, &st, lp);
211       vec_add1 (t->sub_tunnels, st);
212     }
213
214   /* normalize weights and compute sub-tunnel load balancing vector */
215   compute_sub_tunnels_balancing_vector (t);
216 }
217
218 #define foreach_copy_field                      \
219 _(encap_fib_index)                              \
220 _(decap_fib_index)                              \
221 _(decap_next_index)                             \
222 _(vni)                                          \
223 _(action)
224
225 static int
226 add_del_ip_tunnel (vnet_lisp_gpe_add_del_fwd_entry_args_t * a, u8 is_l2,
227                    u32 * tun_index_res)
228 {
229   lisp_gpe_main_t *lgm = &lisp_gpe_main;
230   lisp_gpe_tunnel_t *t = 0;
231   lisp_gpe_tunnel_key_t key;
232   lisp_gpe_sub_tunnel_t *stp = 0;
233   uword *p;
234
235   /* prepare tunnel key */
236   memset (&key, 0, sizeof (key));
237
238   /* fill in the key's remote eid */
239   if (!is_l2)
240     ip_prefix_copy (&key.rmt.ippref, &gid_address_ippref (&a->rmt_eid));
241   else
242     mac_copy (&key.rmt.mac, &gid_address_mac (&a->rmt_eid));
243
244   key.vni = clib_host_to_net_u32 (a->vni);
245
246   p = mhash_get (&lgm->lisp_gpe_tunnel_by_key, &key);
247
248   if (a->is_add)
249     {
250       /* adding a tunnel: tunnel must not already exist */
251       if (p)
252         return VNET_API_ERROR_INVALID_VALUE;
253
254       if (a->decap_next_index >= LISP_GPE_INPUT_N_NEXT)
255         return VNET_API_ERROR_INVALID_DECAP_NEXT;
256
257       pool_get_aligned (lgm->tunnels, t, CLIB_CACHE_LINE_BYTES);
258       memset (t, 0, sizeof (*t));
259
260       /* copy from arg structure */
261 #define _(x) t->x = a->x;
262       foreach_copy_field;
263 #undef _
264
265       t->locator_pairs = vec_dup (a->locator_pairs);
266
267       /* if vni is non-default */
268       if (a->vni)
269         t->flags = LISP_GPE_FLAGS_I;
270
271       /* work in lisp-gpe not legacy mode */
272       t->flags |= LISP_GPE_FLAGS_P;
273
274       /* next proto */
275       if (!is_l2)
276         t->next_protocol = ip_prefix_version (&key.rmt.ippref) == IP4 ?
277           LISP_GPE_NEXT_PROTO_IP4 : LISP_GPE_NEXT_PROTO_IP6;
278       else
279         t->next_protocol = LISP_GPE_NEXT_PROTO_ETHERNET;
280
281       /* build sub-tunnels for lowest priority locator-pairs */
282       if (!a->is_negative)
283         create_sub_tunnels (lgm, t);
284
285       mhash_set (&lgm->lisp_gpe_tunnel_by_key, &key, t - lgm->tunnels, 0);
286
287       /* return tunnel index */
288       if (tun_index_res)
289         tun_index_res[0] = t - lgm->tunnels;
290     }
291   else
292     {
293       /* deleting a tunnel: tunnel must exist */
294       if (!p)
295         {
296           clib_warning ("Tunnel for eid %U doesn't exist!",
297                         format_gid_address, &a->rmt_eid);
298           return VNET_API_ERROR_NO_SUCH_ENTRY;
299         }
300
301       t = pool_elt_at_index (lgm->tunnels, p[0]);
302
303       mhash_unset (&lgm->lisp_gpe_tunnel_by_key, &key, 0);
304
305       vec_foreach (stp, t->sub_tunnels)
306       {
307         vec_free (stp->rewrite);
308       }
309       vec_free (t->sub_tunnels);
310       vec_free (t->sub_tunnels_lbv);
311       vec_free (t->locator_pairs);
312       pool_put (lgm->tunnels, t);
313     }
314
315   return 0;
316 }
317
318 static int
319 build_ip_adjacency (lisp_gpe_main_t * lgm, ip_adjacency_t * adj, u32 table_id,
320                     u32 vni, u32 tun_index, u32 n_sub_tun, u8 is_negative,
321                     u8 action, u8 ip_ver)
322 {
323   uword *lookup_next_index, *lgpe_sw_if_index, *lnip;
324
325   memset (adj, 0, sizeof (adj[0]));
326   adj->n_adj = 1;
327   /* fill in lookup_next_index with a 'legal' value to avoid problems */
328   adj->lookup_next_index = (ip_ver == IP4) ?
329     lgm->ip4_lookup_next_lgpe_ip4_lookup :
330     lgm->ip6_lookup_next_lgpe_ip6_lookup;
331
332   /* positive mapping */
333   if (!is_negative)
334     {
335       /* send packets that hit this adj to lisp-gpe interface output node in
336        * requested vrf. */
337       lnip = (ip_ver == IP4) ?
338         lgm->lgpe_ip4_lookup_next_index_by_table_id :
339         lgm->lgpe_ip6_lookup_next_index_by_table_id;
340       lookup_next_index = hash_get (lnip, table_id);
341       lgpe_sw_if_index = hash_get (lgm->l3_ifaces.sw_if_index_by_vni, vni);
342
343       /* the assumption is that the interface must've been created before
344        * programming the dp */
345       ASSERT (lookup_next_index != 0 && lgpe_sw_if_index != 0);
346
347       /* hijack explicit fib index to store lisp interface node index,
348        * if_address_index for the tunnel index and saved lookup next index
349        * for the number of sub tunnels */
350       adj->explicit_fib_index = lookup_next_index[0];
351       adj->if_address_index = tun_index;
352       adj->rewrite_header.sw_if_index = lgpe_sw_if_index[0];
353       adj->saved_lookup_next_index = n_sub_tun;
354     }
355   /* negative mapping */
356   else
357     {
358       adj->rewrite_header.sw_if_index = ~0;
359       adj->rewrite_header.next_index = ~0;
360       adj->if_address_index = tun_index;
361
362       switch (action)
363         {
364         case LISP_NO_ACTION:
365           /* TODO update timers? */
366         case LISP_FORWARD_NATIVE:
367           /* TODO check if route/next-hop for eid exists in fib and add
368            * more specific for the eid with the next-hop found */
369         case LISP_SEND_MAP_REQUEST:
370           /* insert tunnel that always sends map-request */
371           adj->explicit_fib_index = (ip_ver == IP4) ?
372             LGPE_IP4_LOOKUP_NEXT_LISP_CP_LOOKUP :
373             LGPE_IP6_LOOKUP_NEXT_LISP_CP_LOOKUP;
374           break;
375         case LISP_DROP:
376           /* for drop fwd entries, just add route, no need to add encap tunnel */
377           adj->explicit_fib_index = (ip_ver == IP4 ?
378                                      LGPE_IP4_LOOKUP_NEXT_DROP :
379                                      LGPE_IP6_LOOKUP_NEXT_DROP);
380           break;
381         default:
382           return -1;
383         }
384     }
385   return 0;
386 }
387
388 static int
389 add_del_ip_fwd_entry (lisp_gpe_main_t * lgm,
390                       vnet_lisp_gpe_add_del_fwd_entry_args_t * a)
391 {
392   ip_adjacency_t adj, *adjp;
393   lisp_gpe_tunnel_t *t;
394   u32 rv, tun_index = ~0, n_sub_tuns = 0;
395   ip_prefix_t *rmt_pref, *lcl_pref;
396   u8 ip_ver;
397
398   rmt_pref = &gid_address_ippref (&a->rmt_eid);
399   lcl_pref = &gid_address_ippref (&a->lcl_eid);
400   ip_ver = ip_prefix_version (rmt_pref);
401
402   /* add/del tunnel to tunnels pool and prepares rewrite */
403   if (0 != a->locator_pairs)
404     {
405       rv = add_del_ip_tunnel (a, 0 /* is_l2 */ , &tun_index);
406       if (rv)
407         {
408           clib_warning ("failed to build tunnel!");
409           return rv;
410         }
411       if (a->is_add)
412         {
413           t = pool_elt_at_index (lgm->tunnels, tun_index);
414           n_sub_tuns = t->sub_tunnels_lbv_count;
415         }
416     }
417
418   /* setup adjacency for eid */
419   rv = build_ip_adjacency (lgm, &adj, a->table_id, a->vni, tun_index,
420                            n_sub_tuns, a->is_negative, a->action, ip_ver);
421
422   /* add/delete route for eid */
423   rv |= ip_sd_fib_add_del_route (lgm, rmt_pref, lcl_pref, a->table_id, &adj,
424                                  a->is_add);
425
426   if (rv)
427     {
428       clib_warning ("failed to insert route for tunnel!");
429       return rv;
430     }
431
432   /* check that everything worked */
433   if (CLIB_DEBUG && a->is_add)
434     {
435       u32 adj_index;
436       adj_index = ip_sd_fib_get_route (lgm, rmt_pref, lcl_pref, a->table_id);
437       ASSERT (adj_index != 0);
438
439       adjp = ip_get_adjacency ((ip_ver == IP4) ? lgm->lm4 : lgm->lm6,
440                                adj_index);
441
442       ASSERT (adjp != 0 && adjp->if_address_index == tun_index);
443     }
444
445   return rv;
446 }
447
448 static void
449 make_mac_fib_key (BVT (clib_bihash_kv) * kv, u16 bd_index, u8 src_mac[6],
450                   u8 dst_mac[6])
451 {
452   kv->key[0] = (((u64) bd_index) << 48) | mac_to_u64 (dst_mac);
453   kv->key[1] = mac_to_u64 (src_mac);
454   kv->key[2] = 0;
455 }
456
457 u32
458 lisp_l2_fib_lookup (lisp_gpe_main_t * lgm, u16 bd_index, u8 src_mac[6],
459                     u8 dst_mac[6])
460 {
461   int rv;
462   BVT (clib_bihash_kv) kv, value;
463
464   make_mac_fib_key (&kv, bd_index, src_mac, dst_mac);
465   rv = BV (clib_bihash_search_inline_2) (&lgm->l2_fib, &kv, &value);
466
467   /* no match, try with src 0, catch all for dst */
468   if (rv != 0)
469     {
470       kv.key[1] = 0;
471       rv = BV (clib_bihash_search_inline_2) (&lgm->l2_fib, &kv, &value);
472       if (rv == 0)
473         return value.value;
474     }
475
476   return ~0;
477 }
478
479 u32
480 lisp_l2_fib_add_del_entry (lisp_gpe_main_t * lgm, u16 bd_index, u8 src_mac[6],
481                            u8 dst_mac[6], u32 val, u8 is_add)
482 {
483   BVT (clib_bihash_kv) kv, value;
484   u32 old_val = ~0;
485
486   make_mac_fib_key (&kv, bd_index, src_mac, dst_mac);
487
488   if (BV (clib_bihash_search) (&lgm->l2_fib, &kv, &value) == 0)
489     old_val = value.value;
490
491   if (!is_add)
492     BV (clib_bihash_add_del) (&lgm->l2_fib, &kv, 0 /* is_add */ );
493   else
494     {
495       kv.value = val;
496       BV (clib_bihash_add_del) (&lgm->l2_fib, &kv, 1 /* is_add */ );
497     }
498   return old_val;
499 }
500
501 static void
502 l2_fib_init (lisp_gpe_main_t * lgm)
503 {
504   BV (clib_bihash_init) (&lgm->l2_fib, "l2 fib",
505                          1 << max_log2 (L2_FIB_DEFAULT_HASH_NUM_BUCKETS),
506                          L2_FIB_DEFAULT_HASH_MEMORY_SIZE);
507 }
508
509 static int
510 add_del_l2_fwd_entry (lisp_gpe_main_t * lgm,
511                       vnet_lisp_gpe_add_del_fwd_entry_args_t * a)
512 {
513   int rv;
514   u32 tun_index;
515   bd_main_t *bdm = &bd_main;
516   uword *bd_indexp;
517
518   /* create tunnel */
519   rv = add_del_ip_tunnel (a, 1 /* is_l2 */ , &tun_index);
520   if (rv)
521     return rv;
522
523   bd_indexp = hash_get (bdm->bd_index_by_bd_id, a->bd_id);
524   if (!bd_indexp)
525     {
526       clib_warning ("bridge domain %d doesn't exist", a->bd_id);
527       return -1;
528     }
529
530   /* add entry to l2 lisp fib */
531   lisp_l2_fib_add_del_entry (lgm, bd_indexp[0], gid_address_mac (&a->lcl_eid),
532                              gid_address_mac (&a->rmt_eid), tun_index,
533                              a->is_add);
534   return 0;
535 }
536
537
538 int
539 vnet_lisp_gpe_add_del_fwd_entry (vnet_lisp_gpe_add_del_fwd_entry_args_t * a,
540                                  u32 * hw_if_indexp)
541 {
542   lisp_gpe_main_t *lgm = &lisp_gpe_main;
543   u8 type;
544
545   if (vnet_lisp_gpe_enable_disable_status () == 0)
546     {
547       clib_warning ("LISP is disabled!");
548       return VNET_API_ERROR_LISP_DISABLED;
549     }
550
551   type = gid_address_type (&a->rmt_eid);
552   switch (type)
553     {
554     case GID_ADDR_IP_PREFIX:
555       return add_del_ip_fwd_entry (lgm, a);
556     case GID_ADDR_MAC:
557       return add_del_l2_fwd_entry (lgm, a);
558     default:
559       clib_warning ("Forwarding entries for type %d not supported!", type);
560       return -1;
561     }
562 }
563
564 static clib_error_t *
565 lisp_gpe_add_del_fwd_entry_command_fn (vlib_main_t * vm,
566                                        unformat_input_t * input,
567                                        vlib_cli_command_t * cmd)
568 {
569   unformat_input_t _line_input, *line_input = &_line_input;
570   u8 is_add = 1;
571   ip_address_t lloc, rloc;
572   clib_error_t *error = 0;
573   gid_address_t _reid, *reid = &_reid, _leid, *leid = &_leid;
574   u8 reid_set = 0, leid_set = 0, is_negative = 0, vrf_set = 0, vni_set = 0;
575   u32 vni, vrf, action = ~0, p, w;
576   locator_pair_t pair, *pairs = 0;
577   int rv;
578
579   /* Get a line of input. */
580   if (!unformat_user (input, unformat_line_input, line_input))
581     return 0;
582
583   while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT)
584     {
585       if (unformat (line_input, "del"))
586         is_add = 0;
587       else if (unformat (line_input, "add"))
588         is_add = 1;
589       else if (unformat (line_input, "leid %U", unformat_gid_address, leid))
590         {
591           leid_set = 1;
592         }
593       else if (unformat (line_input, "reid %U", unformat_gid_address, reid))
594         {
595           reid_set = 1;
596         }
597       else if (unformat (line_input, "vni %u", &vni))
598         {
599           gid_address_vni (leid) = vni;
600           gid_address_vni (reid) = vni;
601           vni_set = 1;
602         }
603       else if (unformat (line_input, "vrf %u", &vrf))
604         {
605           vrf_set = 1;
606         }
607       else if (unformat (line_input, "negative action %U",
608                          unformat_negative_mapping_action, &action))
609         {
610           is_negative = 1;
611         }
612       else if (unformat (line_input, "loc-pair %U %U p %d w %d",
613                          unformat_ip_address, &lloc,
614                          unformat_ip_address, &rloc, &p, &w))
615         {
616           pair.lcl_loc = lloc;
617           pair.rmt_loc = rloc;
618           pair.priority = p;
619           pair.weight = w;
620           vec_add1 (pairs, pair);
621         }
622       else
623         {
624           error = unformat_parse_error (line_input);
625           goto done;
626         }
627     }
628   unformat_free (line_input);
629
630   if (!vni_set || !vrf_set)
631     {
632       error = clib_error_return (0, "vni and vrf must be set!");
633       goto done;
634     }
635
636   if (!reid_set)
637     {
638       error = clib_error_return (0, "remote eid must be set!");
639       goto done;
640     }
641
642   if (is_negative)
643     {
644       if (~0 == action)
645         {
646           error = clib_error_return (0, "no action set for negative tunnel!");
647           goto done;
648         }
649     }
650   else
651     {
652       if (vec_len (pairs) == 0)
653         {
654           error = clib_error_return (0, "expected ip4/ip6 locators.");
655           goto done;
656         }
657     }
658
659   if (!leid_set)
660     {
661       /* if leid not set, make sure it's the same AFI like reid */
662       gid_address_type (leid) = gid_address_type (reid);
663       if (GID_ADDR_IP_PREFIX == gid_address_type (reid))
664         gid_address_ip_version (leid) = gid_address_ip_version (reid);
665     }
666
667   /* add fwd entry */
668   vnet_lisp_gpe_add_del_fwd_entry_args_t _a, *a = &_a;
669   memset (a, 0, sizeof (a[0]));
670
671   a->is_add = is_add;
672   a->vni = vni;
673   a->table_id = vrf;
674   gid_address_copy (&a->lcl_eid, leid);
675   gid_address_copy (&a->rmt_eid, reid);
676   a->locator_pairs = pairs;
677
678   rv = vnet_lisp_gpe_add_del_fwd_entry (a, 0);
679   if (0 != rv)
680     {
681       error = clib_error_return (0, "failed to %s gpe tunnel!",
682                                  is_add ? "add" : "delete");
683     }
684
685 done:
686   vec_free (pairs);
687   return error;
688 }
689
690 /* *INDENT-OFF* */
691 VLIB_CLI_COMMAND (lisp_gpe_add_del_fwd_entry_command, static) = {
692   .path = "lisp gpe tunnel",
693   .short_help = "lisp gpe tunnel add/del vni <vni> vrf <vrf> [leid <leid>]"
694       "reid <reid> [lloc <sloc> rloc <rloc>] [negative action <action>]",
695   .function = lisp_gpe_add_del_fwd_entry_command_fn,
696 };
697 /* *INDENT-ON* */
698
699 static u8 *
700 format_decap_next (u8 * s, va_list * args)
701 {
702   u32 next_index = va_arg (*args, u32);
703
704   switch (next_index)
705     {
706     case LISP_GPE_INPUT_NEXT_DROP:
707       return format (s, "drop");
708     case LISP_GPE_INPUT_NEXT_IP4_INPUT:
709       return format (s, "ip4");
710     case LISP_GPE_INPUT_NEXT_IP6_INPUT:
711       return format (s, "ip6");
712     default:
713       return format (s, "unknown %d", next_index);
714     }
715   return s;
716 }
717
718 u8 *
719 format_lisp_gpe_tunnel (u8 * s, va_list * args)
720 {
721   lisp_gpe_tunnel_t *t = va_arg (*args, lisp_gpe_tunnel_t *);
722   lisp_gpe_main_t *lgm = vnet_lisp_gpe_get_main ();
723   locator_pair_t *lp = 0;
724   normalized_sub_tunnel_weights_t *nstw;
725
726   s =
727     format (s, "tunnel %d vni %d (0x%x)\n", t - lgm->tunnels, t->vni, t->vni);
728   s =
729     format (s, " fibs: encap %d, decap %d decap next %U\n",
730             t->encap_fib_index, t->decap_fib_index, format_decap_next,
731             t->decap_next_index);
732   s = format (s, " lisp ver %d ", (t->ver_res >> 6));
733
734 #define _(n,v) if (t->flags & v) s = format (s, "%s-bit ", #n);
735   foreach_lisp_gpe_flag_bit;
736 #undef _
737
738   s = format (s, "next_protocol %d ver_res %x res %x\n",
739               t->next_protocol, t->ver_res, t->res);
740
741   s = format (s, " locator-pairs:\n");
742   vec_foreach (lp, t->locator_pairs)
743   {
744     s = format (s, "  local: %U remote: %U weight %d\n",
745                 format_ip_address, &lp->lcl_loc, format_ip_address,
746                 &lp->rmt_loc, lp->weight);
747   }
748
749   s = format (s, " active sub-tunnels:\n");
750   vec_foreach (nstw, t->norm_sub_tunnel_weights)
751   {
752     lp = vec_elt_at_index (t->locator_pairs, nstw->sub_tunnel_index);
753     s = format (s, "  local: %U remote: %U weight %d\n", format_ip_address,
754                 &lp->lcl_loc, format_ip_address, &lp->rmt_loc, nstw->weight);
755   }
756   return s;
757 }
758
759 static clib_error_t *
760 show_lisp_gpe_tunnel_command_fn (vlib_main_t * vm,
761                                  unformat_input_t * input,
762                                  vlib_cli_command_t * cmd)
763 {
764   lisp_gpe_main_t *lgm = &lisp_gpe_main;
765   lisp_gpe_tunnel_t *t;
766
767   if (pool_elts (lgm->tunnels) == 0)
768     vlib_cli_output (vm, "No lisp-gpe tunnels configured...");
769
770   /* *INDENT-OFF* */
771   pool_foreach (t, lgm->tunnels,
772   ({
773     vlib_cli_output (vm, "%U", format_lisp_gpe_tunnel, t);
774   }));
775   /* *INDENT-ON* */
776
777   return 0;
778 }
779
780 /* *INDENT-OFF* */
781 VLIB_CLI_COMMAND (show_lisp_gpe_tunnel_command, static) =
782 {
783   .path = "show lisp gpe tunnel",
784   .function = show_lisp_gpe_tunnel_command_fn,
785 };
786 /* *INDENT-ON* */
787
788 u8
789 vnet_lisp_gpe_enable_disable_status (void)
790 {
791   lisp_gpe_main_t *lgm = &lisp_gpe_main;
792
793   return lgm->is_en;
794 }
795
796 clib_error_t *
797 vnet_lisp_gpe_enable_disable (vnet_lisp_gpe_enable_disable_args_t * a)
798 {
799   lisp_gpe_main_t *lgm = &lisp_gpe_main;
800   vnet_main_t *vnm = lgm->vnet_main;
801
802   if (a->is_en)
803     {
804       /* add lgpe_ip4_lookup as possible next_node for ip4 lookup */
805       if (lgm->ip4_lookup_next_lgpe_ip4_lookup == ~0)
806         {
807           lgm->ip4_lookup_next_lgpe_ip4_lookup =
808             vlib_node_add_next (vnm->vlib_main, ip4_lookup_node.index,
809                                 lgpe_ip4_lookup_node.index);
810         }
811       /* add lgpe_ip6_lookup as possible next_node for ip6 lookup */
812       if (lgm->ip6_lookup_next_lgpe_ip6_lookup == ~0)
813         {
814           lgm->ip6_lookup_next_lgpe_ip6_lookup =
815             vlib_node_add_next (vnm->vlib_main, ip6_lookup_node.index,
816                                 lgpe_ip6_lookup_node.index);
817         }
818       else
819         {
820           /* ask cp to re-add ifaces and defaults */
821         }
822
823       lgm->is_en = 1;
824     }
825   else
826     {
827       CLIB_UNUSED (uword * val);
828       hash_pair_t *p;
829       u32 *dp_tables = 0, *dp_table;
830       lisp_gpe_tunnel_key_t *tunnels = 0, *tunnel;
831       vnet_lisp_gpe_add_del_fwd_entry_args_t _at, *at = &_at;
832       vnet_lisp_gpe_add_del_iface_args_t _ai, *ai = &_ai;
833
834       /* remove all tunnels */
835
836       /* *INDENT-OFF* */
837       mhash_foreach(tunnel, val, &lgm->lisp_gpe_tunnel_by_key, ({
838         vec_add1(tunnels, tunnel[0]);
839       }));
840       /* *INDENT-ON* */
841
842       vec_foreach (tunnel, tunnels)
843       {
844         memset (at, 0, sizeof (at[0]));
845         at->is_add = 0;
846         if (tunnel->rmt.type == GID_ADDR_IP_PREFIX)
847           {
848             gid_address_type (&at->rmt_eid) = GID_ADDR_IP_PREFIX;
849             ip_prefix_copy (&gid_address_ippref (&at->rmt_eid),
850                             &tunnel->rmt.ippref);
851           }
852         else
853           {
854             gid_address_type (&at->rmt_eid) = GID_ADDR_MAC;
855             mac_copy (&gid_address_mac (&at->rmt_eid), &tunnel->rmt.mac);
856           }
857         vnet_lisp_gpe_add_del_fwd_entry (at, 0);
858       }
859       vec_free (tunnels);
860
861       /* disable all l3 ifaces */
862
863       /* *INDENT-OFF* */
864       hash_foreach_pair(p, lgm->l3_ifaces.hw_if_index_by_dp_table, ({
865         vec_add1(dp_tables, p->key);
866       }));
867       /* *INDENT-ON* */
868
869       vec_foreach (dp_table, dp_tables)
870       {
871         ai->is_add = 0;
872         ai->table_id = dp_table[0];
873         ai->is_l2 = 0;
874
875         /* disables interface and removes defaults */
876         vnet_lisp_gpe_add_del_iface (ai, 0);
877       }
878
879       /* disable all l2 ifaces */
880       _vec_len (dp_tables) = 0;
881
882       /* *INDENT-OFF* */
883       hash_foreach_pair(p, lgm->l2_ifaces.hw_if_index_by_dp_table, ({
884         vec_add1(dp_tables, p->key);
885       }));
886       /* *INDENT-ON* */
887
888       vec_foreach (dp_table, dp_tables)
889       {
890         ai->is_add = 0;
891         ai->bd_id = dp_table[0];
892         ai->is_l2 = 1;
893
894         /* disables interface and removes defaults */
895         vnet_lisp_gpe_add_del_iface (ai, 0);
896       }
897
898       vec_free (dp_tables);
899       lgm->is_en = 0;
900     }
901
902   return 0;
903 }
904
905 static clib_error_t *
906 lisp_gpe_enable_disable_command_fn (vlib_main_t * vm,
907                                     unformat_input_t * input,
908                                     vlib_cli_command_t * cmd)
909 {
910   unformat_input_t _line_input, *line_input = &_line_input;
911   u8 is_en = 1;
912   vnet_lisp_gpe_enable_disable_args_t _a, *a = &_a;
913
914   /* Get a line of input. */
915   if (!unformat_user (input, unformat_line_input, line_input))
916     return 0;
917
918   while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT)
919     {
920       if (unformat (line_input, "enable"))
921         is_en = 1;
922       else if (unformat (line_input, "disable"))
923         is_en = 0;
924       else
925         {
926           return clib_error_return (0, "parse error: '%U'",
927                                     format_unformat_error, line_input);
928         }
929     }
930   a->is_en = is_en;
931   return vnet_lisp_gpe_enable_disable (a);
932 }
933
934 /* *INDENT-OFF* */
935 VLIB_CLI_COMMAND (enable_disable_lisp_gpe_command, static) = {
936   .path = "lisp gpe",
937   .short_help = "lisp gpe [enable|disable]",
938   .function = lisp_gpe_enable_disable_command_fn,
939 };
940 /* *INDENT-ON* */
941
942 static clib_error_t *
943 lisp_show_iface_command_fn (vlib_main_t * vm,
944                             unformat_input_t * input,
945                             vlib_cli_command_t * cmd)
946 {
947   lisp_gpe_main_t *lgm = &lisp_gpe_main;
948   hash_pair_t *p;
949
950   vlib_cli_output (vm, "%=10s%=12s", "vrf", "hw_if_index");
951
952   /* *INDENT-OFF* */
953   hash_foreach_pair (p, lgm->l3_ifaces.hw_if_index_by_dp_table, ({
954     vlib_cli_output (vm, "%=10d%=10d", p->key, p->value[0]);
955   }));
956   /* *INDENT-ON* */
957
958   if (0 != lgm->l2_ifaces.hw_if_index_by_dp_table)
959     {
960       vlib_cli_output (vm, "%=10s%=12s", "bd_id", "hw_if_index");
961       /* *INDENT-OFF* */
962       hash_foreach_pair (p, lgm->l2_ifaces.hw_if_index_by_dp_table, ({
963         vlib_cli_output (vm, "%=10d%=10d", p->key, p->value[0]);
964       }));
965       /* *INDENT-ON* */
966     }
967   return 0;
968 }
969
970 /* *INDENT-OFF* */
971 VLIB_CLI_COMMAND (lisp_show_iface_command) = {
972     .path = "show lisp gpe interface",
973     .short_help = "show lisp gpe interface",
974     .function = lisp_show_iface_command_fn,
975 };
976 /* *INDENT-ON* */
977
978 u8 *
979 format_vnet_lisp_gpe_status (u8 * s, va_list * args)
980 {
981   lisp_gpe_main_t *lgm = &lisp_gpe_main;
982   return format (s, "%s", lgm->is_en ? "enabled" : "disabled");
983 }
984
985 clib_error_t *
986 lisp_gpe_init (vlib_main_t * vm)
987 {
988   lisp_gpe_main_t *lgm = &lisp_gpe_main;
989   clib_error_t *error = 0;
990
991   if ((error = vlib_call_init_function (vm, ip_main_init)))
992     return error;
993
994   if ((error = vlib_call_init_function (vm, ip4_lookup_init)))
995     return error;
996
997   lgm->vnet_main = vnet_get_main ();
998   lgm->vlib_main = vm;
999   lgm->im4 = &ip4_main;
1000   lgm->im6 = &ip6_main;
1001   lgm->lm4 = &ip4_main.lookup_main;
1002   lgm->lm6 = &ip6_main.lookup_main;
1003   lgm->ip4_lookup_next_lgpe_ip4_lookup = ~0;
1004   lgm->ip6_lookup_next_lgpe_ip6_lookup = ~0;
1005
1006   mhash_init (&lgm->lisp_gpe_tunnel_by_key, sizeof (uword),
1007               sizeof (lisp_gpe_tunnel_key_t));
1008
1009   l2_fib_init (lgm);
1010
1011   udp_register_dst_port (vm, UDP_DST_PORT_lisp_gpe,
1012                          lisp_gpe_ip4_input_node.index, 1 /* is_ip4 */ );
1013   udp_register_dst_port (vm, UDP_DST_PORT_lisp_gpe6,
1014                          lisp_gpe_ip6_input_node.index, 0 /* is_ip4 */ );
1015   return 0;
1016 }
1017
1018 VLIB_INIT_FUNCTION (lisp_gpe_init);
1019
1020 /*
1021  * fd.io coding-style-patch-verification: ON
1022  *
1023  * Local Variables:
1024  * eval: (c-set-style "gnu")
1025  * End:
1026  */