blob: 8f420194441d905f032c57964071a22efbea1da3 [file] [log] [blame]
/* $NetBSD: setkey.c,v 1.11.6.1 2007/08/01 11:52:23 vanhu Exp $ */
/* $KAME: setkey.c,v 1.36 2003/09/24 23:52:51 itojun Exp $ */
/*
* Copyright (C) 1995, 1996, 1997, 1998, and 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:
* 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 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
* ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT 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.
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include <sys/types.h>
#include <sys/param.h>
#include <sys/socket.h>
#include <sys/time.h>
#include <sys/stat.h>
#ifndef ANDROID_CHANGES
#include <sys/sysctl.h>
#endif
#include <err.h>
#include <netinet/in.h>
#include <net/pfkeyv2.h>
#include PATH_IPSEC_H
#include <stdio.h>
#include <stdlib.h>
#include <limits.h>
#include <string.h>
#include <ctype.h>
#include <unistd.h>
#include <errno.h>
#include <netdb.h>
#include <fcntl.h>
#include <dirent.h>
#include <time.h>
#ifdef HAVE_READLINE
#include <readline/readline.h>
#include <readline/history.h>
#endif
#include "config.h"
#include "libpfkey.h"
#include "package_version.h"
#define extern /* so that variables in extern.h are not extern... */
#include "extern.h"
#define strlcpy(d,s,l) (strncpy(d,s,l), (d)[(l)-1] = '\0')
void usage __P((int));
int main __P((int, char **));
int get_supported __P((void));
void sendkeyshort __P((u_int));
void promisc __P((void));
int postproc __P((struct sadb_msg *, int));
int verifypriority __P((struct sadb_msg *m));
int fileproc __P((const char *));
const char *numstr __P((int));
void shortdump_hdr __P((void));
void shortdump __P((struct sadb_msg *));
static void printdate __P((void));
static int32_t gmt2local __P((time_t));
void stdin_loop __P((void));
#ifdef ANDROID_CHANGES
#include <stdarg.h>
void err(int eval, const char *fmt, ...) {
va_list p;
va_start(p, fmt);
fprintf(stderr, fmt, p);
va_end(p);
exit(eval);
}
#define errx err
void warnx(const char *fmt, ...) {
va_list p;
va_start(p, fmt);
fprintf(stderr, fmt, p);
va_end(p);
}
#endif
#define MODE_SCRIPT 1
#define MODE_CMDDUMP 2
#define MODE_CMDFLUSH 3
#define MODE_PROMISC 4
#define MODE_STDIN 5
int so;
int f_forever = 0;
int f_all = 0;
int f_verbose = 0;
int f_mode = 0;
int f_cmddump = 0;
int f_policy = 0;
int f_hexdump = 0;
int f_tflag = 0;
int f_notreally = 0;
int f_withports = 0;
#ifdef HAVE_POLICY_FWD
int f_rfcmode = 1;
#define RK_OPTS "rk"
#else
int f_rkwarn = 0;
#define RK_OPTS ""
static void rkwarn(void);
static void
rkwarn(void)
{
if (!f_rkwarn) {
f_rkwarn = 1;
printf("warning: -r and -k options are not supported in this environment\n");
}
}
#endif
static time_t thiszone;
void
usage(int only_version)
{
printf("setkey @(#) %s (%s)\n", TOP_PACKAGE_STRING, TOP_PACKAGE_URL);
if (! only_version) {
printf("usage: setkey [-v" RK_OPTS "] file ...\n");
printf(" setkey [-nv" RK_OPTS "] -c\n");
printf(" setkey [-nv" RK_OPTS "] -f filename\n");
printf(" setkey [-Palpv" RK_OPTS "] -D\n");
printf(" setkey [-Pv] -F\n");
printf(" setkey [-H] -x\n");
printf(" setkey [-V] [-h]\n");
}
exit(1);
}
int
main(argc, argv)
int argc;
char **argv;
{
FILE *fp = stdin;
int c;
if (argc == 1) {
usage(0);
/* NOTREACHED */
}
thiszone = gmt2local(0);
while ((c = getopt(argc, argv, "acdf:HlnvxDFPphVrk?")) != -1) {
switch (c) {
case 'c':
f_mode = MODE_STDIN;
#ifdef HAVE_READLINE
/* disable filename completion */
rl_bind_key('\t', rl_insert);
#endif
break;
case 'f':
f_mode = MODE_SCRIPT;
if ((fp = fopen(optarg, "r")) == NULL) {
err(1, "fopen");
/*NOTREACHED*/
}
break;
case 'D':
f_mode = MODE_CMDDUMP;
break;
case 'F':
f_mode = MODE_CMDFLUSH;
break;
case 'a':
f_all = 1;
break;
case 'l':
f_forever = 1;
break;
case 'n':
f_notreally = 1;
break;
#ifdef __NetBSD__
case 'h':
#endif
case 'H':
f_hexdump = 1;
break;
case 'x':
f_mode = MODE_PROMISC;
f_tflag++;
break;
case 'P':
f_policy = 1;
break;
case 'p':
f_withports = 1;
break;
case 'v':
f_verbose = 1;
break;
case 'r':
#ifdef HAVE_POLICY_FWD
f_rfcmode = 1;
#else
rkwarn();
#endif
break;
case 'k':
#ifdef HAVE_POLICY_FWD
f_rfcmode = 0;
#else
rkwarn();
#endif
break;
case 'V':
usage(1);
break;
/*NOTREACHED*/
#ifndef __NetBSD__
case 'h':
#endif
case '?':
default:
usage(0);
/*NOTREACHED*/
}
}
argc -= optind;
argv += optind;
if (argc > 0) {
while (argc--)
if (fileproc(*argv++) < 0) {
err(1, "%s", argv[-1]);
/*NOTREACHED*/
}
exit(0);
}
so = pfkey_open();
if (so < 0) {
perror("pfkey_open");
exit(1);
}
switch (f_mode) {
case MODE_CMDDUMP:
sendkeyshort(f_policy ? SADB_X_SPDDUMP : SADB_DUMP);
break;
case MODE_CMDFLUSH:
sendkeyshort(f_policy ? SADB_X_SPDFLUSH: SADB_FLUSH);
break;
case MODE_SCRIPT:
if (get_supported() < 0) {
errx(1, "%s", ipsec_strerror());
/*NOTREACHED*/
}
if (parse(&fp))
exit (1);
break;
case MODE_STDIN:
if (get_supported() < 0) {
errx(1, "%s", ipsec_strerror());
/*NOTREACHED*/
}
stdin_loop();
break;
case MODE_PROMISC:
promisc();
/*NOTREACHED*/
default:
usage(0);
/*NOTREACHED*/
}
exit(0);
}
int
get_supported()
{
if (pfkey_send_register(so, SADB_SATYPE_UNSPEC) < 0)
return -1;
if (pfkey_recv_register(so) < 0)
return -1;
return (0);
}
void
stdin_loop()
{
char line[1024], *semicolon, *comment;
size_t linelen = 0;
memset (line, 0, sizeof(line));
parse_init();
while (1) {
#ifdef HAVE_READLINE
char *rbuf;
rbuf = readline ("");
if (! rbuf)
break;
#else
char rbuf[1024];
rbuf[0] = '\0';
fgets (rbuf, sizeof(rbuf), stdin);
if (!rbuf[0])
break;
if (rbuf[strlen(rbuf)-1] == '\n')
rbuf[strlen(rbuf)-1] = '\0';
#endif
comment = strchr(rbuf, '#');
if (comment)
*comment = '\0';
if (!rbuf[0])
continue;
linelen += snprintf (&line[linelen], sizeof(line) - linelen,
"%s%s", linelen > 0 ? " " : "", rbuf);
semicolon = strchr(line, ';');
while (semicolon) {
char saved_char = *++semicolon;
*semicolon = '\0';
#ifdef HAVE_READLINE
add_history (line);
#endif
#ifdef HAVE_PFKEY_POLICY_PRIORITY
last_msg_type = -1; /* invalid message type */
#endif
parse_string (line);
if (exit_now)
return;
if (saved_char) {
*semicolon = saved_char;
linelen = strlen (semicolon);
memmove (line, semicolon, linelen + 1);
semicolon = strchr(line, ';');
}
else {
semicolon = NULL;
linelen = 0;
}
}
}
}
void
sendkeyshort(type)
u_int type;
{
struct sadb_msg msg;
msg.sadb_msg_version = PF_KEY_V2;
msg.sadb_msg_type = type;
msg.sadb_msg_errno = 0;
msg.sadb_msg_satype = SADB_SATYPE_UNSPEC;
msg.sadb_msg_len = PFKEY_UNIT64(sizeof(msg));
msg.sadb_msg_reserved = 0;
msg.sadb_msg_seq = 0;
msg.sadb_msg_pid = getpid();
sendkeymsg((char *)&msg, sizeof(msg));
return;
}
void
promisc()
{
struct sadb_msg msg;
u_char rbuf[1024 * 32]; /* XXX: Enough ? Should I do MSG_PEEK ? */
ssize_t l;
msg.sadb_msg_version = PF_KEY_V2;
msg.sadb_msg_type = SADB_X_PROMISC;
msg.sadb_msg_errno = 0;
msg.sadb_msg_satype = 1;
msg.sadb_msg_len = PFKEY_UNIT64(sizeof(msg));
msg.sadb_msg_reserved = 0;
msg.sadb_msg_seq = 0;
msg.sadb_msg_pid = getpid();
if ((l = send(so, &msg, sizeof(msg), 0)) < 0) {
err(1, "send");
/*NOTREACHED*/
}
while (1) {
struct sadb_msg *base;
if ((l = recv(so, rbuf, sizeof(*base), MSG_PEEK)) < 0) {
err(1, "recv");
/*NOTREACHED*/
}
if (l != sizeof(*base))
continue;
base = (struct sadb_msg *)rbuf;
if ((l = recv(so, rbuf, PFKEY_UNUNIT64(base->sadb_msg_len),
0)) < 0) {
err(1, "recv");
/*NOTREACHED*/
}
printdate();
if (f_hexdump) {
int i;
for (i = 0; i < l; i++) {
if (i % 16 == 0)
printf("%08x: ", i);
printf("%02x ", rbuf[i] & 0xff);
if (i % 16 == 15)
printf("\n");
}
if (l % 16)
printf("\n");
}
/* adjust base pointer for promisc mode */
if (base->sadb_msg_type == SADB_X_PROMISC) {
if ((ssize_t)sizeof(*base) < l)
base++;
else
base = NULL;
}
if (base) {
kdebug_sadb(base);
printf("\n");
fflush(stdout);
}
}
}
int
sendkeymsg(buf, len)
char *buf;
size_t len;
{
u_char rbuf[1024 * 32]; /* XXX: Enough ? Should I do MSG_PEEK ? */
ssize_t l;
struct sadb_msg *msg;
if (f_notreally) {
goto end;
}
{
struct timeval tv;
tv.tv_sec = 1;
tv.tv_usec = 0;
if (setsockopt(so, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv)) < 0) {
perror("setsockopt");
goto end;
}
}
if (f_forever)
shortdump_hdr();
again:
if (f_verbose) {
kdebug_sadb((struct sadb_msg *)buf);
printf("\n");
}
if (f_hexdump) {
int i;
for (i = 0; i < len; i++) {
if (i % 16 == 0)
printf("%08x: ", i);
printf("%02x ", buf[i] & 0xff);
if (i % 16 == 15)
printf("\n");
}
if (len % 16)
printf("\n");
}
if ((l = send(so, buf, len, 0)) < 0) {
perror("send");
goto end;
}
msg = (struct sadb_msg *)rbuf;
do {
if ((l = recv(so, rbuf, sizeof(rbuf), 0)) < 0) {
perror("recv");
goto end;
}
if (PFKEY_UNUNIT64(msg->sadb_msg_len) != l) {
warnx("invalid keymsg length");
break;
}
if (f_verbose) {
kdebug_sadb((struct sadb_msg *)rbuf);
printf("\n");
}
if (postproc(msg, l) < 0)
break;
} while (msg->sadb_msg_errno || msg->sadb_msg_seq);
if (f_forever) {
fflush(stdout);
sleep(1);
goto again;
}
end:
return (0);
}
int
postproc(msg, len)
struct sadb_msg *msg;
int len;
{
#ifdef HAVE_PFKEY_POLICY_PRIORITY
static int priority_support_check = 0;
#endif
if (msg->sadb_msg_errno != 0) {
char inf[80];
const char *errmsg = NULL;
if (f_mode == MODE_SCRIPT)
snprintf(inf, sizeof(inf), "The result of line %d: ", lineno);
else
inf[0] = '\0';
switch (msg->sadb_msg_errno) {
case ENOENT:
switch (msg->sadb_msg_type) {
case SADB_DELETE:
case SADB_GET:
case SADB_X_SPDDELETE:
errmsg = "No entry";
break;
case SADB_DUMP:
errmsg = "No SAD entries";
break;
case SADB_X_SPDDUMP:
errmsg = "No SPD entries";
break;
}
break;
default:
errmsg = strerror(msg->sadb_msg_errno);
}
printf("%s%s.\n", inf, errmsg);
return (-1);
}
switch (msg->sadb_msg_type) {
case SADB_GET:
if (f_withports)
pfkey_sadump_withports(msg);
else
pfkey_sadump(msg);
break;
case SADB_DUMP:
/* filter out DEAD SAs */
if (!f_all) {
caddr_t mhp[SADB_EXT_MAX + 1];
struct sadb_sa *sa;
pfkey_align(msg, mhp);
pfkey_check(mhp);
if ((sa = (struct sadb_sa *)mhp[SADB_EXT_SA]) != NULL) {
if (sa->sadb_sa_state == SADB_SASTATE_DEAD)
break;
}
}
if (f_forever) {
/* TODO: f_withports */
shortdump(msg);
} else {
if (f_withports)
pfkey_sadump_withports(msg);
else
pfkey_sadump(msg);
}
msg = (struct sadb_msg *)((caddr_t)msg +
PFKEY_UNUNIT64(msg->sadb_msg_len));
if (f_verbose) {
kdebug_sadb((struct sadb_msg *)msg);
printf("\n");
}
break;
case SADB_X_SPDGET:
if (f_withports)
pfkey_spdump_withports(msg);
else
pfkey_spdump(msg);
break;
case SADB_X_SPDDUMP:
if (f_withports)
pfkey_spdump_withports(msg);
else
pfkey_spdump(msg);
if (msg->sadb_msg_seq == 0) break;
msg = (struct sadb_msg *)((caddr_t)msg +
PFKEY_UNUNIT64(msg->sadb_msg_len));
if (f_verbose) {
kdebug_sadb((struct sadb_msg *)msg);
printf("\n");
}
break;
#ifdef HAVE_PFKEY_POLICY_PRIORITY
case SADB_X_SPDADD:
if (last_msg_type == SADB_X_SPDADD && last_priority != 0 &&
msg->sadb_msg_pid == getpid() && !priority_support_check) {
priority_support_check = 1;
if (!verifypriority(msg))
printf ("WARNING: Kernel does not support policy priorities\n");
}
break;
#endif
}
return (0);
}
#ifdef HAVE_PFKEY_POLICY_PRIORITY
int
verifypriority(m)
struct sadb_msg *m;
{
caddr_t mhp[SADB_EXT_MAX + 1];
struct sadb_x_policy *xpl;
/* check pfkey message. */
if (pfkey_align(m, mhp)) {
printf("(%s\n", ipsec_strerror());
return 0;
}
if (pfkey_check(mhp)) {
printf("%s\n", ipsec_strerror());
return 0;
}
xpl = (struct sadb_x_policy *) mhp[SADB_X_EXT_POLICY];
if (xpl == NULL) {
printf("no X_POLICY extension.\n");
return 0;
}
/* now make sure they match */
if (last_priority != xpl->sadb_x_policy_priority)
return 0;
return 1;
}
#endif
int
fileproc(filename)
const char *filename;
{
int fd;
ssize_t len, l;
u_char *p, *ep;
struct sadb_msg *msg;
u_char rbuf[1024 * 32]; /* XXX: Enough ? Should I do MSG_PEEK ? */
fd = open(filename, O_RDONLY);
if (fd < 0)
return -1;
l = 0;
while (1) {
len = read(fd, rbuf + l, sizeof(rbuf) - l);
if (len < 0) {
close(fd);
return -1;
} else if (len == 0)
break;
l += len;
}
if (l < sizeof(struct sadb_msg)) {
close(fd);
errno = EINVAL;
return -1;
}
close(fd);
p = rbuf;
ep = rbuf + l;
while (p < ep) {
msg = (struct sadb_msg *)p;
len = PFKEY_UNUNIT64(msg->sadb_msg_len);
postproc(msg, len);
p += len;
}
return (0);
}
/*------------------------------------------------------------*/
static const char *satype[] = {
NULL, NULL, "ah", "esp"
};
static const char *sastate[] = {
"L", "M", "D", "d"
};
static const char *ipproto[] = {
/*0*/ "ip", "icmp", "igmp", "ggp", "ip4",
NULL, "tcp", NULL, "egp", NULL,
/*10*/ NULL, NULL, NULL, NULL, NULL,
NULL, NULL, "udp", NULL, NULL,
/*20*/ NULL, NULL, "idp", NULL, NULL,
NULL, NULL, NULL, NULL, "tp",
/*30*/ NULL, NULL, NULL, NULL, NULL,
NULL, NULL, NULL, NULL, NULL,
/*40*/ NULL, "ip6", NULL, "rt6", "frag6",
NULL, "rsvp", "gre", NULL, NULL,
/*50*/ "esp", "ah", NULL, NULL, NULL,
NULL, NULL, NULL, "icmp6", "none",
/*60*/ "dst6",
};
#define STR_OR_ID(x, tab) \
(((x) < sizeof(tab)/sizeof(tab[0]) && tab[(x)]) ? tab[(x)] : numstr(x))
const char *
numstr(x)
int x;
{
static char buf[20];
snprintf(buf, sizeof(buf), "#%d", x);
return buf;
}
void
shortdump_hdr()
{
printf("%-4s %-3s %-1s %-8s %-7s %s -> %s\n",
"time", "p", "s", "spi", "ltime", "src", "dst");
}
void
shortdump(msg)
struct sadb_msg *msg;
{
caddr_t mhp[SADB_EXT_MAX + 1];
char buf[NI_MAXHOST], pbuf[NI_MAXSERV];
struct sadb_sa *sa;
struct sadb_address *saddr;
struct sadb_lifetime *lts, *lth, *ltc;
struct sockaddr *s;
u_int t;
time_t cur = time(0);
pfkey_align(msg, mhp);
pfkey_check(mhp);
printf("%02lu%02lu", (u_long)(cur % 3600) / 60, (u_long)(cur % 60));
printf(" %-3s", STR_OR_ID(msg->sadb_msg_satype, satype));
if ((sa = (struct sadb_sa *)mhp[SADB_EXT_SA]) != NULL) {
printf(" %-1s", STR_OR_ID(sa->sadb_sa_state, sastate));
printf(" %08x", (u_int32_t)ntohl(sa->sadb_sa_spi));
} else
printf("%-1s %-8s", "?", "?");
lts = (struct sadb_lifetime *)mhp[SADB_EXT_LIFETIME_SOFT];
lth = (struct sadb_lifetime *)mhp[SADB_EXT_LIFETIME_HARD];
ltc = (struct sadb_lifetime *)mhp[SADB_EXT_LIFETIME_CURRENT];
if (lts && lth && ltc) {
if (ltc->sadb_lifetime_addtime == 0)
t = (u_long)0;
else
t = (u_long)(cur - ltc->sadb_lifetime_addtime);
if (t >= 1000)
strlcpy(buf, " big/", sizeof(buf));
else
snprintf(buf, sizeof(buf), " %3lu/", (u_long)t);
printf("%s", buf);
t = (u_long)lth->sadb_lifetime_addtime;
if (t >= 1000)
strlcpy(buf, "big", sizeof(buf));
else
snprintf(buf, sizeof(buf), "%-3lu", (u_long)t);
printf("%s", buf);
} else
printf(" ??\?/???"); /* backslash to avoid trigraph ??/ */
printf(" ");
if ((saddr = (struct sadb_address *)mhp[SADB_EXT_ADDRESS_SRC]) != NULL) {
if (saddr->sadb_address_proto)
printf("%s ", STR_OR_ID(saddr->sadb_address_proto, ipproto));
s = (struct sockaddr *)(saddr + 1);
getnameinfo(s, sysdep_sa_len(s), buf, sizeof(buf),
pbuf, sizeof(pbuf), NI_NUMERICHOST|NI_NUMERICSERV);
if (strcmp(pbuf, "0") != 0)
printf("%s[%s]", buf, pbuf);
else
printf("%s", buf);
} else
printf("?");
printf(" -> ");
if ((saddr = (struct sadb_address *)mhp[SADB_EXT_ADDRESS_DST]) != NULL) {
if (saddr->sadb_address_proto)
printf("%s ", STR_OR_ID(saddr->sadb_address_proto, ipproto));
s = (struct sockaddr *)(saddr + 1);
getnameinfo(s, sysdep_sa_len(s), buf, sizeof(buf),
pbuf, sizeof(pbuf), NI_NUMERICHOST|NI_NUMERICSERV);
if (strcmp(pbuf, "0") != 0)
printf("%s[%s]", buf, pbuf);
else
printf("%s", buf);
} else
printf("?");
printf("\n");
}
/* From: tcpdump(1):gmt2local.c and util.c */
/*
* Print the timestamp
*/
static void
printdate()
{
struct timeval tp;
int s;
if (gettimeofday(&tp, NULL) == -1) {
perror("gettimeofday");
return;
}
if (f_tflag == 1) {
/* Default */
s = (tp.tv_sec + thiszone ) % 86400;
(void)printf("%02d:%02d:%02d.%06u ",
s / 3600, (s % 3600) / 60, s % 60, (u_int32_t)tp.tv_usec);
} else if (f_tflag > 1) {
/* Unix timeval style */
(void)printf("%u.%06u ",
(u_int32_t)tp.tv_sec, (u_int32_t)tp.tv_usec);
}
printf("\n");
}
/*
* Returns the difference between gmt and local time in seconds.
* Use gmtime() and localtime() to keep things simple.
*/
int32_t
gmt2local(time_t t)
{
register int dt, dir;
register struct tm *gmt, *loc;
struct tm sgmt;
if (t == 0)
t = time(NULL);
gmt = &sgmt;
*gmt = *gmtime(&t);
loc = localtime(&t);
dt = (loc->tm_hour - gmt->tm_hour) * 60 * 60 +
(loc->tm_min - gmt->tm_min) * 60;
/*
* If the year or julian day is different, we span 00:00 GMT
* and must add or subtract a day. Check the year first to
* avoid problems when the julian day wraps.
*/
dir = loc->tm_year - gmt->tm_year;
if (dir == 0)
dir = loc->tm_yday - gmt->tm_yday;
dt += dir * 24 * 60 * 60;
return (dt);
}