#include "net.h"
#include "qemu-char.h"
#include "qemu-common.h"
+#include "qemu-error.h"
#include "qemu-option.h"
#include "qemu_socket.h"
-#include "sysemu.h"
typedef struct NetSocketState {
- VLANClientState *vc;
+ VLANClientState nc;
int fd;
int state; /* 0 = getting length, 1 = getting data */
unsigned int index;
} NetSocketListenState;
/* XXX: we consider we can send the whole packet without blocking */
-static ssize_t net_socket_receive(VLANClientState *vc, const uint8_t *buf, size_t size)
+static ssize_t net_socket_receive(VLANClientState *nc, const uint8_t *buf, size_t size)
{
- NetSocketState *s = vc->opaque;
+ NetSocketState *s = DO_UPCAST(NetSocketState, nc, nc);
uint32_t len;
len = htonl(size);
return send_all(s->fd, buf, size);
}
-static ssize_t net_socket_receive_dgram(VLANClientState *vc, const uint8_t *buf, size_t size)
+static ssize_t net_socket_receive_dgram(VLANClientState *nc, const uint8_t *buf, size_t size)
{
- NetSocketState *s = vc->opaque;
+ NetSocketState *s = DO_UPCAST(NetSocketState, nc, nc);
return sendto(s->fd, (const void *)buf, size, 0,
(struct sockaddr *)&s->dgram_dst, sizeof(s->dgram_dst));
buf += l;
size -= l;
if (s->index >= s->packet_len) {
- qemu_send_packet(s->vc, s->buf, s->packet_len);
+ qemu_send_packet(&s->nc, s->buf, s->packet_len);
s->index = 0;
s->state = 0;
}
qemu_set_fd_handler(s->fd, NULL, NULL, NULL);
return;
}
- qemu_send_packet(s->vc, s->buf, size);
+ qemu_send_packet(&s->nc, s->buf, size);
}
-static int net_socket_mcast_create(struct sockaddr_in *mcastaddr)
+static int net_socket_mcast_create(struct sockaddr_in *mcastaddr, struct in_addr *localaddr)
{
struct ip_mreq imr;
int fd;
return -1;
}
- fd = socket(PF_INET, SOCK_DGRAM, 0);
+ fd = qemu_socket(PF_INET, SOCK_DGRAM, 0);
if (fd < 0) {
perror("socket(PF_INET, SOCK_DGRAM)");
return -1;
/* Add host to multicast group */
imr.imr_multiaddr = mcastaddr->sin_addr;
- imr.imr_interface.s_addr = htonl(INADDR_ANY);
+ if (localaddr) {
+ imr.imr_interface = *localaddr;
+ } else {
+ imr.imr_interface.s_addr = htonl(INADDR_ANY);
+ }
ret = setsockopt(fd, IPPROTO_IP, IP_ADD_MEMBERSHIP,
(const char *)&imr, sizeof(struct ip_mreq));
goto fail;
}
+ /* If a bind address is given, only send packets from that address */
+ if (localaddr != NULL) {
+ ret = setsockopt(fd, IPPROTO_IP, IP_MULTICAST_IF,
+ (const char *)localaddr, sizeof(*localaddr));
+ if (ret < 0) {
+ perror("setsockopt(IP_MULTICAST_IF)");
+ goto fail;
+ }
+ }
+
socket_set_nonblock(fd);
return fd;
fail:
return -1;
}
-static void net_socket_cleanup(VLANClientState *vc)
+static void net_socket_cleanup(VLANClientState *nc)
{
- NetSocketState *s = vc->opaque;
+ NetSocketState *s = DO_UPCAST(NetSocketState, nc, nc);
qemu_set_fd_handler(s->fd, NULL, NULL, NULL);
close(s->fd);
- qemu_free(s);
}
+static NetClientInfo net_dgram_socket_info = {
+ .type = NET_CLIENT_TYPE_SOCKET,
+ .size = sizeof(NetSocketState),
+ .receive = net_socket_receive_dgram,
+ .cleanup = net_socket_cleanup,
+};
+
static NetSocketState *net_socket_fd_init_dgram(VLANState *vlan,
const char *model,
const char *name,
struct sockaddr_in saddr;
int newfd;
socklen_t saddr_len;
+ VLANClientState *nc;
NetSocketState *s;
/* fd passed: multicast: "learn" dgram_dst address from bound address and save it
return NULL;
}
/* clone dgram socket */
- newfd = net_socket_mcast_create(&saddr);
+ newfd = net_socket_mcast_create(&saddr, NULL);
if (newfd < 0) {
/* error already reported by net_socket_mcast_create() */
close(fd);
}
}
- s = qemu_mallocz(sizeof(NetSocketState));
+ nc = qemu_new_net_client(&net_dgram_socket_info, vlan, NULL, model, name);
+
+ snprintf(nc->info_str, sizeof(nc->info_str),
+ "socket: fd=%d (%s mcast=%s:%d)",
+ fd, is_connected ? "cloned" : "",
+ inet_ntoa(saddr.sin_addr), ntohs(saddr.sin_port));
+
+ s = DO_UPCAST(NetSocketState, nc, nc);
+
s->fd = fd;
- s->vc = qemu_new_vlan_client(NET_CLIENT_TYPE_SOCKET,
- vlan, NULL, model, name, NULL,
- net_socket_receive_dgram, NULL, NULL,
- net_socket_cleanup, s);
qemu_set_fd_handler(s->fd, net_socket_send_dgram, NULL, s);
/* mcast: save bound address as dst */
if (is_connected) s->dgram_dst=saddr;
- snprintf(s->vc->info_str, sizeof(s->vc->info_str),
- "socket: fd=%d (%s mcast=%s:%d)",
- fd, is_connected? "cloned" : "",
- inet_ntoa(saddr.sin_addr), ntohs(saddr.sin_port));
return s;
}
qemu_set_fd_handler(s->fd, net_socket_send, NULL, s);
}
+static NetClientInfo net_socket_info = {
+ .type = NET_CLIENT_TYPE_SOCKET,
+ .size = sizeof(NetSocketState),
+ .receive = net_socket_receive,
+ .cleanup = net_socket_cleanup,
+};
+
static NetSocketState *net_socket_fd_init_stream(VLANState *vlan,
const char *model,
const char *name,
int fd, int is_connected)
{
+ VLANClientState *nc;
NetSocketState *s;
- s = qemu_mallocz(sizeof(NetSocketState));
+
+ nc = qemu_new_net_client(&net_socket_info, vlan, NULL, model, name);
+
+ snprintf(nc->info_str, sizeof(nc->info_str), "socket: fd=%d", fd);
+
+ s = DO_UPCAST(NetSocketState, nc, nc);
+
s->fd = fd;
- s->vc = qemu_new_vlan_client(NET_CLIENT_TYPE_SOCKET,
- vlan, NULL, model, name, NULL,
- net_socket_receive, NULL, NULL,
- net_socket_cleanup, s);
- snprintf(s->vc->info_str, sizeof(s->vc->info_str),
- "socket: fd=%d", fd);
+
if (is_connected) {
net_socket_connect(s);
} else {
for(;;) {
len = sizeof(saddr);
- fd = accept(s->fd, (struct sockaddr *)&saddr, &len);
+ fd = qemu_accept(s->fd, (struct sockaddr *)&saddr, &len);
if (fd < 0 && errno != EINTR) {
return;
} else if (fd >= 0) {
if (!s1) {
closesocket(fd);
} else {
- snprintf(s1->vc->info_str, sizeof(s1->vc->info_str),
+ snprintf(s1->nc.info_str, sizeof(s1->nc.info_str),
"socket: connection from %s:%d",
inet_ntoa(saddr.sin_addr), ntohs(saddr.sin_port));
}
s = qemu_mallocz(sizeof(NetSocketListenState));
- fd = socket(PF_INET, SOCK_STREAM, 0);
+ fd = qemu_socket(PF_INET, SOCK_STREAM, 0);
if (fd < 0) {
perror("socket");
return -1;
if (parse_host_port(&saddr, host_str) < 0)
return -1;
- fd = socket(PF_INET, SOCK_STREAM, 0);
+ fd = qemu_socket(PF_INET, SOCK_STREAM, 0);
if (fd < 0) {
perror("socket");
return -1;
} else if (err == EINPROGRESS) {
break;
#ifdef _WIN32
- } else if (err == WSAEALREADY) {
+ } else if (err == WSAEALREADY || err == WSAEINVAL) {
break;
#endif
} else {
s = net_socket_fd_init(vlan, model, name, fd, connected);
if (!s)
return -1;
- snprintf(s->vc->info_str, sizeof(s->vc->info_str),
+ snprintf(s->nc.info_str, sizeof(s->nc.info_str),
"socket: connect to %s:%d",
inet_ntoa(saddr.sin_addr), ntohs(saddr.sin_port));
return 0;
static int net_socket_mcast_init(VLANState *vlan,
const char *model,
const char *name,
- const char *host_str)
+ const char *host_str,
+ const char *localaddr_str)
{
NetSocketState *s;
int fd;
struct sockaddr_in saddr;
+ struct in_addr localaddr, *param_localaddr;
if (parse_host_port(&saddr, host_str) < 0)
return -1;
+ if (localaddr_str != NULL) {
+ if (inet_aton(localaddr_str, &localaddr) == 0)
+ return -1;
+ param_localaddr = &localaddr;
+ } else {
+ param_localaddr = NULL;
+ }
- fd = net_socket_mcast_create(&saddr);
+ fd = net_socket_mcast_create(&saddr, param_localaddr);
if (fd < 0)
return -1;
s->dgram_dst = saddr;
- snprintf(s->vc->info_str, sizeof(s->vc->info_str),
+ snprintf(s->nc.info_str, sizeof(s->nc.info_str),
"socket: mcast=%s:%d",
inet_ntoa(saddr.sin_addr), ntohs(saddr.sin_port));
return 0;
if (qemu_opt_get(opts, "listen") ||
qemu_opt_get(opts, "connect") ||
- qemu_opt_get(opts, "mcast")) {
- qemu_error("listen=, connect= and mcast= is invalid with fd=\n");
+ qemu_opt_get(opts, "mcast") ||
+ qemu_opt_get(opts, "localaddr")) {
+ error_report("listen=, connect=, mcast= and localaddr= is invalid with fd=\n");
return -1;
}
if (qemu_opt_get(opts, "fd") ||
qemu_opt_get(opts, "connect") ||
- qemu_opt_get(opts, "mcast")) {
- qemu_error("fd=, connect= and mcast= is invalid with listen=\n");
+ qemu_opt_get(opts, "mcast") ||
+ qemu_opt_get(opts, "localaddr")) {
+ error_report("fd=, connect=, mcast= and localaddr= is invalid with listen=\n");
return -1;
}
if (qemu_opt_get(opts, "fd") ||
qemu_opt_get(opts, "listen") ||
- qemu_opt_get(opts, "mcast")) {
- qemu_error("fd=, listen= and mcast= is invalid with connect=\n");
+ qemu_opt_get(opts, "mcast") ||
+ qemu_opt_get(opts, "localaddr")) {
+ error_report("fd=, listen=, mcast= and localaddr= is invalid with connect=\n");
return -1;
}
return -1;
}
} else if (qemu_opt_get(opts, "mcast")) {
- const char *mcast;
+ const char *mcast, *localaddr;
if (qemu_opt_get(opts, "fd") ||
qemu_opt_get(opts, "connect") ||
qemu_opt_get(opts, "listen")) {
- qemu_error("fd=, connect= and listen= is invalid with mcast=\n");
+ error_report("fd=, connect= and listen= is invalid with mcast=");
return -1;
}
mcast = qemu_opt_get(opts, "mcast");
+ localaddr = qemu_opt_get(opts, "localaddr");
- if (net_socket_mcast_init(vlan, "socket", name, mcast) == -1) {
+ if (net_socket_mcast_init(vlan, "socket", name, mcast, localaddr) == -1) {
return -1;
}
} else {
- qemu_error("-socket requires fd=, listen=, connect= or mcast=\n");
+ error_report("-socket requires fd=, listen=, connect= or mcast=");
return -1;
}
- if (vlan) {
- vlan->nb_host_devs++;
- }
-
return 0;
}