ee198f210042a7de3b3defd8f905bbfbe6d44736
[vpp.git] / src / plugins / sixrd / sixrd.c
1 /*
2  * Copyright (c) 2015 Cisco and/or its affiliates.
3  * Licensed under the Apache License, Version 2.0 (the "License");
4  * you may not use this file except in compliance with the License.
5  * You may obtain a copy of the License at:
6  *
7  *     http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14  */
15
16 #include "sixrd.h"
17 #include <vnet/plugin/plugin.h>
18
19 #include <vnet/fib/fib_table.h>
20 #include <vnet/fib/ip6_fib.h>
21 #include <vnet/adj/adj.h>
22 #include <vpp/app/version.h>
23
24 /*
25  * This code supports the following sixrd modes:
26  * 
27  * 32 EA bits (Complete IPv4 address is embedded):
28  *   ea_bits_len = 32
29  * IPv4 suffix is embedded:
30  *   ea_bits_len = < 32
31  * No embedded address bits (1:1 mode):
32  *   ea_bits_len = 0
33  */
34
35 sixrd_main_t sixrd_main;
36
37 int
38 sixrd_create_domain (ip6_address_t *ip6_prefix,
39                      u8 ip6_prefix_len,
40                      ip4_address_t *ip4_prefix,
41                      u8 ip4_prefix_len,
42                      ip4_address_t *ip4_src,
43                      u32 *sixrd_domain_index,
44                      u16 mtu)
45 {
46   dpo_id_t dpo_v6 = DPO_INVALID, dpo_v4 = DPO_INVALID;
47   sixrd_main_t *mm = &sixrd_main;
48   fib_node_index_t fei;
49   sixrd_domain_t *d;
50
51   /* Get domain index */
52   pool_get_aligned(mm->domains, d, CLIB_CACHE_LINE_BYTES);
53   memset(d, 0, sizeof (*d));
54   *sixrd_domain_index = d - mm->domains;
55
56   /* Init domain struct */
57   d->ip4_prefix.as_u32 = ip4_prefix->as_u32;
58   d->ip4_prefix_len = ip4_prefix_len;
59   d->ip6_prefix = *ip6_prefix;
60   d->ip6_prefix_len = ip6_prefix_len;
61   d->ip4_src = *ip4_src;
62   d->mtu = mtu;
63
64   if (ip4_prefix_len < 32)
65     d->shift = 64 - ip6_prefix_len + (32 - ip4_prefix_len);
66     
67   /* Create IPv6 route/adjacency */
68   fib_prefix_t pfx6 = {
69       .fp_proto = FIB_PROTOCOL_IP6,
70       .fp_len = d->ip6_prefix_len,
71       .fp_addr = {
72           .ip6 = d->ip6_prefix,
73       },
74   };
75   sixrd_dpo_create(DPO_PROTO_IP6,
76                    *sixrd_domain_index,
77                    &dpo_v6);
78   fib_table_entry_special_dpo_add(0, &pfx6,
79                                   FIB_SOURCE_SIXRD,
80                                   FIB_ENTRY_FLAG_EXCLUSIVE,
81                                   &dpo_v6);
82   dpo_reset (&dpo_v6);
83
84   /*
85    * Multiple SIXRD domains may share same source IPv4 TEP
86    * In this case the route will exist and be SixRD sourced.
87    * Find the adj (if any) already contributed and modify it
88    */
89   fib_prefix_t pfx4 = {
90       .fp_proto = FIB_PROTOCOL_IP4,
91       .fp_len = 32,
92       .fp_addr = {
93           .ip4 = d->ip4_src,
94       },
95   };
96   fei = fib_table_lookup_exact_match(0, &pfx4);
97
98   if (FIB_NODE_INDEX_INVALID != fei)
99   {
100       dpo_id_t dpo = DPO_INVALID;
101
102       if (fib_entry_get_dpo_for_source (fei, FIB_SOURCE_SIXRD, &dpo))
103       {
104           /*
105            * modify the existing adj to indicate it's shared
106            * skip to route add.
107            * It is locked to pair with the unlock below.
108            */
109           const dpo_id_t *sd_dpo;
110           sixrd_dpo_t *sd;
111
112           ASSERT(DPO_LOAD_BALANCE == dpo.dpoi_type);
113
114           sd_dpo = load_balance_get_bucket(dpo.dpoi_index, 0);
115           sd = sixrd_dpo_get (sd_dpo->dpoi_index);
116
117           sd->sd_domain = ~0;
118           dpo_copy (&dpo_v4, sd_dpo);
119           dpo_reset (&dpo);
120
121           goto route_add;
122       }
123   }
124   /* first time addition of the route */
125   sixrd_dpo_create(DPO_PROTO_IP4,
126                    *sixrd_domain_index,
127                    &dpo_v4);
128
129 route_add:
130   /*
131    * Create ip4 route. This is a reference counted add. If the prefix
132    * already exists and is SixRD sourced, it is now SixRD source n+1 times
133    * and will need to be removed n+1 times.
134    */
135   fib_table_entry_special_dpo_add(0, &pfx4,
136                                   FIB_SOURCE_SIXRD,
137                                   FIB_ENTRY_FLAG_EXCLUSIVE,
138                                   &dpo_v4);
139   dpo_reset (&dpo_v4);
140
141   return 0;
142 }
143
144 /*
145  * sixrd_delete_domain
146  */
147 int
148 sixrd_delete_domain (u32 sixrd_domain_index)
149 {
150   sixrd_main_t *mm = &sixrd_main;
151   sixrd_domain_t *d;
152
153   if (pool_is_free_index(mm->domains, sixrd_domain_index)) {
154     clib_warning("SIXRD domain delete: domain does not exist: %d",
155                  sixrd_domain_index);
156     return -1;
157   }
158
159   d = pool_elt_at_index(mm->domains, sixrd_domain_index);
160
161   fib_prefix_t pfx = {
162       .fp_proto = FIB_PROTOCOL_IP4,
163       .fp_len = 32,
164       .fp_addr = {
165           .ip4 = d->ip4_src,
166       },
167   };
168   fib_table_entry_special_remove(0, &pfx, FIB_SOURCE_SIXRD);
169
170   fib_prefix_t pfx6 = {
171       .fp_proto = FIB_PROTOCOL_IP6,
172       .fp_len = d->ip6_prefix_len,
173       .fp_addr = {
174           .ip6 = d->ip6_prefix,
175       },
176   };
177   fib_table_entry_special_remove(0, &pfx6, FIB_SOURCE_SIXRD);
178
179   pool_put(mm->domains, d);
180
181   return 0;
182 }
183
184 static clib_error_t *
185 sixrd_add_domain_command_fn (vlib_main_t *vm,
186                            unformat_input_t *input,
187                            vlib_cli_command_t *cmd)
188 {
189   unformat_input_t _line_input, *line_input = &_line_input;
190   ip4_address_t ip4_prefix;
191   ip6_address_t ip6_prefix;
192   ip4_address_t ip4_src;
193   u32 ip6_prefix_len=0, ip4_prefix_len=0, sixrd_domain_index;
194   u32 num_m_args = 0;
195   /* Optional arguments */
196   u32 mtu = 0;
197   clib_error_t *error = 0;
198
199   /* Get a line of input. */
200   if (!unformat_user(input, unformat_line_input, line_input))
201     return 0;
202   while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT) {
203     if (unformat(line_input, "ip6-pfx %U/%d", unformat_ip6_address, &ip6_prefix, &ip6_prefix_len))
204       num_m_args++;
205     else if (unformat(line_input, "ip4-pfx %U/%d", unformat_ip4_address, &ip4_prefix, &ip4_prefix_len))
206       num_m_args++;
207     else if (unformat(line_input, "ip4-src %U", unformat_ip4_address, &ip4_src))
208       num_m_args++;
209     else if (unformat(line_input, "mtu %d", &mtu))
210       num_m_args++;
211     else {
212       error = clib_error_return(0, "unknown input `%U'",
213                                 format_unformat_error, line_input);
214       goto done;
215     }
216   }
217
218   if (num_m_args < 3) {
219     error = clib_error_return(0, "mandatory argument(s) missing");
220     goto done;
221   }
222
223   sixrd_create_domain(&ip6_prefix, ip6_prefix_len, &ip4_prefix, ip4_prefix_len,
224                       &ip4_src, &sixrd_domain_index, mtu);
225
226 done:
227   unformat_free (line_input);
228
229   return error;
230 }
231
232 static clib_error_t *
233 sixrd_del_domain_command_fn (vlib_main_t *vm,
234                            unformat_input_t *input,
235                            vlib_cli_command_t *cmd)
236 {
237   unformat_input_t _line_input, *line_input = &_line_input;
238   u32 num_m_args = 0;
239   u32 sixrd_domain_index;
240   clib_error_t *error = 0;
241
242   /* Get a line of input. */
243   if (! unformat_user(input, unformat_line_input, line_input))
244     return 0;
245  
246   while (unformat_check_input(line_input) != UNFORMAT_END_OF_INPUT) {
247     if (unformat(line_input, "index %d", &sixrd_domain_index))
248       num_m_args++;
249     else {
250       error = clib_error_return(0, "unknown input `%U'",
251                                 format_unformat_error, line_input);
252       goto done;
253     }
254   }
255
256   if (num_m_args != 1) {
257     error = clib_error_return(0, "mandatory argument(s) missing");
258     goto done;
259   }
260
261   sixrd_delete_domain(sixrd_domain_index);
262
263 done:
264   unformat_free (line_input);
265
266   return error;
267 }
268
269 static u8 *
270 format_sixrd_domain (u8 *s, va_list *args)
271 {
272   sixrd_domain_t *d = va_arg(*args, sixrd_domain_t *);
273   sixrd_main_t *mm = &sixrd_main;
274
275   s = format(s,
276              "[%d] ip6-pfx %U/%d ip4-pfx %U/%d ip4-src %U mtu %d",
277              d - mm->domains,
278              format_ip6_address, &d->ip6_prefix, d->ip6_prefix_len,
279              format_ip4_address, &d->ip4_prefix, d->ip4_prefix_len,
280              format_ip4_address, &d->ip4_src, d->mtu);
281
282   return s;
283 }
284
285 static clib_error_t *
286 show_sixrd_domain_command_fn (vlib_main_t *vm, unformat_input_t *input, vlib_cli_command_t *cmd)
287 {
288   sixrd_main_t *mm = &sixrd_main;
289   sixrd_domain_t *d;
290
291   if (pool_elts(mm->domains) == 0)
292     vlib_cli_output(vm, "No SIXRD domains are configured...");
293
294   pool_foreach(d, mm->domains, ({vlib_cli_output(vm, "%U", format_sixrd_domain, d);}));
295
296   return 0;
297
298 }
299
300 static clib_error_t *
301 show_sixrd_stats_command_fn (vlib_main_t *vm, unformat_input_t *input, vlib_cli_command_t *cmd)
302 {
303   sixrd_main_t *mm = &sixrd_main;
304   sixrd_domain_t *d;
305   int domains = 0, domaincount = 0;
306   if (pool_elts (mm->domains) == 0)
307     vlib_cli_output (vm, "No SIXRD domains are configured...");
308
309   pool_foreach(d, mm->domains, ({
310     domains += sizeof(*d);
311     domaincount++;
312   }));
313
314   vlib_cli_output(vm, "SIXRD domains structure: %d\n", sizeof (sixrd_domain_t));
315   vlib_cli_output(vm, "SIXRD domains: %d (%d bytes)\n", domaincount, domains);
316
317   return 0;
318 }
319
320 /*
321  * packet trace format function
322  */
323 u8 *
324 format_sixrd_trace (u8 *s, va_list *args)
325 {
326   CLIB_UNUSED(vlib_main_t *vm) = va_arg (*args, vlib_main_t *);
327   CLIB_UNUSED(vlib_node_t *node) = va_arg (*args, vlib_node_t *);
328   sixrd_trace_t *t = va_arg (*args, sixrd_trace_t *);
329   u32 sixrd_domain_index = t->sixrd_domain_index;
330
331   s = format(s, "SIXRD domain index: %d", sixrd_domain_index);
332
333   return s;
334 }
335
336 VLIB_CLI_COMMAND(sixrd_add_domain_command, static) = {
337   .path = "sixrd add domain",
338   .short_help = 
339   "sixrd add domain ip6-pfx <ip6-pfx> ip4-pfx <ip4-pfx> ip4-src <ip4-addr>",
340   .function = sixrd_add_domain_command_fn,
341 };
342
343 VLIB_CLI_COMMAND(sixrd_del_command, static) = {
344   .path = "sixrd del domain",
345   .short_help = 
346   "sixrd del domain index <domain>",
347   .function = sixrd_del_domain_command_fn,
348 };
349
350 VLIB_CLI_COMMAND(show_sixrd_domain_command, static) = {
351   .path = "show sixrd domain",
352   .function = show_sixrd_domain_command_fn,
353 };
354
355 VLIB_CLI_COMMAND(show_sixrd_stats_command, static) = {
356   .path = "show sixrd stats",
357   .function = show_sixrd_stats_command_fn,
358 };
359
360 /* *INDENT-OFF* */
361 VLIB_PLUGIN_REGISTER () ={
362     .version = VPP_BUILD_VER,
363     .description = "IPv6 Rapid Deployment on IPv4 Infrastructure (RFC5969)",
364 };
365 /* *INDENT-ON* */
366
367 static clib_error_t * sixrd_init (vlib_main_t * vm)
368 {
369   sixrd_main_t *mm = &sixrd_main;
370
371   mm->vnet_main = vnet_get_main();
372   mm->vlib_main = vm;
373
374   sixrd_dpo_module_init ();
375
376   return (NULL);
377 }
378
379 VLIB_INIT_FUNCTION (sixrd_init);