Commit c7f746434f28c06faf9f3ebaac4973502468c4d3

Authored by bellard
1 parent 60e336db

TFTP support (Magnus Damm)


git-svn-id: svn://svn.savannah.nongnu.org/qemu/trunk@1050 c046a42c-6fe2-441c-8c8c-71466251a162
Changelog
... ... @@ -8,6 +8,7 @@ version 0.6.1:
8 8 transparent decompression
9 9 - VMware 3 and 4 read-only disk image support (untested)
10 10 - Support for up to 4 serial ports
  11 + - TFTP server support (Magnus Damm)
11 12  
12 13 version 0.6.0:
13 14  
... ...
Makefile.target
... ... @@ -264,7 +264,7 @@ ifdef CONFIG_SLIRP
264 264 DEFINES+=-I$(SRC_PATH)/slirp
265 265 SLIRP_OBJS=cksum.o if.o ip_icmp.o ip_input.o ip_output.o \
266 266 slirp.o mbuf.o misc.o sbuf.o socket.o tcp_input.o tcp_output.o \
267   -tcp_subr.o tcp_timer.o udp.o bootp.o debug.o
  267 +tcp_subr.o tcp_timer.o udp.o bootp.o debug.o tftp.o
268 268 VL_OBJS+=$(addprefix slirp/, $(SLIRP_OBJS))
269 269 endif
270 270  
... ...
slirp/slirp.h
... ... @@ -210,6 +210,7 @@ int inet_aton _P((const char *cp, struct in_addr *ia));
210 210 #endif
211 211  
212 212 #include "bootp.h"
  213 +#include "tftp.h"
