gnunet-helper-nat-server.c 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714
  1. /*
  2. This file is part of GNUnet.
  3. Copyright (C) 2010 GNUnet e.V.
  4. GNUnet is free software: you can redistribute it and/or modify it
  5. under the terms of the GNU Affero General Public License as published
  6. by the Free Software Foundation, either version 3 of the License,
  7. or (at your 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. Affero General Public License for more details.
  12. You should have received a copy of the GNU Affero General Public License
  13. along with this program. If not, see <http://www.gnu.org/licenses/>.
  14. SPDX-License-Identifier: AGPL3.0-or-later
  15. */
  16. /**
  17. * @file src/nat/gnunet-helper-nat-server.c
  18. * @brief Tool to help bypass NATs using ICMP method; must run as root (SUID will do)
  19. * This code will work under GNU/Linux only (or maybe BSDs, but never W32)
  20. * @author Christian Grothoff
  21. *
  22. * This program will send ONE ICMP message every 500 ms RAW sockets
  23. * to a DUMMY IP address and also listens for ICMP replies. Since
  24. * it uses RAW sockets, it must be installed SUID or run as 'root'.
  25. * In order to keep the security risk of the resulting SUID binary
  26. * minimal, the program ONLY opens the two RAW sockets with root
  27. * privileges, then drops them and only then starts to process
  28. * command line arguments. The code also does not link against
  29. * any shared libraries (except libc) and is strictly minimal
  30. * (except for checking for errors). The following list of people
  31. * have reviewed this code and considered it safe since the last
  32. * modification (if you reviewed it, please have your name added
  33. * to the list):
  34. *
  35. * - Christian Grothoff
  36. * - Nathan Evans
  37. * - Benjamin Kuperman (22 Aug 2010)
  38. * - Jacob Appelbaum (19 Dec 2011)
  39. */
  40. #if HAVE_CONFIG_H
  41. /* Just needed for HAVE_SOCKADDR_IN_SIN_LEN test macro! */
  42. #include "gnunet_config.h"
  43. #else
  44. #define _GNU_SOURCE
  45. #endif
  46. #include <sys/types.h>
  47. #include <sys/socket.h>
  48. #include <arpa/inet.h>
  49. #include <sys/select.h>
  50. #include <sys/time.h>
  51. #include <sys/types.h>
  52. #include <unistd.h>
  53. #include <stdio.h>
  54. #include <string.h>
  55. #include <errno.h>
  56. #include <stdlib.h>
  57. #include <stdint.h>
  58. #include <time.h>
  59. #include <netinet/ip.h>
  60. #include <netinet/ip_icmp.h>
  61. #include <netinet/in.h>
  62. /* The following constant is missing from FreeBSD 9.2 */
  63. #ifndef ICMP_TIME_EXCEEDED
  64. #define ICMP_TIME_EXCEEDED 11
  65. #endif
  66. /**
  67. * Call memcpy() but check for @a n being 0 first. In the latter
  68. * case, it is now safe to pass NULL for @a src or @a dst.
  69. * Unlike traditional memcpy(), returns nothing.
  70. *
  71. * @param dst destination of the copy, may be NULL if @a n is zero
  72. * @param src source of the copy, may be NULL if @a n is zero
  73. * @param n number of bytes to copy
  74. */
  75. #define GNUNET_memcpy(dst, src, n) do { if (0 != n) { (void) memcpy (dst, src, \
  76. n); \
  77. } } while (0)
  78. /**
  79. * Should we print some debug output?
  80. */
  81. #define VERBOSE 0
  82. /**
  83. * Must match packet ID used by gnunet-helper-nat-client.c
  84. */
  85. #define PACKET_ID 256
  86. /**
  87. * Must match IP given in the client.
  88. */
  89. #define DUMMY_IP "192.0.2.86"
  90. /**
  91. * Port for UDP
  92. */
  93. #define NAT_TRAV_PORT 22225
  94. /**
  95. * How often do we send our ICMP messages to receive replies?
  96. */
  97. #define ICMP_SEND_FREQUENCY_MS 500
  98. /**
  99. * IPv4 header.
  100. */
  101. struct ip_header
  102. {
  103. /**
  104. * Version (4 bits) + Internet header length (4 bits)
  105. */
  106. uint8_t vers_ihl;
  107. /**
  108. * Type of service
  109. */
  110. uint8_t tos;
  111. /**
  112. * Total length
  113. */
  114. uint16_t pkt_len;
  115. /**
  116. * Identification
  117. */
  118. uint16_t id;
  119. /**
  120. * Flags (3 bits) + Fragment offset (13 bits)
  121. */
  122. uint16_t flags_frag_offset;
  123. /**
  124. * Time to live
  125. */
  126. uint8_t ttl;
  127. /**
  128. * Protocol
  129. */
  130. uint8_t proto;
  131. /**
  132. * Header checksum
  133. */
  134. uint16_t checksum;
  135. /**
  136. * Source address
  137. */
  138. uint32_t src_ip;
  139. /**
  140. * Destination address
  141. */
  142. uint32_t dst_ip;
  143. };
  144. /**
  145. * Format of ICMP packet.
  146. */
  147. struct icmp_ttl_exceeded_header
  148. {
  149. uint8_t type;
  150. uint8_t code;
  151. uint16_t checksum;
  152. uint32_t unused;
  153. /* followed by original payload */
  154. };
  155. struct icmp_echo_header
  156. {
  157. uint8_t type;
  158. uint8_t code;
  159. uint16_t checksum;
  160. uint32_t reserved;
  161. };
  162. /**
  163. * Beginning of UDP packet.
  164. */
  165. struct udp_header
  166. {
  167. uint16_t src_port;
  168. uint16_t dst_port;
  169. uint16_t length;
  170. uint16_t crc;
  171. };
  172. /**
  173. * Socket we use to receive "fake" ICMP replies.
  174. */
  175. static int icmpsock;
  176. /**
  177. * Socket we use to send our ICMP requests.
  178. */
  179. static int rawsock;
  180. /**
  181. * Socket we use to send our UDP requests.
  182. */
  183. static int udpsock;
  184. /**
  185. * Target "dummy" address.
  186. */
  187. static struct in_addr dummy;
  188. /**
  189. * CRC-16 for IP/ICMP headers.
  190. *
  191. * @param data what to calculate the CRC over
  192. * @param bytes number of bytes in data (must be multiple of 2)
  193. * @return the CRC 16.
  194. */
  195. static uint16_t
  196. calc_checksum (const uint16_t *data, unsigned int bytes)
  197. {
  198. uint32_t sum;
  199. unsigned int i;
  200. sum = 0;
  201. for (i = 0; i < bytes / 2; i++)
  202. sum += data[i];
  203. sum = (sum & 0xffff) + (sum >> 16);
  204. sum = htons (0xffff - sum);
  205. return sum;
  206. }
  207. /**
  208. * Send an ICMP message to the dummy IP.
  209. *
  210. * @param my_ip source address (our ip address)
  211. */
  212. static void
  213. send_icmp_echo (const struct in_addr *my_ip)
  214. {
  215. char packet[sizeof(struct ip_header) + sizeof(struct icmp_echo_header)];
  216. struct icmp_echo_header icmp_echo;
  217. struct ip_header ip_pkt;
  218. struct sockaddr_in dst;
  219. size_t off;
  220. int err;
  221. off = 0;
  222. ip_pkt.vers_ihl = 0x45;
  223. ip_pkt.tos = 0;
  224. ip_pkt.pkt_len = htons (sizeof(packet));
  225. ip_pkt.id = htons (PACKET_ID);
  226. ip_pkt.flags_frag_offset = 0;
  227. ip_pkt.ttl = IPDEFTTL;
  228. ip_pkt.proto = IPPROTO_ICMP;
  229. ip_pkt.checksum = 0;
  230. ip_pkt.src_ip = my_ip->s_addr;
  231. ip_pkt.dst_ip = dummy.s_addr;
  232. ip_pkt.checksum =
  233. htons (calc_checksum ((uint16_t *) &ip_pkt,
  234. sizeof(struct ip_header)));
  235. GNUNET_memcpy (&packet[off],
  236. &ip_pkt,
  237. sizeof(struct ip_header));
  238. off += sizeof(struct ip_header);
  239. icmp_echo.type = ICMP_ECHO;
  240. icmp_echo.code = 0;
  241. icmp_echo.checksum = 0;
  242. icmp_echo.reserved = 0;
  243. icmp_echo.checksum =
  244. htons (calc_checksum
  245. ((uint16_t *) &icmp_echo,
  246. sizeof(struct icmp_echo_header)));
  247. GNUNET_memcpy (&packet[off],
  248. &icmp_echo,
  249. sizeof(struct icmp_echo_header));
  250. off += sizeof(struct icmp_echo_header);
  251. memset (&dst, 0, sizeof(dst));
  252. dst.sin_family = AF_INET;
  253. #if HAVE_SOCKADDR_IN_SIN_LEN
  254. dst.sin_len = sizeof(struct sockaddr_in);
  255. #endif
  256. dst.sin_addr = dummy;
  257. err = sendto (rawsock,
  258. packet,
  259. off,
  260. 0,
  261. (struct sockaddr *) &dst,
  262. sizeof(dst));
  263. if (err < 0)
  264. {
  265. #if VERBOSE
  266. fprintf (stderr,
  267. "sendto failed: %s\n",
  268. strerror (errno));
  269. #endif
  270. }
  271. else if (sizeof(packet) != err)
  272. {
  273. fprintf (stderr,
  274. "Error: partial send of ICMP message\n");
  275. }
  276. }
  277. /**
  278. * Send a UDP message to the dummy IP.
  279. */
  280. static void
  281. send_udp ()
  282. {
  283. struct sockaddr_in dst;
  284. ssize_t err;
  285. memset (&dst, 0, sizeof(dst));
  286. dst.sin_family = AF_INET;
  287. #if HAVE_SOCKADDR_IN_SIN_LEN
  288. dst.sin_len = sizeof(struct sockaddr_in);
  289. #endif
  290. dst.sin_addr = dummy;
  291. dst.sin_port = htons (NAT_TRAV_PORT);
  292. err = sendto (udpsock,
  293. NULL,
  294. 0,
  295. 0,
  296. (struct sockaddr *) &dst,
  297. sizeof(dst));
  298. if (err < 0)
  299. {
  300. #if VERBOSE
  301. fprintf (stderr,
  302. "sendto failed: %s\n",
  303. strerror (errno));
  304. #endif
  305. }
  306. else if (0 != err)
  307. {
  308. fprintf (stderr,
  309. "Error: partial send of ICMP message\n");
  310. }
  311. }
  312. /**
  313. * We've received an ICMP response. Process it.
  314. */
  315. static void
  316. process_icmp_response ()
  317. {
  318. char buf[65536];
  319. ssize_t have;
  320. struct in_addr source_ip;
  321. struct ip_header ip_pkt;
  322. struct icmp_ttl_exceeded_header icmp_ttl;
  323. struct icmp_echo_header icmp_echo;
  324. struct udp_header udp_pkt;
  325. size_t off;
  326. uint16_t port;
  327. have = read (icmpsock, buf, sizeof(buf));
  328. if (-1 == have)
  329. {
  330. fprintf (stderr,
  331. "Error reading raw socket: %s\n",
  332. strerror (errno));
  333. return;
  334. }
  335. #if VERBOSE
  336. fprintf (stderr,
  337. "Received message of %u bytes\n",
  338. (unsigned int) have);
  339. #endif
  340. if (have <
  341. (ssize_t) (sizeof(struct ip_header)
  342. + sizeof(struct icmp_ttl_exceeded_header)
  343. + sizeof(struct ip_header)))
  344. {
  345. /* malformed */
  346. return;
  347. }
  348. off = 0;
  349. GNUNET_memcpy (&ip_pkt,
  350. &buf[off],
  351. sizeof(struct ip_header));
  352. off += sizeof(struct ip_header);
  353. GNUNET_memcpy (&icmp_ttl,
  354. &buf[off],
  355. sizeof(struct icmp_ttl_exceeded_header));
  356. off += sizeof(struct icmp_ttl_exceeded_header);
  357. if ((ICMP_TIME_EXCEEDED != icmp_ttl.type) || (0 != icmp_ttl.code))
  358. {
  359. /* different type than what we want */
  360. return;
  361. }
  362. /* grab source IP of 1st IP header */
  363. source_ip.s_addr = ip_pkt.src_ip;
  364. /* skip 2nd IP header */
  365. GNUNET_memcpy (&ip_pkt,
  366. &buf[off],
  367. sizeof(struct ip_header));
  368. off += sizeof(struct ip_header);
  369. switch (ip_pkt.proto)
  370. {
  371. case IPPROTO_ICMP:
  372. if (have !=
  373. (sizeof(struct ip_header) * 2
  374. + sizeof(struct icmp_ttl_exceeded_header)
  375. + sizeof(struct icmp_echo_header)))
  376. {
  377. /* malformed */
  378. return;
  379. }
  380. /* grab ICMP ECHO content */
  381. GNUNET_memcpy (&icmp_echo,
  382. &buf[off],
  383. sizeof(struct icmp_echo_header));
  384. port = (uint16_t) ntohl (icmp_echo.reserved);
  385. break;
  386. case IPPROTO_UDP:
  387. if (have !=
  388. (sizeof(struct ip_header) * 2
  389. + sizeof(struct icmp_ttl_exceeded_header) + sizeof(struct udp_header)))
  390. {
  391. /* malformed */
  392. return;
  393. }
  394. /* grab UDP content */
  395. GNUNET_memcpy (&udp_pkt,
  396. &buf[off],
  397. sizeof(struct udp_header));
  398. port = ntohs (udp_pkt.length);
  399. break;
  400. default:
  401. /* different type than what we want */
  402. return;
  403. }
  404. if (port == 0)
  405. fprintf (stdout, "%s\n",
  406. inet_ntop (AF_INET, &source_ip, buf, sizeof(buf)));
  407. else
  408. fprintf (stdout, "%s:%u\n",
  409. inet_ntop (AF_INET, &source_ip, buf, sizeof(buf)),
  410. (unsigned int) port);
  411. fflush (stdout);
  412. }
  413. /**
  414. * Fully initialize the raw socket.
  415. *
  416. * @return -1 on error, 0 on success
  417. */
  418. static int
  419. setup_raw_socket ()
  420. {
  421. const int one = 1;
  422. if (-1 ==
  423. setsockopt (rawsock,
  424. SOL_SOCKET,
  425. SO_BROADCAST,
  426. (char *) &one,
  427. sizeof(one)))
  428. {
  429. fprintf (stderr,
  430. "setsockopt failed: %s\n",
  431. strerror (errno));
  432. return -1;
  433. }
  434. if (-1 ==
  435. setsockopt (rawsock,
  436. IPPROTO_IP,
  437. IP_HDRINCL,
  438. (char *) &one,
  439. sizeof(one)))
  440. {
  441. fprintf (stderr,
  442. "setsockopt failed: %s\n",
  443. strerror (errno));
  444. return -1;
  445. }
  446. return 0;
  447. }
  448. /**
  449. * Create a UDP socket for writing.
  450. *
  451. * @param my_ip source address (our ip address)
  452. * @return -1 on error
  453. */
  454. static int
  455. make_udp_socket (const struct in_addr *my_ip)
  456. {
  457. int ret;
  458. struct sockaddr_in addr;
  459. ret = socket (AF_INET, SOCK_DGRAM, 0);
  460. if (-1 == ret)
  461. {
  462. fprintf (stderr,
  463. "Error opening UDP socket: %s\n",
  464. strerror (errno));
  465. return -1;
  466. }
  467. memset (&addr, 0, sizeof(addr));
  468. addr.sin_family = AF_INET;
  469. #if HAVE_SOCKADDR_IN_SIN_LEN
  470. addr.sin_len = sizeof(struct sockaddr_in);
  471. #endif
  472. addr.sin_addr = *my_ip;
  473. addr.sin_port = htons (NAT_TRAV_PORT);
  474. if (0 != bind (ret,
  475. (struct sockaddr *) &addr,
  476. sizeof(addr)))
  477. {
  478. fprintf (stderr,
  479. "Error binding UDP socket to port %u: %s\n",
  480. NAT_TRAV_PORT,
  481. strerror (errno));
  482. (void) close (ret);
  483. return -1;
  484. }
  485. return ret;
  486. }
  487. int
  488. main (int argc,
  489. char *const *argv)
  490. {
  491. struct in_addr external;
  492. fd_set rs;
  493. struct timeval tv;
  494. uid_t uid;
  495. unsigned int alt;
  496. int icmp_eno;
  497. int raw_eno;
  498. int global_ret;
  499. /* Create an ICMP raw socket for reading (we'll check errors later) */
  500. icmpsock = socket (AF_INET,
  501. SOCK_RAW,
  502. IPPROTO_ICMP);
  503. icmp_eno = errno;
  504. /* Create an (ICMP) raw socket for writing (we'll check errors later) */
  505. rawsock = socket (AF_INET,
  506. SOCK_RAW,
  507. IPPROTO_RAW);
  508. raw_eno = errno;
  509. udpsock = -1;
  510. /* drop root rights */
  511. uid = getuid ();
  512. #ifdef HAVE_SETRESUID
  513. if (0 != setresuid (uid, uid, uid))
  514. {
  515. fprintf (stderr,
  516. "Failed to setresuid: %s\n",
  517. strerror (errno));
  518. global_ret = 1;
  519. goto error_exit;
  520. }
  521. #else
  522. if (0 != (setuid (uid) | seteuid (uid)))
  523. {
  524. fprintf (stderr,
  525. "Failed to setuid: %s\n",
  526. strerror (errno));
  527. global_ret = 2;
  528. goto error_exit;
  529. }
  530. #endif
  531. /* Now that we run without root rights, we can do error checking... */
  532. if (2 != argc)
  533. {
  534. fprintf (stderr,
  535. "This program must be started with our (internal NAT) IP as the only argument.\n");
  536. global_ret = 3;
  537. goto error_exit;
  538. }
  539. if (1 != inet_pton (AF_INET, argv[1], &external))
  540. {
  541. fprintf (stderr,
  542. "Error parsing IPv4 address: %s\n",
  543. strerror (errno));
  544. global_ret = 4;
  545. goto error_exit;
  546. }
  547. if (1 != inet_pton (AF_INET, DUMMY_IP, &dummy))
  548. {
  549. fprintf (stderr,
  550. "Internal error converting dummy IP to binary.\n");
  551. global_ret = 5;
  552. goto error_exit;
  553. }
  554. /* error checking icmpsock */
  555. if (-1 == icmpsock)
  556. {
  557. fprintf (stderr,
  558. "Error opening RAW socket: %s\n",
  559. strerror (icmp_eno));
  560. global_ret = 6;
  561. goto error_exit;
  562. }
  563. if (icmpsock >= FD_SETSIZE)
  564. {
  565. /* this could happen if we were started with a large number of already-open
  566. file descriptors... */
  567. fprintf (stderr,
  568. "Socket number too large (%d > %u)\n",
  569. icmpsock,
  570. (unsigned int) FD_SETSIZE);
  571. global_ret = 7;
  572. goto error_exit;
  573. }
  574. /* error checking rawsock */
  575. if (-1 == rawsock)
  576. {
  577. fprintf (stderr,
  578. "Error opening RAW socket: %s\n",
  579. strerror (raw_eno));
  580. global_ret = 8;
  581. goto error_exit;
  582. }
  583. /* no need to check 'rawsock' against FD_SETSIZE as it is never used
  584. with 'select' */
  585. if (0 != setup_raw_socket ())
  586. {
  587. global_ret = 9;
  588. goto error_exit;
  589. }
  590. if (-1 == (udpsock = make_udp_socket (&external)))
  591. {
  592. global_ret = 10;
  593. goto error_exit;
  594. }
  595. alt = 0;
  596. while (1)
  597. {
  598. FD_ZERO (&rs);
  599. FD_SET (icmpsock, &rs);
  600. tv.tv_sec = 0;
  601. tv.tv_usec = ICMP_SEND_FREQUENCY_MS * 1000;
  602. if (-1 == select (icmpsock + 1, &rs, NULL, NULL, &tv))
  603. {
  604. if (errno == EINTR)
  605. continue;
  606. fprintf (stderr,
  607. "select failed: %s\n",
  608. strerror (errno));
  609. break;
  610. }
  611. if (1 == getppid ()) /* Check the parent process id, if 1 the parent has died, so we should die too */
  612. break;
  613. if (FD_ISSET (icmpsock, &rs))
  614. {
  615. process_icmp_response ();
  616. continue;
  617. }
  618. if (0 == (++alt % 2))
  619. send_icmp_echo (&external);
  620. else
  621. send_udp ();
  622. }
  623. /* select failed (internal error or OS out of resources) */
  624. global_ret = 11;
  625. error_exit:
  626. if (-1 != icmpsock)
  627. (void) close (icmpsock);
  628. if (-1 != rawsock)
  629. (void) close (rawsock);
  630. if (-1 != udpsock)
  631. (void) close (udpsock);
  632. return global_ret;
  633. }
  634. /* end of gnunet-helper-nat-server.c */