AF_IPN is no longer protocol #34 (assigned to AF_ISDN).
[vde.git] / vde-2 / src / vde_plug.c
blob35c8272a067e292dd90541120a99fc49ede576c6
1 /* Copyright 2002 Renzo Davoli
2 * Licensed under the GPL
3 * Modified by Ludovico Gardenghi 2005
4 */
6 #include <stdio.h>
7 #include <stdlib.h>
8 #include <signal.h>
9 #include <errno.h>
10 #include <unistd.h>
11 #include <stdint.h>
12 #include <getopt.h>
13 #include <string.h>
14 #include <sys/ioctl.h>
15 #include <sys/socket.h>
16 #include <sys/un.h>
17 #include <sys/uio.h>
18 #include <sys/utsname.h>
19 #include <sys/types.h>
20 #include <sys/stat.h>
21 #include <pwd.h>
22 #include <grp.h>
24 #include <config.h>
25 #include <vde.h>
26 #include <vdecommon.h>
27 #include <libvdeplug.h>
29 #ifdef VDE_IP_LOG
30 #define DO_SYSLOG
31 #endif
32 #ifdef DO_SYSLOG
33 #include <syslog.h>
34 #include <ctype.h>
35 #include <arpa/inet.h>
36 #endif
38 #ifndef MIN
39 #define MIN(X,Y) (((X)<(Y))?(X):(Y))
40 #endif
42 #define BUFSIZE 2048
43 #define ETH_ALEN 6
45 VDECONN *conn;
47 struct utsname me;
48 #define myname me.nodename
50 static struct passwd *callerpwd;
51 #ifdef DO_SYSLOG
52 static char host[256];
54 void write_syslog_entry(char *message)
56 char *ssh_client;
57 size_t ip_length;
59 openlog("vde_plug", 0, LOG_USER);
61 //get the caller IP address
62 //TNX Giordani-Macchia code from vish.c
63 if ((ssh_client=getenv("SSH_CLIENT"))!=NULL)
65 for (ip_length=0;ip_length<sizeof(host)&&ssh_client[ip_length]!=0&&!isspace(ssh_client[ip_length]);ip_length++);
66 if (ip_length>=sizeof(host))
67 ip_length=sizeof(host)-1;
68 memcpy(host,ssh_client,ip_length);
69 host[ip_length]=0;
71 else
72 strcpy(host,"UNKNOWN_IP_ADDRESS");
73 syslog(LOG_INFO,"%s: user %s IP %s",message,callerpwd->pw_name,host);
74 closelog();
77 void write_syslog_close()
79 write_syslog_entry("STOP");
81 #endif
83 #ifdef VDE_IP_LOG
84 #define MAX_IP 256
85 int vde_ip_log;
87 struct header {
88 unsigned char dest[ETH_ALEN];
89 unsigned char src[ETH_ALEN];
90 unsigned char proto[2];
93 union body {
94 struct {
95 unsigned char version;
96 unsigned char filler[11];
97 unsigned char ip4src[4];
98 unsigned char ip4dst[4];
99 } v4;
100 struct {
101 unsigned char version;
102 unsigned char filler[7];
103 unsigned char ip6src[16];
104 unsigned char ip6dst[16];
105 } v6;
106 struct {
107 unsigned char priovlan[2];
108 } vlan;
111 unsigned char ip4list[MAX_IP][4];
112 unsigned char ip6list[MAX_IP][16];
113 static unsigned char nulladdr[16];
115 static int hash4(unsigned char *addr)
117 return((addr[0]+2*addr[1]+3*addr[2]+5*addr[3]) % MAX_IP);
120 static int hash6(unsigned char *addr)
122 return((addr[0]+2*addr[1]+3*addr[2]+5*addr[3]+
123 7*addr[4]+11*addr[5]+13*addr[6]+17*addr[7]+
124 19*addr[8]+23*addr[9]+29*addr[10]+31*addr[11]+
125 37*addr[12]+41*addr[13]+43*addr[14]+47*addr[15]) % MAX_IP);
128 static void vde_ip_check(const unsigned char *buf,int rnx)
130 struct header *ph=(struct header *) buf;
131 register int i,j,vlan=0;
132 char addr[256];
133 union body *pb;
135 pb=(union body *)(ph+1);
136 if (ph->proto[0]==0x81 && ph->proto[1]==0x00) { /*VLAN*/
137 vlan=((pb->vlan.priovlan[0] << 8) + pb->vlan.priovlan[1]) & 0xfff;
138 pb=(union body *)(((char *)pb)+4);
140 if (ph->proto[0]==0x08 && ph->proto[1]==0x00 &&
141 pb->v4.version == 0x45) {
142 /*v4 */
143 i=hash4(pb->v4.ip4src);
144 j=(i+MAX_IP-1)%MAX_IP;
145 while (1) {
146 /* most frequent case first */
147 if (memcmp(pb->v4.ip4src,ip4list[i],4) == 0)
148 break;
149 else if (memcmp(ip4list[i],nulladdr,4) == 0) {
150 memcpy(ip4list[i],pb->v4.ip4src,4);
151 syslog(LOG_INFO,"user %s Real-IP %s has got VDE-IP4 %s on vlan %d",callerpwd->pw_name,host,inet_ntop(AF_INET,ip4list[i],addr,256),vlan);
152 /*new ipv4*/
153 break;
154 } else if (i==j) {
155 syslog(LOG_ERR,"IPv4 table full. Exiting\n");
156 /*full table*/
157 exit(-1);
158 } else
159 i= (i+1)%MAX_IP;
162 else if (ph->proto[0]==0x86 && ph->proto[1]==0xdd &&
163 pb->v4.version == 0x60) {
164 /* v6 */
165 i=hash6(pb->v6.ip6src);
166 j=(i+MAX_IP-1)%MAX_IP;
167 while (1) {
168 /* most frequent case first */
169 if (memcmp(pb->v6.ip6src,ip6list[i],16) == 0)
170 break;
171 else if (memcmp(ip6list[i],nulladdr,16) == 0) {
172 memcpy(ip6list[i],pb->v6.ip6src,16);
173 syslog(LOG_INFO,"user %s Real-IP %s has got VDE-IP6 %s on vlan %d",callerpwd->pw_name,host,inet_ntop(AF_INET6,ip6list[i],addr,256),vlan);
174 /*new ipv6*/
175 break;
176 } else if (i==j) {
177 syslog(LOG_ERR,"IPv6 table full. Exiting\n");
178 /*full table*/
179 exit(-1);
180 } else
181 i= (i+1)%MAX_IP;
185 #endif
187 unsigned char bufin[BUFSIZE];
189 void splitpacket(const unsigned char *buf,int size,VDECONN *conn)
191 static char fragment[BUFSIZE];
192 static char *fragp;
193 static unsigned int rnx,remaining;
195 //fprintf(stderr,"%s: splitpacket rnx=%d remaining=%d size=%d\n",myname,rnx,remaining,size);
196 if (size==0) return;
197 if (rnx>0) {
198 register int amount=MIN(remaining,size);
199 //fprintf(stderr,"%s: fragment amount %d\n",myname,amount);
200 memcpy(fragp,buf,amount);
201 remaining-=amount;
202 fragp+=amount;
203 buf+=amount;
204 size-=amount;
205 if (remaining==0) {
206 //fprintf(stderr,"%s: delivered defrag %d\n",myname,rnx);
207 //send(fd,fragment,rnx,0);
208 #ifdef VDE_IP_LOG
209 if (vde_ip_log)
210 vde_ip_check(buf,rnx);
211 #endif
212 vde_send(conn,fragment,rnx,0);
213 rnx=0;
216 while (size > 0) {
217 rnx=(buf[0]<<8)+buf[1];
218 size-=2;
219 //fprintf(stderr,"%s %d: packet %d size %d %x %x\n",myname,getpid(),rnx,size,buf[0],buf[1]);
220 buf+=2;
221 if (rnx>1521) {
222 fprintf(stderr,"%s: Packet length error size %d rnx %d\n",myname,size,rnx);
223 rnx=0;
224 return;
226 if (rnx > size) {
227 //fprintf(stderr,"%s: begin defrag %d\n",myname,rnx);
228 fragp=fragment;
229 memcpy(fragp,buf,size);
230 remaining=rnx-size;
231 fragp+=size;
232 size=0;
233 } else {
234 //fprintf(stderr,"%s: deliver %d\n",myname,rnx);
235 //send(fd,buf,rnx,0);
236 #ifdef VDE_IP_LOG
237 if (vde_ip_log)
238 vde_ip_check(buf,rnx);
239 #endif
240 vde_send(conn,(char *)buf,rnx,0);
241 buf+=rnx;
242 size-=rnx;
243 rnx=0;
248 static void cleanup(void)
250 vde_close(conn);
253 static void sig_handler(int sig)
255 cleanup();
256 signal(sig, SIG_DFL);
257 kill(getpid(), sig);
260 static void setsighandlers()
262 /* setting signal handlers.
263 * sets clean termination for SIGHUP, SIGINT and SIGTERM, and simply
264 * ignores all the others signals which could cause termination. */
265 struct { int sig; const char *name; int ignore; } signals[] = {
266 { SIGHUP, "SIGHUP", 0 },
267 { SIGINT, "SIGINT", 0 },
268 { SIGPIPE, "SIGPIPE", 1 },
269 { SIGALRM, "SIGALRM", 1 },
270 { SIGTERM, "SIGTERM", 0 },
271 { SIGUSR1, "SIGUSR1", 1 },
272 { SIGUSR2, "SIGUSR2", 1 },
273 { SIGPROF, "SIGPROF", 1 },
274 { SIGVTALRM, "SIGVTALRM", 1 },
275 #ifdef VDE_LINUX
276 { SIGPOLL, "SIGPOLL", 1 },
277 #ifdef SIGSTKFLT
278 { SIGSTKFLT, "SIGSTKFLT", 1 },
279 #endif
280 { SIGIO, "SIGIO", 1 },
281 { SIGPWR, "SIGPWR", 1 },
282 #ifdef SIGUNUSED
283 { SIGUNUSED, "SIGUNUSED", 1 },
284 #endif
285 #endif
286 #ifdef VDE_DARWIN
287 { SIGXCPU, "SIGXCPU", 1 },
288 { SIGXFSZ, "SIGXFSZ", 1 },
289 #endif
290 { 0, NULL, 0 }
293 int i;
294 for(i = 0; signals[i].sig != 0; i++)
295 if(signal(signals[i].sig,
296 signals[i].ignore ? SIG_IGN : sig_handler) < 0)
297 perror("Setting handler");
300 struct pollfd pollv[]={{STDIN_FILENO,POLLIN|POLLHUP},{0,POLLIN|POLLHUP},{0,POLLIN|POLLHUP}};
302 static void netusage() {
303 #ifdef DO_SYSLOG
304 write_syslog_entry("FAILED");
305 #endif
306 fprintf (stderr,"This is a Virtual Distributed Ethernet (vde) tunnel broker. \n"
307 "This is not a login shell, only vde_plug can be executed\n");
308 exit(-1);
311 static void usage(char *progname) {
312 fprintf (stderr,"Usage: %s [-p portnum] [-g group] [-m mod] socketname\n\n",progname);
313 exit(-1);
316 int main(int argc, char **argv)
318 static char *sockname=NULL;
319 int result;
320 register ssize_t nx;
321 struct vde_open_args open_args={.port=0,.group=NULL,.mode=0700};
323 uname(&me);
324 //get the login name
325 callerpwd=getpwuid(getuid());
327 if (argv[0][0] == '-')
328 netusage(); //implies exit
329 /* option parsing */
331 int c;
332 while (1) {
333 int option_index = 0;
335 static struct option long_options[] = {
336 {"sock", 1, 0, 's'},
337 {"vdesock", 1, 0, 's'},
338 {"unix", 1, 0, 's'},
339 {"port", 1, 0, 'p'},
340 {"help",0,0,'h'},
341 {"mod",1,0,'m'},
342 {"group",1,0,'g'},
343 {0, 0, 0, 0}
345 c = GETOPT_LONG (argc, argv, "hc:p:s:m:g:l",
346 long_options, &option_index);
347 if (c == -1)
348 break;
350 switch (c) {
351 case 'c':
352 if (strcmp(optarg,"vde_plug")==0) {
353 #ifdef DO_SYSLOG
354 write_syslog_entry("START");
355 atexit(write_syslog_close);
356 #ifdef VDE_IP_LOG
357 vde_ip_log=1;
358 #endif
359 #endif
362 else
363 netusage(); //implies exit
364 break;
366 case 'p':
367 open_args.port=atoi(optarg);
368 if (open_args.port <= 0)
369 usage(argv[0]); //implies exit
370 break;
372 case 'h':
373 usage(argv[0]); //implies exit
374 break;
376 case 's':
377 sockname=strdup(optarg);
378 break;
380 case 'm':
381 sscanf(optarg,"%o",&(open_args.mode));
382 break;
384 case 'g':
385 open_args.group=strdup(optarg);
386 break;
388 case 'l':
389 #ifdef VDE_IP_LOG
390 write_syslog_entry("START");
391 atexit(write_syslog_close);
392 vde_ip_log=1;
393 break;
394 #endif
396 default:
397 usage(argv[0]); //implies exit
401 if (optind < argc && sockname==NULL)
402 sockname=argv[optind];
404 atexit(cleanup);
405 setsighandlers();
406 conn=vde_open(sockname,"vde_plug:",&open_args);
407 if (conn == NULL)
408 exit(1);
410 pollv[1].fd=vde_datafd(conn);
411 pollv[2].fd=vde_ctlfd(conn);
413 for(;;) {
414 result=poll(pollv,3,-1);
415 if ((pollv[0].revents | pollv[1].revents | pollv[2].revents) & POLLHUP ||
416 pollv[2].revents & POLLIN)
417 break;
418 if (pollv[0].revents & POLLIN) {
419 nx=read(STDIN_FILENO,bufin,sizeof(bufin));
420 /* if POLLIN but not data it means that the stream has been
421 * closed at the other end */
422 /*fprintf(stderr,"%s: RECV %d %x %x \n",myname,nx,bufin[0],bufin[1]);*/
423 if (nx==0)
424 break;
425 splitpacket(bufin,nx,conn);
427 if (pollv[1].revents & POLLIN) {
428 nx=vde_recv(conn,(char *)(bufin+2),BUFSIZE-2,0);
429 if (nx<0)
430 perror("vde_plug: recvfrom ");
431 else
433 bufin[0]=nx >> 8;
434 bufin[1]=nx & 0xff;
435 write(STDOUT_FILENO,bufin,nx+2);
436 /*fprintf(stderr,"%s: SENT %d %x %x \n",myname,nx,bufin[0],bufin[1]);*/
441 return(0);