]> git.puffer.fish Git - mirror/frr.git/commitdiff
pimd: Multicast traceroute client and router 1749/head
authorMladen Sablic <mladen.sablic@gmail.com>
Mon, 12 Feb 2018 22:41:33 +0000 (23:41 +0100)
committerMladen Sablic <mladen.sablic@gmail.com>
Mon, 19 Feb 2018 22:13:55 +0000 (23:13 +0100)
This commit is the implementation of weak multicast traceroute.
It consists of IGMP module dealing with mtrace type IGMP messages
and client program mtrace/mtracebis for initiating mtrace queries.

Signed-off-by: Mladen Sablic <mladen.sablic@gmail.com>
25 files changed:
configure.ac
debianpkg/backports/ubuntu14.04/debian/frr.install
debianpkg/frr.install
doc/Makefile.am
doc/mtracebis.8.in [new file with mode: 0644]
lib/prefix.h
pimd/.gitignore
pimd/COMMANDS
pimd/mtracebis.c [new file with mode: 0644]
pimd/mtracebis_netlink.c [new file with mode: 0644]
pimd/mtracebis_netlink.h [new file with mode: 0644]
pimd/mtracebis_routeget.c [new file with mode: 0644]
pimd/mtracebis_routeget.h [new file with mode: 0644]
pimd/pim_cmd.c
pimd/pim_cmd.h
pimd/pim_igmp.c
pimd/pim_igmp.h
pimd/pim_igmp_mtrace.c [new file with mode: 0644]
pimd/pim_igmp_mtrace.h [new file with mode: 0644]
pimd/pim_oil.c
pimd/pim_oil.h
pimd/pim_vty.c
pimd/pimd.h
pimd/subdir.am
vtysh/vtysh.c

