File:  [ELWIX - Embedded LightWeight unIX -] / fwsync / driver / fwsync.c
Revision 1.13: download - view: text, annotated - select for diffs - revision graph
Fri Aug 26 14:47:36 2022 UTC (20 months, 1 week ago) by misho
Branches: MAIN
CVS tags: fwsync1_2, HEAD, FWSYNC1_1
version 1.1
 - adds list command

/*-
 * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
 *
 * Copyright (c) 2022 Michael Pounov <misho@elwix.org>, CloudSigma AG
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 */
#include "fwsync.h"


static void
fwsync_edge_proc(void *arg)
{
	int e, rcvflg = 0;
	struct uio uio;
	struct mbuf *m = NULL;
	struct fws_proto *pkt;

	DTRACE();

	callout_schedule(&fws_co, hz);

	memset(&uio, 0, sizeof uio);
	uio.uio_resid = 1000000000;
	uio.uio_td = curthread;

	if ((fws_cfg.cfg.on & CFG_SYNC_EDGE) && (fws_ctx.config & CTX_EDGE_READY)) {
		rcvflg = MSG_DONTWAIT;
		e = soreceive(fws_ctx.sockz[CFG_SYNC_ADDR_EDGE], NULL, &uio, &m, NULL, &rcvflg);
		if (e) {
			if (e != EAGAIN)
				printf("error in edge handler #%d\n", e);
			return;
		}
		pkt = mtod(m, struct fws_proto*);
		if (m_length(m, NULL) != sizeof(struct fws_proto)) {
			printf("FWSync packet length=%d isn't match expected %lu\n", 
					m_length(m, NULL), sizeof(struct fws_proto));
			m_freem(m);
			return;
		}

		switch (pkt->fws_version) {
			case FWS_PKTVER_STATE:
				fwsync_add_state(pkt);
				break;
			case FWS_PKTVER_ALIAS:
				fwsync_add_alias(pkt);
				break;
			default:
				printf("FWSync packet was discarded due to wrong version\n");
				break;
		}

		m_freem(m);
	}
}