213 214 #include "libslirp.h"
214 215  
215 216 extern struct ttys *ttys_unit[MAX_INTERFACES];
... ...
slirp/tftp.c 0 → 100644
  1 +/*
  2 + * tftp.c - a simple, read-only tftp server for qemu
  3 + *
  4 + * Copyright (c) 2004 Magnus Damm <damm@opensource.se>
  5 + *
  6 + * Permission is hereby granted, free of charge, to any person obtaining a copy
  7 + * of this software and associated documentation files (the "Software"), to deal
  8 + * in the Software without restriction, including without limitation the rights
  9 + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  10 + * copies of the Software, and to permit persons to whom the Software is
  11 + * furnished to do so, subject to the following conditions:
  12 + *
  13 + * The above copyright notice and this permission notice shall be included in
  14 + * all copies or substantial portions of the Software.
  15 + *
  16 + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  17 + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  18 + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
  19 + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  20 + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  21 + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  22 + * THE SOFTWARE.
  23 + */
  24 +
  25 +#include <slirp.h>
  26 +
  27 +struct tftp_session {
  28 + int in_use;
  29 + unsigned char filename[TFTP_FILENAME_MAX];
  30 +
  31 + struct in_addr client_ip;
  32 + u_int16_t client_port;
  33 +
  34 + struct timeval timestamp;
  35 +};
  36 +
  37 +struct tftp_session tftp_sessions[TFTP_SESSIONS_MAX];
  38 +
  39 +char *tftp_prefix;
  40 +
  41 +static void tftp_session_update(struct tftp_session *spt)
  42 +{
  43 + gettimeofday(&spt->timestamp, 0);
  44 + spt->in_use = 1;
  45 +}
  46 +
  47 +static void tftp_session_terminate(struct tftp_session *spt)
  48 +{
  49 + spt->in_use = 0;
  50 +}
  51 +
  52 +static int tftp_session_allocate(struct tftp_t *tp)
  53 +{
  54 + struct tftp_session *spt;
  55 + struct timeval tv;
  56 + int k;
  57 +
  58 + gettimeofday(&tv, 0);
  59 +
  60 + for (k = 0; k < TFTP_SESSIONS_MAX; k++) {
  61 + spt = &tftp_sessions[k];
  62 +
  63 + if (!spt->in_use) {
  64 + goto found;
  65 + }
  66 +
  67 + /* sessions time out after 5 inactive seconds */
  68 +
  69 + if (tv.tv_sec > (spt->timestamp.tv_sec + 5)) {
  70 + goto found;
  71 + }
  72 + }
  73 +
  74 + return -1;
  75 +
  76 + found:
  77 + memset(spt, 0, sizeof(*spt));
  78 + memcpy(&spt->client_ip, &tp->ip.ip_src, sizeof(spt->client_ip));
  79 + spt->client_port = tp->udp.uh_sport;
  80 +
  81 + tftp_session_update(spt);
  82 +
  83 + return k;
  84 +}
  85 +
  86 +static int tftp_session_find(struct tftp_t *tp)
  87 +{
  88 + struct tftp_session *spt;
  89 + int k;
  90 +
  91 + for (k = 0; k < TFTP_SESSIONS_MAX; k++) {
  92 + spt = &tftp_sessions[k];
  93 +
  94 + if (spt->in_use) {
  95 + if (!memcmp(&spt->client_ip, &tp->ip.ip_src, sizeof(spt->client_ip))) {
  96 + if (spt->client_port == tp->udp.uh_sport) {
  97 + return k;
  98 + }
  99 + }
  100 + }
  101 + }
  102 +
  103 + return -1;
  104 +}
  105 +
  106 +static int tftp_read_data(struct tftp_session *spt, u_int16_t block_nr,
  107 + u_int8_t *buf, int len)
  108 +{
  109 + int fd;
  110 + int bytes_read = 0;
  111 +
  112 + fd = open(spt->filename, O_RDONLY);
  113 +
  114 + if (fd < 0) {
  115 + return -1;
  116 + }
  117 +
  118 + if (len) {
  119 + lseek(fd, block_nr * 512, SEEK_SET);
  120 +
  121 + bytes_read = read(fd, buf, len);
  122 + }
  123 +
  124 + close(fd);
  125 +
  126 + return bytes_read;
  127 +}
  128 +
  129 +static int tftp_send_error(struct tftp_session *spt,
  130 + u_int16_t errorcode, const char *msg,
  131 + struct tftp_t *recv_tp)
  132 +{
  133 + struct sockaddr_in saddr, daddr;
  134 + struct mbuf *m;
  135 + struct tftp_t *tp;
  136 + int nobytes;
  137 +
  138 + m = m_get();
  139 +
  140 + if (!m) {
  141 + return -1;
  142 + }
  143 +
  144 + memset(m->m_data, 0, m->m_size);
  145 +
  146 + m->m_data += if_maxlinkhdr;
  147 + tp = (void *)m->m_data;
  148 + m->m_data += sizeof(struct udpiphdr);
  149 +
  150 + tp->tp_op = htons(TFTP_ERROR);
  151 + tp->x.tp_error.tp_error_code = htons(errorcode);
  152 + strcpy(tp->x.tp_error.tp_msg, msg);
  153 +
  154 + saddr.sin_addr = recv_tp->ip.ip_dst;
  155 + saddr.sin_port = recv_tp->udp.uh_dport;
  156 +
  157 + daddr.sin_addr = spt->client_ip;
  158 + daddr.sin_port = spt->client_port;
  159 +
  160 + nobytes = 2;
  161 +
  162 + m->m_len = sizeof(struct tftp_t) - 514 + 3 + strlen(msg) -
  163 + sizeof(struct ip) - sizeof(struct udphdr);
  164 +
  165 + udp_output2(NULL, m, &saddr, &daddr, IPTOS_LOWDELAY);
  166 +
  167 + tftp_session_terminate(spt);
  168 +
  169 + return 0;
  170 +}
  171 +
  172 +static int tftp_send_data(struct tftp_session *spt,
  173 + u_int16_t block_nr,
  174 + struct tftp_t *recv_tp)
  175 +{
  176 + struct sockaddr_in saddr, daddr;
  177 + struct mbuf *m;
  178 + struct tftp_t *tp;
  179 + int nobytes;
  180 +
  181 + if (block_nr < 1) {
  182 + return -1;
  183 + }
  184 +
  185 + m = m_get();
  186 +
  187 + if (!m) {
  188 + return -1;
  189 + }
  190 +
  191 + memset(m->m_data, 0, m->m_size);
  192 +
  193 + m->m_data += if_maxlinkhdr;
  194 + tp = (void *)m->m_data;
  195 + m->m_data += sizeof(struct udpiphdr);
  196 +
  197 + tp->tp_op = htons(TFTP_DATA);
  198 + tp->x.tp_data.tp_block_nr = htons(block_nr);
  199 +
  200 + saddr.sin_addr = recv_tp->ip.ip_dst;
  201 + saddr.sin_port = recv_tp->udp.uh_dport;
  202 +
  203 + daddr.sin_addr = spt->client_ip;
  204 + daddr.sin_port = spt->client_port;
  205 +
  206 + nobytes = tftp_read_data(spt, block_nr - 1, tp->x.tp_data.tp_buf, 512);
  207 +
  208 + if (nobytes < 0) {
  209 + m_free(m);
  210 +
  211 + /* send "file not found" error back */
  212 +
  213 + tftp_send_error(spt, 1, "File not found", tp);
  214 +
  215 + return -1;
  216 + }
  217 +
  218 + m->m_len = sizeof(struct tftp_t) - (512 - nobytes) -
  219 + sizeof(struct ip) - sizeof(struct udphdr);
  220 +
  221 + udp_output2(NULL, m, &saddr, &daddr, IPTOS_LOWDELAY);
  222 +
  223 + if (nobytes == 512) {
  224 + tftp_session_update(spt);
  225 + }
  226 + else {
  227 + tftp_session_terminate(spt);
  228 + }
  229 +
  230 + return 0;
  231 +}
  232 +
  233 +static void tftp_handle_rrq(struct tftp_t *tp, int pktlen)
  234 +{
  235 + struct tftp_session *spt;
  236 + int s, k, n;
  237 + u_int8_t *src, *dst;
  238 +
  239 + s = tftp_session_allocate(tp);
  240 +
  241 + if (s < 0) {
  242 + return;
  243 + }
  244 +
  245 + spt = &tftp_sessions[s];
  246 +
  247 + src = tp->x.tp_buf;
  248 + dst = spt->filename;
  249 + n = pktlen - ((uint8_t *)&tp->x.tp_buf[0] - (uint8_t *)tp);
  250 +
  251 + /* get name */
  252 +
  253 + for (k = 0; k < n; k++) {
  254 + if (k < TFTP_FILENAME_MAX) {
  255 + dst[k] = src[k];
  256 + }
  257 + else {
  258 + return;
  259 + }
  260 +
  261 + if (src[k] == '\0') {
  262 + break;
  263 + }
  264 + }
  265 +
  266 + if (k >= n) {
  267 + return;
  268 + }
  269 +
  270 + k++;
  271 +
  272 + /* check mode */
  273 + if ((n - k) < 6) {
  274 + return;
  275 + }
  276 +
  277 + if (memcmp(&src[k], "octet\0", 6) != 0) {
  278 + tftp_send_error(spt, 4, "Unsupported transfer mode", tp);
  279 + return;
  280 + }
  281 +
  282 + /* do sanity checks on the filename */
  283 +
  284 + if ((spt->filename[0] != '/')
  285 + || (spt->filename[strlen(spt->filename) - 1] == '/')
  286 + || strstr(spt->filename, "/../")) {
  287 + tftp_send_error(spt, 2, "Access violation", tp);
  288 + return;
  289 + }
  290 +
  291 + /* only allow exported prefixes */
  292 +
  293 + if (!tftp_prefix
  294 + || (strncmp(spt->filename, tftp_prefix, strlen(tftp_prefix)) != 0)) {
  295 + tftp_send_error(spt, 2, "Access violation", tp);
  296 + return;
  297 + }
  298 +
  299 + /* check if the file exists */
  300 +
  301 + if (tftp_read_data(spt, 0, spt->filename, 0) < 0) {
  302 + tftp_send_error(spt, 1, "File not found", tp);
  303 + return;
  304 + }
  305 +
  306 + tftp_send_data(spt, 1, tp);
  307 +}
  308 +
  309 +static void tftp_handle_ack(struct tftp_t *tp, int pktlen)
  310 +{
  311 + int s;
  312 +
  313 + s = tftp_session_find(tp);
  314 +
  315 + if (s < 0) {
  316 + return;
  317 + }
  318 +
  319 + if (tftp_send_data(&tftp_sessions[s],
  320 + ntohs(tp->x.tp_data.tp_block_nr) + 1,
  321 + tp) < 0) {
  322 + return;
  323 + }
  324 +}
  325 +
  326 +void tftp_input(struct mbuf *m)
  327 +{
  328 + struct tftp_t *tp = (struct tftp_t *)m->m_data;
  329 +
  330 + switch(ntohs(tp->tp_op)) {
  331 + case TFTP_RRQ:
  332 + tftp_handle_rrq(tp, m->m_len);
  333 + break;
  334 +
  335 + case TFTP_ACK:
  336 + tftp_handle_ack(tp, m->m_len);
  337 + break;
  338 + }
  339 +}
