Coverage Report

Created: 2026-01-25 06:17

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/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
}