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/arp.h>
20 #include <grub/net/netbuff.h>
23 #include <grub/net/ethernet.h>
24 #include <grub/net/ip.h>
25 #include <grub/time.h>
27 /* ARP header operation codes */
36 /* IANA ARP constant to define hardware type as ethernet. */
37 GRUB_NET_ARPHRD_ETHERNET
= 1
48 static int have_pending
;
49 static grub_uint32_t pending_req
;
52 grub_net_arp_send_request (struct grub_net_network_level_interface
*inf
,
53 const grub_net_network_level_address_t
*proto_addr
)
55 struct grub_net_buff nb
;
56 struct arphdr
*arp_header
;
57 grub_net_link_level_address_t target_hw_addr
;
58 grub_uint8_t
*aux
, arp_data
[128];
62 grub_uint16_t etherpro
;
65 if (proto_addr
->type
== GRUB_NET_NETWORK_LEVEL_PROTOCOL_IPV4
)
68 etherpro
= GRUB_NET_ETHERTYPE_IP
;
71 return grub_error (GRUB_ERR_BUG
, "unsupported address family");
73 /* Build a request packet. */
75 nb
.end
= arp_data
+ sizeof (arp_data
);
76 grub_netbuff_clear (&nb
);
77 grub_netbuff_reserve (&nb
, 128);
79 err
= grub_netbuff_push (&nb
, sizeof (*arp_header
) + 2 * (6 + addrlen
));
83 arp_header
= (struct arphdr
*) nb
.data
;
84 arp_header
->hrd
= grub_cpu_to_be16_compile_time (GRUB_NET_ARPHRD_ETHERNET
);
86 arp_header
->pro
= grub_cpu_to_be16 (etherpro
);
87 arp_header
->pln
= addrlen
;
88 arp_header
->op
= grub_cpu_to_be16_compile_time (ARP_REQUEST
);
89 aux
= (grub_uint8_t
*) arp_header
+ sizeof (*arp_header
);
90 /* Sender hardware address. */
91 grub_memcpy (aux
, &inf
->hwaddress
.mac
, 6);
94 /* Sender protocol address */
95 grub_memcpy (aux
, &inf
->address
.ipv4
, 4);
97 /* Target hardware address */
98 for (i
= 0; i
< 6; i
++)
101 /* Target protocol address */
102 grub_memcpy (aux
, &proto_addr
->ipv4
, 4);
103 grub_memset (&target_hw_addr
.mac
, 0xff, 6);
106 send_ethernet_packet (inf
, &nb
, target_hw_addr
, GRUB_NET_ETHERTYPE_ARP
);
107 for (i
= 0; i
< GRUB_NET_TRIES
; i
++)
109 if (grub_net_link_layer_resolve_check (inf
, proto_addr
))
110 return GRUB_ERR_NONE
;
111 pending_req
= proto_addr
->ipv4
;
113 grub_net_poll_cards (GRUB_NET_INTERVAL
+ (i
* GRUB_NET_INTERVAL_ADDITION
),
115 if (grub_net_link_layer_resolve_check (inf
, proto_addr
))
116 return GRUB_ERR_NONE
;
118 send_ethernet_packet (inf
, &nb
, target_hw_addr
, GRUB_NET_ETHERTYPE_ARP
);
121 return GRUB_ERR_NONE
;
125 grub_net_arp_receive (struct grub_net_buff
*nb
,
126 struct grub_net_card
*card
)
128 struct arphdr
*arp_header
= (struct arphdr
*) nb
->data
;
129 grub_uint8_t
*sender_hardware_address
;
130 grub_uint8_t
*target_hardware_address
;
131 grub_net_network_level_address_t sender_addr
, target_addr
;
132 grub_net_link_level_address_t sender_hw_addr
;
133 struct grub_net_network_level_interface
*inf
;
134 grub_uint8_t
*sender_protocol_address
, *target_protocol_address
;
136 sender_hardware_address
=
137 (grub_uint8_t
*) arp_header
+ sizeof (*arp_header
);
138 sender_protocol_address
= sender_hardware_address
+ arp_header
->hln
;
139 target_hardware_address
= sender_protocol_address
+ arp_header
->pln
;
140 target_protocol_address
= target_hardware_address
+ arp_header
->hln
;
141 if (grub_be_to_cpu16 (arp_header
->pro
) == GRUB_NET_ETHERTYPE_IP
142 && arp_header
->pln
== 4)
144 sender_addr
.type
= GRUB_NET_NETWORK_LEVEL_PROTOCOL_IPV4
;
145 target_addr
.type
= GRUB_NET_NETWORK_LEVEL_PROTOCOL_IPV4
;
146 grub_memcpy (&sender_addr
.ipv4
, sender_protocol_address
, 4);
147 grub_memcpy (&target_addr
.ipv4
, target_protocol_address
, 4);
148 if (grub_memcmp (sender_protocol_address
, &pending_req
, 4) == 0)
152 return GRUB_ERR_NONE
;
154 sender_hw_addr
.type
= GRUB_NET_LINK_LEVEL_PROTOCOL_ETHERNET
;
155 grub_memcpy (sender_hw_addr
.mac
, sender_hardware_address
,
156 sizeof (sender_hw_addr
.mac
));
157 grub_net_link_layer_add_address (card
, &sender_addr
, &sender_hw_addr
, 1);
159 FOR_NET_NETWORK_LEVEL_INTERFACES (inf
)
161 /* Am I the protocol address target? */
162 if (grub_net_addr_cmp (&inf
->address
, &target_addr
) == 0
163 && grub_be_to_cpu16 (arp_header
->op
) == ARP_REQUEST
)
165 grub_net_link_level_address_t target
;
166 /* We've already checked that pln is either 4 or 16. */
168 grub_size_t pln
= arp_header
->pln
;
173 target
.type
= GRUB_NET_LINK_LEVEL_PROTOCOL_ETHERNET
;
174 grub_memcpy (target
.mac
, sender_hardware_address
, 6);
175 grub_memcpy (target_hardware_address
, target
.mac
, 6);
176 grub_memcpy (sender_hardware_address
, inf
->hwaddress
.mac
, 6);
178 grub_memcpy (tmp
, sender_protocol_address
, pln
);
179 grub_memcpy (sender_protocol_address
, target_protocol_address
, pln
);
180 grub_memcpy (target_protocol_address
, tmp
, pln
);
182 /* Change operation to REPLY and send packet */
183 arp_header
->op
= grub_be_to_cpu16 (ARP_REPLY
);
184 send_ethernet_packet (inf
, nb
, target
, GRUB_NET_ETHERTYPE_ARP
);
187 return GRUB_ERR_NONE
;