Commit 3d830459b1eccdb61b75e2712fd364012ce5a115

Authored by bellard
1 parent 87022ff5

'-net socket,mcast=' option support (initial patch by Juan Jose Ciarlante)


git-svn-id: svn://svn.savannah.nongnu.org/qemu/trunk@1710 c046a42c-6fe2-441c-8c8c-71466251a162
Showing 2 changed files with 225 additions and 3 deletions
qemu-doc.texi
... ... @@ -282,8 +282,8 @@ Connect the VLAN @var{n} to a remote VLAN in another QEMU virtual
282 282 machine using a TCP socket connection. If @option{listen} is
283 283 specified, QEMU waits for incoming connections on @var{port}
284 284 (@var{host} is optional). @option{connect} is used to connect to
285   -another QEMU instance using the @option{listen} option. @option{fd=h}
286   -specifies an already opened socket.
  285 +another QEMU instance using the @option{listen} option. @option{fd=h}
  286 +specifies an already opened TCP socket.
287 287  
288 288 Example:
289 289 @example
... ... @@ -293,6 +293,40 @@ qemu linux.img -net nic,macaddr=52:54:00:12:34:56 -net socket,listen=:1234
293 293 qemu linux.img -net nic,macaddr=52:54:00:12:34:57 -net socket,connect=127.0.0.1:1234
294 294 @end example
295 295  
  296 +@item -net socket[,vlan=n][,fd=h][,mcast=maddr:port]
  297 +
  298 +Create a VLAN @var{n} shared with another QEMU virtual
  299 +machines using a UDP multicast socket, effectively making a bus for
  300 +every QEMU with same multicast address @var{maddr} and @var{port}.
  301 +NOTES:
  302 +@enumerate
  303 +@item
  304 +Several QEMU can be running on different hosts and share same bus (assuming
  305 +correct multicast setup for these hosts).
  306 +@item
  307 +mcast support is compatible with User Mode Linux (argument @option{eth@var{N}=mcast}), see
  308 +@url{http://user-mode-linux.sf.net}.
  309 +@item Use @option{fd=h} to specify an already opened UDP multicast socket.
  310 +@end enumerate
  311 +
  312 +Example:
  313 +@example
  314 +# launch one QEMU instance
  315 +qemu linux.img -net nic,macaddr=52:54:00:12:34:56 -net socket,mcast=230.0.0.1:1234
  316 +# launch another QEMU instance on same "bus"
  317 +qemu linux.img -net nic,macaddr=52:54:00:12:34:57 -net socket,mcast=230.0.0.1:1234
  318 +# launch yet another QEMU instance on same "bus"
  319 +qemu linux.img -net nic,macaddr=52:54:00:12:34:58 -net socket,mcast=230.0.0.1:1234
  320 +@end example
  321 +
  322 +Example (User Mode Linux compat.):
  323 +@example
  324 +# launch QEMU instance (note mcast address selected is UML's default)
  325 +qemu linux.img -net nic,macaddr=52:54:00:12:34:56 -net socket,mcast=239.192.168.1:1102
  326 +# launch UML
  327 +/path/to/linux ubd0=/path/to/root_fs eth0=mcast
  328 +@end example
  329 +
296 330 @item -net none
297 331 Indicate that no network devices should be configured. It is used to
298 332 override the default configuration which is activated if no
... ...
... ... @@ -2133,6 +2133,7 @@ typedef struct NetSocketState {
2133 2133 int index;
2134 2134 int packet_len;
2135 2135 uint8_t buf[4096];
  2136 + struct sockaddr_in dgram_dst; /* contains inet host and port destination iff connectionless (SOCK_DGRAM) */
2136 2137 } NetSocketState;
2137 2138  
2138 2139 typedef struct NetSocketListenState {
... ... @@ -2151,6 +2152,13 @@ static void net_socket_receive(void *opaque, const uint8_t *buf, int size)
2151 2152 unix_write(s->fd, buf, size);
2152 2153 }
2153 2154  
  2155 +static void net_socket_receive_dgram(void *opaque, const uint8_t *buf, int size)
  2156 +{
  2157 + NetSocketState *s = opaque;
  2158 + sendto(s->fd, buf, size, 0,
  2159 + (struct sockaddr *)&s->dgram_dst, sizeof(s->dgram_dst));
  2160 +}
  2161 +
2154 2162 static void net_socket_send(void *opaque)
2155 2163 {
2156 2164 NetSocketState *s = opaque;
... ... @@ -2203,13 +2211,140 @@ static void net_socket_send(void *opaque)
2203 2211 }
2204 2212 }
2205 2213  
  2214 +static void net_socket_send_dgram(void *opaque)
  2215 +{
  2216 + NetSocketState *s = opaque;
  2217 + int size;
  2218 +
  2219 + size = recv(s->fd, s->buf, sizeof(s->buf), 0);
  2220 + if (size < 0)
  2221 + return;
  2222 + if (size == 0) {
  2223 + /* end of connection */
  2224 + qemu_set_fd_handler(s->fd, NULL, NULL, NULL);
  2225 + return;
  2226 + }
  2227 + qemu_send_packet(s->vc, s->buf, size);
  2228 +}
  2229 +
  2230 +static int net_socket_mcast_create(struct sockaddr_in *mcastaddr)
  2231 +{
  2232 + struct ip_mreq imr;
  2233 + int fd;
  2234 + int val, ret;
  2235 + if (!IN_MULTICAST(ntohl(mcastaddr->sin_addr.s_addr))) {
  2236 + fprintf(stderr, "qemu: error: specified mcastaddr \"%s\" (0x%08x) does not contain a multicast address\n",
  2237 + inet_ntoa(mcastaddr->sin_addr), ntohl(mcastaddr->sin_addr.s_addr));
  2238 + return -1;
  2239 +
  2240 + }
  2241 + fd = socket(PF_INET, SOCK_DGRAM, 0);
  2242 + if (fd < 0) {
  2243 + perror("socket(PF_INET, SOCK_DGRAM)");
  2244 + return -1;
  2245 + }
  2246 +
  2247 + /* Add host to multicast group */
  2248 + imr.imr_multiaddr = mcastaddr->sin_addr;
  2249 + imr.imr_interface.s_addr = htonl(INADDR_ANY);
  2250 +
  2251 + ret = setsockopt(fd, IPPROTO_IP, IP_ADD_MEMBERSHIP, (void *) &imr, sizeof(struct ip_mreq));
  2252 + if (ret < 0) {
  2253 + perror("setsockopt(IP_ADD_MEMBERSHIP)");
  2254 + goto fail;
  2255 + }
  2256 +
  2257 + /* Force mcast msgs to loopback (eg. several QEMUs in same host */
  2258 + val = 1;
  2259 + ret=setsockopt(fd, SOL_IP, IP_MULTICAST_LOOP, &val, sizeof(val));
  2260 + if (ret < 0) {
  2261 + perror("setsockopt(SOL_IP, IP_MULTICAST_LOOP)");
  2262 + goto fail;
  2263 + }
  2264 +
  2265 + ret=setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &val, sizeof(val));
  2266 + if (ret < 0) {
  2267 + perror("setsockopt(SOL_SOCKET, SO_REUSEADDR)");
  2268 + goto fail;
  2269 + }
  2270 +
  2271 + ret = bind(fd, (struct sockaddr *)mcastaddr, sizeof(*mcastaddr));
  2272 + if (ret < 0) {
  2273 + perror("bind");
  2274 + goto fail;
  2275 + }
  2276 +
  2277 + fcntl(fd, F_SETFL, O_NONBLOCK);
  2278 + return fd;
  2279 +fail:
  2280 + if (fd>=0) close(fd);
  2281 + return -1;
  2282 +}
  2283 +
  2284 +static NetSocketState *net_socket_fd_init_dgram(VLANState *vlan, int fd,
  2285 + int is_connected)
  2286 +{
  2287 + struct sockaddr_in saddr;
  2288 + int newfd;
  2289 + socklen_t saddr_len;
  2290 + NetSocketState *s;
  2291 +
  2292 + /* fd passed: multicast: "learn" dgram_dst address from bound address and save it
  2293 + * Because this may be "shared" socket from a "master" process, datagrams would be recv()
  2294 + * by ONLY ONE process: we must "clone" this dgram socket --jjo
  2295 + */
  2296 +
  2297 + if (is_connected) {
  2298 + if (getsockname(fd, (struct sockaddr *) &saddr, &saddr_len) == 0) {
  2299 + /* must be bound */
  2300 + if (saddr.sin_addr.s_addr==0) {
  2301 + fprintf(stderr, "qemu: error: init_dgram: fd=%d unbound, cannot setup multicast dst addr\n",
  2302 + fd);
  2303 + return NULL;
  2304 + }
  2305 + /* clone dgram socket */
  2306 + newfd = net_socket_mcast_create(&saddr);
  2307 + if (newfd < 0) {
  2308 + /* error already reported by net_socket_mcast_create() */
  2309 + close(fd);
  2310 + return NULL;
  2311 + }
  2312 + /* clone newfd to fd, close newfd */
  2313 + dup2(newfd, fd);
  2314 + close(newfd);
  2315 +
  2316 + } else {
  2317 + fprintf(stderr, "qemu: error: init_dgram: fd=%d failed getsockname(): %s\n",
  2318 + fd, strerror(errno));
  2319 + return NULL;
  2320 + }
  2321 + }
  2322 +
  2323 + s = qemu_mallocz(sizeof(NetSocketState));
  2324 + if (!s)
  2325 + return NULL;
  2326 + s->fd = fd;
  2327 +
  2328 + s->vc = qemu_new_vlan_client(vlan, net_socket_receive_dgram, s);
  2329 + qemu_set_fd_handler(s->fd, net_socket_send_dgram, NULL, s);
  2330 +
  2331 + /* mcast: save bound address as dst */
  2332 + if (is_connected) s->dgram_dst=saddr;
  2333 +
  2334 + snprintf(s->vc->info_str, sizeof(s->vc->info_str),
  2335 + "socket: fd=%d (%s mcast=%s:%d)",
  2336 + fd, is_connected? "cloned" : "",
  2337 + inet_ntoa(saddr.sin_addr), ntohs(saddr.sin_port));
  2338 + return s;
  2339 +}
  2340 +
