diff --git a/sbin/ipfw/ipfw.8 b/sbin/ipfw/ipfw.8 index 3c7aac47a063..63215897b9bd 100644 --- a/sbin/ipfw/ipfw.8 +++ b/sbin/ipfw/ipfw.8 @@ -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 diff --git a/sbin/ipfw/ipfw2.c b/sbin/ipfw/ipfw2.c index f5d8ed604f06..a486fbe25104 100644 --- a/sbin/ipfw/ipfw2.c +++ b/sbin/ipfw/ipfw2.c @@ -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); diff --git a/sys/netinet/ip_fw.h b/sys/netinet/ip_fw.h index 14ca1d5b1f68..086ce8eef31e 100644 --- a/sys/netinet/ip_fw.h +++ b/sys/netinet/ip_fw.h @@ -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 */ + }; /* diff --git a/sys/netinet/ip_fw2.c b/sys/netinet/ip_fw2.c index e9b24df128f4..47fc7f6988fb 100644 --- a/sys/netinet/ip_fw2.c +++ b/sys/netinet/ip_fw2.c @@ -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: