Allow ipfw to forward to a destination that is specified by a table.

for example:
  fwd tablearg ip from any to table(1)
where table 1 has entries of the form:
1.1.1.0/24 10.2.3.4
208.23.2.0/24 router2

This allows trivial implementation of a secondary routing table implemented
in the firewall layer.

I expect more work (under discussion with Glebius) to follow this to clean
up some of the messy parts of ipfw related to tables.

Reviewed by:	Glebius
MFC after:	1 month
This commit is contained in:
Julian Elischer 2006-08-17 22:49:50 +00:00
parent 5657c870dc
commit c487be961a
4 changed files with 85 additions and 18 deletions

View File

@ -684,10 +684,14 @@ Divert packets that match this rule to the
socket bound to port
.Ar port .
The search terminates.
.It Cm fwd | forward Ar ipaddr Ns Op , Ns Ar port
.It Cm fwd | forward Ar ipaddr | tablearg Ns Op , Ns Ar port
Change the next-hop on matching packets to
.Ar ipaddr ,
which can be an IP address or a host name.
The next hop can also be supplied by the last table
looked up for the packet by using the
.Em tablearg
keyword instead of an explicit address.
The search terminates if this rule matches.
.Pp
If
@ -1584,11 +1588,14 @@ This can significantly reduce number of rules in some configurations.
The
.Cm tablearg
argument can be used with the following actions:
.Cm pipe , queue, divert, tee, netgraph, ngtee,
.Cm pipe , queue, divert, tee, netgraph, ngtee, fwd
action parameters:
.Cm tag, untag,
rule options:
.Cm limit, tagged.
.Pp
When used with 'fwd' it is possible to supply table entries with values
that are in the form of IP addresses or hostnames.
See the
.Sx EXAMPLES
Section for example usage of tables and the tablearg keyword.
@ -2380,6 +2387,13 @@ Then we classify traffic using a single rule:
.Dl "ipfw table 1 add 192.168.0.2 1"
.Dl "..."
.Dl "ipfw pipe tablearg ip from table(1) to any"
.Pp
Using the fwd action, the table entries may include hostnames and IP addresses.
.Pp
.Dl "ipfw table 1 add 192.168.2.0/24 10.23.2.1"
.Dl "ipfw table 1 add 192.168.0.0/27 router1.dmz"
.Dl "..."
.Dl "ipfw add 100 fwd tablearg ip from any to table(1)"
.Ss SETS OF RULES
To add a set of rules atomically, e.g.\& set 18:
.Pp

View File

@ -1545,7 +1545,11 @@ show_ipfw(struct ip_fw *rule, int pcwidth, int bcwidth)
{
ipfw_insn_sa *s = (ipfw_insn_sa *)cmd;
printf("fwd %s", inet_ntoa(s->sa.sin_addr));
if (s->sa.sin_addr.s_addr == INADDR_ANY) {
printf("fwd tablearg");
} else {
printf("fwd %s", inet_ntoa(s->sa.sin_addr));
}
if (s->sa.sin_port)
printf(",%d", s->sa.sin_port);
}
@ -4030,11 +4034,14 @@ add(int ac, char *av[])
"illegal forwarding port ``%s''", s);
p->sa.sin_port = (u_short)i;
}
lookup_host(*av, &(p->sa.sin_addr));
if (_substrcmp(*av, "tablearg") == 0) {
p->sa.sin_addr.s_addr = INADDR_ANY; /* htonl not needed */
} else {
lookup_host(*av, &(p->sa.sin_addr));
}
ac--; av++;
break;
}
case TOK_COMMENT:
/* pretend it is a 'count' rule followed by the comment */
action->opcode = O_COUNT;
@ -4943,9 +4950,21 @@ table_handler(int ac, char *av[])
if (lookup_host(*av, (struct in_addr *)&ent.addr) != 0)
errx(EX_NOHOST, "hostname ``%s'' unknown", *av);
ac--; av++;
if (do_add && ac)
ent.value = strtoul(*av, NULL, 0);
else
if (do_add && ac) {
unsigned int tval;
/* isdigit is a bit of a hack here.. */
if (strchr(*av, (int)'.') == NULL && isdigit(**av)) {
ent.value = strtoul(*av, NULL, 0);
} else {
if (lookup_host(*av, (struct in_addr *)&tval) == 0) {
/* The value must be stored in host order *
* so that the values < 65k can be distinguished */
ent.value = ntohl(tval);
} else {
errx(EX_NOHOST, "hostname ``%s'' unknown", *av);
}
}
} else
ent.value = 0;
if (do_cmd(do_add ? IP_FW_TABLE_ADD : IP_FW_TABLE_DEL,
&ent, sizeof(ent)) < 0) {
@ -4978,9 +4997,25 @@ table_handler(int ac, char *av[])
if (do_cmd(IP_FW_TABLE_LIST, tbl, (uintptr_t)&l) < 0)
err(EX_OSERR, "getsockopt(IP_FW_TABLE_LIST)");
for (a = 0; a < tbl->cnt; a++) {
printf("%s/%u %u\n",
inet_ntoa(*(struct in_addr *)&tbl->ent[a].addr),
tbl->ent[a].masklen, tbl->ent[a].value);
/* Heuristic to print it the right way */
/* valuse < 64k are printed as numbers */
unsigned int tval;
tval = tbl->ent[a].value;
if (tval > 0xffff) {
char tbuf[128];
strncpy(tbuf,
inet_ntoa(*(struct in_addr *)
&tbl->ent[a].addr), 127);
/* inet_ntoa expects host order */
tval = htonl(tval);
printf("%s/%u %s\n",
tbuf, tbl->ent[a].masklen,
inet_ntoa(*(struct in_addr *)&tval));
} else {
printf("%s/%u %u\n",
inet_ntoa(*(struct in_addr *)&tbl->ent[a].addr),
tbl->ent[a].masklen, tbl->ent[a].value);
}
}
} else
errx(EX_USAGE, "invalid table command %s", *av);