2206 2341 static void net_socket_connect(void *opaque)
2207 2342 {
2208 2343 NetSocketState *s = opaque;
2209 2344 qemu_set_fd_handler(s->fd, net_socket_send, NULL, s);
2210 2345 }
2211 2346  
2212   -static NetSocketState *net_socket_fd_init(VLANState *vlan, int fd,
  2347 +static NetSocketState *net_socket_fd_init_stream(VLANState *vlan, int fd,
2213 2348 int is_connected)
2214 2349 {
2215 2350 NetSocketState *s;
... ... @@ -2229,6 +2364,28 @@ static NetSocketState *net_socket_fd_init(VLANState *vlan, int fd,
2229 2364 return s;
2230 2365 }
2231 2366  
  2367 +static NetSocketState *net_socket_fd_init(VLANState *vlan, int fd,
  2368 + int is_connected)
  2369 +{
  2370 + int so_type=-1, optlen=sizeof(so_type);
  2371 +
  2372 + if(getsockopt(fd, SOL_SOCKET,SO_TYPE, &so_type, &optlen)< 0) {
  2373 + fprintf(stderr, "qemu: error: setsockopt(SO_TYPE) for fd=%d failed\n", fd);
  2374 + return NULL;
  2375 + }
  2376 + switch(so_type) {
  2377 + case SOCK_DGRAM:
  2378 + return net_socket_fd_init_dgram(vlan, fd, is_connected);
  2379 + case SOCK_STREAM:
  2380 + return net_socket_fd_init_stream(vlan, fd, is_connected);
  2381 + default:
  2382 + /* who knows ... this could be a eg. a pty, do warn and continue as stream */
  2383 + fprintf(stderr, "qemu: warning: socket type=%d for fd=%d is not SOCK_DGRAM or SOCK_STREAM\n", so_type, fd);
  2384 + return net_socket_fd_init_stream(vlan, fd, is_connected);
  2385 + }
  2386 + return NULL;
  2387 +}
  2388 +
2232 2389 static void net_socket_accept(void *opaque)
2233 2390 {
2234 2391 NetSocketListenState *s = opaque;
... ... @@ -2338,6 +2495,33 @@ static int net_socket_connect_init(VLANState *vlan, const char *host_str)
2338 2495 return 0;
2339 2496 }
2340 2497  
  2498 +static int net_socket_mcast_init(VLANState *vlan, const char *host_str)
  2499 +{
  2500 + NetSocketState *s;
  2501 + int fd;
  2502 + struct sockaddr_in saddr;
  2503 +
  2504 + if (parse_host_port(&saddr, host_str) < 0)
  2505 + return -1;
  2506 +
  2507 +
  2508 + fd = net_socket_mcast_create(&saddr);
  2509 + if (fd < 0)
  2510 + return -1;
  2511 +
  2512 + s = net_socket_fd_init(vlan, fd, 0);
  2513 + if (!s)
  2514 + return -1;
  2515 +
  2516 + s->dgram_dst = saddr;
  2517 +
  2518 + snprintf(s->vc->info_str, sizeof(s->vc->info_str),
  2519 + "socket: mcast=%s:%d",
  2520 + inet_ntoa(saddr.sin_addr), ntohs(saddr.sin_port));
  2521 + return 0;
  2522 +
  2523 +}
  2524 +
2341 2525 #endif /* !_WIN32 */
2342 2526  
2343 2527 static int get_param_value(char *buf, int buf_size,
... ... @@ -2474,6 +2658,8 @@ int net_client_init(const char *str)
2474 2658 ret = net_socket_listen_init(vlan, buf);
2475 2659 } else if (get_param_value(buf, sizeof(buf), "connect", p) > 0) {
2476 2660 ret = net_socket_connect_init(vlan, buf);
  2661 + } else if (get_param_value(buf, sizeof(buf), "mcast", p) > 0) {
  2662 + ret = net_socket_mcast_init(vlan, buf);
2477 2663 } else {
2478 2664 fprintf(stderr, "Unknown socket options: %s\n", p);
2479 2665 return -1;
... ... @@ -3812,6 +3998,8 @@ void help(void)
3812 3998 " use 'fd=h' to connect to an already opened TAP interface\n"
3813 3999 "-net socket[,vlan=n][,fd=h][,listen=[host]:port][,connect=host:port]\n"
3814 4000 " connect the vlan 'n' to another VLAN using a socket connection\n"
  4001 + "-net socket[,vlan=n][,fd=h][,mcast=maddr:port]\n"
  4002 + " connect the vlan 'n' to multicast maddr and port\n"
3815 4003 #endif
3816 4004 "-net none use it alone to have zero network devices; if no -net option\n"
3817 4005 " is provided, the default is '-net nic -net user'\n"
... ...