#include <sys/socket.h>
#include <sys/ioctl.h>
#include <sys/time.h>
#include <net/if.h>
#include <net/if_dl.h>
#include <net/if_media.h>
#include <net/if_types.h>
#include <netinet/in.h>
#include <netinet/in_var.h>
#include <netinet6/in6_var.h>
#include <netinet6/nd6.h>
#include <arpa/inet.h>
#include <netinet/ip_ipsp.h>
#include <netinet/if_ether.h>
#include <netdb.h>
#include <net/if_vlan_var.h>
#include <ctype.h>
#include <err.h>
#include <errno.h>
#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <limits.h>
#include <resolv.h>
#include <util.h>
#include <ifaddrs.h>
#define MINIMUM(a, b) (((a) < (b)) ? (a) : (b))
#define MAXIMUM(a, b) (((a) > (b)) ? (a) : (b))
#define HWFEATURESBITS \
"\024\1CSUM_IPv4\2CSUM_TCPv4\3CSUM_UDPv4" \
"\5VLAN_MTU\6VLAN_HWTAGGING\10CSUM_TCPv6" \
"\11CSUM_UDPv6\20WOL"
struct ifreq ifr, ridreq;
struct in_aliasreq in_addreq;
struct in6_ifreq ifr6;
struct in6_ifreq in6_ridreq;
struct in6_aliasreq in6_addreq;
struct sockaddr_in netmask;
char ifname[IFNAMSIZ];
int flags, xflags, setaddr, setmask, setipdst, setbroad, doalias;
u_long metric, mtu;
int rdomainid;
int llprio;
int clearaddr, sock;
int newaddr = 0;
int af = AF_INET;
int explicit_prefix = 0;
int Lflag = 1;
int showcapsflag;
void notealias(const char *, int);
void setifaddr(const char *, int);
void setifrtlabel(const char *, int);
void setifdstaddr(const char *, int);
void addaf(const char *, int);
void removeaf(const char *, int);
void setifbroadaddr(const char *, int);
void setifnetmask(const char *, int);
void setifprefixlen(const char *, int);
void settunnel(const char *, const char *);
void settunneladdr(const char *, int);
void deletetunnel(const char *, int);
void settunnelinst(const char *, int);
void unsettunnelinst(const char *, int);
void settunnelttl(const char *, int);
void setia6flags(const char *, int);
void setia6pltime(const char *, int);
void setia6vltime(const char *, int);
void setia6lifetime(const char *, const char *);
void setia6eui64(const char *, int);
void setrdomain(const char *, int);
void unsetrdomain(const char *, int);
int prefix(void *val, int);
int printgroup(char *, int);
void setifipdst(const char *, int);
void setignore(const char *, int);
int actions;
#define A_SILENT 0x8000000
#define NEXTARG0 0xffffff
#define NEXTARG 0xfffffe
#define NEXTARG2 0xfffffd
const struct cmd {
char *c_name;
int c_parameter;
int c_action;
void (*c_func)(const char *, int);
void (*c_func2)(const char *, const char *);
} cmds[] = {
{ "alias", IFF_UP, 0, notealias },
{ "-alias", -IFF_UP, 0, notealias },
{ "delete", -IFF_UP, 0, notealias },
{ "netmask", NEXTARG, 0, setifnetmask },
{ "broadcast", NEXTARG, 0, setifbroadaddr },
{ "prefixlen", NEXTARG, 0, setifprefixlen},
{ "anycast", IN6_IFF_ANYCAST, 0, setia6flags },
{ "-anycast", -IN6_IFF_ANYCAST, 0, setia6flags },
{ "tentative", IN6_IFF_TENTATIVE, 0, setia6flags },
{ "-tentative", -IN6_IFF_TENTATIVE, 0, setia6flags },
{ "pltime", NEXTARG, 0, setia6pltime },
{ "vltime", NEXTARG, 0, setia6vltime },
{ "eui64", 0, 0, setia6eui64 },
#ifndef SMALL
{ "rtlabel", NEXTARG, 0, setifrtlabel },
{ "-rtlabel", -1, 0, setifrtlabel },
{ "rdomain", NEXTARG, 0, setrdomain },
{ "-rdomain", 0, 0, unsetrdomain },
{ "tunnel", NEXTARG2, 0, NULL, settunnel },
{ "tunneladdr", NEXTARG, 0, settunneladdr },
{ "-tunnel", 0, 0, deletetunnel },
{ "tunneldomain", NEXTARG, 0, settunnelinst },
{ "-tunneldomain", 0, 0, unsettunnelinst },
{ "tunnelttl", NEXTARG, 0, settunnelttl },
{ "-inet", AF_INET, 0, removeaf },
{ "-inet6", AF_INET6, 0, removeaf },
{ "ipdst", NEXTARG, 0, setifipdst },
#endif
{ NULL, 0, 0, setifaddr },
{ NULL, 0, 0, setifdstaddr },
{ NULL, 0, 0, NULL },
};
#define IFFBITS \
"\024\1UP\2BROADCAST\3DEBUG\4LOOPBACK\5POINTOPOINT\6STATICARP" \
"\7RUNNING\10NOARP\11PROMISC\12ALLMULTI\13OACTIVE\14SIMPLEX" \
"\15LINK0\16LINK1\17LINK2\20MULTICAST" \
"\23AUTOCONF6TEMP\24MPLS\25WOL\26AUTOCONF6\27INET6_NOSOII" \
"\30AUTOCONF4"
int getinfo(struct ifreq *, int);
void getsock(int);
void printif(char *, int);
void printb(char *, unsigned int, unsigned char *);
void printb_status(unsigned short, unsigned char *);
const char *get_linkstate(int, int);
void status(int, struct sockaddr_dl *, int);
__dead void usage(void);
const char *get_string(const char *, const char *, u_int8_t *, int *);
int len_string(const u_int8_t *, int);
int print_string(const u_int8_t *, int);
char *sec2str(time_t);
unsigned long get_ts_map(int, int, int);
void in_status(int);
void in_getaddr(const char *, int);
void in_getprefix(const char *, int);
void in6_fillscopeid(struct sockaddr_in6 *);
void in6_alias(struct in6_ifreq *);
void in6_status(int);
void in6_getaddr(const char *, int);
void in6_getprefix(const char *, int);
const struct afswtch {
char *af_name;
short af_af;
void (*af_status)(int);
void (*af_getaddr)(const char *, int);
void (*af_getprefix)(const char *, int);
u_long af_difaddr;
u_long af_aifaddr;
caddr_t af_ridreq;
caddr_t af_addreq;
} afs[] = {
#define C(x) ((caddr_t) &x)
{ "inet", AF_INET, in_status, in_getaddr, in_getprefix,
SIOCDIFADDR, SIOCAIFADDR, C(ridreq), C(in_addreq) },
{ "inet6", AF_INET6, in6_status, in6_getaddr, in6_getprefix,
SIOCDIFADDR_IN6, SIOCAIFADDR_IN6, C(in6_ridreq), C(in6_addreq) },
{ 0, 0, 0, 0 }
};
const struct afswtch *afp;
int ifaliases = 0;
int aflag = 0;
int
main(int argc, char *argv[])
{
const struct afswtch *rafp = NULL;
int create = 0;
int i;
if (argc < 2) {
if (unveil("/", "") == -1)
err(1, "unveil /");
if (unveil(NULL, NULL) == -1)
err(1, "unveil");
aflag = 1;
printif(NULL, 0);
return (0);
}
argc--, argv++;
if (*argv[0] == '-') {
int nomore = 0;
for (i = 1; argv[0][i]; i++) {
switch (argv[0][i]) {
case 'a':
aflag = 1;
nomore = 1;
break;
case 'A':
aflag = 1;
ifaliases = 1;
nomore = 1;
break;
default:
usage();
break;
}
}
if (nomore == 0) {
argc--, argv++;
if (argc < 1)
usage();
if (strlcpy(ifname, *argv, sizeof(ifname)) >= IFNAMSIZ)
errx(1, "interface name '%s' too long", *argv);
}
} else if (strlcpy(ifname, *argv, sizeof(ifname)) >= IFNAMSIZ)
errx(1, "interface name '%s' too long", *argv);
argc--, argv++;
if (unveil(_PATH_RESCONF, "r") == -1)
err(1, "unveil %s", _PATH_RESCONF);
if (unveil(_PATH_HOSTS, "r") == -1)
err(1, "unveil %s", _PATH_HOSTS);
if (unveil(_PATH_SERVICES, "r") == -1)
err(1, "unveil %s", _PATH_SERVICES);
if (unveil(NULL, NULL) == -1)
err(1, "unveil");
if (argc > 0) {
for (afp = rafp = afs; rafp->af_name; rafp++)
if (strcmp(rafp->af_name, *argv) == 0) {
afp = rafp;
argc--;
argv++;
break;
}
rafp = afp;
af = ifr.ifr_addr.sa_family = rafp->af_af;
}
(void) strlcpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name));
in6_addreq.ifra_lifetime.ia6t_pltime = ND6_INFINITE_LIFETIME;
in6_addreq.ifra_lifetime.ia6t_vltime = ND6_INFINITE_LIFETIME;
if (aflag == 0) {
create = (argc > 0) && strcmp(argv[0], "destroy") != 0;
(void)getinfo(&ifr, create);
}
if (argc != 0 && af == AF_INET6)
addaf(ifname, AF_INET6);
while (argc > 0) {
const struct cmd *p;
for (p = cmds; p->c_name; p++)
if (strcmp(*argv, p->c_name) == 0)
break;
if (p->c_name == 0 && setaddr)
for (i = setaddr; i > 0; i--) {
p++;
if (p->c_func == NULL)
errx(1, "%s: bad value", *argv);
}
if (p->c_func || p->c_func2) {
if (p->c_parameter == NEXTARG0) {
const struct cmd *p0;
int noarg = 1;
if (argv[1]) {
for (p0 = cmds; p0->c_name; p0++)
if (strcmp(argv[1],
p0->c_name) == 0) {
noarg = 0;
break;
}
} else
noarg = 0;
if (noarg == 0)
(*p->c_func)(NULL, 0);
else
goto nextarg;
} else if (p->c_parameter == NEXTARG) {
nextarg:
if (argv[1] == NULL)
errx(1, "'%s' requires argument",
p->c_name);
(*p->c_func)(argv[1], 0);
argc--, argv++;
actions = actions | A_SILENT | p->c_action;
} else if (p->c_parameter == NEXTARG2) {
if ((argv[1] == NULL) ||
(argv[2] == NULL))
errx(1, "'%s' requires 2 arguments",
p->c_name);
(*p->c_func2)(argv[1], argv[2]);
argc -= 2;
argv += 2;
actions = actions | A_SILENT | p->c_action;
} else {
(*p->c_func)(*argv, p->c_parameter);
actions = actions | A_SILENT | p->c_action;
}
}
argc--, argv++;
}
if (argc == 0 && actions == 0) {
printif(ifr.ifr_name, aflag ? ifaliases : 1);
return (0);
}
if (af == AF_INET6 && explicit_prefix == 0) {
if (setipdst && (flags & IFF_POINTOPOINT))
setifprefixlen("128", 0);
else
setifprefixlen("64", 0);
}
if (doalias == 0 || (newaddr && clearaddr)) {
(void) strlcpy(rafp->af_ridreq, ifname, sizeof(ifr.ifr_name));
if (setaddr) {
memcpy(&ridreq.ifr_addr, &in_addreq.ifra_addr,
in_addreq.ifra_addr.sin_len);
if (ioctl(sock, SIOCSIFADDR, rafp->af_ridreq) == -1)
err(1, "SIOCSIFADDR");
}
if (setmask) {
memcpy(&ridreq.ifr_addr, &in_addreq.ifra_mask,
in_addreq.ifra_mask.sin_len);
if (ioctl(sock, SIOCSIFNETMASK, rafp->af_ridreq) == -1)
err(1, "SIOCSIFNETMASK");
}
if (setipdst) {
memcpy(&ridreq.ifr_addr, &in_addreq.ifra_dstaddr,
in_addreq.ifra_dstaddr.sin_len);
if (ioctl(sock, SIOCSIFDSTADDR, rafp->af_ridreq) == -1)
err(1, "SIOCSIFDSTADDR");
}
if (setbroad) {
memcpy(&ridreq.ifr_addr, &in_addreq.ifra_broadaddr,
in_addreq.ifra_broadaddr.sin_len);
if (ioctl(sock, SIOCSIFBRDADDR, rafp->af_ridreq) == -1)
err(1, "SIOCSIFBRDADDR");
}
return (0);
}
if (clearaddr) {
(void) strlcpy(rafp->af_ridreq, ifname, sizeof(ifr.ifr_name));
if (ioctl(sock, rafp->af_difaddr, rafp->af_ridreq) == -1) {
if (errno == EADDRNOTAVAIL && (doalias >= 0)) {
} else
err(1, "SIOCDIFADDR");
}
}
if (newaddr) {
(void) strlcpy(rafp->af_addreq, ifname, sizeof(ifr.ifr_name));
if (ioctl(sock, rafp->af_aifaddr, rafp->af_addreq) == -1)
err(1, "SIOCAIFADDR");
}
return (0);
}
void
getsock(int naf)
{
static int oaf = -1;
if (oaf == naf)
return;
if (oaf != -1)
close(sock);
sock = socket(naf, SOCK_DGRAM, 0);
if (sock == -1)
oaf = -1;
else
oaf = naf;
}
int
getinfo(struct ifreq *ifr, int create)
{
getsock(af);
if (sock == -1)
err(1, "socket");
if (!isdigit((unsigned char)ifname[strlen(ifname) - 1]))
return (-1);
if (ioctl(sock, SIOCGIFFLAGS, (caddr_t)ifr) == -1) {
int oerrno = errno;
if (!create)
return (-1);
if (ioctl(sock, SIOCIFCREATE, (caddr_t)ifr) == -1) {
errno = oerrno;
return (-1);
}
if (ioctl(sock, SIOCGIFFLAGS, (caddr_t)ifr) == -1)
return (-1);
}
flags = ifr->ifr_flags & 0xffff;
if (ioctl(sock, SIOCGIFXFLAGS, (caddr_t)ifr) == -1)
ifr->ifr_flags = 0;
xflags = ifr->ifr_flags;
if (ioctl(sock, SIOCGIFMETRIC, (caddr_t)ifr) == -1)
metric = 0;
else
metric = ifr->ifr_metric;
if (ioctl(sock, SIOCGIFMTU, (caddr_t)ifr) == -1)
mtu = 0;
else
mtu = ifr->ifr_mtu;
#ifndef SMALL
if (ioctl(sock, SIOCGIFRDOMAIN, (caddr_t)ifr) == -1)
rdomainid = 0;
else
rdomainid = ifr->ifr_rdomainid;
#endif
if (ioctl(sock, SIOCGIFLLPRIO, (caddr_t)ifr) == -1)
llprio = 0;
else
llprio = ifr->ifr_llprio;
return (0);
}
int
printgroup(char *groupname, int ifaliases)
{
struct ifgroupreq ifgr;
struct ifg_req *ifg;
int len, cnt = 0;
getsock(AF_INET);
bzero(&ifgr, sizeof(ifgr));
strlcpy(ifgr.ifgr_name, groupname, sizeof(ifgr.ifgr_name));
if (ioctl(sock, SIOCGIFGMEMB, (caddr_t)&ifgr) == -1) {
if (errno == EINVAL || errno == ENOTTY ||
errno == ENOENT)
return (-1);
else
err(1, "SIOCGIFGMEMB");
}
len = ifgr.ifgr_len;
if ((ifgr.ifgr_groups = calloc(1, len)) == NULL)
err(1, "printgroup");
if (ioctl(sock, SIOCGIFGMEMB, (caddr_t)&ifgr) == -1)
err(1, "SIOCGIFGMEMB");
for (ifg = ifgr.ifgr_groups; ifg && len >= sizeof(struct ifg_req);
ifg++) {
len -= sizeof(struct ifg_req);
printif(ifg->ifgrq_member, ifaliases);
cnt++;
}
free(ifgr.ifgr_groups);
return (cnt);
}
void
printif(char *name, int ifaliases)
{
struct ifaddrs *ifap, *ifa;
struct if_data *ifdata;
const char *namep;
char *oname = NULL;
struct ifreq *ifrp;
int count = 0, noinet = 1;
size_t nlen = 0;
if (aflag)
name = NULL;
if (name) {
if ((oname = strdup(name)) == NULL)
err(1, "strdup");
nlen = strlen(oname);
if (nlen && !isdigit((unsigned char)oname[nlen - 1]))
if (printgroup(oname, ifaliases) != -1) {
free(oname);
return;
}
}
if (getifaddrs(&ifap) != 0)
err(1, "getifaddrs");
namep = NULL;
for (ifa = ifap; ifa; ifa = ifa->ifa_next) {
if (oname) {
if (nlen && isdigit((unsigned char)oname[nlen - 1])) {
if (strcmp(oname, ifa->ifa_name) != 0)
continue;
} else {
if (strncmp(oname, ifa->ifa_name, nlen) != 0 ||
!isdigit((unsigned char)ifa->ifa_name[nlen]))
continue;
}
}
if (ifa->ifa_addr->sa_family == AF_INET6) {
memset(&ifr6, 0, sizeof(ifr6));
memcpy(&ifr6.ifr_addr, ifa->ifa_addr,
MINIMUM(sizeof(ifr6.ifr_addr), ifa->ifa_addr->sa_len));
ifrp = (struct ifreq *)&ifr6;
} else {
memset(&ifr, 0, sizeof(ifr));
memcpy(&ifr.ifr_addr, ifa->ifa_addr,
MINIMUM(sizeof(ifr.ifr_addr), ifa->ifa_addr->sa_len));
ifrp = 𝔦
}
strlcpy(ifname, ifa->ifa_name, sizeof(ifname));
strlcpy(ifrp->ifr_name, ifa->ifa_name, sizeof(ifrp->ifr_name));
if (ifa->ifa_addr->sa_family == AF_LINK) {
namep = ifa->ifa_name;
if (getinfo(ifrp, 0) < 0)
continue;
ifdata = ifa->ifa_data;
status(1, (struct sockaddr_dl *)ifa->ifa_addr,
ifdata->ifi_link_state);
count++;
noinet = 1;
continue;
}
if (!namep || !strcmp(namep, ifa->ifa_name)) {
const struct afswtch *p;
if (ifa->ifa_addr->sa_family == AF_INET &&
ifaliases == 0 && noinet == 0)
continue;
if ((p = afp) != NULL) {
if (ifa->ifa_addr->sa_family == p->af_af)
p->af_status(1);
} else {
for (p = afs; p->af_name; p++) {
if (ifa->ifa_addr->sa_family ==
p->af_af)
p->af_status(0);
}
}
count++;
if (ifa->ifa_addr->sa_family == AF_INET)
noinet = 0;
continue;
}
}
freeifaddrs(ifap);
free(oname);
if (count == 0) {
fprintf(stderr, "%s: no such interface\n", ifname);
exit(1);
}
}
#define RIDADDR 0
#define ADDR 1
#define MASK 2
#define DSTADDR 3
void
setifaddr(const char *addr, int param)
{
setaddr++;
if (doalias >= 0)
newaddr = 1;
if (doalias == 0)
clearaddr = 1;
afp->af_getaddr(addr, (doalias >= 0 ? ADDR : RIDADDR));
}
#ifndef SMALL
void
setifrtlabel(const char *label, int d)
{
if (d != 0)
ifr.ifr_data = (caddr_t)(const char *)"";
else
ifr.ifr_data = (caddr_t)label;
if (ioctl(sock, SIOCSIFRTLABEL, &ifr) == -1)
warn("SIOCSIFRTLABEL");
}
#endif
void
setifnetmask(const char *addr, int ignored)
{
setmask++;
afp->af_getaddr(addr, MASK);
explicit_prefix = 1;
}
void
setifbroadaddr(const char *addr, int ignored)
{
setbroad++;
afp->af_getaddr(addr, DSTADDR);
}
void
setifipdst(const char *addr, int ignored)
{
in_getaddr(addr, DSTADDR);
setipdst++;
clearaddr = 0;
newaddr = 0;
}
#define rqtosa(x) (&(((struct ifreq *)(afp->x))->ifr_addr))
void
notealias(const char *addr, int param)
{
if (setaddr && doalias == 0 && param < 0)
memcpy(rqtosa(af_ridreq), rqtosa(af_addreq),
rqtosa(af_addreq)->sa_len);
doalias = param;
if (param < 0) {
clearaddr = 1;
newaddr = 0;
} else
clearaddr = 0;
}
void
setifdstaddr(const char *addr, int param)
{
setaddr++;
setipdst++;
afp->af_getaddr(addr, DSTADDR);
}
void
addaf(const char *vname, int value)
{
struct if_afreq ifar;
strlcpy(ifar.ifar_name, ifname, sizeof(ifar.ifar_name));
ifar.ifar_af = value;
if (ioctl(sock, SIOCIFAFATTACH, (caddr_t)&ifar) == -1)
warn("SIOCIFAFATTACH");
}
void
removeaf(const char *vname, int value)
{
struct if_afreq ifar;
strlcpy(ifar.ifar_name, ifname, sizeof(ifar.ifar_name));
ifar.ifar_af = value;
if (ioctl(sock, SIOCIFAFDETACH, (caddr_t)&ifar) == -1)
warn("SIOCIFAFDETACH");
}
void
setia6flags(const char *vname, int value)
{
if (value < 0) {
value = -value;
in6_addreq.ifra_flags &= ~value;
} else
in6_addreq.ifra_flags |= value;
}
void
setia6pltime(const char *val, int d)
{
setia6lifetime("pltime", val);
}
void
setia6vltime(const char *val, int d)
{
setia6lifetime("vltime", val);
}
void
setia6lifetime(const char *cmd, const char *val)
{
const char *errmsg = NULL;
time_t newval, t;
newval = strtonum(val, 0, 1000000, &errmsg);
if (errmsg)
errx(1, "invalid %s %s: %s", cmd, val, errmsg);
t = time(NULL);
if (afp->af_af != AF_INET6)
errx(1, "%s not allowed for this address family", cmd);
if (strcmp(cmd, "vltime") == 0) {
in6_addreq.ifra_lifetime.ia6t_expire = t + newval;
in6_addreq.ifra_lifetime.ia6t_vltime = newval;
} else if (strcmp(cmd, "pltime") == 0) {
in6_addreq.ifra_lifetime.ia6t_preferred = t + newval;
in6_addreq.ifra_lifetime.ia6t_pltime = newval;
}
}
void
setia6eui64(const char *cmd, int val)
{
struct ifaddrs *ifap, *ifa;
const struct sockaddr_in6 *sin6 = NULL;
const struct in6_addr *lladdr = NULL;
struct in6_addr *in6;
if (afp->af_af != AF_INET6)
errx(1, "%s not allowed for this address family", cmd);
addaf(ifname, AF_INET6);
in6 = (struct in6_addr *)&in6_addreq.ifra_addr.sin6_addr;
if (memcmp(&in6addr_any.s6_addr[8], &in6->s6_addr[8], 8) != 0)
errx(1, "interface index is already filled");
if (getifaddrs(&ifap) != 0)
err(1, "getifaddrs");
for (ifa = ifap; ifa; ifa = ifa->ifa_next) {
if (ifa->ifa_addr->sa_family == AF_INET6 &&
strcmp(ifa->ifa_name, ifname) == 0) {
sin6 = (const struct sockaddr_in6 *)ifa->ifa_addr;
if (IN6_IS_ADDR_LINKLOCAL(&sin6->sin6_addr)) {
lladdr = &sin6->sin6_addr;
break;
}
}
}
if (!lladdr)
errx(1, "could not determine link local address");
memcpy(&in6->s6_addr[8], &lladdr->s6_addr[8], 8);
freeifaddrs(ifap);
}
const char *
get_string(const char *val, const char *sep, u_int8_t *buf, int *lenp)
{
int len = *lenp, hexstr;
u_int8_t *p = buf;
hexstr = (val[0] == '0' && tolower((u_char)val[1]) == 'x');
if (hexstr)
val += 2;
for (;;) {
if (*val == '\0')
break;
if (sep != NULL && strchr(sep, *val) != NULL) {
val++;
break;
}
if (hexstr) {
if (!isxdigit((u_char)val[0]) ||
!isxdigit((u_char)val[1])) {
warnx("bad hexadecimal digits");
return NULL;
}
}
if (p > buf + len) {
if (hexstr)
warnx("hexadecimal digits too long");
else
warnx("strings too long");
return NULL;
}
if (hexstr) {
#define tohex(x) (isdigit(x) ? (x) - '0' : tolower(x) - 'a' + 10)
*p++ = (tohex((u_char)val[0]) << 4) |
tohex((u_char)val[1]);
#undef tohex
val += 2;
} else {
if (*val == '\\' &&
sep != NULL && strchr(sep, *(val + 1)) != NULL)
val++;
*p++ = *val++;
}
}
len = p - buf;
if (len < *lenp)
memset(p, 0, *lenp - len);
*lenp = len;
return val;
}
int
len_string(const u_int8_t *buf, int len)
{
int i = 0, hasspc = 0;
if (len < 2 || buf[0] != '0' || tolower(buf[1]) != 'x') {
for (; i < len; i++) {
if (buf[i] & 0x80 || !isprint(buf[i]))
break;
if (isspace(buf[i]))
hasspc++;
}
}
if (i == len) {
if (hasspc || len == 0)
return len + 2;
else
return len;
} else
return (len * 2) + 2;
}
int
print_string(const u_int8_t *buf, int len)
{
int i = 0, hasspc = 0;
if (len < 2 || buf[0] != '0' || tolower(buf[1]) != 'x') {
for (; i < len; i++) {
if (buf[i] & 0x80 || !isprint(buf[i]))
break;
if (isspace(buf[i]))
hasspc++;
}
}
if (i == len) {
if (hasspc || len == 0) {
printf("\"%.*s\"", len, buf);
return len + 2;
} else {
printf("%.*s", len, buf);
return len;
}
} else {
printf("0x");
for (i = 0; i < len; i++)
printf("%02x", buf[i]);
return (len * 2) + 2;
}
}
static void
print_tunnel(const struct if_laddrreq *req)
{
char psrcaddr[NI_MAXHOST];
char pdstaddr[NI_MAXHOST];
const char *ver = "";
const int niflag = NI_NUMERICHOST;
if (req == NULL) {
printf("(unset)");
return;
}
psrcaddr[0] = pdstaddr[0] = '\0';
if (getnameinfo((struct sockaddr *)&req->addr, req->addr.ss_len,
psrcaddr, sizeof(psrcaddr), 0, 0, niflag) != 0)
strlcpy(psrcaddr, "<error>", sizeof(psrcaddr));
if (req->addr.ss_family == AF_INET6)
ver = "6";
printf("inet%s %s", ver, psrcaddr);
if (req->dstaddr.ss_family != AF_UNSPEC) {
in_port_t dstport = 0;
const struct sockaddr_in *sin;
const struct sockaddr_in6 *sin6;
if (getnameinfo((struct sockaddr *)&req->dstaddr,
req->dstaddr.ss_len, pdstaddr, sizeof(pdstaddr),
0, 0, niflag) != 0)
strlcpy(pdstaddr, "<error>", sizeof(pdstaddr));
printf(" -> %s", pdstaddr);
switch (req->dstaddr.ss_family) {
case AF_INET:
sin = (const struct sockaddr_in *)&req->dstaddr;
dstport = sin->sin_port;
break;
case AF_INET6:
sin6 = (const struct sockaddr_in6 *)&req->dstaddr;
dstport = sin6->sin6_port;
break;
}
if (dstport)
printf(":%u", ntohs(dstport));
}
}
static void
phys_status(int force)
{
struct if_laddrreq req;
struct if_laddrreq *r = &req;
memset(&req, 0, sizeof(req));
(void) strlcpy(req.iflr_name, ifname, sizeof(req.iflr_name));
if (ioctl(sock, SIOCGLIFPHYADDR, (caddr_t)&req) == -1) {
if (errno != EADDRNOTAVAIL)
return;
r = NULL;
}
printf("\ttunnel: ");
print_tunnel(r);
if (ioctl(sock, SIOCGLIFPHYTTL, (caddr_t)&ifr) == 0) {
if (ifr.ifr_ttl == -1)
printf(" ttl copy");
else if (ifr.ifr_ttl > 0)
printf(" ttl %d", ifr.ifr_ttl);
}
if (ioctl(sock, SIOCGLIFPHYDF, (caddr_t)&ifr) == 0)
printf(" %s", ifr.ifr_df ? "df" : "nodf");
#ifndef SMALL
if (ioctl(sock, SIOCGLIFPHYECN, (caddr_t)&ifr) == 0)
printf(" %s", ifr.ifr_metric ? "ecn" : "noecn");
if (ioctl(sock, SIOCGLIFPHYRTABLE, (caddr_t)&ifr) == 0 &&
(rdomainid != 0 || ifr.ifr_rdomainid != 0))
printf(" rdomain %d", ifr.ifr_rdomainid);
#endif
printf("\n");
}
#ifndef SMALL
const uint64_t ifm_status_valid_list[] = IFM_STATUS_VALID_LIST;
const struct ifmedia_status_description ifm_status_descriptions[] =
IFM_STATUS_DESCRIPTIONS;
#endif
const struct if_status_description if_status_descriptions[] =
LINK_STATE_DESCRIPTIONS;
const char *
get_linkstate(int mt, int link_state)
{
const struct if_status_description *p;
static char buf[8];
for (p = if_status_descriptions; p->ifs_string != NULL; p++) {
if (LINK_STATE_DESC_MATCH(p, mt, link_state))
return (p->ifs_string);
}
snprintf(buf, sizeof(buf), "[#%d]", link_state);
return buf;
}
void
status(int link, struct sockaddr_dl *sdl, int ls)
{
const struct afswtch *p = afp;
struct ifmediareq ifmr;
#ifndef SMALL
struct ifreq ifrdesc;
char ifdescr[IFDESCRSIZE];
#endif
uint64_t *media_list;
char sep;
printf("%s: ", ifname);
printb("flags", flags | (xflags << 16), IFFBITS);
if (rdomainid)
printf(" rdomain %d", rdomainid);
if (metric)
printf(" metric %lu", metric);
if (mtu)
printf(" mtu %lu", mtu);
putchar('\n');
if (sdl != NULL && sdl->sdl_alen &&
(sdl->sdl_type == IFT_ETHER || sdl->sdl_type == IFT_CARP))
(void)printf("\tlladdr %s\n", ether_ntoa(
(struct ether_addr *)LLADDR(sdl)));
sep = '\t';
#ifndef SMALL
(void) memset(&ifrdesc, 0, sizeof(ifrdesc));
(void) strlcpy(ifrdesc.ifr_name, ifname, sizeof(ifrdesc.ifr_name));
ifrdesc.ifr_data = (caddr_t)&ifdescr;
if (ioctl(sock, SIOCGIFDESCR, &ifrdesc) == 0 &&
strlen(ifrdesc.ifr_data))
printf("\tdescription: %s\n", ifrdesc.ifr_data);
if (sdl != NULL) {
printf("%cindex %u", sep, sdl->sdl_index);
sep = ' ';
}
if (ioctl(sock, SIOCGIFPRIORITY, &ifrdesc) == 0) {
printf("%cpriority %d", sep, ifrdesc.ifr_metric);
sep = ' ';
}
#endif
printf("%cllprio %d\n", sep, llprio);
(void) memset(&ifmr, 0, sizeof(ifmr));
(void) strlcpy(ifmr.ifm_name, ifname, sizeof(ifmr.ifm_name));
if (ioctl(sock, SIOCGIFMEDIA, (caddr_t)&ifmr) == -1) {
if (ls != LINK_STATE_UNKNOWN)
printf("\tstatus: %s\n",
get_linkstate(sdl->sdl_type, ls));
goto proto_status;
}
if (ifmr.ifm_count == 0) {
warnx("%s: no media types?", ifname);
goto proto_status;
}
media_list = calloc(ifmr.ifm_count, sizeof(*media_list));
if (media_list == NULL)
err(1, "calloc");
ifmr.ifm_ulist = media_list;
if (ioctl(sock, SIOCGIFMEDIA, (caddr_t)&ifmr) == -1)
err(1, "SIOCGIFMEDIA");
#ifdef SMALL
printf("\tstatus: %s\n", get_linkstate(sdl->sdl_type, ls));
#else
if (ifmr.ifm_status & IFM_AVALID) {
const struct ifmedia_status_description *ifms;
int bitno, found = 0;
printf("\tstatus: ");
for (bitno = 0; ifm_status_valid_list[bitno] != 0; bitno++) {
for (ifms = ifm_status_descriptions;
ifms->ifms_valid != 0; ifms++) {
if (ifms->ifms_type !=
IFM_TYPE(ifmr.ifm_current) ||
ifms->ifms_valid !=
ifm_status_valid_list[bitno])
continue;
printf("%s%s", found ? ", " : "",
IFM_STATUS_DESC(ifms, ifmr.ifm_status));
found = 1;
break;
}
}
if (found == 0)
printf("unknown");
putchar('\n');
}
#endif
free(media_list);
proto_status:
if (link == 0) {
if ((p = afp) != NULL) {
p->af_status(1);
} else for (p = afs; p->af_name; p++) {
ifr.ifr_addr.sa_family = p->af_af;
p->af_status(0);
}
}
phys_status(0);
}
void
in_status(int force)
{
struct sockaddr_in *sin, sin2;
getsock(AF_INET);
if (sock == -1) {
if (errno == EPROTONOSUPPORT)
return;
err(1, "socket");
}
(void) strlcpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name));
sin = (struct sockaddr_in *)&ifr.ifr_addr;
memcpy(&sin2, &ifr.ifr_addr, sizeof(sin2));
(void) strlcpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name));
if (ioctl(sock, SIOCGIFADDR, (caddr_t)&ifr) == -1) {
warn("SIOCGIFADDR");
memset(&ifr.ifr_addr, 0, sizeof(ifr.ifr_addr));
}
(void) strlcpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name));
sin = (struct sockaddr_in *)&ifr.ifr_addr;
printf("\tinet %s", inet_ntoa(sin->sin_addr));
memcpy(&ifr.ifr_addr, &sin2, sizeof(sin2));
if (ioctl(sock, SIOCGIFNETMASK, (caddr_t)&ifr) == -1) {
if (errno != EADDRNOTAVAIL)
warn("SIOCGIFNETMASK");
memset(&ifr.ifr_addr, 0, sizeof(ifr.ifr_addr));
} else
netmask.sin_addr =
((struct sockaddr_in *)&ifr.ifr_addr)->sin_addr;
if (flags & IFF_POINTOPOINT) {
memcpy(&ifr.ifr_addr, &sin2, sizeof(sin2));
if (ioctl(sock, SIOCGIFDSTADDR, (caddr_t)&ifr) == -1) {
if (errno == EADDRNOTAVAIL)
memset(&ifr.ifr_addr, 0, sizeof(ifr.ifr_addr));
else
warn("SIOCGIFDSTADDR");
}
(void) strlcpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name));
sin = (struct sockaddr_in *)&ifr.ifr_dstaddr;
printf(" --> %s", inet_ntoa(sin->sin_addr));
}
printf(" netmask 0x%x", ntohl(netmask.sin_addr.s_addr));
if (flags & IFF_BROADCAST) {
memcpy(&ifr.ifr_addr, &sin2, sizeof(sin2));
if (ioctl(sock, SIOCGIFBRDADDR, (caddr_t)&ifr) == -1) {
if (errno == EADDRNOTAVAIL)
memset(&ifr.ifr_addr, 0, sizeof(ifr.ifr_addr));
else
warn("SIOCGIFBRDADDR");
}
(void) strlcpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name));
sin = (struct sockaddr_in *)&ifr.ifr_addr;
if (sin->sin_addr.s_addr != 0)
printf(" broadcast %s", inet_ntoa(sin->sin_addr));
}
putchar('\n');
}
void
setifprefixlen(const char *addr, int d)
{
setmask++;
if (afp->af_getprefix)
afp->af_getprefix(addr, MASK);
explicit_prefix = 1;
}
void
in6_fillscopeid(struct sockaddr_in6 *sin6)
{
#ifdef __KAME__
if (IN6_IS_ADDR_LINKLOCAL(&sin6->sin6_addr)) {
sin6->sin6_scope_id =
ntohs(*(u_int16_t *)&sin6->sin6_addr.s6_addr[2]);
sin6->sin6_addr.s6_addr[2] = sin6->sin6_addr.s6_addr[3] = 0;
}
#endif
}
void
in6_alias(struct in6_ifreq *creq)
{
struct sockaddr_in6 *sin6;
struct in6_ifreq ifr6;
u_int32_t scopeid;
char hbuf[NI_MAXHOST];
const int niflag = NI_NUMERICHOST;
getsock(AF_INET6);
if (sock == -1) {
if (errno == EPROTONOSUPPORT)
return;
err(1, "socket");
}
sin6 = (struct sockaddr_in6 *)&creq->ifr_addr;
in6_fillscopeid(sin6);
scopeid = sin6->sin6_scope_id;
if (getnameinfo((struct sockaddr *)sin6, sin6->sin6_len,
hbuf, sizeof(hbuf), NULL, 0, niflag) != 0)
strlcpy(hbuf, "", sizeof hbuf);
printf("\tinet6 %s", hbuf);
if (flags & IFF_POINTOPOINT) {
(void) memset(&ifr6, 0, sizeof(ifr6));
(void) strlcpy(ifr6.ifr_name, ifname, sizeof(ifr6.ifr_name));
ifr6.ifr_addr = creq->ifr_addr;
if (ioctl(sock, SIOCGIFDSTADDR_IN6, (caddr_t)&ifr6) == -1) {
if (errno != EADDRNOTAVAIL)
warn("SIOCGIFDSTADDR_IN6");
(void) memset(&ifr6.ifr_addr, 0, sizeof(ifr6.ifr_addr));
ifr6.ifr_addr.sin6_family = AF_INET6;
ifr6.ifr_addr.sin6_len = sizeof(struct sockaddr_in6);
}
sin6 = (struct sockaddr_in6 *)&ifr6.ifr_addr;
in6_fillscopeid(sin6);
if (getnameinfo((struct sockaddr *)sin6, sin6->sin6_len,
hbuf, sizeof(hbuf), NULL, 0, niflag) != 0)
strlcpy(hbuf, "", sizeof hbuf);
printf(" -> %s", hbuf);
}
(void) memset(&ifr6, 0, sizeof(ifr6));
(void) strlcpy(ifr6.ifr_name, ifname, sizeof(ifr6.ifr_name));
ifr6.ifr_addr = creq->ifr_addr;
if (ioctl(sock, SIOCGIFNETMASK_IN6, (caddr_t)&ifr6) == -1) {
if (errno != EADDRNOTAVAIL)
warn("SIOCGIFNETMASK_IN6");
} else {
sin6 = (struct sockaddr_in6 *)&ifr6.ifr_addr;
printf(" prefixlen %d", prefix(&sin6->sin6_addr,
sizeof(struct in6_addr)));
}
(void) memset(&ifr6, 0, sizeof(ifr6));
(void) strlcpy(ifr6.ifr_name, ifname, sizeof(ifr6.ifr_name));
ifr6.ifr_addr = creq->ifr_addr;
if (ioctl(sock, SIOCGIFAFLAG_IN6, (caddr_t)&ifr6) == -1) {
if (errno != EADDRNOTAVAIL)
warn("SIOCGIFAFLAG_IN6");
} else {
if (ifr6.ifr_ifru.ifru_flags6 & IN6_IFF_ANYCAST)
printf(" anycast");
if (ifr6.ifr_ifru.ifru_flags6 & IN6_IFF_TENTATIVE)
printf(" tentative");
if (ifr6.ifr_ifru.ifru_flags6 & IN6_IFF_DUPLICATED)
printf(" duplicated");
if (ifr6.ifr_ifru.ifru_flags6 & IN6_IFF_DETACHED)
printf(" detached");
if (ifr6.ifr_ifru.ifru_flags6 & IN6_IFF_DEPRECATED)
printf(" deprecated");
if (ifr6.ifr_ifru.ifru_flags6 & IN6_IFF_AUTOCONF)
printf(" autoconf");
if (ifr6.ifr_ifru.ifru_flags6 & IN6_IFF_TEMPORARY)
printf(" autoconfprivacy");
}
if (scopeid)
printf(" scopeid 0x%x", scopeid);
if (Lflag) {
struct in6_addrlifetime *lifetime;
(void) memset(&ifr6, 0, sizeof(ifr6));
(void) strlcpy(ifr6.ifr_name, ifname, sizeof(ifr6.ifr_name));
ifr6.ifr_addr = creq->ifr_addr;
lifetime = &ifr6.ifr_ifru.ifru_lifetime;
if (ioctl(sock, SIOCGIFALIFETIME_IN6, (caddr_t)&ifr6) == -1) {
if (errno != EADDRNOTAVAIL)
warn("SIOCGIFALIFETIME_IN6");
} else if (lifetime->ia6t_preferred || lifetime->ia6t_expire) {
time_t t = time(NULL);
printf(" pltime ");
if (lifetime->ia6t_preferred) {
printf("%s", lifetime->ia6t_preferred < t
? "0" :
sec2str(lifetime->ia6t_preferred - t));
} else
printf("infty");
printf(" vltime ");
if (lifetime->ia6t_expire) {
printf("%s", lifetime->ia6t_expire < t
? "0"
: sec2str(lifetime->ia6t_expire - t));
} else
printf("infty");
}
}
printf("\n");
}
void
in6_status(int force)
{
in6_alias((struct in6_ifreq *)&ifr6);
}
#ifndef SMALL
void
settunnel(const char *src, const char *dst)
{
char buf[HOST_NAME_MAX+1 + sizeof (":65535")], *dstport;
const char *dstip;
struct addrinfo *srcres, *dstres;
int ecode;
struct if_laddrreq req;
if (strchr(dst, ':') == NULL || strchr(dst, ':') != strrchr(dst, ':')) {
dstip = dst;
dstport = NULL;
} else {
if (strlcpy(buf, dst, sizeof(buf)) >= sizeof(buf))
errx(1, "%s bad value", dst);
dstport = strchr(buf, ':');
*dstport++ = '\0';
dstip = buf;
}
if ((ecode = getaddrinfo(src, NULL, NULL, &srcres)) != 0)
errx(1, "error in parsing address string: %s",
gai_strerror(ecode));
if ((ecode = getaddrinfo(dstip, dstport, NULL, &dstres)) != 0)
errx(1, "error in parsing address string: %s",
gai_strerror(ecode));
if (srcres->ai_addr->sa_family != dstres->ai_addr->sa_family)
errx(1,
"source and destination address families do not match");
memset(&req, 0, sizeof(req));
(void) strlcpy(req.iflr_name, ifname, sizeof(req.iflr_name));
memcpy(&req.addr, srcres->ai_addr, srcres->ai_addrlen);
memcpy(&req.dstaddr, dstres->ai_addr, dstres->ai_addrlen);
if (ioctl(sock, SIOCSLIFPHYADDR, &req) == -1)
warn("SIOCSLIFPHYADDR");
freeaddrinfo(srcres);
freeaddrinfo(dstres);
}
void
settunneladdr(const char *addr, int ignored)
{
struct addrinfo hints, *res;
struct if_laddrreq req;
ssize_t len;
int rv;
memset(&hints, 0, sizeof(hints));
hints.ai_family = AF_UNSPEC;
hints.ai_socktype = SOCK_DGRAM;
hints.ai_protocol = 0;
hints.ai_flags = AI_PASSIVE;
rv = getaddrinfo(addr, NULL, &hints, &res);
if (rv != 0)
errx(1, "tunneladdr %s: %s", addr, gai_strerror(rv));
memset(&req, 0, sizeof(req));
len = strlcpy(req.iflr_name, ifname, sizeof(req.iflr_name));
if (len >= sizeof(req.iflr_name))
errx(1, "%s: Interface name too long", ifname);
memcpy(&req.addr, res->ai_addr, res->ai_addrlen);
req.dstaddr.ss_len = 2;
req.dstaddr.ss_family = AF_UNSPEC;
if (ioctl(sock, SIOCSLIFPHYADDR, &req) == -1)
warn("tunneladdr %s", addr);
freeaddrinfo(res);
}
void
deletetunnel(const char *ignored, int alsoignored)
{
if (ioctl(sock, SIOCDIFPHYADDR, &ifr) == -1)
warn("SIOCDIFPHYADDR");
}
void
settunnelinst(const char *id, int param)
{
const char *errmsg = NULL;
int rdomainid;
rdomainid = strtonum(id, 0, RT_TABLEID_MAX, &errmsg);
if (errmsg)
errx(1, "rdomain %s: %s", id, errmsg);
strlcpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name));
ifr.ifr_rdomainid = rdomainid;
if (ioctl(sock, SIOCSLIFPHYRTABLE, (caddr_t)&ifr) == -1)
warn("SIOCSLIFPHYRTABLE");
}
void
unsettunnelinst(const char *ignored, int alsoignored)
{
strlcpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name));
ifr.ifr_rdomainid = 0;
if (ioctl(sock, SIOCSLIFPHYRTABLE, (caddr_t)&ifr) == -1)
warn("SIOCSLIFPHYRTABLE");
}
void
settunnelttl(const char *id, int param)
{
const char *errmsg = NULL;
int ttl;
if (strcmp(id, "copy") == 0)
ttl = -1;
else {
ttl = strtonum(id, 0, 0xff, &errmsg);
if (errmsg)
errx(1, "tunnelttl %s: %s", id, errmsg);
}
strlcpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name));
ifr.ifr_ttl = ttl;
if (ioctl(sock, SIOCSLIFPHYTTL, (caddr_t)&ifr) == -1)
warn("SIOCSLIFPHYTTL");
}
void
utf16_to_char(uint16_t *in, int inlen, char *out, size_t outlen)
{
uint16_t c;
while (outlen > 0) {
c = inlen > 0 ? letoh16(*in) : 0;
if (c == 0 || --outlen == 0) {
*out = '\0';
break;
}
*out++ = isascii(c) ? (char)c : '?';
in++;
inlen--;
}
}
int
char_to_utf16(const char *in, uint16_t *out, size_t outlen)
{
int n = 0;
uint16_t c;
for (;;) {
c = *in++;
if (c == '\0') {
memset(out, 0, outlen);
return n;
}
if (outlen < sizeof (*out))
return -1;
*out++ = htole16(c);
n += sizeof (*out);
outlen -= sizeof (*out);
}
}
#endif
#define SIN(x) ((struct sockaddr_in *) &(x))
struct sockaddr_in *sintab[] = {
SIN(ridreq.ifr_addr), SIN(in_addreq.ifra_addr),
SIN(in_addreq.ifra_mask), SIN(in_addreq.ifra_broadaddr)};
void
in_getaddr(const char *s, int which)
{
struct sockaddr_in *sin = sintab[which], tsin;
struct hostent *hp;
int bits, l;
char p[3];
bzero(&tsin, sizeof(tsin));
sin->sin_len = sizeof(*sin);
if (which != MASK)
sin->sin_family = AF_INET;
if (which == ADDR && strrchr(s, '/') != NULL &&
(bits = inet_net_pton(AF_INET, s, &tsin.sin_addr,
sizeof(tsin.sin_addr))) != -1) {
l = snprintf(p, sizeof(p), "%d", bits);
if (l < 0 || l >= sizeof(p))
errx(1, "%d: bad prefixlen", bits);
setmask++;
in_getprefix(p, MASK);
memcpy(&sin->sin_addr, &tsin.sin_addr, sizeof(sin->sin_addr));
} else if (inet_aton(s, &sin->sin_addr) == 0) {
if ((hp = gethostbyname(s)))
memcpy(&sin->sin_addr, hp->h_addr, hp->h_length);
else
errx(1, "%s: bad value", s);
}
}
void
in_getprefix(const char *plen, int which)
{
struct sockaddr_in *sin = sintab[which];
const char *errmsg = NULL;
u_char *cp;
int len;
len = strtonum(plen, 0, 32, &errmsg);
if (errmsg)
errx(1, "prefix %s: %s", plen, errmsg);
sin->sin_len = sizeof(*sin);
if (which != MASK)
sin->sin_family = AF_INET;
if ((len == 0) || (len == 32)) {
memset(&sin->sin_addr, 0xff, sizeof(struct in_addr));
return;
}
memset((void *)&sin->sin_addr, 0x00, sizeof(sin->sin_addr));
for (cp = (u_char *)&sin->sin_addr; len > 7; len -= 8)
*cp++ = 0xff;
if (len)
*cp = 0xff << (8 - len);
}
void
printb(char *s, unsigned int v, unsigned char *bits)
{
int i, any = 0;
unsigned char c;
if (bits && *bits == 8)
printf("%s=%o", s, v);
else
printf("%s=%x", s, v);
if (bits) {
bits++;
putchar('<');
while ((i = *bits++)) {
if (v & (1 << (i-1))) {
if (any)
putchar(',');
any = 1;
for (; (c = *bits) > 32; bits++)
putchar(c);
} else
for (; *bits > 32; bits++)
;
}
putchar('>');
}
}
void
printb_status(unsigned short v, unsigned char *bits)
{
int i, any = 0;
unsigned char c;
if (bits) {
bits++;
while ((i = *bits++)) {
if (v & (1 << (i-1))) {
if (any)
putchar(',');
any = 1;
for (; (c = *bits) > 32; bits++)
putchar(tolower(c));
} else
for (; *bits > 32; bits++)
;
}
}
}
#define SIN6(x) ((struct sockaddr_in6 *) &(x))
struct sockaddr_in6 *sin6tab[] = {
SIN6(in6_ridreq.ifr_addr), SIN6(in6_addreq.ifra_addr),
SIN6(in6_addreq.ifra_prefixmask), SIN6(in6_addreq.ifra_dstaddr)};
void
in6_getaddr(const char *s, int which)
{
struct sockaddr_in6 *sin6 = sin6tab[which];
struct addrinfo hints, *res;
char buf[HOST_NAME_MAX+1 + sizeof("/128")], *pfxlen;
int error;
memset(&hints, 0, sizeof(hints));
hints.ai_family = AF_INET6;
hints.ai_socktype = SOCK_DGRAM;
if (which == ADDR && strchr(s, '/') != NULL) {
if (strlcpy(buf, s, sizeof(buf)) >= sizeof(buf))
errx(1, "%s: bad value", s);
pfxlen = strchr(buf, '/');
*pfxlen++ = '\0';
s = buf;
setmask++;
in6_getprefix(pfxlen, MASK);
explicit_prefix = 1;
}
error = getaddrinfo(s, "0", &hints, &res);
if (error)
errx(1, "%s: %s", s, gai_strerror(error));
memcpy(sin6, res->ai_addr, res->ai_addrlen);
#ifdef __KAME__
if (IN6_IS_ADDR_LINKLOCAL(&sin6->sin6_addr) &&
*(u_int16_t *)&sin6->sin6_addr.s6_addr[2] == 0 &&
sin6->sin6_scope_id) {
*(u_int16_t *)&sin6->sin6_addr.s6_addr[2] =
htons(sin6->sin6_scope_id & 0xffff);
sin6->sin6_scope_id = 0;
}
#endif
freeaddrinfo(res);
}
void
in6_getprefix(const char *plen, int which)
{
struct sockaddr_in6 *sin6 = sin6tab[which];
const char *errmsg = NULL;
u_char *cp;
int len;
len = strtonum(plen, 0, 128, &errmsg);
if (errmsg)
errx(1, "prefix %s: %s", plen, errmsg);
sin6->sin6_len = sizeof(*sin6);
if (which != MASK)
sin6->sin6_family = AF_INET6;
if ((len == 0) || (len == 128)) {
memset(&sin6->sin6_addr, 0xff, sizeof(struct in6_addr));
return;
}
memset((void *)&sin6->sin6_addr, 0x00, sizeof(sin6->sin6_addr));
for (cp = (u_char *)&sin6->sin6_addr; len > 7; len -= 8)
*cp++ = 0xff;
if (len)
*cp = 0xff << (8 - len);
}
int
prefix(void *val, int size)
{
u_char *nam = (u_char *)val;
int byte, bit, plen = 0;
for (byte = 0; byte < size; byte++, plen += 8)
if (nam[byte] != 0xff)
break;
if (byte == size)
return (plen);
for (bit = 7; bit != 0; bit--, plen++)
if (!(nam[byte] & (1 << bit)))
break;
for (; bit != 0; bit--)
if (nam[byte] & (1 << bit))
return (0);
byte++;
for (; byte < size; byte++)
if (nam[byte])
return (0);
return (plen);
}
__dead void
usage(void)
{
fprintf(stderr,
"usage: ifaddr interface [address_family] "
"[address [dest_address]]\n"
"\t\t[parameters]\n");
exit(1);
}
#ifndef SMALL
void
printifhwfeatures(const char *unused, int show)
{
struct if_data ifrdat;
if (!show) {
if (showcapsflag)
usage();
showcapsflag = 1;
return;
}
bzero(&ifrdat, sizeof(ifrdat));
ifr.ifr_data = (caddr_t)&ifrdat;
if (ioctl(sock, SIOCGIFDATA, (caddr_t)&ifr) == -1)
err(1, "SIOCGIFDATA");
printb("\thwfeatures", (u_int)ifrdat.ifi_capabilities, HWFEATURESBITS);
if (ioctl(sock, SIOCGIFHARDMTU, (caddr_t)&ifr) != -1) {
if (ifr.ifr_hardmtu)
printf(" hardmtu %u", ifr.ifr_hardmtu);
}
putchar('\n');
}
#endif
char *
sec2str(time_t total)
{
static char result[256];
char *p = result;
char *end = &result[sizeof(result)];
snprintf(p, end - p, "%lld", (long long)total);
return (result);
}
#ifndef SMALL
void
setrdomain(const char *id, int param)
{
const char *errmsg = NULL;
int rdomainid;
rdomainid = strtonum(id, 0, RT_TABLEID_MAX, &errmsg);
if (errmsg)
errx(1, "rdomain %s: %s", id, errmsg);
strlcpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name));
ifr.ifr_rdomainid = rdomainid;
if (ioctl(sock, SIOCSIFRDOMAIN, (caddr_t)&ifr) == -1)
warn("SIOCSIFRDOMAIN");
}
void
unsetrdomain(const char *ignored, int alsoignored)
{
strlcpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name));
ifr.ifr_rdomainid = 0;
if (ioctl(sock, SIOCSIFRDOMAIN, (caddr_t)&ifr) == -1)
warn("SIOCSIFRDOMAIN");
}
#endif
#ifdef SMALL
void
setignore(const char *id, int param)
{
}
#endif