mirror of
https://github.com/xen-project/xen.git
synced 2026-10-29 02:30:03 +01:00
and optionally running in user-space. Signed-off-by: Mike Wray <mike.wray@hp.com>
413 lines
11 KiB
C
413 lines
11 KiB
C
/*
|
|
* Copyright (C) 2003 - 2004 Mike Wray <mike.wray@hp.com>.
|
|
*
|
|
* This library is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU Lesser General Public License as
|
|
* published by the Free Software Foundation; either version 2.1 of the
|
|
* License, or (at your option) any later version. This library is
|
|
* distributed in the hope that it will be useful, but WITHOUT ANY
|
|
* WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
|
* FITNESS FOR A PARTICULAR PURPOSE.
|
|
* See the GNU Lesser General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU Lesser General Public License
|
|
* along with this library; if not, write to the Free Software Foundation,
|
|
* Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|
*/
|
|
|
|
#include <stdlib.h>
|
|
#include <errno.h>
|
|
#include <unistd.h>
|
|
#include <sys/socket.h>
|
|
#include <netinet/in.h>
|
|
#include <arpa/inet.h>
|
|
|
|
#include "allocate.h"
|
|
#include "connection.h"
|
|
#include "file_stream.h"
|
|
#include "socket_stream.h"
|
|
|
|
#define MODULE_NAME "conn"
|
|
#define DEBUG 1
|
|
#undef DEBUG
|
|
#include "debug.h"
|
|
|
|
/** Initialize a file stream from a file desciptor.
|
|
*
|
|
* @param fd file descriptor
|
|
* @param mode file mode
|
|
* @param buffered make the stream buffered if 1, unbuffered if 0
|
|
* @param io return parameter for the stream
|
|
* @return 0 on success, error code otherwise
|
|
*/
|
|
int stream_init(int fd, const char *mode, int buffered, IOStream **io){
|
|
int err = 0;
|
|
*io = file_stream_fdopen(fd, mode);
|
|
if(!*io){
|
|
err = -errno;
|
|
perror("fdopen");
|
|
goto exit;
|
|
}
|
|
if(!buffered){
|
|
// Make unbuffered.
|
|
err = file_stream_setvbuf(*io, NULL, _IONBF, 0);
|
|
if(err){
|
|
err = -errno;
|
|
perror("setvbuf");
|
|
goto exit;
|
|
}
|
|
}
|
|
exit:
|
|
if(err && *io){
|
|
IOStream_close(*io);
|
|
*io = NULL;
|
|
}
|
|
return err;
|
|
}
|
|
|
|
ConnList * ConnList_add(ConnList *l, Conn *conn){
|
|
ConnList *v;
|
|
v = ALLOCATE(ConnList);
|
|
v->conn = conn;
|
|
v->next =l;
|
|
return v;
|
|
}
|
|
|
|
ConnList * ConnList_del(ConnList *l, Conn *conn){
|
|
ConnList *prev, *curr, *next;
|
|
for(prev = NULL, curr = l; curr; prev = curr, curr = next){
|
|
next = curr->next;
|
|
if(curr->conn == conn){
|
|
if(prev){
|
|
prev->next = curr->next;
|
|
} else {
|
|
l = curr->next;
|
|
}
|
|
}
|
|
}
|
|
return l;
|
|
}
|
|
|
|
void ConnList_close(ConnList *l){
|
|
for( ; l; l = l->next){
|
|
Conn_close(l->conn);
|
|
}
|
|
}
|
|
|
|
void ConnList_select(ConnList *l, SelectSet *set){
|
|
for( ; l; l = l->next){
|
|
Conn_select(l->conn, set);
|
|
}
|
|
}
|
|
|
|
/** Handle connections according to a select set.
|
|
*
|
|
* @param set indicates ready connections
|
|
*/
|
|
ConnList * ConnList_handle(ConnList *l, SelectSet *set){
|
|
ConnList *prev, *curr, *next;
|
|
Conn *conn;
|
|
int err;
|
|
|
|
for(prev = NULL, curr = l; curr; prev = curr, curr = next){
|
|
next = curr->next;
|
|
conn = curr->conn;
|
|
err = Conn_handle(conn, set);
|
|
if(err){
|
|
if(prev){
|
|
prev->next = curr->next;
|
|
} else {
|
|
l = curr->next;
|
|
}
|
|
}
|
|
}
|
|
return l;
|
|
}
|
|
|
|
Conn *Conn_new(int (*fn)(Conn *conn, int mode), void *data){
|
|
Conn *conn;
|
|
conn = ALLOCATE(Conn);
|
|
conn->fn = fn;
|
|
conn->data = data;
|
|
return conn;
|
|
}
|
|
|
|
int Conn_handler(Conn *conn, int mode){
|
|
int err = 0;
|
|
dprintf(">\n");
|
|
if(conn->fn){
|
|
err = conn->fn(conn, mode);
|
|
} else {
|
|
dprintf("> no handler\n");
|
|
err = -ENOSYS;
|
|
}
|
|
if(err < 0){
|
|
dprintf("> err=%d, closing %d\n", err, conn->sock);
|
|
Conn_close(conn);
|
|
}
|
|
dprintf("< err=%d\n", err);
|
|
return err;
|
|
}
|
|
|
|
int Conn_handle(Conn *conn, SelectSet *set){
|
|
int err = 0;
|
|
int mode = SelectSet_in(set, conn->sock);
|
|
|
|
dprintf("> sock=%d mode=%d\n", conn->sock, mode);
|
|
if(mode){
|
|
err = Conn_handler(conn, mode);
|
|
|
|
}
|
|
return err;
|
|
}
|
|
|
|
void Conn_select(Conn *conn, SelectSet *set){
|
|
dprintf("> sock=%d\n", conn->sock);
|
|
SelectSet_add(set, conn->sock, conn->mode);
|
|
}
|
|
|
|
/** Initialize a connection.
|
|
*
|
|
* @param conn connection
|
|
* @param sock socket
|
|
* @param ipaddr ip address
|
|
* @return 0 on success, error code otherwise
|
|
*/
|
|
int Conn_init(Conn *conn, int sock, int type, int mode, struct sockaddr_in addr){
|
|
int err = 0;
|
|
conn->addr = addr;
|
|
conn->type = type;
|
|
conn->mode = mode;
|
|
conn->sock = sock;
|
|
if(type == SOCK_STREAM){
|
|
err = stream_init(sock, "r", 0, &conn->in);
|
|
if(err) goto exit;
|
|
err = stream_init(sock, "w", 0, &conn->out);
|
|
if(err) goto exit;
|
|
} else {
|
|
conn->in = socket_stream_new(sock);
|
|
conn->out = socket_stream_new(sock);
|
|
socket_stream_set_addr(conn->out, &addr);
|
|
}
|
|
exit:
|
|
if(err) eprintf("< err=%d\n", err);
|
|
return err;
|
|
}
|
|
|
|
/** Open a connection.
|
|
*
|
|
* @param conn connection
|
|
* @param socktype socket type
|
|
* @param ipaddr ip address to connect to
|
|
* @param port port
|
|
* @return 0 on success, error code otherwise
|
|
*/
|
|
int Conn_connect(Conn *conn, int socktype, struct in_addr ipaddr, uint16_t port){
|
|
int err = 0;
|
|
int sock;
|
|
struct sockaddr_in addr_in;
|
|
struct sockaddr *addr = (struct sockaddr *)&addr_in;
|
|
socklen_t addr_n = sizeof(addr_in);
|
|
dprintf("> addr=%s:%d\n", inet_ntoa(ipaddr), ntohs(port));
|
|
sock = socket(AF_INET, socktype, 0);
|
|
if(sock < 0){
|
|
err = -errno;
|
|
goto exit;
|
|
}
|
|
addr_in.sin_family = AF_INET;
|
|
addr_in.sin_addr = ipaddr;
|
|
addr_in.sin_port = port;
|
|
err = connect(sock, addr, addr_n);
|
|
if(err) goto exit;
|
|
err = Conn_init(conn, sock, socktype, 0, addr_in);
|
|
exit:
|
|
if(err){
|
|
perror("Conn_connect");
|
|
eprintf("< err=%d\n", err);
|
|
}
|
|
return err;
|
|
}
|
|
|
|
/** Close a connection.
|
|
*
|
|
* @param conn connection
|
|
*/
|
|
void Conn_close(Conn *conn){
|
|
if(!conn) return;
|
|
if(conn->in) IOStream_close(conn->in);
|
|
if(conn->out) IOStream_close(conn->out);
|
|
shutdown(conn->sock, 2);
|
|
}
|
|
|
|
/** Set socket option to reuse address.
|
|
*/
|
|
int setsock_reuse(int sock, int val){
|
|
int err = 0;
|
|
err = setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &val, sizeof(val));
|
|
if(err < 0){
|
|
err = -errno;
|
|
perror("setsockopt SO_REUSEADDR");
|
|
}
|
|
return err;
|
|
}
|
|
|
|
/** Set socket broadcast option.
|
|
*/
|
|
int setsock_broadcast(int sock, int val){
|
|
int err = 0;
|
|
err = setsockopt(sock, SOL_SOCKET, SO_BROADCAST, &val, sizeof(val));
|
|
if(err < 0){
|
|
err = -errno;
|
|
perror("setsockopt SO_BROADCAST");
|
|
}
|
|
return err;
|
|
}
|
|
|
|
/** Join a socket to a multicast group.
|
|
*/
|
|
int setsock_multicast(int sock, uint32_t iaddr, uint32_t maddr){
|
|
int err = 0;
|
|
struct ip_mreqn mreq = {};
|
|
int mloop = 0;
|
|
// See 'man 7 ip' for these options.
|
|
mreq.imr_multiaddr.s_addr = maddr; // IP multicast address.
|
|
mreq.imr_address.s_addr = iaddr; // Interface IP address.
|
|
mreq.imr_ifindex = 0; // Interface index (0 means any).
|
|
err = setsockopt(sock, SOL_IP, IP_MULTICAST_LOOP, &mloop, sizeof(mloop));
|
|
if(err < 0){
|
|
err = -errno;
|
|
perror("setsockopt IP_MULTICAST_LOOP");
|
|
goto exit;
|
|
}
|
|
err = setsockopt(sock, SOL_IP, IP_ADD_MEMBERSHIP, &mreq, sizeof(mreq));
|
|
if(err < 0){
|
|
err = -errno;
|
|
perror("setsockopt IP_ADD_MEMBERSHIP");
|
|
goto exit;
|
|
}
|
|
exit:
|
|
return err;
|
|
}
|
|
|
|
/** Set a socket's multicast ttl (default is 1).
|
|
*/
|
|
int setsock_multicast_ttl(int sock, uint8_t ttl){
|
|
int err = 0;
|
|
err = setsockopt(sock, SOL_IP, IP_MULTICAST_TTL, &ttl, sizeof(ttl));
|
|
if(err < 0){
|
|
err = -errno;
|
|
perror("setsockopt IP_MULTICAST_TTL");
|
|
}
|
|
return err;
|
|
}
|
|
|
|
int setsock_pktinfo(int sock, int val){
|
|
int err = 0;
|
|
err = setsockopt(sock, SOL_IP, IP_PKTINFO, &val, sizeof(val));
|
|
if(err < 0){
|
|
err = -errno;
|
|
perror("setsockopt IP_PKTINFO");
|
|
}
|
|
return err;
|
|
}
|
|
|
|
char * socket_flags(int flags){
|
|
static char s[6];
|
|
int i = 0;
|
|
s[i++] = (flags & VSOCK_CONNECT ? 'c' : '-');
|
|
s[i++] = (flags & VSOCK_BIND ? 'b' : '-');
|
|
s[i++] = (flags & VSOCK_REUSE ? 'r' : '-');
|
|
s[i++] = (flags & VSOCK_BROADCAST ? 'B' : '-');
|
|
s[i++] = (flags & VSOCK_MULTICAST ? 'M' : '-');
|
|
s[i++] = '\0';
|
|
return s;
|
|
}
|
|
|
|
/** Create a socket.
|
|
* The flags can include VSOCK_REUSE, VSOCK_BROADCAST, VSOCK_CONNECT.
|
|
*
|
|
* @param socktype socket type
|
|
* @param saddr address
|
|
* @param port port
|
|
* @param flags flags
|
|
* @param val return value for the socket connection
|
|
* @return 0 on success, error code otherwise
|
|
*/
|
|
int create_socket(int socktype, uint32_t saddr, uint32_t port, int flags, int *val){
|
|
int err = 0;
|
|
int sock = 0;
|
|
struct sockaddr_in addr_in;
|
|
struct sockaddr *addr = (struct sockaddr *)&addr_in;
|
|
socklen_t addr_n = sizeof(addr_in);
|
|
int reuse, bcast;
|
|
|
|
//dprintf(">\n");
|
|
reuse = (flags & VSOCK_REUSE);
|
|
bcast = (flags & VSOCK_BROADCAST);
|
|
addr_in.sin_family = AF_INET;
|
|
addr_in.sin_addr.s_addr = saddr;
|
|
addr_in.sin_port = port;
|
|
dprintf("> flags=%s addr=%s port=%d\n", socket_flags(flags),
|
|
inet_ntoa(addr_in.sin_addr), ntohs(addr_in.sin_port));
|
|
|
|
sock = socket(AF_INET, socktype, 0);
|
|
if(sock < 0){
|
|
err = -errno;
|
|
goto exit;
|
|
}
|
|
if(reuse){
|
|
err = setsock_reuse(sock, reuse);
|
|
if(err < 0) goto exit;
|
|
}
|
|
if(bcast){
|
|
err = setsock_broadcast(sock, bcast);
|
|
if(err < 0) goto exit;
|
|
}
|
|
if(flags & VSOCK_CONNECT){
|
|
err = connect(sock, addr, addr_n);
|
|
if(err < 0){
|
|
err = -errno;
|
|
perror("connect");
|
|
goto exit;
|
|
}
|
|
}
|
|
if(flags & VSOCK_BIND){
|
|
err = bind(sock, addr, addr_n);
|
|
if(err < 0){
|
|
err = -errno;
|
|
perror("bind");
|
|
goto exit;
|
|
}
|
|
}
|
|
{
|
|
struct sockaddr_in self = {};
|
|
socklen_t self_n = sizeof(self);
|
|
getsockname(sock, (struct sockaddr *)&self, &self_n);
|
|
dprintf("> sockname sock=%d addr=%s port=%d reuse=%d bcast=%d\n",
|
|
sock, inet_ntoa(self.sin_addr), ntohs(self.sin_port),
|
|
reuse, bcast);
|
|
}
|
|
exit:
|
|
*val = (err ? -1 : sock);
|
|
//dprintf("< err=%d\n", err);
|
|
return err;
|
|
}
|
|
|
|
int Conn_socket(int socktype, uint32_t saddr, uint32_t port, int flags, Conn **val){
|
|
int err;
|
|
int sock;
|
|
struct sockaddr_in addr_in;
|
|
Conn *conn;
|
|
|
|
err = create_socket(socktype, saddr, port, flags, &sock);
|
|
if(err) goto exit;
|
|
conn = Conn_new(NULL, NULL);
|
|
addr_in.sin_family = AF_INET;
|
|
addr_in.sin_addr.s_addr = saddr;
|
|
addr_in.sin_port = port;
|
|
Conn_init(conn, sock, socktype, 0, addr_in);
|
|
exit:
|
|
*val = (err ? NULL : conn);
|
|
return err;
|
|
}
|