Index: head/sbin/ipfw/ipfw.8 =================================================================== --- head/sbin/ipfw/ipfw.8 (revision 279587) +++ head/sbin/ipfw/ipfw.8 (working copy) @@ -2078,6 +2078,8 @@ hook number to move packet to. maximum number of connections. .It Cm ipv4 IPv4 nexthop to fwd packets to. +.It Cm ipv6 +IPv6 nexthop to fwd packets to. .El .Pp The Index: head/sbin/ipfw/tables.c =================================================================== --- head/sbin/ipfw/tables.c (revision 279587) +++ head/sbin/ipfw/tables.c (working copy) @@ -35,6 +35,7 @@ #include #include #include +#include #include "ipfw2.h" @@ -1384,6 +1385,7 @@ static void tentry_fill_value(ipfw_obj_header *oh, ipfw_obj_tentry *tent, char *arg, uint8_t type, uint32_t vmask) { + struct addrinfo hints, *res; uint32_t a4, flag, val, vm; ipfw_table_value *v; uint32_t i; @@ -1494,9 +1496,18 @@ tentry_fill_value(ipfw_obj_header *oh, ipfw_obj_te } break; case IPFW_VTYPE_NH6: - if (strchr(n, ':') != NULL && - inet_pton(AF_INET6, n, &v->nh6) == 1) - break; + if (strchr(n, ':') != NULL) { + memset(&hints, 0, sizeof(hints)); + hints.ai_family = AF_INET6; + hints.ai_flags = AI_NUMERICHOST; + if (getaddrinfo(n, NULL, &hints, &res) != 0) + break; + v->nh6 = ((struct sockaddr_in6 *) + res->ai_addr)->sin6_addr; + v->zoneid = ((struct sockaddr_in6 *) + res->ai_addr)->sin6_scope_id; + freeaddrinfo(res); + } etype = "ipv6"; break; } @@ -1643,10 +1654,11 @@ static void table_show_value(char *buf, size_t bufsize, ipfw_table_value *v, uint32_t vmask, int print_ip) { + char abuf[INET6_ADDRSTRLEN + IF_NAMESIZE + 2]; + struct sockaddr_in6 sa6; uint32_t flag, i, l; size_t sz; struct in_addr a4; - char abuf[INET6_ADDRSTRLEN]; sz = bufsize; @@ -1702,8 +1714,15 @@ table_show_value(char *buf, size_t bufsize, ipfw_t l = snprintf(buf, sz, "%d,", v->dscp); break; case IPFW_VTYPE_NH6: - inet_ntop(AF_INET6, &v->nh6, abuf, sizeof(abuf)); - l = snprintf(buf, sz, "%s,", abuf); + sa6.sin6_family = AF_INET6; + sa6.sin6_len = sizeof(sa6); + sa6.sin6_addr = v->nh6; + sa6.sin6_port = 0; + sa6.sin6_scope_id = v->zoneid; + if (getnameinfo((const struct sockaddr *)&sa6, + sa6.sin6_len, abuf, sizeof(abuf), NULL, 0, + NI_NUMERICHOST) == 0) + l = snprintf(buf, sz, "%s,", abuf); break; } @@ -1862,11 +1881,12 @@ struct _table_value { uint32_t nat; /* O_NAT */ uint32_t nh4; uint8_t dscp; - uint8_t spare0[3]; + uint8_t spare0; + uint16_t spare1; /* -- 32 bytes -- */ struct in6_addr nh6; uint32_t limit; /* O_LIMIT */ - uint32_t spare1; + uint32_t zoneid; uint64_t refcnt; /* Number of references */ }; Index: head/sys/netinet/ip_fw.h =================================================================== --- head/sys/netinet/ip_fw.h (revision 279587) +++ head/sys/netinet/ip_fw.h (working copy) @@ -721,7 +721,7 @@ struct _ipfw_dyn_rule { #define IPFW_VTYPE_TAG 0x00000020 /* tag/untag */ #define IPFW_VTYPE_DIVERT 0x00000040 /* divert/tee */ #define IPFW_VTYPE_NETGRAPH 0x00000080 /* netgraph/ngtee */ -#define IPFW_VTYPE_LIMIT 0x00000100 /* IPv6 nexthop */ +#define IPFW_VTYPE_LIMIT 0x00000100 /* limit */ #define IPFW_VTYPE_NH4 0x00000200 /* IPv4 nexthop */ #define IPFW_VTYPE_NH6 0x00000400 /* IPv6 nexthop */ @@ -817,10 +817,11 @@ typedef struct _ipfw_table_value { uint32_t nat; /* O_NAT */ uint32_t nh4; uint8_t dscp; - uint8_t spare0[3]; + uint8_t spare0; + uint16_t spare1; struct in6_addr nh6; uint32_t limit; /* O_LIMIT */ - uint32_t spare1; + uint32_t zoneid; /* scope zone id for nh6 */ uint64_t reserved; } ipfw_table_value; Index: head/sys/netpfil/ipfw/ip_fw2.c =================================================================== --- head/sys/netpfil/ipfw/ip_fw2.c (revision 279587) +++ head/sys/netpfil/ipfw/ip_fw2.c (working copy) @@ -2387,13 +2387,41 @@ do { \ if (q == NULL || q->rule != f || dyn_dir == MATCH_FORWARD) { struct sockaddr_in *sa; + sa = &(((ipfw_insn_sa *)cmd)->sa); 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; +#ifdef INET6 + /* + * We use O_FORWARD_IP opcode for + * fwd rule with tablearg, but tables + * now support IPv6 address. And when + * packet that we are inspecting is + * IPv6 packet, we can use nh6 from + * table value as IPv6 address for + * forwarding. + */ + if (is_ipv6) { + struct sockaddr_in6 *sa6; + + sa6 = args->next_hop6 = + &args->hopstore6; + sa6->sin6_family = AF_INET6; + sa6->sin6_len = sizeof(*sa6); + sa6->sin6_addr = TARG_VAL( + chain, tablearg, nh6); + sa6->sin6_scope_id = TARG_VAL( + chain, tablearg, zoneid); + } else +#endif + { + sa = args->next_hop = + &args->hopstore; + sa->sin_family = AF_INET; + sa->sin_len = sizeof(*sa); + sa->sin_addr.s_addr = htonl( + TARG_VAL(chain, tablearg, + nh4)); + } } else { args->next_hop = sa; } Index: head/sys/netpfil/ipfw/ip_fw_private.h =================================================================== --- head/sys/netpfil/ipfw/ip_fw_private.h (revision 279587) +++ head/sys/netpfil/ipfw/ip_fw_private.h (working copy) @@ -102,7 +102,10 @@ 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 */ + union { /* store here if cannot use a pointer */ + struct sockaddr_in hopstore; + struct sockaddr_in6 hopstore6; + }; }; MALLOC_DECLARE(M_IPFW); @@ -294,11 +297,12 @@ struct table_value { uint32_t nat; /* O_NAT */ uint32_t nh4; uint8_t dscp; - uint8_t spare0[3]; + uint8_t spare0; + uint16_t spare1; /* -- 32 bytes -- */ struct in6_addr nh6; uint32_t limit; /* O_LIMIT */ - uint32_t spare1; + uint32_t zoneid; /* scope zone id for nh6 */ uint64_t refcnt; /* Number of references */ }; Index: head/sys/netpfil/ipfw/ip_fw_table_value.c =================================================================== --- head/sys/netpfil/ipfw/ip_fw_table_value.c (revision 279587) +++ head/sys/netpfil/ipfw/ip_fw_table_value.c (working copy) @@ -117,6 +117,7 @@ mask_table_value(struct table_value *src, struct t _MCPY(dscp, IPFW_VTYPE_DSCP); _MCPY(nh4, IPFW_VTYPE_NH4); _MCPY(nh6, IPFW_VTYPE_NH6); + _MCPY(zoneid, IPFW_VTYPE_NH6); #undef _MCPY } @@ -666,6 +667,7 @@ ipfw_import_table_value_v1(ipfw_table_value *iv) v.nh4 = iv->nh4; v.nh6 = iv->nh6; v.limit = iv->limit; + v.zoneid = iv->zoneid; memcpy(iv, &v, sizeof(ipfw_table_value)); } @@ -691,6 +693,7 @@ ipfw_export_table_value_v1(struct table_value *v, iv.limit = v->limit; iv.nh4 = v->nh4; iv.nh6 = v->nh6; + iv.zoneid = v->zoneid; memcpy(piv, &iv, sizeof(iv)); } Index: head/sys/netinet6/ip6_output.c =================================================================== --- head/sys/netinet6/ip6_output.c (revision 279588) +++ head/sys/netinet6/ip6_output.c (working copy) @@ -845,6 +845,8 @@ again: (fwd_tag = m_tag_find(m, PACKET_TAG_IPFORWARD, NULL)) != NULL) { dst = (struct sockaddr_in6 *)&ro->ro_dst; bcopy((fwd_tag+1), &dst_sa, sizeof(struct sockaddr_in6)); + if (sa6_embedscope(&dst_sa, V_ip6_use_defzone) != 0) + goto badscope; m->m_flags |= M_SKIP_FIREWALL; m->m_flags &= ~M_IP6_NEXTHOP; m_tag_delete(m, fwd_tag);