c4f936eccdaafdf302d2561eff9fcf3f83456c9f
[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
18 lisp_gpe_main_t lisp_gpe_main;
19
20 static int
21 lisp_gpe_rewrite (lisp_gpe_tunnel_t * t)
22 {
23   u8 *rw = 0;
24   lisp_gpe_header_t * lisp0;
25   int len;
26
27   if (ip_addr_version(&t->src) == IP4)
28     {
29       ip4_header_t * ip0;
30       ip4_udp_lisp_gpe_header_t * h0;
31       len = sizeof(*h0);
32
33       vec_validate_aligned(rw, len - 1, CLIB_CACHE_LINE_BYTES);
34
35       h0 = (ip4_udp_lisp_gpe_header_t *) rw;
36
37       /* Fixed portion of the (outer) ip4 header */
38       ip0 = &h0->ip4;
39       ip0->ip_version_and_header_length = 0x45;
40       ip0->ttl = 254;
41       ip0->protocol = IP_PROTOCOL_UDP;
42
43       /* we fix up the ip4 header length and checksum after-the-fact */
44       ip_address_copy_addr(&ip0->src_address, &t->src);
45       ip_address_copy_addr(&ip0->dst_address, &t->dst);
46       ip0->checksum = ip4_header_checksum (ip0);
47
48       /* UDP header, randomize src port on something, maybe? */
49       h0->udp.src_port = clib_host_to_net_u16 (4341);
50       h0->udp.dst_port = clib_host_to_net_u16 (UDP_DST_PORT_lisp_gpe);
51
52       /* LISP-gpe header */
53       lisp0 = &h0->lisp;
54     }
55   else
56     {
57       ip6_header_t * ip0;
58       ip6_udp_lisp_gpe_header_t * h0;
59       len = sizeof(*h0);
60
61       vec_validate_aligned(rw, len - 1, CLIB_CACHE_LINE_BYTES);
62
63       h0 = (ip6_udp_lisp_gpe_header_t *) rw;
64
65       /* Fixed portion of the (outer) ip6 header */
66       ip0 = &h0->ip6;
67       ip0->ip_version_traffic_class_and_flow_label =
68           clib_host_to_net_u32 (0x6 << 28);
69       ip0->hop_limit = 254;
70       ip0->protocol = IP_PROTOCOL_UDP;
71
72       /* we fix up the ip6 header length after-the-fact */
73       ip_address_copy_addr(&ip0->src_address, &t->src);
74       ip_address_copy_addr(&ip0->dst_address, &t->dst);
75
76       /* UDP header, randomize src port on something, maybe? */
77       h0->udp.src_port = clib_host_to_net_u16 (4341);
78       h0->udp.dst_port = clib_host_to_net_u16 (UDP_DST_PORT_lisp_gpe);
79
80       /* LISP-gpe header */
81       lisp0 = &h0->lisp;
82     }
83
84   lisp0->flags = t->flags;
85   lisp0->ver_res = t->ver_res;
86   lisp0->res = t->res;
87   lisp0->next_protocol = t->next_protocol;
88   lisp0->iid = clib_host_to_net_u32 (t->vni);
89
90   t->rewrite = rw;
91   return 0;
92 }
93
94 #define foreach_copy_field                      \
95 _(encap_fib_index)                              \
96 _(decap_fib_index)                              \
97 _(decap_next_index)                             \
98 _(vni)
99
100 static u32
101 add_del_ip_tunnel (vnet_lisp_gpe_add_del_fwd_entry_args_t *a,
102                    u32 * tun_index_res)
103 {
104   lisp_gpe_main_t * lgm = &lisp_gpe_main;
105   lisp_gpe_tunnel_t *t = 0;
106   uword * p;
107   int rv;
108   lisp_gpe_tunnel_key_t key;
109
110   /* prepare tunnel key */
111   memset(&key, 0, sizeof(key));
112   ip_prefix_copy(&key.eid, &gid_address_ippref(&a->deid));
113   ip_address_copy(&key.dst_loc, &a->dlocator);
114   key.iid = clib_host_to_net_u32 (a->vni);
115
116   p = mhash_get (&lgm->lisp_gpe_tunnel_by_key, &key);
117
118   if (a->is_add)
119     {
120       /* adding a tunnel: tunnel must not already exist */
121       if (p)
122         return VNET_API_ERROR_INVALID_VALUE;
123
124       if (a->decap_next_index >= LISP_GPE_INPUT_N_NEXT)
125         return VNET_API_ERROR_INVALID_DECAP_NEXT;
126
127       pool_get_aligned (lgm->tunnels, t, CLIB_CACHE_LINE_BYTES);
128       memset (t, 0, sizeof (*t));
129
130       /* copy from arg structure */
131 #define _(x) t->x = a->x;
132       foreach_copy_field;
133 #undef _
134
135       ip_address_copy(&t->src, &a->slocator);
136       ip_address_copy(&t->dst, &a->dlocator);
137
138       t->flags |= LISP_GPE_FLAGS_P;
139       t->next_protocol = ip_prefix_version(&key.eid) == IP4 ?
140           LISP_GPE_NEXT_PROTO_IP4 : LISP_GPE_NEXT_PROTO_IP6;
141
142       rv = lisp_gpe_rewrite (t);
143
144       if (rv)
145         {
146           pool_put(lgm->tunnels, t);
147           return rv;
148         }
149
150       mhash_set(&lgm->lisp_gpe_tunnel_by_key, &key, t - lgm->tunnels, 0);
151
152       /* return tunnel index */
153       if (tun_index_res)
154         tun_index_res[0] = t - lgm->tunnels;
155     }
156   else
157     {
158       /* deleting a tunnel: tunnel must exist */
159       if (!p)
160         {
161           clib_warning("Tunnel for eid %U doesn't exist!", format_gid_address,
162                        &a->deid);
163           return VNET_API_ERROR_NO_SUCH_ENTRY;
164         }
165
166       t = pool_elt_at_index(lgm->tunnels, p[0]);
167
168       mhash_unset(&lgm->lisp_gpe_tunnel_by_key, &key, 0);
169
170       vec_free(t->rewrite);
171       pool_put(lgm->tunnels, t);
172     }
173
174   return 0;
175 }
176
177 static int
178 add_del_negative_fwd_entry (lisp_gpe_main_t * lgm,
179                             vnet_lisp_gpe_add_del_fwd_entry_args_t * a)
180 {
181   ip_adjacency_t adj;
182   ip_prefix_t * dpref = &gid_address_ippref(&a->deid);
183   ip_prefix_t * spref = &gid_address_ippref(&a->seid);
184
185   /* setup adjacency for eid */
186   memset (&adj, 0, sizeof(adj));
187   adj.n_adj = 1;
188
189   /* fill in 'legal' data to avoid issues */
190   adj.lookup_next_index =  (ip_prefix_version(dpref) == IP4) ?
191                                   lgm->ip4_lookup_next_lgpe_ip4_lookup :
192                                   lgm->ip6_lookup_next_lgpe_ip6_lookup;
193
194   adj.rewrite_header.sw_if_index = ~0;
195   adj.rewrite_header.next_index = ~0;
196
197   switch (a->action)
198     {
199     case NO_ACTION:
200       /* TODO update timers? */
201     case FORWARD_NATIVE:
202       /* TODO check if route/next-hop for eid exists in fib and add
203        * more specific for the eid with the next-hop found */
204     case SEND_MAP_REQUEST:
205       /* insert tunnel that always sends map-request */
206       adj.explicit_fib_index = (ip_prefix_version(dpref) == IP4) ?
207                                LGPE_IP4_LOOKUP_NEXT_LISP_CP_LOOKUP:
208                                LGPE_IP6_LOOKUP_NEXT_LISP_CP_LOOKUP;
209       /* add/delete route for prefix */
210       return ip_sd_fib_add_del_route (lgm, dpref, spref, a->table_id, &adj,
211                                       a->is_add);
212     case DROP:
213       /* for drop fwd entries, just add route, no need to add encap tunnel */
214       adj.explicit_fib_index =  (ip_prefix_version(dpref) == IP4 ?
215               LGPE_IP4_LOOKUP_NEXT_DROP : LGPE_IP6_LOOKUP_NEXT_DROP);
216
217       /* add/delete route for prefix */
218       return ip_sd_fib_add_del_route (lgm, dpref, spref, a->table_id, &adj,
219                                       a->is_add);
220     default:
221       return -1;
222     }
223 }
224
225 int
226 vnet_lisp_gpe_add_del_fwd_entry (vnet_lisp_gpe_add_del_fwd_entry_args_t * a,
227                                  u32 * hw_if_indexp)
228 {
229   lisp_gpe_main_t * lgm = &lisp_gpe_main;
230   ip_adjacency_t adj, * adjp;
231   u32 adj_index, rv, tun_index = ~0;
232   ip_prefix_t * dpref, * spref;
233   uword * lookup_next_index, * lgpe_sw_if_index, * lnip;
234   u8 ip_ver;
235
236   if (vnet_lisp_gpe_enable_disable_status() == 0)
237     {
238       clib_warning ("LISP is disabled!");
239       return VNET_API_ERROR_LISP_DISABLED;
240     }
241
242   /* treat negative fwd entries separately */
243   if (a->is_negative)
244     return add_del_negative_fwd_entry (lgm, a);
245
246   dpref = &gid_address_ippref(&a->deid);
247   spref = &gid_address_ippref(&a->seid);
248   ip_ver = ip_prefix_version(dpref);
249
250   /* add/del tunnel to tunnels pool and prepares rewrite */
251   rv = add_del_ip_tunnel (a, &tun_index);
252   if (rv)
253     return rv;
254
255   /* setup adjacency for eid */
256   memset (&adj, 0, sizeof(adj));
257   adj.n_adj = 1;
258
259   /* fill in lookup_next_index with a 'legal' value to avoid problems */
260   adj.lookup_next_index = (ip_ver == IP4) ?
261           lgm->ip4_lookup_next_lgpe_ip4_lookup :
262           lgm->ip6_lookup_next_lgpe_ip6_lookup;
263
264   if (a->is_add)
265     {
266       /* send packets that hit this adj to lisp-gpe interface output node in
267        * requested vrf. */
268       lnip = (ip_ver == IP4) ?
269               lgm->lgpe_ip4_lookup_next_index_by_table_id :
270               lgm->lgpe_ip6_lookup_next_index_by_table_id;
271       lookup_next_index = hash_get(lnip, a->table_id);
272       lgpe_sw_if_index = hash_get(lgm->lisp_gpe_hw_if_index_by_table_id,
273                                   a->table_id);
274
275       /* the assumption is that the interface must've been created before
276        * programming the dp */
277       ASSERT(lookup_next_index != 0);
278       ASSERT(lgpe_sw_if_index != 0);
279
280       /* hijack explicit fib index to store lisp interface node index and
281        * if_address_index for the tunnel index */
282       adj.explicit_fib_index = lookup_next_index[0];
283       adj.if_address_index = tun_index;
284       adj.rewrite_header.sw_if_index = lgpe_sw_if_index[0];
285     }
286
287   /* add/delete route for prefix */
288   rv = ip_sd_fib_add_del_route (lgm, dpref, spref, a->table_id, &adj,
289                                 a->is_add);
290
291   /* check that everything worked */
292   if (CLIB_DEBUG && a->is_add)
293     {
294       adj_index = ip_sd_fib_get_route (lgm, dpref, spref, a->table_id);
295       ASSERT(adj_index != 0);
296
297       adjp = ip_get_adjacency ((ip_ver == IP4) ? lgm->lm4 : lgm->lm6,
298                                adj_index);
299
300       ASSERT(adjp != 0);
301       ASSERT(adjp->if_address_index == tun_index);
302     }
303
304   return rv;
305 }
306
307 static clib_error_t *
308 lisp_gpe_add_del_fwd_entry_command_fn (vlib_main_t * vm,
309                                        unformat_input_t * input,
310                                        vlib_cli_command_t * cmd)
311 {
312   unformat_input_t _line_input, * line_input = &_line_input;
313   u8 is_add = 1;
314   ip_address_t slocator, dlocator, *slocators = 0, *dlocators = 0;
315   ip_prefix_t * prefp;
316   gid_address_t * eids = 0, eid;
317   clib_error_t * error = 0;
318   u32 i;
319   int rv;
320
321   prefp = &gid_address_ippref(&eid);
322
323   /* Get a line of input. */
324   if (! unformat_user (input, unformat_line_input, line_input))
325     return 0;
326
327   while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT)
328     {
329       if (unformat (line_input, "del"))
330         is_add = 0;
331       else if (unformat (line_input, "add"))
332         is_add = 1;
333       else if (unformat (line_input, "eid %U slocator %U dlocator %U",
334                          unformat_ip_prefix, prefp,
335                          unformat_ip_address, &slocator,
336                          unformat_ip_address, &dlocator))
337         {
338           vec_add1 (eids, eid);
339           vec_add1 (slocators, slocator);
340           vec_add1 (dlocators, dlocator);
341         }
342       else
343         {
344           error = unformat_parse_error (line_input);
345           goto done;
346         }
347     }
348   unformat_free (line_input);
349
350   if (vec_len (eids) + vec_len (slocators) == 0)
351     {
352       error = clib_error_return (0, "expected ip4/ip6 eids/locators.");
353       goto done;
354     }
355
356   if (vec_len (eids) != vec_len (slocators))
357     {
358       error = clib_error_return (0, "number of eids not equal to that of "
359           "locators.");
360       goto done;
361     }
362
363   for (i = 0; i < vec_len(eids); i++)
364     {
365       vnet_lisp_gpe_add_del_fwd_entry_args_t a;
366       memset (&a, 0, sizeof(a));
367
368       a.is_add = is_add;
369       a.deid = eids[i];
370       a.slocator = slocators[i];
371       a.dlocator = dlocators[i];
372       rv = vnet_lisp_gpe_add_del_fwd_entry (&a, 0);
373       if (0 != rv)
374         {
375           error = clib_error_return(0, "failed to %s gpe maptunnel!",
376                                     is_add ? "add" : "delete");
377           break;
378         }
379     }
380
381  done:
382   vec_free(eids);
383   vec_free(slocators);
384   vec_free(dlocators);
385   return error;
386 }
387
388 VLIB_CLI_COMMAND (lisp_gpe_add_del_fwd_entry_command, static) = {
389   .path = "lisp gpe maptunnel",
390   .short_help = "lisp gpe maptunnel eid <eid> sloc <src-locator> "
391       "dloc <dst-locator> [del]",
392   .function = lisp_gpe_add_del_fwd_entry_command_fn,
393 };
394
395 static u8 *
396 format_decap_next (u8 * s, va_list * args)
397 {
398   u32 next_index = va_arg (*args, u32);
399
400   switch (next_index)
401     {
402     case LISP_GPE_INPUT_NEXT_DROP:
403       return format (s, "drop");
404     case LISP_GPE_INPUT_NEXT_IP4_INPUT:
405       return format (s, "ip4");
406     case LISP_GPE_INPUT_NEXT_IP6_INPUT:
407       return format (s, "ip6");
408     default:
409       return format (s, "unknown %d", next_index);
410     }
411   return s;
412 }
413
414 u8 *
415 format_lisp_gpe_tunnel (u8 * s, va_list * args)
416 {
417   lisp_gpe_tunnel_t * t = va_arg (*args, lisp_gpe_tunnel_t *);
418   lisp_gpe_main_t * lgm = &lisp_gpe_main;
419
420   s = format (s,
421               "[%d] %U (src) %U (dst) fibs: encap %d, decap %d",
422               t - lgm->tunnels,
423               format_ip_address, &t->src,
424               format_ip_address, &t->dst,
425               t->encap_fib_index,
426               t->decap_fib_index);
427
428   s = format (s, " decap next %U\n", format_decap_next, t->decap_next_index);
429   s = format (s, "lisp ver %d ", (t->ver_res>>6));
430
431 #define _(n,v) if (t->flags & v) s = format (s, "%s-bit ", #n);
432   foreach_lisp_gpe_flag_bit;
433 #undef _
434
435   s = format (s, "next_protocol %d ver_res %x res %x\n",
436               t->next_protocol, t->ver_res, t->res);
437
438   s = format (s, "iid %d (0x%x)\n", t->vni, t->vni);
439   return s;
440 }
441
442 static clib_error_t *
443 show_lisp_gpe_tunnel_command_fn (vlib_main_t * vm,
444                                 unformat_input_t * input,
445                                 vlib_cli_command_t * cmd)
446 {
447   lisp_gpe_main_t * lgm = &lisp_gpe_main;
448   lisp_gpe_tunnel_t * t;
449   
450   if (pool_elts (lgm->tunnels) == 0)
451     vlib_cli_output (vm, "No lisp-gpe tunnels configured...");
452
453   pool_foreach (t, lgm->tunnels,
454   ({
455     vlib_cli_output (vm, "%U", format_lisp_gpe_tunnel, t);
456   }));
457   
458   return 0;
459 }
460
461 VLIB_CLI_COMMAND (show_lisp_gpe_tunnel_command, static) = {
462     .path = "show lisp gpe tunnel",
463     .function = show_lisp_gpe_tunnel_command_fn,
464 };
465
466 u8
467 vnet_lisp_gpe_enable_disable_status(void)
468 {
469   lisp_gpe_main_t * lgm = &lisp_gpe_main;
470
471   return lgm->is_en;
472 }
473
474 clib_error_t *
475 vnet_lisp_gpe_enable_disable (vnet_lisp_gpe_enable_disable_args_t * a)
476 {
477   lisp_gpe_main_t * lgm = &lisp_gpe_main;
478   vnet_main_t * vnm = lgm->vnet_main;
479
480   if (a->is_en)
481     {
482       /* add lgpe_ip4_lookup as possible next_node for ip4 lookup */
483       if (lgm->ip4_lookup_next_lgpe_ip4_lookup == ~0)
484         {
485           lgm->ip4_lookup_next_lgpe_ip4_lookup = vlib_node_add_next (
486               vnm->vlib_main, ip4_lookup_node.index,
487               lgpe_ip4_lookup_node.index);
488         }
489       /* add lgpe_ip6_lookup as possible next_node for ip6 lookup */
490       if (lgm->ip6_lookup_next_lgpe_ip6_lookup == ~0)
491         {
492           lgm->ip6_lookup_next_lgpe_ip6_lookup = vlib_node_add_next (
493               vnm->vlib_main, ip6_lookup_node.index,
494               lgpe_ip6_lookup_node.index);
495         }
496       else
497         {
498           /* ask cp to re-add ifaces and defaults */
499         }
500
501       lgm->is_en = 1;
502     }
503   else
504     {
505       CLIB_UNUSED(uword * val);
506       hash_pair_t * p;
507       u32 * table_ids = 0, * table_id;
508       lisp_gpe_tunnel_key_t * tunnels = 0, * tunnel;
509       vnet_lisp_gpe_add_del_fwd_entry_args_t _at, * at = &_at;
510       vnet_lisp_gpe_add_del_iface_args_t _ai, * ai= &_ai;
511
512       /* remove all tunnels */
513       mhash_foreach(tunnel, val, &lgm->lisp_gpe_tunnel_by_key, ({
514         vec_add1(tunnels, tunnel[0]);
515       }));
516
517       vec_foreach(tunnel, tunnels) {
518         memset(at, 0, sizeof(at[0]));
519         at->is_add = 0;
520         gid_address_type(&at->deid) = GID_ADDR_IP_PREFIX;
521         ip_prefix_copy(&gid_address_ippref(&at->deid), &tunnel->eid);
522         ip_address_copy(&at->dlocator, &tunnel->dst_loc);
523         vnet_lisp_gpe_add_del_fwd_entry (at, 0);
524       }
525       vec_free(tunnels);
526
527       /* disable all ifaces */
528       hash_foreach_pair(p, lgm->lisp_gpe_hw_if_index_by_table_id, ({
529         vec_add1(table_ids, p->key);
530       }));
531
532       vec_foreach(table_id, table_ids) {
533         ai->is_add = 0;
534         ai->table_id = table_id[0];
535
536         /* disables interface and removes defaults */
537         vnet_lisp_gpe_add_del_iface(ai, 0);
538       }
539       vec_free(table_ids);
540       lgm->is_en = 0;
541     }
542
543   return 0;
544 }
545
546 static clib_error_t *
547 lisp_gpe_enable_disable_command_fn (vlib_main_t * vm, unformat_input_t * input,
548                                     vlib_cli_command_t * cmd)
549 {
550   unformat_input_t _line_input, * line_input = &_line_input;
551   u8 is_en = 1;
552   vnet_lisp_gpe_enable_disable_args_t _a, * a = &_a;
553
554   /* Get a line of input. */
555   if (! unformat_user (input, unformat_line_input, line_input))
556     return 0;
557
558   while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT)
559     {
560       if (unformat (line_input, "enable"))
561         is_en = 1;
562       else if (unformat (line_input, "disable"))
563         is_en = 0;
564       else
565         {
566           return clib_error_return (0, "parse error: '%U'",
567                                    format_unformat_error, line_input);
568         }
569     }
570   a->is_en = is_en;
571   return vnet_lisp_gpe_enable_disable (a);
572 }
573
574 VLIB_CLI_COMMAND (enable_disable_lisp_gpe_command, static) = {
575   .path = "lisp gpe",
576   .short_help = "lisp gpe [enable|disable]",
577   .function = lisp_gpe_enable_disable_command_fn,
578 };
579
580 static clib_error_t *
581 lisp_show_iface_command_fn (vlib_main_t * vm,
582                             unformat_input_t * input,
583                             vlib_cli_command_t * cmd)
584 {
585   lisp_gpe_main_t * lgm = &lisp_gpe_main;
586   hash_pair_t * p;
587
588   vlib_cli_output (vm, "%=10s%=12s", "vrf", "hw_if_index");
589   hash_foreach_pair (p, lgm->lisp_gpe_hw_if_index_by_table_id, ({
590     vlib_cli_output (vm, "%=10d%=10d", p->key, p->value[0]);
591   }));
592   return 0;
593 }
594
595 VLIB_CLI_COMMAND (lisp_show_iface_command) = {
596     .path = "show lisp gpe interface",
597     .short_help = "show lisp gpe interface",
598     .function = lisp_show_iface_command_fn,
599 };
600
601 clib_error_t *
602 lisp_gpe_init (vlib_main_t *vm)
603 {
604   lisp_gpe_main_t * lgm = &lisp_gpe_main;
605   clib_error_t * error = 0;
606
607   if ((error = vlib_call_init_function (vm, ip_main_init)))
608     return error;
609
610   if ((error = vlib_call_init_function (vm, ip4_lookup_init)))
611     return error;
612
613   lgm->vnet_main = vnet_get_main();
614   lgm->vlib_main = vm;
615   lgm->im4 = &ip4_main;
616   lgm->im6 = &ip6_main;
617   lgm->lm4 = &ip4_main.lookup_main;
618   lgm->lm6 = &ip6_main.lookup_main;
619   lgm->ip4_lookup_next_lgpe_ip4_lookup = ~0;
620   lgm->ip6_lookup_next_lgpe_ip6_lookup = ~0;
621
622   mhash_init (&lgm->lisp_gpe_tunnel_by_key, sizeof(uword),
623               sizeof(lisp_gpe_tunnel_key_t));
624
625   udp_register_dst_port (vm, UDP_DST_PORT_lisp_gpe, 
626                          lisp_gpe_ip4_input_node.index, 1 /* is_ip4 */);
627   udp_register_dst_port (vm, UDP_DST_PORT_lisp_gpe6,
628                          lisp_gpe_ip6_input_node.index, 0 /* is_ip4 */);
629   return 0;
630 }
631
632 u8 *
633 format_vnet_lisp_gpe_status (u8 * s, va_list * args)
634 {
635   lisp_gpe_main_t * lgm = &lisp_gpe_main;
636   return format (s, "%s", lgm->is_en ? "enabled" : "disabled");
637 }
638
639 VLIB_INIT_FUNCTION(lisp_gpe_init);