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