... ...
slirp/tftp.h 0 → 100644
  1 +/* tftp defines */
  2 +
  3 +#define TFTP_SESSIONS_MAX 3
  4 +
  5 +#define TFTP_SERVER 69
  6 +
  7 +#define TFTP_RRQ 1
  8 +#define TFTP_WRQ 2
  9 +#define TFTP_DATA 3
  10 +#define TFTP_ACK 4
  11 +#define TFTP_ERROR 5
  12 +
  13 +#define TFTP_FILENAME_MAX 512
  14 +
  15 +struct tftp_t {
  16 + struct ip ip;
  17 + struct udphdr udp;
  18 + u_int16_t tp_op;
  19 + union {
  20 + struct {
  21 + u_int16_t tp_block_nr;
  22 + u_int8_t tp_buf[512];
  23 + } tp_data;
  24 + struct {
  25 + u_int16_t tp_error_code;
  26 + u_int8_t tp_msg[512];
  27 + } tp_error;
  28 + u_int8_t tp_buf[512 + 2];
  29 + } x;
  30 +};
  31 +
  32 +extern char *tftp_prefix;
  33 +
  34 +void tftp_input(struct mbuf *m);
... ...
slirp/udp.c
... ... @@ -153,6 +153,14 @@ udp_input(m, iphlen)
153 153 goto bad;
154 154 }
155 155  
  156 + /*
  157 + * handle TFTP
  158 + */
  159 + if (ntohs(uh->uh_dport) == TFTP_SERVER) {
  160 + tftp_input(m);
  161 + goto bad;
  162 + }
  163 +
