In 2014, I think we can stop trying to outsmart the compiler. Remove
[vde.git] / vde-2 / src / vde_plug.c
blob56014f1a7d82bd78c11dbdcec7112eefea5dd2ff
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>
23 #include <stdarg.h>
25 #include <config.h>
26 #include <vde.h>
27 #include <vdecommon.h>
28 #include <libvdeplug.h>
30 #ifdef VDE_IP_LOG
31 #define DO_SYSLOG
32 #endif
33 #ifdef DO_SYSLOG
34 #include <syslog.h>
35 #include <ctype.h>
36 #include <arpa/inet.h>
37 #endif
39 #ifndef MIN
40 #define MIN(X,Y) (((X)<(Y))?(X):(Y))
41 #endif
43 #define BUFSIZE 4096
44 #define ETH_ALEN 6
46 VDECONN *conn;
47 VDESTREAM *vdestream;
49 struct utsname me;
50 #define myname me.nodename
52 static struct passwd *callerpwd;
53 #ifdef DO_SYSLOG
54 static char host[256];
56 void write_syslog_entry(char *message)
58 char *ssh_client;
59 size_t ip_length;
61 openlog("vde_plug", 0, LOG_USER);
63 //get the caller IP address
64 //TNX Giordani-Macchia code from vish.c
65 if ((ssh_client=getenv("SSH_CLIENT"))!=NULL)
67 for (ip_length=0;ip_length<sizeof(host)&&ssh_client[ip_length]!=0&&!isspace(ssh_client[ip_length]);ip_length++);
68 if (ip_length>=sizeof(host))
69 ip_length=sizeof(host)-1;
70 memcpy(host,ssh_client,ip_length);
71 host[ip_length]=0;
73 else
74 strcpy(host,"UNKNOWN_IP_ADDRESS");
75 syslog(LOG_INFO,"%s: user %s IP %s",message,callerpwd->pw_name,host);
76 closelog();
79 void write_syslog_close()
81 write_syslog_entry("STOP");
83 #endif
85 #ifdef VDE_IP_LOG
86 #define MAX_IP 256
87 int vde_ip_log;
89 struct header {
90 unsigned char dest[ETH_ALEN];
91 unsigned char src[ETH_ALEN];
92 unsigned char proto[2];
95 union body {
96 struct {
97 unsigned char version;
98 unsigned char filler[11];
99 unsigned char ip4src[4];
100 unsigned char ip4dst[4];
101 } v4;
102 struct {
103 unsigned char version;
104 unsigned char filler[7];
105 unsigned char ip6src[16];
106 unsigned char ip6dst[16];
107 } v6;
108 struct {
109 unsigned char priovlan[2];
110 } vlan;
113 unsigned char ip4list[MAX_IP][4];
114 unsigned char ip6list[MAX_IP][16];
115 static unsigned char nulladdr[16];
117 static int hash4(unsigned char *addr)
119 return((addr[0]+2*addr[1]+3*addr[2]+5*addr[3]) % MAX_IP);
122 static int hash6(unsigned char *addr)
124 return((addr[0]+2*addr[1]+3*addr[2]+5*addr[3]+
125 7*addr[4]+11*addr[5]+13*addr[6]+17*addr[7]+
126 19*addr[8]+23*addr[9]+29*addr[10]+31*addr[11]+
127 37*addr[12]+41*addr[13]+43*addr[14]+47*addr[15]) % MAX_IP);
130 static void vde_ip_check(const unsigned char *buf,int rnx)
132 struct header *ph=(struct header *) buf;
133 int i,j,vlan=0;
134 char addr[256];
135 union body *pb;
137 pb=(union body *)(ph+1);
138 if (ph->proto[0]==0x81 && ph->proto[1]==0x00) { /*VLAN*/
139 vlan=((pb->vlan.priovlan[0] << 8) + pb->vlan.priovlan[1]) & 0xfff;
140 pb=(union body *)(((char *)pb)+4);
142 if (ph->proto[0]==0x08 && ph->proto[1]==0x00 &&
143 pb->v4.version == 0x45) {
144 /*v4 */
145 i=hash4(pb->v4.ip4src);
146 j=(i+MAX_IP-1)%MAX_IP;
147 while (1) {
148 /* most frequent case first */
149 if (memcmp(pb->v4.ip4src,ip4list[i],4) == 0)
150 break;
151 else if (memcmp(ip4list[i],nulladdr,4) == 0) {
152 memcpy(ip4list[i],pb->v4.ip4src,4);
153 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);
154 /*new ipv4*/
155 break;
156 } else if (i==j) {
157 syslog(LOG_ERR,"IPv4 table full. Exiting\n");
158 /*full table*/
159 exit(-1);
160 } else
161 i= (i+1)%MAX_IP;
164 else if (ph->proto[0]==0x86 && ph->proto[1]==0xdd &&
165 pb->v4.version == 0x60) {
166 /* v6 */
167 i=hash6(pb->v6.ip6src);
168 j=(i+MAX_IP-1)%MAX_IP;
169 while (1) {
170 /* most frequent case first */
171 if (memcmp(pb->v6.ip6src,ip6list[i],16) == 0)
172 break;
173 else if (memcmp(ip6list[i],nulladdr,16) == 0) {
174 memcpy(ip6list[i],pb->v6.ip6src,16);
175 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);
176 /*new ipv6*/
177 break;
178 } else if (i==j) {
179 syslog(LOG_ERR,"IPv6 table full. Exiting\n");
180 /*full table*/
181 exit(-1);
182 } else
183 i= (i+1)%MAX_IP;
187 #endif
189 void vdeplug_err(void *opaque, int type, char *format,...)
191 va_list args;
193 if (isatty(STDERR_FILENO)) {
194 fprintf(stderr, "%s: Packet length error",myname);
195 va_start(args, format);
196 vfprintf(stderr, format, args);
197 va_end(args);
198 fprintf(stderr,"\n");
202 ssize_t vdeplug_recv(void *opaque, void *buf, size_t count)
204 VDECONN *conn=opaque;
205 #ifdef VDE_IP_LOG
206 if (vde_ip_log)
207 vde_ip_check(buf,count);
208 #endif
209 return vde_send(conn,(char *)buf,count,0);
212 static void cleanup(void)
214 vdestream_close(vdestream);
215 vde_close(conn);
218 static void sig_handler(int sig)
220 cleanup();
221 signal(sig, SIG_DFL);
222 if (sig == SIGTERM)
223 _exit(0);
224 else
225 kill(getpid(), sig);
228 static void setsighandlers()
230 /* setting signal handlers.
231 * sets clean termination for SIGHUP, SIGINT and SIGTERM, and simply
232 * ignores all the others signals which could cause termination. */
233 struct { int sig; const char *name; int ignore; } signals[] = {
234 { SIGHUP, "SIGHUP", 0 },
235 { SIGINT, "SIGINT", 0 },
236 { SIGPIPE, "SIGPIPE", 1 },
237 { SIGALRM, "SIGALRM", 1 },
238 { SIGTERM, "SIGTERM", 0 },
239 { SIGUSR1, "SIGUSR1", 1 },
240 { SIGUSR2, "SIGUSR2", 1 },
241 { SIGPROF, "SIGPROF", 1 },
242 { SIGVTALRM, "SIGVTALRM", 1 },
243 #ifdef VDE_LINUX
244 { SIGPOLL, "SIGPOLL", 1 },
245 #ifdef SIGSTKFLT
246 { SIGSTKFLT, "SIGSTKFLT", 1 },
247 #endif
248 { SIGIO, "SIGIO", 1 },
249 { SIGPWR, "SIGPWR", 1 },
250 #ifdef SIGUNUSED
251 { SIGUNUSED, "SIGUNUSED", 1 },
252 #endif
253 #endif
254 #ifdef VDE_DARWIN
255 { SIGXCPU, "SIGXCPU", 1 },
256 { SIGXFSZ, "SIGXFSZ", 1 },
257 #endif
258 { 0, NULL, 0 }
261 int i;
262 for(i = 0; signals[i].sig != 0; i++)
263 if(signal(signals[i].sig,
264 signals[i].ignore ? SIG_IGN : sig_handler) < 0)
265 perror("Setting handler");
268 struct pollfd pollv[]={{STDIN_FILENO,POLLIN|POLLHUP},{0,POLLIN|POLLHUP},{0,POLLIN|POLLHUP}};
270 static void netusage() {
271 #ifdef DO_SYSLOG
272 write_syslog_entry("FAILED");
273 #endif
274 fprintf (stderr,"This is a Virtual Distributed Ethernet (vde) tunnel broker. \n"
275 "This is not a login shell, only vde_plug can be executed\n");
276 exit(-1);
279 static void usage(char *progname) {
280 fprintf (stderr,"Usage: %s [-p portnum] [-g group] [-m mod] socketname\n\n",progname);
281 exit(-1);
284 unsigned char bufin[BUFSIZE];
286 int main(int argc, char **argv)
288 static char *sockname=NULL;
289 ssize_t nx;
290 struct vde_open_args open_args={.port=0,.group=NULL,.mode=0700};
292 uname(&me);
293 //get the login name
294 callerpwd=getpwuid(getuid());
296 if (argv[0][0] == '-')
297 netusage(); //implies exit
298 /* option parsing */
300 int c;
301 while (1) {
302 int option_index = 0;
304 static struct option long_options[] = {
305 {"sock", 1, 0, 's'},
306 {"vdesock", 1, 0, 's'},
307 {"unix", 1, 0, 's'},
308 {"port", 1, 0, 'p'},
309 {"help",0,0,'h'},
310 {"mod",1,0,'m'},
311 {"group",1,0,'g'},
312 {0, 0, 0, 0}
314 c = GETOPT_LONG (argc, argv, "hc:p:s:m:g:l",
315 long_options, &option_index);
316 if (c == -1)
317 break;
319 switch (c) {
320 case 'c':
321 if (strcmp(optarg,"vde_plug")==0) {
322 #ifdef DO_SYSLOG
323 write_syslog_entry("START");
324 atexit(write_syslog_close);
325 #ifdef VDE_IP_LOG
326 vde_ip_log=1;
327 #endif
328 #endif
331 else
332 netusage(); //implies exit
333 break;
335 case 'p':
336 open_args.port=atoi(optarg);
337 if (open_args.port <= 0)
338 usage(argv[0]); //implies exit
339 break;
341 case 'h':
342 usage(argv[0]); //implies exit
343 break;
345 case 's':
346 sockname=strdup(optarg);
347 break;
349 case 'm':
350 sscanf(optarg,"%o",(unsigned int *)&(open_args.mode));
351 break;
353 case 'g':
354 open_args.group=strdup(optarg);
355 break;
357 case 'l':
358 #ifdef VDE_IP_LOG
359 write_syslog_entry("START");
360 atexit(write_syslog_close);
361 vde_ip_log=1;
362 break;
363 #endif
365 default:
366 usage(argv[0]); //implies exit
370 if (optind < argc && sockname==NULL)
371 sockname=argv[optind];
373 atexit(cleanup);
374 setsighandlers();
375 conn=vde_open(sockname,"vde_plug:",&open_args);
376 if (conn == NULL) {
377 fprintf(stderr,"vde_open %s: %s\n",sockname?sockname:"DEF_SWITCH",strerror(errno));
378 exit(1);
381 vdestream=vdestream_open(conn,STDOUT_FILENO,vdeplug_recv,vdeplug_err);
383 pollv[1].fd=vde_datafd(conn);
384 pollv[2].fd=vde_ctlfd(conn);
386 for(;;) {
387 poll(pollv,3,-1);
388 if ((pollv[0].revents | pollv[1].revents | pollv[2].revents) & POLLHUP ||
389 pollv[2].revents & POLLIN)
390 break;
391 if (pollv[0].revents & POLLIN) {
392 nx=read(STDIN_FILENO,bufin,sizeof(bufin));
393 /* if POLLIN but not data it means that the stream has been
394 * closed at the other end */
395 /*fprintf(stderr,"%s: RECV %d %x %x \n",myname,nx,bufin[0],bufin[1]);*/
396 if (nx==0)
397 break;
398 vdestream_recv(vdestream, bufin, nx);
400 if (pollv[1].revents & POLLIN) {
401 nx=vde_recv(conn,bufin,BUFSIZE-2,0);
402 if (nx<0)
403 perror("vde_plug: recvfrom ");
404 else
406 vdestream_send(vdestream, bufin, nx);
407 /*fprintf(stderr,"%s: SENT %d %x %x \n",myname,nx,bufin[0],bufin[1]);*/
412 return(0);