xref: /openbsd-src/sbin/ipsecctl/ipsecctl.c (revision 4ecd42c0f02778a2fd2482a7dd1be802d96dc9de)
1*4ecd42c0Syasuoka /*	$OpenBSD: ipsecctl.c,v 1.88 2024/02/06 05:39:28 yasuoka Exp $	*/
2f484f2cfShshoexer /*
3f484f2cfShshoexer  * Copyright (c) 2004, 2005 Hans-Joerg Hoexer <hshoexer@openbsd.org>
4f484f2cfShshoexer  *
5f484f2cfShshoexer  * Permission to use, copy, modify, and distribute this software for any
6f484f2cfShshoexer  * purpose with or without fee is hereby granted, provided that the above
7f484f2cfShshoexer  * copyright notice and this permission notice appear in all copies.
8f484f2cfShshoexer  *
9f484f2cfShshoexer  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10f484f2cfShshoexer  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11f484f2cfShshoexer  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12f484f2cfShshoexer  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13f484f2cfShshoexer  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14f484f2cfShshoexer  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15f484f2cfShshoexer  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16f484f2cfShshoexer  */
17f484f2cfShshoexer 
18f484f2cfShshoexer #include <sys/socket.h>
19f484f2cfShshoexer #include <sys/sysctl.h>
20f484f2cfShshoexer #include <sys/queue.h>
21f484f2cfShshoexer #include <sys/stat.h>
22f484f2cfShshoexer #include <net/pfkeyv2.h>
23f484f2cfShshoexer #include <net/route.h>
24f484f2cfShshoexer #include <netinet/in.h>
25f484f2cfShshoexer #include <netinet/ip_ipsp.h>
26f484f2cfShshoexer #include <arpa/inet.h>
27f484f2cfShshoexer 
28aa9f7a4dSmpi #include <assert.h>
29f484f2cfShshoexer #include <err.h>
30f484f2cfShshoexer #include <errno.h>
31f484f2cfShshoexer #include <fcntl.h>
32712e78baShshoexer #include <netdb.h>
33f484f2cfShshoexer #include <stdio.h>
34f484f2cfShshoexer #include <stdlib.h>
354a9adf07Shshoexer #include <string.h>
36f484f2cfShshoexer #include <unistd.h>
37f484f2cfShshoexer 
38f484f2cfShshoexer #include "ipsecctl.h"
391edc1b9aShshoexer #include "pfkey.h"
40f484f2cfShshoexer 
41f484f2cfShshoexer int		 ipsecctl_rules(char *, int);
42f484f2cfShshoexer FILE		*ipsecctl_fopen(const char *, const char *);
436d01698dShshoexer int		 ipsecctl_commit(int, struct ipsecctl *);
44f484f2cfShshoexer int		 ipsecctl_add_rule(struct ipsecctl *, struct ipsec_rule *);
4509203018Shshoexer void		 ipsecctl_free_rule(struct ipsec_rule *);
46aa9f7a4dSmpi int		 ipsecctl_merge_rules(struct ipsec_rule *, struct ipsec_rule *);
47aa9f7a4dSmpi int		 ipsecctl_cmp_ident(struct ipsec_rule *, struct ipsec_rule *);
48aa9f7a4dSmpi int		 ipsecctl_rule_matchsrc(struct ipsec_rule *,
49aa9f7a4dSmpi 		     struct ipsec_addr_wrap *);
50aa9f7a4dSmpi int		 ipsecctl_rule_matchdst(struct ipsec_rule *,
51aa9f7a4dSmpi 		     struct ipsec_addr_wrap *);
5291f765ddShshoexer void		 ipsecctl_print_addr(struct ipsec_addr_wrap *);
539182219dSmarkus void		 ipsecctl_print_proto(u_int8_t);
5457f58d0dSnaddy void		 ipsecctl_print_port(u_int16_t, const char *);
55110db5a9Shshoexer void		 ipsecctl_print_key(struct ipsec_key *);
56110db5a9Shshoexer void		 ipsecctl_print_flow(struct ipsec_rule *, int);
57110db5a9Shshoexer void		 ipsecctl_print_sa(struct ipsec_rule *, int);
58a6bcba92Sbluhm void		 ipsecctl_print_sabundle(struct ipsec_rule *, int);
59f484f2cfShshoexer int		 ipsecctl_flush(int);
60202aab43Stobhe char		*ipsecctl_get_rules(struct ipsecctl *, size_t *);
61202aab43Stobhe void		 ipsecctl_parse_rules(struct ipsecctl *, char *, size_t);
623dd59ad2Shshoexer void		 ipsecctl_print_title(char *);
63202aab43Stobhe void		 ipsecctl_show(int);
648a87fca6Smsf int		 ipsecctl_monitor(int);
65f484f2cfShshoexer void		 usage(void);
663dd59ad2Shshoexer const char	*ipsecctl_lookup_option(char *, const char **);
67eaa84e12Skn static int	 unmask(struct ipsec_addr *);
68895bd039Smarkus int		 sacompare(const void *, const void *);
69f484f2cfShshoexer 
703dd59ad2Shshoexer const char	*showopt;
71f8738d8bShenning char		*isakmpd_fifo = "/var/run/isakmpd.fifo";
723dd59ad2Shshoexer 
733dd59ad2Shshoexer int		 first_title = 1;
743dd59ad2Shshoexer 
753dd59ad2Shshoexer static const char *showopt_list[] = {
763dd59ad2Shshoexer 	"flow", "sa", "all", NULL
773dd59ad2Shshoexer };
78f484f2cfShshoexer 
79110db5a9Shshoexer static const char *direction[] = {"?", "in", "out"};
80110db5a9Shshoexer static const char *flowtype[] = {"?", "use", "acquire", "require", "deny",
81110db5a9Shshoexer     "bypass", "dontacq"};
829182219dSmarkus static const char *satype[] = {"?", "esp", "ah", "ipcomp", "tcpmd5", "ipip"};
83a38d220fShshoexer static const char *tmode[] = {"?", "transport", "tunnel"};
84110db5a9Shshoexer static const char *auth[] = {"?", "psk", "rsa"};
85110db5a9Shshoexer 
86895bd039Smarkus struct sad {
87895bd039Smarkus 	struct sadb_msg	*sad_msg;
88895bd039Smarkus 	u_int32_t	 sad_spi;
89895bd039Smarkus };
90895bd039Smarkus 
91895bd039Smarkus int
sacompare(const void * va,const void * vb)92895bd039Smarkus sacompare(const void *va, const void *vb)
93895bd039Smarkus {
94895bd039Smarkus 	const struct sad *a = va, *b = vb;
95895bd039Smarkus 
96895bd039Smarkus 	if (a->sad_spi < b->sad_spi)
97895bd039Smarkus 		return (-1);
98895bd039Smarkus 	if (a->sad_spi > b->sad_spi)
99895bd039Smarkus 		return (1);
100895bd039Smarkus 	return (0);
101895bd039Smarkus }
102895bd039Smarkus 
103f484f2cfShshoexer int
ipsecctl_rules(char * filename,int opts)104f484f2cfShshoexer ipsecctl_rules(char *filename, int opts)
105f484f2cfShshoexer {
106f484f2cfShshoexer 	struct ipsecctl		 ipsec;
107c272c54fSbluhm 	struct ipsec_rule	*rp;
1086d01698dShshoexer 	int			 action, error = 0;
109f484f2cfShshoexer 
110e7a09a9bSkjell 	bzero(&ipsec, sizeof(ipsec));
111f484f2cfShshoexer 	ipsec.opts = opts;
112f484f2cfShshoexer 	TAILQ_INIT(&ipsec.rule_queue);
113a6bcba92Sbluhm 	TAILQ_INIT(&ipsec.bundle_queue);
114f484f2cfShshoexer 
11520741916Sderaadt 	if (parse_rules(filename, &ipsec) < 0) {
116f484f2cfShshoexer 		warnx("Syntax error in config file: ipsec rules not loaded");
117f484f2cfShshoexer 		error = 1;
118e7a09a9bSkjell 	} else {
1196d01698dShshoexer 		if (opts & IPSECCTL_OPT_DELETE)
12090bd57a7Shshoexer 			action = ACTION_DELETE;
1216d01698dShshoexer 		else
12290bd57a7Shshoexer 			action = ACTION_ADD;
1236d01698dShshoexer 
124e7a09a9bSkjell 		if ((opts & IPSECCTL_OPT_NOACTION) == 0)
1252b6ecedeShshoexer 			error = ipsecctl_commit(action, &ipsec);
126c272c54fSbluhm 
127e7a09a9bSkjell 	}
128c272c54fSbluhm 
129a6bcba92Sbluhm 	/* This also frees the rules in ipsec.bundle_queue. */
130c272c54fSbluhm 	while ((rp = TAILQ_FIRST(&ipsec.rule_queue))) {
131c272c54fSbluhm 		TAILQ_REMOVE(&ipsec.rule_queue, rp, rule_entry);
132c272c54fSbluhm 		ipsecctl_free_rule(rp);
133c272c54fSbluhm 	}
134c272c54fSbluhm 
135c3324a93Skjell 	return (error);
136f484f2cfShshoexer }
137f484f2cfShshoexer 
138f484f2cfShshoexer FILE *
ipsecctl_fopen(const char * name,const char * mode)139f484f2cfShshoexer ipsecctl_fopen(const char *name, const char *mode)
140f484f2cfShshoexer {
141f484f2cfShshoexer 	struct stat	 st;
142f484f2cfShshoexer 	FILE		*fp;
143f484f2cfShshoexer 
144f484f2cfShshoexer 	fp = fopen(name, mode);
145f484f2cfShshoexer 	if (fp == NULL)
146c3324a93Skjell 		return (NULL);
147f484f2cfShshoexer 
148f484f2cfShshoexer 	if (fstat(fileno(fp), &st)) {
149f484f2cfShshoexer 		fclose(fp);
150c3324a93Skjell 		return (NULL);
151f484f2cfShshoexer 	}
152f484f2cfShshoexer 	if (S_ISDIR(st.st_mode)) {
153f484f2cfShshoexer 		fclose(fp);
154f484f2cfShshoexer 		errno = EISDIR;
155c3324a93Skjell 		return (NULL);
156f484f2cfShshoexer 	}
157c3324a93Skjell 	return (fp);
158f484f2cfShshoexer }
159f484f2cfShshoexer 
160f484f2cfShshoexer int
ipsecctl_commit(int action,struct ipsecctl * ipsec)1616d01698dShshoexer ipsecctl_commit(int action, struct ipsecctl *ipsec)
162f484f2cfShshoexer {
163f484f2cfShshoexer 	struct ipsec_rule	*rp;
1642b6ecedeShshoexer 	int			 ret = 0;
165f484f2cfShshoexer 
166f484f2cfShshoexer 	if (pfkey_init() == -1)
1670c7c92aeShshoexer 		errx(1, "ipsecctl_commit: failed to open PF_KEY socket");
168f484f2cfShshoexer 
169c272c54fSbluhm 	TAILQ_FOREACH(rp, &ipsec->rule_queue, rule_entry) {
17090bd57a7Shshoexer 		if (rp->type & RULE_IKE) {
171f8738d8bShenning 			if (ike_ipsec_establish(action, rp, isakmpd_fifo) ==
172f8738d8bShenning 			    -1) {
1734dfa5d1cSbluhm 				warnx("failed to %s ike rule %d",
174e0d21bd9Smarkus 				    action == ACTION_DELETE ? "delete" : "add",
175e0d21bd9Smarkus 				    rp->nr);
1762b6ecedeShshoexer 				ret = 2;
1772b6ecedeShshoexer 			}
17890bd57a7Shshoexer 		} else {
1792b6ecedeShshoexer 			if (pfkey_ipsec_establish(action, rp) == -1) {
180e0d21bd9Smarkus 				warnx("failed to %s rule %d",
181e0d21bd9Smarkus 				    action == ACTION_DELETE ? "delete" : "add",
182e0d21bd9Smarkus 				    rp->nr);
1832b6ecedeShshoexer 				ret = 2;
1842b6ecedeShshoexer 			}
18590bd57a7Shshoexer 		}
18609203018Shshoexer 	}
187f484f2cfShshoexer 
1882b6ecedeShshoexer 	return (ret);
18909203018Shshoexer }
19009203018Shshoexer 
19109203018Shshoexer int
ipsecctl_add_rule(struct ipsecctl * ipsec,struct ipsec_rule * r)19209203018Shshoexer ipsecctl_add_rule(struct ipsecctl *ipsec, struct ipsec_rule *r)
19309203018Shshoexer {
194e48766bcShshoexer 	TAILQ_INSERT_TAIL(&ipsec->rule_queue, r, rule_entry);
19509203018Shshoexer 
19609203018Shshoexer 	if ((ipsec->opts & IPSECCTL_OPT_VERBOSE) && !(ipsec->opts &
19709203018Shshoexer 	    IPSECCTL_OPT_SHOW))
19809203018Shshoexer 		ipsecctl_print_rule(r, ipsec->opts);
19909203018Shshoexer 
20009203018Shshoexer 	return (0);
20109203018Shshoexer }
20209203018Shshoexer 
20309203018Shshoexer void
ipsecctl_free_rule(struct ipsec_rule * rp)20409203018Shshoexer ipsecctl_free_rule(struct ipsec_rule *rp)
20509203018Shshoexer {
206bccfebbfShshoexer 	if (rp->src) {
20790bd57a7Shshoexer 		free(rp->src->name);
208f484f2cfShshoexer 		free(rp->src);
209bccfebbfShshoexer 	}
210bccfebbfShshoexer 	if (rp->dst) {
21190bd57a7Shshoexer 		free(rp->dst->name);
212f484f2cfShshoexer 		free(rp->dst);
213bccfebbfShshoexer 	}
214bccfebbfShshoexer 	if (rp->dst2) {
215bccfebbfShshoexer 		free(rp->dst2->name);
216bccfebbfShshoexer 		free(rp->dst2);
217bccfebbfShshoexer 	}
218435bb41eSmarkus 	if (rp->local) {
219435bb41eSmarkus 		free(rp->local->name);
220435bb41eSmarkus 		free(rp->local);
221435bb41eSmarkus 	}
22290bd57a7Shshoexer 	if (rp->peer) {
22390bd57a7Shshoexer 		free(rp->peer->name);
224f484f2cfShshoexer 		free(rp->peer);
22590bd57a7Shshoexer 	}
226abe65127Shshoexer 	if (rp->auth) {
227abe65127Shshoexer 		free(rp->auth->srcid);
228abe65127Shshoexer 		free(rp->auth->dstid);
229abe65127Shshoexer 		free(rp->auth);
23090bd57a7Shshoexer 	}
231aa4503c0Sreyk 	if (rp->ikeauth) {
232aa4503c0Sreyk 		free(rp->ikeauth->string);
233aa4503c0Sreyk 		free(rp->ikeauth);
234aa4503c0Sreyk 	}
235375db29dShshoexer 	free(rp->xfs);
236336f7f89Smcbride 	free(rp->p1xfs);
237336f7f89Smcbride 	free(rp->p2xfs);
238336f7f89Smcbride 	free(rp->p1life);
239336f7f89Smcbride 	free(rp->p2life);
240670bad1aShshoexer 	if (rp->authkey) {
241670bad1aShshoexer 		free(rp->authkey->data);
242670bad1aShshoexer 		free(rp->authkey);
243110db5a9Shshoexer 	}
244375db29dShshoexer 	if (rp->enckey) {
245375db29dShshoexer 		free(rp->enckey->data);
246375db29dShshoexer 		free(rp->enckey);
247375db29dShshoexer 	}
248befd40c8Sbluhm 	free(rp->p1name);
249e973ffa2Smarkus 	free(rp->p2name);
250e973ffa2Smarkus 	free(rp->p2lid);
251c471c043Smpf 	free(rp->p2nid);
252e973ffa2Smarkus 	free(rp->p2rid);
253f484f2cfShshoexer 	free(rp);
254f484f2cfShshoexer }
255f484f2cfShshoexer 
256aa9f7a4dSmpi /*
257aa9f7a4dSmpi  * Merge two flow rules if they match.
258aa9f7a4dSmpi  *
259aa9f7a4dSmpi  * Return 0 if ``from'' has been merged into ``to'', -1 otherwise.
260aa9f7a4dSmpi  */
261aa9f7a4dSmpi int
ipsecctl_merge_rules(struct ipsec_rule * to,struct ipsec_rule * from)262aa9f7a4dSmpi ipsecctl_merge_rules(struct ipsec_rule *to, struct ipsec_rule *from)
263aa9f7a4dSmpi {
264aa9f7a4dSmpi 	int match = 0;
265aa9f7a4dSmpi 
266aa9f7a4dSmpi 	assert((to->type & RULE_FLOW) && (from->type & RULE_FLOW));
267aa9f7a4dSmpi 
268aa9f7a4dSmpi 	if ((to->satype != from->satype) ||
269aa9f7a4dSmpi 	    (to->direction != from->direction) ||
270aa9f7a4dSmpi 	    (to->sport != from->sport) ||
271aa9f7a4dSmpi 	    (to->dport != from->dport) ||
272aa9f7a4dSmpi 	    (to->proto != from->proto))
273aa9f7a4dSmpi 		return (-1);
274aa9f7a4dSmpi 
275aa9f7a4dSmpi 	if (to->local != NULL || from->local != NULL) {
276aa9f7a4dSmpi 		if ((to->local == NULL) || (from->local == NULL) ||
277aa9f7a4dSmpi 		    memcmp(to->local, from->local, sizeof(*to->local)))
278aa9f7a4dSmpi 			return (-1);
279aa9f7a4dSmpi 	}
280aa9f7a4dSmpi 
281aa9f7a4dSmpi 	if (to->peer != NULL || from->peer != NULL) {
282aa9f7a4dSmpi 		if ((to->peer == NULL) || (from->peer == NULL) ||
283aa9f7a4dSmpi 		    memcmp(to->peer, from->peer, sizeof(*to->peer)))
284aa9f7a4dSmpi 			return (-1);
285aa9f7a4dSmpi 	}
286aa9f7a4dSmpi 
287aa9f7a4dSmpi 	if (ipsecctl_cmp_ident(to, from))
288aa9f7a4dSmpi 		return (-1);
289aa9f7a4dSmpi 
290aa9f7a4dSmpi 	if (ipsecctl_rule_matchsrc(to, from->src)) {
291aa9f7a4dSmpi 		free(from->src->name);
292aa9f7a4dSmpi 		free(from->src);
293aa9f7a4dSmpi 		from->src = NULL;
294aa9f7a4dSmpi 		match = 1;
295aa9f7a4dSmpi 	}
296aa9f7a4dSmpi 	if (ipsecctl_rule_matchdst(to, from->dst)) {
297aa9f7a4dSmpi 		free(from->dst->name);
298aa9f7a4dSmpi 		free(from->dst);
299aa9f7a4dSmpi 		from->dst = NULL;
300aa9f7a4dSmpi 		match = 1;
301aa9f7a4dSmpi 	}
302aa9f7a4dSmpi 
303aa9f7a4dSmpi 	if (!match)
304aa9f7a4dSmpi 		return (-1);
305aa9f7a4dSmpi 
306aa9f7a4dSmpi 	TAILQ_INSERT_TAIL(&to->collapsed_rules, from, bundle_entry);
307aa9f7a4dSmpi 
308aa9f7a4dSmpi 	return (0);
309aa9f7a4dSmpi }
310aa9f7a4dSmpi 
311aa9f7a4dSmpi /*
312aa9f7a4dSmpi  * Return 0 if ``r1'' and ``r2'' IDENTITY match, -1 otherwise.
313aa9f7a4dSmpi  */
314aa9f7a4dSmpi int
ipsecctl_cmp_ident(struct ipsec_rule * r1,struct ipsec_rule * r2)315aa9f7a4dSmpi ipsecctl_cmp_ident(struct ipsec_rule *r1, struct ipsec_rule *r2)
316aa9f7a4dSmpi {
317aa9f7a4dSmpi 	if ((r1->auth == NULL) && (r2->auth == NULL))
318aa9f7a4dSmpi 		return (0) ;
319aa9f7a4dSmpi 
320aa9f7a4dSmpi 	if ((r1->auth == NULL) || (r2->auth == NULL))
321aa9f7a4dSmpi 		return (-1);
322aa9f7a4dSmpi 
323aa9f7a4dSmpi 	if (r1->auth->type != r2->auth->type)
324aa9f7a4dSmpi 		return (-1);
325aa9f7a4dSmpi 
326aa9f7a4dSmpi 	if (r1->auth->srcid != NULL) {
327aa9f7a4dSmpi 		if (r2->auth->srcid == NULL)
328aa9f7a4dSmpi 			return (-1);
329aa9f7a4dSmpi 
330aa9f7a4dSmpi 		if (strcmp(r1->auth->srcid, r2->auth->srcid))
331aa9f7a4dSmpi 			return (-1);
332aa9f7a4dSmpi 	}
333aa9f7a4dSmpi 
334aa9f7a4dSmpi 	if (r1->auth->dstid) {
335aa9f7a4dSmpi 		if (r2->auth->dstid == NULL)
336aa9f7a4dSmpi 			return (-1);
337aa9f7a4dSmpi 
338aa9f7a4dSmpi 		if (strcmp(r1->auth->dstid, r2->auth->dstid))
339aa9f7a4dSmpi 			return (-1);
340aa9f7a4dSmpi 	}
341aa9f7a4dSmpi 
342aa9f7a4dSmpi 	return (0);
343aa9f7a4dSmpi }
344aa9f7a4dSmpi 
345aa9f7a4dSmpi 
346aa9f7a4dSmpi /*
347aa9f7a4dSmpi  * Return 0 if ``r'' or its merged entries contain ``src'', -1 otherwise.
348aa9f7a4dSmpi  */
349aa9f7a4dSmpi int
ipsecctl_rule_matchsrc(struct ipsec_rule * r,struct ipsec_addr_wrap * src)350aa9f7a4dSmpi ipsecctl_rule_matchsrc(struct ipsec_rule *r, struct ipsec_addr_wrap *src)
351aa9f7a4dSmpi {
352aa9f7a4dSmpi 	struct ipsec_rule *r2;
353aa9f7a4dSmpi 
354aa9f7a4dSmpi 	if (memcmp(r->src, src, sizeof(*r->src)) == 0)
355aa9f7a4dSmpi 		return (-1);
356aa9f7a4dSmpi 
357aa9f7a4dSmpi 	TAILQ_FOREACH(r2, &r->collapsed_rules, bundle_entry) {
358aa9f7a4dSmpi 		if (r2->src == NULL)
359aa9f7a4dSmpi 			continue;
360aa9f7a4dSmpi 		if (memcmp(r2->src, src, sizeof(*r->src)) == 0)
361aa9f7a4dSmpi 			return (-1);
362aa9f7a4dSmpi 	}
363aa9f7a4dSmpi 
364aa9f7a4dSmpi 	return (0);
365aa9f7a4dSmpi }
366aa9f7a4dSmpi 
367aa9f7a4dSmpi /*
368aa9f7a4dSmpi  * Return 0 if ``r'' or its merged entries contain ``dst'', -1 otherwise.
369aa9f7a4dSmpi  */
370aa9f7a4dSmpi int
ipsecctl_rule_matchdst(struct ipsec_rule * r,struct ipsec_addr_wrap * dst)371aa9f7a4dSmpi ipsecctl_rule_matchdst(struct ipsec_rule *r, struct ipsec_addr_wrap *dst)
372aa9f7a4dSmpi {
373aa9f7a4dSmpi 	struct ipsec_rule *r2;
374aa9f7a4dSmpi 
375aa9f7a4dSmpi 	if (memcmp(r->dst, dst, sizeof(*r->dst)) == 0)
376aa9f7a4dSmpi 		return (-1);
377aa9f7a4dSmpi 
378aa9f7a4dSmpi 	TAILQ_FOREACH(r2, &r->collapsed_rules, bundle_entry) {
379aa9f7a4dSmpi 		if (r2->dst == NULL)
380aa9f7a4dSmpi 			continue;
381aa9f7a4dSmpi 		if (memcmp(r2->dst, dst, sizeof(*r->dst)) == 0)
382aa9f7a4dSmpi 			return (-1);
383aa9f7a4dSmpi 	}
384aa9f7a4dSmpi 
385aa9f7a4dSmpi 	return (0);
386aa9f7a4dSmpi }
387aa9f7a4dSmpi 
388f484f2cfShshoexer void
ipsecctl_print_addr(struct ipsec_addr_wrap * ipa)38991f765ddShshoexer ipsecctl_print_addr(struct ipsec_addr_wrap *ipa)
390f484f2cfShshoexer {
39191f765ddShshoexer 	int		bits;
392712e78baShshoexer 	char		buf[NI_MAXHOST];
393f484f2cfShshoexer 
394f484f2cfShshoexer 	if (ipa == NULL) {
395f484f2cfShshoexer 		printf("?");
396f484f2cfShshoexer 		return;
397f484f2cfShshoexer 	}
398712e78baShshoexer 	if (inet_ntop(ipa->af, &ipa->address, buf, sizeof(buf)) == NULL)
399f484f2cfShshoexer 		printf("?");
400f484f2cfShshoexer 	else
401f484f2cfShshoexer 		printf("%s", buf);
402f484f2cfShshoexer 
403eaa84e12Skn 	bits = unmask(&ipa->mask);
40491f765ddShshoexer 	if (bits != (ipa->af == AF_INET ? 32 : 128))
40591f765ddShshoexer 		printf("/%d", bits);
406f484f2cfShshoexer }
407f484f2cfShshoexer 
408f484f2cfShshoexer void
ipsecctl_print_proto(u_int8_t proto)4099182219dSmarkus ipsecctl_print_proto(u_int8_t proto)
4109182219dSmarkus {
4119182219dSmarkus 	struct protoent *p;
4129182219dSmarkus 
4139182219dSmarkus 	if ((p = getprotobynumber(proto)) != NULL)
4149182219dSmarkus 		printf("%s", p->p_name);
4159182219dSmarkus 	else
4169182219dSmarkus 		printf("%u", proto);
4179182219dSmarkus }
4189182219dSmarkus 
4199182219dSmarkus void
ipsecctl_print_port(u_int16_t port,const char * proto)42057f58d0dSnaddy ipsecctl_print_port(u_int16_t port, const char *proto)
42157f58d0dSnaddy {
42257f58d0dSnaddy 	struct servent *s;
42357f58d0dSnaddy 
42457f58d0dSnaddy 	if ((s = getservbyport(port, proto)) != NULL)
42557f58d0dSnaddy 		printf("%s", s->s_name);
42657f58d0dSnaddy 	else
42757f58d0dSnaddy 		printf("%u", ntohs(port));
42857f58d0dSnaddy }
42957f58d0dSnaddy 
43057f58d0dSnaddy void
ipsecctl_print_key(struct ipsec_key * key)431110db5a9Shshoexer ipsecctl_print_key(struct ipsec_key *key)
432f484f2cfShshoexer {
433110db5a9Shshoexer 	int	i;
434f484f2cfShshoexer 
435110db5a9Shshoexer 	for (i = 0; i < (int)key->len; i++)
436110db5a9Shshoexer 		printf("%02x", key->data[i]);
437110db5a9Shshoexer }
438f484f2cfShshoexer 
439110db5a9Shshoexer void
ipsecctl_print_flow(struct ipsec_rule * r,int opts)440110db5a9Shshoexer ipsecctl_print_flow(struct ipsec_rule *r, int opts)
441110db5a9Shshoexer {
442aa9f7a4dSmpi 	struct ipsec_rule *r2;
443aa9f7a4dSmpi 
4449182219dSmarkus 	printf("flow %s %s", satype[r->satype], direction[r->direction]);
445110db5a9Shshoexer 
4469182219dSmarkus 	if (r->proto) {
4479182219dSmarkus 		printf(" proto ");
4489182219dSmarkus 		ipsecctl_print_proto(r->proto);
4499182219dSmarkus 	}
450f484f2cfShshoexer 	printf(" from ");
451aa9f7a4dSmpi 	if (opts & IPSECCTL_OPT_COLLAPSE) {
452aa9f7a4dSmpi 		printf("{ ");
453aa9f7a4dSmpi 		ipsecctl_print_addr(r->src);
454aa9f7a4dSmpi 		TAILQ_FOREACH(r2, &r->collapsed_rules, bundle_entry) {
455aa9f7a4dSmpi 			if (r2->src == NULL)
456aa9f7a4dSmpi 				continue;
457aa9f7a4dSmpi 			printf(", ");
458aa9f7a4dSmpi 			ipsecctl_print_addr(r2->src);
459aa9f7a4dSmpi 		}
460aa9f7a4dSmpi 		printf(" }");
461aa9f7a4dSmpi 	} else
462f484f2cfShshoexer 		ipsecctl_print_addr(r->src);
46357f58d0dSnaddy 	if (r->sport) {
46457f58d0dSnaddy 		printf(" port ");
46557f58d0dSnaddy 		ipsecctl_print_port(r->sport,
46657f58d0dSnaddy 		    r->proto == IPPROTO_TCP ? "tcp" : "udp");
46757f58d0dSnaddy 	}
468f484f2cfShshoexer 	printf(" to ");
469aa9f7a4dSmpi 	if (opts & IPSECCTL_OPT_COLLAPSE) {
470aa9f7a4dSmpi 		printf("{ ");
471aa9f7a4dSmpi 		ipsecctl_print_addr(r->dst);
472aa9f7a4dSmpi 		TAILQ_FOREACH(r2, &r->collapsed_rules, bundle_entry) {
473aa9f7a4dSmpi 			if (r2->dst == NULL)
474aa9f7a4dSmpi 				continue;
475aa9f7a4dSmpi 			printf(", ");
476aa9f7a4dSmpi 			ipsecctl_print_addr(r2->dst);
477aa9f7a4dSmpi 		}
478aa9f7a4dSmpi 		printf(" }");
479aa9f7a4dSmpi 	} else
480f484f2cfShshoexer 		ipsecctl_print_addr(r->dst);
48157f58d0dSnaddy 	if (r->dport) {
48257f58d0dSnaddy 		printf(" port ");
48357f58d0dSnaddy 		ipsecctl_print_port(r->dport,
48457f58d0dSnaddy 		    r->proto == IPPROTO_TCP ? "tcp" : "udp");
48557f58d0dSnaddy 	}
486435bb41eSmarkus 	if (r->local) {
487435bb41eSmarkus 		printf(" local ");
488435bb41eSmarkus 		ipsecctl_print_addr(r->local);
489435bb41eSmarkus 	}
4904c98b117Shshoexer 	if (r->peer) {
491f484f2cfShshoexer 		printf(" peer ");
492f484f2cfShshoexer 		ipsecctl_print_addr(r->peer);
4934c98b117Shshoexer 	}
4949382ed8fShshoexer 	if (r->auth) {
495abe65127Shshoexer 		if (r->auth->srcid)
496378cce75Shshoexer 			printf(" srcid %s", r->auth->srcid);
497abe65127Shshoexer 		if (r->auth->dstid)
498378cce75Shshoexer 			printf(" dstid %s", r->auth->dstid);
499abe65127Shshoexer 		if (r->auth->type > 0)
500378cce75Shshoexer 			printf(" %s", auth[r->auth->type]);
5019382ed8fShshoexer 	}
502378cce75Shshoexer 	printf(" type %s", flowtype[r->flowtype]);
50390bd57a7Shshoexer 	printf("\n");
504110db5a9Shshoexer }
505110db5a9Shshoexer 
506110db5a9Shshoexer void
ipsecctl_print_sa(struct ipsec_rule * r,int opts)507110db5a9Shshoexer ipsecctl_print_sa(struct ipsec_rule *r, int opts)
508110db5a9Shshoexer {
5099182219dSmarkus 	printf("%s ", satype[r->satype]);
510ef9c0cabSmikeb 	/* tunnel/transport is only meaningful for esp/ah/ipcomp */
5119182219dSmarkus 	if (r->satype != IPSEC_TCPMD5 && r->satype != IPSEC_IPIP)
512a38d220fShshoexer 		printf("%s ", tmode[r->tmode]);
513110db5a9Shshoexer 	printf("from ");
514110db5a9Shshoexer 	ipsecctl_print_addr(r->src);
515110db5a9Shshoexer 	printf(" to ");
516110db5a9Shshoexer 	ipsecctl_print_addr(r->dst);
517110db5a9Shshoexer 	printf(" spi 0x%08x", r->spi);
5186525f06fShshoexer 
5199182219dSmarkus 	if (r->satype != IPSEC_TCPMD5) {
520375db29dShshoexer 		if (r->xfs && r->xfs->authxf)
521375db29dShshoexer 			printf(" auth %s", r->xfs->authxf->name);
522375db29dShshoexer 		if (r->xfs && r->xfs->encxf)
523375db29dShshoexer 			printf(" enc %s", r->xfs->encxf->name);
52472e25333Shshoexer 		if (r->xfs && r->xfs->compxf)
52572e25333Shshoexer 			printf(" comp %s", r->xfs->compxf->name);
5266525f06fShshoexer 	}
5272cbe2c48Shshoexer 	if (r->authkey && (opts & IPSECCTL_OPT_SHOWKEY)) {
5289182219dSmarkus 		if (r->satype == IPSEC_TCPMD5)
5296525f06fShshoexer 			printf(" ");
5306525f06fShshoexer 		else
53163e1d805Smarkus 			printf(" \\\n\t");
532670bad1aShshoexer 		printf("authkey 0x");
533670bad1aShshoexer 		ipsecctl_print_key(r->authkey);
534110db5a9Shshoexer 	}
5352cbe2c48Shshoexer 	if (r->enckey && (opts & IPSECCTL_OPT_SHOWKEY)) {
5369182219dSmarkus 		if (r->satype == IPSEC_TCPMD5)
5376525f06fShshoexer 			printf(" ");
5386525f06fShshoexer 		else
53963e1d805Smarkus 			printf(" \\\n\t");
540381a2422Shshoexer 		printf("enckey 0x");
541381a2422Shshoexer 		ipsecctl_print_key(r->enckey);
542381a2422Shshoexer 	}
54390bd57a7Shshoexer 	printf("\n");
544c8602fa3Shshoexer }
545110db5a9Shshoexer 
546110db5a9Shshoexer void
ipsecctl_print_sabundle(struct ipsec_rule * r,int opts)547a6bcba92Sbluhm ipsecctl_print_sabundle(struct ipsec_rule *r, int opts)
548b5f834c1Shshoexer {
549a6bcba92Sbluhm 	printf("[bundle %s to ", satype[r->proto]);
550b5f834c1Shshoexer 	ipsecctl_print_addr(r->dst);
551b5f834c1Shshoexer 	printf(" spi 0x%08x with %s to ", r->spi, satype[r->proto2]);
552b5f834c1Shshoexer 	ipsecctl_print_addr(r->dst2);
553b5f834c1Shshoexer 	printf(" spi 0x%08x", r->spi2);
554b5f834c1Shshoexer 
555b5f834c1Shshoexer 	printf("]\n");
556b5f834c1Shshoexer }
557b5f834c1Shshoexer 
558b5f834c1Shshoexer void
ipsecctl_print_rule(struct ipsec_rule * r,int opts)559110db5a9Shshoexer ipsecctl_print_rule(struct ipsec_rule *r, int opts)
560110db5a9Shshoexer {
561aa9f7a4dSmpi 	struct ipsec_rule *r2;
562aa9f7a4dSmpi 
563aa9f7a4dSmpi 	if (opts & IPSECCTL_OPT_VERBOSE2) {
564110db5a9Shshoexer 		printf("@%d", r->nr);
565aa9f7a4dSmpi 		if (opts & IPSECCTL_OPT_COLLAPSE) {
566aa9f7a4dSmpi 			TAILQ_FOREACH(r2, &r->collapsed_rules, bundle_entry) {
567aa9f7a4dSmpi 				printf(",%d", r2->nr);
568aa9f7a4dSmpi 			}
569aa9f7a4dSmpi 		}
570aa9f7a4dSmpi 		printf(" ");
571aa9f7a4dSmpi 	}
572110db5a9Shshoexer 
57316bc4fb7Shshoexer 	if (r->type & RULE_FLOW)
574110db5a9Shshoexer 		ipsecctl_print_flow(r, opts);
57516bc4fb7Shshoexer 	if (r->type & RULE_SA)
576110db5a9Shshoexer 		ipsecctl_print_sa(r, opts);
57790bd57a7Shshoexer 	if (r->type & RULE_IKE)
57890bd57a7Shshoexer 		ike_print_config(r, opts);
579a6bcba92Sbluhm 	if (r->type & RULE_BUNDLE)
580a6bcba92Sbluhm 		ipsecctl_print_sabundle(r, opts);
581f484f2cfShshoexer }
582f484f2cfShshoexer 
583f484f2cfShshoexer int
ipsecctl_flush(int opts)584f484f2cfShshoexer ipsecctl_flush(int opts)
585f484f2cfShshoexer {
586f484f2cfShshoexer 	if (opts & IPSECCTL_OPT_NOACTION)
587c3324a93Skjell 		return (0);
588f484f2cfShshoexer 
589f484f2cfShshoexer 	if (pfkey_init() == -1)
5900c7c92aeShshoexer 		errx(1, "ipsecctl_flush: failed to open PF_KEY socket");
591f484f2cfShshoexer 
592b1bf2fcbShshoexer 	if (pfkey_ipsec_flush() == -1)
593b1bf2fcbShshoexer 		errx(1, "ipsecctl_flush: failed to flush");
594f484f2cfShshoexer 
595c3324a93Skjell 	return (0);
596f484f2cfShshoexer }
597f484f2cfShshoexer 
598202aab43Stobhe char *
ipsecctl_get_rules(struct ipsecctl * ipsec,size_t * need)599202aab43Stobhe ipsecctl_get_rules(struct ipsecctl *ipsec, size_t *need)
600f484f2cfShshoexer {
601f484f2cfShshoexer 	int		 mib[4];
602202aab43Stobhe 	char		*buf;
603f484f2cfShshoexer 
604f484f2cfShshoexer 	mib[0] = CTL_NET;
605f484f2cfShshoexer 	mib[1] = PF_KEY;
606f484f2cfShshoexer 	mib[2] = PF_KEY_V2;
607f484f2cfShshoexer 	mib[3] = NET_KEY_SPD_DUMP;
608f484f2cfShshoexer 
609202aab43Stobhe 	if (sysctl(mib, 4, NULL, need, NULL, 0) == -1)
610bd828a90Shshoexer 		err(1, "ipsecctl_get_rules: sysctl");
611202aab43Stobhe 	if (*need == 0)
612202aab43Stobhe 		return NULL;
613202aab43Stobhe 	if ((buf = malloc(*need)) == NULL)
614bd828a90Shshoexer 		err(1, "ipsecctl_get_rules: malloc");
615202aab43Stobhe 	if (sysctl(mib, 4, buf, need, NULL, 0) == -1)
616bd828a90Shshoexer 		err(1, "ipsecctl_get_rules: sysctl");
617f484f2cfShshoexer 
618202aab43Stobhe 	return buf;
619202aab43Stobhe }
620202aab43Stobhe 
621202aab43Stobhe void
ipsecctl_parse_rules(struct ipsecctl * ipsec,char * buf,size_t need)622202aab43Stobhe ipsecctl_parse_rules(struct ipsecctl *ipsec, char *buf, size_t need)
623202aab43Stobhe {
624202aab43Stobhe 	struct sadb_msg *msg;
625202aab43Stobhe 	struct ipsec_rule *rule, *last = NULL;
626202aab43Stobhe 	char		*lim, *next;
627202aab43Stobhe 
628202aab43Stobhe 	lim = buf + need;
629a4e2382fShshoexer 	for (next = buf; next < lim; next += msg->sadb_msg_len *
630a4e2382fShshoexer 	    PFKEYV2_CHUNK) {
631a4e2382fShshoexer 		msg = (struct sadb_msg *)next;
632a4e2382fShshoexer 		if (msg->sadb_msg_len == 0)
633a4e2382fShshoexer 			break;
634f484f2cfShshoexer 
635f484f2cfShshoexer 		rule = calloc(1, sizeof(struct ipsec_rule));
636f484f2cfShshoexer 		if (rule == NULL)
637202aab43Stobhe 			err(1, "ipsecctl_parse_rules: calloc");
638f484f2cfShshoexer 		rule->nr = ipsec->rule_nr++;
63916bc4fb7Shshoexer 		rule->type |= RULE_FLOW;
640aa9f7a4dSmpi 		TAILQ_INIT(&rule->collapsed_rules);
641f484f2cfShshoexer 
642a4e2382fShshoexer 		if (pfkey_parse(msg, rule))
643202aab43Stobhe 			errx(1, "ipsecctl_parse_rules: "
6448d007042Sreyk 			    "failed to parse PF_KEY message");
645f484f2cfShshoexer 
646aa9f7a4dSmpi 		/*
647aa9f7a4dSmpi 		 * Try to collapse ``rule'' with the last enqueued rule.
648aa9f7a4dSmpi 		 *
649aa9f7a4dSmpi 		 * Note that comparing only the last entry works only if
650aa9f7a4dSmpi 		 * the dump is sorted.
651aa9f7a4dSmpi 		 */
652aa9f7a4dSmpi 		if ((ipsec->opts & IPSECCTL_OPT_COLLAPSE) && (last != NULL) &&
653aa9f7a4dSmpi 		    (ipsecctl_merge_rules(last, rule) == 0))
654aa9f7a4dSmpi 			continue;
655aa9f7a4dSmpi 
656f484f2cfShshoexer 		ipsecctl_add_rule(ipsec, rule);
657aa9f7a4dSmpi 		last = rule;
658f484f2cfShshoexer 	}
6598933a803Shshoexer 
6608933a803Shshoexer 	free(buf);
661f484f2cfShshoexer }
662f484f2cfShshoexer 
663f484f2cfShshoexer void
ipsecctl_print_title(char * title)6643dd59ad2Shshoexer ipsecctl_print_title(char *title)
6653dd59ad2Shshoexer {
6663dd59ad2Shshoexer 	if (!first_title)
6673dd59ad2Shshoexer 		printf("\n");
6683dd59ad2Shshoexer 	first_title = 0;
6693dd59ad2Shshoexer 	printf("%s\n", title);
6703dd59ad2Shshoexer }
6713dd59ad2Shshoexer 
6723dd59ad2Shshoexer void
ipsecctl_show(int opts)673202aab43Stobhe ipsecctl_show(int opts)
674f484f2cfShshoexer {
675f484f2cfShshoexer 	struct ipsecctl ipsec;
676f484f2cfShshoexer 	struct ipsec_rule *rp;
677202aab43Stobhe 	struct sadb_msg *msg;
678202aab43Stobhe 	struct sad	*sad;
679202aab43Stobhe 	int		 mib[5], sacount, i;
680202aab43Stobhe 	size_t		 need = 0, rlen;
681202aab43Stobhe 	char		*sbuf = NULL, *rbuf = NULL, *lim, *next;
682f484f2cfShshoexer 
683202aab43Stobhe 	if (opts & IPSECCTL_OPT_SHOWFLOWS) {
684e7a09a9bSkjell 		bzero(&ipsec, sizeof(ipsec));
685f484f2cfShshoexer 		ipsec.opts = opts;
686f484f2cfShshoexer 		TAILQ_INIT(&ipsec.rule_queue);
687202aab43Stobhe 		rbuf = ipsecctl_get_rules(&ipsec, &rlen);
688202aab43Stobhe 	}
689f484f2cfShshoexer 
690202aab43Stobhe 	if (opts & IPSECCTL_OPT_SHOWSAS) {
691202aab43Stobhe 		mib[0] = CTL_NET;
692202aab43Stobhe 		mib[1] = PF_KEY;
693202aab43Stobhe 		mib[2] = PF_KEY_V2;
694202aab43Stobhe 		mib[3] = NET_KEY_SADB_DUMP;
695202aab43Stobhe 		mib[4] = SADB_SATYPE_UNSPEC;
696202aab43Stobhe 
697202aab43Stobhe 		/* When the SAD is empty we get ENOENT, no need to err(). */
698202aab43Stobhe 		if (sysctl(mib, 5, NULL, &need, NULL, 0) == -1 &&
699202aab43Stobhe 		    errno != ENOENT)
700202aab43Stobhe 			err(1, "ipsecctl_show: sysctl");
701202aab43Stobhe 		if (need > 0) {
702202aab43Stobhe 			if ((sbuf = malloc(need)) == NULL)
703202aab43Stobhe 				err(1, "ipsecctl_show: malloc");
704202aab43Stobhe 			if (sysctl(mib, 5, sbuf, &need, NULL, 0) == -1)
705202aab43Stobhe 				err(1, "ipsecctl_show: sysctl");
706202aab43Stobhe 		}
707202aab43Stobhe 	}
708202aab43Stobhe 
709*4ecd42c0Syasuoka 	if (pledge("stdio dns", NULL) == -1)
710202aab43Stobhe 		err(1, "pledge");
711202aab43Stobhe 
712202aab43Stobhe 	if (rbuf != NULL) {
713202aab43Stobhe 		ipsecctl_parse_rules(&ipsec, rbuf, rlen);
714f484f2cfShshoexer 
7153dd59ad2Shshoexer 		if (opts & IPSECCTL_OPT_SHOWALL)
7163dd59ad2Shshoexer 			ipsecctl_print_title("FLOWS:");
7173dd59ad2Shshoexer 
718202aab43Stobhe 		if (TAILQ_FIRST(&ipsec.rule_queue) != NULL) {
719f484f2cfShshoexer 			while ((rp = TAILQ_FIRST(&ipsec.rule_queue))) {
720e48766bcShshoexer 				TAILQ_REMOVE(&ipsec.rule_queue, rp, rule_entry);
721f484f2cfShshoexer 
7223dd59ad2Shshoexer 				ipsecctl_print_rule(rp, ipsec.opts);
723f484f2cfShshoexer 
72490bd57a7Shshoexer 				free(rp->src->name);
725f484f2cfShshoexer 				free(rp->src);
72690bd57a7Shshoexer 				free(rp->dst->name);
727f484f2cfShshoexer 				free(rp->dst);
728435bb41eSmarkus 				if (rp->local) {
729435bb41eSmarkus 					free(rp->local->name);
730435bb41eSmarkus 					free(rp->local);
731435bb41eSmarkus 				}
7324c98b117Shshoexer 				if (rp->peer) {
73390bd57a7Shshoexer 					free(rp->peer->name);
734f484f2cfShshoexer 					free(rp->peer);
7354c98b117Shshoexer 				}
736abe65127Shshoexer 				if (rp->auth) {
737abe65127Shshoexer 					free(rp->auth->srcid);
738abe65127Shshoexer 					free(rp->auth->dstid);
739abe65127Shshoexer 					free(rp->auth);
740abe65127Shshoexer 				}
741f484f2cfShshoexer 				free(rp);
742f484f2cfShshoexer 			}
743f484f2cfShshoexer 		}
744202aab43Stobhe 	} else if (opts & IPSECCTL_OPT_SHOWALL) {
745202aab43Stobhe 		ipsecctl_print_title("FLOWS:");
746202aab43Stobhe 		if (opts & IPSECCTL_OPT_SHOWALL)
747202aab43Stobhe 			printf("No flows\n");
748202aab43Stobhe 	}
749f484f2cfShshoexer 
750*4ecd42c0Syasuoka 	if (pledge("stdio", NULL) == -1)
751*4ecd42c0Syasuoka 		err(1, "pledge");
752*4ecd42c0Syasuoka 
753202aab43Stobhe 	if (sbuf != NULL) {
7543dd59ad2Shshoexer 		if (opts & IPSECCTL_OPT_SHOWALL)
7553044111dSho 			ipsecctl_print_title("SAD:");
7563dd59ad2Shshoexer 
757895bd039Smarkus 		sacount = 0;
758202aab43Stobhe 		lim = sbuf + need;
759202aab43Stobhe 		for (next = sbuf; next < lim;
7603dd59ad2Shshoexer 		    next += msg->sadb_msg_len * PFKEYV2_CHUNK) {
7613dd59ad2Shshoexer 			msg = (struct sadb_msg *)next;
7623dd59ad2Shshoexer 			if (msg->sadb_msg_len == 0)
7633dd59ad2Shshoexer 				break;
764895bd039Smarkus 			sacount++;
7653dd59ad2Shshoexer 		}
766895bd039Smarkus 		if ((sad = calloc(sacount, sizeof(*sad))) == NULL)
767202aab43Stobhe 			err(1, "ipsecctl_show: calloc");
768895bd039Smarkus 		i = 0;
769202aab43Stobhe 		for (next = sbuf; next < lim;
770895bd039Smarkus 		    next += msg->sadb_msg_len * PFKEYV2_CHUNK) {
771895bd039Smarkus 			msg = (struct sadb_msg *)next;
772895bd039Smarkus 			if (msg->sadb_msg_len == 0)
773895bd039Smarkus 				break;
774895bd039Smarkus 			sad[i].sad_spi = pfkey_get_spi(msg);
775895bd039Smarkus 			sad[i].sad_msg = msg;
776895bd039Smarkus 			i++;
777895bd039Smarkus 		}
778895bd039Smarkus 		qsort(sad, sacount, sizeof(*sad), sacompare);
779895bd039Smarkus 		for (i = 0; i < sacount; i++)
780895bd039Smarkus 			pfkey_print_sa(sad[i].sad_msg, opts);
781895bd039Smarkus 		free(sad);
782202aab43Stobhe 		free(sbuf);
783202aab43Stobhe 	} else if (opts & IPSECCTL_OPT_SHOWALL) {
784202aab43Stobhe 		ipsecctl_print_title("SAD:");
785202aab43Stobhe 		printf("No entries\n");
786202aab43Stobhe 	}
7873dd59ad2Shshoexer }
7883dd59ad2Shshoexer 
7898a87fca6Smsf int
ipsecctl_monitor(int opts)7908a87fca6Smsf ipsecctl_monitor(int opts)
7918a87fca6Smsf {
7928a87fca6Smsf 	return (pfkey_monitor(opts));
7938a87fca6Smsf }
7948a87fca6Smsf 
795f484f2cfShshoexer __dead void
usage(void)796f484f2cfShshoexer usage(void)
797f484f2cfShshoexer {
798f484f2cfShshoexer 	extern char	*__progname;
799f484f2cfShshoexer 
800aa9f7a4dSmpi 	fprintf(stderr, "usage: %s [-cdFkmnv] [-D macro=value] [-f file]"
8013d7fea66Sjmc 	    " [-i fifo] [-s modifier]\n", __progname);
802f484f2cfShshoexer 	exit(1);
803f484f2cfShshoexer }
804f484f2cfShshoexer 
8053dd59ad2Shshoexer const char *
ipsecctl_lookup_option(char * cmd,const char ** list)8063dd59ad2Shshoexer ipsecctl_lookup_option(char *cmd, const char **list)
8073dd59ad2Shshoexer {
8083dd59ad2Shshoexer 	if (cmd != NULL && *cmd)
8093dd59ad2Shshoexer 		for (; *list; list++)
8103dd59ad2Shshoexer 			if (!strncmp(cmd, *list, strlen(cmd)))
8113dd59ad2Shshoexer 				return (*list);
8123dd59ad2Shshoexer 	return (NULL);
8133dd59ad2Shshoexer }
8143dd59ad2Shshoexer 
815f484f2cfShshoexer int
main(int argc,char * argv[])816f484f2cfShshoexer main(int argc, char *argv[])
817f484f2cfShshoexer {
818f484f2cfShshoexer 	int		 error = 0;
819f484f2cfShshoexer 	int		 ch;
820f484f2cfShshoexer 	int		 opts = 0;
821f484f2cfShshoexer 	char		*rulesopt = NULL;
822f484f2cfShshoexer 
823f484f2cfShshoexer 	if (argc < 2)
824f484f2cfShshoexer 		usage();
825f484f2cfShshoexer 
826aa9f7a4dSmpi 	while ((ch = getopt(argc, argv, "cD:df:Fi:kmnvs:")) != -1) {
827f484f2cfShshoexer 		switch (ch) {
828aa9f7a4dSmpi 		case 'c':
829aa9f7a4dSmpi 			opts |= IPSECCTL_OPT_COLLAPSE;
830aa9f7a4dSmpi 			break;
831aa9f7a4dSmpi 
8323d351c90Sreyk 		case 'D':
8333d351c90Sreyk 			if (cmdline_symset(optarg) < 0)
8343d351c90Sreyk 				warnx("could not parse macro definition %s",
8353d351c90Sreyk 				    optarg);
8363d351c90Sreyk 			break;
8373cb2a986Smarkus 
8386d01698dShshoexer 		case 'd':
8396d01698dShshoexer 			opts |= IPSECCTL_OPT_DELETE;
8406d01698dShshoexer 			break;
8413cb2a986Smarkus 
842f484f2cfShshoexer 		case 'f':
843f484f2cfShshoexer 			rulesopt = optarg;
844f484f2cfShshoexer 			break;
845f484f2cfShshoexer 
846f484f2cfShshoexer 		case 'F':
847f484f2cfShshoexer 			opts |= IPSECCTL_OPT_FLUSH;
848f484f2cfShshoexer 			break;
849f484f2cfShshoexer 
850f8738d8bShenning 		case 'i':
851f8738d8bShenning 			isakmpd_fifo = optarg;
852f8738d8bShenning 			break;
853f8738d8bShenning 
8543cb2a986Smarkus 		case 'k':
8553cb2a986Smarkus 			opts |= IPSECCTL_OPT_SHOWKEY;
8563cb2a986Smarkus 			break;
8573cb2a986Smarkus 
8588a87fca6Smsf 		case 'm':
8598a87fca6Smsf 			opts |= IPSECCTL_OPT_MONITOR;
8608a87fca6Smsf 			break;
8618a87fca6Smsf 
862f484f2cfShshoexer 		case 'n':
863f484f2cfShshoexer 			opts |= IPSECCTL_OPT_NOACTION;
864f484f2cfShshoexer 			break;
865f484f2cfShshoexer 
866f484f2cfShshoexer 		case 'v':
867f484f2cfShshoexer 			if (opts & IPSECCTL_OPT_VERBOSE)
868f484f2cfShshoexer 				opts |= IPSECCTL_OPT_VERBOSE2;
869f484f2cfShshoexer 			opts |= IPSECCTL_OPT_VERBOSE;
870f484f2cfShshoexer 			break;
871f484f2cfShshoexer 
872f484f2cfShshoexer 		case 's':
8733dd59ad2Shshoexer 			showopt = ipsecctl_lookup_option(optarg, showopt_list);
8743dd59ad2Shshoexer 			if (showopt == NULL) {
8753dd59ad2Shshoexer 				warnx("Unknown show modifier '%s'", optarg);
8763dd59ad2Shshoexer 				usage();
8773dd59ad2Shshoexer 				/* NOTREACHED */
8783dd59ad2Shshoexer 			}
879f484f2cfShshoexer 			opts |= IPSECCTL_OPT_SHOW;
880f484f2cfShshoexer 			break;
881f484f2cfShshoexer 
882f484f2cfShshoexer 		default:
883f484f2cfShshoexer 			usage();
884f484f2cfShshoexer 			/* NOTREACHED */
885f484f2cfShshoexer 		}
886f484f2cfShshoexer 	}
887f484f2cfShshoexer 
888f484f2cfShshoexer 	if (argc != optind) {
889f484f2cfShshoexer 		warnx("unknown command line argument: %s ...", argv[optind]);
890f484f2cfShshoexer 		usage();
891f484f2cfShshoexer 		/* NOTREACHED */
892f484f2cfShshoexer 	}
893f484f2cfShshoexer 	if (opts & IPSECCTL_OPT_FLUSH)
894f484f2cfShshoexer 		if (ipsecctl_flush(opts))
895f484f2cfShshoexer 			error = 1;
896f484f2cfShshoexer 
897f484f2cfShshoexer 	if (rulesopt != NULL)
898f484f2cfShshoexer 		if (ipsecctl_rules(rulesopt, opts))
899f484f2cfShshoexer 			error = 1;
900f484f2cfShshoexer 
9013dd59ad2Shshoexer 	if (showopt != NULL) {
9023dd59ad2Shshoexer 		switch (*showopt) {
9033dd59ad2Shshoexer 		case 'f':
904202aab43Stobhe 			opts |= IPSECCTL_OPT_SHOWFLOWS;
9053dd59ad2Shshoexer 			break;
9063dd59ad2Shshoexer 		case 's':
907202aab43Stobhe 			opts |= IPSECCTL_OPT_SHOWSAS;
9083dd59ad2Shshoexer 			break;
9093dd59ad2Shshoexer 		case 'a':
910202aab43Stobhe 			opts |= IPSECCTL_OPT_SHOWFLOWS;
911202aab43Stobhe 			opts |= IPSECCTL_OPT_SHOWSAS;
9123dd59ad2Shshoexer 			opts |= IPSECCTL_OPT_SHOWALL;
913202aab43Stobhe 			break;
9143dd59ad2Shshoexer 		}
915202aab43Stobhe 		ipsecctl_show(opts);
9163dd59ad2Shshoexer 	}
917f484f2cfShshoexer 
9188a87fca6Smsf 	if (opts & IPSECCTL_OPT_MONITOR)
9198a87fca6Smsf 		if (ipsecctl_monitor(opts))
9208a87fca6Smsf 			error = 1;
9218a87fca6Smsf 
922f484f2cfShshoexer 	exit(error);
923f484f2cfShshoexer }
92491f765ddShshoexer 
92591f765ddShshoexer static int
unmask(struct ipsec_addr * ipa)926eaa84e12Skn unmask(struct ipsec_addr *ipa)
92791f765ddShshoexer {
92891f765ddShshoexer 	int		i = 31, j = 0, b = 0;
92991f765ddShshoexer 	u_int32_t	tmp;
93091f765ddShshoexer 
93191f765ddShshoexer 	while (j < 4 && ipa->addr32[j] == 0xffffffff) {
93291f765ddShshoexer 		b += 32;
93391f765ddShshoexer 		j++;
93491f765ddShshoexer 	}
93591f765ddShshoexer 	if (j < 4) {
93691f765ddShshoexer 		tmp = ntohl(ipa->addr32[j]);
93791f765ddShshoexer 		for (i = 31; tmp & (1 << i); --i)
93891f765ddShshoexer 			b++;
93991f765ddShshoexer 	}
94091f765ddShshoexer 	return (b);
94191f765ddShshoexer }
942