]>
Commit | Line | Data |
---|---|---|
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 | |
12e41d03 DL |
19 | */ |
20 | ||
21 | #include <zebra.h> | |
22 | ||
23 | #include "log.h" | |
24 | #include "prefix.h" | |
744d91b3 | 25 | #include "if.h" |
12e41d03 DL |
26 | |
27 | #include "pimd.h" | |
28 | #include "pim_int.h" | |
29 | #include "pim_tlv.h" | |
30 | #include "pim_str.h" | |
31 | #include "pim_msg.h" | |
32 | ||
33 | uint8_t *pim_tlv_append_uint16(uint8_t *buf, | |
34 | const uint8_t *buf_pastend, | |
35 | uint16_t option_type, | |
36 | uint16_t option_value) | |
37 | { | |
38 | uint16_t option_len = 2; | |
39 | ||
aea6cb94 DS |
40 | if ((buf + PIM_TLV_OPTION_SIZE(option_len)) > buf_pastend) |
41 | return NULL; | |
12e41d03 DL |
42 | |
43 | *(uint16_t *) buf = htons(option_type); | |
44 | buf += 2; | |
45 | *(uint16_t *) buf = htons(option_len); | |
46 | buf += 2; | |
47 | *(uint16_t *) buf = htons(option_value); | |
48 | buf += option_len; | |
49 | ||
50 | return buf; | |
51 | } | |
52 | ||
53 | uint8_t *pim_tlv_append_2uint16(uint8_t *buf, | |
54 | const uint8_t *buf_pastend, | |
55 | uint16_t option_type, | |
56 | uint16_t option_value1, | |
57 | uint16_t option_value2) | |
58 | { | |
59 | uint16_t option_len = 4; | |
60 | ||
aea6cb94 DS |
61 | if ((buf + PIM_TLV_OPTION_SIZE(option_len)) > buf_pastend) |
62 | return NULL; | |
12e41d03 DL |
63 | |
64 | *(uint16_t *) buf = htons(option_type); | |
65 | buf += 2; | |
66 | *(uint16_t *) buf = htons(option_len); | |
67 | buf += 2; | |
68 | *(uint16_t *) buf = htons(option_value1); | |
69 | buf += 2; | |
70 | *(uint16_t *) buf = htons(option_value2); | |
71 | buf += 2; | |
72 | ||
73 | return buf; | |
74 | } | |
75 | ||
76 | uint8_t *pim_tlv_append_uint32(uint8_t *buf, | |
77 | const uint8_t *buf_pastend, | |
78 | uint16_t option_type, | |
79 | uint32_t option_value) | |
80 | { | |
81 | uint16_t option_len = 4; | |
82 | ||
aea6cb94 DS |
83 | if ((buf + PIM_TLV_OPTION_SIZE(option_len)) > buf_pastend) |
84 | return NULL; | |
12e41d03 DL |
85 | |
86 | *(uint16_t *) buf = htons(option_type); | |
87 | buf += 2; | |
88 | *(uint16_t *) buf = htons(option_len); | |
89 | buf += 2; | |
90 | pim_write_uint32(buf, option_value); | |
91 | buf += option_len; | |
92 | ||
93 | return buf; | |
94 | } | |
95 | ||
96 | #define ucast_ipv4_encoding_len (2 + sizeof(struct in_addr)) | |
97 | ||
984c84f4 DS |
98 | /* |
99 | * An Encoded-Unicast address takes the following format: | |
100 | * | |
101 | * 0 1 2 3 | |
102 | * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 | |
103 | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | |
104 | * | Addr Family | Encoding Type | Unicast Address | |
105 | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+... | |
106 | * | |
107 | * Addr Family | |
108 | * The PIM address family of the 'Unicast Address' field of this | |
109 | * address. | |
110 | * | |
111 | * Values 0-127 are as assigned by the IANA for Internet Address * Families in [7]. Values 128-250 are reserved to be assigned by | |
112 | * the IANA for PIM-specific Address Families. Values 251 though | |
113 | * 255 are designated for private use. As there is no assignment | |
114 | * authority for this space, collisions should be expected. | |
115 | * | |
116 | * Encoding Type | |
117 | * The type of encoding used within a specific Address Family. The | |
118 | * value '0' is reserved for this field and represents the native | |
119 | * encoding of the Address Family. | |
120 | * | |
121 | * Unicast Address | |
122 | * The unicast address as represented by the given Address Family | |
123 | * and Encoding Type. | |
124 | */ | |
8f483f92 DS |
125 | int |
126 | pim_encode_addr_ucast (uint8_t *buf, struct prefix *p) | |
4416b1f6 DS |
127 | { |
128 | switch (p->family) | |
129 | { | |
130 | case AF_INET: | |
131 | *(uint8_t *)buf = PIM_MSG_ADDRESS_FAMILY_IPV4; /* notice: AF_INET != PIM_MSG_ADDRESS_FAMILY_IPV4 */ | |
132 | ++buf; | |
133 | *(uint8_t *)buf = 0; /* ucast IPv4 native encoding type (RFC 4601: 4.9.1) */ | |
134 | ++buf; | |
135 | memcpy (buf, &p->u.prefix4, sizeof (struct in_addr)); | |
136 | return ucast_ipv4_encoding_len; | |
137 | break; | |
138 | default: | |
139 | return 0; | |
140 | break; | |
141 | } | |
142 | } | |
143 | ||
8f483f92 DS |
144 | #define group_ipv4_encoding_len (4 + sizeof (struct in_addr)) |
145 | ||
146 | /* | |
147 | * Encoded-Group addresses take the following format: | |
148 | * | |
149 | * 0 1 2 3 | |
150 | * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 | |
151 | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | |
152 | * | Addr Family | Encoding Type |B| Reserved |Z| Mask Len | | |
153 | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | |
154 | * | Group multicast Address | |
155 | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+... | |
156 | * | |
157 | * Addr Family | |
158 | * Described above. | |
159 | * | |
160 | * Encoding Type | |
161 | * Described above. | |
162 | * | |
163 | * [B]idirectional PIM | |
164 | * Indicates the group range should use Bidirectional PIM [13]. | |
165 | * For PIM-SM defined in this specification, this bit MUST be zero. | |
166 | * | |
167 | * Reserved | |
168 | * Transmitted as zero. Ignored upon receipt. | |
169 | * | |
170 | * Admin Scope [Z]one | |
171 | * indicates the group range is an admin scope zone. This is used | |
172 | * in the Bootstrap Router Mechanism [11] only. For all other | |
173 | * purposes, this bit is set to zero and ignored on receipt. | |
174 | * | |
175 | * Mask Len | |
176 | * The Mask length field is 8 bits. The value is the number of | |
177 | * contiguous one bits that are left justified and used as a mask; | |
178 | * when combined with the group address, it describes a range of | |
179 | * groups. It is less than or equal to the address length in bits | |
180 | * for the given Address Family and Encoding Type. If the message | |
181 | * is sent for a single group, then the Mask length must equal the | |
182 | * address length in bits for the given Address Family and Encoding | |
183 | * Type (e.g., 32 for IPv4 native encoding, 128 for IPv6 native | |
184 | * encoding). | |
185 | * | |
186 | * Group multicast Address | |
187 | * Contains the group address. | |
188 | */ | |
189 | int | |
190 | pim_encode_addr_group (uint8_t *buf, afi_t afi, int bidir, int scope, struct in_addr group) | |
191 | { | |
192 | uint8_t flags = 0; | |
193 | ||
194 | flags |= bidir << 8; | |
195 | flags |= scope; | |
196 | ||
197 | switch (afi) | |
198 | { | |
199 | case AFI_IP: | |
200 | *(uint8_t *)buf = PIM_MSG_ADDRESS_FAMILY_IPV4; | |
201 | ++buf; | |
202 | *(uint8_t *)buf = 0; | |
203 | ++buf; | |
204 | *(uint8_t *)buf = flags; | |
205 | ++buf; | |
206 | *(uint8_t *)buf = 32; | |
207 | ++buf; | |
208 | memcpy (buf, &group, sizeof (struct in_addr)); | |
209 | return group_ipv4_encoding_len; | |
210 | break; | |
211 | default: | |
212 | return 0; | |
213 | break; | |
214 | } | |
215 | } | |
216 | ||
12e41d03 DL |
217 | uint8_t *pim_tlv_append_addrlist_ucast(uint8_t *buf, |
218 | const uint8_t *buf_pastend, | |
219 | struct list *ifconnected) | |
220 | { | |
221 | struct listnode *node; | |
222 | uint16_t option_len = 0; | |
223 | ||
224 | uint8_t *curr; | |
225 | ||
226 | node = listhead(ifconnected); | |
227 | ||
228 | /* Empty address list ? */ | |
229 | if (!node) { | |
230 | return buf; | |
231 | } | |
232 | ||
233 | /* Skip first address (primary) */ | |
234 | node = listnextnode(node); | |
235 | ||
236 | /* Scan secondary address list */ | |
237 | curr = buf + 4; /* skip T and L */ | |
238 | for (; node; node = listnextnode(node)) { | |
239 | struct connected *ifc = listgetdata(node); | |
240 | struct prefix *p = ifc->address; | |
4416b1f6 | 241 | int l_encode; |
12e41d03 | 242 | |
aea6cb94 | 243 | if ((curr + ucast_ipv4_encoding_len) > buf_pastend) |
12e41d03 | 244 | return 0; |
12e41d03 | 245 | |
8f483f92 | 246 | l_encode = pim_encode_addr_ucast (curr, p); |
4416b1f6 DS |
247 | curr += l_encode; |
248 | option_len += l_encode; | |
12e41d03 DL |
249 | } |
250 | ||
169edb7f | 251 | if (PIM_DEBUG_PIM_TRACE_DETAIL) { |
aea6cb94 DS |
252 | zlog_debug("%s: number of encoded secondary unicast IPv4 addresses: %zu", |
253 | __PRETTY_FUNCTION__, | |
254 | option_len / ucast_ipv4_encoding_len); | |
12e41d03 DL |
255 | } |
256 | ||
257 | if (option_len < 1) { | |
258 | /* Empty secondary unicast IPv4 address list */ | |
259 | return buf; | |
260 | } | |
261 | ||
262 | /* | |
263 | * Write T and L | |
264 | */ | |
265 | *(uint16_t *) buf = htons(PIM_MSG_OPTION_TYPE_ADDRESS_LIST); | |
266 | *(uint16_t *) (buf + 2) = htons(option_len); | |
267 | ||
268 | return curr; | |
269 | } | |
270 | ||
271 | static int check_tlv_length(const char *label, const char *tlv_name, | |
272 | const char *ifname, struct in_addr src_addr, | |
273 | int correct_len, int option_len) | |
274 | { | |
275 | if (option_len != correct_len) { | |
eaa54bdb | 276 | char src_str[INET_ADDRSTRLEN]; |
12e41d03 DL |
277 | pim_inet4_dump("<src?>", src_addr, src_str, sizeof(src_str)); |
278 | zlog_warn("%s: PIM hello %s TLV with incorrect value size=%d correct=%d from %s on interface %s", | |
279 | label, tlv_name, | |
280 | option_len, correct_len, | |
281 | src_str, ifname); | |
282 | return -1; | |
283 | } | |
284 | ||
285 | return 0; | |
286 | } | |
287 | ||
288 | static void check_tlv_redefinition_uint16(const char *label, const char *tlv_name, | |
289 | const char *ifname, struct in_addr src_addr, | |
290 | pim_hello_options options, | |
291 | pim_hello_options opt_mask, | |
292 | uint16_t new, uint16_t old) | |
293 | { | |
294 | if (PIM_OPTION_IS_SET(options, opt_mask)) { | |
eaa54bdb | 295 | char src_str[INET_ADDRSTRLEN]; |
12e41d03 DL |
296 | pim_inet4_dump("<src?>", src_addr, src_str, sizeof(src_str)); |
297 | zlog_warn("%s: PIM hello TLV redefined %s=%u old=%u from %s on interface %s", | |
298 | label, tlv_name, | |
299 | new, old, | |
300 | src_str, ifname); | |
301 | } | |
302 | } | |
303 | ||
304 | static void check_tlv_redefinition_uint32(const char *label, const char *tlv_name, | |
305 | const char *ifname, struct in_addr src_addr, | |
306 | pim_hello_options options, | |
307 | pim_hello_options opt_mask, | |
308 | uint32_t new, uint32_t old) | |
309 | { | |
310 | if (PIM_OPTION_IS_SET(options, opt_mask)) { | |
eaa54bdb | 311 | char src_str[INET_ADDRSTRLEN]; |
12e41d03 DL |
312 | pim_inet4_dump("<src?>", src_addr, src_str, sizeof(src_str)); |
313 | zlog_warn("%s: PIM hello TLV redefined %s=%u old=%u from %s on interface %s", | |
314 | label, tlv_name, | |
315 | new, old, | |
316 | src_str, ifname); | |
317 | } | |
318 | } | |
319 | ||
320 | static void check_tlv_redefinition_uint32_hex(const char *label, const char *tlv_name, | |
321 | const char *ifname, struct in_addr src_addr, | |
322 | pim_hello_options options, | |
323 | pim_hello_options opt_mask, | |
324 | uint32_t new, uint32_t old) | |
325 | { | |
326 | if (PIM_OPTION_IS_SET(options, opt_mask)) { | |
eaa54bdb | 327 | char src_str[INET_ADDRSTRLEN]; |
12e41d03 DL |
328 | pim_inet4_dump("<src?>", src_addr, src_str, sizeof(src_str)); |
329 | zlog_warn("%s: PIM hello TLV redefined %s=%08x old=%08x from %s on interface %s", | |
330 | label, tlv_name, | |
331 | new, old, | |
332 | src_str, ifname); | |
333 | } | |
334 | } | |
335 | ||
336 | int pim_tlv_parse_holdtime(const char *ifname, struct in_addr src_addr, | |
337 | pim_hello_options *hello_options, | |
338 | uint16_t *hello_option_holdtime, | |
339 | uint16_t option_len, | |
340 | const uint8_t *tlv_curr) | |
341 | { | |
342 | const char *label = "holdtime"; | |
343 | ||
344 | if (check_tlv_length(__PRETTY_FUNCTION__, label, | |
345 | ifname, src_addr, | |
346 | sizeof(uint16_t), option_len)) { | |
347 | return -1; | |
348 | } | |
349 | ||
350 | check_tlv_redefinition_uint16(__PRETTY_FUNCTION__, label, | |
351 | ifname, src_addr, | |
352 | *hello_options, PIM_OPTION_MASK_HOLDTIME, | |
353 | PIM_TLV_GET_HOLDTIME(tlv_curr), | |
354 | *hello_option_holdtime); | |
355 | ||
356 | PIM_OPTION_SET(*hello_options, PIM_OPTION_MASK_HOLDTIME); | |
357 | ||
358 | *hello_option_holdtime = PIM_TLV_GET_HOLDTIME(tlv_curr); | |
359 | ||
360 | return 0; | |
361 | } | |
362 | ||
363 | int pim_tlv_parse_lan_prune_delay(const char *ifname, struct in_addr src_addr, | |
364 | pim_hello_options *hello_options, | |
365 | uint16_t *hello_option_propagation_delay, | |
366 | uint16_t *hello_option_override_interval, | |
367 | uint16_t option_len, | |
368 | const uint8_t *tlv_curr) | |
369 | { | |
370 | if (check_tlv_length(__PRETTY_FUNCTION__, "lan_prune_delay", | |
371 | ifname, src_addr, | |
372 | sizeof(uint32_t), option_len)) { | |
373 | return -1; | |
374 | } | |
375 | ||
376 | check_tlv_redefinition_uint16(__PRETTY_FUNCTION__, "propagation_delay", | |
377 | ifname, src_addr, | |
378 | *hello_options, PIM_OPTION_MASK_LAN_PRUNE_DELAY, | |
379 | PIM_TLV_GET_PROPAGATION_DELAY(tlv_curr), | |
380 | *hello_option_propagation_delay); | |
381 | ||
382 | PIM_OPTION_SET(*hello_options, PIM_OPTION_MASK_LAN_PRUNE_DELAY); | |
383 | ||
384 | *hello_option_propagation_delay = PIM_TLV_GET_PROPAGATION_DELAY(tlv_curr); | |
385 | if (PIM_TLV_GET_CAN_DISABLE_JOIN_SUPPRESSION(tlv_curr)) { | |
386 | PIM_OPTION_SET(*hello_options, PIM_OPTION_MASK_CAN_DISABLE_JOIN_SUPPRESSION); | |
387 | } | |
388 | else { | |
389 | PIM_OPTION_UNSET(*hello_options, PIM_OPTION_MASK_CAN_DISABLE_JOIN_SUPPRESSION); | |
390 | } | |
391 | ++tlv_curr; | |
392 | ++tlv_curr; | |
393 | *hello_option_override_interval = PIM_TLV_GET_OVERRIDE_INTERVAL(tlv_curr); | |
394 | ||
395 | return 0; | |
396 | } | |
397 | ||
398 | int pim_tlv_parse_dr_priority(const char *ifname, struct in_addr src_addr, | |
399 | pim_hello_options *hello_options, | |
400 | uint32_t *hello_option_dr_priority, | |
401 | uint16_t option_len, | |
402 | const uint8_t *tlv_curr) | |
403 | { | |
404 | const char *label = "dr_priority"; | |
405 | ||
406 | if (check_tlv_length(__PRETTY_FUNCTION__, label, | |
407 | ifname, src_addr, | |
408 | sizeof(uint32_t), option_len)) { | |
409 | return -1; | |
410 | } | |
411 | ||
412 | check_tlv_redefinition_uint32(__PRETTY_FUNCTION__, label, | |
413 | ifname, src_addr, | |
414 | *hello_options, PIM_OPTION_MASK_DR_PRIORITY, | |
415 | PIM_TLV_GET_DR_PRIORITY(tlv_curr), | |
416 | *hello_option_dr_priority); | |
417 | ||
418 | PIM_OPTION_SET(*hello_options, PIM_OPTION_MASK_DR_PRIORITY); | |
419 | ||
420 | *hello_option_dr_priority = PIM_TLV_GET_DR_PRIORITY(tlv_curr); | |
421 | ||
422 | return 0; | |
423 | } | |
424 | ||
425 | int pim_tlv_parse_generation_id(const char *ifname, struct in_addr src_addr, | |
426 | pim_hello_options *hello_options, | |
427 | uint32_t *hello_option_generation_id, | |
428 | uint16_t option_len, | |
429 | const uint8_t *tlv_curr) | |
430 | { | |
431 | const char *label = "generation_id"; | |
432 | ||
433 | if (check_tlv_length(__PRETTY_FUNCTION__, label, | |
434 | ifname, src_addr, | |
435 | sizeof(uint32_t), option_len)) { | |
436 | return -1; | |
437 | } | |
438 | ||
439 | check_tlv_redefinition_uint32_hex(__PRETTY_FUNCTION__, label, | |
440 | ifname, src_addr, | |
441 | *hello_options, PIM_OPTION_MASK_GENERATION_ID, | |
442 | PIM_TLV_GET_GENERATION_ID(tlv_curr), | |
443 | *hello_option_generation_id); | |
444 | ||
445 | PIM_OPTION_SET(*hello_options, PIM_OPTION_MASK_GENERATION_ID); | |
446 | ||
447 | *hello_option_generation_id = PIM_TLV_GET_GENERATION_ID(tlv_curr); | |
448 | ||
449 | return 0; | |
450 | } | |
451 | ||
4416b1f6 DS |
452 | int |
453 | pim_parse_addr_ucast (struct prefix *p, | |
454 | const uint8_t *buf, | |
455 | int buf_size) | |
12e41d03 DL |
456 | { |
457 | const int ucast_encoding_min_len = 3; /* 1 family + 1 type + 1 addr */ | |
458 | const uint8_t *addr; | |
459 | const uint8_t *pastend; | |
460 | int family; | |
461 | int type; | |
462 | ||
463 | if (buf_size < ucast_encoding_min_len) { | |
4416b1f6 | 464 | zlog_warn("%s: unicast address encoding overflow: left=%d needed=%d", |
12e41d03 | 465 | __PRETTY_FUNCTION__, |
4416b1f6 | 466 | buf_size, ucast_encoding_min_len); |
12e41d03 DL |
467 | return -1; |
468 | } | |
469 | ||
470 | addr = buf; | |
471 | pastend = buf + buf_size; | |
472 | ||
473 | family = *addr++; | |
474 | type = *addr++; | |
475 | ||
4416b1f6 DS |
476 | if (type) { |
477 | zlog_warn("%s: unknown unicast address encoding type=%d", | |
478 | __PRETTY_FUNCTION__, | |
479 | type); | |
480 | return -2; | |
481 | } | |
482 | ||
12e41d03 DL |
483 | switch (family) { |
484 | case PIM_MSG_ADDRESS_FAMILY_IPV4: | |
12e41d03 | 485 | if ((addr + sizeof(struct in_addr)) > pastend) { |
4416b1f6 | 486 | zlog_warn("%s: IPv4 unicast address overflow: left=%zd needed=%zu", |
12e41d03 | 487 | __PRETTY_FUNCTION__, |
4416b1f6 | 488 | pastend - addr, sizeof(struct in_addr)); |
12e41d03 DL |
489 | return -3; |
490 | } | |
491 | ||
492 | p->family = AF_INET; /* notice: AF_INET != PIM_MSG_ADDRESS_FAMILY_IPV4 */ | |
493 | memcpy(&p->u.prefix4, addr, sizeof(struct in_addr)); | |
494 | ||
495 | addr += sizeof(struct in_addr); | |
496 | ||
497 | break; | |
498 | default: | |
499 | { | |
4416b1f6 | 500 | zlog_warn("%s: unknown unicast address encoding family=%d from", |
12e41d03 | 501 | __PRETTY_FUNCTION__, |
4416b1f6 | 502 | family); |
12e41d03 DL |
503 | return -4; |
504 | } | |
505 | } | |
506 | ||
507 | return addr - buf; | |
508 | } | |
509 | ||
4416b1f6 | 510 | int |
bce0e645 | 511 | pim_parse_addr_group (struct prefix_sg *sg, |
4416b1f6 DS |
512 | const uint8_t *buf, |
513 | int buf_size) | |
12e41d03 DL |
514 | { |
515 | const int grp_encoding_min_len = 4; /* 1 family + 1 type + 1 reserved + 1 addr */ | |
516 | const uint8_t *addr; | |
517 | const uint8_t *pastend; | |
518 | int family; | |
519 | int type; | |
520 | int mask_len; | |
521 | ||
522 | if (buf_size < grp_encoding_min_len) { | |
4416b1f6 | 523 | zlog_warn("%s: group address encoding overflow: left=%d needed=%d", |
12e41d03 | 524 | __PRETTY_FUNCTION__, |
4416b1f6 | 525 | buf_size, grp_encoding_min_len); |
12e41d03 DL |
526 | return -1; |
527 | } | |
528 | ||
529 | addr = buf; | |
530 | pastend = buf + buf_size; | |
531 | ||
532 | family = *addr++; | |
533 | type = *addr++; | |
534 | //++addr; | |
535 | ++addr; /* skip b_reserved_z fields */ | |
536 | mask_len = *addr++; | |
537 | ||
538 | switch (family) { | |
539 | case PIM_MSG_ADDRESS_FAMILY_IPV4: | |
540 | if (type) { | |
4416b1f6 DS |
541 | zlog_warn("%s: unknown group address encoding type=%d from", |
542 | __PRETTY_FUNCTION__, type); | |
12e41d03 DL |
543 | return -2; |
544 | } | |
545 | ||
546 | if ((addr + sizeof(struct in_addr)) > pastend) { | |
4416b1f6 | 547 | zlog_warn("%s: IPv4 group address overflow: left=%zd needed=%zu from", |
12e41d03 | 548 | __PRETTY_FUNCTION__, |
4416b1f6 | 549 | pastend - addr, sizeof(struct in_addr)); |
12e41d03 DL |
550 | return -3; |
551 | } | |
552 | ||
bce0e645 | 553 | memcpy(&sg->grp.s_addr, addr, sizeof(struct in_addr)); |
12e41d03 DL |
554 | |
555 | addr += sizeof(struct in_addr); | |
556 | ||
557 | break; | |
558 | default: | |
559 | { | |
bce0e645 DS |
560 | zlog_warn("%s: unknown group address encoding family=%d mask_len=%d from", |
561 | __PRETTY_FUNCTION__, family, mask_len); | |
12e41d03 DL |
562 | return -4; |
563 | } | |
564 | } | |
565 | ||
566 | return addr - buf; | |
567 | } | |
568 | ||
4416b1f6 | 569 | int |
a7762e07 | 570 | pim_parse_addr_source(struct prefix_sg *sg, |
4416b1f6 DS |
571 | uint8_t *flags, |
572 | const uint8_t *buf, | |
573 | int buf_size) | |
12e41d03 DL |
574 | { |
575 | const int src_encoding_min_len = 4; /* 1 family + 1 type + 1 reserved + 1 addr */ | |
576 | const uint8_t *addr; | |
577 | const uint8_t *pastend; | |
578 | int family; | |
579 | int type; | |
580 | int mask_len; | |
581 | ||
582 | if (buf_size < src_encoding_min_len) { | |
4416b1f6 | 583 | zlog_warn("%s: source address encoding overflow: left=%d needed=%d", |
12e41d03 | 584 | __PRETTY_FUNCTION__, |
4416b1f6 | 585 | buf_size, src_encoding_min_len); |
12e41d03 DL |
586 | return -1; |
587 | } | |
588 | ||
589 | addr = buf; | |
590 | pastend = buf + buf_size; | |
591 | ||
592 | family = *addr++; | |
593 | type = *addr++; | |
594 | *flags = *addr++; | |
595 | mask_len = *addr++; | |
596 | ||
4416b1f6 DS |
597 | if (type) { |
598 | zlog_warn("%s: unknown source address encoding type=%d: %02x%02x%02x%02x%02x%02x%02x%02x", | |
599 | __PRETTY_FUNCTION__, | |
600 | type, | |
601 | buf[0], buf[1], buf[2], buf[3], buf[4], buf[5], buf[6], buf[7]); | |
602 | return -2; | |
603 | } | |
604 | ||
12e41d03 DL |
605 | switch (family) { |
606 | case PIM_MSG_ADDRESS_FAMILY_IPV4: | |
12e41d03 | 607 | if ((addr + sizeof(struct in_addr)) > pastend) { |
4416b1f6 | 608 | zlog_warn("%s: IPv4 source address overflow: left=%zd needed=%zu", |
12e41d03 | 609 | __PRETTY_FUNCTION__, |
4416b1f6 | 610 | pastend - addr, sizeof(struct in_addr)); |
12e41d03 DL |
611 | return -3; |
612 | } | |
613 | ||
a7762e07 | 614 | memcpy(&sg->src, addr, sizeof(struct in_addr)); |
12e41d03 DL |
615 | |
616 | /* | |
617 | RFC 4601: 4.9.1 Encoded Source and Group Address Formats | |
618 | ||
619 | Encoded-Source Address | |
620 | ||
621 | The mask length MUST be equal to the mask length in bits for | |
622 | the given Address Family and Encoding Type (32 for IPv4 native | |
623 | and 128 for IPv6 native). A router SHOULD ignore any messages | |
624 | received with any other mask length. | |
625 | */ | |
a7762e07 | 626 | if (mask_len != 32) { |
4416b1f6 | 627 | zlog_warn("%s: IPv4 bad source address mask: %d", |
a7762e07 | 628 | __PRETTY_FUNCTION__, mask_len); |
12e41d03 DL |
629 | return -4; |
630 | } | |
631 | ||
632 | addr += sizeof(struct in_addr); | |
633 | ||
634 | break; | |
635 | default: | |
636 | { | |
4416b1f6 | 637 | zlog_warn("%s: unknown source address encoding family=%d: %02x%02x%02x%02x%02x%02x%02x%02x", |
12e41d03 | 638 | __PRETTY_FUNCTION__, |
4416b1f6 | 639 | family, |
12e41d03 DL |
640 | buf[0], buf[1], buf[2], buf[3], buf[4], buf[5], buf[6], buf[7]); |
641 | return -5; | |
642 | } | |
643 | } | |
644 | ||
645 | return addr - buf; | |
646 | } | |
647 | ||
648 | #define FREE_ADDR_LIST(hello_option_addr_list) \ | |
649 | { \ | |
650 | if (hello_option_addr_list) { \ | |
651 | list_delete(hello_option_addr_list); \ | |
652 | hello_option_addr_list = 0; \ | |
653 | } \ | |
654 | } | |
655 | ||
656 | int pim_tlv_parse_addr_list(const char *ifname, struct in_addr src_addr, | |
657 | pim_hello_options *hello_options, | |
658 | struct list **hello_option_addr_list, | |
659 | uint16_t option_len, | |
660 | const uint8_t *tlv_curr) | |
661 | { | |
662 | const uint8_t *addr; | |
663 | const uint8_t *pastend; | |
664 | ||
665 | zassert(hello_option_addr_list); | |
666 | ||
667 | /* | |
668 | Scan addr list | |
669 | */ | |
670 | addr = tlv_curr; | |
671 | pastend = tlv_curr + option_len; | |
672 | while (addr < pastend) { | |
673 | struct prefix tmp; | |
674 | int addr_offset; | |
675 | ||
676 | /* | |
677 | Parse ucast addr | |
678 | */ | |
4416b1f6 | 679 | addr_offset = pim_parse_addr_ucast(&tmp, addr, pastend - addr); |
12e41d03 | 680 | if (addr_offset < 1) { |
eaa54bdb | 681 | char src_str[INET_ADDRSTRLEN]; |
12e41d03 DL |
682 | pim_inet4_dump("<src?>", src_addr, src_str, sizeof(src_str)); |
683 | zlog_warn("%s: pim_parse_addr_ucast() failure: from %s on %s", | |
684 | __PRETTY_FUNCTION__, | |
685 | src_str, ifname); | |
686 | FREE_ADDR_LIST(*hello_option_addr_list); | |
687 | return -1; | |
688 | } | |
689 | addr += addr_offset; | |
690 | ||
691 | /* | |
692 | Debug | |
693 | */ | |
694 | if (PIM_DEBUG_PIM_TRACE) { | |
695 | switch (tmp.family) { | |
696 | case AF_INET: | |
697 | { | |
eaa54bdb DW |
698 | char addr_str[INET_ADDRSTRLEN]; |
699 | char src_str[INET_ADDRSTRLEN]; | |
12e41d03 DL |
700 | pim_inet4_dump("<addr?>", tmp.u.prefix4, addr_str, sizeof(addr_str)); |
701 | pim_inet4_dump("<src?>", src_addr, src_str, sizeof(src_str)); | |
702 | zlog_debug("%s: PIM hello TLV option: list_old_size=%d IPv4 address %s from %s on %s", | |
703 | __PRETTY_FUNCTION__, | |
704 | *hello_option_addr_list ? | |
705 | ((int) listcount(*hello_option_addr_list)) : -1, | |
706 | addr_str, src_str, ifname); | |
707 | } | |
708 | break; | |
709 | default: | |
710 | { | |
eaa54bdb | 711 | char src_str[INET_ADDRSTRLEN]; |
12e41d03 DL |
712 | pim_inet4_dump("<src?>", src_addr, src_str, sizeof(src_str)); |
713 | zlog_debug("%s: PIM hello TLV option: list_old_size=%d UNKNOWN address family from %s on %s", | |
714 | __PRETTY_FUNCTION__, | |
715 | *hello_option_addr_list ? | |
716 | ((int) listcount(*hello_option_addr_list)) : -1, | |
717 | src_str, ifname); | |
718 | } | |
719 | } | |
720 | } | |
721 | ||
722 | /* | |
723 | Exclude neighbor's primary address if incorrectly included in | |
724 | the secondary address list | |
725 | */ | |
726 | if (tmp.family == AF_INET) { | |
727 | if (tmp.u.prefix4.s_addr == src_addr.s_addr) { | |
eaa54bdb | 728 | char src_str[INET_ADDRSTRLEN]; |
12e41d03 DL |
729 | pim_inet4_dump("<src?>", src_addr, src_str, sizeof(src_str)); |
730 | zlog_warn("%s: ignoring primary address in secondary list from %s on %s", | |
731 | __PRETTY_FUNCTION__, | |
732 | src_str, ifname); | |
733 | continue; | |
734 | } | |
735 | } | |
736 | ||
737 | /* | |
738 | Allocate list if needed | |
739 | */ | |
740 | if (!*hello_option_addr_list) { | |
741 | *hello_option_addr_list = list_new(); | |
742 | if (!*hello_option_addr_list) { | |
743 | zlog_err("%s %s: failure: hello_option_addr_list=list_new()", | |
744 | __FILE__, __PRETTY_FUNCTION__); | |
745 | return -2; | |
746 | } | |
747 | (*hello_option_addr_list)->del = (void (*)(void *)) prefix_free; | |
748 | } | |
749 | ||
750 | /* | |
751 | Attach addr to list | |
752 | */ | |
753 | { | |
754 | struct prefix *p; | |
755 | p = prefix_new(); | |
756 | if (!p) { | |
757 | zlog_err("%s %s: failure: prefix_new()", | |
758 | __FILE__, __PRETTY_FUNCTION__); | |
759 | FREE_ADDR_LIST(*hello_option_addr_list); | |
760 | return -3; | |
761 | } | |
762 | p->family = tmp.family; | |
763 | p->u.prefix4 = tmp.u.prefix4; | |
764 | listnode_add(*hello_option_addr_list, p); | |
765 | } | |
766 | ||
767 | } /* while (addr < pastend) */ | |
768 | ||
769 | /* | |
770 | Mark hello option | |
771 | */ | |
772 | PIM_OPTION_SET(*hello_options, PIM_OPTION_MASK_ADDRESS_LIST); | |
773 | ||
774 | return 0; | |
775 | } |