summaryrefslogtreecommitdiff
path: root/sbin/ipsecctl/ipsecctl.c
diff options
context:
space:
mode:
Diffstat (limited to 'sbin/ipsecctl/ipsecctl.c')
-rw-r--r--sbin/ipsecctl/ipsecctl.c414
1 files changed, 414 insertions, 0 deletions
diff --git a/sbin/ipsecctl/ipsecctl.c b/sbin/ipsecctl/ipsecctl.c
new file mode 100644
index 00000000000..25c15585075
--- /dev/null
+++ b/sbin/ipsecctl/ipsecctl.c
@@ -0,0 +1,414 @@
+/* $Id: ipsecctl.c,v 1.1 2005/04/04 22:19:50 hshoexer Exp $ */
+/*
+ * Copyright (c) 2004, 2005 Hans-Joerg Hoexer <hshoexer@openbsd.org>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/socket.h>
+#include <sys/sysctl.h>
+#include <sys/queue.h>
+#include <sys/stat.h>
+#include <net/pfkeyv2.h>
+#include <net/route.h>
+#include <netinet/in.h>
+#include <netinet/ip_ipsp.h>
+#include <arpa/inet.h>
+
+#include <err.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <strings.h>
+#include <unistd.h>
+
+#include "ipsecctl.h"
+
+int ipsecctl_rules(char *, int);
+FILE *ipsecctl_fopen(const char *, const char *);
+int ipsecctl_commit(struct ipsecctl *);
+int ipsecctl_add_rule(struct ipsecctl *, struct ipsec_rule *);
+void ipsecctl_print_addr(struct ipsec_addr *);
+void ipsecctl_print_rule(struct ipsec_rule *, int);
+int ipsecctl_flush(int);
+void ipsecctl_get_rules(struct ipsecctl *);
+void ipsecctl_show(int);
+void usage(void);
+
+const char *infile; /* Used by parse.y */
+
+int
+ipsecctl_rules(char *filename, int opts)
+{
+ FILE *fin;
+ struct ipsecctl ipsec;
+ int error = 0;
+
+ memset(&ipsec, 0, sizeof(ipsec));
+ ipsec.opts = opts;
+ TAILQ_INIT(&ipsec.rule_queue);
+
+ if (strcmp(filename, "-") == 0) {
+ fin = stdin;
+ infile = "stdin";
+ } else {
+ if ((fin = ipsecctl_fopen(filename, "r")) == NULL) {
+ warn("%s", filename);
+ return 1;
+ }
+ infile = filename;
+ }
+
+ if (parse_rules(fin, &ipsec) < 0) {
+ warnx("Syntax error in config file: ipsec rules not loaded");
+ error = 1;
+ }
+ if (((opts & IPSECCTL_OPT_NOACTION) == 0) && (error == 0))
+ if (ipsecctl_commit(&ipsec))
+ err(1, NULL);
+
+ return error;
+}
+
+FILE *
+ipsecctl_fopen(const char *name, const char *mode)
+{
+ struct stat st;
+ FILE *fp;
+
+ fp = fopen(name, mode);
+ if (fp == NULL)
+ return NULL;
+
+ if (fstat(fileno(fp), &st)) {
+ fclose(fp);
+ return NULL;
+ }
+ if (S_ISDIR(st.st_mode)) {
+ fclose(fp);
+ errno = EISDIR;
+ return NULL;
+ }
+ return fp;
+}
+
+int
+ipsecctl_commit(struct ipsecctl *ipsec)
+{
+ struct ipsec_rule *rp;
+
+ if (pfkey_init() == -1)
+ errx(1, "failed to open PF_KEY socket");
+
+ while ((rp = TAILQ_FIRST(&ipsec->rule_queue))) {
+ TAILQ_REMOVE(&ipsec->rule_queue, rp, entries);
+
+ if (pfkey_ipsec_establish(rp) == -1)
+ warnx("failed to add rule %d", rp->nr);
+
+ free(rp->src);
+ free(rp->dst);
+ free(rp->peer);
+ if (rp->auth.srcid)
+ free(rp->auth.srcid);
+ if (rp->auth.dstid)
+ free(rp->auth.dstid);
+ free(rp);
+ }
+
+ return 0;
+}
+
+int
+ipsecctl_add_rule(struct ipsecctl *ipsec, struct ipsec_rule *r)
+{
+ TAILQ_INSERT_TAIL(&ipsec->rule_queue, r, entries);
+
+ if ((ipsec->opts & IPSECCTL_OPT_VERBOSE) && !(ipsec->opts &
+ IPSECCTL_OPT_SHOW))
+ ipsecctl_print_rule(r, ipsec->opts & IPSECCTL_OPT_VERBOSE2);
+
+ return 0;
+}
+
+void
+ipsecctl_print_addr(struct ipsec_addr *ipa)
+{
+ u_int32_t mask;
+ char buf[48];
+
+ if (ipa == NULL) {
+ printf("?");
+ return;
+ }
+ if (inet_ntop(ipa->af, &ipa->v4, buf, sizeof(buf)) == NULL)
+ printf("?");
+ else
+ printf("%s", buf);
+
+ if (ipa->v4mask.mask32 != 0xffffffff) {
+ mask = ntohl(ipa->v4mask.mask32);
+ if (mask == 0)
+ printf("/0");
+ else
+ printf("/%d", 32 - ffs((int) mask) + 1);
+ }
+}
+
+void
+ipsecctl_print_rule(struct ipsec_rule *r, int verbose)
+{
+ static const char *direction[] = {"?", "in", "out"};
+ static const char *proto[] = {"?", "esp", "ah"};
+ static const char *auth[] = {"?", "psk", "rsa"};
+
+ if (verbose)
+ printf("@%d ", r->nr);
+
+ printf("flow %s %s", proto[r->proto], direction[r->direction]);
+ printf(" from ");
+ ipsecctl_print_addr(r->src);
+ printf(" to ");
+ ipsecctl_print_addr(r->dst);
+ printf(" peer ");
+ ipsecctl_print_addr(r->peer);
+
+ if (r->auth.srcid)
+ printf(" srcid %s", r->auth.srcid);
+ if (r->auth.dstid)
+ printf(" dstid %s", r->auth.dstid);
+
+ if (r->auth.type > 0)
+ printf(" %s", auth[r->auth.type]);
+
+ printf("\n");
+}
+
+int
+ipsecctl_flush(int opts)
+{
+ if (opts & IPSECCTL_OPT_NOACTION)
+ return 0;
+
+ if (pfkey_init() == -1)
+ errx(1, "failed to open PF_KEY socket");
+
+ pfkey_ipsec_flush();
+
+ return 0;
+}
+
+void
+ipsecctl_get_rules(struct ipsecctl *ipsec)
+{
+ struct ipsec_policy *ipo;
+ struct ipsec_rule *rule;
+ int mib[4];
+ size_t need;
+ char *buf, *lim, *next;
+
+ mib[0] = CTL_NET;
+ mib[1] = PF_KEY;
+ mib[2] = PF_KEY_V2;
+ mib[3] = NET_KEY_SPD_DUMP;
+
+ if (sysctl(mib, 4, NULL, &need, NULL, 0) == -1)
+ err(1, "sysctl");
+
+ if (need == 0)
+ return;
+ if ((buf = malloc(need)) == NULL)
+ err(1, "malloc");
+ if (sysctl(mib, 4, buf, &need, NULL, 0) == -1)
+ err(1, "sysctl");
+
+ lim = buf + need;
+ for (next = buf; next < lim; next += sizeof(struct ipsec_policy)) {
+ ipo = (struct ipsec_policy *)next;
+
+ /*
+ * We only want static policies and are not interrested in
+ * policies attached to sockets.
+ */
+ if (ipo->ipo_flags & IPSP_POLICY_SOCKET)
+ continue;
+
+ rule = calloc(1, sizeof(struct ipsec_rule));
+ if (rule == NULL)
+ err(1, "malloc");
+ rule->nr = ipsec->rule_nr++;
+
+ /* Source and destination. */
+ if (ipo->ipo_addr.sen_type == SENT_IP4) {
+ rule->src = calloc(1, sizeof(struct ipsec_addr));
+ if (rule->src == NULL)
+ err(1, "calloc");
+ rule->src->af = AF_INET;
+
+ bcopy(&ipo->ipo_addr.sen_ip_src.s_addr, &rule->src->v4,
+ sizeof(struct in_addr));
+ bcopy(&ipo->ipo_mask.sen_ip_src.s_addr,
+ &rule->src->v4mask.mask, sizeof(struct in_addr));
+
+ rule->dst = calloc(1, sizeof(struct ipsec_addr));
+ if (rule->dst == NULL)
+ err(1, "calloc");
+ rule->dst->af = AF_INET;
+
+ bcopy(&ipo->ipo_addr.sen_ip_dst.s_addr, &rule->dst->v4,
+ sizeof(struct in_addr));
+ bcopy(&ipo->ipo_mask.sen_ip_dst.s_addr,
+ &rule->dst->v4mask.mask, sizeof(struct in_addr));
+ } else
+ warnx("unsupported encapsulation policy type %d",
+ ipo->ipo_addr.sen_type);
+
+ /* IPsec gateway. */
+ if (ipo->ipo_dst.sa.sa_family == AF_INET) {
+ rule->peer = calloc(1, sizeof(struct ipsec_addr));
+ if (rule->peer == NULL)
+ err(1, "calloc");
+ rule->peer->af = AF_INET;
+
+ bcopy(&((struct sockaddr_in *)&ipo->ipo_dst.sa)->sin_addr,
+ &rule->peer->v4, sizeof(struct in_addr));
+
+ /* No netmask for peer. */
+ memset(&rule->peer->v4mask, 0xff, sizeof(u_int32_t));
+
+ if (ipo->ipo_sproto == IPPROTO_ESP)
+ rule->proto = IPSEC_ESP;
+ else if (ipo->ipo_sproto == IPPROTO_AH)
+ rule->proto = IPSEC_AH;
+ else {
+ rule->proto = PROTO_UNKNWON;
+ warnx("unsupported protocol %d",
+ ipo->ipo_sproto);
+ }
+
+ if (ipo->ipo_addr.sen_direction == IPSP_DIRECTION_OUT)
+ rule->direction = IPSEC_OUT;
+ else if (ipo->ipo_addr.sen_direction == IPSP_DIRECTION_IN)
+ rule->direction = IPSEC_IN;
+ else {
+ rule->direction = DIRECTION_UNKNOWN;
+ warnx("bogus direction %d",
+ ipo->ipo_addr.sen_direction);
+ }
+ } else
+ warnx("unsupported address family %d",
+ ipo->ipo_dst.sa.sa_family);
+
+ ipsecctl_add_rule(ipsec, rule);
+ }
+}
+
+void
+ipsecctl_show(int opts)
+{
+ struct ipsecctl ipsec;
+ struct ipsec_rule *rp;
+
+ memset(&ipsec, 0, sizeof(ipsec));
+ ipsec.opts = opts;
+ TAILQ_INIT(&ipsec.rule_queue);
+
+ ipsecctl_get_rules(&ipsec);
+
+ while ((rp = TAILQ_FIRST(&ipsec.rule_queue))) {
+ TAILQ_REMOVE(&ipsec.rule_queue, rp, entries);
+
+ ipsecctl_print_rule(rp, ipsec.opts & IPSECCTL_OPT_VERBOSE2);
+
+ free(rp->src);
+ free(rp->dst);
+ free(rp->peer);
+ free(rp);
+ }
+
+ return;
+}
+
+__dead void
+usage(void)
+{
+ extern char *__progname;
+
+ fprintf(stderr, "usage: %s [-Fhnvs] [-f file]\n", __progname);
+ exit(1);
+}
+
+int
+main(int argc, char *argv[])
+{
+ int error = 0;
+ int ch;
+ int opts = 0;
+ char *rulesopt = NULL;
+
+ if (argc < 2)
+ usage();
+
+ while ((ch = getopt(argc, argv, "f:Fhnvs")) != -1) {
+ switch (ch) {
+ case 'f':
+ rulesopt = optarg;
+ break;
+
+ case 'F':
+ opts |= IPSECCTL_OPT_FLUSH;
+ break;
+
+ case 'n':
+ opts |= IPSECCTL_OPT_NOACTION;
+ break;
+
+ case 'v':
+ if (opts & IPSECCTL_OPT_VERBOSE)
+ opts |= IPSECCTL_OPT_VERBOSE2;
+ opts |= IPSECCTL_OPT_VERBOSE;
+ break;
+
+ case 's':
+ opts |= IPSECCTL_OPT_SHOW;
+ break;
+
+ case 'h':
+ /* FALLTHROUGH */
+ default:
+ usage();
+ /* NOTREACHED */
+ }
+ }
+
+ if (argc != optind) {
+ warnx("unknown command line argument: %s ...", argv[optind]);
+ usage();
+ /* NOTREACHED */
+ }
+ if (opts & IPSECCTL_OPT_FLUSH)
+ if (ipsecctl_flush(opts))
+ error = 1;
+
+ if (rulesopt != NULL)
+ if (ipsecctl_rules(rulesopt, opts))
+ error = 1;
+
+ if (opts & IPSECCTL_OPT_SHOW)
+ ipsecctl_show(opts);
+
+ exit(error);
+}