/*- * SPDX-License-Identifier: BSD-2-Clause-FreeBSD * * Copyright (c) 2022 Michael Pounov , 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) { 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; }