Commit 951f13516a50383cd462c9150e643cc9f9566267
1 parent
1f6e24e7
telnet protocol and more consistent syntax (Jason Wessel)
git-svn-id: svn://svn.savannah.nongnu.org/qemu/trunk@2030 c046a42c-6fe2-441c-8c8c-71466251a162
Showing
2 changed files
with
191 additions
and
82 deletions
qemu-doc.texi
| ... | ... | @@ -525,43 +525,60 @@ Write output to filename. No character can be read. |
| 525 | 525 | name pipe @var{filename} |
| 526 | 526 | @item COMn |
| 527 | 527 | [Windows only] Use host serial port @var{n} |
| 528 | -@item udp:remote_port | |
| 529 | -UDP Net Console sent to locahost at remote_port | |
| 530 | -@item udp:remote_host:remote_port | |
| 531 | -UDP Net Console sent to remote_host at remote_port | |
| 532 | -@item udp:src_port:remote_host:remote_port | |
| 533 | -UDP Net Console sent from src_port to remote_host at the remote_port. | |
| 534 | - | |
| 535 | -The udp:* sub options are primary intended for netconsole. If you | |
| 536 | -just want a simple readonly console you can use @code{netcat} or | |
| 537 | -@code{nc}, by starting qemu with: @code{-serial udp:4555} and nc as: | |
| 538 | -@code{nc -u -l -p 4555}. Any time qemu writes something to that port | |
| 539 | -it will appear in the netconsole session. | |
| 528 | +@item udp:[remote_host]:remote_port[@@[src_ip]:src_port] | |
| 529 | +This implements UDP Net Console. When @var{remote_host} or @var{src_ip} are not specified they default to @code{0.0.0.0}. When not using a specifed @var{src_port} a random port is automatically chosen. | |
| 530 | + | |
| 531 | +If you just want a simple readonly console you can use @code{netcat} or | |
| 532 | +@code{nc}, by starting qemu with: @code{-serial udp::4555} and nc as: | |
| 533 | +@code{nc -u -l -p 4555}. Any time qemu writes something to that port it | |
| 534 | +will appear in the netconsole session. | |
| 540 | 535 | |
| 541 | 536 | If you plan to send characters back via netconsole or you want to stop |
| 542 | 537 | and start qemu a lot of times, you should have qemu use the same |
| 543 | 538 | source port each time by using something like @code{-serial |
| 544 | -udp:4556:localhost:4555} to qemu. Another approach is to use a patched | |
| 539 | +udp::4555@@:4556} to qemu. Another approach is to use a patched | |
| 545 | 540 | version of netcat which can listen to a TCP port and send and receive |
| 546 | 541 | characters via udp. If you have a patched version of netcat which |
| 547 | 542 | activates telnet remote echo and single char transfer, then you can |
| 548 | 543 | use the following options to step up a netcat redirector to allow |
| 549 | 544 | telnet on port 5555 to access the qemu port. |
| 550 | 545 | @table @code |
| 551 | -@item Qemu Options | |
| 552 | --serial udp:4556:localhost:4555 | |
| 553 | -@item netcat options | |
| 554 | --u -P 4555 -L localhost:4556 -t -p 5555 -I -T | |
| 546 | +@item Qemu Options: | |
| 547 | +-serial udp::4555@@:4556 | |
| 548 | +@item netcat options: | |
| 549 | +-u -P 4555 -L 0.0.0.0:4556 -t -p 5555 -I -T | |
| 550 | +@item telnet options: | |
| 551 | +localhost 5555 | |
| 552 | +@end table | |
| 553 | + | |
| 554 | + | |
| 555 | +@item tcp:[host]:port[,server][,nowait] | |
| 556 | +The TCP Net Console has two modes of operation. It can send the serial | |
| 557 | +I/O to a location or wait for a connection from a location. By default | |
| 558 | +the TCP Net Console is sent to @var{host} at the @var{port}. If you use | |
| 559 | +the @var{,server} option QEMU will wait for a client socket application | |
| 560 | +to connect to the port before continuing, unless the @code{,nowait} | |
| 561 | +option was specified. If @var{host} is omitted, 0.0.0.0 is assumed. Only | |
| 562 | +one TCP connection at a time is accepted. You can use @code{telnet} to | |
| 563 | +connect to the corresponding character device. | |
| 564 | +@table @code | |
| 565 | +@item Example to send tcp console to 192.168.0.2 port 4444 | |
| 566 | +-serial tcp:192.168.0.2:4444 | |
| 567 | +@item Example to listen and wait on port 4444 for connection | |
| 568 | +-serial tcp::4444,server | |
| 569 | +@item Example to not wait and listen on ip 192.168.0.100 port 4444 | |
| 570 | +-serial tcp:192.168.0.100:4444,server,nowait | |
| 555 | 571 | @end table |
| 556 | 572 | |
| 573 | +@item telnet:host:port[,server][,nowait] | |
| 574 | +The telnet protocol is used instead of raw tcp sockets. The options | |
| 575 | +work the same as if you had specified @code{-serial tcp}. The | |
| 576 | +difference is that the port acts like a telnet server or client using | |
| 577 | +telnet option negotiation. This will also allow you to send the | |
| 578 | +MAGIC_SYSRQ sequence if you use a telnet that supports sending the break | |
| 579 | +sequence. Typically in unix telnet you do it with Control-] and then | |
| 580 | +type "send break" followed by pressing the enter key. | |
| 557 | 581 | |
| 558 | -@item tcp:remote_host:remote_port | |
| 559 | -TCP Net Console sent to remote_host at the remote_port | |
| 560 | -@item tcpl:host:port | |
| 561 | -TCP Net Console: wait for connection on @var{host} on the local port | |
| 562 | -@var{port}. If host is omitted, 0.0.0.0 is assumed. Only one TCP | |
| 563 | -connection at a time is accepted. You can use @code{telnet} to connect | |
| 564 | -to the corresponding character device. | |
| 565 | 582 | @end table |
| 566 | 583 | |
| 567 | 584 | @item -parallel dev | ... | ... |
vl.c
| ... | ... | @@ -2203,16 +2203,16 @@ static void udp_chr_add_read_handler(CharDriverState *chr, |
| 2203 | 2203 | } |
| 2204 | 2204 | |
| 2205 | 2205 | int parse_host_port(struct sockaddr_in *saddr, const char *str); |
| 2206 | +int parse_host_src_port(struct sockaddr_in *haddr, | |
| 2207 | + struct sockaddr_in *saddr, | |
| 2208 | + const char *str); | |
| 2206 | 2209 | |
| 2207 | 2210 | CharDriverState *qemu_chr_open_udp(const char *def) |
| 2208 | 2211 | { |
| 2209 | 2212 | CharDriverState *chr = NULL; |
| 2210 | 2213 | NetCharDriver *s = NULL; |
| 2211 | 2214 | int fd = -1; |
| 2212 | - int con_type; | |
| 2213 | - struct sockaddr_in addr; | |
| 2214 | - const char *p, *r; | |
| 2215 | - int port; | |
| 2215 | + struct sockaddr_in saddr; | |
| 2216 | 2216 | |
| 2217 | 2217 | chr = qemu_mallocz(sizeof(CharDriverState)); |
| 2218 | 2218 | if (!chr) |
| ... | ... | @@ -2227,58 +2227,12 @@ CharDriverState *qemu_chr_open_udp(const char *def) |
| 2227 | 2227 | goto return_err; |
| 2228 | 2228 | } |
| 2229 | 2229 | |
| 2230 | - /* There are three types of port definitions | |
| 2231 | - * 1) udp:remote_port | |
| 2232 | - * Juse use 0.0.0.0 for the IP and send to remote | |
| 2233 | - * 2) udp:remote_host:port | |
| 2234 | - * Use a IP and send traffic to remote | |
| 2235 | - * 3) udp:local_port:remote_host:remote_port | |
| 2236 | - * Use local_port as the originator + #2 | |
| 2237 | - */ | |
| 2238 | - con_type = 0; | |
| 2239 | - p = def; | |
| 2240 | - while ((p = strchr(p, ':'))) { | |
| 2241 | - p++; | |
| 2242 | - con_type++; | |
| 2243 | - } | |
| 2244 | - | |
| 2245 | - p = def; | |
| 2246 | - memset(&addr,0,sizeof(addr)); | |
| 2247 | - addr.sin_family = AF_INET; | |
| 2248 | - addr.sin_addr.s_addr = htonl(INADDR_ANY); | |
| 2249 | - s->daddr.sin_family = AF_INET; | |
| 2250 | - s->daddr.sin_addr.s_addr = htonl(INADDR_ANY); | |
| 2251 | - | |
| 2252 | - switch (con_type) { | |
| 2253 | - case 0: | |
| 2254 | - port = strtol(p, (char **)&r, 0); | |
| 2255 | - if (r == p) { | |
| 2256 | - fprintf(stderr, "Error parsing port number\n"); | |
| 2257 | - goto return_err; | |
| 2258 | - } | |
| 2259 | - s->daddr.sin_port = htons((short)port); | |
| 2260 | - break; | |
| 2261 | - case 2: | |
| 2262 | - port = strtol(p, (char **)&r, 0); | |
| 2263 | - if (r == p) { | |
| 2264 | - fprintf(stderr, "Error parsing port number\n"); | |
| 2265 | - goto return_err; | |
| 2266 | - } | |
| 2267 | - addr.sin_port = htons((short)port); | |
| 2268 | - p = r + 1; | |
| 2269 | - /* Fall through to case 1 now that we have the local port */ | |
| 2270 | - case 1: | |
| 2271 | - if (parse_host_port(&s->daddr, p) < 0) { | |
| 2272 | - fprintf(stderr, "Error parsing host name and port\n"); | |
| 2273 | - goto return_err; | |
| 2274 | - } | |
| 2275 | - break; | |
| 2276 | - default: | |
| 2277 | - fprintf(stderr, "Too many ':' characters\n"); | |
| 2278 | - goto return_err; | |
| 2230 | + if (parse_host_src_port(&s->daddr, &saddr, def) < 0) { | |
| 2231 | + printf("Could not parse: %s\n", def); | |
| 2232 | + goto return_err; | |
| 2279 | 2233 | } |
| 2280 | 2234 | |
| 2281 | - if (bind(fd, (struct sockaddr *)&addr, sizeof(addr)) < 0) | |
| 2235 | + if (bind(fd, (struct sockaddr *)&saddr, sizeof(saddr)) < 0) | |
| 2282 | 2236 | { |
| 2283 | 2237 | perror("bind"); |
| 2284 | 2238 | goto return_err; |
| ... | ... | @@ -2312,6 +2266,7 @@ typedef struct { |
| 2312 | 2266 | int fd, listen_fd; |
| 2313 | 2267 | int connected; |
| 2314 | 2268 | int max_size; |
| 2269 | + int do_telnetopt; | |
| 2315 | 2270 | } TCPCharDriver; |
| 2316 | 2271 | |
| 2317 | 2272 | static void tcp_chr_accept(void *opaque); |
| ... | ... | @@ -2337,6 +2292,56 @@ static int tcp_chr_read_poll(void *opaque) |
| 2337 | 2292 | return s->max_size; |
| 2338 | 2293 | } |
| 2339 | 2294 | |
| 2295 | +#define IAC 255 | |
| 2296 | +#define IAC_BREAK 243 | |
| 2297 | +static void tcp_chr_process_IAC_bytes(CharDriverState *chr, | |
| 2298 | + TCPCharDriver *s, | |
| 2299 | + char *buf, int *size) | |
| 2300 | +{ | |
| 2301 | + /* Handle any telnet client's basic IAC options to satisfy char by | |
| 2302 | + * char mode with no echo. All IAC options will be removed from | |
| 2303 | + * the buf and the do_telnetopt variable will be used to track the | |
| 2304 | + * state of the width of the IAC information. | |
| 2305 | + * | |
| 2306 | + * IAC commands come in sets of 3 bytes with the exception of the | |
| 2307 | + * "IAC BREAK" command and the double IAC. | |
| 2308 | + */ | |
| 2309 | + | |
| 2310 | + int i; | |
| 2311 | + int j = 0; | |
| 2312 | + | |
| 2313 | + for (i = 0; i < *size; i++) { | |
| 2314 | + if (s->do_telnetopt > 1) { | |
| 2315 | + if ((unsigned char)buf[i] == IAC && s->do_telnetopt == 2) { | |
| 2316 | + /* Double IAC means send an IAC */ | |
| 2317 | + if (j != i) | |
| 2318 | + buf[j] = buf[i]; | |
| 2319 | + j++; | |
| 2320 | + s->do_telnetopt = 1; | |
| 2321 | + } else { | |
| 2322 | + if ((unsigned char)buf[i] == IAC_BREAK && s->do_telnetopt == 2) { | |
| 2323 | + /* Handle IAC break commands by sending a serial break */ | |
| 2324 | + chr->chr_event(s->fd_opaque, CHR_EVENT_BREAK); | |
| 2325 | + s->do_telnetopt++; | |
| 2326 | + } | |
| 2327 | + s->do_telnetopt++; | |
| 2328 | + } | |
| 2329 | + if (s->do_telnetopt >= 4) { | |
| 2330 | + s->do_telnetopt = 1; | |
| 2331 | + } | |
| 2332 | + } else { | |
| 2333 | + if ((unsigned char)buf[i] == IAC) { | |
| 2334 | + s->do_telnetopt = 2; | |
| 2335 | + } else { | |
| 2336 | + if (j != i) | |
| 2337 | + buf[j] = buf[i]; | |
| 2338 | + j++; | |
| 2339 | + } | |
| 2340 | + } | |
| 2341 | + } | |
| 2342 | + *size = j; | |
| 2343 | +} | |
| 2344 | + | |
| 2340 | 2345 | static void tcp_chr_read(void *opaque) |
| 2341 | 2346 | { |
| 2342 | 2347 | CharDriverState *chr = opaque; |
| ... | ... | @@ -2360,7 +2365,10 @@ static void tcp_chr_read(void *opaque) |
| 2360 | 2365 | closesocket(s->fd); |
| 2361 | 2366 | s->fd = -1; |
| 2362 | 2367 | } else if (size > 0) { |
| 2363 | - s->fd_read(s->fd_opaque, buf, size); | |
| 2368 | + if (s->do_telnetopt) | |
| 2369 | + tcp_chr_process_IAC_bytes(chr, s, buf, &size); | |
| 2370 | + if (size > 0) | |
| 2371 | + s->fd_read(s->fd_opaque, buf, size); | |
| 2364 | 2372 | } |
| 2365 | 2373 | } |
| 2366 | 2374 | |
| ... | ... | @@ -2385,6 +2393,21 @@ static void tcp_chr_connect(void *opaque) |
| 2385 | 2393 | tcp_chr_read, NULL, chr); |
| 2386 | 2394 | } |
| 2387 | 2395 | |
| 2396 | +#define IACSET(x,a,b,c) x[0] = a; x[1] = b; x[2] = c; | |
| 2397 | +static void tcp_chr_telnet_init(int fd) | |
| 2398 | +{ | |
| 2399 | + char buf[3]; | |
| 2400 | + /* Send the telnet negotion to put telnet in binary, no echo, single char mode */ | |
| 2401 | + IACSET(buf, 0xff, 0xfb, 0x01); /* IAC WILL ECHO */ | |
| 2402 | + send(fd, (char *)buf, 3, 0); | |
| 2403 | + IACSET(buf, 0xff, 0xfb, 0x03); /* IAC WILL Suppress go ahead */ | |
| 2404 | + send(fd, (char *)buf, 3, 0); | |
| 2405 | + IACSET(buf, 0xff, 0xfb, 0x00); /* IAC WILL Binary */ | |
| 2406 | + send(fd, (char *)buf, 3, 0); | |
| 2407 | + IACSET(buf, 0xff, 0xfd, 0x00); /* IAC DO Binary */ | |
| 2408 | + send(fd, (char *)buf, 3, 0); | |
| 2409 | +} | |
| 2410 | + | |
| 2388 | 2411 | static void tcp_chr_accept(void *opaque) |
| 2389 | 2412 | { |
| 2390 | 2413 | CharDriverState *chr = opaque; |
| ... | ... | @@ -2399,6 +2422,8 @@ static void tcp_chr_accept(void *opaque) |
| 2399 | 2422 | if (fd < 0 && errno != EINTR) { |
| 2400 | 2423 | return; |
| 2401 | 2424 | } else if (fd >= 0) { |
| 2425 | + if (s->do_telnetopt) | |
| 2426 | + tcp_chr_telnet_init(fd); | |
| 2402 | 2427 | break; |
| 2403 | 2428 | } |
| 2404 | 2429 | } |
| ... | ... | @@ -2419,16 +2444,34 @@ static void tcp_chr_close(CharDriverState *chr) |
| 2419 | 2444 | } |
| 2420 | 2445 | |
| 2421 | 2446 | static CharDriverState *qemu_chr_open_tcp(const char *host_str, |
| 2422 | - int is_listen) | |
| 2447 | + int is_telnet) | |
| 2423 | 2448 | { |
| 2424 | 2449 | CharDriverState *chr = NULL; |
| 2425 | 2450 | TCPCharDriver *s = NULL; |
| 2426 | 2451 | int fd = -1, ret, err, val; |
| 2452 | + int is_listen = 0; | |
| 2453 | + int is_waitconnect = 1; | |
| 2454 | + const char *ptr; | |
| 2427 | 2455 | struct sockaddr_in saddr; |
| 2428 | 2456 | |
| 2429 | 2457 | if (parse_host_port(&saddr, host_str) < 0) |
| 2430 | 2458 | goto fail; |
| 2431 | 2459 | |
| 2460 | + ptr = host_str; | |
| 2461 | + while((ptr = strchr(ptr,','))) { | |
| 2462 | + ptr++; | |
| 2463 | + if (!strncmp(ptr,"server",6)) { | |
| 2464 | + is_listen = 1; | |
| 2465 | + } else if (!strncmp(ptr,"nowait",6)) { | |
| 2466 | + is_waitconnect = 0; | |
| 2467 | + } else { | |
| 2468 | + printf("Unknown option: %s\n", ptr); | |
| 2469 | + goto fail; | |
| 2470 | + } | |
| 2471 | + } | |
| 2472 | + if (!is_listen) | |
| 2473 | + is_waitconnect = 0; | |
| 2474 | + | |
| 2432 | 2475 | chr = qemu_mallocz(sizeof(CharDriverState)); |
| 2433 | 2476 | if (!chr) |
| 2434 | 2477 | goto fail; |
| ... | ... | @@ -2439,7 +2482,9 @@ static CharDriverState *qemu_chr_open_tcp(const char *host_str, |
| 2439 | 2482 | fd = socket(PF_INET, SOCK_STREAM, 0); |
| 2440 | 2483 | if (fd < 0) |
| 2441 | 2484 | goto fail; |
| 2442 | - socket_set_nonblock(fd); | |
| 2485 | + | |
| 2486 | + if (!is_waitconnect) | |
| 2487 | + socket_set_nonblock(fd); | |
| 2443 | 2488 | |
| 2444 | 2489 | s->connected = 0; |
| 2445 | 2490 | s->fd = -1; |
| ... | ... | @@ -2457,6 +2502,8 @@ static CharDriverState *qemu_chr_open_tcp(const char *host_str, |
| 2457 | 2502 | goto fail; |
| 2458 | 2503 | s->listen_fd = fd; |
| 2459 | 2504 | qemu_set_fd_handler(s->listen_fd, tcp_chr_accept, NULL, chr); |
| 2505 | + if (is_telnet) | |
| 2506 | + s->do_telnetopt = 1; | |
| 2460 | 2507 | } else { |
| 2461 | 2508 | for(;;) { |
| 2462 | 2509 | ret = connect(fd, (struct sockaddr *)&saddr, sizeof(saddr)); |
| ... | ... | @@ -2484,6 +2531,12 @@ static CharDriverState *qemu_chr_open_tcp(const char *host_str, |
| 2484 | 2531 | chr->chr_write = tcp_chr_write; |
| 2485 | 2532 | chr->chr_add_read_handler = tcp_chr_add_read_handler; |
| 2486 | 2533 | chr->chr_close = tcp_chr_close; |
| 2534 | + if (is_listen && is_waitconnect) { | |
| 2535 | + printf("QEMU waiting for connection on: %s\n", host_str); | |
| 2536 | + tcp_chr_accept(chr); | |
| 2537 | + socket_set_nonblock(s->listen_fd); | |
| 2538 | + } | |
| 2539 | + | |
| 2487 | 2540 | return chr; |
| 2488 | 2541 | fail: |
| 2489 | 2542 | if (fd >= 0) |
| ... | ... | @@ -2505,7 +2558,7 @@ CharDriverState *qemu_chr_open(const char *filename) |
| 2505 | 2558 | if (strstart(filename, "tcp:", &p)) { |
| 2506 | 2559 | return qemu_chr_open_tcp(p, 0); |
| 2507 | 2560 | } else |
| 2508 | - if (strstart(filename, "tcpl:", &p)) { | |
| 2561 | + if (strstart(filename, "telnet:", &p)) { | |
| 2509 | 2562 | return qemu_chr_open_tcp(p, 1); |
| 2510 | 2563 | } else |
| 2511 | 2564 | if (strstart(filename, "udp:", &p)) { |
| ... | ... | @@ -2618,6 +2671,45 @@ static int get_str_sep(char *buf, int buf_size, const char **pp, int sep) |
| 2618 | 2671 | return 0; |
| 2619 | 2672 | } |
| 2620 | 2673 | |
| 2674 | +int parse_host_src_port(struct sockaddr_in *haddr, | |
| 2675 | + struct sockaddr_in *saddr, | |
| 2676 | + const char *input_str) | |
| 2677 | +{ | |
| 2678 | + char *str = strdup(input_str); | |
| 2679 | + char *host_str = str; | |
| 2680 | + char *src_str; | |
| 2681 | + char *ptr; | |
| 2682 | + | |
| 2683 | + /* | |
| 2684 | + * Chop off any extra arguments at the end of the string which | |
| 2685 | + * would start with a comma, then fill in the src port information | |
| 2686 | + * if it was provided else use the "any address" and "any port". | |
| 2687 | + */ | |
| 2688 | + if ((ptr = strchr(str,','))) | |
| 2689 | + *ptr = '\0'; | |
| 2690 | + | |
| 2691 | + if ((src_str = strchr(input_str,'@'))) { | |
| 2692 | + *src_str = '\0'; | |
| 2693 | + src_str++; | |
| 2694 | + } | |
| 2695 | + | |
| 2696 | + if (parse_host_port(haddr, host_str) < 0) | |
| 2697 | + goto fail; | |
| 2698 | + | |
| 2699 | + if (!src_str || *src_str == '\0') | |
| 2700 | + src_str = ":0"; | |
| 2701 | + | |
| 2702 | + if (parse_host_port(saddr, src_str) < 0) | |
| 2703 | + goto fail; | |
| 2704 | + | |
| 2705 | + free(str); | |
| 2706 | + return(0); | |
| 2707 | + | |
| 2708 | +fail: | |
| 2709 | + free(str); | |
| 2710 | + return -1; | |
| 2711 | +} | |
| 2712 | + | |
| 2621 | 2713 | int parse_host_port(struct sockaddr_in *saddr, const char *str) |
| 2622 | 2714 | { |
| 2623 | 2715 | char buf[512]; | ... | ... |