1 /*
2  * Multicast traceroute for FRRouting
3  * Copyright (C) 2017  Mladen Sablic
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 /* based on draft-ietf-idmr-traceroute-ipm-07 */
21 
22 #include <zebra.h>
23 
24 #include "pimd.h"
25 #include "pim_util.h"
26 #include "pim_sock.h"
27 #include "pim_rp.h"
28 #include "pim_oil.h"
29 #include "pim_ifchannel.h"
30 #include "pim_macro.h"
31 #include "pim_igmp_mtrace.h"
32 
mtrace_primary_address(struct interface * ifp)33 static struct in_addr mtrace_primary_address(struct interface *ifp)
34 {
35 	struct connected *ifc;
36 	struct listnode *node;
37 	struct in_addr any;
38 	struct pim_interface *pim_ifp;
39 
40 	if (ifp->info) {
41 		pim_ifp = ifp->info;
42 		return pim_ifp->primary_address;
43 	}
44 
45 	any.s_addr = INADDR_ANY;
46 
47 	for (ALL_LIST_ELEMENTS_RO(ifp->connected, node, ifc)) {
48 		struct prefix *p = ifc->address;
49 
50 		if (p->family != AF_INET)
51 			continue;
52 
53 		if (!CHECK_FLAG(ifc->flags, ZEBRA_IFA_SECONDARY))
54 			return p->u.prefix4;
55 		/* in case no primary found, return a secondary */
56 		any = p->u.prefix4;
57 	}
58 	return any;
59 }
60 
mtrace_fwd_info_weak(struct pim_instance * pim,struct igmp_mtrace * mtracep,struct igmp_mtrace_rsp * rspp,struct interface ** ifpp)61 static bool mtrace_fwd_info_weak(struct pim_instance *pim,
62 				    struct igmp_mtrace *mtracep,
63 				    struct igmp_mtrace_rsp *rspp,
64 				    struct interface **ifpp)
65 {
66 	struct pim_nexthop nexthop;
67 	struct interface *ifp_in;
68 	struct in_addr nh_addr;
69 	char nexthop_str[INET_ADDRSTRLEN];
70 
71 	nh_addr.s_addr = INADDR_ANY;
72 
73 	memset(&nexthop, 0, sizeof(nexthop));
74 
75 	if (!pim_nexthop_lookup(pim, &nexthop, mtracep->src_addr, 1)) {
76 		if (PIM_DEBUG_MTRACE)
77 			zlog_debug("mtrace not found neighbor");
78 		return false;
79 	}
80 
81 	if (PIM_DEBUG_MTRACE)
82 		zlog_debug("mtrace pim_nexthop_lookup OK");
83 
84 	if (PIM_DEBUG_MTRACE)
85 		zlog_warn("mtrace next_hop=%s",
86 			  inet_ntop(nexthop.mrib_nexthop_addr.family,
87 				    &nexthop.mrib_nexthop_addr.u.prefix,
88 				    nexthop_str, sizeof(nexthop_str)));
89 
90 	if (nexthop.mrib_nexthop_addr.family == AF_INET)
91 		nh_addr = nexthop.mrib_nexthop_addr.u.prefix4;
92 
93 	ifp_in = nexthop.interface;
94 
95 	/* return interface for forwarding mtrace packets */
96 	*ifpp = ifp_in;
97 
98 	/* 6.2.2. 4. Fill in the Incoming Interface Address... */
99 	rspp->incoming = mtrace_primary_address(ifp_in);
100 	rspp->prev_hop = nh_addr;
101 	rspp->in_count = htonl(MTRACE_UNKNOWN_COUNT);
102 	rspp->total = htonl(MTRACE_UNKNOWN_COUNT);
103 	rspp->rtg_proto = MTRACE_RTG_PROTO_PIM;
104 	return true;
105 }
106 
mtrace_fwd_info(struct pim_instance * pim,struct igmp_mtrace * mtracep,struct igmp_mtrace_rsp * rspp,struct interface ** ifpp)107 static bool mtrace_fwd_info(struct pim_instance *pim,
108 			    struct igmp_mtrace *mtracep,
109 			    struct igmp_mtrace_rsp *rspp,
110 			    struct interface **ifpp)
111 {
112 	struct prefix_sg sg;
113 	struct pim_upstream *up;
114 	struct interface *ifp_in;
115 	struct in_addr nh_addr;
116 	uint32_t total;
117 	char up_str[INET_ADDRSTRLEN];
118 
119 	memset(&sg, 0, sizeof(struct prefix_sg));
120 	sg.src = mtracep->src_addr;
121 	sg.grp = mtracep->grp_addr;
122 
123 	up = pim_upstream_find(pim, &sg);
124 
125 	if (!up) {
126 		sg.src.s_addr = INADDR_ANY;
127 		up = pim_upstream_find(pim, &sg);
128 	}
129 
130 	if (!up)
131 		return false;
132 
133 	if (!up->rpf.source_nexthop.interface) {
134 		if (PIM_DEBUG_TRACE)
135 			zlog_debug("%s: up %s RPF is not present", __func__,
136 				   up->sg_str);
137 		return false;
138 	}
139 
140 	ifp_in = up->rpf.source_nexthop.interface;
141 	nh_addr = up->rpf.source_nexthop.mrib_nexthop_addr.u.prefix4;
142 	total = htonl(MTRACE_UNKNOWN_COUNT);
143 
144 	if (PIM_DEBUG_MTRACE)
145 		zlog_debug("fwd_info: upstream next hop=%s",
146 			   inet_ntop(AF_INET, &(nh_addr), up_str,
147 				     sizeof(up_str)));
148 
149 	if (up->channel_oil)
150 		total = up->channel_oil->cc.pktcnt;
151 
152 	/* return interface for forwarding mtrace packets */
153 	*ifpp = ifp_in;
154 
155 	/* 6.2.2. 4. Fill in the Incoming Interface Address... */
156 	rspp->incoming = mtrace_primary_address(ifp_in);
157 	rspp->prev_hop = nh_addr;
158 	rspp->in_count = htonl(MTRACE_UNKNOWN_COUNT);
159 	rspp->total = total;
160 	rspp->rtg_proto = MTRACE_RTG_PROTO_PIM;
161 
162 	/* 6.2.2. 4. Fill in ... S, and Src Mask */
163 	if (sg.src.s_addr != INADDR_ANY) {
164 		rspp->s = 1;
165 		rspp->src_mask = MTRACE_SRC_MASK_SOURCE;
166 	} else {
167 		rspp->s = 0;
168 		rspp->src_mask = MTRACE_SRC_MASK_GROUP;
169 	}
170 
171 	return true;
172 }
173 
mtrace_rsp_set_fwd_code(struct igmp_mtrace_rsp * mtrace_rspp,enum mtrace_fwd_code fwd_code)174 static void mtrace_rsp_set_fwd_code(struct igmp_mtrace_rsp *mtrace_rspp,
175 				    enum mtrace_fwd_code fwd_code)
176 {
177 	if (mtrace_rspp->fwd_code == MTRACE_FWD_CODE_NO_ERROR)
178 		mtrace_rspp->fwd_code = fwd_code;
179 }
180 
mtrace_rsp_init(struct igmp_mtrace_rsp * mtrace_rspp)181 static void mtrace_rsp_init(struct igmp_mtrace_rsp *mtrace_rspp)
182 {
183 	mtrace_rspp->arrival = 0;
184 	mtrace_rspp->incoming.s_addr = INADDR_ANY;
185 	mtrace_rspp->outgoing.s_addr = INADDR_ANY;
186 	mtrace_rspp->prev_hop.s_addr = INADDR_ANY;
187 	mtrace_rspp->in_count = htonl(MTRACE_UNKNOWN_COUNT);
188 	mtrace_rspp->out_count = htonl(MTRACE_UNKNOWN_COUNT);
189 	mtrace_rspp->total = htonl(MTRACE_UNKNOWN_COUNT);
190 	mtrace_rspp->rtg_proto = 0;
191 	mtrace_rspp->fwd_ttl = 0;
192 	mtrace_rspp->mbz = 0;
193 	mtrace_rspp->s = 0;
194 	mtrace_rspp->src_mask = 0;
195 	mtrace_rspp->fwd_code = MTRACE_FWD_CODE_NO_ERROR;
196 }
197 
mtrace_rsp_debug(uint32_t qry_id,int rsp,struct igmp_mtrace_rsp * mrspp)198 static void mtrace_rsp_debug(uint32_t qry_id, int rsp,
199 			     struct igmp_mtrace_rsp *mrspp)
200 {
201 	char inc_str[INET_ADDRSTRLEN];
202 	char out_str[INET_ADDRSTRLEN];
203 	char prv_str[INET_ADDRSTRLEN];
204 
205 	zlog_debug(
206 		"Rx mt(%d) qid=%ud arr=%x in=%s out=%s prev=%s proto=%d fwd=%d",
207 		rsp, ntohl(qry_id), mrspp->arrival,
208 		inet_ntop(AF_INET, &(mrspp->incoming), inc_str,
209 			  sizeof(inc_str)),
210 		inet_ntop(AF_INET, &(mrspp->outgoing), out_str,
211 			  sizeof(out_str)),
212 		inet_ntop(AF_INET, &(mrspp->prev_hop), prv_str,
213 			  sizeof(prv_str)),
214 		mrspp->rtg_proto, mrspp->fwd_code);
215 }
216 
mtrace_debug(struct pim_interface * pim_ifp,struct igmp_mtrace * mtracep,int mtrace_len)217 static void mtrace_debug(struct pim_interface *pim_ifp,
218 			 struct igmp_mtrace *mtracep, int mtrace_len)
219 {
220 	char inc_str[INET_ADDRSTRLEN];
221 	char grp_str[INET_ADDRSTRLEN];
222 	char src_str[INET_ADDRSTRLEN];
223 	char dst_str[INET_ADDRSTRLEN];
224 	char rsp_str[INET_ADDRSTRLEN];
225 
226 	struct in_addr ga, sa, da, ra;
227 
228 	ga = mtracep->grp_addr;
229 	sa = mtracep->src_addr;
230 	da = mtracep->dst_addr;
231 	ra = mtracep->rsp_addr;
232 
233 	zlog_debug(
234 		"Rx mtrace packet incoming on %s: hops=%d type=%d size=%d, grp=%s, src=%s, dst=%s rsp=%s ttl=%d qid=%ud",
235 		inet_ntop(AF_INET, &(pim_ifp->primary_address), inc_str,
236 			  sizeof(inc_str)),
237 		mtracep->hops, mtracep->type, mtrace_len,
238 		inet_ntop(AF_INET, &ga, grp_str,
239 			  sizeof(grp_str)),
240 		inet_ntop(AF_INET, &sa, src_str,
241 			  sizeof(src_str)),
242 		inet_ntop(AF_INET, &da, dst_str,
243 			  sizeof(dst_str)),
244 		inet_ntop(AF_INET, &ra, rsp_str,
245 			  sizeof(rsp_str)),
246 		mtracep->rsp_ttl, ntohl(mtracep->qry_id));
247 	if (mtrace_len > (int)sizeof(struct igmp_mtrace)) {
248 
249 		int i;
250 
251 		int responses = mtrace_len - sizeof(struct igmp_mtrace);
252 
253 		if ((responses % sizeof(struct igmp_mtrace_rsp)) != 0)
254 			if (PIM_DEBUG_MTRACE)
255 				zlog_debug(
256 					"Mtrace response block of wrong length");
257 
258 		responses = responses / sizeof(struct igmp_mtrace_rsp);
259 
260 		for (i = 0; i < responses; i++)
261 			mtrace_rsp_debug(mtracep->qry_id, i, &mtracep->rsp[i]);
262 	}
263 }
264 
265 /* 5.1 Query Arrival Time */
query_arrival_time(void)266 static uint32_t query_arrival_time(void)
267 {
268 	struct timeval tv;
269 	uint32_t qat;
270 
271 	if (gettimeofday(&tv, NULL) < 0) {
272 		if (PIM_DEBUG_MTRACE)
273 			zlog_warn("Query arrival time lookup failed: errno=%d: %s",
274 				  errno, safe_strerror(errno));
275 		return 0;
276 	}
277 	/* not sure second offset correct, as I get different value */
278 	qat = ((tv.tv_sec + 32384) << 16) + ((tv.tv_usec << 10) / 15625);
279 
280 	return qat;
281 }
282 
mtrace_send_packet(struct interface * ifp,struct igmp_mtrace * mtracep,size_t mtrace_buf_len,struct in_addr dst_addr,struct in_addr group_addr)283 static int mtrace_send_packet(struct interface *ifp,
284 			      struct igmp_mtrace *mtracep,
285 			      size_t mtrace_buf_len, struct in_addr dst_addr,
286 			      struct in_addr group_addr)
287 {
288 	struct sockaddr_in to;
289 	socklen_t tolen;
290 	ssize_t sent;
291 	int ret;
292 	int fd;
293 	char if_str[INET_ADDRSTRLEN];
294 	char rsp_str[INET_ADDRSTRLEN];
295 	uint8_t ttl;
296 
297 	memset(&to, 0, sizeof(to));
298 	to.sin_family = AF_INET;
299 	to.sin_addr = dst_addr;
300 	tolen = sizeof(to);
301 
302 	if (PIM_DEBUG_MTRACE) {
303 		struct in_addr if_addr;
304 
305 		if_addr = mtrace_primary_address(ifp);
306 		zlog_debug(
307 			"Sending mtrace packet to %s on %s",
308 			inet_ntop(AF_INET, &mtracep->rsp_addr, rsp_str,
309 				  sizeof(rsp_str)),
310 			inet_ntop(AF_INET, &if_addr, if_str, sizeof(if_str)));
311 	}
312 
313 	fd = pim_socket_raw(IPPROTO_IGMP);
314 
315 	if (fd < 0)
316 		return -1;
317 
318 	ret = pim_socket_bind(fd, ifp);
319 
320 	if (ret < 0) {
321 		ret = -1;
322 		goto close_fd;
323 	}
324 
325 	if (IPV4_CLASS_DE(ntohl(dst_addr.s_addr))) {
326 		if (IPV4_MC_LINKLOCAL(ntohl(dst_addr.s_addr))) {
327 			ttl = 1;
328 		} else {
329 			if (mtracep->type == PIM_IGMP_MTRACE_RESPONSE)
330 				ttl = mtracep->rsp_ttl;
331 			else
332 				ttl = 64;
333 		}
334 		ret = setsockopt(fd, IPPROTO_IP, IP_MULTICAST_TTL, &ttl,
335 				 sizeof(ttl));
336 
337 		if (ret < 0) {
338 			if (PIM_DEBUG_MTRACE)
339 				zlog_warn("Failed to set socket multicast TTL");
340 			ret = -1;
341 			goto close_fd;
342 		}
343 	}
344 
345 	sent = sendto(fd, (char *)mtracep, mtrace_buf_len, MSG_DONTWAIT,
346 		      (struct sockaddr *)&to, tolen);
347 
348 	if (sent != (ssize_t)mtrace_buf_len) {
349 		char dst_str[INET_ADDRSTRLEN];
350 		char group_str[INET_ADDRSTRLEN];
351 
352 		pim_inet4_dump("<dst?>", dst_addr, dst_str, sizeof(dst_str));
353 		pim_inet4_dump("<group?>", group_addr, group_str,
354 			       sizeof(group_str));
355 		if (sent < 0) {
356 			if (PIM_DEBUG_MTRACE)
357 				zlog_warn(
358 					"Send mtrace request failed for %s on%s: group=%s msg_size=%zd: errno=%d:  %s",
359 					dst_str, ifp->name, group_str,
360 					mtrace_buf_len, errno,
361 					safe_strerror(errno));
362 		} else {
363 			if (PIM_DEBUG_MTRACE)
364 				zlog_warn(
365 					"Send mtrace request failed for %s on %s: group=%s msg_size=%zd: sent=%zd",
366 					dst_str, ifp->name, group_str,
367 					mtrace_buf_len, sent);
368 		}
369 		ret = -1;
370 		goto close_fd;
371 	}
372 	ret = 0;
373 close_fd:
374 	close(fd);
375 	return ret;
376 }
377 
mtrace_un_forward_packet(struct pim_instance * pim,struct ip * ip_hdr,struct interface * interface)378 static int mtrace_un_forward_packet(struct pim_instance *pim, struct ip *ip_hdr,
379 				    struct interface *interface)
380 {
381 	struct pim_nexthop nexthop;
382 	struct sockaddr_in to;
383 	struct interface *if_out;
384 	socklen_t tolen;
385 	int ret;
386 	int fd;
387 	int sent;
388 	uint16_t checksum;
389 
390 	checksum = ip_hdr->ip_sum;
391 
392 	ip_hdr->ip_sum = 0;
393 
394 	if (checksum != in_cksum(ip_hdr, ip_hdr->ip_hl * 4))
395 		return -1;
396 
397 	if (ip_hdr->ip_ttl-- <= 1)
398 		return -1;
399 
400 	ip_hdr->ip_sum = in_cksum(ip_hdr, ip_hdr->ip_hl * 4);
401 
402 	fd = pim_socket_raw(IPPROTO_RAW);
403 
404 	if (fd < 0)
405 		return -1;
406 
407 	pim_socket_ip_hdr(fd);
408 
409 	if (interface == NULL) {
410 		memset(&nexthop, 0, sizeof(nexthop));
411 		if (!pim_nexthop_lookup(pim, &nexthop, ip_hdr->ip_dst, 0)) {
412 			close(fd);
413 			if (PIM_DEBUG_MTRACE)
414 				zlog_warn(
415 					"Dropping mtrace packet, no route to destination");
416 			return -1;
417 		}
418 
419 		if_out = nexthop.interface;
420 	} else {
421 		if_out = interface;
422 	}
423 
424 	ret = pim_socket_bind(fd, if_out);
425 
426 	if (ret < 0) {
427 		close(fd);
428 		return -1;
429 	}
430 
431 	memset(&to, 0, sizeof(to));
432 	to.sin_family = AF_INET;
433 	to.sin_addr = ip_hdr->ip_dst;
434 	tolen = sizeof(to);
435 
436 	sent = sendto(fd, ip_hdr, ntohs(ip_hdr->ip_len), 0,
437 		      (struct sockaddr *)&to, tolen);
438 
439 	close(fd);
440 
441 	if (sent < 0) {
442 		if (PIM_DEBUG_MTRACE)
443 			zlog_warn(
444 				"Failed to forward mtrace packet: sendto errno=%d, %s",
445 				errno, safe_strerror(errno));
446 		return -1;
447 	}
448 
449 	if (PIM_DEBUG_MTRACE) {
450 		zlog_debug("Fwd mtrace packet len=%u to %s ttl=%u",
451 			   ntohs(ip_hdr->ip_len), inet_ntoa(ip_hdr->ip_dst),
452 			   ip_hdr->ip_ttl);
453 	}
454 
455 	return 0;
456 }
457 
mtrace_mc_forward_packet(struct pim_instance * pim,struct ip * ip_hdr)458 static int mtrace_mc_forward_packet(struct pim_instance *pim, struct ip *ip_hdr)
459 {
460 	struct prefix_sg sg;
461 	struct channel_oil *c_oil;
462 	struct listnode *chnode;
463 	struct listnode *chnextnode;
464 	struct pim_ifchannel *ch = NULL;
465 	int ret = -1;
466 
467 	memset(&sg, 0, sizeof(struct prefix_sg));
468 	sg.grp = ip_hdr->ip_dst;
469 
470 	c_oil = pim_find_channel_oil(pim, &sg);
471 
472 	if (c_oil == NULL) {
473 		if (PIM_DEBUG_MTRACE) {
474 			zlog_debug(
475 				"Dropping mtrace multicast packet len=%u to %s ttl=%u",
476 				ntohs(ip_hdr->ip_len),
477 				inet_ntoa(ip_hdr->ip_dst), ip_hdr->ip_ttl);
478 		}
479 		return -1;
480 	}
481 	if (c_oil->up == NULL)
482 		return -1;
483 	if (c_oil->up->ifchannels == NULL)
484 		return -1;
485 	for (ALL_LIST_ELEMENTS(c_oil->up->ifchannels, chnode, chnextnode, ch)) {
486 		if (pim_macro_chisin_oiflist(ch)) {
487 			int r;
488 
489 			r = mtrace_un_forward_packet(pim, ip_hdr,
490 						     ch->interface);
491 			if (r == 0)
492 				ret = 0;
493 		}
494 	}
495 	return ret;
496 }
497 
498 
mtrace_forward_packet(struct pim_instance * pim,struct ip * ip_hdr)499 static int mtrace_forward_packet(struct pim_instance *pim, struct ip *ip_hdr)
500 {
501 	if (IPV4_CLASS_DE(ntohl(ip_hdr->ip_dst.s_addr)))
502 		return mtrace_mc_forward_packet(pim, ip_hdr);
503 	else
504 		return mtrace_un_forward_packet(pim, ip_hdr, NULL);
505 }
506 
mtrace_send_mc_response(struct pim_instance * pim,struct igmp_mtrace * mtracep,size_t mtrace_len)507 static int mtrace_send_mc_response(struct pim_instance *pim,
508 				   struct igmp_mtrace *mtracep,
509 				   size_t mtrace_len)
510 {
511 	struct prefix_sg sg;
512 	struct channel_oil *c_oil;
513 	struct listnode *chnode;
514 	struct listnode *chnextnode;
515 	struct pim_ifchannel *ch = NULL;
516 	int ret = -1;
517 
518 	memset(&sg, 0, sizeof(struct prefix_sg));
519 	sg.grp = mtracep->rsp_addr;
520 
521 	c_oil = pim_find_channel_oil(pim, &sg);
522 
523 	if (c_oil == NULL) {
524 		if (PIM_DEBUG_MTRACE) {
525 			zlog_debug(
526 				"Dropping mtrace multicast response packet len=%u to %s",
527 				(unsigned int)mtrace_len,
528 				inet_ntoa(mtracep->rsp_addr));
529 		}
530 		return -1;
531 	}
532 	if (c_oil->up == NULL)
533 		return -1;
534 	if (c_oil->up->ifchannels == NULL)
535 		return -1;
536 	for (ALL_LIST_ELEMENTS(c_oil->up->ifchannels, chnode, chnextnode, ch)) {
537 		if (pim_macro_chisin_oiflist(ch)) {
538 			int r;
539 
540 			r = mtrace_send_packet(ch->interface, mtracep,
541 					       mtrace_len, mtracep->rsp_addr,
542 					       mtracep->grp_addr);
543 			if (r == 0)
544 				ret = 0;
545 		}
546 	}
547 	return ret;
548 }
549 
550 /* 6.5 Sending Traceroute Responses */
mtrace_send_response(struct pim_instance * pim,struct igmp_mtrace * mtracep,size_t mtrace_len)551 static int mtrace_send_response(struct pim_instance *pim,
552 				struct igmp_mtrace *mtracep, size_t mtrace_len)
553 {
554 	struct pim_nexthop nexthop;
555 
556 	mtracep->type = PIM_IGMP_MTRACE_RESPONSE;
557 
558 	mtracep->checksum = 0;
559 	mtracep->checksum = in_cksum((char *)mtracep, mtrace_len);
560 
561 	if (IPV4_CLASS_DE(ntohl(mtracep->rsp_addr.s_addr))) {
562 		struct pim_rpf *p_rpf;
563 		char grp_str[INET_ADDRSTRLEN];
564 
565 		if (pim_rp_i_am_rp(pim, mtracep->rsp_addr))
566 			return mtrace_send_mc_response(pim, mtracep,
567 						       mtrace_len);
568 
569 		p_rpf = pim_rp_g(pim, mtracep->rsp_addr);
570 
571 		if (p_rpf == NULL) {
572 			if (PIM_DEBUG_MTRACE)
573 				zlog_warn("mtrace no RP for %s",
574 					  inet_ntop(AF_INET,
575 						    &(mtracep->rsp_addr),
576 						    grp_str, sizeof(grp_str)));
577 			return -1;
578 		}
579 		nexthop = p_rpf->source_nexthop;
580 		if (PIM_DEBUG_MTRACE)
581 			zlog_debug("mtrace response to RP");
582 	} else {
583 		memset(&nexthop, 0, sizeof(nexthop));
584 		/* TODO: should use unicast rib lookup */
585 		if (!pim_nexthop_lookup(pim, &nexthop, mtracep->rsp_addr, 1)) {
586 			if (PIM_DEBUG_MTRACE)
587 				zlog_warn(
588 					"Dropped response qid=%ud, no route to response address",
589 					mtracep->qry_id);
590 			return -1;
591 		}
592 	}
593 
594 	return mtrace_send_packet(nexthop.interface, mtracep, mtrace_len,
595 				  mtracep->rsp_addr, mtracep->grp_addr);
596 }
597 
igmp_mtrace_recv_qry_req(struct igmp_sock * igmp,struct ip * ip_hdr,struct in_addr from,const char * from_str,char * igmp_msg,int igmp_msg_len)598 int igmp_mtrace_recv_qry_req(struct igmp_sock *igmp, struct ip *ip_hdr,
599 			     struct in_addr from, const char *from_str,
600 			     char *igmp_msg, int igmp_msg_len)
601 {
602 	static uint32_t qry_id, qry_src;
603 	char mtrace_buf[MTRACE_HDR_SIZE + MTRACE_MAX_HOPS * MTRACE_RSP_SIZE];
604 	struct interface *ifp;
605 	struct interface *out_ifp = NULL;
606 	struct pim_interface *pim_ifp;
607 	struct pim_instance *pim;
608 	struct igmp_mtrace *mtracep;
609 	struct igmp_mtrace_rsp *rspp;
610 	struct in_addr nh_addr;
611 	enum mtrace_fwd_code fwd_code = MTRACE_FWD_CODE_NO_ERROR;
612 	size_t r_len;
613 	int last_rsp_ind = 0;
614 	size_t mtrace_len;
615 	uint16_t recv_checksum;
616 	uint16_t checksum;
617 	bool reached_source;
618 	bool fwd_info;
619 
620 	ifp = igmp->interface;
621 	pim_ifp = ifp->info;
622 	pim = pim_ifp->pim;
623 
624 	/*
625 	 * 6. Router Behaviour
626 	 * Check if mtrace packet is addressed elsewhere and forward,
627 	 * if applicable
628 	 */
629 	if (!IPV4_CLASS_DE(ntohl(ip_hdr->ip_dst.s_addr)))
630 		if (!if_lookup_exact_address(&ip_hdr->ip_dst, AF_INET,
631 					     pim->vrf_id))
632 			return mtrace_forward_packet(pim, ip_hdr);
633 
634 	if (igmp_msg_len < (int)sizeof(struct igmp_mtrace)) {
635 		if (PIM_DEBUG_MTRACE)
636 			zlog_warn(
637 				"Recv mtrace packet from %s on %s: too short, len=%d, min=%zu",
638 				from_str, ifp->name, igmp_msg_len,
639 				sizeof(struct igmp_mtrace));
640 		return -1;
641 	}
642 
643 	mtracep = (struct igmp_mtrace *)igmp_msg;
644 
645 	recv_checksum = mtracep->checksum;
646 
647 	mtracep->checksum = 0;
648 
649 	checksum = in_cksum(igmp_msg, igmp_msg_len);
650 
651 	if (recv_checksum != checksum) {
652 		if (PIM_DEBUG_MTRACE)
653 			zlog_warn(
654 				"Recv mtrace packet from %s on %s: checksum mismatch: received=%x computed=%x",
655 				from_str, ifp->name, recv_checksum, checksum);
656 		return -1;
657 	}
658 
659 	/* Collecting IGMP Rx stats */
660 	igmp->rx_stats.mtrace_req++;
661 
662 	if (PIM_DEBUG_MTRACE)
663 		mtrace_debug(pim_ifp, mtracep, igmp_msg_len);
664 
665 	/* subtract header from message length */
666 	r_len = igmp_msg_len - sizeof(struct igmp_mtrace);
667 
668 	/* Classify mtrace packet, check if it is a query */
669 	if (!r_len) {
670 		if (PIM_DEBUG_MTRACE)
671 			zlog_debug("Received IGMP multicast traceroute query");
672 
673 		/* 6.1.1  Packet verification */
674 		if (!pim_if_connected_to_source(ifp, mtracep->dst_addr)) {
675 			if (IPV4_CLASS_DE(ntohl(ip_hdr->ip_dst.s_addr))) {
676 				if (PIM_DEBUG_MTRACE)
677 					zlog_debug(
678 						"Dropping multicast query on wrong interface");
679 				return -1;
680 			}
681 			/* Unicast query on wrong interface */
682 			fwd_code = MTRACE_FWD_CODE_WRONG_IF;
683 			if (PIM_DEBUG_MTRACE)
684 				zlog_debug("Multicast query on wrong interface");
685 		}
686 		if (qry_id == mtracep->qry_id && qry_src == from.s_addr) {
687 			if (PIM_DEBUG_MTRACE)
688 				zlog_debug(
689 					"Dropping multicast query with duplicate source and id");
690 			return -1;
691 		}
692 		qry_id = mtracep->qry_id;
693 		qry_src = from.s_addr;
694 	}
695 	/* if response fields length is equal to a whole number of responses */
696 	else if ((r_len % sizeof(struct igmp_mtrace_rsp)) == 0) {
697 		r_len = igmp_msg_len - sizeof(struct igmp_mtrace);
698 
699 		if (r_len != 0)
700 			last_rsp_ind = r_len / sizeof(struct igmp_mtrace_rsp);
701 		if (last_rsp_ind > MTRACE_MAX_HOPS) {
702 			if (PIM_DEBUG_MTRACE)
703 				zlog_warn("Mtrace request of excessive size");
704 			return -1;
705 		}
706 	} else {
707 		if (PIM_DEBUG_MTRACE)
708 			zlog_warn(
709 				"Recv mtrace packet from %s on %s: invalid length %d",
710 				from_str, ifp->name, igmp_msg_len);
711 		return -1;
712 	}
713 
714 	/* 6.2.1 Packet Verification - drop not link-local multicast */
715 	if (IPV4_CLASS_DE(ntohl(ip_hdr->ip_dst.s_addr))
716 	    && !IPV4_MC_LINKLOCAL(ntohl(ip_hdr->ip_dst.s_addr))) {
717 		if (PIM_DEBUG_MTRACE)
718 			zlog_warn(
719 				"Recv mtrace packet from %s on %s: not link-local multicast %s",
720 				from_str, ifp->name, inet_ntoa(ip_hdr->ip_dst));
721 		return -1;
722 	}
723 
724 	/* 6.2.2. Normal Processing */
725 
726 	/* 6.2.2. 1. If there is room in the current buffer? */
727 
728 	if (last_rsp_ind == MTRACE_MAX_HOPS) {
729 		/* ...there was no room... */
730 		mtracep->rsp[MTRACE_MAX_HOPS - 1].fwd_code =
731 			MTRACE_FWD_CODE_NO_SPACE;
732 		return mtrace_send_response(pim_ifp->pim, mtracep,
733 					    igmp_msg_len);
734 	}
735 
736 	/* ...insert new response block... */
737 
738 	/* calculate new mtrace lenght with extra response */
739 	mtrace_len = igmp_msg_len + sizeof(struct igmp_mtrace_rsp);
740 
741 	/* copy received query/request */
742 	memcpy(mtrace_buf, igmp_msg, igmp_msg_len);
743 
744 	/* repoint mtracep pointer to copy */
745 	mtracep = (struct igmp_mtrace *)mtrace_buf;
746 
747 	/* pointer for extra response field to be filled in */
748 	rspp = &mtracep->rsp[last_rsp_ind];
749 
750 	/* initialize extra response field */
751 	mtrace_rsp_init(rspp);
752 
753 	/* carry over any error noted when receiving the query */
754 	rspp->fwd_code = fwd_code;
755 
756 	/* ...and fill in Query Arrival Time... */
757 	rspp->arrival = htonl(query_arrival_time());
758 	rspp->outgoing = pim_ifp->primary_address;
759 	rspp->out_count = htonl(MTRACE_UNKNOWN_COUNT);
760 	rspp->fwd_ttl = 1;
761 
762 	/* 6.2.2. 2. Attempt to determine the forwarding information... */
763 
764 	if (mtracep->grp_addr.s_addr != INADDR_ANY)
765 		fwd_info = mtrace_fwd_info(pim, mtracep, rspp, &out_ifp);
766 	else
767 		fwd_info = mtrace_fwd_info_weak(pim, mtracep, rspp, &out_ifp);
768 
769 	/* 6.2.2 3. If no forwarding information... */
770 	if (!fwd_info) {
771 		if (PIM_DEBUG_MTRACE)
772 			zlog_debug("mtrace not found multicast state");
773 		mtrace_rsp_set_fwd_code(rspp, MTRACE_FWD_CODE_NO_ROUTE);
774 		/* 6.2.2. 3. forward the packet to requester */
775 		return mtrace_send_response(pim, mtracep, mtrace_len);
776 	}
777 
778 	nh_addr = rspp->prev_hop;
779 
780 	reached_source = false;
781 
782 	if (nh_addr.s_addr == INADDR_ANY) {
783 		/* no pim? i.e. 7.5.3. No Previous Hop */
784 		if (!out_ifp->info) {
785 			if (PIM_DEBUG_MTRACE)
786 				zlog_debug("mtrace not found incoming if w/ pim");
787 			mtrace_rsp_set_fwd_code(rspp,
788 						MTRACE_FWD_CODE_NO_MULTICAST);
789 			return mtrace_send_response(pim, mtracep, mtrace_len);
790 		}
791 		/* reached source? i.e. 7.5.1 Arriving at source */
792 		if (pim_if_connected_to_source(out_ifp, mtracep->src_addr)) {
793 			reached_source = true;
794 			rspp->prev_hop = mtracep->src_addr;
795 		}
796 		/*
797 		 * 6.4 Forwarding Traceroute Requests:
798 		 * Previous-hop router not known,
799 		 * packet is sent to an appropriate multicast address
800 		 */
801 		(void)inet_aton(MCAST_ALL_ROUTERS, &nh_addr);
802 	}
803 
804 	/* 6.2.2 8. If this router is the Rendez-vous Point */
805 	if (pim_rp_i_am_rp(pim, mtracep->grp_addr)) {
806 		mtrace_rsp_set_fwd_code(rspp, MTRACE_FWD_CODE_REACHED_RP);
807 		/* 7.7.1. PIM-SM ...RP has not performed source-specific join */
808 		if (rspp->src_mask == MTRACE_SRC_MASK_GROUP)
809 			return mtrace_send_response(pim, mtracep, mtrace_len);
810 	}
811 
812 	/*
813 	 * 6.4 Forwarding Traceroute Requests: the number of response
814 	 * blocks exceeds number of responses, so forward to the requester.
815 	 */
816 	if (mtracep->hops <= (last_rsp_ind + 1))
817 		return mtrace_send_response(pim, mtracep, mtrace_len);
818 
819 	/* 7.5.1. Arriving at source: terminate trace */
820 	if (reached_source)
821 		return mtrace_send_response(pim, mtracep, mtrace_len);
822 
823 	mtracep->checksum = 0;
824 
825 	mtracep->checksum = in_cksum(mtrace_buf, mtrace_len);
826 
827 	/* 6.4 Forwarding Traceroute Requests: response blocks less than req. */
828 	return mtrace_send_packet(out_ifp, mtracep, mtrace_len, nh_addr,
829 				  mtracep->grp_addr);
830 }
831 
832 /* 6.3. Traceroute responses */
igmp_mtrace_recv_response(struct igmp_sock * igmp,struct ip * ip_hdr,struct in_addr from,const char * from_str,char * igmp_msg,int igmp_msg_len)833 int igmp_mtrace_recv_response(struct igmp_sock *igmp, struct ip *ip_hdr,
834 			      struct in_addr from, const char *from_str,
835 			      char *igmp_msg, int igmp_msg_len)
836 {
837 	static uint32_t qry_id, rsp_dst;
838 	struct interface *ifp;
839 	struct pim_interface *pim_ifp;
840 	struct pim_instance *pim;
841 	struct igmp_mtrace *mtracep;
842 	uint16_t recv_checksum;
843 	uint16_t checksum;
844 
845 	ifp = igmp->interface;
846 	pim_ifp = ifp->info;
847 	pim = pim_ifp->pim;
848 
849 	if (igmp_msg_len < (int)sizeof(struct igmp_mtrace)) {
850 		if (PIM_DEBUG_MTRACE)
851 			zlog_warn(
852 				"Recv mtrace packet from %s on %s: too short, len=%d, min=%zu",
853 				from_str, ifp->name, igmp_msg_len,
854 				sizeof(struct igmp_mtrace));
855 		return -1;
856 	}
857 
858 	mtracep = (struct igmp_mtrace *)igmp_msg;
859 
860 	recv_checksum = mtracep->checksum;
861 
862 	mtracep->checksum = 0;
863 
864 	checksum = in_cksum(igmp_msg, igmp_msg_len);
865 
866 	if (recv_checksum != checksum) {
867 		if (PIM_DEBUG_MTRACE)
868 			zlog_warn(
869 				"Recv mtrace response from %s on %s: checksum mismatch: received=%x computed=%x",
870 				from_str, ifp->name, recv_checksum, checksum);
871 		return -1;
872 	}
873 
874 	mtracep->checksum = checksum;
875 
876 	/* Collecting IGMP Rx stats */
877 	igmp->rx_stats.mtrace_rsp++;
878 
879 	if (PIM_DEBUG_MTRACE)
880 		mtrace_debug(pim_ifp, mtracep, igmp_msg_len);
881 
882 	/* Drop duplicate packets */
883 	if (qry_id == mtracep->qry_id && rsp_dst == ip_hdr->ip_dst.s_addr) {
884 		if (PIM_DEBUG_MTRACE)
885 			zlog_debug("duplicate mtrace response packet dropped");
886 		return -1;
887 	}
888 
889 	qry_id = mtracep->qry_id;
890 	rsp_dst = ip_hdr->ip_dst.s_addr;
891 
892 	return mtrace_forward_packet(pim, ip_hdr);
893 }
894