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