/* $NetBSD: route.c,v 1.160.2.4 2020/01/24 20:13:45 martin Exp $ */
/*
* Copyright (c) 1983, 1989, 1991, 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. 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.
*/
#include <sys/cdefs.h>
#ifndef lint
__COPYRIGHT("@(#) Copyright (c) 1983, 1989, 1991, 1993\
The Regents of the University of California. All rights reserved.");
#endif /* not lint */
int pid, rtm_addrs;
int sock;
int forcehost, forcenet, doflush, af;
int iflag, Lflag, nflag, qflag, tflag, Sflag, Tflag;
int verbose, aflen = sizeof(struct sockaddr_in), rtag;
int locking, lockrest, debugonly, shortoutput;
struct rt_metrics rt_metrics;
int rtm_inits;
short ns_nullh[] = {0,0,0};
short ns_bh[] = {-1,-1,-1};
if (*argv == NULL) {
if (doflush)
ch = K_FLUSH;
else
goto no_cmd;
} else
ch = keyword(*argv);
switch (ch) {
#ifndef SMALL
case K_GET:
#endif /* SMALL */
case K_CHANGE:
case K_ADD:
case K_DELETE:
if (doflush)
(void)flushroutes(1, argv, 0);
return newroute(argc, argv);
case K_SHOW:
show(argc, argv, Lflag|nflag|Tflag|verbose);
return 0;
#ifndef SMALL
case K_MONITOR:
monitor(argc, argv);
return 0;
#endif /* SMALL */
case K_FLUSH:
return flushroutes(argc, argv, 0);
cmd = argv[0];
af = AF_UNSPEC;
if (*cmd != 'g') {
/* Don't want to read back our messages */
prog_shutdown(sock, SHUT_RD);
}
while (--argc > 0) {
if (**(++argv)== '-') {
switch (key = keyword(1 + *argv)) {
case K_SA:
af = PF_ROUTE;
aflen = sizeof(union sockunion);
break;
#ifndef SMALL
case K_ATALK:
af = AF_APPLETALK;
aflen = sizeof(struct sockaddr_at);
break;
#endif
case K_INET:
af = AF_INET;
aflen = sizeof(struct sockaddr_in);
break;
#ifdef INET6
case K_INET6:
af = AF_INET6;
aflen = sizeof(struct sockaddr_in6);
break;
#endif
case K_LINK:
af = AF_LINK;
aflen = sizeof(struct sockaddr_dl);
break;
#ifndef SMALL
case K_MPLS:
af = AF_MPLS;
aflen = sizeof(struct sockaddr_mpls);
break;
case K_TAG:
if (!--argc)
usage(1+*argv);
af = AF_MPLS;
aflen = sizeof(struct sockaddr_mpls);
(void)getaddr(RTA_TAG, *++argv, 0, soup);
break;
#endif /* SMALL */
case K_IFACE:
case K_INTERFACE:
iflag++;
break;
case K_NOSTATIC:
flags &= ~RTF_STATIC;
break;
case K_LOCK:
locking = 1;
break;
case K_LOCKREST:
lockrest = 1;
break;
case K_HOST:
forcehost++;
break;
case K_REJECT:
flags |= RTF_REJECT;
break;
case K_NOREJECT:
flags &= ~RTF_REJECT;
break;
case K_BLACKHOLE:
flags |= RTF_BLACKHOLE;
break;
case K_NOBLACKHOLE:
flags &= ~RTF_BLACKHOLE;
break;
case K_PROTO1:
flags |= RTF_PROTO1;
break;
case K_PROTO2:
flags |= RTF_PROTO2;
break;
case K_PROXY:
flags |= RTF_ANNOUNCE;
break;
case K_CONNECTED:
flags |= RTF_CONNECTED;
break;
case K_NOCONNECTED:
flags &= ~RTF_CONNECTED;
break;
case K_STATIC:
flags |= RTF_STATIC;
break;
case K_IFA:
if (!--argc)
usage(1+*argv);
(void)getaddr(RTA_IFA, *++argv, 0, soup);
break;
case K_IFP:
if (!--argc)
usage(1+*argv);
(void)getaddr(RTA_IFP, *++argv, 0, soup);
break;
case K_GENMASK:
if (!--argc)
usage(1+*argv);
(void)getaddr(RTA_GENMASK, *++argv, 0, soup);
break;
case K_GATEWAY:
if (!--argc)
usage(1+*argv);
(void)getaddr(RTA_GATEWAY, *++argv, 0, soup);
break;
case K_DST:
if (!--argc)
usage(1+*argv);
ishost = getaddr(RTA_DST, *++argv, &hp, soup);
dest = *argv;
break;
case K_NETMASK:
if (!--argc)
usage(1+*argv);
(void)getaddr(RTA_NETMASK, *++argv, 0, soup);
/* FALLTHROUGH */
case K_NET:
forcenet++;
break;
case K_PREFIXLEN:
if (!--argc)
usage(1+*argv);
ishost = prefixlen(*++argv, soup);
break;
case K_MTU:
case K_HOPCOUNT:
case K_EXPIRE:
case K_RECVPIPE:
case K_SENDPIPE:
case K_SSTHRESH:
case K_RTT:
case K_RTTVAR:
if (!--argc)
usage(1+*argv);
set_metric(*++argv, key);
break;
default:
usage(1+*argv);
}
} else {
if ((rtm_addrs & RTA_DST) == 0) {
dest = *argv;
ishost = getaddr(RTA_DST, *argv, &hp, soup);
} else if ((rtm_addrs & RTA_GATEWAY) == 0) {
gateway = *argv;
(void)getaddr(RTA_GATEWAY, *argv, &hp, soup);
} else {
ret = atoi(*argv);
if (ret == 0) {
if (strcmp(*argv, "0") == 0) {
if (!qflag) {
warnx("%s, %s",
"old usage of trailing 0",
"assuming route to if");
}
} else
usage(NULL);
iflag = 1;
continue;
} else if (ret > 0 && ret < 10) {
if (!qflag) {
warnx("%s, %s",
"old usage of trailing digit",
"assuming route via gateway");
}
iflag = 0;
continue;
}
(void)getaddr(RTA_NETMASK, *argv, 0, soup);
}
}
}
if ((rtm_addrs & RTA_DST) == 0)
errx(EXIT_FAILURE, "missing destination specification");
if (*cmd == 'a' && (rtm_addrs & RTA_GATEWAY) == 0)
errx(EXIT_FAILURE, "missing gateway specification");
if (forcehost && forcenet)
errx(EXIT_FAILURE, "-host and -net conflict");
else if (forcehost)
ishost = 1;
else if (forcenet)
ishost = 0;
flags |= RTF_UP;
if (ishost)
flags |= RTF_HOST;
if (iflag == 0)
flags |= RTF_GATEWAY;
for (attempts = 1; ; attempts++) {
errno = 0;
if ((ret = rtmsg(*cmd, flags, soup)) == 0)
break;
if (errno != ENETUNREACH && errno != ESRCH)
break;
if (af == AF_INET && *gateway && hp && hp->h_addr_list[1]) {
hp->h_addr_list++;
memmove(&soup->so_gate->sin.sin_addr, hp->h_addr_list[0],
hp->h_length);
} else
break;
}
if (*cmd == 'g' || qflag)
goto out;
#ifdef INET6
/*
* XXX the function may need more improvement...
*/
static int
inet6_makenetandmask(const struct sockaddr_in6 * const sin6, struct sou *soup)
{
const char *plen;
struct in6_addr in6;
plen = NULL;
if (IN6_IS_ADDR_UNSPECIFIED(&sin6->sin6_addr) &&
sin6->sin6_scope_id == 0) {
plen = "0";
} else if ((sin6->sin6_addr.s6_addr[0] & 0xe0) == 0x20) {
/* aggregatable global unicast - RFC2374 */
memset(&in6, 0, sizeof(in6));
if (!memcmp(&sin6->sin6_addr.s6_addr[8], &in6.s6_addr[8], 8))
plen = "64";
}
/*
* Interpret an argument as a network address of some kind,
* returning 1 if a host address, 0 if a network address.
*/
static int
getaddr(int which, const char *s, struct hostent **hpp, struct sou *soup)
{
sup su;
struct hostent *hp;
struct netent *np;
u_int32_t val;
char *t;
int afamily; /* local copy of af so we can change it */
if (af == AF_UNSPEC) {
af = AF_INET;
aflen = sizeof(struct sockaddr_in);
}
afamily = af;
rtm_addrs |= which;
switch (which) {
case RTA_DST:
su = soup->so_dst;
break;
case RTA_GATEWAY:
su = soup->so_gate;
break;
case RTA_NETMASK:
su = soup->so_mask;
break;
case RTA_GENMASK:
su = soup->so_genmask;
break;
case RTA_IFP:
su = soup->so_ifp;
afamily = AF_LINK;
break;
case RTA_IFA:
su = soup->so_ifa;
su->sa.sa_family = af;
break;
#ifndef SMALL
case RTA_TAG:
su = soup->so_mpls;
afamily = AF_MPLS;
break;
#endif
default:
su = NULL;
usage("Internal Error");
/*NOTREACHED*/
}
su->sa.sa_len = aflen;
su->sa.sa_family = afamily; /* cases that don't want it have left already */
if (strcmp(s, "default") == 0) {
switch (which) {
case RTA_DST:
forcenet++;
(void)getaddr(RTA_NETMASK, s, 0, soup);
break;
case RTA_NETMASK:
case RTA_GENMASK:
su->sa.sa_len = 0;
}
return 0;
}
switch (afamily) {
#ifdef INET6
case AF_INET6:
{
struct addrinfo hints, *res;
char *slash = 0;
if (which == RTA_DST && (slash = (strrchr(s, '/'))) != 0)
*slash = '\0';
memset(&hints, 0, sizeof(hints));
hints.ai_family = afamily; /*AF_INET6*/
hints.ai_flags = AI_NUMERICHOST;
hints.ai_socktype = SOCK_DGRAM; /*dummy*/
if (getaddrinfo(s, "0", &hints, &res) != 0) {
hints.ai_flags = 0;
if (slash) {
*slash = '/';
slash = 0;
}
if (getaddrinfo(s, "0", &hints, &res) != 0)
errx(EXIT_FAILURE, "%s: bad value", s);
}
if (slash)
*slash = '/';
if (sizeof(su->sin6) != res->ai_addrlen)
errx(EXIT_FAILURE, "%s: bad value", s);
if (res->ai_next) {
errx(EXIT_FAILURE,
"%s: address resolved to multiple values", s);
}
memcpy(&su->sin6, res->ai_addr, sizeof(su->sin6));
freeaddrinfo(res);
inet6_putscopeid(&su->sin6, INET6_IS_ADDR_LINKLOCAL|
INET6_IS_ADDR_MC_LINKLOCAL);
if (hints.ai_flags == AI_NUMERICHOST) {
if (slash)
return prefixlen(slash + 1, soup);
if (which == RTA_DST)
return inet6_makenetandmask(&su->sin6, soup);
return 0;
} else
return 1;
}
#endif
case PF_ROUTE:
su->sa.sa_len = sizeof(*su);
sockaddr(s, &su->sa);
return 1;
#ifndef SMALL
case AF_APPLETALK:
t = strchr (s, '.');
if (!t) {
badataddr:
errx(EXIT_FAILURE, "bad address: %s", s);
}
val = atoi (s);
if (val > 65535)
goto badataddr;
su->sat.sat_addr.s_net = val;
val = atoi (t);
if (val > 256)
goto badataddr;
su->sat.sat_addr.s_node = val;
rtm_addrs |= RTA_NETMASK;
return(forcehost || su->sat.sat_addr.s_node != 0);
case AF_MPLS:
if (which == RTA_DST)
soup->so_dst = readtag(su, s);
else if (which == RTA_TAG)
soup->so_mpls = readtag(su, s);
else
errx(EXIT_FAILURE, "MPLS can be used only as "
"DST or TAG");
return 1;
#endif
if (mplssize != 0 && sizeof(union sockunion) < MPLS_NEW_SIZE) {
free(su);
retsu = malloc(MPLS_NEW_SIZE);
retsu->smpls.smpls_family = AF_MPLS;
}
retsu->smpls.smpls_len = MPLS_NEW_SIZE;
mplssize = 0;
while ((p = strchr(n, ',')) != NULL) {
p[0] = '\0';
addtag(retsu, n, mplssize);
n = p + 1;
mplssize++;
}
addtag(retsu, n, mplssize);
free(norig);
return retsu;
}
static void
addtag(sup su, const char *s, int where)
{
union mpls_shim *ms = &su->smpls.smpls_addr;
if (atoi(s) < 0 || atoi(s) >= (1 << 20))
errx(EXIT_FAILURE, "%s: Bad tag", s);
ms[where].s_addr = 0;
ms[where].shim.label = atoi(s);
ms[where].s_addr = htonl(ms[where].s_addr);
}
#endif /* SMALL */
int
prefixlen(const char *s, struct sou *soup)
{
int max, len = atoi(s);
#ifdef INET6
int q, r;
#endif
switch (af) {
case AF_INET:
max = sizeof(struct in_addr) * 8;
break;
#ifdef INET6
case AF_INET6:
max = sizeof(struct in6_addr) * 8;
break;
#endif
default:
errx(EXIT_FAILURE, "prefixlen is not supported with af %d", af);
/*NOTREACHED*/
}
rtm_addrs |= RTA_NETMASK;
if (len < -1 || len > max)
errx(EXIT_FAILURE, "%s: bad value", s);
static void
extract_addrs(const char *cp, int addrs, const struct sockaddr *sa[], int *nmfp)
{
int i, nmf = -1;
for (i = 0; i < RTAX_MAX; i++) {
if ((1 << i) & addrs) {
sa[i] = (const struct sockaddr *)cp;
if ((i == RTAX_DST || i == RTAX_IFA) &&
nmf == -1)
nmf = sa[i]->sa_family;
RT_ADVANCE(cp, sa[i]);
} else
sa[i] = NULL;
}
if (nmfp != NULL)
*nmfp = nmf;
}
static void
pmsg_addrs(const char *cp, int addrs)
{
const struct sockaddr *sa[RTAX_MAX];
int i, nmf;
if (addrs != 0) {
(void)printf("\nsockaddrs: ");
bprintf(stdout, addrs, addrnames);
(void)putchar('\n');
extract_addrs(cp, addrs, sa, &nmf);
for (i = 0; i < RTAX_MAX; i++) {
if (sa[i] == NULL)
continue;