1
0
mirror of https://git.FreeBSD.org/src.git synced 2024-12-05 09:14:03 +00:00

mrouted from multicast 3.3 distribution

This commit is contained in:
Garrett Wollman 1994-09-08 00:26:13 +00:00
commit 15fe6b8712
Notes: svn2git 2020-12-20 02:59:44 +00:00
svn path=/cvs2svn/branches/XEROX/; revision=2555
svn path=/cvs2svn/tags/MULTICAST_3_3/; revision=2557; tag=vendor/mrouted/3.3
20 changed files with 8519 additions and 0 deletions

48
usr.sbin/mrouted/LICENSE Normal file
View File

@ -0,0 +1,48 @@
The mrouted program is covered by the following license. Use of the
mrouted program represents acceptance of these terms and conditions.
1. STANFORD grants to LICENSEE a nonexclusive and nontransferable license
to use, copy and modify the computer software ``mrouted'' (hereinafter
called the ``Program''), upon the terms and conditions hereinafter set
out and until Licensee discontinues use of the Licensed Program.
2. LICENSEE acknowledges that the Program is a research tool still in
the development state, that it is being supplied ``as is,'' without any
accompanying services from STANFORD, and that this license is entered
into in order to encourage scientific collaboration aimed at further
development and application of the Program.
3. LICENSEE may copy the Program and may sublicense others to use object
code copies of the Program or any derivative version of the Program.
All copies must contain all copyright and other proprietary notices found
in the Program as provided by STANFORD. Title to copyright to the
Program remains with STANFORD.
4. LICENSEE may create derivative versions of the Program. LICENSEE
hereby grants STANFORD a royalty-free license to use, copy, modify,
distribute and sublicense any such derivative works. At the time
LICENSEE provides a copy of a derivative version of the Program to a
third party, LICENSEE shall provide STANFORD with one copy of the source
code of the derivative version at no charge to STANFORD.
5. STANFORD MAKES NO REPRESENTATIONS OR WARRANTIES, EXPRESS OR IMPLIED.
By way of example, but not limitation, STANFORD MAKES NO REPRESENTATION
OR WARRANTIES OF MERCHANTABILITY OR FITNESS FOR ANY PARTICULAR PURPOSE OR
THAT THE USE OF THE LICENSED PROGRAM WILL NOT INFRINGE ANY PATENTS,
COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS. STANFORD shall not be held liable
for any liability nor for any direct, indirect or consequential damages
with respect to any claim by LICENSEE or any third party on account of or
arising from this Agreement or use of the Program.
6. This agreement shall be construed, interpreted and applied in
accordance with the State of California and any legal action arising
out of this Agreement or use of the Program shall be filed in a court
in the State of California.
7. Nothing in this Agreement shall be construed as conferring rights to
use in advertising, publicity or otherwise any trademark or the name
of ``Stanford''.
The mrouted program is COPYRIGHT 1989 by The Board of Trustees of
Leland Stanford Junior University.

201
usr.sbin/mrouted/callout.c Normal file
View File

@ -0,0 +1,201 @@
/*
* The mrouted program is covered by the license in the accompanying file
* named "LICENSE". Use of the mrouted program represents acceptance of
* the terms and conditions listed in that file.
*
* The mrouted program is COPYRIGHT 1989 by The Board of Trustees of
* Leland Stanford Junior University.
*
*
* $Id: callout.c,v 1.1 1994/08/24 23:52:49 thyagara Exp $
*/
#include "defs.h"
/* the code below implements a callout queue */
static int id = 0;
static struct timeout_q *Q = 0; /* pointer to the beginning of timeout queue */
static int in_callout= 0;
typedef void (* cfunc_t)();
struct timeout_q {
struct timeout_q *next; /* next event */
int id;
cfunc_t func ; /* function to call */
char *data; /* func's data */
int time; /* time offset to next event*/
};
callout_init()
{
Q = (struct timeout_q *) 0;
}
/*
* signal handler for SIGALARM that is called once every second
*/
age_callout_queue()
{
struct timeout_q *ptr;
if (in_callout)
return;
in_callout = 1;
ptr = Q;
while (ptr){
if (!ptr->time ) {
/* timeout has happened */
if(ptr->func)
ptr->func(ptr->data);
Q = Q->next;
free(ptr);
ptr = Q;
}
else {
ptr->time --;
#ifdef IGMP_DEBUG
log(LOG_DEBUG,0,"[callout, age_callout_queue] -- time (%d)", ptr->time);
#endif IGMP_DEBUG
in_callout = 0; return;
}
}
in_callout = 0;
return;
}
/*
* sets the timer
*/
int timer_setTimer(delay, action, data)
int delay; /* number of units for timeout */
cfunc_t action; /* function to be called on timeout */
char *data; /* what to call the timeout function with */
{
struct timeout_q *ptr, *node, *prev;
if (in_callout)
return;
in_callout = 1;
/* create a node */
node = (struct timeout_q *)malloc(sizeof(struct timeout_q));
if ((int) node <= 0) {
log(LOG_WARNING, 0, "Malloc Failed in timer_settimer\n");
in_callout = 0;
return -1;
}
node->func = action;
node->data = data;
node->time = delay;
node->next = 0;
node->id = ++id;
prev = ptr = Q;
/* insert node in the queue */
/* if the queue is empty, insert the node and return */
if (!Q)
Q = node;
else {
/* chase the pointer looking for the right place */
while (ptr){
if (delay < ptr->time){
/* right place */
node->next = ptr;
if (ptr == Q)
Q = node;
else
prev->next = node;
ptr->time -= node->time;
print_Q();
in_callout = 0;
return node->id;
}
else {
/* keep moving */
delay -= ptr->time; node->time = delay;
prev = ptr;
ptr = ptr->next;
}
}
prev->next = node;
}
print_Q();
in_callout = 0;
return node->id;
}
/* clears the associated timer */
void timer_clearTimer( id)
int id;
{
struct timeout_q *ptr, *prev;
if (in_callout) return;
in_callout = 1;
if ( !id ) {in_callout = 0; return;}
prev = ptr = Q;
/*
* find the right node, delete it. the subsequent node's time
* gets bumped up
*/
print_Q();
while (ptr){
if (ptr->id == id){
/* got the right node */
/* unlink it from the queue */
if ( ptr == Q)
Q = Q->next;
else
prev->next = ptr->next;
/* increment next node if any */
if (ptr->next != 0)
(ptr->next)->time += ptr->time;
free(ptr->data);
free(ptr);
print_Q();
in_callout = 0;
return;
}
prev = ptr;
ptr = ptr->next;
}
print_Q();
in_callout = 0;
}
/*
* debugging utility
*/
print_Q()
{
struct timeout_q *ptr;
#ifdef IGMP_DEBUG
for(ptr = Q; ptr; ptr = ptr->next)
log(LOG_DEBUG,0,"(%d,%d) ", ptr->id, ptr->time);
#endif IGMP_DEBUG
}

796
usr.sbin/mrouted/config.c Normal file
View File

