Index: sbin/ipfw/ipfw.8 =================================================================== --- sbin/ipfw/ipfw.8 +++ sbin/ipfw/ipfw.8 @@ -2078,6 +2078,8 @@ 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: sbin/ipfw/tables.c =================================================================== --- sbin/ipfw/tables.c +++ sbin/ipfw/tables.c @@ -35,6 +35,7 @@ #include #include #include +#include #include "ipfw2.h" @@ -1384,6 +1385,7 @@ 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,19 @@ } 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) { + v->nh6 = ((struct sockaddr_in6 *) + res->ai_addr)->sin6_addr; + v->zoneid = ((struct sockaddr_in6 *) + res->ai_addr)->sin6_scope_id; + freeaddrinfo(res); + break; + } + } etype = "ipv6"; break; } @@ -1643,10 +1655,11 @@ 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 +1715,15 @@ 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 +1882,12 @@ 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: sys/netinet/ip_fw.h =================================================================== --- sys/netinet/ip_fw.h +++ sys/netinet/ip_fw.h @@ -721,7 +721,7 @@ #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 @@ 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: sys/netpfil/ipfw/ip_fw2.c =================================================================== --- sys/netpfil/ipfw/ip_fw2.c +++ sys/netpfil/ipfw/ip_fw2.c @@ -2387,13 +2387,41 @@ 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: sys/netpfil/ipfw/ip_fw_pfil.c =================================================================== --- sys/netpfil/ipfw/ip_fw_pfil.c +++ sys/netpfil/ipfw/ip_fw_pfil.c @@ -59,6 +59,7 @@ #ifdef INET6 #include #include +#include #endif #include @@ -197,8 +198,20 @@ } #ifdef INET6 if (args.next_hop6 != NULL) { - bcopy(args.next_hop6, (fwd_tag+1), len); - if (in6_localip(&args.next_hop6->sin6_addr)) + struct sockaddr_in6 *sa6; + + sa6 = (struct sockaddr_in6 *)(fwd_tag + 1); + bcopy(args.next_hop6, sa6, len); + /* + * If nh6 address is link-local we should convert + * it to kernel internal form before doing any + * comparisons. + */ + if (sa6_embedscope(sa6, V_ip6_use_defzone) != 0) { + ret = EACCES; + break; + } + if (in6_localip(&sa6->sin6_addr)) (*m0)->m_flags |= M_FASTFWD_OURS; (*m0)->m_flags |= M_IP6_NEXTHOP; } Index: sys/netpfil/ipfw/ip_fw_private.h =================================================================== --- sys/netpfil/ipfw/ip_fw_private.h +++ sys/netpfil/ipfw/ip_fw_private.h @@ -102,7 +102,10 @@ 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 @@ 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: sys/netpfil/ipfw/ip_fw_table_value.c =================================================================== --- sys/netpfil/ipfw/ip_fw_table_value.c +++ sys/netpfil/ipfw/ip_fw_table_value.c @@ -117,6 +117,7 @@ _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 @@ 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 @@ iv.limit = v->limit; iv.nh4 = v->nh4; iv.nh6 = v->nh6; + iv.zoneid = v->zoneid; memcpy(piv, &iv, sizeof(iv)); }