udp.c 5.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212
  1. /*
  2. * GRUB -- GRand Unified Bootloader
  3. * Copyright (C) 2010,2011 Free Software Foundation, Inc.
  4. *
  5. * GRUB is free software: you can redistribute it and/or modify
  6. * it under the terms of the GNU General Public License as published by
  7. * the Free Software Foundation, either version 3 of the License, or
  8. * (at your option) any later version.
  9. *
  10. * GRUB is distributed in the hope that it will be useful,
  11. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13. * GNU General Public License for more details.
  14. *
  15. * You should have received a copy of the GNU General Public License
  16. * along with GRUB. If not, see <http://www.gnu.org/licenses/>.
  17. */
  18. #include <grub/net.h>
  19. #include <grub/net/udp.h>
  20. #include <grub/net/ip.h>
  21. #include <grub/net/netbuff.h>
  22. #include <grub/time.h>
  23. struct grub_net_udp_socket
  24. {
  25. struct grub_net_udp_socket *next;
  26. struct grub_net_udp_socket **prev;
  27. enum { GRUB_NET_SOCKET_START,
  28. GRUB_NET_SOCKET_ESTABLISHED,
  29. GRUB_NET_SOCKET_CLOSED } status;
  30. int in_port;
  31. int out_port;
  32. grub_err_t (*recv_hook) (grub_net_udp_socket_t sock, struct grub_net_buff *nb,
  33. void *recv);
  34. void *recv_hook_data;
  35. grub_net_network_level_address_t out_nla;
  36. grub_net_link_level_address_t ll_target_addr;
  37. struct grub_net_network_level_interface *inf;
  38. };
  39. static struct grub_net_udp_socket *udp_sockets;
  40. #define FOR_UDP_SOCKETS(var) for (var = udp_sockets; var; var = var->next)
  41. static inline void
  42. udp_socket_register (grub_net_udp_socket_t sock)
  43. {
  44. grub_list_push (GRUB_AS_LIST_P (&udp_sockets),
  45. GRUB_AS_LIST (sock));
  46. }
  47. void
  48. grub_net_udp_close (grub_net_udp_socket_t sock)
  49. {
  50. grub_list_remove (GRUB_AS_LIST (sock));
  51. grub_free (sock);
  52. }
  53. grub_net_udp_socket_t
  54. grub_net_udp_open (grub_net_network_level_address_t addr,
  55. grub_uint16_t out_port,
  56. grub_err_t (*recv_hook) (grub_net_udp_socket_t sock,
  57. struct grub_net_buff *nb,
  58. void *data),
  59. void *recv_hook_data)
  60. {
  61. grub_err_t err;
  62. struct grub_net_network_level_interface *inf;
  63. grub_net_network_level_address_t gateway;
  64. grub_net_udp_socket_t socket;
  65. static int in_port = 25300;
  66. grub_net_link_level_address_t ll_target_addr;
  67. if (addr.type != GRUB_NET_NETWORK_LEVEL_PROTOCOL_IPV4
  68. && addr.type != GRUB_NET_NETWORK_LEVEL_PROTOCOL_IPV6)
  69. {
  70. grub_error (GRUB_ERR_BUG, "not an IP address");
  71. return NULL;
  72. }
  73. err = grub_net_route_address (addr, &gateway, &inf);
  74. if (err)
  75. return NULL;
  76. err = grub_net_link_layer_resolve (inf, &gateway, &ll_target_addr);
  77. if (err)
  78. return NULL;
  79. socket = grub_zalloc (sizeof (*socket));
  80. if (socket == NULL)
  81. return NULL;
  82. socket->out_port = out_port;
  83. socket->inf = inf;
  84. socket->out_nla = addr;
  85. socket->ll_target_addr = ll_target_addr;
  86. socket->in_port = in_port++;
  87. socket->status = GRUB_NET_SOCKET_START;
  88. socket->recv_hook = recv_hook;
  89. socket->recv_hook_data = recv_hook_data;
  90. udp_socket_register (socket);
  91. return socket;
  92. }
  93. grub_err_t
  94. grub_net_send_udp_packet (const grub_net_udp_socket_t socket,
  95. struct grub_net_buff *nb)
  96. {
  97. struct udphdr *udph;
  98. grub_err_t err;
  99. COMPILE_TIME_ASSERT (GRUB_NET_UDP_HEADER_SIZE == sizeof (*udph));
  100. err = grub_netbuff_push (nb, sizeof (*udph));
  101. if (err)
  102. return err;
  103. udph = (struct udphdr *) nb->data;
  104. udph->src = grub_cpu_to_be16 (socket->in_port);
  105. udph->dst = grub_cpu_to_be16 (socket->out_port);
  106. udph->chksum = 0;
  107. udph->len = grub_cpu_to_be16 (nb->tail - nb->data);
  108. udph->chksum = grub_net_ip_transport_checksum (nb, GRUB_NET_IP_UDP,
  109. &socket->inf->address,
  110. &socket->out_nla);
  111. return grub_net_send_ip_packet (socket->inf, &(socket->out_nla),
  112. &(socket->ll_target_addr), nb,
  113. GRUB_NET_IP_UDP);
  114. }
  115. grub_err_t
  116. grub_net_recv_udp_packet (struct grub_net_buff *nb,
  117. struct grub_net_network_level_interface *inf,
  118. const grub_net_network_level_address_t *source)
  119. {
  120. struct udphdr *udph;
  121. grub_net_udp_socket_t sock;
  122. grub_err_t err;
  123. /* Ignore broadcast. */
  124. if (!inf)
  125. {
  126. grub_netbuff_free (nb);
  127. return GRUB_ERR_NONE;
  128. }
  129. udph = (struct udphdr *) nb->data;
  130. if (nb->tail - nb->data < (grub_ssize_t) sizeof (*udph))
  131. {
  132. grub_dprintf ("net", "UDP packet too short: %" PRIuGRUB_SIZE "\n",
  133. (grub_size_t) (nb->tail - nb->data));
  134. grub_netbuff_free (nb);
  135. return GRUB_ERR_NONE;
  136. }
  137. FOR_UDP_SOCKETS (sock)
  138. {
  139. if (grub_be_to_cpu16 (udph->dst) == sock->in_port
  140. && inf == sock->inf
  141. && grub_net_addr_cmp (source, &sock->out_nla) == 0
  142. && (sock->status == GRUB_NET_SOCKET_START
  143. || grub_be_to_cpu16 (udph->src) == sock->out_port))
  144. {
  145. if (udph->chksum)
  146. {
  147. grub_uint16_t chk, expected;
  148. chk = udph->chksum;
  149. udph->chksum = 0;
  150. expected = grub_net_ip_transport_checksum (nb, GRUB_NET_IP_UDP,
  151. &sock->out_nla,
  152. &sock->inf->address);
  153. if (expected != chk)
  154. {
  155. grub_dprintf ("net", "Invalid UDP checksum. "
  156. "Expected %x, got %x\n",
  157. grub_be_to_cpu16 (expected),
  158. grub_be_to_cpu16 (chk));
  159. grub_netbuff_free (nb);
  160. return GRUB_ERR_NONE;
  161. }
  162. udph->chksum = chk;
  163. }
  164. if (sock->status == GRUB_NET_SOCKET_START)
  165. {
  166. sock->out_port = grub_be_to_cpu16 (udph->src);
  167. sock->status = GRUB_NET_SOCKET_ESTABLISHED;
  168. }
  169. err = grub_netbuff_pull (nb, sizeof (*udph));
  170. if (err)
  171. return err;
  172. /* App protocol remove its own reader. */
  173. if (sock->recv_hook)
  174. sock->recv_hook (sock, nb, sock->recv_hook_data);
  175. else
  176. grub_netbuff_free (nb);
  177. return GRUB_ERR_NONE;
  178. }
  179. }
  180. grub_netbuff_free (nb);
  181. return GRUB_ERR_NONE;
  182. }