@ -0,0 +1,796 @@
/*
* The mrouted program is covered by the license in the accompanying file
* named "LICENSE". Use of the mrouted program represents acceptance of
* the terms and conditions listed in that file.
*
* The mrouted program is COPYRIGHT 1989 by The Board of Trustees of
* Leland Stanford Junior University.
*
*
* $Id: config.c,v 1.6 1994/08/24 23:52:54 thyagara Exp $
*/
#include "defs.h"
char *configfilename = "/etc/mrouted.conf";
extern int cache_lifetime;
extern int max_prune_lifetime;
/*
* Forward declarations.
*/
static char *next_word();
/*
* Query the kernel to find network interfaces that are multicast-capable
* and install them in the uvifs array.
*/
void config_vifs_from_kernel()
{
struct ifreq ifbuf[32];
struct ifreq *ifrp, *ifend, *mp;
struct ifconf ifc;
register struct uvif *v;
register vifi_t vifi;
int i, n;
u_long addr, mask, subnet;
short flags;
ifc.ifc_buf = (char *)ifbuf;
ifc.ifc_len = sizeof(ifbuf);
if (ioctl(udp_socket, SIOCGIFCONF, (char *)&ifc) < 0)
log(LOG_ERR, errno, "ioctl SIOCGIFCONF");
ifrp = (struct ifreq *)ifbuf;
ifend = (struct ifreq *)((char *)ifbuf + ifc.ifc_len);
/*
* Loop through all of the interfaces.
*/
for (; ifrp < ifend; ifrp = (struct ifreq *)((char *)ifrp + n)) {
struct ifreq ifr;
#if BSD >= 199006
n = ifrp->ifr_addr.sa_len + sizeof(ifrp->ifr_name);
if (n < sizeof(*ifrp))
n = sizeof(*ifrp);
#else
n = sizeof(*ifrp);
#endif
/*
* Ignore any interface for an address family other than IP.
*/
addr = ((struct sockaddr_in *)&ifrp->ifr_addr)->sin_addr.s_addr;
if (ifrp->ifr_addr.sa_family != AF_INET)
continue;
/*
* Need a template to preserve address info that is
* used below to locate the next entry. (Otherwise,
* SIOCGIFFLAGS stomps over it because the requests
* are returned in a union.)
*/
bcopy(ifrp->ifr_name, ifr.ifr_name, sizeof(ifr.ifr_name));
/*
* Ignore loopback interfaces and interfaces that do not support
* multicast.
*/
if (ioctl(udp_socket, SIOCGIFFLAGS, (char *)&ifr) < 0)
log(LOG_ERR, errno, "ioctl SIOCGIFFLAGS for %s", ifr.ifr_name);
flags = ifr.ifr_flags;
if ((flags & (IFF_LOOPBACK|IFF_MULTICAST)) != IFF_MULTICAST) continue;
/*
* Ignore any interface whose address and mask do not define a
* valid subnet number, or whose address is of the form {subnet,0}
* or {subnet,-1}.
*/
if (ioctl(udp_socket, SIOCGIFNETMASK, (char *)&ifr) < 0)
log(LOG_ERR, errno, "ioctl SIOCGIFNETMASK for %s", ifr.ifr_name);
mask = ((struct sockaddr_in *)&ifr.ifr_addr)->sin_addr.s_addr;
subnet = addr & mask;
if (!inet_valid_subnet(subnet, mask) ||
addr == subnet ||
addr == (subnet | ~mask)) {
log(LOG_WARNING, 0,
"ignoring %s, has invalid address (%s) and/or mask (%08x)",
ifr.ifr_name, inet_fmt(addr, s1), ntohl(mask));
continue;
}
/*
* Ignore any interface that is connected to the same subnet as
* one already installed in the uvifs array.
*/
for (vifi = 0, v = uvifs; vifi < numvifs; ++vifi, ++v) {
if ((addr & v->uv_subnetmask) == v->uv_subnet ||
(v->uv_subnet & mask) == subnet) {
log(LOG_WARNING, 0, "ignoring %s, same subnet as %s",
ifr.ifr_name, v->uv_name);
break;
}
}
if (vifi != numvifs) continue;
/*
* If there is room in the uvifs array, install this interface.
*/
if (numvifs == MAXVIFS) {
log(LOG_WARNING, 0, "too many vifs, ignoring %s", ifr.ifr_name);
continue;
}
v = &uvifs[numvifs];
v->uv_flags = 0;
v->uv_metric = DEFAULT_METRIC;
v->uv_rate_limit = DEFAULT_RATE_LIMIT;
v->uv_threshold = DEFAULT_THRESHOLD;
v->uv_lcl_addr = addr;
v->uv_rmt_addr = 0;
v->uv_subnet = subnet;
v->uv_subnetmask = mask;
v->uv_subnetbcast = subnet | ~mask;
strncpy(v->uv_name, ifr.ifr_name, IFNAMSIZ);
v->uv_groups = NULL;
v->uv_neighbors = NULL;
v->uv_acl = NULL;
log(LOG_INFO,0,"installing %s (%s on subnet %s) as vif #%u - rate=%d",
v->uv_name, inet_fmt(addr, s1), inet_fmts(subnet, mask, s2),
numvifs, v->uv_rate_limit);
++numvifs;
/*
* If the interface is not yet up, set the vifs_down flag to
* remind us to check again later.
*/
if (!(flags & IFF_UP)) {
v->uv_flags |= VIFF_DOWN;
vifs_down = TRUE;
}
}
}
static struct ifreq *
ifconfaddr(ifcp, a)
struct ifconf *ifcp;
u_long a;
{
int n;
struct ifreq *ifrp = (struct ifreq *)ifcp->ifc_buf;
struct ifreq *ifend = (struct ifreq *)((char *)ifrp + ifcp->ifc_len);
while (ifrp < ifend) {
if (ifrp->ifr_addr.sa_family == AF_INET &&
((struct sockaddr_in *)&ifrp->ifr_addr)->sin_addr.s_addr == a)
return (ifrp);
#if BSD >= 199006
n = ifrp->ifr_addr.sa_len + sizeof(ifrp->ifr_name);
if (n < sizeof(*ifrp))
++ifrp;
else
ifrp = (struct ifreq *)((char *)ifrp + n);
#else
++ifrp;
#endif
}
return (0);
}
/*
* Checks if the string constitutes a valid interface name
*/
static u_long valid_if(w)
char *w;
{
register vifi_t vifi;
register struct uvif *v;
for (vifi = 0, v = uvifs; vifi < numvifs; vifi++, v++)
if (EQUAL(v->uv_name, w))
return v->uv_lcl_addr;
return NULL;
}
/*
* Read the config file to learn about tunnel vifs and
* non-default phyint parameters.
*/
void config_vifs_from_file()
{
FILE *f;
char linebuf[100];
char *w, *s, c;
u_long lcl_addr, rmt_addr;
struct ifconf ifc;
struct ifreq *ifr;
struct ifreq ffr;
int i;
u_int n;
struct ifreq ifbuf[32];
vifi_t vifi;
struct uvif *v;
u_char order = 0;
vifi_t prev_vif = NO_VIF;
f = fopen(configfilename, "r");
if (f == NULL) {
if (errno != ENOENT)
log(LOG_ERR, errno, "can't open %s", configfilename);
return;
}
ifc.ifc_buf = (char *)ifbuf;
ifc.ifc_len = sizeof(ifbuf);
if (ioctl(udp_socket, SIOCGIFCONF, (char *)&ifc) < 0)
log(LOG_ERR, errno, "ioctl SIOCGIFCONF");
while (fgets(linebuf, sizeof(linebuf), f) != NULL) {
s = linebuf;
if (EQUAL((w = next_word(&s)), "")) {
/*
* blank or comment line; ignore
*/
}
/* Set the cache_lifetime for kernel entries */
else if (EQUAL(w, "cache_lifetime")) {
if (EQUAL((w = next_word(&s)), "")) {
log(LOG_ERR, 0,
"missing cache_lifetime value in %s",
configfilename);
continue;
}
if(sscanf(w, "%u%c", &n, &c) != 1 ||
n < 300 || n > 86400 ) {
log(LOG_ERR, 0,
"invalid cache_lifetime '%s' (300<n>86400) in %s",
w, configfilename);
break;
}
prev_vif = NO_VIF;
cache_lifetime = n;
max_prune_lifetime = cache_lifetime * 2;
}
/* Check if pruning is to be turned off */
else if (EQUAL(w, "pruning")) {
if (!EQUAL((w = next_word(&s)), "off") &&
!EQUAL(w, "on")) {
log(LOG_ERR, 0,
"invalid word '%s' in %s",
w, configfilename);
continue;
}
if (EQUAL(w, "off"))
pruning = 0;
prev_vif = NO_VIF;
}
/* Check for boundary statements (as continuation of a prev. line) */
else if (EQUAL(w, "boundary") && prev_vif != NO_VIF) {
register struct vif_acl *v_acl;
register u_long baddr;
v = &uvifs[prev_vif];
if (EQUAL((w = next_word(&s)), "")) {
log(LOG_ERR, 0,
"missing group address for boundary %s in %s",
inet_fmt(lcl_addr, s1), configfilename);
w = "garbage";
break;
}
if ((sscanf(w, "%[0-9.]/%d", s1, &n) != 2) ||
n < 0 || n> 32) {
log(LOG_ERR, 0,
"incorrect boundary format %s in %s",
w, configfilename);
w = "garbage";
break;
}
if ((baddr = inet_parse(s1)) == 0xffffffff ||
(baddr & 0xff000000) != 0xef000000) {
log(LOG_ERR, 0,
"incorrect boundary address %s in %s",
s1, configfilename);
continue;
}
v_acl = (struct vif_acl *)malloc(sizeof(struct vif_acl));
if (v_acl == NULL)
log(LOG_ERR, 0,
"out of memory");
VAL_TO_MASK(v_acl->acl_mask, n);
v_acl->acl_addr = baddr & v_acl->acl_mask;
/*
* link into data structure
*/
v_acl->acl_next = v->uv_acl;
v->uv_acl = v_acl;
}
else if (EQUAL(w, "phyint")) {
/*
* phyint <local-addr> [disable] [metric <m>] [threshold <t>]
* [rate_limit <b>]
*/
/*
* Check if phyint was the first line - scream if not
*/
if (order) {
log(LOG_ERR, 0,
"phyint stmnts should occur before tunnel stmnts in %s",
configfilename);
continue;
}
/*
* Parse the local address.
*/
if (EQUAL((w = next_word(&s)), "")) {
log(LOG_ERR, 0,
"missing phyint address in %s",
configfilename);
continue;
}
if (isalpha(*w) && !(lcl_addr = valid_if(w))) {
log(LOG_ERR, 0,
"invalid phyint name '%s' in %s",
w, configfilename);
continue;
}
if (isdigit(*w)) {
if ((lcl_addr = inet_parse(w)) == 0xffffffff ||
!inet_valid_host(lcl_addr)) {
log(LOG_ERR, 0,
"invalid phyint address '%s' in %s",
w, configfilename);
continue;
}
}
/*
* Look up the vif with the specified local address.
*/
for (vifi = 0, v = uvifs; vifi < numvifs; ++vifi, ++v) {
if (!(v->uv_flags & VIFF_TUNNEL) &&
lcl_addr == v->uv_lcl_addr) {
break;
}
}
if (vifi == numvifs) {
log(LOG_ERR, 0,
"phyint %s in %s is not a configured interface",
inet_fmt(lcl_addr, s1), configfilename);
continue;
}
/*
* Look for "disable", "metric", "threshold", "rate_limit"
* and "boundary" options.
*/
prev_vif = vifi;
while (!EQUAL((w = next_word(&s)), "")) {
if (EQUAL(w, "disable")) {
v->uv_flags |= VIFF_DISABLED;
}
else if (EQUAL(w, "metric")) {
if(EQUAL((w = next_word(&s)), "")) {
log(LOG_ERR, 0,
"missing metric for phyint %s in %s",
inet_fmt(lcl_addr, s1), configfilename);
w = "garbage";
break;
}
if(sscanf(w, "%u%c", &n, &c) != 1 ||
n < 1 || n >= UNREACHABLE ) {
log(LOG_ERR, 0,
"invalid metric '%s' for phyint %s in %s",
w, inet_fmt(lcl_addr, s1), configfilename);
break;
}
v->uv_metric = n;
}
else if (EQUAL(w, "threshold")) {
if(EQUAL((w = next_word(&s)), "")) {
log(LOG_ERR, 0,
"missing threshold for phyint %s in %s",
inet_fmt(lcl_addr, s1), configfilename);
w = "garbage";
break;
}
if(sscanf(w, "%u%c", &n, &c) != 1 ||
n < 1 || n > 255 ) {
log(LOG_ERR, 0,
"invalid threshold '%s' for phyint %s in %s",
w, inet_fmt(lcl_addr, s1), configfilename);
break;
}
v->uv_threshold = n;
}
else if (EQUAL(w, "rate_limit")) {
if (EQUAL((w = next_word(&s)), "")) {
log(LOG_ERR, 0,
"missing rate_limit for phyint %s in %s",
inet_fmt(rmt_addr, s1), configfilename);
w = "garbage";
break;
}
if(sscanf(w, "%u%c", &n, &c) != 1 ||
n < 0 || n > MAX_RATE_LIMIT ) {
log(LOG_ERR, 0,
"invalid rate limit '%s' for phyint %s in %s",
w, inet_fmt(lcl_addr, s1), configfilename);
break;
}
v->uv_rate_limit = n;
}
else if (EQUAL(w, "boundary")) {
register struct vif_acl *v_acl;
register u_long baddr;
if (EQUAL((w = next_word(&s)), "")) {
log(LOG_ERR, 0,
"missing group address for boundary %s in %s",
inet_fmt(lcl_addr, s1), configfilename);
w = "garbage";
break;
}
if ((sscanf(w, "%[0-9.]/%d", s1, &n) != 2) ||
n < 0 || n> 32) {
log(LOG_ERR, 0,
"incorrect boundary format %s in %s",
w, configfilename);
w = "garbage";
break;
}
if ((baddr = inet_parse(s1)) == 0xffffffff ||
(baddr & 0xef000000) != 0xef000000) {
log(LOG_ERR, 0,
"incorrect boundary address %s in %s",
s1, configfilename);
continue;
}
v_acl = (struct vif_acl *)malloc(sizeof(struct vif_acl));
if (v_acl == NULL)
log(LOG_ERR, 0,
"out of memory");
VAL_TO_MASK(v_acl->acl_mask, n);
v_acl->acl_addr = baddr & v_acl->acl_mask;
/*
* link into data structure
*/
v_acl->acl_next = v->uv_acl;
v->uv_acl = v_acl;
}
else {
log(LOG_ERR, 0,
"invalid keyword (%s) in %s",
w, configfilename);
break;
}
}
if (!EQUAL(w, "")) continue;
}
else if (EQUAL(w, "tunnel")) {
/*
* tunnel <local-addr> <remote-addr> [srcrt] [metric <m>]
* [threshold <t>] [rate_limit <b>]
*/
order++;
/*
* Parse the local address.
*/
if (EQUAL((w = next_word(&s)), "")) {
log(LOG_ERR, 0,
"missing tunnel local address in %s",
configfilename);
continue;
}
if ((lcl_addr = inet_parse(w)) == 0xffffffff ||
!inet_valid_host(lcl_addr)) {
log(LOG_ERR, 0,
"invalid tunnel local address '%s' in %s",
w, configfilename);
continue;
}
/*
* Make sure the local address is one of ours.
*/
ifr = ifconfaddr(&ifc, lcl_addr);
if (ifr == 0) {
log(LOG_ERR, 0,
"tunnel local address %s in %s is not one of ours",
inet_fmt(lcl_addr, s1), configfilename);
continue;
}
/*
* Make sure the local address doesn't name a loopback interface..
*/
strncpy(ffr.ifr_name, ifr->ifr_name, IFNAMSIZ);
if (ioctl(udp_socket, SIOCGIFFLAGS, (char *)&ffr) < 0) {
log(LOG_ERR, errno,
"ioctl SIOCGIFFLAGS for %s", ffr.ifr_name);
}
if (ffr.ifr_flags & IFF_LOOPBACK) {
log(LOG_ERR, 0,
"tunnel local address %s in %s is a loopback interface",
inet_fmt(lcl_addr, s1), configfilename);
continue;
}
/*
* Parse the remote address.
*/
if (EQUAL((w = next_word(&s)), "")) {
log(LOG_ERR, 0,
"missing tunnel remote address in %s",
configfilename);
continue;
}
if ((rmt_addr = inet_parse(w)) == 0xffffffff ||
!inet_valid_host(rmt_addr)) {
log(LOG_ERR, 0,
"invalid tunnel remote address %s in %s",
w, configfilename);
continue;
}
/*
* Make sure the remote address is not one of ours.
*/
if (ifconfaddr(&ifc, rmt_addr) != 0) {
log(LOG_ERR, 0,
"tunnel remote address %s in %s is one of ours",
inet_fmt(rmt_addr, s1), configfilename);
continue;
}
/*
* Make sure the remote address has not been used for another
* tunnel and does not belong to a subnet to which we have direct
* access on an enabled phyint.
*/
for (vifi = 0, v = uvifs; vifi < numvifs; ++vifi, ++v) {
if (v->uv_flags & VIFF_TUNNEL) {
if (rmt_addr == v->uv_rmt_addr) {
log(LOG_ERR, 0,
"duplicate tunnel remote address %s in %s",
inet_fmt(rmt_addr, s1), configfilename);
break;
}
}
else if (!(v->uv_flags & VIFF_DISABLED)) {
if ((rmt_addr & v->uv_subnetmask) == v->uv_subnet) {
log(LOG_ERR, 0,
"unnecessary tunnel remote address %s in %s",
inet_fmt(rmt_addr, s1), configfilename);
break;
}
}
}
if (vifi != numvifs) continue;
/*
* OK, let's initialize a uvif structure for the tunnel.
*/
if (numvifs == MAXVIFS) {
log(LOG_ERR, 0, "too many vifs, ignoring tunnel to %s",
inet_fmt(rmt_addr, s1));
continue;
}
v = &uvifs[numvifs];
v->uv_flags = VIFF_TUNNEL;
v->uv_metric = DEFAULT_METRIC;
v->uv_rate_limit = DEFAULT_RATE_LIMIT;
v->uv_threshold = DEFAULT_THRESHOLD;
v->uv_lcl_addr = lcl_addr;
v->uv_rmt_addr = rmt_addr;
v->uv_subnet = 0;
v->uv_subnetmask = 0;
v->uv_subnetbcast = 0;
strncpy(v->uv_name, ffr.ifr_name, IFNAMSIZ);
v->uv_groups = NULL;
v->uv_neighbors = NULL;
v->uv_acl = NULL;
/*
* set variable to define which interface
*/
prev_vif = numvifs;
/*
* Look for "metric", "threshold", "srcrt", "rate_limit"
* and "boundary" options.
*/
while (!EQUAL((w = next_word(&s)), "")) {
if (EQUAL(w, "metric")) {
if(EQUAL((w = next_word(&s)), "")) {
log(LOG_ERR, 0,
"missing metric for tunnel to %s in %s",
inet_fmt(rmt_addr, s1), configfilename);
w = "garbage";
break;
}
if(sscanf(w, "%u%c", &n, &c) != 1 ||
n < 1 || n >= UNREACHABLE ) {
log(LOG_ERR, 0,
"invalid metric '%s' for tunnel to %s in %s",
w, inet_fmt(rmt_addr, s1), configfilename);
break;
}
v->uv_metric = n;
}
else if (EQUAL(w, "threshold")) {
if(EQUAL((w = next_word(&s)), "")) {
log(LOG_ERR, 0,
"missing threshold for tunnel to %s in %s",
inet_fmt(rmt_addr, s1), configfilename);
w = "garbage";
break;
}
if(sscanf(w, "%u%c", &n, &c) != 1 ||
n < 1 || n > 255 ) {
log(LOG_ERR, 0,
"invalid threshold '%s' for tunnel to %s in %s",
w, inet_fmt(rmt_addr, s1), configfilename);
break;
}
v->uv_threshold = n;
}
else if (EQUAL(w, "srcrt") || EQUAL(w, "sourceroute")) {
v->uv_flags |= VIFF_SRCRT;
}
else if (EQUAL(w, "rate_limit")) {
if (EQUAL((w = next_word(&s)), "")) {
log(LOG_ERR, 0,
"missing rate_limit for tunnel to %s in %s",
inet_fmt(rmt_addr, s1), configfilename);
w = "garbage";
break;
}
if(sscanf(w, "%u%c", &n, &c) != 1 ||
n < 0 || n > MAX_RATE_LIMIT ) {
log(LOG_ERR, 0,
"invalid rate_limit '%s' for tunnel to %s in %s",
w, inet_fmt(rmt_addr, s1), configfilename);
break;
}
v->uv_rate_limit = n;
}
else if (EQUAL(w, "boundary")) {
register struct vif_acl *v_acl;
register u_long baddr;
if (EQUAL((w = next_word(&s)), "")) {
log(LOG_ERR, 0,
"missing group address for tunnel to %s in %s",
inet_fmt(rmt_addr, s1), configfilename);
w = "garbage";
break;
}
if ((sscanf(w, "%[0-9.]/%d", s1, &n) != 2) ||
n < 0 || n> 32) {
log(LOG_ERR, 0,
"incorrect format '%s' for tunnel to %s in %s",
w, inet_fmt(rmt_addr, s1), configfilename);
break;
}
if ((baddr = inet_parse(s1)) == 0xffffffff ||
(baddr & 0xef000000) != 0xef000000) {
log(LOG_ERR, 0,
"incorrect address %s for tunnel to %s in %s",
s1, inet_fmt(rmt_addr, s1), configfilename);
continue;
}
v_acl = (struct vif_acl *)malloc(sizeof(struct vif_acl));
if (v_acl == NULL)
log(LOG_ERR, 0,
"out of memory");
VAL_TO_MASK(v_acl->acl_mask, n);
v_acl->acl_addr = baddr & v_acl->acl_mask;
/*
* link into data structure
*/
v_acl->acl_next = v->uv_acl;
v->uv_acl = v_acl;
}
else {
log(LOG_ERR, 0,
"invalid keyword (%s) in %s",
w, configfilename);
break;
}
}
if (!EQUAL(w, "")) continue;
log(LOG_INFO, 0,
"installing %stunnel from %s to %s as vif #%u - rate=%d",
v->uv_flags & VIFF_SRCRT? "srcrt " : "",
inet_fmt(lcl_addr, s1), inet_fmt(rmt_addr, s2),
numvifs, v->uv_rate_limit);
++numvifs;
if (!(ffr.ifr_flags & IFF_UP)) {
v->uv_flags |= VIFF_DOWN;
vifs_down = TRUE;
}
}
else {
log(LOG_ERR, 0,
"unknown command '%s' in %s", w, configfilename);
}
}
close(f);
}
/*
* Return a pointer to the next "word" in the string to which '*s' points,
* lower-cased and null terminated, and advance '*s' to point beyond the word.
* Words are separated by blanks and/or tabs, and the input string is
* considered to terminate at a newline, '#' (comment), or null character.
* If no words remain, a pointer to a null string ("") is returned.
* Warning: This function clobbers the input string.
*/
static char *next_word(s)
char **s;
{
char *w;
w = *s;
while (*w == ' ' || *w == '\t')
++w;
*s = w;
for(;;) {
switch (**s) {
case ' ' :
case '\t' : **s = '\0';
++*s;
return (w);
case '\n' :
case '#' : **s = '\0';
return (w);
case '\0' : return (w);
default : if (isascii(**s) && isupper(**s))
**s = tolower(**s);
++*s;
}
}
}

170
usr.sbin/mrouted/defs.h Normal file
View File

@ -0,0 +1,170 @@
/*
* The mrouted program is covered by the license in the accompanying file
* named "LICENSE". Use of the mrouted program represents acceptance of
* the terms and conditions listed in that file.
*
* The mrouted program is COPYRIGHT 1989 by The Board of Trustees of
* Leland Stanford Junior University.
*
*
* $Id: defs.h,v 1.8 1994/08/24 23:53:23 thyagara Exp $
*/
#include <stdio.h>
#include <ctype.h>
#include <errno.h>
#include <syslog.h>
#include <signal.h>
#include <sys/param.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/ioctl.h>
#include <sys/time.h>
#include <net/if.h>
#include <netinet/in.h>
#include <netinet/in_systm.h>
#include <netinet/ip.h>
#include <netinet/igmp.h>
#include <netinet/ip_mroute.h>
#include "dvmrp.h"
#include "vif.h"
#include "route.h"
#include "prune.h"
/*
* Miscellaneous constants and macros.
*/
#define FALSE 0
#define TRUE 1
#define EQUAL(s1, s2) (strcmp((s1), (s2)) == 0)
#define TIMER_INTERVAL ROUTE_MAX_REPORT_DELAY
#define PROTOCOL_VERSION 3 /* increment when packet format/content changes */
#define MROUTED_VERSION 3 /* increment on local changes or bug fixes, */
/* reset to 0 whever PROTOCOL_VERSION increments */
#define MROUTED_LEVEL ( (MROUTED_VERSION << 8) | PROTOCOL_VERSION )
/* for IGMP 'group' field of DVMRP messages */
#define DEL_RTE_GROUP 0
#define DEL_ALL_ROUTES 1
/* for Deleting kernel table entries */
/*
* External declarations for global variables and functions.
*/
extern char recv_buf[MAX_IP_PACKET_LEN];
extern char send_buf[MAX_IP_PACKET_LEN];
extern int igmp_socket;
extern u_long allhosts_group;
extern u_long dvmrp_group;
extern u_long dvmrp_genid;
#define DEFAULT_DEBUG 2 /* default if "-d" given without value */
extern int debug;
extern u_char pruning;
extern int routes_changed;
extern int delay_change_reports;
extern unsigned nroutes;
extern struct uvif uvifs[MAXVIFS];
extern vifi_t numvifs;
extern int vifs_down;
extern int udp_socket;
extern char s1[];
extern char s2[];
extern char s3[];
extern int errno;
extern int sys_nerr;
extern char * sys_errlist[];
extern void log();
extern void init_igmp();
extern void accept_igmp();
extern void send_igmp();
extern void init_routes();
extern void start_route_updates();
extern void update_route();
extern void age_routes();
extern void expire_all_routes();
extern void free_all_routes();
extern void accept_probe();
extern void accept_report();
extern void report();
extern void report_to_all_neighbors();
extern int report_next_chunk();
extern void add_vif_to_routes();
extern void delete_vif_from_routes();
extern void delete_neighbor_from_routes();
extern void dump_routes();
extern void init_vifs();
extern void check_vif_state();
extern vifi_t find_vif();
extern void age_vifs();
extern void dump_vifs();
extern void stop_all_vifs();
extern void accept_group_report();
extern void query_groups();
extern void probe_for_neighbors();
extern int update_neighbor();
extern void accept_neighbor_request();
extern void config_vifs_from_kernel();
extern void config_vifs_from_file();
extern int inet_valid_host();
extern int inet_valid_subnet();
extern char * inet_fmt();
extern char * inet_fmts();
extern u_long inet_parse();
extern int inet_cksum();
extern struct rtentry * determine_route();
extern void init_ktable();
extern int grplst_mem();
extern void add_table_entry();
extern void del_table_entry();
extern void update_table_entry();
extern void update_lclgrp();
extern void delete_lclgrp();
extern unsigned kroutes;
extern void send_prune();
extern void accept_prune();
extern int no_entry_exists();
extern struct ktable * find_src_grp();
extern int rtr_cnt();
extern void free_all_prunes();
extern void age_table_entry();
extern void dump_cache();
extern void chkgrp_graft();
extern void accept_graft();
extern void send_graft_ack();
extern void send_graft();
extern void accept_g_ack();
extern void mtrace();
extern char * malloc();
extern char * fgets();
extern FILE * fopen();
#ifndef htonl
extern u_long htonl();
extern u_long ntohl();
#endif

152
usr.sbin/mrouted/dvmrp.h Normal file
View File

