1 /*
2 VTun - Virtual Tunnel over TCP/IP network.
3
4 Copyright (C) 1998-2016 Maxim Krasnyansky <max_mk@yahoo.com>
5
6 VTun has been derived from VPPP package by Maxim Krasnyansky.
7
8 This program is free software; you can redistribute it and/or modify
9 it under the terms of the GNU General Public License as published by
10 the Free Software Foundation; either version 2 of the License, or
11 (at your option) any later version.
12
13 This program is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 GNU General Public License for more details.
17 */
18
19 /*
20 * $Id: tunnel.c,v 1.14.2.4 2016/10/01 21:27:51 mtbishop Exp $
21 */
22
23 #include "config.h"
24
25 #include <unistd.h>
26 #include <fcntl.h>
27 #include <stdlib.h>
28 #include <stdio.h>
29 #include <string.h>
30 #include <ctype.h>
31 #include <stdarg.h>
32 #include <sys/time.h>
33 #include <sys/wait.h>
34 #include <syslog.h>
35 #include <signal.h>
36
37 #ifdef HAVE_NETINET_IN_H
38 #include <netinet/in.h>
39 #endif
40
41 #ifdef HAVE_NETINET_IN_SYSTM_H
42 #include <netinet/in_systm.h>
43 #endif
44
45 #ifdef HAVE_NETINET_IP_H
46 #include <netinet/ip.h>
47 #endif
48
49 #ifdef HAVE_NETINET_TCP_H
50 #include <netinet/tcp.h>
51 #endif
52
53 #include "vtun.h"
54 #include "linkfd.h"
55 #include "lib.h"
56 #include "netlib.h"
57 #include "driver.h"
58
59 int (*dev_write)(int fd, char *buf, int len);
60 int (*dev_read)(int fd, char *buf, int len);
61
62 int (*proto_write)(int fd, char *buf, int len);
63 int (*proto_read)(int fd, char *buf);
64
65 /* Initialize and start the tunnel.
66 Returns:
67 -1 - critical error
68 0 - normal close or noncritical error
69 */
70
tunnel(struct vtun_host * host)71 int tunnel(struct vtun_host *host)
72 {
73 int null_fd, pid, opt;
74 int fd[2]={-1, -1};
75 char dev[VTUN_DEV_LEN]="";
76 int interface_already_open = 0;
77
78 if ( (host->persist == VTUN_PERSIST_KEEPIF) &&
79 (host->loc_fd >= 0) )
80 interface_already_open = 1;
81
82 /* Initialize device. */
83 if( host->dev ){
84 strncpy(dev, host->dev, VTUN_DEV_LEN);
85 dev[VTUN_DEV_LEN-1]='\0';
86 }
87 if( ! interface_already_open ){
88 switch( host->flags & VTUN_TYPE_MASK ){
89 case VTUN_TTY:
90 if( (fd[0]=pty_open(dev)) < 0 ){
91 vtun_syslog(LOG_ERR,"Can't allocate pseudo tty. %s(%d)", strerror(errno), errno);
92 return -1;
93 }
94 break;
95
96 case VTUN_PIPE:
97 if( pipe_open(fd) < 0 ){
98 vtun_syslog(LOG_ERR,"Can't create pipe. %s(%d)", strerror(errno), errno);
99 return -1;
100 }
101 break;
102
103 case VTUN_ETHER:
104 if( (fd[0]=tap_open(dev)) < 0 ){
105 vtun_syslog(LOG_ERR,"Can't allocate tap device %s. %s(%d)", dev, strerror(errno), errno);
106 return -1;
107 }
108 break;
109
110 case VTUN_TUN:
111 if( (fd[0]=tun_open(dev)) < 0 ){
112 vtun_syslog(LOG_ERR,"Can't allocate tun device %s. %s(%d)", dev, strerror(errno), errno);
113 return -1;
114 }
115 break;
116 }
117 host->loc_fd = fd[0];
118 }
119 host->sopt.dev = strdup(dev);
120
121 /* Initialize protocol. */
122 switch( host->flags & VTUN_PROT_MASK ){
123 case VTUN_TCP:
124 opt=1;
125 setsockopt(host->rmt_fd,SOL_SOCKET,SO_KEEPALIVE,&opt,sizeof(opt) );
126
127 opt=1;
128 setsockopt(host->rmt_fd,IPPROTO_TCP,TCP_NODELAY,&opt,sizeof(opt) );
129
130 proto_write = tcp_write;
131 proto_read = tcp_read;
132
133 break;
134
135 case VTUN_UDP:
136 if( (opt = udp_session(host)) == -1){
137 vtun_syslog(LOG_ERR,"Can't establish UDP session");
138 close(fd[1]);
139 if( ! ( host->persist == VTUN_PERSIST_KEEPIF ) )
140 close(fd[0]);
141 return 0;
142 }
143
144 proto_write = udp_write;
145 proto_read = udp_read;
146
147 break;
148 }
149
150 #ifdef HAVE_WORKING_FORK
151 switch( (pid=fork()) ){
152 case -1:
153 vtun_syslog(LOG_ERR,"Couldn't fork()");
154 if( ! ( host->persist == VTUN_PERSIST_KEEPIF ) )
155 close(fd[0]);
156 close(fd[1]);
157 return 0;
158 case 0:
159 /* do this only the first time when in persist = keep mode */
160 if( ! interface_already_open ){
161 switch( host->flags & VTUN_TYPE_MASK ){
162 case VTUN_TTY:
163 /* Open pty slave (becomes controlling terminal) */
164 if( (fd[1] = open(dev, O_RDWR)) < 0){
165 vtun_syslog(LOG_ERR,"Couldn't open slave pty");
166 exit(0);
167 }
168 /* Fall through */
169 case VTUN_PIPE:
170 null_fd = open("/dev/null", O_RDWR);
171 close(fd[0]);
172 close(0); dup(fd[1]);
173 close(1); dup(fd[1]);
174 close(fd[1]);
175
176 /* Route stderr to /dev/null */
177 close(2); dup(null_fd);
178 close(null_fd);
179 break;
180 case VTUN_ETHER:
181 case VTUN_TUN:
182 break;
183 }
184 }
185 /* Run list of up commands */
186 set_title("%s running up commands", host->host);
187 llist_trav(&host->up, run_cmd, &host->sopt);
188
189 exit(0);
190 }
191 #else
192 vtun_syslog(LOG_ERR,"Couldn't run up commands: fork() not available");
193 #endif
194
195 switch( host->flags & VTUN_TYPE_MASK ){
196 case VTUN_TTY:
197 set_title("%s tty", host->host);
198
199 dev_read = pty_read;
200 dev_write = pty_write;
201 break;
202
203 case VTUN_PIPE:
204 /* Close second end of the pipe */
205 close(fd[1]);
206 set_title("%s pipe", host->host);
207
208 dev_read = pipe_read;
209 dev_write = pipe_write;
210 break;
211
212 case VTUN_ETHER:
213 set_title("%s ether %s", host->host, dev);
214
215 dev_read = tap_read;
216 dev_write = tap_write;
217 break;
218
219 case VTUN_TUN:
220 set_title("%s tun %s", host->host, dev);
221
222 dev_read = tun_read;
223 dev_write = tun_write;
224 break;
225 }
226
227 opt = linkfd(host);
228
229 #ifdef HAVE_WORKING_FORK
230 set_title("%s running down commands", host->host);
231 llist_trav(&host->down, run_cmd, &host->sopt);
232 #else
233 vtun_syslog(LOG_ERR,"Couldn't run down commands: fork() not available");
234 #endif
235
236 if(! ( host->persist == VTUN_PERSIST_KEEPIF ) ) {
237 set_title("%s closing", host->host);
238
239 /* Gracefully destroy interface */
240 switch( host->flags & VTUN_TYPE_MASK ){
241 case VTUN_TUN:
242 tun_close(fd[0], dev);
243 break;
244
245 case VTUN_ETHER:
246 tap_close(fd[0], dev);
247 break;
248 }
249
250 close(host->loc_fd);
251 }
252
253 /* Close all other fds */
254 close(host->rmt_fd);
255 close(fd[1]);
256
257 return opt;
258 }
259