#include <sys/types.h>
#include <sys/ioctl.h>
#include <sys/socket.h>
#include <sys/stat.h>
#include <net/if.h>
#include <netinet/in.h>
#include <net/pfvar.h>
#include <arpa/inet.h>
#include <sys/sysctl.h>
#include <err.h>
#include <errno.h>
#include <fcntl.h>
#include <limits.h>
#include <netdb.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <syslog.h>
#include <stdarg.h>
#include <libgen.h>
#include "pfctl_parser.h"
#include "pfctl.h"
void changerule_usage(void);
int do_chng_cmd(char *, int, int);
extern int dev;
extern char *anchoropt;
extern char *pf_device;
__dead void
changerule_usage(void)
{
extern char *__progname;
fprintf(stderr, "usage: %s", __progname);
fprintf(stderr, "[-a anchor] [ -i ruleNo ] [ -I ruleNo ]\n");
exit(1);
}
int
do_chng_cmd(char *anchorname, int cmd, int rule_no)
{
struct pfctl pf;
struct pf_anchor rs_anchor;
struct pf_ruleset *rs = &rs_anchor.ruleset;
struct pfioc_rule pcr;
memset(&pf, 0, sizeof(pf));
memset(&rs_anchor, 0, sizeof(rs_anchor));
pf.anchor = &rs_anchor;
pf_init_ruleset(rs);
if (strlcpy(pf.anchor->path, anchorname,
sizeof(pf.anchor->path)) >= sizeof (pf.anchor->path))
errx(1, "%s: strlcpy", __func__);
pf.astack[0] = pf.anchor;
pf.asd = 0;
pf.dev = dev;
memset(&pcr, 0, sizeof(pcr));
strlcpy(pcr.anchor, anchorname, sizeof(pcr.anchor));
pcr.action = PF_CHANGE_GET_TICKET;
if (ioctl(dev, DIOCCHANGERULE, &pcr) < 0)
errx(1, "ioctl(ticket) @ %s", __func__);
pcr.action = cmd;
pcr.nr = rule_no;
if (cmd != PF_CHANGE_REMOVE) {
if (parse_config("-", &pf) < 0) {
errx(1, "Syntax error in rule");
return (1);
}
if (TAILQ_FIRST(rs->rules.active.ptr) != NULL)
memcpy(&pcr.rule, TAILQ_FIRST(rs->rules.active.ptr),
sizeof(pcr.rule));
else
errx(1, "no rule");
}
if (ioctl(dev, DIOCCHANGERULE, &pcr) < 0) {
errx(1, "ioctl(commit) @ %s", __func__);
}
return (0);
}
int
main(int argc, char *argv[])
{
char anchorname[PATH_MAX];
const char *errstr;
int ch;
int rule_no;
int chng_cmd;
int after = 0;
if (argc < 2)
changerule_usage();
while ((ch = getopt(argc, argv, "a:i:I:r:")) != -1) {
switch (ch) {
case 'a':
anchoropt = optarg;
break;
case 'I':
after = 1;
case 'i':
rule_no = strtonum(optarg, -1, 0x7fffffff, &errstr);
if (errstr != NULL) {
warnx("Rule number outside of range <%d, %d\n",
-1, 0x7fffffff);
exit(1);
}
switch (rule_no) {
case 0:
chng_cmd = PF_CHANGE_ADD_HEAD;
break;
case -1:
chng_cmd = PF_CHANGE_ADD_TAIL;
break;
default:
if (after)
chng_cmd = PF_CHANGE_ADD_AFTER;
else
chng_cmd = PF_CHANGE_ADD_BEFORE;
}
break;
case 'r':
rule_no = strtonum(optarg, -1, 0x7fffffff, &errstr);
if (errstr != NULL) {
warnx("Rule number outside of range <%d, %d\n",
-1, 0x7fffffff);
exit(1);
}
chng_cmd = PF_CHANGE_REMOVE;
break;
default:
changerule_usage();
}
}
if (argc != optind) {
warnx("unknown command line argument: %s ...", argv[optind]);
changerule_usage();
}
memset(anchorname, 0, sizeof(anchorname));
if (anchoropt != NULL) {
if (anchoropt[0] == '\0')
errx(1, "anchor name must not be empty");
if (anchoropt[0] == '_' || strstr(anchoropt, "/_") != NULL)
errx(1, "anchor names beginning with '_' cannot "
"be modified from the command line");
int len = strlen(anchoropt);
if (anchoropt[len - 1] == '*') {
warnx("wildcard anchors not supported");
changerule_usage();
}
if (strlcpy(anchorname, anchoropt,
sizeof(anchorname)) >= sizeof(anchorname))
errx(1, "anchor name '%s' too long",
anchoropt);
}
dev = open(pf_device, O_RDWR);
if (dev == -1)
err(1, "/dev/pf");
return (do_chng_cmd(anchoropt, chng_cmd, rule_no));
}