diff options
Diffstat (limited to 'usr.sbin/pim6dd/trace.c')
-rw-r--r-- | usr.sbin/pim6dd/trace.c | 540 |
1 files changed, 540 insertions, 0 deletions
diff --git a/usr.sbin/pim6dd/trace.c b/usr.sbin/pim6dd/trace.c new file mode 100644 index 0000000..1556097 --- /dev/null +++ b/usr.sbin/pim6dd/trace.c @@ -0,0 +1,540 @@ +/* + * 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. + */ +/* + * Questions concerning this software should be directed to + * Pavlin Ivanov Radoslavov (pavlin@catarina.usc.edu) + * + * $Id: trace.c,v 1.5 1999/09/16 08:46:00 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. + * + * $FreeBSD$ + */ + + +#include "defs.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; +#ifdef SM_ONLY + rpentry_t *rpentry_ptr; +#endif + + /* 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 */ + } + +#ifdef SM_ONLY + /* + * 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; +#endif /* SM_ONLY */ + + 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 */ +#ifdef SM_ONLY + 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 +#endif /* SM_ONLY */ + { + 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; +} |