VDE 2.1.6, 2006-12-21
[vde.git] / vde / vde_plug.c
blob3b8778f2d344d7e21f6a04ae3aaed58c22a0f2d3
1 /* Copyright 2003 Renzo Davoli
2 * Licensed under the GPL
3 */
5 #include <config.h>
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 <sys/ioctl.h>
14 #include <sys/socket.h>
15 #include <sys/un.h>
16 #include <sys/uio.h>
17 #include <sys/poll.h>
18 #include <sys/utsname.h>
19 #include <vde.h>
20 #ifdef VDE_IP_LOG
21 #define DO_SYSLOG
22 #endif
23 #ifdef DO_SYSLOG
24 #include <syslog.h>
25 #include <sys/types.h>
26 #include <pwd.h>
27 #include <ctype.h>
28 #include <arpa/inet.h>
29 #endif
31 #define SWITCH_MAGIC 0xfeedface
32 #define BUFSIZE 2048
33 #define ETH_ALEN 6
35 enum request_type { REQ_NEW_CONTROL };
37 struct request_v3 {
38 uint32_t magic;
39 uint32_t version;
40 enum request_type type;
41 struct sockaddr_un sock;
44 struct utsname me;
45 #define myname me.nodename
47 #ifdef DO_SYSLOG
48 static struct passwd *callerpwd;
49 static char host[256];
51 void write_syslog_entry(char *message)
53 char *ssh_client;
54 size_t ip_length;
56 openlog("vde_plug", 0, LOG_USER);
58 //get the login name
59 callerpwd=getpwuid(getuid());
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 packet {
88 struct {
89 unsigned char dest[ETH_ALEN];
90 unsigned char src[ETH_ALEN];
91 unsigned char proto[2];
92 } header;
93 union {
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 } body;
109 unsigned char ip4list[MAX_IP][4];
110 unsigned char ip6list[MAX_IP][16];
111 static unsigned char nulladdr[16];
113 static int hash4(char *addr)
115 return((addr[0]+2*addr[1]+3*addr[2]+5*addr[3]) % MAX_IP);
118 static int hash6(char *addr)
120 return((addr[0]+2*addr[1]+3*addr[2]+5*addr[3]+
121 7*addr[4]+11*addr[5]+13*addr[6]+17*addr[7]+
122 19*addr[7]+23*addr[8]+29*addr[9]+31*addr[10]+
123 37*addr[7]+41*addr[8]+43*addr[9]+47*addr[10]) % MAX_IP);
126 static void vde_ip_check(const char *buf,int rnx)
128 struct packet *p=(struct packet *) buf;
129 register int i,j;
130 char addr[256];
132 if (p->header.proto[0]==0x08 && p->header.proto[1]==0x00 &&
133 p->body.v4.version == 0x45) {
134 /*v4 */
135 i=hash4(p->body.v4.ip4src);
136 j=(i+MAX_IP-1)%MAX_IP;
137 while (1) {
138 /* more frequent case first */
139 if (memcmp(p->body.v4.ip4src,ip4list[i],4) == 0)
140 break;
141 else if (memcmp(ip4list[i],nulladdr,4) == 0) {
142 memcpy(ip4list[i],p->body.v4.ip4src,4);
143 syslog(LOG_INFO,"user %s Real-IP %s has got VDE-IP4 %s",callerpwd->pw_name,host,inet_ntop(AF_INET,ip4list[i],addr,256));
144 /*new ipv4*/
145 break;
146 } else if (i==j) {
147 syslog(LOG_ERR,"IPv4 table full. Exiting\n");
148 /*full table*/
149 exit(-1);
150 } else
151 i= (i+1)%MAX_IP;
154 else if (p->header.proto[0]==0x86 && p->header.proto[1]==0xdd &&
155 p->body.v4.version == 0x60) {
156 /* v6 */
157 i=hash6(p->body.v6.ip6src);
158 j=(i+MAX_IP-1)%MAX_IP;
159 while (1) {
160 /* more frequent case first */
161 if (memcmp(p->body.v6.ip6src,ip6list[i],16) == 0)
162 break;
163 else if (memcmp(ip6list[i],nulladdr,16) == 0) {
164 memcpy(ip6list[i],p->body.v6.ip6src,16);
165 syslog(LOG_INFO,"user %s Real-IP %s has got VDE-IP6 %s",callerpwd->pw_name,host,inet_ntop(AF_INET6,ip6list[i],addr,256));
166 /*new ipv6*/
167 break;
168 } else if (i==j) {
169 syslog(LOG_ERR,"IPv6 table full. Exiting\n");
170 /*full table*/
171 exit(-1);
172 } else
173 i= (i+1)%MAX_IP;
178 #endif
180 static int send_fd(char *name, int fddata, struct sockaddr_un *datasock, int group)
182 int pid = getpid();
183 struct request_v3 req;
184 int fdctl;
186 struct sockaddr_un sock;
188 if((fdctl = socket(AF_UNIX, SOCK_STREAM, 0)) < 0){
189 perror("socket");
190 exit(1);
193 sock.sun_family = AF_UNIX;
194 snprintf(sock.sun_path, sizeof(sock.sun_path), "%s", name);
195 if(connect(fdctl, (struct sockaddr *) &sock, sizeof(sock))){
196 perror("connect");
197 exit(1);
200 req.magic=SWITCH_MAGIC;
201 req.version=3;
202 req.type=REQ_NEW_CONTROL+((group > 0)?((geteuid()<<8) + group) << 8:0);
204 req.sock.sun_family=AF_UNIX;
205 memset(req.sock.sun_path, 0, sizeof(req.sock.sun_path));
206 sprintf(&req.sock.sun_path[1], "%5d", pid);
208 if(bind(fddata, (struct sockaddr *) &req.sock, sizeof(req.sock)) < 0){
209 perror("bind");
210 exit(1);
213 if (send(fdctl,&req,sizeof(req),0) < 0) {
214 perror("send");
215 exit(1);
218 if (recv(fdctl,datasock,sizeof(struct sockaddr_un),0)<0) {
219 perror("recv");
220 exit(1);
223 return fdctl;
226 unsigned char bufin[BUFSIZE];
227 #define MIN(X,Y) ((X)<(Y))?(X):(Y)
229 void splitpacket(const unsigned char *buf,int size,int fd, struct sockaddr_un *pd)
231 static unsigned char fragment[BUFSIZE];
232 static char *fragp;
233 static unsigned int rnx,remaining;
235 //fprintf(stderr,"%s: splitpacket rnx=%d remaining=%d size=%d\n",myname,rnx,remaining,size);
236 if (size==0) return;
237 if (rnx>0) {
238 register int amount=MIN(remaining,size);
239 //fprintf(stderr,"%s: fragment amount %d\n",myname,amount);
240 memcpy(fragp,buf,amount);
241 remaining-=amount;
242 fragp+=amount;
243 buf+=amount;
244 size-=amount;
245 if (remaining==0) {
246 //fprintf(stderr,"%s: delivered defrag %d\n",myname,rnx);
247 //send(fd,fragment,rnx,0);
248 sendto(fd,fragment,rnx,0,(struct sockaddr *) pd,sizeof(struct sockaddr_un));
250 rnx=0;
253 while (size > 0) {
254 rnx=(buf[0]<<8)+buf[1];
255 size-=2;
256 //fprintf(stderr,"%s: packet %d size %d %x %x\n",myname,rnx,size,buf[0],buf[1]);
257 buf+=2;
258 if (rnx>1521) {
259 fprintf(stderr,"%s: Packet length error size %d rnx %d\n",myname,size,rnx);
260 rnx=0;
261 return;
263 if (rnx > size) {
264 //fprintf(stderr,"%s: begin defrag %d\n",myname,rnx);
265 fragp=fragment;
266 memcpy(fragp,buf,size);
267 remaining=rnx-size;
268 fragp+=size;
269 size=0;
270 } else {
271 //fprintf(stderr,"%s: deliver %d\n",myname,rnx);
272 //send(fd,buf,rnx,0);
273 #ifdef VDE_IP_LOG
274 if (vde_ip_log)
275 vde_ip_check(buf,rnx);
276 #endif
277 sendto(fd,buf,rnx,0,(struct sockaddr *) pd,sizeof(struct sockaddr_un));
278 buf+=rnx;
279 size-=rnx;
280 rnx=0;
285 struct pollfd pollv[]={{STDIN_FILENO,POLLIN|POLLHUP,0},{0,POLLIN|POLLHUP,0}};
287 static void netusage() {
288 #ifdef DO_SYSLOG
289 write_syslog_entry("FAILED");
290 #endif
291 fprintf (stderr,"This is a Virtual Distributed Ethernet (vde) tunnel broker. \n"
292 "This is not a login shell, only vde_plug can be executed\n");
293 exit(-1);
296 static void usage(char *progname) {
297 fprintf (stderr,"Usage: %s [-g num] [socketname]\n ( 0 < num < 256 )\n\n",progname);
298 exit(-1);
301 int main(int argc, char **argv)
303 int fddata;
304 char *sockname=NULL;
305 struct sockaddr_un dataout;
306 struct sockaddr_un datain;
307 int datainsize;
308 int result;
309 int group=0;
310 int connected_fd;
311 register ssize_t nx;
313 uname(&me);
314 if (argv[0][0] == '-')
315 netusage(); //implies exit
316 sockname=VDESTDSOCK;
317 /* option parsing */
319 int c;
320 while (1) {
321 int option_index = 0;
323 static struct option long_options[] = {
324 {"group", 1, 0, 's'},
325 {"sock", 1, 0, 's'},
326 {"vdesock", 1, 0, 's'},
327 {"unix", 1, 0, 's'},
328 {"help",0,0,'h'},
329 {0, 0, 0, 0}
331 c = getopt_long_only (argc, argv, "c:g:s:l",
332 long_options, &option_index);
333 if (c == -1)
334 break;
336 switch (c) {
337 case 'c':
338 if (strcmp(optarg,"vde_plug")==0) {
339 #ifdef DO_SYSLOG
340 write_syslog_entry("START");
341 atexit(write_syslog_close);
342 #ifdef VDE_IP_LOG
343 vde_ip_log=1;
344 #endif
345 #endif
348 else
349 netusage(); //implies exit
350 break;
352 case 'g':
353 group=atoi(optarg);
354 if (group <= 0 || group > 255 )
355 usage(argv[0]); //implies exit
356 break;
358 case 'h':
359 usage(argv[0]); //implies exit
360 break;
362 case 's':
363 sockname=strdup(optarg);
364 break;
366 case 'l':
367 #ifdef VDE_IP_LOG
368 write_syslog_entry("START");
369 atexit(write_syslog_close);
370 vde_ip_log=1;
371 break;
372 #endif
374 default:
375 usage(argv[0]); //implies exit
379 if (optind < argc && sockname==VDESTDSOCK)
380 sockname=argv[optind];
382 if((fddata = socket(AF_UNIX, SOCK_DGRAM, 0)) < 0){
383 perror("socket");
384 exit(1);
386 connected_fd=send_fd(sockname, fddata, &dataout, group);
387 pollv[1].fd=fddata;
389 for(;;) {
390 result=poll(pollv,2,-1);
391 if (pollv[0].revents & POLLHUP || pollv[1].revents & POLLHUP)
392 break;
393 if (pollv[0].revents & POLLIN) {
394 nx=read(STDIN_FILENO,bufin,sizeof(bufin));
395 /* if POLLIN but not data it means that the stream has been
396 * closed at the other end */
397 if (nx==0)
398 break;
399 splitpacket(bufin,nx,fddata,&dataout);
400 //sendto(fddata,bufin,nx,0,(struct sockaddr *) &dataout,sizeof(dataout));
403 if (pollv[1].revents & POLLIN) {
404 datainsize=sizeof(datain);
405 nx=recvfrom(fddata,bufin+2,BUFSIZE-2,0,(struct sockaddr *) &datain, &datainsize);
406 if (nx<0)
407 perror("vde_plug: recvfrom ");
408 else
410 bufin[0]=nx >> 8;
411 bufin[1]=nx & 0xff;
412 write(STDOUT_FILENO,bufin,nx+2);
413 //fprintf(stderr,"%s: SENT %d %x %x \n",myname,nx,bufin[0],bufin[1]);
418 return(0);