]>
Commit | Line | Data |
---|---|---|
db9ecf05 | 1 | /* SPDX-License-Identifier: LGPL-2.1-or-later */ |
996d1697 | 2 | /*** |
810adae9 | 3 | Copyright © 2014 Axis Communications AB. All rights reserved. |
996d1697 TG |
4 | ***/ |
5 | ||
996d1697 | 6 | #include <arpa/inet.h> |
8f815e8b YW |
7 | #include <linux/filter.h> |
8 | #include <netinet/if_ether.h> | |
996d1697 | 9 | |
996d1697 | 10 | #include "arp-util.h" |
504bf2b5 | 11 | #include "ether-addr-util.h" |
3ffd4af2 | 12 | #include "fd-util.h" |
ecad63f8 | 13 | #include "in-addr-util.h" |
f11cba74 | 14 | #include "unaligned.h" |
996d1697 | 15 | |
e2c7c38b | 16 | int arp_update_filter(int fd, const struct in_addr *a, const struct ether_addr *mac) { |
996d1697 TG |
17 | struct sock_filter filter[] = { |
18 | BPF_STMT(BPF_LD + BPF_W + BPF_LEN, 0), /* A <- packet length */ | |
19 | BPF_JUMP(BPF_JMP + BPF_JGE + BPF_K, sizeof(struct ether_arp), 1, 0), /* packet >= arp packet ? */ | |
20 | BPF_STMT(BPF_RET + BPF_K, 0), /* ignore */ | |
21 | BPF_STMT(BPF_LD + BPF_H + BPF_ABS, offsetof(struct ether_arp, ea_hdr.ar_hrd)), /* A <- header */ | |
22 | BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, ARPHRD_ETHER, 1, 0), /* header == ethernet ? */ | |
23 | BPF_STMT(BPF_RET + BPF_K, 0), /* ignore */ | |
24 | BPF_STMT(BPF_LD + BPF_H + BPF_ABS, offsetof(struct ether_arp, ea_hdr.ar_pro)), /* A <- protocol */ | |
25 | BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, ETHERTYPE_IP, 1, 0), /* protocol == IP ? */ | |
26 | BPF_STMT(BPF_RET + BPF_K, 0), /* ignore */ | |
27 | BPF_STMT(BPF_LD + BPF_B + BPF_ABS, offsetof(struct ether_arp, ea_hdr.ar_hln)), /* A <- hardware address length */ | |
28 | BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, sizeof(struct ether_addr), 1, 0), /* length == sizeof(ether_addr)? */ | |
29 | BPF_STMT(BPF_RET + BPF_K, 0), /* ignore */ | |
30 | BPF_STMT(BPF_LD + BPF_B + BPF_ABS, offsetof(struct ether_arp, ea_hdr.ar_pln)), /* A <- protocol address length */ | |
31 | BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, sizeof(struct in_addr), 1, 0), /* length == sizeof(in_addr) ? */ | |
32 | BPF_STMT(BPF_RET + BPF_K, 0), /* ignore */ | |
33 | BPF_STMT(BPF_LD + BPF_H + BPF_ABS, offsetof(struct ether_arp, ea_hdr.ar_op)), /* A <- operation */ | |
34 | BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, ARPOP_REQUEST, 2, 0), /* protocol == request ? */ | |
35 | BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, ARPOP_REPLY, 1, 0), /* protocol == reply ? */ | |
36 | BPF_STMT(BPF_RET + BPF_K, 0), /* ignore */ | |
37 | /* Sender Hardware Address must be different from our own */ | |
e2c7c38b | 38 | BPF_STMT(BPF_LDX + BPF_IMM, unaligned_read_be32(&mac->ether_addr_octet[0])), /* X <- 4 bytes of client's MAC */ |
996d1697 | 39 | BPF_STMT(BPF_LD + BPF_W + BPF_ABS, offsetof(struct ether_arp, arp_sha)), /* A <- 4 bytes of SHA */ |
e2c7c38b YW |
40 | BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_X, 0, 0, 4), /* A == X ? */ |
41 | BPF_STMT(BPF_LDX + BPF_IMM, unaligned_read_be16(&mac->ether_addr_octet[4])), /* X <- remainder of client's MAC */ | |
996d1697 | 42 | BPF_STMT(BPF_LD + BPF_H + BPF_ABS, offsetof(struct ether_arp, arp_sha) + 4), /* A <- remainder of SHA */ |
e2c7c38b | 43 | BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_X, 0, 0, 1), /* A == X ? */ |
996d1697 | 44 | BPF_STMT(BPF_RET + BPF_K, 0), /* ignore */ |
13e785f7 | 45 | /* Sender Protocol Address or Target Protocol Address must be equal to the one we care about */ |
e2c7c38b | 46 | BPF_STMT(BPF_LDX + BPF_IMM, htobe32(a->s_addr)), /* X <- clients IP */ |
996d1697 | 47 | BPF_STMT(BPF_LD + BPF_W + BPF_ABS, offsetof(struct ether_arp, arp_spa)), /* A <- SPA */ |
e2c7c38b YW |
48 | BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_X, 0, 0, 1), /* A == X ? */ |
49 | BPF_STMT(BPF_RET + BPF_K, UINT32_MAX), /* accept */ | |
996d1697 | 50 | BPF_STMT(BPF_LD + BPF_W + BPF_ABS, offsetof(struct ether_arp, arp_tpa)), /* A <- TPA */ |
e2c7c38b YW |
51 | BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_X, 0, 0, 1), /* A == 0 ? */ |
52 | BPF_STMT(BPF_RET + BPF_K, UINT32_MAX), /* accept */ | |
996d1697 TG |
53 | BPF_STMT(BPF_RET + BPF_K, 0), /* ignore */ |
54 | }; | |
55 | struct sock_fprog fprog = { | |
51d11351 YW |
56 | .len = ELEMENTSOF(filter), |
57 | .filter = (struct sock_filter*) filter, | |
996d1697 | 58 | }; |
d17ed573 YW |
59 | |
60 | assert(fd >= 0); | |
61 | ||
62 | if (setsockopt(fd, SOL_SOCKET, SO_ATTACH_FILTER, &fprog, sizeof(fprog)) < 0) | |
63 | return -errno; | |
64 | ||
65 | return 0; | |
66 | } | |
67 | ||
e2c7c38b | 68 | int arp_network_bind_raw_socket(int ifindex, const struct in_addr *a, const struct ether_addr *mac) { |
996d1697 | 69 | union sockaddr_union link = { |
51d11351 | 70 | .ll.sll_family = AF_PACKET, |
8e38570e | 71 | .ll.sll_protocol = htobe16(ETH_P_ARP), |
51d11351 YW |
72 | .ll.sll_ifindex = ifindex, |
73 | .ll.sll_halen = ETH_ALEN, | |
74 | .ll.sll_addr = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }, | |
996d1697 | 75 | }; |
5bb1d7fb | 76 | _cleanup_close_ int s = -EBADF; |
d17ed573 | 77 | int r; |
996d1697 TG |
78 | |
79 | assert(ifindex > 0); | |
e2c7c38b | 80 | assert(mac); |
996d1697 | 81 | |
40eb1b0a | 82 | s = socket(AF_PACKET, SOCK_DGRAM | SOCK_CLOEXEC | SOCK_NONBLOCK, 0); |
996d1697 TG |
83 | if (s < 0) |
84 | return -errno; | |
85 | ||
e2c7c38b | 86 | r = arp_update_filter(s, a, mac); |
d17ed573 YW |
87 | if (r < 0) |
88 | return r; | |
996d1697 | 89 | |
28ba416c | 90 | if (bind(s, &link.sa, sizeof(link.ll)) < 0) |
996d1697 TG |
91 | return -errno; |
92 | ||
c10d6bdb | 93 | return TAKE_FD(s); |
996d1697 TG |
94 | } |
95 | ||
e1a3915b | 96 | int arp_send_packet( |
51d11351 YW |
97 | int fd, |
98 | int ifindex, | |
ecad63f8 | 99 | const struct in_addr *pa, |
51d11351 YW |
100 | const struct ether_addr *ha, |
101 | bool announce) { | |
102 | ||
996d1697 | 103 | union sockaddr_union link = { |
51d11351 | 104 | .ll.sll_family = AF_PACKET, |
8e38570e | 105 | .ll.sll_protocol = htobe16(ETH_P_ARP), |
51d11351 YW |
106 | .ll.sll_ifindex = ifindex, |
107 | .ll.sll_halen = ETH_ALEN, | |
108 | .ll.sll_addr = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }, | |
996d1697 TG |
109 | }; |
110 | struct ether_arp arp = { | |
51d11351 YW |
111 | .ea_hdr.ar_hrd = htobe16(ARPHRD_ETHER), /* HTYPE */ |
112 | .ea_hdr.ar_pro = htobe16(ETHERTYPE_IP), /* PTYPE */ | |
113 | .ea_hdr.ar_hln = ETH_ALEN, /* HLEN */ | |
ecad63f8 | 114 | .ea_hdr.ar_pln = sizeof(struct in_addr), /* PLEN */ |
51d11351 | 115 | .ea_hdr.ar_op = htobe16(ARPOP_REQUEST), /* REQUEST */ |
996d1697 | 116 | }; |
f6720fe6 | 117 | ssize_t n; |
996d1697 TG |
118 | |
119 | assert(fd >= 0); | |
504bf2b5 | 120 | assert(ifindex > 0); |
ecad63f8 YW |
121 | assert(pa); |
122 | assert(in4_addr_is_set(pa)); | |
996d1697 | 123 | assert(ha); |
504bf2b5 | 124 | assert(!ether_addr_is_null(ha)); |
996d1697 TG |
125 | |
126 | memcpy(&arp.arp_sha, ha, ETH_ALEN); | |
ecad63f8 | 127 | memcpy(&arp.arp_tpa, pa, sizeof(struct in_addr)); |
996d1697 TG |
128 | |
129 | if (announce) | |
ecad63f8 | 130 | memcpy(&arp.arp_spa, pa, sizeof(struct in_addr)); |
996d1697 | 131 | |
f6720fe6 YW |
132 | n = sendto(fd, &arp, sizeof(struct ether_arp), 0, &link.sa, sizeof(link.ll)); |
133 | if (n < 0) | |
996d1697 | 134 | return -errno; |
f6720fe6 YW |
135 | if (n != sizeof(struct ether_arp)) |
136 | return -EIO; | |
996d1697 TG |
137 | |
138 | return 0; | |
139 | } |