tagging vde-2 version 2.3.2
[vde.git] / 2.3.2 / src / vde_switch / datasock.c
blobb2413838e5f8f5ee0a7bff31a5a78c249e5579c5
1 /* Copyright 2005 Renzo Davoli - VDE-2
2 * --pidfile/-p and cleanup management by Mattia Belletti (C) 2004.
3 * Licensed under the GPLv2
4 * Modified by Ludovico Gardenghi 2005
5 * -g option (group management) by Daniel P. Berrange
6 * dir permission patch by Alessio Caprari 2006
7 */
9 #include <stdio.h>
10 #include <fcntl.h>
11 #include <errno.h>
12 #include <string.h>
13 #include <unistd.h>
14 #include <syslog.h>
15 #include <stdlib.h>
16 #include <stdint.h>
17 #include <libgen.h>
18 #include <sys/types.h>
19 #include <sys/stat.h>
20 #include <sys/ioctl.h>
21 #include <sys/socket.h>
22 #include <sys/types.h>
23 #include <sys/un.h>
24 #include <net/if.h>
25 #include <stdarg.h>
26 #include <limits.h>
27 #include <grp.h>
28 #define _GNU_SOURCE
29 #include <getopt.h>
31 #include <config.h>
32 #include <vde.h>
33 #include <vdecommon.h>
35 #include "port.h"
36 #include "switch.h"
37 #include "sockutils.h"
38 #include "consmgmt.h"
40 static struct swmodule swmi;
41 static struct mod_support modfun;
42 static unsigned int ctl_type;
43 static unsigned int wd_type;
44 static unsigned int data_type;
46 static char *rel_ctl_socket = NULL;
47 static char ctl_socket[PATH_MAX];
49 static int mode = -1;
50 static int dirmode = -1;
51 static gid_t grp_owner = -1;
53 #define MODULENAME "unix prog"
55 #define DATA_BUF_SIZE 131072
56 #define SWITCH_MAGIC 0xfeedface
57 #define REQBUFLEN 256
59 enum request_type { REQ_NEW_CONTROL, REQ_NEW_PORT0 };
61 struct request_v1 {
62 uint32_t magic;
63 enum request_type type;
64 union {
65 struct {
66 unsigned char addr[ETH_ALEN];
67 struct sockaddr_un name;
68 } new_control;
69 } u;
70 char description[];
71 } __attribute__((packed));
73 struct request_v3 {
74 uint32_t magic;
75 uint32_t version;
76 enum request_type type;
77 struct sockaddr_un sock;
78 char description[];
79 } __attribute__((packed));
81 union request {
82 struct request_v1 v1;
83 struct request_v3 v3;
86 static int send_datasock(int fd_ctl, int fd_data, void *packet, int len, int port)
88 if (send(fd_data, packet, len, 0) < 0) {
89 int rv=errno;
90 if(rv != EAGAIN && rv != EWOULDBLOCK)
91 printlog(LOG_WARNING,"send_sockaddr port %d: %s",port,strerror(errno));
92 else
93 rv=EWOULDBLOCK;
94 return -rv;
96 return 0;
99 #define GETFILEOWNER(PATH) ({\
100 struct stat s; \
101 (stat((PATH),&s)?-1:s.st_uid); \
104 static struct endpoint *new_port_v1_v3(int fd_ctl, int type_port,
105 struct sockaddr_un *sun_out)
107 int n, portno;
108 struct endpoint *ep;
109 enum request_type type = type_port & 0xff;
110 int port_request=type_port >> 8;
111 uid_t user=-1;
112 int fd_data;
113 #ifdef VDE_DARWIN
114 int sockbufsize = DATA_BUF_SIZE;
115 int optsize = sizeof(sockbufsize);
116 #endif
117 struct sockaddr_un sun_in;
118 // init sun_in memory
119 memset(&sun_in,0,sizeof(sun_in));
120 switch(type){
121 case REQ_NEW_PORT0:
122 port_request= -1;
123 /* no break: falltrough */
124 case REQ_NEW_CONTROL:
125 if (sun_out->sun_path[0] != 0) { //not for unnamed sockets
126 if (access(sun_out->sun_path,R_OK | W_OK) != 0) { //socket error
127 remove_fd(fd_ctl);
128 return NULL;
130 user=GETFILEOWNER(sun_out->sun_path);
133 if((fd_data = socket(PF_UNIX, SOCK_DGRAM, 0)) < 0){
134 printlog(LOG_ERR,"socket: %s",strerror(errno));
135 remove_fd(fd_ctl);
136 return NULL;
138 if(fcntl(fd_data, F_SETFL, O_NONBLOCK) < 0){
139 printlog(LOG_ERR,"Setting O_NONBLOCK on data fd %s",strerror(errno));
140 close(fd_data);
141 remove_fd(fd_ctl);
142 return NULL;
145 #ifdef VDE_DARWIN
146 if(setsockopt(fd_data, SOL_SOCKET, SO_SNDBUF, &sockbufsize, optsize) < 0)
147 printlog(LOG_WARNING, "Warning: setting send buffer size on data fd %d to %d failed, expect packet loss: %s",
148 fd_data, sockbufsize, strerror(errno));
149 if(setsockopt(fd_data, SOL_SOCKET, SO_RCVBUF, &sockbufsize, optsize) < 0)
150 printlog(LOG_WARNING, "Warning: setting send buffer size on data fd %d to %d failed, expect packet loss: %s",
151 fd_data, sockbufsize, strerror(errno));
152 #endif
154 if (connect(fd_data, (struct sockaddr *) sun_out, sizeof(struct sockaddr_un)) < 0) {
155 printlog(LOG_ERR,"Connecting to client data socket %s",strerror(errno));
156 close(fd_data);
157 remove_fd(fd_ctl);
158 return NULL;
161 ep = setup_ep(port_request, fd_ctl, fd_data, user, &modfun);
162 if(ep == NULL)
163 return NULL;
164 portno=ep_get_port(ep);
165 add_fd(fd_data,data_type,ep);
166 sun_in.sun_family = AF_UNIX;
167 snprintf(sun_in.sun_path,sizeof(sun_in.sun_path),"%s/%03d.%d",ctl_socket,portno,fd_data);
169 if ((unlink(sun_in.sun_path) < 0 && errno != ENOENT) ||
170 bind(fd_data, (struct sockaddr *) &sun_in, sizeof(struct sockaddr_un)) < 0){
171 printlog(LOG_ERR,"Binding to data socket %s",strerror(errno));
172 close_ep(ep);
173 return NULL;
175 if (geteuid() != 0)
176 user = -1;
177 if (user != -1)
178 chmod(sun_in.sun_path,mode & 0700);
179 else
180 chmod(sun_in.sun_path,mode);
181 if(chown(sun_in.sun_path,user,grp_owner) < 0) {
182 printlog(LOG_ERR, "chown: %s", strerror(errno));
183 unlink(sun_in.sun_path);
184 close_ep(ep);
185 return NULL;
188 n = write(fd_ctl, &sun_in, sizeof(sun_in));
189 if(n != sizeof(sun_in)){
190 printlog(LOG_WARNING,"Sending data socket name %s",strerror(errno));
191 close_ep(ep);
192 return NULL;
194 if (type==REQ_NEW_PORT0)
195 setmgmtperm(sun_in.sun_path);
196 return ep;
197 break;
198 default:
199 printlog(LOG_WARNING,"Bad request type : %d", type);
200 remove_fd(fd_ctl);
201 return NULL;
205 static void handle_io(unsigned char type,int fd,int revents,void *arg)
207 struct endpoint *ep=arg;
208 if (type == data_type) {
209 #ifdef VDE_PQ2
210 if (revents & POLLOUT)
211 handle_out_packet(ep);
212 #endif
213 if (revents & POLLIN) {
214 struct bipacket packet;
215 int len;
217 len=recv(fd, &(packet.p), sizeof(struct packet),0);
218 if(len < 0){
219 if (errno == EAGAIN || errno == EWOULDBLOCK) return;
220 printlog(LOG_WARNING,"Reading data: %s",strerror(errno));
222 else if(len == 0)
223 printlog(LOG_WARNING,"EOF data port: %s",strerror(errno));
224 else if(len >= ETH_HEADER_SIZE)
225 handle_in_packet(ep, &(packet.p), len);
228 else if (type == wd_type) {
229 char reqbuf[REQBUFLEN+1];
230 union request *req=(union request *)reqbuf;
231 int len;
233 len = read(fd, reqbuf, REQBUFLEN);
234 if (len < 0) {
235 if(errno != EAGAIN && errno != EWOULDBLOCK){
236 printlog(LOG_WARNING,"Reading request %s", strerror(errno));
237 remove_fd(fd);
239 return;
240 } else if (len > 0) {
241 reqbuf[len]=0;
242 if(req->v1.magic == SWITCH_MAGIC){
243 if(req->v3.version == 3) {
244 ep=new_port_v1_v3(fd, req->v3.type, &(req->v3.sock));
245 if (ep != NULL) {
246 mainloop_set_private_data(fd,ep);
247 setup_description(ep,strdup(req->v3.description));
250 else if(req->v3.version > 2 || req->v3.version == 2) {
251 printlog(LOG_ERR, "Request for a version %d port, which this "
252 "vde_switch doesn't support", req->v3.version);
253 remove_fd(fd);
255 else {
256 ep=new_port_v1_v3(fd, req->v1.type, &(req->v1.u.new_control.name));
257 if (ep != NULL) {
258 mainloop_set_private_data(fd,ep);
259 setup_description(ep,strdup(req->v1.description));
263 else {
264 printlog(LOG_WARNING,"V0 request not supported");
265 remove_fd(fd);
266 return;
268 } else {
269 if (ep != NULL)
270 close_ep(ep);
271 else
272 remove_fd(fd);
275 else /*if (type == ctl_type)*/ {
276 struct sockaddr addr;
277 socklen_t len;
278 int new;
280 len = sizeof(addr);
281 new = accept(fd, &addr, &len);
282 if(new < 0){
283 printlog(LOG_WARNING,"accept %s",strerror(errno));
284 return;
287 if(fcntl(new, F_SETFL, O_NONBLOCK) < 0){
288 printlog(LOG_WARNING,"fcntl - setting O_NONBLOCK %s",strerror(errno));
289 close(new);
290 return;
293 add_fd(new,wd_type,NULL);
297 static void cleanup(unsigned char type,int fd,void *arg)
299 struct sockaddr_un clun;
300 int test_fd;
302 if (fd < 0) {
303 if (!strlen(ctl_socket)) {
304 /* ctl_socket has not been created yet */
305 return;
307 if((test_fd = socket(AF_UNIX, SOCK_STREAM, 0)) < 0){
308 printlog(LOG_ERR,"socket %s",strerror(errno));
310 clun.sun_family=AF_UNIX;
311 snprintf(clun.sun_path,sizeof(clun.sun_path),"%s/ctl",ctl_socket);
312 if(connect(test_fd, (struct sockaddr *) &clun, sizeof(clun))){
313 close(test_fd);
314 if(unlink(clun.sun_path) < 0)
315 printlog(LOG_WARNING,"Could not remove ctl socket '%s': %s", ctl_socket, strerror(errno));
316 else if(rmdir(ctl_socket) < 0)
317 printlog(LOG_WARNING,"Could not remove ctl dir '%s': %s", ctl_socket, strerror(errno));
319 else printlog(LOG_WARNING,"Cleanup not removing files");
320 } else {
321 if (type == data_type && arg != NULL) {
322 int portno=ep_get_port(arg);
323 snprintf(clun.sun_path,sizeof(clun.sun_path),"%s/%03d.%d",ctl_socket,portno,fd);
324 unlink(clun.sun_path);
326 close(fd);
330 #define DIRMODEARG 0x100
332 static struct option long_options[] = {
333 {"sock", 1, 0, 's'},
334 {"vdesock", 1, 0, 's'},
335 {"unix", 1, 0, 's'},
336 {"mod", 1, 0, 'm'},
337 {"mode", 1, 0, 'm'},
338 {"dirmode", 1, 0, DIRMODEARG},
339 {"group", 1, 0, 'g'},
342 #define Nlong_options (sizeof(long_options)/sizeof(struct option));
344 static void usage(void)
346 printf(
347 "(opts from datasock module)\n"
348 " -s, --sock SOCK control directory pathname\n"
349 " -s, --vdesock SOCK Same as --sock SOCK\n"
350 " -s, --unix SOCK Same as --sock SOCK\n"
351 " -m, --mode MODE Permissions for the control socket (octal)\n"
352 " --dirmode MODE Permissions for the sockets directory (octal)\n"
353 " -g, --group GROUP Group owner for comm sockets\n"
357 static int parseopt(int c, char *optarg)
359 int outc=0;
360 struct group *grp;
361 switch (c) {
362 case 's':
363 if (!(rel_ctl_socket = strdup(optarg))) {
364 fprintf(stderr, "Memory error while parsing '%s'\n", optarg);
365 exit(1);
367 break;
368 case 'm':
369 sscanf(optarg,"%o",&mode);
370 break;
371 case 'g':
372 if (!(grp = getgrnam(optarg))) {
373 fprintf(stderr, "No such group '%s'\n", optarg);
374 exit(1);
376 grp_owner=grp->gr_gid;
377 break;
378 case DIRMODEARG:
379 sscanf(optarg, "%o", &dirmode);
380 break;
381 default:
382 outc=c;
384 return outc;
387 static void init(void)
389 int connect_fd;
390 struct sockaddr_un sun;
391 int one = 1;
393 /* Set up default modes */
394 if (mode < 0 && dirmode < 0)
396 /* Default values */
397 mode = 00600; /* -rw------- for the ctl socket */
398 dirmode = 02700; /* -rwx--S--- for the directory */
400 else if (mode >= 0 && dirmode < 0)
402 /* If only mode (-m) has been specified, we guess the dirmode from it,
403 * adding the executable bit where needed */
405 # define ADDBIT(mode, conditionmask, add) ((mode & conditionmask) ? ((mode & conditionmask) | add) : (mode & conditionmask))
407 dirmode = 02000 | /* Add also setgid */
408 ADDBIT(mode, 0600, 0100) |
409 ADDBIT(mode, 0060, 0010) |
410 ADDBIT(mode, 0006, 0001);
412 else if (mode < 0 && dirmode >= 0)
414 /* If only dirmode (--dirmode) has been specified, we guess the ctl
415 * socket mode from it, turning off the executable bit everywhere */
416 mode = dirmode & 0666;
419 if((connect_fd = socket(PF_UNIX, SOCK_STREAM, 0)) < 0){
420 printlog(LOG_ERR,"Could not obtain a BSD socket: %s", strerror(errno));
421 return;
423 if(setsockopt(connect_fd, SOL_SOCKET, SO_REUSEADDR, (char *) &one,
424 sizeof(one)) < 0){
425 printlog(LOG_ERR,"Could not set socket options on %d: %s", connect_fd, strerror(errno));
426 return;
428 if(fcntl(connect_fd, F_SETFL, O_NONBLOCK) < 0){
429 printlog(LOG_ERR,"Could not set O_NONBLOCK on connection fd %d: %s", connect_fd, strerror(errno));
430 return;
432 /* resolve ctl_socket, eventually defaulting to standard paths */
433 if (rel_ctl_socket == NULL) {
434 rel_ctl_socket = (geteuid()==0)?VDESTDSOCK:VDETMPSOCK;
436 if (((mkdir(rel_ctl_socket, 0777) < 0) && (errno != EEXIST))) {
437 fprintf(stderr,"Cannot create ctl directory '%s': %s\n",
438 rel_ctl_socket, strerror(errno));
439 exit(-1);
441 if (!vde_realpath(rel_ctl_socket, ctl_socket)) {
442 fprintf(stderr,"Cannot resolve ctl dir path '%s': %s\n",
443 rel_ctl_socket, strerror(errno));
444 exit(1);
447 if(chown(ctl_socket,-1,grp_owner) < 0) {
448 rmdir(ctl_socket);
449 printlog(LOG_ERR, "Could not chown socket '%s': %s", sun.sun_path, strerror(errno));
450 exit(-1);
452 if (chmod(ctl_socket, dirmode) < 0) {
453 printlog(LOG_ERR,"Could not set the VDE ctl directory '%s' permissions: %s", ctl_socket, strerror(errno));
454 exit(-1);
456 sun.sun_family = AF_UNIX;
457 snprintf(sun.sun_path,sizeof(sun.sun_path),"%s/ctl",ctl_socket);
458 if(bind(connect_fd, (struct sockaddr *) &sun, sizeof(sun)) < 0){
459 if((errno == EADDRINUSE) && still_used(&sun)){
460 printlog(LOG_ERR, "Could not bind to socket '%s/ctl': %s", ctl_socket, strerror(errno));
461 exit(-1);
463 else if(bind(connect_fd, (struct sockaddr *) &sun, sizeof(sun)) < 0){
464 printlog(LOG_ERR, "Could not bind to socket '%s/ctl' (second attempt): %s", ctl_socket, strerror(errno));
465 exit(-1);
468 chmod(sun.sun_path,mode);
469 if(chown(sun.sun_path,-1,grp_owner) < 0) {
470 printlog(LOG_ERR, "Could not chown socket '%s': %s", sun.sun_path, strerror(errno));
471 exit(-1);
473 if(listen(connect_fd, 15) < 0){
474 printlog(LOG_ERR,"Could not listen on fd %d: %s", connect_fd, strerror(errno));
475 exit(-1);
477 ctl_type=add_type(&swmi,0);
478 wd_type=add_type(&swmi,0);
479 data_type=add_type(&swmi,1);
480 add_fd(connect_fd,ctl_type,NULL);
483 static int showinfo(FILE *fd)
485 printoutc(fd,"ctl dir %s",ctl_socket);
486 printoutc(fd,"std mode 0%03o",mode);
487 return 0;
490 static struct comlist cl[]={
491 {"ds","============","DATA SOCKET MENU",NULL,NOARG},
492 {"ds/showinfo","","show ds info",showinfo,NOARG|WITHFILE},
495 static void delep (int fd_ctl, int fd_data, void *descr)
497 if (fd_data>=0) remove_fd(fd_data);
498 if (fd_ctl>=0) remove_fd(fd_ctl);
499 if (descr) free(descr);
502 void start_datasock(void)
504 modfun.modname=swmi.swmname=MODULENAME;
505 swmi.swmnopts=Nlong_options;
506 swmi.swmopts=long_options;
507 swmi.usage=usage;
508 swmi.parseopt=parseopt;
509 swmi.init=init;
510 swmi.handle_io=handle_io;
511 swmi.cleanup=cleanup;
512 modfun.sender=send_datasock;
513 modfun.delep=delep;
514 ADDCL(cl);
515 add_swm(&swmi);