3994150d895a10d585363bc6e232fe2dfe93bc74
[vpp.git] / src / vnet / ipsec / ipsec_api.c
1 /*
2  *------------------------------------------------------------------
3  * ipsec_api.c - ipsec api
4  *
5  * Copyright (c) 2016 Cisco and/or its affiliates.
6  * Licensed under the Apache License, Version 2.0 (the "License");
7  * you may not use this file except in compliance with the License.
8  * You may obtain a copy of the License at:
9  *
10  *     http://www.apache.org/licenses/LICENSE-2.0
11  *
12  * Unless required by applicable law or agreed to in writing, software
13  * distributed under the License is distributed on an "AS IS" BASIS,
14  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15  * See the License for the specific language governing permissions and
16  * limitations under the License.
17  *------------------------------------------------------------------
18  */
19
20 #include <vnet/vnet.h>
21 #include <vlibmemory/api.h>
22
23 #include <vnet/interface.h>
24 #include <vnet/api_errno.h>
25 #include <vnet/ip/ip.h>
26 #include <vnet/ip/ip_types_api.h>
27 #include <vnet/ipsec/ipsec_types_api.h>
28 #include <vnet/tunnel/tunnel_types_api.h>
29 #include <vnet/fib/fib.h>
30 #include <vnet/ipip/ipip.h>
31 #include <vnet/tunnel/tunnel_types_api.h>
32 #include <vnet/ipsec/ipsec.h>
33 #include <vnet/ipsec/ipsec_tun.h>
34 #include <vnet/ipsec/ipsec_itf.h>
35
36 #include <vnet/format_fns.h>
37 #include <vnet/ipsec/ipsec.api_enum.h>
38 #include <vnet/ipsec/ipsec.api_types.h>
39
40 #define REPLY_MSG_ID_BASE ipsec_main.msg_id_base
41 #include <vlibapi/api_helper_macros.h>
42
43 static void
44 vl_api_ipsec_spd_add_del_t_handler (vl_api_ipsec_spd_add_del_t * mp)
45 {
46   vlib_main_t *vm __attribute__ ((unused)) = vlib_get_main ();
47   vl_api_ipsec_spd_add_del_reply_t *rmp;
48   int rv;
49
50   rv = ipsec_add_del_spd (vm, ntohl (mp->spd_id), mp->is_add);
51
52   REPLY_MACRO (VL_API_IPSEC_SPD_ADD_DEL_REPLY);
53 }
54
55 static void vl_api_ipsec_interface_add_del_spd_t_handler
56   (vl_api_ipsec_interface_add_del_spd_t * mp)
57 {
58   vlib_main_t *vm __attribute__ ((unused)) = vlib_get_main ();
59   vl_api_ipsec_interface_add_del_spd_reply_t *rmp;
60   int rv;
61   u32 sw_if_index __attribute__ ((unused));
62   u32 spd_id __attribute__ ((unused));
63
64   sw_if_index = ntohl (mp->sw_if_index);
65   spd_id = ntohl (mp->spd_id);
66
67   VALIDATE_SW_IF_INDEX (mp);
68
69   rv = ipsec_set_interface_spd (vm, sw_if_index, spd_id, mp->is_add);
70
71   BAD_SW_IF_INDEX_LABEL;
72
73   REPLY_MACRO (VL_API_IPSEC_INTERFACE_ADD_DEL_SPD_REPLY);
74 }
75
76 static void vl_api_ipsec_tunnel_protect_update_t_handler
77   (vl_api_ipsec_tunnel_protect_update_t * mp)
78 {
79   vlib_main_t *vm __attribute__ ((unused)) = vlib_get_main ();
80   vl_api_ipsec_tunnel_protect_update_reply_t *rmp;
81   u32 sw_if_index, ii, *sa_ins = NULL;
82   ip_address_t nh;
83   int rv;
84
85   sw_if_index = ntohl (mp->tunnel.sw_if_index);
86
87   VALIDATE_SW_IF_INDEX (&(mp->tunnel));
88
89   for (ii = 0; ii < mp->tunnel.n_sa_in; ii++)
90     vec_add1 (sa_ins, ntohl (mp->tunnel.sa_in[ii]));
91
92   ip_address_decode2 (&mp->tunnel.nh, &nh);
93
94   rv = ipsec_tun_protect_update (sw_if_index, &nh,
95                                  ntohl (mp->tunnel.sa_out), sa_ins);
96
97   BAD_SW_IF_INDEX_LABEL;
98
99   REPLY_MACRO (VL_API_IPSEC_TUNNEL_PROTECT_UPDATE_REPLY);
100 }
101
102 static void vl_api_ipsec_tunnel_protect_del_t_handler
103   (vl_api_ipsec_tunnel_protect_del_t * mp)
104 {
105   vlib_main_t *vm __attribute__ ((unused)) = vlib_get_main ();
106   vl_api_ipsec_tunnel_protect_del_reply_t *rmp;
107   ip_address_t nh;
108   u32 sw_if_index;
109   int rv;
110
111   sw_if_index = ntohl (mp->sw_if_index);
112
113   VALIDATE_SW_IF_INDEX (mp);
114
115   ip_address_decode2 (&mp->nh, &nh);
116   rv = ipsec_tun_protect_del (sw_if_index, &nh);
117
118   BAD_SW_IF_INDEX_LABEL;
119
120   REPLY_MACRO (VL_API_IPSEC_TUNNEL_PROTECT_DEL_REPLY);
121 }
122
123 typedef struct ipsec_dump_walk_ctx_t_
124 {
125   vl_api_registration_t *reg;
126   u32 context;
127   u32 sw_if_index;
128 } ipsec_dump_walk_ctx_t;
129
130 static walk_rc_t
131 send_ipsec_tunnel_protect_details (index_t itpi, void *arg)
132 {
133   ipsec_dump_walk_ctx_t *ctx = arg;
134   vl_api_ipsec_tunnel_protect_details_t *mp;
135   ipsec_tun_protect_t *itp;
136   u32 ii = 0;
137   ipsec_sa_t *sa;
138
139   itp = ipsec_tun_protect_get (itpi);
140
141   mp = vl_msg_api_alloc (sizeof (*mp) + (sizeof (u32) * itp->itp_n_sa_in));
142   clib_memset (mp, 0, sizeof (*mp));
143   mp->_vl_msg_id =
144     ntohs (REPLY_MSG_ID_BASE + VL_API_IPSEC_TUNNEL_PROTECT_DETAILS);
145   mp->context = ctx->context;
146
147   mp->tun.sw_if_index = htonl (itp->itp_sw_if_index);
148   ip_address_encode2 (itp->itp_key, &mp->tun.nh);
149
150   sa = ipsec_sa_get (itp->itp_out_sa);
151   mp->tun.sa_out = htonl (sa->id);
152   mp->tun.n_sa_in = itp->itp_n_sa_in;
153   /* *INDENT-OFF* */
154   FOR_EACH_IPSEC_PROTECT_INPUT_SA(itp, sa,
155   ({
156     mp->tun.sa_in[ii++] = htonl (sa->id);
157   }));
158   /* *INDENT-ON* */
159
160   vl_api_send_msg (ctx->reg, (u8 *) mp);
161
162   return (WALK_CONTINUE);
163 }
164
165 static void
166 vl_api_ipsec_tunnel_protect_dump_t_handler (vl_api_ipsec_tunnel_protect_dump_t
167                                             * mp)
168 {
169   vl_api_registration_t *reg;
170   u32 sw_if_index;
171
172   reg = vl_api_client_index_to_registration (mp->client_index);
173   if (!reg)
174     return;
175
176   ipsec_dump_walk_ctx_t ctx = {
177     .reg = reg,
178     .context = mp->context,
179   };
180
181   sw_if_index = ntohl (mp->sw_if_index);
182
183   if (~0 == sw_if_index)
184     {
185       ipsec_tun_protect_walk (send_ipsec_tunnel_protect_details, &ctx);
186     }
187   else
188     {
189       ipsec_tun_protect_walk_itf (sw_if_index,
190                                   send_ipsec_tunnel_protect_details, &ctx);
191     }
192 }
193
194 static int
195 ipsec_spd_action_decode (vl_api_ipsec_spd_action_t in,
196                          ipsec_policy_action_t * out)
197 {
198   in = clib_net_to_host_u32 (in);
199
200   switch (in)
201     {
202 #define _(v,f,s) case IPSEC_API_SPD_ACTION_##f: \
203       *out = IPSEC_POLICY_ACTION_##f;              \
204       return (0);
205       foreach_ipsec_policy_action
206 #undef _
207     }
208   return (VNET_API_ERROR_UNIMPLEMENTED);
209 }
210
211 static void vl_api_ipsec_spd_entry_add_del_t_handler
212   (vl_api_ipsec_spd_entry_add_del_t * mp)
213 {
214   vlib_main_t *vm __attribute__ ((unused)) = vlib_get_main ();
215   vl_api_ipsec_spd_entry_add_del_reply_t *rmp;
216   ip46_type_t itype;
217   u32 stat_index;
218   int rv;
219
220   stat_index = ~0;
221
222   ipsec_policy_t p;
223
224   clib_memset (&p, 0, sizeof (p));
225
226   p.id = ntohl (mp->entry.spd_id);
227   p.priority = ntohl (mp->entry.priority);
228
229   itype = ip_address_decode (&mp->entry.remote_address_start, &p.raddr.start);
230   ip_address_decode (&mp->entry.remote_address_stop, &p.raddr.stop);
231   ip_address_decode (&mp->entry.local_address_start, &p.laddr.start);
232   ip_address_decode (&mp->entry.local_address_stop, &p.laddr.stop);
233
234   p.is_ipv6 = (itype == IP46_TYPE_IP6);
235
236   p.protocol =
237     mp->entry.protocol ? mp->entry.protocol : IPSEC_POLICY_PROTOCOL_ANY;
238   p.rport.start = ntohs (mp->entry.remote_port_start);
239   p.rport.stop = ntohs (mp->entry.remote_port_stop);
240   p.lport.start = ntohs (mp->entry.local_port_start);
241   p.lport.stop = ntohs (mp->entry.local_port_stop);
242
243   rv = ipsec_spd_action_decode (mp->entry.policy, &p.policy);
244
245   if (rv)
246     goto out;
247
248   /* policy action resolve unsupported */
249   if (p.policy == IPSEC_POLICY_ACTION_RESOLVE)
250     {
251       clib_warning ("unsupported action: 'resolve'");
252       rv = VNET_API_ERROR_UNIMPLEMENTED;
253       goto out;
254     }
255   p.sa_id = ntohl (mp->entry.sa_id);
256   rv =
257     ipsec_policy_mk_type (mp->entry.is_outbound, p.is_ipv6, p.policy,
258                           &p.type);
259   if (rv)
260     goto out;
261
262   rv = ipsec_add_del_policy (vm, &p, mp->is_add, &stat_index);
263   if (rv)
264     goto out;
265
266 out:
267   /* *INDENT-OFF* */
268   REPLY_MACRO2 (VL_API_IPSEC_SPD_ENTRY_ADD_DEL_REPLY,
269   ({
270     rmp->stat_index = ntohl(stat_index);
271   }));
272   /* *INDENT-ON* */
273 }
274
275 static void
276 vl_api_ipsec_spd_entry_add_del_v2_t_handler (
277   vl_api_ipsec_spd_entry_add_del_v2_t *mp)
278 {
279   vlib_main_t *vm __attribute__ ((unused)) = vlib_get_main ();
280   vl_api_ipsec_spd_entry_add_del_reply_t *rmp;
281   ip46_type_t itype;
282   u32 stat_index;
283   int rv;
284
285   stat_index = ~0;
286
287   ipsec_policy_t p;
288
289   clib_memset (&p, 0, sizeof (p));
290
291   p.id = ntohl (mp->entry.spd_id);
292   p.priority = ntohl (mp->entry.priority);
293
294   itype = ip_address_decode (&mp->entry.remote_address_start, &p.raddr.start);
295   ip_address_decode (&mp->entry.remote_address_stop, &p.raddr.stop);
296   ip_address_decode (&mp->entry.local_address_start, &p.laddr.start);
297   ip_address_decode (&mp->entry.local_address_stop, &p.laddr.stop);
298
299   p.is_ipv6 = (itype == IP46_TYPE_IP6);
300
301   p.protocol = mp->entry.protocol;
302   p.rport.start = ntohs (mp->entry.remote_port_start);
303   p.rport.stop = ntohs (mp->entry.remote_port_stop);
304   p.lport.start = ntohs (mp->entry.local_port_start);
305   p.lport.stop = ntohs (mp->entry.local_port_stop);
306
307   rv = ipsec_spd_action_decode (mp->entry.policy, &p.policy);
308
309   if (rv)
310     goto out;
311
312   /* policy action resolve unsupported */
313   if (p.policy == IPSEC_POLICY_ACTION_RESOLVE)
314     {
315       clib_warning ("unsupported action: 'resolve'");
316       rv = VNET_API_ERROR_UNIMPLEMENTED;
317       goto out;
318     }
319   p.sa_id = ntohl (mp->entry.sa_id);
320   rv =
321     ipsec_policy_mk_type (mp->entry.is_outbound, p.is_ipv6, p.policy, &p.type);
322   if (rv)
323     goto out;
324
325   rv = ipsec_add_del_policy (vm, &p, mp->is_add, &stat_index);
326   if (rv)
327     goto out;
328
329 out:
330   REPLY_MACRO2 (VL_API_IPSEC_SPD_ENTRY_ADD_DEL_V2_REPLY,
331                 ({ rmp->stat_index = ntohl (stat_index); }));
332 }
333
334 static void vl_api_ipsec_sad_entry_add_del_t_handler
335   (vl_api_ipsec_sad_entry_add_del_t * mp)
336 {
337   vl_api_ipsec_sad_entry_add_del_reply_t *rmp;
338   ipsec_key_t crypto_key, integ_key;
339   ipsec_crypto_alg_t crypto_alg;
340   ipsec_integ_alg_t integ_alg;
341   ipsec_protocol_t proto;
342   ipsec_sa_flags_t flags;
343   u32 id, spi, sa_index = ~0;
344   tunnel_t tun = {
345     .t_flags = TUNNEL_FLAG_NONE,
346     .t_encap_decap_flags = TUNNEL_ENCAP_DECAP_FLAG_NONE,
347     .t_dscp = 0,
348     .t_mode = TUNNEL_MODE_P2P,
349     .t_table_id = 0,
350     .t_hop_limit = 255,
351   };
352   int rv;
353
354   id = ntohl (mp->entry.sad_id);
355   if (!mp->is_add)
356     {
357       rv = ipsec_sa_unlock_id (id);
358       goto out;
359     }
360   spi = ntohl (mp->entry.spi);
361
362   rv = ipsec_proto_decode (mp->entry.protocol, &proto);
363
364   if (rv)
365     goto out;
366
367   rv = ipsec_crypto_algo_decode (mp->entry.crypto_algorithm, &crypto_alg);
368
369   if (rv)
370     goto out;
371
372   rv = ipsec_integ_algo_decode (mp->entry.integrity_algorithm, &integ_alg);
373
374   if (rv)
375     goto out;
376
377   ipsec_key_decode (&mp->entry.crypto_key, &crypto_key);
378   ipsec_key_decode (&mp->entry.integrity_key, &integ_key);
379
380   flags = ipsec_sa_flags_decode (mp->entry.flags);
381
382   ip_address_decode2 (&mp->entry.tunnel_src, &tun.t_src);
383   ip_address_decode2 (&mp->entry.tunnel_dst, &tun.t_dst);
384
385   rv = ipsec_sa_add_and_lock (id, spi, proto, crypto_alg, &crypto_key,
386                               integ_alg, &integ_key, flags, mp->entry.salt,
387                               htons (mp->entry.udp_src_port),
388                               htons (mp->entry.udp_dst_port), &tun, &sa_index);
389
390 out:
391   /* *INDENT-OFF* */
392   REPLY_MACRO2 (VL_API_IPSEC_SAD_ENTRY_ADD_DEL_REPLY,
393   {
394     rmp->stat_index = htonl (sa_index);
395   });
396   /* *INDENT-ON* */
397 }
398
399 static void vl_api_ipsec_sad_entry_add_del_v2_t_handler
400   (vl_api_ipsec_sad_entry_add_del_v2_t * mp)
401 {
402   vlib_main_t *vm __attribute__ ((unused)) = vlib_get_main ();
403   vl_api_ipsec_sad_entry_add_del_v2_reply_t *rmp;
404   ipsec_key_t crypto_key, integ_key;
405   ipsec_crypto_alg_t crypto_alg;
406   ipsec_integ_alg_t integ_alg;
407   ipsec_protocol_t proto;
408   ipsec_sa_flags_t flags;
409   u32 id, spi, sa_index = ~0;
410   int rv;
411   tunnel_t tun = {
412     .t_flags = TUNNEL_FLAG_NONE,
413     .t_encap_decap_flags = TUNNEL_ENCAP_DECAP_FLAG_NONE,
414     .t_dscp = 0,
415     .t_mode = TUNNEL_MODE_P2P,
416     .t_table_id = htonl (mp->entry.tx_table_id),
417     .t_hop_limit = 255,
418   };
419
420   id = ntohl (mp->entry.sad_id);
421   if (!mp->is_add)
422     {
423       rv = ipsec_sa_unlock_id (id);
424       goto out;
425     }
426
427   spi = ntohl (mp->entry.spi);
428
429   rv = ipsec_proto_decode (mp->entry.protocol, &proto);
430
431   if (rv)
432     goto out;
433
434   rv = ipsec_crypto_algo_decode (mp->entry.crypto_algorithm, &crypto_alg);
435
436   if (rv)
437     goto out;
438
439   rv = ipsec_integ_algo_decode (mp->entry.integrity_algorithm, &integ_alg);
440
441   if (rv)
442     goto out;
443
444   rv = tunnel_encap_decap_flags_decode (mp->entry.tunnel_flags,
445                                         &tun.t_encap_decap_flags);
446
447   if (rv)
448     goto out;
449
450   ipsec_key_decode (&mp->entry.crypto_key, &crypto_key);
451   ipsec_key_decode (&mp->entry.integrity_key, &integ_key);
452
453   flags = ipsec_sa_flags_decode (mp->entry.flags);
454   tun.t_dscp = ip_dscp_decode (mp->entry.dscp);
455
456   ip_address_decode2 (&mp->entry.tunnel_src, &tun.t_src);
457   ip_address_decode2 (&mp->entry.tunnel_dst, &tun.t_dst);
458
459     rv = ipsec_sa_add_and_lock (
460       id, spi, proto, crypto_alg, &crypto_key, integ_alg, &integ_key, flags,
461       mp->entry.salt, htons (mp->entry.udp_src_port),
462       htons (mp->entry.udp_dst_port), &tun, &sa_index);
463
464 out:
465   /* *INDENT-OFF* */
466   REPLY_MACRO2 (VL_API_IPSEC_SAD_ENTRY_ADD_DEL_V2_REPLY,
467   {
468     rmp->stat_index = htonl (sa_index);
469   });
470   /* *INDENT-ON* */
471 }
472
473 static int
474 ipsec_sad_entry_add_v3 (const vl_api_ipsec_sad_entry_v3_t *entry,
475                         u32 *sa_index)
476 {
477   ipsec_key_t crypto_key, integ_key;
478   ipsec_crypto_alg_t crypto_alg;
479   ipsec_integ_alg_t integ_alg;
480   ipsec_protocol_t proto;
481   ipsec_sa_flags_t flags;
482   u32 id, spi;
483   tunnel_t tun = { 0 };
484   int rv;
485
486   id = ntohl (entry->sad_id);
487   spi = ntohl (entry->spi);
488
489   rv = ipsec_proto_decode (entry->protocol, &proto);
490
491   if (rv)
492     return (rv);
493
494   rv = ipsec_crypto_algo_decode (entry->crypto_algorithm, &crypto_alg);
495
496   if (rv)
497     return (rv);
498
499   rv = ipsec_integ_algo_decode (entry->integrity_algorithm, &integ_alg);
500
501   if (rv)
502     return (rv);
503
504   flags = ipsec_sa_flags_decode (entry->flags);
505
506   if (flags & IPSEC_SA_FLAG_IS_TUNNEL)
507     {
508       rv = tunnel_decode (&entry->tunnel, &tun);
509
510       if (rv)
511         return (rv);
512     }
513
514   ipsec_key_decode (&entry->crypto_key, &crypto_key);
515   ipsec_key_decode (&entry->integrity_key, &integ_key);
516
517   return ipsec_sa_add_and_lock (id, spi, proto, crypto_alg, &crypto_key,
518                                 integ_alg, &integ_key, flags, entry->salt,
519                                 htons (entry->udp_src_port),
520                                 htons (entry->udp_dst_port), &tun, sa_index);
521 }
522
523 static void
524 vl_api_ipsec_sad_entry_add_del_v3_t_handler (
525   vl_api_ipsec_sad_entry_add_del_v3_t *mp)
526 {
527   vl_api_ipsec_sad_entry_add_del_v3_reply_t *rmp;
528   u32 id, sa_index = ~0;
529   int rv;
530
531   id = ntohl (mp->entry.sad_id);
532
533   if (!mp->is_add)
534     {
535       rv = ipsec_sa_unlock_id (id);
536     }
537   else
538     {
539       rv = ipsec_sad_entry_add_v3 (&mp->entry, &sa_index);
540     }
541
542   REPLY_MACRO2 (VL_API_IPSEC_SAD_ENTRY_ADD_DEL_V3_REPLY,
543                 { rmp->stat_index = htonl (sa_index); });
544 }
545
546 static void
547 vl_api_ipsec_sad_entry_del_t_handler (vl_api_ipsec_sad_entry_del_t *mp)
548 {
549   vl_api_ipsec_sad_entry_del_reply_t *rmp;
550   int rv;
551
552   rv = ipsec_sa_unlock_id (ntohl (mp->id));
553
554   REPLY_MACRO (VL_API_IPSEC_SAD_ENTRY_DEL_REPLY);
555 }
556
557 static void
558 vl_api_ipsec_sad_entry_add_t_handler (vl_api_ipsec_sad_entry_add_t *mp)
559 {
560   vl_api_ipsec_sad_entry_add_reply_t *rmp;
561   u32 sa_index = ~0;
562   int rv;
563
564   rv = ipsec_sad_entry_add_v3 (&mp->entry, &sa_index);
565
566   REPLY_MACRO2 (VL_API_IPSEC_SAD_ENTRY_ADD_REPLY,
567                 { rmp->stat_index = htonl (sa_index); });
568 }
569
570 static void
571 vl_api_ipsec_sad_entry_update_t_handler (vl_api_ipsec_sad_entry_update_t *mp)
572 {
573   vl_api_ipsec_sad_entry_update_reply_t *rmp;
574   u32 id;
575   tunnel_t tun = { 0 };
576   int rv;
577
578   id = ntohl (mp->sad_id);
579
580   if (mp->is_tun)
581     {
582       rv = tunnel_decode (&mp->tunnel, &tun);
583
584       if (rv)
585         goto out;
586     }
587
588   rv = ipsec_sa_update (id, htons (mp->udp_src_port), htons (mp->udp_dst_port),
589                         &tun, mp->is_tun);
590
591 out:
592   REPLY_MACRO (VL_API_IPSEC_SAD_ENTRY_UPDATE_REPLY);
593 }
594
595 static void
596 send_ipsec_spds_details (ipsec_spd_t * spd, vl_api_registration_t * reg,
597                          u32 context)
598 {
599   vl_api_ipsec_spds_details_t *mp;
600   u32 n_policies = 0;
601
602   mp = vl_msg_api_alloc (sizeof (*mp));
603   clib_memset (mp, 0, sizeof (*mp));
604   mp->_vl_msg_id = ntohs (REPLY_MSG_ID_BASE + VL_API_IPSEC_SPDS_DETAILS);
605   mp->context = context;
606
607   mp->spd_id = htonl (spd->id);
608 #define _(s, n) n_policies += vec_len (spd->policies[IPSEC_SPD_POLICY_##s]);
609   foreach_ipsec_spd_policy_type
610 #undef _
611     mp->npolicies = htonl (n_policies);
612
613   vl_api_send_msg (reg, (u8 *) mp);
614 }
615
616 static void
617 vl_api_ipsec_spds_dump_t_handler (vl_api_ipsec_spds_dump_t * mp)
618 {
619   vl_api_registration_t *reg;
620   ipsec_main_t *im = &ipsec_main;
621   ipsec_spd_t *spd;
622
623   reg = vl_api_client_index_to_registration (mp->client_index);
624   if (!reg)
625     return;
626
627   pool_foreach (spd, im->spds)  {
628     send_ipsec_spds_details (spd, reg, mp->context);
629   }
630 }
631
632 vl_api_ipsec_spd_action_t
633 ipsec_spd_action_encode (ipsec_policy_action_t in)
634 {
635   vl_api_ipsec_spd_action_t out = IPSEC_API_SPD_ACTION_BYPASS;
636
637   switch (in)
638     {
639 #define _(v,f,s) case IPSEC_POLICY_ACTION_##f: \
640       out = IPSEC_API_SPD_ACTION_##f;          \
641       break;
642       foreach_ipsec_policy_action
643 #undef _
644     }
645   return (clib_host_to_net_u32 (out));
646 }
647
648 static void
649 send_ipsec_spd_details (ipsec_policy_t * p, vl_api_registration_t * reg,
650                         u32 context)
651 {
652   vl_api_ipsec_spd_details_t *mp;
653
654   mp = vl_msg_api_alloc (sizeof (*mp));
655   clib_memset (mp, 0, sizeof (*mp));
656   mp->_vl_msg_id = ntohs (REPLY_MSG_ID_BASE + VL_API_IPSEC_SPD_DETAILS);
657   mp->context = context;
658
659   mp->entry.spd_id = htonl (p->id);
660   mp->entry.priority = htonl (p->priority);
661   mp->entry.is_outbound = ((p->type == IPSEC_SPD_POLICY_IP6_OUTBOUND) ||
662                            (p->type == IPSEC_SPD_POLICY_IP4_OUTBOUND));
663
664   ip_address_encode (&p->laddr.start, IP46_TYPE_ANY,
665                      &mp->entry.local_address_start);
666   ip_address_encode (&p->laddr.stop, IP46_TYPE_ANY,
667                      &mp->entry.local_address_stop);
668   ip_address_encode (&p->raddr.start, IP46_TYPE_ANY,
669                      &mp->entry.remote_address_start);
670   ip_address_encode (&p->raddr.stop, IP46_TYPE_ANY,
671                      &mp->entry.remote_address_stop);
672   mp->entry.local_port_start = htons (p->lport.start);
673   mp->entry.local_port_stop = htons (p->lport.stop);
674   mp->entry.remote_port_start = htons (p->rport.start);
675   mp->entry.remote_port_stop = htons (p->rport.stop);
676   mp->entry.protocol = p->protocol;
677   mp->entry.policy = ipsec_spd_action_encode (p->policy);
678   mp->entry.sa_id = htonl (p->sa_id);
679
680   vl_api_send_msg (reg, (u8 *) mp);
681 }
682
683 static void
684 vl_api_ipsec_spd_dump_t_handler (vl_api_ipsec_spd_dump_t * mp)
685 {
686   vl_api_registration_t *reg;
687   ipsec_main_t *im = &ipsec_main;
688   ipsec_spd_policy_type_t ptype;
689   ipsec_policy_t *policy;
690   ipsec_spd_t *spd;
691   uword *p;
692   u32 spd_index, *ii;
693
694   reg = vl_api_client_index_to_registration (mp->client_index);
695   if (!reg)
696     return;
697
698   p = hash_get (im->spd_index_by_spd_id, ntohl (mp->spd_id));
699   if (!p)
700     return;
701
702   spd_index = p[0];
703   spd = pool_elt_at_index (im->spds, spd_index);
704
705   FOR_EACH_IPSEC_SPD_POLICY_TYPE(ptype) {
706     vec_foreach(ii, spd->policies[ptype])
707       {
708         policy = pool_elt_at_index(im->policies, *ii);
709
710         if (mp->sa_id == ~(0) || ntohl (mp->sa_id) == policy->sa_id)
711           send_ipsec_spd_details (policy, reg, mp->context);
712       }
713   }
714 }
715
716 static void
717 send_ipsec_spd_interface_details (vl_api_registration_t * reg, u32 spd_index,
718                                   u32 sw_if_index, u32 context)
719 {
720   vl_api_ipsec_spd_interface_details_t *mp;
721
722   mp = vl_msg_api_alloc (sizeof (*mp));
723   clib_memset (mp, 0, sizeof (*mp));
724   mp->_vl_msg_id =
725     ntohs (REPLY_MSG_ID_BASE + VL_API_IPSEC_SPD_INTERFACE_DETAILS);
726   mp->context = context;
727
728   mp->spd_index = htonl (spd_index);
729   mp->sw_if_index = htonl (sw_if_index);
730
731   vl_api_send_msg (reg, (u8 *) mp);
732 }
733
734 static void
735 vl_api_ipsec_spd_interface_dump_t_handler (vl_api_ipsec_spd_interface_dump_t *
736                                            mp)
737 {
738   ipsec_main_t *im = &ipsec_main;
739   vl_api_registration_t *reg;
740   u32 k, v, spd_index;
741
742   reg = vl_api_client_index_to_registration (mp->client_index);
743   if (!reg)
744     return;
745
746   if (mp->spd_index_valid)
747     {
748       spd_index = ntohl (mp->spd_index);
749       /* *INDENT-OFF* */
750       hash_foreach(k, v, im->spd_index_by_sw_if_index, ({
751         if (v == spd_index)
752           send_ipsec_spd_interface_details(reg, v, k, mp->context);
753       }));
754       /* *INDENT-ON* */
755     }
756   else
757     {
758       hash_foreach(k, v, im->spd_index_by_sw_if_index, ({
759         send_ipsec_spd_interface_details(reg, v, k, mp->context);
760       }));
761     }
762 }
763
764 static void
765 vl_api_ipsec_itf_create_t_handler (vl_api_ipsec_itf_create_t * mp)
766 {
767   vl_api_ipsec_itf_create_reply_t *rmp;
768   tunnel_mode_t mode;
769   u32 sw_if_index = ~0;
770   int rv;
771
772   rv = tunnel_mode_decode (mp->itf.mode, &mode);
773
774   if (!rv)
775     rv = ipsec_itf_create (ntohl (mp->itf.user_instance), mode, &sw_if_index);
776
777   /* *INDENT-OFF* */
778   REPLY_MACRO2 (VL_API_IPSEC_ITF_CREATE_REPLY,
779   ({
780     rmp->sw_if_index = htonl (sw_if_index);
781   }));
782   /* *INDENT-ON* */
783 }
784
785 static void
786 vl_api_ipsec_itf_delete_t_handler (vl_api_ipsec_itf_delete_t * mp)
787 {
788   vl_api_ipsec_itf_delete_reply_t *rmp;
789   int rv;
790
791   rv = ipsec_itf_delete (ntohl (mp->sw_if_index));
792
793   REPLY_MACRO (VL_API_IPSEC_ITF_DELETE_REPLY);
794 }
795
796 static walk_rc_t
797 send_ipsec_itf_details (ipsec_itf_t *itf, void *arg)
798 {
799   ipsec_dump_walk_ctx_t *ctx = arg;
800   vl_api_ipsec_itf_details_t *mp;
801
802   if (~0 != ctx->sw_if_index && ctx->sw_if_index != itf->ii_sw_if_index)
803     return (WALK_CONTINUE);
804
805   mp = vl_msg_api_alloc (sizeof (*mp));
806   clib_memset (mp, 0, sizeof (*mp));
807   mp->_vl_msg_id = ntohs (REPLY_MSG_ID_BASE + VL_API_IPSEC_ITF_DETAILS);
808   mp->context = ctx->context;
809
810   mp->itf.mode = tunnel_mode_encode (itf->ii_mode);
811   mp->itf.user_instance = htonl (itf->ii_user_instance);
812   mp->itf.sw_if_index = htonl (itf->ii_sw_if_index);
813   vl_api_send_msg (ctx->reg, (u8 *) mp);
814
815   return (WALK_CONTINUE);
816 }
817
818 static void
819 vl_api_ipsec_itf_dump_t_handler (vl_api_ipsec_itf_dump_t * mp)
820 {
821   vl_api_registration_t *reg;
822
823   reg = vl_api_client_index_to_registration (mp->client_index);
824   if (!reg)
825     return;
826
827   ipsec_dump_walk_ctx_t ctx = {
828     .reg = reg,
829     .context = mp->context,
830     .sw_if_index = ntohl (mp->sw_if_index),
831   };
832
833   ipsec_itf_walk (send_ipsec_itf_details, &ctx);
834 }
835
836 typedef struct ipsec_sa_dump_match_ctx_t_
837 {
838   index_t sai;
839   u32 sw_if_index;
840 } ipsec_sa_dump_match_ctx_t;
841
842 static walk_rc_t
843 ipsec_sa_dump_match_sa (index_t itpi, void *arg)
844 {
845   ipsec_sa_dump_match_ctx_t *ctx = arg;
846   ipsec_tun_protect_t *itp;
847   index_t sai;
848
849   itp = ipsec_tun_protect_get (itpi);
850
851   if (itp->itp_out_sa == ctx->sai)
852     {
853       ctx->sw_if_index = itp->itp_sw_if_index;
854       return (WALK_STOP);
855     }
856
857   FOR_EACH_IPSEC_PROTECT_INPUT_SAI (itp, sai,
858   ({
859     if (sai == ctx->sai)
860       {
861         ctx->sw_if_index = itp->itp_sw_if_index;
862         return (WALK_STOP);
863       }
864   }));
865
866   return (WALK_CONTINUE);
867 }
868
869 static walk_rc_t
870 send_ipsec_sa_details (ipsec_sa_t * sa, void *arg)
871 {
872   ipsec_dump_walk_ctx_t *ctx = arg;
873   vl_api_ipsec_sa_details_t *mp;
874
875   mp = vl_msg_api_alloc (sizeof (*mp));
876   clib_memset (mp, 0, sizeof (*mp));
877   mp->_vl_msg_id = ntohs (REPLY_MSG_ID_BASE + VL_API_IPSEC_SA_DETAILS);
878   mp->context = ctx->context;
879
880   mp->entry.sad_id = htonl (sa->id);
881   mp->entry.spi = htonl (sa->spi);
882   mp->entry.protocol = ipsec_proto_encode (sa->protocol);
883   mp->entry.tx_table_id = htonl (sa->tunnel.t_table_id);
884
885   mp->entry.crypto_algorithm = ipsec_crypto_algo_encode (sa->crypto_alg);
886   ipsec_key_encode (&sa->crypto_key, &mp->entry.crypto_key);
887
888   mp->entry.integrity_algorithm = ipsec_integ_algo_encode (sa->integ_alg);
889   ipsec_key_encode (&sa->integ_key, &mp->entry.integrity_key);
890
891   mp->entry.flags = ipsec_sad_flags_encode (sa);
892   mp->entry.salt = clib_host_to_net_u32 (sa->salt);
893
894   if (ipsec_sa_is_set_IS_PROTECT (sa))
895     {
896       ipsec_sa_dump_match_ctx_t ctx = {
897         .sai = sa - ipsec_sa_pool,
898         .sw_if_index = ~0,
899       };
900       ipsec_tun_protect_walk (ipsec_sa_dump_match_sa, &ctx);
901
902       mp->sw_if_index = htonl (ctx.sw_if_index);
903     }
904   else
905     mp->sw_if_index = ~0;
906
907   if (ipsec_sa_is_set_IS_TUNNEL (sa))
908     {
909       ip_address_encode2 (&sa->tunnel.t_src, &mp->entry.tunnel_src);
910       ip_address_encode2 (&sa->tunnel.t_dst, &mp->entry.tunnel_dst);
911     }
912   if (ipsec_sa_is_set_UDP_ENCAP (sa))
913     {
914       mp->entry.udp_src_port = sa->udp_hdr.src_port;
915       mp->entry.udp_dst_port = sa->udp_hdr.dst_port;
916     }
917
918   mp->seq_outbound = clib_host_to_net_u64 (((u64) sa->seq));
919   mp->last_seq_inbound = clib_host_to_net_u64 (((u64) sa->seq));
920   if (ipsec_sa_is_set_USE_ESN (sa))
921     {
922       mp->seq_outbound |= (u64) (clib_host_to_net_u32 (sa->seq_hi));
923       mp->last_seq_inbound |= (u64) (clib_host_to_net_u32 (sa->seq_hi));
924     }
925   if (ipsec_sa_is_set_USE_ANTI_REPLAY (sa))
926     mp->replay_window = clib_host_to_net_u64 (sa->replay_window);
927
928   mp->stat_index = clib_host_to_net_u32 (sa->stat_index);
929
930   vl_api_send_msg (ctx->reg, (u8 *) mp);
931
932   return (WALK_CONTINUE);
933 }
934
935 static void
936 vl_api_ipsec_sa_dump_t_handler (vl_api_ipsec_sa_dump_t * mp)
937 {
938   vl_api_registration_t *reg;
939
940   reg = vl_api_client_index_to_registration (mp->client_index);
941   if (!reg)
942     return;
943
944   ipsec_dump_walk_ctx_t ctx = {
945     .reg = reg,
946     .context = mp->context,
947   };
948
949   ipsec_sa_walk (send_ipsec_sa_details, &ctx);
950 }
951
952 static walk_rc_t
953 send_ipsec_sa_v2_details (ipsec_sa_t * sa, void *arg)
954 {
955   ipsec_dump_walk_ctx_t *ctx = arg;
956   vl_api_ipsec_sa_v2_details_t *mp;
957
958   mp = vl_msg_api_alloc (sizeof (*mp));
959   clib_memset (mp, 0, sizeof (*mp));
960   mp->_vl_msg_id = ntohs (REPLY_MSG_ID_BASE + VL_API_IPSEC_SA_V2_DETAILS);
961   mp->context = ctx->context;
962
963   mp->entry.sad_id = htonl (sa->id);
964   mp->entry.spi = htonl (sa->spi);
965   mp->entry.protocol = ipsec_proto_encode (sa->protocol);
966   mp->entry.tx_table_id = htonl (sa->tunnel.t_table_id);
967
968   mp->entry.crypto_algorithm = ipsec_crypto_algo_encode (sa->crypto_alg);
969   ipsec_key_encode (&sa->crypto_key, &mp->entry.crypto_key);
970
971   mp->entry.integrity_algorithm = ipsec_integ_algo_encode (sa->integ_alg);
972   ipsec_key_encode (&sa->integ_key, &mp->entry.integrity_key);
973
974   mp->entry.flags = ipsec_sad_flags_encode (sa);
975   mp->entry.salt = clib_host_to_net_u32 (sa->salt);
976
977   if (ipsec_sa_is_set_IS_PROTECT (sa))
978     {
979       ipsec_sa_dump_match_ctx_t ctx = {
980         .sai = sa - ipsec_sa_pool,
981         .sw_if_index = ~0,
982       };
983       ipsec_tun_protect_walk (ipsec_sa_dump_match_sa, &ctx);
984
985       mp->sw_if_index = htonl (ctx.sw_if_index);
986     }
987   else
988     mp->sw_if_index = ~0;
989
990   if (ipsec_sa_is_set_IS_TUNNEL (sa))
991     {
992       ip_address_encode2 (&sa->tunnel.t_src, &mp->entry.tunnel_src);
993       ip_address_encode2 (&sa->tunnel.t_dst, &mp->entry.tunnel_dst);
994     }
995   if (ipsec_sa_is_set_UDP_ENCAP (sa))
996     {
997       mp->entry.udp_src_port = sa->udp_hdr.src_port;
998       mp->entry.udp_dst_port = sa->udp_hdr.dst_port;
999     }
1000
1001   mp->entry.tunnel_flags =
1002     tunnel_encap_decap_flags_encode (sa->tunnel.t_encap_decap_flags);
1003   mp->entry.dscp = ip_dscp_encode (sa->tunnel.t_dscp);
1004
1005   mp->seq_outbound = clib_host_to_net_u64 (((u64) sa->seq));
1006   mp->last_seq_inbound = clib_host_to_net_u64 (((u64) sa->seq));
1007   if (ipsec_sa_is_set_USE_ESN (sa))
1008     {
1009       mp->seq_outbound |= (u64) (clib_host_to_net_u32 (sa->seq_hi));
1010       mp->last_seq_inbound |= (u64) (clib_host_to_net_u32 (sa->seq_hi));
1011     }
1012   if (ipsec_sa_is_set_USE_ANTI_REPLAY (sa))
1013     mp->replay_window = clib_host_to_net_u64 (sa->replay_window);
1014
1015   mp->stat_index = clib_host_to_net_u32 (sa->stat_index);
1016
1017   vl_api_send_msg (ctx->reg, (u8 *) mp);
1018
1019   return (WALK_CONTINUE);
1020 }
1021
1022 static void
1023 vl_api_ipsec_sa_v2_dump_t_handler (vl_api_ipsec_sa_v2_dump_t *mp)
1024 {
1025   vl_api_registration_t *reg;
1026
1027   reg = vl_api_client_index_to_registration (mp->client_index);
1028   if (!reg)
1029     return;
1030
1031   ipsec_dump_walk_ctx_t ctx = {
1032     .reg = reg,
1033     .context = mp->context,
1034   };
1035
1036   ipsec_sa_walk (send_ipsec_sa_v2_details, &ctx);
1037 }
1038
1039 static walk_rc_t
1040 send_ipsec_sa_v3_details (ipsec_sa_t *sa, void *arg)
1041 {
1042   ipsec_dump_walk_ctx_t *ctx = arg;
1043   vl_api_ipsec_sa_v3_details_t *mp;
1044
1045   mp = vl_msg_api_alloc (sizeof (*mp));
1046   clib_memset (mp, 0, sizeof (*mp));
1047   mp->_vl_msg_id = ntohs (REPLY_MSG_ID_BASE + VL_API_IPSEC_SA_V3_DETAILS);
1048   mp->context = ctx->context;
1049
1050   mp->entry.sad_id = htonl (sa->id);
1051   mp->entry.spi = htonl (sa->spi);
1052   mp->entry.protocol = ipsec_proto_encode (sa->protocol);
1053
1054   mp->entry.crypto_algorithm = ipsec_crypto_algo_encode (sa->crypto_alg);
1055   ipsec_key_encode (&sa->crypto_key, &mp->entry.crypto_key);
1056
1057   mp->entry.integrity_algorithm = ipsec_integ_algo_encode (sa->integ_alg);
1058   ipsec_key_encode (&sa->integ_key, &mp->entry.integrity_key);
1059
1060   mp->entry.flags = ipsec_sad_flags_encode (sa);
1061   mp->entry.salt = clib_host_to_net_u32 (sa->salt);
1062
1063   if (ipsec_sa_is_set_IS_PROTECT (sa))
1064     {
1065       ipsec_sa_dump_match_ctx_t ctx = {
1066         .sai = sa - ipsec_sa_pool,
1067         .sw_if_index = ~0,
1068       };
1069       ipsec_tun_protect_walk (ipsec_sa_dump_match_sa, &ctx);
1070
1071       mp->sw_if_index = htonl (ctx.sw_if_index);
1072     }
1073   else
1074     mp->sw_if_index = ~0;
1075
1076   if (ipsec_sa_is_set_IS_TUNNEL (sa))
1077     tunnel_encode (&sa->tunnel, &mp->entry.tunnel);
1078
1079   if (ipsec_sa_is_set_UDP_ENCAP (sa))
1080     {
1081       mp->entry.udp_src_port = sa->udp_hdr.src_port;
1082       mp->entry.udp_dst_port = sa->udp_hdr.dst_port;
1083     }
1084
1085   mp->seq_outbound = clib_host_to_net_u64 (((u64) sa->seq));
1086   mp->last_seq_inbound = clib_host_to_net_u64 (((u64) sa->seq));
1087   if (ipsec_sa_is_set_USE_ESN (sa))
1088     {
1089       mp->seq_outbound |= (u64) (clib_host_to_net_u32 (sa->seq_hi));
1090       mp->last_seq_inbound |= (u64) (clib_host_to_net_u32 (sa->seq_hi));
1091     }
1092   if (ipsec_sa_is_set_USE_ANTI_REPLAY (sa))
1093     mp->replay_window = clib_host_to_net_u64 (sa->replay_window);
1094
1095   mp->stat_index = clib_host_to_net_u32 (sa->stat_index);
1096
1097   vl_api_send_msg (ctx->reg, (u8 *) mp);
1098
1099   return (WALK_CONTINUE);
1100 }
1101
1102 static void
1103 vl_api_ipsec_sa_v3_dump_t_handler (vl_api_ipsec_sa_v3_dump_t *mp)
1104 {
1105   vl_api_registration_t *reg;
1106
1107   reg = vl_api_client_index_to_registration (mp->client_index);
1108   if (!reg)
1109     return;
1110
1111   ipsec_dump_walk_ctx_t ctx = {
1112     .reg = reg,
1113     .context = mp->context,
1114   };
1115
1116   ipsec_sa_walk (send_ipsec_sa_v3_details, &ctx);
1117 }
1118
1119 static void
1120 vl_api_ipsec_backend_dump_t_handler (vl_api_ipsec_backend_dump_t * mp)
1121 {
1122   vl_api_registration_t *rp;
1123   ipsec_main_t *im = &ipsec_main;
1124   u32 context = mp->context;
1125
1126   rp = vl_api_client_index_to_registration (mp->client_index);
1127
1128   if (rp == 0)
1129     {
1130       clib_warning ("Client %d AWOL", mp->client_index);
1131       return;
1132     }
1133
1134   ipsec_ah_backend_t *ab;
1135   ipsec_esp_backend_t *eb;
1136   /* *INDENT-OFF* */
1137   pool_foreach (ab, im->ah_backends) {
1138     vl_api_ipsec_backend_details_t *mp = vl_msg_api_alloc (sizeof (*mp));
1139     clib_memset (mp, 0, sizeof (*mp));
1140     mp->_vl_msg_id = ntohs (REPLY_MSG_ID_BASE + VL_API_IPSEC_BACKEND_DETAILS);
1141     mp->context = context;
1142     snprintf ((char *)mp->name, sizeof (mp->name), "%.*s", vec_len (ab->name),
1143               ab->name);
1144     mp->protocol = ntohl (IPSEC_API_PROTO_AH);
1145     mp->index = ab - im->ah_backends;
1146     mp->active = mp->index == im->ah_current_backend ? 1 : 0;
1147     vl_api_send_msg (rp, (u8 *)mp);
1148   }
1149   pool_foreach (eb, im->esp_backends) {
1150     vl_api_ipsec_backend_details_t *mp = vl_msg_api_alloc (sizeof (*mp));
1151     clib_memset (mp, 0, sizeof (*mp));
1152     mp->_vl_msg_id = ntohs (REPLY_MSG_ID_BASE + VL_API_IPSEC_BACKEND_DETAILS);
1153     mp->context = context;
1154     snprintf ((char *)mp->name, sizeof (mp->name), "%.*s", vec_len (eb->name),
1155               eb->name);
1156     mp->protocol = ntohl (IPSEC_API_PROTO_ESP);
1157     mp->index = eb - im->esp_backends;
1158     mp->active = mp->index == im->esp_current_backend ? 1 : 0;
1159     vl_api_send_msg (rp, (u8 *)mp);
1160   }
1161   /* *INDENT-ON* */
1162 }
1163
1164 static void
1165 vl_api_ipsec_select_backend_t_handler (vl_api_ipsec_select_backend_t * mp)
1166 {
1167   ipsec_main_t *im = &ipsec_main;
1168   vl_api_ipsec_select_backend_reply_t *rmp;
1169   ipsec_protocol_t protocol;
1170   int rv = 0;
1171   if (pool_elts (ipsec_sa_pool) > 0)
1172     {
1173       rv = VNET_API_ERROR_INSTANCE_IN_USE;
1174       goto done;
1175     }
1176
1177   rv = ipsec_proto_decode (mp->protocol, &protocol);
1178
1179   if (rv)
1180     goto done;
1181
1182   switch (protocol)
1183     {
1184     case IPSEC_PROTOCOL_ESP:
1185       rv = ipsec_select_esp_backend (im, mp->index);
1186       break;
1187     case IPSEC_PROTOCOL_AH:
1188       rv = ipsec_select_ah_backend (im, mp->index);
1189       break;
1190     default:
1191       rv = VNET_API_ERROR_INVALID_PROTOCOL;
1192       break;
1193     }
1194 done:
1195   REPLY_MACRO (VL_API_IPSEC_SELECT_BACKEND_REPLY);
1196 }
1197
1198 static void
1199 vl_api_ipsec_set_async_mode_t_handler (vl_api_ipsec_set_async_mode_t * mp)
1200 {
1201   vl_api_ipsec_set_async_mode_reply_t *rmp;
1202   int rv = 0;
1203
1204   ipsec_set_async_mode (mp->async_enable);
1205
1206   REPLY_MACRO (VL_API_IPSEC_SET_ASYNC_MODE_REPLY);
1207 }
1208
1209 #include <vnet/ipsec/ipsec.api.c>
1210 static clib_error_t *
1211 ipsec_api_hookup (vlib_main_t * vm)
1212 {
1213   /*
1214    * Set up the (msg_name, crc, message-id) table
1215    */
1216   REPLY_MSG_ID_BASE = setup_message_id_table ();
1217
1218   return 0;
1219 }
1220
1221 VLIB_API_INIT_FUNCTION (ipsec_api_hookup);
1222
1223 /*
1224  * fd.io coding-style-patch-verification: ON
1225  *
1226  * Local Variables:
1227  * eval: (c-set-style "gnu")
1228  * End:
1229  */