TUNInterface_sunos.c 6.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198
  1. /* vim: set expandtab ts=4 sw=4: */
  2. /*
  3. * You may redistribute this program and/or modify it under the terms of
  4. * the GNU General Public License as published by the Free Software Foundation,
  5. * either version 3 of the License, or (at your option) any later version.
  6. *
  7. * This program is distributed in the hope that it will be useful,
  8. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  9. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  10. * GNU General Public License for more details.
  11. *
  12. * You should have received a copy of the GNU General Public License
  13. * along with this program. If not, see <https://www.gnu.org/licenses/>.
  14. */
  15. #include "interface/tuntap/TUNInterface.h"
  16. #include "util/AddrTools.h"
  17. #include "util/Identity.h"
  18. #include "util/events/Pipe.h"
  19. #include "wire/Ethernet.h"
  20. #include "wire/Error.h"
  21. #include <errno.h>
  22. #include <stdio.h>
  23. #include <sys/ioctl.h>
  24. #include <unistd.h>
  25. #include <string.h>
  26. #include <sys/socket.h>
  27. #include <sys/types.h>
  28. #include <stdlib.h>
  29. #include <stddef.h>
  30. #include <net/if.h>
  31. #include <ctype.h>
  32. #include <sys/stropts.h>
  33. #include <sys/sockio.h>
  34. #include <fcntl.h>
  35. #include <net/route.h>
  36. /**
  37. * Since some illumos distributions (namely SmartOS) don't ship `net/if_tun.h`,
  38. * define those IOCTL constants here.
  39. */
  40. #define TUNNEWPPA (('T'<<16) | 0x0001)
  41. #define TUNSETPPA (('T'<<16) | 0x0002)
  42. struct TUNInterface_Illumos_pvt
  43. {
  44. struct Iface internalIf;
  45. struct Iface externalIf;
  46. struct Pipe* const pipe;
  47. Identity
  48. };
  49. /**
  50. * Illumos has no concept of packet info, it only supports IPv4 and IPv6
  51. * through TUN devices and it detects it by reading the version byte.
  52. */
  53. static uint16_t ethertypeForPacketType(uint8_t highByte)
  54. {
  55. return ((highByte >> 4) == 6) ? Ethernet_TYPE_IP6 : Ethernet_TYPE_IP4;
  56. }
  57. static Iface_DEFUN incomingFromWire(struct Message* message, struct Iface* externalIf)
  58. {
  59. struct TUNInterface_Illumos_pvt* ctx =
  60. Identity_containerOf(externalIf, struct TUNInterface_Illumos_pvt, externalIf);
  61. if (Message_getLength(message) < 4) {
  62. return 0;
  63. }
  64. Er_assert(Message_eshift(message, 4));
  65. ((uint16_t*) message->msgbytes)[0] = 0;
  66. ((uint16_t*) message->msgbytes)[1] = ethertypeForPacketType(message->msgbytes[4]);
  67. return Iface_next(&ctx->internalIf, message);
  68. }
  69. static Iface_DEFUN incomingFromUs(struct Message* message, struct Iface* internalIf)
  70. {
  71. struct TUNInterface_Illumos_pvt* ctx =
  72. Identity_containerOf(internalIf, struct TUNInterface_Illumos_pvt, internalIf);
  73. Er_assert(Message_eshift(message, -4));
  74. uint16_t ethertype = ((uint16_t*) message->msgbytes)[-1];
  75. if (ethertype != Ethernet_TYPE_IP6 && ethertype != Ethernet_TYPE_IP4) {
  76. Assert_true(!"Unsupported ethertype");
  77. }
  78. return Iface_next(&ctx->externalIf, message);
  79. }
  80. Er_DEFUN(struct Iface* TUNInterface_new(const char* interfaceName,
  81. char assignedInterfaceName[TUNInterface_IFNAMSIZ],
  82. int isTapMode,
  83. struct EventBase* base,
  84. struct Log* logger,
  85. struct Allocator* alloc))
  86. {
  87. // tap mode is not supported at all by the sunos tun driver.
  88. if (isTapMode) { Er_raise(alloc, "tap mode not supported on this platform"); }
  89. // Extract the number eg: 0 from tun0
  90. int ppa = 0;
  91. if (interfaceName) {
  92. for (uint32_t i = 0; i < strlen(interfaceName); i++) {
  93. if (isdigit(interfaceName[i])) {
  94. ppa = atoi(interfaceName);
  95. }
  96. }
  97. }
  98. // Open the descriptor
  99. int tunFd = open("/dev/tun", O_RDWR);
  100. // Either the name is specified and we use TUNSETPPA,
  101. // or it's not specified and we just want a TUNNEWPPA
  102. if (ppa) {
  103. ppa = ioctl(tunFd, TUNSETPPA, ppa);
  104. } else {
  105. ppa = ioctl(tunFd, TUNNEWPPA, -1);
  106. }
  107. int ipFd = open("/dev/ip6", O_RDWR, 0);
  108. int tunFd2 = open("/dev/tun", O_RDWR, 0);
  109. if (tunFd < 0 || ipFd < 0 || ppa < 0 || tunFd2 < 0) {
  110. int err = errno;
  111. close(tunFd);
  112. close(ipFd);
  113. close(tunFd2);
  114. char* error = NULL;
  115. if (tunFd < 0) {
  116. error = "open(\"/dev/tun\")";
  117. } else if (ipFd < 0) {
  118. error = "open(\"/dev/ip6\")";
  119. } else if (ppa < 0) {
  120. error = "ioctl(TUNNEWPPA)";
  121. } else if (tunFd2 < 0) {
  122. error = "open(\"/dev/tun\") (opening for plumbing interface)";
  123. }
  124. Er_raise(alloc, "%s [%s]", error, strerror(err));
  125. }
  126. struct lifreq ifr = {
  127. .lifr_ppa = ppa,
  128. .lifr_flags = IFF_IPV6
  129. };
  130. // Since devices are numbered rather than named, it's not possible to have tun0 and cjdns0
  131. // so we'll skip the pretty names and call everything tunX
  132. int maxNameSize = (LIFNAMSIZ < TUNInterface_IFNAMSIZ) ? LIFNAMSIZ : TUNInterface_IFNAMSIZ;
  133. if (assignedInterfaceName) {
  134. snprintf(assignedInterfaceName, maxNameSize, "tun%d", ppa);
  135. }
  136. snprintf(ifr.lifr_name, maxNameSize, "tun%d", ppa);
  137. char* error = NULL;
  138. if (ioctl(tunFd, I_SRDOPT, RMSGD) < 0) {
  139. error = "putting tun into message-discard mode";
  140. } else if (ioctl(tunFd2, I_PUSH, "ip") < 0) {
  141. // add the ip module
  142. error = "ioctl(I_PUSH)";
  143. } else if (ioctl(tunFd2, SIOCSLIFNAME, &ifr) < 0) {
  144. // set the name of the interface and specify it as ipv6
  145. error = "ioctl(SIOCSLIFNAME)";
  146. } else if (ioctl(ipFd, I_LINK, tunFd2) < 0) {
  147. // link the device to the ipv6 router
  148. error = "ioctl(I_LINK)";
  149. }
  150. if (error) {
  151. int err = errno;
  152. close(ipFd);
  153. close(tunFd2);
  154. close(tunFd);
  155. Er_raise(alloc, "%s [%s]", error, strerror(err));
  156. }
  157. close(ipFd);
  158. struct Pipe* p = Er(Pipe_forFd(tunFd, false, base, logger, alloc));
  159. struct TUNInterface_Illumos_pvt* ctx =
  160. Allocator_clone(alloc, (&(struct TUNInterface_Illumos_pvt) {
  161. .pipe = p,
  162. .externalIf = { .send = incomingFromWire },
  163. .internalIf = { .send = incomingFromUs },
  164. }));
  165. Iface_plumb(&ctx->externalIf, p);
  166. Identity_set(ctx);
  167. Er_ret(&ctx->generic);
  168. }