diff options
Diffstat (limited to 'openbsd-compat/port-net.c')
-rw-r--r-- | openbsd-compat/port-net.c | 374 |
1 files changed, 374 insertions, 0 deletions
diff --git a/openbsd-compat/port-net.c b/openbsd-compat/port-net.c new file mode 100644 index 000000000..7050629c3 --- /dev/null +++ b/openbsd-compat/port-net.c | |||
@@ -0,0 +1,374 @@ | |||
1 | /* | ||
2 | * Copyright (c) 2005 Reyk Floeter <reyk@openbsd.org> | ||
3 | * | ||
4 | * Permission to use, copy, modify, and distribute this software for any | ||
5 | * purpose with or without fee is hereby granted, provided that the above | ||
6 | * copyright notice and this permission notice appear in all copies. | ||
7 | * | ||
8 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES | ||
9 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF | ||
10 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR | ||
11 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES | ||
12 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN | ||
13 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF | ||
14 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | ||
15 | */ | ||
16 | |||
17 | #include "includes.h" | ||
18 | |||
19 | #include <sys/types.h> | ||
20 | #include <sys/ioctl.h> | ||
21 | |||
22 | #include <netinet/in.h> | ||
23 | #include <arpa/inet.h> | ||
24 | #include <netinet/ip.h> | ||
25 | |||
26 | #include <errno.h> | ||
27 | #include <fcntl.h> | ||
28 | #include <stdarg.h> | ||
29 | #include <string.h> | ||
30 | #include <unistd.h> | ||
31 | |||
32 | #include "openbsd-compat/sys-queue.h" | ||
33 | #include "log.h" | ||
34 | #include "misc.h" | ||
35 | #include "sshbuf.h" | ||
36 | #include "channels.h" | ||
37 | #include "ssherr.h" | ||
38 | |||
39 | /* | ||
40 | * This file contains various portability code for network support, | ||
41 | * including tun/tap forwarding and routing domains. | ||
42 | */ | ||
43 | |||
44 | #if defined(SYS_RDOMAIN_LINUX) || defined(SSH_TUN_LINUX) | ||
45 | #include <linux/if.h> | ||
46 | #endif | ||
47 | |||
48 | #if defined(SYS_RDOMAIN_LINUX) | ||
49 | char * | ||
50 | sys_get_rdomain(int fd) | ||
51 | { | ||
52 | char dev[IFNAMSIZ + 1]; | ||
53 | socklen_t len = sizeof(dev) - 1; | ||
54 | |||
55 | if (getsockopt(fd, SOL_SOCKET, SO_BINDTODEVICE, dev, &len) == -1) { | ||
56 | error("%s: cannot determine VRF for fd=%d : %s", | ||
57 | __func__, fd, strerror(errno)); | ||
58 | return NULL; | ||
59 | } | ||
60 | dev[len] = '\0'; | ||
61 | return strdup(dev); | ||
62 | } | ||
63 | |||
64 | int | ||
65 | sys_set_rdomain(int fd, const char *name) | ||
66 | { | ||
67 | if (setsockopt(fd, SOL_SOCKET, SO_BINDTODEVICE, | ||
68 | name, strlen(name)) == -1) { | ||
69 | error("%s: setsockopt(%d, SO_BINDTODEVICE, %s): %s", | ||
70 | __func__, fd, name, strerror(errno)); | ||
71 | return -1; | ||
72 | } | ||
73 | return 0; | ||
74 | } | ||
75 | |||
76 | int | ||
77 | sys_valid_rdomain(const char *name) | ||
78 | { | ||
79 | int fd; | ||
80 | |||
81 | /* | ||
82 | * This is a pretty crappy way to test. It would be better to | ||
83 | * check whether "name" represents a VRF device, but apparently | ||
84 | * that requires an rtnetlink transaction. | ||
85 | */ | ||
86 | if ((fd = socket(AF_INET, SOCK_STREAM, 0)) == -1) | ||
87 | return 0; | ||
88 | if (setsockopt(fd, SOL_SOCKET, SO_BINDTODEVICE, | ||
89 | name, strlen(name)) == -1) { | ||
90 | close(fd); | ||
91 | return 0; | ||
92 | } | ||
93 | close(fd); | ||
94 | return 1; | ||
95 | } | ||
96 | #elif defined(SYS_RDOMAIN_XXX) | ||
97 | /* XXX examples */ | ||
98 | char * | ||
99 | sys_get_rdomain(int fd) | ||
100 | { | ||
101 | return NULL; | ||
102 | } | ||
103 | |||
104 | int | ||
105 | sys_set_rdomain(int fd, const char *name) | ||
106 | { | ||
107 | return -1; | ||
108 | } | ||
109 | |||
110 | int | ||
111 | valid_rdomain(const char *name) | ||
112 | { | ||
113 | return 0; | ||
114 | } | ||
115 | |||
116 | void | ||
117 | sys_set_process_rdomain(const char *name) | ||
118 | { | ||
119 | fatal("%s: not supported", __func__); | ||
120 | } | ||
121 | #endif /* defined(SYS_RDOMAIN_XXX) */ | ||
122 | |||
123 | /* | ||
124 | * This is the portable version of the SSH tunnel forwarding, it | ||
125 | * uses some preprocessor definitions for various platform-specific | ||
126 | * settings. | ||
127 | * | ||
128 | * SSH_TUN_LINUX Use the (newer) Linux tun/tap device | ||
129 | * SSH_TUN_FREEBSD Use the FreeBSD tun/tap device | ||
130 | * SSH_TUN_COMPAT_AF Translate the OpenBSD address family | ||
131 | * SSH_TUN_PREPEND_AF Prepend/remove the address family | ||
132 | */ | ||
133 | |||
134 | /* | ||
135 | * System-specific tunnel open function | ||
136 | */ | ||
137 | |||
138 | #if defined(SSH_TUN_LINUX) | ||
139 | #include <linux/if_tun.h> | ||
140 | |||
141 | int | ||
142 | sys_tun_open(int tun, int mode, char **ifname) | ||
143 | { | ||
144 | struct ifreq ifr; | ||
145 | int fd = -1; | ||
146 | const char *name = NULL; | ||
147 | |||
148 | if (ifname != NULL) | ||
149 | *ifname = NULL; | ||
150 | |||
151 | if ((fd = open("/dev/net/tun", O_RDWR)) == -1) { | ||
152 | debug("%s: failed to open tunnel control interface: %s", | ||
153 | __func__, strerror(errno)); | ||
154 | return (-1); | ||
155 | } | ||
156 | |||
157 | bzero(&ifr, sizeof(ifr)); | ||
158 | |||
159 | if (mode == SSH_TUNMODE_ETHERNET) { | ||
160 | ifr.ifr_flags = IFF_TAP; | ||
161 | name = "tap%d"; | ||
162 | } else { | ||
163 | ifr.ifr_flags = IFF_TUN; | ||
164 | name = "tun%d"; | ||
165 | } | ||
166 | ifr.ifr_flags |= IFF_NO_PI; | ||
167 | |||
168 | if (tun != SSH_TUNID_ANY) { | ||
169 | if (tun > SSH_TUNID_MAX) { | ||
170 | debug("%s: invalid tunnel id %x: %s", __func__, | ||
171 | tun, strerror(errno)); | ||
172 | goto failed; | ||
173 | } | ||
174 | snprintf(ifr.ifr_name, sizeof(ifr.ifr_name), name, tun); | ||
175 | } | ||
176 | |||
177 | if (ioctl(fd, TUNSETIFF, &ifr) == -1) { | ||
178 | debug("%s: failed to configure tunnel (mode %d): %s", __func__, | ||
179 | mode, strerror(errno)); | ||
180 | goto failed; | ||
181 | } | ||
182 | |||
183 | if (tun == SSH_TUNID_ANY) | ||
184 | debug("%s: tunnel mode %d fd %d", __func__, mode, fd); | ||
185 | else | ||
186 | debug("%s: %s mode %d fd %d", __func__, ifr.ifr_name, mode, fd); | ||
187 | |||
188 | if (ifname != NULL && (*ifname = strdup(ifr.ifr_name))) | ||
189 | goto failed; | ||
190 | |||
191 | return (fd); | ||
192 | |||
193 | failed: | ||
194 | close(fd); | ||
195 | return (-1); | ||
196 | } | ||
197 | #endif /* SSH_TUN_LINUX */ | ||
198 | |||
199 | #ifdef SSH_TUN_FREEBSD | ||
200 | #include <sys/socket.h> | ||
201 | #include <net/if.h> | ||
202 | |||
203 | #ifdef HAVE_NET_IF_TUN_H | ||
204 | #include <net/if_tun.h> | ||
205 | #endif | ||
206 | |||
207 | int | ||
208 | sys_tun_open(int tun, int mode, char **ifname) | ||
209 | { | ||
210 | struct ifreq ifr; | ||
211 | char name[100]; | ||
212 | int fd = -1, sock, flag; | ||
213 | const char *tunbase = "tun"; | ||
214 | |||
215 | if (ifname != NULL) | ||
216 | *ifname = NULL; | ||
217 | |||
218 | if (mode == SSH_TUNMODE_ETHERNET) { | ||
219 | #ifdef SSH_TUN_NO_L2 | ||
220 | debug("%s: no layer 2 tunnelling support", __func__); | ||
221 | return (-1); | ||
222 | #else | ||
223 | tunbase = "tap"; | ||
224 | #endif | ||
225 | } | ||
226 | |||
227 | /* Open the tunnel device */ | ||
228 | if (tun <= SSH_TUNID_MAX) { | ||
229 | snprintf(name, sizeof(name), "/dev/%s%d", tunbase, tun); | ||
230 | fd = open(name, O_RDWR); | ||
231 | } else if (tun == SSH_TUNID_ANY) { | ||
232 | for (tun = 100; tun >= 0; tun--) { | ||
233 | snprintf(name, sizeof(name), "/dev/%s%d", | ||
234 | tunbase, tun); | ||
235 | if ((fd = open(name, O_RDWR)) >= 0) | ||
236 | break; | ||
237 | } | ||
238 | } else { | ||
239 | debug("%s: invalid tunnel %u\n", __func__, tun); | ||
240 | return (-1); | ||
241 | } | ||
242 | |||
243 | if (fd < 0) { | ||
244 | debug("%s: %s open failed: %s", __func__, name, | ||
245 | strerror(errno)); | ||
246 | return (-1); | ||
247 | } | ||
248 | |||
249 | /* Turn on tunnel headers */ | ||
250 | flag = 1; | ||
251 | #if defined(TUNSIFHEAD) && !defined(SSH_TUN_PREPEND_AF) | ||
252 | if (mode != SSH_TUNMODE_ETHERNET && | ||
253 | ioctl(fd, TUNSIFHEAD, &flag) == -1) { | ||
254 | debug("%s: ioctl(%d, TUNSIFHEAD, 1): %s", __func__, fd, | ||
255 | strerror(errno)); | ||
256 | close(fd); | ||
257 | } | ||
258 | #endif | ||
259 | |||
260 | debug("%s: %s mode %d fd %d", __func__, name, mode, fd); | ||
261 | |||
262 | /* Set the tunnel device operation mode */ | ||
263 | snprintf(ifr.ifr_name, sizeof(ifr.ifr_name), "%s%d", tunbase, tun); | ||
264 | if ((sock = socket(PF_UNIX, SOCK_STREAM, 0)) == -1) | ||
265 | goto failed; | ||
266 | |||
267 | if (ioctl(sock, SIOCGIFFLAGS, &ifr) == -1) | ||
268 | goto failed; | ||
269 | if ((ifr.ifr_flags & IFF_UP) == 0) { | ||
270 | ifr.ifr_flags |= IFF_UP; | ||
271 | if (ioctl(sock, SIOCSIFFLAGS, &ifr) == -1) | ||
272 | goto failed; | ||
273 | } | ||
274 | |||
275 | if (ifname != NULL && (*ifname = strdup(ifr.ifr_name))) | ||
276 | goto failed; | ||
277 | |||
278 | close(sock); | ||
279 | return (fd); | ||
280 | |||
281 | failed: | ||
282 | if (fd >= 0) | ||
283 | close(fd); | ||
284 | if (sock >= 0) | ||
285 | close(sock); | ||
286 | debug("%s: failed to set %s mode %d: %s", __func__, name, | ||
287 | mode, strerror(errno)); | ||
288 | return (-1); | ||
289 | } | ||
290 | #endif /* SSH_TUN_FREEBSD */ | ||
291 | |||
292 | /* | ||
293 | * System-specific channel filters | ||
294 | */ | ||
295 | |||
296 | #if defined(SSH_TUN_FILTER) | ||
297 | /* | ||
298 | * The tunnel forwarding protocol prepends the address family of forwarded | ||
299 | * IP packets using OpenBSD's numbers. | ||
300 | */ | ||
301 | #define OPENBSD_AF_INET 2 | ||
302 | #define OPENBSD_AF_INET6 24 | ||
303 | |||
304 | int | ||
305 | sys_tun_infilter(struct ssh *ssh, struct Channel *c, char *buf, int _len) | ||
306 | { | ||
307 | int r; | ||
308 | size_t len; | ||
309 | char *ptr = buf; | ||
310 | #if defined(SSH_TUN_PREPEND_AF) | ||
311 | char rbuf[CHAN_RBUF]; | ||
312 | struct ip iph; | ||
313 | #endif | ||
314 | #if defined(SSH_TUN_PREPEND_AF) || defined(SSH_TUN_COMPAT_AF) | ||
315 | u_int32_t af; | ||
316 | #endif | ||
317 | |||
318 | /* XXX update channel input filter API to use unsigned length */ | ||
319 | if (_len < 0) | ||
320 | return -1; | ||
321 | len = _len; | ||
322 | |||
323 | #if defined(SSH_TUN_PREPEND_AF) | ||
324 | if (len <= sizeof(iph) || len > sizeof(rbuf) - 4) | ||
325 | return -1; | ||
326 | /* Determine address family from packet IP header. */ | ||
327 | memcpy(&iph, buf, sizeof(iph)); | ||
328 | af = iph.ip_v == 6 ? OPENBSD_AF_INET6 : OPENBSD_AF_INET; | ||
329 | /* Prepend address family to packet using OpenBSD constants */ | ||
330 | memcpy(rbuf + 4, buf, len); | ||
331 | len += 4; | ||
332 | POKE_U32(rbuf, af); | ||
333 | ptr = rbuf; | ||
334 | #elif defined(SSH_TUN_COMPAT_AF) | ||
335 | /* Convert existing address family header to OpenBSD value */ | ||
336 | if (len <= 4) | ||
337 | return -1; | ||
338 | af = PEEK_U32(buf); | ||
339 | /* Put it back */ | ||
340 | POKE_U32(buf, af == AF_INET6 ? OPENBSD_AF_INET6 : OPENBSD_AF_INET); | ||
341 | #endif | ||
342 | |||
343 | if ((r = sshbuf_put_string(c->input, ptr, len)) != 0) | ||
344 | fatal("%s: buffer error: %s", __func__, ssh_err(r)); | ||
345 | return (0); | ||
346 | } | ||
347 | |||
348 | u_char * | ||
349 | sys_tun_outfilter(struct ssh *ssh, struct Channel *c, | ||
350 | u_char **data, size_t *dlen) | ||
351 | { | ||
352 | u_char *buf; | ||
353 | u_int32_t af; | ||
354 | int r; | ||
355 | |||
356 | /* XXX new API is incompatible with this signature. */ | ||
357 | if ((r = sshbuf_get_string(c->output, data, dlen)) != 0) | ||
358 | fatal("%s: buffer error: %s", __func__, ssh_err(r)); | ||
359 | if (*dlen < sizeof(af)) | ||
360 | return (NULL); | ||
361 | buf = *data; | ||
362 | |||
363 | #if defined(SSH_TUN_PREPEND_AF) | ||
364 | /* skip address family */ | ||
365 | *dlen -= sizeof(af); | ||
366 | buf = *data + sizeof(af); | ||
367 | #elif defined(SSH_TUN_COMPAT_AF) | ||
368 | /* translate address family */ | ||
369 | af = (PEEK_U32(buf) == OPENBSD_AF_INET6) ? AF_INET6 : AF_INET; | ||
370 | POKE_U32(buf, af); | ||
371 | #endif | ||
372 | return (buf); | ||
373 | } | ||
374 | #endif /* SSH_TUN_FILTER */ | ||