pimd: Multicast traceroute client and router

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>
This commit is contained in:
Mladen Sablic 2018-02-12 23:41:33 +01:00
parent 6ac12ea313
commit 4d9ad5dcd0
25 changed files with 2274 additions and 2 deletions

View File

@ -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

View 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

View 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

View 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 \

25
doc/mtracebis.8.in Normal file
View File

@ -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

View 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

1
pimd/.gitignore vendored
View File

@ -2,6 +2,7 @@
Makefile.in
libpim.a
pimd
mtracebis
test_igmpv3_join
tags
TAGS

View 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-

371
pimd/mtracebis.c Normal file
View File

@ -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__ */

700
pimd/mtracebis_netlink.c Normal file
View File

@ -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__ */

130
pimd/mtracebis_netlink.h Normal file
View File

@ -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__ */

98
pimd/mtracebis_routeget.c Normal file
View File

@ -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__ */

31
pimd/mtracebis_routeget.h Normal file
View File

@ -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__ */

View 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);

View 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);

View 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);

View 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)

727
pimd/pim_igmp_mtrace.c Normal file
View File

@ -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);
}

103
pimd/pim_igmp_mtrace.h Normal file
View File

@ -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 */

View 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;

View 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);

View 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;

View File

@ -112,6 +112,7 @@
#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);

View 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

View 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)