crypto-openssl: use getrandom to reseed openssl
[vpp.git] / src / plugins / crypto_openssl / main.c
1 /*
2  *------------------------------------------------------------------
3  * Copyright (c) 2019 Cisco and/or its affiliates.
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at:
7  *
8  *     http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  *------------------------------------------------------------------
16  */
17
18 #include <sys/random.h>
19
20 #include <openssl/evp.h>
21 #include <openssl/hmac.h>
22 #include <openssl/rand.h>
23 #include <openssl/sha.h>
24
25 #include <vlib/vlib.h>
26 #include <vnet/plugin/plugin.h>
27 #include <vnet/crypto/crypto.h>
28 #include <vpp/app/version.h>
29
30 typedef struct
31 {
32   CLIB_CACHE_LINE_ALIGN_MARK (cacheline0);
33   EVP_CIPHER_CTX *evp_cipher_ctx;
34   HMAC_CTX *hmac_ctx;
35   EVP_MD_CTX *hash_ctx;
36 #if OPENSSL_VERSION_NUMBER < 0x10100000L
37   HMAC_CTX _hmac_ctx;
38 #endif
39 } openssl_per_thread_data_t;
40
41 static openssl_per_thread_data_t *per_thread_data = 0;
42
43 #define foreach_openssl_aes_evp_op                                            \
44   _ (cbc, DES_CBC, EVP_des_cbc, 8)                                            \
45   _ (cbc, 3DES_CBC, EVP_des_ede3_cbc, 8)                                      \
46   _ (cbc, AES_128_CBC, EVP_aes_128_cbc, 16)                                   \
47   _ (cbc, AES_192_CBC, EVP_aes_192_cbc, 16)                                   \
48   _ (cbc, AES_256_CBC, EVP_aes_256_cbc, 16)                                   \
49   _ (gcm, AES_128_GCM, EVP_aes_128_gcm, 8)                                    \
50   _ (gcm, AES_192_GCM, EVP_aes_192_gcm, 8)                                    \
51   _ (gcm, AES_256_GCM, EVP_aes_256_gcm, 8)                                    \
52   _ (cbc, AES_128_CTR, EVP_aes_128_ctr, 8)                                    \
53   _ (cbc, AES_192_CTR, EVP_aes_192_ctr, 8)                                    \
54   _ (cbc, AES_256_CTR, EVP_aes_256_ctr, 8)
55
56 #define foreach_openssl_chacha20_evp_op                                       \
57   _ (chacha20_poly1305, CHACHA20_POLY1305, EVP_chacha20_poly1305, 8)
58
59 #if OPENSSL_VERSION_NUMBER >= 0x10100000L
60 #define foreach_openssl_evp_op foreach_openssl_aes_evp_op \
61                                foreach_openssl_chacha20_evp_op
62 #else
63 #define foreach_openssl_evp_op foreach_openssl_aes_evp_op
64 #endif
65
66 #ifndef EVP_CTRL_AEAD_GET_TAG
67 #define EVP_CTRL_AEAD_GET_TAG EVP_CTRL_GCM_GET_TAG
68 #endif
69
70 #ifndef EVP_CTRL_AEAD_SET_TAG
71 #define EVP_CTRL_AEAD_SET_TAG EVP_CTRL_GCM_SET_TAG
72 #endif
73
74 #define foreach_openssl_hash_op                                               \
75   _ (SHA1, EVP_sha1)                                                          \
76   _ (SHA224, EVP_sha224)                                                      \
77   _ (SHA256, EVP_sha256)                                                      \
78   _ (SHA384, EVP_sha384)                                                      \
79   _ (SHA512, EVP_sha512)
80
81 #define foreach_openssl_hmac_op \
82   _(MD5, EVP_md5) \
83   _(SHA1, EVP_sha1) \
84   _(SHA224, EVP_sha224) \
85   _(SHA256, EVP_sha256) \
86   _(SHA384, EVP_sha384) \
87   _(SHA512, EVP_sha512)
88
89 static_always_inline u32
90 openssl_ops_enc_cbc (vlib_main_t *vm, vnet_crypto_op_t *ops[],
91                      vnet_crypto_op_chunk_t *chunks, u32 n_ops,
92                      const EVP_CIPHER *cipher, const int iv_len)
93 {
94   openssl_per_thread_data_t *ptd = vec_elt_at_index (per_thread_data,
95                                                      vm->thread_index);
96   EVP_CIPHER_CTX *ctx = ptd->evp_cipher_ctx;
97   vnet_crypto_op_chunk_t *chp;
98   u32 i, j, curr_len = 0;
99   u8 out_buf[VLIB_BUFFER_DEFAULT_DATA_SIZE * 5];
100
101   for (i = 0; i < n_ops; i++)
102     {
103       vnet_crypto_op_t *op = ops[i];
104       vnet_crypto_key_t *key = vnet_crypto_get_key (op->key_index);
105       int out_len = 0;
106
107       if (op->flags & VNET_CRYPTO_OP_FLAG_INIT_IV)
108         RAND_bytes (op->iv, iv_len);
109
110       EVP_EncryptInit_ex (ctx, cipher, NULL, key->data, op->iv);
111
112       if (op->flags & VNET_CRYPTO_OP_FLAG_CHAINED_BUFFERS)
113         EVP_CIPHER_CTX_set_padding (ctx, 0);
114
115       if (op->flags & VNET_CRYPTO_OP_FLAG_CHAINED_BUFFERS)
116         {
117           chp = chunks + op->chunk_index;
118           u32 offset = 0;
119           for (j = 0; j < op->n_chunks; j++)
120             {
121               EVP_EncryptUpdate (ctx, out_buf + offset, &out_len, chp->src,
122                                  chp->len);
123               curr_len = chp->len;
124               offset += out_len;
125               chp += 1;
126             }
127           if (out_len < curr_len)
128             EVP_EncryptFinal_ex (ctx, out_buf + offset, &out_len);
129
130           offset = 0;
131           chp = chunks + op->chunk_index;
132           for (j = 0; j < op->n_chunks; j++)
133             {
134               clib_memcpy_fast (chp->dst, out_buf + offset, chp->len);
135               offset += chp->len;
136               chp += 1;
137             }
138         }
139       else
140         {
141           EVP_EncryptUpdate (ctx, op->dst, &out_len, op->src, op->len);
142           if (out_len < op->len)
143             EVP_EncryptFinal_ex (ctx, op->dst + out_len, &out_len);
144         }
145       op->status = VNET_CRYPTO_OP_STATUS_COMPLETED;
146     }
147   return n_ops;
148 }
149
150 static_always_inline u32
151 openssl_ops_dec_cbc (vlib_main_t *vm, vnet_crypto_op_t *ops[],
152                      vnet_crypto_op_chunk_t *chunks, u32 n_ops,
153                      const EVP_CIPHER *cipher, const int iv_len)
154 {
155   openssl_per_thread_data_t *ptd = vec_elt_at_index (per_thread_data,
156                                                      vm->thread_index);
157   EVP_CIPHER_CTX *ctx = ptd->evp_cipher_ctx;
158   vnet_crypto_op_chunk_t *chp;
159   u32 i, j, curr_len = 0;
160   u8 out_buf[VLIB_BUFFER_DEFAULT_DATA_SIZE * 5];
161
162   for (i = 0; i < n_ops; i++)
163     {
164       vnet_crypto_op_t *op = ops[i];
165       vnet_crypto_key_t *key = vnet_crypto_get_key (op->key_index);
166       int out_len = 0;
167
168       EVP_DecryptInit_ex (ctx, cipher, NULL, key->data, op->iv);
169
170       if (op->flags & VNET_CRYPTO_OP_FLAG_CHAINED_BUFFERS)
171         EVP_CIPHER_CTX_set_padding (ctx, 0);
172
173       if (op->flags & VNET_CRYPTO_OP_FLAG_CHAINED_BUFFERS)
174         {
175           chp = chunks + op->chunk_index;
176           u32 offset = 0;
177           for (j = 0; j < op->n_chunks; j++)
178             {
179               EVP_DecryptUpdate (ctx, out_buf + offset, &out_len, chp->src,
180                                  chp->len);
181               curr_len = chp->len;
182               offset += out_len;
183               chp += 1;
184             }
185           if (out_len < curr_len)
186             EVP_DecryptFinal_ex (ctx, out_buf + offset, &out_len);
187
188           offset = 0;
189           chp = chunks + op->chunk_index;
190           for (j = 0; j < op->n_chunks; j++)
191             {
192               clib_memcpy_fast (chp->dst, out_buf + offset, chp->len);
193               offset += chp->len;
194               chp += 1;
195             }
196         }
197       else
198         {
199           EVP_DecryptUpdate (ctx, op->dst, &out_len, op->src, op->len);
200           if (out_len < op->len)
201             EVP_DecryptFinal_ex (ctx, op->dst + out_len, &out_len);
202         }
203       op->status = VNET_CRYPTO_OP_STATUS_COMPLETED;
204     }
205   return n_ops;
206 }
207
208 static_always_inline u32
209 openssl_ops_enc_aead (vlib_main_t *vm, vnet_crypto_op_t *ops[],
210                       vnet_crypto_op_chunk_t *chunks, u32 n_ops,
211                       const EVP_CIPHER *cipher, int is_gcm, const int iv_len)
212 {
213   openssl_per_thread_data_t *ptd = vec_elt_at_index (per_thread_data,
214                                                      vm->thread_index);
215   EVP_CIPHER_CTX *ctx = ptd->evp_cipher_ctx;
216   vnet_crypto_op_chunk_t *chp;
217   u32 i, j;
218   for (i = 0; i < n_ops; i++)
219     {
220       vnet_crypto_op_t *op = ops[i];
221       vnet_crypto_key_t *key = vnet_crypto_get_key (op->key_index);
222       int len = 0;
223
224       if (op->flags & VNET_CRYPTO_OP_FLAG_INIT_IV)
225         RAND_bytes (op->iv, 8);
226
227       EVP_EncryptInit_ex (ctx, cipher, 0, 0, 0);
228       if (is_gcm)
229         EVP_CIPHER_CTX_ctrl (ctx, EVP_CTRL_GCM_SET_IVLEN, 12, NULL);
230       EVP_EncryptInit_ex (ctx, 0, 0, key->data, op->iv);
231       if (op->aad_len)
232         EVP_EncryptUpdate (ctx, NULL, &len, op->aad, op->aad_len);
233       if (op->flags & VNET_CRYPTO_OP_FLAG_CHAINED_BUFFERS)
234         {
235           chp = chunks + op->chunk_index;
236           for (j = 0; j < op->n_chunks; j++)
237             {
238               EVP_EncryptUpdate (ctx, chp->dst, &len, chp->src, chp->len);
239               chp += 1;
240             }
241         }
242       else
243         EVP_EncryptUpdate (ctx, op->dst, &len, op->src, op->len);
244       EVP_EncryptFinal_ex (ctx, op->dst + len, &len);
245       EVP_CIPHER_CTX_ctrl (ctx, EVP_CTRL_AEAD_GET_TAG, op->tag_len, op->tag);
246       op->status = VNET_CRYPTO_OP_STATUS_COMPLETED;
247     }
248   return n_ops;
249 }
250
251 static_always_inline u32
252 openssl_ops_enc_gcm (vlib_main_t *vm, vnet_crypto_op_t *ops[],
253                      vnet_crypto_op_chunk_t *chunks, u32 n_ops,
254                      const EVP_CIPHER *cipher, const int iv_len)
255 {
256   return openssl_ops_enc_aead (vm, ops, chunks, n_ops, cipher,
257                                /* is_gcm */ 1, iv_len);
258 }
259
260 static_always_inline __clib_unused u32
261 openssl_ops_enc_chacha20_poly1305 (vlib_main_t *vm, vnet_crypto_op_t *ops[],
262                                    vnet_crypto_op_chunk_t *chunks, u32 n_ops,
263                                    const EVP_CIPHER *cipher, const int iv_len)
264 {
265   return openssl_ops_enc_aead (vm, ops, chunks, n_ops, cipher,
266                                /* is_gcm */ 0, iv_len);
267 }
268
269 static_always_inline u32
270 openssl_ops_dec_aead (vlib_main_t *vm, vnet_crypto_op_t *ops[],
271                       vnet_crypto_op_chunk_t *chunks, u32 n_ops,
272                       const EVP_CIPHER *cipher, int is_gcm, const int iv_len)
273 {
274   openssl_per_thread_data_t *ptd = vec_elt_at_index (per_thread_data,
275                                                      vm->thread_index);
276   EVP_CIPHER_CTX *ctx = ptd->evp_cipher_ctx;
277   vnet_crypto_op_chunk_t *chp;
278   u32 i, j, n_fail = 0;
279   for (i = 0; i < n_ops; i++)
280     {
281       vnet_crypto_op_t *op = ops[i];
282       vnet_crypto_key_t *key = vnet_crypto_get_key (op->key_index);
283       int len = 0;
284
285       EVP_DecryptInit_ex (ctx, cipher, 0, 0, 0);
286       if (is_gcm)
287         EVP_CIPHER_CTX_ctrl (ctx, EVP_CTRL_GCM_SET_IVLEN, 12, 0);
288       EVP_DecryptInit_ex (ctx, 0, 0, key->data, op->iv);
289       if (op->aad_len)
290         EVP_DecryptUpdate (ctx, 0, &len, op->aad, op->aad_len);
291       if (op->flags & VNET_CRYPTO_OP_FLAG_CHAINED_BUFFERS)
292         {
293           chp = chunks + op->chunk_index;
294           for (j = 0; j < op->n_chunks; j++)
295             {
296               EVP_DecryptUpdate (ctx, chp->dst, &len, chp->src, chp->len);
297               chp += 1;
298             }
299         }
300       else
301         EVP_DecryptUpdate (ctx, op->dst, &len, op->src, op->len);
302       EVP_CIPHER_CTX_ctrl (ctx, EVP_CTRL_AEAD_SET_TAG, op->tag_len, op->tag);
303
304       if (EVP_DecryptFinal_ex (ctx, op->dst + len, &len) > 0)
305         op->status = VNET_CRYPTO_OP_STATUS_COMPLETED;
306       else
307         {
308           n_fail++;
309           op->status = VNET_CRYPTO_OP_STATUS_FAIL_BAD_HMAC;
310         }
311     }
312   return n_ops - n_fail;
313 }
314
315 static_always_inline u32
316 openssl_ops_dec_gcm (vlib_main_t *vm, vnet_crypto_op_t *ops[],
317                      vnet_crypto_op_chunk_t *chunks, u32 n_ops,
318                      const EVP_CIPHER *cipher, const int iv_len)
319 {
320   return openssl_ops_dec_aead (vm, ops, chunks, n_ops, cipher,
321                                /* is_gcm */ 1, iv_len);
322 }
323
324 static_always_inline __clib_unused u32
325 openssl_ops_dec_chacha20_poly1305 (vlib_main_t *vm, vnet_crypto_op_t *ops[],
326                                    vnet_crypto_op_chunk_t *chunks, u32 n_ops,
327                                    const EVP_CIPHER *cipher, const int iv_len)
328 {
329   return openssl_ops_dec_aead (vm, ops, chunks, n_ops, cipher,
330                                /* is_gcm */ 0, iv_len);
331 }
332
333 static_always_inline u32
334 openssl_ops_hash (vlib_main_t *vm, vnet_crypto_op_t *ops[],
335                   vnet_crypto_op_chunk_t *chunks, u32 n_ops, const EVP_MD *md)
336 {
337   openssl_per_thread_data_t *ptd =
338     vec_elt_at_index (per_thread_data, vm->thread_index);
339   EVP_MD_CTX *ctx = ptd->hash_ctx;
340   vnet_crypto_op_chunk_t *chp;
341   u32 md_len, i, j, n_fail = 0;
342
343   for (i = 0; i < n_ops; i++)
344     {
345       vnet_crypto_op_t *op = ops[i];
346
347       EVP_DigestInit_ex (ctx, md, NULL);
348       if (op->flags & VNET_CRYPTO_OP_FLAG_CHAINED_BUFFERS)
349         {
350           chp = chunks + op->chunk_index;
351           for (j = 0; j < op->n_chunks; j++)
352             {
353               EVP_DigestUpdate (ctx, chp->src, chp->len);
354               chp += 1;
355             }
356         }
357       else
358         EVP_DigestUpdate (ctx, op->src, op->len);
359
360       EVP_DigestFinal_ex (ctx, op->digest, &md_len);
361       op->digest_len = md_len;
362       op->status = VNET_CRYPTO_OP_STATUS_COMPLETED;
363     }
364   return n_ops - n_fail;
365 }
366
367 static_always_inline u32
368 openssl_ops_hmac (vlib_main_t * vm, vnet_crypto_op_t * ops[],
369                   vnet_crypto_op_chunk_t * chunks, u32 n_ops,
370                   const EVP_MD * md)
371 {
372   u8 buffer[64];
373   openssl_per_thread_data_t *ptd = vec_elt_at_index (per_thread_data,
374                                                      vm->thread_index);
375   HMAC_CTX *ctx = ptd->hmac_ctx;
376   vnet_crypto_op_chunk_t *chp;
377   u32 i, j, n_fail = 0;
378   for (i = 0; i < n_ops; i++)
379     {
380       vnet_crypto_op_t *op = ops[i];
381       vnet_crypto_key_t *key = vnet_crypto_get_key (op->key_index);
382       unsigned int out_len = 0;
383       size_t sz = op->digest_len ? op->digest_len : EVP_MD_size (md);
384
385       HMAC_Init_ex (ctx, key->data, vec_len (key->data), md, NULL);
386       if (op->flags & VNET_CRYPTO_OP_FLAG_CHAINED_BUFFERS)
387         {
388           chp = chunks + op->chunk_index;
389           for (j = 0; j < op->n_chunks; j++)
390             {
391               HMAC_Update (ctx, chp->src, chp->len);
392               chp += 1;
393             }
394         }
395       else
396         HMAC_Update (ctx, op->src, op->len);
397       HMAC_Final (ctx, buffer, &out_len);
398
399       if (op->flags & VNET_CRYPTO_OP_FLAG_HMAC_CHECK)
400         {
401           if ((memcmp (op->digest, buffer, sz)))
402             {
403               n_fail++;
404               op->status = VNET_CRYPTO_OP_STATUS_FAIL_BAD_HMAC;
405               continue;
406             }
407         }
408       else
409         clib_memcpy_fast (op->digest, buffer, sz);
410       op->status = VNET_CRYPTO_OP_STATUS_COMPLETED;
411     }
412   return n_ops - n_fail;
413 }
414
415 #define _(m, a, b, iv)                                                        \
416   static u32 openssl_ops_enc_##a (vlib_main_t *vm, vnet_crypto_op_t *ops[],   \
417                                   u32 n_ops)                                  \
418   {                                                                           \
419     return openssl_ops_enc_##m (vm, ops, 0, n_ops, b (), iv);                 \
420   }                                                                           \
421                                                                               \
422   u32 openssl_ops_dec_##a (vlib_main_t *vm, vnet_crypto_op_t *ops[],          \
423                            u32 n_ops)                                         \
424   {                                                                           \
425     return openssl_ops_dec_##m (vm, ops, 0, n_ops, b (), iv);                 \
426   }                                                                           \
427                                                                               \
428   static u32 openssl_ops_enc_chained_##a (                                    \
429     vlib_main_t *vm, vnet_crypto_op_t *ops[], vnet_crypto_op_chunk_t *chunks, \
430     u32 n_ops)                                                                \
431   {                                                                           \
432     return openssl_ops_enc_##m (vm, ops, chunks, n_ops, b (), iv);            \
433   }                                                                           \
434                                                                               \
435   static u32 openssl_ops_dec_chained_##a (                                    \
436     vlib_main_t *vm, vnet_crypto_op_t *ops[], vnet_crypto_op_chunk_t *chunks, \
437     u32 n_ops)                                                                \
438   {                                                                           \
439     return openssl_ops_dec_##m (vm, ops, chunks, n_ops, b (), iv);            \
440   }
441
442 foreach_openssl_evp_op;
443 #undef _
444
445 #define _(a, b)                                                               \
446   static u32 openssl_ops_hash_##a (vlib_main_t *vm, vnet_crypto_op_t *ops[],  \
447                                    u32 n_ops)                                 \
448   {                                                                           \
449     return openssl_ops_hash (vm, ops, 0, n_ops, b ());                        \
450   }                                                                           \
451   static u32 openssl_ops_hash_chained_##a (                                   \
452     vlib_main_t *vm, vnet_crypto_op_t *ops[], vnet_crypto_op_chunk_t *chunks, \
453     u32 n_ops)                                                                \
454   {                                                                           \
455     return openssl_ops_hash (vm, ops, chunks, n_ops, b ());                   \
456   }
457
458 foreach_openssl_hash_op;
459 #undef _
460
461 #define _(a, b) \
462 static u32 \
463 openssl_ops_hmac_##a (vlib_main_t * vm, vnet_crypto_op_t * ops[], u32 n_ops) \
464 { return openssl_ops_hmac (vm, ops, 0, n_ops, b ()); } \
465 static u32 \
466 openssl_ops_hmac_chained_##a (vlib_main_t * vm, vnet_crypto_op_t * ops[], \
467     vnet_crypto_op_chunk_t *chunks, u32 n_ops) \
468 { return openssl_ops_hmac (vm, ops, chunks, n_ops, b ()); } \
469
470 foreach_openssl_hmac_op;
471 #undef _
472
473
474 clib_error_t *
475 crypto_openssl_init (vlib_main_t * vm)
476 {
477   vlib_thread_main_t *tm = vlib_get_thread_main ();
478   openssl_per_thread_data_t *ptd;
479   u8 seed[32];
480
481   if (getrandom (&seed, sizeof (seed), 0) != sizeof (seed))
482     return clib_error_return_unix (0, "getrandom() failed");
483
484   RAND_seed (seed, sizeof (seed));
485
486   u32 eidx = vnet_crypto_register_engine (vm, "openssl", 50, "OpenSSL");
487
488 #define _(m, a, b, iv)                                                        \
489   vnet_crypto_register_ops_handlers (vm, eidx, VNET_CRYPTO_OP_##a##_ENC,      \
490                                      openssl_ops_enc_##a,                     \
491                                      openssl_ops_enc_chained_##a);            \
492   vnet_crypto_register_ops_handlers (vm, eidx, VNET_CRYPTO_OP_##a##_DEC,      \
493                                      openssl_ops_dec_##a,                     \
494                                      openssl_ops_dec_chained_##a);
495
496   foreach_openssl_evp_op;
497 #undef _
498
499 #define _(a, b) \
500   vnet_crypto_register_ops_handlers (vm, eidx, VNET_CRYPTO_OP_##a##_HMAC, \
501                                     openssl_ops_hmac_##a, \
502                                     openssl_ops_hmac_chained_##a); \
503
504   foreach_openssl_hmac_op;
505 #undef _
506
507 #define _(a, b)                                                               \
508   vnet_crypto_register_ops_handlers (vm, eidx, VNET_CRYPTO_OP_##a##_HASH,     \
509                                      openssl_ops_hash_##a,                    \
510                                      openssl_ops_hash_chained_##a);
511
512   foreach_openssl_hash_op;
513 #undef _
514
515   vec_validate_aligned (per_thread_data, tm->n_vlib_mains - 1,
516                         CLIB_CACHE_LINE_BYTES);
517
518   vec_foreach (ptd, per_thread_data)
519   {
520     ptd->evp_cipher_ctx = EVP_CIPHER_CTX_new ();
521 #if OPENSSL_VERSION_NUMBER >= 0x10100000L
522     ptd->hmac_ctx = HMAC_CTX_new ();
523     ptd->hash_ctx = EVP_MD_CTX_create ();
524 #else
525     HMAC_CTX_init (&(ptd->_hmac_ctx));
526     ptd->hmac_ctx = &ptd->_hmac_ctx;
527 #endif
528   }
529
530   return 0;
531 }
532
533 /* *INDENT-OFF* */
534 VLIB_INIT_FUNCTION (crypto_openssl_init) =
535 {
536   .runs_after = VLIB_INITS ("vnet_crypto_init"),
537 };
538 /* *INDENT-ON* */
539
540
541 /* *INDENT-OFF* */
542 VLIB_PLUGIN_REGISTER () = {
543   .version = VPP_BUILD_VER,
544   .description = "OpenSSL Crypto Engine",
545 };
546 /* *INDENT-ON* */
547
548 /*
549  * fd.io coding-style-patch-verification: ON
550  *
551  * Local Variables:
552  * eval: (c-set-style "gnu")
553  * End:
554  */