Upgraded GRUB2 to 2.00 release.
[AROS.git] / arch / all-pc / boot / grub2-aros / grub-core / net / udp.c
blob919dca8dbd99700a1fa56c70231eff5e660f3c82
1 /*
2 * GRUB -- GRand Unified Bootloader
3 * Copyright (C) 2010,2011 Free Software Foundation, Inc.
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.
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.
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/>.
19 #include <grub/net.h>
20 #include <grub/net/udp.h>
21 #include <grub/net/ip.h>
22 #include <grub/net/netbuff.h>
23 #include <grub/time.h>
25 struct grub_net_udp_socket
27 struct grub_net_udp_socket *next;
28 struct grub_net_udp_socket **prev;
30 enum { GRUB_NET_SOCKET_START,
31 GRUB_NET_SOCKET_ESTABLISHED,
32 GRUB_NET_SOCKET_CLOSED } status;
33 int in_port;
34 int out_port;
35 grub_err_t (*recv_hook) (grub_net_udp_socket_t sock, struct grub_net_buff *nb,
36 void *recv);
37 void *recv_hook_data;
38 grub_net_network_level_address_t out_nla;
39 grub_net_link_level_address_t ll_target_addr;
40 struct grub_net_network_level_interface *inf;
43 static struct grub_net_udp_socket *udp_sockets;
45 #define FOR_UDP_SOCKETS(var) for (var = udp_sockets; var; var = var->next)
47 static inline void
48 udp_socket_register (grub_net_udp_socket_t sock)
50 grub_list_push (GRUB_AS_LIST_P (&udp_sockets),
51 GRUB_AS_LIST (sock));
54 void
55 grub_net_udp_close (grub_net_udp_socket_t sock)
57 grub_list_remove (GRUB_AS_LIST (sock));
58 grub_free (sock);
61 grub_net_udp_socket_t
62 grub_net_udp_open (grub_net_network_level_address_t addr,
63 grub_uint16_t out_port,
64 grub_err_t (*recv_hook) (grub_net_udp_socket_t sock,
65 struct grub_net_buff *nb,
66 void *data),
67 void *recv_hook_data)
69 grub_err_t err;
70 struct grub_net_network_level_interface *inf;
71 grub_net_network_level_address_t gateway;
72 grub_net_udp_socket_t socket;
73 static int in_port = 25300;
74 grub_net_link_level_address_t ll_target_addr;
76 if (addr.type != GRUB_NET_NETWORK_LEVEL_PROTOCOL_IPV4
77 && addr.type != GRUB_NET_NETWORK_LEVEL_PROTOCOL_IPV6)
79 grub_error (GRUB_ERR_BUG, "not an IP address");
80 return NULL;
83 err = grub_net_route_address (addr, &gateway, &inf);
84 if (err)
85 return NULL;
87 err = grub_net_link_layer_resolve (inf, &gateway, &ll_target_addr);
88 if (err)
89 return NULL;
91 socket = grub_zalloc (sizeof (*socket));
92 if (socket == NULL)
93 return NULL;
95 socket->out_port = out_port;
96 socket->inf = inf;
97 socket->out_nla = addr;
98 socket->ll_target_addr = ll_target_addr;
99 socket->in_port = in_port++;
100 socket->status = GRUB_NET_SOCKET_START;
101 socket->recv_hook = recv_hook;
102 socket->recv_hook_data = recv_hook_data;
104 udp_socket_register (socket);
106 return socket;
109 grub_err_t
110 grub_net_send_udp_packet (const grub_net_udp_socket_t socket,
111 struct grub_net_buff *nb)
113 struct udphdr *udph;
114 grub_err_t err;
116 COMPILE_TIME_ASSERT (GRUB_NET_UDP_HEADER_SIZE == sizeof (*udph));
118 err = grub_netbuff_push (nb, sizeof (*udph));
119 if (err)
120 return err;
122 udph = (struct udphdr *) nb->data;
123 udph->src = grub_cpu_to_be16 (socket->in_port);
124 udph->dst = grub_cpu_to_be16 (socket->out_port);
126 udph->chksum = 0;
127 udph->len = grub_cpu_to_be16 (nb->tail - nb->data);
129 udph->chksum = grub_net_ip_transport_checksum (nb, GRUB_NET_IP_UDP,
130 &socket->inf->address,
131 &socket->out_nla);
133 return grub_net_send_ip_packet (socket->inf, &(socket->out_nla),
134 &(socket->ll_target_addr), nb,
135 GRUB_NET_IP_UDP);
138 grub_err_t
139 grub_net_recv_udp_packet (struct grub_net_buff *nb,
140 struct grub_net_network_level_interface *inf,
141 const grub_net_network_level_address_t *source)
143 struct udphdr *udph;
144 grub_net_udp_socket_t sock;
145 grub_err_t err;
147 /* Ignore broadcast. */
148 if (!inf)
150 grub_netbuff_free (nb);
151 return GRUB_ERR_NONE;
154 udph = (struct udphdr *) nb->data;
155 if (nb->tail - nb->data < (grub_ssize_t) sizeof (*udph))
157 grub_dprintf ("net", "UDP packet too short: %" PRIuGRUB_SIZE "\n",
158 nb->tail - nb->data);
159 grub_netbuff_free (nb);
160 return GRUB_ERR_NONE;
163 FOR_UDP_SOCKETS (sock)
165 if (grub_be_to_cpu16 (udph->dst) == sock->in_port
166 && inf == sock->inf
167 && grub_net_addr_cmp (source, &sock->out_nla) == 0
168 && (sock->status == GRUB_NET_SOCKET_START
169 || grub_be_to_cpu16 (udph->src) == sock->out_port))
171 if (udph->chksum)
173 grub_uint16_t chk, expected;
174 chk = udph->chksum;
175 udph->chksum = 0;
176 expected = grub_net_ip_transport_checksum (nb, GRUB_NET_IP_UDP,
177 &sock->out_nla,
178 &sock->inf->address);
179 if (expected != chk)
181 grub_dprintf ("net", "Invalid UDP checksum. "
182 "Expected %x, got %x\n",
183 grub_be_to_cpu16 (expected),
184 grub_be_to_cpu16 (chk));
185 grub_netbuff_free (nb);
186 return GRUB_ERR_NONE;
188 udph->chksum = chk;
191 if (sock->status == GRUB_NET_SOCKET_START)
193 sock->out_port = grub_be_to_cpu16 (udph->src);
194 sock->status = GRUB_NET_SOCKET_ESTABLISHED;
197 err = grub_netbuff_pull (nb, sizeof (*udph));
198 if (err)
199 return err;
201 /* App protocol remove its own reader. */
202 if (sock->recv_hook)
203 sock->recv_hook (sock, nb, sock->recv_hook_data);
204 else
205 grub_netbuff_free (nb);
206 return GRUB_ERR_NONE;
209 grub_netbuff_free (nb);
210 return GRUB_ERR_NONE;