]> git.proxmox.com Git - mirror_frr.git/blame - pimd/pim_tlv.c
lib: Fixup ns.c
[mirror_frr.git] / pimd / pim_tlv.c
CommitLineData
12e41d03
DL
1/*
2 PIM for Quagga
3 Copyright (C) 2008 Everton da Silva Marques
4
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation; either version 2 of the License, or
8 (at your option) any later version.
9
10 This program is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with this program; see the file COPYING; if not, write to the
17 Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
18 MA 02110-1301 USA
19
20 $QuaggaId: $Format:%an, %ai, %h$ $
21*/
22
23#include <zebra.h>
24
25#include "log.h"
26#include "prefix.h"
744d91b3 27#include "if.h"
12e41d03
DL
28
29#include "pimd.h"
30#include "pim_int.h"
31#include "pim_tlv.h"
32#include "pim_str.h"
33#include "pim_msg.h"
34
35uint8_t *pim_tlv_append_uint16(uint8_t *buf,
36 const uint8_t *buf_pastend,
37 uint16_t option_type,
38 uint16_t option_value)
39{
40 uint16_t option_len = 2;
41
aea6cb94
DS
42 if ((buf + PIM_TLV_OPTION_SIZE(option_len)) > buf_pastend)
43 return NULL;
12e41d03
DL
44
45 *(uint16_t *) buf = htons(option_type);
46 buf += 2;
47 *(uint16_t *) buf = htons(option_len);
48 buf += 2;
49 *(uint16_t *) buf = htons(option_value);
50 buf += option_len;
51
52 return buf;
53}
54
55uint8_t *pim_tlv_append_2uint16(uint8_t *buf,
56 const uint8_t *buf_pastend,
57 uint16_t option_type,
58 uint16_t option_value1,
59 uint16_t option_value2)
60{
61 uint16_t option_len = 4;
62
aea6cb94
DS
63 if ((buf + PIM_TLV_OPTION_SIZE(option_len)) > buf_pastend)
64 return NULL;
12e41d03
DL
65
66 *(uint16_t *) buf = htons(option_type);
67 buf += 2;
68 *(uint16_t *) buf = htons(option_len);
69 buf += 2;
70 *(uint16_t *) buf = htons(option_value1);
71 buf += 2;
72 *(uint16_t *) buf = htons(option_value2);
73 buf += 2;
74
75 return buf;
76}
77
78uint8_t *pim_tlv_append_uint32(uint8_t *buf,
79 const uint8_t *buf_pastend,
80 uint16_t option_type,
81 uint32_t option_value)
82{
83 uint16_t option_len = 4;
84
aea6cb94
DS
85 if ((buf + PIM_TLV_OPTION_SIZE(option_len)) > buf_pastend)
86 return NULL;
12e41d03
DL
87
88 *(uint16_t *) buf = htons(option_type);
89 buf += 2;
90 *(uint16_t *) buf = htons(option_len);
91 buf += 2;
92 pim_write_uint32(buf, option_value);
93 buf += option_len;
94
95 return buf;
96}
97
98#define ucast_ipv4_encoding_len (2 + sizeof(struct in_addr))
99
4416b1f6
DS
100static int
101pim_encode_unicast_address (uint8_t *buf, struct prefix *p)
102{
103 switch (p->family)
104 {
105 case AF_INET:
106 *(uint8_t *)buf = PIM_MSG_ADDRESS_FAMILY_IPV4; /* notice: AF_INET != PIM_MSG_ADDRESS_FAMILY_IPV4 */
107 ++buf;
108 *(uint8_t *)buf = 0; /* ucast IPv4 native encoding type (RFC 4601: 4.9.1) */
109 ++buf;
110 memcpy (buf, &p->u.prefix4, sizeof (struct in_addr));
111 return ucast_ipv4_encoding_len;
112 break;
113 default:
114 return 0;
115 break;
116 }
117}
118
12e41d03
DL
119uint8_t *pim_tlv_append_addrlist_ucast(uint8_t *buf,
120 const uint8_t *buf_pastend,
121 struct list *ifconnected)
122{
123 struct listnode *node;
124 uint16_t option_len = 0;
125
126 uint8_t *curr;
127
128 node = listhead(ifconnected);
129
130 /* Empty address list ? */
131 if (!node) {
132 return buf;
133 }
134
135 /* Skip first address (primary) */
136 node = listnextnode(node);
137
138 /* Scan secondary address list */
139 curr = buf + 4; /* skip T and L */
140 for (; node; node = listnextnode(node)) {
141 struct connected *ifc = listgetdata(node);
142 struct prefix *p = ifc->address;
4416b1f6 143 int l_encode;
12e41d03 144
aea6cb94 145 if ((curr + ucast_ipv4_encoding_len) > buf_pastend)
12e41d03 146 return 0;
12e41d03 147
4416b1f6
DS
148 l_encode = pim_encode_unicast_address (curr, p);
149 curr += l_encode;
150 option_len += l_encode;
12e41d03
DL
151 }
152
169edb7f 153 if (PIM_DEBUG_PIM_TRACE_DETAIL) {
aea6cb94
DS
154 zlog_debug("%s: number of encoded secondary unicast IPv4 addresses: %zu",
155 __PRETTY_FUNCTION__,
156 option_len / ucast_ipv4_encoding_len);
12e41d03
DL
157 }
158
159 if (option_len < 1) {
160 /* Empty secondary unicast IPv4 address list */
161 return buf;
162 }
163
164 /*
165 * Write T and L
166 */
167 *(uint16_t *) buf = htons(PIM_MSG_OPTION_TYPE_ADDRESS_LIST);
168 *(uint16_t *) (buf + 2) = htons(option_len);
169
170 return curr;
171}
172
173static int check_tlv_length(const char *label, const char *tlv_name,
174 const char *ifname, struct in_addr src_addr,
175 int correct_len, int option_len)
176{
177 if (option_len != correct_len) {
178 char src_str[100];
179 pim_inet4_dump("<src?>", src_addr, src_str, sizeof(src_str));
180 zlog_warn("%s: PIM hello %s TLV with incorrect value size=%d correct=%d from %s on interface %s",
181 label, tlv_name,
182 option_len, correct_len,
183 src_str, ifname);
184 return -1;
185 }
186
187 return 0;
188}
189
190static void check_tlv_redefinition_uint16(const char *label, const char *tlv_name,
191 const char *ifname, struct in_addr src_addr,
192 pim_hello_options options,
193 pim_hello_options opt_mask,
194 uint16_t new, uint16_t old)
195{
196 if (PIM_OPTION_IS_SET(options, opt_mask)) {
197 char src_str[100];
198 pim_inet4_dump("<src?>", src_addr, src_str, sizeof(src_str));
199 zlog_warn("%s: PIM hello TLV redefined %s=%u old=%u from %s on interface %s",
200 label, tlv_name,
201 new, old,
202 src_str, ifname);
203 }
204}
205
206static void check_tlv_redefinition_uint32(const char *label, const char *tlv_name,
207 const char *ifname, struct in_addr src_addr,
208 pim_hello_options options,
209 pim_hello_options opt_mask,
210 uint32_t new, uint32_t old)
211{
212 if (PIM_OPTION_IS_SET(options, opt_mask)) {
213 char src_str[100];
214 pim_inet4_dump("<src?>", src_addr, src_str, sizeof(src_str));
215 zlog_warn("%s: PIM hello TLV redefined %s=%u old=%u from %s on interface %s",
216 label, tlv_name,
217 new, old,
218 src_str, ifname);
219 }
220}
221
222static void check_tlv_redefinition_uint32_hex(const char *label, const char *tlv_name,
223 const char *ifname, struct in_addr src_addr,
224 pim_hello_options options,
225 pim_hello_options opt_mask,
226 uint32_t new, uint32_t old)
227{
228 if (PIM_OPTION_IS_SET(options, opt_mask)) {
229 char src_str[100];
230 pim_inet4_dump("<src?>", src_addr, src_str, sizeof(src_str));
231 zlog_warn("%s: PIM hello TLV redefined %s=%08x old=%08x from %s on interface %s",
232 label, tlv_name,
233 new, old,
234 src_str, ifname);
235 }
236}
237
238int pim_tlv_parse_holdtime(const char *ifname, struct in_addr src_addr,
239 pim_hello_options *hello_options,
240 uint16_t *hello_option_holdtime,
241 uint16_t option_len,
242 const uint8_t *tlv_curr)
243{
244 const char *label = "holdtime";
245
246 if (check_tlv_length(__PRETTY_FUNCTION__, label,
247 ifname, src_addr,
248 sizeof(uint16_t), option_len)) {
249 return -1;
250 }
251
252 check_tlv_redefinition_uint16(__PRETTY_FUNCTION__, label,
253 ifname, src_addr,
254 *hello_options, PIM_OPTION_MASK_HOLDTIME,
255 PIM_TLV_GET_HOLDTIME(tlv_curr),
256 *hello_option_holdtime);
257
258 PIM_OPTION_SET(*hello_options, PIM_OPTION_MASK_HOLDTIME);
259
260 *hello_option_holdtime = PIM_TLV_GET_HOLDTIME(tlv_curr);
261
262 return 0;
263}
264
265int pim_tlv_parse_lan_prune_delay(const char *ifname, struct in_addr src_addr,
266 pim_hello_options *hello_options,
267 uint16_t *hello_option_propagation_delay,
268 uint16_t *hello_option_override_interval,
269 uint16_t option_len,
270 const uint8_t *tlv_curr)
271{
272 if (check_tlv_length(__PRETTY_FUNCTION__, "lan_prune_delay",
273 ifname, src_addr,
274 sizeof(uint32_t), option_len)) {
275 return -1;
276 }
277
278 check_tlv_redefinition_uint16(__PRETTY_FUNCTION__, "propagation_delay",
279 ifname, src_addr,
280 *hello_options, PIM_OPTION_MASK_LAN_PRUNE_DELAY,
281 PIM_TLV_GET_PROPAGATION_DELAY(tlv_curr),
282 *hello_option_propagation_delay);
283
284 PIM_OPTION_SET(*hello_options, PIM_OPTION_MASK_LAN_PRUNE_DELAY);
285
286 *hello_option_propagation_delay = PIM_TLV_GET_PROPAGATION_DELAY(tlv_curr);
287 if (PIM_TLV_GET_CAN_DISABLE_JOIN_SUPPRESSION(tlv_curr)) {
288 PIM_OPTION_SET(*hello_options, PIM_OPTION_MASK_CAN_DISABLE_JOIN_SUPPRESSION);
289 }
290 else {
291 PIM_OPTION_UNSET(*hello_options, PIM_OPTION_MASK_CAN_DISABLE_JOIN_SUPPRESSION);
292 }
293 ++tlv_curr;
294 ++tlv_curr;
295 *hello_option_override_interval = PIM_TLV_GET_OVERRIDE_INTERVAL(tlv_curr);
296
297 return 0;
298}
299
300int pim_tlv_parse_dr_priority(const char *ifname, struct in_addr src_addr,
301 pim_hello_options *hello_options,
302 uint32_t *hello_option_dr_priority,
303 uint16_t option_len,
304 const uint8_t *tlv_curr)
305{
306 const char *label = "dr_priority";
307
308 if (check_tlv_length(__PRETTY_FUNCTION__, label,
309 ifname, src_addr,
310 sizeof(uint32_t), option_len)) {
311 return -1;
312 }
313
314 check_tlv_redefinition_uint32(__PRETTY_FUNCTION__, label,
315 ifname, src_addr,
316 *hello_options, PIM_OPTION_MASK_DR_PRIORITY,
317 PIM_TLV_GET_DR_PRIORITY(tlv_curr),
318 *hello_option_dr_priority);
319
320 PIM_OPTION_SET(*hello_options, PIM_OPTION_MASK_DR_PRIORITY);
321
322 *hello_option_dr_priority = PIM_TLV_GET_DR_PRIORITY(tlv_curr);
323
324 return 0;
325}
326
327int pim_tlv_parse_generation_id(const char *ifname, struct in_addr src_addr,
328 pim_hello_options *hello_options,
329 uint32_t *hello_option_generation_id,
330 uint16_t option_len,
331 const uint8_t *tlv_curr)
332{
333 const char *label = "generation_id";
334
335 if (check_tlv_length(__PRETTY_FUNCTION__, label,
336 ifname, src_addr,
337 sizeof(uint32_t), option_len)) {
338 return -1;
339 }
340
341 check_tlv_redefinition_uint32_hex(__PRETTY_FUNCTION__, label,
342 ifname, src_addr,
343 *hello_options, PIM_OPTION_MASK_GENERATION_ID,
344 PIM_TLV_GET_GENERATION_ID(tlv_curr),
345 *hello_option_generation_id);
346
347 PIM_OPTION_SET(*hello_options, PIM_OPTION_MASK_GENERATION_ID);
348
349 *hello_option_generation_id = PIM_TLV_GET_GENERATION_ID(tlv_curr);
350
351 return 0;
352}
353
4416b1f6
DS
354int
355pim_parse_addr_ucast (struct prefix *p,
356 const uint8_t *buf,
357 int buf_size)
12e41d03
DL
358{
359 const int ucast_encoding_min_len = 3; /* 1 family + 1 type + 1 addr */
360 const uint8_t *addr;
361 const uint8_t *pastend;
362 int family;
363 int type;
364
365 if (buf_size < ucast_encoding_min_len) {
4416b1f6 366 zlog_warn("%s: unicast address encoding overflow: left=%d needed=%d",
12e41d03 367 __PRETTY_FUNCTION__,
4416b1f6 368 buf_size, ucast_encoding_min_len);
12e41d03
DL
369 return -1;
370 }
371
372 addr = buf;
373 pastend = buf + buf_size;
374
375 family = *addr++;
376 type = *addr++;
377
4416b1f6
DS
378 if (type) {
379 zlog_warn("%s: unknown unicast address encoding type=%d",
380 __PRETTY_FUNCTION__,
381 type);
382 return -2;
383 }
384
12e41d03
DL
385 switch (family) {
386 case PIM_MSG_ADDRESS_FAMILY_IPV4:
12e41d03 387 if ((addr + sizeof(struct in_addr)) > pastend) {
4416b1f6 388 zlog_warn("%s: IPv4 unicast address overflow: left=%zd needed=%zu",
12e41d03 389 __PRETTY_FUNCTION__,
4416b1f6 390 pastend - addr, sizeof(struct in_addr));
12e41d03
DL
391 return -3;
392 }
393
394 p->family = AF_INET; /* notice: AF_INET != PIM_MSG_ADDRESS_FAMILY_IPV4 */
395 memcpy(&p->u.prefix4, addr, sizeof(struct in_addr));
396
397 addr += sizeof(struct in_addr);
398
399 break;
400 default:
401 {
4416b1f6 402 zlog_warn("%s: unknown unicast address encoding family=%d from",
12e41d03 403 __PRETTY_FUNCTION__,
4416b1f6 404 family);
12e41d03
DL
405 return -4;
406 }
407 }
408
409 return addr - buf;
410}
411
4416b1f6
DS
412int
413pim_parse_addr_group (struct prefix *p,
414 const uint8_t *buf,
415 int buf_size)
12e41d03
DL
416{
417 const int grp_encoding_min_len = 4; /* 1 family + 1 type + 1 reserved + 1 addr */
418 const uint8_t *addr;
419 const uint8_t *pastend;
420 int family;
421 int type;
422 int mask_len;
423
424 if (buf_size < grp_encoding_min_len) {
4416b1f6 425 zlog_warn("%s: group address encoding overflow: left=%d needed=%d",
12e41d03 426 __PRETTY_FUNCTION__,
4416b1f6 427 buf_size, grp_encoding_min_len);
12e41d03
DL
428 return -1;
429 }
430
431 addr = buf;
432 pastend = buf + buf_size;
433
434 family = *addr++;
435 type = *addr++;
436 //++addr;
437 ++addr; /* skip b_reserved_z fields */
438 mask_len = *addr++;
439
440 switch (family) {
441 case PIM_MSG_ADDRESS_FAMILY_IPV4:
442 if (type) {
4416b1f6
DS
443 zlog_warn("%s: unknown group address encoding type=%d from",
444 __PRETTY_FUNCTION__, type);
12e41d03
DL
445 return -2;
446 }
447
448 if ((addr + sizeof(struct in_addr)) > pastend) {
4416b1f6 449 zlog_warn("%s: IPv4 group address overflow: left=%zd needed=%zu from",
12e41d03 450 __PRETTY_FUNCTION__,
4416b1f6 451 pastend - addr, sizeof(struct in_addr));
12e41d03
DL
452 return -3;
453 }
454
455 p->family = AF_INET; /* notice: AF_INET != PIM_MSG_ADDRESS_FAMILY_IPV4 */
456 memcpy(&p->u.prefix4, addr, sizeof(struct in_addr));
457 p->prefixlen = mask_len;
458
459 addr += sizeof(struct in_addr);
460
461 break;
462 default:
463 {
4416b1f6
DS
464 zlog_warn("%s: unknown group address encoding family=%d from",
465 __PRETTY_FUNCTION__, family);
12e41d03
DL
466 return -4;
467 }
468 }
469
470 return addr - buf;
471}
472
4416b1f6
DS
473int
474pim_parse_addr_source(struct prefix *p,
475 uint8_t *flags,
476 const uint8_t *buf,
477 int buf_size)
12e41d03
DL
478{
479 const int src_encoding_min_len = 4; /* 1 family + 1 type + 1 reserved + 1 addr */
480 const uint8_t *addr;
481 const uint8_t *pastend;
482 int family;
483 int type;
484 int mask_len;
485
486 if (buf_size < src_encoding_min_len) {
4416b1f6 487 zlog_warn("%s: source address encoding overflow: left=%d needed=%d",
12e41d03 488 __PRETTY_FUNCTION__,
4416b1f6 489 buf_size, src_encoding_min_len);
12e41d03
DL
490 return -1;
491 }
492
493 addr = buf;
494 pastend = buf + buf_size;
495
496 family = *addr++;
497 type = *addr++;
498 *flags = *addr++;
499 mask_len = *addr++;
500
4416b1f6
DS
501 if (type) {
502 zlog_warn("%s: unknown source address encoding type=%d: %02x%02x%02x%02x%02x%02x%02x%02x",
503 __PRETTY_FUNCTION__,
504 type,
505 buf[0], buf[1], buf[2], buf[3], buf[4], buf[5], buf[6], buf[7]);
506 return -2;
507 }
508
12e41d03
DL
509 switch (family) {
510 case PIM_MSG_ADDRESS_FAMILY_IPV4:
12e41d03 511 if ((addr + sizeof(struct in_addr)) > pastend) {
4416b1f6 512 zlog_warn("%s: IPv4 source address overflow: left=%zd needed=%zu",
12e41d03 513 __PRETTY_FUNCTION__,
4416b1f6 514 pastend - addr, sizeof(struct in_addr));
12e41d03
DL
515 return -3;
516 }
517
518 p->family = AF_INET; /* notice: AF_INET != PIM_MSG_ADDRESS_FAMILY_IPV4 */
519 memcpy(&p->u.prefix4, addr, sizeof(struct in_addr));
520 p->prefixlen = mask_len;
521
522 /*
523 RFC 4601: 4.9.1 Encoded Source and Group Address Formats
524
525 Encoded-Source Address
526
527 The mask length MUST be equal to the mask length in bits for
528 the given Address Family and Encoding Type (32 for IPv4 native
529 and 128 for IPv6 native). A router SHOULD ignore any messages
530 received with any other mask length.
531 */
532 if (p->prefixlen != 32) {
4416b1f6
DS
533 zlog_warn("%s: IPv4 bad source address mask: %d",
534 __PRETTY_FUNCTION__, p->prefixlen);
12e41d03
DL
535 return -4;
536 }
537
538 addr += sizeof(struct in_addr);
539
540 break;
541 default:
542 {
4416b1f6 543 zlog_warn("%s: unknown source address encoding family=%d: %02x%02x%02x%02x%02x%02x%02x%02x",
12e41d03 544 __PRETTY_FUNCTION__,
4416b1f6 545 family,
12e41d03
DL
546 buf[0], buf[1], buf[2], buf[3], buf[4], buf[5], buf[6], buf[7]);
547 return -5;
548 }
549 }
550
551 return addr - buf;
552}
553
554#define FREE_ADDR_LIST(hello_option_addr_list) \
555{ \
556 if (hello_option_addr_list) { \
557 list_delete(hello_option_addr_list); \
558 hello_option_addr_list = 0; \
559 } \
560}
561
562int pim_tlv_parse_addr_list(const char *ifname, struct in_addr src_addr,
563 pim_hello_options *hello_options,
564 struct list **hello_option_addr_list,
565 uint16_t option_len,
566 const uint8_t *tlv_curr)
567{
568 const uint8_t *addr;
569 const uint8_t *pastend;
570
571 zassert(hello_option_addr_list);
572
573 /*
574 Scan addr list
575 */
576 addr = tlv_curr;
577 pastend = tlv_curr + option_len;
578 while (addr < pastend) {
579 struct prefix tmp;
580 int addr_offset;
581
582 /*
583 Parse ucast addr
584 */
4416b1f6 585 addr_offset = pim_parse_addr_ucast(&tmp, addr, pastend - addr);
12e41d03
DL
586 if (addr_offset < 1) {
587 char src_str[100];
588 pim_inet4_dump("<src?>", src_addr, src_str, sizeof(src_str));
589 zlog_warn("%s: pim_parse_addr_ucast() failure: from %s on %s",
590 __PRETTY_FUNCTION__,
591 src_str, ifname);
592 FREE_ADDR_LIST(*hello_option_addr_list);
593 return -1;
594 }
595 addr += addr_offset;
596
597 /*
598 Debug
599 */
600 if (PIM_DEBUG_PIM_TRACE) {
601 switch (tmp.family) {
602 case AF_INET:
603 {
604 char addr_str[100];
605 char src_str[100];
606 pim_inet4_dump("<addr?>", tmp.u.prefix4, addr_str, sizeof(addr_str));
607 pim_inet4_dump("<src?>", src_addr, src_str, sizeof(src_str));
608 zlog_debug("%s: PIM hello TLV option: list_old_size=%d IPv4 address %s from %s on %s",
609 __PRETTY_FUNCTION__,
610 *hello_option_addr_list ?
611 ((int) listcount(*hello_option_addr_list)) : -1,
612 addr_str, src_str, ifname);
613 }
614 break;
615 default:
616 {
617 char src_str[100];
618 pim_inet4_dump("<src?>", src_addr, src_str, sizeof(src_str));
619 zlog_debug("%s: PIM hello TLV option: list_old_size=%d UNKNOWN address family from %s on %s",
620 __PRETTY_FUNCTION__,
621 *hello_option_addr_list ?
622 ((int) listcount(*hello_option_addr_list)) : -1,
623 src_str, ifname);
624 }
625 }
626 }
627
628 /*
629 Exclude neighbor's primary address if incorrectly included in
630 the secondary address list
631 */
632 if (tmp.family == AF_INET) {
633 if (tmp.u.prefix4.s_addr == src_addr.s_addr) {
634 char src_str[100];
635 pim_inet4_dump("<src?>", src_addr, src_str, sizeof(src_str));
636 zlog_warn("%s: ignoring primary address in secondary list from %s on %s",
637 __PRETTY_FUNCTION__,
638 src_str, ifname);
639 continue;
640 }
641 }
642
643 /*
644 Allocate list if needed
645 */
646 if (!*hello_option_addr_list) {
647 *hello_option_addr_list = list_new();
648 if (!*hello_option_addr_list) {
649 zlog_err("%s %s: failure: hello_option_addr_list=list_new()",
650 __FILE__, __PRETTY_FUNCTION__);
651 return -2;
652 }
653 (*hello_option_addr_list)->del = (void (*)(void *)) prefix_free;
654 }
655
656 /*
657 Attach addr to list
658 */
659 {
660 struct prefix *p;
661 p = prefix_new();
662 if (!p) {
663 zlog_err("%s %s: failure: prefix_new()",
664 __FILE__, __PRETTY_FUNCTION__);
665 FREE_ADDR_LIST(*hello_option_addr_list);
666 return -3;
667 }
668 p->family = tmp.family;
669 p->u.prefix4 = tmp.u.prefix4;
670 listnode_add(*hello_option_addr_list, p);
671 }
672
673 } /* while (addr < pastend) */
674
675 /*
676 Mark hello option
677 */
678 PIM_OPTION_SET(*hello_options, PIM_OPTION_MASK_ADDRESS_LIST);
679
680 return 0;
681}