diff options
-rw-r--r-- | sbin/ipfw/Makefile | 1 | ||||
-rw-r--r-- | sbin/ipfw/ipfw.8 | 43 | ||||
-rw-r--r-- | sbin/ipfw/ipfw2.c | 247 | ||||
-rw-r--r-- | sys/netinet/ip_fw.h | 10 | ||||
-rw-r--r-- | sys/netinet/ip_fw2.c | 44 |
5 files changed, 312 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; } /* diff --git a/sys/netinet/ip_fw.h b/sys/netinet/ip_fw.h index ac903f5..943b505 100644 --- a/sys/netinet/ip_fw.h +++ b/sys/netinet/ip_fw.h @@ -134,6 +134,7 @@ enum ipfw_opcodes { /* arguments (4 byte each) */ O_IP_DST_LOOKUP, /* arg1=table number, u32=value */ O_ANTISPOOF, /* none */ O_JAIL, /* u32 = id */ + O_ALTQ, /* u32 = altq classif. qid */ O_LAST_OPCODE /* not an opcode! */ }; @@ -251,6 +252,14 @@ typedef struct _ipfw_insn_pipe { } ipfw_insn_pipe; /* + * This is used for storing an altq queue id number. + */ +typedef struct _ipfw_insn_altq { + ipfw_insn o; + u_int32_t qid; +} ipfw_insn_altq; + +/* * This is used for limit rules. */ typedef struct _ipfw_insn_limit { @@ -293,6 +302,7 @@ typedef struct _ipfw_insn_log { * first instruction (at r->cmd) MUST BE an O_PROBE_STATE * + if a rule has a "log" option, then the first action * (at ACTION_PTR(r)) MUST be O_LOG + * + if a rule has an "altq" option, it comes after "log" * * NOTE: we use a simple linked list of rules because we never need * to delete a rule without scanning the list. We do not use diff --git a/sys/netinet/ip_fw2.c b/sys/netinet/ip_fw2.c index 2d8197f..9108d7b 100644 --- a/sys/netinet/ip_fw2.c +++ b/sys/netinet/ip_fw2.c @@ -77,6 +77,7 @@ #include <netinet/tcpip.h> #include <netinet/udp.h> #include <netinet/udp_var.h> +#include <altq/if_altq.h> #ifdef IPSEC #include <netinet6/ipsec.h> @@ -553,6 +554,13 @@ ipfw_log(struct ip_fw *f, u_int hlen, struct ether_header *eh, if (l->log_left == 0) limit_reached = l->max_log; cmd += F_LEN(cmd); /* point to first action */ + if (cmd->opcode == O_ALTQ) { + ipfw_insn_altq *altq = (ipfw_insn_altq *)cmd; + + snprintf(SNPARGS(action2, 0), "Altq %d", + altq->qid); + cmd += F_LEN(cmd); + } if (cmd->opcode == O_PROB) cmd += F_LEN(cmd); @@ -1324,6 +1332,8 @@ lookup_next_rule(struct ip_fw *me) cmd = ACTION_PTR(me); if (cmd->opcode == O_LOG) cmd += F_LEN(cmd); + if (cmd->opcode == O_ALTQ) + cmd += F_LEN(cmd); if ( cmd->opcode == O_SKIPTO ) for (rule = me->next; rule ; rule = rule->next) if (rule->rulenum >= cmd->arg1) @@ -2212,6 +2222,32 @@ check_body: (TH_RST | TH_ACK | TH_SYN)) != TH_SYN); break; + case O_ALTQ: { + struct altq_tag *at; + ipfw_insn_altq *altq = (ipfw_insn_altq *)cmd; + + match = 1; + mtag = m_tag_get(PACKET_TAG_PF_QID, + sizeof(struct altq_tag), + M_NOWAIT); + if (mtag == NULL) { + /* + * Let the packet fall back to the + * default ALTQ. + */ + break; + } + at = (struct altq_tag *)(mtag+1); + at->qid = altq->qid; + if (hlen != 0) + at->af = AF_INET; + else + at->af = AF_LINK; + at->hdr = ip; + m_tag_prepend(m, mtag); + break; + } + case O_LOG: if (fw_verbose) ipfw_log(f, hlen, args->eh, m, oif); @@ -2275,6 +2311,9 @@ check_body: * or to the SKIPTO target ('goto again' after * having set f, cmd and l), respectively. * + * O_LOG and O_ALTQ action parameters: + * perform some action and set match = 1; + * * O_LIMIT and O_KEEP_STATE: these opcodes are * not real 'actions', and are stored right * before the 'action' part of the rule. @@ -2974,6 +3013,11 @@ check_ipfw_struct(struct ip_fw *rule, int size) goto bad_size; break; + case O_ALTQ: + if (cmdlen != F_INSN_SIZE(ipfw_insn_altq)) + goto bad_size; + break; + case O_PIPE: case O_QUEUE: if (cmdlen != F_INSN_SIZE(ipfw_insn_pipe)) |