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/>.
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
;
35 grub_err_t (*recv_hook
) (grub_net_udp_socket_t sock
, struct grub_net_buff
*nb
,
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)
48 udp_socket_register (grub_net_udp_socket_t sock
)
50 grub_list_push (GRUB_AS_LIST_P (&udp_sockets
),
55 grub_net_udp_close (grub_net_udp_socket_t sock
)
57 grub_list_remove (GRUB_AS_LIST (sock
));
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
,
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");
83 err
= grub_net_route_address (addr
, &gateway
, &inf
);
87 err
= grub_net_link_layer_resolve (inf
, &gateway
, &ll_target_addr
);
91 socket
= grub_zalloc (sizeof (*socket
));
95 socket
->out_port
= out_port
;
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
);
110 grub_net_send_udp_packet (const grub_net_udp_socket_t socket
,
111 struct grub_net_buff
*nb
)
116 COMPILE_TIME_ASSERT (GRUB_NET_UDP_HEADER_SIZE
== sizeof (*udph
));
118 err
= grub_netbuff_push (nb
, sizeof (*udph
));
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
);
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
,
133 return grub_net_send_ip_packet (socket
->inf
, &(socket
->out_nla
),
134 &(socket
->ll_target_addr
), nb
,
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
)
144 grub_net_udp_socket_t sock
;
147 /* Ignore broadcast. */
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 (grub_size_t
) (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
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
))
173 grub_uint16_t chk
, expected
;
176 expected
= grub_net_ip_transport_checksum (nb
, GRUB_NET_IP_UDP
,
178 &sock
->inf
->address
);
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
;
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
));
201 /* App protocol remove its own reader. */
203 sock
->recv_hook (sock
, nb
, sock
->recv_hook_data
);
205 grub_netbuff_free (nb
);
206 return GRUB_ERR_NONE
;
209 grub_netbuff_free (nb
);
210 return GRUB_ERR_NONE
;