@ -0,0 +1,152 @@
/*
* The mrouted program is covered by the license in the accompanying file
* named "LICENSE". Use of the mrouted program represents acceptance of
* the terms and conditions listed in that file.
*
* The mrouted program is COPYRIGHT 1989 by The Board of Trustees of
* Leland Stanford Junior University.
*
*
* $Id: dvmrp.h,v 1.6 1994/08/24 23:53:30 thyagara Exp $
*/
/*
* A DVMRP message consists of an IP header + an IGMP header + (for some types)
* zero or more bytes of data.
*
* For REPORT messages, the data is route information; the route information
* consists of one or more lists of the following form:
*
* (mask, (origin, metric), (origin, metric), ...)
*
* where:
*
* "mask" is the subnet mask for all the origins in the list.
* It is always THREE bytes long, containing the low-order
* three bytes of the mask (the high-order byte is always
* 0xff and therefore need not be transmitted).
*
* "origin" is the number of a subnet from which multicast datagrams
* may originate. It is from one to four bytes long,
* depending on the value of "mask":
* if all bytes of the mask are zero
* the subnet number is one byte long
* else if the low-order two bytes of the mask are zero
* the subnet number is two bytes long
* else if the lowest-order byte of the mask is zero
* the subnet number is three bytes long,
* else
* the subnet number is four bytes long.
*
* "metric" is a one-byte value consisting of two subfields:
* - the high-order bit is a flag which, when set, indicates
* the last (origin, metric) pair of a list.
* - the low-order seven bits contain the routing metric for
* the corresponding origin, relative to the sender of the
* DVMRP report. The metric may have the value of UNREACHABLE
* added to it as a "split horizon" indication (so called
* "poisoned reverse").
*
* Within a list, the origin subnet numbers must be in ascending order, and
* the lists themselves are in order of increasing mask value. A message may
* not exceed 576 bytes, the default maximum IP reassembly size, including
* the IP and IGMP headers; the route information may be split across more
* than one message if necessary, by terminating a list in one message and
* starting a new list in the next message (repeating the same mask value,
* if necessary).
*
* For NEIGHBORS messages, the data is neighboring-router information
* consisting of one or more lists of the following form:
*
* (local-addr, metric, threshold, ncount, neighbor, neighbor, ...)
*
* where:
*
* "local-addr" is the sending router's address as seen by the neighbors
* in this list; it is always four bytes long.
* "metric" is a one-byte unsigned value, the TTL `cost' of forwarding
* packets to any of the neighbors on this list.
* "threshold" is a one-byte unsigned value, a lower bound on the TTL a
* packet must have to be forwarded to any of the neighbors on
* this list.
* "ncount" is the number of neighbors in this list.
* "neighbor" is the address of a neighboring router, four bytes long.
*
* As with REPORT messages, NEIGHBORS messages should not exceed 576 bytes,
* including the IP and IGMP headers; split longer messages by terminating the
* list in one and continuing in another, repeating the local-addr, etc., if
* necessary.
*
* For NEIGHBORS2 messages, the data is identical to NEIGHBORS except
* there is a flags byte before the neighbor count:
*
* (local-addr, metric, threshold, flags, ncount, neighbor, neighbor, ...)
*/
/*
* DVMRP message types (carried in the "code" field of an IGMP header)
*/
#define DVMRP_PROBE 1 /* for finding neighbors */
#define DVMRP_REPORT 2 /* for reporting some or all routes */
#define DVMRP_ASK_NEIGHBORS 3 /* sent by mapper, asking for a list */
/* of this router's neighbors. */
#define DVMRP_NEIGHBORS 4 /* response to such a request */
#define DVMRP_ASK_NEIGHBORS2 5 /* as above, want new format reply */
#define DVMRP_NEIGHBORS2 6
#define DVMRP_PRUNE 7 /* prune message */
#define DVMRP_GRAFT 8 /* graft message */
#define DVMRP_GRAFT_ACK 9 /* graft acknowledgement */
/*
* 'flags' byte values in DVMRP_NEIGHBORS2 reply.
*/
#define DVMRP_NF_TUNNEL 0x01 /* neighbors reached via tunnel */
#define DVMRP_NF_SRCRT 0x02 /* tunnel uses IP source routing */
#define DVMRP_NF_DOWN 0x10 /* kernel state of interface */
#define DVMRP_NF_DISABLED 0x20 /* administratively disabled */
#define DVMRP_NF_QUERIER 0x40 /* I am the subnet's querier */
/*
* Limit on length of route data
*/
#define MAX_IP_PACKET_LEN 576
#define MIN_IP_HEADER_LEN 20
#define MAX_IP_HEADER_LEN 60
#define MAX_DVMRP_DATA_LEN \
( MAX_IP_PACKET_LEN - MAX_IP_HEADER_LEN - IGMP_MINLEN )
/*
* Various protocol constants (all times in seconds)
*/
/* address for multicast DVMRP msgs */
#define INADDR_DVMRP_GROUP (u_long)0xe0000004 /* 224.0.0.4 */
#define ROUTE_MAX_REPORT_DELAY 5 /* max delay for reporting changes */
/* (This is the timer interrupt */
/* interval; all times must be */
/* multiples of this value.) */
#define ROUTE_REPORT_INTERVAL 60 /* periodic route report interval */
#define ROUTE_SWITCH_TIME 140 /* time to switch to equivalent gw */
#define ROUTE_EXPIRE_TIME 200 /* time to mark route invalid */
#define ROUTE_DISCARD_TIME 340 /* time to garbage collect route */
#define LEAF_CONFIRMATION_TIME 200 /* time to consider subnet a leaf */
#define NEIGHBOR_PROBE_INTERVAL 10 /* periodic neighbor probe interval */
#define NEIGHBOR_EXPIRE_TIME 140 /* time to consider neighbor gone */
#define GROUP_QUERY_INTERVAL 125 /* periodic group query interval */
#define GROUP_EXPIRE_TIME 270 /* time to consider group gone */
#define UNREACHABLE 32 /* "infinity" metric, must be <= 64 */
#define DEFAULT_METRIC 1 /* default subnet/tunnel metric */
#define DEFAULT_THRESHOLD 1 /* default subnet/tunnel threshold */
#define MAX_RATE_LIMIT 100000 /* max rate limit */
#define DEFAULT_RATE_LIMIT 0 /* default rate limit */
#define DEFAULT_CACHE_LIFETIME 300 /* kernel route entry discard time */
#define GRAFT_TIMEOUT_VAL 5 /* retransmission time for grafts */
#define OLD_AGE_THRESHOLD 2

289
usr.sbin/mrouted/igmp.c Normal file
View File

@ -0,0 +1,289 @@
/*
* The mrouted program is covered by the license in the accompanying file
* named "LICENSE". Use of the mrouted program represents acceptance of
* the terms and conditions listed in that file.
*
* The mrouted program is COPYRIGHT 1989 by The Board of Trustees of
* Leland Stanford Junior University.
*
*
* $Id: igmp.c,v 1.8 1994/08/24 23:53:32 thyagara Exp $
*/
#include "defs.h"
/*
* Exported variables.
*/
char recv_buf[MAX_IP_PACKET_LEN]; /* input packet buffer */
char send_buf[MAX_IP_PACKET_LEN]; /* output packet buffer */
int igmp_socket; /* socket for all network I/O */
u_long allhosts_group; /* allhosts addr in net order */
u_long dvmrp_group; /* DVMRP grp addr in net order */
u_long dvmrp_genid; /* IGMP generation id */
/*
* Open and initialize the igmp socket, and fill in the non-changing
* IP header fields in the output packet buffer.
*/
void init_igmp()
{
struct ip *ip;
if ((igmp_socket = socket(AF_INET, SOCK_RAW, IPPROTO_IGMP)) < 0)
log(LOG_ERR, errno, "IGMP socket");
k_hdr_include(TRUE); /* include IP header when sending */
k_set_rcvbuf(48*1024); /* lots of input buffering */
k_set_ttl(1); /* restrict multicasts to one hop */
k_set_loop(FALSE); /* disable multicast loopback */
ip = (struct ip *)send_buf;
ip->ip_tos = 0;
ip->ip_off = 0;
ip->ip_p = IPPROTO_IGMP;
ip->ip_ttl = MAXTTL; /* applies to unicasts only */
allhosts_group = htonl(INADDR_ALLHOSTS_GROUP);
dvmrp_group = htonl(INADDR_DVMRP_GROUP);
}
/* %%% hack for PIM %%% */
#define IGMP_PIM 0x14
#define PIM_QUERY 0
#define PIM_REGISTER 1
#define PIM_REGISTER_STOP 2
#define PIM_JOIN_PRUNE 3
#define PIM_RP_REACHABLE 4
#define PIM_ASSERT 5
#define PIM_GRAFT 6
#define PIM_GRAFT_ACK 7
static char *packet_kind(type, code)
u_char type, code;
{
switch (type) {
case IGMP_HOST_MEMBERSHIP_QUERY: return "membership query ";
case IGMP_HOST_MEMBERSHIP_REPORT: return "membership report ";
case IGMP_HOST_NEW_MEMBERSHIP_REPORT: return "new membership report ";
case IGMP_HOST_LEAVE_MESSAGE: return "leave message";
case IGMP_DVMRP:
switch (code) {
case DVMRP_PROBE: return "neighbor probe ";
case DVMRP_REPORT: return "route report ";
case DVMRP_ASK_NEIGHBORS: return "neighbor request ";
case DVMRP_NEIGHBORS: return "neighbor list ";
case DVMRP_ASK_NEIGHBORS2: return "neighbor request 2";
case DVMRP_NEIGHBORS2: return "neighbor list 2 ";
case DVMRP_PRUNE: return "prune message ";
case DVMRP_GRAFT: return "graft message ";
case DVMRP_GRAFT_ACK: return "graft message ack ";
default: return "unknown DVMRP msg ";
}
case IGMP_PIM: /* %%% hack for PIM %%% */
switch (code) {
case PIM_QUERY: return "PIM Router-Query ";
case PIM_REGISTER: return "PIM Register ";
case PIM_REGISTER_STOP: return "PIM Register-Stop ";
case PIM_JOIN_PRUNE: return "PIM Join/Prune ";
case PIM_RP_REACHABLE: return "PIM RP-Reachable ";
case PIM_ASSERT: return "PIM Assert ";
case PIM_GRAFT: return "PIM Graft ";
case PIM_GRAFT_ACK: return "PIM Graft-Ack ";
default: return "unknown PIM msg ";
}
case IGMP_MTRACE: return "IGMP trace query ";
case IGMP_MTRACE_RESP: return "IGMP trace reply ";
default: return "unknown IGMP msg ";
}
}
/*
* Process a newly received IGMP packet that is sitting in the input
* packet buffer.
*/
void accept_igmp(recvlen)
int recvlen;
{
register vifi_t vifi;
register u_long src, dst, group;
struct ip *ip;
struct igmp *igmp;
int ipdatalen, iphdrlen, igmpdatalen;
if (recvlen < sizeof(struct ip)) {
log(LOG_WARNING, 0,
"received packet too short (%u bytes) for IP header", recvlen);
return;
}
ip = (struct ip *)recv_buf;
src = ip->ip_src.s_addr;
dst = ip->ip_dst.s_addr;
/*
* this is most likely a message from the kernel indicating that
* a new src grp pair message has arrived and so, it would be
* necessary to install a route into the kernel for this.
*/
if (ip->ip_p == 0) {
if (src == NULL || dst == NULL)
log(LOG_WARNING, 0, "kernel request not accurate");
else
add_table_entry(src, dst);
return;
}
iphdrlen = ip->ip_hl << 2;
ipdatalen = ip->ip_len;
if (iphdrlen + ipdatalen != recvlen) {
log(LOG_WARNING, 0,
"received packet shorter (%u bytes) than hdr+data length (%u+%u)",
recvlen, iphdrlen, ipdatalen);
return;
}
igmp = (struct igmp *)(recv_buf + iphdrlen);
group = igmp->igmp_group.s_addr;
igmpdatalen = ipdatalen - IGMP_MINLEN;
if (igmpdatalen < 0) {
log(LOG_WARNING, 0,
"received IP data field too short (%u bytes) for IGMP, from %s",
ipdatalen, inet_fmt(src, s1));
return;
}
log(LOG_DEBUG, 0, "RECV %s from %-15s to %s",
packet_kind(igmp->igmp_type, igmp->igmp_code),
inet_fmt(src, s1), inet_fmt(dst, s2));
switch (igmp->igmp_type) {
case IGMP_HOST_MEMBERSHIP_QUERY:
/* we have to do the determination of the querrier router here */
return;
case IGMP_HOST_MEMBERSHIP_REPORT:
case IGMP_HOST_NEW_MEMBERSHIP_REPORT:
accept_group_report(src, dst, group,igmp->igmp_type);
return;
case IGMP_HOST_LEAVE_MESSAGE:
leave_group_message(src, dst, group);
return;
case IGMP_DVMRP:
switch (igmp->igmp_code) {
case DVMRP_PROBE:
accept_probe(src, dst,
(char *)(igmp+1), igmpdatalen, group);
return;
case DVMRP_REPORT:
accept_report(src, dst,
(char *)(igmp+1), igmpdatalen, group);
return;
case DVMRP_ASK_NEIGHBORS:
accept_neighbor_request(src, dst);
return;
case DVMRP_ASK_NEIGHBORS2:
accept_neighbor_request2(src, dst);
return;
case DVMRP_NEIGHBORS:
accept_neighbors(src, dst, (char *)(igmp+1), igmpdatalen,
group);
return;
case DVMRP_NEIGHBORS2:
accept_neighbors2(src, dst, (char *)(igmp+1), igmpdatalen,
group);
return;
case DVMRP_PRUNE:
accept_prune(src, dst, (char *)(igmp+1), igmpdatalen);
return;
case DVMRP_GRAFT:
accept_graft(src, dst, (char *)(igmp+1), igmpdatalen);
return;
case DVMRP_GRAFT_ACK:
accept_g_ack(src, dst, (char *)(igmp+1), igmpdatalen);
return;
default:
log(LOG_INFO, 0,
"ignoring unknown DVMRP message code %u from %s to %s",
igmp->igmp_code, inet_fmt(src, s1),
inet_fmt(dst, s2));
return;
}
case IGMP_PIM: /* %%% hack for PIM %%% */
return;
case IGMP_MTRACE:
mtrace(src, dst, group, (char *)(igmp+1),
igmp->igmp_code, igmpdatalen);
return;
default:
log(LOG_INFO, 0,
"ignoring unknown IGMP message type %u from %s to %s",
igmp->igmp_type, inet_fmt(src, s1),
inet_fmt(dst, s2));
return;
}
}
/*
* Construct an IGMP message in the output packet buffer. The caller may
* have already placed data in that buffer, of length 'datalen'. Then send
* the message from the interface with IP address 'src' to destination 'dst'.
*/
void send_igmp(src, dst, type, code, group, datalen)
u_long src, dst;
int type, code;
u_long group;
int datalen;
{
static struct sockaddr_in sdst = {AF_INET};
struct ip *ip;
struct igmp *igmp;
ip = (struct ip *)send_buf;
ip->ip_src.s_addr = src;
ip->ip_dst.s_addr = dst;
ip->ip_len = MIN_IP_HEADER_LEN + IGMP_MINLEN + datalen;
igmp = (struct igmp *)(send_buf + MIN_IP_HEADER_LEN);
igmp->igmp_type = type;
igmp->igmp_code = code;
igmp->igmp_group.s_addr = group;
igmp->igmp_cksum = 0;
igmp->igmp_cksum = inet_cksum((u_short *)igmp,
IGMP_MINLEN + datalen);
if (IN_MULTICAST(ntohl(dst))) k_set_if(src);
if (dst == allhosts_group) k_set_loop(TRUE);
sdst.sin_addr.s_addr = dst;
if (sendto(igmp_socket, send_buf, ip->ip_len, 0,
(struct sockaddr *)&sdst, sizeof(sdst)) < 0) {
if (errno == ENETDOWN) check_vif_state();
else log(LOG_WARNING, errno, "sendto on %s", inet_fmt(src, s1));
}
if (dst == allhosts_group) k_set_loop(FALSE);
log(LOG_DEBUG, 0, "SENT %s from %-15s to %s",
packet_kind(type, code), inet_fmt(src, s1), inet_fmt(dst, s2));
}

187
usr.sbin/mrouted/inet.c Normal file
View File

