Vde_plug sets automagically the minimum permissions
[vde.git] / vde-2 / src / vde_switch / datasock.c
blobb7ee1506003478f4840a35fa32e1333fe913a22b
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 real_ctl_socket[PATH_MAX];
47 static char *ctl_socket = real_ctl_socket;
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, int ctl_fd, void *packet, int len, void *data, int port)
88 int n;
89 struct sockaddr *dst=(struct sockaddr *)data;
91 n = len - sendto(fd, packet, len, 0, dst, sizeof(struct sockaddr_un));
92 if(n){
93 int rv=errno;
94 #ifndef VDE_PQ
95 if(errno != EAGAIN) printlog(LOG_WARNING,"send_sockaddr port %d: %s",port,strerror(errno));
96 #endif
97 if (n>len)
98 return -rv;
99 else
100 return n;
102 return 0;
105 static void closeport(int fd, int portno)
107 if (fd>0)
108 remove_fd(fd);
111 static int newport(int fd, int portno, uid_t user)
113 int data_fd;
114 struct sockaddr_un sun;
115 #ifdef VDE_DARWIN
116 int sockbufsize = DATA_BUF_SIZE;
117 int optsize = sizeof(sockbufsize);
118 #endif
120 if((data_fd = socket(PF_UNIX, SOCK_DGRAM, 0)) < 0){
121 printlog(LOG_ERR,"socket: %s",strerror(errno));
122 return -1;
124 if(fcntl(data_fd, F_SETFL, O_NONBLOCK) < 0){
125 printlog(LOG_ERR,"Setting O_NONBLOCK on data fd %s",strerror(errno));
126 return -1;
129 #ifdef VDE_DARWIN
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));
136 #endif
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);
144 return -1;
146 if (geteuid() != 0)
147 user = -1;
148 if (user != -1)
149 chmod(sun.sun_path,mode & 0700);
150 else
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);
156 return -1;
159 add_fd(data_fd,data_type,portno);
161 return data_fd;
164 static void *memdup(void *src,int size)
166 void *dst=malloc(size);
167 if (dst != NULL)
168 memcpy(dst,src,size);
169 return dst;
172 #define GETFILEOWNER(PATH) ({\
173 struct stat s; \
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)
180 int n, port;
181 enum request_type type = type_port & 0xff;
182 int port_request=type_port >> 8;
183 uid_t user=-1;
184 struct sockaddr_un sun_in;
185 switch(type){
186 case REQ_NEW_PORT0:
187 port_request= -1;
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
192 remove_fd(fd);
193 return -1;
195 user=GETFILEOWNER(sun_out->sun_path);
197 port = setup_ep(port_request, fd, memdup(sun_out,sizeof(struct sockaddr_un)), user, &modfun);
198 if(port<0) {
199 remove_fd(fd);
200 return -1;
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));
207 close_ep(port,fd);
208 return -1;
210 if (type==REQ_NEW_PORT0)
211 setmgmtperm(sun_in.sun_path);
212 return port;
213 break;
214 default:
215 printlog(LOG_WARNING,"Bad request type : %d", type);
216 remove_fd(fd);
217 return -1;
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;
226 int len;
227 socklen_t socklen = sizeof(sock);
229 len=recvfrom(fd, &(packet.p), sizeof(struct packet),0, &sock, &socklen);
230 if(len < 0){
231 if (errno == EAGAIN) return;
232 printlog(LOG_WARNING,"Reading data: %s",strerror(errno));
234 else if(len == 0)
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;
242 int len;
244 len = read(fd, reqbuf, REQBUFLEN);
245 if (len < 0) {
246 if(errno != EAGAIN){
247 printlog(LOG_WARNING,"Reading request %s", strerror(errno));
248 remove_fd(fd);
250 return;
251 } else if (len > 0) {
252 reqbuf[len]=0;
253 if(req->v1.magic == SWITCH_MAGIC){
254 int port=-1;
255 if(req->v3.version == 3) {
256 port=new_port_v1_v3(fd, req->v3.type, &(req->v3.sock));
257 if (port>=0) {
258 *arg=port;
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);
265 remove_fd(fd);
267 else {
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));
272 else {
273 printlog(LOG_WARNING,"V0 request not supported");
274 remove_fd(fd);
275 return;
277 } else {
278 if (*arg >= 0)
279 close_ep(*arg,fd);
280 else
281 remove_fd(fd);
284 else /*if (type == ctl_type)*/ {
285 struct sockaddr addr;
286 socklen_t len;
287 int new;
289 len = sizeof(addr);
290 new = accept(fd, &addr, &len);
291 if(new < 0){
292 printlog(LOG_WARNING,"accept %s",strerror(errno));
293 return;
295 if(fcntl(new, F_SETFL, O_NONBLOCK) < 0){
296 printlog(LOG_WARNING,"fcntl - setting O_NONBLOCK %s",strerror(errno));
297 close(new);
298 return;
301 add_fd(new,wd_type,-1);
305 static void cleanup(unsigned char type,int fd,int arg)
307 struct sockaddr_un clun;
308 int test_fd;
310 if (fd < 0) {
311 if((test_fd = socket(AF_UNIX, SOCK_STREAM, 0)) < 0){
312 printlog(LOG_ERR,"socket %s",strerror(errno));
314 clun.sun_family=AF_UNIX;
315 snprintf(clun.sun_path,sizeof(clun.sun_path),"%s/ctl",ctl_socket);
316 if(connect(test_fd, (struct sockaddr *) &clun, sizeof(clun))){
317 close(test_fd);
318 if(unlink(clun.sun_path) < 0)
319 printlog(LOG_WARNING,"Could not remove ctl socket '%s': %s", ctl_socket, strerror(errno));
320 else if(rmdir(ctl_socket) < 0)
321 printlog(LOG_WARNING,"Could not remove ctl dir '%s': %s", ctl_socket, strerror(errno));
323 else printlog(LOG_WARNING,"Cleanup not removing files");
324 } else {
325 if (type == data_type && arg>=0) {
326 snprintf(clun.sun_path,sizeof(clun.sun_path),"%s/%03d",ctl_socket,arg);
327 unlink(clun.sun_path);
329 close(fd);
333 #define DIRMODEARG 0x100
335 static struct option long_options[] = {
336 {"sock", 1, 0, 's'},
337 {"vdesock", 1, 0, 's'},
338 {"unix", 1, 0, 's'},
339 {"mod", 1, 0, 'm'},
340 {"mode", 1, 0, 'm'},
341 {"dirmode", 1, 0, DIRMODEARG},
342 {"group", 1, 0, 'g'},
345 #define Nlong_options (sizeof(long_options)/sizeof(struct option));
347 static void usage(void)
349 printf(
350 "(opts from datasock module)\n"
351 " -s, --sock SOCK control directory pathname\n"
352 " -s, --vdesock SOCK Same as --sock SOCK\n"
353 " -s, --unix SOCK Same as --sock SOCK\n"
354 " -m, --mode MODE Permissions for the control socket (octal)\n"
355 " --dirmode MODE Permissions for the sockets directory (octal)\n"
356 " -g, --group GROUP Group owner for comm sockets\n"
360 static int parseopt(int c, char *optarg)
362 int outc=0;
363 struct group *grp;
364 switch (c) {
365 case 's':
366 /* This should return NULL as the path probably does not exist */
367 vde_realpath(optarg, real_ctl_socket);
368 ctl_socket = real_ctl_socket;
369 break;
370 case 'm':
371 sscanf(optarg,"%o",&mode);
372 break;
373 case 'g':
374 if (!(grp = getgrnam(optarg))) {
375 fprintf(stderr, "No such group '%s'\n", optarg);
376 exit(1);
378 grp_owner=grp->gr_gid;
379 break;
380 case DIRMODEARG:
381 sscanf(optarg, "%o", &dirmode);
382 break;
383 default:
384 outc=c;
386 return outc;
389 static void init(void)
391 int connect_fd;
392 struct sockaddr_un sun;
393 int one = 1;
395 /* Set up default modes */
396 if (mode < 0 && dirmode < 0)
398 /* Default values */
399 mode = 00600; /* -rw------- for the ctl socket */
400 dirmode = 02700; /* -rwx--S--- for the directory */
402 else if (mode >= 0 && dirmode < 0)
404 /* If only mode (-m) has been specified, we guess the dirmode from it,
405 * adding the executable bit where needed */
407 # define ADDBIT(mode, conditionmask, add) ((mode & conditionmask) ? ((mode & conditionmask) | add) : (mode & conditionmask))
409 dirmode = 02000 | /* Add also setgid */
410 ADDBIT(mode, 0600, 0100) |
411 ADDBIT(mode, 0060, 0010) |
412 ADDBIT(mode, 0006, 0001);
414 else if (mode < 0 && dirmode >= 0)
416 /* If only dirmode (--dirmode) has been specified, we guess the ctl
417 * socket mode from it, turning off the executable bit everywhere */
418 mode = dirmode & 0666;
421 if((connect_fd = socket(PF_UNIX, SOCK_STREAM, 0)) < 0){
422 printlog(LOG_ERR,"Could not obtain a BSD socket: %s", strerror(errno));
423 return;
425 if(setsockopt(connect_fd, SOL_SOCKET, SO_REUSEADDR, (char *) &one,
426 sizeof(one)) < 0){
427 printlog(LOG_ERR,"Could not set socket options on %d: %s", connect_fd, strerror(errno));
428 return;
430 if(fcntl(connect_fd, F_SETFL, O_NONBLOCK) < 0){
431 printlog(LOG_ERR,"Could not set O_NONBLOCK on connection fd %d: %s", connect_fd, strerror(errno));
432 return;
434 if (((mkdir(ctl_socket, 0777) < 0) && (errno != EEXIST))){
435 printlog(LOG_ERR,"Could not create the VDE ctl directory '%s': %s", ctl_socket, strerror(errno));
436 exit(-1);
438 if(chown(ctl_socket,-1,grp_owner) < 0) {
439 rmdir(ctl_socket);
440 printlog(LOG_ERR, "Could not chown socket '%s': %s", sun.sun_path, strerror(errno));
441 exit(-1);
443 if (chmod(ctl_socket, dirmode) < 0) {
444 printlog(LOG_ERR,"Could not set the VDE ctl directory '%s' permissions: %s", ctl_socket, strerror(errno));
445 exit(-1);
447 sun.sun_family = AF_UNIX;
448 snprintf(sun.sun_path,sizeof(sun.sun_path),"%s/ctl",ctl_socket);
449 if(bind(connect_fd, (struct sockaddr *) &sun, sizeof(sun)) < 0){
450 if((errno == EADDRINUSE) && still_used(&sun)){
451 printlog(LOG_ERR, "Could not bind to socket '%s/ctl': %s", ctl_socket, strerror(errno));
452 exit(-1);
454 else if(bind(connect_fd, (struct sockaddr *) &sun, sizeof(sun)) < 0){
455 printlog(LOG_ERR, "Could not bind to socket '%s/ctl' (second attempt): %s", ctl_socket, strerror(errno));
456 exit(-1);
459 chmod(sun.sun_path,mode);
460 if(chown(sun.sun_path,-1,grp_owner) < 0) {
461 printlog(LOG_ERR, "Could not chown socket '%s': %s", sun.sun_path, strerror(errno));
462 exit(-1);
464 if(listen(connect_fd, 15) < 0){
465 printlog(LOG_ERR,"Could not listen on fd %d: %s", connect_fd, strerror(errno));
466 exit(-1);
468 ctl_type=add_type(&swmi,0);
469 wd_type=add_type(&swmi,0);
470 data_type=add_type(&swmi,1);
471 add_fd(connect_fd,ctl_type,-1);
474 static int showinfo(FILE *fd)
476 printoutc(fd,"ctl dir %s",ctl_socket);
477 printoutc(fd,"std mode 0%03o",mode);
478 return 0;
481 static struct comlist cl[]={
482 {"ds","============","DATA SOCKET MENU",NULL,NOARG},
483 {"ds/showinfo","","show ds info",showinfo,NOARG|WITHFILE},
486 static void delep (int fd, void* data, void *descr)
488 if (fd>=0) remove_fd(fd);
489 if (data) free(data);
490 if (descr) free(descr);
493 void start_datasock(void)
495 ctl_socket = (geteuid()==0)?VDESTDSOCK:VDETMPSOCK;
496 modfun.modname=swmi.swmname=MODULENAME;
497 swmi.swmnopts=Nlong_options;
498 swmi.swmopts=long_options;
499 swmi.usage=usage;
500 swmi.parseopt=parseopt;
501 swmi.init=init;
502 swmi.handle_input=handle_input;
503 swmi.cleanup=cleanup;
504 modfun.sender=send_datasock;
505 modfun.newport=newport;
506 modfun.delep=delep;
507 modfun.delport=closeport;
508 ADDCL(cl);
509 add_swm(&swmi);