View File

@ -521,6 +521,8 @@ struct ip_fw_args {
struct inpcb *inp;
struct _ip6dn_args dummypar; /* dummynet->ip6_output */
struct sockaddr_in hopstore; /* store here if cannot use a pointer */
};
/*

View File

@ -738,7 +738,7 @@ static u_int64_t norule_counter; /* counter for ipfw_log(NULL...) */
*/
static void
ipfw_log(struct ip_fw *f, u_int hlen, struct ip_fw_args *args,
struct mbuf *m, struct ifnet *oif, u_short offset)
struct mbuf *m, struct ifnet *oif, u_short offset, uint32_t tablearg)
{
struct ether_header *eh = args->eh;
char *action;
@ -831,9 +831,15 @@ ipfw_log(struct ip_fw *f, u_int hlen, struct ip_fw_args *args,
case O_FORWARD_IP: {
ipfw_insn_sa *sa = (ipfw_insn_sa *)cmd;
int len;
struct in_addr dummyaddr;
if (sa->sa.sin_addr.s_addr == INADDR_ANY)
dummyaddr.s_addr = htonl(tablearg);
else
dummyaddr.s_addr = sa->sa.sin_addr.s_addr;
len = snprintf(SNPARGS(action2, 0), "Forward to %s",
inet_ntoa(sa->sa.sin_addr));
inet_ntoa(dummyaddr));
if (sa->sa.sin_port)
snprintf(SNPARGS(action2, len), ":%d",
sa->sa.sin_port);
@ -2785,7 +2791,7 @@ do { \
case O_LOG:
if (fw_verbose)
ipfw_log(f, hlen, args, m, oif, offset);
ipfw_log(f, hlen, args, m, oif, offset, tablearg);
match = 1;
break;
@ -3141,13 +3147,23 @@ do { \
retval = IP_FW_DENY;
goto done;
case O_FORWARD_IP:
case O_FORWARD_IP: {
struct sockaddr_in *sa;
sa = &(((ipfw_insn_sa *)cmd)->sa);
if (args->eh) /* not valid on layer2 pkts */
break;
if (!q || dyn_dir == MATCH_FORWARD)
args->next_hop =
&((ipfw_insn_sa *)cmd)->sa;
if (!q || dyn_dir == MATCH_FORWARD) {
if (sa->sin_addr.s_addr == INADDR_ANY) {
bcopy(sa, &args->hopstore,
sizeof(*sa));
args->hopstore.sin_addr.s_addr = htonl(tablearg);
args->next_hop = &args->hopstore;
} else {
args->next_hop = sa;
}
}
retval = IP_FW_PASS;
}
goto done;
case O_NETGRAPH: