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 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  */
19 
20 #include <zebra.h>
21 
22 #include "log.h"
23 #include "if.h"
24 
25 #include "pimd.h"
26 #include "pim_pim.h"
27 #include "pim_str.h"
28 #include "pim_tlv.h"
29 #include "pim_util.h"
30 #include "pim_hello.h"
31 #include "pim_iface.h"
32 #include "pim_neighbor.h"
33 #include "pim_upstream.h"
34 #include "pim_bsm.h"
35 
on_trace(const char * label,struct interface * ifp,struct in_addr src)36 static void on_trace(const char *label, struct interface *ifp,
37 		     struct in_addr src)
38 {
39 	if (PIM_DEBUG_PIM_TRACE) {
40 		char src_str[INET_ADDRSTRLEN];
41 		pim_inet4_dump("<src?>", src, src_str, sizeof(src_str));
42 		zlog_debug("%s: from %s on %s", label, src_str, ifp->name);
43 	}
44 }
45 
tlv_trace_bool(const char * label,const char * tlv_name,const char * ifname,struct in_addr src_addr,int isset,int value)46 static void tlv_trace_bool(const char *label, const char *tlv_name,
47 			   const char *ifname, struct in_addr src_addr,
48 			   int isset, int value)
49 {
50 	if (isset) {
51 		char src_str[INET_ADDRSTRLEN];
52 		pim_inet4_dump("<src?>", src_addr, src_str, sizeof(src_str));
53 		zlog_debug(
54 			"%s: PIM hello option from %s on interface %s: %s=%d",
55 			label, src_str, ifname, tlv_name, value);
56 	}
57 }
58 
tlv_trace_uint16(const char * label,const char * tlv_name,const char * ifname,struct in_addr src_addr,int isset,uint16_t value)59 static void tlv_trace_uint16(const char *label, const char *tlv_name,
60 			     const char *ifname, struct in_addr src_addr,
61 			     int isset, uint16_t value)
62 {
63 	if (isset) {
64 		char src_str[INET_ADDRSTRLEN];
65 		pim_inet4_dump("<src?>", src_addr, src_str, sizeof(src_str));
66 		zlog_debug(
67 			"%s: PIM hello option from %s on interface %s: %s=%u",
68 			label, src_str, ifname, tlv_name, value);
69 	}
70 }
71 
tlv_trace_uint32(const char * label,const char * tlv_name,const char * ifname,struct in_addr src_addr,int isset,uint32_t value)72 static void tlv_trace_uint32(const char *label, const char *tlv_name,
73 			     const char *ifname, struct in_addr src_addr,
74 			     int isset, uint32_t value)
75 {
76 	if (isset) {
77 		char src_str[INET_ADDRSTRLEN];
78 		pim_inet4_dump("<src?>", src_addr, src_str, sizeof(src_str));
79 		zlog_debug(
80 			"%s: PIM hello option from %s on interface %s: %s=%u",
81 			label, src_str, ifname, tlv_name, value);
82 	}
83 }
84 
tlv_trace_uint32_hex(const char * label,const char * tlv_name,const char * ifname,struct in_addr src_addr,int isset,uint32_t value)85 static void tlv_trace_uint32_hex(const char *label, const char *tlv_name,
86 				 const char *ifname, struct in_addr src_addr,
87 				 int isset, uint32_t value)
88 {
89 	if (isset) {
90 		char src_str[INET_ADDRSTRLEN];
91 		pim_inet4_dump("<src?>", src_addr, src_str, sizeof(src_str));
92 		zlog_debug(
93 			"%s: PIM hello option from %s on interface %s: %s=%08x",
94 			label, src_str, ifname, tlv_name, value);
95 	}
96 }
97 
98 #if 0
99 static void tlv_trace(const char *label, const char *tlv_name,
100 		      const char *ifname, struct in_addr src_addr,
101 		      int isset)
102 {
103   if (isset) {
104     char src_str[INET_ADDRSTRLEN];
105     pim_inet4_dump("<src?>", src_addr, src_str, sizeof(src_str));
106     zlog_debug("%s: PIM hello option from %s on interface %s: %s",
107 	       label,
108 	       src_str, ifname,
109 	       tlv_name);
110   }
111 }
112 #endif
113 
tlv_trace_list(const char * label,const char * tlv_name,const char * ifname,struct in_addr src_addr,int isset,struct list * addr_list)114 static void tlv_trace_list(const char *label, const char *tlv_name,
115 			   const char *ifname, struct in_addr src_addr,
116 			   int isset, struct list *addr_list)
117 {
118 	if (isset) {
119 		char src_str[INET_ADDRSTRLEN];
120 		pim_inet4_dump("<src?>", src_addr, src_str, sizeof(src_str));
121 		zlog_debug(
122 			"%s: PIM hello option from %s on interface %s: %s size=%d list=%p",
123 			label, src_str, ifname, tlv_name,
124 			addr_list ? ((int)listcount(addr_list)) : -1,
125 			(void *)addr_list);
126 	}
127 }
128 
129 #define FREE_ADDR_LIST                                                         \
130 	if (hello_option_addr_list) {                                          \
131 		list_delete(&hello_option_addr_list);                          \
132 	}
133 
134 #define FREE_ADDR_LIST_THEN_RETURN(code)                                       \
135 	{                                                                      \
136 		FREE_ADDR_LIST                                                 \
137 		return (code);                                                 \
138 	}
139 
pim_hello_recv(struct interface * ifp,struct in_addr src_addr,uint8_t * tlv_buf,int tlv_buf_size)140 int pim_hello_recv(struct interface *ifp, struct in_addr src_addr,
141 		   uint8_t *tlv_buf, int tlv_buf_size)
142 {
143 	struct pim_interface *pim_ifp;
144 	struct pim_neighbor *neigh;
145 	uint8_t *tlv_curr;
146 	uint8_t *tlv_pastend;
147 	pim_hello_options hello_options =
148 		0; /* bit array recording options found */
149 	uint16_t hello_option_holdtime = 0;
150 	uint16_t hello_option_propagation_delay = 0;
151 	uint16_t hello_option_override_interval = 0;
152 	uint32_t hello_option_dr_priority = 0;
153 	uint32_t hello_option_generation_id = 0;
154 	struct list *hello_option_addr_list = 0;
155 
156 	if (PIM_DEBUG_PIM_HELLO)
157 		on_trace(__func__, ifp, src_addr);
158 
159 	pim_ifp = ifp->info;
160 	zassert(pim_ifp);
161 
162 	++pim_ifp->pim_ifstat_hello_recv;
163 
164 	/*
165 	  Parse PIM hello TLVs
166 	 */
167 	zassert(tlv_buf_size >= 0);
168 	tlv_curr = tlv_buf;
169 	tlv_pastend = tlv_buf + tlv_buf_size;
170 
171 	while (tlv_curr < tlv_pastend) {
172 		uint16_t option_type;
173 		uint16_t option_len;
174 		int remain = tlv_pastend - tlv_curr;
175 
176 		if (remain < PIM_TLV_MIN_SIZE) {
177 			if (PIM_DEBUG_PIM_HELLO) {
178 				char src_str[INET_ADDRSTRLEN];
179 				pim_inet4_dump("<src?>", src_addr, src_str,
180 					       sizeof(src_str));
181 				zlog_debug(
182 					"%s: short PIM hello TLV size=%d < min=%d from %s on interface %s",
183 					__func__, remain, PIM_TLV_MIN_SIZE,
184 					src_str, ifp->name);
185 			}
186 			FREE_ADDR_LIST_THEN_RETURN(-1);
187 		}
188 
189 		option_type = PIM_TLV_GET_TYPE(tlv_curr);
190 		tlv_curr += PIM_TLV_TYPE_SIZE;
191 		option_len = PIM_TLV_GET_LENGTH(tlv_curr);
192 		tlv_curr += PIM_TLV_LENGTH_SIZE;
193 
194 		if ((tlv_curr + option_len) > tlv_pastend) {
195 			if (PIM_DEBUG_PIM_HELLO) {
196 				char src_str[INET_ADDRSTRLEN];
197 				pim_inet4_dump("<src?>", src_addr, src_str,
198 					       sizeof(src_str));
199 				zlog_debug(
200 					"%s: long PIM hello TLV type=%d length=%d > left=%td from %s on interface %s",
201 					__func__, option_type, option_len,
202 					tlv_pastend - tlv_curr, src_str,
203 					ifp->name);
204 			}
205 			FREE_ADDR_LIST_THEN_RETURN(-2);
206 		}
207 
208 		if (PIM_DEBUG_PIM_HELLO) {
209 			char src_str[INET_ADDRSTRLEN];
210 			pim_inet4_dump("<src?>", src_addr, src_str,
211 				       sizeof(src_str));
212 			zlog_debug(
213 				"%s: parse left_size=%d: PIM hello TLV type=%d length=%d from %s on %s",
214 				__func__, remain, option_type, option_len,
215 				src_str, ifp->name);
216 		}
217 
218 		switch (option_type) {
219 		case PIM_MSG_OPTION_TYPE_HOLDTIME:
220 			if (pim_tlv_parse_holdtime(ifp->name, src_addr,
221 						   &hello_options,
222 						   &hello_option_holdtime,
223 						   option_len, tlv_curr)) {
224 				FREE_ADDR_LIST_THEN_RETURN(-3);
225 			}
226 			break;
227 		case PIM_MSG_OPTION_TYPE_LAN_PRUNE_DELAY:
228 			if (pim_tlv_parse_lan_prune_delay(
229 				    ifp->name, src_addr, &hello_options,
230 				    &hello_option_propagation_delay,
231 				    &hello_option_override_interval, option_len,
232 				    tlv_curr)) {
233 				FREE_ADDR_LIST_THEN_RETURN(-4);
234 			}
235 			break;
236 		case PIM_MSG_OPTION_TYPE_DR_PRIORITY:
237 			if (pim_tlv_parse_dr_priority(ifp->name, src_addr,
238 						      &hello_options,
239 						      &hello_option_dr_priority,
240 						      option_len, tlv_curr)) {
241 				FREE_ADDR_LIST_THEN_RETURN(-5);
242 			}
243 			break;
244 		case PIM_MSG_OPTION_TYPE_GENERATION_ID:
245 			if (pim_tlv_parse_generation_id(
246 				    ifp->name, src_addr, &hello_options,
247 				    &hello_option_generation_id, option_len,
248 				    tlv_curr)) {
249 				FREE_ADDR_LIST_THEN_RETURN(-6);
250 			}
251 			break;
252 		case PIM_MSG_OPTION_TYPE_ADDRESS_LIST:
253 			if (pim_tlv_parse_addr_list(ifp->name, src_addr,
254 						    &hello_options,
255 						    &hello_option_addr_list,
256 						    option_len, tlv_curr)) {
257 				return -7;
258 			}
259 			break;
260 		case PIM_MSG_OPTION_TYPE_DM_STATE_REFRESH:
261 			if (PIM_DEBUG_PIM_HELLO) {
262 				char src_str[INET_ADDRSTRLEN];
263 				pim_inet4_dump("<src?>", src_addr, src_str,
264 					       sizeof(src_str));
265 				zlog_debug(
266 					"%s: ignoring PIM hello dense-mode state refresh TLV option type=%d length=%d from %s on interface %s",
267 					__func__, option_type, option_len,
268 					src_str, ifp->name);
269 			}
270 			break;
271 		default:
272 			if (PIM_DEBUG_PIM_HELLO) {
273 				char src_str[INET_ADDRSTRLEN];
274 				pim_inet4_dump("<src?>", src_addr, src_str,
275 					       sizeof(src_str));
276 				zlog_debug(
277 					"%s: ignoring unknown PIM hello TLV type=%d length=%d from %s on interface %s",
278 					__func__, option_type, option_len,
279 					src_str, ifp->name);
280 			}
281 		}
282 
283 		tlv_curr += option_len;
284 	}
285 
286 	/*
287 	  Check received PIM hello options
288 	*/
289 
290 	if (PIM_DEBUG_PIM_HELLO) {
291 		tlv_trace_uint16(__func__, "holdtime", ifp->name, src_addr,
292 				 PIM_OPTION_IS_SET(hello_options,
293 						   PIM_OPTION_MASK_HOLDTIME),
294 				 hello_option_holdtime);
295 		tlv_trace_uint16(
296 			__func__, "propagation_delay", ifp->name, src_addr,
297 			PIM_OPTION_IS_SET(hello_options,
298 					  PIM_OPTION_MASK_LAN_PRUNE_DELAY),
299 			hello_option_propagation_delay);
300 		tlv_trace_uint16(
301 			__func__, "override_interval", ifp->name, src_addr,
302 			PIM_OPTION_IS_SET(hello_options,
303 					  PIM_OPTION_MASK_LAN_PRUNE_DELAY),
304 			hello_option_override_interval);
305 		tlv_trace_bool(
306 			__func__, "can_disable_join_suppression", ifp->name,
307 			src_addr,
308 			PIM_OPTION_IS_SET(hello_options,
309 					  PIM_OPTION_MASK_LAN_PRUNE_DELAY),
310 			PIM_OPTION_IS_SET(
311 				hello_options,
312 				PIM_OPTION_MASK_CAN_DISABLE_JOIN_SUPPRESSION));
313 		tlv_trace_uint32(__func__, "dr_priority", ifp->name, src_addr,
314 				 PIM_OPTION_IS_SET(hello_options,
315 						   PIM_OPTION_MASK_DR_PRIORITY),
316 				 hello_option_dr_priority);
317 		tlv_trace_uint32_hex(
318 			__func__, "generation_id", ifp->name, src_addr,
319 			PIM_OPTION_IS_SET(hello_options,
320 					  PIM_OPTION_MASK_GENERATION_ID),
321 			hello_option_generation_id);
322 		tlv_trace_list(__func__, "address_list", ifp->name, src_addr,
323 			       PIM_OPTION_IS_SET(hello_options,
324 						 PIM_OPTION_MASK_ADDRESS_LIST),
325 			       hello_option_addr_list);
326 	}
327 
328 	if (!PIM_OPTION_IS_SET(hello_options, PIM_OPTION_MASK_HOLDTIME)) {
329 		if (PIM_DEBUG_PIM_HELLO) {
330 			char src_str[INET_ADDRSTRLEN];
331 			pim_inet4_dump("<src?>", src_addr, src_str,
332 				       sizeof(src_str));
333 			zlog_debug(
334 				"%s: PIM hello missing holdtime from %s on interface %s",
335 				__func__, src_str, ifp->name);
336 		}
337 	}
338 
339 	/*
340 	  New neighbor?
341 	*/
342 
343 	neigh = pim_neighbor_find(ifp, src_addr);
344 	if (!neigh) {
345 		/* Add as new neighbor */
346 
347 		neigh = pim_neighbor_add(
348 			ifp, src_addr, hello_options, hello_option_holdtime,
349 			hello_option_propagation_delay,
350 			hello_option_override_interval,
351 			hello_option_dr_priority, hello_option_generation_id,
352 			hello_option_addr_list, PIM_NEIGHBOR_SEND_DELAY);
353 		if (!neigh) {
354 			if (PIM_DEBUG_PIM_HELLO) {
355 				char src_str[INET_ADDRSTRLEN];
356 				pim_inet4_dump("<src?>", src_addr, src_str,
357 					       sizeof(src_str));
358 				zlog_warn(
359 					"%s: failure creating PIM neighbor %s on interface %s",
360 					__func__, src_str, ifp->name);
361 			}
362 			FREE_ADDR_LIST_THEN_RETURN(-8);
363 		}
364 		/* Forward BSM if required */
365 		if (!pim_bsm_new_nbr_fwd(neigh, ifp)) {
366 			if (PIM_DEBUG_PIM_HELLO)
367 				zlog_debug(
368 					"%s: forwarding bsm to new nbr failed",
369 					__func__);
370 		}
371 
372 		/* actual addr list has been saved under neighbor */
373 		return 0;
374 	}
375 
376 	/*
377 	  Received generation ID ?
378 	*/
379 
380 	if (PIM_OPTION_IS_SET(hello_options, PIM_OPTION_MASK_GENERATION_ID)) {
381 		/* GenID mismatch ? */
382 		if (!PIM_OPTION_IS_SET(neigh->hello_options,
383 				       PIM_OPTION_MASK_GENERATION_ID)
384 		    || (hello_option_generation_id != neigh->generation_id)) {
385 			/* GenID mismatch, then replace neighbor */
386 
387 			if (PIM_DEBUG_PIM_HELLO) {
388 				char src_str[INET_ADDRSTRLEN];
389 				pim_inet4_dump("<src?>", src_addr, src_str,
390 					       sizeof(src_str));
391 				zlog_debug(
392 					"%s: GenId mismatch new=%08x old=%08x: replacing neighbor %s on %s",
393 					__func__, hello_option_generation_id,
394 					neigh->generation_id, src_str,
395 					ifp->name);
396 			}
397 
398 			pim_upstream_rpf_genid_changed(pim_ifp->pim,
399 						       neigh->source_addr);
400 
401 			pim_neighbor_delete(ifp, neigh, "GenID mismatch");
402 			neigh = pim_neighbor_add(ifp, src_addr, hello_options,
403 						 hello_option_holdtime,
404 						 hello_option_propagation_delay,
405 						 hello_option_override_interval,
406 						 hello_option_dr_priority,
407 						 hello_option_generation_id,
408 						 hello_option_addr_list,
409 						 PIM_NEIGHBOR_SEND_NOW);
410 			if (!neigh) {
411 				if (PIM_DEBUG_PIM_HELLO) {
412 					char src_str[INET_ADDRSTRLEN];
413 					pim_inet4_dump("<src?>", src_addr,
414 						       src_str,
415 						       sizeof(src_str));
416 					zlog_debug(
417 						"%s: failure re-creating PIM neighbor %s on interface %s",
418 						__func__, src_str, ifp->name);
419 				}
420 				FREE_ADDR_LIST_THEN_RETURN(-9);
421 			}
422 			/* Forward BSM if required */
423 			if (!pim_bsm_new_nbr_fwd(neigh, ifp)) {
424 				if (PIM_DEBUG_PIM_HELLO)
425 					zlog_debug(
426 						"%s: forwarding bsm to new nbr failed",
427 						__func__);
428 			}
429 			/* actual addr list is saved under neighbor */
430 			return 0;
431 
432 		} /* GenId mismatch: replace neighbor */
433 
434 	} /* GenId received */
435 
436 	/*
437 	  Update existing neighbor
438 	*/
439 
440 	pim_neighbor_update(neigh, hello_options, hello_option_holdtime,
441 			    hello_option_dr_priority, hello_option_addr_list);
442 	/* actual addr list is saved under neighbor */
443 	return 0;
444 }
445 
pim_hello_build_tlv(struct interface * ifp,uint8_t * tlv_buf,int tlv_buf_size,uint16_t holdtime,uint32_t dr_priority,uint32_t generation_id,uint16_t propagation_delay,uint16_t override_interval,int can_disable_join_suppression)446 int pim_hello_build_tlv(struct interface *ifp, uint8_t *tlv_buf,
447 			int tlv_buf_size, uint16_t holdtime,
448 			uint32_t dr_priority, uint32_t generation_id,
449 			uint16_t propagation_delay, uint16_t override_interval,
450 			int can_disable_join_suppression)
451 {
452 	uint8_t *curr = tlv_buf;
453 	uint8_t *pastend = tlv_buf + tlv_buf_size;
454 	uint8_t *tmp;
455 	struct pim_interface *pim_ifp = ifp->info;
456 	struct pim_instance *pim = pim_ifp->pim;
457 
458 	/*
459 	 * Append options
460 	 */
461 
462 	/* Holdtime */
463 	curr = pim_tlv_append_uint16(curr, pastend,
464 				     PIM_MSG_OPTION_TYPE_HOLDTIME, holdtime);
465 	if (!curr) {
466 		if (PIM_DEBUG_PIM_HELLO) {
467 			zlog_debug(
468 				"%s: could not set PIM hello Holdtime option for interface %s",
469 				__func__, ifp->name);
470 		}
471 		return -1;
472 	}
473 
474 	/* LAN Prune Delay */
475 	tmp = pim_tlv_append_2uint16(curr, pastend,
476 				     PIM_MSG_OPTION_TYPE_LAN_PRUNE_DELAY,
477 				     propagation_delay, override_interval);
478 	if (!tmp) {
479 		if (PIM_DEBUG_PIM_HELLO) {
480 			zlog_debug(
481 				"%s: could not set PIM LAN Prune Delay option for interface %s",
482 				__func__, ifp->name);
483 		}
484 		return -1;
485 	}
486 	if (can_disable_join_suppression) {
487 		*(curr + 4) |= 0x80; /* enable T bit */
488 	}
489 	curr = tmp;
490 
491 	/* DR Priority */
492 	curr = pim_tlv_append_uint32(
493 		curr, pastend, PIM_MSG_OPTION_TYPE_DR_PRIORITY, dr_priority);
494 	if (!curr) {
495 		if (PIM_DEBUG_PIM_HELLO) {
496 			zlog_debug(
497 				"%s: could not set PIM hello DR Priority option for interface %s",
498 				__func__, ifp->name);
499 		}
500 		return -2;
501 	}
502 
503 	/* Generation ID */
504 	curr = pim_tlv_append_uint32(curr, pastend,
505 				     PIM_MSG_OPTION_TYPE_GENERATION_ID,
506 				     generation_id);
507 	if (!curr) {
508 		if (PIM_DEBUG_PIM_HELLO) {
509 			zlog_debug(
510 				"%s: could not set PIM hello Generation ID option for interface %s",
511 				__func__, ifp->name);
512 		}
513 		return -3;
514 	}
515 
516 	/* Secondary Address List */
517 	if (ifp->connected->count) {
518 		curr = pim_tlv_append_addrlist_ucast(curr, pastend,
519 						     ifp->connected, AF_INET);
520 		if (!curr) {
521 			if (PIM_DEBUG_PIM_HELLO) {
522 				zlog_debug(
523 					"%s: could not set PIM hello v4 Secondary Address List option for interface %s",
524 					__func__, ifp->name);
525 			}
526 			return -4;
527 		}
528 		if (pim->send_v6_secondary) {
529 			curr = pim_tlv_append_addrlist_ucast(
530 				curr, pastend, ifp->connected, AF_INET6);
531 			if (!curr) {
532 				if (PIM_DEBUG_PIM_HELLO) {
533 					zlog_debug(
534 						"%s: could not sent PIM hello v6 secondary Address List option for interface %s",
535 						__func__, ifp->name);
536 				}
537 				return -4;
538 			}
539 		}
540 	}
541 
542 	return curr - tlv_buf;
543 }
544 
545 /*
546   RFC 4601: 4.3.1.  Sending Hello Messages
547 
548   Thus, if a router needs to send a Join/Prune or Assert message on an
549   interface on which it has not yet sent a Hello message with the
550   currently configured IP address, then it MUST immediately send the
551   relevant Hello message without waiting for the Hello Timer to
552   expire, followed by the Join/Prune or Assert message.
553 */
pim_hello_require(struct interface * ifp)554 void pim_hello_require(struct interface *ifp)
555 {
556 	struct pim_interface *pim_ifp;
557 
558 	zassert(ifp);
559 
560 	pim_ifp = ifp->info;
561 
562 	zassert(pim_ifp);
563 
564 	if (pim_ifp->pim_ifstat_hello_sent)
565 		return;
566 
567 	pim_hello_restart_now(ifp); /* Send hello and restart timer */
568 }
569