c11 safe string handling support
[vpp.git] / src / plugins / ioam / encap / ip6_ioam_pot.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 #include <vlib/vlib.h>
16 #include <vnet/vnet.h>
17 #include <vnet/pg/pg.h>
18 #include <vppinfra/error.h>
19 #include <vnet/ip/ip6.h>
20
21 #include <vppinfra/hash.h>
22 #include <vppinfra/error.h>
23 #include <vppinfra/elog.h>
24
25 #include <ioam/encap/ip6_ioam_pot.h>
26 #include <ioam/lib-pot/pot_util.h>
27
28 #define foreach_ip6_hop_by_hop_ioam_pot_stats                           \
29   _(PROCESSED, "Pkts with ip6 hop-by-hop pot options")                  \
30   _(PROFILE_MISS, "Pkts with ip6 hop-by-hop pot options but no profile set") \
31   _(PASSED, "Pkts with POT in Policy")                                  \
32   _(FAILED, "Pkts with POT out of Policy") 
33
34 static char * ip6_hop_by_hop_ioam_pot_stats_strings[] = {
35 #define _(sym,string) string,
36   foreach_ip6_hop_by_hop_ioam_pot_stats
37 #undef _
38 };
39
40 typedef enum {
41 #define _(sym,str) IP6_IOAM_POT_##sym,
42   foreach_ip6_hop_by_hop_ioam_pot_stats
43 #undef _
44   IP6_IOAM_POT_N_STATS,
45 } ip6_ioam_pot_stats_t;
46
47 typedef struct {
48   /* stats */
49   u64 counters[ARRAY_LEN(ip6_hop_by_hop_ioam_pot_stats_strings)];
50   
51   /* convenience */
52   vlib_main_t * vlib_main;
53   vnet_main_t * vnet_main;
54 } ip6_hop_by_hop_ioam_pot_main_t;
55
56 ip6_hop_by_hop_ioam_pot_main_t ip6_hop_by_hop_ioam_pot_main;
57
58 always_inline void 
59 ip6_ioam_stats_increment_counter (u32 counter_index, u64 increment)
60 {
61   ip6_hop_by_hop_ioam_pot_main_t *hm = &ip6_hop_by_hop_ioam_pot_main;
62
63   hm->counters[counter_index] += increment;
64 }
65
66
67 static u8 * format_ioam_pot (u8 * s, va_list * args)
68 {
69   ioam_pot_option_t * pot0 = va_arg (*args, ioam_pot_option_t *);
70   u64 random, cumulative;
71   random = cumulative = 0;
72   if (pot0) 
73     { 
74       random = clib_net_to_host_u64 (pot0->random);
75       cumulative = clib_net_to_host_u64 (pot0->cumulative);
76     }
77
78   s = format (s, "random = 0x%Lx, Cumulative = 0x%Lx, Index = 0x%x", 
79               random, cumulative, pot0 ? pot0->reserved_profile_id : ~0);
80   return s;
81 }
82
83 u8 *
84 ip6_hbh_ioam_proof_of_transit_trace_handler (u8 *s, ip6_hop_by_hop_option_t *opt)
85 {
86   ioam_pot_option_t *pot;
87
88   s = format (s, "    POT opt present\n");
89   pot = (ioam_pot_option_t *) opt;
90   s = format (s, "         %U\n", format_ioam_pot, pot);
91   return (s);
92 }
93
94 int
95 ip6_hbh_ioam_proof_of_transit_handler (vlib_buffer_t *b,
96                                        ip6_header_t *ip,
97                                        ip6_hop_by_hop_option_t *opt0)
98 {
99   ioam_pot_option_t * pot0;
100   u64 random = 0, cumulative = 0;
101   int rv = 0;
102   u8 pot_profile_index;
103   pot_profile *pot_profile = 0, *new_profile = 0;
104   u8 pot_encap = 0;
105
106   pot0 = (ioam_pot_option_t *) opt0;
107   pot_encap = (pot0->random == 0);
108   pot_profile_index = pot_profile_get_active_id();
109   pot_profile = pot_profile_get_active();
110   if (pot_encap && PREDICT_FALSE(!pot_profile))
111     {
112       ip6_ioam_stats_increment_counter (IP6_IOAM_POT_PROFILE_MISS, 1);
113       return(-1);
114     }
115   if (pot_encap)
116     {
117       pot0->reserved_profile_id =
118         pot_profile_index & PROFILE_ID_MASK;
119       pot_profile_incr_usage_stats(pot_profile);
120     } 
121   else 
122     { /* Non encap node */
123       if (PREDICT_FALSE(pot0->reserved_profile_id != 
124                         pot_profile_index || pot_profile == 0)) 
125         {
126           /* New profile announced by encap node. */
127           new_profile =
128             pot_profile_find(pot0->reserved_profile_id); 
129           if (PREDICT_FALSE(new_profile == 0 ||
130                             new_profile->valid == 0)) 
131             {
132               ip6_ioam_stats_increment_counter (IP6_IOAM_POT_PROFILE_MISS, 1);
133               return(-1);
134             } 
135           else 
136             {
137               pot_profile_index = pot0->reserved_profile_id;
138               pot_profile = new_profile;
139               pot_profile_set_active(pot_profile_index);
140               pot_profile_reset_usage_stats(pot_profile);
141             }
142         }
143       pot_profile_incr_usage_stats(pot_profile);
144     }
145
146   if (pot0->random == 0) 
147     {
148       pot0->random = clib_host_to_net_u64(pot_generate_random(pot_profile));
149       pot0->cumulative = 0;
150     }
151   random = clib_net_to_host_u64(pot0->random);
152   cumulative = clib_net_to_host_u64(pot0->cumulative);
153   pot0->cumulative = clib_host_to_net_u64(
154                                           pot_update_cumulative(pot_profile,
155                                                                 cumulative,
156                                                                 random));
157   ip6_ioam_stats_increment_counter (IP6_IOAM_POT_PROCESSED, 1);
158
159   return (rv);
160 }
161
162 int
163 ip6_hbh_ioam_proof_of_transit_pop_handler (vlib_buffer_t *b, ip6_header_t *ip,
164                                            ip6_hop_by_hop_option_t *opt0)
165 {
166   ioam_pot_option_t * pot0;
167   u64 random = 0;
168   u64 cumulative = 0;
169   int rv = 0;
170   pot_profile *pot_profile = 0;
171   u8 result = 0;
172
173   pot0 = (ioam_pot_option_t *) opt0;
174   random = clib_net_to_host_u64(pot0->random);
175   cumulative = clib_net_to_host_u64(pot0->cumulative);
176   pot_profile = pot_profile_get_active();
177   result =  pot_validate (pot_profile,
178                           cumulative, random);
179
180   if (result == 1)
181     {
182       ip6_ioam_stats_increment_counter (IP6_IOAM_POT_PASSED, 1);
183     }
184   else
185     {
186       ip6_ioam_stats_increment_counter (IP6_IOAM_POT_FAILED, 1);
187     }
188   return (rv);
189 }
190
191 int ip6_hop_by_hop_ioam_pot_rewrite_handler (u8 *rewrite_string, u8 *rewrite_size)
192 {
193   ioam_pot_option_t * pot_option;
194   if (rewrite_string && *rewrite_size == sizeof(ioam_pot_option_t))
195     {
196       pot_option = (ioam_pot_option_t *)rewrite_string;
197       pot_option->hdr.type = HBH_OPTION_TYPE_IOAM_PROOF_OF_TRANSIT
198         | HBH_OPTION_TYPE_DATA_CHANGE_ENROUTE;
199       pot_option->hdr.length = sizeof (ioam_pot_option_t) - 
200         sizeof (ip6_hop_by_hop_option_t);
201       return(0);
202     }
203   return(-1);
204 }
205
206 static clib_error_t *
207 ip6_show_ioam_pot_cmd_fn (vlib_main_t * vm,
208                           unformat_input_t * input,
209                           vlib_cli_command_t * cmd)
210 {
211   ip6_hop_by_hop_ioam_pot_main_t *hm = &ip6_hop_by_hop_ioam_pot_main;
212   u8 *s = 0;
213   int i = 0;
214
215   for ( i = 0; i < IP6_IOAM_POT_N_STATS; i++)
216     {
217       s = format(s, " %s - %lu\n", ip6_hop_by_hop_ioam_pot_stats_strings[i],
218                hm->counters[i]);
219     }
220
221   vlib_cli_output(vm, "%v", s);
222   vec_free(s);
223   return 0;
224 }
225
226
227 VLIB_CLI_COMMAND (ip6_show_ioam_pot_cmd, static) = {
228   .path = "show ioam pot",
229   .short_help = "iOAM pot statistics",
230   .function = ip6_show_ioam_pot_cmd_fn,
231 };
232
233
234 static clib_error_t *
235 ip6_hop_by_hop_ioam_pot_init (vlib_main_t * vm)
236 {
237   ip6_hop_by_hop_ioam_pot_main_t * hm = &ip6_hop_by_hop_ioam_pot_main;
238   clib_error_t * error;
239
240   if ((error = vlib_call_init_function (vm, ip6_hop_by_hop_ioam_init)))
241     return(error);
242
243   hm->vlib_main = vm;
244   hm->vnet_main = vnet_get_main();
245   clib_memset(hm->counters, 0, sizeof(hm->counters));
246   
247   if (ip6_hbh_register_option(HBH_OPTION_TYPE_IOAM_PROOF_OF_TRANSIT, ip6_hbh_ioam_proof_of_transit_handler,
248                               ip6_hbh_ioam_proof_of_transit_trace_handler) < 0)
249     return (clib_error_create("registration of HBH_OPTION_TYPE_IOAM_PROOF_OF_TRANSIT failed"));
250
251   if (ip6_hbh_add_register_option(HBH_OPTION_TYPE_IOAM_PROOF_OF_TRANSIT,
252                                   sizeof(ioam_pot_option_t),
253                                   ip6_hop_by_hop_ioam_pot_rewrite_handler) < 0)
254     return (clib_error_create("registration of HBH_OPTION_TYPE_IOAM_PROOF_OF_TRANSIT for rewrite failed"));
255
256   if (ip6_hbh_pop_register_option(HBH_OPTION_TYPE_IOAM_PROOF_OF_TRANSIT,
257                                   ip6_hbh_ioam_proof_of_transit_pop_handler) < 0)
258     return (clib_error_create("registration of HBH_OPTION_TYPE_IOAM_PROOF_OF_TRANSIT POP failed"));
259
260   return (0);
261 }
262
263 VLIB_INIT_FUNCTION (ip6_hop_by_hop_ioam_pot_init);
264
265