]>
git.ipfire.org Git - thirdparty/systemd.git/blob - src/libsystemd-network/arp-util.c
1 /* SPDX-License-Identifier: LGPL-2.1+ */
3 Copyright © 2014 Axis Communications AB. All rights reserved.
7 #include <linux/filter.h>
8 #include <netinet/if_ether.h>
12 #include "unaligned.h"
15 int arp_network_bind_raw_socket(int ifindex
, be32_t address
, const struct ether_addr
*eth_mac
) {
16 struct sock_filter filter
[] = {
17 BPF_STMT(BPF_LD
+ BPF_W
+ BPF_LEN
, 0), /* A <- packet length */
18 BPF_JUMP(BPF_JMP
+ BPF_JGE
+ BPF_K
, sizeof(struct ether_arp
), 1, 0), /* packet >= arp packet ? */
19 BPF_STMT(BPF_RET
+ BPF_K
, 0), /* ignore */
20 BPF_STMT(BPF_LD
+ BPF_H
+ BPF_ABS
, offsetof(struct ether_arp
, ea_hdr
.ar_hrd
)), /* A <- header */
21 BPF_JUMP(BPF_JMP
+ BPF_JEQ
+ BPF_K
, ARPHRD_ETHER
, 1, 0), /* header == ethernet ? */
22 BPF_STMT(BPF_RET
+ BPF_K
, 0), /* ignore */
23 BPF_STMT(BPF_LD
+ BPF_H
+ BPF_ABS
, offsetof(struct ether_arp
, ea_hdr
.ar_pro
)), /* A <- protocol */
24 BPF_JUMP(BPF_JMP
+ BPF_JEQ
+ BPF_K
, ETHERTYPE_IP
, 1, 0), /* protocol == IP ? */
25 BPF_STMT(BPF_RET
+ BPF_K
, 0), /* ignore */
26 BPF_STMT(BPF_LD
+ BPF_B
+ BPF_ABS
, offsetof(struct ether_arp
, ea_hdr
.ar_hln
)), /* A <- hardware address length */
27 BPF_JUMP(BPF_JMP
+ BPF_JEQ
+ BPF_K
, sizeof(struct ether_addr
), 1, 0), /* length == sizeof(ether_addr)? */
28 BPF_STMT(BPF_RET
+ BPF_K
, 0), /* ignore */
29 BPF_STMT(BPF_LD
+ BPF_B
+ BPF_ABS
, offsetof(struct ether_arp
, ea_hdr
.ar_pln
)), /* A <- protocol address length */
30 BPF_JUMP(BPF_JMP
+ BPF_JEQ
+ BPF_K
, sizeof(struct in_addr
), 1, 0), /* length == sizeof(in_addr) ? */
31 BPF_STMT(BPF_RET
+ BPF_K
, 0), /* ignore */
32 BPF_STMT(BPF_LD
+ BPF_H
+ BPF_ABS
, offsetof(struct ether_arp
, ea_hdr
.ar_op
)), /* A <- operation */
33 BPF_JUMP(BPF_JMP
+ BPF_JEQ
+ BPF_K
, ARPOP_REQUEST
, 2, 0), /* protocol == request ? */
34 BPF_JUMP(BPF_JMP
+ BPF_JEQ
+ BPF_K
, ARPOP_REPLY
, 1, 0), /* protocol == reply ? */
35 BPF_STMT(BPF_RET
+ BPF_K
, 0), /* ignore */
36 /* Sender Hardware Address must be different from our own */
37 BPF_STMT(BPF_LD
+ BPF_IMM
, unaligned_read_be32(ð_mac
->ether_addr_octet
[0])),/* A <- 4 bytes of client's MAC */
38 BPF_STMT(BPF_MISC
+ BPF_TAX
, 0), /* X <- A */
39 BPF_STMT(BPF_LD
+ BPF_W
+ BPF_ABS
, offsetof(struct ether_arp
, arp_sha
)), /* A <- 4 bytes of SHA */
40 BPF_STMT(BPF_ALU
+ BPF_XOR
+ BPF_X
, 0), /* A xor X */
41 BPF_JUMP(BPF_JMP
+ BPF_JEQ
+ BPF_K
, 0, 0, 6), /* A == 0 ? */
42 BPF_STMT(BPF_LD
+ BPF_IMM
, unaligned_read_be16(ð_mac
->ether_addr_octet
[4])),/* A <- remainder of client's MAC */
43 BPF_STMT(BPF_MISC
+ BPF_TAX
, 0), /* X <- A */
44 BPF_STMT(BPF_LD
+ BPF_H
+ BPF_ABS
, offsetof(struct ether_arp
, arp_sha
) + 4), /* A <- remainder of SHA */
45 BPF_STMT(BPF_ALU
+ BPF_XOR
+ BPF_X
, 0), /* A xor X */
46 BPF_JUMP(BPF_JMP
+ BPF_JEQ
+ BPF_K
, 0, 0, 1), /* A == 0 ? */
47 BPF_STMT(BPF_RET
+ BPF_K
, 0), /* ignore */
48 /* Sender Protocol Address or Target Protocol Address must be equal to the one we care about */
49 BPF_STMT(BPF_LD
+ BPF_IMM
, htobe32(address
)), /* A <- clients IP */
50 BPF_STMT(BPF_MISC
+ BPF_TAX
, 0), /* X <- A */
51 BPF_STMT(BPF_LD
+ BPF_W
+ BPF_ABS
, offsetof(struct ether_arp
, arp_spa
)), /* A <- SPA */
52 BPF_STMT(BPF_ALU
+ BPF_XOR
+ BPF_X
, 0), /* X xor A */
53 BPF_JUMP(BPF_JMP
+ BPF_JEQ
+ BPF_K
, 0, 0, 1), /* A == 0 ? */
54 BPF_STMT(BPF_RET
+ BPF_K
, 65535), /* return all */
55 BPF_STMT(BPF_LD
+ BPF_IMM
, htobe32(address
)), /* A <- clients IP */
56 BPF_STMT(BPF_MISC
+ BPF_TAX
, 0), /* X <- A */
57 BPF_STMT(BPF_LD
+ BPF_W
+ BPF_ABS
, offsetof(struct ether_arp
, arp_tpa
)), /* A <- TPA */
58 BPF_STMT(BPF_ALU
+ BPF_XOR
+ BPF_X
, 0), /* X xor A */
59 BPF_JUMP(BPF_JMP
+ BPF_JEQ
+ BPF_K
, 0, 0, 1), /* A == 0 ? */
60 BPF_STMT(BPF_RET
+ BPF_K
, 65535), /* return all */
61 BPF_STMT(BPF_RET
+ BPF_K
, 0), /* ignore */
63 struct sock_fprog fprog
= {
64 .len
= ELEMENTSOF(filter
),
65 .filter
= (struct sock_filter
*) filter
67 union sockaddr_union link
= {
68 .ll
.sll_family
= AF_PACKET
,
69 .ll
.sll_protocol
= htobe16(ETH_P_ARP
),
70 .ll
.sll_ifindex
= ifindex
,
71 .ll
.sll_halen
= ETH_ALEN
,
72 .ll
.sll_addr
= { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff },
74 _cleanup_close_
int s
= -1;
79 s
= socket(AF_PACKET
, SOCK_DGRAM
| SOCK_CLOEXEC
| SOCK_NONBLOCK
, 0);
83 r
= setsockopt(s
, SOL_SOCKET
, SO_ATTACH_FILTER
, &fprog
, sizeof(fprog
));
87 r
= bind(s
, &link
.sa
, sizeof(link
.ll
));
94 static int arp_send_packet(int fd
, int ifindex
,
95 be32_t pa
, const struct ether_addr
*ha
,
97 union sockaddr_union link
= {
98 .ll
.sll_family
= AF_PACKET
,
99 .ll
.sll_protocol
= htobe16(ETH_P_ARP
),
100 .ll
.sll_ifindex
= ifindex
,
101 .ll
.sll_halen
= ETH_ALEN
,
102 .ll
.sll_addr
= { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff },
104 struct ether_arp arp
= {
105 .ea_hdr
.ar_hrd
= htobe16(ARPHRD_ETHER
), /* HTYPE */
106 .ea_hdr
.ar_pro
= htobe16(ETHERTYPE_IP
), /* PTYPE */
107 .ea_hdr
.ar_hln
= ETH_ALEN
, /* HLEN */
108 .ea_hdr
.ar_pln
= sizeof(be32_t
), /* PLEN */
109 .ea_hdr
.ar_op
= htobe16(ARPOP_REQUEST
), /* REQUEST */
117 memcpy(&arp
.arp_sha
, ha
, ETH_ALEN
);
118 memcpy(&arp
.arp_tpa
, &pa
, sizeof(pa
));
121 memcpy(&arp
.arp_spa
, &pa
, sizeof(pa
));
123 r
= sendto(fd
, &arp
, sizeof(struct ether_arp
), 0, &link
.sa
, sizeof(link
.ll
));
130 int arp_send_probe(int fd
, int ifindex
,
131 be32_t pa
, const struct ether_addr
*ha
) {
132 return arp_send_packet(fd
, ifindex
, pa
, ha
, false);
135 int arp_send_announcement(int fd
, int ifindex
,
136 be32_t pa
, const struct ether_addr
*ha
) {
137 return arp_send_packet(fd
, ifindex
, pa
, ha
, true);