Commit 3d830459b1eccdb61b75e2712fd364012ce5a115
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 |
vl.c
@@ -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" |