summaryrefslogtreecommitdiffstats
path: root/sbin/ipfw/ipfw2.c
diff options
context:
space:
mode:
authorbz <bz@FreeBSD.org>2011-08-20 17:05:11 +0000
committerbz <bz@FreeBSD.org>2011-08-20 17:05:11 +0000
commiteccbdd061bd53b771dcd87b1708dfbc43cfb72a7 (patch)
treef3e27acb3ae7873708cae6d77a7b90d7a8d9e68c /sbin/ipfw/ipfw2.c
parentfa01a4aee0c4b294791a3b7515a6776750af5f04 (diff)
downloadFreeBSD-src-eccbdd061bd53b771dcd87b1708dfbc43cfb72a7.zip
FreeBSD-src-eccbdd061bd53b771dcd87b1708dfbc43cfb72a7.tar.gz
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)
Diffstat (limited to 'sbin/ipfw/ipfw2.c')
-rw-r--r--sbin/ipfw/ipfw2.c108
1 files changed, 88 insertions, 20 deletions
diff --git a/sbin/ipfw/ipfw2.c b/sbin/ipfw/ipfw2.c
index e389e99..615c4d1 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;
}
OpenPOWER on IntegriCloud