summaryrefslogtreecommitdiff
path: root/sbin/routed/rtquery
diff options
context:
space:
mode:
authorMichael Shalayeff <mickey@cvs.openbsd.org>1996-09-05 13:59:02 +0000
committerMichael Shalayeff <mickey@cvs.openbsd.org>1996-09-05 13:59:02 +0000
commit35859215e82e540ee7ac7506a2fc76110ab82eb6 (patch)
tree4c946ae681d2e798acb0a69c11887248fa04a24e /sbin/routed/rtquery
parent7e8ba13c1858637372a6d1fa599db0527a4a7b8c (diff)
new routed from SGI.
rip1, rip2, icmp, rdisc.
Diffstat (limited to 'sbin/routed/rtquery')
-rw-r--r--sbin/routed/rtquery/Makefile8
-rw-r--r--sbin/routed/rtquery/rtquery.8101
-rw-r--r--sbin/routed/rtquery/rtquery.c646
3 files changed, 755 insertions, 0 deletions
diff --git a/sbin/routed/rtquery/Makefile b/sbin/routed/rtquery/Makefile
new file mode 100644
index 00000000000..331841cf82f
--- /dev/null
+++ b/sbin/routed/rtquery/Makefile
@@ -0,0 +1,8 @@
+# $OpenBSD: Makefile,v 1.1 1996/09/05 13:59:00 mickey Exp $
+# @(#)Makefile 8.1 (Berkeley) 6/5/93
+
+PROG= rtquery
+MAN= rtquery.8
+
+.include "../../Makefile.inc"
+.include <bsd.prog.mk>
diff --git a/sbin/routed/rtquery/rtquery.8 b/sbin/routed/rtquery/rtquery.8
new file mode 100644
index 00000000000..1afb8b9478b
--- /dev/null
+++ b/sbin/routed/rtquery/rtquery.8
@@ -0,0 +1,101 @@
+.Dd June 1, 1996
+.Dt RTQUERY 8
+.Os BSD 4.4
+.Sh NAME
+.Nm rtquery
+.Nd query routing daemons for their routing tables
+.Sh SYNOPSIS
+.Nm
+.Op Fl np1
+.Op Fl w Ar timeout
+.Op Fl r Ar addr
+.Ar host ...
+.Sh DESCRIPTION
+.Nm Rtquery
+is used to query a network routing daemon,
+.Xr routed 8
+or
+.Xr gated 8 ,
+for its routing table by sending a
+.Em request
+or
+.Em poll
+command. The routing information in any routing
+.Em response
+packets returned is displayed numerically and symbolically.
+.Pp
+.Em Rtquery
+by default uses the
+.Em request
+command.
+When the
+.Ar -p
+option is specified,
+.Nm rtquery
+uses the
+.Em poll
+command, an
+undocumented extension to the RIP protocol supported by
+.Xr gated 8 .
+When querying gated, the
+.Em poll
+command is preferred over the
+.I Request
+command because the response is not subject to Split Horizon and/or
+Poisoned Reverse, and because some versions of gated do not answer
+the Request command. Routed does not answer the Poll command, but
+recognizes Requests coming from rtquery and so answers completely.
+.Pp
+.Em Rtquery
+is also used to turn tracing on or off in
+.Em routed .
+.Pp
+Options supported by
+.Nm rtquery :
+.Bl -tag -width Ds
+.It Fl n
+Normally network and host numbers are displayed both symbolically
+and numerically.
+The
+.Fl n
+option displays only the numeric network and host numbers.
+.It Fl p
+Uses the
+.Em Poll
+command to request full routing information from
+.Xr gated 8 ,
+This is an undocumented extension RIP protocol supported only by
+.Xr gated 8 .
+.It Fl 1
+query using RIP version 1 instead of RIP version 2.
+.It Fl w Ar timeout
+changes the delay for an answer from each host.
+By default, each host is given 15 seconds to respond.
+.It Fl r Ar addr
+ask about the route to destination
+.Em addr .
+.It Fl t Ar op
+change tracing, where
+.Em op
+is one of the following.
+Requests from processes not running with UID 0 or on distant networks
+are generally ignored.
+.El
+.Bl -tag -width Ds -offset indent-two
+.It Em on=filename
+turn tracing on into the specified file. That file must usually
+have been specified when the daemon was started or be the same
+as a fixed name, often
+.Pa /tmp/routed.log .
+.It Em more
+increases the debugging level.
+.It Em off
+turns off tracing.
+.El
+.Sh SEE ALSO
+.Xr routed 8 ,
+.Xr gated 8 .
+.br
+RFC\ 1058 - Routing Information Protocol, RIPv1
+.br
+RFC\ 1723 - Routing Information Protocol, RIPv2
diff --git a/sbin/routed/rtquery/rtquery.c b/sbin/routed/rtquery/rtquery.c
new file mode 100644
index 00000000000..1437334b286
--- /dev/null
+++ b/sbin/routed/rtquery/rtquery.c
@@ -0,0 +1,646 @@
+/* $OpenBSD: rtquery.c,v 1.1 1996/09/05 13:59:00 mickey Exp $ */
+
+/*-
+ * Copyright (c) 1982, 1986, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * 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 University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+char copyright[] =
+"@(#) Copyright (c) 1982, 1986, 1993\n\
+ The Regents of the University of California. All rights reserved.\n";
+
+#if !defined(lint)
+static char sccsid[] = "@(#)query.c 8.1 (Berkeley) 6/5/93";
+#endif
+
+#include <sys/param.h>
+#include <sys/protosw.h>
+#include <sys/socket.h>
+#include <sys/time.h>
+#include <netinet/in.h>
+#define RIPVERSION RIPv2
+#include <protocols/routed.h>
+#include <arpa/inet.h>
+#include <netdb.h>
+#include <errno.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#ifdef sgi
+#include <strings.h>
+#include <bstring.h>
+#endif
+
+#ifndef sgi
+#define _HAVE_SIN_LEN
+#endif
+
+#define WTIME 15 /* Time to wait for all responses */
+#define STIME (250*1000) /* usec to wait for another response */
+
+int s;
+
+char *pgmname;
+
+union {
+ struct rip rip;
+ char packet[MAXPACKETSIZE+MAXPATHLEN];
+} omsg_buf;
+#define OMSG omsg_buf.rip
+int omsg_len = sizeof(struct rip);
+
+union {
+ struct rip rip;
+ char packet[MAXPACKETSIZE+1024];
+ } imsg_buf;
+#define IMSG imsg_buf.rip
+
+int nflag; /* numbers, no names */
+int pflag; /* play the `gated` game */
+int ripv2 = 1; /* use RIP version 2 */
+int wtime = WTIME;
+int rflag; /* 1=ask about a particular route */
+int trace;
+int not_trace;
+
+struct timeval sent; /* when query sent */
+
+static void rip_input(struct sockaddr_in*, int);
+static int out(char *);
+static void trace_loop(char *argv[]);
+static void query_loop(char *argv[], int);
+static int getnet(char *, struct netinfo *);
+static u_int std_mask(u_int);
+
+
+int
+main(int argc,
+ char *argv[])
+{
+ int ch, bsize;
+ char *p, *options, *value;
+
+ OMSG.rip_nets[0].n_dst = RIP_DEFAULT;
+ OMSG.rip_nets[0].n_family = RIP_AF_UNSPEC;
+ OMSG.rip_nets[0].n_metric = htonl(HOPCNT_INFINITY);
+
+ pgmname = argv[0];
+ while ((ch = getopt(argc, argv, "np1w:r:t:")) != EOF)
+ switch (ch) {
+ case 'n':
+ not_trace = 1;
+ nflag = 1;
+ break;
+
+ case 'p':
+ not_trace = 1;
+ pflag = 1;
+ break;
+
+ case '1':
+ ripv2 = 0;
+ break;
+
+ case 'w':
+ not_trace = 1;
+ wtime = (int)strtoul(optarg, &p, 0);
+ if (*p != '\0'
+ || wtime <= 0)
+ goto usage;
+ break;
+
+ case 'r':
+ not_trace = 1;
+ if (rflag)
+ goto usage;
+ rflag = getnet(optarg, &OMSG.rip_nets[0]);
+ if (!rflag) {
+ struct hostent *hp = gethostbyname(optarg);
+ if (hp == 0) {
+ fprintf(stderr, "%s: %s:",
+ pgmname, optarg);
+ herror(0);
+ exit(1);
+ }
+ bcopy(hp->h_addr, &OMSG.rip_nets[0].n_dst,
+ sizeof(OMSG.rip_nets[0].n_dst));
+ OMSG.rip_nets[0].n_family = RIP_AF_INET;
+ OMSG.rip_nets[0].n_mask = -1;
+ rflag = 1;
+ }
+ break;
+
+ case 't':
+ trace = 1;
+ options = optarg;
+ while (*options != '\0') {
+ char *traceopts[] = {
+# define TRACE_ON 0
+ "on",
+# define TRACE_MORE 1
+ "more",
+# define TRACE_OFF 2
+ "off",
+ 0
+ };
+ switch (getsubopt(&options,traceopts,&value)) {
+ case TRACE_ON:
+ OMSG.rip_cmd = RIPCMD_TRACEON;
+ if (!value
+ || strlen(value) > MAXPATHLEN)
+ goto usage;
+ strcpy((char*)OMSG.rip_tracefile,value);
+ omsg_len += (strlen(value)
+ - sizeof(OMSG.ripun));
+ break;
+ case TRACE_MORE:
+ if (value)
+ goto usage;
+ OMSG.rip_cmd = RIPCMD_TRACEON;
+ OMSG.rip_tracefile[0] = '\0';
+ break;
+ case TRACE_OFF:
+ if (value)
+ goto usage;
+ OMSG.rip_cmd = RIPCMD_TRACEOFF;
+ OMSG.rip_tracefile[0] = '\0';
+ break;
+ default:
+ goto usage;
+ }
+ }
+ break;
+
+ default:
+ goto usage;
+ }
+ argv += optind;
+ argc -= optind;
+ if ((not_trace && trace) || argc == 0) {
+usage: fprintf(stderr, "%s: [-np1v] [-r tgt_rt] [-w wtime]"
+ " host1 [host2 ...]\n"
+ "or\t-t {on=filename|more|off} host1 host2 ...\n",
+ pgmname);
+ exit(1);
+ }
+
+ s = socket(AF_INET, SOCK_DGRAM, 0);
+ if (s < 0) {
+ perror("socket");
+ exit(2);
+ }
+
+ /* be prepared to receive a lot of routes */
+ for (bsize = 127*1024; ; bsize -= 1024) {
+ if (setsockopt(s, SOL_SOCKET, SO_RCVBUF,
+ &bsize, sizeof(bsize)) == 0)
+ break;
+ if (bsize <= 4*1024) {
+ perror("setsockopt SO_RCVBUF");
+ break;
+ }
+ }
+
+ if (trace)
+ trace_loop(argv);
+ else
+ query_loop(argv, argc);
+ /* NOTREACHED */
+}
+
+
+/* tell the target hosts about tracing
+ */
+static void
+trace_loop(char *argv[])
+{
+ struct sockaddr_in myaddr;
+ int res;
+
+ if (geteuid() != 0) {
+ (void)fprintf(stderr, "-t requires UID 0\n");
+ exit(1);
+ }
+
+ if (ripv2) {
+ OMSG.rip_vers = RIPv2;
+ } else {
+ OMSG.rip_vers = RIPv1;
+ }
+
+ bzero(&myaddr, sizeof(myaddr));
+ myaddr.sin_family = AF_INET;
+#ifdef _HAVE_SIN_LEN
+ myaddr.sin_len = sizeof(myaddr);
+#endif
+ myaddr.sin_port = htons(IPPORT_RESERVED-1);
+ while (bind(s, (struct sockaddr *)&myaddr, sizeof(myaddr)) < 0) {
+ if (errno != EADDRINUSE
+ || myaddr.sin_port == 0) {
+ perror("bind");
+ exit(2);
+ }
+ myaddr.sin_port = htons(ntohs(myaddr.sin_port)-1);
+ }
+
+ res = 1;
+ while (*argv != 0) {
+ if (out(*argv++) <= 0)
+ res = 0;
+ }
+ exit(res);
+}
+
+
+/* query all of the listed hosts
+ */
+static void
+query_loop(char *argv[], int argc)
+{
+ struct seen {
+ struct seen *next;
+ struct in_addr addr;
+ } *seen, *sp;
+ int answered = 0;
+ int cc;
+ fd_set bits;
+ struct timeval now, delay;
+ struct sockaddr_in from;
+ int fromlen;
+
+
+ OMSG.rip_cmd = (pflag) ? RIPCMD_POLL : RIPCMD_REQUEST;
+ if (ripv2) {
+ OMSG.rip_vers = RIPv2;
+ } else {
+ OMSG.rip_vers = RIPv1;
+ OMSG.rip_nets[0].n_mask = 0;
+ }
+
+ /* ask the first (valid) host */
+ seen = 0;
+ while (0 > out(*argv++)) {
+ if (*argv == 0)
+ exit(-1);
+ answered++;
+ }
+
+ FD_ZERO(&bits);
+ for (;;) {
+ FD_SET(s, &bits);
+ delay.tv_sec = 0;
+ delay.tv_usec = STIME;
+ cc = select(s+1, &bits, 0,0, &delay);
+ if (cc > 0) {
+ fromlen = sizeof(from);
+ cc = recvfrom(s, imsg_buf.packet,
+ sizeof(imsg_buf.packet), 0,
+ (struct sockaddr *)&from, &fromlen);
+ if (cc < 0) {
+ perror("recvfrom");
+ exit(1);
+ }
+ /* count the distinct responding hosts.
+ * You cannot match responding hosts with
+ * addresses to which queries were transmitted,
+ * because a router might respond with a
+ * different source address.
+ */
+ for (sp = seen; sp != 0; sp = sp->next) {
+ if (sp->addr.s_addr == from.sin_addr.s_addr)
+ break;
+ }
+ if (sp == 0) {
+ sp = malloc(sizeof(*sp));
+ sp->addr = from.sin_addr;
+ sp->next = seen;
+ seen = sp;
+ answered++;
+ }
+
+ rip_input(&from, cc);
+ continue;
+ }
+
+ if (cc < 0) {
+ if ( errno == EINTR)
+ continue;
+ perror("select");
+ exit(1);
+ }
+
+ /* After a pause in responses, probe another host.
+ * This reduces the intermingling of answers.
+ */
+ while (*argv != 0 && 0 > out(*argv++))
+ answered++;
+
+ /* continue until no more packets arrive
+ * or we have heard from all hosts
+ */
+ if (answered >= argc)
+ break;
+
+ /* or until we have waited a long time
+ */
+ if (gettimeofday(&now, 0) < 0) {
+ perror("gettimeofday(now)");
+ exit(1);
+ }
+ if (sent.tv_sec + wtime <= now.tv_sec)
+ break;
+ }
+
+ /* fail if there was no answer */
+ exit (answered >= argc ? 0 : 1);
+}
+
+
+/* sent do one host
+ */
+static int
+out(char *host)
+{
+ struct sockaddr_in router;
+ struct hostent *hp;
+
+ if (gettimeofday(&sent, 0) < 0) {
+ perror("gettimeofday(sent)");
+ return -1;
+ }
+
+ bzero(&router, sizeof(router));
+ router.sin_family = AF_INET;
+#ifdef _HAVE_SIN_LEN
+ router.sin_len = sizeof(router);
+#endif
+ if (!inet_aton(host, &router.sin_addr)) {
+ hp = gethostbyname(host);
+ if (hp == 0) {
+ herror(host);
+ return -1;
+ }
+ bcopy(hp->h_addr, &router.sin_addr, sizeof(router.sin_addr));
+ }
+ router.sin_port = htons(RIP_PORT);
+
+ if (sendto(s, &omsg_buf, omsg_len, 0,
+ (struct sockaddr *)&router, sizeof(router)) < 0) {
+ perror(host);
+ return -1;
+ }
+
+ return 0;
+}
+
+
+/*
+ * Handle an incoming RIP packet.
+ */
+static void
+rip_input(struct sockaddr_in *from,
+ int size)
+{
+ struct netinfo *n, *lim;
+ struct in_addr in;
+ char *name;
+ char net_buf[80];
+ u_int mask, dmask;
+ char *sp;
+ int i;
+ struct hostent *hp;
+ struct netent *np;
+ struct netauth *a;
+
+
+ if (nflag) {
+ printf("%s:", inet_ntoa(from->sin_addr));
+ } else {
+ hp = gethostbyaddr((char*)&from->sin_addr,
+ sizeof(struct in_addr), AF_INET);
+ if (hp == 0) {
+ printf("%s:",
+ inet_ntoa(from->sin_addr));
+ } else {
+ printf("%s (%s):", hp->h_name,
+ inet_ntoa(from->sin_addr));
+ }
+ }
+ if (IMSG.rip_cmd != RIPCMD_RESPONSE) {
+ printf("\n unexpected response type %d\n", IMSG.rip_cmd);
+ return;
+ }
+ printf(" RIPv%d%s %d bytes\n", IMSG.rip_vers,
+ (IMSG.rip_vers != RIPv1 && IMSG.rip_vers != RIPv2) ? " ?" : "",
+ size);
+ if (size > MAXPACKETSIZE) {
+ if (size > sizeof(imsg_buf) - sizeof(*n)) {
+ printf(" at least %d bytes too long\n",
+ size-MAXPACKETSIZE);
+ size = sizeof(imsg_buf) - sizeof(*n);
+ } else {
+ printf(" %d bytes too long\n",
+ size-MAXPACKETSIZE);
+ }
+ } else if (size%sizeof(*n) != sizeof(struct rip)%sizeof(*n)) {
+ printf(" response of bad length=%d\n", size);
+ }
+
+ n = IMSG.rip_nets;
+ lim = (struct netinfo *)((char*)n + size) - 1;
+ for (; n <= lim; n++) {
+ name = "";
+ if (n->n_family == RIP_AF_INET) {
+ in.s_addr = n->n_dst;
+ (void)strcpy(net_buf, inet_ntoa(in));
+
+ mask = ntohl(n->n_mask);
+ dmask = mask & -mask;
+ if (mask != 0) {
+ sp = &net_buf[strlen(net_buf)];
+ if (IMSG.rip_vers == RIPv1) {
+ (void)sprintf(sp," mask=%#x ? ",mask);
+ mask = 0;
+ } else if (mask + dmask == 0) {
+ for (i = 0;
+ (i != 32
+ && ((1<<i)&mask) == 0);
+ i++)
+ continue;
+ (void)sprintf(sp, "/%d",32-i);
+ } else {
+ (void)sprintf(sp," (mask %#x)", mask);
+ }
+ }
+
+ if (!nflag) {
+ if (mask == 0) {
+ mask = std_mask(in.s_addr);
+ if ((ntohl(in.s_addr) & ~mask) != 0)
+ mask = 0;
+ }
+ /* Without a netmask, do not worry about
+ * whether the destination is a host or a
+ * network. Try both and use the first name
+ * we get.
+ *
+ * If we have a netmask we can make a
+ * good guess.
+ */
+ if ((in.s_addr & ~mask) == 0) {
+ np = getnetbyaddr((long)in.s_addr,
+ AF_INET);
+ if (np != 0)
+ name = np->n_name;
+ else if (in.s_addr == 0)
+ name = "default";
+ }
+ if (name[0] == '\0'
+ && ((in.s_addr & ~mask) != 0
+ || mask == 0xffffffff)) {
+ hp = gethostbyaddr((char*)&in,
+ sizeof(in),
+ AF_INET);
+ if (hp != 0)
+ name = hp->h_name;
+ }
+ }
+
+ } else if (n->n_family == RIP_AF_AUTH) {
+ a = (struct netauth*)n;
+ (void)printf(" authentication type %d: ",
+ a->a_type);
+ for (i = 0; i < sizeof(a->au.au_pw); i++)
+ (void)printf("%02x ", a->au.au_pw[i]);
+ putc('\n', stdout);
+ continue;
+
+ } else {
+ (void)sprintf(net_buf, "(af %#x) %d.%d.%d.%d",
+ ntohs(n->n_family),
+ (char)(n->n_dst >> 24),
+ (char)(n->n_dst >> 16),
+ (char)(n->n_dst >> 8),
+ (char)n->n_dst);
+ }
+
+ (void)printf(" %-18s metric %2d %-10s",
+ net_buf, ntohl(n->n_metric), name);
+
+ if (n->n_nhop != 0) {
+ in.s_addr = n->n_nhop;
+ if (nflag)
+ hp = 0;
+ else
+ hp = gethostbyaddr((char*)&in, sizeof(in),
+ AF_INET);
+ (void)printf(" nhop=%-15s%s",
+ (hp != 0) ? hp->h_name : inet_ntoa(in),
+ (IMSG.rip_vers == RIPv1) ? " ?" : "");
+ }
+ if (n->n_tag != 0)
+ (void)printf(" tag=%#x%s", n->n_tag,
+ (IMSG.rip_vers == RIPv1) ? " ?" : "");
+ putc('\n', stdout);
+ }
+}
+
+
+/* Return the classical netmask for an IP address.
+ */
+static u_int
+std_mask(u_int addr) /* in network order */
+{
+ NTOHL(addr); /* was a host, not a network */
+
+ if (addr == 0) /* default route has mask 0 */
+ return 0;
+ if (IN_CLASSA(addr))
+ return IN_CLASSA_NET;
+ if (IN_CLASSB(addr))
+ return IN_CLASSB_NET;
+ return IN_CLASSC_NET;
+}
+
+
+/* get a network number as a name or a number, with an optional "/xx"
+ * netmask.
+ */
+static int /* 0=bad */
+getnet(char *name,
+ struct netinfo *rt)
+{
+ int i;
+ struct netent *nentp;
+ u_int mask;
+ struct in_addr in;
+ char hname[MAXHOSTNAMELEN+1];
+ char *mname, *p;
+
+
+ /* Detect and separate "1.2.3.4/24"
+ */
+ if (0 != (mname = rindex(name,'/'))) {
+ i = (int)(mname - name);
+ if (i > sizeof(hname)-1) /* name too long */
+ return 0;
+ bcopy(name, hname, i);
+ hname[i] = '\0';
+ mname++;
+ name = hname;
+ }
+
+ nentp = getnetbyname(name);
+ if (nentp != 0) {
+ in.s_addr = nentp->n_net;
+ } else if (inet_aton(name, &in) == 1) {
+ NTOHL(in.s_addr);
+ } else {
+ return 0;
+ }
+
+ if (mname == 0) {
+ mask = std_mask(in.s_addr);
+ if ((~mask & in.s_addr) != 0)
+ mask = 0xffffffff;
+ } else {
+ mask = (u_int)strtoul(mname, &p, 0);
+ if (*p != '\0' || mask > 32)
+ return 0;
+ mask = 0xffffffff << (32-mask);
+ }
+
+ rt->n_dst = htonl(in.s_addr);
+ rt->n_family = RIP_AF_INET;
+ rt->n_mask = htonl(mask);
+ return 1;
+}