@ -0,0 +1,187 @@
/*
* The mrouted program is covered by the license in the accompanying file
* named "LICENSE". Use of the mrouted program represents acceptance of
* the terms and conditions listed in that file.
*
* The mrouted program is COPYRIGHT 1989 by The Board of Trustees of
* Leland Stanford Junior University.
*
*
* $Id: inet.c,v 1.4 1993/05/30 01:36:38 deering Exp $
*/
#include "defs.h"
/*
* Exported variables.
*/
char s1[16]; /* buffers to hold the string representations */
char s2[16]; /* of IP addresses, to be passed to inet_fmt() */
char s3[16]; /* or inet_fmts(). */
/*
* Verify that a given IP address is credible as a host address.
* (Without a mask, cannot detect addresses of the form {subnet,0} or
* {subnet,-1}.)
*/
int inet_valid_host(naddr)
u_long naddr;
{
register u_long addr;
addr = ntohl(naddr);
return (!(IN_MULTICAST(addr) ||
IN_BADCLASS (addr) ||
(addr & 0xff000000) == 0));
}
/*
* Verify that a given subnet number and mask pair are credible.
*/
int inet_valid_subnet(nsubnet, nmask)
u_long nsubnet, nmask;
{
register u_long subnet, mask;
subnet = ntohl(nsubnet);
mask = ntohl(nmask);
if ((subnet & mask) != subnet) return (FALSE);
if (IN_CLASSA(subnet)) {
if (mask < 0xff000000 ||
(subnet & 0xff000000) == 0 ||
(subnet & 0xff000000) == 0x7f000000) return (FALSE);
}
else if (IN_CLASSB(subnet)) {
if (mask < 0xffff0000) return (FALSE);
}
else if (IN_CLASSC(subnet)) {
if (mask < 0xffffff00) return (FALSE);
}
else return (FALSE);
return (TRUE);
}
/*
* Convert an IP address in u_long (network) format into a printable string.
*/
char *inet_fmt(addr, s)
u_long addr;
char *s;
{
register u_char *a;
a = (u_char *)&addr;
sprintf(s, "%u.%u.%u.%u", a[0], a[1], a[2], a[3]);
return (s);
}
/*
* Convert an IP subnet number in u_long (network) format into a printable
* string.
*/
char *inet_fmts(addr, mask, s)
u_long addr, mask;
char *s;
{
register u_char *a, *m;
a = (u_char *)&addr;
m = (u_char *)&mask;
if (m[3] != 0) sprintf(s, "%u.%u.%u.%u", a[0], a[1], a[2], a[3]);
else if (m[2] != 0) sprintf(s, "%u.%u.%u", a[0], a[1], a[2]);
else if (m[1] != 0) sprintf(s, "%u.%u", a[0], a[1]);
else sprintf(s, "%u", a[0]);
return (s);
}
/*
* Convert the printable string representation of an IP address into the
* u_long (network) format. Return 0xffffffff on error. (To detect the
* legal address with that value, you must explicitly compare the string
* with "255.255.255.255".)
*/
u_long inet_parse(s)
char *s;
{
u_long a;
u_int a0, a1, a2, a3;
char c;
if (sscanf(s, "%u.%u.%u.%u%c", &a0, &a1, &a2, &a3, &c) != 4 ||
a0 > 255 || a1 > 255 || a2 > 255 || a3 > 255)
return (0xffffffff);
((u_char *)&a)[0] = a0;
((u_char *)&a)[1] = a1;
((u_char *)&a)[2] = a2;
((u_char *)&a)[3] = a3;
return (a);
}
/*
* inet_cksum extracted from:
* P I N G . C
*
* Author -
* Mike Muuss
* U. S. Army Ballistic Research Laboratory
* December, 1983
* Modified at Uc Berkeley
*
* (ping.c) Status -
* Public Domain. Distribution Unlimited.
*
* I N _ C K S U M
*
* Checksum routine for Internet Protocol family headers (C Version)
*
*/
int inet_cksum(addr, len)
u_short *addr;
u_int len;
{
register int nleft = (int)len;
register u_short *w = addr;
u_short answer = 0;
register int sum = 0;
/*
* Our algorithm is simple, using a 32 bit accumulator (sum),
* we add sequential 16 bit words to it, and at the end, fold
* back all the carry bits from the top 16 bits into the lower
* 16 bits.
*/
while( nleft > 1 ) {
sum += *w++;
nleft -= 2;
}
/* mop up an odd byte, if necessary */
if( nleft == 1 ) {
*(u_char *) (&answer) = *(u_char *)w ;
sum += answer;
}
/*
* add back carry outs from top 16 bits to low 16 bits
*/
sum = (sum >> 16) + (sum & 0xffff); /* add hi 16 to low 16 */
sum += (sum >> 16); /* add carry */
answer = ~sum; /* truncate to 16 bits */
return (answer);
}

183
usr.sbin/mrouted/kern.c Normal file
View File

@ -0,0 +1,183 @@
/*
* The mrouted program is covered by the license in the accompanying file
* named "LICENSE". Use of the mrouted program represents acceptance of
* the terms and conditions listed in that file.
*
* The mrouted program is COPYRIGHT 1989 by The Board of Trustees of
* Leland Stanford Junior University.
*
*
* $Id: kern.c,v 1.6 1994/08/24 23:53:37 thyagara Exp $
*/
#include "defs.h"
void k_set_rcvbuf(bufsize)
int bufsize;
{
if (setsockopt(igmp_socket, SOL_SOCKET, SO_RCVBUF,
(char *)&bufsize, sizeof(bufsize)) < 0)
log(LOG_ERR, errno, "setsockopt SO_RCVBUF %u", bufsize);
}
void k_hdr_include(bool)
int bool;
{
#ifdef IP_HDRINCL
if (setsockopt(igmp_socket, IPPROTO_IP, IP_HDRINCL,
(char *)&bool, sizeof(bool)) < 0)
log(LOG_ERR, errno, "setsockopt IP_HDRINCL %u", bool);
#endif
}
void k_set_ttl(t)
int t;
{
u_char ttl;
ttl = t;
if (setsockopt(igmp_socket, IPPROTO_IP, IP_MULTICAST_TTL,
(char *)&ttl, sizeof(ttl)) < 0)
log(LOG_ERR, errno, "setsockopt IP_MULTICAST_TTL %u", ttl);
}
void k_set_loop(l)
int l;
{
u_char loop;
loop = l;
if (setsockopt(igmp_socket, IPPROTO_IP, IP_MULTICAST_LOOP,
(char *)&loop, sizeof(loop)) < 0)
log(LOG_ERR, errno, "setsockopt IP_MULTICAST_LOOP %u", loop);
}
void k_set_if(ifa)
u_long ifa;
{
struct in_addr adr;
adr.s_addr = ifa;
if (setsockopt(igmp_socket, IPPROTO_IP, IP_MULTICAST_IF,
(char *)&adr, sizeof(adr)) < 0)
log(LOG_ERR, errno, "setsockopt IP_MULTICAST_IF %s",
inet_fmt(ifa, s1));
}
void k_join(grp, ifa)
u_long grp;
u_long ifa;
{
struct ip_mreq mreq;
mreq.imr_multiaddr.s_addr = grp;
mreq.imr_interface.s_addr = ifa;
if (setsockopt(igmp_socket, IPPROTO_IP, IP_ADD_MEMBERSHIP,
(char *)&mreq, sizeof(mreq)) < 0)
log(LOG_WARNING, errno, "can't join group %s on interface %s",
inet_fmt(grp, s1), inet_fmt(ifa, s2));
}
void k_leave(grp, ifa)
u_long grp;
u_long ifa;
{
struct ip_mreq mreq;
mreq.imr_multiaddr.s_addr = grp;
mreq.imr_interface.s_addr = ifa;
if (setsockopt(igmp_socket, IPPROTO_IP, IP_DROP_MEMBERSHIP,
(char *)&mreq, sizeof(mreq)) < 0)
log(LOG_WARNING, errno, "can't leave group %s on interface %s",
inet_fmt(grp, s1), inet_fmt(ifa, s2));
}
void k_init_dvmrp()
{
if (setsockopt(igmp_socket, IPPROTO_IP, DVMRP_INIT,
(char *)NULL, 0) < 0)
log(LOG_ERR, errno, "can't enable DVMRP routing in kernel");
}
void k_stop_dvmrp()
{
if (setsockopt(igmp_socket, IPPROTO_IP, DVMRP_DONE,
(char *)NULL, 0) < 0)
log(LOG_WARNING, errno, "can't disable DVMRP routing in kernel");
}
void k_add_vif(vifi, v)
vifi_t vifi;
struct uvif *v;
{
struct vifctl vc;
vc.vifc_vifi = vifi;
vc.vifc_flags = v->uv_flags & VIFF_KERNEL_FLAGS;
vc.vifc_threshold = v->uv_threshold;
vc.vifc_rate_limit = v->uv_rate_limit;
vc.vifc_lcl_addr.s_addr = v->uv_lcl_addr;
vc.vifc_rmt_addr.s_addr = v->uv_rmt_addr;
if (setsockopt(igmp_socket, IPPROTO_IP, DVMRP_ADD_VIF,
(char *)&vc, sizeof(vc)) < 0)
log(LOG_ERR, errno, "setsockopt DVMRP_ADD_VIF");
}
void k_del_vif(vifi)
vifi_t vifi;
{
if (setsockopt(igmp_socket, IPPROTO_IP, DVMRP_DEL_VIF,
(char *)&vifi, sizeof(vifi)) < 0)
log(LOG_ERR, errno, "setsockopt DVMRP_DEL_VIF");
}
/*
* Adds a (source, mcastgrp) entry to the kernel
*/
void k_add_rg(kt)
struct ktable *kt;
{
struct mfcctl mc;
/* copy table values so that setsockopt can process it */
COPY_TABLES(kt, mc);
/* write to kernel space */
if (setsockopt(igmp_socket, IPPROTO_IP, DVMRP_ADD_MFC,
(char *)&mc, sizeof(mc)) < 0)
log(LOG_WARNING, errno, "setsockopt DVMRP_ADD_MFC");
}
/*
* Deletes a (source, mcastgrp) entry from the kernel
*/
void k_del_rg(kt)
struct ktable *kt;
{
struct mfcctl mc;
/* copy table values so that setsockopt can process it */
COPY_TABLES(kt, mc);
/* write to kernel space */
if (setsockopt(igmp_socket, IPPROTO_IP, DVMRP_DEL_MFC,
(char *)&mc, sizeof(mc)) < 0)
log(LOG_WARNING, errno, "setsockopt DVMRP_DEL_MFC");
}

439
usr.sbin/mrouted/main.c Normal file
View File

@ -0,0 +1,439 @@
/*
* The mrouted program is covered by the license in the accompanying file
* named "LICENSE". Use of the mrouted program represents acceptance of
* the terms and conditions listed in that file.
*
* The mrouted program is COPYRIGHT 1989 by The Board of Trustees of
* Leland Stanford Junior University.
*
*
* $Id: main.c,v 1.8 1994/08/24 23:53:42 thyagara Exp $
*/
/*
* Written by Steve Deering, Stanford University, February 1989.
*
* (An earlier version of DVMRP was implemented by David Waitzman of
* BBN STC by extending Berkeley's routed program. Some of Waitzman's
* extensions have been incorporated into mrouted, but none of the
* original routed code has been adopted.)
*/
#include "defs.h"
extern char *configfilename;
static char pidfilename[] = "/etc/mrouted.pid";
static char dumpfilename[] = "/usr/tmp/mrouted.dump";
static char cachefilename[] = "/usr/tmp/mrouted.cache";
static char genidfilename[] = "/usr/tmp/mrouted.genid";
int cache_lifetime = DEFAULT_CACHE_LIFETIME;
int max_prune_lifetime = DEFAULT_CACHE_LIFETIME * 2;
int debug = 0;
u_char pruning = 1; /* Enable pruning by default */
/*
* Forward declarations.
*/
static void fasttimer();
static void timer();
static void hup();
static void dump();
static void fdump();
static void cdump();
static void restart();
main(argc, argv)
int argc;
char *argv[];
{
register int recvlen;
register int omask;
int dummy;
FILE *fp;
extern uid_t geteuid();
struct timeval tv;
struct timezone tzp;
u_long prev_genid;
setlinebuf(stderr);
if (geteuid() != 0) {
fprintf(stderr, "must be root\n");
exit(1);
}
argv++, argc--;
while (argc > 0 && *argv[0] == '-') {
if (strcmp(*argv, "-d") == 0) {
if (argc > 1 && isdigit(*(argv + 1)[0])) {
argv++, argc--;
debug = atoi(*argv);
} else
debug = DEFAULT_DEBUG;
} else if (strcmp(*argv, "-c") == 0) {
if (argc > 1) {
argv++, argc--;
configfilename = *argv;
} else
goto usage;
} else if (strcmp(*argv, "-p") == 0) {
pruning = 0;
} else
goto usage;
argv++, argc--;
}
if (argc > 0) {
usage: fprintf(stderr,
"usage: mrouted [-p] [-c configfile] [-d [debug_level]]\n");
exit(1);
}
if (debug == 0) {
/*
* Detach from the terminal
*/
int t;
if (fork()) exit(0);
(void)close(0);
(void)close(1);
(void)close(2);
(void)open("/", 0);
(void)dup2(0, 1);
(void)dup2(0, 2);
t = open("/dev/tty", 2);
if (t >= 0) {
(void)ioctl(t, TIOCNOTTY, (char *)0);
(void)close(t);
}
}
else
fprintf(stderr, "debug level %u\n", debug);
#ifdef LOG_DAEMON
(void)openlog("mrouted", LOG_PID, LOG_DAEMON);
(void)setlogmask(LOG_UPTO(LOG_NOTICE));
#else
(void)openlog("mrouted", LOG_PID);
#endif
log(LOG_NOTICE, 0, "mrouted version %d.%d",
PROTOCOL_VERSION, MROUTED_VERSION);
srandom(gethostid());
/*
* Get generation id
*/
gettimeofday(&tv, &tzp);
dvmrp_genid = tv.tv_sec;
fp = fopen(genidfilename, "r");
if (fp != NULL) {
fscanf(fp, "%d", &prev_genid);
if (prev_genid == dvmrp_genid)
dvmrp_genid++;
(void) fclose(fp);
}
fp = fopen(genidfilename, "w");
if (fp != NULL) {
fprintf(fp, "%d", dvmrp_genid);
(void) fclose(fp);
}
callout_init();
init_igmp();
k_init_dvmrp(); /* enable DVMRP routing in kernel */
init_routes();
init_ktable();
init_vifs();
if (debug)
fprintf(stderr, "pruning %s\n", pruning ? "on" : "off");
fp = fopen(pidfilename, "w");
if (fp != NULL) {
fprintf(fp, "%d\n", getpid());
(void) fclose(fp);
}
if (debug >= 2) dump();
(void)signal(SIGALRM, fasttimer);
(void)signal(SIGHUP, restart);
(void)signal(SIGTERM, hup);
(void)signal(SIGINT, hup);
(void)signal(SIGUSR1, fdump);
(void)signal(SIGUSR2, cdump);
if (debug != 0)
(void)signal(SIGQUIT, dump);
(void)alarm(1); /* schedule first timer interrupt */
/*
* Main receive loop.
*/
dummy = 0;
for(;;) {
recvlen = recvfrom(igmp_socket, recv_buf, sizeof(recv_buf),
0, NULL, &dummy);
if (recvlen < 0) {
if (errno != EINTR) log(LOG_ERR, errno, "recvfrom");
continue;
}
omask = sigblock(sigmask(SIGALRM));
accept_igmp(recvlen);
(void)sigsetmask(omask);
}
}
/*
* routine invoked every second. It's main goal is to cycle through
* the routing table and send partial updates to all neighbors at a
* rate that will cause the entire table to be sent in ROUTE_REPORT_INTERVAL
* seconds. Also, every TIMER_INTERVAL seconds it calls timer() to
* do all the other time-based processing.
*/
static void fasttimer()
{
static unsigned int tlast;
static unsigned int nsent;
register unsigned int t = tlast + 1;
register int n;
/*
* if we're in the last second, send everything that's left.
* otherwise send at least the fraction we should have sent by now.
*/
if (t >= ROUTE_REPORT_INTERVAL) {
register int nleft = nroutes - nsent;
while (nleft > 0) {
if ((n = report_next_chunk()) <= 0)
break;
nleft -= n;
}
tlast = 0;
nsent = 0;
} else {
register unsigned int ncum = nroutes * t / ROUTE_REPORT_INTERVAL;
while (nsent < ncum) {
if ((n = report_next_chunk()) <= 0)
break;
nsent += n;
}
tlast = t;
}
if ((t % TIMER_INTERVAL) == 0)
timer();
age_callout_queue();/* Advance the timer for the callout queue
for groups */
alarm(1);
}
/*
* The 'virtual_time' variable is initialized to a value that will cause the
* first invocation of timer() to send a probe or route report to all vifs
* and send group membership queries to all subnets for which this router is
* querier. This first invocation occurs approximately TIMER_INTERVAL seconds
* after the router starts up. Note that probes for neighbors and queries
* for group memberships are also sent at start-up time, as part of initial-
* ization. This repetition after a short interval is desirable for quickly
* building up topology and membership information in the presence of possible
* packet loss.
*
* 'virtual_time' advances at a rate that is only a crude approximation of
* real time, because it does not take into account any time spent processing,
* and because the timer intervals are sometimes shrunk by a random amount to
* avoid unwanted synchronization with other routers.
*/
static u_long virtual_time = 0;
/*
* Timer routine. Performs periodic neighbor probing, route reporting, and
* group querying duties, and drives various timers in routing entries and
* virtual interface data structures.
*/
static void timer()
{
age_routes(); /* Advance the timers in the route entries */
age_vifs(); /* Advance the timers for neighbors */
age_table_entry(); /* Advance the timers for the cache entries */
if (virtual_time % GROUP_QUERY_INTERVAL == 0) {
/*
* Time to query the local group memberships on all subnets
* for which this router is the elected querier.
*/
query_groups();
}
if (virtual_time % NEIGHBOR_PROBE_INTERVAL == 0) {
/*
* Time to send a probe on all vifs from which no neighbors have
* been heard. Also, check if any inoperative interfaces have now
* come up. (If they have, they will also be probed as part of
* their initialization.)
*/
probe_for_neighbors();
if (vifs_down)
check_vif_state();
}
delay_change_reports = FALSE;
if (routes_changed) {
/*
* Some routes have changed since the last timer interrupt, but
* have not been reported yet. Report the changed routes to all
* neighbors.
*/
report_to_all_neighbors(CHANGED_ROUTES);
}
/*
* Advance virtual time
*/
virtual_time += TIMER_INTERVAL;
}
/*
* On hangup signal, let everyone know we're going away.
*/
static void hup()
{
log(LOG_INFO, 0, "hup");
expire_all_routes();
report_to_all_neighbors(ALL_ROUTES);
exit(1);
}
/*
* Dump internal data structures to stderr.
*/
static void dump()
{
dump_vifs(stderr);
dump_routes(stderr);
}
/*
* Dump internal data structures to a file.
*/
static void fdump()
{
FILE *fp;
fp = fopen(dumpfilename, "w");
if (fp != NULL) {
dump_vifs(fp);
dump_routes(fp);
(void) fclose(fp);
}
}
/*
* Dump local cache contents to a file.
*/
static void cdump()
{
FILE *fp;
fp = fopen(cachefilename, "w");
if (fp != NULL) {
dump_cache(fp);
(void) fclose(fp);
}
}
/*
* Restart mrouted
*/
static void restart()
{
register int omask;
log(LOG_INFO, 0, "restart");
/*
* reset all the entries
*/
omask = sigblock(sigmask(SIGALRM));
free_all_prunes();
free_all_routes();
stop_all_vifs();
k_stop_dvmrp();
/*
* start processing again
*/
dvmrp_genid++;
pruning = 1;
init_igmp();
k_init_dvmrp(); /* enable DVMRP routing in kernel */
init_routes();
init_ktable();
init_vifs();
(void)sigsetmask(omask);
}
/*
* Log errors and other messages to the system log daemon and to stderr,
* according to the severity of the message and the current debug level.
* For errors of severity LOG_ERR or worse, terminate the program.
*/
void log(severity, syserr, format, a, b, c, d, e)
int severity, syserr;
char *format;
int a, b, c, d, e;
{
char fmt[100];
switch (debug) {
case 0: break;
case 1: if (severity > LOG_NOTICE) break;
case 2: if (severity > LOG_INFO ) break;
default:
fmt[0] = '\0';
if (severity == LOG_WARNING) strcat(fmt, "warning - ");
strncat(fmt, format, 80);
fprintf(stderr, fmt, a, b, c, d, e);
if (syserr == 0)
fprintf(stderr, "\n");
else if(syserr < sys_nerr)
fprintf(stderr, ": %s\n", sys_errlist[syserr]);
else
fprintf(stderr, ": errno %d\n", syserr);
}
if (severity <= LOG_NOTICE) {
fmt[0] = '\0';
if (severity == LOG_WARNING) strcat(fmt, "warning - ");
strncat(fmt, format, 80);
if (syserr != 0) {
strcat(fmt, ": %m");
errno = syserr;
}
syslog(severity, fmt, a, b, c, d, e);
if (severity <= LOG_ERR) exit(-1);
}
}

