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