2 * libvdeplug - A library to connect to a VDE Switch.
3 * Copyright (C) 2006 Renzo Davoli, University of Bologna
5 * This library is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU Lesser General Public License as published by
7 * the Free Software Foundation version 2.1 of the License, or (at
8 * your option) any later version.
10 * This library is distributed in the hope that it will be useful, but
11 * WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser
13 * General Public License for more details.
15 * You should have received a copy of the GNU Lesser General Public
16 * License along with this library; if not, write to the Free Software
17 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
21 #include <sys/socket.h>
29 #include <sys/types.h>
36 #include <vdecommon.h>
38 #include <libvdeplug.h>
42 #define AF_IPN 34 /* IPN sockets */
47 #define AF_NETBEUI PF_NETBEUI
52 #define AF_IPN_STOLEN AF_NETBEUI /* IPN temporary sockets */
53 #define PF_IPN_STOLEN AF_IPN_STOLEN
57 #define IPN_SO_DESCR 1
60 /* Fallback names for the control socket, NULL-terminated array of absolute
62 char *fallback_sockname
[] = {
63 "/var/run/vde.ctl/ctl",
69 /* Fallback directories for the data socket, NULL-terminated array of absolute
70 * directory names, with no trailing /. */
71 const char *fallback_dirname
[] = {
81 struct sockaddr_un inpath
;
84 #define SWITCH_MAGIC 0xfeedface
87 enum request_type
{ REQ_NEW_CONTROL
, REQ_NEW_PORT0
};
92 enum request_type type
;
93 struct sockaddr_un sock
;
94 char description
[MAXDESCR
];
95 } __attribute__((packed
));
97 VDECONN
*vde_open_real(char *given_sockname
, char *descr
,int interface_version
,
98 struct vde_open_args
*open_args
)
100 struct vdeconn
*conn
;
101 struct passwd
*callerpwd
;
102 struct request_v3 req
;
104 static struct sockaddr_un sockun
;
105 static struct sockaddr_un dataout
;
111 char real_sockname
[PATH_MAX
];
112 char *sockname
= real_sockname
;
114 if (open_args
!= NULL
) {
115 if (interface_version
== 1) {
116 port
=open_args
->port
;
117 group
=open_args
->group
;
118 mode
=open_args
->mode
;
126 if ((conn
=calloc(1,sizeof(struct vdeconn
)))==NULL
)
132 callerpwd
=getpwuid(getuid());
133 req
.type
= REQ_NEW_CONTROL
;
134 if (given_sockname
== NULL
|| *given_sockname
== '\0')
135 given_sockname
= NULL
;
138 if(given_sockname
[strlen(given_sockname
)-1] == ']' && (split
=rindex(given_sockname
,'[')) != NULL
) {
143 req
.type
= REQ_NEW_PORT0
;
144 if (*given_sockname
==0)
145 given_sockname
= NULL
;
149 /* Canonicalize the sockname: we need to send an absolute pathname to the
150 * switch (we don't know its cwd) for the data socket. Appending
151 * given_sockname to getcwd() would be enough, but we could end up with a
152 * name longer than PATH_MAX that couldn't be used as sun_path. */
153 if (given_sockname
&& vde_realpath(given_sockname
, real_sockname
) == NULL
)
160 if((conn
->fddata
= socket(AF_IPN
,SOCK_RAW
,IPN_ANY
)) >= 0) {
161 /* IPN service exists */
162 sockun
.sun_family
= AF_IPN
;
164 if((conn
->fddata
= socket(AF_IPN_STOLEN
,SOCK_RAW
,IPN_ANY
)) >= 0) {
165 /* IPN_STOLEN service exists */
166 sockun
.sun_family
= AF_IPN_STOLEN
;
168 if (conn
->fddata
>= 0) {
169 if (port
!= 0 || req
.type
== REQ_NEW_PORT0
)
170 setsockopt(conn
->fddata
,0,IPN_SO_PORT
,&port
,sizeof(port
));
171 /* If we're given a sockname, just try it */
174 snprintf(sockun
.sun_path
, sizeof(sockun
.sun_path
), "%s", sockname
);
175 res
= connect(conn
->fddata
, (struct sockaddr
*) &sockun
, sizeof(sockun
));
177 /* Else try all the fallback socknames, one by one */
181 for (i
= 0, res
= -1; fallback_sockname
[i
] && (res
!= 0); i
++)
183 snprintf(sockun
.sun_path
, sizeof(sockun
.sun_path
), "%s", fallback_sockname
[i
]);
184 res
= connect(conn
->fddata
, (struct sockaddr
*) &sockun
, sizeof(sockun
));
188 /* If one of the connect succeeded, we're done */
191 snprintf(req
.description
,MAXDESCR
,"%s user=%s PID=%d %s",
192 descr
,(callerpwd
!= NULL
)?callerpwd
->pw_name
:"??",
193 pid
,getenv("SSH_CLIENT")?getenv("SSH_CLIENT"):"");
194 setsockopt(conn
->fddata
,0,IPN_SO_DESCR
,req
.description
,
195 strlen(req
.description
+1));
196 *(conn
->inpath
.sun_path
)=0; /*null string, do not delete "return path"*/
202 if((conn
->fdctl
= socket(AF_UNIX
, SOCK_STREAM
, 0)) < 0){
206 if((conn
->fddata
= socket(AF_UNIX
, SOCK_DGRAM
, 0)) < 0){
213 sockun
.sun_family
= AF_UNIX
;
215 /* If we're given a sockname, just try it (remember: sockname is the
216 * canonicalized version of given_sockname - though we don't strictly need
217 * the canonicalized versiono here). sockname should be the name of a
218 * *directory* which contains the control socket, named ctl. Older
219 * versions of VDE used a socket instead of a directory (so an additional
220 * attempt with %s instead of %s/ctl could be made), but they should
221 * really not be used anymore. */
224 snprintf(sockun
.sun_path
, sizeof(sockun
.sun_path
), "%s/ctl", sockname
);
225 res
= connect(conn
->fdctl
, (struct sockaddr
*) &sockun
, sizeof(sockun
));
227 /* Else try all the fallback socknames, one by one */
231 for (i
= 0, res
= -1; fallback_sockname
[i
] && (res
!= 0); i
++)
233 /* Remember sockname for the data socket directory */
234 sockname
= fallback_sockname
[i
];
235 snprintf(sockun
.sun_path
, sizeof(sockun
.sun_path
), "%s", sockname
);
236 res
= connect(conn
->fdctl
, (struct sockaddr
*) &sockun
, sizeof(sockun
));
250 req
.magic
=SWITCH_MAGIC
;
252 req
.type
=req
.type
+(port
<< 8);
253 req
.sock
.sun_family
=AF_UNIX
;
255 /* First choice, store the return socket from the switch in the control
256 * dir. We assume that given_sockname (hence sockname) is a directory.
257 * Should be a safe assumption unless someone modifies the previous group
258 * of connect() attempts (see the comments above for more information). */
259 memset(req
.sock
.sun_path
, 0, sizeof(req
.sock
.sun_path
));
262 /* Here sockname is the last successful one in the previous step. */
263 sprintf(req
.sock
.sun_path
, "%s/.%05d-%05d", sockname
, pid
, sockno
++);
264 res
=bind(conn
->fddata
, (struct sockaddr
*) &req
.sock
, sizeof (req
.sock
));
266 while (res
< 0 && errno
== EADDRINUSE
);
268 /* It didn't work. So we cycle on the fallback directories until we find a
269 * suitable one (or the list ends). */
273 for (i
= 0, res
= -1; fallback_dirname
[i
] && (res
!= 0); i
++)
275 memset(req
.sock
.sun_path
, 0, sizeof(req
.sock
.sun_path
));
278 sprintf(req
.sock
.sun_path
, "%s/vde.%05d-%05d", fallback_dirname
[i
], pid
, sockno
++);
279 res
= bind(conn
->fddata
, (struct sockaddr
*) &req
.sock
, sizeof (req
.sock
));
281 while (res
< 0 && errno
== EADDRINUSE
);
285 /* Nothing worked, so cleanup and return with an error. */
295 memcpy(&(conn
->inpath
),&req
.sock
,sizeof(req
.sock
));
299 if ((gs
=getgrnam(group
)) == NULL
)
303 chown(conn
->inpath
.sun_path
,-1,gid
);
305 chmod(conn
->inpath
.sun_path
,mode
);
307 snprintf(req
.description
,MAXDESCR
,"%s user=%s PID=%d %s SOCK=%s",
308 descr
,(callerpwd
!= NULL
)?callerpwd
->pw_name
:"??",
309 pid
,getenv("SSH_CLIENT")?getenv("SSH_CLIENT"):"",req
.sock
.sun_path
);
311 if (send(conn
->fdctl
,&req
,sizeof(req
)-MAXDESCR
+strlen(req
.description
),0) < 0) {
320 if (recv(conn
->fdctl
,&(dataout
),sizeof(struct sockaddr_un
),0)<0) {
329 if (connect(conn
->fddata
,(struct sockaddr
*)&(dataout
),sizeof(struct sockaddr_un
))<0) {
337 chmod(dataout
.sun_path
,mode
);
342 ssize_t
vde_recv(VDECONN
*conn
,void *buf
,size_t len
,int flags
)
344 if (__builtin_expect(conn
!=0,1))
345 return recv(conn
->fddata
,buf
,len
,0);
352 ssize_t
vde_send(VDECONN
*conn
,const void *buf
,size_t len
,int flags
)
354 if (__builtin_expect(conn
!=0,1))
355 return send(conn
->fddata
,buf
,len
,0);
362 int vde_datafd(VDECONN
*conn
)
364 if (__builtin_expect(conn
!=0,1))
372 int vde_ctlfd(VDECONN
*conn
)
374 if (__builtin_expect(conn
!=0,1))
382 int vde_close(VDECONN
*conn
)
384 if (__builtin_expect(conn
!=0,1)) {
385 if (*(conn
->inpath
.sun_path
))
386 unlink(conn
->inpath
.sun_path
);