953
usr.sbin/mrouted/mapper.c Normal file
View File

@ -0,0 +1,953 @@
/* Mapper for connections between MRouteD multicast routers.
* Written by Pavel Curtis <Pavel@PARC.Xerox.Com>
*
* $Id: mapper.c,v 1.8 1994/08/24 23:53:54 thyagara Exp $
*/
/*
* Copyright (c) Xerox Corporation 1992. All rights reserved.
*
* License is granted to copy, to use, and to make and to use derivative
* works for research and evaluation purposes, provided that Xerox is
* acknowledged in all documentation pertaining to any such copy or derivative
* work. Xerox grants no other licenses expressed or implied. The Xerox trade
* name should not be used in any advertising without its written permission.
*
* XEROX CORPORATION MAKES NO REPRESENTATIONS CONCERNING EITHER THE
* MERCHANTABILITY OF THIS SOFTWARE OR THE SUITABILITY OF THIS SOFTWARE
* FOR ANY PARTICULAR PURPOSE. The software is provided "as is" without
* express or implied warranty of any kind.
*
* These notices must be retained in any copies of any part of this software.
*/
#include <netdb.h>
#include <sys/time.h>
#include "defs.h"
#define DEFAULT_TIMEOUT 2 /* How long to wait before retrying requests */
#define DEFAULT_RETRIES 1 /* How many times to ask each router */
/* All IP addresses are stored in the data structure in NET order. */
typedef struct neighbor {
struct neighbor *next;
u_long addr; /* IP address in NET order */
u_char metric; /* TTL cost of forwarding */
u_char threshold; /* TTL threshold to forward */
u_short flags; /* flags on connection */
#define NF_PRESENT 0x8000 /* True if flags are meaningful */
} Neighbor;
typedef struct interface {
struct interface *next;
u_long addr; /* IP address of the interface in NET order */
Neighbor *neighbors; /* List of neighbors' IP addresses */
} Interface;
typedef struct node {
u_long addr; /* IP address of this entry in NET order */
u_long version; /* which mrouted version is running */
int tries; /* How many requests sent? -1 for aliases */
union {
struct node *alias; /* If alias, to what? */
struct interface *interfaces; /* Else, neighbor data */
} u;
struct node *left, *right;
} Node;
Node *routers = 0;
u_long our_addr, target_addr = 0; /* in NET order */
int debug = 0;
int retries = DEFAULT_RETRIES;
int timeout = DEFAULT_TIMEOUT;
int show_names = TRUE;
vifi_t numvifs; /* to keep loader happy */
/* (see COPY_TABLES macro called in kern.c) */
Node *find_node(addr, ptr)
u_long addr;
Node **ptr;
{
Node *n = *ptr;
if (!n) {
*ptr = n = (Node *) malloc(sizeof(Node));
n->addr = addr;
n->version = 0;
n->tries = 0;
n->u.interfaces = 0;
n->left = n->right = 0;
return n;
} else if (addr == n->addr)
return n;
else if (addr < n->addr)
return find_node(addr, &(n->left));
else
return find_node(addr, &(n->right));
}
Interface *find_interface(addr, node)
u_long addr;
Node *node;
{
Interface *ifc;
for (ifc = node->u.interfaces; ifc; ifc = ifc->next)
if (ifc->addr == addr)
return ifc;
ifc = (Interface *) malloc(sizeof(Interface));
ifc->addr = addr;
ifc->next = node->u.interfaces;
node->u.interfaces = ifc;
ifc->neighbors = 0;
return ifc;
}
Neighbor *find_neighbor(addr, node)
u_long addr;
Node *node;
{
Interface *ifc;
for (ifc = node->u.interfaces; ifc; ifc = ifc->next) {
Neighbor *nb;
for (nb = ifc->neighbors; nb; nb = nb->next)
if (nb->addr == addr)
return nb;
}
return 0;
}
/*
* Log errors and other messages to stderr, according to the severity of the
* message and the current debug level. For errors of severity LOG_ERR or
* worse, terminate the program.
*/
void log(severity, syserr, format, a, b, c, d, e)
int severity, syserr;
char *format;
int a, b, c, d, e;
{
char fmt[100];
switch (debug) {
case 0: if (severity > LOG_WARNING) return;
case 1: if (severity > LOG_NOTICE ) return;
case 2: if (severity > LOG_INFO ) return;
default:
fmt[0] = '\0';
if (severity == LOG_WARNING)
strcat(fmt, "warning - ");
strncat(fmt, format, 80);
fprintf(stderr, fmt, a, b, c, d, e);
if (syserr == 0)
fprintf(stderr, "\n");
else if (syserr < sys_nerr)
fprintf(stderr, ": %s\n", sys_errlist[syserr]);
else
fprintf(stderr, ": errno %d\n", syserr);
}
if (severity <= LOG_ERR)
exit(-1);
}
/*
* Send a neighbors-list request.
*/
void ask(dst)
u_long dst;
{
send_igmp(our_addr, dst, IGMP_DVMRP, DVMRP_ASK_NEIGHBORS,
htonl(MROUTED_LEVEL), 0);
}
void ask2(dst)
u_long dst;
{
send_igmp(our_addr, dst, IGMP_DVMRP, DVMRP_ASK_NEIGHBORS2,
htonl(MROUTED_LEVEL), 0);
}
/*
* Process an incoming group membership report.
*/
void accept_group_report(src, dst, group)
u_long src, dst, group;
{
log(LOG_INFO, 0, "ignoring IGMP group membership report from %s to %s",
inet_fmt(src, s1), inet_fmt(dst, s2));
}
/*
* Process an incoming neighbor probe message.
*/
void accept_probe(src, dst)
u_long src, dst;
{
log(LOG_INFO, 0, "ignoring DVMRP probe from %s to %s",
inet_fmt(src, s1), inet_fmt(dst, s2));
}
/*
* Process an incoming route report message.
*/
void accept_report(src, dst, p, datalen)
u_long src, dst;
char *p;
int datalen;
{
log(LOG_INFO, 0, "ignoring DVMRP routing report from %s to %s",
inet_fmt(src, s1), inet_fmt(dst, s2));
}
/*
* Process an incoming neighbor-list request message.
*/
void accept_neighbor_request(src, dst)
u_long src, dst;
{
if (src != our_addr)
log(LOG_INFO, 0,
"ignoring spurious DVMRP neighbor request from %s to %s",
inet_fmt(src, s1), inet_fmt(dst, s2));
}
void accept_neighbor_request2(src, dst)
u_long src, dst;
{
if (src != our_addr)
log(LOG_INFO, 0,
"ignoring spurious DVMRP neighbor request2 from %s to %s",
inet_fmt(src, s1), inet_fmt(dst, s2));
}
/*
* Process an incoming neighbor-list message.
*/
void accept_neighbors(src, dst, p, datalen, level)
u_long src, dst, level;
u_char *p;
int datalen;
{
Node *node = find_node(src, &routers);
if (node->tries == 0) /* Never heard of 'em; must have hit them at */
node->tries = 1; /* least once, though...*/
else if (node->tries == -1) /* follow alias link */
node = node->u.alias;
#define GET_ADDR(a) (a = ((u_long)*p++ << 24), a += ((u_long)*p++ << 16),\
a += ((u_long)*p++ << 8), a += *p++)
/* if node is running a recent mrouted, ask for additional info */
if (level != 0) {
node->version = ntohl(level);
node->tries = 0;
ask2(src);
return;
}
if (debug > 3) {
int i;
fprintf(stderr, " datalen = %d\n", datalen);
for (i = 0; i < datalen; i++) {
if ((i & 0xF) == 0)
fprintf(stderr, " ");
fprintf(stderr, " %02x", p[i]);
if ((i & 0xF) == 0xF)
fprintf(stderr, "\n");
}
if ((datalen & 0xF) != 0xF)
fprintf(stderr, "\n");
}
while (datalen > 0) { /* loop through interfaces */
u_long ifc_addr;
u_char metric, threshold, ncount;
Node *ifc_node;
Interface *ifc;
Neighbor *old_neighbors;
if (datalen < 4 + 3) {
log(LOG_WARNING, 0, "received truncated interface record from %s",
inet_fmt(src, s1));
return;
}
GET_ADDR(ifc_addr);
ifc_addr = htonl(ifc_addr);
metric = *p++;
threshold = *p++;
ncount = *p++;
datalen -= 4 + 3;
/* Fix up any alias information */
ifc_node = find_node(ifc_addr, &routers);
if (ifc_node->tries == 0) { /* new node */
ifc_node->tries = -1;
ifc_node->u.alias = node;
} else if (ifc_node != node
&& (ifc_node->tries > 0 || ifc_node->u.alias != node)) {
/* must merge two hosts' nodes */
Interface *ifc_i, *next_ifc_i;
if (ifc_node->tries == -1) {
Node *tmp = ifc_node->u.alias;
ifc_node->u.alias = node;
ifc_node = tmp;
}
/* Merge ifc_node (foo_i) into node (foo_n) */
if (ifc_node->tries > node->tries)
node->tries = ifc_node->tries;
for (ifc_i = ifc_node->u.interfaces; ifc_i; ifc_i = next_ifc_i) {
Neighbor *nb_i, *next_nb_i, *nb_n;
Interface *ifc_n = find_interface(ifc_i->addr, node);
old_neighbors = ifc_n->neighbors;
for (nb_i = ifc_i->neighbors; nb_i; nb_i = next_nb_i) {
next_nb_i = nb_i->next;
for (nb_n = old_neighbors; nb_n; nb_n = nb_n->next)
if (nb_i->addr == nb_n->addr) {
if (nb_i->metric != nb_n->metric
|| nb_i->threshold != nb_i->threshold)
log(LOG_WARNING, 0,
"inconsistent %s for neighbor %s of %s",
"metric/threshold",
inet_fmt(nb_i->addr, s1),
inet_fmt(node->addr, s2));
free(nb_i);
break;
}
if (!nb_n) { /* no match for this neighbor yet */
nb_i->next = ifc_n->neighbors;
ifc_n->neighbors = nb_i;
}
}
next_ifc_i = ifc_i->next;
free(ifc_i);
}
ifc_node->tries = -1;
ifc_node->u.alias = node;
}
ifc = find_interface(ifc_addr, node);
old_neighbors = ifc->neighbors;
/* Add the neighbors for this interface */
while (ncount--) {
u_long neighbor;
Neighbor *nb;
Node *n_node;
if (datalen < 4) {
log(LOG_WARNING, 0, "received truncated neighbor list from %s",
inet_fmt(src, s1));
return;
}
GET_ADDR(neighbor);
neighbor = htonl(neighbor);
datalen -= 4;
for (nb = old_neighbors; nb; nb = nb->next)
if (nb->addr == neighbor) {
if (metric != nb->metric || threshold != nb->threshold)
log(LOG_WARNING, 0,
"inconsistent %s for neighbor %s of %s",
"metric/threshold",
inet_fmt(nb->addr, s1), inet_fmt(node->addr, s2));
goto next_neighbor;
}
nb = (Neighbor *) malloc(sizeof(Neighbor));
nb->next = ifc->neighbors;
ifc->neighbors = nb;
nb->addr = neighbor;
nb->metric = metric;
nb->threshold = threshold;
nb->flags = 0;
n_node = find_node(neighbor, &routers);
if (n_node->tries == 0 && !target_addr) { /* it's a new router */
ask(neighbor);
n_node->tries = 1;
}
next_neighbor: ;
}
}
}
void accept_neighbors2(src, dst, p, datalen)
u_long src, dst;
u_char *p;
int datalen;
{
Node *node = find_node(src, &routers);
if (node->tries == 0) /* Never heard of 'em; must have hit them at */
node->tries = 1; /* least once, though...*/
else if (node->tries == -1) /* follow alias link */
node = node->u.alias;
while (datalen > 0) { /* loop through interfaces */
u_long ifc_addr;
u_char metric, threshold, ncount, flags;
Node *ifc_node;
Interface *ifc;
Neighbor *old_neighbors;
if (datalen < 4 + 4) {
log(LOG_WARNING, 0, "received truncated interface record from %s",
inet_fmt(src, s1));
return;
}
ifc_addr = *(u_long*)p;
p += 4;
metric = *p++;
threshold = *p++;
flags = *p++;
ncount = *p++;
datalen -= 4 + 4;
/* Fix up any alias information */
ifc_node = find_node(ifc_addr, &routers);
if (ifc_node->tries == 0) { /* new node */
ifc_node->tries = -1;
ifc_node->u.alias = node;
} else if (ifc_node != node
&& (ifc_node->tries > 0 || ifc_node->u.alias != node)) {
/* must merge two hosts' nodes */
Interface *ifc_i, *next_ifc_i;
if (ifc_node->tries == -1) {
Node *tmp = ifc_node->u.alias;
ifc_node->u.alias = node;
ifc_node = tmp;
}
/* Merge ifc_node (foo_i) into node (foo_n) */
if (ifc_node->tries > node->tries)
node->tries = ifc_node->tries;
for (ifc_i = ifc_node->u.interfaces; ifc_i; ifc_i = next_ifc_i) {
Neighbor *nb_i, *next_nb_i, *nb_n;
Interface *ifc_n = find_interface(ifc_i->addr, node);
old_neighbors = ifc_n->neighbors;
for (nb_i = ifc_i->neighbors; nb_i; nb_i = next_nb_i) {
next_nb_i = nb_i->next;
for (nb_n = old_neighbors; nb_n; nb_n = nb_n->next)
if (nb_i->addr == nb_n->addr) {
if (nb_i->metric != nb_n->metric
|| nb_i->threshold != nb_i->threshold)
log(LOG_WARNING, 0,
"inconsistent %s for neighbor %s of %s",
"metric/threshold",
inet_fmt(nb_i->addr, s1),
inet_fmt(node->addr, s2));
free(nb_i);
break;
}
if (!nb_n) { /* no match for this neighbor yet */
nb_i->next = ifc_n->neighbors;
ifc_n->neighbors = nb_i;
}
}
next_ifc_i = ifc_i->next;
free(ifc_i);
}
ifc_node->tries = -1;
ifc_node->u.alias = node;
}
ifc = find_interface(ifc_addr, node);
old_neighbors = ifc->neighbors;
/* Add the neighbors for this interface */
while (ncount--) {
u_long neighbor;
Neighbor *nb;
Node *n_node;
if (datalen < 4) {
log(LOG_WARNING, 0, "received truncated neighbor list from %s",
inet_fmt(src, s1));
return;
}
neighbor = *(u_long*)p;
p += 4;
datalen -= 4;
if (neighbor == 0)
/* make leaf nets point to themselves */
neighbor = ifc_addr;
for (nb = old_neighbors; nb; nb = nb->next)
if (nb->addr == neighbor) {
if (metric != nb->metric || threshold != nb->threshold)
log(LOG_WARNING, 0,
"inconsistent %s for neighbor %s of %s",
"metric/threshold",
inet_fmt(nb->addr, s1), inet_fmt(node->addr, s2));
goto next_neighbor;
}
nb = (Neighbor *) malloc(sizeof(Neighbor));
nb->next = ifc->neighbors;
ifc->neighbors = nb;
nb->addr = neighbor;
nb->metric = metric;
nb->threshold = threshold;
nb->flags = flags | NF_PRESENT;
n_node = find_node(neighbor, &routers);
if (n_node->tries == 0 && !target_addr) { /* it's a new router */
ask(neighbor);
n_node->tries = 1;
}
next_neighbor: ;
}
}
}
void check_vif_state()
{
log(LOG_NOTICE, 0, "network marked down...");
}
int retry_requests(node)
Node *node;
{
int result;
if (node) {
result = retry_requests(node->left);
if (node->tries > 0 && node->tries < retries) {
if (node->version)
ask2(node->addr);
else
ask(node->addr);
node->tries++;
result = 1;
}
return retry_requests(node->right) || result;
} else
return 0;
}
char *inet_name(addr)
u_long addr;
{
struct hostent *e;
e = gethostbyaddr(&addr, sizeof(addr), AF_INET);
return e ? e->h_name : 0;
}
void print_map(node)
Node *node;
{
if (node) {
char *name, *addr;
print_map(node->left);
addr = inet_fmt(node->addr, s1);
if (!target_addr
|| (node->tries >= 0 && node->u.interfaces)
|| (node->tries == -1
&& node->u.alias->tries >= 0
&& node->u.alias->u.interfaces)) {
if (show_names && (name = inet_name(node->addr)))
printf("%s (%s):", addr, name);
else
printf("%s:", addr);
if (node->tries < 0)
printf(" alias for %s\n\n", inet_fmt(node->u.alias->addr, s1));
else if (!node->u.interfaces)
printf(" no response to query\n\n");
else {
Interface *ifc;
if (node->version)
printf(" <v%d.%d>", node->version & 0xff,
(node->version >> 8) & 0xff);
printf("\n");
for (ifc = node->u.interfaces; ifc; ifc = ifc->next) {
Neighbor *nb;
char *ifc_name = inet_fmt(ifc->addr, s1);
int ifc_len = strlen(ifc_name);
int count = 0;
printf(" %s:", ifc_name);
for (nb = ifc->neighbors; nb; nb = nb->next) {
if (count > 0)
printf("%*s", ifc_len + 5, "");
printf(" %s", inet_fmt(nb->addr, s1));
if (show_names && (name = inet_name(nb->addr)))
printf(" (%s)", name);
printf(" [%d/%d", nb->metric, nb->threshold);
if (nb->flags) {
u_short flags = nb->flags;
if (flags & DVMRP_NF_TUNNEL)
printf("/tunnel");
if (flags & DVMRP_NF_SRCRT)
printf("/srcrt");
if (flags & DVMRP_NF_QUERIER)
printf("/querier");
if (flags & DVMRP_NF_DISABLED)
printf("/disabled");
if (flags & DVMRP_NF_DOWN)
printf("/down");
}
printf("]\n");
count++;
}
}
printf("\n");
}
}
print_map(node->right);
}
}
char *graph_name(addr, buf)
u_long addr;
char *buf;
{
char *name;
if (show_names && (name = inet_name(addr)))
strcpy(buf, name);
else
inet_fmt(addr, buf);
return buf;
}
void graph_edges(node)
Node *node;
{
Interface *ifc;
Neighbor *nb;
char name[100];
if (node) {
graph_edges(node->left);
if (node->tries >= 0) {
printf(" %d {$ NP %d0 %d0 $} \"%s%s\" \n",
(int) node->addr,
node->addr & 0xFF, (node->addr >> 8) & 0xFF,
graph_name(node->addr, name),
node->u.interfaces ? "" : "*");
for (ifc = node->u.interfaces; ifc; ifc = ifc->next)
for (nb = ifc->neighbors; nb; nb = nb->next) {
Node *nb_node = find_node(nb->addr, &routers);
Neighbor *nb2;
if (nb_node->tries < 0)
nb_node = nb_node->u.alias;
if (node != nb_node &&
(!(nb2 = find_neighbor(node->addr, nb_node))
|| node->addr < nb_node->addr)) {
printf(" %d \"%d/%d",
nb_node->addr, nb->metric, nb->threshold);
if (nb2 && (nb2->metric != nb->metric
|| nb2->threshold != nb->threshold))
printf(",%d/%d", nb2->metric, nb2->threshold);
if (nb->flags & NF_PRESENT)
printf("%s%s",
nb->flags & DVMRP_NF_SRCRT ? "" :
nb->flags & DVMRP_NF_TUNNEL ? "E" : "P",
nb->flags & DVMRP_NF_DOWN ? "D" : "");
printf("\"\n");
}
}
printf(" ;\n");
}
graph_edges(node->right);
}
}
void elide_aliases(node)
Node *node;
{
if (node) {
elide_aliases(node->left);
if (node->tries >= 0) {
Interface *ifc;
for (ifc = node->u.interfaces; ifc; ifc = ifc->next) {
Neighbor *nb;
for (nb = ifc->neighbors; nb; nb = nb->next) {
Node *nb_node = find_node(nb->addr, &routers);
if (nb_node->tries < 0)
nb->addr = nb_node->u.alias->addr;
}
}
}
elide_aliases(node->right);
}
}
void graph_map()
{
u_long now = time(0);
char *nowstr = ctime(&now);
nowstr[24] = '\0'; /* Kill the newline at the end */
elide_aliases(routers);
printf("GRAPH \"Multicast Router Connectivity: %s\" = UNDIRECTED\n",
nowstr);
graph_edges(routers);
printf("END\n");
}
int get_number(var, deflt, pargv, pargc)
int *var, *pargc, deflt;
char ***pargv;
{
if ((*pargv)[0][2] == '\0') { /* Get the value from the next argument */
if (*pargc > 1 && isdigit((*pargv)[1][0])) {
(*pargv)++, (*pargc)--;
*var = atoi((*pargv)[0]);
return 1;
} else if (deflt >= 0) {
*var = deflt;
return 1;
} else
return 0;
} else { /* Get value from the rest of this argument */
if (isdigit((*pargv)[0][2])) {
*var = atoi((*pargv)[0] + 2);
return 1;
} else {
return 0;
}
}
}
u_long host_addr(name)
char *name;
{
struct hostent *e = gethostbyname(name);
int addr;
if (e)
memcpy(&addr, e->h_addr_list[0], e->h_length);
else {
addr = inet_addr(name);
if (addr == -1)
addr = 0;
}
return addr;
}
main(argc, argv)
int argc;
char *argv[];
{
int flood = FALSE, graph = FALSE;
#ifdef SYSV
setvbuf(stderr, NULL, _IOLBF, 0);
#else
setlinebuf(stderr);
#endif
if (geteuid() != 0) {
fprintf(stderr, "must be root\n");
exit(1);
}
argv++, argc--;
while (argc > 0 && argv[0][0] == '-') {
switch (argv[0][1]) {
case 'd':
if (!get_number(&debug, DEFAULT_DEBUG, &argv, &argc))
goto usage;
break;
case 'f':
flood = TRUE;
break;
case 'g':
graph = TRUE;
break;
case 'n':
show_names = FALSE;
break;
case 'r':
if (!get_number(&retries, -1, &argv, &argc))
goto usage;
break;
case 't':
if (!get_number(&timeout, -1, &argv, &argc))
goto usage;
break;
default:
goto usage;
}
argv++, argc--;
}
if (argc > 1) {
usage:
fprintf(stderr,
"Usage: map-mbone [-f] [-g] [-n] [-t timeout] %s\n\n",
"[-r retries] [-d [debug-level]] [router]");
fprintf(stderr, "\t-f Flood the routing graph with queries\n");
fprintf(stderr, "\t (True by default unless `router' is given)\n");
fprintf(stderr, "\t-g Generate output in GraphEd format\n");
fprintf(stderr, "\t-n Don't look up DNS names for routers\n");
exit(1);
} else if (argc == 1 && !(target_addr = host_addr(argv[0]))) {
fprintf(stderr, "Unknown host: %s\n", argv[0]);
exit(2);
}
if (debug)
fprintf(stderr, "Debug level %u\n", debug);
init_igmp();
{ /* Find a good local address for us. */
int udp;
struct sockaddr_in addr;
int addrlen = sizeof(addr);
addr.sin_family = AF_INET;
addr.sin_addr.s_addr = dvmrp_group;
addr.sin_port = htons(2000); /* any port over 1024 will do... */
if ((udp = socket(AF_INET, SOCK_DGRAM, 0)) < 0
|| connect(udp, (struct sockaddr *) &addr, sizeof(addr)) < 0
|| getsockname(udp, (struct sockaddr *) &addr, &addrlen) < 0) {
perror("Determining local address");
exit(-1);
}
close(udp);
our_addr = addr.sin_addr.s_addr;
}
/* Send initial seed message to all local routers */
ask(target_addr ? target_addr : allhosts_group);
if (target_addr) {
Node *n = find_node(target_addr, &routers);
n->tries = 1;
if (flood)
target_addr = 0;
}
/* Main receive loop */
for(;;) {
fd_set fds;
struct timeval tv;
int count, recvlen, dummy = 0;
FD_ZERO(&fds);
FD_SET(igmp_socket, &fds);
tv.tv_sec = timeout;
tv.tv_usec = 0;
count = select(igmp_socket + 1, &fds, 0, 0, &tv);
if (count < 0) {
if (errno != EINTR)
perror("select");
continue;
} else if (count == 0) {
log(LOG_DEBUG, 0, "Timed out receiving neighbor lists");
if (retry_requests(routers))
continue;
else
break;
}
recvlen = recvfrom(igmp_socket, recv_buf, sizeof(recv_buf),
0, NULL, &dummy);
if (recvlen >= 0)
accept_igmp(recvlen);
else if (errno != EINTR)
perror("recvfrom");
}
printf("\n");
if (graph)
graph_map();
else {
if (!target_addr)
printf("Multicast Router Connectivity:\n\n");
print_map(routers);
}
exit(0);
}
void accept_prune()
{
}
void accept_graft()
{
}
void accept_g_ack()
{
}
void add_table_entry()
{
}
void leave_group_message()
{
}
void mtrace()
{
}

