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