<commit_before>/*
 * npf.c -- Configure NetBSD's packet filter from a Lua script
 * <NAME> <<EMAIL>>
 * <NAME> <<EMAIL>>
 *
 * TODO
 *  - Does it make sense to avoid needing bit32.bor()? Do we redefine it as
 *  just a set of booleans, all logically ORed together?
 *
 *  - The stats names need to be cleaned up.
 */

#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>

#include <npf.h>
#include <net/if.h>

#include <lua.h>
#include <lauxlib.h>
#include <lualib.h>

#define NPF_DEV_PATH  "/dev/npf"

typedef struct _lnpfconf
{
  int fd;  /* -1 when not in use */
  nl_config_t *conf;
}lnpfconf_t;

typedef struct _lnpfrule
{
  nl_rule_t *rule;
}lnpfrule_t;

typedef struct _lnpfrproc
{
  nl_rproc_t *rproc;
}lnpfrproc_t;

typedef struct _lnpftable
{
  nl_table_t *table;
}lnpftable_t;

int luaopen_npf(lua_State *L);

/*
 * tbl = npf.stats()
 * Return a table containing npf stats
 */
static int
lua_npf_stats(lua_State *L)
{
  int fd;
  static const struct stats_s {
    /* Note: -1 indicates a new section. */
    int		index;
    const char *	name;
  } stats[] = {
    { NPF_STAT_PASS_DEFAULT, "default_pass" },
    { NPF_STAT_PASS_RULESET, "ruleset_pass" },
    { NPF_STAT_PASS_CONN, "state_pass" },
    { NPF_STAT_BLOCK_DEFAULT, "default_block" },
    { NPF_STAT_BLOCK_RULESET, "ruleset_block" },
    { NPF_STAT_CONN_CREATE, "state_allocations" },
    { NPF_STAT_CONN_DESTROY, "state_destructions" },
    { NPF_STAT_NAT_CREATE, "nat_entry_allocations" },
    { NPF_STAT_NAT_DESTROY, "nat_entry_destructions" },
    { NPF_STAT_NBUF_NONCONTIG, "noncontiguous_cases" },
    { NPF_STAT_NBUF_CONTIG_FAIL, "contig_alloc_failures" },
    { NPF_STAT_INVALID_STATE, "cases_in_total" },
    { NPF_STAT_INVALID_STATE_TCP1, "tcp_case_I" },
    { NPF_STAT_INVALID_STATE_TCP2, "tcp_case_II" },
    { NPF_STAT_INVALID_STATE_TCP3, "tcp_case_III" },
    { NPF_STAT_RACE_NAT, "nat_association_race"	},
    { NPF_STAT_RACE_CONN, "duplicate_state_race" },
    { NPF_STAT_FRAGMENTS, "fragments" },
    { NPF_STAT_REASSEMBLY, "reassembled" },
    { NPF_STAT_REASSFAIL, "failed_reassembly" },
    { NPF_STAT_ERROR, "unexpected_errors" }
  };

  if ((fd = open(NPF_DEV_PATH, O_RDONLY)) == -1) {
    luaL_error(L, "open(" NPF_DEV_PATH ") failed");
  }

  uint64_t *st = calloc(1, NPF_STATS_SIZE);

  if (ioctl(fd, IOC_NPF_STATS, &st) != 0) {
    close(fd);
    free(st);
    luaL_error(L, "ioctl(IOC_NPF_STATS) failed");
  }

  lua_newtable(L);

  for (unsigned i = 0; i < __arraycount(stats); i++) {
    const char *sname = stats[i].name;
    int sidx = stats[i].index;

    lua_pushstring(L, sname);
    lua_pushinteger(L, (lua_Integer)st[sidx]);
    lua_settable(L, -3);
  }

  close(fd);
  free(st);

  return 1;
}

/*
 * conf = npf.config.create()
 */
static int
lua_npf_config_create(lua_State *L)
{
  lnpfconf_t *lconf;

  lconf = lua_newuserdata(L, sizeof(lnpfconf_t));
  lconf->fd = -1;
  lconf->conf = npf_config_create();

  luaL_getmetatable(L, "nl_config_t");
  lua_setmetatable(L, -2);

  return 1;
}