480
usr.sbin/mrouted/mrinfo.c Normal file
View File

@ -0,0 +1,480 @@
/*
* This tool requests configuration info from a multicast router
* and prints the reply (if any). Invoke it as:
*
* mrinfo router-name-or-address
*
* Written Wed Mar 24 1993 by Van Jacobson (adapted from the
* multicast mapper written by Pavel Curtis).
*
* The lawyers insist we include the following UC copyright notice.
* The mapper from which this is derived contained a Xerox copyright
* notice which follows the UC one. Try not to get depressed noting
* that the legal gibberish is larger than the program.
*
* Copyright (c) 1993 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
* documentation and/or other materials provided with the distribution.
* 3. All advertising materials mentioning features or use of this software
* must display the following acknowledgement:
* This product includes software developed by the Computer Systems
* Engineering Group at Lawrence Berkeley Laboratory.
* 4. Neither the name of the University nor of the Laboratory 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.
* ---------------------------------
* Copyright (c) Xerox Corporation 1992. All rights reserved.
*
* License is granted to copy, to use, and to make and to use derivative works
* for research and evaluation purposes, provided that Xerox is acknowledged
* in all documentation pertaining to any such copy or derivative work. Xerox
* grants no other licenses expressed or implied. The Xerox trade name should
* not be used in any advertising without its written permission.
*
* XEROX CORPORATION MAKES NO REPRESENTATIONS CONCERNING EITHER THE
* MERCHANTABILITY OF THIS SOFTWARE OR THE SUITABILITY OF THIS SOFTWARE FOR
* ANY PARTICULAR PURPOSE. The software is provided "as is" without express
* or implied warranty of any kind.
*
* These notices must be retained in any copies of any part of this software.
*/
#ifndef lint
static char rcsid[] =
"@(#) $Id: mrinfo.c,v 1.7 1994/08/24 23:54:04 thyagara Exp $";
/* original rcsid:
"@(#) Header: mrinfo.c,v 1.6 93/04/08 15:14:16 van Exp (LBL)";
*/
#endif
#include <netdb.h>
#include <sys/time.h>
#include "defs.h"
#define DEFAULT_TIMEOUT 4 /* How long to wait before retrying requests */
#define DEFAULT_RETRIES 3 /* How many times to ask each router */
u_long our_addr, target_addr = 0; /* in NET order */
int debug = 0;
int retries = DEFAULT_RETRIES;
int timeout = DEFAULT_TIMEOUT;
int target_level;
vifi_t numvifs; /* to keep loader happy */
/* (see COPY_TABLES macro called in kern.c) */
char *
inet_name(addr)
u_long addr;
{
struct hostent *e;
e = gethostbyaddr(&addr, sizeof(addr), AF_INET);
return e ? e->h_name : "?";
}
/*
* Log errors and other messages to stderr, according to the severity of the
* message and the current debug level. For errors of severity LOG_ERR or
* worse, terminate the program.
*/
void
log(severity, syserr, format, a, b, c, d, e)
int severity, syserr;
char *format;
int a, b, c, d, e;
{
char fmt[100];
switch (debug) {
case 0:
if (severity > LOG_WARNING)
return;
case 1:
if (severity > LOG_NOTICE)
return;
case 2:
if (severity > LOG_INFO)
return;
default:
fmt[0] = '\0';
if (severity == LOG_WARNING)
strcat(fmt, "warning - ");
strncat(fmt, format, 80);
fprintf(stderr, fmt, a, b, c, d, e);
if (syserr == 0)
fprintf(stderr, "\n");
else if (syserr < sys_nerr)
fprintf(stderr, ": %s\n", sys_errlist[syserr]);
else
fprintf(stderr, ": errno %d\n", syserr);
}
if (severity <= LOG_ERR)
exit(-1);
}
/*
* Send a neighbors-list request.
*/
void
ask(dst)
u_long dst;
{
send_igmp(our_addr, dst, IGMP_DVMRP, DVMRP_ASK_NEIGHBORS,
htonl(MROUTED_LEVEL), 0);
}
void
ask2(dst)
u_long dst;
{
send_igmp(our_addr, dst, IGMP_DVMRP, DVMRP_ASK_NEIGHBORS2,
htonl(MROUTED_LEVEL), 0);
}
/*
* Process an incoming neighbor-list message.
*/
void
accept_neighbors(src, dst, p, datalen)
u_long src, dst;
u_char *p;
int datalen;
{
u_char *ep = p + datalen;
#define GET_ADDR(a) (a = ((u_long)*p++ << 24), a += ((u_long)*p++ << 16),\
a += ((u_long)*p++ << 8), a += *p++)
printf("%s (%s):\n", inet_fmt(src, s1), inet_name(src));
while (p < ep) {
register u_long laddr;
register u_char metric;
register u_char thresh;
register int ncount;
GET_ADDR(laddr);
laddr = htonl(laddr);
metric = *p++;
thresh = *p++;
ncount = *p++;
while (--ncount >= 0) {
register u_long neighbor;
GET_ADDR(neighbor);
neighbor = htonl(neighbor);
printf(" %s -> ", inet_fmt(laddr, s1));
printf("%s (%s) [%d/%d]\n", inet_fmt(neighbor, s1),
inet_name(neighbor), metric, thresh);
}
}
}
void
accept_neighbors2(src, dst, p, datalen)
u_long src, dst;
u_char *p;
int datalen;
{
u_char *ep = p + datalen;
printf("%s (%s) [version %d.%d]:\n", inet_fmt(src, s1), inet_name(src),
target_level & 0xff, (target_level >> 8) & 0xff);
while (p < ep) {
register u_char metric;
register u_char thresh;
register u_char flags;
register int ncount;
register u_long laddr = *(u_long*)p;
p += 4;
metric = *p++;
thresh = *p++;
flags = *p++;
ncount = *p++;
while (--ncount >= 0) {
register u_long neighbor = *(u_long*)p;
p += 4;
printf(" %s -> ", inet_fmt(laddr, s1));
printf("%s (%s) [%d/%d", inet_fmt(neighbor, s1),
inet_name(neighbor), metric, thresh);
if (flags & DVMRP_NF_TUNNEL)
printf("/tunnel");
if (flags & DVMRP_NF_SRCRT)
printf("/srcrt");
if (flags & DVMRP_NF_QUERIER)
printf("/querier");
if (flags & DVMRP_NF_DISABLED)
printf("/disabled");
if (flags & DVMRP_NF_DOWN)
printf("/down");
printf("]\n");
}
}
}
int
get_number(var, deflt, pargv, pargc)
int *var, *pargc, deflt;
char ***pargv;
{
if ((*pargv)[0][2] == '\0') { /* Get the value from the next
* argument */
if (*pargc > 1 && isdigit((*pargv)[1][0])) {
(*pargv)++, (*pargc)--;
*var = atoi((*pargv)[0]);
return 1;
} else if (deflt >= 0) {
*var = deflt;
return 1;
} else
return 0;
} else { /* Get value from the rest of this argument */
if (isdigit((*pargv)[0][2])) {
*var = atoi((*pargv)[0] + 2);
return 1;
} else {
return 0;
}
}
}
u_long
host_addr(name)
char *name;
{
struct hostent *e = gethostbyname(name);
int addr;
if (e)
memcpy(&addr, e->h_addr_list[0], e->h_length);
else {
addr = inet_addr(name);
if (addr == -1)
addr = 0;
}
return addr;
}
main(argc, argv)
int argc;
char *argv[];
{
setlinebuf(stderr);
if (geteuid() != 0) {
fprintf(stderr, "must be root\n");
exit(1);
}
argv++, argc--;
while (argc > 0 && argv[0][0] == '-') {
switch (argv[0][1]) {
case 'd':
if (!get_number(&debug, DEFAULT_DEBUG, &argv, &argc))
goto usage;
break;
case 'r':
if (!get_number(&retries, -1, &argv, &argc))
goto usage;
break;
case 't':
if (!get_number(&timeout, -1, &argv, &argc))
goto usage;
break;
default:
goto usage;
}
argv++, argc--;
}
if (argc > 1 || (argc == 1 && !(target_addr = host_addr(argv[0])))) {
usage: fprintf(stderr,
"Usage: mrinfo [-t timeout] [-r retries] router\n");
exit(1);
}
if (target_addr == 0)
goto usage;
if (debug)
fprintf(stderr, "Debug level %u\n", debug);
init_igmp();
{ /* Find a good local address for us. */
int udp;
struct sockaddr_in addr;
int addrlen = sizeof(addr);
addr.sin_family = AF_INET;
addr.sin_addr.s_addr = target_addr;
addr.sin_port = htons(2000); /* any port over 1024 will
* do... */
if ((udp = socket(AF_INET, SOCK_DGRAM, 0)) < 0
|| connect(udp, (struct sockaddr *) & addr, sizeof(addr)) < 0
|| getsockname(udp, (struct sockaddr *) & addr, &addrlen) < 0) {
perror("Determining local address");
exit(-1);
}
close(udp);
our_addr = addr.sin_addr.s_addr;
}
ask(target_addr);
/* Main receive loop */
for (;;) {
fd_set fds;
struct timeval tv;
int count, recvlen, dummy = 0;
register u_long src, dst, group;
struct ip *ip;
struct igmp *igmp;
int ipdatalen, iphdrlen, igmpdatalen;
FD_ZERO(&fds);
FD_SET(igmp_socket, &fds);
tv.tv_sec = timeout;
tv.tv_usec = 0;
count = select(igmp_socket + 1, &fds, 0, 0, &tv);
if (count < 0) {
if (errno != EINTR)
perror("select");
continue;
} else if (count == 0) {
log(LOG_DEBUG, 0, "Timed out receiving neighbor lists");
if (--retries < 0)
exit(1);
if (target_level == 0)
ask(target_addr);
else
ask2(target_addr);
continue;
}
recvlen = recvfrom(igmp_socket, recv_buf, sizeof(recv_buf),
0, NULL, &dummy);
if (recvlen <= 0) {
if (recvlen && errno != EINTR)
perror("recvfrom");
continue;
}
if (recvlen < sizeof(struct ip)) {
log(LOG_WARNING, 0,
"packet too short (%u bytes) for IP header",
recvlen);
continue;
}
ip = (struct ip *) recv_buf;
src = ip->ip_src.s_addr;
if (src != target_addr) {
fprintf(stderr, "mrinfo: got reply from %s",
inet_fmt(src, s1));
fprintf(stderr, " instead of %s\n",
inet_fmt(target_addr, s1));
continue;
}
dst = ip->ip_dst.s_addr;
iphdrlen = ip->ip_hl << 2;
ipdatalen = ip->ip_len;
if (iphdrlen + ipdatalen != recvlen) {
log(LOG_WARNING, 0,
"packet shorter (%u bytes) than hdr+data length (%u+%u)",
recvlen, iphdrlen, ipdatalen);
continue;
}
igmp = (struct igmp *) (recv_buf + iphdrlen);
group = igmp->igmp_group.s_addr;
igmpdatalen = ipdatalen - IGMP_MINLEN;
if (igmpdatalen < 0) {
log(LOG_WARNING, 0,
"IP data field too short (%u bytes) for IGMP, from %s",
ipdatalen, inet_fmt(src, s1));
continue;
}
if (igmp->igmp_type != IGMP_DVMRP)
continue;
switch (igmp->igmp_code) {
case DVMRP_NEIGHBORS:
if (group) {
/* knows about DVMRP_NEIGHBORS2 msg */
if (target_level == 0) {
target_level = ntohl(group);
ask2(target_addr);
}
} else {
accept_neighbors(src, dst, (char *)(igmp + 1),
igmpdatalen);
exit(0);
}
break;
case DVMRP_NEIGHBORS2:
accept_neighbors2(src, dst, (char *)(igmp + 1),
igmpdatalen);
exit(0);
}
}
}
/* dummies */
void accept_probe()
{
}
void accept_group_report()
{
}
void accept_neighbor_request2()
{
}
void accept_report()
{
}
void accept_neighbor_request()
{
}
void accept_prune()
{
}
void accept_graft()
{
}
void accept_g_ack()
{
}
void add_table_entry()
{
}
void check_vif_state()
{
}
void leave_group_message()
{
}
void mtrace()
{
}

