xref: /linux/tools/testing/selftests/net/ovpn/ovpn-cli.c (revision 2c7e4a2663a1ab5a740c59c31991579b6b865a26)
1959bc330SAntonio Quartulli // SPDX-License-Identifier: GPL-2.0
2959bc330SAntonio Quartulli /*  OpenVPN data channel accelerator
3959bc330SAntonio Quartulli  *
4959bc330SAntonio Quartulli  *  Copyright (C) 2020-2025 OpenVPN, Inc.
5959bc330SAntonio Quartulli  *
6959bc330SAntonio Quartulli  *  Author:	Antonio Quartulli <antonio@openvpn.net>
7959bc330SAntonio Quartulli  */
8959bc330SAntonio Quartulli 
9959bc330SAntonio Quartulli #include <stdio.h>
10959bc330SAntonio Quartulli #include <inttypes.h>
11959bc330SAntonio Quartulli #include <stdbool.h>
12959bc330SAntonio Quartulli #include <string.h>
13959bc330SAntonio Quartulli #include <errno.h>
14959bc330SAntonio Quartulli #include <unistd.h>
15959bc330SAntonio Quartulli #include <arpa/inet.h>
16959bc330SAntonio Quartulli #include <net/if.h>
17959bc330SAntonio Quartulli #include <netinet/in.h>
18959bc330SAntonio Quartulli #include <time.h>
19959bc330SAntonio Quartulli 
20959bc330SAntonio Quartulli #include <linux/ovpn.h>
21959bc330SAntonio Quartulli #include <linux/types.h>
22959bc330SAntonio Quartulli #include <linux/netlink.h>
23959bc330SAntonio Quartulli 
24959bc330SAntonio Quartulli #include <netlink/socket.h>
25959bc330SAntonio Quartulli #include <netlink/netlink.h>
26959bc330SAntonio Quartulli #include <netlink/genl/genl.h>
27959bc330SAntonio Quartulli #include <netlink/genl/family.h>
28959bc330SAntonio Quartulli #include <netlink/genl/ctrl.h>
29959bc330SAntonio Quartulli 
30959bc330SAntonio Quartulli #include <mbedtls/base64.h>
31959bc330SAntonio Quartulli #include <mbedtls/error.h>
32959bc330SAntonio Quartulli 
33959bc330SAntonio Quartulli #include <sys/socket.h>
34959bc330SAntonio Quartulli 
35959bc330SAntonio Quartulli /* defines to make checkpatch happy */
36959bc330SAntonio Quartulli #define strscpy strncpy
37959bc330SAntonio Quartulli #define __always_unused __attribute__((__unused__))
38959bc330SAntonio Quartulli 
39959bc330SAntonio Quartulli /* libnl < 3.5.0 does not set the NLA_F_NESTED on its own, therefore we
40959bc330SAntonio Quartulli  * have to explicitly do it to prevent the kernel from failing upon
41959bc330SAntonio Quartulli  * parsing of the message
42959bc330SAntonio Quartulli  */
43959bc330SAntonio Quartulli #define nla_nest_start(_msg, _type) \
44959bc330SAntonio Quartulli 	nla_nest_start(_msg, (_type) | NLA_F_NESTED)
45959bc330SAntonio Quartulli 
46959bc330SAntonio Quartulli /* libnl < 3.11.0 does not implement nla_get_uint() */
ovpn_nla_get_uint(struct nlattr * attr)47959bc330SAntonio Quartulli uint64_t ovpn_nla_get_uint(struct nlattr *attr)
48959bc330SAntonio Quartulli {
49959bc330SAntonio Quartulli 	if (nla_len(attr) == sizeof(uint32_t))
50959bc330SAntonio Quartulli 		return nla_get_u32(attr);
51959bc330SAntonio Quartulli 	else
52959bc330SAntonio Quartulli 		return nla_get_u64(attr);
53959bc330SAntonio Quartulli }
54959bc330SAntonio Quartulli 
55959bc330SAntonio Quartulli typedef int (*ovpn_nl_cb)(struct nl_msg *msg, void *arg);
56959bc330SAntonio Quartulli 
57959bc330SAntonio Quartulli enum ovpn_key_direction {
58959bc330SAntonio Quartulli 	KEY_DIR_IN = 0,
59959bc330SAntonio Quartulli 	KEY_DIR_OUT,
60959bc330SAntonio Quartulli };
61959bc330SAntonio Quartulli 
62959bc330SAntonio Quartulli #define KEY_LEN (256 / 8)
63959bc330SAntonio Quartulli #define NONCE_LEN 8
64959bc330SAntonio Quartulli 
65959bc330SAntonio Quartulli #define PEER_ID_UNDEF 0x00FFFFFF
66959bc330SAntonio Quartulli #define MAX_PEERS 10
67959bc330SAntonio Quartulli 
68959bc330SAntonio Quartulli struct nl_ctx {
69959bc330SAntonio Quartulli 	struct nl_sock *nl_sock;
70959bc330SAntonio Quartulli 	struct nl_msg *nl_msg;
71959bc330SAntonio Quartulli 	struct nl_cb *nl_cb;
72959bc330SAntonio Quartulli 
73959bc330SAntonio Quartulli 	int ovpn_dco_id;
74959bc330SAntonio Quartulli };
75959bc330SAntonio Quartulli 
76959bc330SAntonio Quartulli enum ovpn_cmd {
77959bc330SAntonio Quartulli 	CMD_INVALID,
78959bc330SAntonio Quartulli 	CMD_NEW_IFACE,
79959bc330SAntonio Quartulli 	CMD_DEL_IFACE,
80959bc330SAntonio Quartulli 	CMD_LISTEN,
81959bc330SAntonio Quartulli 	CMD_CONNECT,
82959bc330SAntonio Quartulli 	CMD_NEW_PEER,
83959bc330SAntonio Quartulli 	CMD_NEW_MULTI_PEER,
84959bc330SAntonio Quartulli 	CMD_SET_PEER,
85959bc330SAntonio Quartulli 	CMD_DEL_PEER,
86959bc330SAntonio Quartulli 	CMD_GET_PEER,
87959bc330SAntonio Quartulli 	CMD_NEW_KEY,
88959bc330SAntonio Quartulli 	CMD_DEL_KEY,
89959bc330SAntonio Quartulli 	CMD_GET_KEY,
90959bc330SAntonio Quartulli 	CMD_SWAP_KEYS,
91959bc330SAntonio Quartulli 	CMD_LISTEN_MCAST,
92959bc330SAntonio Quartulli };
93959bc330SAntonio Quartulli 
94959bc330SAntonio Quartulli struct ovpn_ctx {
95959bc330SAntonio Quartulli 	enum ovpn_cmd cmd;
96959bc330SAntonio Quartulli 
97959bc330SAntonio Quartulli 	__u8 key_enc[KEY_LEN];
98959bc330SAntonio Quartulli 	__u8 key_dec[KEY_LEN];
99959bc330SAntonio Quartulli 	__u8 nonce[NONCE_LEN];
100959bc330SAntonio Quartulli 
101959bc330SAntonio Quartulli 	enum ovpn_cipher_alg cipher;
102959bc330SAntonio Quartulli 
103959bc330SAntonio Quartulli 	sa_family_t sa_family;
104959bc330SAntonio Quartulli 
105959bc330SAntonio Quartulli 	unsigned long peer_id;
106959bc330SAntonio Quartulli 	unsigned long lport;
107959bc330SAntonio Quartulli 
108959bc330SAntonio Quartulli 	union {
109959bc330SAntonio Quartulli 		struct sockaddr_in in4;
110959bc330SAntonio Quartulli 		struct sockaddr_in6 in6;
111959bc330SAntonio Quartulli 	} remote;
112959bc330SAntonio Quartulli 
113959bc330SAntonio Quartulli 	union {
114959bc330SAntonio Quartulli 		struct sockaddr_in in4;
115959bc330SAntonio Quartulli 		struct sockaddr_in6 in6;
116959bc330SAntonio Quartulli 	} peer_ip;
117959bc330SAntonio Quartulli 
118959bc330SAntonio Quartulli 	bool peer_ip_set;
119959bc330SAntonio Quartulli 
120959bc330SAntonio Quartulli 	unsigned int ifindex;
121959bc330SAntonio Quartulli 	char ifname[IFNAMSIZ];
122959bc330SAntonio Quartulli 	enum ovpn_mode mode;
123959bc330SAntonio Quartulli 	bool mode_set;
124959bc330SAntonio Quartulli 
125959bc330SAntonio Quartulli 	int socket;
126959bc330SAntonio Quartulli 	int cli_sockets[MAX_PEERS];
127959bc330SAntonio Quartulli 
128959bc330SAntonio Quartulli 	__u32 keepalive_interval;
129959bc330SAntonio Quartulli 	__u32 keepalive_timeout;
130959bc330SAntonio Quartulli 
131959bc330SAntonio Quartulli 	enum ovpn_key_direction key_dir;
132959bc330SAntonio Quartulli 	enum ovpn_key_slot key_slot;
133959bc330SAntonio Quartulli 	int key_id;
134959bc330SAntonio Quartulli 
135959bc330SAntonio Quartulli 	const char *peers_file;
136959bc330SAntonio Quartulli };
137959bc330SAntonio Quartulli 
ovpn_nl_recvmsgs(struct nl_ctx * ctx)138959bc330SAntonio Quartulli static int ovpn_nl_recvmsgs(struct nl_ctx *ctx)
139959bc330SAntonio Quartulli {
140959bc330SAntonio Quartulli 	int ret;
141959bc330SAntonio Quartulli 
142959bc330SAntonio Quartulli 	ret = nl_recvmsgs(ctx->nl_sock, ctx->nl_cb);
143959bc330SAntonio Quartulli 
144959bc330SAntonio Quartulli 	switch (ret) {
145959bc330SAntonio Quartulli 	case -NLE_INTR:
146959bc330SAntonio Quartulli 		fprintf(stderr,
147959bc330SAntonio Quartulli 			"netlink received interrupt due to signal - ignoring\n");
148959bc330SAntonio Quartulli 		break;
149959bc330SAntonio Quartulli 	case -NLE_NOMEM:
150959bc330SAntonio Quartulli 		fprintf(stderr, "netlink out of memory error\n");
151959bc330SAntonio Quartulli 		break;
152959bc330SAntonio Quartulli 	case -NLE_AGAIN:
153959bc330SAntonio Quartulli 		fprintf(stderr,
154959bc330SAntonio Quartulli 			"netlink reports blocking read - aborting wait\n");
155959bc330SAntonio Quartulli 		break;
156959bc330SAntonio Quartulli 	default:
157959bc330SAntonio Quartulli 		if (ret)
158959bc330SAntonio Quartulli 			fprintf(stderr, "netlink reports error (%d): %s\n",
159959bc330SAntonio Quartulli 				ret, nl_geterror(-ret));
160959bc330SAntonio Quartulli 		break;
161959bc330SAntonio Quartulli 	}
162959bc330SAntonio Quartulli 
163959bc330SAntonio Quartulli 	return ret;
164959bc330SAntonio Quartulli }
165959bc330SAntonio Quartulli 
nl_ctx_alloc_flags(struct ovpn_ctx * ovpn,int cmd,int flags)166959bc330SAntonio Quartulli static struct nl_ctx *nl_ctx_alloc_flags(struct ovpn_ctx *ovpn, int cmd,
167959bc330SAntonio Quartulli 					 int flags)
168959bc330SAntonio Quartulli {
169959bc330SAntonio Quartulli 	struct nl_ctx *ctx;
170959bc330SAntonio Quartulli 	int err, ret;
171959bc330SAntonio Quartulli 
172959bc330SAntonio Quartulli 	ctx = calloc(1, sizeof(*ctx));
173959bc330SAntonio Quartulli 	if (!ctx)
174959bc330SAntonio Quartulli 		return NULL;
175959bc330SAntonio Quartulli 
176959bc330SAntonio Quartulli 	ctx->nl_sock = nl_socket_alloc();
177959bc330SAntonio Quartulli 	if (!ctx->nl_sock) {
178959bc330SAntonio Quartulli 		fprintf(stderr, "cannot allocate netlink socket\n");
179959bc330SAntonio Quartulli 		goto err_free;
180959bc330SAntonio Quartulli 	}
181959bc330SAntonio Quartulli 
182959bc330SAntonio Quartulli 	nl_socket_set_buffer_size(ctx->nl_sock, 8192, 8192);
183959bc330SAntonio Quartulli 
184959bc330SAntonio Quartulli 	ret = genl_connect(ctx->nl_sock);
185959bc330SAntonio Quartulli 	if (ret) {
186959bc330SAntonio Quartulli 		fprintf(stderr, "cannot connect to generic netlink: %s\n",
187959bc330SAntonio Quartulli 			nl_geterror(ret));
188959bc330SAntonio Quartulli 		goto err_sock;
189959bc330SAntonio Quartulli 	}
190959bc330SAntonio Quartulli 
191959bc330SAntonio Quartulli 	/* enable Extended ACK for detailed error reporting */
192959bc330SAntonio Quartulli 	err = 1;
193959bc330SAntonio Quartulli 	setsockopt(nl_socket_get_fd(ctx->nl_sock), SOL_NETLINK, NETLINK_EXT_ACK,
194959bc330SAntonio Quartulli 		   &err, sizeof(err));
195959bc330SAntonio Quartulli 
196959bc330SAntonio Quartulli 	ctx->ovpn_dco_id = genl_ctrl_resolve(ctx->nl_sock, OVPN_FAMILY_NAME);
197959bc330SAntonio Quartulli 	if (ctx->ovpn_dco_id < 0) {
198959bc330SAntonio Quartulli 		fprintf(stderr, "cannot find ovpn_dco netlink component: %d\n",
199959bc330SAntonio Quartulli 			ctx->ovpn_dco_id);
200959bc330SAntonio Quartulli 		goto err_free;
201959bc330SAntonio Quartulli 	}
202959bc330SAntonio Quartulli 
203959bc330SAntonio Quartulli 	ctx->nl_msg = nlmsg_alloc();
204959bc330SAntonio Quartulli 	if (!ctx->nl_msg) {
205959bc330SAntonio Quartulli 		fprintf(stderr, "cannot allocate netlink message\n");
206959bc330SAntonio Quartulli 		goto err_sock;
207959bc330SAntonio Quartulli 	}
208959bc330SAntonio Quartulli 
209959bc330SAntonio Quartulli 	ctx->nl_cb = nl_cb_alloc(NL_CB_DEFAULT);
210959bc330SAntonio Quartulli 	if (!ctx->nl_cb) {
211959bc330SAntonio Quartulli 		fprintf(stderr, "failed to allocate netlink callback\n");
212959bc330SAntonio Quartulli 		goto err_msg;
213959bc330SAntonio Quartulli 	}
214959bc330SAntonio Quartulli 
215959bc330SAntonio Quartulli 	nl_socket_set_cb(ctx->nl_sock, ctx->nl_cb);
216959bc330SAntonio Quartulli 
217959bc330SAntonio Quartulli 	genlmsg_put(ctx->nl_msg, 0, 0, ctx->ovpn_dco_id, 0, flags, cmd, 0);
218959bc330SAntonio Quartulli 
219959bc330SAntonio Quartulli 	if (ovpn->ifindex > 0)
220959bc330SAntonio Quartulli 		NLA_PUT_U32(ctx->nl_msg, OVPN_A_IFINDEX, ovpn->ifindex);
221959bc330SAntonio Quartulli 
222959bc330SAntonio Quartulli 	return ctx;
223959bc330SAntonio Quartulli nla_put_failure:
224959bc330SAntonio Quartulli err_msg:
225959bc330SAntonio Quartulli 	nlmsg_free(ctx->nl_msg);
226959bc330SAntonio Quartulli err_sock:
227959bc330SAntonio Quartulli 	nl_socket_free(ctx->nl_sock);
228959bc330SAntonio Quartulli err_free:
229959bc330SAntonio Quartulli 	free(ctx);
230959bc330SAntonio Quartulli 	return NULL;
231959bc330SAntonio Quartulli }
232959bc330SAntonio Quartulli 
nl_ctx_alloc(struct ovpn_ctx * ovpn,int cmd)233959bc330SAntonio Quartulli static struct nl_ctx *nl_ctx_alloc(struct ovpn_ctx *ovpn, int cmd)
234959bc330SAntonio Quartulli {
235959bc330SAntonio Quartulli 	return nl_ctx_alloc_flags(ovpn, cmd, 0);
236959bc330SAntonio Quartulli }
237959bc330SAntonio Quartulli 
nl_ctx_free(struct nl_ctx * ctx)238959bc330SAntonio Quartulli static void nl_ctx_free(struct nl_ctx *ctx)
239959bc330SAntonio Quartulli {
240959bc330SAntonio Quartulli 	if (!ctx)
241959bc330SAntonio Quartulli 		return;
242959bc330SAntonio Quartulli 
243959bc330SAntonio Quartulli 	nl_socket_free(ctx->nl_sock);
244959bc330SAntonio Quartulli 	nlmsg_free(ctx->nl_msg);
245959bc330SAntonio Quartulli 	nl_cb_put(ctx->nl_cb);
246959bc330SAntonio Quartulli 	free(ctx);
247959bc330SAntonio Quartulli }
248959bc330SAntonio Quartulli 
ovpn_nl_cb_error(struct sockaddr_nl (* nla)__always_unused,struct nlmsgerr * err,void * arg)249959bc330SAntonio Quartulli static int ovpn_nl_cb_error(struct sockaddr_nl (*nla)__always_unused,
250959bc330SAntonio Quartulli 			    struct nlmsgerr *err, void *arg)
251959bc330SAntonio Quartulli {
252959bc330SAntonio Quartulli 	struct nlmsghdr *nlh = (struct nlmsghdr *)err - 1;
253959bc330SAntonio Quartulli 	struct nlattr *tb_msg[NLMSGERR_ATTR_MAX + 1];
254959bc330SAntonio Quartulli 	int len = nlh->nlmsg_len;
255959bc330SAntonio Quartulli 	struct nlattr *attrs;
256959bc330SAntonio Quartulli 	int *ret = arg;
257959bc330SAntonio Quartulli 	int ack_len = sizeof(*nlh) + sizeof(int) + sizeof(*nlh);
258959bc330SAntonio Quartulli 
259959bc330SAntonio Quartulli 	*ret = err->error;
260959bc330SAntonio Quartulli 
261959bc330SAntonio Quartulli 	if (!(nlh->nlmsg_flags & NLM_F_ACK_TLVS))
262959bc330SAntonio Quartulli 		return NL_STOP;
263959bc330SAntonio Quartulli 
264959bc330SAntonio Quartulli 	if (!(nlh->nlmsg_flags & NLM_F_CAPPED))
265959bc330SAntonio Quartulli 		ack_len += err->msg.nlmsg_len - sizeof(*nlh);
266959bc330SAntonio Quartulli 
267959bc330SAntonio Quartulli 	if (len <= ack_len)
268959bc330SAntonio Quartulli 		return NL_STOP;
269959bc330SAntonio Quartulli 
270959bc330SAntonio Quartulli 	attrs = (void *)((uint8_t *)nlh + ack_len);
271959bc330SAntonio Quartulli 	len -= ack_len;
272959bc330SAntonio Quartulli 
273959bc330SAntonio Quartulli 	nla_parse(tb_msg, NLMSGERR_ATTR_MAX, attrs, len, NULL);
274959bc330SAntonio Quartulli 	if (tb_msg[NLMSGERR_ATTR_MSG]) {
275959bc330SAntonio Quartulli 		len = strnlen((char *)nla_data(tb_msg[NLMSGERR_ATTR_MSG]),
276959bc330SAntonio Quartulli 			      nla_len(tb_msg[NLMSGERR_ATTR_MSG]));
277959bc330SAntonio Quartulli 		fprintf(stderr, "kernel error: %*s\n", len,
278959bc330SAntonio Quartulli 			(char *)nla_data(tb_msg[NLMSGERR_ATTR_MSG]));
279959bc330SAntonio Quartulli 	}
280959bc330SAntonio Quartulli 
281959bc330SAntonio Quartulli 	if (tb_msg[NLMSGERR_ATTR_MISS_NEST]) {
282959bc330SAntonio Quartulli 		fprintf(stderr, "missing required nesting type %u\n",
283959bc330SAntonio Quartulli 			nla_get_u32(tb_msg[NLMSGERR_ATTR_MISS_NEST]));
284959bc330SAntonio Quartulli 	}
285959bc330SAntonio Quartulli 
286959bc330SAntonio Quartulli 	if (tb_msg[NLMSGERR_ATTR_MISS_TYPE]) {
287959bc330SAntonio Quartulli 		fprintf(stderr, "missing required attribute type %u\n",
288959bc330SAntonio Quartulli 			nla_get_u32(tb_msg[NLMSGERR_ATTR_MISS_TYPE]));
289959bc330SAntonio Quartulli 	}
290959bc330SAntonio Quartulli 
291959bc330SAntonio Quartulli 	return NL_STOP;
292959bc330SAntonio Quartulli }
293959bc330SAntonio Quartulli 
ovpn_nl_cb_finish(struct nl_msg (* msg)__always_unused,void * arg)294959bc330SAntonio Quartulli static int ovpn_nl_cb_finish(struct nl_msg (*msg)__always_unused,
295959bc330SAntonio Quartulli 			     void *arg)
296959bc330SAntonio Quartulli {
297959bc330SAntonio Quartulli 	int *status = arg;
298959bc330SAntonio Quartulli 
299959bc330SAntonio Quartulli 	*status = 0;
300959bc330SAntonio Quartulli 	return NL_SKIP;
301959bc330SAntonio Quartulli }
302959bc330SAntonio Quartulli 
ovpn_nl_cb_ack(struct nl_msg (* msg)__always_unused,void * arg)303959bc330SAntonio Quartulli static int ovpn_nl_cb_ack(struct nl_msg (*msg)__always_unused,
304959bc330SAntonio Quartulli 			  void *arg)
305959bc330SAntonio Quartulli {
306959bc330SAntonio Quartulli 	int *status = arg;
307959bc330SAntonio Quartulli 
308959bc330SAntonio Quartulli 	*status = 0;
309959bc330SAntonio Quartulli 	return NL_STOP;
310959bc330SAntonio Quartulli }
311959bc330SAntonio Quartulli 
ovpn_nl_msg_send(struct nl_ctx * ctx,ovpn_nl_cb cb)312959bc330SAntonio Quartulli static int ovpn_nl_msg_send(struct nl_ctx *ctx, ovpn_nl_cb cb)
313959bc330SAntonio Quartulli {
314959bc330SAntonio Quartulli 	int status = 1;
315959bc330SAntonio Quartulli 
316959bc330SAntonio Quartulli 	nl_cb_err(ctx->nl_cb, NL_CB_CUSTOM, ovpn_nl_cb_error, &status);
317959bc330SAntonio Quartulli 	nl_cb_set(ctx->nl_cb, NL_CB_FINISH, NL_CB_CUSTOM, ovpn_nl_cb_finish,
318959bc330SAntonio Quartulli 		  &status);
319959bc330SAntonio Quartulli 	nl_cb_set(ctx->nl_cb, NL_CB_ACK, NL_CB_CUSTOM, ovpn_nl_cb_ack, &status);
320959bc330SAntonio Quartulli 
321959bc330SAntonio Quartulli 	if (cb)
322959bc330SAntonio Quartulli 		nl_cb_set(ctx->nl_cb, NL_CB_VALID, NL_CB_CUSTOM, cb, ctx);
323959bc330SAntonio Quartulli 
324959bc330SAntonio Quartulli 	nl_send_auto_complete(ctx->nl_sock, ctx->nl_msg);
325959bc330SAntonio Quartulli 
326959bc330SAntonio Quartulli 	while (status == 1)
327959bc330SAntonio Quartulli 		ovpn_nl_recvmsgs(ctx);
328959bc330SAntonio Quartulli 
329959bc330SAntonio Quartulli 	if (status < 0)
330959bc330SAntonio Quartulli 		fprintf(stderr, "failed to send netlink message: %s (%d)\n",
331959bc330SAntonio Quartulli 			strerror(-status), status);
332959bc330SAntonio Quartulli 
333959bc330SAntonio Quartulli 	return status;
334959bc330SAntonio Quartulli }
335959bc330SAntonio Quartulli 
ovpn_parse_key(const char * file,struct ovpn_ctx * ctx)336959bc330SAntonio Quartulli static int ovpn_parse_key(const char *file, struct ovpn_ctx *ctx)
337959bc330SAntonio Quartulli {
338959bc330SAntonio Quartulli 	int idx_enc, idx_dec, ret = -1;
339959bc330SAntonio Quartulli 	unsigned char *ckey = NULL;
340959bc330SAntonio Quartulli 	__u8 *bkey = NULL;
341959bc330SAntonio Quartulli 	size_t olen = 0;
342959bc330SAntonio Quartulli 	long ckey_len;
343959bc330SAntonio Quartulli 	FILE *fp;
344959bc330SAntonio Quartulli 
345959bc330SAntonio Quartulli 	fp = fopen(file, "r");
346959bc330SAntonio Quartulli 	if (!fp) {
347959bc330SAntonio Quartulli 		fprintf(stderr, "cannot open: %s\n", file);
348959bc330SAntonio Quartulli 		return -1;
349959bc330SAntonio Quartulli 	}
350959bc330SAntonio Quartulli 
351959bc330SAntonio Quartulli 	/* get file size */
352959bc330SAntonio Quartulli 	fseek(fp, 0L, SEEK_END);
353959bc330SAntonio Quartulli 	ckey_len = ftell(fp);
354959bc330SAntonio Quartulli 	rewind(fp);
355959bc330SAntonio Quartulli 
356959bc330SAntonio Quartulli 	/* if the file is longer, let's just read a portion */
357959bc330SAntonio Quartulli 	if (ckey_len > 256)
358959bc330SAntonio Quartulli 		ckey_len = 256;
359959bc330SAntonio Quartulli 
360959bc330SAntonio Quartulli 	ckey = malloc(ckey_len);
361959bc330SAntonio Quartulli 	if (!ckey)
362959bc330SAntonio Quartulli 		goto err;
363959bc330SAntonio Quartulli 
364959bc330SAntonio Quartulli 	ret = fread(ckey, 1, ckey_len, fp);
365959bc330SAntonio Quartulli 	if (ret != ckey_len) {
366959bc330SAntonio Quartulli 		fprintf(stderr,
367959bc330SAntonio Quartulli 			"couldn't read enough data from key file: %dbytes read\n",
368959bc330SAntonio Quartulli 			ret);
369959bc330SAntonio Quartulli 		goto err;
370959bc330SAntonio Quartulli 	}
371959bc330SAntonio Quartulli 
372959bc330SAntonio Quartulli 	olen = 0;
373959bc330SAntonio Quartulli 	ret = mbedtls_base64_decode(NULL, 0, &olen, ckey, ckey_len);
374959bc330SAntonio Quartulli 	if (ret != MBEDTLS_ERR_BASE64_BUFFER_TOO_SMALL) {
375959bc330SAntonio Quartulli 		char buf[256];
376959bc330SAntonio Quartulli 
377959bc330SAntonio Quartulli 		mbedtls_strerror(ret, buf, sizeof(buf));
378959bc330SAntonio Quartulli 		fprintf(stderr, "unexpected base64 error1: %s (%d)\n", buf,
379959bc330SAntonio Quartulli 			ret);
380959bc330SAntonio Quartulli 
381959bc330SAntonio Quartulli 		goto err;
382959bc330SAntonio Quartulli 	}
383959bc330SAntonio Quartulli 
384959bc330SAntonio Quartulli 	bkey = malloc(olen);
385959bc330SAntonio Quartulli 	if (!bkey) {
386959bc330SAntonio Quartulli 		fprintf(stderr, "cannot allocate binary key buffer\n");
387959bc330SAntonio Quartulli 		goto err;
388959bc330SAntonio Quartulli 	}
389959bc330SAntonio Quartulli 
390959bc330SAntonio Quartulli 	ret = mbedtls_base64_decode(bkey, olen, &olen, ckey, ckey_len);
391959bc330SAntonio Quartulli 	if (ret) {
392959bc330SAntonio Quartulli 		char buf[256];
393959bc330SAntonio Quartulli 
394959bc330SAntonio Quartulli 		mbedtls_strerror(ret, buf, sizeof(buf));
395959bc330SAntonio Quartulli 		fprintf(stderr, "unexpected base64 error2: %s (%d)\n", buf,
396959bc330SAntonio Quartulli 			ret);
397959bc330SAntonio Quartulli 
398959bc330SAntonio Quartulli 		goto err;
399959bc330SAntonio Quartulli 	}
400959bc330SAntonio Quartulli 
401959bc330SAntonio Quartulli 	if (olen < 2 * KEY_LEN + NONCE_LEN) {
402959bc330SAntonio Quartulli 		fprintf(stderr,
403959bc330SAntonio Quartulli 			"not enough data in key file, found %zdB but needs %dB\n",
404959bc330SAntonio Quartulli 			olen, 2 * KEY_LEN + NONCE_LEN);
405959bc330SAntonio Quartulli 		goto err;
406959bc330SAntonio Quartulli 	}
407959bc330SAntonio Quartulli 
408959bc330SAntonio Quartulli 	switch (ctx->key_dir) {
409959bc330SAntonio Quartulli 	case KEY_DIR_IN:
410959bc330SAntonio Quartulli 		idx_enc = 0;
411959bc330SAntonio Quartulli 		idx_dec = 1;
412959bc330SAntonio Quartulli 		break;
413959bc330SAntonio Quartulli 	case KEY_DIR_OUT:
414959bc330SAntonio Quartulli 		idx_enc = 1;
415959bc330SAntonio Quartulli 		idx_dec = 0;
416959bc330SAntonio Quartulli 		break;
417959bc330SAntonio Quartulli 	default:
418959bc330SAntonio Quartulli 		goto err;
419959bc330SAntonio Quartulli 	}
420959bc330SAntonio Quartulli 
421959bc330SAntonio Quartulli 	memcpy(ctx->key_enc, bkey + KEY_LEN * idx_enc, KEY_LEN);
422959bc330SAntonio Quartulli 	memcpy(ctx->key_dec, bkey + KEY_LEN * idx_dec, KEY_LEN);
423959bc330SAntonio Quartulli 	memcpy(ctx->nonce, bkey + 2 * KEY_LEN, NONCE_LEN);
424959bc330SAntonio Quartulli 
425959bc330SAntonio Quartulli 	ret = 0;
426959bc330SAntonio Quartulli 
427959bc330SAntonio Quartulli err:
428959bc330SAntonio Quartulli 	fclose(fp);
429959bc330SAntonio Quartulli 	free(bkey);
430959bc330SAntonio Quartulli 	free(ckey);
431959bc330SAntonio Quartulli 
432959bc330SAntonio Quartulli 	return ret;
433959bc330SAntonio Quartulli }
434959bc330SAntonio Quartulli 
ovpn_parse_cipher(const char * cipher,struct ovpn_ctx * ctx)435959bc330SAntonio Quartulli static int ovpn_parse_cipher(const char *cipher, struct ovpn_ctx *ctx)
436959bc330SAntonio Quartulli {
437959bc330SAntonio Quartulli 	if (strcmp(cipher, "aes") == 0)
438959bc330SAntonio Quartulli 		ctx->cipher = OVPN_CIPHER_ALG_AES_GCM;
439959bc330SAntonio Quartulli 	else if (strcmp(cipher, "chachapoly") == 0)
440959bc330SAntonio Quartulli 		ctx->cipher = OVPN_CIPHER_ALG_CHACHA20_POLY1305;
441959bc330SAntonio Quartulli 	else if (strcmp(cipher, "none") == 0)
442959bc330SAntonio Quartulli 		ctx->cipher = OVPN_CIPHER_ALG_NONE;
443959bc330SAntonio Quartulli 	else
444959bc330SAntonio Quartulli 		return -ENOTSUP;
445959bc330SAntonio Quartulli 
446959bc330SAntonio Quartulli 	return 0;
447959bc330SAntonio Quartulli }
448959bc330SAntonio Quartulli 
ovpn_parse_key_direction(const char * dir,struct ovpn_ctx * ctx)449959bc330SAntonio Quartulli static int ovpn_parse_key_direction(const char *dir, struct ovpn_ctx *ctx)
450959bc330SAntonio Quartulli {
451959bc330SAntonio Quartulli 	int in_dir;
452959bc330SAntonio Quartulli 
453959bc330SAntonio Quartulli 	in_dir = strtoll(dir, NULL, 10);
454959bc330SAntonio Quartulli 	switch (in_dir) {
455959bc330SAntonio Quartulli 	case KEY_DIR_IN:
456959bc330SAntonio Quartulli 	case KEY_DIR_OUT:
457959bc330SAntonio Quartulli 		ctx->key_dir = in_dir;
458959bc330SAntonio Quartulli 		break;
459959bc330SAntonio Quartulli 	default:
460959bc330SAntonio Quartulli 		fprintf(stderr,
461959bc330SAntonio Quartulli 			"invalid key direction provided. Can be 0 or 1 only\n");
462959bc330SAntonio Quartulli 		return -1;
463959bc330SAntonio Quartulli 	}
464959bc330SAntonio Quartulli 
465959bc330SAntonio Quartulli 	return 0;
466959bc330SAntonio Quartulli }
467959bc330SAntonio Quartulli 
ovpn_socket(struct ovpn_ctx * ctx,sa_family_t family,int proto)468959bc330SAntonio Quartulli static int ovpn_socket(struct ovpn_ctx *ctx, sa_family_t family, int proto)
469959bc330SAntonio Quartulli {
470959bc330SAntonio Quartulli 	struct sockaddr_storage local_sock = { 0 };
471959bc330SAntonio Quartulli 	struct sockaddr_in6 *in6;
472959bc330SAntonio Quartulli 	struct sockaddr_in *in;
473959bc330SAntonio Quartulli 	int ret, s, sock_type;
474959bc330SAntonio Quartulli 	size_t sock_len;
475959bc330SAntonio Quartulli 
476959bc330SAntonio Quartulli 	if (proto == IPPROTO_UDP)
477959bc330SAntonio Quartulli 		sock_type = SOCK_DGRAM;
478959bc330SAntonio Quartulli 	else if (proto == IPPROTO_TCP)
479959bc330SAntonio Quartulli 		sock_type = SOCK_STREAM;
480959bc330SAntonio Quartulli 	else
481959bc330SAntonio Quartulli 		return -EINVAL;
482959bc330SAntonio Quartulli 
483959bc330SAntonio Quartulli 	s = socket(family, sock_type, 0);
484959bc330SAntonio Quartulli 	if (s < 0) {
485959bc330SAntonio Quartulli 		perror("cannot create socket");
486959bc330SAntonio Quartulli 		return -1;
487959bc330SAntonio Quartulli 	}
488959bc330SAntonio Quartulli 
489959bc330SAntonio Quartulli 	switch (family) {
490959bc330SAntonio Quartulli 	case AF_INET:
491959bc330SAntonio Quartulli 		in = (struct sockaddr_in *)&local_sock;
492959bc330SAntonio Quartulli 		in->sin_family = family;
493959bc330SAntonio Quartulli 		in->sin_port = htons(ctx->lport);
494959bc330SAntonio Quartulli 		in->sin_addr.s_addr = htonl(INADDR_ANY);
495959bc330SAntonio Quartulli 		sock_len = sizeof(*in);
496959bc330SAntonio Quartulli 		break;
497959bc330SAntonio Quartulli 	case AF_INET6:
498959bc330SAntonio Quartulli 		in6 = (struct sockaddr_in6 *)&local_sock;
499959bc330SAntonio Quartulli 		in6->sin6_family = family;
500959bc330SAntonio Quartulli 		in6->sin6_port = htons(ctx->lport);
501959bc330SAntonio Quartulli 		in6->sin6_addr = in6addr_any;
502959bc330SAntonio Quartulli 		sock_len = sizeof(*in6);
503959bc330SAntonio Quartulli 		break;
504959bc330SAntonio Quartulli 	default:
505959bc330SAntonio Quartulli 		return -1;
506959bc330SAntonio Quartulli 	}
507959bc330SAntonio Quartulli 
508959bc330SAntonio Quartulli 	int opt = 1;
509959bc330SAntonio Quartulli 
510959bc330SAntonio Quartulli 	ret = setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));
511959bc330SAntonio Quartulli 
512959bc330SAntonio Quartulli 	if (ret < 0) {
513959bc330SAntonio Quartulli 		perror("setsockopt for SO_REUSEADDR");
514959bc330SAntonio Quartulli 		return ret;
515959bc330SAntonio Quartulli 	}
516959bc330SAntonio Quartulli 
517959bc330SAntonio Quartulli 	ret = setsockopt(s, SOL_SOCKET, SO_REUSEPORT, &opt, sizeof(opt));
518959bc330SAntonio Quartulli 	if (ret < 0) {
519959bc330SAntonio Quartulli 		perror("setsockopt for SO_REUSEPORT");
520959bc330SAntonio Quartulli 		return ret;
521959bc330SAntonio Quartulli 	}
522959bc330SAntonio Quartulli 
523959bc330SAntonio Quartulli 	if (family == AF_INET6) {
524959bc330SAntonio Quartulli 		opt = 0;
525959bc330SAntonio Quartulli 		if (setsockopt(s, IPPROTO_IPV6, IPV6_V6ONLY, &opt,
526959bc330SAntonio Quartulli 			       sizeof(opt))) {
527959bc330SAntonio Quartulli 			perror("failed to set IPV6_V6ONLY");
528959bc330SAntonio Quartulli 			return -1;
529959bc330SAntonio Quartulli 		}
530959bc330SAntonio Quartulli 	}
531959bc330SAntonio Quartulli 
532959bc330SAntonio Quartulli 	ret = bind(s, (struct sockaddr *)&local_sock, sock_len);
533959bc330SAntonio Quartulli 	if (ret < 0) {
534959bc330SAntonio Quartulli 		perror("cannot bind socket");
535959bc330SAntonio Quartulli 		goto err_socket;
536959bc330SAntonio Quartulli 	}
537959bc330SAntonio Quartulli 
538959bc330SAntonio Quartulli 	ctx->socket = s;
539959bc330SAntonio Quartulli 	ctx->sa_family = family;
540959bc330SAntonio Quartulli 	return 0;
541959bc330SAntonio Quartulli 
542959bc330SAntonio Quartulli err_socket:
543959bc330SAntonio Quartulli 	close(s);
544959bc330SAntonio Quartulli 	return -1;
545959bc330SAntonio Quartulli }
546959bc330SAntonio Quartulli 
ovpn_udp_socket(struct ovpn_ctx * ctx,sa_family_t family)547959bc330SAntonio Quartulli static int ovpn_udp_socket(struct ovpn_ctx *ctx, sa_family_t family)
548959bc330SAntonio Quartulli {
549959bc330SAntonio Quartulli 	return ovpn_socket(ctx, family, IPPROTO_UDP);
550959bc330SAntonio Quartulli }
551959bc330SAntonio Quartulli 
ovpn_listen(struct ovpn_ctx * ctx,sa_family_t family)552959bc330SAntonio Quartulli static int ovpn_listen(struct ovpn_ctx *ctx, sa_family_t family)
553959bc330SAntonio Quartulli {
554959bc330SAntonio Quartulli 	int ret;
555959bc330SAntonio Quartulli 
556959bc330SAntonio Quartulli 	ret = ovpn_socket(ctx, family, IPPROTO_TCP);
557959bc330SAntonio Quartulli 	if (ret < 0)
558959bc330SAntonio Quartulli 		return ret;
559959bc330SAntonio Quartulli 
560959bc330SAntonio Quartulli 	ret = listen(ctx->socket, 10);
561959bc330SAntonio Quartulli 	if (ret < 0) {
562959bc330SAntonio Quartulli 		perror("listen");
563959bc330SAntonio Quartulli 		close(ctx->socket);
564959bc330SAntonio Quartulli 		return -1;
565959bc330SAntonio Quartulli 	}
566959bc330SAntonio Quartulli 
567959bc330SAntonio Quartulli 	return 0;
568959bc330SAntonio Quartulli }
569959bc330SAntonio Quartulli 
ovpn_accept(struct ovpn_ctx * ctx)570959bc330SAntonio Quartulli static int ovpn_accept(struct ovpn_ctx *ctx)
571959bc330SAntonio Quartulli {
572959bc330SAntonio Quartulli 	socklen_t socklen;
573959bc330SAntonio Quartulli 	int ret;
574959bc330SAntonio Quartulli 
575959bc330SAntonio Quartulli 	socklen = sizeof(ctx->remote);
576959bc330SAntonio Quartulli 	ret = accept(ctx->socket, (struct sockaddr *)&ctx->remote, &socklen);
577959bc330SAntonio Quartulli 	if (ret < 0) {
578959bc330SAntonio Quartulli 		perror("accept");
579959bc330SAntonio Quartulli 		goto err;
580959bc330SAntonio Quartulli 	}
581959bc330SAntonio Quartulli 
582959bc330SAntonio Quartulli 	fprintf(stderr, "Connection received!\n");
583959bc330SAntonio Quartulli 
584959bc330SAntonio Quartulli 	switch (socklen) {
585959bc330SAntonio Quartulli 	case sizeof(struct sockaddr_in):
586959bc330SAntonio Quartulli 	case sizeof(struct sockaddr_in6):
587959bc330SAntonio Quartulli 		break;
588959bc330SAntonio Quartulli 	default:
589959bc330SAntonio Quartulli 		fprintf(stderr, "error: expecting IPv4 or IPv6 connection\n");
590959bc330SAntonio Quartulli 		close(ret);
591959bc330SAntonio Quartulli 		ret = -EINVAL;
592959bc330SAntonio Quartulli 		goto err;
593959bc330SAntonio Quartulli 	}
594959bc330SAntonio Quartulli 
595959bc330SAntonio Quartulli 	return ret;
596959bc330SAntonio Quartulli err:
597959bc330SAntonio Quartulli 	close(ctx->socket);
598959bc330SAntonio Quartulli 	return ret;
599959bc330SAntonio Quartulli }
600959bc330SAntonio Quartulli 
ovpn_connect(struct ovpn_ctx * ovpn)601959bc330SAntonio Quartulli static int ovpn_connect(struct ovpn_ctx *ovpn)
602959bc330SAntonio Quartulli {
603959bc330SAntonio Quartulli 	socklen_t socklen;
604959bc330SAntonio Quartulli 	int s, ret;
605959bc330SAntonio Quartulli 
606959bc330SAntonio Quartulli 	s = socket(ovpn->remote.in4.sin_family, SOCK_STREAM, 0);
607959bc330SAntonio Quartulli 	if (s < 0) {
608959bc330SAntonio Quartulli 		perror("cannot create socket");
609959bc330SAntonio Quartulli 		return -1;
610959bc330SAntonio Quartulli 	}
611959bc330SAntonio Quartulli 
612959bc330SAntonio Quartulli 	switch (ovpn->remote.in4.sin_family) {
613959bc330SAntonio Quartulli 	case AF_INET:
614959bc330SAntonio Quartulli 		socklen = sizeof(struct sockaddr_in);
615959bc330SAntonio Quartulli 		break;
616959bc330SAntonio Quartulli 	case AF_INET6:
617959bc330SAntonio Quartulli 		socklen = sizeof(struct sockaddr_in6);
618959bc330SAntonio Quartulli 		break;
619959bc330SAntonio Quartulli 	default:
620959bc330SAntonio Quartulli 		return -EOPNOTSUPP;
621959bc330SAntonio Quartulli 	}
622959bc330SAntonio Quartulli 
623959bc330SAntonio Quartulli 	ret = connect(s, (struct sockaddr *)&ovpn->remote, socklen);
624959bc330SAntonio Quartulli 	if (ret < 0) {
625959bc330SAntonio Quartulli 		perror("connect");
626959bc330SAntonio Quartulli 		goto err;
627959bc330SAntonio Quartulli 	}
628959bc330SAntonio Quartulli 
629959bc330SAntonio Quartulli 	fprintf(stderr, "connected\n");
630959bc330SAntonio Quartulli 
631959bc330SAntonio Quartulli 	ovpn->socket = s;
632959bc330SAntonio Quartulli 
633959bc330SAntonio Quartulli 	return 0;
634959bc330SAntonio Quartulli err:
635959bc330SAntonio Quartulli 	close(s);
636959bc330SAntonio Quartulli 	return ret;
637959bc330SAntonio Quartulli }
638959bc330SAntonio Quartulli 
ovpn_new_peer(struct ovpn_ctx * ovpn,bool is_tcp)639959bc330SAntonio Quartulli static int ovpn_new_peer(struct ovpn_ctx *ovpn, bool is_tcp)
640959bc330SAntonio Quartulli {
641959bc330SAntonio Quartulli 	struct nlattr *attr;
642959bc330SAntonio Quartulli 	struct nl_ctx *ctx;
643959bc330SAntonio Quartulli 	int ret = -1;
644959bc330SAntonio Quartulli 
645959bc330SAntonio Quartulli 	ctx = nl_ctx_alloc(ovpn, OVPN_CMD_PEER_NEW);
646959bc330SAntonio Quartulli 	if (!ctx)
647959bc330SAntonio Quartulli 		return -ENOMEM;
648959bc330SAntonio Quartulli 
649959bc330SAntonio Quartulli 	attr = nla_nest_start(ctx->nl_msg, OVPN_A_PEER);
650959bc330SAntonio Quartulli 	NLA_PUT_U32(ctx->nl_msg, OVPN_A_PEER_ID, ovpn->peer_id);
651959bc330SAntonio Quartulli 	NLA_PUT_U32(ctx->nl_msg, OVPN_A_PEER_SOCKET, ovpn->socket);
652959bc330SAntonio Quartulli 
653959bc330SAntonio Quartulli 	if (!is_tcp) {
654959bc330SAntonio Quartulli 		switch (ovpn->remote.in4.sin_family) {
655959bc330SAntonio Quartulli 		case AF_INET:
656959bc330SAntonio Quartulli 			NLA_PUT_U32(ctx->nl_msg, OVPN_A_PEER_REMOTE_IPV4,
657959bc330SAntonio Quartulli 				    ovpn->remote.in4.sin_addr.s_addr);
658959bc330SAntonio Quartulli 			NLA_PUT_U16(ctx->nl_msg, OVPN_A_PEER_REMOTE_PORT,
659959bc330SAntonio Quartulli 				    ovpn->remote.in4.sin_port);
660959bc330SAntonio Quartulli 			break;
661959bc330SAntonio Quartulli 		case AF_INET6:
662959bc330SAntonio Quartulli 			NLA_PUT(ctx->nl_msg, OVPN_A_PEER_REMOTE_IPV6,
663959bc330SAntonio Quartulli 				sizeof(ovpn->remote.in6.sin6_addr),
664959bc330SAntonio Quartulli 				&ovpn->remote.in6.sin6_addr);
665959bc330SAntonio Quartulli 			NLA_PUT_U32(ctx->nl_msg,
666959bc330SAntonio Quartulli 				    OVPN_A_PEER_REMOTE_IPV6_SCOPE_ID,
667959bc330SAntonio Quartulli 				    ovpn->remote.in6.sin6_scope_id);
668959bc330SAntonio Quartulli 			NLA_PUT_U16(ctx->nl_msg, OVPN_A_PEER_REMOTE_PORT,
669959bc330SAntonio Quartulli 				    ovpn->remote.in6.sin6_port);
670959bc330SAntonio Quartulli 			break;
671959bc330SAntonio Quartulli 		default:
672959bc330SAntonio Quartulli 			fprintf(stderr,
673959bc330SAntonio Quartulli 				"Invalid family for remote socket address\n");
674959bc330SAntonio Quartulli 			goto nla_put_failure;
675959bc330SAntonio Quartulli 		}
676959bc330SAntonio Quartulli 	}
677959bc330SAntonio Quartulli 
678959bc330SAntonio Quartulli 	if (ovpn->peer_ip_set) {
679959bc330SAntonio Quartulli 		switch (ovpn->peer_ip.in4.sin_family) {
680959bc330SAntonio Quartulli 		case AF_INET:
681959bc330SAntonio Quartulli 			NLA_PUT_U32(ctx->nl_msg, OVPN_A_PEER_VPN_IPV4,
682959bc330SAntonio Quartulli 				    ovpn->peer_ip.in4.sin_addr.s_addr);
683959bc330SAntonio Quartulli 			break;
684959bc330SAntonio Quartulli 		case AF_INET6:
685959bc330SAntonio Quartulli 			NLA_PUT(ctx->nl_msg, OVPN_A_PEER_VPN_IPV6,
686959bc330SAntonio Quartulli 				sizeof(struct in6_addr),
687959bc330SAntonio Quartulli 				&ovpn->peer_ip.in6.sin6_addr);
688959bc330SAntonio Quartulli 			break;
689959bc330SAntonio Quartulli 		default:
690959bc330SAntonio Quartulli 			fprintf(stderr, "Invalid family for peer address\n");
691959bc330SAntonio Quartulli 			goto nla_put_failure;
692959bc330SAntonio Quartulli 		}
693959bc330SAntonio Quartulli 	}
694959bc330SAntonio Quartulli 
695959bc330SAntonio Quartulli 	nla_nest_end(ctx->nl_msg, attr);
696959bc330SAntonio Quartulli 
697959bc330SAntonio Quartulli 	ret = ovpn_nl_msg_send(ctx, NULL);
698959bc330SAntonio Quartulli nla_put_failure:
699959bc330SAntonio Quartulli 	nl_ctx_free(ctx);
700959bc330SAntonio Quartulli 	return ret;
701959bc330SAntonio Quartulli }
702959bc330SAntonio Quartulli 
ovpn_set_peer(struct ovpn_ctx * ovpn)703959bc330SAntonio Quartulli static int ovpn_set_peer(struct ovpn_ctx *ovpn)
704959bc330SAntonio Quartulli {
705959bc330SAntonio Quartulli 	struct nlattr *attr;
706959bc330SAntonio Quartulli 	struct nl_ctx *ctx;
707959bc330SAntonio Quartulli 	int ret = -1;
708959bc330SAntonio Quartulli 
709959bc330SAntonio Quartulli 	ctx = nl_ctx_alloc(ovpn, OVPN_CMD_PEER_SET);
710959bc330SAntonio Quartulli 	if (!ctx)
711959bc330SAntonio Quartulli 		return -ENOMEM;
712959bc330SAntonio Quartulli 
713959bc330SAntonio Quartulli 	attr = nla_nest_start(ctx->nl_msg, OVPN_A_PEER);
714959bc330SAntonio Quartulli 	NLA_PUT_U32(ctx->nl_msg, OVPN_A_PEER_ID, ovpn->peer_id);
715959bc330SAntonio Quartulli 	NLA_PUT_U32(ctx->nl_msg, OVPN_A_PEER_KEEPALIVE_INTERVAL,
716959bc330SAntonio Quartulli 		    ovpn->keepalive_interval);
717959bc330SAntonio Quartulli 	NLA_PUT_U32(ctx->nl_msg, OVPN_A_PEER_KEEPALIVE_TIMEOUT,
718959bc330SAntonio Quartulli 		    ovpn->keepalive_timeout);
719959bc330SAntonio Quartulli 	nla_nest_end(ctx->nl_msg, attr);
720959bc330SAntonio Quartulli 
721959bc330SAntonio Quartulli 	ret = ovpn_nl_msg_send(ctx, NULL);
722959bc330SAntonio Quartulli nla_put_failure:
723959bc330SAntonio Quartulli 	nl_ctx_free(ctx);
724959bc330SAntonio Quartulli 	return ret;
725959bc330SAntonio Quartulli }
726959bc330SAntonio Quartulli 
ovpn_del_peer(struct ovpn_ctx * ovpn)727959bc330SAntonio Quartulli static int ovpn_del_peer(struct ovpn_ctx *ovpn)
728959bc330SAntonio Quartulli {
729959bc330SAntonio Quartulli 	struct nlattr *attr;
730959bc330SAntonio Quartulli 	struct nl_ctx *ctx;
731959bc330SAntonio Quartulli 	int ret = -1;
732959bc330SAntonio Quartulli 
733959bc330SAntonio Quartulli 	ctx = nl_ctx_alloc(ovpn, OVPN_CMD_PEER_DEL);
734959bc330SAntonio Quartulli 	if (!ctx)
735959bc330SAntonio Quartulli 		return -ENOMEM;
736959bc330SAntonio Quartulli 
737959bc330SAntonio Quartulli 	attr = nla_nest_start(ctx->nl_msg, OVPN_A_PEER);
738959bc330SAntonio Quartulli 	NLA_PUT_U32(ctx->nl_msg, OVPN_A_PEER_ID, ovpn->peer_id);
739959bc330SAntonio Quartulli 	nla_nest_end(ctx->nl_msg, attr);
740959bc330SAntonio Quartulli 
741959bc330SAntonio Quartulli 	ret = ovpn_nl_msg_send(ctx, NULL);
742959bc330SAntonio Quartulli nla_put_failure:
743959bc330SAntonio Quartulli 	nl_ctx_free(ctx);
744959bc330SAntonio Quartulli 	return ret;
745959bc330SAntonio Quartulli }
746959bc330SAntonio Quartulli 
ovpn_handle_peer(struct nl_msg * msg,void (* arg)__always_unused)747959bc330SAntonio Quartulli static int ovpn_handle_peer(struct nl_msg *msg, void (*arg)__always_unused)
748959bc330SAntonio Quartulli {
749959bc330SAntonio Quartulli 	struct nlattr *pattrs[OVPN_A_PEER_MAX + 1];
750959bc330SAntonio Quartulli 	struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));
751959bc330SAntonio Quartulli 	struct nlattr *attrs[OVPN_A_MAX + 1];
752959bc330SAntonio Quartulli 	__u16 rport = 0, lport = 0;
753959bc330SAntonio Quartulli 
754959bc330SAntonio Quartulli 	nla_parse(attrs, OVPN_A_MAX, genlmsg_attrdata(gnlh, 0),
755959bc330SAntonio Quartulli 		  genlmsg_attrlen(gnlh, 0), NULL);
756959bc330SAntonio Quartulli 
757959bc330SAntonio Quartulli 	if (!attrs[OVPN_A_PEER]) {
758959bc330SAntonio Quartulli 		fprintf(stderr, "no packet content in netlink message\n");
759959bc330SAntonio Quartulli 		return NL_SKIP;
760959bc330SAntonio Quartulli 	}
761959bc330SAntonio Quartulli 
762959bc330SAntonio Quartulli 	nla_parse(pattrs, OVPN_A_PEER_MAX, nla_data(attrs[OVPN_A_PEER]),
763959bc330SAntonio Quartulli 		  nla_len(attrs[OVPN_A_PEER]), NULL);
764959bc330SAntonio Quartulli 
765959bc330SAntonio Quartulli 	if (pattrs[OVPN_A_PEER_ID])
766959bc330SAntonio Quartulli 		fprintf(stderr, "* Peer %u\n",
767959bc330SAntonio Quartulli 			nla_get_u32(pattrs[OVPN_A_PEER_ID]));
768959bc330SAntonio Quartulli 
769959bc330SAntonio Quartulli 	if (pattrs[OVPN_A_PEER_SOCKET_NETNSID])
770959bc330SAntonio Quartulli 		fprintf(stderr, "\tsocket NetNS ID: %d\n",
771959bc330SAntonio Quartulli 			nla_get_s32(pattrs[OVPN_A_PEER_SOCKET_NETNSID]));
772959bc330SAntonio Quartulli 
773959bc330SAntonio Quartulli 	if (pattrs[OVPN_A_PEER_VPN_IPV4]) {
774959bc330SAntonio Quartulli 		char buf[INET_ADDRSTRLEN];
775959bc330SAntonio Quartulli 
776959bc330SAntonio Quartulli 		inet_ntop(AF_INET, nla_data(pattrs[OVPN_A_PEER_VPN_IPV4]),
777959bc330SAntonio Quartulli 			  buf, sizeof(buf));
778959bc330SAntonio Quartulli 		fprintf(stderr, "\tVPN IPv4: %s\n", buf);
779959bc330SAntonio Quartulli 	}
780959bc330SAntonio Quartulli 
781959bc330SAntonio Quartulli 	if (pattrs[OVPN_A_PEER_VPN_IPV6]) {
782959bc330SAntonio Quartulli 		char buf[INET6_ADDRSTRLEN];
783959bc330SAntonio Quartulli 
784959bc330SAntonio Quartulli 		inet_ntop(AF_INET6, nla_data(pattrs[OVPN_A_PEER_VPN_IPV6]),
785959bc330SAntonio Quartulli 			  buf, sizeof(buf));
786959bc330SAntonio Quartulli 		fprintf(stderr, "\tVPN IPv6: %s\n", buf);
787959bc330SAntonio Quartulli 	}
788959bc330SAntonio Quartulli 
789959bc330SAntonio Quartulli 	if (pattrs[OVPN_A_PEER_LOCAL_PORT])
790959bc330SAntonio Quartulli 		lport = ntohs(nla_get_u16(pattrs[OVPN_A_PEER_LOCAL_PORT]));
791959bc330SAntonio Quartulli 
792959bc330SAntonio Quartulli 	if (pattrs[OVPN_A_PEER_REMOTE_PORT])
793959bc330SAntonio Quartulli 		rport = ntohs(nla_get_u16(pattrs[OVPN_A_PEER_REMOTE_PORT]));
794959bc330SAntonio Quartulli 
795959bc330SAntonio Quartulli 	if (pattrs[OVPN_A_PEER_REMOTE_IPV6]) {
796959bc330SAntonio Quartulli 		void *ip = pattrs[OVPN_A_PEER_REMOTE_IPV6];
797959bc330SAntonio Quartulli 		char buf[INET6_ADDRSTRLEN];
798959bc330SAntonio Quartulli 		int scope_id = -1;
799959bc330SAntonio Quartulli 
800959bc330SAntonio Quartulli 		if (pattrs[OVPN_A_PEER_REMOTE_IPV6_SCOPE_ID]) {
801959bc330SAntonio Quartulli 			void *p = pattrs[OVPN_A_PEER_REMOTE_IPV6_SCOPE_ID];
802959bc330SAntonio Quartulli 
803959bc330SAntonio Quartulli 			scope_id = nla_get_u32(p);
804959bc330SAntonio Quartulli 		}
805959bc330SAntonio Quartulli 
806959bc330SAntonio Quartulli 		inet_ntop(AF_INET6, nla_data(ip), buf, sizeof(buf));
807959bc330SAntonio Quartulli 		fprintf(stderr, "\tRemote: %s:%hu (scope-id: %u)\n", buf, rport,
808959bc330SAntonio Quartulli 			scope_id);
809959bc330SAntonio Quartulli 
810959bc330SAntonio Quartulli 		if (pattrs[OVPN_A_PEER_LOCAL_IPV6]) {
811959bc330SAntonio Quartulli 			void *ip = pattrs[OVPN_A_PEER_LOCAL_IPV6];
812959bc330SAntonio Quartulli 
813959bc330SAntonio Quartulli 			inet_ntop(AF_INET6, nla_data(ip), buf, sizeof(buf));
814959bc330SAntonio Quartulli 			fprintf(stderr, "\tLocal: %s:%hu\n", buf, lport);
815959bc330SAntonio Quartulli 		}
816959bc330SAntonio Quartulli 	}
817959bc330SAntonio Quartulli 
818959bc330SAntonio Quartulli 	if (pattrs[OVPN_A_PEER_REMOTE_IPV4]) {
819959bc330SAntonio Quartulli 		void *ip = pattrs[OVPN_A_PEER_REMOTE_IPV4];
820959bc330SAntonio Quartulli 		char buf[INET_ADDRSTRLEN];
821959bc330SAntonio Quartulli 
822959bc330SAntonio Quartulli 		inet_ntop(AF_INET, nla_data(ip), buf, sizeof(buf));
823959bc330SAntonio Quartulli 		fprintf(stderr, "\tRemote: %s:%hu\n", buf, rport);
824959bc330SAntonio Quartulli 
825959bc330SAntonio Quartulli 		if (pattrs[OVPN_A_PEER_LOCAL_IPV4]) {
826959bc330SAntonio Quartulli 			void *p = pattrs[OVPN_A_PEER_LOCAL_IPV4];
827959bc330SAntonio Quartulli 
828959bc330SAntonio Quartulli 			inet_ntop(AF_INET, nla_data(p), buf, sizeof(buf));
829959bc330SAntonio Quartulli 			fprintf(stderr, "\tLocal: %s:%hu\n", buf, lport);
830959bc330SAntonio Quartulli 		}
831959bc330SAntonio Quartulli 	}
832959bc330SAntonio Quartulli 
833959bc330SAntonio Quartulli 	if (pattrs[OVPN_A_PEER_KEEPALIVE_INTERVAL]) {
834959bc330SAntonio Quartulli 		void *p = pattrs[OVPN_A_PEER_KEEPALIVE_INTERVAL];
835959bc330SAntonio Quartulli 
836959bc330SAntonio Quartulli 		fprintf(stderr, "\tKeepalive interval: %u sec\n",
837959bc330SAntonio Quartulli 			nla_get_u32(p));
838959bc330SAntonio Quartulli 	}
839959bc330SAntonio Quartulli 
840959bc330SAntonio Quartulli 	if (pattrs[OVPN_A_PEER_KEEPALIVE_TIMEOUT])
841959bc330SAntonio Quartulli 		fprintf(stderr, "\tKeepalive timeout: %u sec\n",
842959bc330SAntonio Quartulli 			nla_get_u32(pattrs[OVPN_A_PEER_KEEPALIVE_TIMEOUT]));
843959bc330SAntonio Quartulli 
844959bc330SAntonio Quartulli 	if (pattrs[OVPN_A_PEER_VPN_RX_BYTES])
845959bc330SAntonio Quartulli 		fprintf(stderr, "\tVPN RX bytes: %" PRIu64 "\n",
846959bc330SAntonio Quartulli 			ovpn_nla_get_uint(pattrs[OVPN_A_PEER_VPN_RX_BYTES]));
847959bc330SAntonio Quartulli 
848959bc330SAntonio Quartulli 	if (pattrs[OVPN_A_PEER_VPN_TX_BYTES])
849959bc330SAntonio Quartulli 		fprintf(stderr, "\tVPN TX bytes: %" PRIu64 "\n",
850959bc330SAntonio Quartulli 			ovpn_nla_get_uint(pattrs[OVPN_A_PEER_VPN_TX_BYTES]));
851959bc330SAntonio Quartulli 
852959bc330SAntonio Quartulli 	if (pattrs[OVPN_A_PEER_VPN_RX_PACKETS])
853959bc330SAntonio Quartulli 		fprintf(stderr, "\tVPN RX packets: %" PRIu64 "\n",
854959bc330SAntonio Quartulli 			ovpn_nla_get_uint(pattrs[OVPN_A_PEER_VPN_RX_PACKETS]));
855959bc330SAntonio Quartulli 
856959bc330SAntonio Quartulli 	if (pattrs[OVPN_A_PEER_VPN_TX_PACKETS])
857959bc330SAntonio Quartulli 		fprintf(stderr, "\tVPN TX packets: %" PRIu64 "\n",
858959bc330SAntonio Quartulli 			ovpn_nla_get_uint(pattrs[OVPN_A_PEER_VPN_TX_PACKETS]));
859959bc330SAntonio Quartulli 
860959bc330SAntonio Quartulli 	if (pattrs[OVPN_A_PEER_LINK_RX_BYTES])
861959bc330SAntonio Quartulli 		fprintf(stderr, "\tLINK RX bytes: %" PRIu64 "\n",
862959bc330SAntonio Quartulli 			ovpn_nla_get_uint(pattrs[OVPN_A_PEER_LINK_RX_BYTES]));
863959bc330SAntonio Quartulli 
864959bc330SAntonio Quartulli 	if (pattrs[OVPN_A_PEER_LINK_TX_BYTES])
865959bc330SAntonio Quartulli 		fprintf(stderr, "\tLINK TX bytes: %" PRIu64 "\n",
866959bc330SAntonio Quartulli 			ovpn_nla_get_uint(pattrs[OVPN_A_PEER_LINK_TX_BYTES]));
867959bc330SAntonio Quartulli 
868959bc330SAntonio Quartulli 	if (pattrs[OVPN_A_PEER_LINK_RX_PACKETS])
869959bc330SAntonio Quartulli 		fprintf(stderr, "\tLINK RX packets: %" PRIu64 "\n",
870959bc330SAntonio Quartulli 			ovpn_nla_get_uint(pattrs[OVPN_A_PEER_LINK_RX_PACKETS]));
871959bc330SAntonio Quartulli 
872959bc330SAntonio Quartulli 	if (pattrs[OVPN_A_PEER_LINK_TX_PACKETS])
873959bc330SAntonio Quartulli 		fprintf(stderr, "\tLINK TX packets: %" PRIu64 "\n",
874959bc330SAntonio Quartulli 			ovpn_nla_get_uint(pattrs[OVPN_A_PEER_LINK_TX_PACKETS]));
875959bc330SAntonio Quartulli 
876959bc330SAntonio Quartulli 	return NL_SKIP;
877959bc330SAntonio Quartulli }
878959bc330SAntonio Quartulli 
ovpn_get_peer(struct ovpn_ctx * ovpn)879959bc330SAntonio Quartulli static int ovpn_get_peer(struct ovpn_ctx *ovpn)
880959bc330SAntonio Quartulli {
881959bc330SAntonio Quartulli 	int flags = 0, ret = -1;
882959bc330SAntonio Quartulli 	struct nlattr *attr;
883959bc330SAntonio Quartulli 	struct nl_ctx *ctx;
884959bc330SAntonio Quartulli 
885959bc330SAntonio Quartulli 	if (ovpn->peer_id == PEER_ID_UNDEF)
886959bc330SAntonio Quartulli 		flags = NLM_F_DUMP;
887959bc330SAntonio Quartulli 
888959bc330SAntonio Quartulli 	ctx = nl_ctx_alloc_flags(ovpn, OVPN_CMD_PEER_GET, flags);
889959bc330SAntonio Quartulli 	if (!ctx)
890959bc330SAntonio Quartulli 		return -ENOMEM;
891959bc330SAntonio Quartulli 
892959bc330SAntonio Quartulli 	if (ovpn->peer_id != PEER_ID_UNDEF) {
893959bc330SAntonio Quartulli 		attr = nla_nest_start(ctx->nl_msg, OVPN_A_PEER);
894959bc330SAntonio Quartulli 		NLA_PUT_U32(ctx->nl_msg, OVPN_A_PEER_ID, ovpn->peer_id);
895959bc330SAntonio Quartulli 		nla_nest_end(ctx->nl_msg, attr);
896959bc330SAntonio Quartulli 	}
897959bc330SAntonio Quartulli 
898959bc330SAntonio Quartulli 	ret = ovpn_nl_msg_send(ctx, ovpn_handle_peer);
899959bc330SAntonio Quartulli nla_put_failure:
900959bc330SAntonio Quartulli 	nl_ctx_free(ctx);
901959bc330SAntonio Quartulli 	return ret;
902959bc330SAntonio Quartulli }
903959bc330SAntonio Quartulli 
ovpn_new_key(struct ovpn_ctx * ovpn)904959bc330SAntonio Quartulli static int ovpn_new_key(struct ovpn_ctx *ovpn)
905959bc330SAntonio Quartulli {
906959bc330SAntonio Quartulli 	struct nlattr *keyconf, *key_dir;
907959bc330SAntonio Quartulli 	struct nl_ctx *ctx;
908959bc330SAntonio Quartulli 	int ret = -1;
909959bc330SAntonio Quartulli 
910959bc330SAntonio Quartulli 	ctx = nl_ctx_alloc(ovpn, OVPN_CMD_KEY_NEW);
911959bc330SAntonio Quartulli 	if (!ctx)
912959bc330SAntonio Quartulli 		return -ENOMEM;
913959bc330SAntonio Quartulli 
914959bc330SAntonio Quartulli 	keyconf = nla_nest_start(ctx->nl_msg, OVPN_A_KEYCONF);
915959bc330SAntonio Quartulli 	NLA_PUT_U32(ctx->nl_msg, OVPN_A_KEYCONF_PEER_ID, ovpn->peer_id);
916959bc330SAntonio Quartulli 	NLA_PUT_U32(ctx->nl_msg, OVPN_A_KEYCONF_SLOT, ovpn->key_slot);
917959bc330SAntonio Quartulli 	NLA_PUT_U32(ctx->nl_msg, OVPN_A_KEYCONF_KEY_ID, ovpn->key_id);
918959bc330SAntonio Quartulli 	NLA_PUT_U32(ctx->nl_msg, OVPN_A_KEYCONF_CIPHER_ALG, ovpn->cipher);
919959bc330SAntonio Quartulli 
920959bc330SAntonio Quartulli 	key_dir = nla_nest_start(ctx->nl_msg, OVPN_A_KEYCONF_ENCRYPT_DIR);
921959bc330SAntonio Quartulli 	NLA_PUT(ctx->nl_msg, OVPN_A_KEYDIR_CIPHER_KEY, KEY_LEN, ovpn->key_enc);
922959bc330SAntonio Quartulli 	NLA_PUT(ctx->nl_msg, OVPN_A_KEYDIR_NONCE_TAIL, NONCE_LEN, ovpn->nonce);
923959bc330SAntonio Quartulli 	nla_nest_end(ctx->nl_msg, key_dir);
924959bc330SAntonio Quartulli 
925959bc330SAntonio Quartulli 	key_dir = nla_nest_start(ctx->nl_msg, OVPN_A_KEYCONF_DECRYPT_DIR);
926959bc330SAntonio Quartulli 	NLA_PUT(ctx->nl_msg, OVPN_A_KEYDIR_CIPHER_KEY, KEY_LEN, ovpn->key_dec);
927959bc330SAntonio Quartulli 	NLA_PUT(ctx->nl_msg, OVPN_A_KEYDIR_NONCE_TAIL, NONCE_LEN, ovpn->nonce);
928959bc330SAntonio Quartulli 	nla_nest_end(ctx->nl_msg, key_dir);
929959bc330SAntonio Quartulli 
930959bc330SAntonio Quartulli 	nla_nest_end(ctx->nl_msg, keyconf);
931959bc330SAntonio Quartulli 
932959bc330SAntonio Quartulli 	ret = ovpn_nl_msg_send(ctx, NULL);
933959bc330SAntonio Quartulli nla_put_failure:
934959bc330SAntonio Quartulli 	nl_ctx_free(ctx);
935959bc330SAntonio Quartulli 	return ret;
936959bc330SAntonio Quartulli }
937959bc330SAntonio Quartulli 
ovpn_del_key(struct ovpn_ctx * ovpn)938959bc330SAntonio Quartulli static int ovpn_del_key(struct ovpn_ctx *ovpn)
939959bc330SAntonio Quartulli {
940959bc330SAntonio Quartulli 	struct nlattr *keyconf;
941959bc330SAntonio Quartulli 	struct nl_ctx *ctx;
942959bc330SAntonio Quartulli 	int ret = -1;
943959bc330SAntonio Quartulli 
944959bc330SAntonio Quartulli 	ctx = nl_ctx_alloc(ovpn, OVPN_CMD_KEY_DEL);
945959bc330SAntonio Quartulli 	if (!ctx)
946959bc330SAntonio Quartulli 		return -ENOMEM;
947959bc330SAntonio Quartulli 
948959bc330SAntonio Quartulli 	keyconf = nla_nest_start(ctx->nl_msg, OVPN_A_KEYCONF);
949959bc330SAntonio Quartulli 	NLA_PUT_U32(ctx->nl_msg, OVPN_A_KEYCONF_PEER_ID, ovpn->peer_id);
950959bc330SAntonio Quartulli 	NLA_PUT_U32(ctx->nl_msg, OVPN_A_KEYCONF_SLOT, ovpn->key_slot);
951959bc330SAntonio Quartulli 	nla_nest_end(ctx->nl_msg, keyconf);
952959bc330SAntonio Quartulli 
953959bc330SAntonio Quartulli 	ret = ovpn_nl_msg_send(ctx, NULL);
954959bc330SAntonio Quartulli nla_put_failure:
955959bc330SAntonio Quartulli 	nl_ctx_free(ctx);
956959bc330SAntonio Quartulli 	return ret;
957959bc330SAntonio Quartulli }
958959bc330SAntonio Quartulli 
ovpn_handle_key(struct nl_msg * msg,void (* arg)__always_unused)959959bc330SAntonio Quartulli static int ovpn_handle_key(struct nl_msg *msg, void (*arg)__always_unused)
960959bc330SAntonio Quartulli {
961959bc330SAntonio Quartulli 	struct nlattr *kattrs[OVPN_A_KEYCONF_MAX + 1];
962959bc330SAntonio Quartulli 	struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));
963959bc330SAntonio Quartulli 	struct nlattr *attrs[OVPN_A_MAX + 1];
964959bc330SAntonio Quartulli 
965959bc330SAntonio Quartulli 	nla_parse(attrs, OVPN_A_MAX, genlmsg_attrdata(gnlh, 0),
966959bc330SAntonio Quartulli 		  genlmsg_attrlen(gnlh, 0), NULL);
967959bc330SAntonio Quartulli 
968959bc330SAntonio Quartulli 	if (!attrs[OVPN_A_KEYCONF]) {
969959bc330SAntonio Quartulli 		fprintf(stderr, "no packet content in netlink message\n");
970959bc330SAntonio Quartulli 		return NL_SKIP;
971959bc330SAntonio Quartulli 	}
972959bc330SAntonio Quartulli 
973959bc330SAntonio Quartulli 	nla_parse(kattrs, OVPN_A_KEYCONF_MAX, nla_data(attrs[OVPN_A_KEYCONF]),
974959bc330SAntonio Quartulli 		  nla_len(attrs[OVPN_A_KEYCONF]), NULL);
975959bc330SAntonio Quartulli 
976959bc330SAntonio Quartulli 	if (kattrs[OVPN_A_KEYCONF_PEER_ID])
977959bc330SAntonio Quartulli 		fprintf(stderr, "* Peer %u\n",
978959bc330SAntonio Quartulli 			nla_get_u32(kattrs[OVPN_A_KEYCONF_PEER_ID]));
979959bc330SAntonio Quartulli 	if (kattrs[OVPN_A_KEYCONF_SLOT]) {
980959bc330SAntonio Quartulli 		fprintf(stderr, "\t- Slot: ");
981959bc330SAntonio Quartulli 		switch (nla_get_u32(kattrs[OVPN_A_KEYCONF_SLOT])) {
982959bc330SAntonio Quartulli 		case OVPN_KEY_SLOT_PRIMARY:
983959bc330SAntonio Quartulli 			fprintf(stderr, "primary\n");
984959bc330SAntonio Quartulli 			break;
985959bc330SAntonio Quartulli 		case OVPN_KEY_SLOT_SECONDARY:
986959bc330SAntonio Quartulli 			fprintf(stderr, "secondary\n");
987959bc330SAntonio Quartulli 			break;
988959bc330SAntonio Quartulli 		default:
989959bc330SAntonio Quartulli 			fprintf(stderr, "invalid (%u)\n",
990959bc330SAntonio Quartulli 				nla_get_u32(kattrs[OVPN_A_KEYCONF_SLOT]));
991959bc330SAntonio Quartulli 			break;
992959bc330SAntonio Quartulli 		}
993959bc330SAntonio Quartulli 	}
994959bc330SAntonio Quartulli 	if (kattrs[OVPN_A_KEYCONF_KEY_ID])
995959bc330SAntonio Quartulli 		fprintf(stderr, "\t- Key ID: %u\n",
996959bc330SAntonio Quartulli 			nla_get_u32(kattrs[OVPN_A_KEYCONF_KEY_ID]));
997959bc330SAntonio Quartulli 	if (kattrs[OVPN_A_KEYCONF_CIPHER_ALG]) {
998959bc330SAntonio Quartulli 		fprintf(stderr, "\t- Cipher: ");
999959bc330SAntonio Quartulli 		switch (nla_get_u32(kattrs[OVPN_A_KEYCONF_CIPHER_ALG])) {
1000959bc330SAntonio Quartulli 		case OVPN_CIPHER_ALG_NONE:
1001959bc330SAntonio Quartulli 			fprintf(stderr, "none\n");
1002959bc330SAntonio Quartulli 			break;
1003959bc330SAntonio Quartulli 		case OVPN_CIPHER_ALG_AES_GCM:
1004959bc330SAntonio Quartulli 			fprintf(stderr, "aes-gcm\n");
1005959bc330SAntonio Quartulli 			break;
1006959bc330SAntonio Quartulli 		case OVPN_CIPHER_ALG_CHACHA20_POLY1305:
1007959bc330SAntonio Quartulli 			fprintf(stderr, "chacha20poly1305\n");
1008959bc330SAntonio Quartulli 			break;
1009959bc330SAntonio Quartulli 		default:
1010959bc330SAntonio Quartulli 			fprintf(stderr, "invalid (%u)\n",
1011959bc330SAntonio Quartulli 				nla_get_u32(kattrs[OVPN_A_KEYCONF_CIPHER_ALG]));
1012959bc330SAntonio Quartulli 			break;
1013959bc330SAntonio Quartulli 		}
1014959bc330SAntonio Quartulli 	}
1015959bc330SAntonio Quartulli 
1016959bc330SAntonio Quartulli 	return NL_SKIP;
1017959bc330SAntonio Quartulli }
1018959bc330SAntonio Quartulli 
ovpn_get_key(struct ovpn_ctx * ovpn)1019959bc330SAntonio Quartulli static int ovpn_get_key(struct ovpn_ctx *ovpn)
1020959bc330SAntonio Quartulli {
1021959bc330SAntonio Quartulli 	struct nlattr *keyconf;
1022959bc330SAntonio Quartulli 	struct nl_ctx *ctx;
1023959bc330SAntonio Quartulli 	int ret = -1;
1024959bc330SAntonio Quartulli 
1025959bc330SAntonio Quartulli 	ctx = nl_ctx_alloc(ovpn, OVPN_CMD_KEY_GET);
1026959bc330SAntonio Quartulli 	if (!ctx)
1027959bc330SAntonio Quartulli 		return -ENOMEM;
1028959bc330SAntonio Quartulli 
1029959bc330SAntonio Quartulli 	keyconf = nla_nest_start(ctx->nl_msg, OVPN_A_KEYCONF);
1030959bc330SAntonio Quartulli 	NLA_PUT_U32(ctx->nl_msg, OVPN_A_KEYCONF_PEER_ID, ovpn->peer_id);
1031959bc330SAntonio Quartulli 	NLA_PUT_U32(ctx->nl_msg, OVPN_A_KEYCONF_SLOT, ovpn->key_slot);
1032959bc330SAntonio Quartulli 	nla_nest_end(ctx->nl_msg, keyconf);
1033959bc330SAntonio Quartulli 
1034959bc330SAntonio Quartulli 	ret = ovpn_nl_msg_send(ctx, ovpn_handle_key);
1035959bc330SAntonio Quartulli nla_put_failure:
1036959bc330SAntonio Quartulli 	nl_ctx_free(ctx);
1037959bc330SAntonio Quartulli 	return ret;
1038959bc330SAntonio Quartulli }
1039959bc330SAntonio Quartulli 
ovpn_swap_keys(struct ovpn_ctx * ovpn)1040959bc330SAntonio Quartulli static int ovpn_swap_keys(struct ovpn_ctx *ovpn)
1041959bc330SAntonio Quartulli {
1042959bc330SAntonio Quartulli 	struct nl_ctx *ctx;
1043959bc330SAntonio Quartulli 	struct nlattr *kc;
1044959bc330SAntonio Quartulli 	int ret = -1;
1045959bc330SAntonio Quartulli 
1046959bc330SAntonio Quartulli 	ctx = nl_ctx_alloc(ovpn, OVPN_CMD_KEY_SWAP);
1047959bc330SAntonio Quartulli 	if (!ctx)
1048959bc330SAntonio Quartulli 		return -ENOMEM;
1049959bc330SAntonio Quartulli 
1050959bc330SAntonio Quartulli 	kc = nla_nest_start(ctx->nl_msg, OVPN_A_KEYCONF);
1051959bc330SAntonio Quartulli 	NLA_PUT_U32(ctx->nl_msg, OVPN_A_KEYCONF_PEER_ID, ovpn->peer_id);
1052959bc330SAntonio Quartulli 	nla_nest_end(ctx->nl_msg, kc);
1053959bc330SAntonio Quartulli 
1054959bc330SAntonio Quartulli 	ret = ovpn_nl_msg_send(ctx, NULL);
1055959bc330SAntonio Quartulli nla_put_failure:
1056959bc330SAntonio Quartulli 	nl_ctx_free(ctx);
1057959bc330SAntonio Quartulli 	return ret;
1058959bc330SAntonio Quartulli }
1059959bc330SAntonio Quartulli 
1060959bc330SAntonio Quartulli /* Helper function used to easily add attributes to a rtnl message */
ovpn_addattr(struct nlmsghdr * n,int maxlen,int type,const void * data,int alen)1061959bc330SAntonio Quartulli static int ovpn_addattr(struct nlmsghdr *n, int maxlen, int type,
1062959bc330SAntonio Quartulli 			const void *data, int alen)
1063959bc330SAntonio Quartulli {
1064959bc330SAntonio Quartulli 	int len = RTA_LENGTH(alen);
1065959bc330SAntonio Quartulli 	struct rtattr *rta;
1066959bc330SAntonio Quartulli 
1067959bc330SAntonio Quartulli 	if ((int)(NLMSG_ALIGN(n->nlmsg_len) + RTA_ALIGN(len)) > maxlen)	{
1068959bc330SAntonio Quartulli 		fprintf(stderr, "%s: rtnl: message exceeded bound of %d\n",
1069959bc330SAntonio Quartulli 			__func__, maxlen);
1070959bc330SAntonio Quartulli 		return -EMSGSIZE;
1071959bc330SAntonio Quartulli 	}
1072959bc330SAntonio Quartulli 
1073959bc330SAntonio Quartulli 	rta = nlmsg_tail(n);
1074959bc330SAntonio Quartulli 	rta->rta_type = type;
1075959bc330SAntonio Quartulli 	rta->rta_len = len;
1076959bc330SAntonio Quartulli 
1077959bc330SAntonio Quartulli 	if (!data)
1078959bc330SAntonio Quartulli 		memset(RTA_DATA(rta), 0, alen);
1079959bc330SAntonio Quartulli 	else
1080959bc330SAntonio Quartulli 		memcpy(RTA_DATA(rta), data, alen);
1081959bc330SAntonio Quartulli 
1082959bc330SAntonio Quartulli 	n->nlmsg_len = NLMSG_ALIGN(n->nlmsg_len) + RTA_ALIGN(len);
1083959bc330SAntonio Quartulli 
1084959bc330SAntonio Quartulli 	return 0;
1085959bc330SAntonio Quartulli }
1086959bc330SAntonio Quartulli 
ovpn_nest_start(struct nlmsghdr * msg,size_t max_size,int attr)1087959bc330SAntonio Quartulli static struct rtattr *ovpn_nest_start(struct nlmsghdr *msg, size_t max_size,
1088959bc330SAntonio Quartulli 				      int attr)
1089959bc330SAntonio Quartulli {
1090959bc330SAntonio Quartulli 	struct rtattr *nest = nlmsg_tail(msg);
1091959bc330SAntonio Quartulli 
1092959bc330SAntonio Quartulli 	if (ovpn_addattr(msg, max_size, attr, NULL, 0) < 0)
1093959bc330SAntonio Quartulli 		return NULL;
1094959bc330SAntonio Quartulli 
1095959bc330SAntonio Quartulli 	return nest;
1096959bc330SAntonio Quartulli }
1097959bc330SAntonio Quartulli 
ovpn_nest_end(struct nlmsghdr * msg,struct rtattr * nest)1098959bc330SAntonio Quartulli static void ovpn_nest_end(struct nlmsghdr *msg, struct rtattr *nest)
1099959bc330SAntonio Quartulli {
1100959bc330SAntonio Quartulli 	nest->rta_len = (uint8_t *)nlmsg_tail(msg) - (uint8_t *)nest;
1101959bc330SAntonio Quartulli }
1102959bc330SAntonio Quartulli 
1103959bc330SAntonio Quartulli #define RT_SNDBUF_SIZE (1024 * 2)
1104959bc330SAntonio Quartulli #define RT_RCVBUF_SIZE (1024 * 4)
1105959bc330SAntonio Quartulli 
1106959bc330SAntonio Quartulli /* Open RTNL socket */
ovpn_rt_socket(void)1107959bc330SAntonio Quartulli static int ovpn_rt_socket(void)
1108959bc330SAntonio Quartulli {
1109959bc330SAntonio Quartulli 	int sndbuf = RT_SNDBUF_SIZE, rcvbuf = RT_RCVBUF_SIZE, fd;
1110959bc330SAntonio Quartulli 
1111959bc330SAntonio Quartulli 	fd = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE);
1112959bc330SAntonio Quartulli 	if (fd < 0) {
1113959bc330SAntonio Quartulli 		fprintf(stderr, "%s: cannot open netlink socket\n", __func__);
1114959bc330SAntonio Quartulli 		return fd;
1115959bc330SAntonio Quartulli 	}
1116959bc330SAntonio Quartulli 
1117959bc330SAntonio Quartulli 	if (setsockopt(fd, SOL_SOCKET, SO_SNDBUF, &sndbuf,
1118959bc330SAntonio Quartulli 		       sizeof(sndbuf)) < 0) {
1119959bc330SAntonio Quartulli 		fprintf(stderr, "%s: SO_SNDBUF\n", __func__);
1120959bc330SAntonio Quartulli 		close(fd);
1121959bc330SAntonio Quartulli 		return -1;
1122959bc330SAntonio Quartulli 	}
1123959bc330SAntonio Quartulli 
1124959bc330SAntonio Quartulli 	if (setsockopt(fd, SOL_SOCKET, SO_RCVBUF, &rcvbuf,
1125959bc330SAntonio Quartulli 		       sizeof(rcvbuf)) < 0) {
1126959bc330SAntonio Quartulli 		fprintf(stderr, "%s: SO_RCVBUF\n", __func__);
1127959bc330SAntonio Quartulli 		close(fd);
1128959bc330SAntonio Quartulli 		return -1;
1129959bc330SAntonio Quartulli 	}
1130959bc330SAntonio Quartulli 
1131959bc330SAntonio Quartulli 	return fd;
1132959bc330SAntonio Quartulli }
1133959bc330SAntonio Quartulli 
1134959bc330SAntonio Quartulli /* Bind socket to Netlink subsystem */
ovpn_rt_bind(int fd,uint32_t groups)1135959bc330SAntonio Quartulli static int ovpn_rt_bind(int fd, uint32_t groups)
1136959bc330SAntonio Quartulli {
1137959bc330SAntonio Quartulli 	struct sockaddr_nl local = { 0 };
1138959bc330SAntonio Quartulli 	socklen_t addr_len;
1139959bc330SAntonio Quartulli 
1140959bc330SAntonio Quartulli 	local.nl_family = AF_NETLINK;
1141959bc330SAntonio Quartulli 	local.nl_groups = groups;
1142959bc330SAntonio Quartulli 
1143959bc330SAntonio Quartulli 	if (bind(fd, (struct sockaddr *)&local, sizeof(local)) < 0) {
1144959bc330SAntonio Quartulli 		fprintf(stderr, "%s: cannot bind netlink socket: %d\n",
1145959bc330SAntonio Quartulli 			__func__, errno);
1146959bc330SAntonio Quartulli 		return -errno;
1147959bc330SAntonio Quartulli 	}
1148959bc330SAntonio Quartulli 
1149959bc330SAntonio Quartulli 	addr_len = sizeof(local);
1150959bc330SAntonio Quartulli 	if (getsockname(fd, (struct sockaddr *)&local, &addr_len) < 0) {
1151959bc330SAntonio Quartulli 		fprintf(stderr, "%s: cannot getsockname: %d\n", __func__,
1152959bc330SAntonio Quartulli 			errno);
1153959bc330SAntonio Quartulli 		return -errno;
1154959bc330SAntonio Quartulli 	}
1155959bc330SAntonio Quartulli 
1156959bc330SAntonio Quartulli 	if (addr_len != sizeof(local)) {
1157959bc330SAntonio Quartulli 		fprintf(stderr, "%s: wrong address length %d\n", __func__,
1158959bc330SAntonio Quartulli 			addr_len);
1159959bc330SAntonio Quartulli 		return -EINVAL;
1160959bc330SAntonio Quartulli 	}
1161959bc330SAntonio Quartulli 
1162959bc330SAntonio Quartulli 	if (local.nl_family != AF_NETLINK) {
1163959bc330SAntonio Quartulli 		fprintf(stderr, "%s: wrong address family %d\n", __func__,
1164959bc330SAntonio Quartulli 			local.nl_family);
1165959bc330SAntonio Quartulli 		return -EINVAL;
1166959bc330SAntonio Quartulli 	}
1167959bc330SAntonio Quartulli 
1168959bc330SAntonio Quartulli 	return 0;
1169959bc330SAntonio Quartulli }
1170959bc330SAntonio Quartulli 
1171959bc330SAntonio Quartulli typedef int (*ovpn_parse_reply_cb)(struct nlmsghdr *msg, void *arg);
1172959bc330SAntonio Quartulli 
1173959bc330SAntonio Quartulli /* Send Netlink message and run callback on reply (if specified) */
ovpn_rt_send(struct nlmsghdr * payload,pid_t peer,unsigned int groups,ovpn_parse_reply_cb cb,void * arg_cb)1174959bc330SAntonio Quartulli static int ovpn_rt_send(struct nlmsghdr *payload, pid_t peer,
1175959bc330SAntonio Quartulli 			unsigned int groups, ovpn_parse_reply_cb cb,
1176959bc330SAntonio Quartulli 			void *arg_cb)
1177959bc330SAntonio Quartulli {
1178959bc330SAntonio Quartulli 	int len, rem_len, fd, ret, rcv_len;
1179959bc330SAntonio Quartulli 	struct sockaddr_nl nladdr = { 0 };
1180959bc330SAntonio Quartulli 	struct nlmsgerr *err;
1181959bc330SAntonio Quartulli 	struct nlmsghdr *h;
1182959bc330SAntonio Quartulli 	char buf[1024 * 16];
1183959bc330SAntonio Quartulli 	struct iovec iov = {
1184959bc330SAntonio Quartulli 		.iov_base = payload,
1185959bc330SAntonio Quartulli 		.iov_len = payload->nlmsg_len,
1186959bc330SAntonio Quartulli 	};
1187959bc330SAntonio Quartulli 	struct msghdr nlmsg = {
1188959bc330SAntonio Quartulli 		.msg_name = &nladdr,
1189959bc330SAntonio Quartulli 		.msg_namelen = sizeof(nladdr),
1190959bc330SAntonio Quartulli 		.msg_iov = &iov,
1191959bc330SAntonio Quartulli 		.msg_iovlen = 1,
1192959bc330SAntonio Quartulli 	};
1193959bc330SAntonio Quartulli 
1194959bc330SAntonio Quartulli 	nladdr.nl_family = AF_NETLINK;
1195959bc330SAntonio Quartulli 	nladdr.nl_pid = peer;
1196959bc330SAntonio Quartulli 	nladdr.nl_groups = groups;
1197959bc330SAntonio Quartulli 
1198959bc330SAntonio Quartulli 	payload->nlmsg_seq = time(NULL);
1199959bc330SAntonio Quartulli 
1200959bc330SAntonio Quartulli 	/* no need to send reply */
1201959bc330SAntonio Quartulli 	if (!cb)
1202959bc330SAntonio Quartulli 		payload->nlmsg_flags |= NLM_F_ACK;
1203959bc330SAntonio Quartulli 
1204959bc330SAntonio Quartulli 	fd = ovpn_rt_socket();
1205959bc330SAntonio Quartulli 	if (fd < 0) {
1206959bc330SAntonio Quartulli 		fprintf(stderr, "%s: can't open rtnl socket\n", __func__);
1207959bc330SAntonio Quartulli 		return -errno;
1208959bc330SAntonio Quartulli 	}
1209959bc330SAntonio Quartulli 
1210959bc330SAntonio Quartulli 	ret = ovpn_rt_bind(fd, 0);
1211959bc330SAntonio Quartulli 	if (ret < 0) {
1212959bc330SAntonio Quartulli 		fprintf(stderr, "%s: can't bind rtnl socket\n", __func__);
1213959bc330SAntonio Quartulli 		ret = -errno;
1214959bc330SAntonio Quartulli 		goto out;
1215959bc330SAntonio Quartulli 	}
1216959bc330SAntonio Quartulli 
1217959bc330SAntonio Quartulli 	ret = sendmsg(fd, &nlmsg, 0);
1218959bc330SAntonio Quartulli 	if (ret < 0) {
1219959bc330SAntonio Quartulli 		fprintf(stderr, "%s: rtnl: error on sendmsg()\n", __func__);
1220959bc330SAntonio Quartulli 		ret = -errno;
1221959bc330SAntonio Quartulli 		goto out;
1222959bc330SAntonio Quartulli 	}
1223959bc330SAntonio Quartulli 
1224959bc330SAntonio Quartulli 	/* prepare buffer to store RTNL replies */
1225959bc330SAntonio Quartulli 	memset(buf, 0, sizeof(buf));
1226959bc330SAntonio Quartulli 	iov.iov_base = buf;
1227959bc330SAntonio Quartulli 
1228959bc330SAntonio Quartulli 	while (1) {
1229959bc330SAntonio Quartulli 		/*
1230959bc330SAntonio Quartulli 		 * iov_len is modified by recvmsg(), therefore has to be initialized before
1231959bc330SAntonio Quartulli 		 * using it again
1232959bc330SAntonio Quartulli 		 */
1233959bc330SAntonio Quartulli 		iov.iov_len = sizeof(buf);
1234959bc330SAntonio Quartulli 		rcv_len = recvmsg(fd, &nlmsg, 0);
1235959bc330SAntonio Quartulli 		if (rcv_len < 0) {
1236959bc330SAntonio Quartulli 			if (errno == EINTR || errno == EAGAIN) {
1237959bc330SAntonio Quartulli 				fprintf(stderr, "%s: interrupted call\n",
1238959bc330SAntonio Quartulli 					__func__);
1239959bc330SAntonio Quartulli 				continue;
1240959bc330SAntonio Quartulli 			}
1241959bc330SAntonio Quartulli 			fprintf(stderr, "%s: rtnl: error on recvmsg()\n",
1242959bc330SAntonio Quartulli 				__func__);
1243959bc330SAntonio Quartulli 			ret = -errno;
1244959bc330SAntonio Quartulli 			goto out;
1245959bc330SAntonio Quartulli 		}
1246959bc330SAntonio Quartulli 
1247959bc330SAntonio Quartulli 		if (rcv_len == 0) {
1248959bc330SAntonio Quartulli 			fprintf(stderr,
1249959bc330SAntonio Quartulli 				"%s: rtnl: socket reached unexpected EOF\n",
1250959bc330SAntonio Quartulli 				__func__);
1251959bc330SAntonio Quartulli 			ret = -EIO;
1252959bc330SAntonio Quartulli 			goto out;
1253959bc330SAntonio Quartulli 		}
1254959bc330SAntonio Quartulli 
1255959bc330SAntonio Quartulli 		if (nlmsg.msg_namelen != sizeof(nladdr)) {
1256959bc330SAntonio Quartulli 			fprintf(stderr,
1257959bc330SAntonio Quartulli 				"%s: sender address length: %u (expected %zu)\n",
1258959bc330SAntonio Quartulli 				__func__, nlmsg.msg_namelen, sizeof(nladdr));
1259959bc330SAntonio Quartulli 			ret = -EIO;
1260959bc330SAntonio Quartulli 			goto out;
1261959bc330SAntonio Quartulli 		}
1262959bc330SAntonio Quartulli 
1263959bc330SAntonio Quartulli 		h = (struct nlmsghdr *)buf;
1264959bc330SAntonio Quartulli 		while (rcv_len >= (int)sizeof(*h)) {
1265959bc330SAntonio Quartulli 			len = h->nlmsg_len;
1266959bc330SAntonio Quartulli 			rem_len = len - sizeof(*h);
1267959bc330SAntonio Quartulli 
1268959bc330SAntonio Quartulli 			if (rem_len < 0 || len > rcv_len) {
1269959bc330SAntonio Quartulli 				if (nlmsg.msg_flags & MSG_TRUNC) {
1270959bc330SAntonio Quartulli 					fprintf(stderr, "%s: truncated message\n",
1271959bc330SAntonio Quartulli 						__func__);
1272959bc330SAntonio Quartulli 					ret = -EIO;
1273959bc330SAntonio Quartulli 					goto out;
1274959bc330SAntonio Quartulli 				}
1275959bc330SAntonio Quartulli 				fprintf(stderr, "%s: malformed message: len=%d\n",
1276959bc330SAntonio Quartulli 					__func__, len);
1277959bc330SAntonio Quartulli 				ret = -EIO;
1278959bc330SAntonio Quartulli 				goto out;
1279959bc330SAntonio Quartulli 			}
1280959bc330SAntonio Quartulli 
1281959bc330SAntonio Quartulli 			if (h->nlmsg_type == NLMSG_DONE) {
1282959bc330SAntonio Quartulli 				ret = 0;
1283959bc330SAntonio Quartulli 				goto out;
1284959bc330SAntonio Quartulli 			}
1285959bc330SAntonio Quartulli 
1286959bc330SAntonio Quartulli 			if (h->nlmsg_type == NLMSG_ERROR) {
1287959bc330SAntonio Quartulli 				err = (struct nlmsgerr *)NLMSG_DATA(h);
1288959bc330SAntonio Quartulli 				if (rem_len < (int)sizeof(struct nlmsgerr)) {
1289959bc330SAntonio Quartulli 					fprintf(stderr, "%s: ERROR truncated\n",
1290959bc330SAntonio Quartulli 						__func__);
1291959bc330SAntonio Quartulli 					ret = -EIO;
1292959bc330SAntonio Quartulli 					goto out;
1293959bc330SAntonio Quartulli 				}
1294959bc330SAntonio Quartulli 
1295959bc330SAntonio Quartulli 				if (err->error) {
1296959bc330SAntonio Quartulli 					fprintf(stderr, "%s: (%d) %s\n",
1297959bc330SAntonio Quartulli 						__func__, err->error,
1298959bc330SAntonio Quartulli 						strerror(-err->error));
1299959bc330SAntonio Quartulli 					ret = err->error;
1300959bc330SAntonio Quartulli 					goto out;
1301959bc330SAntonio Quartulli 				}
1302959bc330SAntonio Quartulli 
1303959bc330SAntonio Quartulli 				ret = 0;
1304959bc330SAntonio Quartulli 				if (cb)	{
1305959bc330SAntonio Quartulli 					int r = cb(h, arg_cb);
1306959bc330SAntonio Quartulli 
1307959bc330SAntonio Quartulli 					if (r <= 0)
1308959bc330SAntonio Quartulli 						ret = r;
1309959bc330SAntonio Quartulli 				}
1310959bc330SAntonio Quartulli 				goto out;
1311959bc330SAntonio Quartulli 			}
1312959bc330SAntonio Quartulli 
1313959bc330SAntonio Quartulli 			if (cb) {
1314959bc330SAntonio Quartulli 				int r = cb(h, arg_cb);
1315959bc330SAntonio Quartulli 
1316959bc330SAntonio Quartulli 				if (r <= 0) {
1317959bc330SAntonio Quartulli 					ret = r;
1318959bc330SAntonio Quartulli 					goto out;
1319959bc330SAntonio Quartulli 				}
1320959bc330SAntonio Quartulli 			} else {
1321959bc330SAntonio Quartulli 				fprintf(stderr, "%s: RTNL: unexpected reply\n",
1322959bc330SAntonio Quartulli 					__func__);
1323959bc330SAntonio Quartulli 			}
1324959bc330SAntonio Quartulli 
1325959bc330SAntonio Quartulli 			rcv_len -= NLMSG_ALIGN(len);
1326959bc330SAntonio Quartulli 			h = (struct nlmsghdr *)((uint8_t *)h +
1327959bc330SAntonio Quartulli 						NLMSG_ALIGN(len));
1328959bc330SAntonio Quartulli 		}
1329959bc330SAntonio Quartulli 
1330959bc330SAntonio Quartulli 		if (nlmsg.msg_flags & MSG_TRUNC) {
1331959bc330SAntonio Quartulli 			fprintf(stderr, "%s: message truncated\n", __func__);
1332959bc330SAntonio Quartulli 			continue;
1333959bc330SAntonio Quartulli 		}
1334959bc330SAntonio Quartulli 
1335959bc330SAntonio Quartulli 		if (rcv_len) {
1336959bc330SAntonio Quartulli 			fprintf(stderr, "%s: rtnl: %d not parsed bytes\n",
1337959bc330SAntonio Quartulli 				__func__, rcv_len);
1338959bc330SAntonio Quartulli 			ret = -1;
1339959bc330SAntonio Quartulli 			goto out;
1340959bc330SAntonio Quartulli 		}
1341959bc330SAntonio Quartulli 	}
1342959bc330SAntonio Quartulli out:
1343959bc330SAntonio Quartulli 	close(fd);
1344959bc330SAntonio Quartulli 
1345959bc330SAntonio Quartulli 	return ret;
1346959bc330SAntonio Quartulli }
1347959bc330SAntonio Quartulli 
1348959bc330SAntonio Quartulli struct ovpn_link_req {
1349959bc330SAntonio Quartulli 	struct nlmsghdr n;
1350959bc330SAntonio Quartulli 	struct ifinfomsg i;
1351959bc330SAntonio Quartulli 	char buf[256];
1352959bc330SAntonio Quartulli };
1353959bc330SAntonio Quartulli 
ovpn_new_iface(struct ovpn_ctx * ovpn)1354959bc330SAntonio Quartulli static int ovpn_new_iface(struct ovpn_ctx *ovpn)
1355959bc330SAntonio Quartulli {
1356959bc330SAntonio Quartulli 	struct rtattr *linkinfo, *data;
1357959bc330SAntonio Quartulli 	struct ovpn_link_req req = { 0 };
1358959bc330SAntonio Quartulli 	int ret = -1;
1359959bc330SAntonio Quartulli 
1360959bc330SAntonio Quartulli 	fprintf(stdout, "Creating interface %s with mode %u\n", ovpn->ifname,
1361959bc330SAntonio Quartulli 		ovpn->mode);
1362959bc330SAntonio Quartulli 
1363959bc330SAntonio Quartulli 	req.n.nlmsg_len = NLMSG_LENGTH(sizeof(req.i));
1364959bc330SAntonio Quartulli 	req.n.nlmsg_flags = NLM_F_REQUEST | NLM_F_CREATE | NLM_F_EXCL;
1365959bc330SAntonio Quartulli 	req.n.nlmsg_type = RTM_NEWLINK;
1366959bc330SAntonio Quartulli 
1367959bc330SAntonio Quartulli 	if (ovpn_addattr(&req.n, sizeof(req), IFLA_IFNAME, ovpn->ifname,
1368959bc330SAntonio Quartulli 			 strlen(ovpn->ifname) + 1) < 0)
1369959bc330SAntonio Quartulli 		goto err;
1370959bc330SAntonio Quartulli 
1371959bc330SAntonio Quartulli 	linkinfo = ovpn_nest_start(&req.n, sizeof(req), IFLA_LINKINFO);
1372959bc330SAntonio Quartulli 	if (!linkinfo)
1373959bc330SAntonio Quartulli 		goto err;
1374959bc330SAntonio Quartulli 
1375959bc330SAntonio Quartulli 	if (ovpn_addattr(&req.n, sizeof(req), IFLA_INFO_KIND, OVPN_FAMILY_NAME,
1376959bc330SAntonio Quartulli 			 strlen(OVPN_FAMILY_NAME) + 1) < 0)
1377959bc330SAntonio Quartulli 		goto err;
1378959bc330SAntonio Quartulli 
1379959bc330SAntonio Quartulli 	if (ovpn->mode_set) {
1380959bc330SAntonio Quartulli 		data = ovpn_nest_start(&req.n, sizeof(req), IFLA_INFO_DATA);
1381959bc330SAntonio Quartulli 		if (!data)
1382959bc330SAntonio Quartulli 			goto err;
1383959bc330SAntonio Quartulli 
1384959bc330SAntonio Quartulli 		if (ovpn_addattr(&req.n, sizeof(req), IFLA_OVPN_MODE,
1385959bc330SAntonio Quartulli 				 &ovpn->mode, sizeof(uint8_t)) < 0)
1386959bc330SAntonio Quartulli 			goto err;
1387959bc330SAntonio Quartulli 
1388959bc330SAntonio Quartulli 		ovpn_nest_end(&req.n, data);
1389959bc330SAntonio Quartulli 	}
1390959bc330SAntonio Quartulli 
1391959bc330SAntonio Quartulli 	ovpn_nest_end(&req.n, linkinfo);
1392959bc330SAntonio Quartulli 
1393959bc330SAntonio Quartulli 	req.i.ifi_family = AF_PACKET;
1394959bc330SAntonio Quartulli 
1395959bc330SAntonio Quartulli 	ret = ovpn_rt_send(&req.n, 0, 0, NULL, NULL);
1396959bc330SAntonio Quartulli err:
1397959bc330SAntonio Quartulli 	return ret;
1398959bc330SAntonio Quartulli }
1399959bc330SAntonio Quartulli 
ovpn_del_iface(struct ovpn_ctx * ovpn)1400959bc330SAntonio Quartulli static int ovpn_del_iface(struct ovpn_ctx *ovpn)
1401959bc330SAntonio Quartulli {
1402959bc330SAntonio Quartulli 	struct ovpn_link_req req = { 0 };
1403959bc330SAntonio Quartulli 
1404959bc330SAntonio Quartulli 	fprintf(stdout, "Deleting interface %s ifindex %u\n", ovpn->ifname,
1405959bc330SAntonio Quartulli 		ovpn->ifindex);
1406959bc330SAntonio Quartulli 
1407959bc330SAntonio Quartulli 	req.n.nlmsg_len = NLMSG_LENGTH(sizeof(req.i));
1408959bc330SAntonio Quartulli 	req.n.nlmsg_flags = NLM_F_REQUEST;
1409959bc330SAntonio Quartulli 	req.n.nlmsg_type = RTM_DELLINK;
1410959bc330SAntonio Quartulli 
1411959bc330SAntonio Quartulli 	req.i.ifi_family = AF_PACKET;
1412959bc330SAntonio Quartulli 	req.i.ifi_index = ovpn->ifindex;
1413959bc330SAntonio Quartulli 
1414959bc330SAntonio Quartulli 	return ovpn_rt_send(&req.n, 0, 0, NULL, NULL);
1415959bc330SAntonio Quartulli }
1416959bc330SAntonio Quartulli 
nl_seq_check(struct nl_msg (* msg)__always_unused,void (* arg)__always_unused)1417959bc330SAntonio Quartulli static int nl_seq_check(struct nl_msg (*msg)__always_unused,
1418959bc330SAntonio Quartulli 			void (*arg)__always_unused)
1419959bc330SAntonio Quartulli {
1420959bc330SAntonio Quartulli 	return NL_OK;
1421959bc330SAntonio Quartulli }
1422959bc330SAntonio Quartulli 
1423959bc330SAntonio Quartulli struct mcast_handler_args {
1424959bc330SAntonio Quartulli 	const char *group;
1425959bc330SAntonio Quartulli 	int id;
1426959bc330SAntonio Quartulli };
1427959bc330SAntonio Quartulli 
mcast_family_handler(struct nl_msg * msg,void * arg)1428959bc330SAntonio Quartulli static int mcast_family_handler(struct nl_msg *msg, void *arg)
1429959bc330SAntonio Quartulli {
1430959bc330SAntonio Quartulli 	struct mcast_handler_args *grp = arg;
1431959bc330SAntonio Quartulli 	struct nlattr *tb[CTRL_ATTR_MAX + 1];
1432959bc330SAntonio Quartulli 	struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));
1433959bc330SAntonio Quartulli 	struct nlattr *mcgrp;
1434959bc330SAntonio Quartulli 	int rem_mcgrp;
1435959bc330SAntonio Quartulli 
1436959bc330SAntonio Quartulli 	nla_parse(tb, CTRL_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
1437959bc330SAntonio Quartulli 		  genlmsg_attrlen(gnlh, 0), NULL);
1438959bc330SAntonio Quartulli 
1439959bc330SAntonio Quartulli 	if (!tb[CTRL_ATTR_MCAST_GROUPS])
1440959bc330SAntonio Quartulli 		return NL_SKIP;
1441959bc330SAntonio Quartulli 
1442959bc330SAntonio Quartulli 	nla_for_each_nested(mcgrp, tb[CTRL_ATTR_MCAST_GROUPS], rem_mcgrp) {
1443959bc330SAntonio Quartulli 		struct nlattr *tb_mcgrp[CTRL_ATTR_MCAST_GRP_MAX + 1];
1444959bc330SAntonio Quartulli 
1445959bc330SAntonio Quartulli 		nla_parse(tb_mcgrp, CTRL_ATTR_MCAST_GRP_MAX,
1446959bc330SAntonio Quartulli 			  nla_data(mcgrp), nla_len(mcgrp), NULL);
1447959bc330SAntonio Quartulli 
1448959bc330SAntonio Quartulli 		if (!tb_mcgrp[CTRL_ATTR_MCAST_GRP_NAME] ||
1449959bc330SAntonio Quartulli 		    !tb_mcgrp[CTRL_ATTR_MCAST_GRP_ID])
1450959bc330SAntonio Quartulli 			continue;
1451959bc330SAntonio Quartulli 		if (strncmp(nla_data(tb_mcgrp[CTRL_ATTR_MCAST_GRP_NAME]),
1452959bc330SAntonio Quartulli 			    grp->group, nla_len(tb_mcgrp[CTRL_ATTR_MCAST_GRP_NAME])))
1453959bc330SAntonio Quartulli 			continue;
1454959bc330SAntonio Quartulli 		grp->id = nla_get_u32(tb_mcgrp[CTRL_ATTR_MCAST_GRP_ID]);
1455959bc330SAntonio Quartulli 		break;
1456959bc330SAntonio Quartulli 	}
1457959bc330SAntonio Quartulli 
1458959bc330SAntonio Quartulli 	return NL_SKIP;
1459959bc330SAntonio Quartulli }
1460959bc330SAntonio Quartulli 
mcast_error_handler(struct sockaddr_nl (* nla)__always_unused,struct nlmsgerr * err,void * arg)1461959bc330SAntonio Quartulli static int mcast_error_handler(struct sockaddr_nl (*nla)__always_unused,
1462959bc330SAntonio Quartulli 			       struct nlmsgerr *err, void *arg)
1463959bc330SAntonio Quartulli {
1464959bc330SAntonio Quartulli 	int *ret = arg;
1465959bc330SAntonio Quartulli 
1466959bc330SAntonio Quartulli 	*ret = err->error;
1467959bc330SAntonio Quartulli 	return NL_STOP;
1468959bc330SAntonio Quartulli }
1469959bc330SAntonio Quartulli 
mcast_ack_handler(struct nl_msg (* msg)__always_unused,void * arg)1470959bc330SAntonio Quartulli static int mcast_ack_handler(struct nl_msg (*msg)__always_unused, void *arg)
1471959bc330SAntonio Quartulli {
1472959bc330SAntonio Quartulli 	int *ret = arg;
1473959bc330SAntonio Quartulli 
1474959bc330SAntonio Quartulli 	*ret = 0;
1475959bc330SAntonio Quartulli 	return NL_STOP;
1476959bc330SAntonio Quartulli }
1477959bc330SAntonio Quartulli 
ovpn_handle_msg(struct nl_msg * msg,void * arg)1478959bc330SAntonio Quartulli static int ovpn_handle_msg(struct nl_msg *msg, void *arg)
1479959bc330SAntonio Quartulli {
1480959bc330SAntonio Quartulli 	struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));
1481959bc330SAntonio Quartulli 	struct nlattr *attrs[OVPN_A_MAX + 1];
1482959bc330SAntonio Quartulli 	struct nlmsghdr *nlh = nlmsg_hdr(msg);
1483959bc330SAntonio Quartulli 	char ifname[IF_NAMESIZE];
1484959bc330SAntonio Quartulli 	int *ret = arg;
1485959bc330SAntonio Quartulli 	__u32 ifindex;
1486959bc330SAntonio Quartulli 
1487959bc330SAntonio Quartulli 	fprintf(stderr, "received message from ovpn-dco\n");
1488959bc330SAntonio Quartulli 
1489959bc330SAntonio Quartulli 	*ret = -1;
1490959bc330SAntonio Quartulli 
1491959bc330SAntonio Quartulli 	if (!genlmsg_valid_hdr(nlh, 0)) {
1492959bc330SAntonio Quartulli 		fprintf(stderr, "invalid header\n");
1493959bc330SAntonio Quartulli 		return NL_STOP;
1494959bc330SAntonio Quartulli 	}
1495959bc330SAntonio Quartulli 
1496959bc330SAntonio Quartulli 	if (nla_parse(attrs, OVPN_A_MAX, genlmsg_attrdata(gnlh, 0),
1497959bc330SAntonio Quartulli 		      genlmsg_attrlen(gnlh, 0), NULL)) {
1498959bc330SAntonio Quartulli 		fprintf(stderr, "received bogus data from ovpn-dco\n");
1499959bc330SAntonio Quartulli 		return NL_STOP;
1500959bc330SAntonio Quartulli 	}
1501959bc330SAntonio Quartulli 
1502959bc330SAntonio Quartulli 	if (!attrs[OVPN_A_IFINDEX]) {
1503959bc330SAntonio Quartulli 		fprintf(stderr, "no ifindex in this message\n");
1504959bc330SAntonio Quartulli 		return NL_STOP;
1505959bc330SAntonio Quartulli 	}
1506959bc330SAntonio Quartulli 
1507959bc330SAntonio Quartulli 	ifindex = nla_get_u32(attrs[OVPN_A_IFINDEX]);
1508959bc330SAntonio Quartulli 	if (!if_indextoname(ifindex, ifname)) {
1509959bc330SAntonio Quartulli 		fprintf(stderr, "cannot resolve ifname for ifindex: %u\n",
1510959bc330SAntonio Quartulli 			ifindex);
1511959bc330SAntonio Quartulli 		return NL_STOP;
1512959bc330SAntonio Quartulli 	}
1513959bc330SAntonio Quartulli 
1514959bc330SAntonio Quartulli 	switch (gnlh->cmd) {
1515959bc330SAntonio Quartulli 	case OVPN_CMD_PEER_DEL_NTF:
1516959bc330SAntonio Quartulli 		fprintf(stdout, "received CMD_PEER_DEL_NTF\n");
1517959bc330SAntonio Quartulli 		break;
1518959bc330SAntonio Quartulli 	case OVPN_CMD_KEY_SWAP_NTF:
1519959bc330SAntonio Quartulli 		fprintf(stdout, "received CMD_KEY_SWAP_NTF\n");
1520959bc330SAntonio Quartulli 		break;
1521959bc330SAntonio Quartulli 	default:
1522959bc330SAntonio Quartulli 		fprintf(stderr, "received unknown command: %d\n", gnlh->cmd);
1523959bc330SAntonio Quartulli 		return NL_STOP;
1524959bc330SAntonio Quartulli 	}
1525959bc330SAntonio Quartulli 
1526959bc330SAntonio Quartulli 	*ret = 0;
1527959bc330SAntonio Quartulli 	return NL_OK;
1528959bc330SAntonio Quartulli }
1529959bc330SAntonio Quartulli 
ovpn_get_mcast_id(struct nl_sock * sock,const char * family,const char * group)1530959bc330SAntonio Quartulli static int ovpn_get_mcast_id(struct nl_sock *sock, const char *family,
1531959bc330SAntonio Quartulli 			     const char *group)
1532959bc330SAntonio Quartulli {
1533959bc330SAntonio Quartulli 	struct nl_msg *msg;
1534959bc330SAntonio Quartulli 	struct nl_cb *cb;
1535959bc330SAntonio Quartulli 	int ret, ctrlid;
1536959bc330SAntonio Quartulli 	struct mcast_handler_args grp = {
1537959bc330SAntonio Quartulli 		.group = group,
1538959bc330SAntonio Quartulli 		.id = -ENOENT,
1539959bc330SAntonio Quartulli 	};
1540959bc330SAntonio Quartulli 
1541959bc330SAntonio Quartulli 	msg = nlmsg_alloc();
1542959bc330SAntonio Quartulli 	if (!msg)
1543959bc330SAntonio Quartulli 		return -ENOMEM;
1544959bc330SAntonio Quartulli 
1545959bc330SAntonio Quartulli 	cb = nl_cb_alloc(NL_CB_DEFAULT);
1546959bc330SAntonio Quartulli 	if (!cb) {
1547959bc330SAntonio Quartulli 		ret = -ENOMEM;
1548959bc330SAntonio Quartulli 		goto out_fail_cb;
1549959bc330SAntonio Quartulli 	}
1550959bc330SAntonio Quartulli 
1551959bc330SAntonio Quartulli 	ctrlid = genl_ctrl_resolve(sock, "nlctrl");
1552959bc330SAntonio Quartulli 
1553959bc330SAntonio Quartulli 	genlmsg_put(msg, 0, 0, ctrlid, 0, 0, CTRL_CMD_GETFAMILY, 0);
1554959bc330SAntonio Quartulli 
1555959bc330SAntonio Quartulli 	ret = -ENOBUFS;
1556959bc330SAntonio Quartulli 	NLA_PUT_STRING(msg, CTRL_ATTR_FAMILY_NAME, family);
1557959bc330SAntonio Quartulli 
1558959bc330SAntonio Quartulli 	ret = nl_send_auto_complete(sock, msg);
1559959bc330SAntonio Quartulli 	if (ret < 0)
1560959bc330SAntonio Quartulli 		goto nla_put_failure;
1561959bc330SAntonio Quartulli 
1562959bc330SAntonio Quartulli 	ret = 1;
1563959bc330SAntonio Quartulli 
1564959bc330SAntonio Quartulli 	nl_cb_err(cb, NL_CB_CUSTOM, mcast_error_handler, &ret);
1565959bc330SAntonio Quartulli 	nl_cb_set(cb, NL_CB_ACK, NL_CB_CUSTOM, mcast_ack_handler, &ret);
1566959bc330SAntonio Quartulli 	nl_cb_set(cb, NL_CB_VALID, NL_CB_CUSTOM, mcast_family_handler, &grp);
1567959bc330SAntonio Quartulli 
1568959bc330SAntonio Quartulli 	while (ret > 0)
1569959bc330SAntonio Quartulli 		nl_recvmsgs(sock, cb);
1570959bc330SAntonio Quartulli 
1571959bc330SAntonio Quartulli 	if (ret == 0)
1572959bc330SAntonio Quartulli 		ret = grp.id;
1573959bc330SAntonio Quartulli  nla_put_failure:
1574959bc330SAntonio Quartulli 	nl_cb_put(cb);
1575959bc330SAntonio Quartulli  out_fail_cb:
1576959bc330SAntonio Quartulli 	nlmsg_free(msg);
1577959bc330SAntonio Quartulli 	return ret;
1578959bc330SAntonio Quartulli }
1579959bc330SAntonio Quartulli 
ovpn_listen_mcast(void)1580959bc330SAntonio Quartulli static int ovpn_listen_mcast(void)
1581959bc330SAntonio Quartulli {
1582959bc330SAntonio Quartulli 	struct nl_sock *sock;
1583959bc330SAntonio Quartulli 	struct nl_cb *cb;
1584959bc330SAntonio Quartulli 	int mcid, ret;
1585959bc330SAntonio Quartulli 
1586959bc330SAntonio Quartulli 	sock = nl_socket_alloc();
1587959bc330SAntonio Quartulli 	if (!sock) {
1588959bc330SAntonio Quartulli 		fprintf(stderr, "cannot allocate netlink socket\n");
1589959bc330SAntonio Quartulli 		goto err_free;
1590959bc330SAntonio Quartulli 	}
1591959bc330SAntonio Quartulli 
1592959bc330SAntonio Quartulli 	nl_socket_set_buffer_size(sock, 8192, 8192);
1593959bc330SAntonio Quartulli 
1594959bc330SAntonio Quartulli 	ret = genl_connect(sock);
1595959bc330SAntonio Quartulli 	if (ret < 0) {
1596959bc330SAntonio Quartulli 		fprintf(stderr, "cannot connect to generic netlink: %s\n",
1597959bc330SAntonio Quartulli 			nl_geterror(ret));
1598959bc330SAntonio Quartulli 		goto err_free;
1599959bc330SAntonio Quartulli 	}
1600959bc330SAntonio Quartulli 
1601959bc330SAntonio Quartulli 	mcid = ovpn_get_mcast_id(sock, OVPN_FAMILY_NAME, OVPN_MCGRP_PEERS);
1602959bc330SAntonio Quartulli 	if (mcid < 0) {
1603959bc330SAntonio Quartulli 		fprintf(stderr, "cannot get mcast group: %s\n",
1604959bc330SAntonio Quartulli 			nl_geterror(mcid));
1605959bc330SAntonio Quartulli 		goto err_free;
1606959bc330SAntonio Quartulli 	}
1607959bc330SAntonio Quartulli 
1608959bc330SAntonio Quartulli 	ret = nl_socket_add_membership(sock, mcid);
1609959bc330SAntonio Quartulli 	if (ret) {
1610959bc330SAntonio Quartulli 		fprintf(stderr, "failed to join mcast group: %d\n", ret);
1611959bc330SAntonio Quartulli 		goto err_free;
1612959bc330SAntonio Quartulli 	}
1613959bc330SAntonio Quartulli 
1614959bc330SAntonio Quartulli 	ret = 1;
1615959bc330SAntonio Quartulli 	cb = nl_cb_alloc(NL_CB_DEFAULT);
1616959bc330SAntonio Quartulli 	nl_cb_set(cb, NL_CB_SEQ_CHECK, NL_CB_CUSTOM, nl_seq_check, NULL);
1617959bc330SAntonio Quartulli 	nl_cb_set(cb, NL_CB_VALID, NL_CB_CUSTOM, ovpn_handle_msg, &ret);
1618959bc330SAntonio Quartulli 	nl_cb_err(cb, NL_CB_CUSTOM, ovpn_nl_cb_error, &ret);
1619959bc330SAntonio Quartulli 
1620959bc330SAntonio Quartulli 	while (ret == 1) {
1621959bc330SAntonio Quartulli 		int err = nl_recvmsgs(sock, cb);
1622959bc330SAntonio Quartulli 
1623959bc330SAntonio Quartulli 		if (err < 0) {
1624959bc330SAntonio Quartulli 			fprintf(stderr,
1625959bc330SAntonio Quartulli 				"cannot receive netlink message: (%d) %s\n",
1626959bc330SAntonio Quartulli 				err, nl_geterror(-err));
1627959bc330SAntonio Quartulli 			ret = -1;
1628959bc330SAntonio Quartulli 			break;
1629959bc330SAntonio Quartulli 		}
1630959bc330SAntonio Quartulli 	}
1631959bc330SAntonio Quartulli 
1632959bc330SAntonio Quartulli 	nl_cb_put(cb);
1633959bc330SAntonio Quartulli err_free:
1634959bc330SAntonio Quartulli 	nl_socket_free(sock);
1635959bc330SAntonio Quartulli 	return ret;
1636959bc330SAntonio Quartulli }
1637959bc330SAntonio Quartulli 
usage(const char * cmd)1638959bc330SAntonio Quartulli static void usage(const char *cmd)
1639959bc330SAntonio Quartulli {
1640959bc330SAntonio Quartulli 	fprintf(stderr,
1641959bc330SAntonio Quartulli 		"Usage %s <command> <iface> [arguments..]\n",
1642959bc330SAntonio Quartulli 		cmd);
1643959bc330SAntonio Quartulli 	fprintf(stderr, "where <command> can be one of the following\n\n");
1644959bc330SAntonio Quartulli 
1645959bc330SAntonio Quartulli 	fprintf(stderr, "* new_iface <iface> [mode]: create new ovpn interface\n");
1646959bc330SAntonio Quartulli 	fprintf(stderr, "\tiface: ovpn interface name\n");
1647959bc330SAntonio Quartulli 	fprintf(stderr, "\tmode:\n");
1648959bc330SAntonio Quartulli 	fprintf(stderr, "\t\t- P2P for peer-to-peer mode (i.e. client)\n");
1649959bc330SAntonio Quartulli 	fprintf(stderr, "\t\t- MP for multi-peer mode (i.e. server)\n");
1650959bc330SAntonio Quartulli 
1651959bc330SAntonio Quartulli 	fprintf(stderr, "* del_iface <iface>: delete ovpn interface\n");
1652959bc330SAntonio Quartulli 	fprintf(stderr, "\tiface: ovpn interface name\n");
1653959bc330SAntonio Quartulli 
1654959bc330SAntonio Quartulli 	fprintf(stderr,
1655959bc330SAntonio Quartulli 		"* listen <iface> <lport> <peers_file> [ipv6]: listen for incoming peer TCP connections\n");
1656959bc330SAntonio Quartulli 	fprintf(stderr, "\tiface: ovpn interface name\n");
1657959bc330SAntonio Quartulli 	fprintf(stderr, "\tlport: TCP port to listen to\n");
1658959bc330SAntonio Quartulli 	fprintf(stderr,
1659959bc330SAntonio Quartulli 		"\tpeers_file: file containing one peer per line: Line format:\n");
1660959bc330SAntonio Quartulli 	fprintf(stderr, "\t\t<peer_id> <vpnaddr>\n");
1661959bc330SAntonio Quartulli 	fprintf(stderr,
1662959bc330SAntonio Quartulli 		"\tipv6: whether the socket should listen to the IPv6 wildcard address\n");
1663959bc330SAntonio Quartulli 
1664959bc330SAntonio Quartulli 	fprintf(stderr,
1665959bc330SAntonio Quartulli 		"* connect <iface> <peer_id> <raddr> <rport> [key_file]: start connecting peer of TCP-based VPN session\n");
1666959bc330SAntonio Quartulli 	fprintf(stderr, "\tiface: ovpn interface name\n");
1667959bc330SAntonio Quartulli 	fprintf(stderr, "\tpeer_id: peer ID of the connecting peer\n");
1668959bc330SAntonio Quartulli 	fprintf(stderr, "\traddr: peer IP address to connect to\n");
1669959bc330SAntonio Quartulli 	fprintf(stderr, "\trport: peer TCP port to connect to\n");
1670959bc330SAntonio Quartulli 	fprintf(stderr,
1671959bc330SAntonio Quartulli 		"\tkey_file: file containing the symmetric key for encryption\n");
1672959bc330SAntonio Quartulli 
1673959bc330SAntonio Quartulli 	fprintf(stderr,
1674959bc330SAntonio Quartulli 		"* new_peer <iface> <peer_id> <lport> <raddr> <rport> [vpnaddr]: add new peer\n");
1675959bc330SAntonio Quartulli 	fprintf(stderr, "\tiface: ovpn interface name\n");
1676959bc330SAntonio Quartulli 	fprintf(stderr, "\tlport: local UDP port to bind to\n");
1677959bc330SAntonio Quartulli 	fprintf(stderr,
1678959bc330SAntonio Quartulli 		"\tpeer_id: peer ID to be used in data packets to/from this peer\n");
1679959bc330SAntonio Quartulli 	fprintf(stderr, "\traddr: peer IP address\n");
1680959bc330SAntonio Quartulli 	fprintf(stderr, "\trport: peer UDP port\n");
1681959bc330SAntonio Quartulli 	fprintf(stderr, "\tvpnaddr: peer VPN IP\n");
1682959bc330SAntonio Quartulli 
1683959bc330SAntonio Quartulli 	fprintf(stderr,
1684959bc330SAntonio Quartulli 		"* new_multi_peer <iface> <lport> <peers_file>: add multiple peers as listed in the file\n");
1685959bc330SAntonio Quartulli 	fprintf(stderr, "\tiface: ovpn interface name\n");
1686959bc330SAntonio Quartulli 	fprintf(stderr, "\tlport: local UDP port to bind to\n");
1687959bc330SAntonio Quartulli 	fprintf(stderr,
1688959bc330SAntonio Quartulli 		"\tpeers_file: text file containing one peer per line. Line format:\n");
1689959bc330SAntonio Quartulli 	fprintf(stderr, "\t\t<peer_id> <raddr> <rport> <vpnaddr>\n");
1690959bc330SAntonio Quartulli 
1691959bc330SAntonio Quartulli 	fprintf(stderr,
1692959bc330SAntonio Quartulli 		"* set_peer <iface> <peer_id> <keepalive_interval> <keepalive_timeout>: set peer attributes\n");
1693959bc330SAntonio Quartulli 	fprintf(stderr, "\tiface: ovpn interface name\n");
1694959bc330SAntonio Quartulli 	fprintf(stderr, "\tpeer_id: peer ID of the peer to modify\n");
1695959bc330SAntonio Quartulli 	fprintf(stderr,
1696959bc330SAntonio Quartulli 		"\tkeepalive_interval: interval for sending ping messages\n");
1697959bc330SAntonio Quartulli 	fprintf(stderr,
1698959bc330SAntonio Quartulli 		"\tkeepalive_timeout: time after which a peer is timed out\n");
1699959bc330SAntonio Quartulli 
1700959bc330SAntonio Quartulli 	fprintf(stderr, "* del_peer <iface> <peer_id>: delete peer\n");
1701959bc330SAntonio Quartulli 	fprintf(stderr, "\tiface: ovpn interface name\n");
1702959bc330SAntonio Quartulli 	fprintf(stderr, "\tpeer_id: peer ID of the peer to delete\n");
1703959bc330SAntonio Quartulli 
1704959bc330SAntonio Quartulli 	fprintf(stderr, "* get_peer <iface> [peer_id]: retrieve peer(s) status\n");
1705959bc330SAntonio Quartulli 	fprintf(stderr, "\tiface: ovpn interface name\n");
1706959bc330SAntonio Quartulli 	fprintf(stderr,
1707959bc330SAntonio Quartulli 		"\tpeer_id: peer ID of the peer to query. All peers are returned if omitted\n");
1708959bc330SAntonio Quartulli 
1709959bc330SAntonio Quartulli 	fprintf(stderr,
1710959bc330SAntonio Quartulli 		"* new_key <iface> <peer_id> <slot> <key_id> <cipher> <key_dir> <key_file>: set data channel key\n");
1711959bc330SAntonio Quartulli 	fprintf(stderr, "\tiface: ovpn interface name\n");
1712959bc330SAntonio Quartulli 	fprintf(stderr,
1713959bc330SAntonio Quartulli 		"\tpeer_id: peer ID of the peer to configure the key for\n");
1714959bc330SAntonio Quartulli 	fprintf(stderr, "\tslot: either 1 (primary) or 2 (secondary)\n");
1715959bc330SAntonio Quartulli 	fprintf(stderr, "\tkey_id: an ID from 0 to 7\n");
1716959bc330SAntonio Quartulli 	fprintf(stderr,
1717959bc330SAntonio Quartulli 		"\tcipher: cipher to use, supported: aes (AES-GCM), chachapoly (CHACHA20POLY1305)\n");
1718959bc330SAntonio Quartulli 	fprintf(stderr,
1719959bc330SAntonio Quartulli 		"\tkey_dir: key direction, must 0 on one host and 1 on the other\n");
1720959bc330SAntonio Quartulli 	fprintf(stderr, "\tkey_file: file containing the pre-shared key\n");
1721959bc330SAntonio Quartulli 
1722959bc330SAntonio Quartulli 	fprintf(stderr,
1723959bc330SAntonio Quartulli 		"* del_key <iface> <peer_id> [slot]: erase existing data channel key\n");
1724959bc330SAntonio Quartulli 	fprintf(stderr, "\tiface: ovpn interface name\n");
1725959bc330SAntonio Quartulli 	fprintf(stderr, "\tpeer_id: peer ID of the peer to modify\n");
1726959bc330SAntonio Quartulli 	fprintf(stderr, "\tslot: slot to erase. PRIMARY if omitted\n");
1727959bc330SAntonio Quartulli 
1728959bc330SAntonio Quartulli 	fprintf(stderr,
1729959bc330SAntonio Quartulli 		"* get_key <iface> <peer_id> <slot>: retrieve non sensible key data\n");
1730959bc330SAntonio Quartulli 	fprintf(stderr, "\tiface: ovpn interface name\n");
1731959bc330SAntonio Quartulli 	fprintf(stderr, "\tpeer_id: peer ID of the peer to query\n");
1732959bc330SAntonio Quartulli 	fprintf(stderr, "\tslot: either 1 (primary) or 2 (secondary)\n");
1733959bc330SAntonio Quartulli 
1734959bc330SAntonio Quartulli 	fprintf(stderr,
1735959bc330SAntonio Quartulli 		"* swap_keys <iface> <peer_id>: swap content of primary and secondary key slots\n");
1736959bc330SAntonio Quartulli 	fprintf(stderr, "\tiface: ovpn interface name\n");
1737959bc330SAntonio Quartulli 	fprintf(stderr, "\tpeer_id: peer ID of the peer to modify\n");
1738959bc330SAntonio Quartulli 
1739959bc330SAntonio Quartulli 	fprintf(stderr,
1740959bc330SAntonio Quartulli 		"* listen_mcast: listen to ovpn netlink multicast messages\n");
1741959bc330SAntonio Quartulli }
1742959bc330SAntonio Quartulli 
ovpn_parse_remote(struct ovpn_ctx * ovpn,const char * host,const char * service,const char * vpnip)1743959bc330SAntonio Quartulli static int ovpn_parse_remote(struct ovpn_ctx *ovpn, const char *host,
1744959bc330SAntonio Quartulli 			     const char *service, const char *vpnip)
1745959bc330SAntonio Quartulli {
1746959bc330SAntonio Quartulli 	int ret;
1747959bc330SAntonio Quartulli 	struct addrinfo *result;
1748959bc330SAntonio Quartulli 	struct addrinfo hints = {
1749959bc330SAntonio Quartulli 		.ai_family = ovpn->sa_family,
1750959bc330SAntonio Quartulli 		.ai_socktype = SOCK_DGRAM,
1751959bc330SAntonio Quartulli 		.ai_protocol = IPPROTO_UDP
1752959bc330SAntonio Quartulli 	};
1753959bc330SAntonio Quartulli 
1754959bc330SAntonio Quartulli 	if (host) {
1755959bc330SAntonio Quartulli 		ret = getaddrinfo(host, service, &hints, &result);
17568624daf9SAntonio Quartulli 		if (ret) {
17578624daf9SAntonio Quartulli 			fprintf(stderr, "getaddrinfo on remote error: %s\n",
17588624daf9SAntonio Quartulli 				gai_strerror(ret));
1759959bc330SAntonio Quartulli 			return -1;
17608624daf9SAntonio Quartulli 		}
1761959bc330SAntonio Quartulli 
1762959bc330SAntonio Quartulli 		if (!(result->ai_family == AF_INET &&
1763959bc330SAntonio Quartulli 		      result->ai_addrlen == sizeof(struct sockaddr_in)) &&
1764959bc330SAntonio Quartulli 		    !(result->ai_family == AF_INET6 &&
1765959bc330SAntonio Quartulli 		      result->ai_addrlen == sizeof(struct sockaddr_in6))) {
1766959bc330SAntonio Quartulli 			ret = -EINVAL;
1767959bc330SAntonio Quartulli 			goto out;
1768959bc330SAntonio Quartulli 		}
1769959bc330SAntonio Quartulli 
1770959bc330SAntonio Quartulli 		memcpy(&ovpn->remote, result->ai_addr, result->ai_addrlen);
1771959bc330SAntonio Quartulli 	}
1772959bc330SAntonio Quartulli 
1773959bc330SAntonio Quartulli 	if (vpnip) {
1774959bc330SAntonio Quartulli 		ret = getaddrinfo(vpnip, NULL, &hints, &result);
17758624daf9SAntonio Quartulli 		if (ret) {
17768624daf9SAntonio Quartulli 			fprintf(stderr, "getaddrinfo on vpnip error: %s\n",
17778624daf9SAntonio Quartulli 				gai_strerror(ret));
1778959bc330SAntonio Quartulli 			return -1;
17798624daf9SAntonio Quartulli 		}
1780959bc330SAntonio Quartulli 
1781959bc330SAntonio Quartulli 		if (!(result->ai_family == AF_INET &&
1782959bc330SAntonio Quartulli 		      result->ai_addrlen == sizeof(struct sockaddr_in)) &&
1783959bc330SAntonio Quartulli 		    !(result->ai_family == AF_INET6 &&
1784959bc330SAntonio Quartulli 		      result->ai_addrlen == sizeof(struct sockaddr_in6))) {
1785959bc330SAntonio Quartulli 			ret = -EINVAL;
1786959bc330SAntonio Quartulli 			goto out;
1787959bc330SAntonio Quartulli 		}
1788959bc330SAntonio Quartulli 
1789959bc330SAntonio Quartulli 		memcpy(&ovpn->peer_ip, result->ai_addr, result->ai_addrlen);
1790959bc330SAntonio Quartulli 		ovpn->sa_family = result->ai_family;
1791959bc330SAntonio Quartulli 
1792959bc330SAntonio Quartulli 		ovpn->peer_ip_set = true;
1793959bc330SAntonio Quartulli 	}
1794959bc330SAntonio Quartulli 
1795959bc330SAntonio Quartulli 	ret = 0;
1796959bc330SAntonio Quartulli out:
1797959bc330SAntonio Quartulli 	freeaddrinfo(result);
1798959bc330SAntonio Quartulli 	return ret;
1799959bc330SAntonio Quartulli }
1800959bc330SAntonio Quartulli 
ovpn_parse_new_peer(struct ovpn_ctx * ovpn,const char * peer_id,const char * raddr,const char * rport,const char * vpnip)1801959bc330SAntonio Quartulli static int ovpn_parse_new_peer(struct ovpn_ctx *ovpn, const char *peer_id,
1802959bc330SAntonio Quartulli 			       const char *raddr, const char *rport,
1803959bc330SAntonio Quartulli 			       const char *vpnip)
1804959bc330SAntonio Quartulli {
1805959bc330SAntonio Quartulli 	ovpn->peer_id = strtoul(peer_id, NULL, 10);
1806959bc330SAntonio Quartulli 	if (errno == ERANGE || ovpn->peer_id > PEER_ID_UNDEF) {
1807959bc330SAntonio Quartulli 		fprintf(stderr, "peer ID value out of range\n");
1808959bc330SAntonio Quartulli 		return -1;
1809959bc330SAntonio Quartulli 	}
1810959bc330SAntonio Quartulli 
1811959bc330SAntonio Quartulli 	return ovpn_parse_remote(ovpn, raddr, rport, vpnip);
1812959bc330SAntonio Quartulli }
1813959bc330SAntonio Quartulli 
ovpn_parse_key_slot(const char * arg,struct ovpn_ctx * ovpn)1814959bc330SAntonio Quartulli static int ovpn_parse_key_slot(const char *arg, struct ovpn_ctx *ovpn)
1815959bc330SAntonio Quartulli {
1816959bc330SAntonio Quartulli 	int slot = strtoul(arg, NULL, 10);
1817959bc330SAntonio Quartulli 
1818959bc330SAntonio Quartulli 	if (errno == ERANGE || slot < 1 || slot > 2) {
1819959bc330SAntonio Quartulli 		fprintf(stderr, "key slot out of range\n");
1820959bc330SAntonio Quartulli 		return -1;
1821959bc330SAntonio Quartulli 	}
1822959bc330SAntonio Quartulli 
1823959bc330SAntonio Quartulli 	switch (slot) {
1824959bc330SAntonio Quartulli 	case 1:
1825959bc330SAntonio Quartulli 		ovpn->key_slot = OVPN_KEY_SLOT_PRIMARY;
1826959bc330SAntonio Quartulli 		break;
1827959bc330SAntonio Quartulli 	case 2:
1828959bc330SAntonio Quartulli 		ovpn->key_slot = OVPN_KEY_SLOT_SECONDARY;
1829959bc330SAntonio Quartulli 		break;
1830959bc330SAntonio Quartulli 	}
1831959bc330SAntonio Quartulli 
1832959bc330SAntonio Quartulli 	return 0;
1833959bc330SAntonio Quartulli }
1834959bc330SAntonio Quartulli 
ovpn_send_tcp_data(int socket)1835959bc330SAntonio Quartulli static int ovpn_send_tcp_data(int socket)
1836959bc330SAntonio Quartulli {
1837959bc330SAntonio Quartulli 	uint16_t len = htons(1000);
1838959bc330SAntonio Quartulli 	uint8_t buf[1002];
1839959bc330SAntonio Quartulli 	int ret;
1840959bc330SAntonio Quartulli 
1841959bc330SAntonio Quartulli 	memcpy(buf, &len, sizeof(len));
1842959bc330SAntonio Quartulli 	memset(buf + sizeof(len), 0x86, sizeof(buf) - sizeof(len));
1843959bc330SAntonio Quartulli 
1844959bc330SAntonio Quartulli 	ret = send(socket, buf, sizeof(buf), MSG_NOSIGNAL);
1845959bc330SAntonio Quartulli 
1846959bc330SAntonio Quartulli 	fprintf(stdout, "Sent %u bytes over TCP socket\n", ret);
1847959bc330SAntonio Quartulli 
1848959bc330SAntonio Quartulli 	return ret > 0 ? 0 : ret;
1849959bc330SAntonio Quartulli }
1850959bc330SAntonio Quartulli 
ovpn_recv_tcp_data(int socket)1851959bc330SAntonio Quartulli static int ovpn_recv_tcp_data(int socket)
1852959bc330SAntonio Quartulli {
1853959bc330SAntonio Quartulli 	uint8_t buf[1002];
1854959bc330SAntonio Quartulli 	uint16_t len;
1855959bc330SAntonio Quartulli 	int ret;
1856959bc330SAntonio Quartulli 
1857959bc330SAntonio Quartulli 	ret = recv(socket, buf, sizeof(buf), MSG_NOSIGNAL);
1858959bc330SAntonio Quartulli 
1859959bc330SAntonio Quartulli 	if (ret < 2) {
1860959bc330SAntonio Quartulli 		fprintf(stderr, ">>>> Error while reading TCP data: %d\n", ret);
1861959bc330SAntonio Quartulli 		return ret;
1862959bc330SAntonio Quartulli 	}
1863959bc330SAntonio Quartulli 
1864959bc330SAntonio Quartulli 	memcpy(&len, buf, sizeof(len));
1865959bc330SAntonio Quartulli 	len = ntohs(len);
1866959bc330SAntonio Quartulli 
1867959bc330SAntonio Quartulli 	fprintf(stdout, ">>>> Received %u bytes over TCP socket, header: %u\n",
1868959bc330SAntonio Quartulli 		ret, len);
1869959bc330SAntonio Quartulli 
1870959bc330SAntonio Quartulli 	return 0;
1871959bc330SAntonio Quartulli }
1872959bc330SAntonio Quartulli 
ovpn_parse_cmd(const char * cmd)1873959bc330SAntonio Quartulli static enum ovpn_cmd ovpn_parse_cmd(const char *cmd)
1874959bc330SAntonio Quartulli {
1875959bc330SAntonio Quartulli 	if (!strcmp(cmd, "new_iface"))
1876959bc330SAntonio Quartulli 		return CMD_NEW_IFACE;
1877959bc330SAntonio Quartulli 
1878959bc330SAntonio Quartulli 	if (!strcmp(cmd, "del_iface"))
1879959bc330SAntonio Quartulli 		return CMD_DEL_IFACE;
1880959bc330SAntonio Quartulli 
1881959bc330SAntonio Quartulli 	if (!strcmp(cmd, "listen"))
1882959bc330SAntonio Quartulli 		return CMD_LISTEN;
1883959bc330SAntonio Quartulli 
1884959bc330SAntonio Quartulli 	if (!strcmp(cmd, "connect"))
1885959bc330SAntonio Quartulli 		return CMD_CONNECT;
1886959bc330SAntonio Quartulli 
1887959bc330SAntonio Quartulli 	if (!strcmp(cmd, "new_peer"))
1888959bc330SAntonio Quartulli 		return CMD_NEW_PEER;
1889959bc330SAntonio Quartulli 
1890959bc330SAntonio Quartulli 	if (!strcmp(cmd, "new_multi_peer"))
1891959bc330SAntonio Quartulli 		return CMD_NEW_MULTI_PEER;
1892959bc330SAntonio Quartulli 
1893959bc330SAntonio Quartulli 	if (!strcmp(cmd, "set_peer"))
1894959bc330SAntonio Quartulli 		return CMD_SET_PEER;
1895959bc330SAntonio Quartulli 
1896959bc330SAntonio Quartulli 	if (!strcmp(cmd, "del_peer"))
1897959bc330SAntonio Quartulli 		return CMD_DEL_PEER;
1898959bc330SAntonio Quartulli 
1899959bc330SAntonio Quartulli 	if (!strcmp(cmd, "get_peer"))
1900959bc330SAntonio Quartulli 		return CMD_GET_PEER;
1901959bc330SAntonio Quartulli 
1902959bc330SAntonio Quartulli 	if (!strcmp(cmd, "new_key"))
1903959bc330SAntonio Quartulli 		return CMD_NEW_KEY;
1904959bc330SAntonio Quartulli 
1905959bc330SAntonio Quartulli 	if (!strcmp(cmd, "del_key"))
1906959bc330SAntonio Quartulli 		return CMD_DEL_KEY;
1907959bc330SAntonio Quartulli 
1908959bc330SAntonio Quartulli 	if (!strcmp(cmd, "get_key"))
1909959bc330SAntonio Quartulli 		return CMD_GET_KEY;
1910959bc330SAntonio Quartulli 
1911959bc330SAntonio Quartulli 	if (!strcmp(cmd, "swap_keys"))
1912959bc330SAntonio Quartulli 		return CMD_SWAP_KEYS;
1913959bc330SAntonio Quartulli 
1914959bc330SAntonio Quartulli 	if (!strcmp(cmd, "listen_mcast"))
1915959bc330SAntonio Quartulli 		return CMD_LISTEN_MCAST;
1916959bc330SAntonio Quartulli 
1917959bc330SAntonio Quartulli 	return CMD_INVALID;
1918959bc330SAntonio Quartulli }
1919959bc330SAntonio Quartulli 
1920959bc330SAntonio Quartulli /* Send process to background and waits for signal.
1921959bc330SAntonio Quartulli  *
1922959bc330SAntonio Quartulli  * This helper is called at the end of commands
1923959bc330SAntonio Quartulli  * creating sockets, so that the latter stay alive
1924959bc330SAntonio Quartulli  * along with the process that created them.
1925959bc330SAntonio Quartulli  *
1926959bc330SAntonio Quartulli  * A signal is expected to be delivered in order to
1927959bc330SAntonio Quartulli  * terminate the waiting processes
1928959bc330SAntonio Quartulli  */
ovpn_waitbg(void)1929959bc330SAntonio Quartulli static void ovpn_waitbg(void)
1930959bc330SAntonio Quartulli {
1931959bc330SAntonio Quartulli 	daemon(1, 1);
1932959bc330SAntonio Quartulli 	pause();
1933959bc330SAntonio Quartulli }
1934959bc330SAntonio Quartulli 
ovpn_run_cmd(struct ovpn_ctx * ovpn)1935959bc330SAntonio Quartulli static int ovpn_run_cmd(struct ovpn_ctx *ovpn)
1936959bc330SAntonio Quartulli {
1937944f8b6aSAntonio Quartulli 	char peer_id[10], vpnip[INET6_ADDRSTRLEN], laddr[128], lport[10];
1938944f8b6aSAntonio Quartulli 	char raddr[128], rport[10];
1939959bc330SAntonio Quartulli 	int n, ret;
1940959bc330SAntonio Quartulli 	FILE *fp;
1941959bc330SAntonio Quartulli 
1942959bc330SAntonio Quartulli 	switch (ovpn->cmd) {
1943959bc330SAntonio Quartulli 	case CMD_NEW_IFACE:
1944959bc330SAntonio Quartulli 		ret = ovpn_new_iface(ovpn);
1945959bc330SAntonio Quartulli 		break;
1946959bc330SAntonio Quartulli 	case CMD_DEL_IFACE:
1947959bc330SAntonio Quartulli 		ret = ovpn_del_iface(ovpn);
1948959bc330SAntonio Quartulli 		break;
1949959bc330SAntonio Quartulli 	case CMD_LISTEN:
1950959bc330SAntonio Quartulli 		ret = ovpn_listen(ovpn, ovpn->sa_family);
1951959bc330SAntonio Quartulli 		if (ret < 0) {
1952959bc330SAntonio Quartulli 			fprintf(stderr, "cannot listen on TCP socket\n");
1953959bc330SAntonio Quartulli 			return ret;
1954959bc330SAntonio Quartulli 		}
1955959bc330SAntonio Quartulli 
1956959bc330SAntonio Quartulli 		fp = fopen(ovpn->peers_file, "r");
1957959bc330SAntonio Quartulli 		if (!fp) {
1958959bc330SAntonio Quartulli 			fprintf(stderr, "cannot open file: %s\n",
1959959bc330SAntonio Quartulli 				ovpn->peers_file);
1960959bc330SAntonio Quartulli 			return -1;
1961959bc330SAntonio Quartulli 		}
1962959bc330SAntonio Quartulli 
1963959bc330SAntonio Quartulli 		int num_peers = 0;
1964959bc330SAntonio Quartulli 
1965959bc330SAntonio Quartulli 		while ((n = fscanf(fp, "%s %s\n", peer_id, vpnip)) == 2) {
1966959bc330SAntonio Quartulli 			struct ovpn_ctx peer_ctx = { 0 };
1967959bc330SAntonio Quartulli 
1968959bc330SAntonio Quartulli 			if (num_peers == MAX_PEERS) {
1969959bc330SAntonio Quartulli 				fprintf(stderr, "max peers reached!\n");
1970959bc330SAntonio Quartulli 				return -E2BIG;
1971959bc330SAntonio Quartulli 			}
1972959bc330SAntonio Quartulli 
1973959bc330SAntonio Quartulli 			peer_ctx.ifindex = ovpn->ifindex;
1974959bc330SAntonio Quartulli 			peer_ctx.sa_family = ovpn->sa_family;
1975959bc330SAntonio Quartulli 
1976959bc330SAntonio Quartulli 			peer_ctx.socket = ovpn_accept(ovpn);
1977959bc330SAntonio Quartulli 			if (peer_ctx.socket < 0) {
1978959bc330SAntonio Quartulli 				fprintf(stderr, "cannot accept connection!\n");
1979959bc330SAntonio Quartulli 				return -1;
1980959bc330SAntonio Quartulli 			}
1981959bc330SAntonio Quartulli 
1982959bc330SAntonio Quartulli 			/* store peer sockets to test TCP I/O */
1983959bc330SAntonio Quartulli 			ovpn->cli_sockets[num_peers] = peer_ctx.socket;
1984959bc330SAntonio Quartulli 
1985959bc330SAntonio Quartulli 			ret = ovpn_parse_new_peer(&peer_ctx, peer_id, NULL,
1986959bc330SAntonio Quartulli 						  NULL, vpnip);
1987959bc330SAntonio Quartulli 			if (ret < 0) {
1988959bc330SAntonio Quartulli 				fprintf(stderr, "error while parsing line\n");
1989959bc330SAntonio Quartulli 				return -1;
1990959bc330SAntonio Quartulli 			}
1991959bc330SAntonio Quartulli 
1992959bc330SAntonio Quartulli 			ret = ovpn_new_peer(&peer_ctx, true);
1993959bc330SAntonio Quartulli 			if (ret < 0) {
1994959bc330SAntonio Quartulli 				fprintf(stderr,
1995959bc330SAntonio Quartulli 					"cannot add peer to VPN: %s %s\n",
1996959bc330SAntonio Quartulli 					peer_id, vpnip);
1997959bc330SAntonio Quartulli 				return ret;
1998959bc330SAntonio Quartulli 			}
1999959bc330SAntonio Quartulli 			num_peers++;
2000959bc330SAntonio Quartulli 		}
2001959bc330SAntonio Quartulli 
2002959bc330SAntonio Quartulli 		for (int i = 0; i < num_peers; i++) {
2003959bc330SAntonio Quartulli 			ret = ovpn_recv_tcp_data(ovpn->cli_sockets[i]);
2004959bc330SAntonio Quartulli 			if (ret < 0)
2005959bc330SAntonio Quartulli 				break;
2006959bc330SAntonio Quartulli 		}
2007959bc330SAntonio Quartulli 		ovpn_waitbg();
2008959bc330SAntonio Quartulli 		break;
2009959bc330SAntonio Quartulli 	case CMD_CONNECT:
2010959bc330SAntonio Quartulli 		ret = ovpn_connect(ovpn);
2011959bc330SAntonio Quartulli 		if (ret < 0) {
2012959bc330SAntonio Quartulli 			fprintf(stderr, "cannot connect TCP socket\n");
2013959bc330SAntonio Quartulli 			return ret;
2014959bc330SAntonio Quartulli 		}
2015959bc330SAntonio Quartulli 
2016959bc330SAntonio Quartulli 		ret = ovpn_new_peer(ovpn, true);
2017959bc330SAntonio Quartulli 		if (ret < 0) {
2018959bc330SAntonio Quartulli 			fprintf(stderr, "cannot add peer to VPN\n");
2019959bc330SAntonio Quartulli 			close(ovpn->socket);
2020959bc330SAntonio Quartulli 			return ret;
2021959bc330SAntonio Quartulli 		}
2022959bc330SAntonio Quartulli 
2023959bc330SAntonio Quartulli 		if (ovpn->cipher != OVPN_CIPHER_ALG_NONE) {
2024959bc330SAntonio Quartulli 			ret = ovpn_new_key(ovpn);
2025959bc330SAntonio Quartulli 			if (ret < 0) {
2026959bc330SAntonio Quartulli 				fprintf(stderr, "cannot set key\n");
2027959bc330SAntonio Quartulli 				return ret;
2028959bc330SAntonio Quartulli 			}
2029959bc330SAntonio Quartulli 		}
2030959bc330SAntonio Quartulli 
2031959bc330SAntonio Quartulli 		ret = ovpn_send_tcp_data(ovpn->socket);
2032959bc330SAntonio Quartulli 		ovpn_waitbg();
2033959bc330SAntonio Quartulli 		break;
2034959bc330SAntonio Quartulli 	case CMD_NEW_PEER:
2035959bc330SAntonio Quartulli 		ret = ovpn_udp_socket(ovpn, AF_INET6);
2036959bc330SAntonio Quartulli 		if (ret < 0)
2037959bc330SAntonio Quartulli 			return ret;
2038959bc330SAntonio Quartulli 
2039959bc330SAntonio Quartulli 		ret = ovpn_new_peer(ovpn, false);
2040959bc330SAntonio Quartulli 		ovpn_waitbg();
2041959bc330SAntonio Quartulli 		break;
2042959bc330SAntonio Quartulli 	case CMD_NEW_MULTI_PEER:
2043959bc330SAntonio Quartulli 		ret = ovpn_udp_socket(ovpn, AF_INET6);
2044959bc330SAntonio Quartulli 		if (ret < 0)
2045959bc330SAntonio Quartulli 			return ret;
2046959bc330SAntonio Quartulli 
2047959bc330SAntonio Quartulli 		fp = fopen(ovpn->peers_file, "r");
2048959bc330SAntonio Quartulli 		if (!fp) {
2049959bc330SAntonio Quartulli 			fprintf(stderr, "cannot open file: %s\n",
2050959bc330SAntonio Quartulli 				ovpn->peers_file);
2051959bc330SAntonio Quartulli 			return -1;
2052959bc330SAntonio Quartulli 		}
2053959bc330SAntonio Quartulli 
2054944f8b6aSAntonio Quartulli 		while ((n = fscanf(fp, "%s %s %s %s %s %s\n", peer_id, laddr,
2055944f8b6aSAntonio Quartulli 				   lport, raddr, rport, vpnip)) == 6) {
2056959bc330SAntonio Quartulli 			struct ovpn_ctx peer_ctx = { 0 };
2057959bc330SAntonio Quartulli 
2058959bc330SAntonio Quartulli 			peer_ctx.ifindex = ovpn->ifindex;
2059959bc330SAntonio Quartulli 			peer_ctx.socket = ovpn->socket;
2060959bc330SAntonio Quartulli 			peer_ctx.sa_family = AF_UNSPEC;
2061959bc330SAntonio Quartulli 
2062959bc330SAntonio Quartulli 			ret = ovpn_parse_new_peer(&peer_ctx, peer_id, raddr,
2063959bc330SAntonio Quartulli 						  rport, vpnip);
2064959bc330SAntonio Quartulli 			if (ret < 0) {
2065959bc330SAntonio Quartulli 				fprintf(stderr, "error while parsing line\n");
2066959bc330SAntonio Quartulli 				return -1;
2067959bc330SAntonio Quartulli 			}
2068959bc330SAntonio Quartulli 
2069959bc330SAntonio Quartulli 			ret = ovpn_new_peer(&peer_ctx, false);
2070959bc330SAntonio Quartulli 			if (ret < 0) {
2071959bc330SAntonio Quartulli 				fprintf(stderr,
2072959bc330SAntonio Quartulli 					"cannot add peer to VPN: %s %s %s %s\n",
2073959bc330SAntonio Quartulli 					peer_id, raddr, rport, vpnip);
2074959bc330SAntonio Quartulli 				return ret;
2075959bc330SAntonio Quartulli 			}
2076959bc330SAntonio Quartulli 		}
2077959bc330SAntonio Quartulli 		ovpn_waitbg();
2078959bc330SAntonio Quartulli 		break;
2079959bc330SAntonio Quartulli 	case CMD_SET_PEER:
2080959bc330SAntonio Quartulli 		ret = ovpn_set_peer(ovpn);
2081959bc330SAntonio Quartulli 		break;
2082959bc330SAntonio Quartulli 	case CMD_DEL_PEER:
2083959bc330SAntonio Quartulli 		ret = ovpn_del_peer(ovpn);
2084959bc330SAntonio Quartulli 		break;
2085959bc330SAntonio Quartulli 	case CMD_GET_PEER:
2086959bc330SAntonio Quartulli 		if (ovpn->peer_id == PEER_ID_UNDEF)
2087959bc330SAntonio Quartulli 			fprintf(stderr, "List of peers connected to: %s\n",
2088959bc330SAntonio Quartulli 				ovpn->ifname);
2089959bc330SAntonio Quartulli 
2090959bc330SAntonio Quartulli 		ret = ovpn_get_peer(ovpn);
2091959bc330SAntonio Quartulli 		break;
2092959bc330SAntonio Quartulli 	case CMD_NEW_KEY:
2093959bc330SAntonio Quartulli 		ret = ovpn_new_key(ovpn);
2094959bc330SAntonio Quartulli 		break;
2095959bc330SAntonio Quartulli 	case CMD_DEL_KEY:
2096959bc330SAntonio Quartulli 		ret = ovpn_del_key(ovpn);
2097959bc330SAntonio Quartulli 		break;
2098959bc330SAntonio Quartulli 	case CMD_GET_KEY:
2099959bc330SAntonio Quartulli 		ret = ovpn_get_key(ovpn);
2100959bc330SAntonio Quartulli 		break;
2101959bc330SAntonio Quartulli 	case CMD_SWAP_KEYS:
2102959bc330SAntonio Quartulli 		ret = ovpn_swap_keys(ovpn);
2103959bc330SAntonio Quartulli 		break;
2104959bc330SAntonio Quartulli 	case CMD_LISTEN_MCAST:
2105959bc330SAntonio Quartulli 		ret = ovpn_listen_mcast();
2106959bc330SAntonio Quartulli 		break;
2107959bc330SAntonio Quartulli 	case CMD_INVALID:
2108959bc330SAntonio Quartulli 		break;
2109959bc330SAntonio Quartulli 	}
2110959bc330SAntonio Quartulli 
2111959bc330SAntonio Quartulli 	return ret;
2112959bc330SAntonio Quartulli }
2113959bc330SAntonio Quartulli 
ovpn_parse_cmd_args(struct ovpn_ctx * ovpn,int argc,char * argv[])2114959bc330SAntonio Quartulli static int ovpn_parse_cmd_args(struct ovpn_ctx *ovpn, int argc, char *argv[])
2115959bc330SAntonio Quartulli {
2116959bc330SAntonio Quartulli 	int ret;
2117959bc330SAntonio Quartulli 
2118959bc330SAntonio Quartulli 	/* no args required for LISTEN_MCAST */
2119959bc330SAntonio Quartulli 	if (ovpn->cmd == CMD_LISTEN_MCAST)
2120959bc330SAntonio Quartulli 		return 0;
2121959bc330SAntonio Quartulli 
2122959bc330SAntonio Quartulli 	/* all commands need an ifname */
2123959bc330SAntonio Quartulli 	if (argc < 3)
2124959bc330SAntonio Quartulli 		return -EINVAL;
2125959bc330SAntonio Quartulli 
2126959bc330SAntonio Quartulli 	strscpy(ovpn->ifname, argv[2], IFNAMSIZ - 1);
2127959bc330SAntonio Quartulli 	ovpn->ifname[IFNAMSIZ - 1] = '\0';
2128959bc330SAntonio Quartulli 
2129959bc330SAntonio Quartulli 	/* all commands, except NEW_IFNAME, needs an ifindex */
2130959bc330SAntonio Quartulli 	if (ovpn->cmd != CMD_NEW_IFACE) {
2131959bc330SAntonio Quartulli 		ovpn->ifindex = if_nametoindex(ovpn->ifname);
2132959bc330SAntonio Quartulli 		if (!ovpn->ifindex) {
2133959bc330SAntonio Quartulli 			fprintf(stderr, "cannot find interface: %s\n",
2134959bc330SAntonio Quartulli 				strerror(errno));
2135959bc330SAntonio Quartulli 			return -1;
2136959bc330SAntonio Quartulli 		}
2137959bc330SAntonio Quartulli 	}
2138959bc330SAntonio Quartulli 
2139959bc330SAntonio Quartulli 	switch (ovpn->cmd) {
2140959bc330SAntonio Quartulli 	case CMD_NEW_IFACE:
2141959bc330SAntonio Quartulli 		if (argc < 4)
2142959bc330SAntonio Quartulli 			break;
2143959bc330SAntonio Quartulli 
2144959bc330SAntonio Quartulli 		if (!strcmp(argv[3], "P2P")) {
2145959bc330SAntonio Quartulli 			ovpn->mode = OVPN_MODE_P2P;
2146959bc330SAntonio Quartulli 		} else if (!strcmp(argv[3], "MP")) {
2147959bc330SAntonio Quartulli 			ovpn->mode = OVPN_MODE_MP;
2148959bc330SAntonio Quartulli 		} else {
2149959bc330SAntonio Quartulli 			fprintf(stderr, "Cannot parse iface mode: %s\n",
2150959bc330SAntonio Quartulli 				argv[3]);
2151959bc330SAntonio Quartulli 			return -1;
2152959bc330SAntonio Quartulli 		}
2153959bc330SAntonio Quartulli 		ovpn->mode_set = true;
2154959bc330SAntonio Quartulli 		break;
2155959bc330SAntonio Quartulli 	case CMD_DEL_IFACE:
2156959bc330SAntonio Quartulli 		break;
2157959bc330SAntonio Quartulli 	case CMD_LISTEN:
2158959bc330SAntonio Quartulli 		if (argc < 5)
2159959bc330SAntonio Quartulli 			return -EINVAL;
2160959bc330SAntonio Quartulli 
2161959bc330SAntonio Quartulli 		ovpn->lport = strtoul(argv[3], NULL, 10);
2162959bc330SAntonio Quartulli 		if (errno == ERANGE || ovpn->lport > 65535) {
2163959bc330SAntonio Quartulli 			fprintf(stderr, "lport value out of range\n");
2164959bc330SAntonio Quartulli 			return -1;
2165959bc330SAntonio Quartulli 		}
2166959bc330SAntonio Quartulli 
2167959bc330SAntonio Quartulli 		ovpn->peers_file = argv[4];
2168959bc330SAntonio Quartulli 
2169*fdf4064aSAntonio Quartulli 		ovpn->sa_family = AF_INET;
2170959bc330SAntonio Quartulli 		if (argc > 5 && !strcmp(argv[5], "ipv6"))
2171959bc330SAntonio Quartulli 			ovpn->sa_family = AF_INET6;
2172959bc330SAntonio Quartulli 		break;
2173959bc330SAntonio Quartulli 	case CMD_CONNECT:
2174959bc330SAntonio Quartulli 		if (argc < 6)
2175959bc330SAntonio Quartulli 			return -EINVAL;
2176959bc330SAntonio Quartulli 
2177959bc330SAntonio Quartulli 		ovpn->sa_family = AF_INET;
2178959bc330SAntonio Quartulli 
2179959bc330SAntonio Quartulli 		ret = ovpn_parse_new_peer(ovpn, argv[3], argv[4], argv[5],
2180959bc330SAntonio Quartulli 					  NULL);
2181959bc330SAntonio Quartulli 		if (ret < 0) {
2182959bc330SAntonio Quartulli 			fprintf(stderr, "Cannot parse remote peer data\n");
2183959bc330SAntonio Quartulli 			return -1;
2184959bc330SAntonio Quartulli 		}
2185959bc330SAntonio Quartulli 
2186959bc330SAntonio Quartulli 		if (argc > 6) {
2187959bc330SAntonio Quartulli 			ovpn->key_slot = OVPN_KEY_SLOT_PRIMARY;
2188959bc330SAntonio Quartulli 			ovpn->key_id = 0;
2189959bc330SAntonio Quartulli 			ovpn->cipher = OVPN_CIPHER_ALG_AES_GCM;
2190959bc330SAntonio Quartulli 			ovpn->key_dir = KEY_DIR_OUT;
2191959bc330SAntonio Quartulli 
2192959bc330SAntonio Quartulli 			ret = ovpn_parse_key(argv[6], ovpn);
2193959bc330SAntonio Quartulli 			if (ret)
2194959bc330SAntonio Quartulli 				return -1;
2195959bc330SAntonio Quartulli 		}
2196959bc330SAntonio Quartulli 		break;
2197959bc330SAntonio Quartulli 	case CMD_NEW_PEER:
2198959bc330SAntonio Quartulli 		if (argc < 7)
2199959bc330SAntonio Quartulli 			return -EINVAL;
2200959bc330SAntonio Quartulli 
2201959bc330SAntonio Quartulli 		ovpn->lport = strtoul(argv[4], NULL, 10);
2202959bc330SAntonio Quartulli 		if (errno == ERANGE || ovpn->lport > 65535) {
2203959bc330SAntonio Quartulli 			fprintf(stderr, "lport value out of range\n");
2204959bc330SAntonio Quartulli 			return -1;
2205959bc330SAntonio Quartulli 		}
2206959bc330SAntonio Quartulli 
2207959bc330SAntonio Quartulli 		const char *vpnip = (argc > 7) ? argv[7] : NULL;
2208959bc330SAntonio Quartulli 
2209959bc330SAntonio Quartulli 		ret = ovpn_parse_new_peer(ovpn, argv[3], argv[5], argv[6],
2210959bc330SAntonio Quartulli 					  vpnip);
2211959bc330SAntonio Quartulli 		if (ret < 0)
2212959bc330SAntonio Quartulli 			return -1;
2213959bc330SAntonio Quartulli 		break;
2214959bc330SAntonio Quartulli 	case CMD_NEW_MULTI_PEER:
2215959bc330SAntonio Quartulli 		if (argc < 5)
2216959bc330SAntonio Quartulli 			return -EINVAL;
2217959bc330SAntonio Quartulli 
2218959bc330SAntonio Quartulli 		ovpn->lport = strtoul(argv[3], NULL, 10);
2219959bc330SAntonio Quartulli 		if (errno == ERANGE || ovpn->lport > 65535) {
2220959bc330SAntonio Quartulli 			fprintf(stderr, "lport value out of range\n");
2221959bc330SAntonio Quartulli 			return -1;
2222959bc330SAntonio Quartulli 		}
2223959bc330SAntonio Quartulli 
2224959bc330SAntonio Quartulli 		ovpn->peers_file = argv[4];
2225959bc330SAntonio Quartulli 		break;
2226959bc330SAntonio Quartulli 	case CMD_SET_PEER:
2227959bc330SAntonio Quartulli 		if (argc < 6)
2228959bc330SAntonio Quartulli 			return -EINVAL;
2229959bc330SAntonio Quartulli 
2230959bc330SAntonio Quartulli 		ovpn->peer_id = strtoul(argv[3], NULL, 10);
2231959bc330SAntonio Quartulli 		if (errno == ERANGE || ovpn->peer_id > PEER_ID_UNDEF) {
2232959bc330SAntonio Quartulli 			fprintf(stderr, "peer ID value out of range\n");
2233959bc330SAntonio Quartulli 			return -1;
2234959bc330SAntonio Quartulli 		}
2235959bc330SAntonio Quartulli 
2236959bc330SAntonio Quartulli 		ovpn->keepalive_interval = strtoul(argv[4], NULL, 10);
2237959bc330SAntonio Quartulli 		if (errno == ERANGE) {
2238959bc330SAntonio Quartulli 			fprintf(stderr,
2239959bc330SAntonio Quartulli 				"keepalive interval value out of range\n");
2240959bc330SAntonio Quartulli 			return -1;
2241959bc330SAntonio Quartulli 		}
2242959bc330SAntonio Quartulli 
2243959bc330SAntonio Quartulli 		ovpn->keepalive_timeout = strtoul(argv[5], NULL, 10);
2244959bc330SAntonio Quartulli 		if (errno == ERANGE) {
2245959bc330SAntonio Quartulli 			fprintf(stderr,
2246959bc330SAntonio Quartulli 				"keepalive interval value out of range\n");
2247959bc330SAntonio Quartulli 			return -1;
2248959bc330SAntonio Quartulli 		}
2249959bc330SAntonio Quartulli 		break;
2250959bc330SAntonio Quartulli 	case CMD_DEL_PEER:
2251959bc330SAntonio Quartulli 		if (argc < 4)
2252959bc330SAntonio Quartulli 			return -EINVAL;
2253959bc330SAntonio Quartulli 
2254959bc330SAntonio Quartulli 		ovpn->peer_id = strtoul(argv[3], NULL, 10);
2255959bc330SAntonio Quartulli 		if (errno == ERANGE || ovpn->peer_id > PEER_ID_UNDEF) {
2256959bc330SAntonio Quartulli 			fprintf(stderr, "peer ID value out of range\n");
2257959bc330SAntonio Quartulli 			return -1;
2258959bc330SAntonio Quartulli 		}
2259959bc330SAntonio Quartulli 		break;
2260959bc330SAntonio Quartulli 	case CMD_GET_PEER:
2261959bc330SAntonio Quartulli 		ovpn->peer_id = PEER_ID_UNDEF;
2262959bc330SAntonio Quartulli 		if (argc > 3) {
2263959bc330SAntonio Quartulli 			ovpn->peer_id = strtoul(argv[3], NULL, 10);
2264959bc330SAntonio Quartulli 			if (errno == ERANGE || ovpn->peer_id > PEER_ID_UNDEF) {
2265959bc330SAntonio Quartulli 				fprintf(stderr, "peer ID value out of range\n");
2266959bc330SAntonio Quartulli 				return -1;
2267959bc330SAntonio Quartulli 			}
2268959bc330SAntonio Quartulli 		}
2269959bc330SAntonio Quartulli 		break;
2270959bc330SAntonio Quartulli 	case CMD_NEW_KEY:
2271959bc330SAntonio Quartulli 		if (argc < 9)
2272959bc330SAntonio Quartulli 			return -EINVAL;
2273959bc330SAntonio Quartulli 
2274959bc330SAntonio Quartulli 		ovpn->peer_id = strtoul(argv[3], NULL, 10);
2275959bc330SAntonio Quartulli 		if (errno == ERANGE) {
2276959bc330SAntonio Quartulli 			fprintf(stderr, "peer ID value out of range\n");
2277959bc330SAntonio Quartulli 			return -1;
2278959bc330SAntonio Quartulli 		}
2279959bc330SAntonio Quartulli 
2280959bc330SAntonio Quartulli 		ret = ovpn_parse_key_slot(argv[4], ovpn);
2281959bc330SAntonio Quartulli 		if (ret)
2282959bc330SAntonio Quartulli 			return -1;
2283959bc330SAntonio Quartulli 
2284959bc330SAntonio Quartulli 		ovpn->key_id = strtoul(argv[5], NULL, 10);
2285959bc330SAntonio Quartulli 		if (errno == ERANGE || ovpn->key_id > 2) {
2286959bc330SAntonio Quartulli 			fprintf(stderr, "key ID out of range\n");
2287959bc330SAntonio Quartulli 			return -1;
2288959bc330SAntonio Quartulli 		}
2289959bc330SAntonio Quartulli 
2290959bc330SAntonio Quartulli 		ret = ovpn_parse_cipher(argv[6], ovpn);
2291959bc330SAntonio Quartulli 		if (ret < 0)
2292959bc330SAntonio Quartulli 			return -1;
2293959bc330SAntonio Quartulli 
2294959bc330SAntonio Quartulli 		ret = ovpn_parse_key_direction(argv[7], ovpn);
2295959bc330SAntonio Quartulli 		if (ret < 0)
2296959bc330SAntonio Quartulli 			return -1;
2297959bc330SAntonio Quartulli 
2298959bc330SAntonio Quartulli 		ret = ovpn_parse_key(argv[8], ovpn);
2299959bc330SAntonio Quartulli 		if (ret)
2300959bc330SAntonio Quartulli 			return -1;
2301959bc330SAntonio Quartulli 		break;
2302959bc330SAntonio Quartulli 	case CMD_DEL_KEY:
2303959bc330SAntonio Quartulli 		if (argc < 4)
2304959bc330SAntonio Quartulli 			return -EINVAL;
2305959bc330SAntonio Quartulli 
2306959bc330SAntonio Quartulli 		ovpn->peer_id = strtoul(argv[3], NULL, 10);
2307959bc330SAntonio Quartulli 		if (errno == ERANGE) {
2308959bc330SAntonio Quartulli 			fprintf(stderr, "peer ID value out of range\n");
2309959bc330SAntonio Quartulli 			return -1;
2310959bc330SAntonio Quartulli 		}
2311959bc330SAntonio Quartulli 
2312959bc330SAntonio Quartulli 		ret = ovpn_parse_key_slot(argv[4], ovpn);
2313959bc330SAntonio Quartulli 		if (ret)
2314959bc330SAntonio Quartulli 			return ret;
2315959bc330SAntonio Quartulli 		break;
2316959bc330SAntonio Quartulli 	case CMD_GET_KEY:
2317959bc330SAntonio Quartulli 		if (argc < 5)
2318959bc330SAntonio Quartulli 			return -EINVAL;
2319959bc330SAntonio Quartulli 
2320959bc330SAntonio Quartulli 		ovpn->peer_id = strtoul(argv[3], NULL, 10);
2321959bc330SAntonio Quartulli 		if (errno == ERANGE) {
2322959bc330SAntonio Quartulli 			fprintf(stderr, "peer ID value out of range\n");
2323959bc330SAntonio Quartulli 			return -1;
2324959bc330SAntonio Quartulli 		}
2325959bc330SAntonio Quartulli 
2326959bc330SAntonio Quartulli 		ret = ovpn_parse_key_slot(argv[4], ovpn);
2327959bc330SAntonio Quartulli 		if (ret)
2328959bc330SAntonio Quartulli 			return ret;
2329959bc330SAntonio Quartulli 		break;
2330959bc330SAntonio Quartulli 	case CMD_SWAP_KEYS:
2331959bc330SAntonio Quartulli 		if (argc < 4)
2332959bc330SAntonio Quartulli 			return -EINVAL;
2333959bc330SAntonio Quartulli 
2334959bc330SAntonio Quartulli 		ovpn->peer_id = strtoul(argv[3], NULL, 10);
2335959bc330SAntonio Quartulli 		if (errno == ERANGE) {
2336959bc330SAntonio Quartulli 			fprintf(stderr, "peer ID value out of range\n");
2337959bc330SAntonio Quartulli 			return -1;
2338959bc330SAntonio Quartulli 		}
2339959bc330SAntonio Quartulli 		break;
2340959bc330SAntonio Quartulli 	case CMD_LISTEN_MCAST:
2341959bc330SAntonio Quartulli 		break;
2342959bc330SAntonio Quartulli 	case CMD_INVALID:
2343959bc330SAntonio Quartulli 		break;
2344959bc330SAntonio Quartulli 	}
2345959bc330SAntonio Quartulli 
2346959bc330SAntonio Quartulli 	return 0;
2347959bc330SAntonio Quartulli }
2348959bc330SAntonio Quartulli 
main(int argc,char * argv[])2349959bc330SAntonio Quartulli int main(int argc, char *argv[])
2350959bc330SAntonio Quartulli {
2351959bc330SAntonio Quartulli 	struct ovpn_ctx ovpn;
2352959bc330SAntonio Quartulli 	int ret;
2353959bc330SAntonio Quartulli 
2354959bc330SAntonio Quartulli 	if (argc < 2) {
2355959bc330SAntonio Quartulli 		usage(argv[0]);
2356959bc330SAntonio Quartulli 		return -1;
2357959bc330SAntonio Quartulli 	}
2358959bc330SAntonio Quartulli 
2359959bc330SAntonio Quartulli 	memset(&ovpn, 0, sizeof(ovpn));
2360944f8b6aSAntonio Quartulli 	ovpn.sa_family = AF_UNSPEC;
2361959bc330SAntonio Quartulli 	ovpn.cipher = OVPN_CIPHER_ALG_NONE;
2362959bc330SAntonio Quartulli 
2363959bc330SAntonio Quartulli 	ovpn.cmd = ovpn_parse_cmd(argv[1]);
2364959bc330SAntonio Quartulli 	if (ovpn.cmd == CMD_INVALID) {
2365959bc330SAntonio Quartulli 		fprintf(stderr, "Error: unknown command.\n\n");
2366959bc330SAntonio Quartulli 		usage(argv[0]);
2367959bc330SAntonio Quartulli 		return -1;
2368959bc330SAntonio Quartulli 	}
2369959bc330SAntonio Quartulli 
2370959bc330SAntonio Quartulli 	ret = ovpn_parse_cmd_args(&ovpn, argc, argv);
2371959bc330SAntonio Quartulli 	if (ret < 0) {
2372959bc330SAntonio Quartulli 		fprintf(stderr, "Error: invalid arguments.\n\n");
2373959bc330SAntonio Quartulli 		if (ret == -EINVAL)
2374959bc330SAntonio Quartulli 			usage(argv[0]);
2375959bc330SAntonio Quartulli 		return ret;
2376959bc330SAntonio Quartulli 	}
2377959bc330SAntonio Quartulli 
2378959bc330SAntonio Quartulli 	ret = ovpn_run_cmd(&ovpn);
2379959bc330SAntonio Quartulli 	if (ret)
2380959bc330SAntonio Quartulli 		fprintf(stderr, "Cannot execute command: %s (%d)\n",
2381959bc330SAntonio Quartulli 			strerror(-ret), ret);
2382959bc330SAntonio Quartulli 
2383959bc330SAntonio Quartulli 	return ret;
2384959bc330SAntonio Quartulli }
2385