int
fwsync_cfg(struct ip_fw_chain *ch, ip_fw3_opheader *op3, struct sockopt_data *sd)
{
	ipfw_obj_header *oh;
	struct ipfw_sync_cfg *ucfg;
	size_t sz;
	int e;

	DTRACE();

	sz = sizeof(*oh) + sizeof(*ucfg);
	/* Check minimum header size */
	if (sd->valsize < sz)
		return (EINVAL);

	oh = (ipfw_obj_header*) sd->kbuf;

	/* Basic length checks for TLVs */
	if (oh->ntlv.head.length != sizeof(oh->ntlv))
		return (EINVAL);

	ucfg = (struct ipfw_sync_cfg*) (oh + 1);

	/* Check if name is properly terminated */
	if (strnlen(ucfg->name, sizeof(ucfg->name)) == sizeof(ucfg->name))
		return (EINVAL);

	if (ucfg->mode == CFG_SYNC_EDGE && !fws_cfg.cfg.edge && !(fws_ctx.config & CTX_CFG_EDGE) && 
			!strcmp(ucfg->name, "edge") && ucfg->addrs == 1) {
		fws_cfg.cfg.edge = 1;
		memcpy(&fws_cfg.cfg_addr[CFG_SYNC_ADDR_EDGE], &ucfg->addr[CFG_SYNC_ADDR_EDGE], 
				sizeof fws_cfg.cfg_addr[CFG_SYNC_ADDR_EDGE]);
		fws_ctx.config |= CTX_CFG_EDGE;

		e = socreate((fws_cfg.cfg_addr[CFG_SYNC_ADDR_EDGE].addr.sa_family == AF_INET) ? AF_INET : AF_INET6, 
				&fws_ctx.sockz[CFG_SYNC_ADDR_EDGE], SOCK_DGRAM, IPPROTO_UDP, curthread->td_ucred, curthread);
		if (e) {
			printf("fwsync edge socreate failed #%d\n", e);
			return e;
		}

		e = sobind(fws_ctx.sockz[CFG_SYNC_ADDR_EDGE], &fws_cfg.cfg_addr[CFG_SYNC_ADDR_EDGE].addr, curthread);
		if (e) {
			if (e != EADDRINUSE)
				printf("fwsync edge sobind failed #%d\n", e);
			else
				printf("fwsync edge address in use!\n");
			return e;
		} else
			fws_ctx.config |= CTX_EDGE_READY;
	} else if (ucfg->mode == CFG_SYNC_COLLECTOR && !(fws_ctx.config & CTX_CFG_COLLECTOR_1) && 
			!strcmp(ucfg->name, "collector") && ucfg->addrs > 0 && ucfg->addrs < 3) {
		fws_cfg.cfg.collector = 1;
		fws_cfg.cfg.addrs = ucfg->addrs;
		memcpy(&fws_cfg.cfg_addr[CFG_SYNC_ADDR_COLLECTOR_1], &ucfg->addr[CFG_SYNC_ADDR_COLLECTOR_1], 
				sizeof fws_cfg.cfg_addr[CFG_SYNC_ADDR_COLLECTOR_1]);

		fws_ctx.config |= CTX_CFG_COLLECTOR_1;

		e = socreate((fws_cfg.cfg_addr[CFG_SYNC_ADDR_COLLECTOR_1].addr.sa_family == AF_INET) ? AF_INET : AF_INET6, 
				&fws_ctx.sockz[CFG_SYNC_ADDR_COLLECTOR_1], SOCK_DGRAM, IPPROTO_UDP, curthread->td_ucred, curthread);
		if (e) {
			printf("fwsync collector %d socreate failed #%d\n", e, CFG_SYNC_ADDR_COLLECTOR_1);
			return e;
		} else
			fws_ctx.config |= CTX_COLLECTOR_1_READY;

		if (fws_cfg.cfg.addrs > 1) {
			memcpy(&fws_cfg.cfg_addr[CFG_SYNC_ADDR_COLLECTOR_2], &ucfg->addr[CFG_SYNC_ADDR_COLLECTOR_2], 
					sizeof fws_cfg.cfg_addr[CFG_SYNC_ADDR_COLLECTOR_2]);

			fws_ctx.config |= CTX_CFG_COLLECTOR_2;

			e = socreate((fws_cfg.cfg_addr[CFG_SYNC_ADDR_COLLECTOR_2].addr.sa_family == AF_INET) ? AF_INET : AF_INET6, 
					&fws_ctx.sockz[CFG_SYNC_ADDR_COLLECTOR_2], SOCK_DGRAM, IPPROTO_UDP, curthread->td_ucred, curthread);
			if (e) {
				printf("fwsync collector %d socreate failed #%d\n", e, CFG_SYNC_ADDR_COLLECTOR_2);
				return e;
			} else
				fws_ctx.config |= CTX_COLLECTOR_2_READY;
		}
	} else
		return (EINVAL);

	return 0;
}

int
fwsync_destroy(struct ip_fw_chain *ch, ip_fw3_opheader *op3, struct sockopt_data *sd)
{
	int *n;
	ipfw_obj_header *oh;
	size_t sz;

	DTRACE();

	sz = sizeof(*oh) + sizeof(int);
	/* Check minimum header size */
	if (sd->valsize < sz)
		return (EINVAL);

	oh = (ipfw_obj_header*) sd->kbuf;

	/* Basic length checks for TLVs */
	if (oh->ntlv.head.length != sizeof(oh->ntlv))
		return (EINVAL);

	n = (int*) (oh + 1);
	if (*n & CFG_SYNC_EDGE) {
		if (fws_ctx.config & CTX_EDGE_ONLINE) {
			ipfw_unregister_state_sync();
			ipfw_unregister_alias_sync();
		}

		callout_drain(&fws_co);

		fws_cfg.cfg.on &= ~CFG_SYNC_EDGE;
		fws_cfg.cfg.edge = 0;
		fws_cfg.cfg.addrs = 0;
		memset(fws_cfg.cfg_addr, 0, sizeof fws_cfg.cfg_addr[0]);

		soshutdown(fws_ctx.sockz[CFG_SYNC_ADDR_EDGE], SHUT_RD);
		soclose(fws_ctx.sockz[CFG_SYNC_ADDR_EDGE]);
	}
	if (*n & CFG_SYNC_COLLECTOR) {
		if (fws_ctx.config & (CTX_COLLECTOR_1_ONLINE | CTX_COLLECTOR_2_ONLINE)) {
			ipfw_unregister_state_hook();
			ipfw_unregister_alias_hook();
		}

		taskqueue_drain(fws_tq, &fws_sndpkt_task);

		fws_cfg.cfg.on &= ~CFG_SYNC_COLLECTOR;
		fws_cfg.cfg.collector = 0;
		fws_cfg.cfg.addrs = 0;
		memset(fws_cfg.cfg_addr + 1, 0, sizeof fws_cfg.cfg_addr[0] * 2);

		if (fws_ctx.config & CTX_COLLECTOR_2_READY)
			soclose(fws_ctx.sockz[CFG_SYNC_ADDR_COLLECTOR_2]);
		if (fws_ctx.config & CTX_COLLECTOR_1_READY)
			soclose(fws_ctx.sockz[CFG_SYNC_ADDR_COLLECTOR_1]);
	}

	fws_ctx.config ^= fws_ctx.config;

	memset(&fws_acct, 0, sizeof fws_acct);

	return 0;
}