319
usr.sbin/mrouted/mrouted.8 Normal file
View File

@ -0,0 +1,319 @@
'\"COPYRIGHT 1989 by The Board of Trustees of Leland Stanford Junior University.
.TH MROUTED 8
.UC 5
.SH NAME
mrouted \- IP multicast routing daemon
.SH SYNOPSIS
.B /etc/mrouted
[
.B \-p
] [
.B \-c
.I config_file
] [
.B \-d
[
.I debug_level
]]
.SH DESCRIPTION
.I Mrouted
is an implementation of the Distance-Vector Multicast Routing
Protocol (DVMRP), an earlier version of which is specified in RFC-1075.
It maintains topological knowledge via a distance-vector routing protocol
(like RIP, described in RFC-1058), upon which it implements a multicast
datagram forwarding algorithm called Reverse Path Multicasting.
.PP
.I Mrouted
forwards a multicast datagram along a shortest (reverse) path tree
rooted at the subnet on which the datagram originates. The multicast
delivery tree may be thought of as a broadcast delivery tree that has
been pruned back so that it does not extend beyond those subnetworks
that have members of the destination group. Hence, datagrams
are not forwarded along those branches which have no listeners of the
multicast group. The IP time-to-live of a multicast datagram can be
used to limit the range of multicast datagrams.
.PP
In order to support multicasting among subnets that are separated by (unicast)
routers that do not support IP multicasting,
.I mrouted
includes support for
"tunnels", which are virtual point-to-point links between pairs of
.IR mrouted s
located anywhere in an internet. IP multicast packets are encapsulated for
transmission through tunnels, so that they look like normal unicast datagrams
to intervening routers and subnets. The encapsulation
is added on entry to a tunnel, and stripped off
on exit from a tunnel.
By default, the packets are encapsulated using the IP-in-IP protocol
(IP protocol number 4).
Older versions of
.I mrouted
tunnel using IP source routing, which puts a heavy load on some
types of routers.
This version supports IP source route tunnelling only for backwards
compatibility.
.PP
The tunnelling mechanism allows
.I mrouted
to establish a virtual internet, for
the purpose of multicasting only, which is independent of the physical
internet, and which may span multiple Autonomous Systems. This capability
is intended for experimental support of internet multicasting only, pending
widespread support for multicast routing by the regular (unicast) routers.
.I Mrouted
suffers from the well-known scaling problems of any distance-vector
routing protocol, and does not (yet) support hierarchical multicast routing.
.PP
.I Mrouted
handles multicast routing only; there may or may not be unicast routing
software running on the same machine as
.IR mrouted .
With the use of tunnels, it
is not necessary for
.I mrouted
to have access to more than one physical subnet
in order to perform multicast forwarding.
.br
.ne 5
.SH INVOCATION
.PP
If no "\-d" option is given, or if the debug level is specified as 0,
.I mrouted
detaches from the invoking terminal. Otherwise, it remains attached to the
invoking terminal and responsive to signals from that terminal. If "\-d" is
given with no argument, the debug level defaults to 2. Regardless of the
debug level,
.I mrouted
always writes warning and error messages to the system
log demon. Non-zero debug levels have the following effects:
.IP "level 1"
all syslog'ed messages are also printed to stderr.
.IP "level 2"
all level 1 messages plus notifications of "significant"
events are printed to stderr.
.IP "level 3"
all level 2 messages plus notifications of all packet
arrivals and departures are printed to stderr.
.SH CONFIGURATION
.PP
.I Mrouted
automatically configures itself to forward on all multicast-capable
interfaces, i.e., interfaces that have the IFF_MULTICAST flag set (excluding
the loopback "interface"), and it finds other
.IR mrouted s
directly reachable
via those interfaces. To override the default configuration, or to add
tunnel links to other
.IR mrouted s,
configuration commands may be placed in
/etc/mrouted.conf (or an alternative file, specified by the "\-c" option).
There are four types of configuration commands:
.nf
phyint <local-addr> [disable] [metric <m>]
[threshold <t>] [rate_limit <b>]
[boundary <scoped-addr>/<mask-len>]
tunnel <local-addr> <remote-addr> [metric <m>]
[threshold <t>] [srcrt] [rate_limit <b>]
[boundary <scoped-addr>/<mask-len>]
cache_lifetime <ct>
pruning <off/on>
.fi
.PP
One note about the configuration commands - all the phyint and tunnel
command options must be on a single line except for the boundary option
which may begin on a separate line. A single phyint or tunnel command may
have multiple boundary options.
.PP
The phyint command can be used to disable multicast routing on the physical
interface identified by local IP address <local-addr>, or to associate a
non-default metric or threshold with the specified physical interface.
The local IP address <local-addr> may be alternatively replaced by the
interface name (e.g le0) for the phyint command only.
Phyint commands must precede tunnel commands.
.PP
The tunnel command can be used to establish a tunnel link between local
IP address <local-addr> and remote IP address <remote-addr>, and to associate
a non-default metric or threshold with that tunnel. The tunnel must be set
up in the mrouted.conf files of both routers before it can be used.
For backwards compatibility with older
.IR mrouted s,
the srcrt keyword specifies
encapsulation using IP source routing.
.PP
The cache_lifetime is a value that determines the amount of time that a
cached multicast route stays in kernel before timing out. The value of this
entry should lie between 300 (5 min) and 86400 (1 day). It defaults to 300.
.PP
The pruning <off/on> option is provided for
.IR mrouted
to act as a non-pruning router. It is also possible to start
.Ir mrouted
in a non-pruning mode using the "-p" option on the command line. It is
expected that a router would be configured in this manner for test
purposes only. The default mode is pruning enabled.
.PP
The metric is the "cost" associated with sending a datagram on the given
interface or tunnel; it may be used to influence the choice of routes.
The metric defaults to 1. Metrics should be kept as small as possible,
because
.I mrouted
cannot route along paths with a sum of metrics greater
than 31.
.LP
The threshold is the minimum IP time-to-live required for a multicast datagram
to be forwarded to the given interface or tunnel. It is used to control the
scope of multicast datagrams. (The TTL of forwarded packets is only compared
to the threshold, it is not decremented by the threshold. Every multicast
router decrements the TTL by 1.) The default threshold is 1.
.LP
In general, all
.IR mrouted s
connected to a particular subnet or tunnel should
use the same metric and threshold for that subnet or tunnel.
.PP
The rate_limit option allows the network administrator to specify a
certain bandwidth in Kbits/second which would be allocated to multicast
traffic.
.PP
The boundary option allows an interface
to be configured as an administrative boundary for the specified
scoped address. Packets belonging to this address will not
be forwarded on a scoped interface.
.PP
.I Mrouted
will not initiate execution if it has fewer than two enabled vifs,
where a vif (virtual interface) is either a physical multicast-capable
interface or a tunnel. It will log a warning if all of its vifs are
tunnels; such an
.I mrouted
configuration would be better replaced by more
direct tunnels (i.e., eliminate the middle man).
.SH SIGNALS
.PP
.I Mrouted
responds to the following signals:
.IP HUP
restarts
.I mrouted .
The configuration file is reread every time this signal is evoked.
.IP INT
terminates execution gracefully (i.e., by sending
good-bye messages to all neighboring routers).
.IP TERM
same as INT
.IP USR1
dumps the internal routing tables to /usr/tmp/mrouted.dump.
.IP USR2
dumps the internal cache tables to /usr/tmp/mrouted.cache.
.IP QUIT
dumps the internal routing tables to stderr (only if
.I mrouted
was invoked with a non-zero debug level).
.bp
.SH EXAMPLE
.PP
The routing tables look like this:
.nf
Virtual Interface Table
Vif Local-Address Metric Thresh Flags
0 36.2.0.8 subnet: 36.2 1 1 querier
groups: 224.0.2.1
224.0.0.4
pkts in: 3456
pkts out: 2322323
1 36.11.0.1 subnet: 36.11 1 1 querier
groups: 224.0.2.1
224.0.1.0
224.0.0.4
pkts in: 345
pkts out: 3456
2 36.2.0.8 tunnel: 36.8.0.77 3 1
peers: 36.8.0.77 (2.2)
boundaries: 239.0.1
: 239.1.2
pkts in: 34545433
pkts out: 234342
3 36.2.0.8 tunnel: 36.6.8.23 3 16
Multicast Routing Table (1136 entries)
Origin-Subnet From-Gateway Metric In-Vif Out-Vifs
36.2 1 0 1* 2 3*
36.8 36.8.0.77 4 2 0* 1* 3*
36.11 1 1 0* 2 3*
.
.
.
.fi
In this example, there are four vifs connecting to two subnets and two
tunnels. The vif 3 tunnel is not in use (no peer address). The vif 0 and
vif 1 subnets have some groups present; tunnels never have any groups. This
instance of
.I mrouted
is the one responsible for sending periodic group
membership queries on the vif 0 and vif 1 subnets, as indicated by the
"querier" flags. The list of boundaries indicate the scoped addresses on that
interface. A count of the no. of incoming and outgoing packets is also
shown at each interface.
.PP
Associated with each subnet from which a multicast datagram can originate
is the address of the previous hop router (unless the subnet is directly-
connected), the metric of the path back to the origin, the incoming vif for
multicasts from that origin, and a list of outgoing vifs. "*" means that
the outgoing vif is connected to a leaf of the broadcast tree rooted at the
origin, and a multicast datagram from that origin will be forwarded on that
outgoing vif only if there are members of the destination group on that leaf.
.bp
.PP
.I Mrouted
also maintains a copy of the kernel forwarding cache table. Entries
are created and deleted by
.I mrouted.
.PP
The cache tables look like this:
.nf
Multicast Routing Cache Table (325 entries)
Origin-Subnet Mcast-group CTmr IVif Prcv# Psnt Forwvifs
134.207.7 224.2.140.239 300 1 0 0 2
138.15.103 224.2.203.214 295 1 2 P 0p 2p
128.237.0 224.2.253.119 290 1 1 0 2p
129.215.200 224.2.207.48 40 1 1 0p 2
36.77.14 239.0.1.234 345 2b
.fi
Each entry is characterized by the origin subnet number and the
destination multicast group. The 'CTmr' field indicates the lifetime
(in seconds) of the entry. The entry is deleted from the cache table
when the timer decrements to zero. The Ivif field indicates the
incoming vif for multicast packets from that origin. Each router also
maintains a record of the number of prunes received from neighbouring
routers for a particular source and group. If there are no members of
a multicast group on any downward link of the multicast tree for a
subnet, a prune message is sent to the upstream router. They are
indicated by a "P" in the Psnt field. The Forwvifs field shows the
interfaces along which datagrams belonging to the source-group are
forwarded. A "p" indicates that no datagrams are being forwarded along
that interface. An unlisted interface is a leaf subnet with are no
members of the particular group on that subnet. A "b" on an interface
indicates that it is a boundary interface, i.e. traffic will not be
forwarded on the scoped address on that interface.
.SH FILES
/etc/mrouted.conf
.SH SEE ALSO
DVMRP is described, along with other multicast routing algorithms, in the
paper "Multicast Routing in Internetworks and Extended LANs" by S. Deering,
in the Proceedings of the ACM SIGCOMM '88 Conference.
.SH AUTHORS
Steve Deering & Ajit Thyagarajan

View File

@ -0,0 +1,26 @@
# $Id: mrouted.conf,v 1.5 1994/08/24 23:54:21 thyagara Exp $
#
# This is the configuration file for "mrouted", an IP multicast router.
# mrouted looks for it in "/etc/mrouted.conf".
#
# Command formats:
#
# cache_lifetime 3600
# pruning on
#
# phyint <local-addr> [disable] [metric <m>] [threshold <t>] [rate_limit <b>]
# [boundary <scoped-addr>/<mask-len>]
# tunnel <local-addr> <remote-addr> [srcrt] [metric <m>]
# [threshold <t>] [rate_limit <b>]
# [boundary <scoped-addr>/<mask-len>]
#
# NOTE: any phyint commands MUST precede any tunnel commands
# NOTE: boundary commands may appear on a separate line
# (OTHER keywords must be on the same line as phyint or tunnel)
# NOTE: the mask-len is the no. of leading 1's in the mask
#
phyint 128.4.2.2 metric 1 threshold 16 boundary 239.2.0.0/16
boundary 239.5.8.0/24
tunnel 128.4.0.77 128.4.0.8 metric 3 rate_limit 500 # <-- EXAMPLE
boundary 239.2.3.3/16 # 239.2.x.x is scoped

459
usr.sbin/mrouted/mtrace.c Normal file
View File