156 164 /*
157 165 * Locate pcb for datagram.
158 166 */
... ...
... ... @@ -2334,6 +2334,7 @@ void help(void)
2334 2334 "-tun-fd fd use this fd as already opened tap/tun interface\n"
2335 2335 #ifdef CONFIG_SLIRP
2336 2336 "-user-net use user mode network stack [default if no tap/tun script]\n"
  2337 + "-tftp prefix allow tftp access to files starting with prefix [only with -user-net enabled]\n"
2337 2338 #endif
2338 2339 "-dummy-net use dummy network stack\n"
2339 2340 "\n"
... ... @@ -2408,6 +2409,7 @@ enum {
2408 2409 QEMU_OPTION_n,
2409 2410 QEMU_OPTION_tun_fd,
2410 2411 QEMU_OPTION_user_net,
  2412 + QEMU_OPTION_tftp,
2411 2413 QEMU_OPTION_dummy_net,
2412 2414  
2413 2415 QEMU_OPTION_kernel,
... ... @@ -2460,6 +2462,7 @@ const QEMUOption qemu_options[] = {
2460 2462 { "tun-fd", HAS_ARG, QEMU_OPTION_tun_fd },
2461 2463 #ifdef CONFIG_SLIRP
2462 2464 { "user-net", 0, QEMU_OPTION_user_net },
  2465 + { "tftp", HAS_ARG, QEMU_OPTION_tftp },
2463 2466 #endif
2464 2467 { "dummy-net", 0, QEMU_OPTION_dummy_net },
2465 2468  
... ... @@ -2751,9 +2754,17 @@ int main(int argc, char **argv)
2751 2754 }
2752 2755 }
2753 2756 break;
  2757 +#ifdef CONFIG_SLIRP
  2758 + case QEMU_OPTION_tftp:
  2759 + {
  2760 + extern const char *tftp_prefix;
  2761 + tftp_prefix = optarg;
  2762 + }
  2763 + break;
2754 2764 case QEMU_OPTION_user_net:
2755 2765 net_if_type = NET_IF_USER;
2756 2766 break;
  2767 +#endif
2757 2768 case QEMU_OPTION_dummy_net:
2758 2769 net_if_type = NET_IF_DUMMY;
2759 2770 break;
... ...