Fixed some details on help messages
[vde.git] / vde-2 / datasock.c
blobd12b0f253c29caccb5befec0680a85b555a15b24
1 /* Copyright 2005 Renzo Davoli - VDE-2
2 * --pidfile/-p and cleanup management by Mattia Belletti (C) 2004.
3 * Licensed under the GPLv2
4 */
6 #include <config.h>
7 #include <stdio.h>
8 #include <fcntl.h>
9 #include <errno.h>
10 #include <string.h>
11 #include <unistd.h>
12 #include <syslog.h>
13 #include <stdlib.h>
14 #include <stdint.h>
15 #include <libgen.h>
16 #include <sys/types.h>
17 #include <sys/stat.h>
18 #include <sys/ioctl.h>
19 #include <sys/socket.h>
20 #include <sys/types.h>
21 #include <linux/un.h>
22 #include <net/if.h>
23 #include <stdarg.h>
24 #define _GNU_SOURCE
25 #include <getopt.h>
27 #include <linux/if_tun.h>
29 #include <port.h>
30 #include <switch.h>
31 #include <vde.h>
32 #include <sockutils.h>
33 #include <consmgmt.h>
36 static struct swmodule swmi;
37 static struct mod_support modfun;
38 static unsigned int ctl_type;
39 static unsigned int wd_type;
40 static unsigned int data_type;
41 static int mode = 0700;
43 static char *ctl_socket;
45 #define MODULENAME "unix prog"
47 #define SWITCH_MAGIC 0xfeedface
48 #define REQBUFLEN 256
50 enum request_type { REQ_NEW_CONTROL, REQ_NEW_PORT0 };
52 struct request_v1 {
53 uint32_t magic;
54 enum request_type type;
55 union {
56 struct {
57 unsigned char addr[ETH_ALEN];
58 struct sockaddr_un name;
59 } new_control;
60 } u;
61 char description[];
64 struct request_v3 {
65 uint32_t magic;
66 uint32_t version;
67 enum request_type type;
68 struct sockaddr_un sock;
69 char description[];
72 union request {
73 struct request_v1 v1;
74 struct request_v3 v3;
77 static void send_datasock(int fd, int ctl_fd, void *packet, int len, void *data, int port)
79 int n;
81 struct sockaddr *dst=(struct sockaddr *)data;
83 n = sendto(fd, packet, len, 0, dst, sizeof(struct sockaddr_un));
84 if(n != len){
85 if(errno != EAGAIN) printlog(LOG_WARNING,"send_sockaddr port %d: %s",port,strerror(errno));
89 static void closeport(int fd, int portno)
91 if (fd>0)
92 remove_fd(fd);
95 static int newport(int fd, int portno)
97 int data_fd;
98 struct sockaddr_un sun;
99 if((data_fd = socket(PF_UNIX, SOCK_DGRAM, 0)) < 0){
100 printlog(LOG_ERR,"socket: %s",strerror(errno));
101 return -1;
103 if(fcntl(data_fd, F_SETFL, O_NONBLOCK) < 0){
104 printlog(LOG_ERR,"Setting O_NONBLOCK on data fd %s",strerror(errno));
105 return -1;
107 sun.sun_family = AF_UNIX;
108 snprintf(sun.sun_path,UNIX_PATH_MAX,"%s/%03d",ctl_socket,portno);
109 if ((unlink(sun.sun_path) < 0 && errno != ENOENT) ||
110 bind(data_fd, (struct sockaddr *) &sun, sizeof(sun)) < 0){
111 printlog(LOG_ERR,"Binding to data socket %s",strerror(errno));
112 close_ep(portno-1,fd);
113 return -1;
115 chmod(sun.sun_path,mode);
117 add_fd(data_fd,data_type,portno);
119 return data_fd;
122 static void *memdup(void *src,int size)
124 void *dst=malloc(size);
125 if (dst != NULL)
126 memcpy(dst,src,size);
127 return dst;
130 #define GETFILEOWNER(PATH) ({\
131 struct stat s; \
132 (stat((PATH),&s)?-1:s.st_uid); \
135 static int checksockperm(char *path,char *lpath)
137 int rvuid=0;
138 if (access(path,R_OK | W_OK) != 0)
139 return -1;
140 if (geteuid() == 0) { /* switch run by root */
141 int luid;
142 if ((rvuid=GETFILEOWNER(path)) < 0)
143 return -1;
144 luid=GETFILEOWNER(lpath);
145 if (luid > 0 && luid != rvuid) {
146 errno=EADDRINUSE;
147 return -1;
150 return rvuid;
153 static int new_port_v1_v3(int fd, int type_port,
154 struct sockaddr_un *sun_out)
156 int n, port;
157 enum request_type type = type_port & 0xff;
158 int port_request=type_port >> 8;
159 int cluid=-1;
160 struct sockaddr_un sun_in;
161 switch(type){
162 case REQ_NEW_PORT0:
163 port_request= -1;
164 /* no break: falltrough */
165 case REQ_NEW_CONTROL:
166 port = setup_ep(port_request, fd, memdup(sun_out,sizeof(struct sockaddr_un)), &modfun);
167 if(port<0) {
168 remove_fd(fd);
169 return -1;
171 sun_in.sun_family = AF_UNIX;
172 snprintf(sun_in.sun_path,UNIX_PATH_MAX,"%s/%03d",ctl_socket,port);
173 if (sun_out->sun_path[0] != 0) { //not for unnamed sockets
174 if ((cluid=checksockperm(sun_out->sun_path,sun_in.sun_path)) < 0) {
175 printlog(LOG_WARNING,"Data_out socket permission: %s",strerror(errno));
176 close_ep(port,fd);
177 return -1;
180 n = write(fd, &sun_in, sizeof(sun_in));
181 if(n != sizeof(sun_in)){
182 printlog(LOG_WARNING,"Sending data socket name %s",strerror(errno));
183 close_ep(port,fd);
184 return -1;
186 if (type==REQ_NEW_PORT0)
187 setmgmtperm(sun_in.sun_path);
188 else if (cluid > 0) {
189 chown(sun_in.sun_path,cluid,-1);
190 chmod(sun_in.sun_path,mode & 0700);
192 return port;
193 break;
194 default:
195 printlog(LOG_WARNING,"Bad request type : %d", type);
196 remove_fd(fd);
197 return -1;
201 static void handle_input(unsigned char type,int fd,int revents,int *arg)
203 if (type == data_type) {
204 struct bipacket packet;
205 struct sockaddr sock;
206 int len, socklen = sizeof(sock);
208 len=recvfrom(fd, &(packet.p), sizeof(struct packet),0, &sock, &socklen);
209 if(len < 0){
210 if (errno == EAGAIN) return;
211 printlog(LOG_WARNING,"Reading data: %s",strerror(errno));
213 else if(len == 0)
214 printlog(LOG_WARNING,"EOF data port: %s",strerror(errno));
215 else if(len >= ETH_HEADER_SIZE)
216 handle_in_packet(*arg, &(packet.p), len);
218 else if (type == wd_type) {
219 char reqbuf[REQBUFLEN+1];
220 union request *req=(union request *)reqbuf;
221 int len;
223 len = read(fd, reqbuf, REQBUFLEN);
224 if (len < 0) {
225 if(errno != EAGAIN){
226 printlog(LOG_WARNING,"Reading request %s", strerror(errno));
227 remove_fd(fd);
229 return;
230 } else if (len > 0) {
231 reqbuf[len]=0;
232 if(req->v1.magic == SWITCH_MAGIC){
233 int port=-1;
234 if(req->v3.version == 3) {
235 *arg=port=new_port_v1_v3(fd, req->v3.type, &(req->v3.sock));
236 setup_description(*arg,fd,strdup(req->v3.description));
238 else if(req->v3.version > 2 || req->v3.version == 2) {
239 printlog(LOG_ERR, "Request for a version %d port, which this "
240 "vde_switch doesn't support", req->v3.version);
241 remove_fd(fd);
243 else {
244 *arg=port=new_port_v1_v3(fd, req->v1.type, &(req->v1.u.new_control.name));
245 setup_description(*arg,fd,strdup(req->v1.description));
248 else {
249 printlog(LOG_WARNING,"V0 request not supported");
250 remove_fd(fd);
251 return;
253 } else {
254 close_ep(*arg,fd);
257 else /*if (type == ctl_type)*/ {
258 struct sockaddr addr;
259 int len, new;
261 len = sizeof(addr);
262 new = accept(fd, &addr, &len);
263 if(new < 0){
264 printlog(LOG_WARNING,"accept %s",strerror(errno));
265 return;
267 if(fcntl(new, F_SETFL, O_NONBLOCK) < 0){
268 printlog(LOG_WARNING,"fcntl - setting O_NONBLOCK %s",strerror(errno));
269 close(new);
270 return;
273 add_fd(new,wd_type,-1);
277 static void cleanup(unsigned char type,int fd,int arg)
279 char path[UNIX_PATH_MAX];
280 if (fd < 0) {
281 snprintf(path,UNIX_PATH_MAX,"%s/ctl",ctl_socket);
282 if(unlink(path) < 0)
283 printlog(LOG_WARNING,"Couldn't remove ctl socket '%s' : %s", ctl_socket, strerror(errno));
284 else if(rmdir(ctl_socket) < 0)
285 printlog(LOG_WARNING,"Couldn't remove ctl dir '%s' : %s", ctl_socket, strerror(errno));
286 } else {
287 if (type == data_type && arg>=0) {
288 snprintf(path,UNIX_PATH_MAX,"%s/%03d",ctl_socket,arg);
289 unlink(path);
291 close(fd);
295 static struct option long_options[] = {
296 {"sock", 1, 0, 's'},
297 {"vdesock", 1, 0, 's'},
298 {"unix", 1, 0, 's'},
299 {"mod", 1, 0, 'm'},
302 #define Nlong_options (sizeof(long_options)/sizeof(struct option));
304 static void usage(void)
306 printf(
307 "(opts from datasock module)\n"
308 " -s, --sock SOCK control directory pathname\n"
309 " -s, --vdesock SOCK Same as --sock SOCK\n"
310 " -s, --unix SOCK Same as --sock SOCK\n"
311 " -m, --mod MODE Standard access mode for comm sockets (octal)\n"
315 static int parseopt(int c, char *optarg)
317 int outc=0;
318 switch (c) {
319 case 's':
320 ctl_socket=strdup(optarg);
321 break;
322 case 'm':
323 sscanf(optarg,"%o",&mode);
324 break;
325 default:
326 outc=c;
328 return outc;
331 static void init(void)
333 int connect_fd;
334 struct sockaddr_un sun;
335 int one = 1;
337 if((connect_fd = socket(PF_UNIX, SOCK_STREAM, 0)) < 0){
338 printlog(LOG_ERR,"socket: %s",strerror(errno));
339 return;
341 if(setsockopt(connect_fd, SOL_SOCKET, SO_REUSEADDR, (char *) &one,
342 sizeof(one)) < 0){
343 printlog(LOG_ERR,"setsockopt: %s",strerror(errno));
344 return;
346 if(fcntl(connect_fd, F_SETFL, O_NONBLOCK) < 0){
347 printlog(LOG_ERR,"Setting O_NONBLOCK on connection fd: %s",strerror(errno));
348 return;
350 if (mkdir(ctl_socket, (mode | 0100) & 0755) < 0) {
351 printlog(LOG_ERR,"creating vde ctl dir: %s",strerror(errno));
352 return;
354 sun.sun_family = AF_UNIX;
355 snprintf(sun.sun_path,UNIX_PATH_MAX,"%s/ctl",ctl_socket);
356 if(bind(connect_fd, (struct sockaddr *) &sun, sizeof(sun)) < 0){
357 if((errno == EADDRINUSE) && still_used(&sun)) return;
358 else if(bind(connect_fd, (struct sockaddr *) &sun, sizeof(sun)) < 0){
359 printlog(LOG_ERR,"bind %s",strerror(errno));
360 return;
363 chmod(sun.sun_path,mode);
364 if(listen(connect_fd, 15) < 0){
365 printlog(LOG_ERR,"listen: %s",strerror(errno));
366 return;
368 ctl_type=add_type(&swmi,0);
369 wd_type=add_type(&swmi,0);
370 data_type=add_type(&swmi,1);
371 add_fd(connect_fd,ctl_type,-1);
374 static int showinfo(int fd)
376 printoutc(fd,"ctl dir %s",ctl_socket);
377 printoutc(fd,"std mode 0%03o",mode);
378 return 0;
381 static struct comlist cl[]={
382 {"ds","============","DATA SOCKET MENU",NULL,NOARG},
383 {"ds/showinfo","","show ds info",showinfo,NOARG|WITHFD},
386 static void delep (int fd, void* data, void *descr)
388 if (fd>=0) remove_fd(fd);
389 if (data) free(data);
390 if (descr) free(descr);
393 void start_datasock(void)
395 ctl_socket = (geteuid()==0)?VDESTDSOCK:VDETMPSOCK;
396 modfun.modname=swmi.swmname=MODULENAME;
397 swmi.swmnopts=Nlong_options;
398 swmi.swmopts=long_options;
399 swmi.usage=usage;
400 swmi.parseopt=parseopt;
401 swmi.init=init;
402 swmi.handle_input=handle_input;
403 swmi.cleanup=cleanup;
404 modfun.sender=send_datasock;
405 modfun.newport=newport;
406 modfun.delep=delep;
407 modfun.delport=closeport;
408 ADDCL(cl);
409 add_swm(&swmi);