1/*	from $OpenBSD: ifconfig.c,v 1.82 2003/10/19 05:43:35 mcbride Exp $ */
2
3/*-
4 * SPDX-License-Identifier: BSD-2-Clause
5 *
6 * Copyright (c) 2002 Michael Shalayeff. All rights reserved.
7 * Copyright (c) 2003 Ryan McBride. All rights reserved.
8 *
9 * Redistribution and use in source and binary forms, with or without
10 * modification, are permitted provided that the following conditions
11 * are met:
12 * 1. Redistributions of source code must retain the above copyright
13 *    notice, this list of conditions and the following disclaimer.
14 * 2. Redistributions in binary form must reproduce the above copyright
15 *    notice, this list of conditions and the following disclaimer in the
16 *    documentation and/or other materials provided with the distribution.
17 *
18 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
19 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
20 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
21 * IN NO EVENT SHALL THE AUTHOR OR HIS RELATIVES BE LIABLE FOR ANY DIRECT,
22 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
23 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
24 * SERVICES; LOSS OF MIND, USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
25 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
26 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
27 * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
28 * THE POSSIBILITY OF SUCH DAMAGE.
29 */
30
31#include <sys/param.h>
32#include <sys/ioctl.h>
33#include <sys/socket.h>
34#include <sys/sockio.h>
35
36#include <stdlib.h>
37#include <unistd.h>
38
39#include <net/if.h>
40#include <netinet/in.h>
41#include <netinet/in_var.h>
42#include <netinet/ip_carp.h>
43
44#include <arpa/inet.h>
45
46#include <ctype.h>
47#include <stdbool.h>
48#include <stdio.h>
49#include <string.h>
50#include <stdlib.h>
51#include <unistd.h>
52#include <err.h>
53#include <errno.h>
54#include <netdb.h>
55
56#include <libifconfig.h>
57
58#include "ifconfig.h"
59
60static const char *carp_states[] = { CARP_STATES };
61
62static void setcarp_callback(if_ctx *, void *);
63
64static int carpr_vhid = -1;
65static int carpr_advskew = -1;
66static int carpr_advbase = -1;
67static int carpr_state = -1;
68static struct in_addr carp_addr;
69static struct in6_addr carp_addr6;
70static unsigned char const *carpr_key;
71static carp_version_t carpr_version;
72static uint8_t carpr_vrrp_prio;
73static uint16_t carpr_vrrp_adv_inter;
74
75static void
76carp_status(if_ctx *ctx)
77{
78	struct ifconfig_carp carpr[CARP_MAXVHID];
79	char addr_buf[NI_MAXHOST];
80
81	if (ifconfig_carp_get_info(lifh, ctx->ifname, carpr, CARP_MAXVHID) == -1)
82		return;
83
84	for (size_t i = 0; i < carpr[0].carpr_count; i++) {
85		switch (carpr[i].carpr_version) {
86		case CARP_VERSION_CARP:
87			printf("\tcarp: %s vhid %d advbase %d advskew %d",
88			    carp_states[carpr[i].carpr_state], carpr[i].carpr_vhid,
89			    carpr[i].carpr_advbase, carpr[i].carpr_advskew);
90			if (ctx->args->printkeys && carpr[i].carpr_key[0] != '\0')
91				printf(" key \"%s\"\n", carpr[i].carpr_key);
92			else
93				printf("\n");
94
95			inet_ntop(AF_INET6, &carpr[i].carpr_addr6, addr_buf,
96			    sizeof(addr_buf));
97
98			printf("\t      peer %s peer6 %s\n",
99			    inet_ntoa(carpr[i].carpr_addr), addr_buf);
100			break;
101		case CARP_VERSION_VRRPv3:
102			printf("\tvrrp: %s vrid %d prio %d interval %d\n",
103			    carp_states[carpr[i].carpr_state], carpr[i].carpr_vhid,
104			    carpr[i].carpr_vrrp_prio, carpr[i].carpr_vrrp_adv_inter);
105			break;
106		}
107	}
108}
109
110static void
111setcarp_vhid(if_ctx *ctx, const char *val, int dummy __unused)
112{
113	const struct afswtch *afp = ctx->afp;
114
115	carpr_vhid = atoi(val);
116
117	if (carpr_vhid <= 0 || carpr_vhid > CARP_MAXVHID)
118		errx(1, "vhid must be greater than 0 and less than %u",
119		    CARP_MAXVHID);
120
121	if (afp->af_setvhid == NULL)
122		errx(1, "%s doesn't support carp(4)", afp->af_name);
123	afp->af_setvhid(carpr_vhid);
124	callback_register(setcarp_callback, NULL);
125}
126
127static void
128setcarp_callback(if_ctx *ctx, void *arg __unused)
129{
130	struct ifconfig_carp carpr = { };
131
132	if (ifconfig_carp_get_vhid(lifh, ctx->ifname, &carpr, carpr_vhid) == -1) {
133		if (ifconfig_err_errno(lifh) != ENOENT)
134			return;
135	}
136
137	carpr.carpr_vhid = carpr_vhid;
138	if (carpr_key != NULL)
139		/* XXX Should hash the password into the key here? */
140		strlcpy(carpr.carpr_key, carpr_key, CARP_KEY_LEN);
141	if (carpr_advskew > -1)
142		carpr.carpr_advskew = carpr_advskew;
143	if (carpr_advbase > -1)
144		carpr.carpr_advbase = carpr_advbase;
145	if (carpr_state > -1)
146		carpr.carpr_state = carpr_state;
147	if (carp_addr.s_addr != INADDR_ANY)
148		carpr.carpr_addr = carp_addr;
149	if (! IN6_IS_ADDR_UNSPECIFIED(&carp_addr6))
150		memcpy(&carpr.carpr_addr6, &carp_addr6,
151		    sizeof(carp_addr6));
152	if (carpr_version != 0)
153		carpr.carpr_version = carpr_version;
154	if (carpr_vrrp_prio != 0)
155		carpr.carpr_vrrp_prio = carpr_vrrp_prio;
156	if (carpr_vrrp_adv_inter != 0)
157		carpr.carpr_vrrp_adv_inter = carpr_vrrp_adv_inter;
158
159	if (ifconfig_carp_set_info(lifh, ctx->ifname, &carpr))
160		err(1, "SIOCSVH");
161}
162
163static void
164setcarp_passwd(if_ctx *ctx __unused, const char *val, int dummy __unused)
165{
166
167	if (carpr_vhid == -1)
168		errx(1, "passwd requires vhid");
169
170	carpr_key = val;
171}
172
173static void
174setcarp_advskew(if_ctx *ctx __unused, const char *val, int dummy __unused)
175{
176
177	if (carpr_vhid == -1)
178		errx(1, "advskew requires vhid");
179
180	carpr_advskew = atoi(val);
181}
182
183static void
184setcarp_advbase(if_ctx *ctx __unused, const char *val, int dummy __unused)
185{
186
187	if (carpr_vhid == -1)
188		errx(1, "advbase requires vhid");
189
190	carpr_advbase = atoi(val);
191}
192
193static void
194setcarp_state(if_ctx *ctx __unused, const char *val, int dummy __unused)
195{
196	int i;
197
198	if (carpr_vhid == -1)
199		errx(1, "state requires vhid");
200
201	for (i = 0; i <= CARP_MAXSTATE; i++)
202		if (strcasecmp(carp_states[i], val) == 0) {
203			carpr_state = i;
204			return;
205		}
206
207	errx(1, "unknown state");
208}
209
210static void
211setcarp_peer(if_ctx *ctx __unused, const char *val, int dummy __unused)
212{
213	carp_addr.s_addr = inet_addr(val);
214}
215
216static void
217setcarp_mcast(if_ctx *ctx __unused, const char *val __unused, int dummy __unused)
218{
219	carp_addr.s_addr = htonl(INADDR_CARP_GROUP);
220}
221
222static void
223setcarp_peer6(if_ctx *ctx __unused, const char *val, int dummy __unused)
224{
225	struct addrinfo hints, *res;
226
227	memset(&hints, 0, sizeof(hints));
228	hints.ai_family = AF_INET6;
229	hints.ai_flags = AI_NUMERICHOST;
230
231	if (getaddrinfo(val, NULL, &hints, &res) != 0)
232		errx(1, "Invalid IPv6 address %s", val);
233
234	memcpy(&carp_addr6, &(satosin6(res->ai_addr))->sin6_addr, sizeof(carp_addr6));
235	freeaddrinfo(res);
236}
237
238static void
239setcarp_mcast6(if_ctx *ctx __unused, const char *val __unused, int dummy __unused)
240{
241	bzero(&carp_addr6, sizeof(carp_addr6));
242	carp_addr6.s6_addr[0] = 0xff;
243	carp_addr6.s6_addr[1] = 0x02;
244	carp_addr6.s6_addr[15] = 0x12;
245}
246
247static void
248setcarp_version(if_ctx *ctx __unused, const char *val, int dummy __unused)
249{
250	carpr_version = atoi(val);
251
252	if (carpr_version != CARP_VERSION_CARP && carpr_version != CARP_VERSION_VRRPv3)
253		errx(1, "version must be %d or %d", CARP_VERSION_CARP,
254		    CARP_VERSION_VRRPv3);
255}
256
257static void
258setvrrp_prio(if_ctx *ctx __unused, const char *val, int dummy __unused)
259{
260	carpr_vrrp_prio = atoi(val);
261}
262
263static void
264setvrrp_interval(if_ctx *ctx __unused, const char *val, int dummy __unused)
265{
266	carpr_vrrp_adv_inter = atoi(val);
267
268	if (carpr_vrrp_adv_inter == 0 || carpr_vrrp_adv_inter > VRRP_MAX_INTERVAL)
269		errx(1, "vrrpinterval must be greater than 0 and less than %d", VRRP_MAX_INTERVAL);
270}
271
272static struct cmd carp_cmds[] = {
273	DEF_CMD_ARG("advbase",	setcarp_advbase),
274	DEF_CMD_ARG("advskew",	setcarp_advskew),
275	DEF_CMD_ARG("pass",	setcarp_passwd),
276	DEF_CMD_ARG("vhid",	setcarp_vhid),
277	DEF_CMD_ARG("state",	setcarp_state),
278	DEF_CMD_ARG("peer",	setcarp_peer),
279	DEF_CMD("mcast",	0,	setcarp_mcast),
280	DEF_CMD_ARG("peer6",	setcarp_peer6),
281	DEF_CMD("mcast6", 	0,	setcarp_mcast6),
282	DEF_CMD_ARG("carpver",	setcarp_version),
283	DEF_CMD_ARG("vrrpprio",	setvrrp_prio),
284	DEF_CMD_ARG("vrrpinterval",	setvrrp_interval),
285};
286static struct afswtch af_carp = {
287	.af_name	= "af_carp",
288	.af_af		= AF_UNSPEC,
289	.af_other_status = carp_status,
290};
291
292static __constructor void
293carp_ctor(void)
294{
295	/* Default to multicast. */
296	setcarp_mcast(NULL, NULL, 0);
297	setcarp_mcast6(NULL, NULL, 0);
298
299	for (size_t i = 0; i < nitems(carp_cmds);  i++)
300		cmd_register(&carp_cmds[i]);
301	af_register(&af_carp);
302}
303