/*************************************************************************
* (C) 2022 CloudSigma AG - Sofia/Bulgaria
* by Michael Pounov <misho@elwix.org>
**************************************************************************/
#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) {
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]);
if (fws_ctx.config & CTX_EDGE_ONLINE) {
callout_drain(&fws_co);
ipfw_unregister_state_sync();
ipfw_unregister_alias_sync();
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;
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)
{
DTRACE();
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>