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