File:  [ELWIX - Embedded LightWeight unIX -] / fwsync / patches / sync.c
Revision 1.4: download - view: text, annotated - select for diffs - revision graph
Fri Aug 26 14:47:36 2022 UTC (20 months 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.
 *
 * command line interface for sync state of IP firewall
 */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <sys/param.h>
#include <netinet/in.h>
#include <net/if.h>
#include <net/if_dl.h>
#include <arpa/inet.h>
#include <netinet/ip_fw.h>
#include <err.h>
#include <sysexits.h>

#include "ipfw2.h"

typedef union {
	struct sockaddr_storage	ss;
	struct sockaddr		sa;
	struct sockaddr_un	sun;
	struct sockaddr_in	sin;
	struct sockaddr_in6	sin6;
	struct sockaddr_dl	sdl;
} sockaddr_t;
#define E_SOCKADDR_INIT	{ .ss = { 0 } }
#define E_SOCKADDR_MAX	MIN(sizeof(sockaddr_t), 0xff)

#define SYNC_SHIFT_ARG	{ ac--; av++; }
#define DEF_SYNC_PORT	20611

void
ipfw_config_sync(int ac, char **av)
{
	u_short port;
	sockaddr_t *sa = NULL;
	int len, j, i = 0;
	char *str, *buf, show[BUFSIZ], host[64];
	void *ptr;
	ipfw_obj_header *oh;
	struct ipfw_sync_cfg *cfg;

	SYNC_SHIFT_ARG;

	len = sizeof(*oh) + sizeof(*cfg);
	buf = malloc(len);
	if (!buf)
		errx(EX_OSERR, "malloc failed");
	else
		memset(buf, 0, len);
	oh = (ipfw_obj_header*) buf;
	cfg = (struct ipfw_sync_cfg*) (oh + 1);
	oh->ntlv.head.length = sizeof(oh->ntlv);

	if (ac && !strcmp(*av, "edge")) {
		SYNC_SHIFT_ARG;
		if (ac && !strcmp(*av, "port"))
			SYNC_SHIFT_ARG;
		if (!ac)
			errx(EX_DATAERR, "missing edge port\n");

		strlcpy(host, *av, sizeof host);
		if ((str = strchr(host, ','))) {
			*str++ = 0;
			port = strtol(str, NULL, 10);
			if (!port)
				errx(EX_DATAERR, "incorrect port number\n");
		} else {
			port = strtol(host, NULL, 10);
			if (!port)
				errx(EX_DATAERR, "incorrect port number\n");
			strlcpy(host, "0.0.0.0", sizeof host);
		}

		strlcpy(oh->ntlv.name, "edge", sizeof(oh->ntlv.name));
		strlcpy(cfg->name, "edge", sizeof(cfg->name));
		cfg->mode = CFG_SYNC_EDGE;
		cfg->addrs = 1;
		cfg->addr[0].addr.sa_family = strchr(host, ':') ? AF_INET6 : AF_INET;
		if (cfg->addr[0].addr.sa_family == AF_INET) {
				cfg->addr[0].ip4.sin_len = sizeof cfg->addr[0].ip4;
				cfg->addr[0].ip4.sin_port = htons(port);
				if (inet_pton(AF_INET, host, &cfg->addr[0].ip4.sin_addr) != 1)
					errx(EX_DATAERR, "invalid edge IPv4 address\n");
		} else {
				cfg->addr[0].ip6.sin6_len = sizeof cfg->addr[0].ip6;
				cfg->addr[0].ip6.sin6_port = htons(port);
				if (inet_pton(AF_INET6, host, &cfg->addr[0].ip6.sin6_addr) != 1)
					errx(EX_DATAERR, "invalid edge IPv6 address\n");
		}
	} else if (ac && !strcmp(*av, "collector")) {
		SYNC_SHIFT_ARG;
		if (!ac)
			errx(EX_DATAERR, "missing destination(s) address[,port]\n");
		while (ac && *av) {
			ptr = realloc(sa, E_SOCKADDR_MAX * (i + 1));
			if (!ptr) {
				free(sa);
				errx(EX_DATAERR, "not enough memory for collectors\n");
			} else
				sa = ptr;
			memset(sa + i, 0, E_SOCKADDR_MAX);
			if ((str = strchr(*av, ','))) {
				*str++ = 0;
				port = strtol(str, NULL, 10);
				if (!port) {
					free(sa);
					errx(EX_DATAERR, "incorrect port number\n");
				}
			} else
				port = DEF_SYNC_PORT;
			sa[i].sa.sa_family = strchr(*av, ':') ? AF_INET6 : AF_INET;
			if (sa[i].sa.sa_family == AF_INET) {
				sa[i].sa.sa_len = sizeof sa[i].sin;
				sa[i].sin.sin_port = htons(port);
				if (inet_pton(AF_INET, *av, &sa[i].sin.sin_addr) != 1) {
					free(sa);
					errx(EX_DATAERR, "invalid collector address\n");
				}

				cfg->addr[1 + i].ip4.sin_len = sizeof cfg->addr[1 + i].ip4;
				cfg->addr[1 + i].ip4.sin_family = AF_INET;
				cfg->addr[1 + i].ip4.sin_port = htons(port);
				memcpy(&cfg->addr[1 + i].ip4.sin_addr, &sa[i].sin.sin_addr, 
						sizeof cfg->addr[1 + i].ip4.sin_addr);
			} else {
				sa[i].sa.sa_len = sizeof sa[i].sin6;
				sa[i].sin6.sin6_port = htons(port);
				if (inet_pton(AF_INET6, *av, &sa[i].sin6.sin6_addr) != 1) {
					free(sa);
					errx(EX_DATAERR, "invalid collector address\n");
				}

				cfg->addr[1 + i].ip6.sin6_len = sizeof cfg->addr[1 + i].ip6;
				cfg->addr[1 + i].ip6.sin6_family = AF_INET6;
				cfg->addr[1 + i].ip6.sin6_port = htons(port);
				memcpy(&cfg->addr[1 + i].ip6.sin6_addr, &sa[i].sin6.sin6_addr, 
						sizeof cfg->addr[1 + i].ip6.sin6_addr);
			}

			i++;
			SYNC_SHIFT_ARG;

			if (i == 2)	/* maximum 2 collectors at same time */
				break;
		}
		free(sa);

		strlcpy(oh->ntlv.name, "collector", sizeof(oh->ntlv.name));
		strlcpy(cfg->name, "collector", sizeof(cfg->name));
		cfg->mode = CFG_SYNC_COLLECTOR;
		cfg->addrs = MIN(i, 2);
	} else
		errx(EX_DATAERR, "missing type of service edge or collector\n");

	i = do_set3(IP_FW_SYNC_XCONFIG, &oh->opheader, len);
	if (i)
		err(1, "setsockopt(%s)", "IP_FW_SYNC_XCONFIG");

	if (!g_co.do_quiet) {
		/* After every modification, we show the resultant rule. */
		if (cfg->mode == CFG_SYNC_EDGE) {
			printf("edge port %hu\n", ntohs(cfg->addr[0].ip4.sin_port));
		} else {
			printf("collector");
			for (j = 1; j < cfg->addrs + 1; j++) {
				if (cfg->addr[j].addr.sa_family == AF_INET)
					printf(" %s,%hu", 
							inet_ntop(AF_INET, &cfg->addr[j].ip4.sin_addr, 
								show, sizeof show), 
							ntohs(cfg->addr[j].ip4.sin_port));
				else
					printf(" %s,%hu", 
							inet_ntop(AF_INET6, &cfg->addr[j].ip6.sin6_addr, 
								show, sizeof show), 
							ntohs(cfg->addr[j].ip6.sin6_port));
			}
			printf("\n");
		}
	}

	free(buf);
}

void
ipfw_list_sync(int ac, char **av)
{
	ipfw_obj_header *oh;
	struct ipfw_sync_cfg *cfg;
	size_t sz;

	SYNC_SHIFT_ARG;

	sz = sizeof *oh + sizeof *cfg;
	while (42) {
		if (!(oh = malloc(sz)))
			return;
		else
			memset(oh, 0, sz);
		cfg = (struct ipfw_sync_cfg*) (oh + 1);
		oh->ntlv.head.length = sizeof(oh->ntlv);
		strlcpy(oh->ntlv.name, ac ? *av : "", sizeof(oh->ntlv.name));
		strlcpy(cfg->name, ac ? *av : "", sizeof(cfg->name));

		if (do_get3(IP_FW_SYNC_LIST, &oh->opheader, &sz)) {
			free(oh);
			if (errno == ENOMEM)
				continue;
			return;
		}

		break;
	}

	if (!ac || !strcmp(*av, "edge"))
		printf("sync edge states %lu aliases %lu\n", 
				*(uint64_t*) (cfg->addr[0].ip6.sin6_addr.s6_addr + 8), 
				*(uint64_t*) (cfg->addr[1].ip6.sin6_addr.s6_addr + 8));
	if (!ac || !strcmp(*av, "collector"))
		printf("sync collector states %lu aliases %lu\n", 
				*(uint64_t*) cfg->addr[0].ip6.sin6_addr.s6_addr, 
				*(uint64_t*) cfg->addr[1].ip6.sin6_addr.s6_addr);
}

void
ipfw_show_sync(int ac, char **av)
{
	ipfw_obj_header *oh;
	struct ipfw_sync_cfg *cfg;
	size_t sz;
	char show[BUFSIZ];
	int i;

	SYNC_SHIFT_ARG;

	sz = sizeof *oh + sizeof *cfg;
	while (42) {
		if (!(oh = malloc(sz)))
			return;
		else
			memset(oh, 0, sz);
		cfg = (struct ipfw_sync_cfg*) (oh + 1);
		oh->ntlv.head.length = sizeof(oh->ntlv);
		strlcpy(oh->ntlv.name, ac ? *av : "", sizeof(oh->ntlv.name));
		strlcpy(cfg->name, ac ? *av : "", sizeof(cfg->name));

		if (do_get3(IP_FW_SYNC_XGETCONFIG, &oh->opheader, &sz)) {
			free(oh);
			if (errno == ENOMEM)
				continue;
			return;
		}

		break;
	}

	i = strtol(cfg->name, NULL, 10);
	if (!ac || !strcmp(*av, "edge"))
		printf("ipfw sync %s edge\n", (i & CFG_SYNC_EDGE) ? "start" : "stop");
	if (!ac || !strcmp(*av, "collector"))
		printf("ipfw sync %s collector\n", (i & CFG_SYNC_COLLECTOR) ? "start" : "stop");
	if ((!ac || !strcmp(*av, "edge")) && cfg->mode & CFG_SYNC_EDGE)
		printf("ipfw sync config edge port %hu\n", ntohs(cfg->addr[0].ip4.sin_port));
	if ((!ac || !strcmp(*av, "collector")) && cfg->mode & CFG_SYNC_COLLECTOR) {
		printf("ipfw sync config collector");
		for (i = 1; i < cfg->addrs + 1; i++) {
			if (cfg->addr[i].addr.sa_family == AF_INET)
				printf(" %s,%hu", 
						inet_ntop(AF_INET, &cfg->addr[i].ip4.sin_addr, 
							show, sizeof show), 
						ntohs(cfg->addr[i].ip4.sin_port));
			else
				printf(" %s,%hu", 
						inet_ntop(AF_INET6, &cfg->addr[i].ip6.sin6_addr, 
							show, sizeof show), 
						ntohs(cfg->addr[i].ip6.sin6_port));
		}
		printf("\n");
	}
}

void
ipfw_start_sync(int ac, char **av)
{
	int *n;
	ipfw_obj_header *oh;
	size_t sz;
	char *buf;

	SYNC_SHIFT_ARG;

	sz = sizeof *oh + sizeof(int);
	buf = malloc(sz);
	if (!buf)
		errx(EX_OSERR, "malloc failed");
	else
		memset(buf, 0, sz);
	oh = (ipfw_obj_header*) buf;
	n = (int*) (oh + 1);
	oh->ntlv.head.length = sizeof(oh->ntlv);

	if (!ac || !strcmp(*av, "edge")) {
		*n = CFG_SYNC_EDGE;
	}
	if (!ac || !strcmp(*av, "collector")) {
		*n |= CFG_SYNC_COLLECTOR;
	}

	if (do_set3(IP_FW_SYNC_START, &oh->opheader, sz))
		err(1, "setsockopt(%s)", "IP_FW_SYNC_START");

	if (!g_co.do_quiet) {
		if (!ac || !strcmp(*av, "edge"))
			printf("ipfw sync start edge\n");
		if (!ac || !strcmp(*av, "collector"))
			printf("ipfw sync start collector\n");
	}
}

void
ipfw_stop_sync(int ac, char **av)
{
	int *n;
	ipfw_obj_header *oh;
	size_t sz;
	char *buf;

	SYNC_SHIFT_ARG;

	sz = sizeof *oh + sizeof(int);
	buf = malloc(sz);
	if (!buf)
		errx(EX_OSERR, "malloc failed");
	else
		memset(buf, 0, sz);
	oh = (ipfw_obj_header*) buf;
	n = (int*) (oh + 1);
	oh->ntlv.head.length = sizeof(oh->ntlv);

	if (!ac || !strcmp(*av, "edge")) {
		*n = CFG_SYNC_EDGE;
	}
	if (!ac || !strcmp(*av, "collector")) {
		*n |= CFG_SYNC_COLLECTOR;
	}

	if (do_set3(IP_FW_SYNC_STOP, &oh->opheader, sz))
		err(1, "setsockopt(%s)", "IP_FW_SYNC_STOP");

	if (!g_co.do_quiet) {
		if (!ac || !strcmp(*av, "edge"))
			printf("ipfw sync stop edge\n");
		if (!ac || !strcmp(*av, "collector"))
			printf("ipfw sync stop collector\n");
	}
}

void
ipfw_flush_sync(int ac, char **av)
{
	int *n;
	ipfw_obj_header *oh;
	size_t sz;
	char *buf;

	SYNC_SHIFT_ARG;

	sz = sizeof *oh + sizeof(int);
	buf = malloc(sz);
	if (!buf)
		errx(EX_OSERR, "malloc failed");
	else
		memset(buf, 0, sz);
	oh = (ipfw_obj_header*) buf;
	n = (int*) (oh + 1);
	oh->ntlv.head.length = sizeof(oh->ntlv);

	if (!ac || !strcmp(*av, "edge")) {
		*n = CFG_SYNC_EDGE;
	}
	if (!ac || !strcmp(*av, "collector")) {
		*n |= CFG_SYNC_COLLECTOR;
	}

	if (do_set3(IP_FW_SYNC_DESTROY, &oh->opheader, sz))
		err(1, "setsockopt(%s)", "IP_FW_SYNC_DESTROY");

	if (!g_co.do_quiet) {
		if (!ac || !strcmp(*av, "edge"))
			printf("ipfw sync flush edge\n");
		if (!ac || !strcmp(*av, "collector"))
			printf("ipfw sync flush collector\n");
	}
}

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