| 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114211521162117211821192120212121222123212421252126212721282129213021312132213321342135213621372138213921402141214221432144214521462147214821492150215121522153215421552156215721582159216021612162216321642165216621672168216921702171217221732174217521762177217821792180218121822183218421852186218721882189219021912192219321942195219621972198219922002201220222032204220522062207220822092210221122122213221422152216221722182219222022212222222322242225222622272228222922302231223222332234223522362237223822392240224122422243224422452246224722482249225022512252225322542255225622572258225922602261226222632264226522662267226822692270227122722273227422752276227722782279228022812282228322842285228622872288228922902291229222932294229522962297229822992300230123022303230423052306230723082309231023112312231323142315231623172318231923202321232223232324232523262327232823292330233123322333233423352336233723382339234023412342234323442345234623472348234923502351235223532354235523562357235823592360236123622363236423652366236723682369237023712372237323742375237623772378237923802381238223832384238523862387 |
- // SPDX-License-Identifier: GPL-2.0
- /* OpenVPN data channel accelerator
- *
- * Copyright (C) 2020-2025 OpenVPN, Inc.
- *
- * Author: Antonio Quartulli <antonio@openvpn.net>
- */
- #include <stdio.h>
- #include <inttypes.h>
- #include <stdbool.h>
- #include <string.h>
- #include <errno.h>
- #include <unistd.h>
- #include <arpa/inet.h>
- #include <net/if.h>
- #include <netinet/in.h>
- #include <time.h>
- #include <linux/ovpn.h>
- #include <linux/types.h>
- #include <linux/netlink.h>
- #include <netlink/socket.h>
- #include <netlink/netlink.h>
- #include <netlink/genl/genl.h>
- #include <netlink/genl/family.h>
- #include <netlink/genl/ctrl.h>
- #include <mbedtls/base64.h>
- #include <mbedtls/error.h>
- #include <sys/socket.h>
- #include "kselftest.h"
- /* defines to make checkpatch happy */
- #define strscpy strncpy
- /* 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);
- }
- typedef int (*ovpn_nl_cb)(struct nl_msg *msg, void *arg);
- enum ovpn_key_direction {
- KEY_DIR_IN = 0,
- KEY_DIR_OUT,
- };
- #define KEY_LEN (256 / 8)
- #define NONCE_LEN 8
- #define PEER_ID_UNDEF 0x00FFFFFF
- #define MAX_PEERS 10
- struct nl_ctx {
- struct nl_sock *nl_sock;
- struct nl_msg *nl_msg;
- struct nl_cb *nl_cb;
- int ovpn_dco_id;
- };
- enum ovpn_cmd {
- CMD_INVALID,
- CMD_NEW_IFACE,
- CMD_DEL_IFACE,
- CMD_LISTEN,
- CMD_CONNECT,
- CMD_NEW_PEER,
- CMD_NEW_MULTI_PEER,
- CMD_SET_PEER,
- CMD_DEL_PEER,
- CMD_GET_PEER,
- CMD_NEW_KEY,
- CMD_DEL_KEY,
- CMD_GET_KEY,
- CMD_SWAP_KEYS,
- CMD_LISTEN_MCAST,
- };
- struct ovpn_ctx {
- enum ovpn_cmd cmd;
- __u8 key_enc[KEY_LEN];
- __u8 key_dec[KEY_LEN];
- __u8 nonce[NONCE_LEN];
- enum ovpn_cipher_alg cipher;
- sa_family_t sa_family;
- unsigned long peer_id;
- unsigned long lport;
- union {
- struct sockaddr_in in4;
- struct sockaddr_in6 in6;
- } remote;
- union {
- struct sockaddr_in in4;
- struct sockaddr_in6 in6;
- } peer_ip;
- bool peer_ip_set;
- unsigned int ifindex;
- char ifname[IFNAMSIZ];
- enum ovpn_mode mode;
- bool mode_set;
- int socket;
- int cli_sockets[MAX_PEERS];
- __u32 keepalive_interval;
- __u32 keepalive_timeout;
- enum ovpn_key_direction key_dir;
- enum ovpn_key_slot key_slot;
- int key_id;
- const char *peers_file;
- };
- static int 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;
- }
- static struct 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;
- ctx->nl_sock = nl_socket_alloc();
- if (!ctx->nl_sock) {
- fprintf(stderr, "cannot allocate netlink socket\n");
- goto err_free;
- }
- nl_socket_set_buffer_size(ctx->nl_sock, 8192, 8192);
- ret = genl_connect(ctx->nl_sock);
- if (ret) {
- fprintf(stderr, "cannot connect to generic netlink: %s\n",
- nl_geterror(ret));
- goto err_sock;
- }
- /* enable Extended ACK for detailed error reporting */
- err = 1;
- setsockopt(nl_socket_get_fd(ctx->nl_sock), SOL_NETLINK, NETLINK_EXT_ACK,
- &err, sizeof(err));
- ctx->ovpn_dco_id = genl_ctrl_resolve(ctx->nl_sock, OVPN_FAMILY_NAME);
- if (ctx->ovpn_dco_id < 0) {
- fprintf(stderr, "cannot find ovpn_dco netlink component: %d\n",
- ctx->ovpn_dco_id);
- goto err_free;
- }
- ctx->nl_msg = nlmsg_alloc();
- if (!ctx->nl_msg) {
- fprintf(stderr, "cannot allocate netlink message\n");
- goto err_sock;
- }
- ctx->nl_cb = nl_cb_alloc(NL_CB_DEFAULT);
- if (!ctx->nl_cb) {
- fprintf(stderr, "failed to allocate netlink callback\n");
- goto err_msg;
- }
- nl_socket_set_cb(ctx->nl_sock, ctx->nl_cb);
- genlmsg_put(ctx->nl_msg, 0, 0, ctx->ovpn_dco_id, 0, flags, cmd, 0);
- if (ovpn->ifindex > 0)
- NLA_PUT_U32(ctx->nl_msg, OVPN_A_IFINDEX, ovpn->ifindex);
- return ctx;
- nla_put_failure:
- err_msg:
- nlmsg_free(ctx->nl_msg);
- err_sock:
- nl_socket_free(ctx->nl_sock);
- err_free:
- free(ctx);
- return NULL;
- }
- static struct nl_ctx *nl_ctx_alloc(struct ovpn_ctx *ovpn, int cmd)
- {
- return nl_ctx_alloc_flags(ovpn, cmd, 0);
- }
- static void nl_ctx_free(struct nl_ctx *ctx)
- {
- if (!ctx)
- return;
- nl_socket_free(ctx->nl_sock);
- nlmsg_free(ctx->nl_msg);
- nl_cb_put(ctx->nl_cb);
- free(ctx);
- }
- static int ovpn_nl_cb_error(struct sockaddr_nl (*nla)__always_unused,
- struct nlmsgerr *err, void *arg)
- {
- struct nlmsghdr *nlh = (struct nlmsghdr *)err - 1;
- struct nlattr *tb_msg[NLMSGERR_ATTR_MAX + 1];
- int len = nlh->nlmsg_len;
- struct nlattr *attrs;
- int *ret = arg;
- int ack_len = sizeof(*nlh) + sizeof(int) + sizeof(*nlh);
- *ret = err->error;
- if (!(nlh->nlmsg_flags & NLM_F_ACK_TLVS))
- return NL_STOP;
- if (!(nlh->nlmsg_flags & NLM_F_CAPPED))
- ack_len += err->msg.nlmsg_len - sizeof(*nlh);
- if (len <= ack_len)
- return NL_STOP;
- attrs = (void *)((uint8_t *)nlh + ack_len);
- len -= ack_len;
- nla_parse(tb_msg, NLMSGERR_ATTR_MAX, attrs, len, NULL);
- if (tb_msg[NLMSGERR_ATTR_MSG]) {
- len = strnlen((char *)nla_data(tb_msg[NLMSGERR_ATTR_MSG]),
- nla_len(tb_msg[NLMSGERR_ATTR_MSG]));
- fprintf(stderr, "kernel error: %*s\n", len,
- (char *)nla_data(tb_msg[NLMSGERR_ATTR_MSG]));
- }
- if (tb_msg[NLMSGERR_ATTR_MISS_NEST]) {
- fprintf(stderr, "missing required nesting type %u\n",
- nla_get_u32(tb_msg[NLMSGERR_ATTR_MISS_NEST]));
- }
- if (tb_msg[NLMSGERR_ATTR_MISS_TYPE]) {
- fprintf(stderr, "missing required attribute type %u\n",
- nla_get_u32(tb_msg[NLMSGERR_ATTR_MISS_TYPE]));
- }
- return NL_STOP;
- }
- static int ovpn_nl_cb_finish(struct nl_msg (*msg)__always_unused,
- void *arg)
- {
- int *status = arg;
- *status = 0;
- return NL_SKIP;
- }
- static int ovpn_nl_cb_ack(struct nl_msg (*msg)__always_unused,
- void *arg)
- {
- int *status = arg;
- *status = 0;
- return NL_STOP;
- }
- static int ovpn_nl_msg_send(struct nl_ctx *ctx, ovpn_nl_cb cb)
- {
- int status = 1;
- nl_cb_err(ctx->nl_cb, NL_CB_CUSTOM, ovpn_nl_cb_error, &status);
- nl_cb_set(ctx->nl_cb, NL_CB_FINISH, NL_CB_CUSTOM, ovpn_nl_cb_finish,
- &status);
- nl_cb_set(ctx->nl_cb, NL_CB_ACK, NL_CB_CUSTOM, ovpn_nl_cb_ack, &status);
- if (cb)
- nl_cb_set(ctx->nl_cb, NL_CB_VALID, NL_CB_CUSTOM, cb, ctx);
- nl_send_auto_complete(ctx->nl_sock, ctx->nl_msg);
- while (status == 1)
- ovpn_nl_recvmsgs(ctx);
- if (status < 0)
- fprintf(stderr, "failed to send netlink message: %s (%d)\n",
- strerror(-status), status);
- return status;
- }
- static int ovpn_parse_key(const char *file, struct ovpn_ctx *ctx)
- {
- int idx_enc, idx_dec, ret = -1;
- unsigned char *ckey = NULL;
- __u8 *bkey = NULL;
- size_t olen = 0;
- long ckey_len;
- FILE *fp;
- fp = fopen(file, "r");
- if (!fp) {
- fprintf(stderr, "cannot open: %s\n", file);
- return -1;
- }
- /* get file size */
- fseek(fp, 0L, SEEK_END);
- ckey_len = ftell(fp);
- rewind(fp);
- /* if the file is longer, let's just read a portion */
- if (ckey_len > 256)
- ckey_len = 256;
- ckey = malloc(ckey_len);
- if (!ckey)
- goto err;
- ret = fread(ckey, 1, ckey_len, fp);
- if (ret != ckey_len) {
- fprintf(stderr,
- "couldn't read enough data from key file: %dbytes read\n",
- ret);
- goto err;
- }
- olen = 0;
- ret = mbedtls_base64_decode(NULL, 0, &olen, ckey, ckey_len);
- if (ret != MBEDTLS_ERR_BASE64_BUFFER_TOO_SMALL) {
- char buf[256];
- mbedtls_strerror(ret, buf, sizeof(buf));
- fprintf(stderr, "unexpected base64 error1: %s (%d)\n", buf,
- ret);
- goto err;
- }
- bkey = malloc(olen);
- if (!bkey) {
- fprintf(stderr, "cannot allocate binary key buffer\n");
- goto err;
- }
- ret = mbedtls_base64_decode(bkey, olen, &olen, ckey, ckey_len);
- if (ret) {
- char buf[256];
- mbedtls_strerror(ret, buf, sizeof(buf));
- fprintf(stderr, "unexpected base64 error2: %s (%d)\n", buf,
- ret);
- goto err;
- }
- if (olen < 2 * KEY_LEN + NONCE_LEN) {
- fprintf(stderr,
- "not enough data in key file, found %zdB but needs %dB\n",
- olen, 2 * KEY_LEN + NONCE_LEN);
- goto err;
- }
- switch (ctx->key_dir) {
- case KEY_DIR_IN:
- idx_enc = 0;
- idx_dec = 1;
- break;
- case KEY_DIR_OUT:
- idx_enc = 1;
- idx_dec = 0;
- break;
- default:
- goto err;
- }
- memcpy(ctx->key_enc, bkey + KEY_LEN * idx_enc, KEY_LEN);
- memcpy(ctx->key_dec, bkey + KEY_LEN * idx_dec, KEY_LEN);
- memcpy(ctx->nonce, bkey + 2 * KEY_LEN, NONCE_LEN);
- ret = 0;
- err:
- fclose(fp);
- free(bkey);
- free(ckey);
- return ret;
- }
- static int ovpn_parse_cipher(const char *cipher, struct ovpn_ctx *ctx)
- {
- if (strcmp(cipher, "aes") == 0)
- ctx->cipher = OVPN_CIPHER_ALG_AES_GCM;
- else if (strcmp(cipher, "chachapoly") == 0)
- ctx->cipher = OVPN_CIPHER_ALG_CHACHA20_POLY1305;
- else if (strcmp(cipher, "none") == 0)
- ctx->cipher = OVPN_CIPHER_ALG_NONE;
- else
- return -ENOTSUP;
- return 0;
- }
- static int ovpn_parse_key_direction(const char *dir, struct ovpn_ctx *ctx)
- {
- int in_dir;
- in_dir = strtoll(dir, NULL, 10);
- switch (in_dir) {
- case KEY_DIR_IN:
- case KEY_DIR_OUT:
- ctx->key_dir = in_dir;
- break;
- default:
- fprintf(stderr,
- "invalid key direction provided. Can be 0 or 1 only\n");
- return -1;
- }
- return 0;
- }
- static int ovpn_socket(struct ovpn_ctx *ctx, sa_family_t family, int proto)
- {
- struct sockaddr_storage local_sock = { 0 };
- struct sockaddr_in6 *in6;
- struct sockaddr_in *in;
- int ret, s, sock_type;
- size_t sock_len;
- if (proto == IPPROTO_UDP)
- sock_type = SOCK_DGRAM;
- else if (proto == IPPROTO_TCP)
- sock_type = SOCK_STREAM;
- else
- return -EINVAL;
- s = socket(family, sock_type, 0);
- if (s < 0) {
- perror("cannot create socket");
- return -1;
- }
- switch (family) {
- case AF_INET:
- in = (struct sockaddr_in *)&local_sock;
- in->sin_family = family;
- in->sin_port = htons(ctx->lport);
- in->sin_addr.s_addr = htonl(INADDR_ANY);
- sock_len = sizeof(*in);
- break;
- case AF_INET6:
- in6 = (struct sockaddr_in6 *)&local_sock;
- in6->sin6_family = family;
- in6->sin6_port = htons(ctx->lport);
- in6->sin6_addr = in6addr_any;
- sock_len = sizeof(*in6);
- break;
- default:
- return -1;
- }
- int opt = 1;
- ret = setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));
- if (ret < 0) {
- perror("setsockopt for SO_REUSEADDR");
- return ret;
- }
- ret = setsockopt(s, SOL_SOCKET, SO_REUSEPORT, &opt, sizeof(opt));
- if (ret < 0) {
- perror("setsockopt for SO_REUSEPORT");
- return ret;
- }
- if (family == AF_INET6) {
- opt = 0;
- if (setsockopt(s, IPPROTO_IPV6, IPV6_V6ONLY, &opt,
- sizeof(opt))) {
- perror("failed to set IPV6_V6ONLY");
- return -1;
- }
- }
- ret = bind(s, (struct sockaddr *)&local_sock, sock_len);
- if (ret < 0) {
- perror("cannot bind socket");
- goto err_socket;
- }
- ctx->socket = s;
- ctx->sa_family = family;
- return 0;
- err_socket:
- close(s);
- return -1;
- }
- static int ovpn_udp_socket(struct ovpn_ctx *ctx, sa_family_t family)
- {
- return ovpn_socket(ctx, family, IPPROTO_UDP);
- }
- static int ovpn_listen(struct ovpn_ctx *ctx, sa_family_t family)
- {
- int ret;
- ret = ovpn_socket(ctx, family, IPPROTO_TCP);
- if (ret < 0)
- return ret;
- ret = listen(ctx->socket, 10);
- if (ret < 0) {
- perror("listen");
- close(ctx->socket);
- return -1;
- }
- return 0;
- }
- static int ovpn_accept(struct ovpn_ctx *ctx)
- {
- socklen_t socklen;
- int ret;
- socklen = sizeof(ctx->remote);
- ret = accept(ctx->socket, (struct sockaddr *)&ctx->remote, &socklen);
- if (ret < 0) {
- perror("accept");
- goto err;
- }
- fprintf(stderr, "Connection received!\n");
- switch (socklen) {
- case sizeof(struct sockaddr_in):
- case sizeof(struct sockaddr_in6):
- break;
- default:
- fprintf(stderr, "error: expecting IPv4 or IPv6 connection\n");
- close(ret);
- ret = -EINVAL;
- goto err;
- }
- return ret;
- err:
- close(ctx->socket);
- return ret;
- }
- static int ovpn_connect(struct ovpn_ctx *ovpn)
- {
- socklen_t socklen;
- int s, ret;
- s = socket(ovpn->remote.in4.sin_family, SOCK_STREAM, 0);
- if (s < 0) {
- perror("cannot create socket");
- return -1;
- }
- switch (ovpn->remote.in4.sin_family) {
- case AF_INET:
- socklen = sizeof(struct sockaddr_in);
- break;
- case AF_INET6:
- socklen = sizeof(struct sockaddr_in6);
- break;
- default:
- return -EOPNOTSUPP;
- }
- ret = connect(s, (struct sockaddr *)&ovpn->remote, socklen);
- if (ret < 0) {
- perror("connect");
- goto err;
- }
- fprintf(stderr, "connected\n");
- ovpn->socket = s;
- return 0;
- err:
- close(s);
- return ret;
- }
- static int ovpn_new_peer(struct ovpn_ctx *ovpn, bool is_tcp)
- {
- struct nlattr *attr;
- struct nl_ctx *ctx;
- int ret = -1;
- ctx = nl_ctx_alloc(ovpn, OVPN_CMD_PEER_NEW);
- if (!ctx)
- return -ENOMEM;
- attr = nla_nest_start(ctx->nl_msg, OVPN_A_PEER);
- NLA_PUT_U32(ctx->nl_msg, OVPN_A_PEER_ID, ovpn->peer_id);
- NLA_PUT_U32(ctx->nl_msg, OVPN_A_PEER_SOCKET, ovpn->socket);
- if (!is_tcp) {
- switch (ovpn->remote.in4.sin_family) {
- case AF_INET:
- NLA_PUT_U32(ctx->nl_msg, OVPN_A_PEER_REMOTE_IPV4,
- ovpn->remote.in4.sin_addr.s_addr);
- NLA_PUT_U16(ctx->nl_msg, OVPN_A_PEER_REMOTE_PORT,
- ovpn->remote.in4.sin_port);
- break;
- case AF_INET6:
- NLA_PUT(ctx->nl_msg, OVPN_A_PEER_REMOTE_IPV6,
- sizeof(ovpn->remote.in6.sin6_addr),
- &ovpn->remote.in6.sin6_addr);
- NLA_PUT_U32(ctx->nl_msg,
- OVPN_A_PEER_REMOTE_IPV6_SCOPE_ID,
- ovpn->remote.in6.sin6_scope_id);
- NLA_PUT_U16(ctx->nl_msg, OVPN_A_PEER_REMOTE_PORT,
- ovpn->remote.in6.sin6_port);
- break;
- default:
- fprintf(stderr,
- "Invalid family for remote socket address\n");
- goto nla_put_failure;
- }
- }
- if (ovpn->peer_ip_set) {
- switch (ovpn->peer_ip.in4.sin_family) {
- case AF_INET:
- NLA_PUT_U32(ctx->nl_msg, OVPN_A_PEER_VPN_IPV4,
- ovpn->peer_ip.in4.sin_addr.s_addr);
- break;
- case AF_INET6:
- NLA_PUT(ctx->nl_msg, OVPN_A_PEER_VPN_IPV6,
- sizeof(struct in6_addr),
- &ovpn->peer_ip.in6.sin6_addr);
- break;
- default:
- fprintf(stderr, "Invalid family for peer address\n");
- goto nla_put_failure;
- }
- }
- nla_nest_end(ctx->nl_msg, attr);
- ret = ovpn_nl_msg_send(ctx, NULL);
- nla_put_failure:
- nl_ctx_free(ctx);
- return ret;
- }
- static int ovpn_set_peer(struct ovpn_ctx *ovpn)
- {
- struct nlattr *attr;
- struct nl_ctx *ctx;
- int ret = -1;
- ctx = nl_ctx_alloc(ovpn, OVPN_CMD_PEER_SET);
- if (!ctx)
- return -ENOMEM;
- attr = nla_nest_start(ctx->nl_msg, OVPN_A_PEER);
- NLA_PUT_U32(ctx->nl_msg, OVPN_A_PEER_ID, ovpn->peer_id);
- NLA_PUT_U32(ctx->nl_msg, OVPN_A_PEER_KEEPALIVE_INTERVAL,
- ovpn->keepalive_interval);
- NLA_PUT_U32(ctx->nl_msg, OVPN_A_PEER_KEEPALIVE_TIMEOUT,
- ovpn->keepalive_timeout);
- nla_nest_end(ctx->nl_msg, attr);
- ret = ovpn_nl_msg_send(ctx, NULL);
- nla_put_failure:
- nl_ctx_free(ctx);
- return ret;
- }
- static int ovpn_del_peer(struct ovpn_ctx *ovpn)
- {
- struct nlattr *attr;
- struct nl_ctx *ctx;
- int ret = -1;
- ctx = nl_ctx_alloc(ovpn, OVPN_CMD_PEER_DEL);
- if (!ctx)
- return -ENOMEM;
- attr = nla_nest_start(ctx->nl_msg, OVPN_A_PEER);
- NLA_PUT_U32(ctx->nl_msg, OVPN_A_PEER_ID, ovpn->peer_id);
- nla_nest_end(ctx->nl_msg, attr);
- ret = ovpn_nl_msg_send(ctx, NULL);
- nla_put_failure:
- nl_ctx_free(ctx);
- return ret;
- }
- static int ovpn_handle_peer(struct nl_msg *msg, void (*arg)__always_unused)
- {
- struct nlattr *pattrs[OVPN_A_PEER_MAX + 1];
- struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));
- struct nlattr *attrs[OVPN_A_MAX + 1];
- __u16 rport = 0, lport = 0;
- nla_parse(attrs, OVPN_A_MAX, genlmsg_attrdata(gnlh, 0),
- genlmsg_attrlen(gnlh, 0), NULL);
- if (!attrs[OVPN_A_PEER]) {
- fprintf(stderr, "no packet content in netlink message\n");
- return NL_SKIP;
- }
- nla_parse(pattrs, OVPN_A_PEER_MAX, nla_data(attrs[OVPN_A_PEER]),
- nla_len(attrs[OVPN_A_PEER]), NULL);
- if (pattrs[OVPN_A_PEER_ID])
- fprintf(stderr, "* Peer %u\n",
- nla_get_u32(pattrs[OVPN_A_PEER_ID]));
- if (pattrs[OVPN_A_PEER_SOCKET_NETNSID])
- fprintf(stderr, "\tsocket NetNS ID: %d\n",
- nla_get_s32(pattrs[OVPN_A_PEER_SOCKET_NETNSID]));
- if (pattrs[OVPN_A_PEER_VPN_IPV4]) {
- char buf[INET_ADDRSTRLEN];
- inet_ntop(AF_INET, nla_data(pattrs[OVPN_A_PEER_VPN_IPV4]),
- buf, sizeof(buf));
- fprintf(stderr, "\tVPN IPv4: %s\n", buf);
- }
- if (pattrs[OVPN_A_PEER_VPN_IPV6]) {
- char buf[INET6_ADDRSTRLEN];
- inet_ntop(AF_INET6, nla_data(pattrs[OVPN_A_PEER_VPN_IPV6]),
- buf, sizeof(buf));
- fprintf(stderr, "\tVPN IPv6: %s\n", buf);
- }
- if (pattrs[OVPN_A_PEER_LOCAL_PORT])
- lport = ntohs(nla_get_u16(pattrs[OVPN_A_PEER_LOCAL_PORT]));
- if (pattrs[OVPN_A_PEER_REMOTE_PORT])
- rport = ntohs(nla_get_u16(pattrs[OVPN_A_PEER_REMOTE_PORT]));
- if (pattrs[OVPN_A_PEER_REMOTE_IPV6]) {
- void *ip = pattrs[OVPN_A_PEER_REMOTE_IPV6];
- char buf[INET6_ADDRSTRLEN];
- int scope_id = -1;
- if (pattrs[OVPN_A_PEER_REMOTE_IPV6_SCOPE_ID]) {
- void *p = pattrs[OVPN_A_PEER_REMOTE_IPV6_SCOPE_ID];
- scope_id = nla_get_u32(p);
- }
- inet_ntop(AF_INET6, nla_data(ip), buf, sizeof(buf));
- fprintf(stderr, "\tRemote: %s:%hu (scope-id: %u)\n", buf, rport,
- scope_id);
- if (pattrs[OVPN_A_PEER_LOCAL_IPV6]) {
- void *ip = pattrs[OVPN_A_PEER_LOCAL_IPV6];
- inet_ntop(AF_INET6, nla_data(ip), buf, sizeof(buf));
- fprintf(stderr, "\tLocal: %s:%hu\n", buf, lport);
- }
- }
- if (pattrs[OVPN_A_PEER_REMOTE_IPV4]) {
- void *ip = pattrs[OVPN_A_PEER_REMOTE_IPV4];
- char buf[INET_ADDRSTRLEN];
- inet_ntop(AF_INET, nla_data(ip), buf, sizeof(buf));
- fprintf(stderr, "\tRemote: %s:%hu\n", buf, rport);
- if (pattrs[OVPN_A_PEER_LOCAL_IPV4]) {
- void *p = pattrs[OVPN_A_PEER_LOCAL_IPV4];
- inet_ntop(AF_INET, nla_data(p), buf, sizeof(buf));
- fprintf(stderr, "\tLocal: %s:%hu\n", buf, lport);
- }
- }
- if (pattrs[OVPN_A_PEER_KEEPALIVE_INTERVAL]) {
- void *p = pattrs[OVPN_A_PEER_KEEPALIVE_INTERVAL];
- fprintf(stderr, "\tKeepalive interval: %u sec\n",
- nla_get_u32(p));
- }
- if (pattrs[OVPN_A_PEER_KEEPALIVE_TIMEOUT])
- fprintf(stderr, "\tKeepalive timeout: %u sec\n",
- nla_get_u32(pattrs[OVPN_A_PEER_KEEPALIVE_TIMEOUT]));
- if (pattrs[OVPN_A_PEER_VPN_RX_BYTES])
- fprintf(stderr, "\tVPN RX bytes: %" PRIu64 "\n",
- ovpn_nla_get_uint(pattrs[OVPN_A_PEER_VPN_RX_BYTES]));
- if (pattrs[OVPN_A_PEER_VPN_TX_BYTES])
- fprintf(stderr, "\tVPN TX bytes: %" PRIu64 "\n",
- ovpn_nla_get_uint(pattrs[OVPN_A_PEER_VPN_TX_BYTES]));
- if (pattrs[OVPN_A_PEER_VPN_RX_PACKETS])
- fprintf(stderr, "\tVPN RX packets: %" PRIu64 "\n",
- ovpn_nla_get_uint(pattrs[OVPN_A_PEER_VPN_RX_PACKETS]));
- if (pattrs[OVPN_A_PEER_VPN_TX_PACKETS])
- fprintf(stderr, "\tVPN TX packets: %" PRIu64 "\n",
- ovpn_nla_get_uint(pattrs[OVPN_A_PEER_VPN_TX_PACKETS]));
- if (pattrs[OVPN_A_PEER_LINK_RX_BYTES])
- fprintf(stderr, "\tLINK RX bytes: %" PRIu64 "\n",
- ovpn_nla_get_uint(pattrs[OVPN_A_PEER_LINK_RX_BYTES]));
- if (pattrs[OVPN_A_PEER_LINK_TX_BYTES])
- fprintf(stderr, "\tLINK TX bytes: %" PRIu64 "\n",
- ovpn_nla_get_uint(pattrs[OVPN_A_PEER_LINK_TX_BYTES]));
- if (pattrs[OVPN_A_PEER_LINK_RX_PACKETS])
- fprintf(stderr, "\tLINK RX packets: %" PRIu64 "\n",
- ovpn_nla_get_uint(pattrs[OVPN_A_PEER_LINK_RX_PACKETS]));
- if (pattrs[OVPN_A_PEER_LINK_TX_PACKETS])
- fprintf(stderr, "\tLINK TX packets: %" PRIu64 "\n",
- ovpn_nla_get_uint(pattrs[OVPN_A_PEER_LINK_TX_PACKETS]));
- return NL_SKIP;
- }
- static int ovpn_get_peer(struct ovpn_ctx *ovpn)
- {
- int flags = 0, ret = -1;
- struct nlattr *attr;
- struct nl_ctx *ctx;
- if (ovpn->peer_id == PEER_ID_UNDEF)
- flags = NLM_F_DUMP;
- ctx = nl_ctx_alloc_flags(ovpn, OVPN_CMD_PEER_GET, flags);
- if (!ctx)
- return -ENOMEM;
- if (ovpn->peer_id != PEER_ID_UNDEF) {
- attr = nla_nest_start(ctx->nl_msg, OVPN_A_PEER);
- NLA_PUT_U32(ctx->nl_msg, OVPN_A_PEER_ID, ovpn->peer_id);
- nla_nest_end(ctx->nl_msg, attr);
- }
- ret = ovpn_nl_msg_send(ctx, ovpn_handle_peer);
- nla_put_failure:
- nl_ctx_free(ctx);
- return ret;
- }
- static int ovpn_new_key(struct ovpn_ctx *ovpn)
- {
- struct nlattr *keyconf, *key_dir;
- struct nl_ctx *ctx;
- int ret = -1;
- ctx = nl_ctx_alloc(ovpn, OVPN_CMD_KEY_NEW);
- if (!ctx)
- return -ENOMEM;
- keyconf = nla_nest_start(ctx->nl_msg, OVPN_A_KEYCONF);
- NLA_PUT_U32(ctx->nl_msg, OVPN_A_KEYCONF_PEER_ID, ovpn->peer_id);
- NLA_PUT_U32(ctx->nl_msg, OVPN_A_KEYCONF_SLOT, ovpn->key_slot);
- NLA_PUT_U32(ctx->nl_msg, OVPN_A_KEYCONF_KEY_ID, ovpn->key_id);
- NLA_PUT_U32(ctx->nl_msg, OVPN_A_KEYCONF_CIPHER_ALG, ovpn->cipher);
- key_dir = nla_nest_start(ctx->nl_msg, OVPN_A_KEYCONF_ENCRYPT_DIR);
- NLA_PUT(ctx->nl_msg, OVPN_A_KEYDIR_CIPHER_KEY, KEY_LEN, ovpn->key_enc);
- NLA_PUT(ctx->nl_msg, OVPN_A_KEYDIR_NONCE_TAIL, NONCE_LEN, ovpn->nonce);
- nla_nest_end(ctx->nl_msg, key_dir);
- key_dir = nla_nest_start(ctx->nl_msg, OVPN_A_KEYCONF_DECRYPT_DIR);
- NLA_PUT(ctx->nl_msg, OVPN_A_KEYDIR_CIPHER_KEY, KEY_LEN, ovpn->key_dec);
- NLA_PUT(ctx->nl_msg, OVPN_A_KEYDIR_NONCE_TAIL, NONCE_LEN, ovpn->nonce);
- nla_nest_end(ctx->nl_msg, key_dir);
- nla_nest_end(ctx->nl_msg, keyconf);
- ret = ovpn_nl_msg_send(ctx, NULL);
- nla_put_failure:
- nl_ctx_free(ctx);
- return ret;
- }
- static int ovpn_del_key(struct ovpn_ctx *ovpn)
- {
- struct nlattr *keyconf;
- struct nl_ctx *ctx;
- int ret = -1;
- ctx = nl_ctx_alloc(ovpn, OVPN_CMD_KEY_DEL);
- if (!ctx)
- return -ENOMEM;
- keyconf = nla_nest_start(ctx->nl_msg, OVPN_A_KEYCONF);
- NLA_PUT_U32(ctx->nl_msg, OVPN_A_KEYCONF_PEER_ID, ovpn->peer_id);
- NLA_PUT_U32(ctx->nl_msg, OVPN_A_KEYCONF_SLOT, ovpn->key_slot);
- nla_nest_end(ctx->nl_msg, keyconf);
- ret = ovpn_nl_msg_send(ctx, NULL);
- nla_put_failure:
- nl_ctx_free(ctx);
- return ret;
- }
- static int ovpn_handle_key(struct nl_msg *msg, void (*arg)__always_unused)
- {
- struct nlattr *kattrs[OVPN_A_KEYCONF_MAX + 1];
- struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));
- struct nlattr *attrs[OVPN_A_MAX + 1];
- nla_parse(attrs, OVPN_A_MAX, genlmsg_attrdata(gnlh, 0),
- genlmsg_attrlen(gnlh, 0), NULL);
- if (!attrs[OVPN_A_KEYCONF]) {
- fprintf(stderr, "no packet content in netlink message\n");
- return NL_SKIP;
- }
- nla_parse(kattrs, OVPN_A_KEYCONF_MAX, nla_data(attrs[OVPN_A_KEYCONF]),
- nla_len(attrs[OVPN_A_KEYCONF]), NULL);
- if (kattrs[OVPN_A_KEYCONF_PEER_ID])
- fprintf(stderr, "* Peer %u\n",
- nla_get_u32(kattrs[OVPN_A_KEYCONF_PEER_ID]));
- if (kattrs[OVPN_A_KEYCONF_SLOT]) {
- fprintf(stderr, "\t- Slot: ");
- switch (nla_get_u32(kattrs[OVPN_A_KEYCONF_SLOT])) {
- case OVPN_KEY_SLOT_PRIMARY:
- fprintf(stderr, "primary\n");
- break;
- case OVPN_KEY_SLOT_SECONDARY:
- fprintf(stderr, "secondary\n");
- break;
- default:
- fprintf(stderr, "invalid (%u)\n",
- nla_get_u32(kattrs[OVPN_A_KEYCONF_SLOT]));
- break;
- }
- }
- if (kattrs[OVPN_A_KEYCONF_KEY_ID])
- fprintf(stderr, "\t- Key ID: %u\n",
- nla_get_u32(kattrs[OVPN_A_KEYCONF_KEY_ID]));
- if (kattrs[OVPN_A_KEYCONF_CIPHER_ALG]) {
- fprintf(stderr, "\t- Cipher: ");
- switch (nla_get_u32(kattrs[OVPN_A_KEYCONF_CIPHER_ALG])) {
- case OVPN_CIPHER_ALG_NONE:
- fprintf(stderr, "none\n");
- break;
- case OVPN_CIPHER_ALG_AES_GCM:
- fprintf(stderr, "aes-gcm\n");
- break;
- case OVPN_CIPHER_ALG_CHACHA20_POLY1305:
- fprintf(stderr, "chacha20poly1305\n");
- break;
- default:
- fprintf(stderr, "invalid (%u)\n",
- nla_get_u32(kattrs[OVPN_A_KEYCONF_CIPHER_ALG]));
- break;
- }
- }
- return NL_SKIP;
- }
- static int ovpn_get_key(struct ovpn_ctx *ovpn)
- {
- struct nlattr *keyconf;
- struct nl_ctx *ctx;
- int ret = -1;
- ctx = nl_ctx_alloc(ovpn, OVPN_CMD_KEY_GET);
- if (!ctx)
- return -ENOMEM;
- keyconf = nla_nest_start(ctx->nl_msg, OVPN_A_KEYCONF);
- NLA_PUT_U32(ctx->nl_msg, OVPN_A_KEYCONF_PEER_ID, ovpn->peer_id);
- NLA_PUT_U32(ctx->nl_msg, OVPN_A_KEYCONF_SLOT, ovpn->key_slot);
- nla_nest_end(ctx->nl_msg, keyconf);
- ret = ovpn_nl_msg_send(ctx, ovpn_handle_key);
- nla_put_failure:
- nl_ctx_free(ctx);
- return ret;
- }
- static int ovpn_swap_keys(struct ovpn_ctx *ovpn)
- {
- struct nl_ctx *ctx;
- struct nlattr *kc;
- int ret = -1;
- ctx = nl_ctx_alloc(ovpn, OVPN_CMD_KEY_SWAP);
- if (!ctx)
- return -ENOMEM;
- kc = nla_nest_start(ctx->nl_msg, OVPN_A_KEYCONF);
- NLA_PUT_U32(ctx->nl_msg, OVPN_A_KEYCONF_PEER_ID, ovpn->peer_id);
- nla_nest_end(ctx->nl_msg, kc);
- 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 */
- static int ovpn_addattr(struct nlmsghdr *n, int maxlen, int type,
- const void *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;
- }
- rta = nlmsg_tail(n);
- rta->rta_type = type;
- rta->rta_len = len;
- if (!data)
- memset(RTA_DATA(rta), 0, alen);
- else
- memcpy(RTA_DATA(rta), data, alen);
- n->nlmsg_len = NLMSG_ALIGN(n->nlmsg_len) + RTA_ALIGN(len);
- return 0;
- }
- static struct rtattr *ovpn_nest_start(struct nlmsghdr *msg, size_t max_size,
- int attr)
- {
- struct rtattr *nest = nlmsg_tail(msg);
- if (ovpn_addattr(msg, max_size, attr, NULL, 0) < 0)
- return NULL;
- return nest;
- }
- static void ovpn_nest_end(struct nlmsghdr *msg, struct rtattr *nest)
- {
- nest->rta_len = (uint8_t *)nlmsg_tail(msg) - (uint8_t *)nest;
- }
- #define RT_SNDBUF_SIZE (1024 * 2)
- #define RT_RCVBUF_SIZE (1024 * 4)
- /* Open RTNL socket */
- static int ovpn_rt_socket(void)
- {
- int sndbuf = RT_SNDBUF_SIZE, rcvbuf = RT_RCVBUF_SIZE, fd;
- fd = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE);
- if (fd < 0) {
- fprintf(stderr, "%s: cannot open netlink socket\n", __func__);
- return fd;
- }
- if (setsockopt(fd, SOL_SOCKET, SO_SNDBUF, &sndbuf,
- sizeof(sndbuf)) < 0) {
- fprintf(stderr, "%s: SO_SNDBUF\n", __func__);
- close(fd);
- return -1;
- }
- if (setsockopt(fd, SOL_SOCKET, SO_RCVBUF, &rcvbuf,
- sizeof(rcvbuf)) < 0) {
- fprintf(stderr, "%s: SO_RCVBUF\n", __func__);
- close(fd);
- return -1;
- }
- return fd;
- }
- /* Bind socket to Netlink subsystem */
- static int ovpn_rt_bind(int fd, uint32_t groups)
- {
- struct sockaddr_nl local = { 0 };
- socklen_t addr_len;
- local.nl_family = AF_NETLINK;
- local.nl_groups = groups;
- if (bind(fd, (struct sockaddr *)&local, sizeof(local)) < 0) {
- fprintf(stderr, "%s: cannot bind netlink socket: %d\n",
- __func__, errno);
- return -errno;
- }
- addr_len = sizeof(local);
- if (getsockname(fd, (struct sockaddr *)&local, &addr_len) < 0) {
- fprintf(stderr, "%s: cannot getsockname: %d\n", __func__,
- errno);
- return -errno;
- }
- if (addr_len != sizeof(local)) {
- fprintf(stderr, "%s: wrong address length %d\n", __func__,
- addr_len);
- return -EINVAL;
- }
- if (local.nl_family != AF_NETLINK) {
- fprintf(stderr, "%s: wrong address family %d\n", __func__,
- local.nl_family);
- return -EINVAL;
- }
- return 0;
- }
- typedef int (*ovpn_parse_reply_cb)(struct nlmsghdr *msg, void *arg);
- /* Send Netlink message and run callback on reply (if specified) */
- static int ovpn_rt_send(struct nlmsghdr *payload, pid_t peer,
- unsigned int groups, ovpn_parse_reply_cb cb,
- void *arg_cb)
- {
- int len, rem_len, fd, ret, rcv_len;
- struct sockaddr_nl nladdr = { 0 };
- struct nlmsgerr *err;
- struct nlmsghdr *h;
- char buf[1024 * 16];
- struct iovec iov = {
- .iov_base = payload,
- .iov_len = payload->nlmsg_len,
- };
- struct msghdr nlmsg = {
- .msg_name = &nladdr,
- .msg_namelen = sizeof(nladdr),
- .msg_iov = &iov,
- .msg_iovlen = 1,
- };
- nladdr.nl_family = AF_NETLINK;
- nladdr.nl_pid = peer;
- nladdr.nl_groups = groups;
- payload->nlmsg_seq = time(NULL);
- /* 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;
- }
- if (nlmsg.msg_namelen != sizeof(nladdr)) {
- fprintf(stderr,
- "%s: sender address length: %u (expected %zu)\n",
- __func__, nlmsg.msg_namelen, sizeof(nladdr));
- ret = -EIO;
- goto out;
- }
- h = (struct nlmsghdr *)buf;
- while (rcv_len >= (int)sizeof(*h)) {
- len = h->nlmsg_len;
- rem_len = len - sizeof(*h);
- if (rem_len < 0 || len > rcv_len) {
- if (nlmsg.msg_flags & MSG_TRUNC) {
- fprintf(stderr, "%s: truncated message\n",
- __func__);
- ret = -EIO;
- goto out;
- }
- fprintf(stderr, "%s: malformed message: len=%d\n",
- __func__, len);
- ret = -EIO;
- goto out;
- }
- if (h->nlmsg_type == NLMSG_DONE) {
- ret = 0;
- goto out;
- }
- if (h->nlmsg_type == NLMSG_ERROR) {
- err = (struct nlmsgerr *)NLMSG_DATA(h);
- if (rem_len < (int)sizeof(struct nlmsgerr)) {
- fprintf(stderr, "%s: ERROR truncated\n",
- __func__);
- ret = -EIO;
- goto out;
- }
- if (err->error) {
- fprintf(stderr, "%s: (%d) %s\n",
- __func__, err->error,
- strerror(-err->error));
- ret = err->error;
- goto out;
- }
- ret = 0;
- if (cb) {
- int r = cb(h, arg_cb);
- if (r <= 0)
- ret = r;
- }
- goto out;
- }
- if (cb) {
- int r = cb(h, arg_cb);
- if (r <= 0) {
- ret = r;
- goto out;
- }
- } else {
- fprintf(stderr, "%s: RTNL: unexpected reply\n",
- __func__);
- }
- rcv_len -= NLMSG_ALIGN(len);
- h = (struct nlmsghdr *)((uint8_t *)h +
- NLMSG_ALIGN(len));
- }
- if (nlmsg.msg_flags & MSG_TRUNC) {
- fprintf(stderr, "%s: message truncated\n", __func__);
- continue;
- }
- if (rcv_len) {
- fprintf(stderr, "%s: rtnl: %d not parsed bytes\n",
- __func__, rcv_len);
- ret = -1;
- goto out;
- }
- }
- out:
- close(fd);
- return ret;
- }
- struct ovpn_link_req {
- struct nlmsghdr n;
- struct ifinfomsg i;
- char buf[256];
- };
- static int ovpn_new_iface(struct ovpn_ctx *ovpn)
- {
- struct rtattr *linkinfo, *data;
- struct ovpn_link_req req = { 0 };
- int ret = -1;
- fprintf(stdout, "Creating interface %s with mode %u\n", ovpn->ifname,
- ovpn->mode);
- req.n.nlmsg_len = NLMSG_LENGTH(sizeof(req.i));
- req.n.nlmsg_flags = NLM_F_REQUEST | NLM_F_CREATE | NLM_F_EXCL;
- req.n.nlmsg_type = RTM_NEWLINK;
- if (ovpn_addattr(&req.n, sizeof(req), IFLA_IFNAME, ovpn->ifname,
- strlen(ovpn->ifname) + 1) < 0)
- goto err;
- linkinfo = ovpn_nest_start(&req.n, sizeof(req), IFLA_LINKINFO);
- if (!linkinfo)
- goto err;
- if (ovpn_addattr(&req.n, sizeof(req), IFLA_INFO_KIND, OVPN_FAMILY_NAME,
- strlen(OVPN_FAMILY_NAME) + 1) < 0)
- goto err;
- if (ovpn->mode_set) {
- data = ovpn_nest_start(&req.n, sizeof(req), IFLA_INFO_DATA);
- if (!data)
- goto err;
- if (ovpn_addattr(&req.n, sizeof(req), IFLA_OVPN_MODE,
- &ovpn->mode, sizeof(uint8_t)) < 0)
- goto err;
- ovpn_nest_end(&req.n, data);
- }
- ovpn_nest_end(&req.n, linkinfo);
- req.i.ifi_family = AF_PACKET;
- ret = ovpn_rt_send(&req.n, 0, 0, NULL, NULL);
- err:
- return ret;
- }
- static int ovpn_del_iface(struct ovpn_ctx *ovpn)
- {
- struct ovpn_link_req req = { 0 };
- fprintf(stdout, "Deleting interface %s ifindex %u\n", ovpn->ifname,
- ovpn->ifindex);
- req.n.nlmsg_len = NLMSG_LENGTH(sizeof(req.i));
- req.n.nlmsg_flags = NLM_F_REQUEST;
- req.n.nlmsg_type = RTM_DELLINK;
- req.i.ifi_family = AF_PACKET;
- req.i.ifi_index = ovpn->ifindex;
- return ovpn_rt_send(&req.n, 0, 0, NULL, NULL);
- }
- static int nl_seq_check(struct nl_msg (*msg)__always_unused,
- void (*arg)__always_unused)
- {
- return NL_OK;
- }
- struct mcast_handler_args {
- const char *group;
- int id;
- };
- static int mcast_family_handler(struct nl_msg *msg, void *arg)
- {
- struct mcast_handler_args *grp = arg;
- struct nlattr *tb[CTRL_ATTR_MAX + 1];
- struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));
- struct nlattr *mcgrp;
- int rem_mcgrp;
- nla_parse(tb, CTRL_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
- genlmsg_attrlen(gnlh, 0), NULL);
- if (!tb[CTRL_ATTR_MCAST_GROUPS])
- return NL_SKIP;
- nla_for_each_nested(mcgrp, tb[CTRL_ATTR_MCAST_GROUPS], rem_mcgrp) {
- struct nlattr *tb_mcgrp[CTRL_ATTR_MCAST_GRP_MAX + 1];
- nla_parse(tb_mcgrp, CTRL_ATTR_MCAST_GRP_MAX,
- nla_data(mcgrp), nla_len(mcgrp), NULL);
- if (!tb_mcgrp[CTRL_ATTR_MCAST_GRP_NAME] ||
- !tb_mcgrp[CTRL_ATTR_MCAST_GRP_ID])
- continue;
- if (strncmp(nla_data(tb_mcgrp[CTRL_ATTR_MCAST_GRP_NAME]),
- grp->group, nla_len(tb_mcgrp[CTRL_ATTR_MCAST_GRP_NAME])))
- continue;
- grp->id = nla_get_u32(tb_mcgrp[CTRL_ATTR_MCAST_GRP_ID]);
- break;
- }
- return NL_SKIP;
- }
- static int mcast_error_handler(struct sockaddr_nl (*nla)__always_unused,
- struct nlmsgerr *err, void *arg)
- {
- int *ret = arg;
- *ret = err->error;
- return NL_STOP;
- }
- static int mcast_ack_handler(struct nl_msg (*msg)__always_unused, void *arg)
- {
- int *ret = arg;
- *ret = 0;
- return NL_STOP;
- }
- static int ovpn_handle_msg(struct nl_msg *msg, void *arg)
- {
- struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));
- struct nlattr *attrs[OVPN_A_MAX + 1];
- struct nlmsghdr *nlh = nlmsg_hdr(msg);
- char ifname[IF_NAMESIZE];
- int *ret = arg;
- __u32 ifindex;
- fprintf(stderr, "received message from ovpn-dco\n");
- *ret = -1;
- if (!genlmsg_valid_hdr(nlh, 0)) {
- fprintf(stderr, "invalid header\n");
- return NL_STOP;
- }
- if (nla_parse(attrs, OVPN_A_MAX, genlmsg_attrdata(gnlh, 0),
- genlmsg_attrlen(gnlh, 0), NULL)) {
- fprintf(stderr, "received bogus data from ovpn-dco\n");
- return NL_STOP;
- }
- if (!attrs[OVPN_A_IFINDEX]) {
- fprintf(stderr, "no ifindex in this message\n");
- return NL_STOP;
- }
- ifindex = nla_get_u32(attrs[OVPN_A_IFINDEX]);
- if (!if_indextoname(ifindex, ifname)) {
- fprintf(stderr, "cannot resolve ifname for ifindex: %u\n",
- ifindex);
- return NL_STOP;
- }
- switch (gnlh->cmd) {
- case OVPN_CMD_PEER_DEL_NTF:
- fprintf(stdout, "received CMD_PEER_DEL_NTF\n");
- break;
- case OVPN_CMD_KEY_SWAP_NTF:
- fprintf(stdout, "received CMD_KEY_SWAP_NTF\n");
- break;
- default:
- fprintf(stderr, "received unknown command: %d\n", gnlh->cmd);
- return NL_STOP;
- }
- *ret = 0;
- return NL_OK;
- }
- static int ovpn_get_mcast_id(struct nl_sock *sock, const char *family,
- const char *group)
- {
- struct nl_msg *msg;
- struct nl_cb *cb;
- int ret, ctrlid;
- struct mcast_handler_args grp = {
- .group = group,
- .id = -ENOENT,
- };
- msg = nlmsg_alloc();
- if (!msg)
- return -ENOMEM;
- cb = nl_cb_alloc(NL_CB_DEFAULT);
- if (!cb) {
- ret = -ENOMEM;
- goto out_fail_cb;
- }
- ctrlid = genl_ctrl_resolve(sock, "nlctrl");
- genlmsg_put(msg, 0, 0, ctrlid, 0, 0, CTRL_CMD_GETFAMILY, 0);
- ret = -ENOBUFS;
- NLA_PUT_STRING(msg, CTRL_ATTR_FAMILY_NAME, family);
- ret = nl_send_auto_complete(sock, msg);
- if (ret < 0)
- goto nla_put_failure;
- ret = 1;
- nl_cb_err(cb, NL_CB_CUSTOM, mcast_error_handler, &ret);
- nl_cb_set(cb, NL_CB_ACK, NL_CB_CUSTOM, mcast_ack_handler, &ret);
- nl_cb_set(cb, NL_CB_VALID, NL_CB_CUSTOM, mcast_family_handler, &grp);
- while (ret > 0)
- nl_recvmsgs(sock, cb);
- if (ret == 0)
- ret = grp.id;
- nla_put_failure:
- nl_cb_put(cb);
- out_fail_cb:
- nlmsg_free(msg);
- return ret;
- }
- static int ovpn_listen_mcast(void)
- {
- struct nl_sock *sock;
- struct nl_cb *cb;
- int mcid, ret;
- sock = nl_socket_alloc();
- if (!sock) {
- fprintf(stderr, "cannot allocate netlink socket\n");
- ret = -ENOMEM;
- goto err_free;
- }
- nl_socket_set_buffer_size(sock, 8192, 8192);
- ret = genl_connect(sock);
- if (ret < 0) {
- fprintf(stderr, "cannot connect to generic netlink: %s\n",
- nl_geterror(ret));
- goto err_free;
- }
- mcid = ovpn_get_mcast_id(sock, OVPN_FAMILY_NAME, OVPN_MCGRP_PEERS);
- if (mcid < 0) {
- fprintf(stderr, "cannot get mcast group: %s\n",
- nl_geterror(mcid));
- goto err_free;
- }
- ret = nl_socket_add_membership(sock, mcid);
- if (ret) {
- fprintf(stderr, "failed to join mcast group: %d\n", ret);
- goto err_free;
- }
- ret = 1;
- cb = nl_cb_alloc(NL_CB_DEFAULT);
- nl_cb_set(cb, NL_CB_SEQ_CHECK, NL_CB_CUSTOM, nl_seq_check, NULL);
- nl_cb_set(cb, NL_CB_VALID, NL_CB_CUSTOM, ovpn_handle_msg, &ret);
- nl_cb_err(cb, NL_CB_CUSTOM, ovpn_nl_cb_error, &ret);
- while (ret == 1) {
- int err = nl_recvmsgs(sock, cb);
- if (err < 0) {
- fprintf(stderr,
- "cannot receive netlink message: (%d) %s\n",
- err, nl_geterror(-err));
- ret = -1;
- break;
- }
- }
- nl_cb_put(cb);
- err_free:
- nl_socket_free(sock);
- return ret;
- }
- static void usage(const char *cmd)
- {
- fprintf(stderr,
- "Usage %s <command> <iface> [arguments..]\n",
- cmd);
- fprintf(stderr, "where <command> can be one of the following\n\n");
- fprintf(stderr, "* new_iface <iface> [mode]: create new ovpn interface\n");
- fprintf(stderr, "\tiface: ovpn interface name\n");
- fprintf(stderr, "\tmode:\n");
- fprintf(stderr, "\t\t- P2P for peer-to-peer mode (i.e. client)\n");
- fprintf(stderr, "\t\t- MP for multi-peer mode (i.e. server)\n");
- fprintf(stderr, "* del_iface <iface>: delete ovpn interface\n");
- fprintf(stderr, "\tiface: ovpn interface name\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");
- }
- static int ovpn_parse_remote(struct ovpn_ctx *ovpn, const char *host,
- const char *service, const char *vpnip)
- {
- int ret;
- struct addrinfo *result;
- struct addrinfo hints = {
- .ai_family = ovpn->sa_family,
- .ai_socktype = SOCK_DGRAM,
- .ai_protocol = IPPROTO_UDP
- };
- if (host) {
- ret = getaddrinfo(host, service, &hints, &result);
- if (ret) {
- fprintf(stderr, "getaddrinfo on remote error: %s\n",
- gai_strerror(ret));
- return -1;
- }
- if (!(result->ai_family == AF_INET &&
- result->ai_addrlen == sizeof(struct sockaddr_in)) &&
- !(result->ai_family == AF_INET6 &&
- result->ai_addrlen == sizeof(struct sockaddr_in6))) {
- ret = -EINVAL;
- goto out;
- }
- memcpy(&ovpn->remote, result->ai_addr, result->ai_addrlen);
- }
- if (vpnip) {
- ret = getaddrinfo(vpnip, NULL, &hints, &result);
- if (ret) {
- fprintf(stderr, "getaddrinfo on vpnip error: %s\n",
- gai_strerror(ret));
- return -1;
- }
- if (!(result->ai_family == AF_INET &&
- result->ai_addrlen == sizeof(struct sockaddr_in)) &&
- !(result->ai_family == AF_INET6 &&
- result->ai_addrlen == sizeof(struct sockaddr_in6))) {
- ret = -EINVAL;
- goto out;
- }
- memcpy(&ovpn->peer_ip, result->ai_addr, result->ai_addrlen);
- ovpn->sa_family = result->ai_family;
- ovpn->peer_ip_set = true;
- }
- ret = 0;
- out:
- freeaddrinfo(result);
- return ret;
- }
- static int ovpn_parse_new_peer(struct ovpn_ctx *ovpn, const char *peer_id,
- const char *raddr, const char *rport,
- const char *vpnip)
- {
- ovpn->peer_id = strtoul(peer_id, NULL, 10);
- if (errno == ERANGE || ovpn->peer_id > PEER_ID_UNDEF) {
- fprintf(stderr, "peer ID value out of range\n");
- return -1;
- }
- return ovpn_parse_remote(ovpn, raddr, rport, vpnip);
- }
- static int ovpn_parse_key_slot(const char *arg, struct ovpn_ctx *ovpn)
- {
- int slot = strtoul(arg, NULL, 10);
- if (errno == ERANGE || slot < 1 || slot > 2) {
- fprintf(stderr, "key slot out of range\n");
- return -1;
- }
- switch (slot) {
- case 1:
- ovpn->key_slot = OVPN_KEY_SLOT_PRIMARY;
- break;
- case 2:
- ovpn->key_slot = OVPN_KEY_SLOT_SECONDARY;
- break;
- }
- return 0;
- }
- static int ovpn_send_tcp_data(int socket)
- {
- uint16_t len = htons(1000);
- uint8_t buf[1002];
- int ret;
- memcpy(buf, &len, sizeof(len));
- memset(buf + sizeof(len), 0x86, sizeof(buf) - sizeof(len));
- ret = send(socket, buf, sizeof(buf), MSG_NOSIGNAL);
- fprintf(stdout, "Sent %u bytes over TCP socket\n", ret);
- return ret > 0 ? 0 : ret;
- }
- static int 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;
- }
- static enum ovpn_cmd ovpn_parse_cmd(const char *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
- */
- static void ovpn_waitbg(void)
- {
- daemon(1, 1);
- pause();
- }
- static int 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;
- }
- int num_peers = 0;
- while ((n = fscanf(fp, "%s %s\n", peer_id, vpnip)) == 2) {
- struct ovpn_ctx peer_ctx = { 0 };
- if (num_peers == MAX_PEERS) {
- fprintf(stderr, "max peers reached!\n");
- return -E2BIG;
- }
- peer_ctx.ifindex = ovpn->ifindex;
- peer_ctx.sa_family = ovpn->sa_family;
- peer_ctx.socket = ovpn_accept(ovpn);
- if (peer_ctx.socket < 0) {
- fprintf(stderr, "cannot accept connection!\n");
- 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;
- }
- while ((n = fscanf(fp, "%s %s %s %s %s %s\n", peer_id, laddr,
- lport, raddr, rport, vpnip)) == 6) {
- struct ovpn_ctx peer_ctx = { 0 };
- peer_ctx.ifindex = ovpn->ifindex;
- peer_ctx.socket = ovpn->socket;
- peer_ctx.sa_family = AF_UNSPEC;
- 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;
- }
- static int 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;
- strscpy(ovpn->ifname, argv[2], IFNAMSIZ - 1);
- ovpn->ifname[IFNAMSIZ - 1] = '\0';
- /* all commands, except NEW_IFNAME, needs an ifindex */
- if (ovpn->cmd != CMD_NEW_IFACE) {
- ovpn->ifindex = if_nametoindex(ovpn->ifname);
- if (!ovpn->ifindex) {
- fprintf(stderr, "cannot find interface: %s\n",
- strerror(errno));
- return -1;
- }
- }
- switch (ovpn->cmd) {
- case CMD_NEW_IFACE:
- if (argc < 4)
- break;
- if (!strcmp(argv[3], "P2P")) {
- ovpn->mode = OVPN_MODE_P2P;
- } else if (!strcmp(argv[3], "MP")) {
- ovpn->mode = OVPN_MODE_MP;
- } else {
- fprintf(stderr, "Cannot parse iface mode: %s\n",
- argv[3]);
- return -1;
- }
- ovpn->mode_set = true;
- break;
- case CMD_DEL_IFACE:
- break;
- case CMD_LISTEN:
- 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];
- ovpn->sa_family = AF_INET;
- if (argc > 5 && !strcmp(argv[5], "ipv6"))
- ovpn->sa_family = AF_INET6;
- break;
- case CMD_CONNECT:
- if (argc < 6)
- return -EINVAL;
- ovpn->sa_family = AF_INET;
- ret = ovpn_parse_new_peer(ovpn, argv[3], argv[4], argv[5],
- NULL);
- if (ret < 0) {
- fprintf(stderr, "Cannot parse remote peer data\n");
- return -1;
- }
- if (argc > 6) {
- ovpn->key_slot = OVPN_KEY_SLOT_PRIMARY;
- ovpn->key_id = 0;
- ovpn->cipher = OVPN_CIPHER_ALG_AES_GCM;
- ovpn->key_dir = KEY_DIR_OUT;
- 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;
- }
- const char *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;
- if (argc < 2) {
- usage(argv[0]);
- return -1;
- }
- memset(&ovpn, 0, sizeof(ovpn));
- ovpn.sa_family = AF_UNSPEC;
- ovpn.cipher = OVPN_CIPHER_ALG_NONE;
- ovpn.cmd = ovpn_parse_cmd(argv[1]);
- if (ovpn.cmd == CMD_INVALID) {
- fprintf(stderr, "Error: unknown command.\n\n");
- usage(argv[0]);
- return -1;
- }
- ret = ovpn_parse_cmd_args(&ovpn, argc, argv);
- if (ret < 0) {
- fprintf(stderr, "Error: invalid arguments.\n\n");
- if (ret == -EINVAL)
- usage(argv[0]);
- return ret;
- }
- ret = ovpn_run_cmd(&ovpn);
- if (ret)
- fprintf(stderr, "Cannot execute command: %s (%d)\n",
- strerror(-ret), ret);
- return ret;
- }
|