aboutsummaryrefslogtreecommitdiff
path: root/sbin/ipfw
diff options
context:
space:
mode:
authorBjoern A. Zeeb <bz@FreeBSD.org>2011-08-20 17:05:11 +0000
committerBjoern A. Zeeb <bz@FreeBSD.org>2011-08-20 17:05:11 +0000
commit8a006adb24414b61097ec694cc28fb15de51ac2e (patch)
treef3e27acb3ae7873708cae6d77a7b90d7a8d9e68c /sbin/ipfw
parent90bc35de385e85cb48397df487713be27026cddd (diff)
downloadsrc-8a006adb24414b61097ec694cc28fb15de51ac2e.tar.gz
src-8a006adb24414b61097ec694cc28fb15de51ac2e.zip
Add support for IPv6 to ipfw fwd:
Distinguish IPv4 and IPv6 addresses and optional port numbers in user space to set the option for the correct protocol family. Add support in the kernel for carrying the new IPv6 destination address and port. Add support to TCP and UDP for IPv6 and fix UDP IPv4 to not change the address in the IP header. Add support for IPv6 forwarding to a non-local destination. Add a regession test uitilizing VIMAGE to check all 20 possible combinations I could think of. Obtained from: David Dolson at Sandvine Incorporated (original version for ipfw fwd IPv6 support) Sponsored by: Sandvine Incorporated PR: bin/117214 MFC after: 4 weeks Approved by: re (kib)
Notes
Notes: svn path=/head/; revision=225044
Diffstat (limited to 'sbin/ipfw')
-rw-r--r--sbin/ipfw/ipfw.84
-rw-r--r--sbin/ipfw/ipfw2.c108
2 files changed, 90 insertions, 22 deletions
diff --git a/sbin/ipfw/ipfw.8 b/sbin/ipfw/ipfw.8
index 6fc12c82c132..9d0028736d01 100644
--- a/sbin/ipfw/ipfw.8
+++ b/sbin/ipfw/ipfw.8
@@ -1,7 +1,7 @@
.\"
.\" $FreeBSD$
.\"
-.Dd June 29, 2011
+.Dd August 20, 2011
.Dt IPFW 8
.Os
.Sh NAME
@@ -726,7 +726,7 @@ The search terminates.
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
+For IPv4, the next hop can also be supplied by the last table
looked up for the packet by using the
.Cm tablearg
keyword instead of an explicit address.
diff --git a/sbin/ipfw/ipfw2.c b/sbin/ipfw/ipfw2.c
index e389e99f81ab..615c4d1292d5 100644
--- a/sbin/ipfw/ipfw2.c
+++ b/sbin/ipfw/ipfw2.c
@@ -1111,6 +1111,18 @@ show_ipfw(struct ip_fw *rule, int pcwidth, int bcwidth)
}
break;
+ case O_FORWARD_IP6:
+ {
+ char buf[4 + INET6_ADDRSTRLEN + 1];
+ ipfw_insn_sa6 *s = (ipfw_insn_sa6 *)cmd;
+
+ printf("fwd %s", inet_ntop(AF_INET6, &s->sa.sin6_addr,
+ buf, sizeof(buf)));
+ if (s->sa.sin6_port)
+ printf(",%d", s->sa.sin6_port);
+ }
+ break;
+
case O_LOG: /* O_LOG is printed last */
logptr = (ipfw_insn_log *)cmd;
break;
@@ -2809,40 +2821,96 @@ chkarg:
break;
case TOK_FORWARD: {
- ipfw_insn_sa *p = (ipfw_insn_sa *)action;
+ /*
+ * Locate the address-port separator (':' or ',').
+ * Could be one of the following:
+ * hostname:port
+ * IPv4 a.b.c.d,port
+ * IPv4 a.b.c.d:port
+ * IPv6 w:x:y::z,port
+ * The ':' can only be used with hostname and IPv4 address.
+ * XXX-BZ Should we also support [w:x:y::z]:port?
+ */
+ struct sockaddr_storage result;
+ struct addrinfo *res;
char *s, *end;
+ int family;
+ u_short port_number;
NEED1("missing forward address[:port]");
- action->opcode = O_FORWARD_IP;
- action->len = F_INSN_SIZE(ipfw_insn_sa);
-
- /*
- * In the kernel we assume AF_INET and use only
- * sin_port and sin_addr. Remember to set sin_len as
- * the routing code seems to use it too.
- */
- p->sa.sin_family = AF_INET;
- p->sa.sin_len = sizeof(struct sockaddr_in);
- p->sa.sin_port = 0;
/*
* locate the address-port separator (':' or ',')
*/
- s = strchr(*av, ':');
- if (s == NULL)
- s = strchr(*av, ',');
+ s = strchr(*av, ',');
+ if (s == NULL) {
+ /* Distinguish between IPv4:port and IPv6 cases. */
+ s = strchr(*av, ':');
+ if (s && strchr(s+1, ':'))
+ s = NULL; /* no port */
+ }
+
+ port_number = 0;
if (s != NULL) {
+ /* Terminate host portion and set s to start of port. */
*(s++) = '\0';
i = strtoport(s, &end, 0 /* base */, 0 /* proto */);
if (s == end)
errx(EX_DATAERR,
"illegal forwarding port ``%s''", s);
- p->sa.sin_port = (u_short)i;
+ port_number = (u_short)i;
+ }
+
+ if (_substrcmp(*av, "tablearg") == 0) {
+ family = PF_INET;
+ ((struct sockaddr_in*)&result)->sin_addr.s_addr =
+ INADDR_ANY;
+ } else {
+ /*
+ * Resolve the host name or address to a family and a
+ * network representation of the addres.
+ */
+ if (getaddrinfo(*av, NULL, NULL, &res))
+ errx(EX_DATAERR, NULL);
+ /* Just use the first host in the answer. */
+ family = res->ai_family;
+ memcpy(&result, res->ai_addr, res->ai_addrlen);
+ freeaddrinfo(res);
+ }
+
+ if (family == PF_INET) {
+ ipfw_insn_sa *p = (ipfw_insn_sa *)action;
+
+ action->opcode = O_FORWARD_IP;
+ action->len = F_INSN_SIZE(ipfw_insn_sa);
+
+ /*
+ * In the kernel we assume AF_INET and use only
+ * sin_port and sin_addr. Remember to set sin_len as
+ * the routing code seems to use it too.
+ */
+ p->sa.sin_len = sizeof(struct sockaddr_in);
+ p->sa.sin_family = AF_INET;
+ p->sa.sin_port = port_number;
+ p->sa.sin_addr.s_addr =
+ ((struct sockaddr_in *)&result)->sin_addr.s_addr;
+ } else if (family == PF_INET6) {
+ ipfw_insn_sa6 *p = (ipfw_insn_sa6 *)action;
+
+ action->opcode = O_FORWARD_IP6;
+ action->len = F_INSN_SIZE(ipfw_insn_sa6);
+
+ p->sa.sin6_len = sizeof(struct sockaddr_in6);
+ p->sa.sin6_family = AF_INET6;
+ p->sa.sin6_port = port_number;
+ p->sa.sin6_flowinfo = 0;
+ p->sa.sin6_scope_id = 0;
+ /* No table support for v6 yet. */
+ bcopy(&((struct sockaddr_in6*)&result)->sin6_addr,
+ &p->sa.sin6_addr, sizeof(p->sa.sin6_addr));
+ } else {
+ errx(EX_DATAERR, "Invalid address family in forward action");
}
- if (_substrcmp(*av, "tablearg") == 0)
- p->sa.sin_addr.s_addr = INADDR_ANY;
- else
- lookup_host(*av, &(p->sa.sin_addr));
av++;
break;
}