Sync with latest KAME.
Obtained from: KAME
This commit is contained in:
parent
6aec4e0c37
commit
259df286b9
Notes:
svn2git
2020-12-20 02:59:44 +00:00
svn path=/head/; revision=62632
@ -15,7 +15,7 @@
|
||||
|
||||
PROG= rtsold
|
||||
SRCS= rtsold.c rtsol.c if.c probe.c dump.c
|
||||
CFLAGS+=-DINET6
|
||||
CFLAGS+=-DINET6 -DHAVE_GETIFADDRS
|
||||
LDADD+= -lkvm
|
||||
DPADD+= ${LIBKVM}
|
||||
|
||||
|
@ -1,7 +1,7 @@
|
||||
/*
|
||||
* Copyright (C) 1999 WIDE Project.
|
||||
* All rights reserved.
|
||||
*
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
@ -13,7 +13,7 @@
|
||||
* 3. Neither the name of the project nor the names of its contributors
|
||||
* may be used to endorse or promote products derived from this software
|
||||
* without specific prior written permission.
|
||||
*
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
@ -31,7 +31,9 @@
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <sys/time.h>
|
||||
#include <sys/socket.h>
|
||||
|
||||
#include <net/if.h>
|
||||
#include <netinet/in.h>
|
||||
#include <netinet/icmp6.h>
|
||||
|
||||
@ -47,6 +49,7 @@ static FILE *fp;
|
||||
|
||||
extern struct ifinfo *iflist;
|
||||
|
||||
static void dump_interface_status __P((void));
|
||||
static char *sec2str __P((time_t));
|
||||
char *ifstatstr[] = {"IDLE", "DELAY", "PROBE", "DOWN", "TENTATIVE"};
|
||||
|
||||
|
@ -1,7 +1,7 @@
|
||||
/*
|
||||
* Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project.
|
||||
* All rights reserved.
|
||||
*
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
@ -13,7 +13,7 @@
|
||||
* 3. Neither the name of the project nor the names of its contributors
|
||||
* may be used to endorse or promote products derived from this software
|
||||
* without specific prior written permission.
|
||||
*
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
@ -35,12 +35,23 @@
|
||||
#include <sys/ioctl.h>
|
||||
|
||||
#include <net/if.h>
|
||||
#if defined(__FreeBSD__) && __FreeBSD__ >= 3
|
||||
#include <net/if_var.h>
|
||||
#endif /* __FreeBSD__ >= 3 */
|
||||
#include <net/if_types.h>
|
||||
#include <net/route.h>
|
||||
#include <net/if_dl.h>
|
||||
#include <net/if_media.h>
|
||||
#include <net/ethernet.h>
|
||||
#ifdef __FreeBSD__
|
||||
# include <net/ethernet.h>
|
||||
#endif
|
||||
#ifdef __NetBSD__
|
||||
#include <net/if_ether.h>
|
||||
#endif
|
||||
#if defined(__bsdi__) || defined(__OpenBSD__)
|
||||
# include <netinet/in.h>
|
||||
# include <netinet/if_ether.h>
|
||||
#endif
|
||||
#include <netinet/in.h>
|
||||
#include <netinet/icmp6.h>
|
||||
|
||||
@ -53,25 +64,27 @@
|
||||
#include <string.h>
|
||||
#include <fcntl.h>
|
||||
#include <errno.h>
|
||||
#include <kvm.h>
|
||||
#include <nlist.h>
|
||||
#include <limits.h>
|
||||
#ifdef HAVE_GETIFADDRS
|
||||
#include <ifaddrs.h>
|
||||
#endif
|
||||
|
||||
#include "rtsold.h"
|
||||
|
||||
extern int rssock;
|
||||
static int ifsock;
|
||||
|
||||
static int getifa __P((char *name, struct in6_ifaddr *ifap));
|
||||
static int get_llflag __P((const char *name));
|
||||
#ifndef HAVE_GETIFADDRS
|
||||
static unsigned int if_maxindex __P((void));
|
||||
#endif
|
||||
static void get_rtaddrs __P((int addrs, struct sockaddr *sa,
|
||||
struct sockaddr **rti_info));
|
||||
|
||||
int
|
||||
ifinit()
|
||||
{
|
||||
if ((ifsock = socket(AF_INET6, SOCK_DGRAM, 0)) < 0) {
|
||||
warnmsg(LOG_ERR, __FUNCTION__, "socket: %s", strerror(errno));
|
||||
return(-1);
|
||||
}
|
||||
ifsock = rssock;
|
||||
|
||||
return(0);
|
||||
}
|
||||
@ -80,7 +93,7 @@ int
|
||||
interface_up(char *name)
|
||||
{
|
||||
struct ifreq ifr;
|
||||
struct in6_ifaddr ifa;
|
||||
int llflag;
|
||||
|
||||
strncpy(ifr.ifr_name, name, sizeof(ifr.ifr_name));
|
||||
|
||||
@ -100,24 +113,24 @@ interface_up(char *name)
|
||||
|
||||
warnmsg(LOG_DEBUG, __FUNCTION__, "checking if %s is ready...", name);
|
||||
|
||||
if (getifa(name, &ifa) < 0) {
|
||||
llflag = get_llflag(name);
|
||||
if (llflag < 0) {
|
||||
warnmsg(LOG_WARNING, __FUNCTION__,
|
||||
"getifa() failed, anyway I'll try");
|
||||
"get_llflag() failed, anyway I'll try");
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!(ifa.ia6_flags & IN6_IFF_NOTREADY)) {
|
||||
if (!(llflag & IN6_IFF_NOTREADY)) {
|
||||
warnmsg(LOG_DEBUG, __FUNCTION__,
|
||||
"%s is ready", name);
|
||||
return(0);
|
||||
}
|
||||
else {
|
||||
if (ifa.ia6_flags & IN6_IFF_TENTATIVE) {
|
||||
} else {
|
||||
if (llflag & IN6_IFF_TENTATIVE) {
|
||||
warnmsg(LOG_DEBUG, __FUNCTION__, "%s is tentative",
|
||||
name);
|
||||
return IFS_TENTATIVE;
|
||||
}
|
||||
if (ifa.ia6_flags & IN6_IFF_DUPLICATED)
|
||||
if (llflag & IN6_IFF_DUPLICATED)
|
||||
warnmsg(LOG_DEBUG, __FUNCTION__, "%s is duplicated",
|
||||
name);
|
||||
return -1;
|
||||
@ -130,7 +143,7 @@ interface_status(struct ifinfo *ifinfo)
|
||||
char *ifname = ifinfo->ifname;
|
||||
struct ifreq ifr;
|
||||
struct ifmediareq ifmr;
|
||||
|
||||
|
||||
/* get interface flags */
|
||||
memset(&ifr, 0, sizeof(ifr));
|
||||
strncpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name));
|
||||
@ -188,14 +201,14 @@ interface_status(struct ifinfo *ifinfo)
|
||||
return(1);
|
||||
}
|
||||
|
||||
#define ROUNDUP(a, size) \
|
||||
#define ROUNDUP(a, size) \
|
||||
(((a) & ((size)-1)) ? (1 + ((a) | ((size)-1))) : (a))
|
||||
|
||||
#define NEXT_SA(ap) (ap) = (struct sockaddr *) \
|
||||
#define NEXT_SA(ap) (ap) = (struct sockaddr *) \
|
||||
((caddr_t)(ap) + ((ap)->sa_len ? ROUNDUP((ap)->sa_len,\
|
||||
sizeof(u_long)) :\
|
||||
sizeof(u_long)))
|
||||
#define ROUNDUP8(a) (1 + (((a) - 1) | 7))
|
||||
#define ROUNDUP8(a) (1 + (((a) - 1) | 7))
|
||||
|
||||
int
|
||||
lladdropt_length(struct sockaddr_dl *sdl)
|
||||
@ -258,6 +271,8 @@ if_nametosdl(char *name)
|
||||
if ((sa = rti_info[RTAX_IFP]) != NULL) {
|
||||
if (sa->sa_family == AF_LINK) {
|
||||
sdl = (struct sockaddr_dl *)sa;
|
||||
if (strlen(name) != sdl->sdl_nlen)
|
||||
continue; /* not same len */
|
||||
if (strncmp(&sdl->sdl_data[0],
|
||||
name,
|
||||
sdl->sdl_nlen) == 0) {
|
||||
@ -297,91 +312,141 @@ getinet6sysctl(int code)
|
||||
|
||||
/*------------------------------------------------------------*/
|
||||
|
||||
static struct nlist nl[] = {
|
||||
#define N_IFNET 0
|
||||
{ "_ifnet" },
|
||||
{ "" },
|
||||
};
|
||||
|
||||
#define KREAD(x, y, z) { \
|
||||
if (kvm_read(kvmd, (u_long)x, (void *)y, sizeof(z)) != sizeof(z)) { \
|
||||
warnmsg(LOG_ERR, __FUNCTION__, "kvm_read failed"); \
|
||||
goto bad; \
|
||||
} \
|
||||
}
|
||||
|
||||
/* get ia6_flags for link-local addr on if. returns -1 on error. */
|
||||
static int
|
||||
getifa(char *name, struct in6_ifaddr *ifap)
|
||||
get_llflag(const char *name)
|
||||
{
|
||||
u_short index;
|
||||
kvm_t *kvmd = NULL;
|
||||
char buf[_POSIX2_LINE_MAX];
|
||||
struct ifnet *ifp;
|
||||
struct ifnet ifnet;
|
||||
struct in6_ifaddr *ifa;
|
||||
#ifdef HAVE_GETIFADDRS
|
||||
struct ifaddrs *ifap, *ifa;
|
||||
struct in6_ifreq ifr6;
|
||||
struct sockaddr_in6 *sin6;
|
||||
int s;
|
||||
|
||||
if (!ifap)
|
||||
if ((s = socket(PF_INET6, SOCK_DGRAM, 0)) < 0) {
|
||||
warnmsg(LOG_ERR, __FUNCTION__, "socket(SOCK_DGRAM): %s",
|
||||
strerror(errno));
|
||||
exit(1);
|
||||
|
||||
index = (u_short)if_nametoindex(name);
|
||||
if (index == 0) {
|
||||
warnmsg(LOG_ERR, __FUNCTION__, "if_nametoindex failed for %s",
|
||||
name);
|
||||
goto bad;
|
||||
}
|
||||
if ((kvmd = kvm_openfiles(NULL, NULL, NULL, O_RDONLY, buf)) == NULL) {
|
||||
warnmsg(LOG_ERR, __FUNCTION__, "kvm_openfiles failed");
|
||||
goto bad;
|
||||
}
|
||||
if (kvm_nlist(kvmd, nl) < 0) {
|
||||
warnmsg(LOG_ERR, __FUNCTION__, "kvm_nlist failed");
|
||||
goto bad;
|
||||
}
|
||||
if (nl[N_IFNET].n_value == 0) {
|
||||
warnmsg(LOG_ERR, __FUNCTION__, "symbol \"%s\" not found",
|
||||
nl[N_IFNET].n_name);
|
||||
goto bad;
|
||||
if (getifaddrs(&ifap) != 0) {
|
||||
warnmsg(LOG_ERR, __FUNCTION__, "etifaddrs: %s",
|
||||
strerror(errno));
|
||||
exit(1);
|
||||
}
|
||||
|
||||
KREAD(nl[N_IFNET].n_value, &ifp, struct ifnet *);
|
||||
while (ifp) {
|
||||
KREAD(ifp, &ifnet, struct ifnet);
|
||||
if (ifnet.if_index == index)
|
||||
break;
|
||||
ifp = TAILQ_NEXT(&ifnet, if_link);
|
||||
}
|
||||
if (!ifp) {
|
||||
warnmsg(LOG_ERR, __FUNCTION__, "interface \"%s\" not found",
|
||||
name);
|
||||
goto bad;
|
||||
}
|
||||
for (ifa = ifap; ifa; ifa = ifa->ifa_next) {
|
||||
if (strlen(ifa->ifa_name) != strlen(name)
|
||||
|| strncmp(ifa->ifa_name, name, strlen(name)) != 0)
|
||||
continue;
|
||||
if (ifa->ifa_addr->sa_family != AF_INET6)
|
||||
continue;
|
||||
sin6 = (struct sockaddr_in6 *)ifa->ifa_addr;
|
||||
if (!IN6_IS_ADDR_LINKLOCAL(&sin6->sin6_addr))
|
||||
continue;
|
||||
|
||||
ifa = (struct in6_ifaddr *)TAILQ_FIRST(&ifnet.if_addrhead);
|
||||
while (ifa) {
|
||||
KREAD(ifa, ifap, *ifap);
|
||||
if (ifap->ia_addr.sin6_family == AF_INET6
|
||||
&& IN6_IS_ADDR_LINKLOCAL(&ifap->ia_addr.sin6_addr)) {
|
||||
kvm_close(kvmd);
|
||||
return 0;
|
||||
memset(&ifr6, 0, sizeof(ifr6));
|
||||
strcpy(ifr6.ifr_name, name);
|
||||
memcpy(&ifr6.ifr_ifru.ifru_addr, sin6, sin6->sin6_len);
|
||||
if (ioctl(s, SIOCGIFAFLAG_IN6, &ifr6) < 0) {
|
||||
warnmsg(LOG_ERR, __FUNCTION__,
|
||||
"ioctl(SIOCGIFAFLAG_IN6): %s", strerror(errno));
|
||||
exit(1);
|
||||
}
|
||||
|
||||
ifa = (struct in6_ifaddr *)
|
||||
TAILQ_NEXT((struct ifaddr *)ifap, ifa_link);
|
||||
freeifaddrs(ifap);
|
||||
close(s);
|
||||
return ifr6.ifr_ifru.ifru_flags6;
|
||||
}
|
||||
warnmsg(LOG_ERR, __FUNCTION__, "no IPv6 link-local address for %s",
|
||||
name);
|
||||
|
||||
bad:
|
||||
if (kvmd)
|
||||
kvm_close(kvmd);
|
||||
freeifaddrs(ifap);
|
||||
close(s);
|
||||
return -1;
|
||||
#else
|
||||
int s;
|
||||
unsigned int maxif;
|
||||
struct ifreq *iflist;
|
||||
struct ifconf ifconf;
|
||||
struct ifreq *ifr, *ifr_end;
|
||||
struct sockaddr_in6 *sin6;
|
||||
struct in6_ifreq ifr6;
|
||||
|
||||
maxif = if_maxindex() + 1;
|
||||
iflist = (struct ifreq *)malloc(maxif * BUFSIZ); /* XXX */
|
||||
if (iflist == NULL) {
|
||||
warnmsg(LOG_ERR, __FUNCTION__, "not enough core");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
if ((s = socket(PF_INET6, SOCK_DGRAM, 0)) < 0) {
|
||||
warnmsg(LOG_ERR, __FUNCTION__, "socket(SOCK_DGRAM): %s",
|
||||
strerror(errno));
|
||||
exit(1);
|
||||
}
|
||||
memset(&ifconf, 0, sizeof(ifconf));
|
||||
ifconf.ifc_req = iflist;
|
||||
ifconf.ifc_len = maxif * BUFSIZ; /* XXX */
|
||||
if (ioctl(s, SIOCGIFCONF, &ifconf) < 0) {
|
||||
warnmsg(LOG_ERR, __FUNCTION__, "ioctl(SIOCGIFCONF): %s",
|
||||
strerror(errno));
|
||||
exit(1);
|
||||
}
|
||||
|
||||
/* Look for this interface in the list */
|
||||
ifr_end = (struct ifreq *) (ifconf.ifc_buf + ifconf.ifc_len);
|
||||
for (ifr = ifconf.ifc_req;
|
||||
ifr < ifr_end;
|
||||
ifr = (struct ifreq *) ((char *) &ifr->ifr_addr
|
||||
+ ifr->ifr_addr.sa_len)) {
|
||||
if (strlen(ifr->ifr_name) != strlen(name)
|
||||
|| strncmp(ifr->ifr_name, name, strlen(name)) != 0)
|
||||
continue;
|
||||
if (ifr->ifr_addr.sa_family != AF_INET6)
|
||||
continue;
|
||||
sin6 = (struct sockaddr_in6 *)&ifr->ifr_addr;
|
||||
if (!IN6_IS_ADDR_LINKLOCAL(&sin6->sin6_addr))
|
||||
continue;
|
||||
|
||||
memset(&ifr6, 0, sizeof(ifr6));
|
||||
strcpy(ifr6.ifr_name, name);
|
||||
memcpy(&ifr6.ifr_ifru.ifru_addr, sin6, sin6->sin6_len);
|
||||
if (ioctl(s, SIOCGIFAFLAG_IN6, &ifr6) < 0) {
|
||||
warnmsg(LOG_ERR, __FUNCTION__,
|
||||
"ioctl(SIOCGIFAFLAG_IN6): %s", strerror(errno));
|
||||
exit(1);
|
||||
}
|
||||
|
||||
free(iflist);
|
||||
close(s);
|
||||
return ifr6.ifr_ifru.ifru_flags6;
|
||||
}
|
||||
|
||||
free(iflist);
|
||||
close(s);
|
||||
return -1;
|
||||
#endif
|
||||
}
|
||||
|
||||
#ifndef HAVE_GETIFADDRS
|
||||
static unsigned int
|
||||
if_maxindex()
|
||||
{
|
||||
struct if_nameindex *p, *p0;
|
||||
unsigned int max = 0;
|
||||
|
||||
p0 = if_nameindex();
|
||||
for (p = p0; p && p->if_index && p->if_name; p++) {
|
||||
if (max < p->if_index)
|
||||
max = p->if_index;
|
||||
}
|
||||
if_freenameindex(p0);
|
||||
return max;
|
||||
}
|
||||
#endif
|
||||
|
||||
static void
|
||||
get_rtaddrs(int addrs, struct sockaddr *sa, struct sockaddr **rti_info)
|
||||
{
|
||||
int i;
|
||||
|
||||
|
||||
for (i = 0; i < RTAX_MAX; i++) {
|
||||
if (addrs & (1 << i)) {
|
||||
rti_info[i] = sa;
|
||||
|
@ -1,7 +1,7 @@
|
||||
/*
|
||||
* Copyright (C) 1998 WIDE Project.
|
||||
* All rights reserved.
|
||||
*
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
@ -13,7 +13,7 @@
|
||||
* 3. Neither the name of the project nor the names of its contributors
|
||||
* may be used to endorse or promote products derived from this software
|
||||
* without specific prior written permission.
|
||||
*
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
@ -36,7 +36,9 @@
|
||||
#include <sys/uio.h>
|
||||
|
||||
#include <net/if.h>
|
||||
#if defined(__FreeBSD__) && __FreeBSD__ >= 3
|
||||
#include <net/if_var.h>
|
||||
#endif /* __FreeBSD__ >= 3 */
|
||||
|
||||
#include <netinet/in.h>
|
||||
#include <netinet6/in6_var.h>
|
||||
@ -49,6 +51,7 @@
|
||||
#include <unistd.h>
|
||||
#include <string.h>
|
||||
#include <syslog.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "rtsold.h"
|
||||
|
||||
@ -57,11 +60,19 @@ static struct iovec sndiov[2];
|
||||
static int probesock;
|
||||
static void sendprobe __P((struct in6_addr *addr, int ifindex));
|
||||
|
||||
|
||||
int
|
||||
probe_init()
|
||||
{
|
||||
static u_char sndcmsgbuf[CMSG_SPACE(sizeof(struct in6_pktinfo)) +
|
||||
CMSG_SPACE(sizeof(int))];
|
||||
int scmsglen = CMSG_SPACE(sizeof(struct in6_pktinfo)) +
|
||||
CMSG_SPACE(sizeof(int));
|
||||
static u_char *sndcmsgbuf = NULL;
|
||||
|
||||
if (sndcmsgbuf == NULL &&
|
||||
(sndcmsgbuf = (u_char *)malloc(scmsglen)) == NULL) {
|
||||
warnmsg(LOG_ERR, __FUNCTION__, "malloc failed");
|
||||
return(-1);
|
||||
}
|
||||
|
||||
if ((probesock = socket(AF_INET6, SOCK_RAW, IPPROTO_NONE)) < 0) {
|
||||
warnmsg(LOG_ERR, __FUNCTION__, "socket: %s", strerror(errno));
|
||||
@ -79,13 +90,13 @@ probe_init()
|
||||
sndmhdr.msg_iov = sndiov;
|
||||
sndmhdr.msg_iovlen = 1;
|
||||
sndmhdr.msg_control = (caddr_t)sndcmsgbuf;
|
||||
sndmhdr.msg_controllen = sizeof(sndcmsgbuf);
|
||||
sndmhdr.msg_controllen = scmsglen;
|
||||
|
||||
return(0);
|
||||
}
|
||||
|
||||
/*
|
||||
* Probe if each router in the default router list is still alive.
|
||||
* Probe if each router in the default router list is still alive.
|
||||
*/
|
||||
void
|
||||
defrouter_probe(int ifindex)
|
||||
|
@ -1,7 +1,7 @@
|
||||
/*
|
||||
* Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project.
|
||||
* All rights reserved.
|
||||
*
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
@ -13,7 +13,7 @@
|
||||
* 3. Neither the name of the project nor the names of its contributors
|
||||
* may be used to endorse or promote products derived from this software
|
||||
* without specific prior written permission.
|
||||
*
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
@ -55,7 +55,7 @@
|
||||
#include <syslog.h>
|
||||
#include "rtsold.h"
|
||||
|
||||
#define ALLROUTER "ff02::2"
|
||||
#define ALLROUTER "ff02::2"
|
||||
|
||||
static struct msghdr rcvmhdr;
|
||||
static struct msghdr sndmhdr;
|
||||
@ -63,7 +63,7 @@ static struct iovec rcviov[2];
|
||||
static struct iovec sndiov[2];
|
||||
static struct sockaddr_in6 from;
|
||||
|
||||
static int rssock;
|
||||
int rssock;
|
||||
|
||||
static struct sockaddr_in6 sin6_allrouters = {sizeof(sin6_allrouters), AF_INET6};
|
||||
|
||||
@ -73,11 +73,21 @@ sockopen()
|
||||
int on;
|
||||
struct icmp6_filter filt;
|
||||
static u_char answer[1500];
|
||||
static u_char rcvcmsgbuf[CMSG_SPACE(sizeof(struct in6_pktinfo)) +
|
||||
CMSG_SPACE(sizeof(int))];
|
||||
static u_char sndcmsgbuf[CMSG_SPACE(sizeof(struct in6_pktinfo)) +
|
||||
CMSG_SPACE(sizeof(int))];
|
||||
int rcvcmsglen, sndcmsglen;
|
||||
static u_char *rcvcmsgbuf = NULL, *sndcmsgbuf = NULL;
|
||||
|
||||
sndcmsglen = rcvcmsglen = CMSG_SPACE(sizeof(struct in6_pktinfo)) +
|
||||
CMSG_SPACE(sizeof(int));
|
||||
if (rcvcmsgbuf == NULL && (rcvcmsgbuf = malloc(rcvcmsglen)) == NULL) {
|
||||
warnmsg(LOG_ERR, __FUNCTION__,
|
||||
"malloc for receive msghdr failed");
|
||||
return(-1);
|
||||
}
|
||||
if (sndcmsgbuf == NULL && (sndcmsgbuf = malloc(sndcmsglen)) == NULL) {
|
||||
warnmsg(LOG_ERR, __FUNCTION__,
|
||||
"malloc for send msghdr failed");
|
||||
return(-1);
|
||||
}
|
||||
memset(&sin6_allrouters, 0, sizeof(struct sockaddr_in6));
|
||||
if (inet_pton(AF_INET6, ALLROUTER,
|
||||
&sin6_allrouters.sin6_addr.s6_addr) != 1) {
|
||||
@ -93,21 +103,39 @@ sockopen()
|
||||
|
||||
/* specify to tell receiving interface */
|
||||
on = 1;
|
||||
#ifdef IPV6_RECVPKTINFO
|
||||
if (setsockopt(rssock, IPPROTO_IPV6, IPV6_RECVPKTINFO, &on,
|
||||
sizeof(on)) < 0) {
|
||||
warnmsg(LOG_ERR, __FUNCTION__, "IPV6_RECVPKTINFO: %s",
|
||||
strerror(errno));
|
||||
exit(1);
|
||||
}
|
||||
#else /* old adv. API */
|
||||
if (setsockopt(rssock, IPPROTO_IPV6, IPV6_PKTINFO, &on,
|
||||
sizeof(on)) < 0) {
|
||||
warnmsg(LOG_ERR, __FUNCTION__, "IPV6_PKTINFO: %s",
|
||||
strerror(errno));
|
||||
exit(1);
|
||||
}
|
||||
#endif
|
||||
|
||||
on = 1;
|
||||
/* specify to tell value of hoplimit field of received IP6 hdr */
|
||||
#ifdef IPV6_RECVHOPLIMIT
|
||||
if (setsockopt(rssock, IPPROTO_IPV6, IPV6_RECVHOPLIMIT, &on,
|
||||
sizeof(on)) < 0) {
|
||||
warnmsg(LOG_ERR, __FUNCTION__, "IPV6_RECVHOPLIMIT: %s",
|
||||
strerror(errno));
|
||||
exit(1);
|
||||
}
|
||||
#else /* old adv. API */
|
||||
if (setsockopt(rssock, IPPROTO_IPV6, IPV6_HOPLIMIT, &on,
|
||||
sizeof(on)) < 0) {
|
||||
warnmsg(LOG_ERR, __FUNCTION__, "IPV6_HOPLIMIT: %s",
|
||||
strerror(errno));
|
||||
exit(1);
|
||||
}
|
||||
#endif
|
||||
|
||||
/* specfiy to accept only router advertisements on the socket */
|
||||
ICMP6_FILTER_SETBLOCKALL(&filt);
|
||||
@ -127,14 +155,14 @@ sockopen()
|
||||
rcvmhdr.msg_iov = rcviov;
|
||||
rcvmhdr.msg_iovlen = 1;
|
||||
rcvmhdr.msg_control = (caddr_t) rcvcmsgbuf;
|
||||
rcvmhdr.msg_controllen = sizeof(rcvcmsgbuf);
|
||||
rcvmhdr.msg_controllen = rcvcmsglen;
|
||||
|
||||
/* initialize msghdr for sending packets */
|
||||
sndmhdr.msg_namelen = sizeof(struct sockaddr_in6);
|
||||
sndmhdr.msg_iov = sndiov;
|
||||
sndmhdr.msg_iovlen = 1;
|
||||
sndmhdr.msg_control = (caddr_t)sndcmsgbuf;
|
||||
sndmhdr.msg_controllen = sizeof(sndcmsgbuf);
|
||||
sndmhdr.msg_controllen = sndcmsglen;
|
||||
|
||||
return(rssock);
|
||||
}
|
||||
|
@ -1,6 +1,6 @@
|
||||
.\" Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project.
|
||||
.\" All rights reserved.
|
||||
.\"
|
||||
.\"
|
||||
.\" Redistribution and use in source and binary forms, with or without
|
||||
.\" modification, are permitted provided that the following conditions
|
||||
.\" are met:
|
||||
@ -12,7 +12,7 @@
|
||||
.\" 3. Neither the name of the project nor the names of its contributors
|
||||
.\" may be used to endorse or promote products derived from this software
|
||||
.\" without specific prior written permission.
|
||||
.\"
|
||||
.\"
|
||||
.\" THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
|
||||
.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
@ -25,7 +25,7 @@
|
||||
.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
.\" SUCH DAMAGE.
|
||||
.\"
|
||||
.\" $Id: rtsold.8,v 1.4 1999/09/30 00:57:15 jinmei Exp $
|
||||
.\" $Id: rtsold.8,v 1.9 2000/07/04 14:44:38 jinmei Exp $
|
||||
.\" $FreeBSD$
|
||||
.\"
|
||||
.Dd May 17, 1998
|
||||
@ -53,11 +53,26 @@ If a node (re)attaches to a link,
|
||||
sends some Router Solicitations on the link destined to the link-local scope
|
||||
all-routers multicast address to discover new routers
|
||||
and to get non link-local addresses.
|
||||
.Lp
|
||||
.Pp
|
||||
If you invoke the program as
|
||||
.Nm rtsol ,
|
||||
it will transmit probes from the specified
|
||||
.Ar interface ,
|
||||
without becoming a daemon.
|
||||
In other words,
|
||||
.Nm rtsol
|
||||
behaves as
|
||||
.Do
|
||||
.Nm
|
||||
.Fl f1
|
||||
.Ar interfaces
|
||||
.Dc .
|
||||
.Pp
|
||||
Specifically,
|
||||
.Nm
|
||||
sends at most 3 Router Solicitations on an interface
|
||||
after one of the following events:
|
||||
.Pp
|
||||
.Bl -bullet -compact
|
||||
.It
|
||||
Just after invocation of
|
||||
@ -66,32 +81,32 @@ daemon.
|
||||
.It
|
||||
The interface is up after a temporary interface failure.
|
||||
.Nm Rtsold
|
||||
detects it by periodically probing if the status of the
|
||||
interface is active or not.
|
||||
Note that some network cards and drivers do not allow users
|
||||
to extract link state.
|
||||
detects such failures by periodically probing to see if the status
|
||||
of the interface is active or not.
|
||||
Note that some network cards and drivers do not allow the extraction
|
||||
of link state.
|
||||
In such cases,
|
||||
.Nm
|
||||
cannot detect the change of the interface status.
|
||||
.It
|
||||
Every one minute if
|
||||
Every 60 seconds if the
|
||||
.Fl m
|
||||
option is specified and
|
||||
option is specified and the
|
||||
.Nm
|
||||
daemon cannot get the interface status.
|
||||
This feature does not conform to IPv6 neighbor discovery
|
||||
specification, but is provided for mobile stations.
|
||||
Default interval of router advertisements, which is on the order of 10
|
||||
Default interval for router advertisements, which is on the order of 10
|
||||
minutes, is slightly long for mobile stations.
|
||||
This feature is provided
|
||||
for such stations so that they can find new routers as soon as possible
|
||||
when they attach another link.
|
||||
when they attach to another link.
|
||||
.El
|
||||
.Lp
|
||||
Once
|
||||
.Nm
|
||||
sends a Router Solicitation, and receives a valid Router Advertisement,
|
||||
it desists from sending additional solicitations on that interface, until
|
||||
it refrains from sending additional solicitations on that interface, until
|
||||
the next time one of the above events occurs.
|
||||
.Lp
|
||||
When sending a Router Solicitation on an interface,
|
||||
@ -103,7 +118,7 @@ Upon receipt of signal
|
||||
.Dv SIGUSR1 ,
|
||||
.Nm
|
||||
will dump the current internal state into
|
||||
.Pa /var/tmp/rtsold.dump.
|
||||
.Pa /var/run/rtsold.dump.
|
||||
.\"
|
||||
.Sh OPTIONS
|
||||
.Bl -tag -width indent
|
||||
@ -134,18 +149,9 @@ periodically sends Router Solicitation on an interface that does not support
|
||||
ioctl.
|
||||
.It Fl 1
|
||||
Perform only one probe.
|
||||
Transmit Router Solcitation packet until valid Router Advertisement packet
|
||||
Transmit Router Solicitation packet until valid Router Advertisement packet
|
||||
arrives all the interfaces more than once, then exit.
|
||||
.El
|
||||
.Pp
|
||||
If you invoke the program as
|
||||
.Nm rtsol ,
|
||||
it will behave as
|
||||
.Do
|
||||
.Nm
|
||||
.Fl f1
|
||||
.Ar interfaces
|
||||
.Dc .
|
||||
.Sh RETURN VALUES
|
||||
The program exits with 0 on success, non-zero on failures.
|
||||
.\"
|
||||
@ -154,7 +160,7 @@ The program exits with 0 on success, non-zero on failures.
|
||||
.It Pa /var/run/rtsold.pid
|
||||
the pid of the currently running
|
||||
.Nm rtsold .
|
||||
.It Pa /var/tmp/rtsold.dump
|
||||
.It Pa /var/run/rtsold.dump
|
||||
dumps internal state on.
|
||||
.El
|
||||
.\"
|
||||
@ -165,11 +171,22 @@ dumps internal state on.
|
||||
.Sh HISTORY
|
||||
The
|
||||
.Nm
|
||||
command is based on
|
||||
command is based on the
|
||||
.Nm rtsol
|
||||
command, which first appeared in WIDE/KAME IPv6 protocol stack kit.
|
||||
.Nm rtsol
|
||||
is now integrated into
|
||||
.Xr rtsold 8 .
|
||||
.\" .Sh BUGS
|
||||
.\" (to be written)
|
||||
.\"
|
||||
.Sh BUGS
|
||||
In some operating systems, when a PCMCIA network card is removed
|
||||
and reinserted, the corresponding interface index is changed.
|
||||
However,
|
||||
.Nm
|
||||
does not assume such changes, and always uses the index that
|
||||
it got at invocation. As a result,
|
||||
.Nm
|
||||
may not work if you reinsert a network card.
|
||||
In such a case,
|
||||
.Nm
|
||||
should be killed and restarted.
|
||||
|
@ -1,7 +1,7 @@
|
||||
/*
|
||||
* Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project.
|
||||
* All rights reserved.
|
||||
*
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
@ -13,7 +13,7 @@
|
||||
* 3. Neither the name of the project nor the names of its contributors
|
||||
* may be used to endorse or promote products derived from this software
|
||||
* without specific prior written permission.
|
||||
*
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
@ -31,7 +31,9 @@
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <sys/time.h>
|
||||
#include <sys/socket.h>
|
||||
|
||||
#include <net/if.h>
|
||||
#include <net/if_dl.h>
|
||||
|
||||
#include <netinet/in.h>
|
||||
@ -55,36 +57,39 @@ static int log_upto = 999;
|
||||
static int fflag = 0;
|
||||
|
||||
/* protocol constatns */
|
||||
#define MAX_RTR_SOLICITATION_DELAY 1 /* second */
|
||||
#define RTR_SOLICITATION_INTERVAL 4 /* seconds */
|
||||
#define MAX_RTR_SOLICITATIONS 3 /* times */
|
||||
#define MAX_RTR_SOLICITATION_DELAY 1 /* second */
|
||||
#define RTR_SOLICITATION_INTERVAL 4 /* seconds */
|
||||
#define MAX_RTR_SOLICITATIONS 3 /* times */
|
||||
|
||||
/* implementation dependent constants */
|
||||
#define PROBE_INTERVAL 60 /* secondes XXX: should be configurable */
|
||||
#define PROBE_INTERVAL 60 /* secondes XXX: should be configurable */
|
||||
|
||||
/* utility macros */
|
||||
/* a < b */
|
||||
#define TIMEVAL_LT(a, b) (((a).tv_sec < (b).tv_sec) ||\
|
||||
#define TIMEVAL_LT(a, b) (((a).tv_sec < (b).tv_sec) ||\
|
||||
(((a).tv_sec == (b).tv_sec) && \
|
||||
((a).tv_usec < (b).tv_usec)))
|
||||
|
||||
/* a <= b */
|
||||
#define TIMEVAL_LEQ(a, b) (((a).tv_sec < (b).tv_sec) ||\
|
||||
#define TIMEVAL_LEQ(a, b) (((a).tv_sec < (b).tv_sec) ||\
|
||||
(((a).tv_sec == (b).tv_sec) &&\
|
||||
((a).tv_usec <= (b).tv_usec)))
|
||||
|
||||
/* a == b */
|
||||
#define TIMEVAL_EQ(a, b) (((a).tv_sec==(b).tv_sec) && ((a).tv_usec==(b).tv_usec))
|
||||
#define TIMEVAL_EQ(a, b) (((a).tv_sec==(b).tv_sec) && ((a).tv_usec==(b).tv_usec))
|
||||
|
||||
int main __P((int argc, char *argv[]));
|
||||
|
||||
/* static variables and functions */
|
||||
static int mobile_node = 0;
|
||||
static int do_dump;
|
||||
static char *dumpfilename = "/var/tmp/rtsold.dump"; /* XXX: should be configurable */
|
||||
static char *dumpfilename = "/var/run/rtsold.dump"; /* XXX: should be configurable */
|
||||
static char *pidfilename = "/var/run/rtsold.pid"; /* should be configurable */
|
||||
|
||||
static int ifconfig __P((char *ifname));
|
||||
#if 0
|
||||
static int ifreconfig __P((char *ifname));
|
||||
#endif
|
||||
static int make_packet __P((struct ifinfo *ifinfo));
|
||||
static struct timeval *rtsol_check_timer __P((void));
|
||||
static void TIMEVAL_ADD __P((struct timeval *a, struct timeval *b,
|
||||
@ -92,7 +97,7 @@ static void TIMEVAL_ADD __P((struct timeval *a, struct timeval *b,
|
||||
static void TIMEVAL_SUB __P((struct timeval *a, struct timeval *b,
|
||||
struct timeval *result));
|
||||
|
||||
static void rtsold_set_dump_file __P(());
|
||||
static void rtsold_set_dump_file __P((void));
|
||||
static void usage __P((char *progname));
|
||||
|
||||
int
|
||||
@ -161,8 +166,10 @@ main(argc, argv)
|
||||
setlogmask(LOG_UPTO(log_upto));
|
||||
}
|
||||
|
||||
#ifndef HAVE_ARC4RANDOM
|
||||
/* random value initilization */
|
||||
srandom((u_long)time(NULL));
|
||||
#endif
|
||||
|
||||
/* warn if accept_rtadv is down */
|
||||
if (!getinet6sysctl(IPV6CTL_ACCEPT_RTADV))
|
||||
@ -172,19 +179,23 @@ main(argc, argv)
|
||||
if (signal(SIGUSR1, (void *)rtsold_set_dump_file) < 0)
|
||||
errx(1, "failed to set signal for dump status");
|
||||
|
||||
/*
|
||||
* Open a socket for sending RS and receiving RA.
|
||||
* This should be done before calling ifinit(), since the function
|
||||
* uses the socket.
|
||||
*/
|
||||
if ((s = sockopen()) < 0)
|
||||
errx(1, "failed to open a socket");
|
||||
|
||||
/* configuration per interface */
|
||||
if (ifinit())
|
||||
errx(1, "failed to initilizatoin interfaces");
|
||||
while (argc--) {
|
||||
if (ifconfig(*argv))
|
||||
errx(1, "failed to initilize %s", *argv);
|
||||
errx(1, "failed to initialize %s", *argv);
|
||||
argv++;
|
||||
}
|
||||
|
||||
/* open a socket for sending RS and receiving RA */
|
||||
if ((s = sockopen()) < 0)
|
||||
errx(1, "failed to open a socket");
|
||||
|
||||
/* setup for probing default routers */
|
||||
if (probe_init())
|
||||
errx(1, "failed to setup for probing routers");
|
||||
@ -210,7 +221,6 @@ main(argc, argv)
|
||||
FD_ZERO(&fdset);
|
||||
FD_SET(s, &fdset);
|
||||
while (1) { /* main loop */
|
||||
extern int errno;
|
||||
int e;
|
||||
struct fd_set select_fd = fdset;
|
||||
|
||||
@ -218,7 +228,7 @@ main(argc, argv)
|
||||
do_dump = 0;
|
||||
rtsold_dump_file(dumpfilename);
|
||||
}
|
||||
|
||||
|
||||
timeout = rtsol_check_timer();
|
||||
|
||||
if (once) {
|
||||
@ -269,11 +279,13 @@ ifconfig(char *ifname)
|
||||
if (find_ifinfo(sdl->sdl_index)) {
|
||||
warnmsg(LOG_ERR, __FUNCTION__,
|
||||
"interface %s was already cofigured", ifname);
|
||||
free(sdl);
|
||||
return(-1);
|
||||
}
|
||||
|
||||
if ((ifinfo = malloc(sizeof(*ifinfo))) == NULL) {
|
||||
warnmsg(LOG_ERR, __FUNCTION__, "memory allocation failed");
|
||||
free(sdl);
|
||||
return(-1);
|
||||
}
|
||||
memset(ifinfo, 0, sizeof(*ifinfo));
|
||||
@ -318,11 +330,38 @@ ifconfig(char *ifname)
|
||||
return(0);
|
||||
|
||||
bad:
|
||||
free(ifinfo);
|
||||
free(ifinfo->sdl);
|
||||
free(ifinfo);
|
||||
return(-1);
|
||||
}
|
||||
|
||||
#if 0
|
||||
static int
|
||||
ifreconfig(char *ifname)
|
||||
{
|
||||
struct ifinfo *ifi, *prev;
|
||||
int rv;
|
||||
|
||||
prev = NULL;
|
||||
for (ifi = iflist; ifi; ifi = ifi->next) {
|
||||
if (strncmp(ifi->ifname, ifname, sizeof(ifi->ifname)) == 0)
|
||||
break;
|
||||
prev = ifi;
|
||||
}
|
||||
prev->next = ifi->next;
|
||||
|
||||
rv = ifconfig(ifname);
|
||||
|
||||
/* reclaim it after ifconfig() in case ifname is pointer inside ifi */
|
||||
if (ifi->rs_data)
|
||||
free(ifi->rs_data);
|
||||
free(ifi->sdl);
|
||||
free(ifi);
|
||||
|
||||
return rv;
|
||||
}
|
||||
#endif
|
||||
|
||||
struct ifinfo *
|
||||
find_ifinfo(int ifindex)
|
||||
{
|
||||
@ -480,8 +519,8 @@ rtsol_check_timer()
|
||||
void
|
||||
rtsol_timer_update(struct ifinfo *ifinfo)
|
||||
{
|
||||
#define MILLION 1000000
|
||||
#define DADRETRY 10 /* XXX: adhoc */
|
||||
#define MILLION 1000000
|
||||
#define DADRETRY 10 /* XXX: adhoc */
|
||||
long interval;
|
||||
struct timeval now;
|
||||
|
||||
@ -499,14 +538,18 @@ rtsol_timer_update(struct ifinfo *ifinfo)
|
||||
break;
|
||||
case IFS_IDLE:
|
||||
if (mobile_node) {
|
||||
/* XXX should be configurable */
|
||||
/* XXX should be configurable */
|
||||
ifinfo->timer.tv_sec = 3;
|
||||
}
|
||||
else
|
||||
ifinfo->timer = tm_max; /* stop timer(valid?) */
|
||||
break;
|
||||
case IFS_DELAY:
|
||||
#ifndef HAVE_ARC4RANDOM
|
||||
interval = random() % (MAX_RTR_SOLICITATION_DELAY * MILLION);
|
||||
#else
|
||||
interval = arc4random() % (MAX_RTR_SOLICITATION_DELAY * MILLION);
|
||||
#endif
|
||||
ifinfo->timer.tv_sec = interval / MILLION;
|
||||
ifinfo->timer.tv_usec = interval % MILLION;
|
||||
break;
|
||||
@ -541,7 +584,7 @@ rtsol_timer_update(struct ifinfo *ifinfo)
|
||||
}
|
||||
|
||||
/* timer related utility functions */
|
||||
#define MILLION 1000000
|
||||
#define MILLION 1000000
|
||||
|
||||
/* result = a + b */
|
||||
static void
|
||||
|
@ -1,7 +1,7 @@
|
||||
/*
|
||||
* Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project.
|
||||
* All rights reserved.
|
||||
*
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
@ -13,7 +13,7 @@
|
||||
* 3. Neither the name of the project nor the names of its contributors
|
||||
* may be used to endorse or promote products derived from this software
|
||||
* without specific prior written permission.
|
||||
*
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
@ -33,7 +33,7 @@ struct ifinfo {
|
||||
struct ifinfo *next; /* pointer to the next interface */
|
||||
|
||||
struct sockaddr_dl *sdl; /* link-layer address */
|
||||
char ifname[16]; /* interface name */
|
||||
char ifname[IF_NAMESIZE]; /* interface name */
|
||||
int active; /* interface status */
|
||||
int probeinterval; /* interval of probe timer(if necessary) */
|
||||
int probetimer; /* rest of probe timer */
|
||||
@ -43,6 +43,7 @@ struct ifinfo {
|
||||
int dadcount;
|
||||
struct timeval timer;
|
||||
struct timeval expire;
|
||||
int errors; /* # of errors we've got - detect wedge */
|
||||
|
||||
int racnt; /* total # of valid RAs it have got */
|
||||
|
||||
@ -51,11 +52,11 @@ struct ifinfo {
|
||||
};
|
||||
|
||||
/* per interface status */
|
||||
#define IFS_IDLE 0
|
||||
#define IFS_DELAY 1
|
||||
#define IFS_PROBE 2
|
||||
#define IFS_DOWN 3
|
||||
#define IFS_TENTATIVE 4
|
||||
#define IFS_IDLE 0
|
||||
#define IFS_DELAY 1
|
||||
#define IFS_PROBE 2
|
||||
#define IFS_DOWN 3
|
||||
#define IFS_TENTATIVE 4
|
||||
|
||||
/* rtsold.c */
|
||||
extern struct timeval tm_max;
|
||||
|
Loading…
Reference in New Issue
Block a user