freebsd-nq/usr.sbin/pim6sd/trace.c

560 lines
16 KiB
C
Raw Normal View History

/*
* Copyright (C) 1999 WIDE Project.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the project nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
/*
* Copyright (c) 1998 by the University of Southern California.
* All rights reserved.
*
* Permission to use, copy, modify, and distribute this software and
* its documentation in source and binary forms for lawful
* non-commercial purposes and without fee is hereby granted, provided
* that the above copyright notice appear in all copies and that both
* the copyright notice and this permission notice appear in supporting
* documentation, and that any documentation, advertising materials,
* and other materials related to such distribution and use acknowledge
* that the software was developed by the University of Southern
* California and/or Information Sciences Institute.
* The name of the University of Southern California may not
* be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THE UNIVERSITY OF SOUTHERN CALIFORNIA DOES NOT MAKE ANY REPRESENTATIONS
* ABOUT THE SUITABILITY OF THIS SOFTWARE FOR ANY PURPOSE. THIS SOFTWARE IS
* PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES,
* INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, TITLE, AND
* NON-INFRINGEMENT.
*
* IN NO EVENT SHALL USC, OR ANY OTHER CONTRIBUTOR BE LIABLE FOR ANY
* SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES, WHETHER IN CONTRACT,
* TORT, OR OTHER FORM OF ACTION, ARISING OUT OF OR IN CONNECTION WITH,
* THE USE OR PERFORMANCE OF THIS SOFTWARE.
*
* Other copyrights might apply to parts of this software and are so
* noted when applicable.
*
* $FreeBSD$
*/
/*
* Questions concerning this software should be directed to
* Pavlin Ivanov Radoslavov (pavlin@catarina.usc.edu)
*
* $Id: trace.c,v 1.7 1999/09/16 08:45:45 jinmei Exp $
*/
/*
* Part of this program has been derived from mrouted.
* The mrouted program is covered by the license in the accompanying file
* named "LICENSE.mrouted".
*
* The mrouted program is COPYRIGHT 1989 by The Board of Trustees of
* Leland Stanford Junior University.
*
*/
#include <sys/types.h>
#include <sys/socket.h>
#include "vif.h"
#include "inet6.h"
#include <sys/ioctl.h>
#include <sys/uio.h>
#include <errno.h>
#include <net/if.h>
#if defined(__FreeBSD__) && __FreeBSD__ >= 3
#include <net/if_var.h>
#endif
#include <netinet/in.h>
#include <netinet/icmp6.h>
#include <netinet/ip6.h>
#include <netinet6/ip6_mroute.h>
#include <netinet6/in6_var.h>
#include <arpa/inet.h>
#include <stdlib.h>
#include <syslog.h>
#include <string.h>
#include "defs.h"
#include "mld6.h"
#include "kern.h"
#include "debug.h"
#include "mld6_proto.h"
#include "route.h"
#include "rp.h"
#include "trace.h"
/* TODO */
/*
* Traceroute function which returns traceroute replies to the requesting
* router. Also forwards the request to downstream routers.
*/
void
accept_mtrace(src, dst, group, ifindex, data, no, datalen)
struct sockaddr_in6 *src;
struct in6_addr *dst;
struct in6_addr *group;
int ifindex;
char *data;
u_int no; /* promoted u_char */
int datalen;
{
u_char type;
mrtentry_t *mrt;
struct tr6_query *qry;
struct tr6_resp *resp;
int vifi, ovifi;
char *p;
int rcount;
int errcode = TR_NO_ERR;
int resptype;
struct timeval tp;
struct sioc_mif_req6 mreq;
struct in6_addr parent_address;
struct sockaddr_in6 src_sa6 = {sizeof(src_sa6), AF_INET6};
struct sockaddr_in6 dst_sa6 = {sizeof(dst_sa6), AF_INET6};
struct sockaddr_in6 resp_sa6 = {sizeof(resp_sa6), AF_INET6};
struct sockaddr_in6 grp_sa6 = {sizeof(grp_sa6), AF_INET6};
struct sockaddr_in6 *sa_global;
rpentry_t *rpentry_ptr;
/* Remember qid across invocations */
static u_int32 oqid = 0;
/* timestamp the request/response */
gettimeofday(&tp, 0);
/*
* Check if it is a query or a response
*/
if (datalen == QLEN) {
type = QUERY;
IF_DEBUG(DEBUG_TRACE)
log(LOG_DEBUG, 0, "Initial traceroute query rcvd "
"from %s to %s",
inet6_fmt(&src->sin6_addr),
inet6_fmt(dst));
}
else if ((datalen - QLEN) % RLEN == 0) {
type = RESP;
IF_DEBUG(DEBUG_TRACE)
log(LOG_DEBUG, 0, "In-transit traceroute query rcvd "
"from %s to %s",
inet6_fmt(&src->sin6_addr),
inet6_fmt(dst));
if (IN6_IS_ADDR_MULTICAST(dst)) {
IF_DEBUG(DEBUG_TRACE)
log(LOG_DEBUG, 0, "Dropping multicast response");
return;
}
}
else {
log(LOG_WARNING, 0, "%s from %s to %s",
"Non decipherable traceroute request recieved",
inet6_fmt(&src->sin6_addr), inet6_fmt(dst));
return;
}
qry = (struct tr6_query *)data;
src_sa6.sin6_addr = qry->tr_src;
src_sa6.sin6_scope_id =
(IN6_IS_ADDR_LINKLOCAL(&qry->tr_src)
|| IN6_IS_ADDR_MC_LINKLOCAL(&qry->tr_src)) ? ifindex : 0;
dst_sa6.sin6_addr = qry->tr_dst;
dst_sa6.sin6_scope_id =
(IN6_IS_ADDR_LINKLOCAL(&qry->tr_dst)
|| IN6_IS_ADDR_MC_LINKLOCAL(&qry->tr_dst)) ? ifindex : 0;
grp_sa6.sin6_addr = *group;
grp_sa6.sin6_scope_id = 0;
/*
* if it is a packet with all reports filled, drop it
*/
if ((rcount = (datalen - QLEN)/RLEN) == no) {
IF_DEBUG(DEBUG_TRACE)
log(LOG_DEBUG, 0, "packet with all reports filled in");
return;
}
IF_DEBUG(DEBUG_TRACE) {
log(LOG_DEBUG, 0, "s: %s g: %s d: %s ",
inet6_fmt(&qry->tr_src),
inet6_fmt(group), inet6_fmt(&qry->tr_dst));
log(LOG_DEBUG, 0, "rhlim: %d rd: %s", qry->tr_rhlim,
inet6_fmt(&qry->tr_raddr));
log(LOG_DEBUG, 0, "rcount:%d, qid:%06x", rcount, qry->tr_qid);
}
/* determine the routing table entry for this traceroute */
mrt = find_route(&src_sa6, &grp_sa6, MRTF_SG | MRTF_WC | MRTF_PMBR,
DONT_CREATE);
IF_DEBUG(DEBUG_TRACE) {
if (mrt != (mrtentry_t *)NULL) {
if (mrt->upstream != (pim_nbr_entry_t *)NULL)
parent_address = mrt->upstream->address.sin6_addr;
else
parent_address = in6addr_any;
log(LOG_DEBUG, 0,
"mrt parent mif: %d rtr: %s metric: %d",
mrt->incoming,
inet6_fmt(&parent_address), mrt->metric);
/* TODO
log(LOG_DEBUG, 0, "mrt origin %s",
RT_FMT(rt, s1));
*/
} else
log(LOG_DEBUG, 0, "...no route");
}
/*
* Query type packet - check if rte exists
* Check if the query destination is a vif connected to me.
* and if so, whether I should start response back
*/
if (type == QUERY) {
if (oqid == qry->tr_qid) {
/*
* If the multicast router is a member of the group
* being queried, and the query is multicasted,
* then the router can recieve multiple copies of
* the same query. If we have already replied to
* this traceroute, just ignore it this time.
*
* This is not a total solution, but since if this
* fails you only get N copies, N <= the number of
* interfaces on the router, it is not fatal.
*/
IF_DEBUG(DEBUG_TRACE)
log(LOG_DEBUG, 0,
"ignoring duplicate traceroute packet");
return;
}
if (mrt == (mrtentry_t *)NULL) {
IF_DEBUG(DEBUG_TRACE)
log(LOG_DEBUG, 0,
"Mcast traceroute: no route entry %s",
inet6_fmt(&qry->tr_src));
#if 0
if (IN6_IS_ADDR_MULTICAST(dst))
return;
#endif
}
vifi = find_vif_direct(&dst_sa6);
if (vifi == NO_VIF) {
/*
* The traceroute destination is not on one of
* my subnet vifs.
*/
IF_DEBUG(DEBUG_TRACE)
log(LOG_DEBUG, 0,
"Destination %s not an interface",
inet6_fmt(&qry->tr_dst));
if (IN6_IS_ADDR_MULTICAST(dst))
return;
errcode = TR_WRONG_IF;
} else if (mrt != (mrtentry_t *)NULL &&
!IF_ISSET(vifi, &mrt->oifs)) {
IF_DEBUG(DEBUG_TRACE)
log(LOG_DEBUG, 0,
"Destination %s not on forwarding tree "
"for src %s",
inet6_fmt(&qry->tr_dst),
inet6_fmt(&qry->tr_src));
if (IN6_IS_ADDR_MULTICAST(dst))
return;
errcode = TR_WRONG_IF;
}
}
else {
/*
* determine which interface the packet came in on
* RESP packets travel hop-by-hop so this either traversed
* a tunnel or came from a directly attached mrouter.
*/
if ((vifi = find_vif_direct(src)) == NO_VIF) {
IF_DEBUG(DEBUG_TRACE)
log(LOG_DEBUG, 0,
"Wrong interface for packet");
errcode = TR_WRONG_IF;
}
}
/* Now that we've decided to send a response, save the qid */
oqid = qry->tr_qid;
IF_DEBUG(DEBUG_TRACE)
log(LOG_DEBUG, 0, "Sending traceroute response");
/* copy the packet to the sending buffer */
p = mld6_send_buf + sizeof(struct mld6_hdr);
bcopy(data, p, datalen);
p += datalen;
/*
* If there is no room to insert our reply, coopt the previous hop
* error indication to relay this fact.
*/
if (p + sizeof(struct tr6_resp) > mld6_send_buf + RECV_BUF_SIZE) {
resp = (struct tr6_resp *)p - 1;
resp->tr_rflags = TR_NO_SPACE;
mrt = NULL;
goto sendit;
}
/*
* fill in initial response fields
*/
resp = (struct tr6_resp *)p;
bzero(resp, sizeof(struct tr6_resp));
datalen += (RLEN + sizeof(struct mld6_hdr));
resp->tr_qarr = htonl(((tp.tv_sec + JAN_1970) << 16) +
((tp.tv_usec << 10) / 15625));
resp->tr_rproto = PROTO_PIM;
resp->tr_outifid = (vifi == NO_VIF) ? TR_NO_VIF : htonl(vifi);
resp->tr_rflags = errcode;
if ((sa_global = max_global_address()) == NULL) /* impossible */
log(LOG_ERR, 0, "acept_mtrace: max_global_address returns NULL");
resp->tr_lcladdr = sa_global->sin6_addr;
/*
* obtain # of packets out on interface
*/
mreq.mifi = vifi;
if (vifi != NO_VIF &&
ioctl(udp_socket, SIOCGETMIFCNT_IN6, (char *)&mreq) >= 0)
resp->tr_vifout = htonl(mreq.ocount);
else
resp->tr_vifout = 0xffffffff;
/*
* fill in scoping & pruning information
*/
/* TODO */
#if 0
if (mrt != (mrtentry_t *)NULL)
for (gt = rt->rt_groups; gt; gt = gt->gt_next) {
if (gt->gt_mcastgrp >= group)
break;
}
else
gt = NULL;
if (gt && gt->gt_mcastgrp == group) {
struct stable *st;
for (st = gt->gt_srctbl; st; st = st->st_next)
if (qry->tr_src == st->st_origin)
break;
sg_req.src.s_addr = qry->tr_src;
sg_req.grp.s_addr = group;
if (st && st->st_ctime != 0 &&
ioctl(udp_socket, SIOCGETSGCNT, (char *)&sg_req) >= 0)
resp->tr_pktcnt = htonl(sg_req.pktcnt + st->st_savpkt);
else
resp->tr_pktcnt = htonl(st ? st->st_savpkt : 0xffffffff);
if (VIFM_ISSET(vifi, gt->gt_scope))
resp->tr_rflags = TR_SCOPED;
else if (gt->gt_prsent_timer)
resp->tr_rflags = TR_PRUNED;
else if (!VIFM_ISSET(vifi, gt->gt_grpmems))
if (VIFM_ISSET(vifi, rt->rt_children) &&
NBRM_ISSETMASK(uvifs[vifi].uv_nbrmap,
rt->rt_subordinates)) /*XXX*/
resp->tr_rflags = TR_OPRUNED;
else
resp->tr_rflags = TR_NO_FWD;
} else {
if (scoped_addr(vifi, group))
resp->tr_rflags = TR_SCOPED;
else if (rt && !VIFM_ISSET(vifi, rt->rt_children))
resp->tr_rflags = TR_NO_FWD;
}
#endif /* 0 */
/*
* if no rte exists, set NO_RTE error
*/
if (mrt == (mrtentry_t *)NULL) {
src->sin6_addr = *dst; /* the dst address of resp. pkt */
resp->tr_inifid = TR_NO_VIF;
resp->tr_rflags = TR_NO_RTE;
memset(&resp->tr_rmtaddr, 0, sizeof(struct in6_addr));
} else {
/* get # of packets in on interface */
mreq.mifi = mrt->incoming;
if (ioctl(udp_socket, SIOCGETMIFCNT_IN6, (char *)&mreq) >= 0)
resp->tr_vifin = htonl(mreq.icount);
else
resp->tr_vifin = 0xffffffff;
/*
* TODO
* MASK_TO_VAL(rt->rt_originmask, resp->tr_smask);
*/
resp->tr_inifid = htonl(mrt->incoming);
if (mrt->upstream != (pim_nbr_entry_t *)NULL)
parent_address = mrt->upstream->address.sin6_addr;
else
parent_address = in6addr_any;
resp->tr_rmtaddr = parent_address;
if (!IF_ISSET(vifi, &mrt->oifs)) {
IF_DEBUG(DEBUG_TRACE)
log(LOG_DEBUG, 0,
"Destination %s not on forwarding tree "
"for src %s",
inet6_fmt(&qry->tr_dst),
inet6_fmt(&qry->tr_src));
resp->tr_rflags = TR_WRONG_IF;
}
#if 0
if (rt->rt_metric >= UNREACHABLE) {
resp->tr_rflags = TR_NO_RTE;
/* Hack to send reply directly */
rt = NULL;
}
#endif /* 0 */
}
/*
* If we're the RP for the trace group, note it.
*/
rpentry_ptr = rp_match(&grp_sa6);
if (rpentry_ptr && local_address(&rpentry_ptr->address) != NO_VIF)
resp->tr_rflags = TR_RP;
sendit:
/*
* if metric is 1 or no. of reports is 1, send response to requestor
* else send to upstream router. If the upstream router can't handle
* mtrace, set an error code and send to requestor anyway.
*/
IF_DEBUG(DEBUG_TRACE)
log(LOG_DEBUG, 0, "rcount:%d, no:%d", rcount, no);
ovifi = NO_VIF; /* unspecified */
if ((rcount + 1 == no) || (mrt == NULL) || (mrt->metric == 1)) {
resptype = MLD6_MTRACE_RESP;
resp_sa6.sin6_addr = qry->tr_raddr;
if (IN6_IS_ADDR_LINKLOCAL(&resp_sa6.sin6_addr) ||
IN6_IS_ADDR_MC_LINKLOCAL(&resp_sa6.sin6_addr)) {
if ((ovifi = find_vif_direct(&dst_sa6)) == NO_VIF) {
log(LOG_INFO, 0,
"can't determine outgoing i/f for mtrace "
"response.");
return;
}
}
} else
/* TODO */
{
#if 0
if (!can_mtrace(rt->rt_parent, rt->rt_gateway)) {
resp_sa6.sin6_addr = qry->tr_raddr;
resp->tr_rflags = TR_OLD_ROUTER;
resptype = MLD6_MTRACE_RESP;
} else
#endif /* 0 */
if (mrt->incoming &&
(uvifs[mrt->incoming].uv_flags & MIFF_REGISTER)) {
log(LOG_DEBUG, 0,
"incoming i/f is for register. "
"Can't be forwarded anymore.");
resp_sa6.sin6_addr = qry->tr_raddr;
resptype = MLD6_MTRACE_RESP;
} else {
if (mrt->upstream != (pim_nbr_entry_t *)NULL)
parent_address =
mrt->upstream->address.sin6_addr;
else
parent_address = allrouters_group.sin6_addr;
resp_sa6.sin6_addr = parent_address;
ovifi = mrt->incoming;
resptype = MLD6_MTRACE;
}
}
if (IN6_IS_ADDR_MULTICAST(&resp_sa6.sin6_addr)) {
struct sockaddr_in6 *sa6;
/*
* Send the reply on a known multicast capable vif.
* If we don't have one, we can't source any
* multicasts anyway.
*/
if (IN6_IS_ADDR_MC_LINKLOCAL(&resp_sa6.sin6_addr)) {
sa6 = &uvifs[ovifi].uv_linklocal->pa_addr;
ifindex = uvifs[ovifi].uv_ifindex;
}
else {
if (phys_vif != -1 &&
(sa6 = uv_global(phys_vif)) != NULL) {
IF_DEBUG(DEBUG_TRACE)
log(LOG_DEBUG, 0,
"Sending reply to %s from %s",
inet6_fmt(dst),
inet6_fmt(&sa6->sin6_addr));
ifindex = uvifs[phys_vif].uv_ifindex;
}
else {
log(LOG_INFO, 0, "No enabled phyints -- %s",
"dropping traceroute reply");
return;
}
}
k_set_hlim(mld6_socket, qry->tr_rhlim);
send_mld6(resptype, no, sa6, &resp_sa6, group,
ifindex, 0, datalen, 0);
k_set_hlim(mld6_socket, 1);
} else {
struct sockaddr_in6 *sa6 = NULL;
ifindex = -1; /* unspecified by default */
if (IN6_IS_ADDR_LINKLOCAL(&resp_sa6.sin6_addr)) {
/* ovifi must be valid in this case */
ifindex = uvifs[ovifi].uv_ifindex;
sa6 = &uvifs[ovifi].uv_linklocal->pa_addr;
}
IF_DEBUG(DEBUG_TRACE)
log(LOG_DEBUG, 0, "Sending %s to %s from %s",
resptype == MLD6_MTRACE_RESP ?
"reply" : "request on",
inet6_fmt(dst),
sa6 ? inet6_fmt(&sa6->sin6_addr) : "unspecified");
send_mld6(resptype, no, sa6, &resp_sa6, group, ifindex,
0, datalen, 0);
}
return;
}