index 6bd2d44fe864475545210f4c926982ff11ccef90..0d6c99acfa64b8f2f61b3310a81b4acb765fcf4e 100755 (executable)
@@ -1886,6 +1886,7 @@ AC_CONFIG_FILES([Makefile
          doc/eigrpd.8
          doc/ripngd.8
          doc/pimd.8
+         doc/mtracebis.8
          doc/nhrpd.8
          doc/vtysh.1
          doc/watchfrr.8
index adce915e1f075d7083f7c51a8669184efd25c7f1..8e83bdf6df8a644c455b15517232df87699e24b7 100644 (file)
@@ -1,5 +1,6 @@
 etc/frr/
 usr/bin/vtysh
+usr/bin/mtracebis
 usr/include/frr/
 usr/lib/
 tools/frr etc/init.d/
@@ -15,6 +16,7 @@ usr/share/man/man8/ripngd.8
 usr/share/man/man8/zebra.8
 usr/share/man/man8/isisd.8
 usr/share/man/man8/watchfrr.8
+usr/share/man/man8/mtracebis.8
 usr/share/snmp/mibs/
 tools/etc/* etc/
 tools/*.service    lib/systemd/system
index 2d86009dba8a1b47354a1c91ed8c51ee2ea2db23..1aee4d540e8868b7f41cd86f203fae61efc63089 100644 (file)
@@ -1,5 +1,6 @@
 etc/frr/
 usr/bin/vtysh
+usr/bin/mtracebis
 usr/include/frr/
 usr/lib/
 tools/frr usr/lib/frr
@@ -16,6 +17,7 @@ usr/share/man/man8/zebra.8
 usr/share/man/man8/isisd.8
 usr/share/man/man8/watchfrr.8
 usr/share/man/man8/frr-args.8
+usr/share/man/man8/mtracebis.8
 usr/share/snmp/mibs/
 tools/etc/* etc/
 tools/*.service    lib/systemd/system
index 7aaa36556f25da4647ba0f0ed1a8552f469d5a74..9c81202db9e7938500a0fcabf6d5888fb48c4516 100644 (file)
@@ -104,6 +104,7 @@ man_MANS = frr.1 frr-args.8
 
 if PIMD
 man_MANS += pimd.8
+man_MANS += mtracebis.8
 endif
 
 if BGPD
@@ -169,6 +170,7 @@ EXTRA_DIST = BGP-TypeCode draft-zebra-00.ms draft-zebra-00.txt \
        ripd.8.in \
        ripngd.8.in \
        pimd.8.in \
+       mtracebis.8.in \
        nhrpd.8.in \
        vtysh.1.in \
        watchfrr.8.in \
diff --git a/doc/mtracebis.8.in b/doc/mtracebis.8.in
new file mode 100644 (file)
index 0000000..3abc717
--- /dev/null
@@ -0,0 +1,25 @@
+.\" This file was originally generated by help2man 1.44.1.
+.TH MTRACEBIS "8" "February 2018" "mtracebis 0.1" "System Administration Utilities"
+.SH NAME
+mtracebis \- a program to initiate multicast traceroute "mtrace" queries
+.SH SYNOPSIS
+mtracebis
+<multicast source>
+.SH DESCRIPTION
+.B mtracebis
+is a program used to test multicast connectivity in a multicast and multicast
+traceroute enabled IP network.
+.PP
+Initial version of the program requires multicast source IP address and
+initiates a weak traceroute across the network. This tests whether the
+interfaces towards the source are multicast enabled. First query sent is a
+full query, capable of crossing the network all the way to the source. If this
+fails, hop-by-hop queries are initiated.
+.PP
+Hop-by-hop queries start by requesting only a response from the nearest router.
+Following that, next query is extended to the next two routers, and so on...
+until a set of routers is tested for connectivity.
+.SH SEE ALSO
+See the project homepage at <@PACKAGE_URL@>.
+.SH AUTHORS
+Copyright 2018 Mladen Sablic
index bcc2230607802c25d826cacc88d4db56d3b4db37..5bf7d498c1ca7073e8f34b1a8870e0c332c2563b 100644 (file)
@@ -239,6 +239,7 @@ static inline void ipv4_addr_copy(struct in_addr *dst,
 #define IPV4_NET127(a)  ((((u_int32_t) (a)) & 0xff000000) == 0x7f000000)
 #define IPV4_LINKLOCAL(a) ((((u_int32_t) (a)) & 0xffff0000) == 0xa9fe0000)
 #define IPV4_CLASS_DE(a)  ((((u_int32_t) (a)) & 0xe0000000) == 0xe0000000)
+#define IPV4_MC_LINKLOCAL(a)  ((((u_int32_t) (a)) & 0xffffff00) == 0xe0000000)
 
 /* Max bit/byte length of IPv6 address. */
 #define IPV6_MAX_BYTELEN    16
index e23216b058a7de476952e7b25f8219b74fdb61a3..1f56cfaecdabd2dac7344f859ef8710ce6b03079 100644 (file)
@@ -2,6 +2,7 @@
 Makefile.in
 libpim.a
 pimd
+mtracebis
 test_igmpv3_join
 tags
 TAGS
index c545eca56e45d034132b5dac63c3496416c7c82b..6f2e020bd87f5c146d5c5574fa55790f7671cbcd 100644 (file)
@@ -59,6 +59,7 @@ debug commands:
        clear ip pim interfaces         Reset PIM interfaces
        clear ip pim oil                        Rescan PIM OIL (output interface list)
        debug igmp                      IGMP protocol activity
+       debug mtrace                    Mtrace protocol activity
        debug mroute                    PIM interaction with kernel MFC cache
        debug pim                       PIM protocol activity
        debug pim zebra                 ZEBRA protocol activity
@@ -76,4 +77,6 @@ statistics commands:
        pimd:
        show memory pim         PIM memory statistics
 
+vtysh:
+       mtrace                  Multicast traceroute
 -x-
diff --git a/pimd/mtracebis.c b/pimd/mtracebis.c
new file mode 100644 (file)
index 0000000..2032cdc
--- /dev/null
@@ -0,0 +1,371 @@
+/*
+ * Multicast Traceroute for FRRouting
+ * Copyright (C) 2018  Mladen Sablic
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; see the file COPYING; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifdef __linux__
+
+#include "pim_igmp_mtrace.h"
+
+#include "checksum.h"
+#include "mtracebis_routeget.h"
+
+#include <sys/select.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/time.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include <net/if.h>
+
+#define MTRACEBIS_VERSION "0.1"
+#define MTRACE_TIMEOUT (5)
+
+#define IP_HDR_LEN (sizeof(struct ip))
+#define IP_RA_LEN (4)
+#define MTRACE_BUF_LEN (MTRACE_HDR_SIZE + (MTRACE_MAX_HOPS * MTRACE_RSP_SIZE))
+#define IP_AND_MTRACE_BUF_LEN (IP_HDR_LEN + IP_RA_LEN + MTRACE_BUF_LEN)
+
+static const char *progname;
+static void usage(void)
+{
+       fprintf(stderr, "Usage : %s <multicast source>\n", progname);
+}
+static void version(void)
+{
+       fprintf(stderr, "%s %s\n", progname, MTRACEBIS_VERSION);
+}
+
+static int send_query(int fd, struct in_addr to_addr,
+                     struct igmp_mtrace *mtrace)
+{
+       struct sockaddr_in to;
+       socklen_t tolen;
+       int sent;
+
+       memset(&to, 0, sizeof(to));
+       to.sin_family = AF_INET;
+       to.sin_addr = to_addr;
+       tolen = sizeof(to);
+
+       sent = sendto(fd, (char *)mtrace, sizeof(*mtrace), MSG_DONTWAIT,
+                     (struct sockaddr *)&to, tolen);
+
+       if (sent < 1)
+               return -1;
+       return 0;
+}
+
+static void print_query(struct igmp_mtrace *mtrace)
+{
+       char src_str[INET_ADDRSTRLEN];
+       char dst_str[INET_ADDRSTRLEN];
+       char grp_str[INET_ADDRSTRLEN];
+
+       printf("* Mtrace from %s to %s via group %s\n",
+              inet_ntop(AF_INET, &mtrace->src_addr, src_str, sizeof(src_str)),
+              inet_ntop(AF_INET, &mtrace->dst_addr, dst_str, sizeof(dst_str)),
+              inet_ntop(AF_INET, &mtrace->grp_addr, grp_str, sizeof(grp_str)));
+}
+
+static int recv_response(int fd, long msec, int *hops)
+{
+       int recvd;
+       char mtrace_buf[IP_AND_MTRACE_BUF_LEN];
+       struct ip *ip;
+       struct igmp_mtrace *mtrace;
+       int mtrace_len;
+       int responses;
+       int i;
+       u_short sum;
+
+       recvd = recvfrom(fd, mtrace_buf, IP_AND_MTRACE_BUF_LEN, 0, NULL, 0);
+
+       if (recvd < 1) {
+               fprintf(stderr, "recvfrom error: %s\n", strerror(errno));
+               return -1;
+       }
+
+       if (recvd < (int)sizeof(struct ip)) {
+               fprintf(stderr, "no ip header\n");
+               return -1;
+       }
+
+       ip = (struct ip *)mtrace_buf;
+
+       if (ip->ip_v != 4) {
+               fprintf(stderr, "IP not version 4\n");
+               return -1;
+       }
+
+       sum = ip->ip_sum;
+       ip->ip_sum = 0;
+
+       if (sum != in_cksum(ip, ip->ip_hl * 4))
+               return -1;
+
+       mtrace = (struct igmp_mtrace *)(mtrace_buf + (4 * ip->ip_hl));
+
+       mtrace_len = ntohs(ip->ip_len) - ip->ip_hl * 4;
+
+       if (mtrace_len < (int)MTRACE_HDR_SIZE)
+               return -1;
+
+       sum = mtrace->checksum;
+       mtrace->checksum = 0;
+       if (sum != in_cksum(mtrace, mtrace_len)) {
+               fprintf(stderr, "mtrace checksum wrong\n");
+               return -1;
+       }
+
+       if (mtrace->type != PIM_IGMP_MTRACE_RESPONSE)
+               return -1;
+
+
+       responses = mtrace_len - sizeof(struct igmp_mtrace);
+       responses /= sizeof(struct igmp_mtrace_rsp);
+
+       printf("%ld ms received responses from %d hops.\n", msec, responses);
+
+       if (hops)
+               *hops = responses;
+
+       for (i = 0; i < responses; i++) {
+               struct igmp_mtrace_rsp *rsp = &mtrace->rsp[i];
+
+               if (rsp->fwd_code != 0)
+                       printf("-%d fwd. code 0x%2x.\n", i, rsp->fwd_code);
+       }
+
+       return 0;
+}
+
+static int wait_for_response(int fd, int *hops)
+{
+       fd_set readfds;
+       struct timeval timeout;
+       int ret = -1;
+       long msec, rmsec, tmsec;
+
+       FD_ZERO(&readfds);
+       FD_SET(fd, &readfds);
+
+       memset(&timeout, 0, sizeof(timeout));
+
+       timeout.tv_sec = MTRACE_TIMEOUT;
+
+       tmsec = timeout.tv_sec * 1000 + timeout.tv_usec / 1000;
+       do {
+               ret = select(fd + 1, &readfds, NULL, NULL, &timeout);
+               if (ret <= 0)
+                       return ret;
+               rmsec = timeout.tv_sec * 1000 + timeout.tv_usec / 1000;
+               msec = tmsec - rmsec;
+       } while (recv_response(fd, msec, hops) != 0);
+
+       return ret;
+}
+
+int main(int argc, char *const argv[])
+{
+       struct in_addr mc_source;
+       struct in_addr iface_addr;
+       struct in_addr gw_addr;
+       struct in_addr mtrace_addr;
+       struct igmp_mtrace mtrace;
+       int hops = 255;
+       int rhops;
+       int maxhops = 255;
+       int perhop = 3;
+       int ifindex;
+       int unicast = 1;
+       int ttl = 64;
+       int fd = -1;
+       int ret = -1;
+       int c;
+       int i, j;
+       char ifname[IF_NAMESIZE];
+       char ip_str[INET_ADDRSTRLEN];
+
+       mtrace_addr.s_addr = inet_addr("224.0.1.32");
+
+       uid_t uid = getuid();
+
+       if (uid != 0) {
+               printf("must run as root\n");
+               exit(EXIT_FAILURE);
+       }
+
+       if (argc <= 0)
+               progname = "mtracebis";
+       else
+               progname = argv[0];
+
+       if (argc != 2) {
+               usage();
+               exit(EXIT_FAILURE);
+       }
+
+       while (1) {
+               static struct option long_options[] = {
+                       {"help", no_argument, 0, 'h'},
+                       {"version", no_argument, 0, 'v'},
+                       {0, 0, 0, 0} };
+               int option_index = 0;
+
+               c = getopt_long(argc, argv, "vh", long_options, &option_index);
+
+               if (c == -1)
+                       break;
+
+               switch (c) {
+               case 'h':
+                       usage();
+                       exit(0);
+               case 'v':
+                       version();
+                       exit(0);
+               default:
+                       usage();
+                       exit(EXIT_FAILURE);
+               }
+       }
+       if (inet_pton(AF_INET, argv[1], &mc_source) != 1) {
+               usage();
+               fprintf(stderr, "%s: %s not a valid IPv4 address\n", argv[0],
+                       argv[1]);
+               exit(EXIT_FAILURE);
+       }
+
+       ifindex = routeget(mc_source, &iface_addr, &gw_addr);
+       if (ifindex < 0) {
+               fprintf(stderr, "%s: failed to get route to source %s\n",
+                       argv[0], argv[1]);
+               exit(EXIT_FAILURE);
+       }
+
+       if (if_indextoname(ifindex, ifname) == NULL) {
+               fprintf(stderr, "%s: if_indextoname error: %s\n", argv[0],
+                       strerror(errno));
+               exit(EXIT_FAILURE);
+       }
+
+       /* zero mtrace struct */
+       memset((char *)&mtrace, 0, sizeof(mtrace));
+
+       /* set up query */
+       mtrace.type = PIM_IGMP_MTRACE_QUERY_REQUEST;
+       mtrace.hops = hops;
+       mtrace.checksum = 0;
+       mtrace.grp_addr.s_addr = 0;
+       mtrace.src_addr = mc_source;
+       mtrace.dst_addr = iface_addr;
+       mtrace.rsp_addr = unicast ? iface_addr : mtrace_addr;
+       mtrace.rsp_ttl = ttl;
+       mtrace.qry_id = 0xffffff & time(NULL);
+
+       mtrace.checksum = in_cksum(&mtrace, sizeof(mtrace));
+
+       fd = socket(AF_INET, SOCK_RAW, IPPROTO_IGMP);
+
+       if (fd < 1) {
+               fprintf(stderr, "%s: socket error: %s\n", argv[0],
+                       strerror(errno));
+               exit(EXIT_FAILURE);
+       }
+
+       ret = setsockopt(fd, SOL_SOCKET, SO_BINDTODEVICE, ifname,
+                        strlen(ifname));
+
+       if (ret < 0) {
+               fprintf(stderr, "%s: setsockopt error: %s\n", argv[0],
+                       strerror(errno));
+               ret = EXIT_FAILURE;
+               goto close_fd;
+       }
+
+       print_query(&mtrace);
+       if (send_query(fd, gw_addr, &mtrace) < 0) {
+               fprintf(stderr, "%s: sendto error: %s\n", argv[0],
+                       strerror(errno));
+               ret = EXIT_FAILURE;
+               goto close_fd;
+       }
+       printf("Querying full reverse path...\n");
+       ret = wait_for_response(fd, NULL);
+       if (ret > 0) {
+               ret = 0;
+               goto close_fd;
+       }
+       if (ret < 0) {
+               fprintf(stderr, "%s: select error: %s\n", argv[0],
+                       strerror(errno));
+               ret = EXIT_FAILURE;
+               goto close_fd;
+       }
+       printf(" * ");
+       printf("switching to hop-by-hop:\n");
+       printf("%3d  ? (%s)\n", 0,
+              inet_ntop(AF_INET, &mtrace.dst_addr, ip_str, sizeof(ip_str)));
+       for (i = 1; i < maxhops; i++) {
+               printf("%3d ", -i);
+               mtrace.hops = i;
+               for (j = 0; j < perhop; j++) {
+                       mtrace.qry_id++;
+                       mtrace.checksum = 0;
+                       mtrace.checksum = in_cksum(&mtrace, sizeof(mtrace));
+                       if (send_query(fd, gw_addr, &mtrace) < 0) {
+                               fprintf(stderr, "%s: sendto error: %s\n",
+                                       argv[0], strerror(errno));
+                               ret = EXIT_FAILURE;
+                               goto close_fd;
+                       }
+                       ret = wait_for_response(fd, &rhops);
+                       if (ret > 0) {
+                               if (i > rhops) {
+                                       ret = 0;
+                                       goto close_fd;
+                               }
+                               break;
+                       }
+                       printf(" *");
+               }
+               if (ret <= 0)
+                       printf("\n");
+       }
+       ret = 0;
+close_fd:
+       close(fd);
+       exit(ret);
+}
+
+#else /* __linux__ */
+
+#include <stdio.h>
+#include <stdlib.h>
+
+int main(int argc, char *argv[])
+{
+       printf("%s implemented only for GNU/Linux\n", argv[0]);
+       exit(0);
+}
+
+#endif /* __linux__ */
diff --git a/pimd/mtracebis_netlink.c b/pimd/mtracebis_netlink.c
new file mode 100644 (file)
index 0000000..42b80b2
--- /dev/null
@@ -0,0 +1,700 @@
+/*
+ * libnetlink.c        RTnetlink service routines.
+ *
+ *             This program is free software; you can redistribute it and/or
+ *             modify it under the terms of the GNU General Public License
+ *             as published by the Free Software Foundation; either version
+ *             2 of the License, or (at your option) any later version.
+ *
+ * Authors:    Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru>
+ *
+ */
+
+#ifdef __linux__
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <syslog.h>
+#include <fcntl.h>
+#include <net/if_arp.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <string.h>
+#include <errno.h>
+#include <time.h>
+#include <sys/uio.h>
+#include <assert.h>
+
+#include "mtracebis_netlink.h"
+
+int rcvbuf = 1024 * 1024;
+
+void rtnl_close(struct rtnl_handle *rth)
+{
+       if (rth->fd >= 0) {
+               close(rth->fd);
+               rth->fd = -1;
+       }
+}
+
+int rtnl_open_byproto(struct rtnl_handle *rth, unsigned subscriptions,
+                     int protocol)
+{
+       socklen_t addr_len;
+       int sndbuf = 32768;
+
+       memset(rth, 0, sizeof(*rth));
+
+       rth->fd = socket(AF_NETLINK, SOCK_RAW, protocol);
+       if (rth->fd < 0) {
+               perror("Cannot open netlink socket");
+               return -1;
+       }
+
+       if (setsockopt(rth->fd,SOL_SOCKET,SO_SNDBUF,&sndbuf,sizeof(sndbuf)) < 0) {
+               perror("SO_SNDBUF");
+               return -1;
+       }
+
+       if (setsockopt(rth->fd,SOL_SOCKET,SO_RCVBUF,&rcvbuf,sizeof(rcvbuf)) < 0) {
+               perror("SO_RCVBUF");
+               return -1;
+       }
+
+       memset(&rth->local, 0, sizeof(rth->local));
+       rth->local.nl_family = AF_NETLINK;
+       rth->local.nl_groups = subscriptions;
+
+       if (bind(rth->fd, (struct sockaddr*)&rth->local, sizeof(rth->local)) < 0) {
+               perror("Cannot bind netlink socket");
+               return -1;
+       }
+       addr_len = sizeof(rth->local);
+       if (getsockname(rth->fd, (struct sockaddr*)&rth->local, &addr_len) < 0) {
+               perror("Cannot getsockname");
+               return -1;
+       }
+       if (addr_len != sizeof(rth->local)) {
+               fprintf(stderr, "Wrong address length %d\n", addr_len);
+               return -1;
+       }
+       if (rth->local.nl_family != AF_NETLINK) {
+               fprintf(stderr, "Wrong address family %d\n", rth->local.nl_family);
+               return -1;
+       }
+       rth->seq = time(NULL);
+       return 0;
+}
+
+int rtnl_open(struct rtnl_handle *rth, unsigned subscriptions)
+{
+       return rtnl_open_byproto(rth, subscriptions, NETLINK_ROUTE);
+}
+
+int rtnl_wilddump_request(struct rtnl_handle *rth, int family, int type)
+{
+       struct {
+               struct nlmsghdr nlh;
+               struct rtgenmsg g;
+       } req;
+
+       memset(&req, 0, sizeof(req));
+       req.nlh.nlmsg_len = sizeof(req);
+       req.nlh.nlmsg_type = type;
+       req.nlh.nlmsg_flags = NLM_F_ROOT|NLM_F_MATCH|NLM_F_REQUEST;
+       req.nlh.nlmsg_pid = 0;
+       req.nlh.nlmsg_seq = rth->dump = ++rth->seq;
+       req.g.rtgen_family = family;
+
+       return send(rth->fd, (void*)&req, sizeof(req), 0);
+}
+
+int rtnl_send(struct rtnl_handle *rth, const char *buf, int len)
+{
+       return send(rth->fd, buf, len, 0);
+}
+
+int rtnl_send_check(struct rtnl_handle *rth, const char *buf, int len)
+{
+       struct nlmsghdr *h;
+       int status;
+       char resp[1024];
+
+       status = send(rth->fd, buf, len, 0);
+       if (status < 0)
+               return status;
+
+       /* Check for immediate errors */
+       status = recv(rth->fd, resp, sizeof(resp), MSG_DONTWAIT|MSG_PEEK);
+       if (status < 0) {
+               if (errno == EAGAIN)
+                       return 0;
+               return -1;
+       }
+
+       for (h = (struct nlmsghdr *)resp; NLMSG_OK(h, (uint32_t)status);
+            h = NLMSG_NEXT(h, status)) {
+               if (h->nlmsg_type == NLMSG_ERROR) {
+                       struct nlmsgerr *err = (struct nlmsgerr*)NLMSG_DATA(h);
+                       if (h->nlmsg_len < NLMSG_LENGTH(sizeof(struct nlmsgerr)))
+                               fprintf(stderr, "ERROR truncated\n");
+                       else
+                               errno = -err->error;
+                       return -1;
+               }
+       }
+
+       return 0;
+}
+
+int rtnl_dump_request(struct rtnl_handle *rth, int type, void *req, int len)
+{
+       struct nlmsghdr nlh;
+       struct sockaddr_nl nladdr;
+       struct iovec iov[2] = {
+               { .iov_base = &nlh, .iov_len = sizeof(nlh) },
+               { .iov_base = req, .iov_len = len }
+       };
+       struct msghdr msg = {
+               .msg_name = &nladdr,
+               .msg_namelen =  sizeof(nladdr),
+               .msg_iov = iov,
+               .msg_iovlen = 2,
+       };
+
+       memset(&nladdr, 0, sizeof(nladdr));
+       nladdr.nl_family = AF_NETLINK;
+
+       nlh.nlmsg_len = NLMSG_LENGTH(len);
+       nlh.nlmsg_type = type;
+       nlh.nlmsg_flags = NLM_F_ROOT|NLM_F_MATCH|NLM_F_REQUEST;
+       nlh.nlmsg_pid = 0;
+       nlh.nlmsg_seq = rth->dump = ++rth->seq;
+
+       return sendmsg(rth->fd, &msg, 0);
+}
+
+int rtnl_dump_filter_l(struct rtnl_handle *rth,
+                      const struct rtnl_dump_filter_arg *arg)
+{
+       struct sockaddr_nl nladdr;
+       struct iovec iov;
+       struct msghdr msg = {
+               .msg_name = &nladdr,
+               .msg_namelen = sizeof(nladdr),
+               .msg_iov = &iov,
+               .msg_iovlen = 1,
+       };
+       char buf[16384];
+
+       iov.iov_base = buf;
+       while (1) {
+               int status;
+               const struct rtnl_dump_filter_arg *a;
+               int found_done = 0;
+               int msglen = 0;
+
+               iov.iov_len = sizeof(buf);
+               status = recvmsg(rth->fd, &msg, 0);
+
+               if (status < 0) {
+                       if (errno == EINTR || errno == EAGAIN)
+                               continue;
+                       fprintf(stderr, "netlink receive error %s (%d)\n",
+                               strerror(errno), errno);
+                       return -1;
+               }
+
+               if (status == 0) {
+                       fprintf(stderr, "EOF on netlink\n");
+                       return -1;
+               }
+
+               for (a = arg; a->filter; a++) {
+                       struct nlmsghdr *h = (struct nlmsghdr*)buf;
+                       msglen = status;
+
+                       while (NLMSG_OK(h, (uint32_t)msglen)) {
+                               int err;
+
+                               if (nladdr.nl_pid != 0 ||
+                                   h->nlmsg_pid != rth->local.nl_pid ||
+                                   h->nlmsg_seq != rth->dump) {
+                                       if (a->junk) {
+                                               err = a->junk(&nladdr, h,
+                                                             a->arg2);
+                                               if (err < 0)
+                                                       return err;
+                                       }
+                                       goto skip_it;
+                               }
+
+                               if (h->nlmsg_type == NLMSG_DONE) {
+                                       found_done = 1;
+                                       break; /* process next filter */
+                               }
+                               if (h->nlmsg_type == NLMSG_ERROR) {
+                                       struct nlmsgerr *err = (struct nlmsgerr*)NLMSG_DATA(h);
+                                       if (h->nlmsg_len < NLMSG_LENGTH(sizeof(struct nlmsgerr))) {
+                                               fprintf(stderr,
+                                                       "ERROR truncated\n");
+                                       } else {
+                                               errno = -err->error;
+                                               perror("RTNETLINK answers");
+                                       }
+                                       return -1;
+                               }
+                               err = a->filter(&nladdr, h, a->arg1);
+                               if (err < 0)
+                                       return err;
+
+skip_it:
+                               h = NLMSG_NEXT(h, msglen);
+                       }
+               }
+
+               if (found_done)
+                       return 0;
+
+               if (msg.msg_flags & MSG_TRUNC) {
+                       fprintf(stderr, "Message truncated\n");
+                       continue;
+               }
+               if (msglen) {
+                       fprintf(stderr, "!!!Remnant of size %d\n", msglen);
+                       exit(1);
+               }
+       }
+}
+
+int rtnl_dump_filter(struct rtnl_handle *rth,
+                    rtnl_filter_t filter,
+                    void *arg1,
+                    rtnl_filter_t junk,
+                    void *arg2)
+{
+       const struct rtnl_dump_filter_arg a[2] = {
+               { .filter = filter, .arg1 = arg1, .junk = junk, .arg2 = arg2 },
+               { .filter = NULL,   .arg1 = NULL, .junk = NULL, .arg2 = NULL }
+       };
+
+       return rtnl_dump_filter_l(rth, a);
+}
+
+int rtnl_talk(struct rtnl_handle *rtnl, struct nlmsghdr *n, pid_t peer,
+             unsigned groups, struct nlmsghdr *answer,
+             rtnl_filter_t junk,
+             void *jarg)
+{
+       int status;
+       unsigned seq;
+       struct nlmsghdr *h;
+       struct sockaddr_nl nladdr;
+       struct iovec iov = {
+               .iov_base = (void*) n,
+               .iov_len = n->nlmsg_len
+       };
+       struct msghdr msg = {
+               .msg_name = &nladdr,
+               .msg_namelen = sizeof(nladdr),
+               .msg_iov = &iov,
+               .msg_iovlen = 1,
+       };
+       char   buf[16384];
+
+       memset(&nladdr, 0, sizeof(nladdr));
+       nladdr.nl_family = AF_NETLINK;
+       nladdr.nl_pid = peer;
+       nladdr.nl_groups = groups;
+
+       n->nlmsg_seq = seq = ++rtnl->seq;
+
+       if (answer == NULL)
+               n->nlmsg_flags |= NLM_F_ACK;
+
+       status = sendmsg(rtnl->fd, &msg, 0);
+
+       if (status < 0) {
+               perror("Cannot talk to rtnetlink");
+               return -1;
+       }
+
+       memset(buf,0,sizeof(buf));
+
+       iov.iov_base = buf;
+
+       while (1) {
+               iov.iov_len = sizeof(buf);
+               status = recvmsg(rtnl->fd, &msg, 0);
+
+               if (status < 0) {
+                       if (errno == EINTR || errno == EAGAIN)
+                               continue;
+                       fprintf(stderr, "netlink receive error %s (%d)\n",
+                               strerror(errno), errno);
+                       return -1;
+               }
+               if (status == 0) {
+                       fprintf(stderr, "EOF on netlink\n");
+                       return -1;
+               }
+               if (msg.msg_namelen != sizeof(nladdr)) {
+                       fprintf(stderr, "sender address length == %d\n", msg.msg_namelen);
+                       exit(1);
+               }
+               for (h = (struct nlmsghdr*)buf; status >= (int)sizeof(*h); ) {
+                       int err;
+                       int len = h->nlmsg_len;
+                       int l = len - sizeof(*h);
+
+                       if (l<0 || len>status) {
+                               if (msg.msg_flags & MSG_TRUNC) {
+                                       fprintf(stderr, "Truncated message\n");
+                                       return -1;
+                               }
+                               fprintf(stderr, "!!!malformed message: len=%d\n", len);
+                               exit(1);
+                       }
+
+                       if ((int)nladdr.nl_pid != peer ||
+                           h->nlmsg_pid != rtnl->local.nl_pid ||
+                           h->nlmsg_seq != seq) {
+                               if (junk) {
+                                       err = junk(&nladdr, h, jarg);
+                                       if (err < 0)
+                                               return err;
+                               }
+                               /* Don't forget to skip that message. */
+                               status -= NLMSG_ALIGN(len);
+                               h = (struct nlmsghdr*)((char*)h + NLMSG_ALIGN(len));
+                               continue;
+                       }
+
+                       if (h->nlmsg_type == NLMSG_ERROR) {
+                               struct nlmsgerr *err = (struct nlmsgerr*)NLMSG_DATA(h);
+                               if (l < (int)sizeof(struct nlmsgerr)) {
+                                       fprintf(stderr, "ERROR truncated\n");
+                               } else {
+                                       errno = -err->error;
+                                       if (errno == 0) {
+                                               if (answer)
+                                                       memcpy(answer, h, h->nlmsg_len);
+                                               return 0;
+                                       }
+                                       perror("RTNETLINK answers");
+                               }
+                               return -1;
+                       }
+                       if (answer) {
+                               memcpy(answer, h, h->nlmsg_len);
+                               return 0;
+                       }
+
+                       fprintf(stderr, "Unexpected reply!!!\n");
+
+                       status -= NLMSG_ALIGN(len);
+                       h = (struct nlmsghdr*)((char*)h + NLMSG_ALIGN(len));
+               }
+               if (msg.msg_flags & MSG_TRUNC) {
+                       fprintf(stderr, "Message truncated\n");
+                       continue;
+               }
+               if (status) {
+                       fprintf(stderr, "!!!Remnant of size %d\n", status);
+                       exit(1);
+               }
+       }
+}
+
+int rtnl_listen(struct rtnl_handle *rtnl,
+               rtnl_filter_t handler,
+               void *jarg)
+{
+       int status;
+       struct nlmsghdr *h;
+       struct sockaddr_nl nladdr;
+       struct iovec iov;
+       struct msghdr msg = {
+               .msg_name = &nladdr,
+               .msg_namelen = sizeof(nladdr),
+               .msg_iov = &iov,
+               .msg_iovlen = 1,
+       };
+       char   buf[8192];
+
+       memset(&nladdr, 0, sizeof(nladdr));
+       nladdr.nl_family = AF_NETLINK;
+       nladdr.nl_pid = 0;
+       nladdr.nl_groups = 0;
+
+       iov.iov_base = buf;
+       while (1) {
+               iov.iov_len = sizeof(buf);
+               status = recvmsg(rtnl->fd, &msg, 0);
+
+               if (status < 0) {
+                       if (errno == EINTR || errno == EAGAIN)
+                               continue;
+                       fprintf(stderr, "netlink receive error %s (%d)\n",
+                               strerror(errno), errno);
+                       if (errno == ENOBUFS)
+                               continue;
+                       return -1;
+               }
+               if (status == 0) {
+                       fprintf(stderr, "EOF on netlink\n");
+                       return -1;
+               }
+               if (msg.msg_namelen != sizeof(nladdr)) {
+                       fprintf(stderr, "Sender address length == %d\n", msg.msg_namelen);
+                       exit(1);
+               }
+               for (h =(struct nlmsghdr*)buf; status >= (int)sizeof(*h); ) {
+                       int err;
+                       int len = h->nlmsg_len;
+                       int l = len - sizeof(*h);
+
+                       if (l<0 || len>status) {
+                               if (msg.msg_flags & MSG_TRUNC) {
+                                       fprintf(stderr, "Truncated message\n");
+                                       return -1;
+                               }
+                               fprintf(stderr, "!!!malformed message: len=%d\n", len);
+                               exit(1);
+                       }
+
+                       err = handler(&nladdr, h, jarg);
+                       if (err < 0)
+                               return err;
+
+                       status -= NLMSG_ALIGN(len);
+                       h = (struct nlmsghdr*)((char*)h + NLMSG_ALIGN(len));
+               }
+               if (msg.msg_flags & MSG_TRUNC) {
+                       fprintf(stderr, "Message truncated\n");
+                       continue;
+               }
+               if (status) {
+                       fprintf(stderr, "!!!Remnant of size %d\n", status);
+                       exit(1);
+               }
+       }
+}
+
+int rtnl_from_file(FILE *rtnl, rtnl_filter_t handler,
+                  void *jarg)
+{
+       int status;
+       struct sockaddr_nl nladdr;
+       char   buf[8192];
+       struct nlmsghdr *h = (void*)buf;
+
+       memset(&nladdr, 0, sizeof(nladdr));
+       nladdr.nl_family = AF_NETLINK;
+       nladdr.nl_pid = 0;
+       nladdr.nl_groups = 0;
+
+       while (1) {
+               int err, len;
+               int l;
+
+               status = fread(&buf, 1, sizeof(*h), rtnl);
+
+               if (status < 0) {
+                       if (errno == EINTR)
+                               continue;
+                       perror("rtnl_from_file: fread");
+                       return -1;
+               }
+               if (status == 0)
+                       return 0;
+
+               len = h->nlmsg_len;
+               l = len - sizeof(*h);
+
+               if (l<0 || len>(int)sizeof(buf)) {
+                       fprintf(stderr, "!!!malformed message: len=%d @%lu\n",
+                               len, ftell(rtnl));
+                       return -1;
+               }
+
+               status = fread(NLMSG_DATA(h), 1, NLMSG_ALIGN(l), rtnl);
+
+               if (status < 0) {
+                       perror("rtnl_from_file: fread");
+                       return -1;
+               }
+               if (status < l) {
+                       fprintf(stderr, "rtnl-from_file: truncated message\n");
+                       return -1;
+               }
+
+               err = handler(&nladdr, h, jarg);
+               if (err < 0)
+                       return err;
+       }
+}
+
+int addattr32(struct nlmsghdr *n, int maxlen, int type, __u32 data)
+{
+       int len = RTA_LENGTH(4);
+       struct rtattr *rta;
+       if ((int)(NLMSG_ALIGN(n->nlmsg_len) + len) > maxlen) {
+               fprintf(stderr,"addattr32: Error! max allowed bound %d exceeded\n",maxlen);
+               return -1;
+       }
+       rta = NLMSG_TAIL(n);
+       rta->rta_type = type;
+       rta->rta_len = len;
+       memcpy(RTA_DATA(rta), &data, 4);
+       n->nlmsg_len = NLMSG_ALIGN(n->nlmsg_len) + len;
+       return 0;
+}
+
+int addattr_l(struct nlmsghdr *n, int maxlen, int type, const void *data,
+             int alen)
+{
+       int len = RTA_LENGTH(alen);
+       struct rtattr *rta;
+
+       if ((int)(NLMSG_ALIGN(n->nlmsg_len) + RTA_ALIGN(len)) > maxlen) {
+               fprintf(stderr, "addattr_l ERROR: message exceeded bound of %d\n",maxlen);
+               return -1;
+       }
+       rta = NLMSG_TAIL(n);
+       rta->rta_type = type;
+       rta->rta_len = len;
+
+       if (data)
+               memcpy(RTA_DATA(rta), data, alen);
+       else
+               assert(alen == 0);
+
+       n->nlmsg_len = NLMSG_ALIGN(n->nlmsg_len) + RTA_ALIGN(len);
+       return 0;
+}
+
+int addraw_l(struct nlmsghdr *n, int maxlen, const void *data, int len)
+{
+       if ((int)(NLMSG_ALIGN(n->nlmsg_len) + NLMSG_ALIGN(len)) > maxlen) {
+               fprintf(stderr, "addraw_l ERROR: message exceeded bound of %d\n",maxlen);
+               return -1;
+       }
+
+       memcpy(NLMSG_TAIL(n), data, len);
+       memset((uint8_t *) NLMSG_TAIL(n) + len, 0, NLMSG_ALIGN(len) - len);
+       n->nlmsg_len = NLMSG_ALIGN(n->nlmsg_len) + NLMSG_ALIGN(len);
+       return 0;
+}
+
+struct rtattr *addattr_nest(struct nlmsghdr *n, int maxlen, int type)
+{
+       struct rtattr *nest = NLMSG_TAIL(n);
+
+       addattr_l(n, maxlen, type, NULL, 0);
+       return nest;
+}
+
+int addattr_nest_end(struct nlmsghdr *n, struct rtattr *nest)
+{
+       nest->rta_len = (uint8_t *)NLMSG_TAIL(n) - (uint8_t *)nest;
+       return n->nlmsg_len;
+}
+
+struct rtattr *addattr_nest_compat(struct nlmsghdr *n, int maxlen, int type,
+                                  const void *data, int len)
+{
+       struct rtattr *start = NLMSG_TAIL(n);
+
+       addattr_l(n, maxlen, type, data, len);
+       addattr_nest(n, maxlen, type);
+       return start;
+}
+
+int addattr_nest_compat_end(struct nlmsghdr *n, struct rtattr *start)
+{
+       struct rtattr *nest = start + NLMSG_ALIGN(start->rta_len);
+
+       start->rta_len = (uint8_t *)NLMSG_TAIL(n) - (uint8_t *)start;
+       addattr_nest_end(n, nest);
+       return n->nlmsg_len;
+}
+
+int rta_addattr32(struct rtattr *rta, int maxlen, int type, __u32 data)
+{
+       int len = RTA_LENGTH(4);
+       struct rtattr *subrta;
+
+       if ((int)(RTA_ALIGN(rta->rta_len) + len) > maxlen) {
+               fprintf(stderr,"rta_addattr32: Error! max allowed bound %d exceeded\n",maxlen);
+               return -1;
+       }
+       subrta = (struct rtattr*)(((char*)rta) + RTA_ALIGN(rta->rta_len));
+       subrta->rta_type = type;
+       subrta->rta_len = len;
+       memcpy(RTA_DATA(subrta), &data, 4);
+       rta->rta_len = NLMSG_ALIGN(rta->rta_len) + len;
+       return 0;
+}
+
+int rta_addattr_l(struct rtattr *rta, int maxlen, int type,
+                 const void *data, int alen)
+{
+       struct rtattr *subrta;
+       int len = RTA_LENGTH(alen);
+
+       if ((int)(RTA_ALIGN(rta->rta_len) + RTA_ALIGN(len)) > maxlen) {
+               fprintf(stderr,"rta_addattr_l: Error! max allowed bound %d exceeded\n",maxlen);
+               return -1;
+       }
+       subrta = (struct rtattr*)(((char*)rta) + RTA_ALIGN(rta->rta_len));
+       subrta->rta_type = type;
+       subrta->rta_len = len;
+       memcpy(RTA_DATA(subrta), data, alen);
+       rta->rta_len = NLMSG_ALIGN(rta->rta_len) + RTA_ALIGN(len);
+       return 0;
+}
+
+int parse_rtattr(struct rtattr *tb[], int max, struct rtattr *rta, int len)
+{
+       memset(tb, 0, sizeof(struct rtattr *) * (max + 1));
+       while (RTA_OK(rta, len)) {
+               if ((rta->rta_type <= max) && (!tb[rta->rta_type]))
+                       tb[rta->rta_type] = rta;
+               rta = RTA_NEXT(rta,len);
+       }
+       if (len)
+               fprintf(stderr, "!!!Deficit %d, rta_len=%d\n", len, rta->rta_len);
+       return 0;
+}
+
+int parse_rtattr_byindex(struct rtattr *tb[], int max, struct rtattr *rta, int len)
+{
+       int i = 0;
+
+       memset(tb, 0, sizeof(struct rtattr *) * max);
+       while (RTA_OK(rta, len)) {
+               if (rta->rta_type <= max && i < max)
+                       tb[i++] = rta;
+               rta = RTA_NEXT(rta,len);
+       }
+       if (len)
+               fprintf(stderr, "!!!Deficit %d, rta_len=%d\n", len, rta->rta_len);
+       return i;
+}
+
+int __parse_rtattr_nested_compat(struct rtattr *tb[], int max, struct rtattr *rta,
+                                int len)
+{
+       if ((int)RTA_PAYLOAD(rta) < len)
+               return -1;
+       if (RTA_PAYLOAD(rta) >= RTA_ALIGN(len) + sizeof(struct rtattr)) {
+               rta = (struct rtattr *)(uint8_t *)RTA_DATA(rta)+RTA_ALIGN(len);
+               return parse_rtattr_nested(tb, max, rta);
+       }
+       memset(tb, 0, sizeof(struct rtattr *) * (max + 1));
+       return 0;
+}
+
+#endif /* __linux__ */
diff --git a/pimd/mtracebis_netlink.h b/pimd/mtracebis_netlink.h
new file mode 100644 (file)
index 0000000..7a60ead
--- /dev/null
@@ -0,0 +1,130 @@
+/*
+ * libnetlink.c        RTnetlink service routines.
+ *
+ *             This program is free software; you can redistribute it and/or
+ *             modify it under the terms of the GNU General Public License
+ *             as published by the Free Software Foundation; either version
+ *             2 of the License, or (at your option) any later version.
+ *
+ * Authors:    Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru>
+ *
+ */
+
+#ifdef __linux__
+
+#ifndef __LIBNETLINK_H__
+#define __LIBNETLINK_H__ 1
+
+#include <asm/types.h>
+#include <linux/netlink.h>
+#include <linux/rtnetlink.h>
+#include <linux/if_link.h>
+#include <linux/if_addr.h>
+#include <linux/neighbour.h>
+
+struct rtnl_handle
+{
+       int                     fd;
+       struct sockaddr_nl      local;
+       struct sockaddr_nl      peer;
+       __u32                   seq;
+       __u32                   dump;
+};
+
+extern int rcvbuf;
+
+extern int rtnl_open(struct rtnl_handle *rth, unsigned subscriptions);
+extern int rtnl_open_byproto(struct rtnl_handle *rth, unsigned subscriptions, int protocol);
+extern void rtnl_close(struct rtnl_handle *rth);
+extern int rtnl_wilddump_request(struct rtnl_handle *rth, int fam, int type);
+extern int rtnl_dump_request(struct rtnl_handle *rth, int type, void *req, int len);
+
+typedef int (*rtnl_filter_t)(const struct sockaddr_nl *,
+                            struct nlmsghdr *n, void *);
+
+struct rtnl_dump_filter_arg
+{
+       rtnl_filter_t filter;
+       void *arg1;
+       rtnl_filter_t junk;
+       void *arg2;
+};
+
+extern int rtnl_dump_filter_l(struct rtnl_handle *rth,
+                             const struct rtnl_dump_filter_arg *arg);
+extern int rtnl_dump_filter(struct rtnl_handle *rth, rtnl_filter_t filter,
+                           void *arg1,
+                           rtnl_filter_t junk,
+                           void *arg2);
+
+extern int rtnl_talk(struct rtnl_handle *rtnl, struct nlmsghdr *n, pid_t peer,
+                    unsigned groups, struct nlmsghdr *answer,
+                    rtnl_filter_t junk,
+                    void *jarg);
+extern int rtnl_send(struct rtnl_handle *rth, const char *buf, int);
+extern int rtnl_send_check(struct rtnl_handle *rth, const char *buf, int);
+
+extern int addattr32(struct nlmsghdr *n, int maxlen, int type, __u32 data);
+extern int addattr_l(struct nlmsghdr *n, int maxlen, int type, const void *data, int alen);
+extern int addraw_l(struct nlmsghdr *n, int maxlen, const void *data, int len);
+extern struct rtattr *addattr_nest(struct nlmsghdr *n, int maxlen, int type);
+extern int addattr_nest_end(struct nlmsghdr *n, struct rtattr *nest);
+extern struct rtattr *addattr_nest_compat(struct nlmsghdr *n, int maxlen, int type, const void *data, int len);
+extern int addattr_nest_compat_end(struct nlmsghdr *n, struct rtattr *nest);
+extern int rta_addattr32(struct rtattr *rta, int maxlen, int type, __u32 data);
+extern int rta_addattr_l(struct rtattr *rta, int maxlen, int type, const void *data, int alen);
+
+extern int parse_rtattr(struct rtattr *tb[], int max, struct rtattr *rta, int len);
+extern int parse_rtattr_byindex(struct rtattr *tb[], int max, struct rtattr *rta, int len);
+extern int __parse_rtattr_nested_compat(struct rtattr *tb[], int max, struct rtattr *rta, int len);
+
+#define parse_rtattr_nested(tb, max, rta) \
+       (parse_rtattr((tb), (max), RTA_DATA(rta), RTA_PAYLOAD(rta)))
+
+#define parse_rtattr_nested_compat(tb, max, rta, data, len) \
+({     data = RTA_PAYLOAD(rta) >= len ? RTA_DATA(rta) : NULL; \
+       __parse_rtattr_nested_compat(tb, max, rta, len); })
+
+extern int rtnl_listen(struct rtnl_handle *, rtnl_filter_t handler,
+                      void *jarg);
+extern int rtnl_from_file(FILE *, rtnl_filter_t handler,
+                      void *jarg);
+
+#define NLMSG_TAIL(nmsg) \
+       ((struct rtattr *) (((uint8_t *) (nmsg)) + NLMSG_ALIGN((nmsg)->nlmsg_len)))
+
+#ifndef IFA_RTA
+#define IFA_RTA(r) \
+       ((struct rtattr*)(((char*)(r)) + NLMSG_ALIGN(sizeof(struct ifaddrmsg))))
+#endif
+#ifndef IFA_PAYLOAD
+#define IFA_PAYLOAD(n) NLMSG_PAYLOAD(n,sizeof(struct ifaddrmsg))
+#endif
+
+#ifndef IFLA_RTA
+#define IFLA_RTA(r) \
+       ((struct rtattr*)(((char*)(r)) + NLMSG_ALIGN(sizeof(struct ifinfomsg))))
+#endif
+#ifndef IFLA_PAYLOAD
+#define IFLA_PAYLOAD(n)        NLMSG_PAYLOAD(n,sizeof(struct ifinfomsg))
+#endif
+
+#ifndef NDA_RTA
+#define NDA_RTA(r) \
+       ((struct rtattr*)(((char*)(r)) + NLMSG_ALIGN(sizeof(struct ndmsg))))
+#endif
+#ifndef NDA_PAYLOAD
+#define NDA_PAYLOAD(n) NLMSG_PAYLOAD(n,sizeof(struct ndmsg))
+#endif
+
+#ifndef NDTA_RTA
+#define NDTA_RTA(r) \
+       ((struct rtattr*)(((char*)(r)) + NLMSG_ALIGN(sizeof(struct ndtmsg))))
+#endif
+#ifndef NDTA_PAYLOAD
+#define NDTA_PAYLOAD(n) NLMSG_PAYLOAD(n,sizeof(struct ndtmsg))
+#endif
+
+#endif /* __LIBNETLINK_H__ */
+
+#endif /* __linux__ */
diff --git a/pimd/mtracebis_routeget.c b/pimd/mtracebis_routeget.c
new file mode 100644 (file)
index 0000000..d75aaa3
--- /dev/null
@@ -0,0 +1,98 @@
+/*
+ * Multicast Traceroute for FRRouting
+ * Copyright (C) 2018  Mladen Sablic
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; see the file COPYING; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifdef __linux__
+
+#include <asm/types.h>
+#include <linux/netlink.h>
+#include <linux/rtnetlink.h>
+#include <sys/types.h>
+#include <stdio.h>
+#include <arpa/inet.h>
+#include <string.h>
+
+#include "mtracebis_netlink.h"
+#include "mtracebis_routeget.h"
+
+static int find_dst(struct nlmsghdr *n, struct in_addr *src, struct in_addr *gw)
+{
+       struct rtmsg *r = NLMSG_DATA(n);
+       int len = n->nlmsg_len;
+       struct rtattr *tb[RTA_MAX + 1];
+
+       len -= NLMSG_LENGTH(sizeof(*r));
+       if (len < 0) {
+               fprintf(stderr, "BUG: wrong nlmsg len %d\n", len);
+               return -1;
+       }
+
+       parse_rtattr(tb, RTA_MAX, RTM_RTA(r), len);
+       if (tb[RTA_PREFSRC])
+               src->s_addr = *(uint32_t *)RTA_DATA(tb[RTA_PREFSRC]);
+       if (tb[RTA_GATEWAY])
+               gw->s_addr = *(uint32_t *)RTA_DATA(tb[RTA_GATEWAY]);
+       if (tb[RTA_OIF])
+               return *(int *)RTA_DATA(tb[RTA_OIF]);
+       return 0;
+}
+
+int routeget(struct in_addr dst, struct in_addr *src, struct in_addr *gw)
+{
+       struct {
+               struct nlmsghdr n;
+               struct rtmsg r;
+               char buf[1024];
+       } req;
+       int ret;
+       struct rtnl_handle rth = {.fd = -1};
+
+       memset(&req, 0, sizeof(req));
+
+       req.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct rtmsg));
+       req.n.nlmsg_flags = NLM_F_REQUEST;
+       req.n.nlmsg_type = RTM_GETROUTE;
+       req.r.rtm_family = AF_INET;
+       req.r.rtm_table = 0;
+       req.r.rtm_protocol = 0;
+       req.r.rtm_scope = 0;
+       req.r.rtm_type = 0;
+       req.r.rtm_src_len = 0;
+       req.r.rtm_dst_len = 0;
+       req.r.rtm_tos = 0;
+
+       addattr_l(&req.n, sizeof(req), RTA_DST, &dst.s_addr, 4);
+       req.r.rtm_dst_len = 32;
+
+       ret = rtnl_open(&rth, 0);
+
+       if (ret < 0)
+               return ret;
+
+       if (rtnl_talk(&rth, &req.n, 0, 0, &req.n, NULL, NULL) < 0) {
+               ret = -1;
+               goto close_rth;
+       }
+
+       ret = find_dst(&req.n, src, gw);
+close_rth:
+       rtnl_close(&rth);
+       return ret;
+}
+
+#endif /* __linux__ */
diff --git a/pimd/mtracebis_routeget.h b/pimd/mtracebis_routeget.h
new file mode 100644 (file)
index 0000000..18ecf6e
--- /dev/null
@@ -0,0 +1,31 @@
+/*
+ * Multicast Traceroute for FRRouting
+ * Copyright (C) 2018  Mladen Sablic
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; see the file COPYING; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifdef __linux__
+
+#ifndef ROUTEGET_H
+#define ROUTEGET_H
+
+#include <netinet/in.h>
+
+int routeget(struct in_addr dst, struct in_addr *src, struct in_addr *gw);
+
+#endif /* ROUTEGET */
+
+#endif /* __linux__ */
index 11aeeddf93ae96e96eb72d6883f75ffad51d3e31..fc07b706a9797ce9560f8f95325f11ce484f2044 100644 (file)
@@ -7311,6 +7311,27 @@ DEFUN (no_debug_msdp_packets,
 ALIAS(no_debug_msdp_packets, undebug_msdp_packets_cmd, "undebug msdp packets",
       UNDEBUG_STR DEBUG_MSDP_STR DEBUG_MSDP_PACKETS_STR)
 
+DEFUN (debug_mtrace,
+       debug_mtrace_cmd,
+       "debug mtrace",
+       DEBUG_STR
+       DEBUG_MTRACE_STR)
+{
+       PIM_DO_DEBUG_MTRACE;
+       return CMD_SUCCESS;
+}
+
+DEFUN (no_debug_mtrace,
+       no_debug_mtrace_cmd,
+       "no debug mtrace",
+       NO_STR
+       DEBUG_STR
+       DEBUG_MTRACE_STR)
+{
+       PIM_DONT_DEBUG_MTRACE;
+       return CMD_SUCCESS;
+}
+
 DEFUN_NOSH (show_debugging_pim,
            show_debugging_pim_cmd,
            "show debugging [pim]",
@@ -8721,6 +8742,8 @@ void pim_cmd_init(void)
        install_element(ENABLE_NODE, &debug_msdp_packets_cmd);
        install_element(ENABLE_NODE, &no_debug_msdp_packets_cmd);
        install_element(ENABLE_NODE, &undebug_msdp_packets_cmd);
+       install_element(ENABLE_NODE, &debug_mtrace_cmd);
+       install_element(ENABLE_NODE, &no_debug_mtrace_cmd);
 
        install_element(CONFIG_NODE, &debug_igmp_cmd);
        install_element(CONFIG_NODE, &no_debug_igmp_cmd);
@@ -8763,6 +8786,8 @@ void pim_cmd_init(void)
        install_element(CONFIG_NODE, &debug_msdp_packets_cmd);
        install_element(CONFIG_NODE, &no_debug_msdp_packets_cmd);
        install_element(CONFIG_NODE, &undebug_msdp_packets_cmd);
+       install_element(CONFIG_NODE, &debug_mtrace_cmd);
+       install_element(CONFIG_NODE, &no_debug_mtrace_cmd);
 
        install_element(CONFIG_NODE, &ip_msdp_mesh_group_member_cmd);
        install_element(VRF_NODE, &ip_msdp_mesh_group_member_cmd);
index 8867514876a8283a6d3622a8a40192d473e7a563..b58da30bdd64847fb9d873d783d34c8e5db86da0 100644 (file)
@@ -63,6 +63,7 @@
 #define DEBUG_MSDP_EVENTS_STR                       "MSDP protocol events\n"
 #define DEBUG_MSDP_INTERNAL_STR                     "MSDP protocol internal\n"
 #define DEBUG_MSDP_PACKETS_STR                      "MSDP protocol packets\n"
+#define DEBUG_MTRACE_STR                            "Mtrace protocol activity\n"
 
 void pim_cmd_init(void);
 
index 7524119e52ad92931e96ebf7cf23d22d9e65e16a..05224203649baeba21900ea0432e2e92f7025e9e 100644 (file)
@@ -29,6 +29,7 @@
 #include "pim_igmp.h"
 #include "pim_igmpv2.h"
 #include "pim_igmpv3.h"
+#include "pim_igmp_mtrace.h"
 #include "pim_iface.h"
 #include "pim_sock.h"
 #include "pim_mroute.h"
@@ -504,6 +505,16 @@ int pim_igmp_packet(struct igmp_sock *igmp, char *buf, size_t len)
        case PIM_IGMP_V2_LEAVE_GROUP:
                return igmp_v2_recv_leave(igmp, ip_hdr->ip_src, from_str,
                                          igmp_msg, igmp_msg_len);
+
+       case PIM_IGMP_MTRACE_RESPONSE:
+               return igmp_mtrace_recv_response(igmp, ip_hdr, ip_hdr->ip_src,
+                                                from_str, igmp_msg,
+                                                igmp_msg_len);
+               break;
+       case PIM_IGMP_MTRACE_QUERY_REQUEST:
+               return igmp_mtrace_recv_qry_req(igmp, ip_hdr, ip_hdr->ip_src,
+                                               from_str, igmp_msg,
+                                               igmp_msg_len);
        }
 
        zlog_warn("Ignoring unsupported IGMP message type: %d", msg_type);
index 275f25f63f21817a3a3b910c7ae9f954fa672456..962c50e76a66eb0f2d75d84d5c6f36d001d45762 100644 (file)
@@ -37,6 +37,8 @@
 #define PIM_IGMP_V1_MEMBERSHIP_REPORT (0x12)
 #define PIM_IGMP_V2_MEMBERSHIP_REPORT (0x16)
 #define PIM_IGMP_V2_LEAVE_GROUP       (0x17)
+#define PIM_IGMP_MTRACE_RESPONSE      (0x1E)
+#define PIM_IGMP_MTRACE_QUERY_REQUEST (0x1F)
 #define PIM_IGMP_V3_MEMBERSHIP_REPORT (0x22)
 
 #define IGMP_V3_REPORT_HEADER_SIZE    (8)
diff --git a/pimd/pim_igmp_mtrace.c b/pimd/pim_igmp_mtrace.c
new file mode 100644 (file)
index 0000000..629e10c
--- /dev/null
@@ -0,0 +1,727 @@
+/*
+ * Multicast traceroute for FRRouting
+ * Copyright (C) 2017  Mladen Sablic
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; see the file COPYING; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <zebra.h>
+
+#include "pimd.h"
+#include "pim_util.h"
+#include "pim_sock.h"
+#include "pim_rp.h"
+#include "pim_oil.h"
+#include "pim_ifchannel.h"
+#include "pim_macro.h"
+#include "pim_igmp_mtrace.h"
+
+static void mtrace_rsp_init(struct igmp_mtrace_rsp *mtrace_rspp)
+{
+       mtrace_rspp->arrival = 0;
+       mtrace_rspp->incoming.s_addr = 0;
+       mtrace_rspp->outgoing.s_addr = 0;
+       mtrace_rspp->prev_hop.s_addr = 0;
+       mtrace_rspp->in_count = MTRACE_UNKNOWN_COUNT;
+       mtrace_rspp->out_count = MTRACE_UNKNOWN_COUNT;
+       mtrace_rspp->total = MTRACE_UNKNOWN_COUNT;
+       mtrace_rspp->rtg_proto = 0;
+       mtrace_rspp->fwd_ttl = 0;
+       mtrace_rspp->mbz = 0;
+       mtrace_rspp->s = 0;
+       mtrace_rspp->src_mask = 0;
+       mtrace_rspp->fwd_code = MTRACE_FWD_CODE_NO_ERROR;
+}
+
+static void mtrace_rsp_debug(uint32_t qry_id, int rsp,
+                            struct igmp_mtrace_rsp *mrspp)
+{
+       char inc_str[INET_ADDRSTRLEN];
+       char out_str[INET_ADDRSTRLEN];
+       char prv_str[INET_ADDRSTRLEN];
+
+       zlog_debug(
+               "Rx mt(%d) qid=%ud arr=%x in=%s out=%s prev=%s proto=%d fwd=%d",
+               rsp, ntohl(qry_id), mrspp->arrival,
+               inet_ntop(AF_INET, &(mrspp->incoming), inc_str,
+                         sizeof(inc_str)),
+               inet_ntop(AF_INET, &(mrspp->outgoing), out_str,
+                         sizeof(out_str)),
+               inet_ntop(AF_INET, &(mrspp->prev_hop), prv_str,
+                         sizeof(prv_str)),
+               mrspp->rtg_proto, mrspp->fwd_code);
+}
+
+static void mtrace_debug(struct pim_interface *pim_ifp,
+                        struct igmp_mtrace *mtracep, int mtrace_len)
+{
+       char inc_str[INET_ADDRSTRLEN];
+       char grp_str[INET_ADDRSTRLEN];
+       char src_str[INET_ADDRSTRLEN];
+       char dst_str[INET_ADDRSTRLEN];
+       char rsp_str[INET_ADDRSTRLEN];
+
+       zlog_debug(
+               "Rx mtrace packet incoming on %s: "
+               "hops=%d type=%d size=%d, grp=%s, src=%s,"
+               " dst=%s rsp=%s ttl=%d qid=%ud",
+               inet_ntop(AF_INET, &(pim_ifp->primary_address), inc_str,
+                         sizeof(inc_str)),
+               mtracep->hops, mtracep->type, mtrace_len,
+               inet_ntop(AF_INET, &(mtracep->grp_addr), grp_str,
+                         sizeof(grp_str)),
+               inet_ntop(AF_INET, &(mtracep->src_addr), src_str,
+                         sizeof(src_str)),
+               inet_ntop(AF_INET, &(mtracep->dst_addr), dst_str,
+                         sizeof(dst_str)),
+               inet_ntop(AF_INET, &(mtracep->rsp_addr), rsp_str,
+                         sizeof(rsp_str)),
+               mtracep->rsp_ttl, ntohl(mtracep->qry_id));
+       if (mtrace_len > (int)sizeof(struct igmp_mtrace)) {
+
+               int i;
+
+               int responses = mtrace_len - sizeof(struct igmp_mtrace);
+
+               if ((responses % sizeof(struct igmp_mtrace_rsp)) != 0)
+                       if (PIM_DEBUG_MTRACE)
+                               zlog_debug(
+                                       "Mtrace response block of wrong"
+                                        " length");
+
+               responses = responses / sizeof(struct igmp_mtrace_rsp);
+
+               for (i = 0; i < responses; i++)
+                       mtrace_rsp_debug(mtracep->qry_id, i, &mtracep->rsp[i]);
+       }
+}
+
+/* 5.1 Query Arrival Time */
+static uint32_t query_arrival_time(void)
+{
+       struct timeval tv;
+       uint32_t qat;
+
+       char m_qat[] = "Query arrival time lookup failed: errno=%d: %s";
+
+       if (gettimeofday(&tv, NULL) < 0) {
+               if (PIM_DEBUG_MTRACE)
+                       zlog_warn(m_qat, errno, safe_strerror(errno));
+               return 0;
+       }
+       /* not sure second offset correct, as I get different value */
+       qat = ((tv.tv_sec + 32384) << 16) + ((tv.tv_usec << 10) / 15625);
+
+       return qat;
+}
+
+static int 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)
+{
+       struct sockaddr_in to;
+       struct pim_interface *pim_ifp;
+       socklen_t tolen;
+       ssize_t sent;
+       int ret;
+       int fd;
+       char pim_str[INET_ADDRSTRLEN];
+       char rsp_str[INET_ADDRSTRLEN];
+       u_char ttl;
+
+       pim_ifp = ifp->info;
+
+       memset(&to, 0, sizeof(to));
+       to.sin_family = AF_INET;
+       to.sin_addr = dst_addr;
+       tolen = sizeof(to);
+
+       if (PIM_DEBUG_MTRACE)
+               zlog_debug("Sending mtrace packet to %s on %s",
+                          inet_ntop(AF_INET, &mtracep->rsp_addr, rsp_str,
+                                    sizeof(rsp_str)),
+                          inet_ntop(AF_INET, &pim_ifp->primary_address,
+                                    pim_str, sizeof(pim_str)));
+
+       fd = pim_socket_raw(IPPROTO_IGMP);
+
+       if (fd < 0)
+               return -1;
+
+       ret = pim_socket_bind(fd, ifp);
+
+       if (ret < 0) {
+               ret = -1;
+               goto close_fd;
+       }
+
+       if (IPV4_CLASS_DE(ntohl(dst_addr.s_addr))) {
+               if (IPV4_MC_LINKLOCAL(ntohl(dst_addr.s_addr))) {
+                       ttl = 1;
+               } else {
+                       if (mtracep->type == PIM_IGMP_MTRACE_RESPONSE)
+                               ttl = mtracep->rsp_ttl;
+                       else
+                               ttl = 64;
+               }
+               ret = setsockopt(fd, IPPROTO_IP, IP_MULTICAST_TTL, &ttl,
+                                sizeof(ttl));
+
+               if (ret < 0) {
+                       if (PIM_DEBUG_MTRACE)
+                               zlog_warn("Failed to set socket multicast TTL");
+                       ret = -1;
+                       goto close_fd;
+               }
+       }
+
+       sent = sendto(fd, (char *)mtracep, mtrace_buf_len, MSG_DONTWAIT,
+                     (struct sockaddr *)&to, tolen);
+
+       if (sent != (ssize_t)mtrace_buf_len) {
+               char dst_str[INET_ADDRSTRLEN];
+               char group_str[INET_ADDRSTRLEN];
+
+               pim_inet4_dump("<dst?>", dst_addr, dst_str, sizeof(dst_str));
+               pim_inet4_dump("<group?>", group_addr, group_str,
+                              sizeof(group_str));
+               if (sent < 0) {
+                       if (PIM_DEBUG_MTRACE)
+                               zlog_warn(
+                                       "Send mtrace request failed for %s on"
+                                       "%s: group=%s msg_size=%zd: errno=%d: "
+                                       " %s",
+                                       dst_str, ifp->name, group_str,
+                                       mtrace_buf_len, errno,
+                                       safe_strerror(errno));
+               } else {
+                       if (PIM_DEBUG_MTRACE)
+                               zlog_warn(
+                                       "Send mtrace request failed for %s on"
+                                       " %s: group=%s msg_size=%zd: sent=%zd",
+                                       dst_str, ifp->name, group_str,
+                                       mtrace_buf_len, sent);
+               }
+               ret = -1;
+               goto close_fd;
+       }
+       ret = 0;
+close_fd:
+       close(fd);
+       return ret;
+}
+
+static int mtrace_un_forward_packet(struct pim_instance *pim, struct ip *ip_hdr,
+                                   struct interface *interface)
+{
+       struct pim_nexthop nexthop;
+       struct sockaddr_in to;
+       struct interface *if_out;
+       socklen_t tolen;
+       int ret;
+       int fd;
+       int sent;
+       uint16_t checksum;
+
+       checksum = ip_hdr->ip_sum;
+
+       ip_hdr->ip_sum = 0;
+
+       if (checksum != in_cksum(ip_hdr, ip_hdr->ip_hl * 4))
+               return -1;
+
+       if (ip_hdr->ip_ttl-- <= 1)
+               return -1;
+
+       ip_hdr->ip_sum = in_cksum(ip_hdr, ip_hdr->ip_hl * 4);
+
+       fd = pim_socket_raw(IPPROTO_RAW);
+
+       if (fd < 0)
+               return -1;
+
+       pim_socket_ip_hdr(fd);
+
+       if (interface == NULL) {
+               ret = pim_nexthop_lookup(pim, &nexthop, ip_hdr->ip_dst, 0);
+
+               if (ret != 0) {
+                       if (PIM_DEBUG_MTRACE)
+                               zlog_warn(
+                                       "Dropping mtrace packet, "
+                                       "no route to destination");
+                       return -1;
+               }
+
+               if_out = nexthop.interface;
+       } else {
+               if_out = interface;
+       }
+
+       ret = pim_socket_bind(fd, if_out);
+
+       if (ret < 0) {
+               close(fd);
+               return -1;
+       }
+
+       memset(&to, 0, sizeof(to));
+       to.sin_family = AF_INET;
+       to.sin_addr = ip_hdr->ip_dst;
+       tolen = sizeof(to);
+
+       sent = sendto(fd, ip_hdr, ntohs(ip_hdr->ip_len), 0,
+                     (struct sockaddr *)&to, tolen);
+
+       close(fd);
+
+       if (sent < 0) {
+               if (PIM_DEBUG_MTRACE)
+                       zlog_warn(
+                               "Failed to forward mtrace packet:"
+                               " sendto errno=%d, %s",
+                               errno, safe_strerror(errno));
+               return -1;
+       }
+
+       if (PIM_DEBUG_MTRACE) {
+               zlog_debug("Fwd mtrace packet len=%u to %s ttl=%u",
+                          ntohs(ip_hdr->ip_len), inet_ntoa(ip_hdr->ip_dst),
+                          ip_hdr->ip_ttl);
+       }
+
+       return 0;
+}
+
+static int mtrace_mc_forward_packet(struct pim_instance *pim, struct ip *ip_hdr)
+{
+       struct prefix_sg sg;
+       struct channel_oil *c_oil;
+       struct listnode *chnode;
+       struct listnode *chnextnode;
+       struct pim_ifchannel *ch = NULL;
+       int ret = -1;
+
+       memset(&sg, 0, sizeof(struct prefix_sg));
+       sg.grp = ip_hdr->ip_dst;
+
+       c_oil = pim_find_channel_oil(pim, &sg);
+
+       if (c_oil == NULL) {
+               if (PIM_DEBUG_MTRACE) {
+                       zlog_debug(
+                               "Dropping mtrace multicast packet "
+                               "len=%u to %s ttl=%u",
+                               ntohs(ip_hdr->ip_len),
+                               inet_ntoa(ip_hdr->ip_dst), ip_hdr->ip_ttl);
+               }
+               return -1;
+       }
+       if (c_oil->up == NULL)
+               return -1;
+       if (c_oil->up->ifchannels == NULL)
+               return -1;
+       for (ALL_LIST_ELEMENTS(c_oil->up->ifchannels, chnode, chnextnode, ch)) {
+               if (pim_macro_chisin_oiflist(ch)) {
+                       int r;
+
+                       r = mtrace_un_forward_packet(pim, ip_hdr,
+                                                    ch->interface);
+                       if (r == 0)
+                               ret = 0;
+               }
+       }
+       return ret;
+}
+
+
+static int mtrace_forward_packet(struct pim_instance *pim, struct ip *ip_hdr)
+{
+       if (IPV4_CLASS_DE(ntohl(ip_hdr->ip_dst.s_addr)))
+               return mtrace_mc_forward_packet(pim, ip_hdr);
+       else
+               return mtrace_un_forward_packet(pim, ip_hdr, NULL);
+}
+
+/* 6.5 Sending Traceroute Responses */
+static int mtrace_send_mc_response(struct pim_instance *pim,
+                                  struct igmp_mtrace *mtracep,
+                                  size_t mtrace_len)
+{
+       struct prefix_sg sg;
+       struct channel_oil *c_oil;
+       struct listnode *chnode;
+       struct listnode *chnextnode;
+       struct pim_ifchannel *ch = NULL;
+       int ret = -1;
+
+       memset(&sg, 0, sizeof(struct prefix_sg));
+       sg.grp = mtracep->rsp_addr;
+
+       c_oil = pim_find_channel_oil(pim, &sg);
+
+       if (c_oil == NULL) {
+               if (PIM_DEBUG_MTRACE) {
+                       zlog_debug(
+                               "Dropping mtrace multicast response packet "
+                               "len=%u to %s",
+                               (unsigned int)mtrace_len,
+                               inet_ntoa(mtracep->rsp_addr));
+               }
+               return -1;
+       }
+       if (c_oil->up == NULL)
+               return -1;
+       if (c_oil->up->ifchannels == NULL)
+               return -1;
+       for (ALL_LIST_ELEMENTS(c_oil->up->ifchannels, chnode, chnextnode, ch)) {
+               if (pim_macro_chisin_oiflist(ch)) {
+                       int r;
+
+                       r = mtrace_send_packet(ch->interface, mtracep,
+                                              mtrace_len, mtracep->rsp_addr,
+                                              mtracep->grp_addr);
+                       if (r == 0)
+                               ret = 0;
+               }
+       }
+       return ret;
+}
+
+static int mtrace_send_response(struct pim_instance *pim,
+                               struct igmp_mtrace *mtracep, size_t mtrace_len)
+{
+       struct pim_nexthop nexthop;
+       int ret;
+
+       mtracep->type = PIM_IGMP_MTRACE_RESPONSE;
+
+       mtracep->checksum = 0;
+       mtracep->checksum = in_cksum((char *)mtracep, mtrace_len);
+
+       if (IPV4_CLASS_DE(ntohl(mtracep->rsp_addr.s_addr))) {
+               struct pim_rpf *p_rpf;
+               char grp_str[INET_ADDRSTRLEN];
+
+               if (pim_rp_i_am_rp(pim, mtracep->rsp_addr))
+                       return mtrace_send_mc_response(pim, mtracep,
+                                                      mtrace_len);
+
+               p_rpf = pim_rp_g(pim, mtracep->rsp_addr);
+
+               if (p_rpf == NULL) {
+                       if (PIM_DEBUG_MTRACE)
+                               zlog_warn("mtrace no RP for %s",
+                                         inet_ntop(AF_INET,
+                                                   &(mtracep->rsp_addr),
+                                                   grp_str, sizeof(grp_str)));
+                       return -1;
+               }
+               nexthop = p_rpf->source_nexthop;
+               if (PIM_DEBUG_MTRACE)
+                       zlog_debug("mtrace response to RP");
+       } else {
+               /* TODO: should use unicast rib lookup */
+               ret = pim_nexthop_lookup(pim, &nexthop, mtracep->rsp_addr, 1);
+
+               if (ret != 0) {
+                       if (PIM_DEBUG_MTRACE)
+                               zlog_warn(
+                                       "Dropped response qid=%ud, no route to "
+                                       "response address",
+                                       mtracep->qry_id);
+                       return -1;
+               }
+       }
+
+       return mtrace_send_packet(nexthop.interface, mtracep, mtrace_len,
+                                 mtracep->rsp_addr, mtracep->grp_addr);
+}
+
+int 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)
+{
+       static uint32_t qry_id, qry_src;
+       char mtrace_buf[MTRACE_HDR_SIZE + MTRACE_MAX_HOPS * MTRACE_RSP_SIZE];
+       struct pim_nexthop nexthop;
+       struct interface *ifp;
+       struct interface *out_ifp;
+       struct pim_interface *pim_ifp;
+       struct pim_interface *pim_out_ifp;
+       struct pim_instance *pim;
+       struct igmp_mtrace *mtracep;
+       struct igmp_mtrace_rsp *rspp;
+       struct in_addr nh_addr;
+       enum mtrace_fwd_code fwd_code = MTRACE_FWD_CODE_NO_ERROR;
+       int ret;
+       size_t r_len;
+       int last_rsp_ind = 0;
+       size_t mtrace_len;
+       uint16_t recv_checksum;
+       uint16_t checksum;
+
+       ifp = igmp->interface;
+       pim_ifp = ifp->info;
+       pim = pim_ifp->pim;
+
+       /*
+        * 6. Router Behaviour
+        * Check if mtrace packet is addressed elsewhere and forward,
+        * if applicable
+        */
+       if (!IPV4_CLASS_DE(ntohl(ip_hdr->ip_dst.s_addr)))
+               if (!if_lookup_exact_address(&ip_hdr->ip_dst, AF_INET,
+                                            pim->vrf_id))
+                       return mtrace_forward_packet(pim, ip_hdr);
+
+       if (igmp_msg_len < (int)sizeof(struct igmp_mtrace)) {
+               if (PIM_DEBUG_MTRACE)
+                       zlog_warn(
+                               "Recv mtrace packet from %s on %s: too short,"
+                               " len=%d, min=%lu",
+                               from_str, ifp->name, igmp_msg_len,
+                               sizeof(struct igmp_mtrace));
+               return -1;
+       }
+
+       mtracep = (struct igmp_mtrace *)igmp_msg;
+
+       recv_checksum = mtracep->checksum;
+
+       mtracep->checksum = 0;
+
+       checksum = in_cksum(igmp_msg, igmp_msg_len);
+
+       if (recv_checksum != checksum) {
+               if (PIM_DEBUG_MTRACE)
+                       zlog_warn(
+                               "Recv mtrace packet from %s on %s: checksum"
+                               " mismatch: received=%x computed=%x",
+                               from_str, ifp->name, recv_checksum, checksum);
+               return -1;
+       }
+
+       if (PIM_DEBUG_MTRACE)
+               mtrace_debug(pim_ifp, mtracep, igmp_msg_len);
+
+       /* subtract header from message length */
+       r_len = igmp_msg_len - sizeof(struct igmp_mtrace);
+
+       /* Classify mtrace packet, check if it is a query */
+       if (!r_len) {
+               if (PIM_DEBUG_MTRACE)
+                       zlog_debug("Received IGMP multicast traceroute query");
+
+               /* 6.1.1  Packet verification */
+               if (!pim_if_connected_to_source(ifp, mtracep->dst_addr)) {
+                       if (IPV4_CLASS_DE(ntohl(ip_hdr->ip_dst.s_addr))) {
+                               if (PIM_DEBUG_MTRACE)
+                                       zlog_debug(
+                                               "Dropping multicast query "
+                                               "on wrong interface");
+                               return -1;
+                       }
+                       /* Unicast query on wrong interface */
+                       fwd_code = MTRACE_FWD_CODE_WRONG_IF;
+               }
+               if (qry_id == mtracep->qry_id && qry_src == from.s_addr) {
+                       if (PIM_DEBUG_MTRACE)
+                               zlog_debug(
+                                       "Dropping multicast query with "
+                                       "duplicate source and id");
+                       return -1;
+               }
+               qry_id = mtracep->qry_id;
+               qry_src = from.s_addr;
+       }
+       /* if response fields length is equal to a whole number of responses */
+       else if ((r_len % sizeof(struct igmp_mtrace_rsp)) == 0) {
+               r_len = igmp_msg_len - sizeof(struct igmp_mtrace);
+
+               if (r_len != 0)
+                       last_rsp_ind = r_len / sizeof(struct igmp_mtrace_rsp);
+               if (last_rsp_ind > MTRACE_MAX_HOPS) {
+                       if (PIM_DEBUG_MTRACE)
+                               zlog_warn("Mtrace request of excessive size");
+                       return -1;
+               }
+       } else {
+               if (PIM_DEBUG_MTRACE)
+                       zlog_warn(
+                               "Recv mtrace packet from %s on %s: "
+                               "invalid length %d",
+                               from_str, ifp->name, igmp_msg_len);
+               return -1;
+       }
+
+       /* 6.2.1 Packet Verification - drop not link-local multicast */
+       if (IPV4_CLASS_DE(ntohl(ip_hdr->ip_dst.s_addr))
+           && !IPV4_MC_LINKLOCAL(ntohl(ip_hdr->ip_dst.s_addr))) {
+               if (PIM_DEBUG_MTRACE)
+                       zlog_warn(
+                               "Recv mtrace packet from %s on %s:"
+                               " not link-local multicast %s",
+                               from_str, ifp->name, inet_ntoa(ip_hdr->ip_dst));
+               return -1;
+       }
+
+       /* 6.2.2. Normal Processing */
+
+       /* 6.2.2. 1. */
+
+       if (last_rsp_ind == MTRACE_MAX_HOPS) {
+               mtracep->rsp[MTRACE_MAX_HOPS - 1].fwd_code =
+                       MTRACE_FWD_CODE_NO_SPACE;
+               return mtrace_send_response(pim_ifp->pim, mtracep,
+                                           igmp_msg_len);
+       }
+
+       /* calculate new mtrace mtrace lenght with extra response */
+       mtrace_len = igmp_msg_len + sizeof(struct igmp_mtrace_rsp);
+
+       /* copy received query/request */
+       memcpy(mtrace_buf, igmp_msg, igmp_msg_len);
+
+       /* repoint mtracep pointer to copy */
+       mtracep = (struct igmp_mtrace *)mtrace_buf;
+
+       /* pointer for extra response field to be filled in */
+       rspp = &mtracep->rsp[last_rsp_ind];
+
+       /* initialize extra response field */
+       mtrace_rsp_init(rspp);
+
+       rspp->arrival = htonl(query_arrival_time());
+       rspp->outgoing = pim_ifp->primary_address;
+       rspp->out_count = htonl(MTRACE_UNKNOWN_COUNT);
+
+       /* 6.2.2. 2. Attempt to determine forwarding information */
+
+       nh_addr.s_addr = 0;
+
+       ret = pim_nexthop_lookup(pim, &nexthop, mtracep->src_addr, 1);
+
+       if (ret == 0) {
+               char nexthop_str[INET_ADDRSTRLEN];
+
+               if (PIM_DEBUG_MTRACE)
+                       zlog_debug("mtrace pim_nexthop_lookup OK");
+
+               if (PIM_DEBUG_MTRACE)
+                       zlog_warn("mtrace next_hop=%s",
+                                 inet_ntop(nexthop.mrib_nexthop_addr.family,
+                                           &nexthop.mrib_nexthop_addr.u.prefix,
+                                           nexthop_str, sizeof(nexthop_str)));
+
+               if (nexthop.mrib_nexthop_addr.family == AF_INET)
+                       nh_addr = nexthop.mrib_nexthop_addr.u.prefix4;
+       }
+       /* 6.4 Forwarding Traceroute Requests: ... Otherwise, ... */
+       else {
+               if (PIM_DEBUG_MTRACE)
+                       zlog_debug("mtrace not found neighbor");
+               if (!fwd_code)
+                       rspp->fwd_code = MTRACE_FWD_CODE_NO_ROUTE;
+               else
+                       rspp->fwd_code = fwd_code;
+               /* 6.5 Sending Traceroute Responses */
+               return mtrace_send_response(pim, mtracep, mtrace_len);
+       }
+
+       out_ifp = nexthop.interface;
+       pim_out_ifp = out_ifp->info;
+
+       rspp->incoming = pim_out_ifp->primary_address;
+       rspp->prev_hop = nh_addr;
+       rspp->in_count = htonl(MTRACE_UNKNOWN_COUNT);
+       rspp->total = htonl(MTRACE_UNKNOWN_COUNT);
+       rspp->rtg_proto = MTRACE_RTG_PROTO_PIM;
+       rspp->s = 1;
+       rspp->src_mask = 32;
+
+       if (nh_addr.s_addr == 0) {
+               /* reached source? */
+               if (pim_if_connected_to_source(out_ifp, mtracep->src_addr))
+                       return mtrace_send_response(pim, mtracep, mtrace_len);
+               /*
+                * 6.4 Forwarding Traceroute Requests:
+                * Previous-hop router not known
+                */
+               inet_aton(MCAST_ALL_ROUTERS, &nh_addr);
+       }
+
+       if (mtracep->hops <= (last_rsp_ind + 1))
+               return mtrace_send_response(pim, mtracep, mtrace_len);
+
+       mtracep->checksum = 0;
+
+       mtracep->checksum = in_cksum(mtrace_buf, mtrace_len);
+
+       return mtrace_send_packet(out_ifp, mtracep, mtrace_len, nh_addr,
+                                 mtracep->grp_addr);
+}
+
+int 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)
+{
+       static uint32_t qry_id, rsp_dst;
+       struct interface *ifp;
+       struct pim_interface *pim_ifp;
+       struct pim_instance *pim;
+       struct igmp_mtrace *mtracep;
+       uint16_t recv_checksum;
+       uint16_t checksum;
+
+       ifp = igmp->interface;
+       pim_ifp = ifp->info;
+       pim = pim_ifp->pim;
+
+       mtracep = (struct igmp_mtrace *)igmp_msg;
+
+       recv_checksum = mtracep->checksum;
+
+       mtracep->checksum = 0;
+
+       checksum = in_cksum(igmp_msg, igmp_msg_len);
+
+       if (recv_checksum != checksum) {
+               if (PIM_DEBUG_MTRACE)
+                       zlog_warn(
+                               "Recv mtrace response from %s on %s: checksum"
+                               " mismatch: received=%x computed=%x",
+                               from_str, ifp->name, recv_checksum, checksum);
+               return -1;
+       }
+
+       mtracep->checksum = checksum;
+
+       if (PIM_DEBUG_MTRACE)
+               mtrace_debug(pim_ifp, mtracep, igmp_msg_len);
+
+       /* Drop duplicate packets */
+       if (qry_id == mtracep->qry_id && rsp_dst == ip_hdr->ip_dst.s_addr) {
+               if (PIM_DEBUG_MTRACE)
+                       zlog_debug("duplicate mtrace response packet dropped");
+               return -1;
+       }
+
+       qry_id = mtracep->qry_id;
+       rsp_dst = ip_hdr->ip_dst.s_addr;
+
+       return mtrace_forward_packet(pim, ip_hdr);
+}
diff --git a/pimd/pim_igmp_mtrace.h b/pimd/pim_igmp_mtrace.h
new file mode 100644 (file)
index 0000000..b5c1008
--- /dev/null
@@ -0,0 +1,103 @@
+/*
+ * Multicast traceroute for FRRouting
+ * Copyright (C) 2017  Mladen Sablic
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; see the file COPYING; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef PIM_IGMP_MTRACE_H
+#define PIM_IGMP_MTRACE_H
+
+#include <zebra.h>
+
+#include "pim_igmp.h"
+
+#define MTRACE_MAX_HOPS (255)
+#define MTRACE_UNKNOWN_COUNT (0xffffffff)
+
+enum mtrace_fwd_code {
+       MTRACE_FWD_CODE_NO_ERROR        = 0x00,
+       MTRACE_FWD_CODE_WRONG_IF        = 0x01,
+       MTRACE_FWD_CODE_PRUNE_SENT      = 0x02,
+       MTRACE_FWD_CODE_PRUNE_RCVD      = 0x03,
+       MTRACE_FWD_CODE_SCOPED          = 0x04,
+       MTRACE_FWD_CODE_NO_ROUTE        = 0x05,
+       MTRACE_FWD_CODE_WRONG_LAST_HOP  = 0x06,
+       MTRACE_FWD_CODE_NOT_FORWARDING  = 0x07,
+       MTRACE_FWD_CODE_REACHED_RP      = 0x08,
+       MTRACE_FWD_CODE_RPF_IF          = 0x09,
+       MTRACE_FWD_CODE_NO_MULTICAST    = 0x0A,
+       MTRACE_FWD_CODE_INFO_HIDDEN     = 0x0B,
+       MTRACE_FWD_CODE_NO_SPACE        = 0x81,
+       MTRACE_FWD_CODE_OLD_ROUTER      = 0x82,
+       MTRACE_FWD_CODE_ADMIN_PROHIB    = 0x83
+};
+
+enum mtrace_rtg_proto {
+       MTRACE_RTG_PROTO_DVMRP          = 1,
+       MTRACE_RTG_PROTO_MOSPF          = 2,
+       MTRACE_RTG_PROTO_PIM            = 3,
+       MTRACE_RTG_PROTO_CBT            = 4,
+       MTRACE_RTG_PROTO_PIM_SPECIAL    = 5,
+       MTRACE_RTG_PROTO_PIM_STATIC     = 6,
+       MTRACE_RTG_PROTO_DVMRP_STATIC   = 7,
+       MTRACE_RTG_PROTO_PIM_MBGP       = 8,
+       MTRACE_RTG_PROTO_CBT_SPECIAL    = 9,
+       MTRACE_RTG_PROTO_CBT_STATIC     = 10,
+       MTRACE_RTG_PROTO_PIM_ASSERT     = 11,
+};
+
+struct igmp_mtrace_rsp {
+       uint32_t arrival;
+       struct in_addr incoming;
+       struct in_addr outgoing;
+       struct in_addr prev_hop;
+       uint32_t in_count;
+       uint32_t out_count;
+       uint32_t total;
+       uint32_t rtg_proto : 8;
+       uint32_t fwd_ttl : 8;
+       /* little endian order for next three fields */
+       uint32_t src_mask : 6;
+       uint32_t s : 1;
+       uint32_t mbz : 1;
+       uint32_t fwd_code : 8;
+} __attribute__((packed));
+
+struct igmp_mtrace {
+       uint8_t type;
+       uint8_t hops;
+       uint16_t checksum;
+       struct in_addr grp_addr;
+       struct in_addr src_addr;
+       struct in_addr dst_addr;
+       struct in_addr rsp_addr;
+       uint32_t rsp_ttl : 8;
+       uint32_t qry_id : 24;
+       struct igmp_mtrace_rsp rsp[0];
+} __attribute__((packed));
+
+#define MTRACE_HDR_SIZE (sizeof(struct igmp_mtrace))
+#define MTRACE_RSP_SIZE (sizeof(struct igmp_mtrace_rsp))
+
+int 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);
+
+int 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);
+
+#endif /* PIM_IGMP_MTRACE_H */
index c45b0ce14c34ca46e327f62811870db55b8bb864..53bbf54f3e3f36897645db0bd7d77682fdff5493 100644 (file)
@@ -135,8 +135,8 @@ void pim_channel_oil_free(struct channel_oil *c_oil)
        XFREE(MTYPE_PIM_CHANNEL_OIL, c_oil);
 }
 
