vpn_api.c 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589
  1. /*
  2. This file is part of GNUnet.
  3. (C) 2012 Christian Grothoff
  4. GNUnet is free software; you can redistribute it and/or modify
  5. it under the terms of the GNU General Public License as published
  6. by the Free Software Foundation; either version 3, or (at your
  7. option) any later version.
  8. GNUnet is distributed in the hope that it will be useful, but
  9. WITHOUT ANY WARRANTY; without even the implied warranty of
  10. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  11. General Public License for more details.
  12. You should have received a copy of the GNU General Public License
  13. along with GNUnet; see the file COPYING. If not, write to the
  14. Free Software Foundation, Inc., 59 Temple Place - Suite 330,
  15. Boston, MA 02111-1307, USA.
  16. */
  17. /**
  18. * @file vpn/vpn_api.c
  19. * @brief library to access the VPN service and tell it how to redirect traffic
  20. * @author Christian Grothoff
  21. */
  22. #include "platform.h"
  23. #include "gnunet_vpn_service.h"
  24. #include "vpn.h"
  25. /**
  26. * Opaque VPN handle
  27. */
  28. struct GNUNET_VPN_Handle
  29. {
  30. /**
  31. * Configuration we use.
  32. */
  33. const struct GNUNET_CONFIGURATION_Handle *cfg;
  34. /**
  35. * Connection to VPN service.
  36. */
  37. struct GNUNET_CLIENT_Connection *client;
  38. /**
  39. * Active transmission request.
  40. */
  41. struct GNUNET_CLIENT_TransmitHandle *th;
  42. /**
  43. * Head of list of active redirection requests.
  44. */
  45. struct GNUNET_VPN_RedirectionRequest *rr_head;
  46. /**
  47. * Tail of list of active redirection requests.
  48. */
  49. struct GNUNET_VPN_RedirectionRequest *rr_tail;
  50. /**
  51. * Identifier of a reconnect task.
  52. */
  53. GNUNET_SCHEDULER_TaskIdentifier rt;
  54. /**
  55. * How long do we wait until we try to reconnect?
  56. */
  57. struct GNUNET_TIME_Relative backoff;
  58. /**
  59. * ID of the last request that was submitted to the service.
  60. */
  61. uint64_t request_id_gen;
  62. };
  63. /**
  64. * Opaque redirection request handle.
  65. */
  66. struct GNUNET_VPN_RedirectionRequest
  67. {
  68. /**
  69. * Element in DLL.
  70. */
  71. struct GNUNET_VPN_RedirectionRequest *next;
  72. /**
  73. * Element in DLL.
  74. */
  75. struct GNUNET_VPN_RedirectionRequest *prev;
  76. /**
  77. * Pointer to the VPN struct.
  78. */
  79. struct GNUNET_VPN_Handle *vh;
  80. /**
  81. * Target IP address for the redirection, or NULL for
  82. * redirection to service. Allocated after this struct.
  83. */
  84. const void *addr;
  85. /**
  86. * Function to call with the designated IP address.
  87. */
  88. GNUNET_VPN_AllocationCallback cb;
  89. /**
  90. * Closure for 'cb'.
  91. */
  92. void *cb_cls;
  93. /**
  94. * For service redirection, identity of the peer offering the service.
  95. */
  96. struct GNUNET_PeerIdentity peer;
  97. /**
  98. * For service redirection, service descriptor.
  99. */
  100. struct GNUNET_HashCode serv;
  101. /**
  102. * At what time should the created service mapping expire?
  103. */
  104. struct GNUNET_TIME_Absolute expiration_time;
  105. /**
  106. * non-zero if this request has been sent to the service.
  107. */
  108. uint64_t request_id;
  109. /**
  110. * Desired address family for the result.
  111. */
  112. int result_af;
  113. /**
  114. * Address family of 'addr'. AF_INET or AF_INET6.
  115. */
  116. int addr_af;
  117. /**
  118. * For service redirection, IPPROT_UDP or IPPROTO_TCP.
  119. */
  120. uint8_t protocol;
  121. };
  122. /**
  123. * Disconnect from the service (communication error) and reconnect later.
  124. *
  125. * @param vh handle to reconnect.
  126. */
  127. static void
  128. reconnect (struct GNUNET_VPN_Handle *vh);
  129. /**
  130. * Function called when we receive a message from the VPN service.
  131. *
  132. * @param cls the `struct GNUNET_VPN_Handle`
  133. * @param msg message received, NULL on timeout or fatal error
  134. */
  135. static void
  136. receive_response (void *cls,
  137. const struct GNUNET_MessageHeader* msg)
  138. {
  139. struct GNUNET_VPN_Handle *vh = cls;
  140. const struct RedirectToIpResponseMessage *rm;
  141. struct GNUNET_VPN_RedirectionRequest *rr;
  142. size_t msize;
  143. size_t alen;
  144. int af;
  145. if (NULL == msg)
  146. {
  147. reconnect (vh);
  148. return;
  149. }
  150. if ( (ntohs (msg->type) != GNUNET_MESSAGE_TYPE_VPN_CLIENT_USE_IP) ||
  151. (sizeof (struct RedirectToIpResponseMessage) > (msize = ntohs (msg->size))) )
  152. {
  153. GNUNET_break (0);
  154. reconnect (vh);
  155. return;
  156. }
  157. rm = (const struct RedirectToIpResponseMessage *) msg;
  158. af = (int) ntohl (rm->result_af);
  159. switch (af)
  160. {
  161. case AF_UNSPEC:
  162. alen = 0;
  163. break;
  164. case AF_INET:
  165. alen = sizeof (struct in_addr);
  166. break;
  167. case AF_INET6:
  168. alen = sizeof (struct in6_addr);
  169. break;
  170. default:
  171. GNUNET_break (0);
  172. reconnect (vh);
  173. return;
  174. }
  175. if ( (msize != alen + sizeof (struct RedirectToIpResponseMessage)) ||
  176. (0 == rm->request_id) )
  177. {
  178. GNUNET_break (0);
  179. reconnect (vh);
  180. return;
  181. }
  182. GNUNET_CLIENT_receive (vh->client,
  183. &receive_response, vh,
  184. GNUNET_TIME_UNIT_FOREVER_REL);
  185. for (rr = vh->rr_head; NULL != rr; rr = rr->next)
  186. {
  187. if (rr->request_id == rm->request_id)
  188. {
  189. GNUNET_CONTAINER_DLL_remove (vh->rr_head,
  190. vh->rr_tail,
  191. rr);
  192. rr->cb (rr->cb_cls,
  193. af,
  194. (af == AF_UNSPEC) ? NULL : &rm[1]);
  195. GNUNET_free (rr);
  196. break;
  197. }
  198. }
  199. }
  200. /**
  201. * We're ready to transmit a request to the VPN service. Do it.
  202. *
  203. * @param cls the 'struct GNUNET_VPN_Handle*'
  204. * @param size number of bytes available in buf
  205. * @param buf where to copy the request
  206. * @return number of bytes copied to 'buf'
  207. */
  208. static size_t
  209. transmit_request (void *cls,
  210. size_t size,
  211. void *buf)
  212. {
  213. struct GNUNET_VPN_Handle *vh = cls;
  214. struct GNUNET_VPN_RedirectionRequest *rr;
  215. struct RedirectToIpRequestMessage rip;
  216. struct RedirectToServiceRequestMessage rs;
  217. char *cbuf;
  218. size_t alen;
  219. size_t ret;
  220. vh->th = NULL;
  221. /* find a pending request */
  222. rr = vh->rr_head;
  223. while ( (NULL != rr) &&
  224. (0 != rr->request_id) )
  225. rr = rr->next;
  226. if (NULL == rr)
  227. return 0;
  228. if (0 == size)
  229. {
  230. reconnect (vh);
  231. return 0;
  232. }
  233. /* if first request, start receive loop */
  234. if (0 == vh->request_id_gen)
  235. GNUNET_CLIENT_receive (vh->client,
  236. &receive_response, vh,
  237. GNUNET_TIME_UNIT_FOREVER_REL);
  238. if (NULL == rr->addr)
  239. {
  240. ret = sizeof (struct RedirectToServiceRequestMessage);
  241. GNUNET_assert (ret <= size);
  242. rs.header.size = htons ((uint16_t) ret);
  243. rs.header.type = htons (GNUNET_MESSAGE_TYPE_VPN_CLIENT_REDIRECT_TO_SERVICE);
  244. rs.reserved = htonl (0);
  245. rs.expiration_time = GNUNET_TIME_absolute_hton (rr->expiration_time);
  246. rs.protocol = htonl (rr->protocol);
  247. rs.result_af = htonl (rr->result_af);
  248. rs.target = rr->peer;
  249. rs.service_descriptor = rr->serv;
  250. rs.request_id = rr->request_id = ++vh->request_id_gen;
  251. memcpy (buf, &rs, sizeof (struct RedirectToServiceRequestMessage));
  252. }
  253. else
  254. {
  255. switch (rr->addr_af)
  256. {
  257. case AF_INET:
  258. alen = sizeof (struct in_addr);
  259. break;
  260. case AF_INET6:
  261. alen = sizeof (struct in6_addr);
  262. break;
  263. default:
  264. GNUNET_assert (0);
  265. return 0;
  266. }
  267. ret = alen + sizeof (struct RedirectToIpRequestMessage);
  268. GNUNET_assert (ret <= size);
  269. rip.header.size = htons ((uint16_t) ret);
  270. rip.header.type = htons (GNUNET_MESSAGE_TYPE_VPN_CLIENT_REDIRECT_TO_IP);
  271. rip.reserved = htonl (0);
  272. rip.expiration_time = GNUNET_TIME_absolute_hton (rr->expiration_time);
  273. rip.result_af = htonl (rr->result_af);
  274. rip.addr_af = htonl (rr->addr_af);
  275. rip.request_id = rr->request_id = ++vh->request_id_gen;
  276. cbuf = buf;
  277. memcpy (cbuf, &rip, sizeof (struct RedirectToIpRequestMessage));
  278. memcpy (&cbuf[sizeof (struct RedirectToIpRequestMessage)], rr->addr, alen);
  279. }
  280. /* test if there are more pending requests */
  281. while ( (NULL != rr) &&
  282. (0 != rr->request_id) )
  283. rr = rr->next;
  284. if (NULL != rr)
  285. vh->th = GNUNET_CLIENT_notify_transmit_ready (vh->client,
  286. sizeof (struct RedirectToServiceRequestMessage),
  287. GNUNET_TIME_UNIT_FOREVER_REL,
  288. GNUNET_NO,
  289. &transmit_request,
  290. vh);
  291. return ret;
  292. }
  293. /**
  294. * Add a request to our request queue and transmit it.
  295. *
  296. * @param rr request to queue and transmit.
  297. */
  298. static void
  299. queue_request (struct GNUNET_VPN_RedirectionRequest *rr)
  300. {
  301. struct GNUNET_VPN_Handle *vh;
  302. vh = rr->vh;
  303. GNUNET_CONTAINER_DLL_insert_tail (vh->rr_head,
  304. vh->rr_tail,
  305. rr);
  306. if ( (NULL == vh->th) &&
  307. (NULL != vh->client) )
  308. vh->th = GNUNET_CLIENT_notify_transmit_ready (vh->client,
  309. sizeof (struct RedirectToServiceRequestMessage),
  310. GNUNET_TIME_UNIT_FOREVER_REL,
  311. GNUNET_NO,
  312. &transmit_request,
  313. vh);
  314. }
  315. /**
  316. * Connect to the VPN service and start again to transmit our requests.
  317. *
  318. * @param cls the 'struct GNUNET_VPN_Handle *'
  319. * @param tc scheduler context
  320. */
  321. static void
  322. connect_task (void *cls,
  323. const struct GNUNET_SCHEDULER_TaskContext *tc)
  324. {
  325. struct GNUNET_VPN_Handle *vh = cls;
  326. vh->rt = GNUNET_SCHEDULER_NO_TASK;
  327. vh->client = GNUNET_CLIENT_connect ("vpn", vh->cfg);
  328. GNUNET_assert (NULL != vh->client);
  329. GNUNET_assert (NULL == vh->th);
  330. if (NULL != vh->rr_head)
  331. vh->th = GNUNET_CLIENT_notify_transmit_ready (vh->client,
  332. sizeof (struct RedirectToServiceRequestMessage),
  333. GNUNET_TIME_UNIT_FOREVER_REL,
  334. GNUNET_NO,
  335. &transmit_request,
  336. vh);
  337. }
  338. /**
  339. * Disconnect from the service (communication error) and reconnect later.
  340. *
  341. * @param vh handle to reconnect.
  342. */
  343. static void
  344. reconnect (struct GNUNET_VPN_Handle *vh)
  345. {
  346. struct GNUNET_VPN_RedirectionRequest *rr;
  347. if (NULL != vh->th)
  348. {
  349. GNUNET_CLIENT_notify_transmit_ready_cancel (vh->th);
  350. vh->th = NULL;
  351. }
  352. GNUNET_CLIENT_disconnect (vh->client);
  353. vh->client = NULL;
  354. vh->request_id_gen = 0;
  355. for (rr = vh->rr_head; NULL != rr; rr = rr->next)
  356. rr->request_id = 0;
  357. vh->backoff = GNUNET_TIME_relative_max (GNUNET_TIME_UNIT_MILLISECONDS,
  358. GNUNET_TIME_relative_min (GNUNET_TIME_relative_multiply (vh->backoff, 2),
  359. GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 30)));
  360. vh->rt = GNUNET_SCHEDULER_add_delayed (vh->backoff,
  361. &connect_task,
  362. vh);
  363. }
  364. /**
  365. * Cancel redirection request with the service.
  366. *
  367. * @param rr request to cancel
  368. */
  369. void
  370. GNUNET_VPN_cancel_request (struct GNUNET_VPN_RedirectionRequest *rr)
  371. {
  372. struct GNUNET_VPN_Handle *vh;
  373. vh = rr->vh;
  374. GNUNET_CONTAINER_DLL_remove (vh->rr_head,
  375. vh->rr_tail,
  376. rr);
  377. GNUNET_free (rr);
  378. }
  379. /**
  380. * Tell the VPN that a forwarding to a particular peer offering a
  381. * particular service is requested. The VPN is to reserve a
  382. * particular IP for the redirection and return it. The VPN will
  383. * begin the redirection as soon as possible and maintain it as long
  384. * as it is actively used and keeping it is feasible. Given resource
  385. * limitations, the longest inactive mappings will be destroyed.
  386. *
  387. * @param vh VPN handle
  388. * @param result_af desired address family for the returned allocation
  389. * can also be AF_UNSPEC
  390. * @param protocol protocol, IPPROTO_UDP or IPPROTO_TCP
  391. * @param peer target peer for the redirection
  392. * @param serv service descriptor to give to the peer
  393. * @param expiration_time at what time should the redirection expire?
  394. * (this should not impact connections that are active at that time)
  395. * @param cb function to call with the IP
  396. * @param cb_cls closure for cb
  397. * @return handle to cancel the request (means the callback won't be
  398. * invoked anymore; the mapping may or may not be established
  399. * anyway)
  400. */
  401. struct GNUNET_VPN_RedirectionRequest *
  402. GNUNET_VPN_redirect_to_peer (struct GNUNET_VPN_Handle *vh,
  403. int result_af,
  404. uint8_t protocol,
  405. const struct GNUNET_PeerIdentity *peer,
  406. const struct GNUNET_HashCode *serv,
  407. struct GNUNET_TIME_Absolute expiration_time,
  408. GNUNET_VPN_AllocationCallback cb,
  409. void *cb_cls)
  410. {
  411. struct GNUNET_VPN_RedirectionRequest *rr;
  412. rr = GNUNET_new (struct GNUNET_VPN_RedirectionRequest);
  413. rr->vh = vh;
  414. rr->cb = cb;
  415. rr->cb_cls = cb_cls;
  416. rr->peer = *peer;
  417. rr->serv = *serv;
  418. rr->expiration_time = expiration_time;
  419. rr->result_af = result_af;
  420. rr->protocol = protocol;
  421. queue_request (rr);
  422. return rr;
  423. }
  424. /**
  425. * Tell the VPN that forwarding to the Internet via some exit node is
  426. * requested. Note that both UDP and TCP traffic will be forwarded,
  427. * but possibly to different exit nodes. The VPN is to reserve a
  428. * particular IP for the redirection and return it. The VPN will
  429. * begin the redirection as soon as possible and maintain it as long
  430. * as it is actively used and keeping it is feasible. Given resource
  431. * limitations, the longest inactive mappings will be destroyed.
  432. *
  433. * @param vh VPN handle
  434. * @param result_af desired address family for the returned allocation
  435. * @param addr_af address family for 'addr', AF_INET or AF_INET6
  436. * @param addr destination IP address on the Internet; destination
  437. * port is to be taken from the VPN packet itself
  438. * @param expiration_time at what time should the redirection expire?
  439. * (this should not impact connections that are active at that time)
  440. * @param cb function to call with the IP
  441. * @param cb_cls closure for cb
  442. * @return handle to cancel the request (means the callback won't be
  443. * invoked anymore; the mapping may or may not be established
  444. * anyway)
  445. */
  446. struct GNUNET_VPN_RedirectionRequest *
  447. GNUNET_VPN_redirect_to_ip (struct GNUNET_VPN_Handle *vh,
  448. int result_af,
  449. int addr_af,
  450. const void *addr,
  451. struct GNUNET_TIME_Absolute expiration_time,
  452. GNUNET_VPN_AllocationCallback cb,
  453. void *cb_cls)
  454. {
  455. struct GNUNET_VPN_RedirectionRequest *rr;
  456. size_t alen;
  457. switch (addr_af)
  458. {
  459. case AF_INET:
  460. alen = sizeof (struct in_addr);
  461. break;
  462. case AF_INET6:
  463. alen = sizeof (struct in6_addr);
  464. break;
  465. default:
  466. GNUNET_break (0);
  467. return NULL;
  468. }
  469. rr = GNUNET_malloc (sizeof (struct GNUNET_VPN_RedirectionRequest) + alen);
  470. rr->vh = vh;
  471. rr->addr = &rr[1];
  472. rr->cb = cb;
  473. rr->cb_cls = cb_cls;
  474. rr->expiration_time = expiration_time;
  475. rr->result_af = result_af;
  476. rr->addr_af = addr_af;
  477. memcpy (&rr[1], addr, alen);
  478. queue_request (rr);
  479. return rr;
  480. }
  481. /**
  482. * Connect to the VPN service
  483. *
  484. * @param cfg configuration to use
  485. * @return VPN handle
  486. */
  487. struct GNUNET_VPN_Handle *
  488. GNUNET_VPN_connect (const struct GNUNET_CONFIGURATION_Handle *cfg)
  489. {
  490. struct GNUNET_VPN_Handle *vh;
  491. vh = GNUNET_new (struct GNUNET_VPN_Handle);
  492. vh->cfg = cfg;
  493. vh->client = GNUNET_CLIENT_connect ("vpn", cfg);
  494. if (NULL == vh->client)
  495. {
  496. GNUNET_free (vh);
  497. return NULL;
  498. }
  499. return vh;
  500. }
  501. /**
  502. * Disconnect from the VPN service.
  503. *
  504. * @param vh VPN handle
  505. */
  506. void
  507. GNUNET_VPN_disconnect (struct GNUNET_VPN_Handle *vh)
  508. {
  509. GNUNET_assert (NULL == vh->rr_head);
  510. if (NULL != vh->th)
  511. {
  512. GNUNET_CLIENT_notify_transmit_ready_cancel (vh->th);
  513. vh->th = NULL;
  514. }
  515. if (NULL != vh->client)
  516. {
  517. GNUNET_CLIENT_disconnect (vh->client);
  518. vh->client = NULL;
  519. }
  520. if (GNUNET_SCHEDULER_NO_TASK != vh->rt)
  521. {
  522. GNUNET_SCHEDULER_cancel (vh->rt);
  523. vh->rt = GNUNET_SCHEDULER_NO_TASK;
  524. }
  525. GNUNET_free (vh);
  526. }
  527. /* end of vpn_api.c */