]>
Commit | Line | Data |
---|---|---|
acddc0ed | 1 | // SPDX-License-Identifier: GPL-2.0-or-later |
41ee5442 | 2 | /* |
63d4bd12 | 3 | * VRRP ARP handling. |
41ee5442 QY |
4 | * Copyright (C) 2001-2017 Alexandre Cassen |
5 | * Portions: | |
63d4bd12 QY |
6 | * Copyright (C) 2018-2019 Cumulus Networks, Inc. |
7 | * Quentin Young | |
41ee5442 | 8 | */ |
41ee5442 QY |
9 | #include <zebra.h> |
10 | ||
41ee5442 | 11 | #include <linux/if_packet.h> |
63d4bd12 | 12 | #include <net/if_arp.h> |
41ee5442 QY |
13 | #include <netinet/if_ether.h> |
14 | ||
15 | #include "lib/if.h" | |
16 | #include "lib/linklist.h" | |
17 | #include "lib/log.h" | |
18 | #include "lib/memory.h" | |
19 | #include "lib/prefix.h" | |
20 | ||
21 | #include "vrrp.h" | |
22 | #include "vrrp_arp.h" | |
b637bcd4 | 23 | #include "vrrp_debug.h" |
41ee5442 | 24 | |
4ec94408 QY |
25 | #define VRRP_LOGPFX "[ARP] " |
26 | ||
41ee5442 QY |
27 | /* |
28 | * The size of the garp packet buffer should be the large enough to hold the | |
29 | * largest arp packet to be sent + the size of the link layer header for the | |
30 | * corresponding protocol. In this case we hardcode for Ethernet. | |
31 | */ | |
32 | #define GARP_BUFFER_SIZE \ | |
33 | sizeof(struct ether_header) + sizeof(struct arphdr) + 2 * ETH_ALEN \ | |
34 | + 2 * sizeof(struct in_addr) | |
35 | ||
36 | /* static vars */ | |
37 | static int garp_fd = -1; | |
38 | ||
39 | /* Send the gratuitous ARP message */ | |
354b49d6 QY |
40 | static ssize_t vrrp_send_garp(struct interface *ifp, uint8_t *buf, |
41 | ssize_t pack_len) | |
41ee5442 QY |
42 | { |
43 | struct sockaddr_ll sll; | |
44 | ssize_t len; | |
45 | ||
46 | /* Build the dst device */ | |
47 | memset(&sll, 0, sizeof(sll)); | |
48 | sll.sll_family = AF_PACKET; | |
49 | sll.sll_protocol = ETH_P_ARP; | |
354b49d6 | 50 | sll.sll_ifindex = (int)ifp->ifindex; |
41ee5442 QY |
51 | sll.sll_halen = ifp->hw_addr_len; |
52 | memset(sll.sll_addr, 0xFF, ETH_ALEN); | |
53 | ||
54 | /* Send packet */ | |
55 | len = sendto(garp_fd, buf, pack_len, 0, (struct sockaddr *)&sll, | |
56 | sizeof(sll)); | |
57 | ||
58 | return len; | |
59 | } | |
60 | ||
61 | /* Build a gratuitous ARP message over a specific interface */ | |
62 | static ssize_t vrrp_build_garp(uint8_t *buf, struct interface *ifp, | |
63 | struct in_addr *v4) | |
64 | { | |
65 | uint8_t *arp_ptr; | |
66 | ||
67 | if (ifp->hw_addr_len == 0) | |
68 | return -1; | |
69 | ||
70 | /* Build Ethernet header */ | |
354b49d6 | 71 | struct ether_header *eth = (struct ether_header *)buf; |
41ee5442 QY |
72 | |
73 | memset(eth->ether_dhost, 0xFF, ETH_ALEN); | |
74 | memcpy(eth->ether_shost, ifp->hw_addr, ETH_ALEN); | |
75 | eth->ether_type = htons(ETHERTYPE_ARP); | |
76 | ||
77 | /* Build ARP payload */ | |
354b49d6 | 78 | struct arphdr *arph = (struct arphdr *)(buf + ETHER_HDR_LEN); |
41ee5442 QY |
79 | |
80 | arph->ar_hrd = htons(HWTYPE_ETHER); | |
81 | arph->ar_pro = htons(ETHERTYPE_IP); | |
82 | arph->ar_hln = ifp->hw_addr_len; | |
83 | arph->ar_pln = sizeof(struct in_addr); | |
84 | arph->ar_op = htons(ARPOP_REQUEST); | |
c23edd74 | 85 | arp_ptr = (uint8_t *)(arph + 1); |
41ee5442 QY |
86 | /* Source MAC: us */ |
87 | memcpy(arp_ptr, ifp->hw_addr, ifp->hw_addr_len); | |
88 | arp_ptr += ifp->hw_addr_len; | |
89 | /* Source IP: us */ | |
c23edd74 | 90 | memcpy(arp_ptr, v4, sizeof(struct in_addr)); |
41ee5442 QY |
91 | arp_ptr += sizeof(struct in_addr); |
92 | /* Dest MAC: broadcast */ | |
93 | memset(arp_ptr, 0xFF, ETH_ALEN); | |
94 | arp_ptr += ifp->hw_addr_len; | |
95 | /* Dest IP: us */ | |
c23edd74 | 96 | memcpy(arp_ptr, v4, sizeof(struct in_addr)); |
41ee5442 QY |
97 | arp_ptr += sizeof(struct in_addr); |
98 | ||
99 | return arp_ptr - buf; | |
100 | } | |
101 | ||
862f2f37 | 102 | void vrrp_garp_send(struct vrrp_router *r, struct in_addr *v4) |
41ee5442 | 103 | { |
dad18a2f | 104 | struct interface *ifp = r->mvl_ifp; |
41ee5442 QY |
105 | uint8_t garpbuf[GARP_BUFFER_SIZE]; |
106 | ssize_t garpbuf_len; | |
107 | ssize_t sent_len; | |
108 | char astr[INET_ADDRSTRLEN]; | |
109 | ||
110 | /* If the interface doesn't support ARP, don't try sending */ | |
111 | if (ifp->flags & IFF_NOARP) { | |
112 | zlog_warn( | |
613b45b0 | 113 | VRRP_LOGPFX VRRP_LOGPFX_VRID VRRP_LOGPFX_FAM |
ab059def | 114 | "Unable to send gratuitous ARP on %s; has IFF_NOARP", |
613b45b0 | 115 | r->vr->vrid, family2str(r->family), ifp->name); |
41ee5442 QY |
116 | return; |
117 | } | |
118 | ||
119 | /* Build garp */ | |
120 | garpbuf_len = vrrp_build_garp(garpbuf, ifp, v4); | |
121 | ||
ab059def QY |
122 | if (garpbuf_len < 0) { |
123 | zlog_warn( | |
124 | VRRP_LOGPFX VRRP_LOGPFX_VRID VRRP_LOGPFX_FAM | |
125 | "Unable to send gratuitous ARP on %s; MAC address unknown", | |
126 | r->vr->vrid, family2str(r->family), ifp->name); | |
127 | return; | |
128 | }; | |
129 | ||
41ee5442 QY |
130 | /* Send garp */ |
131 | inet_ntop(AF_INET, v4, astr, sizeof(astr)); | |
b637bcd4 QY |
132 | |
133 | DEBUGD(&vrrp_dbg_arp, | |
613b45b0 | 134 | VRRP_LOGPFX VRRP_LOGPFX_VRID VRRP_LOGPFX_FAM |
b637bcd4 | 135 | "Sending gratuitous ARP on %s for %s", |
613b45b0 | 136 | r->vr->vrid, family2str(r->family), ifp->name, astr); |
b637bcd4 QY |
137 | if (DEBUG_MODE_CHECK(&vrrp_dbg_arp, DEBUG_MODE_ALL)) |
138 | zlog_hexdump(garpbuf, garpbuf_len); | |
139 | ||
41ee5442 QY |
140 | sent_len = vrrp_send_garp(ifp, garpbuf, garpbuf_len); |
141 | ||
142 | if (sent_len < 0) | |
613b45b0 | 143 | zlog_warn(VRRP_LOGPFX VRRP_LOGPFX_VRID VRRP_LOGPFX_FAM |
4ec94408 | 144 | "Error sending gratuitous ARP on %s for %s", |
613b45b0 | 145 | r->vr->vrid, family2str(r->family), ifp->name, astr); |
6332c77f QY |
146 | else |
147 | ++r->stats.garp_tx_cnt; | |
41ee5442 QY |
148 | } |
149 | ||
862f2f37 | 150 | void vrrp_garp_send_all(struct vrrp_router *r) |
41ee5442 | 151 | { |
862f2f37 QY |
152 | assert(r->family == AF_INET); |
153 | ||
dad18a2f | 154 | struct interface *ifp = r->mvl_ifp; |
41ee5442 QY |
155 | |
156 | /* If the interface doesn't support ARP, don't try sending */ | |
157 | if (ifp->flags & IFF_NOARP) { | |
158 | zlog_warn( | |
613b45b0 | 159 | VRRP_LOGPFX VRRP_LOGPFX_VRID VRRP_LOGPFX_FAM |
1d5453d6 | 160 | "Unable to send gratuitous ARP on %s; has IFF_NOARP", |
613b45b0 | 161 | r->vr->vrid, family2str(r->family), ifp->name); |
41ee5442 QY |
162 | return; |
163 | } | |
164 | ||
165 | struct listnode *ln; | |
862f2f37 | 166 | struct ipaddr *ip; |
41ee5442 | 167 | |
862f2f37 QY |
168 | for (ALL_LIST_ELEMENTS_RO(r->addrs, ln, ip)) |
169 | vrrp_garp_send(r, &ip->ipaddr_v4); | |
41ee5442 QY |
170 | } |
171 | ||
172 | ||
173 | void vrrp_garp_init(void) | |
174 | { | |
175 | /* Create the socket descriptor */ | |
176 | /* FIXME: why ETH_P_RARP? */ | |
40744000 | 177 | errno = 0; |
0cf6db21 | 178 | frr_with_privs(&vrrp_privs) { |
40744000 QY |
179 | garp_fd = socket(PF_PACKET, SOCK_RAW | SOCK_CLOEXEC, |
180 | htons(ETH_P_RARP)); | |
181 | } | |
41ee5442 | 182 | |
b637bcd4 QY |
183 | if (garp_fd > 0) { |
184 | DEBUGD(&vrrp_dbg_sock, | |
185 | VRRP_LOGPFX "Initialized gratuitous ARP socket"); | |
186 | DEBUGD(&vrrp_dbg_arp, | |
187 | VRRP_LOGPFX "Initialized gratuitous ARP subsystem"); | |
188 | } else { | |
4ec94408 | 189 | zlog_err(VRRP_LOGPFX |
b637bcd4 | 190 | "Error initializing gratuitous ARP subsystem"); |
41ee5442 QY |
191 | } |
192 | } | |
193 | ||
194 | void vrrp_garp_fini(void) | |
195 | { | |
196 | close(garp_fd); | |
197 | garp_fd = -1; | |
b637bcd4 QY |
198 | |
199 | DEBUGD(&vrrp_dbg_arp, | |
200 | VRRP_LOGPFX "Deinitialized gratuitous ARP subsystem"); | |
41ee5442 | 201 | } |
40744000 QY |
202 | |
203 | bool vrrp_garp_is_init(void) | |
204 | { | |
205 | return garp_fd > 0; | |
206 | } |