@ -0,0 +1,459 @@
#include <netdb.h>
#include <sys/time.h>
#include "defs.h"
#define DEFAULT_TIMEOUT 10 /* How long to wait before retrying requests */
int timeout = DEFAULT_TIMEOUT;
vifi_t numvifs; /* to keep loader happy */
/* (see COPY_TABLES macro called in kern.c) */
char *
inet_name(addr)
u_long addr;
{
struct hostent *e;
e = gethostbyaddr(&addr, sizeof(addr), AF_INET);
return e ? e->h_name : "?";
}
u_long
host_addr(name)
char *name;
{
struct hostent *e = gethostbyname(name);
int addr;
if (e)
memcpy(&addr, e->h_addr_list[0], e->h_length);
else {
addr = inet_addr(name);
if (addr == -1)
addr = 0;
}
return addr;
}
char *
proto_type(type)
u_char type;
{
switch (type) {
case PROTO_DVMRP:
return ("PROTO_DVMRP");
case PROTO_MOSPF:
return ("PROTO_MOSPF");
case PROTO_PIM:
return ("PROTO_PIM");
case PROTO_CBT:
return ("PROTO_CBT");
default:
return ("PROTO_UNKNOWN");
}
}
char *
flag_type(type)
u_char type;
{
switch (type) {
case TR_NO_ERR:
return ("NO_ERR");
case TR_WRONG_IF:
return ("WRONG_IF");
case TR_PRUNED:
return ("PRUNED");
case TR_SCOPED:
return ("SCOPED");
case TR_NO_RTE:
return ("NO_RTE");
default:
return ("INVALID ERR");
}
}
int
t_diff(a_sec, a_usec, b_sec, b_usec)
u_long a_sec, a_usec, b_sec, b_usec;
{
int d = a_sec - b_sec;
int ms = a_usec - b_usec;
if ((d < 0) ||
((d == 0) && (ms < 0))) {
d = b_sec - a_sec;
ms = b_usec - a_usec;
}
switch (d) {
case 0:
break;
case 2:
ms += 1000000;
case 1:
ms += 1000000;
break;
default:
ms += (1000000) * d;
}
return (ms/1000);
}
main(argc, argv)
int argc;
char *argv[];
{
struct timeval tq;
struct timezone tzp;
u_long resptime;
int udp;
struct sockaddr_in addr;
int addrlen = sizeof(addr);
u_long lcl_addr = 0; /* in NET order */
u_long qid = ((u_long)random() >> 8);
u_long qsrc = NULL;
u_long qgrp = NULL;
u_long qdst = NULL;
u_char qno = 0;
u_long raddr = NULL;
u_char qttl = 1;
u_char rttl = 1;
u_long dst = NULL;
struct tr_query *query;
struct tr_rlist *tr_rlist = NULL;
char *p;
int datalen = 0;
int i;
int done = 0;
argv++, argc--;
while (argc > 0 && *argv[0] == '-') {
switch (argv[0][1]) {
case 's':
if (argc > 1 && isdigit(*(argv + 1)[0])) {
argv++, argc--;
qsrc = host_addr(argv[0]);
break;
} else
goto usage;
case 'g':
if (argc > 1 && isdigit(*(argv + 1)[0])) {
argv++, argc--;
qgrp = host_addr(argv[0]);
break;
} else
goto usage;
case 'd':
if (argc > 1 && isdigit(*(argv + 1)[0])) {
argv++, argc--;
qdst = host_addr(argv[0]);
break;
} else
goto usage;
case 'x':
if (argc > 1 && isdigit(*(argv + 1)[0])) {
argv++, argc--;
dst = host_addr(argv[0]);
break;
} else
goto usage;
case 't':
if (argc > 1 && isdigit(*(argv + 1)[0])) {
argv++, argc--;
qttl = atoi(argv[0]);
if (qttl < 1)
qttl = 1;
break;
} else
goto usage;
case 'n':
if (argc > 1 && isdigit(*(argv + 1)[0])) {
argv++, argc--;
qno = atoi(argv[0]);
break;
} else
goto usage;
case 'l':
if (argc > 1 && isdigit(*(argv + 1)[0])) {
argv++, argc--;
rttl = atoi(argv[0]);
break;
} else
goto usage;
case 'r':
if (argc > 1 && isdigit(*(argv + 1)[0])) {
argv++, argc--;
raddr = host_addr(argv[0]);
break;
} else
goto usage;
default:
goto usage;
}
argv++, argc--;
}
if (argc > 0) {
usage: printf("usage: mtrace -s <src> -g <grp> -d <dst> -n <# reports> \n");
printf(" -t <ttl> [-x <qdst>] [-r <rdst>] [-l <rttl>]\n");
exit(1);
}
printf("Mtrace src %s grp %s dst %s #%d\n", inet_fmt(qsrc, s1),
inet_fmt(qgrp, s2), inet_fmt(qdst, s3), qno);
printf(" resp ttl %d resp addr %s\n", rttl, inet_fmt(raddr, s1));
init_igmp();
/* Obtain the local address from which to send out packets */
addr.sin_family = AF_INET;
addr.sin_addr.s_addr = qgrp;
addr.sin_port = htons(2000);
if (((udp = socket(AF_INET, SOCK_DGRAM, 0)) < 0) ||
(connect(udp, (struct sockaddr *) &addr, sizeof(addr)) < 0) ||
getsockname(udp, (struct sockaddr *) &addr, &addrlen) < 0) {
perror("Determining local address");
exit(-1);
}
close(udp);
lcl_addr = addr.sin_addr.s_addr;
/* Got the local address now */
/* Now, make up the IGMP packet to send */
query = (struct tr_query *)(send_buf + MIN_IP_HEADER_LEN + IGMP_MINLEN);
query->tr_src = qsrc;
query->tr_dst = qdst;
query->tr_qid = qid;
if (raddr)
query->tr_raddr = raddr;
else
query->tr_raddr = lcl_addr;
query->tr_rttl = rttl;
datalen += sizeof(struct tr_query);
if (IN_MULTICAST(ntohl(qgrp)))
k_set_ttl(qttl);
else
k_set_ttl(1);
if (dst == NULL)
dst = qgrp;
/*
* set timer to calculate delays & send query
*/
gettimeofday(&tq, &tzp);
send_igmp(lcl_addr, dst, IGMP_MTRACE, qno,
qgrp, datalen);
/*
* If the response is to be a multicast address, make sure we
* are listening on that multicast address.
*/
if (IN_MULTICAST(ntohl(raddr)))
k_join(raddr, lcl_addr);
/* Wait for our reply now */
while (!done) {
fd_set fds;
struct timeval tv;
struct timezone tzp;
int count, recvlen, dummy = 0;
register u_long src, dst, group, smask;
struct ip *ip;
struct igmp *igmp;
struct tr_resp *resp;
int ipdatalen, iphdrlen, igmpdatalen;
int rno;
FD_ZERO(&fds);
FD_SET(igmp_socket, &fds);
/* need to input timeout as optional argument */
tv.tv_sec = timeout;
tv.tv_usec = 0;
count = select(igmp_socket + 1, &fds, 0, 0, &tv);
if (count < 0) {
if (errno != EINTR)
perror("select");
continue;
} else if (count == 0) {
printf("Timed out receiving responses\n");
exit(1);
}
recvlen = recvfrom(igmp_socket, recv_buf, sizeof(recv_buf),
0, NULL, &dummy);
if (recvlen <= 0) {
if (recvlen && errno != EINTR)
perror("recvfrom");
continue;
}
if (recvlen < sizeof(struct ip)) {
log(LOG_WARNING, 0,
"packet too short (%u bytes) for IP header",
recvlen);
continue;
}
ip = (struct ip *) recv_buf;
iphdrlen = ip->ip_hl << 2;
ipdatalen = ip->ip_len;
if (iphdrlen + ipdatalen != recvlen) {
printf("packet shorter (%u bytes) than hdr+data length (%u+%u)\n",
recvlen, iphdrlen, ipdatalen);
continue;
}
igmp = (struct igmp *) (recv_buf + iphdrlen);
group = igmp->igmp_group.s_addr;
igmpdatalen = ipdatalen - IGMP_MINLEN;
if (igmpdatalen < 0) {
printf("IP data field too short (%u bytes) for IGMP, from %s\n",
ipdatalen, inet_fmt(src, s1));
continue;
}
if (igmp->igmp_type != IGMP_MTRACE &&
igmp->igmp_type != IGMP_MTRACE_RESP)
continue;
if (igmpdatalen == QLEN)
continue;
if ((igmpdatalen - QLEN)%RLEN) {
printf("packet with incorrect datalen\n");
continue;
}
query = (struct tr_query *)(igmp + 1);
/* If this is query with a different id, ignore! */
if (query->tr_qid != qid)
continue;
/*
* Most of the sanity checking done at this point.
* This is the packet we have been waiting for all this time
*/
resp = (struct tr_resp *)(query + 1);
rno = (igmpdatalen - QLEN)/RLEN;
/*
* print the responses out in reverse order (from src to dst)
*/
printf("src: <%s> grp: <%s> dst: <%s>\n\n", inet_fmt(qsrc, s1),
inet_fmt(qgrp, s2), inet_fmt(qdst, s3));
VAL_TO_MASK(smask, (resp+rno-1)->tr_smask);
if (((resp+rno-1)->tr_inaddr & smask) == (qsrc & smask))
printf(" %-15s \n", inet_fmt(qsrc, s1));
else
printf(" * * *\n");
resptime = 0;
while (rno--) {
struct tr_resp *r = resp + rno;
printf(" | \n");
printf(" %-15s ", inet_fmt(r->tr_inaddr, s1));
printf("ttl %d ", r->tr_fttl);
printf("cum: %d ms ",
t_diff(r->tr_qarr >> 16, (r->tr_qarr & 0xffff) << 4,
tq.tv_sec & 0xffff, tq.tv_usec));
printf("hop: %d ms ",
t_diff(resptime >> 16, (resptime & 0xffff) << 4,
r->tr_qarr >> 16, (r->tr_qarr & 0xffff) << 4));
printf("%s ", proto_type(r->tr_rproto));
printf("%s\n", flag_type(r->tr_rflags));
printf(" %-15s ", inet_fmt(r->tr_outaddr, s1));
printf("v_in: %ld ", r->tr_vifin);
printf("v_out: %ld ", r->tr_vifout);
printf("pkts: %ld\n", r->tr_pktcnt);
resptime = r->tr_qarr;
}
printf(" | \n");
printf(" %-15s \n", inet_fmt(qdst, s1));
/*
* if the response was multicast back, leave the group
*/
if (IN_MULTICAST(ntohl(raddr)))
k_leave(raddr, lcl_addr);
/* If I don't expect any more replies, exit here */
exit(0);
}
}
/* dummies */
void log()
{
}
void accept_probe()
{
}
void accept_group_report()
{
}
void accept_neighbors()
{
}
void accept_neighbors2()
{
}
void accept_neighbor_request2()
{
}
void accept_report()
{
}
void accept_neighbor_request()
{
}
void accept_prune()
{
}
void accept_graft()
{
}
void accept_g_ack()
{
}
void add_table_entry()
{
}
void check_vif_state()
{
}
void mtrace()
{
}
void leave_group_message()
{
}

1370
usr.sbin/mrouted/prune.c Normal file

File diff suppressed because it is too large Load Diff

123
usr.sbin/mrouted/prune.h Normal file
View File

@ -0,0 +1,123 @@
/*
* The mrouted program is covered by the license in the accompanying file
* named "LICENSE". Use of the mrouted program represents acceptance of
* the terms and conditions listed in that file.
*
* The mrouted program is COPYRIGHT 1989 by The Board of Trustees of
* Leland Stanford Junior University.
*
*
* $Id: prune.h,v 1.3 1994/08/24 23:54:40 thyagara Exp $
*/
/*
* Macro for copying the user-level cache table to the kernel
* level table variable passed on by the setsock option
*/
#define COPY_TABLES(from, to) { \
register u_int _i; \
(to).mfcc_origin.s_addr = (from)->kt_origin; \
(to).mfcc_mcastgrp.s_addr = (from)->kt_mcastgrp; \
(to).mfcc_originmask.s_addr = (from)->kt_originmask; \
(to).mfcc_parent = (from)->kt_parent; \
for (_i = 0; _i < numvifs; _i++) \
(to).mfcc_ttls[_i] = (from)->kt_ttls[_i]; \
};
/*
* User level Kernel Cache Table structure
*
* A copy of the kernel table is kept at the user level. Modifications are
* made to this table and then passed on to the kernel. A timeout value is
* an extra field in the user level table.
*
*/
struct ktable
{
struct ktable *kt_next; /* pointer to the next entry */
u_long kt_origin; /* subnet origin of multicasts */
u_long kt_mcastgrp; /* multicast group associated */
u_long kt_originmask; /* subnet mask for origin */
vifi_t kt_parent; /* incoming vif */
u_long kt_gateway; /* upstream router */
vifbitmap_t kt_children; /* outgoing children vifs */
vifbitmap_t kt_leaves; /* subset of outgoing children vifs */
vifbitmap_t kt_scope; /* scoped interfaces */
u_char kt_ttls[MAXVIFS]; /* ttl vector for forwarding */
vifbitmap_t kt_grpmems; /* forw. vifs for src, grp */
int kt_timer; /* for timing out entry in cache */
struct prunlst *kt_rlist; /* router list nghboring this rter */
u_short kt_prun_count; /* count of total no. of prunes */
int kt_prsent_timer; /* prune lifetime timer */
u_int kt_grftsnt; /* graft sent upstream */
};
/*
* structure to store incoming prunes
*/
struct prunlst
{
struct prunlst *rl_next;
u_long rl_router;
u_long rl_router_subnet;
vifi_t rl_vifi;
int rl_timer;
};
struct tr_query {
u_long tr_src; /* traceroute source */
u_long tr_dst; /* traceroute destination */
u_long tr_raddr; /* traceroute response address */
struct {
u_int ttl : 8; /* traceroute response ttl */
u_int qid : 24; /* traceroute query id */
} q;
} tr_query;
#define tr_rttl q.ttl
#define tr_qid q.qid
struct tr_resp {
u_long tr_qarr; /* query arrival time */
u_long tr_inaddr; /* incoming interface address */
u_long tr_outaddr; /* outgoing interface address */
u_long tr_rmtaddr; /* parent address in source tree */
u_long tr_vifin; /* input packet count on interface */
u_long tr_vifout; /* output packet count on interface */
u_long tr_pktcnt; /* total incoming packets for src-grp */
u_char tr_rproto; /* routing protocol deployed on router */
u_char tr_fttl; /* ttl required to forward on outvif */
u_char tr_smask; /* subnet mask for src addr */
u_char tr_rflags; /* forwarding error codes */
} tr_resp;
/* defs within mtrace */
#define QUERY 1
#define RESP 2
#define QLEN sizeof(struct tr_query)
#define RLEN sizeof(struct tr_resp)
/* fields for tr_rflags (forwarding error codes) */
#define TR_NO_ERR 0x0
#define TR_WRONG_IF 0x1
#define TR_PRUNED 0x2
#define TR_SCOPED 0x4
#define TR_NO_RTE 0x5
/* fields for tr_rproto (routing protocol) */
#define PROTO_DVMRP 0x1
#define PROTO_MOSPF 0x2
#define PROTO_PIM 0x3
#define PROTO_CBT 0x4
#define MASK_TO_VAL(x, i) { \
(i) = 0; \
while ((x) << (i)) \
(i)++; \
}
#define VAL_TO_MASK(x, i) { \
x = ~((1 << (32 - (i))) - 1); \
}

1076
usr.sbin/mrouted/route.c Normal file

File diff suppressed because it is too large Load Diff

50
usr.sbin/mrouted/route.h Normal file
View File

@ -0,0 +1,50 @@
/*
* The mrouted program is covered by the license in the accompanying file
* named "LICENSE". Use of the mrouted program represents acceptance of
* the terms and conditions listed in that file.
*
* The mrouted program is COPYRIGHT 1989 by The Board of Trustees of
* Leland Stanford Junior University.
*
*
* $Id: route.h,v 1.3 1993/05/30 01:36:38 deering Exp $
*/
/*
* Routing Table Entry, one per subnet from which a multicast could originate.
* (Note: all addresses, subnet numbers and masks are kept in NETWORK order.)
*
* The Routing Table is stored as a singly-linked list of these structures,
* ordered by increasing value of rt_originmask and, secondarily, by
* increasing value of rt_origin within each rt_originmask value.
* This data structure is efficient for generating route reports, whether
* full or partial, for processing received full reports, for clearing the
* CHANGED flags, and for periodically advancing the timers in all routes.
* It is not so efficient for updating a small number of routes in response
* to a partial report. In a stable topology, the latter are rare; if they
* turn out to be costing a lot, we can add an auxiliary hash table for
* faster access to arbitrary route entries.
*/
struct rtentry {
struct rtentry *rt_next; /* link to next entry MUST BE FIRST */
u_long rt_origin; /* subnet origin of multicasts */
u_long rt_originmask; /* subnet mask for origin */
short rt_originwidth; /* # bytes of origin subnet number */
u_char rt_metric; /* cost of route back to origin */
u_char rt_flags; /* RTF_ flags defined below */
u_long rt_gateway; /* first-hop gateway back to origin */
vifi_t rt_parent; /* incoming vif (ie towards origin) */
vifbitmap_t rt_children; /* outgoing children vifs */
vifbitmap_t rt_leaves; /* subset of outgoing children vifs */
u_long *rt_dominants; /* per vif dominant gateways */
u_long *rt_subordinates; /* per vif subordinate gateways */
u_long *rt_leaf_timers; /* per vif leaf confirmation timers */
u_long rt_timer; /* for timing out the route entry */
};
#define RTF_CHANGED 0x01 /* route changed but not reported */
#define RTF_LEAF_TIMING 0x02 /* some leaf timers are running */
#define ALL_ROUTES 0 /* possible arguments to report() */
#define CHANGED_ROUTES 1 /* and report_to_all_neighbors() */

1136
usr.sbin/mrouted/vif.c Normal file

File diff suppressed because it is too large Load Diff

62
usr.sbin/mrouted/vif.h Normal file
View File

@ -0,0 +1,62 @@
/*
* The mrouted program is covered by the license in the accompanying file
* named "LICENSE". Use of the mrouted program represents acceptance of
* the terms and conditions listed in that file.
*
* The mrouted program is COPYRIGHT 1989 by The Board of Trustees of
* Leland Stanford Junior University.
*
*
* $Id: vif.h,v 1.6 1994/08/24 23:54:47 thyagara Exp $
*/
/*
* User level Virtual Interface structure
*
* A "virtual interface" is either a physical, multicast-capable interface
* (called a "phyint") or a virtual point-to-point link (called a "tunnel").
* (Note: all addresses, subnet numbers and masks are kept in NETWORK order.)
*/
struct uvif {
u_short uv_flags; /* VIFF_ flags defined below */
u_char uv_metric; /* cost of this vif */
u_int uv_rate_limit; /* rate limit on this vif */
u_char uv_threshold; /* min ttl required to forward on vif */
u_long uv_lcl_addr; /* local address of this vif */
u_long uv_rmt_addr; /* remote end-point addr (tunnels only) */
u_long uv_subnet; /* subnet number (phyints only) */
u_long uv_subnetmask; /* subnet mask (phyints only) */
u_long uv_subnetbcast;/* subnet broadcast addr (phyints only) */
char uv_name[IFNAMSIZ]; /* interface name */
struct listaddr *uv_groups; /* list of local groups (phyints only) */
struct listaddr *uv_neighbors; /* list of neighboring routers */
struct vif_acl *uv_acl; /* access control list of groups */
};
#define VIFF_KERNEL_FLAGS (VIFF_TUNNEL|VIFF_SRCRT)
#define VIFF_DOWN 0x0100 /* kernel state of interface */
#define VIFF_DISABLED 0x0200 /* administratively disabled */
#define VIFF_QUERIER 0x0400 /* I am the subnet's querier */
#define VIFF_ONEWAY 0x0800 /* Maybe one way interface */
struct vif_acl {
struct vif_acl *acl_next; /* next acl member */
u_long acl_addr; /* Group address */
u_long acl_mask; /* Group addr. mask */
};
struct listaddr {
struct listaddr *al_next; /* link to next addr, MUST BE FIRST */
u_long al_addr; /* local group or neighbor address */
u_long al_timer; /* for timing out group or neighbor */
u_long al_genid; /* generation id for neighbor */
u_char al_pv; /* router protocol version */
u_char al_mv; /* router mrouted version */
u_long al_timerid; /* returned by set timer */
u_long al_query; /* second query in case of leave*/
u_short al_old; /* if old memberships are present */
u_short al_last; /* # of query's since last old rep */
};
#define NO_VIF ((vifi_t)MAXVIFS) /* An invalid vif index */