summaryrefslogtreecommitdiffstats
path: root/sbin
diff options
context:
space:
mode:
Diffstat (limited to 'sbin')
-rw-r--r--sbin/ipfw/Makefile1
-rw-r--r--sbin/ipfw/ipfw.843
-rw-r--r--sbin/ipfw/ipfw2.c247
3 files changed, 258 insertions, 33 deletions
diff --git a/sbin/ipfw/Makefile b/sbin/ipfw/Makefile
index 023f682..bfd1561 100644
--- a/sbin/ipfw/Makefile
+++ b/sbin/ipfw/Makefile
@@ -4,5 +4,6 @@ PROG= ipfw
SRCS= ipfw2.c
WARNS?= 0
MAN= ipfw.8
+CFLAGS+= -I${.CURDIR}/../../sys/contrib/pf
.include <bsd.prog.mk>
diff --git a/sbin/ipfw/ipfw.8 b/sbin/ipfw/ipfw.8
index 7f911ed..ca2d5fd 100644
--- a/sbin/ipfw/ipfw.8
+++ b/sbin/ipfw/ipfw.8
@@ -26,10 +26,10 @@
.Op Ar number ...
.Nm
.Cm enable
-.Brq Cm firewall | one_pass | debug | verbose | dyn_keepalive
+.Brq Cm firewall | altq | one_pass | debug | verbose | dyn_keepalive
.Nm
.Cm disable
-.Brq Cm firewall | one_pass | debug | verbose | dyn_keepalive
+.Brq Cm firewall | altq | one_pass | debug | verbose | dyn_keepalive
.Pp
.Nm
.Cm set Oo Cm disable Ar number ... Oc Op Cm enable Ar number ...
@@ -438,6 +438,7 @@ rules is the following:
.br
.Ar " " action
.Op Cm log Op Cm logamount Ar number
+.Op Cm altq Ar queue
.Ar body
.Ed
.Pp
@@ -469,6 +470,8 @@ window
for ICMP packets
.It User/group ID
When the packet can be associated with a local socket.
+.It Divert status
+Whether a packet came from a divert socket (e.g. natd).
.El
.Pp
Note that some of the above information, e.g.\& source MAC or IP addresses and
@@ -560,6 +563,41 @@ command.
Note: logging is done after all other packet matching conditions
have been successfully verified, and before performing the final
action (accept, deny, etc.) on the packet.
+.It Cm altq Ar queue
+When a packet matches a rule with the
+.Cm altq
+keyword, the ALTQ identifier for the given
+.Ar queue
+(see
+.Xr pf.conf 5 )
+will be attached.
+Note that this ALTQ tag is only meaningful for packets going "out" of IPFW,
+and not being rejected or going to divert sockets.
+Note that if there is insufficient memory at the time the packet is
+processed, it will not be tagged, so it is wise to make your ALTQ
+"default" queue policy account for this.
+If multiple
+.Cm altq
+rules match a single packet, subsequent tags are ignored by ALTQ.
+.Pp
+You must run
+.Xr pfctl 8
+to set up the queues before IPFW will be able to look them up by name,
+and if the ALTQ disciplines are rearranged, the rules in containing the
+queue identifiers in the kernel will likely have gone stale and need
+to be reloaded.
+Stale queue identifiers will probably misclassify
+.Pp
+All system ALTQ processing can be turned on or off via
+.Nm
+.Cm enable Ar altq
+and
+.Nm
+.Cm disable Ar altq .
+The usage of
+.Em net.inet.ip.fw.one_pass
+is irrelevant to ALTQ traffic shaping, as the actual rule action is followed
+always after adding an ALTQ tag.
.El
.Ss RULE ACTIONS
A rule can be associated with one of the following actions, which
@@ -2275,6 +2313,7 @@ the sleep terminates thus restoring the previous situation.
.Xr ip 4 ,
.Xr ipfirewall 4 ,
.Xr protocols 5 ,
+.Xr pf.conf 5 ,
.Xr services 5 ,
.Xr init 8 ,
.Xr kldload 8 ,
diff --git a/sbin/ipfw/ipfw2.c b/sbin/ipfw/ipfw2.c
index 97cbf4c..7828fab 100644
--- a/sbin/ipfw/ipfw2.c
+++ b/sbin/ipfw/ipfw2.c
@@ -27,6 +27,7 @@
#include <sys/sysctl.h>
#include <sys/time.h>
#include <sys/wait.h>
+#include <sys/queue.h>
#include <ctype.h>
#include <err.h>
@@ -43,8 +44,11 @@
#include <timeconv.h> /* XXX do we need this ? */
#include <unistd.h>
#include <sysexits.h>
+#include <unistd.h>
+#include <fcntl.h>
#include <net/if.h>
+#include <net/pfvar.h>
#include <netinet/in.h>
#include <netinet/in_systm.h>
#include <netinet/ip.h>
@@ -202,6 +206,9 @@ enum tokens {
TOK_UNREACH,
TOK_CHECKSTATE,
+ TOK_ALTQ,
+ TOK_LOG,
+
TOK_UID,
TOK_GID,
TOK_JAIL,
@@ -302,6 +309,12 @@ struct _s_x rule_actions[] = {
{ NULL, 0 } /* terminator */
};
+struct _s_x rule_action_params[] = {
+ { "altq", TOK_ALTQ },
+ { "log", TOK_LOG },
+ { NULL, 0 } /* terminator */
+};
+
struct _s_x rule_options[] = {
{ "uid", TOK_UID },
{ "gid", TOK_GID },
@@ -563,6 +576,107 @@ strtoport(char *s, char **end, int base, int proto)
}
/*
+ * Map between current altq queue id numbers and names.
+ */
+static int altq_fetched = 0;
+static TAILQ_HEAD(, pf_altq) altq_entries =
+ TAILQ_HEAD_INITIALIZER(altq_entries);
+
+static void
+altq_set_enabled(int enabled)
+{
+ int pffd;
+
+ pffd = open("/dev/pf", O_RDWR);
+ if (pffd == -1)
+ err(EX_UNAVAILABLE,
+ "altq support opening pf(4) control device");
+ if (enabled) {
+ if (ioctl(pffd, DIOCSTARTALTQ) != 0 && errno != EEXIST)
+ err(EX_UNAVAILABLE, "enabling altq");
+ } else {
+ if (ioctl(pffd, DIOCSTOPALTQ) != 0 && errno != ENOENT)
+ err(EX_UNAVAILABLE, "disabling altq");
+ }
+ close(pffd);
+}
+
+static void
+altq_fetch()
+{
+ struct pfioc_altq pfioc;
+ struct pf_altq *altq;
+ int pffd, mnr;
+
+ if (altq_fetched)
+ return;
+ altq_fetched = 1;
+ pffd = open("/dev/pf", O_RDONLY);
+ if (pffd == -1) {
+ warn("altq support opening pf(4) control device");
+ return;
+ }
+ bzero(&pfioc, sizeof(pfioc));
+ if (ioctl(pffd, DIOCGETALTQS, &pfioc) != 0) {
+ warn("altq support getting queue list");
+ close(pffd);
+ return;
+ }
+ mnr = pfioc.nr;
+ for (pfioc.nr = 0; pfioc.nr < mnr; pfioc.nr++) {
+ if (ioctl(pffd, DIOCGETALTQ, &pfioc) != 0) {
+ if (errno == EBUSY)
+ break;
+ warn("altq support getting queue list");
+ close(pffd);
+ return;
+ }
+ if (pfioc.altq.qid == 0)
+ continue;
+ altq = malloc(sizeof(*altq));
+ if (altq == NULL)
+ err(EX_OSERR, "malloc");
+ *altq = pfioc.altq;
+ TAILQ_INSERT_TAIL(&altq_entries, altq, entries);
+ }
+ close(pffd);
+}
+
+static u_int32_t
+altq_name_to_qid(const char *name)
+{
+ struct pf_altq *altq;
+
+ altq_fetch();
+ TAILQ_FOREACH(altq, &altq_entries, entries)
+ if (strcmp(name, altq->qname) == 0)
+ break;
+ if (altq == NULL)
+ errx(EX_DATAERR, "altq has no queue named `%s'", name);
+ return altq->qid;
+}
+
+static const char *
+altq_qid_to_name(u_int32_t qid)
+{
+ struct pf_altq *altq;
+
+ altq_fetch();
+ TAILQ_FOREACH(altq, &altq_entries, entries)
+ if (qid == altq->qid)
+ break;
+ if (altq == NULL)
+ return NULL;
+ return altq->qname;
+}
+
+static void
+fill_altq_qid(u_int32_t *qid, const char *av)
+{
+ *qid = altq_name_to_qid(av);
+}
+
+/*
* Fill the body of the command with the list of port ranges.
*/
static int
@@ -908,6 +1022,7 @@ show_ipfw(struct ip_fw *rule, int pcwidth, int bcwidth)
int proto = 0; /* default */
int flags = 0; /* prerequisites */
ipfw_insn_log *logptr = NULL; /* set if we find an O_LOG */
+ ipfw_insn_altq *altqptr = NULL; /* set if we find an O_ALTQ */
int or_block = 0; /* we are in an or block */
uint32_t set_disable;
@@ -1033,8 +1148,12 @@ show_ipfw(struct ip_fw *rule, int pcwidth, int bcwidth)
logptr = (ipfw_insn_log *)cmd;
break;
+ case O_ALTQ: /* O_ALTQ is printed after O_LOG */
+ altqptr = (ipfw_insn_altq *)cmd;
+ break;
+
default:
- printf("** unrecognized action %d len %d",
+ printf("** unrecognized action %d len %d ",
cmd->opcode, cmd->len);
}
}
@@ -1044,6 +1163,15 @@ show_ipfw(struct ip_fw *rule, int pcwidth, int bcwidth)
else
printf(" log");
}
+ if (altqptr) {
+ const char *qname;
+
+ qname = altq_qid_to_name(altqptr->qid);
+ if (qname == NULL)
+ printf(" altq ?<%u>", altqptr->qid);
+ else
+ printf(" altq %s", qname);
+ }
/*
* then print the body.
@@ -1174,6 +1302,23 @@ show_ipfw(struct ip_fw *rule, int pcwidth, int bcwidth)
printf(cmd->len & F_NOT ? " out" : " in");
break;
+ case O_DIVERTED:
+ switch (cmd->arg1) {
+ case 3:
+ printf(" diverted");
+ break;
+ case 1:
+ printf(" diverted-loopback");
+ break;
+ case 2:
+ printf(" diverted-output");
+ break;
+ default:
+ printf(" diverted-?<%u>", cmd->arg1);
+ break;
+ }
+ break;
+
case O_LAYER2:
printf(" layer2");
break;
@@ -1709,6 +1854,8 @@ sysctl_handler(int ac, char *av[], int which)
} else if (strncmp(*av, "dyn_keepalive", strlen(*av)) == 0) {
sysctlbyname("net.inet.ip.fw.dyn_keepalive", NULL, 0,
&which, sizeof(which));
+ } else if (strncmp(*av, "altq", strlen(*av)) == 0) {
+ altq_set_enabled(which);
} else {
warnx("unrecognize enable/disable keyword: %s\n", *av);
}
@@ -1903,9 +2050,10 @@ help(void)
"set [disable N... enable N...] | move [rule] X to Y | swap X Y | show\n"
"table N {add ip[/bits] [value] | delete ip[/bits] | flush | list}\n"
"\n"
-"RULE-BODY: check-state [LOG] | ACTION [LOG] ADDR [OPTION_LIST]\n"
+"RULE-BODY: check-state [PARAMS] | ACTION [PARAMS] ADDR [OPTION_LIST]\n"
"ACTION: check-state | allow | count | deny | reject | skipto N |\n"
" {divert|tee} PORT | forward ADDR | pipe N | queue N\n"
+"PARAMS: [log [logamount LOGLIMIT]] [altq QUEUE_NAME]\n"
"ADDR: [ MAC dst src ether_type ] \n"
" [ from IPADDR [ PORT ] to IPADDR [ PORTLIST ] ]\n"
"IPADDR: [not] { any | me | ip/bits{x,y,z} | table(t[,v]) | IPLIST }\n"
@@ -2756,11 +2904,11 @@ add_ports(ipfw_insn *cmd, char *av, u_char proto, int opcode)
* Rules are added into the 'rulebuf' and then copied in the correct order
* into the actual rule.
*
- * The syntax for a rule starts with the action, followed by an
- * optional log action, and the various match patterns.
+ * The syntax for a rule starts with the action, followed by
+ * optional action parameters, and the various match patterns.
* In the assembled microcode, the first opcode must be an O_PROBE_STATE
* (generated if the rule includes a keep-state option), then the
- * various match patterns, the "log" action, and the actual action.
+ * various match patterns, log/altq actions, and the actual action.
*
*/
static void
@@ -2783,6 +2931,7 @@ add(int ac, char *av[])
* various flags used to record that we entered some fields.
*/
ipfw_insn *have_state = NULL; /* check-state or keep-state */
+ ipfw_insn *have_log = NULL, *have_altq = NULL;
size_t len;
int i;
@@ -2945,32 +3094,63 @@ add(int ac, char *av[])
action = next_cmd(action);
/*
+ * [altq queuename] -- altq tag, optional
* [log [logamount N]] -- log, optional
*
- * If exists, it goes first in the cmdbuf, but then it is
+ * If they exist, it go first in the cmdbuf, but then it is
* skipped in the copy section to the end of the buffer.
*/
- if (ac && !strncmp(*av, "log", strlen(*av))) {
- ipfw_insn_log *c = (ipfw_insn_log *)cmd;
- int l;
+ while (ac != 0 && (i = match_token(rule_action_params, *av)) != -1) {
+ ac--; av++;
+ switch (i) {
+ case TOK_LOG:
+ {
+ ipfw_insn_log *c = (ipfw_insn_log *)cmd;
+ int l;
- cmd->len = F_INSN_SIZE(ipfw_insn_log);
- cmd->opcode = O_LOG;
- av++; ac--;
- if (ac && !strncmp(*av, "logamount", strlen(*av))) {
- ac--; av++;
- NEED1("logamount requires argument");
- l = atoi(*av);
- if (l < 0)
- errx(EX_DATAERR, "logamount must be positive");
- c->max_log = l;
+ if (have_log)
+ errx(EX_DATAERR,
+ "log cannot be specified more than once");
+ have_log = (ipfw_insn *)c;
+ cmd->len = F_INSN_SIZE(ipfw_insn_log);
+ cmd->opcode = O_LOG;
+ if (ac && !strncmp(*av, "logamount", strlen(*av))) {
+ ac--; av++;
+ NEED1("logamount requires argument");
+ l = atoi(*av);
+ if (l < 0)
+ errx(EX_DATAERR,
+ "logamount must be positive");
+ c->max_log = l;
+ ac--; av++;
+ } else {
+ len = sizeof(c->max_log);
+ if (sysctlbyname("net.inet.ip.fw.verbose_limit",
+ &c->max_log, &len, NULL, 0) == -1)
+ errx(1, "sysctlbyname(\"%s\")",
+ "net.inet.ip.fw.verbose_limit");
+ }
+ }
+ break;
+
+ case TOK_ALTQ:
+ {
+ ipfw_insn_altq *a = (ipfw_insn_altq *)cmd;
+
+ NEED1("missing altq queue name");
+ if (have_altq)
+ errx(EX_DATAERR,
+ "altq cannot be specified more than once");
+ have_altq = (ipfw_insn *)a;
+ cmd->len = F_INSN_SIZE(ipfw_insn_altq);
+ cmd->opcode = O_ALTQ;
+ fill_altq_qid(&a->qid, *av);
ac--; av++;
- } else {
- len = sizeof(c->max_log);
- if (sysctlbyname("net.inet.ip.fw.verbose_limit",
- &c->max_log, &len, NULL, 0) == -1)
- errx(1, "sysctlbyname(\"%s\")",
- "net.inet.ip.fw.verbose_limit");
+ }
+ break;
+
+ default:
+ abort();
}
cmd = next_cmd(cmd);
}
@@ -3533,7 +3713,7 @@ done:
dst = next_cmd(dst);
}
/*
- * copy all commands but O_LOG, O_KEEP_STATE, O_LIMIT
+ * copy all commands but O_LOG, O_KEEP_STATE, O_LIMIT, O_ALTQ
*/
for (src = (ipfw_insn *)cmdbuf; src != cmd; src += i) {
i = F_LEN(src);
@@ -3542,6 +3722,7 @@ done:
case O_LOG:
case O_KEEP_STATE:
case O_LIMIT:
+ case O_ALTQ:
break;
default:
bcopy(src, dst, i * sizeof(uint32_t));
@@ -3563,12 +3744,16 @@ done:
rule->act_ofs = dst - rule->cmd;
/*
- * put back O_LOG if necessary
+ * put back O_LOG, O_ALTQ if necessary
*/
- src = (ipfw_insn *)cmdbuf;
- if (src->opcode == O_LOG) {
- i = F_LEN(src);
- bcopy(src, dst, i * sizeof(uint32_t));
+ if (have_log) {
+ i = F_LEN(have_log);
+ bcopy(have_log, dst, i * sizeof(uint32_t));
+ dst += i;
+ }
+ if (have_altq) {
+ i = F_LEN(have_altq);
+ bcopy(have_altq, dst, i * sizeof(uint32_t));
dst += i;
}
/*
OpenPOWER on IntegriCloud