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