int
fwsync_get_cfg(struct ip_fw_chain *ch, ip_fw3_opheader *op3, struct sockopt_data *sd)
{
	ipfw_obj_header *oh;
	struct ipfw_sync_cfg *ucfg;
	size_t sz;

	DTRACE();

	sz = sizeof(*oh) + sizeof(*ucfg);
	/* Check minimum header size */
	if (sd->valsize < sz)
		return (EINVAL);

	oh = (struct _ipfw_obj_header*) ipfw_get_sopt_header(sd, sz);

	/* Basic length checks for TLVs */
	if (oh->ntlv.head.length != sizeof(oh->ntlv))
		return (EINVAL);

	ucfg = (struct ipfw_sync_cfg*) (oh + 1);

	/* Check if name is properly terminated */
	if (strnlen(ucfg->name, sizeof(ucfg->name)) == sizeof(ucfg->name))
		return (EINVAL);

	snprintf(ucfg->name, sizeof ucfg->name, "%d", fws_cfg.cfg.on);
	ucfg->mode = 0;
	if (fws_cfg.cfg.edge)
		ucfg->mode |= CFG_SYNC_EDGE;
	if (fws_cfg.cfg.collector)
		ucfg->mode |= CFG_SYNC_COLLECTOR;
	ucfg->addrs = (fws_cfg.cfg.addrs != 1) ? fws_cfg.cfg.addrs : 1;
	memcpy(ucfg->addr, fws_cfg.cfg_addr, sizeof ucfg->addr);

	return 0;
}

int
fwsync_list(struct ip_fw_chain *ch, ip_fw3_opheader *op3, struct sockopt_data *sd)
{
	ipfw_obj_header *oh;
	struct ipfw_sync_cfg *ucfg;
	size_t sz;

	DTRACE();

	sz = sizeof(*oh) + sizeof(*ucfg);
	/* Check minimum header size */
	if (sd->valsize < sz)
		return (EINVAL);

	oh = (struct _ipfw_obj_header*) ipfw_get_sopt_header(sd, sz);

	/* Basic length checks for TLVs */
	if (oh->ntlv.head.length != sizeof(oh->ntlv))
		return (EINVAL);

	ucfg = (struct ipfw_sync_cfg*) (oh + 1);

	/* Check if name is properly terminated */
	if (strnlen(ucfg->name, sizeof(ucfg->name)) == sizeof(ucfg->name))
		return (EINVAL);

	ucfg->mode = 0;
	if (fws_cfg.cfg.edge)
		ucfg->mode |= CFG_SYNC_EDGE;
	if (fws_cfg.cfg.collector)
		ucfg->mode |= CFG_SYNC_COLLECTOR;
	ucfg->addrs = 2;
	memcpy(ucfg->addr[0].ip6.sin6_addr.s6_addr, &fws_acct.states[0], sizeof(uint64_t));
	memcpy(ucfg->addr[0].ip6.sin6_addr.s6_addr + 8, &fws_acct.states[1], sizeof(uint64_t));
	memcpy(ucfg->addr[1].ip6.sin6_addr.s6_addr, &fws_acct.aliases[0], sizeof(uint64_t));
	memcpy(ucfg->addr[1].ip6.sin6_addr.s6_addr + 8, &fws_acct.aliases[1], sizeof(uint64_t));

	return 0;
}