/*
 * Destroy a config context. This needs to handle two cases: Called explicitly
 * or called by the garbage collector.
 */
static int
lua_npf_config_destroy(lua_State *L)
{
  lnpfconf_t *lconf;

  lconf = luaL_checkudata(L, 1, "nl_config_t");

  if(lconf->conf) {
    npf_config_destroy(lconf->conf);
    lconf->conf = NULL;
  }

  if(lconf->fd != -1) {
    if(close(lconf->fd) != -1) {
      luaL_error(L, "close() failed with errno=%d\n", errno);
    }
    lconf->fd = -1;
  }

  return 0;
}

/*
 * status? = npf.config.submit(conf, fd)
 */
static int
lua_npf_config_submit(lua_State *L)
{
  lnpfconf_t *lconf;
  int status;

  lconf = luaL_checkudata(L, 1, "nl_config_t");

  if (lconf->fd == -1) {
    lconf->fd = open(NPF_DEV_PATH, O_RDONLY);
    if (lconf->fd == -1) {
      luaL_error(L, "open(" NPF_DEV_PATH ") failed");
    }
  }
  status = npf_config_submit(lconf->conf, lconf->fd);

  lua_pushinteger(L, status);
  return 1;
}

/*
 * status? = npf.config.flush(fd)
 */
