diff options
author | Ole Troan <otroan@employees.org> | 2023-10-12 18:54:55 +0200 |
---|---|---|
committer | Ole Tr�an <otroan@employees.org> | 2023-10-16 10:59:22 +0000 |
commit | ff344a98afd2057cd0df312a9d7277a95853fd0a (patch) | |
tree | 08243d8cf38c29e6d14610aa5169adeab2575471 /src | |
parent | 1fe132ec1a33edf89503140aff0a26ed73542296 (diff) |
npt66: icmp6 alg to handle icmp6 error messages
Support rewriting the inner packet for ICMP6 error messages.
Type: feature
Change-Id: I7e11f53626037075a23310f1cb7e673b0cb52843
Signed-off-by: Ole Troan <otroan@employees.org>
Diffstat (limited to 'src')
-rw-r--r-- | src/plugins/npt66/npt66_node.c | 77 |
1 files changed, 76 insertions, 1 deletions
diff --git a/src/plugins/npt66/npt66_node.c b/src/plugins/npt66/npt66_node.c index ebe33593700..f74f9143998 100644 --- a/src/plugins/npt66/npt66_node.c +++ b/src/plugins/npt66/npt66_node.c @@ -159,6 +159,69 @@ done: return rv; } +static int +npt66_icmp6_translate (vlib_buffer_t *b, ip6_header_t *outer_ip, + icmp46_header_t *icmp, npt66_binding_t *binding, + int dir) +{ + ip6_header_t *ip = (ip6_header_t *) (icmp + 2); + int rv = 0; + vlib_main_t *vm = vlib_get_main (); + + if (clib_net_to_host_u16 (outer_ip->payload_length) < + sizeof (icmp46_header_t) + 4 + sizeof (ip6_header_t)) + { + clib_warning ("ICMP6 payload too short"); + return -1; + } + + // Validate checksums + int bogus_length; + u16 sum16; + sum16 = ip6_tcp_udp_icmp_compute_checksum (vm, b, outer_ip, &bogus_length); + if (sum16 != 0 && sum16 != 0xffff) + { + clib_warning ("ICMP6 checksum failed"); + return -1; + } + if (dir == VLIB_RX) + { + if (!ip6_prefix_cmp (ip->src_address, binding->external, + binding->external_plen)) + { + clib_warning ( + "npt66_icmp6_translate: src address is not internal (%U -> %U)", + format_ip6_address, &ip->src_address, format_ip6_address, + &ip->dst_address); + goto done; + } + ip->src_address = ip6_prefix_copy (ip->src_address, binding->internal, + binding->internal_plen); + /* Checksum neutrality */ + rv = npt66_adjust_checksum (binding->internal_plen, true, binding->delta, + &ip->src_address); + } + else + { + if (!ip6_prefix_cmp (ip->dst_address, binding->external, + binding->external_plen)) + { + clib_warning ( + "npt66_icmp6_translate: dst address is not external (%U -> %U)", + format_ip6_address, &ip->src_address, format_ip6_address, + &ip->dst_address); + goto done; + } + ip->dst_address = ip6_prefix_copy (ip->dst_address, binding->internal, + binding->internal_plen); + rv = npt66_adjust_checksum (binding->internal_plen, false, + binding->delta, &ip->dst_address); + } +done: + + return rv; +} + /* * Lookup the packet tuple in the flow cache, given the lookup mask. * If a binding is found, rewrite the packet according to instructions, @@ -194,8 +257,20 @@ npt66_node_inline (vlib_main_t *vm, vlib_node_runtime_t *node, /* By default pass packet to next node in the feature chain */ vnet_feature_next_u16 (next, b[0]); + int rv; + icmp46_header_t *icmp = (icmp46_header_t *) (ip + 1); + if (ip->protocol == IP_PROTOCOL_ICMP6 && icmp->type < 128) + { + rv = npt66_icmp6_translate (b[0], ip, icmp, binding, dir); + if (rv < 0) + { + clib_warning ("ICMP6 npt66_translate failed"); + *next = NPT66_NEXT_DROP; + goto next; + } + } + rv = npt66_translate (ip, binding, dir); - int rv = npt66_translate (ip, binding, dir); if (rv < 0) { vlib_node_increment_counter (vm, node->node_index, |