int
fwsync_start(struct ip_fw_chain *ch, ip_fw3_opheader *op3, struct sockopt_data *sd)
{
	int *n;
	ipfw_obj_header *oh;
	size_t sz;

	DTRACE();

	sz = sizeof(*oh) + sizeof(int);
	/* Check minimum header size */
	if (sd->valsize < sz)
		return (EINVAL);

	oh = (ipfw_obj_header*) sd->kbuf;

	/* Basic length checks for TLVs */
	if (oh->ntlv.head.length != sizeof(oh->ntlv))
		return (EINVAL);

	n = (int*) (oh + 1);

	if ((*n & CFG_SYNC_EDGE) && (fws_ctx.config & CTX_EDGE_READY)) {
		fws_cfg.cfg.on |= CFG_SYNC_EDGE;

		callout_reset(&fws_co, hz, fwsync_edge_proc, NULL);

		if (!(fws_ctx.config & CTX_EDGE_ONLINE)) {
			ipfw_register_state_sync(fwsync_state_sync);
			ipfw_register_alias_sync(fwsync_alias_sync);
		}

		fws_ctx.config |= CTX_EDGE_ONLINE;
	}

	if ((*n & CFG_SYNC_COLLECTOR) && (fws_ctx.config & CTX_COLLECTOR_1_READY)) {
		fws_cfg.cfg.on |= CFG_SYNC_COLLECTOR;

		if (!(fws_ctx.config & (CTX_COLLECTOR_1_ONLINE | CTX_COLLECTOR_2_ONLINE))) {
			ipfw_register_state_hook(fwsync_state_handler);
			ipfw_register_alias_hook(fwsync_alias_handler);
		}

		fws_ctx.config |= CTX_COLLECTOR_1_ONLINE;
	}

	if ((*n & CFG_SYNC_COLLECTOR) && (fws_ctx.config & CTX_COLLECTOR_2_READY)) {
		fws_cfg.cfg.on |= CFG_SYNC_COLLECTOR;

		if (!(fws_ctx.config & (CTX_COLLECTOR_1_ONLINE | CTX_COLLECTOR_2_ONLINE))) {
			ipfw_register_state_hook(fwsync_state_handler);
			ipfw_register_alias_hook(fwsync_alias_handler);
		}

		fws_ctx.config |= CTX_COLLECTOR_2_ONLINE;
	}

	return 0;
}

int
fwsync_stop(struct ip_fw_chain *ch, ip_fw3_opheader *op3, struct sockopt_data *sd)
{
	int *n;
	ipfw_obj_header *oh;
	size_t sz;

	DTRACE();

	sz = sizeof(*oh) + sizeof(int);
	/* Check minimum header size */
	if (sd->valsize < sz)
		return (EINVAL);

	oh = (ipfw_obj_header*) sd->kbuf;

	/* Basic length checks for TLVs */
	if (oh->ntlv.head.length != sizeof(oh->ntlv))
		return (EINVAL);

	n = (int*) (oh + 1);

	if ((*n & CFG_SYNC_EDGE) && (fws_ctx.config & CTX_CFG_EDGE)) {
		fws_cfg.cfg.on &= ~CFG_SYNC_EDGE;
		fws_ctx.config &= ~CTX_EDGE_ONLINE;

		callout_drain(&fws_co);

		ipfw_unregister_state_sync();
		ipfw_unregister_alias_sync();
	}

	if ((*n & CFG_SYNC_COLLECTOR) && (fws_ctx.config & CTX_COLLECTOR_2_ONLINE))
		fws_ctx.config &= ~CTX_COLLECTOR_2_ONLINE;
	if ((*n & CFG_SYNC_COLLECTOR) && (fws_ctx.config & CTX_COLLECTOR_1_ONLINE))
		fws_ctx.config &= ~CTX_COLLECTOR_1_ONLINE;

	if ((*n & CFG_SYNC_COLLECTOR) && 
			!(fws_ctx.config & (CTX_COLLECTOR_1_ONLINE | CTX_COLLECTOR_2_ONLINE))) {
		fws_cfg.cfg.on &= ~CFG_SYNC_COLLECTOR;

		ipfw_unregister_state_hook();
		ipfw_unregister_alias_hook();

		taskqueue_drain(fws_tq, &fws_sndpkt_task);
	}

	return 0;
}

FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>