output.c revision e704a8f24a369484ba8f4a1cf49d4db00dd91166
/*
* Copyright 2007 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*
* Copyright (c) 1983, 1988, 1993
* The Regents of the University of California. 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
* 3. All advertising materials mentioning features or use of this software
* must display the following acknowledgment:
* This product includes software developed by the University of
* California, Berkeley and its contributors.
* 4. Neither the name of the University 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 REGENTS 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 REGENTS 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.
*
*/
#pragma ident "%Z%%M% %I% %E% SMI"
#include "defs.h"
#include <md5.h>
#include <alloca.h>
/*
* walk the tree of routes with this for output
*/
static struct {
struct sockaddr_in to;
struct auth *a;
#define WS_GEN_LIMIT_MAX 1024
} ws;
/* A buffer for what can be heard by both RIPv1 and RIPv2 listeners */
static union pkt_buf ripv12_buf;
/* Another for only RIPv2 listeners */
static union pkt_buf rip_v2_buf;
void
bufinit(void)
{
}
/*
* Send the contents of the global buffer via the non-multicast socket
*/
int /* <0 on failure */
int size) /* this many bytes */
{
struct sockaddr_in sin;
int flags;
const char *msg;
int res;
int ifindex;
flags = 0;
msglog("Cannot send RIP message to %s",
return (-1);
}
switch (type) {
case OUT_QUERY:
msg = "Answer Query";
break;
case OUT_UNICAST:
msg = "Send";
break;
case OUT_BROADCAST:
msg = "Send bcast";
break;
case OUT_MULTICAST:
msg = "Send mcast";
break;
case NO_OUT_MULTICAST:
case NO_OUT_RIPV2:
default:
#ifdef DEBUG
abort();
#endif
return (-1);
}
/*
* IP_PKTINFO overrides IP_MULTICAST_IF, so we don't set ifindex
* for multicast traffic.
*/
if (rip_sock_interface != ifp) {
/*
* For multicast, we have to choose the source
* address. This is either the local address
* (non-point-to-point) or the remote address.
*/
if (type == OUT_MULTICAST &&
LOGERR("setsockopt(rip_sock, IP_MULTICAST_IF)");
return (-1);
}
}
}
}
return (res);
}
/*
* Semantically identical to sendto(), but sends the message through a
* specific interface (if ifindex is non-zero) using IP_PKTINFO.
*/
int
{
struct in_pktinfo *ipip;
if (ifindex != 0) {
/*
* We can't precisely predict the alignment padding we'll
* need, so we allocate the maximum alignment and then
* use CMSG_NXTHDR() to fix it up at the end.
*/
/*
* Correct the control message length.
*/
}
}
/*
* Find the first key for a packet to send.
* Try for a key that is eligible and has not expired, but settle for
* the last key if they have all expired.
* If no key is ready yet, give up.
*/
struct auth *
{
int i;
return (NULL);
return (NULL);
for (i = 0; i < MAX_AUTH_KEYS; i++, ap++) {
/* stop looking after the last key */
break;
/* ignore keys that are not ready yet */
continue;
/* note best expired password as a fall-back */
continue;
}
/* note key with the best future */
}
return (res);
}
void
{
/*
* (start to) install authentication if appropriate
*/
return;
wb->n++;
wb->n++;
}
}
void
{
int len;
wb->n++;
}
/*
* Send the buffer
*/
static void
{
/*
* Output multicast only if legal.
* If we would multicast and it would be illegal, then discard the
* packet.
*/
case NO_OUT_MULTICAST:
trace_pkt("skip multicast to %s because impossible",
break;
case NO_OUT_RIPV2:
break;
default:
break;
}
}
/*
* Put an entry into the packet
*/
static void
{
/*
* Skip this route if doing a flash update and it and the routes
* it aggregates have not changed recently.
*/
return;
dstcount = 0;
/*
* If we are sending RIPv2 packets that cannot (or must not) be
* heard by RIPv1 listeners, do not worry about sub- or supernets.
* Subnets (from other networks) can only be sent via multicast.
* A pair of subnet routes might have been promoted so that they
* are legal to send by RIPv1.
* If RIPv1 is off, use the multicast buffer.
*/
/* use the RIPv2-only buffer */
} else {
/*
* use the RIPv1-or-RIPv2 buffer
*/
/*
* Convert supernet route into corresponding set of network
* routes for RIPv1, but leave non-contiguous netmasks
* to ag_check().
*/
/*
* Punt if we would have to generate an
* unreasonable number of routes.
*/
if (TRACECONTENTS)
trace_misc("sending %s-->%s as 1"
" instead of %d routes",
dstcount + 1);
dstcount = 0;
} else {
}
}
}
do {
/*
* If the route is from router-discovery or we are
* admit only a bad metric.
*/
/*
* Any non-zero bits in the supposedly unused RIPv1 fields
* cause the old `routed` to ignore the route.
* That means the mask and so forth cannot be sent
*/
NULL))
}
} while (dstcount-- > 0);
}
/*
* Supply one route from the table
*/
/* ARGSUSED */
static int
{
/*
* Do not advertise external remote interfaces or passive interfaces.
*/
return (0);
/*
*/
return (0);
/*
* Do not advertise routes which would lead to forwarding on a
* non-forwarding interface.
*/
return (0);
/*
* If being quiet about our ability to forward, then
* do not say anything unless responding to a query,
* except about our main interface.
*/
return (0);
/*
* do not collide with the fake default route
*/
return (0);
/*
* Do not send manual synthetic network routes
* into the subnet.
*/
return (0);
} else {
/*
* Do not send automatic synthetic network routes
* if they are not needed because no RIPv1 listeners
* can hear them.
*/
return (0);
/*
* Do not send automatic synthetic network routes to
* the real subnet.
*/
return (0);
}
nhop = 0;
} else {
/*
* Advertise the next hop if this is not a route for one
* of our interfaces and the next hop is on the same
* network as the target.
* The final determination is made by supply_out().
*/
else
nhop = 0;
}
ags = 0;
/*
* Always suppress network routes into other, existing
* network routes
*/
ags |= AGS_SUPPRESS;
/*
* Generate supernets if allowed.
* If we can be heard by RIPv1 systems, we will
* later convert back to ordinary nets.
* This unifies dealing with received supernets.
*/
ags |= AGS_AGGREGATE;
/*
* We should always suppress (into existing network routes)
* the host routes for the local end of our point-to-point
* links.
* If we are suppressing host routes in general, then do so.
* Avoid advertising host routes onto their own network,
* where they should be handled by proxy-ARP.
*/
ags |= AGS_SUPPRESS;
/*
* Aggregate stray host routes into network routes if allowed.
* We cannot aggregate host routes into small network routes
* without confusing RIPv1 listeners into thinking the
* network routes are host routes.
*/
ags |= AGS_AGGREGATE;
}
/*
* Do not send RIPv1 advertisements of subnets to other
* networks. If possible, multicast them by RIPv2.
*/
/*
* Do not send a route back to where it came from, except in
* response to a query. This is "split-horizon". That means not
* advertising back to the same network and so via the same interface.
*
* We want to suppress routes that might have been fragmented
* from this route by a RIPv1 router and sent back to us, and so we
* cannot forget this route here. Let the split-horizon route
* suppress the fragmented routes and then itself be forgotten.
*
* Include the routes for both ends of point-to-point interfaces
* among those suppressed by split-horizon, since the other side
* should knows them as well as we do.
*
* Notice spare routes with the same metric that we are about to
* advertise, to split the horizon on redundant, inactive paths.
*/
continue;
/*
* If we do not mark the route with AGS_SPLIT_HZ here,
* it will be poisoned-reverse, or advertised back
* toward its source with an infinite metric.
* If we have recently advertised the route with a
* better metric than we now have, then we should
* poison-reverse the route before suppressing it for
* split-horizon.
*
* In almost all cases, if there is no spare for the
* route then it is either old and dead or a brand
* new route. If it is brand new, there is no need
* for poison-reverse. If it is old and dead, it
* is already poisoned.
*/
ags |= AGS_SPLIT_HZ;
ags &= ~AGS_SUPPRESS;
}
break;
}
}
/*
* Keep track of the best metric with which the
* route has been advertised recently.
*/
}
/*
* Adjust the outgoing metric by the cost of the link.
* Avoid aggregation when a route is counting to infinity.
*/
/*
* If this is a static route pointing to the same interface
* upon which we are sending out the RIP RESPONSE
* adjust the preference so that we don't aggregate into this
* route. Note that the maximum possible hop count on a route
* per RFC 2453 is 16 (HOPCNT_INFINITY)
*/
/*
* Do not advertise stable routes that will be ignored,
* unless we are answering a query.
* If the route recently was advertised with a metric that
* would have been less than infinity through this interface,
* we need to continue to advertise it in order to poison it.
*/
if (metric >= HOPCNT_INFINITY) {
return (0);
}
/*
* and so can ignore all rt_spares[i] with i > 0
*/
return (0);
}
/*
* Supply dst with the contents of the routing tables.
* If this won't fit in one packet, chop it up into several.
*/
void
enum output_type type,
int flash, /* 1=flash update */
int vers, /* RIP version */
{
type == OUT_MULTICAST)
} else {
else
return;
}
if (flash)
/*
* Routes in the table were already adjusted by their respective
* destination interface costs (which are zero by default) on
* input. The following is the value by which each route's metric
* will be bumped up on output.
*/
switch (type) {
case OUT_MULTICAST:
else
break;
case OUT_QUERY:
/* FALLTHROUGH */
case OUT_BROADCAST:
case OUT_UNICAST:
break;
case NO_OUT_MULTICAST:
case NO_OUT_RIPV2:
return; /* no output */
}
/* full RIPv2 only if cannot be heard by RIPv1 listeners */
if (type != OUT_BROADCAST)
}
/* See if this packet needs authenticating */
!ws.a->warnedflag) {
/*
* If the best key is an expired one, we may as
* well use it. Log this event.
*/
"Using expired auth while transmitting to %s",
}
} else {
}
/*
* Fake a default route if asked and if there is not already
* a better, real default route.
*/
0, 0, 0, supply_out);
} else {
}
/*
* If both RIPv2 and the poor-man's router discovery
* kludge are on, arrange to advertise an extra
* default route via RIPv1.
*/
v12buf.n++;
}
}
ag_flush(0, 0, supply_out);
/*
* Flush the packet buffers, provided they are not empty and
* do not contain only the password.
*/
/*
* If we sent nothing and this is an answer to a query, send
* an empty buffer.
*/
}
}
/*
* send all of the routing table or just do a flash update
*/
void
{
enum output_type type;
int vers;
if (!rip_enabled)
return;
trace_act("send %s and inhibit dynamic updates for %.3f sec",
/*
* Skip interfaces not doing RIP or for which IP
* forwarding isn't turned on. Skip duplicate
* interfaces, we don't want to generate duplicate
* packets. Do try broken interfaces to see if they
* have healed.
*/
continue;
/* skip turned off interfaces */
continue;
/* skip interfaces we shouldn't use */
continue;
/*
* Ignore the interface if it's not broadcast,
* point-to-point, or remote. It must be non-broadcast
* multiaccess, and therefore unsupported.
*/
continue;
}
update_seqno++; /* all routes are up to date */
}
/*
* Ask for routes
* Do it only once to an interface, and not even after the interface
* was broken and recovered.
*/
void
rip_query(void)
{
enum output_type type;
if (!rip_enabled)
return;
/*
* Skip interfaces those already queried. Do not ask
* via interfaces through which we don't accept input.
* Do not ask via interfaces that cannot send RIP
* packets. Don't send queries on duplicate
* interfaces, that would generate duplicate packets
* on link. Do try broken interfaces to see if they
* have healed.
*/
continue;
/* skip turned off interfaces */
continue;
/* skip interfaces we shouldn't use */
continue;
/*
* Ignore the interface if it's not broadcast,
* point-to-point, or remote. It must be non-broadcast
* multiaccess, and therefore unsupported.
*/
continue;
/*
* Send a RIPv1 query only if allowed and if we will
* listen to RIPv1 routers.
*/
} else {
}
}
}