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,8 +282,8 @@ Connect the VLAN @var{n} to a remote VLAN in another QEMU virtual
282 machine using a TCP socket connection. If @option{listen} is 282 machine using a TCP socket connection. If @option{listen} is
283 specified, QEMU waits for incoming connections on @var{port} 283 specified, QEMU waits for incoming connections on @var{port}
284 (@var{host} is optional). @option{connect} is used to connect to 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 Example: 288 Example:
289 @example 289 @example
@@ -293,6 +293,40 @@ qemu linux.img -net nic,macaddr=52:54:00:12:34:56 -net socket,listen=:1234 @@ -293,6 +293,40 @@ qemu linux.img -net nic,macaddr=52:54:00:12:34:56 -net socket,listen=:1234
293 qemu linux.img -net nic,macaddr=52:54:00:12:34:57 -net socket,connect=127.0.0.1:1234 293 qemu linux.img -net nic,macaddr=52:54:00:12:34:57 -net socket,connect=127.0.0.1:1234
294 @end example 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 @item -net none 330 @item -net none
297 Indicate that no network devices should be configured. It is used to 331 Indicate that no network devices should be configured. It is used to
298 override the default configuration which is activated if no 332 override the default configuration which is activated if no
@@ -2133,6 +2133,7 @@ typedef struct NetSocketState { @@ -2133,6 +2133,7 @@ typedef struct NetSocketState {
2133 int index; 2133 int index;
2134 int packet_len; 2134 int packet_len;
2135 uint8_t buf[4096]; 2135 uint8_t buf[4096];
  2136 + struct sockaddr_in dgram_dst; /* contains inet host and port destination iff connectionless (SOCK_DGRAM) */
2136 } NetSocketState; 2137 } NetSocketState;
2137 2138
2138 typedef struct NetSocketListenState { 2139 typedef struct NetSocketListenState {
@@ -2151,6 +2152,13 @@ static void net_socket_receive(void *opaque, const uint8_t *buf, int size) @@ -2151,6 +2152,13 @@ static void net_socket_receive(void *opaque, const uint8_t *buf, int size)
2151 unix_write(s->fd, buf, size); 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 static void net_socket_send(void *opaque) 2162 static void net_socket_send(void *opaque)
2155 { 2163 {
2156 NetSocketState *s = opaque; 2164 NetSocketState *s = opaque;
@@ -2203,13 +2211,140 @@ static void net_socket_send(void *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 static void net_socket_connect(void *opaque) 2341 static void net_socket_connect(void *opaque)
2207 { 2342 {
2208 NetSocketState *s = opaque; 2343 NetSocketState *s = opaque;
2209 qemu_set_fd_handler(s->fd, net_socket_send, NULL, s); 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 int is_connected) 2348 int is_connected)
2214 { 2349 {
2215 NetSocketState *s; 2350 NetSocketState *s;
@@ -2229,6 +2364,28 @@ static NetSocketState *net_socket_fd_init(VLANState *vlan, int fd, @@ -2229,6 +2364,28 @@ static NetSocketState *net_socket_fd_init(VLANState *vlan, int fd,
2229 return s; 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 static void net_socket_accept(void *opaque) 2389 static void net_socket_accept(void *opaque)
2233 { 2390 {
2234 NetSocketListenState *s = opaque; 2391 NetSocketListenState *s = opaque;
@@ -2338,6 +2495,33 @@ static int net_socket_connect_init(VLANState *vlan, const char *host_str) @@ -2338,6 +2495,33 @@ static int net_socket_connect_init(VLANState *vlan, const char *host_str)
2338 return 0; 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 #endif /* !_WIN32 */ 2525 #endif /* !_WIN32 */
2342 2526
2343 static int get_param_value(char *buf, int buf_size, 2527 static int get_param_value(char *buf, int buf_size,
@@ -2474,6 +2658,8 @@ int net_client_init(const char *str) @@ -2474,6 +2658,8 @@ int net_client_init(const char *str)
2474 ret = net_socket_listen_init(vlan, buf); 2658 ret = net_socket_listen_init(vlan, buf);
2475 } else if (get_param_value(buf, sizeof(buf), "connect", p) > 0) { 2659 } else if (get_param_value(buf, sizeof(buf), "connect", p) > 0) {
2476 ret = net_socket_connect_init(vlan, buf); 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 } else { 2663 } else {
2478 fprintf(stderr, "Unknown socket options: %s\n", p); 2664 fprintf(stderr, "Unknown socket options: %s\n", p);
2479 return -1; 2665 return -1;
@@ -3812,6 +3998,8 @@ void help(void) @@ -3812,6 +3998,8 @@ void help(void)
3812 " use 'fd=h' to connect to an already opened TAP interface\n" 3998 " use 'fd=h' to connect to an already opened TAP interface\n"
3813 "-net socket[,vlan=n][,fd=h][,listen=[host]:port][,connect=host:port]\n" 3999 "-net socket[,vlan=n][,fd=h][,listen=[host]:port][,connect=host:port]\n"
3814 " connect the vlan 'n' to another VLAN using a socket connection\n" 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 #endif 4003 #endif
3816 "-net none use it alone to have zero network devices; if no -net option\n" 4004 "-net none use it alone to have zero network devices; if no -net option\n"
3817 " is provided, the default is '-net nic -net user'\n" 4005 " is provided, the default is '-net nic -net user'\n"