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
22 #include <errno.h>
23 #include <stdio.h>
24 #include <sys/ioctl.h>
25 #include <unistd.h>
26 #include <string.h>
27 #include <sys/socket.h>
28 #include <sys/types.h>
29 #include <stdlib.h>
30 #include <stddef.h>
31 #include <net/if.h>
32 #include <ctype.h>
33 #include <sys/stropts.h>
34 #include <sys/sockio.h>
35 #include <fcntl.h>
36 #include <net/route.h>
37
38 /**
39 * Since some illumos distributions (namely SmartOS) don't ship `net/if_tun.h`,
40 * define those IOCTL constants here.
41 */
42 #define TUNNEWPPA (('T'<<16) | 0x0001)
43 #define TUNSETPPA (('T'<<16) | 0x0002)
44
45 struct TUNInterface_Illumos_pvt
46 {
47 struct Iface internalIf;
48 struct Iface externalIf;
49 struct Pipe* const pipe;
50 Identity
51 };
52
53 /**
54 * Illumos has no concept of packet info, it only supports IPv4 and IPv6
55 * through TUN devices and it detects it by reading the version byte.
56 */
ethertypeForPacketType(uint8_t highByte)57 static uint16_t ethertypeForPacketType(uint8_t highByte)
58 {
59 return ((highByte >> 4) == 6) ? Ethernet_TYPE_IP6 : Ethernet_TYPE_IP4;
60 }
61
incomingFromWire(struct Message * message,struct Iface * externalIf)62 static Iface_DEFUN incomingFromWire(struct Message* message, struct Iface* externalIf)
63 {
64 struct TUNInterface_Illumos_pvt* ctx =
65 Identity_containerOf(externalIf, struct TUNInterface_Illumos_pvt, externalIf);
66
67 if (message->length < 4) {
68 return 0;
69 }
70
71 Er_assert(Message_eshift(message, 4));
72 ((uint16_t*) message->bytes)[0] = 0;
73 ((uint16_t*) message->bytes)[1] = ethertypeForPacketType(message->bytes[4]);
74
75 return Iface_next(&ctx->internalIf, message);
76 }
77
incomingFromUs(struct Message * message,struct Iface * internalIf)78 static Iface_DEFUN incomingFromUs(struct Message* message, struct Iface* internalIf)
79 {
80 struct TUNInterface_Illumos_pvt* ctx =
81 Identity_containerOf(internalIf, struct TUNInterface_Illumos_pvt, internalIf);
82
83 Er_assert(Message_eshift(message, -4));
84 uint16_t ethertype = ((uint16_t*) message->bytes)[-1];
85 if (ethertype != Ethernet_TYPE_IP6 && ethertype != Ethernet_TYPE_IP4) {
86 Assert_true(!"Unsupported ethertype");
87 }
88
89 return Iface_next(&ctx->externalIf, message);
90 }
91
Er_DEFUN(struct Iface * TUNInterface_new (const char * interfaceName,char assignedInterfaceName[TUNInterface_IFNAMSIZ],int isTapMode,struct EventBase * base,struct Log * logger,struct Allocator * alloc))92 Er_DEFUN(struct Iface* TUNInterface_new(const char* interfaceName,
93 char assignedInterfaceName[TUNInterface_IFNAMSIZ],
94 int isTapMode,
95 struct EventBase* base,
96 struct Log* logger,
97 struct Allocator* alloc))
98 {
99 // tap mode is not supported at all by the sunos tun driver.
100 if (isTapMode) { Er_raise(alloc, "tap mode not supported on this platform"); }
101
102 // Extract the number eg: 0 from tun0
103 int ppa = 0;
104 if (interfaceName) {
105 for (uint32_t i = 0; i < strlen(interfaceName); i++) {
106 if (isdigit(interfaceName[i])) {
107 ppa = atoi(interfaceName);
108 }
109 }
110 }
111
112 // Open the descriptor
113 int tunFd = open("/dev/tun", O_RDWR);
114
115 // Either the name is specified and we use TUNSETPPA,
116 // or it's not specified and we just want a TUNNEWPPA
117 if (ppa) {
118 ppa = ioctl(tunFd, TUNSETPPA, ppa);
119 } else {
120 ppa = ioctl(tunFd, TUNNEWPPA, -1);
121 }
122
123 int ipFd = open("/dev/ip6", O_RDWR, 0);
124 int tunFd2 = open("/dev/tun", O_RDWR, 0);
125
126 if (tunFd < 0 || ipFd < 0 || ppa < 0 || tunFd2 < 0) {
127 int err = errno;
128 close(tunFd);
129 close(ipFd);
130 close(tunFd2);
131
132 char* error = NULL;
133 if (tunFd < 0) {
134 error = "open(\"/dev/tun\")";
135 } else if (ipFd < 0) {
136 error = "open(\"/dev/ip6\")";
137 } else if (ppa < 0) {
138 error = "ioctl(TUNNEWPPA)";
139 } else if (tunFd2 < 0) {
140 error = "open(\"/dev/tun\") (opening for plumbing interface)";
141 }
142 Er_raise(alloc, "%s [%s]", error, strerror(err));
143 }
144
145 struct lifreq ifr = {
146 .lifr_ppa = ppa,
147 .lifr_flags = IFF_IPV6
148 };
149
150 // Since devices are numbered rather than named, it's not possible to have tun0 and cjdns0
151 // so we'll skip the pretty names and call everything tunX
152 int maxNameSize = (LIFNAMSIZ < TUNInterface_IFNAMSIZ) ? LIFNAMSIZ : TUNInterface_IFNAMSIZ;
153 if (assignedInterfaceName) {
154 snprintf(assignedInterfaceName, maxNameSize, "tun%d", ppa);
155 }
156 snprintf(ifr.lifr_name, maxNameSize, "tun%d", ppa);
157
158 char* error = NULL;
159
160 if (ioctl(tunFd, I_SRDOPT, RMSGD) < 0) {
161 error = "putting tun into message-discard mode";
162
163 } else if (ioctl(tunFd2, I_PUSH, "ip") < 0) {
164 // add the ip module
165 error = "ioctl(I_PUSH)";
166
167 } else if (ioctl(tunFd2, SIOCSLIFNAME, &ifr) < 0) {
168 // set the name of the interface and specify it as ipv6
169 error = "ioctl(SIOCSLIFNAME)";
170
171 } else if (ioctl(ipFd, I_LINK, tunFd2) < 0) {
172 // link the device to the ipv6 router
173 error = "ioctl(I_LINK)";
174 }
175
176 if (error) {
177 int err = errno;
178 close(ipFd);
179 close(tunFd2);
180 close(tunFd);
181 Er_raise(alloc, "%s [%s]", error, strerror(err));
182 }
183
184 close(ipFd);
185
186 struct Pipe* p = Er(Pipe_forFd(tunFd, false, base, logger, alloc));
187
188 struct TUNInterface_Illumos_pvt* ctx =
189 Allocator_clone(alloc, (&(struct TUNInterface_Illumos_pvt) {
190 .pipe = p,
191 .externalIf = { .send = incomingFromWire },
192 .internalIf = { .send = incomingFromUs },
193 }));
194 Iface_plumb(&ctx->externalIf, p);
195 Identity_set(ctx);
196
197 Er_ret(&ctx->generic);
198 }
199