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" | ... | ... |