GCC Code Coverage Report
Directory: ./ Exec Total Coverage
File: usr.sbin/sasyncd/carp.c Lines: 0 116 0.0 %
Date: 2017-11-07 Branches: 0 67 0.0 %

Line Branch Exec Source
1
/*	$OpenBSD: carp.c,v 1.16 2017/08/31 16:19:22 otto 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
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
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
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*
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
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
184
carp_check_state()
185
{
186
	carp_update_state(carp_get_state(cfgstate.carp_ifname));
187
}
188
189
void
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
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
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
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(PF_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, PF_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
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
}