/* defines to make checkpatch happy */ #define strscpy strncpy #define __always_unused __attribute__((__unused__))
/* libnl < 3.5.0 does not set the NLA_F_NESTED on its own, therefore we * have to explicitly do it to prevent the kernel from failing upon * parsing of the message
*/ #define nla_nest_start(_msg, _type) \
nla_nest_start(_msg, (_type) | NLA_F_NESTED)
/* libnl < 3.11.0 does not implement nla_get_uint() */
uint64_t ovpn_nla_get_uint(struct nlattr *attr)
{ if (nla_len(attr) == sizeof(uint32_t)) return nla_get_u32(attr); else return nla_get_u64(attr);
}
enum ovpn_key_direction key_dir; enum ovpn_key_slot key_slot; int key_id;
constchar *peers_file;
};
staticint ovpn_nl_recvmsgs(struct nl_ctx *ctx)
{ int ret;
ret = nl_recvmsgs(ctx->nl_sock, ctx->nl_cb);
switch (ret) { case -NLE_INTR:
fprintf(stderr, "netlink received interrupt due to signal - ignoring\n"); break; case -NLE_NOMEM:
fprintf(stderr, "netlink out of memory error\n"); break; case -NLE_AGAIN:
fprintf(stderr, "netlink reports blocking read - aborting wait\n"); break; default: if (ret)
fprintf(stderr, "netlink reports error (%d): %s\n",
ret, nl_geterror(-ret)); break;
}
return ret;
}
staticstruct nl_ctx *nl_ctx_alloc_flags(struct ovpn_ctx *ovpn, int cmd, int flags)
{ struct nl_ctx *ctx; int err, ret;
ctx = calloc(1, sizeof(*ctx)); if (!ctx) return NULL;
ret = ovpn_nl_msg_send(ctx, NULL);
nla_put_failure:
nl_ctx_free(ctx); return ret;
}
/* Helper function used to easily add attributes to a rtnl message */ staticint ovpn_addattr(struct nlmsghdr *n, int maxlen, int type, constvoid *data, int alen)
{ int len = RTA_LENGTH(alen); struct rtattr *rta;
if ((int)(NLMSG_ALIGN(n->nlmsg_len) + RTA_ALIGN(len)) > maxlen) {
fprintf(stderr, "%s: rtnl: message exceeded bound of %d\n",
__func__, maxlen); return -EMSGSIZE;
}
/* no need to send reply */ if (!cb)
payload->nlmsg_flags |= NLM_F_ACK;
fd = ovpn_rt_socket(); if (fd < 0) {
fprintf(stderr, "%s: can't open rtnl socket\n", __func__); return -errno;
}
ret = ovpn_rt_bind(fd, 0); if (ret < 0) {
fprintf(stderr, "%s: can't bind rtnl socket\n", __func__);
ret = -errno; goto out;
}
ret = sendmsg(fd, &nlmsg, 0); if (ret < 0) {
fprintf(stderr, "%s: rtnl: error on sendmsg()\n", __func__);
ret = -errno; goto out;
}
/* prepare buffer to store RTNL replies */
memset(buf, 0, sizeof(buf));
iov.iov_base = buf;
while (1) { /* * iov_len is modified by recvmsg(), therefore has to be initialized before * using it again
*/
iov.iov_len = sizeof(buf);
rcv_len = recvmsg(fd, &nlmsg, 0); if (rcv_len < 0) { if (errno == EINTR || errno == EAGAIN) {
fprintf(stderr, "%s: interrupted call\n",
__func__); continue;
}
fprintf(stderr, "%s: rtnl: error on recvmsg()\n",
__func__);
ret = -errno; goto out;
}
if (rcv_len == 0) {
fprintf(stderr, "%s: rtnl: socket reached unexpected EOF\n",
__func__);
ret = -EIO; goto out;
}
staticvoid usage(constchar *cmd)
{
fprintf(stderr, "Usage %s <command> <iface> [arguments..]\n",
cmd);
fprintf(stderr, "where <command> can be one of the following\n\n");
fprintf(stderr, "* listen <iface> <lport> <peers_file> [ipv6]: listen for incoming peer TCP connections\n");
fprintf(stderr, "\tiface: ovpn interface name\n");
fprintf(stderr, "\tlport: TCP port to listen to\n");
fprintf(stderr, "\tpeers_file: file containing one peer per line: Line format:\n");
fprintf(stderr, "\t\t<peer_id> <vpnaddr>\n");
fprintf(stderr, "\tipv6: whether the socket should listen to the IPv6 wildcard address\n");
fprintf(stderr, "* connect <iface> <peer_id> <raddr> <rport> [key_file]: start connecting peer of TCP-based VPN session\n");
fprintf(stderr, "\tiface: ovpn interface name\n");
fprintf(stderr, "\tpeer_id: peer ID of the connecting peer\n");
fprintf(stderr, "\traddr: peer IP address to connect to\n");
fprintf(stderr, "\trport: peer TCP port to connect to\n");
fprintf(stderr, "\tkey_file: file containing the symmetric key for encryption\n");
fprintf(stderr, "* new_peer <iface> <peer_id> <lport> <raddr> <rport> [vpnaddr]: add new peer\n");
fprintf(stderr, "\tiface: ovpn interface name\n");
fprintf(stderr, "\tlport: local UDP port to bind to\n");
fprintf(stderr, "\tpeer_id: peer ID to be used in data packets to/from this peer\n");
fprintf(stderr, "\traddr: peer IP address\n");
fprintf(stderr, "\trport: peer UDP port\n");
fprintf(stderr, "\tvpnaddr: peer VPN IP\n");
fprintf(stderr, "* new_multi_peer <iface> <lport> <peers_file>: add multiple peers as listed in the file\n");
fprintf(stderr, "\tiface: ovpn interface name\n");
fprintf(stderr, "\tlport: local UDP port to bind to\n");
fprintf(stderr, "\tpeers_file: text file containing one peer per line. Line format:\n");
fprintf(stderr, "\t\t<peer_id> <raddr> <rport> <vpnaddr>\n");
fprintf(stderr, "* set_peer <iface> <peer_id> <keepalive_interval> <keepalive_timeout>: set peer attributes\n");
fprintf(stderr, "\tiface: ovpn interface name\n");
fprintf(stderr, "\tpeer_id: peer ID of the peer to modify\n");
fprintf(stderr, "\tkeepalive_interval: interval for sending ping messages\n");
fprintf(stderr, "\tkeepalive_timeout: time after which a peer is timed out\n");
fprintf(stderr, "* del_peer <iface> <peer_id>: delete peer\n");
fprintf(stderr, "\tiface: ovpn interface name\n");
fprintf(stderr, "\tpeer_id: peer ID of the peer to delete\n");
fprintf(stderr, "* get_peer <iface> [peer_id]: retrieve peer(s) status\n");
fprintf(stderr, "\tiface: ovpn interface name\n");
fprintf(stderr, "\tpeer_id: peer ID of the peer to query. All peers are returned if omitted\n");
fprintf(stderr, "* new_key <iface> <peer_id> <slot> <key_id> <cipher> <key_dir> <key_file>: set data channel key\n");
fprintf(stderr, "\tiface: ovpn interface name\n");
fprintf(stderr, "\tpeer_id: peer ID of the peer to configure the key for\n");
fprintf(stderr, "\tslot: either 1 (primary) or 2 (secondary)\n");
fprintf(stderr, "\tkey_id: an ID from 0 to 7\n");
fprintf(stderr, "\tcipher: cipher to use, supported: aes (AES-GCM), chachapoly (CHACHA20POLY1305)\n");
fprintf(stderr, "\tkey_dir: key direction, must 0 on one host and 1 on the other\n");
fprintf(stderr, "\tkey_file: file containing the pre-shared key\n");
fprintf(stderr, "* del_key <iface> <peer_id> [slot]: erase existing data channel key\n");
fprintf(stderr, "\tiface: ovpn interface name\n");
fprintf(stderr, "\tpeer_id: peer ID of the peer to modify\n");
fprintf(stderr, "\tslot: slot to erase. PRIMARY if omitted\n");
fprintf(stderr, "* get_key <iface> <peer_id> <slot>: retrieve non sensible key data\n");
fprintf(stderr, "\tiface: ovpn interface name\n");
fprintf(stderr, "\tpeer_id: peer ID of the peer to query\n");
fprintf(stderr, "\tslot: either 1 (primary) or 2 (secondary)\n");
fprintf(stderr, "* swap_keys <iface> <peer_id>: swap content of primary and secondary key slots\n");
fprintf(stderr, "\tiface: ovpn interface name\n");
fprintf(stderr, "\tpeer_id: peer ID of the peer to modify\n");
fprintf(stderr, "* listen_mcast: listen to ovpn netlink multicast messages\n");
}
ret = send(socket, buf, sizeof(buf), MSG_NOSIGNAL);
fprintf(stdout, "Sent %u bytes over TCP socket\n", ret);
return ret > 0 ? 0 : ret;
}
staticint ovpn_recv_tcp_data(int socket)
{
uint8_t buf[1002];
uint16_t len; int ret;
ret = recv(socket, buf, sizeof(buf), MSG_NOSIGNAL);
if (ret < 2) {
fprintf(stderr, ">>>> Error while reading TCP data: %d\n", ret); return ret;
}
memcpy(&len, buf, sizeof(len));
len = ntohs(len);
fprintf(stdout, ">>>> Received %u bytes over TCP socket, header: %u\n",
ret, len);
return 0;
}
staticenum ovpn_cmd ovpn_parse_cmd(constchar *cmd)
{ if (!strcmp(cmd, "new_iface")) return CMD_NEW_IFACE;
if (!strcmp(cmd, "del_iface")) return CMD_DEL_IFACE;
if (!strcmp(cmd, "listen")) return CMD_LISTEN;
if (!strcmp(cmd, "connect")) return CMD_CONNECT;
if (!strcmp(cmd, "new_peer")) return CMD_NEW_PEER;
if (!strcmp(cmd, "new_multi_peer")) return CMD_NEW_MULTI_PEER;
if (!strcmp(cmd, "set_peer")) return CMD_SET_PEER;
if (!strcmp(cmd, "del_peer")) return CMD_DEL_PEER;
if (!strcmp(cmd, "get_peer")) return CMD_GET_PEER;
if (!strcmp(cmd, "new_key")) return CMD_NEW_KEY;
if (!strcmp(cmd, "del_key")) return CMD_DEL_KEY;
if (!strcmp(cmd, "get_key")) return CMD_GET_KEY;
if (!strcmp(cmd, "swap_keys")) return CMD_SWAP_KEYS;
if (!strcmp(cmd, "listen_mcast")) return CMD_LISTEN_MCAST;
return CMD_INVALID;
}
/* Send process to background and waits for signal. * * This helper is called at the end of commands * creating sockets, so that the latter stay alive * along with the process that created them. * * A signal is expected to be delivered in order to * terminate the waiting processes
*/ staticvoid ovpn_waitbg(void)
{
daemon(1, 1);
pause();
}
staticint ovpn_run_cmd(struct ovpn_ctx *ovpn)
{ char peer_id[10], vpnip[INET6_ADDRSTRLEN], laddr[128], lport[10]; char raddr[128], rport[10]; int n, ret;
FILE *fp;
switch (ovpn->cmd) { case CMD_NEW_IFACE:
ret = ovpn_new_iface(ovpn); break; case CMD_DEL_IFACE:
ret = ovpn_del_iface(ovpn); break; case CMD_LISTEN:
ret = ovpn_listen(ovpn, ovpn->sa_family); if (ret < 0) {
fprintf(stderr, "cannot listen on TCP socket\n"); return ret;
}
fp = fopen(ovpn->peers_file, "r"); if (!fp) {
fprintf(stderr, "cannot open file: %s\n",
ovpn->peers_file); return -1;
}
/* store peer sockets to test TCP I/O */
ovpn->cli_sockets[num_peers] = peer_ctx.socket;
ret = ovpn_parse_new_peer(&peer_ctx, peer_id, NULL,
NULL, vpnip); if (ret < 0) {
fprintf(stderr, "error while parsing line\n"); return -1;
}
ret = ovpn_new_peer(&peer_ctx, true); if (ret < 0) {
fprintf(stderr, "cannot add peer to VPN: %s %s\n",
peer_id, vpnip); return ret;
}
num_peers++;
}
for (int i = 0; i < num_peers; i++) {
ret = ovpn_recv_tcp_data(ovpn->cli_sockets[i]); if (ret < 0) break;
}
ovpn_waitbg(); break; case CMD_CONNECT:
ret = ovpn_connect(ovpn); if (ret < 0) {
fprintf(stderr, "cannot connect TCP socket\n"); return ret;
}
ret = ovpn_new_peer(ovpn, true); if (ret < 0) {
fprintf(stderr, "cannot add peer to VPN\n");
close(ovpn->socket); return ret;
}
if (ovpn->cipher != OVPN_CIPHER_ALG_NONE) {
ret = ovpn_new_key(ovpn); if (ret < 0) {
fprintf(stderr, "cannot set key\n"); return ret;
}
}
ret = ovpn_send_tcp_data(ovpn->socket);
ovpn_waitbg(); break; case CMD_NEW_PEER:
ret = ovpn_udp_socket(ovpn, AF_INET6); if (ret < 0) return ret;
ret = ovpn_new_peer(ovpn, false);
ovpn_waitbg(); break; case CMD_NEW_MULTI_PEER:
ret = ovpn_udp_socket(ovpn, AF_INET6); if (ret < 0) return ret;
fp = fopen(ovpn->peers_file, "r"); if (!fp) {
fprintf(stderr, "cannot open file: %s\n",
ovpn->peers_file); return -1;
}
ret = ovpn_parse_new_peer(&peer_ctx, peer_id, raddr,
rport, vpnip); if (ret < 0) {
fprintf(stderr, "error while parsing line\n"); return -1;
}
ret = ovpn_new_peer(&peer_ctx, false); if (ret < 0) {
fprintf(stderr, "cannot add peer to VPN: %s %s %s %s\n",
peer_id, raddr, rport, vpnip); return ret;
}
}
ovpn_waitbg(); break; case CMD_SET_PEER:
ret = ovpn_set_peer(ovpn); break; case CMD_DEL_PEER:
ret = ovpn_del_peer(ovpn); break; case CMD_GET_PEER: if (ovpn->peer_id == PEER_ID_UNDEF)
fprintf(stderr, "List of peers connected to: %s\n",
ovpn->ifname);
ret = ovpn_get_peer(ovpn); break; case CMD_NEW_KEY:
ret = ovpn_new_key(ovpn); break; case CMD_DEL_KEY:
ret = ovpn_del_key(ovpn); break; case CMD_GET_KEY:
ret = ovpn_get_key(ovpn); break; case CMD_SWAP_KEYS:
ret = ovpn_swap_keys(ovpn); break; case CMD_LISTEN_MCAST:
ret = ovpn_listen_mcast(); break; case CMD_INVALID:
ret = -EINVAL; break;
}
return ret;
}
staticint ovpn_parse_cmd_args(struct ovpn_ctx *ovpn, int argc, char *argv[])
{ int ret;
/* no args required for LISTEN_MCAST */ if (ovpn->cmd == CMD_LISTEN_MCAST) return 0;
/* all commands need an ifname */ if (argc < 3) return -EINVAL;
ret = ovpn_parse_key(argv[6], ovpn); if (ret) return -1;
} break; case CMD_NEW_PEER: if (argc < 7) return -EINVAL;
ovpn->lport = strtoul(argv[4], NULL, 10); if (errno == ERANGE || ovpn->lport > 65535) {
fprintf(stderr, "lport value out of range\n"); return -1;
}
constchar *vpnip = (argc > 7) ? argv[7] : NULL;
ret = ovpn_parse_new_peer(ovpn, argv[3], argv[5], argv[6],
vpnip); if (ret < 0) return -1; break; case CMD_NEW_MULTI_PEER: if (argc < 5) return -EINVAL;
ovpn->lport = strtoul(argv[3], NULL, 10); if (errno == ERANGE || ovpn->lport > 65535) {
fprintf(stderr, "lport value out of range\n"); return -1;
}
ovpn->peers_file = argv[4]; break; case CMD_SET_PEER: if (argc < 6) return -EINVAL;
ovpn->peer_id = strtoul(argv[3], NULL, 10); if (errno == ERANGE || ovpn->peer_id > PEER_ID_UNDEF) {
fprintf(stderr, "peer ID value out of range\n"); return -1;
}
ovpn->keepalive_interval = strtoul(argv[4], NULL, 10); if (errno == ERANGE) {
fprintf(stderr, "keepalive interval value out of range\n"); return -1;
}
ovpn->keepalive_timeout = strtoul(argv[5], NULL, 10); if (errno == ERANGE) {
fprintf(stderr, "keepalive interval value out of range\n"); return -1;
} break; case CMD_DEL_PEER: if (argc < 4) return -EINVAL;
ovpn->peer_id = strtoul(argv[3], NULL, 10); if (errno == ERANGE || ovpn->peer_id > PEER_ID_UNDEF) {
fprintf(stderr, "peer ID value out of range\n"); return -1;
} break; case CMD_GET_PEER:
ovpn->peer_id = PEER_ID_UNDEF; if (argc > 3) {
ovpn->peer_id = strtoul(argv[3], NULL, 10); if (errno == ERANGE || ovpn->peer_id > PEER_ID_UNDEF) {
fprintf(stderr, "peer ID value out of range\n"); return -1;
}
} break; case CMD_NEW_KEY: if (argc < 9) return -EINVAL;
ovpn->peer_id = strtoul(argv[3], NULL, 10); if (errno == ERANGE) {
fprintf(stderr, "peer ID value out of range\n"); return -1;
}
ret = ovpn_parse_key_slot(argv[4], ovpn); if (ret) return -1;
ovpn->key_id = strtoul(argv[5], NULL, 10); if (errno == ERANGE || ovpn->key_id > 2) {
fprintf(stderr, "key ID out of range\n"); return -1;
}
ret = ovpn_parse_cipher(argv[6], ovpn); if (ret < 0) return -1;
ret = ovpn_parse_key_direction(argv[7], ovpn); if (ret < 0) return -1;
ret = ovpn_parse_key(argv[8], ovpn); if (ret) return -1; break; case CMD_DEL_KEY: if (argc < 4) return -EINVAL;
ovpn->peer_id = strtoul(argv[3], NULL, 10); if (errno == ERANGE) {
fprintf(stderr, "peer ID value out of range\n"); return -1;
}
ret = ovpn_parse_key_slot(argv[4], ovpn); if (ret) return ret; break; case CMD_GET_KEY: if (argc < 5) return -EINVAL;
ovpn->peer_id = strtoul(argv[3], NULL, 10); if (errno == ERANGE) {
fprintf(stderr, "peer ID value out of range\n"); return -1;
}
ret = ovpn_parse_key_slot(argv[4], ovpn); if (ret) return ret; break; case CMD_SWAP_KEYS: if (argc < 4) return -EINVAL;
ovpn->peer_id = strtoul(argv[3], NULL, 10); if (errno == ERANGE) {
fprintf(stderr, "peer ID value out of range\n"); return -1;
} break; case CMD_LISTEN_MCAST: break; case CMD_INVALID: break;
}
return 0;
}
int main(int argc, char *argv[])
{ struct ovpn_ctx ovpn; int ret;
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.