]>
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 */ | |
53 | static ssize_t vrrp_send_garp(struct interface *ifp, uint8_t *buf, ssize_t pack_len) | |
54 | { | |
55 | struct sockaddr_ll sll; | |
56 | ssize_t len; | |
57 | ||
58 | /* Build the dst device */ | |
59 | memset(&sll, 0, sizeof(sll)); | |
60 | sll.sll_family = AF_PACKET; | |
61 | sll.sll_protocol = ETH_P_ARP; | |
62 | sll.sll_ifindex = (int) ifp->ifindex; | |
63 | sll.sll_halen = ifp->hw_addr_len; | |
64 | memset(sll.sll_addr, 0xFF, ETH_ALEN); | |
65 | ||
66 | /* Send packet */ | |
67 | len = sendto(garp_fd, buf, pack_len, 0, (struct sockaddr *)&sll, | |
68 | sizeof(sll)); | |
69 | ||
70 | return len; | |
71 | } | |
72 | ||
73 | /* Build a gratuitous ARP message over a specific interface */ | |
74 | static ssize_t vrrp_build_garp(uint8_t *buf, struct interface *ifp, | |
75 | struct in_addr *v4) | |
76 | { | |
77 | uint8_t *arp_ptr; | |
78 | ||
79 | if (ifp->hw_addr_len == 0) | |
80 | return -1; | |
81 | ||
82 | /* Build Ethernet header */ | |
83 | struct ether_header *eth = (struct ether_header *) buf; | |
84 | ||
85 | memset(eth->ether_dhost, 0xFF, ETH_ALEN); | |
86 | memcpy(eth->ether_shost, ifp->hw_addr, ETH_ALEN); | |
87 | eth->ether_type = htons(ETHERTYPE_ARP); | |
88 | ||
89 | /* Build ARP payload */ | |
90 | struct arphdr *arph = (struct arphdr *) (buf + ETHER_HDR_LEN); | |
91 | ||
92 | arph->ar_hrd = htons(HWTYPE_ETHER); | |
93 | arph->ar_pro = htons(ETHERTYPE_IP); | |
94 | arph->ar_hln = ifp->hw_addr_len; | |
95 | arph->ar_pln = sizeof(struct in_addr); | |
96 | arph->ar_op = htons(ARPOP_REQUEST); | |
c23edd74 | 97 | arp_ptr = (uint8_t *)(arph + 1); |
41ee5442 QY |
98 | /* Source MAC: us */ |
99 | memcpy(arp_ptr, ifp->hw_addr, ifp->hw_addr_len); | |
100 | arp_ptr += ifp->hw_addr_len; | |
101 | /* Source IP: us */ | |
c23edd74 | 102 | memcpy(arp_ptr, v4, sizeof(struct in_addr)); |
41ee5442 QY |
103 | arp_ptr += sizeof(struct in_addr); |
104 | /* Dest MAC: broadcast */ | |
105 | memset(arp_ptr, 0xFF, ETH_ALEN); | |
106 | arp_ptr += ifp->hw_addr_len; | |
107 | /* Dest IP: us */ | |
c23edd74 | 108 | memcpy(arp_ptr, v4, sizeof(struct in_addr)); |
41ee5442 QY |
109 | arp_ptr += sizeof(struct in_addr); |
110 | ||
111 | return arp_ptr - buf; | |
112 | } | |
113 | ||
862f2f37 | 114 | void vrrp_garp_send(struct vrrp_router *r, struct in_addr *v4) |
41ee5442 | 115 | { |
dad18a2f | 116 | struct interface *ifp = r->mvl_ifp; |
41ee5442 QY |
117 | uint8_t garpbuf[GARP_BUFFER_SIZE]; |
118 | ssize_t garpbuf_len; | |
119 | ssize_t sent_len; | |
120 | char astr[INET_ADDRSTRLEN]; | |
121 | ||
122 | /* If the interface doesn't support ARP, don't try sending */ | |
123 | if (ifp->flags & IFF_NOARP) { | |
124 | zlog_warn( | |
4ec94408 | 125 | VRRP_LOGPFX VRRP_LOGPFX_VRID |
41ee5442 | 126 | "Unable to send gratuitous ARP on %s; has IFF_NOARP\n", |
862f2f37 | 127 | r->vr->vrid, ifp->name); |
41ee5442 QY |
128 | return; |
129 | } | |
130 | ||
131 | /* Build garp */ | |
132 | garpbuf_len = vrrp_build_garp(garpbuf, ifp, v4); | |
133 | ||
134 | /* Send garp */ | |
135 | inet_ntop(AF_INET, v4, astr, sizeof(astr)); | |
b637bcd4 QY |
136 | |
137 | DEBUGD(&vrrp_dbg_arp, | |
138 | VRRP_LOGPFX VRRP_LOGPFX_VRID | |
139 | "Sending gratuitous ARP on %s for %s", | |
140 | r->vr->vrid, ifp->name, astr); | |
141 | if (DEBUG_MODE_CHECK(&vrrp_dbg_arp, DEBUG_MODE_ALL)) | |
142 | zlog_hexdump(garpbuf, garpbuf_len); | |
143 | ||
41ee5442 QY |
144 | sent_len = vrrp_send_garp(ifp, garpbuf, garpbuf_len); |
145 | ||
146 | if (sent_len < 0) | |
4ec94408 QY |
147 | zlog_warn(VRRP_LOGPFX VRRP_LOGPFX_VRID |
148 | "Error sending gratuitous ARP on %s for %s", | |
862f2f37 | 149 | r->vr->vrid, ifp->name, astr); |
6332c77f QY |
150 | else |
151 | ++r->stats.garp_tx_cnt; | |
41ee5442 QY |
152 | } |
153 | ||
862f2f37 | 154 | void vrrp_garp_send_all(struct vrrp_router *r) |
41ee5442 | 155 | { |
862f2f37 QY |
156 | assert(r->family == AF_INET); |
157 | ||
dad18a2f | 158 | struct interface *ifp = r->mvl_ifp; |
41ee5442 QY |
159 | |
160 | /* If the interface doesn't support ARP, don't try sending */ | |
161 | if (ifp->flags & IFF_NOARP) { | |
162 | zlog_warn( | |
4ec94408 | 163 | VRRP_LOGPFX VRRP_LOGPFX_VRID |
41ee5442 | 164 | "Unable to send gratuitous ARP on %s; has IFF_NOARP\n", |
862f2f37 | 165 | r->vr->vrid, ifp->name); |
41ee5442 QY |
166 | return; |
167 | } | |
168 | ||
169 | struct listnode *ln; | |
862f2f37 | 170 | struct ipaddr *ip; |
41ee5442 | 171 | |
862f2f37 QY |
172 | for (ALL_LIST_ELEMENTS_RO(r->addrs, ln, ip)) |
173 | vrrp_garp_send(r, &ip->ipaddr_v4); | |
41ee5442 QY |
174 | } |
175 | ||
176 | ||
177 | void vrrp_garp_init(void) | |
178 | { | |
179 | /* Create the socket descriptor */ | |
180 | /* FIXME: why ETH_P_RARP? */ | |
40744000 QY |
181 | errno = 0; |
182 | frr_elevate_privs(&vrrp_privs) { | |
183 | garp_fd = socket(PF_PACKET, SOCK_RAW | SOCK_CLOEXEC, | |
184 | htons(ETH_P_RARP)); | |
185 | } | |
41ee5442 | 186 | |
b637bcd4 QY |
187 | if (garp_fd > 0) { |
188 | DEBUGD(&vrrp_dbg_sock, | |
189 | VRRP_LOGPFX "Initialized gratuitous ARP socket"); | |
190 | DEBUGD(&vrrp_dbg_arp, | |
191 | VRRP_LOGPFX "Initialized gratuitous ARP subsystem"); | |
192 | } else { | |
4ec94408 | 193 | zlog_err(VRRP_LOGPFX |
b637bcd4 | 194 | "Error initializing gratuitous ARP subsystem"); |
41ee5442 QY |
195 | } |
196 | } | |
197 | ||
198 | void vrrp_garp_fini(void) | |
199 | { | |
200 | close(garp_fd); | |
201 | garp_fd = -1; | |
b637bcd4 QY |
202 | |
203 | DEBUGD(&vrrp_dbg_arp, | |
204 | VRRP_LOGPFX "Deinitialized gratuitous ARP subsystem"); | |
41ee5442 | 205 | } |
40744000 QY |
206 | |
207 | bool vrrp_garp_is_init(void) | |
208 | { | |
209 | return garp_fd > 0; | |
210 | } |