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