1 /* vi: set sw=4 ts=4: */
3 * Mostly stolen from: dhcpcd - DHCP client daemon
4 * by Yoichi Hariguchi <yoichi@fore.com>
6 * Licensed under GPLv2, see file LICENSE in this source tree.
8 #include <netinet/if_ether.h>
9 #include <net/if_arp.h>
16 uint8_t h_dest
[6]; /* 00 destination ether addr */
17 uint8_t h_source
[6]; /* 06 source ether addr */
18 uint16_t h_proto
; /* 0c packet type ID field */
21 uint16_t htype
; /* 0e hardware type (must be ARPHRD_ETHER) */
22 uint16_t ptype
; /* 10 protocol type (must be ETH_P_IP) */
23 uint8_t hlen
; /* 12 hardware address length (must be 6) */
24 uint8_t plen
; /* 13 protocol address length (must be 4) */
25 uint16_t operation
; /* 14 ARP opcode */
26 uint8_t sHaddr
[6]; /* 16 sender's hardware address */
27 uint8_t sInaddr
[4]; /* 1c sender's IP address */
28 uint8_t tHaddr
[6]; /* 20 target's hardware address */
29 uint8_t tInaddr
[4]; /* 26 target's IP address */
30 uint8_t pad
[18]; /* 2a pad for min. ethernet payload (60 bytes) */
37 /* Returns 1 if no reply received */
38 int FAST_FUNC
arpping(uint32_t test_nip
,
39 const uint8_t *safe_mac
,
42 const char *interface
)
46 #define s (pfd[0].fd) /* socket */
47 int rv
= 1; /* "no reply received" yet */
48 struct sockaddr addr
; /* for interface name */
51 s
= socket(PF_PACKET
, SOCK_PACKET
, htons(ETH_P_ARP
));
53 bb_perror_msg(bb_msg_can_not_create_raw_socket
);
57 if (setsockopt_broadcast(s
) == -1) {
58 bb_perror_msg("can't enable bcast on raw socket");
62 /* send arp request */
63 memset(&arp
, 0, sizeof(arp
));
64 memset(arp
.h_dest
, 0xff, 6); /* MAC DA */
65 memcpy(arp
.h_source
, from_mac
, 6); /* MAC SA */
66 arp
.h_proto
= htons(ETH_P_ARP
); /* protocol type (Ethernet) */
67 arp
.htype
= htons(ARPHRD_ETHER
); /* hardware type */
68 arp
.ptype
= htons(ETH_P_IP
); /* protocol type (ARP message) */
69 arp
.hlen
= 6; /* hardware address length */
70 arp
.plen
= 4; /* protocol address length */
71 arp
.operation
= htons(ARPOP_REQUEST
); /* ARP op code */
72 memcpy(arp
.sHaddr
, from_mac
, 6); /* source hardware address */
73 memcpy(arp
.sInaddr
, &from_ip
, sizeof(from_ip
)); /* source IP address */
74 /* tHaddr is zero-filled */ /* target hardware address */
75 memcpy(arp
.tInaddr
, &test_nip
, sizeof(test_nip
));/* target IP address */
77 memset(&addr
, 0, sizeof(addr
));
78 safe_strncpy(addr
.sa_data
, interface
, sizeof(addr
.sa_data
));
79 if (sendto(s
, &arp
, sizeof(arp
), 0, &addr
, sizeof(addr
)) < 0) {
80 // TODO: error message? caller didn't expect us to fail,
81 // just returning 1 "no reply received" misleads it.
85 /* wait for arp reply, and check it */
88 typedef uint32_t aliased_uint32_t FIX_ALIASING
;
90 unsigned prevTime
= monotonic_ms();
92 pfd
[0].events
= POLLIN
;
93 r
= safe_poll(pfd
, 1, timeout_ms
);
97 r
= safe_read(s
, &arp
, sizeof(arp
));
101 //log3("sHaddr %02x:%02x:%02x:%02x:%02x:%02x",
102 // arp.sHaddr[0], arp.sHaddr[1], arp.sHaddr[2],
103 // arp.sHaddr[3], arp.sHaddr[4], arp.sHaddr[5]);
105 if (r
>= ARP_MSG_SIZE
106 && arp
.operation
== htons(ARPOP_REPLY
)
107 /* don't check it: Linux doesn't return proper tHaddr (fixed in 2.6.24?) */
108 /* && memcmp(arp.tHaddr, from_mac, 6) == 0 */
109 && *(aliased_uint32_t
*)arp
.sInaddr
== test_nip
111 /* if ARP source MAC matches safe_mac
112 * (which is client's MAC), then it's not a conflict
113 * (client simply already has this IP and replies to ARPs!)
115 if (!safe_mac
|| memcmp(safe_mac
, arp
.sHaddr
, 6) != 0)
117 //else log2("sHaddr == safe_mac");
121 timeout_ms
-= (unsigned)monotonic_ms() - prevTime
+ 1;
123 /* We used to check "timeout_ms > 0", but
124 * this is more under/overflow-resistant
125 * (people did see overflows here when system time jumps):
127 } while ((unsigned)timeout_ms
<= 2000);
131 log1("%srp reply received for this address", rv
? "No a" : "A");