wget.c 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448
  1. // SPDX-License-Identifier: GPL-2.0
  2. /*
  3. * WGET/HTTP support driver based on U-BOOT's nfs.c
  4. * Copyright Duncan Hare <dh@synoia.com> 2017
  5. */
  6. #include <command.h>
  7. #include <common.h>
  8. #include <display_options.h>
  9. #include <env.h>
  10. #include <image.h>
  11. #include <mapmem.h>
  12. #include <net.h>
  13. #include <net/tcp.h>
  14. #include <net/wget.h>
  15. static const char bootfile1[] = "GET ";
  16. static const char bootfile3[] = " HTTP/1.0\r\n\r\n";
  17. static const char http_eom[] = "\r\n\r\n";
  18. static const char http_ok[] = "200";
  19. static const char content_len[] = "Content-Length";
  20. static const char linefeed[] = "\r\n";
  21. static struct in_addr web_server_ip;
  22. static int our_port;
  23. static int wget_timeout_count;
  24. struct pkt_qd {
  25. uchar *pkt;
  26. unsigned int tcp_seq_num;
  27. unsigned int len;
  28. };
  29. /*
  30. * This is a control structure for out of order packets received.
  31. * The actual packet bufers are in the kernel space, and are
  32. * expected to be overwritten by the downloaded image.
  33. */
  34. #define PKTQ_SZ (PKTBUFSRX / 4)
  35. static struct pkt_qd pkt_q[PKTQ_SZ];
  36. static int pkt_q_idx;
  37. static unsigned long content_length;
  38. static unsigned int packets;
  39. static unsigned int initial_data_seq_num;
  40. static enum wget_state current_wget_state;
  41. static char *image_url;
  42. static unsigned int wget_timeout = WGET_TIMEOUT;
  43. static enum net_loop_state wget_loop_state;
  44. /* Timeout retry parameters */
  45. static u8 retry_action; /* actions for TCP retry */
  46. static unsigned int retry_tcp_ack_num; /* TCP retry acknowledge number*/
  47. static unsigned int retry_tcp_seq_num; /* TCP retry sequence number */
  48. static int retry_len; /* TCP retry length */
  49. /**
  50. * store_block() - store block in memory
  51. * @src: source of data
  52. * @offset: offset
  53. * @len: length
  54. */
  55. static inline int store_block(uchar *src, unsigned int offset, unsigned int len)
  56. {
  57. ulong newsize = offset + len;
  58. uchar *ptr;
  59. ptr = map_sysmem(image_load_addr + offset, len);
  60. memcpy(ptr, src, len);
  61. unmap_sysmem(ptr);
  62. if (net_boot_file_size < (offset + len))
  63. net_boot_file_size = newsize;
  64. return 0;
  65. }
  66. /**
  67. * wget_send_stored() - wget response dispatcher
  68. *
  69. * WARNING, This, and only this, is the place in wget.c where
  70. * SEQUENCE NUMBERS are swapped between incoming (RX)
  71. * and outgoing (TX).
  72. * Procedure wget_handler() is correct for RX traffic.
  73. */
  74. static void wget_send_stored(void)
  75. {
  76. u8 action = retry_action;
  77. int len = retry_len;
  78. unsigned int tcp_ack_num = retry_tcp_seq_num + (len == 0 ? 1 : len);
  79. unsigned int tcp_seq_num = retry_tcp_ack_num;
  80. uchar *ptr, *offset;
  81. switch (current_wget_state) {
  82. case WGET_CLOSED:
  83. debug_cond(DEBUG_WGET, "wget: send SYN\n");
  84. current_wget_state = WGET_CONNECTING;
  85. net_send_tcp_packet(0, SERVER_PORT, our_port, action,
  86. tcp_seq_num, tcp_ack_num);
  87. packets = 0;
  88. break;
  89. case WGET_CONNECTING:
  90. pkt_q_idx = 0;
  91. net_send_tcp_packet(0, SERVER_PORT, our_port, action,
  92. tcp_seq_num, tcp_ack_num);
  93. ptr = net_tx_packet + net_eth_hdr_size() +
  94. IP_TCP_HDR_SIZE + TCP_TSOPT_SIZE + 2;
  95. offset = ptr;
  96. memcpy(offset, &bootfile1, strlen(bootfile1));
  97. offset += strlen(bootfile1);
  98. memcpy(offset, image_url, strlen(image_url));
  99. offset += strlen(image_url);
  100. memcpy(offset, &bootfile3, strlen(bootfile3));
  101. offset += strlen(bootfile3);
  102. net_send_tcp_packet((offset - ptr), SERVER_PORT, our_port,
  103. TCP_PUSH, tcp_seq_num, tcp_ack_num);
  104. current_wget_state = WGET_CONNECTED;
  105. break;
  106. case WGET_CONNECTED:
  107. case WGET_TRANSFERRING:
  108. case WGET_TRANSFERRED:
  109. net_send_tcp_packet(0, SERVER_PORT, our_port, action,
  110. tcp_seq_num, tcp_ack_num);
  111. break;
  112. }
  113. }
  114. static void wget_send(u8 action, unsigned int tcp_seq_num,
  115. unsigned int tcp_ack_num, int len)
  116. {
  117. retry_action = action;
  118. retry_tcp_ack_num = tcp_ack_num;
  119. retry_tcp_seq_num = tcp_seq_num;
  120. retry_len = len;
  121. wget_send_stored();
  122. }
  123. void wget_fail(char *error_message, unsigned int tcp_seq_num,
  124. unsigned int tcp_ack_num, u8 action)
  125. {
  126. printf("wget: Transfer Fail - %s\n", error_message);
  127. net_set_timeout_handler(0, NULL);
  128. wget_send(action, tcp_seq_num, tcp_ack_num, 0);
  129. }
  130. void wget_success(u8 action, unsigned int tcp_seq_num,
  131. unsigned int tcp_ack_num, int len, int packets)
  132. {
  133. printf("Packets received %d, Transfer Successful\n", packets);
  134. wget_send(action, tcp_seq_num, tcp_ack_num, len);
  135. }
  136. /*
  137. * Interfaces of U-BOOT
  138. */
  139. static void wget_timeout_handler(void)
  140. {
  141. if (++wget_timeout_count > WGET_RETRY_COUNT) {
  142. puts("\nRetry count exceeded; starting again\n");
  143. wget_send(TCP_RST, 0, 0, 0);
  144. net_start_again();
  145. } else {
  146. puts("T ");
  147. net_set_timeout_handler(wget_timeout +
  148. WGET_TIMEOUT * wget_timeout_count,
  149. wget_timeout_handler);
  150. wget_send_stored();
  151. }
  152. }
  153. #define PKT_QUEUE_OFFSET 0x20000
  154. #define PKT_QUEUE_PACKET_SIZE 0x800
  155. static void wget_connected(uchar *pkt, unsigned int tcp_seq_num,
  156. u8 action, unsigned int tcp_ack_num, unsigned int len)
  157. {
  158. uchar *pkt_in_q;
  159. char *pos;
  160. int hlen, i;
  161. uchar *ptr1;
  162. pkt[len] = '\0';
  163. pos = strstr((char *)pkt, http_eom);
  164. if (!pos) {
  165. debug_cond(DEBUG_WGET,
  166. "wget: Connected, data before Header %p\n", pkt);
  167. pkt_in_q = (void *)image_load_addr + PKT_QUEUE_OFFSET +
  168. (pkt_q_idx * PKT_QUEUE_PACKET_SIZE);
  169. ptr1 = map_sysmem((phys_addr_t)pkt_in_q, len);
  170. memcpy(ptr1, pkt, len);
  171. unmap_sysmem(ptr1);
  172. pkt_q[pkt_q_idx].pkt = pkt_in_q;
  173. pkt_q[pkt_q_idx].tcp_seq_num = tcp_seq_num;
  174. pkt_q[pkt_q_idx].len = len;
  175. pkt_q_idx++;
  176. if (pkt_q_idx >= PKTQ_SZ) {
  177. printf("wget: Fatal error, queue overrun!\n");
  178. net_set_state(NETLOOP_FAIL);
  179. return;
  180. }
  181. } else {
  182. debug_cond(DEBUG_WGET, "wget: Connected HTTP Header %p\n", pkt);
  183. /* sizeof(http_eom) - 1 is the string length of (http_eom) */
  184. hlen = pos - (char *)pkt + sizeof(http_eom) - 1;
  185. pos = strstr((char *)pkt, linefeed);
  186. if (pos > 0)
  187. i = pos - (char *)pkt;
  188. else
  189. i = hlen;
  190. printf("%.*s", i, pkt);
  191. current_wget_state = WGET_TRANSFERRING;
  192. if (strstr((char *)pkt, http_ok) == 0) {
  193. debug_cond(DEBUG_WGET,
  194. "wget: Connected Bad Xfer\n");
  195. initial_data_seq_num = tcp_seq_num + hlen;
  196. wget_loop_state = NETLOOP_FAIL;
  197. wget_send(action, tcp_seq_num, tcp_ack_num, len);
  198. } else {
  199. debug_cond(DEBUG_WGET,
  200. "wget: Connctd pkt %p hlen %x\n",
  201. pkt, hlen);
  202. initial_data_seq_num = tcp_seq_num + hlen;
  203. pos = strstr((char *)pkt, content_len);
  204. if (!pos) {
  205. content_length = -1;
  206. } else {
  207. pos += sizeof(content_len) + 2;
  208. strict_strtoul(pos, 10, &content_length);
  209. debug_cond(DEBUG_WGET,
  210. "wget: Connected Len %lu\n",
  211. content_length);
  212. }
  213. net_boot_file_size = 0;
  214. if (len > hlen)
  215. store_block(pkt + hlen, 0, len - hlen);
  216. debug_cond(DEBUG_WGET,
  217. "wget: Connected Pkt %p hlen %x\n",
  218. pkt, hlen);
  219. for (i = 0; i < pkt_q_idx; i++) {
  220. ptr1 = map_sysmem(
  221. (phys_addr_t)(pkt_q[i].pkt),
  222. pkt_q[i].len);
  223. store_block(ptr1,
  224. pkt_q[i].tcp_seq_num -
  225. initial_data_seq_num,
  226. pkt_q[i].len);
  227. unmap_sysmem(ptr1);
  228. debug_cond(DEBUG_WGET,
  229. "wget: Connctd pkt Q %p len %x\n",
  230. pkt_q[i].pkt, pkt_q[i].len);
  231. }
  232. }
  233. }
  234. wget_send(action, tcp_seq_num, tcp_ack_num, len);
  235. }
  236. /**
  237. * wget_handler() - TCP handler of wget
  238. * @pkt: pointer to the application packet
  239. * @dport: destination TCP port
  240. * @sip: source IP address
  241. * @sport: source TCP port
  242. * @tcp_seq_num: TCP sequential number
  243. * @tcp_ack_num: TCP acknowledgment number
  244. * @action: TCP action (SYN, ACK, FIN, etc)
  245. * @len: packet length
  246. *
  247. * In the "application push" invocation, the TCP header with all
  248. * its information is pointed to by the packet pointer.
  249. */
  250. static void wget_handler(uchar *pkt, u16 dport,
  251. struct in_addr sip, u16 sport,
  252. u32 tcp_seq_num, u32 tcp_ack_num,
  253. u8 action, unsigned int len)
  254. {
  255. enum tcp_state wget_tcp_state = tcp_get_tcp_state();
  256. net_set_timeout_handler(wget_timeout, wget_timeout_handler);
  257. packets++;
  258. switch (current_wget_state) {
  259. case WGET_CLOSED:
  260. debug_cond(DEBUG_WGET, "wget: Handler: Error!, State wrong\n");
  261. break;
  262. case WGET_CONNECTING:
  263. debug_cond(DEBUG_WGET,
  264. "wget: Connecting In len=%x, Seq=%u, Ack=%u\n",
  265. len, tcp_seq_num, tcp_ack_num);
  266. if (!len) {
  267. if (wget_tcp_state == TCP_ESTABLISHED) {
  268. debug_cond(DEBUG_WGET,
  269. "wget: Cting, send, len=%x\n", len);
  270. wget_send(action, tcp_seq_num, tcp_ack_num,
  271. len);
  272. } else {
  273. printf("%.*s", len, pkt);
  274. wget_fail("wget: Handler Connected Fail\n",
  275. tcp_seq_num, tcp_ack_num, action);
  276. }
  277. }
  278. break;
  279. case WGET_CONNECTED:
  280. debug_cond(DEBUG_WGET, "wget: Connected seq=%u, len=%x\n",
  281. tcp_seq_num, len);
  282. if (!len) {
  283. wget_fail("Image not found, no data returned\n",
  284. tcp_seq_num, tcp_ack_num, action);
  285. } else {
  286. wget_connected(pkt, tcp_seq_num, action, tcp_ack_num, len);
  287. }
  288. break;
  289. case WGET_TRANSFERRING:
  290. debug_cond(DEBUG_WGET,
  291. "wget: Transferring, seq=%x, ack=%x,len=%x\n",
  292. tcp_seq_num, tcp_ack_num, len);
  293. if (tcp_seq_num >= initial_data_seq_num &&
  294. store_block(pkt, tcp_seq_num - initial_data_seq_num,
  295. len) != 0) {
  296. wget_fail("wget: store error\n",
  297. tcp_seq_num, tcp_ack_num, action);
  298. return;
  299. }
  300. switch (wget_tcp_state) {
  301. case TCP_FIN_WAIT_2:
  302. wget_send(TCP_ACK, tcp_seq_num, tcp_ack_num, len);
  303. fallthrough;
  304. case TCP_SYN_SENT:
  305. case TCP_SYN_RECEIVED:
  306. case TCP_CLOSING:
  307. case TCP_FIN_WAIT_1:
  308. case TCP_CLOSED:
  309. net_set_state(NETLOOP_FAIL);
  310. break;
  311. case TCP_ESTABLISHED:
  312. wget_send(TCP_ACK, tcp_seq_num, tcp_ack_num,
  313. len);
  314. wget_loop_state = NETLOOP_SUCCESS;
  315. break;
  316. case TCP_CLOSE_WAIT: /* End of transfer */
  317. current_wget_state = WGET_TRANSFERRED;
  318. wget_send(action | TCP_ACK | TCP_FIN,
  319. tcp_seq_num, tcp_ack_num, len);
  320. break;
  321. }
  322. break;
  323. case WGET_TRANSFERRED:
  324. printf("Packets received %d, Transfer Successful\n", packets);
  325. net_set_state(wget_loop_state);
  326. break;
  327. }
  328. }
  329. #define RANDOM_PORT_START 1024
  330. #define RANDOM_PORT_RANGE 0x4000
  331. /**
  332. * random_port() - make port a little random (1024-17407)
  333. *
  334. * Return: random port number from 1024 to 17407
  335. *
  336. * This keeps the math somewhat trivial to compute, and seems to work with
  337. * all supported protocols/clients/servers
  338. */
  339. static unsigned int random_port(void)
  340. {
  341. return RANDOM_PORT_START + (get_timer(0) % RANDOM_PORT_RANGE);
  342. }
  343. #define BLOCKSIZE 512
  344. void wget_start(void)
  345. {
  346. image_url = strchr(net_boot_file_name, ':');
  347. if (image_url > 0) {
  348. web_server_ip = string_to_ip(net_boot_file_name);
  349. ++image_url;
  350. net_server_ip = web_server_ip;
  351. } else {
  352. web_server_ip = net_server_ip;
  353. image_url = net_boot_file_name;
  354. }
  355. debug_cond(DEBUG_WGET,
  356. "wget: Transfer HTTP Server %pI4; our IP %pI4\n",
  357. &web_server_ip, &net_ip);
  358. /* Check if we need to send across this subnet */
  359. if (net_gateway.s_addr && net_netmask.s_addr) {
  360. struct in_addr our_net;
  361. struct in_addr server_net;
  362. our_net.s_addr = net_ip.s_addr & net_netmask.s_addr;
  363. server_net.s_addr = net_server_ip.s_addr & net_netmask.s_addr;
  364. if (our_net.s_addr != server_net.s_addr)
  365. debug_cond(DEBUG_WGET,
  366. "wget: sending through gateway %pI4",
  367. &net_gateway);
  368. }
  369. debug_cond(DEBUG_WGET, "URL '%s'\n", image_url);
  370. if (net_boot_file_expected_size_in_blocks) {
  371. debug_cond(DEBUG_WGET, "wget: Size is 0x%x Bytes = ",
  372. net_boot_file_expected_size_in_blocks * BLOCKSIZE);
  373. print_size(net_boot_file_expected_size_in_blocks * BLOCKSIZE,
  374. "");
  375. }
  376. debug_cond(DEBUG_WGET,
  377. "\nwget:Load address: 0x%lx\nLoading: *\b", image_load_addr);
  378. net_set_timeout_handler(wget_timeout, wget_timeout_handler);
  379. tcp_set_tcp_handler(wget_handler);
  380. wget_timeout_count = 0;
  381. current_wget_state = WGET_CLOSED;
  382. our_port = random_port();
  383. /*
  384. * Zero out server ether to force arp resolution in case
  385. * the server ip for the previous u-boot command, for example dns
  386. * is not the same as the web server ip.
  387. */
  388. memset(net_server_ethaddr, 0, 6);
  389. wget_send(TCP_SYN, 0, 0, 0);
  390. }