build: use static deps for pkg-config libs.private
[dpdk.git] / examples / ipsec-secgw / sp6.c
1 /* SPDX-License-Identifier: BSD-3-Clause
2  * Copyright(c) 2016 Intel Corporation
3  */
4
5 /*
6  * Security Policies
7  */
8 #include <sys/types.h>
9 #include <netinet/in.h>
10 #include <netinet/ip6.h>
11
12 #include <rte_acl.h>
13 #include <rte_ip.h>
14
15 #include "ipsec.h"
16 #include "parser.h"
17
18 #define MAX_ACL_RULE_NUM        1024
19
20 enum {
21         IP6_PROTO,
22         IP6_SRC0,
23         IP6_SRC1,
24         IP6_SRC2,
25         IP6_SRC3,
26         IP6_DST0,
27         IP6_DST1,
28         IP6_DST2,
29         IP6_DST3,
30         IP6_SRCP,
31         IP6_DSTP,
32         IP6_NUM
33 };
34
35 #define IP6_ADDR_SIZE 16
36
37 static struct rte_acl_field_def ip6_defs[IP6_NUM] = {
38         {
39         .type = RTE_ACL_FIELD_TYPE_BITMASK,
40         .size = sizeof(uint8_t),
41         .field_index = IP6_PROTO,
42         .input_index = IP6_PROTO,
43         .offset = 0,
44         },
45         {
46         .type = RTE_ACL_FIELD_TYPE_MASK,
47         .size = 4,
48         .field_index = IP6_SRC0,
49         .input_index = IP6_SRC0,
50         .offset = 2
51         },
52         {
53         .type = RTE_ACL_FIELD_TYPE_MASK,
54         .size = 4,
55         .field_index = IP6_SRC1,
56         .input_index = IP6_SRC1,
57         .offset = 6
58         },
59         {
60         .type = RTE_ACL_FIELD_TYPE_MASK,
61         .size = 4,
62         .field_index = IP6_SRC2,
63         .input_index = IP6_SRC2,
64         .offset = 10
65         },
66         {
67         .type = RTE_ACL_FIELD_TYPE_MASK,
68         .size = 4,
69         .field_index = IP6_SRC3,
70         .input_index = IP6_SRC3,
71         .offset = 14
72         },
73         {
74         .type = RTE_ACL_FIELD_TYPE_MASK,
75         .size = 4,
76         .field_index = IP6_DST0,
77         .input_index = IP6_DST0,
78         .offset = 18
79         },
80         {
81         .type = RTE_ACL_FIELD_TYPE_MASK,
82         .size = 4,
83         .field_index = IP6_DST1,
84         .input_index = IP6_DST1,
85         .offset = 22
86         },
87         {
88         .type = RTE_ACL_FIELD_TYPE_MASK,
89         .size = 4,
90         .field_index = IP6_DST2,
91         .input_index = IP6_DST2,
92         .offset = 26
93         },
94         {
95         .type = RTE_ACL_FIELD_TYPE_MASK,
96         .size = 4,
97         .field_index = IP6_DST3,
98         .input_index = IP6_DST3,
99         .offset = 30
100         },
101         {
102         .type = RTE_ACL_FIELD_TYPE_RANGE,
103         .size = sizeof(uint16_t),
104         .field_index = IP6_SRCP,
105         .input_index = IP6_SRCP,
106         .offset = 34
107         },
108         {
109         .type = RTE_ACL_FIELD_TYPE_RANGE,
110         .size = sizeof(uint16_t),
111         .field_index = IP6_DSTP,
112         .input_index = IP6_SRCP,
113         .offset = 36
114         }
115 };
116
117 RTE_ACL_RULE_DEF(acl6_rules, RTE_DIM(ip6_defs));
118
119 static struct acl6_rules acl6_rules_out[MAX_ACL_RULE_NUM];
120 static uint32_t nb_acl6_rules_out;
121
122 static struct acl6_rules acl6_rules_in[MAX_ACL_RULE_NUM];
123 static uint32_t nb_acl6_rules_in;
124
125 void
126 parse_sp6_tokens(char **tokens, uint32_t n_tokens,
127         struct parse_status *status)
128 {
129         struct acl6_rules *rule_ipv6 = NULL;
130
131         uint32_t *ri = NULL; /* rule index */
132         uint32_t ti = 0; /* token index */
133
134         uint32_t esp_p = 0;
135         uint32_t protect_p = 0;
136         uint32_t bypass_p = 0;
137         uint32_t discard_p = 0;
138         uint32_t pri_p = 0;
139         uint32_t src_p = 0;
140         uint32_t dst_p = 0;
141         uint32_t proto_p = 0;
142         uint32_t sport_p = 0;
143         uint32_t dport_p = 0;
144
145         if (strcmp(tokens[1], "in") == 0) {
146                 ri = &nb_acl6_rules_in;
147
148                 APP_CHECK(*ri <= MAX_ACL_RULE_NUM - 1, status, "too "
149                         "many sp rules, abort insertion\n");
150                 if (status->status < 0)
151                         return;
152
153                 rule_ipv6 = &acl6_rules_in[*ri];
154
155         } else if (strcmp(tokens[1], "out") == 0) {
156                 ri = &nb_acl6_rules_out;
157
158                 APP_CHECK(*ri <= MAX_ACL_RULE_NUM - 1, status, "too "
159                         "many sp rules, abort insertion\n");
160                 if (status->status < 0)
161                         return;
162
163                 rule_ipv6 = &acl6_rules_out[*ri];
164
165         } else {
166                 APP_CHECK(0, status, "unrecognized input \"%s\", expect"
167                         " \"in\" or \"out\"\n", tokens[ti]);
168                 return;
169         }
170
171         rule_ipv6->data.category_mask = 1;
172
173
174         for (ti = 2; ti < n_tokens; ti++) {
175                 if (strcmp(tokens[ti], "esp") == 0) {
176                         /* currently do nothing */
177                         APP_CHECK_PRESENCE(esp_p, tokens[ti], status);
178                         if (status->status < 0)
179                                 return;
180                         esp_p = 1;
181                         continue;
182                 }
183
184                 if (strcmp(tokens[ti], "protect") == 0) {
185                         APP_CHECK_PRESENCE(protect_p, tokens[ti], status);
186                         if (status->status < 0)
187                                 return;
188                         APP_CHECK(bypass_p == 0, status, "conflict item "
189                                 "between \"%s\" and \"%s\"", tokens[ti],
190                                 "bypass");
191                         if (status->status < 0)
192                                 return;
193                         APP_CHECK(discard_p == 0, status, "conflict item "
194                                 "between \"%s\" and \"%s\"", tokens[ti],
195                                 "discard");
196                         if (status->status < 0)
197                                 return;
198                         INCREMENT_TOKEN_INDEX(ti, n_tokens, status);
199                         if (status->status < 0)
200                                 return;
201                         APP_CHECK_TOKEN_IS_NUM(tokens, ti, status);
202                         if (status->status < 0)
203                                 return;
204
205                         rule_ipv6->data.userdata =
206                                 PROTECT(atoi(tokens[ti]));
207
208                         protect_p = 1;
209                         continue;
210                 }
211
212                 if (strcmp(tokens[ti], "bypass") == 0) {
213                         APP_CHECK_PRESENCE(bypass_p, tokens[ti], status);
214                         if (status->status < 0)
215                                 return;
216                         APP_CHECK(protect_p == 0, status, "conflict item "
217                                 "between \"%s\" and \"%s\"", tokens[ti],
218                                 "protect");
219                         if (status->status < 0)
220                                 return;
221                         APP_CHECK(discard_p == 0, status, "conflict item "
222                                 "between \"%s\" and \"%s\"", tokens[ti],
223                                 "discard");
224                         if (status->status < 0)
225                                 return;
226
227                         rule_ipv6->data.userdata = BYPASS;
228
229                         bypass_p = 1;
230                         continue;
231                 }
232
233                 if (strcmp(tokens[ti], "discard") == 0) {
234                         APP_CHECK_PRESENCE(discard_p, tokens[ti], status);
235                         if (status->status < 0)
236                                 return;
237                         APP_CHECK(protect_p == 0, status, "conflict item "
238                                 "between \"%s\" and \"%s\"", tokens[ti],
239                                 "protect");
240                         if (status->status < 0)
241                                 return;
242                         APP_CHECK(bypass_p == 0, status, "conflict item "
243                                 "between \"%s\" and \"%s\"", tokens[ti],
244                                 "discard");
245                         if (status->status < 0)
246                                 return;
247
248                         rule_ipv6->data.userdata = DISCARD;
249
250                         discard_p = 1;
251                         continue;
252                 }
253
254                 if (strcmp(tokens[ti], "pri") == 0) {
255                         APP_CHECK_PRESENCE(pri_p, tokens[ti], status);
256                         if (status->status < 0)
257                                 return;
258                         INCREMENT_TOKEN_INDEX(ti, n_tokens, status);
259                         if (status->status < 0)
260                                 return;
261                         APP_CHECK_TOKEN_IS_NUM(tokens, ti, status);
262                         if (status->status < 0)
263                                 return;
264
265                         rule_ipv6->data.priority = atoi(tokens[ti]);
266
267                         pri_p = 1;
268                         continue;
269                 }
270
271                 if (strcmp(tokens[ti], "src") == 0) {
272                         struct in6_addr ip;
273                         uint32_t depth;
274
275                         APP_CHECK_PRESENCE(src_p, tokens[ti], status);
276                         if (status->status < 0)
277                                 return;
278                         INCREMENT_TOKEN_INDEX(ti, n_tokens, status);
279                         if (status->status < 0)
280                                 return;
281
282                         APP_CHECK(parse_ipv6_addr(tokens[ti], &ip,
283                                 &depth) == 0, status, "unrecognized "
284                                 "input \"%s\", expect valid ipv6 "
285                                 "addr", tokens[ti]);
286                         if (status->status < 0)
287                                 return;
288
289                         rule_ipv6->field[1].value.u32 =
290                                 (uint32_t)ip.s6_addr[0] << 24 |
291                                 (uint32_t)ip.s6_addr[1] << 16 |
292                                 (uint32_t)ip.s6_addr[2] << 8 |
293                                 (uint32_t)ip.s6_addr[3];
294                         rule_ipv6->field[1].mask_range.u32 =
295                                 (depth > 32) ? 32 : depth;
296                         depth = (depth > 32) ? (depth - 32) : 0;
297                         rule_ipv6->field[2].value.u32 =
298                                 (uint32_t)ip.s6_addr[4] << 24 |
299                                 (uint32_t)ip.s6_addr[5] << 16 |
300                                 (uint32_t)ip.s6_addr[6] << 8 |
301                                 (uint32_t)ip.s6_addr[7];
302                         rule_ipv6->field[2].mask_range.u32 =
303                                 (depth > 32) ? 32 : depth;
304                         depth = (depth > 32) ? (depth - 32) : 0;
305                         rule_ipv6->field[3].value.u32 =
306                                 (uint32_t)ip.s6_addr[8] << 24 |
307                                 (uint32_t)ip.s6_addr[9] << 16 |
308                                 (uint32_t)ip.s6_addr[10] << 8 |
309                                 (uint32_t)ip.s6_addr[11];
310                         rule_ipv6->field[3].mask_range.u32 =
311                                 (depth > 32) ? 32 : depth;
312                         depth = (depth > 32) ? (depth - 32) : 0;
313                         rule_ipv6->field[4].value.u32 =
314                                 (uint32_t)ip.s6_addr[12] << 24 |
315                                 (uint32_t)ip.s6_addr[13] << 16 |
316                                 (uint32_t)ip.s6_addr[14] << 8 |
317                                 (uint32_t)ip.s6_addr[15];
318                         rule_ipv6->field[4].mask_range.u32 =
319                                 (depth > 32) ? 32 : depth;
320
321                         src_p = 1;
322                         continue;
323                 }
324
325                 if (strcmp(tokens[ti], "dst") == 0) {
326                         struct in6_addr ip;
327                         uint32_t depth;
328
329                         APP_CHECK_PRESENCE(dst_p, tokens[ti], status);
330                         if (status->status < 0)
331                                 return;
332                         INCREMENT_TOKEN_INDEX(ti, n_tokens, status);
333                         if (status->status < 0)
334                                 return;
335
336                         APP_CHECK(parse_ipv6_addr(tokens[ti], &ip,
337                                 &depth) == 0, status, "unrecognized "
338                                 "input \"%s\", expect valid ipv6 "
339                                 "addr", tokens[ti]);
340                         if (status->status < 0)
341                                 return;
342
343                         rule_ipv6->field[5].value.u32 =
344                                 (uint32_t)ip.s6_addr[0] << 24 |
345                                 (uint32_t)ip.s6_addr[1] << 16 |
346                                 (uint32_t)ip.s6_addr[2] << 8 |
347                                 (uint32_t)ip.s6_addr[3];
348                         rule_ipv6->field[5].mask_range.u32 =
349                                 (depth > 32) ? 32 : depth;
350                         depth = (depth > 32) ? (depth - 32) : 0;
351                         rule_ipv6->field[6].value.u32 =
352                                 (uint32_t)ip.s6_addr[4] << 24 |
353                                 (uint32_t)ip.s6_addr[5] << 16 |
354                                 (uint32_t)ip.s6_addr[6] << 8 |
355                                 (uint32_t)ip.s6_addr[7];
356                         rule_ipv6->field[6].mask_range.u32 =
357                                 (depth > 32) ? 32 : depth;
358                         depth = (depth > 32) ? (depth - 32) : 0;
359                         rule_ipv6->field[7].value.u32 =
360                                 (uint32_t)ip.s6_addr[8] << 24 |
361                                 (uint32_t)ip.s6_addr[9] << 16 |
362                                 (uint32_t)ip.s6_addr[10] << 8 |
363                                 (uint32_t)ip.s6_addr[11];
364                         rule_ipv6->field[7].mask_range.u32 =
365                                 (depth > 32) ? 32 : depth;
366                         depth = (depth > 32) ? (depth - 32) : 0;
367                         rule_ipv6->field[8].value.u32 =
368                                 (uint32_t)ip.s6_addr[12] << 24 |
369                                 (uint32_t)ip.s6_addr[13] << 16 |
370                                 (uint32_t)ip.s6_addr[14] << 8 |
371                                 (uint32_t)ip.s6_addr[15];
372                         rule_ipv6->field[8].mask_range.u32 =
373                                 (depth > 32) ? 32 : depth;
374
375                         dst_p = 1;
376                         continue;
377                 }
378
379                 if (strcmp(tokens[ti], "proto") == 0) {
380                         uint16_t low, high;
381
382                         APP_CHECK_PRESENCE(proto_p, tokens[ti], status);
383                         if (status->status < 0)
384                                 return;
385                         INCREMENT_TOKEN_INDEX(ti, n_tokens, status);
386                         if (status->status < 0)
387                                 return;
388
389                         APP_CHECK(parse_range(tokens[ti], &low, &high)
390                                 == 0, status, "unrecognized input \"%s\""
391                                 ", expect \"from:to\"", tokens[ti]);
392                         if (status->status < 0)
393                                 return;
394                         APP_CHECK(low <= 0xff, status, "proto low "
395                                 "over-limit");
396                         if (status->status < 0)
397                                 return;
398                         APP_CHECK(high <= 0xff, status, "proto high "
399                                 "over-limit");
400                         if (status->status < 0)
401                                 return;
402
403                         rule_ipv6->field[0].value.u8 = (uint8_t)low;
404                         rule_ipv6->field[0].mask_range.u8 = (uint8_t)high;
405
406                         proto_p = 1;
407                         continue;
408                 }
409
410                 if (strcmp(tokens[ti], "sport") == 0) {
411                         uint16_t port_low, port_high;
412
413                         APP_CHECK_PRESENCE(sport_p, tokens[ti], status);
414                         if (status->status < 0)
415                                 return;
416                         INCREMENT_TOKEN_INDEX(ti, n_tokens, status);
417                         if (status->status < 0)
418                                 return;
419
420                         APP_CHECK(parse_range(tokens[ti], &port_low,
421                                 &port_high) == 0, status, "unrecognized "
422                                 "input \"%s\", expect \"port_from:"
423                                 "port_to\"", tokens[ti]);
424                         if (status->status < 0)
425                                 return;
426
427                         rule_ipv6->field[9].value.u16 = port_low;
428                         rule_ipv6->field[9].mask_range.u16 = port_high;
429
430                         sport_p = 1;
431                         continue;
432                 }
433
434                 if (strcmp(tokens[ti], "dport") == 0) {
435                         uint16_t port_low, port_high;
436
437                         APP_CHECK_PRESENCE(dport_p, tokens[ti], status);
438                         if (status->status < 0)
439                                 return;
440                         INCREMENT_TOKEN_INDEX(ti, n_tokens, status);
441                         if (status->status < 0)
442                                 return;
443
444                         APP_CHECK(parse_range(tokens[ti], &port_low,
445                                 &port_high) == 0, status, "unrecognized "
446                                 "input \"%s\", expect \"port_from:"
447                                 "port_to\"", tokens[ti]);
448                         if (status->status < 0)
449                                 return;
450
451                         rule_ipv6->field[10].value.u16 = port_low;
452                         rule_ipv6->field[10].mask_range.u16 = port_high;
453
454                         dport_p = 1;
455                         continue;
456                 }
457
458                 /* unrecognizeable input */
459                 APP_CHECK(0, status, "unrecognized input \"%s\"",
460                         tokens[ti]);
461                 return;
462         }
463
464         /* check if argument(s) are missing */
465         APP_CHECK(esp_p == 1, status, "missing argument \"esp\"");
466         if (status->status < 0)
467                 return;
468
469         APP_CHECK(protect_p | bypass_p | discard_p, status, "missing "
470                 "argument \"protect\", \"bypass\", or \"discard\"");
471         if (status->status < 0)
472                 return;
473
474         *ri = *ri + 1;
475 }
476
477 static inline void
478 print_one_ip6_rule(const struct acl6_rules *rule, int32_t extra)
479 {
480         uint8_t a, b, c, d;
481
482         uint32_t_to_char(rule->field[IP6_SRC0].value.u32,
483                 &a, &b, &c, &d);
484         printf("%.2x%.2x:%.2x%.2x", a, b, c, d);
485         uint32_t_to_char(rule->field[IP6_SRC1].value.u32,
486                 &a, &b, &c, &d);
487         printf(":%.2x%.2x:%.2x%.2x", a, b, c, d);
488         uint32_t_to_char(rule->field[IP6_SRC2].value.u32,
489                 &a, &b, &c, &d);
490         printf(":%.2x%.2x:%.2x%.2x", a, b, c, d);
491         uint32_t_to_char(rule->field[IP6_SRC3].value.u32,
492                 &a, &b, &c, &d);
493         printf(":%.2x%.2x:%.2x%.2x/%u ", a, b, c, d,
494                         rule->field[IP6_SRC0].mask_range.u32
495                         + rule->field[IP6_SRC1].mask_range.u32
496                         + rule->field[IP6_SRC2].mask_range.u32
497                         + rule->field[IP6_SRC3].mask_range.u32);
498
499         uint32_t_to_char(rule->field[IP6_DST0].value.u32,
500                 &a, &b, &c, &d);
501         printf("%.2x%.2x:%.2x%.2x", a, b, c, d);
502         uint32_t_to_char(rule->field[IP6_DST1].value.u32,
503                 &a, &b, &c, &d);
504         printf(":%.2x%.2x:%.2x%.2x", a, b, c, d);
505         uint32_t_to_char(rule->field[IP6_DST2].value.u32,
506                 &a, &b, &c, &d);
507         printf(":%.2x%.2x:%.2x%.2x", a, b, c, d);
508         uint32_t_to_char(rule->field[IP6_DST3].value.u32,
509                 &a, &b, &c, &d);
510         printf(":%.2x%.2x:%.2x%.2x/%u ", a, b, c, d,
511                         rule->field[IP6_DST0].mask_range.u32
512                         + rule->field[IP6_DST1].mask_range.u32
513                         + rule->field[IP6_DST2].mask_range.u32
514                         + rule->field[IP6_DST3].mask_range.u32);
515
516         printf("%hu : %hu %hu : %hu 0x%hhx/0x%hhx ",
517                 rule->field[IP6_SRCP].value.u16,
518                 rule->field[IP6_SRCP].mask_range.u16,
519                 rule->field[IP6_DSTP].value.u16,
520                 rule->field[IP6_DSTP].mask_range.u16,
521                 rule->field[IP6_PROTO].value.u8,
522                 rule->field[IP6_PROTO].mask_range.u8);
523         if (extra)
524                 printf("0x%x-0x%x-0x%x ",
525                         rule->data.category_mask,
526                         rule->data.priority,
527                         rule->data.userdata);
528 }
529
530 static inline void
531 dump_ip6_rules(const struct acl6_rules *rule, int32_t num, int32_t extra)
532 {
533         int32_t i;
534
535         for (i = 0; i < num; i++, rule++) {
536                 printf("\t%d:", i + 1);
537                 print_one_ip6_rule(rule, extra);
538                 printf("\n");
539         }
540 }
541
542 static struct rte_acl_ctx *
543 acl6_init(const char *name, int32_t socketid, const struct acl6_rules *rules,
544                 uint32_t rules_nb)
545 {
546         char s[PATH_MAX];
547         struct rte_acl_param acl_param;
548         struct rte_acl_config acl_build_param;
549         struct rte_acl_ctx *ctx;
550
551         printf("Creating SP context with %u max rules\n", MAX_ACL_RULE_NUM);
552
553         memset(&acl_param, 0, sizeof(acl_param));
554
555         /* Create ACL contexts */
556         snprintf(s, sizeof(s), "%s_%d", name, socketid);
557
558         printf("IPv4 %s entries [%u]:\n", s, rules_nb);
559         dump_ip6_rules(rules, rules_nb, 1);
560
561         acl_param.name = s;
562         acl_param.socket_id = socketid;
563         acl_param.rule_size = RTE_ACL_RULE_SZ(RTE_DIM(ip6_defs));
564         acl_param.max_rule_num = MAX_ACL_RULE_NUM;
565
566         ctx = rte_acl_create(&acl_param);
567         if (ctx == NULL)
568                 rte_exit(EXIT_FAILURE, "Failed to create ACL context\n");
569
570         if (rte_acl_add_rules(ctx, (const struct rte_acl_rule *)rules,
571                                 rules_nb) < 0)
572                 rte_exit(EXIT_FAILURE, "add rules failed\n");
573
574         /* Perform builds */
575         memset(&acl_build_param, 0, sizeof(acl_build_param));
576
577         acl_build_param.num_categories = DEFAULT_MAX_CATEGORIES;
578         acl_build_param.num_fields = RTE_DIM(ip6_defs);
579         memcpy(&acl_build_param.defs, ip6_defs, sizeof(ip6_defs));
580
581         if (rte_acl_build(ctx, &acl_build_param) != 0)
582                 rte_exit(EXIT_FAILURE, "Failed to build ACL trie\n");
583
584         rte_acl_dump(ctx);
585
586         return ctx;
587 }
588
589 void
590 sp6_init(struct socket_ctx *ctx, int32_t socket_id)
591 {
592         const char *name;
593
594         if (ctx == NULL)
595                 rte_exit(EXIT_FAILURE, "NULL context.\n");
596
597         if (ctx->sp_ip6_in != NULL)
598                 rte_exit(EXIT_FAILURE, "Inbound IPv6 SP DB for socket %u "
599                                 "already initialized\n", socket_id);
600
601         if (ctx->sp_ip6_out != NULL)
602                 rte_exit(EXIT_FAILURE, "Outbound IPv6 SP DB for socket %u "
603                                 "already initialized\n", socket_id);
604
605         if (nb_acl6_rules_in > 0) {
606                 name = "sp_ip6_in";
607                 ctx->sp_ip6_in = (struct sp_ctx *)acl6_init(name,
608                         socket_id, acl6_rules_in, nb_acl6_rules_in);
609         } else
610                 RTE_LOG(WARNING, IPSEC, "No IPv6 SP Inbound rule "
611                         "specified\n");
612
613         if (nb_acl6_rules_out > 0) {
614                 name = "sp_ip6_out";
615                 ctx->sp_ip6_out = (struct sp_ctx *)acl6_init(name,
616                         socket_id, acl6_rules_out, nb_acl6_rules_out);
617         } else
618                 RTE_LOG(WARNING, IPSEC, "No IPv6 SP Outbound rule "
619                         "specified\n");
620 }
621
622 /*
623  * Search though SP rules for given SPI.
624  */
625 int
626 sp6_spi_present(uint32_t spi, int inbound)
627 {
628         uint32_t i, num;
629         const struct acl6_rules *acr;
630
631         if (inbound != 0) {
632                 acr = acl6_rules_in;
633                 num = nb_acl6_rules_in;
634         } else {
635                 acr = acl6_rules_out;
636                 num = nb_acl6_rules_out;
637         }
638
639         for (i = 0; i != num; i++) {
640                 if (acr[i].data.userdata == PROTECT(spi))
641                         return i;
642         }
643
644         return -ENOENT;
645 }