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