static int
lua_npf_config_flush(lua_State *L)
{
  lnpfconf_t *lconf;
  int status;

  lconf = luaL_checkudata(L, 1, "nl_config_t");
  if (lconf->fd == -1) {
    lconf->fd = open(NPF_DEV_PATH, O_RDONLY);
    if (lconf->fd == -1) {
      luaL_error(L, "open(" NPF_DEV_PATH ") failed");
    }
  }
<commit_msg>Remember that luaL_error() is like printf().<commit_after>/*
 * npf.c -- Configure NetBSD's packet filter from a Lua script
 * <NAME> <<EMAIL>>
 * <NAME> <<EMAIL>>
 *
 * TODO
 *  - Does it make sense to avoid needing bit32.bor()? Do we redefine it as
 *  just a set of booleans, all logically ORed together?
 *
 *  - The stats names need to be cleaned up.
 */

#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>

#include <npf.h>
#include <net/if.h>

#include <lua.h>
#include <lauxlib.h>
#include <lualib.h>

#define NPF_DEV_PATH  "/dev/npf"

typedef struct _lnpfconf
{
  int fd;  /* -1 when not in use */
  nl_config_t *conf;
}lnpfconf_t;

typedef struct _lnpfrule
{
  nl_rule_t *rule;
}lnpfrule_t;

typedef struct _lnpfrproc
{
  nl_rproc_t *rproc;
}lnpfrproc_t;

typedef struct _lnpftable
{
  nl_table_t *table;
}lnpftable_t;

int luaopen_npf(lua_State *L);

/*
 * tbl = npf.stats()
 * Return a table containing npf stats
 */
static int
lua_npf_stats(lua_State *L)
{
  int fd;
  static const struct stats_s {
    /* Note: -1 indicates a new section. */
    int		index;
    const char *	name;
  } stats[] = {
    { NPF_STAT_PASS_DEFAULT, "default_pass" },
    { NPF_STAT_PASS_RULESET, "ruleset_pass" },
    { NPF_STAT_PASS_CONN, "state_pass" },
    { NPF_STAT_BLOCK_DEFAULT, "default_block" },
    { NPF_STAT_BLOCK_RULESET, "ruleset_block" },
    { NPF_STAT_CONN_CREATE, "state_allocations" },
    { NPF_STAT_CONN_DESTROY, "state_destructions" },
    { NPF_STAT_NAT_CREATE, "nat_entry_allocations" },
    { NPF_STAT_NAT_DESTROY, "nat_entry_destructions" },
    { NPF_STAT_NBUF_NONCONTIG, "noncontiguous_cases" },
    { NPF_STAT_NBUF_CONTIG_FAIL, "contig_alloc_failures" },
    { NPF_STAT_INVALID_STATE, "cases_in_total" },
    { NPF_STAT_INVALID_STATE_TCP1, "tcp_case_I" },
    { NPF_STAT_INVALID_STATE_TCP2, "tcp_case_II" },
    { NPF_STAT_INVALID_STATE_TCP3, "tcp_case_III" },
    { NPF_STAT_RACE_NAT, "nat_association_race"	},
    { NPF_STAT_RACE_CONN, "duplicate_state_race" },
    { NPF_STAT_FRAGMENTS, "fragments" },
    { NPF_STAT_REASSEMBLY, "reassembled" },
    { NPF_STAT_REASSFAIL, "failed_reassembly" },
    { NPF_STAT_ERROR, "unexpected_errors" }
  };

  if ((fd = open(NPF_DEV_PATH, O_RDONLY)) == -1) {
    luaL_error(L, "open(\"%s\") failed", NPF_DEV_PATH);
  }

  uint64_t *st = calloc(1, NPF_STATS_SIZE);

  if (ioctl(fd, IOC_NPF_STATS, &st) != 0) {
    close(fd);
    free(st);
    luaL_error(L, "ioctl(IOC_NPF_STATS) failed");
  }

  lua_newtable(L);

  for (unsigned i = 0; i < __arraycount(stats); i++) {
    const char *sname = stats[i].name;
    int sidx = stats[i].index;

    lua_pushstring(L, sname);
    lua_pushinteger(L, (lua_Integer)st[sidx]);
    lua_settable(L, -3);
  }

  close(fd);
  free(st);

  return 1;
}

/*
 * conf = npf.config.create()
 */
static int
lua_npf_config_create(lua_State *L)
{
  lnpfconf_t *lconf;

  lconf = lua_newuserdata(L, sizeof(lnpfconf_t));
  lconf->fd = -1;
  lconf->conf = npf_config_create();

  luaL_getmetatable(L, "nl_config_t");
  lua_setmetatable(L, -2);

  return 1;
}

/*
 * Destroy a config context. This needs to handle two cases: Called explicitly
 * or called by the garbage collector.
 */
static int
lua_npf_config_destroy(lua_State *L)
{
  lnpfconf_t *lconf;

  lconf = luaL_checkudata(L, 1, "nl_config_t");

  if(lconf->conf) {
    npf_config_destroy(lconf->conf);
    lconf->conf = NULL;
  }

  if(lconf->fd != -1) {
    if(close(lconf->fd) != -1) {
      luaL_error(L, "close() failed with errno=%d\n", errno);
    }
    lconf->fd = -1;
  }

  return 0;
}

/*
 * status? = npf.config.submit(conf, fd)
 */
static int
lua_npf_config_submit(lua_State *L)
{
  lnpfconf_t *lconf;
  int status;

  lconf = luaL_checkudata(L, 1, "nl_config_t");

  if (lconf->fd == -1) {
    lconf->fd = open(NPF_DEV_PATH, O_RDONLY);
    if (lconf->fd == -1) {
      luaL_error(L, "open(\"%s\") failed", NPF_DEV_PATH);
    }
  }
  status = npf_config_submit(lconf->conf, lconf->fd);

  lua_pushinteger(L, status);
  return 1;
}

/*
 * status? = npf.config.flush(fd)
 */
static int
lua_npf_config_flush(lua_State *L)
{
  lnpfconf_t *lconf;
  int status;

  lconf = luaL_checkudata(L, 1, "nl_config_t");
  if (lconf->fd == -1) {
    lconf->fd = open(NPF_DEV_PATH, O_RDONLY);
    if (lconf->fd == -1) {
      luaL_error(L, "open(\"%s\") failed", NPF_DEV_PATH);
    }
  }

{'fraction_non_alphanumeric': 0.14945433658816773, 'fraction_numerical': 0.0068925904652498565, 'mean_word_length': 3.6806451612903226, 'pattern_counts': {'":': 0, '<': 37, '<?xml version=': 0, '>': 67, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '4270492', 'n_tokens_mistral': 3835, 'n_tokens_neox': 3689, 'n_words': 707}