Several patches from Daniel P. Barrange, Red Hat inc. (TNX!)
[vde.git] / vde-2 / datasock.c
blob2c3295084c7d8a40991ebf4e8c663ee7d5270e2f
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 */
8 #include <config.h>
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 <grp.h>
27 #define _GNU_SOURCE
28 #include <getopt.h>
30 //#include <linux/if_tun.h>
32 #include <port.h>
33 #include <switch.h>
34 #include <vde.h>
35 #include <sockutils.h>
36 #include <consmgmt.h>
38 static struct swmodule swmi;
39 static struct mod_support modfun;
40 static unsigned int ctl_type;
41 static unsigned int wd_type;
42 static unsigned int data_type;
43 static int mode = 0700;
45 static char *ctl_socket;
46 static gid_t grp_owner = -1;
48 #define MODULENAME "unix prog"
50 #define DATA_BUF_SIZE 131072
51 #define SWITCH_MAGIC 0xfeedface
52 #define REQBUFLEN 256
54 enum request_type { REQ_NEW_CONTROL, REQ_NEW_PORT0 };
56 struct request_v1 {
57 uint32_t magic;
58 enum request_type type;
59 union {
60 struct {
61 unsigned char addr[ETH_ALEN];
62 struct sockaddr_un name;
63 } new_control;
64 } u;
65 char description[];
68 struct request_v3 {
69 uint32_t magic;
70 uint32_t version;
71 enum request_type type;
72 struct sockaddr_un sock;
73 char description[];
76 union request {
77 struct request_v1 v1;
78 struct request_v3 v3;
81 static int send_datasock(int fd, int ctl_fd, void *packet, int len, void *data, int port)
83 int n;
84 struct sockaddr *dst=(struct sockaddr *)data;
86 n = len - sendto(fd, packet, len, 0, dst, sizeof(struct sockaddr_un));
87 if(n){
88 int rv=errno;
89 #ifndef VDE_PQ
90 if(errno != EAGAIN) printlog(LOG_WARNING,"send_sockaddr port %d: %s",port,strerror(errno));
91 #endif
92 if (n>len)
93 return -rv;
94 else
95 return n;
97 return 0;
100 static void closeport(int fd, int portno)
102 if (fd>0)
103 remove_fd(fd);
106 static int newport(int fd, int portno)
108 int data_fd;
109 struct sockaddr_un sun;
110 #ifdef VDE_DARWIN
111 int sockbufsize = DATA_BUF_SIZE;
112 int optsize = sizeof(sockbufsize);
113 #endif
115 if((data_fd = socket(PF_UNIX, SOCK_DGRAM, 0)) < 0){
116 printlog(LOG_ERR,"socket: %s",strerror(errno));
117 return -1;
119 if(fcntl(data_fd, F_SETFL, O_NONBLOCK) < 0){
120 printlog(LOG_ERR,"Setting O_NONBLOCK on data fd %s",strerror(errno));
121 return -1;
124 #ifdef VDE_DARWIN
125 if(setsockopt(data_fd, SOL_SOCKET, SO_SNDBUF, &sockbufsize, optsize) < 0)
126 printlog(LOG_WARNING, "Warning: setting send buffer size on data fd %d to %d failed, expect packet loss: %s",
127 data_fd, sockbufsize, strerror(errno));
128 if(setsockopt(data_fd, SOL_SOCKET, SO_RCVBUF, &sockbufsize, optsize) < 0)
129 printlog(LOG_WARNING, "Warning: setting send buffer size on data fd %d to %d failed, expect packet loss: %s",
130 data_fd, sockbufsize, strerror(errno));
131 #endif
133 sun.sun_family = AF_UNIX;
134 snprintf(sun.sun_path,sizeof(sun.sun_path),"%s/%03d",ctl_socket,portno);
135 if ((unlink(sun.sun_path) < 0 && errno != ENOENT) ||
136 bind(data_fd, (struct sockaddr *) &sun, sizeof(sun)) < 0){
137 printlog(LOG_ERR,"Binding to data socket %s",strerror(errno));
138 close_ep(portno-1,fd);
139 return -1;
141 chmod(sun.sun_path,mode);
142 if(chown(sun.sun_path,-1,grp_owner) < 0) {
143 printlog(LOG_ERR, "chown: %s", strerror(errno));
144 close_ep(portno-1,fd);
145 return -1;
148 add_fd(data_fd,data_type,portno);
150 return data_fd;
153 static void *memdup(void *src,int size)
155 void *dst=malloc(size);
156 if (dst != NULL)
157 memcpy(dst,src,size);
158 return dst;
161 #define GETFILEOWNER(PATH) ({\
162 struct stat s; \
163 (stat((PATH),&s)?-1:s.st_uid); \
166 static int checksockperm(char *path,char *lpath)
168 int rvuid=0;
169 if (access(path,R_OK | W_OK) != 0)
170 return -1;
171 if (geteuid() == 0) { /* switch run by root */
172 int luid;
173 if ((rvuid=GETFILEOWNER(path)) < 0)
174 return -1;
175 luid=GETFILEOWNER(lpath);
176 if (luid > 0 && luid != rvuid) {
177 errno=EADDRINUSE;
178 return -1;
181 return rvuid;
184 static int new_port_v1_v3(int fd, int type_port,
185 struct sockaddr_un *sun_out)
187 int n, port;
188 enum request_type type = type_port & 0xff;
189 int port_request=type_port >> 8;
190 int cluid=-1;
191 struct sockaddr_un sun_in;
192 switch(type){
193 case REQ_NEW_PORT0:
194 port_request= -1;
195 /* no break: falltrough */
196 case REQ_NEW_CONTROL:
197 port = setup_ep(port_request, fd, memdup(sun_out,sizeof(struct sockaddr_un)), &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 if (sun_out->sun_path[0] != 0) { //not for unnamed sockets
205 if ((cluid=checksockperm(sun_out->sun_path,sun_in.sun_path)) < 0) {
206 printlog(LOG_WARNING,"Data_out socket permission: %s",strerror(errno));
207 close_ep(port,fd);
208 return -1;
211 n = write(fd, &sun_in, sizeof(sun_in));
212 if(n != sizeof(sun_in)){
213 printlog(LOG_WARNING,"Sending data socket name %s",strerror(errno));
214 close_ep(port,fd);
215 return -1;
217 if (type==REQ_NEW_PORT0)
218 setmgmtperm(sun_in.sun_path);
219 else if (cluid > 0) {
220 chown(sun_in.sun_path,cluid,-1);
221 chmod(sun_in.sun_path,mode & 0700);
223 return port;
224 break;
225 default:
226 printlog(LOG_WARNING,"Bad request type : %d", type);
227 remove_fd(fd);
228 return -1;
232 static void handle_input(unsigned char type,int fd,int revents,int *arg)
234 if (type == data_type) {
235 struct bipacket packet;
236 struct sockaddr sock;
237 int len;
238 socklen_t socklen = sizeof(sock);
240 len=recvfrom(fd, &(packet.p), sizeof(struct packet),0, &sock, &socklen);
241 if(len < 0){
242 if (errno == EAGAIN) return;
243 printlog(LOG_WARNING,"Reading data: %s",strerror(errno));
245 else if(len == 0)
246 printlog(LOG_WARNING,"EOF data port: %s",strerror(errno));
247 else if(len >= ETH_HEADER_SIZE)
248 handle_in_packet(*arg, &(packet.p), len);
250 else if (type == wd_type) {
251 char reqbuf[REQBUFLEN+1];
252 union request *req=(union request *)reqbuf;
253 int len;
255 len = read(fd, reqbuf, REQBUFLEN);
256 if (len < 0) {
257 if(errno != EAGAIN){
258 printlog(LOG_WARNING,"Reading request %s", strerror(errno));
259 remove_fd(fd);
261 return;
262 } else if (len > 0) {
263 reqbuf[len]=0;
264 if(req->v1.magic == SWITCH_MAGIC){
265 int port=-1;
266 if(req->v3.version == 3) {
267 *arg=port=new_port_v1_v3(fd, req->v3.type, &(req->v3.sock));
268 setup_description(*arg,fd,strdup(req->v3.description));
270 else if(req->v3.version > 2 || req->v3.version == 2) {
271 printlog(LOG_ERR, "Request for a version %d port, which this "
272 "vde_switch doesn't support", req->v3.version);
273 remove_fd(fd);
275 else {
276 *arg=port=new_port_v1_v3(fd, req->v1.type, &(req->v1.u.new_control.name));
277 setup_description(*arg,fd,strdup(req->v1.description));
280 else {
281 printlog(LOG_WARNING,"V0 request not supported");
282 remove_fd(fd);
283 return;
285 } else {
286 close_ep(*arg,fd);
289 else /*if (type == ctl_type)*/ {
290 struct sockaddr addr;
291 socklen_t len;
292 int new;
294 len = sizeof(addr);
295 new = accept(fd, &addr, &len);
296 if(new < 0){
297 printlog(LOG_WARNING,"accept %s",strerror(errno));
298 return;
300 if(fcntl(new, F_SETFL, O_NONBLOCK) < 0){
301 printlog(LOG_WARNING,"fcntl - setting O_NONBLOCK %s",strerror(errno));
302 close(new);
303 return;
306 add_fd(new,wd_type,-1);
310 static void cleanup(unsigned char type,int fd,int arg)
312 struct sockaddr_un clun;
313 if (fd < 0) {
314 snprintf(clun.sun_path,sizeof(clun.sun_path),"%s/ctl",ctl_socket);
315 if(unlink(clun.sun_path) < 0)
316 printlog(LOG_WARNING,"Couldn't remove ctl socket '%s' : %s", ctl_socket, strerror(errno));
317 else if(rmdir(ctl_socket) < 0)
318 printlog(LOG_WARNING,"Couldn't remove ctl dir '%s' : %s", ctl_socket, strerror(errno));
319 } else {
320 if (type == data_type && arg>=0) {
321 snprintf(clun.sun_path,sizeof(clun.sun_path),"%s/%03d",ctl_socket,arg);
322 unlink(clun.sun_path);
324 close(fd);
328 static struct option long_options[] = {
329 {"sock", 1, 0, 's'},
330 {"vdesock", 1, 0, 's'},
331 {"unix", 1, 0, 's'},
332 {"mod", 1, 0, 'm'},
333 {"group", 1, 0, 'g'},
336 #define Nlong_options (sizeof(long_options)/sizeof(struct option));
338 static void usage(void)
340 printf(
341 "(opts from datasock module)\n"
342 " -s, --sock SOCK control directory pathname\n"
343 " -s, --vdesock SOCK Same as --sock SOCK\n"
344 " -s, --unix SOCK Same as --sock SOCK\n"
345 " -m, --mod MODE Standard access mode for comm sockets (octal)\n"
346 " -g, --group GROUP Group owner for comm sockets\n"
350 static int parseopt(int c, char *optarg)
352 int outc=0;
353 struct group *grp;
354 switch (c) {
355 case 's':
356 ctl_socket=strdup(optarg);
357 break;
358 case 'm':
359 sscanf(optarg,"%o",&mode);
360 break;
361 case 'g':
362 if (!(grp = getgrnam(optarg))) {
363 fprintf(stderr, "No such group '%s'\n", optarg);
364 exit(1);
366 grp_owner=grp->gr_gid;
367 break;
368 default:
369 outc=c;
371 return outc;
374 static void init(void)
376 int connect_fd;
377 struct sockaddr_un sun;
378 int one = 1;
380 if((connect_fd = socket(PF_UNIX, SOCK_STREAM, 0)) < 0){
381 printlog(LOG_ERR,"socket: %s",strerror(errno));
382 return;
384 if(setsockopt(connect_fd, SOL_SOCKET, SO_REUSEADDR, (char *) &one,
385 sizeof(one)) < 0){
386 printlog(LOG_ERR,"setsockopt: %s",strerror(errno));
387 return;
389 if(fcntl(connect_fd, F_SETFL, O_NONBLOCK) < 0){
390 printlog(LOG_ERR,"Setting O_NONBLOCK on connection fd: %s",strerror(errno));
391 return;
393 if (mkdir(ctl_socket, (mode | 0100) & 0755) < 0) {
394 printlog(LOG_ERR,"creating vde ctl dir: %s",strerror(errno));
395 return;
397 sun.sun_family = AF_UNIX;
398 snprintf(sun.sun_path,sizeof(sun.sun_path),"%s/ctl",ctl_socket);
399 if(bind(connect_fd, (struct sockaddr *) &sun, sizeof(sun)) < 0){
400 if((errno == EADDRINUSE) && still_used(&sun)) return;
401 else if(bind(connect_fd, (struct sockaddr *) &sun, sizeof(sun)) < 0){
402 printlog(LOG_ERR,"bind %s",strerror(errno));
403 return;
406 chmod(sun.sun_path,mode);
407 if(chown(sun.sun_path,-1,grp_owner) < 0) {
408 printlog(LOG_ERR, "chown: %s", strerror(errno));
409 return;
411 if(listen(connect_fd, 15) < 0){
412 printlog(LOG_ERR,"listen: %s",strerror(errno));
413 return;
415 ctl_type=add_type(&swmi,0);
416 wd_type=add_type(&swmi,0);
417 data_type=add_type(&swmi,1);
418 add_fd(connect_fd,ctl_type,-1);
421 static int showinfo(int fd)
423 printoutc(fd,"ctl dir %s",ctl_socket);
424 printoutc(fd,"std mode 0%03o",mode);
425 return 0;
428 static struct comlist cl[]={
429 {"ds","============","DATA SOCKET MENU",NULL,NOARG},
430 {"ds/showinfo","","show ds info",showinfo,NOARG|WITHFD},
433 static void delep (int fd, void* data, void *descr)
435 if (fd>=0) remove_fd(fd);
436 if (data) free(data);
437 if (descr) free(descr);
440 void start_datasock(void)
442 ctl_socket = (geteuid()==0)?VDESTDSOCK:VDETMPSOCK;
443 modfun.modname=swmi.swmname=MODULENAME;
444 swmi.swmnopts=Nlong_options;
445 swmi.swmopts=long_options;
446 swmi.usage=usage;
447 swmi.parseopt=parseopt;
448 swmi.init=init;
449 swmi.handle_input=handle_input;
450 swmi.cleanup=cleanup;
451 modfun.sender=send_datasock;
452 modfun.newport=newport;
453 modfun.delep=delep;
454 modfun.delport=closeport;
455 ADDCL(cl);
456 add_swm(&swmi);