1 /* $OpenBSD: carp.c,v 1.19 2024/04/23 13:34:51 jsg Exp $ */
2
3 /*
4 * Copyright (c) 2005 H�kan Olsson. All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 *
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
17 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
18 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
19 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
20 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
21 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
22 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
23 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
25 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 */
27
28 /*
29 * This code was written under funding by Multicom Security AB.
30 */
31
32
33 #include <sys/types.h>
34 #include <sys/ioctl.h>
35 #include <sys/select.h>
36 #include <sys/socket.h>
37 #include <net/if.h>
38 #include <net/route.h>
39
40 #include <errno.h>
41 #include <stdio.h>
42 #include <string.h>
43 #include <unistd.h>
44
45 #include "monitor.h"
46 #include "sasyncd.h"
47
48 int carp_demoted = 0;
49
50 /* Map CARP interface link state into RUNSTATE enum */
51 static enum RUNSTATE
carp_map_state(u_char link_state)52 carp_map_state(u_char link_state)
53 {
54 enum RUNSTATE state = FAIL;
55
56 switch(link_state) {
57 case LINK_STATE_UP:
58 case LINK_STATE_HALF_DUPLEX:
59 case LINK_STATE_FULL_DUPLEX:
60 state = MASTER;
61 break;
62 case LINK_STATE_DOWN:
63 state = SLAVE;
64 break;
65 case LINK_STATE_UNKNOWN:
66 case LINK_STATE_INVALID:
67 state = INIT;
68 break;
69 }
70
71 return state;
72 }
73
74 static enum RUNSTATE
carp_get_state(char * ifname)75 carp_get_state(char *ifname)
76 {
77 struct ifreq ifr;
78 struct if_data ifrdat;
79 int s, saved_errno;
80
81 if (!ifname || !*ifname) {
82 errno = ENOENT;
83 return FAIL;
84 }
85
86 memset(&ifr, 0, sizeof ifr);
87 strlcpy(ifr.ifr_name, ifname, sizeof ifr.ifr_name);
88
89 s = socket(AF_INET, SOCK_DGRAM, 0);
90 if (s < 0)
91 return FAIL;
92
93 ifr.ifr_data = (caddr_t)&ifrdat;
94 if (ioctl(s, SIOCGIFDATA, (caddr_t)&ifr) == -1) {
95 saved_errno = errno;
96 close(s);
97 errno = saved_errno;
98 return FAIL;
99 }
100 close(s);
101 return carp_map_state(ifrdat.ifi_link_state);
102 }
103
104 void
carp_demote(int demote,int force)105 carp_demote(int demote, int force)
106 {
107 struct ifgroupreq ifgr;
108 int s;
109
110 if (carp_demoted + demote < 0) {
111 log_msg(1, "carp_demote: mismatched promotion");
112 return;
113 }
114
115 s = socket(AF_INET, SOCK_DGRAM, 0);
116 if (s < 0) {
117 log_msg(1, "carp_demote: couldn't open socket");
118 return;
119 }
120
121 bzero(&ifgr, sizeof(ifgr));
122 strlcpy(ifgr.ifgr_name, cfgstate.carp_ifgroup, sizeof(ifgr.ifgr_name));
123
124 /* Unless we force it, don't demote if we're not demoting already. */
125 if (!force) {
126 if (ioctl(s, SIOCGIFGATTR, (caddr_t)&ifgr) == -1) {
127 log_msg(1, "carp_demote: unable to get "
128 "the demote state of group '%s'",
129 cfgstate.carp_ifgroup);
130 goto done;
131 }
132
133 if (ifgr.ifgr_attrib.ifg_carp_demoted == 0)
134 goto done;
135 }
136
137 ifgr.ifgr_attrib.ifg_carp_demoted = demote;
138 if (ioctl(s, SIOCSIFGATTR, (caddr_t)&ifgr) == -1)
139 log_msg(1, "carp_demote: unable to %s the demote state "
140 "of group '%s'", (demote > 0) ?
141 "increment" : "decrement", cfgstate.carp_ifgroup);
142 else {
143 carp_demoted += demote;
144 log_msg(1, "carp_demote: %sed the demote state "
145 "of group '%s'", (demote > 0) ?
146 "increment" : "decrement", cfgstate.carp_ifgroup);
147 }
148 done:
149 close(s);
150 }
151
152 const char*
carp_state_name(enum RUNSTATE state)153 carp_state_name(enum RUNSTATE state)
154 {
155 static const char *carpstate[] = CARPSTATES;
156
157 if ((unsigned)state > FAIL)
158 state = FAIL;
159 return carpstate[state];
160 }
161
162 void
carp_update_state(enum RUNSTATE current_state)163 carp_update_state(enum RUNSTATE current_state)
164 {
165
166 if ((unsigned)current_state > FAIL) {
167 log_err("carp_update_state: invalid carp state, abort");
168 cfgstate.runstate = FAIL;
169 return;
170 }
171
172 if (current_state != cfgstate.runstate) {
173 log_msg(1, "carp_update_state: switching state to %s",
174 carp_state_name(current_state));
175 cfgstate.runstate = current_state;
176 if (current_state == MASTER)
177 pfkey_set_promisc();
178 control_setrun();
179 net_ctl_update_state();
180 }
181 }
182
183 void
carp_check_state(void)184 carp_check_state(void)
185 {
186 carp_update_state(carp_get_state(cfgstate.carp_ifname));
187 }
188
189 void
carp_set_rfd(fd_set * fds)190 carp_set_rfd(fd_set *fds)
191 {
192 if (cfgstate.route_socket != -1)
193 FD_SET(cfgstate.route_socket, fds);
194 }
195
196 static void
carp_read(void)197 carp_read(void)
198 {
199 char msg[2048];
200 struct rt_msghdr *rtm = (struct rt_msghdr *)&msg;
201 struct if_msghdr ifm;
202 ssize_t len;
203
204 len = read(cfgstate.route_socket, msg, sizeof(msg));
205
206 if (len < (ssize_t)sizeof(struct rt_msghdr) ||
207 rtm->rtm_version != RTM_VERSION ||
208 rtm->rtm_type != RTM_IFINFO)
209 return;
210
211 memcpy(&ifm, rtm, sizeof(ifm));
212
213 if (ifm.ifm_index == cfgstate.carp_ifindex)
214 carp_update_state(carp_map_state(ifm.ifm_data.ifi_link_state));
215 }
216
217 void
carp_read_message(fd_set * fds)218 carp_read_message(fd_set *fds)
219 {
220 if (cfgstate.route_socket != -1)
221 if (FD_ISSET(cfgstate.route_socket, fds))
222 (void)carp_read();
223 }
224
225 /* Initialize the CARP state. */
226 int
carp_init(void)227 carp_init(void)
228 {
229 unsigned int rtfilter;
230
231 cfgstate.route_socket = -1;
232 if (cfgstate.lockedstate != INIT) {
233 cfgstate.runstate = cfgstate.lockedstate;
234 log_msg(1, "carp_init: locking runstate to %s",
235 carp_state_name(cfgstate.runstate));
236 return 0;
237 }
238
239 if (!cfgstate.carp_ifname || !*cfgstate.carp_ifname) {
240 fprintf(stderr, "No carp interface\n");
241 return -1;
242 }
243
244 cfgstate.carp_ifindex = if_nametoindex(cfgstate.carp_ifname);
245 if (!cfgstate.carp_ifindex) {
246 fprintf(stderr, "No carp interface index\n");
247 return -1;
248 }
249
250 cfgstate.route_socket = socket(AF_ROUTE, SOCK_RAW, 0);
251 if (cfgstate.route_socket < 0) {
252 fprintf(stderr, "No routing socket\n");
253 return -1;
254 }
255
256 rtfilter = ROUTE_FILTER(RTM_IFINFO);
257 if (setsockopt(cfgstate.route_socket, AF_ROUTE, ROUTE_MSGFILTER,
258 &rtfilter, sizeof(rtfilter)) == -1) /* not fatal */
259 log_msg(2, "carp_init: setsockopt");
260
261 cfgstate.runstate = carp_get_state(cfgstate.carp_ifname);
262 if (cfgstate.runstate == FAIL) {
263 fprintf(stderr, "Failed to check interface \"%s\".\n",
264 cfgstate.carp_ifname);
265 fprintf(stderr, "Correct or manually select runstate.\n");
266 return -1;
267 }
268 log_msg(1, "carp_init: initializing runstate to %s",
269 carp_state_name(cfgstate.runstate));
270
271 return 0;
272 }
273
274 /* Enable or disable isakmpd/iked connection checker. */
275 void
control_setrun(void)276 control_setrun(void)
277 {
278 if (cfgstate.runstate == MASTER) {
279 if (monitor_control_active(1))
280 log_msg(0, "failed to activate controlled daemon");
281 } else {
282 if (monitor_control_active(0))
283 log_msg(0, "failed to passivate controlled daemon");
284 }
285 }
286