-static struct channel_oil *pim_find_channel_oil(struct pim_instance *pim,
-                                               struct prefix_sg *sg)
+struct channel_oil *pim_find_channel_oil(struct pim_instance *pim,
+                                        struct prefix_sg *sg)
 {
        struct channel_oil *c_oil = NULL;
        struct channel_oil lookup;
index 1168ba0a8fe9b8a5c3520a4dc00649e3c4027501..94d3840e98db72aafa561e706620cac66f0ed399 100644 (file)
@@ -86,6 +86,8 @@ void pim_oil_init(struct pim_instance *pim);
 void pim_oil_terminate(struct pim_instance *pim);
 
 void pim_channel_oil_free(struct channel_oil *c_oil);
+struct channel_oil *pim_find_channel_oil(struct pim_instance *pim,
+                                        struct prefix_sg *sg);
 struct channel_oil *pim_channel_oil_add(struct pim_instance *pim,
                                        struct prefix_sg *sg,
                                        int input_vif_index);
index 791680a9114237c1b16a264393b4a892453d52ed..688bc42c3dddf3c8682bf4e7b6c8920bba00278b 100644 (file)
@@ -78,6 +78,11 @@ int pim_debug_config_write(struct vty *vty)
                ++writes;
        }
 
+       if (PIM_DEBUG_MTRACE) {
+               vty_out(vty, "debug mtrace\n");
+               ++writes;
+       }
+
        if (PIM_DEBUG_MROUTE_DETAIL) {
                vty_out(vty, "debug mroute detail\n");
                ++writes;
index cd00a2df46b96a61f9a2babd4eb5cdab44340fb0..de7f2593196ce07ec621aa127376c7939412e2d0 100644 (file)
 #define PIM_MASK_PIM_NHT             (1 << 22)
 #define PIM_MASK_PIM_NHT_DETAIL      (1 << 23)
 #define PIM_MASK_PIM_NHT_RP          (1 << 24)
+#define PIM_MASK_MTRACE              (1 << 25)
 /* Remember 32 bits!!! */
 
 /* PIM error codes */
@@ -188,6 +189,7 @@ extern int32_t qpim_register_probe_time;
 #define PIM_DEBUG_PIM_NHT             (qpim_debugs & PIM_MASK_PIM_NHT)
 #define PIM_DEBUG_PIM_NHT_DETAIL      (qpim_debugs & PIM_MASK_PIM_NHT_DETAIL)
 #define PIM_DEBUG_PIM_NHT_RP          (qpim_debugs & PIM_MASK_PIM_NHT_RP)
+#define PIM_DEBUG_MTRACE              (qpim_debugs & PIM_MASK_MTRACE)
 
 #define PIM_DEBUG_EVENTS       (qpim_debugs & (PIM_MASK_PIM_EVENTS | PIM_MASK_IGMP_EVENTS | PIM_MASK_MSDP_EVENTS))
 #define PIM_DEBUG_PACKETS      (qpim_debugs & (PIM_MASK_PIM_PACKETS | PIM_MASK_IGMP_PACKETS | PIM_MASK_MSDP_PACKETS))
@@ -216,6 +218,7 @@ extern int32_t qpim_register_probe_time;
 #define PIM_DO_DEBUG_MSDP_INTERNAL       (qpim_debugs |= PIM_MASK_MSDP_INTERNAL)
 #define PIM_DO_DEBUG_PIM_NHT             (qpim_debugs |= PIM_MASK_PIM_NHT)
 #define PIM_DO_DEBUG_PIM_NHT_RP          (qpim_debugs |= PIM_MASK_PIM_NHT_RP)
+#define PIM_DO_DEBUG_MTRACE              (qpim_debugs |= PIM_MASK_MTRACE)
 
 #define PIM_DONT_DEBUG_PIM_EVENTS          (qpim_debugs &= ~PIM_MASK_PIM_EVENTS)
 #define PIM_DONT_DEBUG_PIM_PACKETS         (qpim_debugs &= ~PIM_MASK_PIM_PACKETS)
@@ -240,6 +243,7 @@ extern int32_t qpim_register_probe_time;
 #define PIM_DONT_DEBUG_MSDP_INTERNAL       (qpim_debugs &= ~PIM_MASK_MSDP_INTERNAL)
 #define PIM_DONT_DEBUG_PIM_NHT             (qpim_debugs &= ~PIM_MASK_PIM_NHT)
 #define PIM_DONT_DEBUG_PIM_NHT_RP          (qpim_debugs &= ~PIM_MASK_PIM_NHT_RP)
+#define PIM_DONT_DEBUG_MTRACE              (qpim_debugs &= ~PIM_MASK_MTRACE)
 
 void pim_init(void);
 void pim_terminate(void);
index 2fcb06157656d6bff33da1efe22c8e966a83924e..2254362221bfa9bd98a1d7499f3775d28f5ba1fa 100644 (file)
@@ -5,6 +5,7 @@
 if PIMD
 noinst_LIBRARIES += pimd/libpim.a
 sbin_PROGRAMS += pimd/pimd
+bin_PROGRAMS += pimd/mtracebis
 noinst_PROGRAMS += pimd/test_igmpv3_join
 dist_examples_DATA += pimd/pimd.conf.sample
 endif
@@ -18,6 +19,7 @@ pimd_libpim_a_SOURCES = \
        pimd/pim_iface.c \
        pimd/pim_ifchannel.c \
        pimd/pim_igmp.c \
+       pimd/pim_igmp_mtrace.c \
        pimd/pim_igmpv2.c \
        pimd/pim_igmpv3.c \
        pimd/pim_instance.c \
@@ -66,6 +68,7 @@ noinst_HEADERS += \
        pimd/pim_ifchannel.h \
        pimd/pim_igmp.h \
        pimd/pim_igmp_join.h \
+       pimd/pim_igmp_mtrace.h \
        pimd/pim_igmpv2.h \
        pimd/pim_igmpv3.h \
        pimd/pim_instance.h \
@@ -101,6 +104,8 @@ noinst_HEADERS += \
        pimd/pim_zebra.h \
        pimd/pim_zlookup.h \
        pimd/pimd.h \
+       pimd/mtracebis_netlink.h \
+       pimd/mtracebis_routeget.h \
        # end
 
 pimd_pimd_LDADD = pimd/libpim.a lib/libfrr.la @LIBCAP@
@@ -108,3 +113,9 @@ pimd_pimd_SOURCES = pimd/pim_main.c
 
 pimd_test_igmpv3_join_LDADD = lib/libfrr.la
 pimd_test_igmpv3_join_SOURCES = pimd/test_igmpv3_join.c
+
+pimd_mtracebis_LDADD =  lib/libfrr.la
+pimd_mtracebis_SOURCES = pimd/mtracebis.c \
+                        pimd/mtracebis_netlink.c \
+                        pimd/mtracebis_routeget.c \
+                        # end
index 94c4ba43305be2e2e461d978477913bead00ffd2..65e9c9f8c51f107b398fc4079a22510b0599d4ca 100644 (file)
@@ -2628,6 +2628,19 @@ ALIAS(vtysh_traceroute, vtysh_traceroute_ip_cmd, "traceroute ip WORD",
       "IP trace\n"
       "Trace route to destination address or hostname\n")
 
+DEFUN (vtysh_mtrace,
+       vtysh_mtrace_cmd,
+       "mtrace WORD",
+       "Multicast trace route to multicast source\n"
+       "Multicast trace route to multicast source address\n")
+{
+       int idx = 1;
+
+       argv_find(argv, argc, "WORD", &idx);
+       execute_command("mtracebis", 1, argv[idx]->arg, NULL);
+       return CMD_SUCCESS;
+}
+
 DEFUN (vtysh_ping6,
        vtysh_ping6_cmd,
        "ping ipv6 WORD",
@@ -3327,6 +3340,7 @@ void vtysh_init_vty(void)
        install_element(VIEW_NODE, &vtysh_ping_ip_cmd);
        install_element(VIEW_NODE, &vtysh_traceroute_cmd);
        install_element(VIEW_NODE, &vtysh_traceroute_ip_cmd);
+       install_element(VIEW_NODE, &vtysh_mtrace_cmd);
        install_element(VIEW_NODE, &vtysh_ping6_cmd);
        install_element(VIEW_NODE, &vtysh_traceroute6_cmd);
 #if defined(HAVE_SHELL_ACCESS)