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
18 #include <sys/types.h>
20 #include <sys/ioctl.h>
21 #include <sys/socket.h>
22 #include <sys/types.h>
33 #include <vdecommon.h>
37 #include "sockutils.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
];
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
59 enum request_type
{ REQ_NEW_CONTROL
, REQ_NEW_PORT0
};
63 enum request_type type
;
66 unsigned char addr
[ETH_ALEN
];
67 struct sockaddr_un name
;
71 } __attribute__((packed
));
76 enum request_type type
;
77 struct sockaddr_un sock
;
79 } __attribute__((packed
));
86 static int send_datasock(int fd
, int ctl_fd
, void *packet
, int len
, void *data
, int port
)
89 struct sockaddr
*dst
=(struct sockaddr
*)data
;
91 n
= len
- sendto(fd
, packet
, len
, 0, dst
, sizeof(struct sockaddr_un
));
95 if(errno
!= EAGAIN
&& errno
!= EWOULDBLOCK
) printlog(LOG_WARNING
,"send_sockaddr port %d: %s",port
,strerror(errno
));
105 static void closeport(int fd
, int portno
)
111 static int newport(int fd
, int portno
, uid_t user
)
114 struct sockaddr_un sun
;
116 int sockbufsize
= DATA_BUF_SIZE
;
117 int optsize
= sizeof(sockbufsize
);
120 if((data_fd
= socket(PF_UNIX
, SOCK_DGRAM
, 0)) < 0){
121 printlog(LOG_ERR
,"socket: %s",strerror(errno
));
124 if(fcntl(data_fd
, F_SETFL
, O_NONBLOCK
) < 0){
125 printlog(LOG_ERR
,"Setting O_NONBLOCK on data fd %s",strerror(errno
));
130 if(setsockopt(data_fd
, SOL_SOCKET
, SO_SNDBUF
, &sockbufsize
, optsize
) < 0)
131 printlog(LOG_WARNING
, "Warning: setting send buffer size on data fd %d to %d failed, expect packet loss: %s",
132 data_fd
, sockbufsize
, strerror(errno
));
133 if(setsockopt(data_fd
, SOL_SOCKET
, SO_RCVBUF
, &sockbufsize
, optsize
) < 0)
134 printlog(LOG_WARNING
, "Warning: setting send buffer size on data fd %d to %d failed, expect packet loss: %s",
135 data_fd
, sockbufsize
, strerror(errno
));
138 sun
.sun_family
= AF_UNIX
;
139 snprintf(sun
.sun_path
,sizeof(sun
.sun_path
),"%s/%03d",ctl_socket
,portno
);
140 if ((unlink(sun
.sun_path
) < 0 && errno
!= ENOENT
) ||
141 bind(data_fd
, (struct sockaddr
*) &sun
, sizeof(sun
)) < 0){
142 printlog(LOG_ERR
,"Binding to data socket %s",strerror(errno
));
143 close_ep(portno
-1,fd
);
149 chmod(sun
.sun_path
,mode
& 0700);
151 chmod(sun
.sun_path
,mode
);
152 if(chown(sun
.sun_path
,user
,grp_owner
) < 0) {
153 printlog(LOG_ERR
, "chown: %s", strerror(errno
));
154 unlink(sun
.sun_path
);
155 close_ep(portno
-1,fd
);
159 add_fd(data_fd
,data_type
,portno
);
164 static void *memdup(void *src
,int size
)
166 void *dst
=malloc(size
);
168 memcpy(dst
,src
,size
);
172 #define GETFILEOWNER(PATH) ({\
174 (stat((PATH),&s)?-1:s.st_uid); \
177 static int new_port_v1_v3(int fd
, int type_port
,
178 struct sockaddr_un
*sun_out
)
181 enum request_type type
= type_port
& 0xff;
182 int port_request
=type_port
>> 8;
184 struct sockaddr_un sun_in
;
188 /* no break: falltrough */
189 case REQ_NEW_CONTROL
:
190 if (sun_out
->sun_path
[0] != 0) { //not for unnamed sockets
191 if (access(sun_out
->sun_path
,R_OK
| W_OK
) != 0) { //socket error
195 user
=GETFILEOWNER(sun_out
->sun_path
);
197 port
= setup_ep(port_request
, fd
, memdup(sun_out
,sizeof(struct sockaddr_un
)), user
, &modfun
);
202 sun_in
.sun_family
= AF_UNIX
;
203 snprintf(sun_in
.sun_path
,sizeof(sun_in
.sun_path
),"%s/%03d",ctl_socket
,port
);
204 n
= write(fd
, &sun_in
, sizeof(sun_in
));
205 if(n
!= sizeof(sun_in
)){
206 printlog(LOG_WARNING
,"Sending data socket name %s",strerror(errno
));
210 if (type
==REQ_NEW_PORT0
)
211 setmgmtperm(sun_in
.sun_path
);
215 printlog(LOG_WARNING
,"Bad request type : %d", type
);
221 static void handle_input(unsigned char type
,int fd
,int revents
,int *arg
)
223 if (type
== data_type
) {
224 struct bipacket packet
;
225 struct sockaddr sock
;
227 socklen_t socklen
= sizeof(sock
);
229 len
=recvfrom(fd
, &(packet
.p
), sizeof(struct packet
),0, &sock
, &socklen
);
231 if (errno
== EAGAIN
|| errno
== EWOULDBLOCK
) return;
232 printlog(LOG_WARNING
,"Reading data: %s",strerror(errno
));
235 printlog(LOG_WARNING
,"EOF data port: %s",strerror(errno
));
236 else if(len
>= ETH_HEADER_SIZE
)
237 handle_in_packet(*arg
, &(packet
.p
), len
);
239 else if (type
== wd_type
) {
240 char reqbuf
[REQBUFLEN
+1];
241 union request
*req
=(union request
*)reqbuf
;
244 len
= read(fd
, reqbuf
, REQBUFLEN
);
246 if(errno
!= EAGAIN
&& errno
!= EWOULDBLOCK
){
247 printlog(LOG_WARNING
,"Reading request %s", strerror(errno
));
251 } else if (len
> 0) {
253 if(req
->v1
.magic
== SWITCH_MAGIC
){
255 if(req
->v3
.version
== 3) {
256 port
=new_port_v1_v3(fd
, req
->v3
.type
, &(req
->v3
.sock
));
259 setup_description(*arg
,fd
,strdup(req
->v3
.description
));
262 else if(req
->v3
.version
> 2 || req
->v3
.version
== 2) {
263 printlog(LOG_ERR
, "Request for a version %d port, which this "
264 "vde_switch doesn't support", req
->v3
.version
);
268 *arg
=port
=new_port_v1_v3(fd
, req
->v1
.type
, &(req
->v1
.u
.new_control
.name
));
269 setup_description(*arg
,fd
,strdup(req
->v1
.description
));
273 printlog(LOG_WARNING
,"V0 request not supported");
284 else /*if (type == ctl_type)*/ {
285 struct sockaddr addr
;
290 new = accept(fd
, &addr
, &len
);
292 printlog(LOG_WARNING
,"accept %s",strerror(errno
));
295 if(fcntl(new, F_SETFL
, O_NONBLOCK
) < 0){
296 printlog(LOG_WARNING
,"fcntl - setting O_NONBLOCK %s",strerror(errno
));
301 add_fd(new,wd_type
,-1);
305 static void cleanup(unsigned char type
,int fd
,int arg
)
307 struct sockaddr_un clun
;
311 if (!strlen(ctl_socket
)) {
312 /* ctl_socket has not been created yet */
315 if((test_fd
= socket(AF_UNIX
, SOCK_STREAM
, 0)) < 0){
316 printlog(LOG_ERR
,"socket %s",strerror(errno
));
318 clun
.sun_family
=AF_UNIX
;
319 snprintf(clun
.sun_path
,sizeof(clun
.sun_path
),"%s/ctl",ctl_socket
);
320 if(connect(test_fd
, (struct sockaddr
*) &clun
, sizeof(clun
))){
322 if(unlink(clun
.sun_path
) < 0)
323 printlog(LOG_WARNING
,"Could not remove ctl socket '%s': %s", ctl_socket
, strerror(errno
));
324 else if(rmdir(ctl_socket
) < 0)
325 printlog(LOG_WARNING
,"Could not remove ctl dir '%s': %s", ctl_socket
, strerror(errno
));
327 else printlog(LOG_WARNING
,"Cleanup not removing files");
329 if (type
== data_type
&& arg
>=0) {
330 snprintf(clun
.sun_path
,sizeof(clun
.sun_path
),"%s/%03d",ctl_socket
,arg
);
331 unlink(clun
.sun_path
);
337 #define DIRMODEARG 0x100
339 static struct option long_options
[] = {
341 {"vdesock", 1, 0, 's'},
345 {"dirmode", 1, 0, DIRMODEARG
},
346 {"group", 1, 0, 'g'},
349 #define Nlong_options (sizeof(long_options)/sizeof(struct option));
351 static void usage(void)
354 "(opts from datasock module)\n"
355 " -s, --sock SOCK control directory pathname\n"
356 " -s, --vdesock SOCK Same as --sock SOCK\n"
357 " -s, --unix SOCK Same as --sock SOCK\n"
358 " -m, --mode MODE Permissions for the control socket (octal)\n"
359 " --dirmode MODE Permissions for the sockets directory (octal)\n"
360 " -g, --group GROUP Group owner for comm sockets\n"
364 static int parseopt(int c
, char *optarg
)
370 if (!(rel_ctl_socket
= strdup(optarg
))) {
371 fprintf(stderr
, "Memory error while parsing '%s'\n", optarg
);
376 sscanf(optarg
,"%o",&mode
);
379 if (!(grp
= getgrnam(optarg
))) {
380 fprintf(stderr
, "No such group '%s'\n", optarg
);
383 grp_owner
=grp
->gr_gid
;
386 sscanf(optarg
, "%o", &dirmode
);
394 static void init(void)
397 struct sockaddr_un sun
;
400 /* Set up default modes */
401 if (mode
< 0 && dirmode
< 0)
404 mode
= 00600; /* -rw------- for the ctl socket */
405 dirmode
= 02700; /* -rwx--S--- for the directory */
407 else if (mode
>= 0 && dirmode
< 0)
409 /* If only mode (-m) has been specified, we guess the dirmode from it,
410 * adding the executable bit where needed */
412 # define ADDBIT(mode, conditionmask, add) ((mode & conditionmask) ? ((mode & conditionmask) | add) : (mode & conditionmask))
414 dirmode
= 02000 | /* Add also setgid */
415 ADDBIT(mode
, 0600, 0100) |
416 ADDBIT(mode
, 0060, 0010) |
417 ADDBIT(mode
, 0006, 0001);
419 else if (mode
< 0 && dirmode
>= 0)
421 /* If only dirmode (--dirmode) has been specified, we guess the ctl
422 * socket mode from it, turning off the executable bit everywhere */
423 mode
= dirmode
& 0666;
426 if((connect_fd
= socket(PF_UNIX
, SOCK_STREAM
, 0)) < 0){
427 printlog(LOG_ERR
,"Could not obtain a BSD socket: %s", strerror(errno
));
430 if(setsockopt(connect_fd
, SOL_SOCKET
, SO_REUSEADDR
, (char *) &one
,
432 printlog(LOG_ERR
,"Could not set socket options on %d: %s", connect_fd
, strerror(errno
));
435 if(fcntl(connect_fd
, F_SETFL
, O_NONBLOCK
) < 0){
436 printlog(LOG_ERR
,"Could not set O_NONBLOCK on connection fd %d: %s", connect_fd
, strerror(errno
));
439 /* resolve ctl_socket, eventually defaulting to standard paths */
440 if (rel_ctl_socket
== NULL
) {
441 rel_ctl_socket
= (geteuid()==0)?VDESTDSOCK
:VDETMPSOCK
;
443 if (((mkdir(rel_ctl_socket
, 0777) < 0) && (errno
!= EEXIST
))) {
444 fprintf(stderr
,"Cannot create ctl directory '%s': %s\n",
445 rel_ctl_socket
, strerror(errno
));
448 if (!vde_realpath(rel_ctl_socket
, ctl_socket
)) {
449 fprintf(stderr
,"Cannot resolve ctl dir path '%s': %s\n",
450 rel_ctl_socket
, strerror(errno
));
454 if(chown(ctl_socket
,-1,grp_owner
) < 0) {
456 printlog(LOG_ERR
, "Could not chown socket '%s': %s", sun
.sun_path
, strerror(errno
));
459 if (chmod(ctl_socket
, dirmode
) < 0) {
460 printlog(LOG_ERR
,"Could not set the VDE ctl directory '%s' permissions: %s", ctl_socket
, strerror(errno
));
463 sun
.sun_family
= AF_UNIX
;
464 snprintf(sun
.sun_path
,sizeof(sun
.sun_path
),"%s/ctl",ctl_socket
);
465 if(bind(connect_fd
, (struct sockaddr
*) &sun
, sizeof(sun
)) < 0){
466 if((errno
== EADDRINUSE
) && still_used(&sun
)){
467 printlog(LOG_ERR
, "Could not bind to socket '%s/ctl': %s", ctl_socket
, strerror(errno
));
470 else if(bind(connect_fd
, (struct sockaddr
*) &sun
, sizeof(sun
)) < 0){
471 printlog(LOG_ERR
, "Could not bind to socket '%s/ctl' (second attempt): %s", ctl_socket
, strerror(errno
));
475 chmod(sun
.sun_path
,mode
);
476 if(chown(sun
.sun_path
,-1,grp_owner
) < 0) {
477 printlog(LOG_ERR
, "Could not chown socket '%s': %s", sun
.sun_path
, strerror(errno
));
480 if(listen(connect_fd
, 15) < 0){
481 printlog(LOG_ERR
,"Could not listen on fd %d: %s", connect_fd
, strerror(errno
));
484 ctl_type
=add_type(&swmi
,0);
485 wd_type
=add_type(&swmi
,0);
486 data_type
=add_type(&swmi
,1);
487 add_fd(connect_fd
,ctl_type
,-1);
490 static int showinfo(FILE *fd
)
492 printoutc(fd
,"ctl dir %s",ctl_socket
);
493 printoutc(fd
,"std mode 0%03o",mode
);
497 static struct comlist cl
[]={
498 {"ds","============","DATA SOCKET MENU",NULL
,NOARG
},
499 {"ds/showinfo","","show ds info",showinfo
,NOARG
|WITHFILE
},
502 static void delep (int fd
, void* data
, void *descr
)
504 if (fd
>=0) remove_fd(fd
);
505 if (data
) free(data
);
506 if (descr
) free(descr
);
509 void start_datasock(void)
511 modfun
.modname
=swmi
.swmname
=MODULENAME
;
512 swmi
.swmnopts
=Nlong_options
;
513 swmi
.swmopts
=long_options
;
515 swmi
.parseopt
=parseopt
;
517 swmi
.handle_input
=handle_input
;
518 swmi
.cleanup
=cleanup
;
519 modfun
.sender
=send_datasock
;
520 modfun
.newport
=newport
;
522 modfun
.delport
=closeport
;