xref: /openbsd/usr.sbin/sasyncd/carp.c (revision e6c7c102)
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