]> git.proxmox.com Git - mirror_frr.git/blame - bgpd/bgp_flowspec_util.c
bgpd: flowspec code support for ipv6
[mirror_frr.git] / bgpd / bgp_flowspec_util.c
CommitLineData
034cdee9
PG
1/* BGP FlowSpec Utilities
2 * Portions:
3 * Copyright (C) 2017 ChinaTelecom SDN Group
4 * Copyright (C) 2018 6WIND
5 *
6 * FRRouting is free software; you can redistribute it and/or modify it
7 * under the terms of the GNU General Public License as published by the
8 * Free Software Foundation; either version 2, or (at your option) any
9 * later version.
10 *
11 * FRRouting is distributed in the hope that it will be useful, but
12 * WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License along
17 * with this program; see the file COPYING; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
19 */
20
21#include "zebra.h"
22
23#include "prefix.h"
02705213 24#include "lib_errors.h"
98a9dbc7 25
0378bcaa 26#include "bgp_route.h"
98a9dbc7 27#include "bgp_table.h"
034cdee9 28#include "bgp_flowspec_util.h"
98a9dbc7 29#include "bgp_flowspec_private.h"
47555ee9 30#include "bgp_pbr.h"
4f3be667 31#include "bgp_errors.h"
034cdee9
PG
32
33static void hex2bin(uint8_t *hex, int *bin)
34{
35 int remainder = *hex;
36 int i = 0;
37
38 while (remainder >= 1 && i < 8) {
39 bin[7-i] = remainder % 2;
40 remainder = remainder / 2;
41 i++;
42 }
43 for (; i < 8; i++)
44 bin[7-i] = 0;
45}
46
47static int hexstr2num(uint8_t *hexstr, int len)
48{
49 int i = 0;
50 int num = 0;
51
52 for (i = 0; i < len; i++)
53 num = hexstr[i] + 16*16*num;
54 return num;
55}
56
47555ee9
PG
57/* call bgp_flowspec_op_decode
58 * returns offset
59 */
60static int bgp_flowspec_call_non_opaque_decode(uint8_t *nlri_content, int len,
61 struct bgp_pbr_match_val *mval,
62 uint8_t *match_num, int *error)
63{
64 int ret;
65
66 ret = bgp_flowspec_op_decode(
67 BGP_FLOWSPEC_CONVERT_TO_NON_OPAQUE,
68 nlri_content,
69 len,
70 mval, error);
71 if (*error < 0)
e50f7cfd 72 flog_err(EC_BGP_FLOWSPEC_PACKET,
1c50c1c0 73 "%s: flowspec_op_decode error %d", __func__, *error);
47555ee9
PG
74 else
75 *match_num = *error;
76 return ret;
77}
78
1840384b 79
bd494ec5
DS
80bool bgp_flowspec_contains_prefix(const struct prefix *pfs,
81 struct prefix *input, int prefix_check)
47555ee9
PG
82{
83 uint32_t offset = 0;
84 int type;
85 int ret = 0, error = 0;
86 uint8_t *nlri_content = (uint8_t *)pfs->u.prefix_flowspec.ptr;
87 size_t len = pfs->u.prefix_flowspec.prefixlen;
1840384b 88 afi_t afi = family2afi(pfs->u.prefix_flowspec.family);
47555ee9
PG
89 struct prefix compare;
90
91 error = 0;
92 while (offset < len-1 && error >= 0) {
93 type = nlri_content[offset];
94 offset++;
95 switch (type) {
96 case FLOWSPEC_DEST_PREFIX:
97 case FLOWSPEC_SRC_PREFIX:
98 memset(&compare, 0, sizeof(struct prefix));
99 ret = bgp_flowspec_ip_address(
100 BGP_FLOWSPEC_CONVERT_TO_NON_OPAQUE,
101 nlri_content+offset,
102 len - offset,
1840384b
PG
103 &compare, &error,
104 afi);
47555ee9
PG
105 if (ret <= 0)
106 break;
107 if (prefix_check &&
108 compare.prefixlen != input->prefixlen)
109 break;
110 if (compare.family != input->family)
111 break;
112 if ((input->family == AF_INET) &&
113 IPV4_ADDR_SAME(&input->u.prefix4,
114 &compare.u.prefix4))
115 return true;
116 if ((input->family == AF_INET6) &&
117 IPV6_ADDR_SAME(&input->u.prefix6.s6_addr,
118 &compare.u.prefix6.s6_addr))
119 return true;
120 break;
121 case FLOWSPEC_IP_PROTOCOL:
122 case FLOWSPEC_PORT:
123 case FLOWSPEC_DEST_PORT:
124 case FLOWSPEC_SRC_PORT:
125 case FLOWSPEC_ICMP_TYPE:
126 case FLOWSPEC_ICMP_CODE:
127 ret = bgp_flowspec_op_decode(BGP_FLOWSPEC_VALIDATE_ONLY,
128 nlri_content+offset,
129 len - offset,
130 NULL, &error);
131 break;
588ec356 132 case FLOWSPEC_FRAGMENT:
47555ee9 133 case FLOWSPEC_TCP_FLAGS:
588ec356 134 ret = bgp_flowspec_bitmask_decode(
47555ee9
PG
135 BGP_FLOWSPEC_VALIDATE_ONLY,
136 nlri_content+offset,
137 len - offset,
138 NULL, &error);
139 break;
140 case FLOWSPEC_PKT_LEN:
141 case FLOWSPEC_DSCP:
142 ret = bgp_flowspec_op_decode(
143 BGP_FLOWSPEC_VALIDATE_ONLY,
144 nlri_content + offset,
145 len - offset, NULL,
146 &error);
147 break;
47555ee9
PG
148 default:
149 error = -1;
150 break;
151 }
152 offset += ret;
153 }
154 return false;
155}
034cdee9
PG
156
157/*
158 * handle the flowspec address src/dst or generic address NLRI
159 * return number of bytes analysed ( >= 0).
160 */
161int bgp_flowspec_ip_address(enum bgp_flowspec_util_nlri_t type,
162 uint8_t *nlri_ptr,
163 uint32_t max_len,
1840384b
PG
164 void *result, int *error,
165 afi_t afi)
034cdee9
PG
166{
167 char *display = (char *)result; /* for return_string */
168 struct prefix *prefix = (struct prefix *)result;
169 uint32_t offset = 0;
170 struct prefix prefix_local;
171 int psize;
172
173 *error = 0;
174 memset(&prefix_local, 0, sizeof(struct prefix));
175 /* read the prefix length */
176 prefix_local.prefixlen = nlri_ptr[offset];
177 psize = PSIZE(prefix_local.prefixlen);
178 offset++;
179 /* TODO Flowspec IPv6 Support */
180 prefix_local.family = AF_INET;
181 /* Prefix length check. */
182 if (prefix_local.prefixlen > prefix_blen(&prefix_local) * 8)
183 *error = -1;
184 /* When packet overflow occur return immediately. */
185 if (psize + offset > max_len)
186 *error = -1;
187 /* Defensive coding, double-check
188 * the psize fits in a struct prefix
189 */
190 if (psize > (ssize_t)sizeof(prefix_local.u))
191 *error = -1;
192 memcpy(&prefix_local.u.prefix, &nlri_ptr[offset], psize);
193 offset += psize;
194 switch (type) {
195 case BGP_FLOWSPEC_RETURN_STRING:
196 prefix2str(&prefix_local, display,
197 BGP_FLOWSPEC_STRING_DISPLAY_MAX);
198 break;
199 case BGP_FLOWSPEC_CONVERT_TO_NON_OPAQUE:
200 PREFIX_COPY_IPV4(prefix, &prefix_local)
201 break;
202 case BGP_FLOWSPEC_VALIDATE_ONLY:
203 default:
204 break;
205 }
206 return offset;
207}
208
209/*
210 * handle the flowspec operator NLRI
211 * return number of bytes analysed
212 * if there is an error, the passed error param is used to give error:
213 * -1 if decoding error,
362a06e3
PG
214 * if result is a string, its assumed length
215 * is BGP_FLOWSPEC_STRING_DISPLAY_MAX
034cdee9
PG
216 */
217int bgp_flowspec_op_decode(enum bgp_flowspec_util_nlri_t type,
218 uint8_t *nlri_ptr,
219 uint32_t max_len,
220 void *result, int *error)
221{
222 int op[8];
223 int len, value, value_size;
224 int loop = 0;
225 char *ptr = (char *)result; /* for return_string */
226 uint32_t offset = 0;
362a06e3
PG
227 int len_string = BGP_FLOWSPEC_STRING_DISPLAY_MAX;
228 int len_written;
47555ee9 229 struct bgp_pbr_match_val *mval = (struct bgp_pbr_match_val *)result;
034cdee9
PG
230
231 *error = 0;
232 do {
47555ee9
PG
233 if (loop > BGP_PBR_MATCH_VAL_MAX)
234 *error = -2;
034cdee9
PG
235 hex2bin(&nlri_ptr[offset], op);
236 offset++;
237 len = 2*op[2]+op[3];
238 value_size = 1 << len;
239 value = hexstr2num(&nlri_ptr[offset], value_size);
240 /* can not be < and > at the same time */
241 if (op[5] == 1 && op[6] == 1)
242 *error = -1;
243 /* if first element, AND bit can not be set */
244 if (op[1] == 1 && loop == 0)
245 *error = -1;
246 switch (type) {
247 case BGP_FLOWSPEC_RETURN_STRING:
362a06e3
PG
248 if (loop) {
249 len_written = snprintf(ptr, len_string,
250 ", ");
251 len_string -= len_written;
252 ptr += len_written;
253 }
254 if (op[5] == 1) {
255 len_written = snprintf(ptr, len_string,
256 "<");
257 len_string -= len_written;
258 ptr += len_written;
259 }
260 if (op[6] == 1) {
261 len_written = snprintf(ptr, len_string,
262 ">");
263 len_string -= len_written;
264 ptr += len_written;
265 }
266 if (op[7] == 1) {
267 len_written = snprintf(ptr, len_string,
268 "=");
269 len_string -= len_written;
270 ptr += len_written;
271 }
272 len_written = snprintf(ptr, len_string,
273 " %d ", value);
274 len_string -= len_written;
275 ptr += len_written;
034cdee9
PG
276 break;
277 case BGP_FLOWSPEC_CONVERT_TO_NON_OPAQUE:
47555ee9
PG
278 /* limitation: stop converting */
279 if (*error == -2)
280 break;
281 mval->value = value;
282 if (op[5] == 1)
283 mval->compare_operator |=
284 OPERATOR_COMPARE_LESS_THAN;
285 if (op[6] == 1)
286 mval->compare_operator |=
287 OPERATOR_COMPARE_GREATER_THAN;
288 if (op[7] == 1)
289 mval->compare_operator |=
290 OPERATOR_COMPARE_EQUAL_TO;
291 if (op[1] == 1)
292 mval->unary_operator = OPERATOR_UNARY_AND;
293 else
294 mval->unary_operator = OPERATOR_UNARY_OR;
295 mval++;
034cdee9
PG
296 break;
297 case BGP_FLOWSPEC_VALIDATE_ONLY:
298 default:
299 /* no action */
300 break;
301 }
302 offset += value_size;
303 loop++;
304 } while (op[0] == 0 && offset < max_len - 1);
305 if (offset > max_len)
306 *error = -1;
307 /* use error parameter to count the number of entries */
308 if (*error == 0)
309 *error = loop;
310 return offset;
311}
312
313
314/*
588ec356 315 * handle the flowspec tcpflags or fragment field
034cdee9
PG
316 * return number of bytes analysed
317 * if there is an error, the passed error param is used to give error:
318 * -1 if decoding error,
362a06e3
PG
319 * if result is a string, its assumed length
320 * is BGP_FLOWSPEC_STRING_DISPLAY_MAX
034cdee9 321 */
588ec356 322int bgp_flowspec_bitmask_decode(enum bgp_flowspec_util_nlri_t type,
034cdee9
PG
323 uint8_t *nlri_ptr,
324 uint32_t max_len,
325 void *result, int *error)
326{
327 int op[8];
328 int len, value_size, loop = 0, value;
329 char *ptr = (char *)result; /* for return_string */
47555ee9 330 struct bgp_pbr_match_val *mval = (struct bgp_pbr_match_val *)result;
034cdee9 331 uint32_t offset = 0;
362a06e3
PG
332 int len_string = BGP_FLOWSPEC_STRING_DISPLAY_MAX;
333 int len_written;
034cdee9
PG
334
335 *error = 0;
336 do {
47555ee9
PG
337 if (loop > BGP_PBR_MATCH_VAL_MAX)
338 *error = -2;
034cdee9
PG
339 hex2bin(&nlri_ptr[offset], op);
340 /* if first element, AND bit can not be set */
341 if (op[1] == 1 && loop == 0)
342 *error = -1;
343 offset++;
344 len = 2 * op[2] + op[3];
345 value_size = 1 << len;
346 value = hexstr2num(&nlri_ptr[offset], value_size);
347 switch (type) {
348 case BGP_FLOWSPEC_RETURN_STRING:
362a06e3
PG
349 if (op[1] == 1 && loop != 0) {
350 len_written = snprintf(ptr, len_string,
01ffd28b 351 ",&");
362a06e3
PG
352 len_string -= len_written;
353 ptr += len_written;
354 } else if (op[1] == 0 && loop != 0) {
355 len_written = snprintf(ptr, len_string,
01ffd28b 356 ",|");
362a06e3
PG
357 len_string -= len_written;
358 ptr += len_written;
359 }
01ffd28b
PG
360 if (op[7] == 1) {
361 len_written = snprintf(ptr, len_string,
362 "= ");
363 len_string -= len_written;
364 ptr += len_written;
365 } else {
366 len_written = snprintf(ptr, len_string,
367 "∋ ");
362a06e3
PG
368 len_string -= len_written;
369 ptr += len_written;
370 }
01ffd28b
PG
371 if (op[6] == 1) {
372 len_written = snprintf(ptr, len_string,
373 "! ");
362a06e3
PG
374 len_string -= len_written;
375 ptr += len_written;
376 }
01ffd28b 377 len_written = snprintf(ptr, len_string,
362a06e3
PG
378 "%d", value);
379 len_string -= len_written;
380 ptr += len_written;
034cdee9
PG
381 break;
382 case BGP_FLOWSPEC_CONVERT_TO_NON_OPAQUE:
47555ee9
PG
383 /* limitation: stop converting */
384 if (*error == -2)
385 break;
386 mval->value = value;
387 if (op[6] == 1) {
388 /* different from */
389 mval->compare_operator |=
390 OPERATOR_COMPARE_LESS_THAN;
391 mval->compare_operator |=
392 OPERATOR_COMPARE_GREATER_THAN;
393 } else
394 mval->compare_operator |=
395 OPERATOR_COMPARE_EQUAL_TO;
396 if (op[7] == 1)
397 mval->compare_operator |=
398 OPERATOR_COMPARE_EXACT_MATCH;
399 if (op[1] == 1)
400 mval->unary_operator =
401 OPERATOR_UNARY_AND;
402 else
403 mval->unary_operator =
404 OPERATOR_UNARY_OR;
405 mval++;
034cdee9
PG
406 break;
407 case BGP_FLOWSPEC_VALIDATE_ONLY:
408 default:
409 /* no action */
410 break;
411 }
412 offset += value_size;
413 loop++;
414 } while (op[0] == 0 && offset < max_len - 1);
415 if (offset > max_len)
416 *error = -1;
417 /* use error parameter to count the number of entries */
418 if (*error == 0)
419 *error = loop;
420 return offset;
421}
422
47555ee9 423int bgp_flowspec_match_rules_fill(uint8_t *nlri_content, int len,
1840384b
PG
424 struct bgp_pbr_entry_main *bpem,
425 afi_t afi)
98a9dbc7 426{
47555ee9
PG
427 int offset = 0, error = 0;
428 struct prefix *prefix;
429 struct bgp_pbr_match_val *mval;
430 uint8_t *match_num;
431 uint8_t bitmask = 0;
432 int ret = 0, type;
98a9dbc7 433
47555ee9 434 while (offset < len - 1 && error >= 0) {
98a9dbc7
PG
435 type = nlri_content[offset];
436 offset++;
437 switch (type) {
438 case FLOWSPEC_DEST_PREFIX:
439 case FLOWSPEC_SRC_PREFIX:
47555ee9
PG
440 bitmask = 0;
441 if (type == FLOWSPEC_DEST_PREFIX) {
442 bitmask |= PREFIX_DST_PRESENT;
443 prefix = &bpem->dst_prefix;
444 } else {
445 bitmask |= PREFIX_SRC_PRESENT;
446 prefix = &bpem->src_prefix;
447 }
98a9dbc7
PG
448 ret = bgp_flowspec_ip_address(
449 BGP_FLOWSPEC_CONVERT_TO_NON_OPAQUE,
47555ee9 450 nlri_content + offset,
98a9dbc7 451 len - offset,
1840384b
PG
452 prefix, &error,
453 afi);
47555ee9 454 if (error < 0)
e50f7cfd 455 flog_err(EC_BGP_FLOWSPEC_PACKET,
1c50c1c0
QY
456 "%s: flowspec_ip_address error %d",
457 __func__, error);
4c2876fb
PG
458 else {
459 /* if src or dst address is 0.0.0.0,
460 * ignore that rule
461 */
462 if (prefix->family == AF_INET
975a328e 463 && prefix->u.prefix4.s_addr == INADDR_ANY)
5fa779c9 464 bpem->match_bitmask_iprule |= bitmask;
4c2876fb
PG
465 else
466 bpem->match_bitmask |= bitmask;
467 }
47555ee9 468 offset += ret;
98a9dbc7
PG
469 break;
470 case FLOWSPEC_IP_PROTOCOL:
47555ee9
PG
471 match_num = &(bpem->match_protocol_num);
472 mval = (struct bgp_pbr_match_val *)
473 &(bpem->protocol);
474 offset += bgp_flowspec_call_non_opaque_decode(
475 nlri_content + offset,
476 len - offset,
477 mval, match_num,
478 &error);
479 break;
98a9dbc7 480 case FLOWSPEC_PORT:
47555ee9
PG
481 match_num = &(bpem->match_port_num);
482 mval = (struct bgp_pbr_match_val *)
483 &(bpem->port);
484 offset += bgp_flowspec_call_non_opaque_decode(
485 nlri_content + offset,
486 len - offset,
487 mval, match_num,
488 &error);
489 break;
98a9dbc7 490 case FLOWSPEC_DEST_PORT:
47555ee9
PG
491 match_num = &(bpem->match_dst_port_num);
492 mval = (struct bgp_pbr_match_val *)
493 &(bpem->dst_port);
494 offset += bgp_flowspec_call_non_opaque_decode(
495 nlri_content + offset,
496 len - offset,
497 mval, match_num,
498 &error);
499 break;
98a9dbc7 500 case FLOWSPEC_SRC_PORT:
47555ee9
PG
501 match_num = &(bpem->match_src_port_num);
502 mval = (struct bgp_pbr_match_val *)
503 &(bpem->src_port);
504 offset += bgp_flowspec_call_non_opaque_decode(
505 nlri_content + offset,
506 len - offset,
507 mval, match_num,
508 &error);
509 break;
98a9dbc7 510 case FLOWSPEC_ICMP_TYPE:
47555ee9
PG
511 match_num = &(bpem->match_icmp_type_num);
512 mval = (struct bgp_pbr_match_val *)
513 &(bpem->icmp_type);
514 offset += bgp_flowspec_call_non_opaque_decode(
515 nlri_content + offset,
516 len - offset,
517 mval, match_num,
518 &error);
98a9dbc7 519 break;
47555ee9
PG
520 case FLOWSPEC_ICMP_CODE:
521 match_num = &(bpem->match_icmp_code_num);
522 mval = (struct bgp_pbr_match_val *)
523 &(bpem->icmp_code);
524 offset += bgp_flowspec_call_non_opaque_decode(
525 nlri_content + offset,
526 len - offset,
527 mval, match_num,
528 &error);
98a9dbc7
PG
529 break;
530 case FLOWSPEC_PKT_LEN:
47555ee9
PG
531 match_num =
532 &(bpem->match_packet_length_num);
533 mval = (struct bgp_pbr_match_val *)
534 &(bpem->packet_length);
535 offset += bgp_flowspec_call_non_opaque_decode(
536 nlri_content + offset,
537 len - offset,
538 mval, match_num,
539 &error);
540 break;
98a9dbc7 541 case FLOWSPEC_DSCP:
47555ee9
PG
542 match_num = &(bpem->match_dscp_num);
543 mval = (struct bgp_pbr_match_val *)
544 &(bpem->dscp);
545 offset += bgp_flowspec_call_non_opaque_decode(
546 nlri_content + offset,
547 len - offset,
548 mval, match_num,
549 &error);
550 break;
551 case FLOWSPEC_TCP_FLAGS:
588ec356 552 ret = bgp_flowspec_bitmask_decode(
47555ee9
PG
553 BGP_FLOWSPEC_CONVERT_TO_NON_OPAQUE,
554 nlri_content + offset,
555 len - offset,
556 &bpem->tcpflags, &error);
557 if (error < 0)
1c50c1c0
QY
558 flog_err(
559 EC_BGP_FLOWSPEC_PACKET,
560 "%s: flowspec_tcpflags_decode error %d",
561 __func__, error);
47555ee9
PG
562 else
563 bpem->match_tcpflags_num = error;
564 /* contains the number of slots used */
565 offset += ret;
98a9dbc7
PG
566 break;
567 case FLOWSPEC_FRAGMENT:
588ec356 568 ret = bgp_flowspec_bitmask_decode(
47555ee9
PG
569 BGP_FLOWSPEC_CONVERT_TO_NON_OPAQUE,
570 nlri_content + offset,
571 len - offset, &bpem->fragment,
572 &error);
573 if (error < 0)
1c50c1c0
QY
574 flog_err(
575 EC_BGP_FLOWSPEC_PACKET,
576 "%s: flowspec_fragment_type_decode error %d",
577 __func__, error);
47555ee9 578 else
588ec356 579 bpem->match_fragment_num = error;
47555ee9 580 offset += ret;
98a9dbc7
PG
581 break;
582 default:
450971aa 583 flog_err(EC_LIB_DEVELOPMENT, "%s: unknown type %d\n",
1c50c1c0 584 __func__, type);
98a9dbc7 585 }
98a9dbc7 586 }
5fa779c9
PG
587 if (bpem->match_packet_length_num || bpem->match_fragment_num ||
588 bpem->match_tcpflags_num || bpem->match_dscp_num ||
589 bpem->match_packet_length_num || bpem->match_icmp_code_num ||
590 bpem->match_icmp_type_num || bpem->match_port_num ||
591 bpem->match_src_port_num || bpem->match_dst_port_num ||
592 bpem->match_protocol_num || bpem->match_bitmask)
593 bpem->type = BGP_PBR_IPSET;
594 else if ((bpem->match_bitmask_iprule & PREFIX_SRC_PRESENT) ||
595 (bpem->match_bitmask_iprule & PREFIX_DST_PRESENT))
596 /* the extracted policy rule may not need an
597 * iptables/ipset filtering. check this may not be
598 * a standard ip rule : permit any to any ( eg)
599 */
600 bpem->type = BGP_PBR_IPRULE;
601 else
602 bpem->type = BGP_PBR_UNDEFINED;
47555ee9 603 return error;
98a9dbc7 604}
0378bcaa
PG
605
606/* return 1 if FS entry invalid or no NH IP */
3dc339cd 607bool bgp_flowspec_get_first_nh(struct bgp *bgp, struct bgp_path_info *pi,
1840384b 608 struct prefix *p, afi_t afi)
0378bcaa
PG
609{
610 struct bgp_pbr_entry_main api;
611 int i;
9bcb3eef 612 struct bgp_dest *dest = pi->net;
0378bcaa
PG
613 struct bgp_pbr_entry_action *api_action;
614
615 memset(&api, 0, sizeof(struct bgp_pbr_entry_main));
9bcb3eef
DS
616 if (bgp_pbr_build_and_validate_entry(bgp_dest_get_prefix(dest), pi,
617 &api)
b54892e0 618 < 0)
3dc339cd 619 return true;
0378bcaa
PG
620 for (i = 0; i < api.action_num; i++) {
621 api_action = &api.actions[i];
622 if (api_action->action != ACTION_REDIRECT_IP)
623 continue;
1840384b
PG
624 p->family = afi2family(afi);
625 if (afi == AFI_IP) {
626 p->prefixlen = IPV4_MAX_BITLEN;
627 p->u.prefix4 = api_action->u.zr.redirect_ip_v4;
628 } else {
629 p->prefixlen = IPV6_MAX_BITLEN;
630 memcpy(&p->u.prefix6, &api_action->u.zr.redirect_ip_v6,
631 sizeof(struct in6_addr));
632 }
3dc339cd 633 return false;
0378bcaa 634 }
3dc339cd 635 return true;
0378bcaa 636}