src/libexec/snmpd/snmpd_metrics/pf.c
2023-07-10 00:10:46 +00:00

501 lines
9.9 KiB
C

/* $OpenBSD: pf.c,v 1.1.1.1 2022/09/01 14:20:33 martijn Exp $ */
/*
* Copyright (c) 2012 Joel Knight <joel@openbsd.org>
* Copyright (c) 2002 Cedric Berger
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* - Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* - 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 COPYRIGHT HOLDERS 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
* COPYRIGHT HOLDERS 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 <sys/types.h>
#include <sys/socket.h>
#include <sys/ioctl.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <net/if.h>
#include <net/pfvar.h>
#include <err.h>
#include <errno.h>
#include <fcntl.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <event.h>
#include "snmpd.h"
int devpf = 0;
size_t buf_esize[PFRB_MAX] = { 0,
sizeof(struct pfr_table), sizeof(struct pfr_tstats),
sizeof(struct pfr_addr), sizeof(struct pfr_astats),
sizeof(struct pfi_kif), sizeof(struct pfioc_trans_e)
};
void
pf_init(void)
{
if ((devpf = open("/dev/pf", O_RDONLY)) == -1)
fatal("pf_init");
}
int
pf_get_stats(struct pf_status *s)
{
extern int devpf;
memset(s, 0, sizeof(*s));
if (ioctl(devpf, DIOCGETSTATUS, s) == -1) {
log_warn("DIOCGETSTATUS");
return (-1);
}
return (0);
}
int
pfr_get_astats(struct pfr_table *tbl, struct pfr_astats *addr, int *size,
int flags)
{
struct pfioc_table io;
extern int devpf;
if (tbl == NULL || size == NULL || *size < 0 ||
(*size && addr == NULL))
return (-1);
bzero(&io, sizeof io);
io.pfrio_flags = flags;
io.pfrio_table = *tbl;
io.pfrio_buffer = addr;
io.pfrio_esize = sizeof(*addr);
io.pfrio_size = *size;
if (ioctl(devpf, DIOCRGETASTATS, &io) == -1)
return (-1);
*size = io.pfrio_size;
return (0);
}
int
pfr_get_tstats(struct pfr_table *filter, struct pfr_tstats *tbl, int *size,
int flags)
{
struct pfioc_table io;
extern int devpf;
if (size == NULL || *size < 0 || (*size && tbl == NULL))
return (-1);
bzero(&io, sizeof io);
io.pfrio_flags = flags;
if (filter != NULL)
io.pfrio_table = *filter;
io.pfrio_buffer = tbl;
io.pfrio_esize = sizeof(*tbl);
io.pfrio_size = *size;
if (ioctl(devpf, DIOCRGETTSTATS, &io) == -1)
return (-1);
*size = io.pfrio_size;
return (0);
}
int
pfr_buf_grow(struct pfr_buffer *b, int minsize)
{
caddr_t p;
size_t bs;
if (minsize != 0 && minsize <= b->pfrb_msize)
return (0);
bs = buf_esize[b->pfrb_type];
if (!b->pfrb_msize) {
if (minsize < 64)
minsize = 64;
b->pfrb_caddr = calloc(bs, minsize);
if (b->pfrb_caddr == NULL)
return (-1);
b->pfrb_msize = minsize;
} else {
if (minsize == 0)
minsize = b->pfrb_msize * 2;
if (minsize < 0 || (size_t)minsize >= SIZE_MAX / bs) {
/* msize overflow */
return (-1);
}
p = reallocarray(b->pfrb_caddr, minsize, bs);
if (p == NULL)
return (-1);
bzero(p + b->pfrb_msize * bs, (minsize - b->pfrb_msize) * bs);
b->pfrb_caddr = p;
b->pfrb_msize = minsize;
}
return (0);
}
const void *
pfr_buf_next(struct pfr_buffer *b, const void *prev)
{
size_t bs;
if (b == NULL)
return (NULL);
if (b->pfrb_size == 0)
return (NULL);
if (prev == NULL)
return (b->pfrb_caddr);
bs = buf_esize[b->pfrb_type];
if ((((const char *)prev)-((char *)b->pfrb_caddr)) / bs >=
(size_t)b->pfrb_size-1)
return (NULL);
return (((const char *)prev) + bs);
}
int
pfi_get_ifaces(const char *filter, struct pfi_kif *buf, int *size)
{
struct pfioc_iface io;
extern int devpf;
if (size == NULL || *size < 0 || (*size && buf == NULL)) {
errno = EINVAL;
return (-1);
}
bzero(&io, sizeof io);
if (filter != NULL)
if (strlcpy(io.pfiio_name, filter, sizeof(io.pfiio_name)) >=
sizeof(io.pfiio_name)) {
errno = EINVAL;
return (-1);
}
io.pfiio_buffer = buf;
io.pfiio_esize = sizeof(*buf);
io.pfiio_size = *size;
if (ioctl(devpf, DIOCIGETIFACES, &io) == -1)
return (-1);
*size = io.pfiio_size;
return (0);
}
int
pfi_get(struct pfr_buffer *b, const char *filter)
{
bzero(b, sizeof(struct pfr_buffer));
b->pfrb_type = PFRB_IFACES;
for (;;) {
pfr_buf_grow(b, b->pfrb_size);
b->pfrb_size = b->pfrb_msize;
if (pfi_get_ifaces(filter, b->pfrb_caddr, &(b->pfrb_size)))
return (1);
if (b->pfrb_size <= b->pfrb_msize)
break;
}
return (0);
}
int
pfi_count(void)
{
struct pfr_buffer b;
const struct pfi_kif *p;
int c = 0;
if (pfi_get(&b, NULL)) {
free(b.pfrb_caddr);
return (-1);
}
PFRB_FOREACH(p, &b)
c++;
free(b.pfrb_caddr);
return (c);
}
int
pfi_get_if(struct pfi_kif *rp, int idx)
{
struct pfr_buffer b;
const struct pfi_kif *p;
int i = 1;
if (pfi_get(&b, NULL)) {
free(b.pfrb_caddr);
return (-1);
}
PFRB_FOREACH(p, &b) {
if (i == idx)
break;
i++;
}
if (p == NULL) {
free(b.pfrb_caddr);
return (-1);
}
bcopy(p, rp, sizeof(struct pfi_kif));
free(b.pfrb_caddr);
return (0);
}
int
pft_get(struct pfr_buffer *b, struct pfr_table *filter)
{
bzero(b, sizeof(struct pfr_buffer));
b->pfrb_type = PFRB_TSTATS;
for (;;) {
pfr_buf_grow(b, b->pfrb_size);
b->pfrb_size = b->pfrb_msize;
if (pfr_get_tstats(filter, b->pfrb_caddr, &(b->pfrb_size), 0))
return (1);
if (b->pfrb_size <= b->pfrb_msize)
break;
}
return (0);
}
int
pft_get_table(struct pfr_tstats *rts, int idx)
{
struct pfr_buffer b;
const struct pfr_tstats *ts;
int i = 1;
if (pft_get(&b, NULL)) {
free(b.pfrb_caddr);
return (-1);
}
PFRB_FOREACH(ts, &b) {
if (!(ts->pfrts_flags & PFR_TFLAG_ACTIVE))
continue;
if (i == idx)
break;
i++;
}
if (ts == NULL) {
free(b.pfrb_caddr);
return (-1);
}
bcopy(ts, rts, sizeof(struct pfr_tstats));
free(b.pfrb_caddr);
return (0);
}
int
pft_count(void)
{
struct pfr_buffer b;
const struct pfr_tstats *ts;
int c = 0;
if (pft_get(&b, NULL)) {
free(b.pfrb_caddr);
return (-1);
}
PFRB_FOREACH(ts, &b) {
if (!(ts->pfrts_flags & PFR_TFLAG_ACTIVE))
continue;
c++;
}
free(b.pfrb_caddr);
return (c);
}
int
pfta_get(struct pfr_buffer *b, struct pfr_table *filter)
{
bzero(b, sizeof(struct pfr_buffer));
b->pfrb_type = PFRB_ASTATS;
for (;;) {
pfr_buf_grow(b, b->pfrb_size);
b->pfrb_size = b->pfrb_msize;
if (pfr_get_astats(filter, b->pfrb_caddr, &(b->pfrb_size), 0)) {
return (1);
}
if (b->pfrb_size <= b->pfrb_msize)
break;
}
return (0);
}
int
pfta_get_addr(struct pfr_astats *ras, int tblidx)
{
struct pfr_buffer ba;
struct pfr_tstats ts;
struct pfr_table filter;
const struct pfr_astats *as;
if (pft_get_table(&ts, tblidx))
return (-1);
bzero(&filter, sizeof(filter));
if (strlcpy(filter.pfrt_name, ts.pfrts_name,
sizeof(filter.pfrt_name)) >= sizeof(filter.pfrt_name)) {
return (-1);
}
if (pfta_get(&ba, &filter) || ba.pfrb_size == 0) {
free(ba.pfrb_caddr);
return (-1);
}
PFRB_FOREACH(as, &ba) {
if (as->pfras_a.pfra_af != AF_INET)
continue;
if ((memcmp(&as->pfras_a.pfra_ip4addr, &ras->pfras_a.pfra_ip4addr,
sizeof(as->pfras_a.pfra_ip4addr)) == 0)
&& (as->pfras_a.pfra_net == ras->pfras_a.pfra_net))
break;
}
if (as == NULL) {
free(ba.pfrb_caddr);
return (-1);
}
bcopy(as, ras, sizeof(struct pfr_astats));
free(ba.pfrb_caddr);
return (0);
}
int
pfta_get_nextaddr(struct pfr_astats *ras, int *tblidx)
{
struct pfr_buffer ba;
struct pfr_tstats ts;
struct pfr_table filter;
const struct pfr_astats *as;
int i, found = 0, cmp;
ba.pfrb_caddr = NULL;
for (i = *tblidx; !pft_get_table(&ts, i); i++) {
bzero(&filter, sizeof(filter));
if (strlcpy(filter.pfrt_name, ts.pfrts_name,
sizeof(filter.pfrt_name)) >= sizeof(filter.pfrt_name))
goto fail;
if (pfta_get(&ba, &filter) || ba.pfrb_size == 0)
goto fail;
PFRB_FOREACH(as, &ba) {
if (as->pfras_a.pfra_af != AF_INET)
continue;
if (found)
goto found;
cmp = memcmp(&as->pfras_a.pfra_ip4addr,
&ras->pfras_a.pfra_ip4addr,
sizeof(as->pfras_a.pfra_ip4addr));
if (cmp == 0) {
if (as->pfras_a.pfra_net ==
ras->pfras_a.pfra_net)
found = 1;
if (as->pfras_a.pfra_net >
ras->pfras_a.pfra_net)
goto found;
} else if (cmp > 0)
goto found;
}
free(ba.pfrb_caddr);
ba.pfrb_caddr = NULL;
}
fail:
free(ba.pfrb_caddr);
return (-1);
found:
bcopy(as, ras, sizeof(struct pfr_astats));
*tblidx = i;
free(ba.pfrb_caddr);
return (0);
}
int
pfta_get_first(struct pfr_astats *ras)
{
struct pfr_buffer ba;
struct pfr_tstats ts;
struct pfr_table filter;
const struct pfr_astats *as;
if (pft_get_table(&ts, 1))
return (-1);
bzero(&filter, sizeof(filter));
if (strlcpy(filter.pfrt_name, ts.pfrts_name,
sizeof(filter.pfrt_name)) >= sizeof(filter.pfrt_name)) {
return (-1);
}
if (pfta_get(&ba, &filter) || ba.pfrb_size == 0) {
free(ba.pfrb_caddr);
return (-1);
}
/* take the first AF_INET addr */
PFRB_FOREACH(as, &ba) {
if (as->pfras_a.pfra_af != AF_INET)
continue;
break;
}
if (as == NULL) {
free(ba.pfrb_caddr);
return (-1);
}
bcopy(as, ras, sizeof(struct pfr_astats));
free(ba.pfrb_caddr);
return (0);
}