summaryrefslogtreecommitdiffstats
path: root/src/plugins/nat/pnat
diff options
context:
space:
mode:
Diffstat (limited to 'src/plugins/nat/pnat')
-rw-r--r--src/plugins/nat/pnat/pnat.api32
-rw-r--r--src/plugins/nat/pnat/pnat.c36
-rw-r--r--src/plugins/nat/pnat/pnat.h23
-rw-r--r--src/plugins/nat/pnat/pnat_cli.c87
-rw-r--r--src/plugins/nat/pnat/pnat_node.c6
-rw-r--r--src/plugins/nat/pnat/pnat_node.h47
-rw-r--r--src/plugins/nat/pnat/tests/missing_rule.def8
-rw-r--r--src/plugins/nat/pnat/tests/packets.def69
-rw-r--r--src/plugins/nat/pnat/tests/pnat_test.c (renamed from src/plugins/nat/pnat/pnat_test.c)323
-rw-r--r--src/plugins/nat/pnat/tests/pnat_test_stubs.h (renamed from src/plugins/nat/pnat/pnat_test_stubs.h)25
-rwxr-xr-xsrc/plugins/nat/pnat/tests/test_genpackets.py38
-rw-r--r--src/plugins/nat/pnat/tests/test_packets.h106
12 files changed, 640 insertions, 160 deletions
diff --git a/src/plugins/nat/pnat/pnat.api b/src/plugins/nat/pnat/pnat.api
index c18c89445e9..fe7bf98b00b 100644
--- a/src/plugins/nat/pnat/pnat.api
+++ b/src/plugins/nat/pnat/pnat.api
@@ -13,7 +13,7 @@
* limitations under the License.
*/
-option version = "0.0.1";
+option version = "0.1.1";
import "vnet/interface_types.api";
import "vnet/ip/ip_types.api";
@@ -24,6 +24,8 @@ enum pnat_mask
PNAT_DA = 0x2,
PNAT_SPORT = 0x4,
PNAT_DPORT = 0x8,
+ PNAT_COPY_BYTE = 0x10,
+ PNAT_CLEAR_BYTE = 0x20,
};
enum pnat_attachment_point
@@ -33,7 +35,7 @@ enum pnat_attachment_point
PNAT_ATTACHMENT_POINT_MAX,
};
-typedef pnat_5tuple
+typedef pnat_match_tuple
{
vl_api_ip4_address_t src;
vl_api_ip4_address_t dst;
@@ -43,12 +45,24 @@ typedef pnat_5tuple
vl_api_pnat_mask_t mask;
};
+typedef pnat_rewrite_tuple
+{
+ vl_api_ip4_address_t src;
+ vl_api_ip4_address_t dst;
+ u16 sport;
+ u16 dport;
+ vl_api_pnat_mask_t mask; // needed?
+ u8 from_offset;
+ u8 to_offset;
+ u8 clear_offset;
+};
+
autoendian define pnat_binding_add
{
u32 client_index;
u32 context;
- vl_api_pnat_5tuple_t match;
- vl_api_pnat_5tuple_t rewrite;
+ vl_api_pnat_match_tuple_t match;
+ vl_api_pnat_rewrite_tuple_t rewrite;
};
autoendian define pnat_binding_add_reply
@@ -107,8 +121,8 @@ define pnat_bindings_get_reply
define pnat_bindings_details
{
u32 context;
- vl_api_pnat_5tuple_t match;
- vl_api_pnat_5tuple_t rewrite;
+ vl_api_pnat_match_tuple_t match;
+ vl_api_pnat_rewrite_tuple_t rewrite;
};
define pnat_interfaces_get
@@ -147,6 +161,12 @@ counters pnat {
units "packets";
description "rewrite failed";
};
+ tooshort {
+ severity info;
+ type counter64;
+ units "packets";
+ description "packet too short for rewrite";
+ };
};
paths {
diff --git a/src/plugins/nat/pnat/pnat.c b/src/plugins/nat/pnat/pnat.c
index a47521f1747..7f02ec4b2f8 100644
--- a/src/plugins/nat/pnat/pnat.c
+++ b/src/plugins/nat/pnat/pnat.c
@@ -178,7 +178,7 @@ static int pnat_disable_interface(u32 sw_if_index,
*/
static inline void pnat_calc_key_from_5tuple(u32 sw_if_index,
pnat_attachment_point_t attachment,
- pnat_5tuple_t *match,
+ pnat_match_tuple_t *match,
clib_bihash_kv_16_8_t *kv) {
pnat_mask_fast_t mask = pnat_mask2fast(match->mask);
ip4_address_t src, dst;
@@ -202,6 +202,10 @@ pnat_instructions_t pnat_instructions_from_mask(pnat_mask_t m) {
i |= PNAT_INSTR_SOURCE_PORT;
if (m & PNAT_DPORT)
i |= PNAT_INSTR_DESTINATION_PORT;
+ if (m & PNAT_COPY_BYTE)
+ i |= PNAT_INSTR_COPY_BYTE;
+ if (m & PNAT_CLEAR_BYTE)
+ i |= PNAT_INSTR_CLEAR_BYTE;
return i;
}
@@ -251,7 +255,15 @@ static int pnat_interface_check_mask(u32 sw_if_index,
return 0;
}
-int pnat_binding_add(pnat_5tuple_t *match, pnat_5tuple_t *rewrite, u32 *index) {
+/*
+ * Add a binding to the binding table.
+ * Returns 0 on success.
+ * -1: Invalid mask
+ * -2: Only matches ports for UDP or TCP
+ *
+ */
+int pnat_binding_add(pnat_match_tuple_t *match, pnat_rewrite_tuple_t *rewrite,
+ u32 *index) {
pnat_main_t *pm = &pnat_main;
*index = -1;
@@ -272,21 +284,25 @@ int pnat_binding_add(pnat_5tuple_t *match, pnat_5tuple_t *rewrite, u32 *index) {
memcpy(&t->post_sa, &rewrite->src, 4);
t->post_sp = rewrite->sport;
t->post_dp = rewrite->dport;
+ t->from_offset = rewrite->from_offset;
+ t->to_offset = rewrite->to_offset;
+ t->clear_offset = rewrite->clear_offset;
t->instructions = pnat_instructions_from_mask(rewrite->mask);
/* These are only used for show commands and trace */
t->match = *match;
-
- /* Rewrite of protocol is not supported, ignore. */
t->rewrite = *rewrite;
- t->rewrite.proto = 0;
*index = t - pm->translations;
return 0;
}
+
+/*
+ * Looks a match flow in the flow cache, returns the index in the binding table
+ */
u32 pnat_flow_lookup(u32 sw_if_index, pnat_attachment_point_t attachment,
- pnat_5tuple_t *match) {
+ pnat_match_tuple_t *match) {
pnat_main_t *pm = &pnat_main;
clib_bihash_kv_16_8_t kv, value;
pnat_calc_key_from_5tuple(sw_if_index, attachment, match, &kv);
@@ -296,6 +312,14 @@ u32 pnat_flow_lookup(u32 sw_if_index, pnat_attachment_point_t attachment,
return ~0;
}
+/*
+ * Attach a binding to an interface / direction.
+ * Returns 0 on success.
+ * -1: Binding does not exist
+ * -2: Interface mask does not match
+ * -3: Existing match entry in flow table
+ * -4: Adding flow table entry failed
+ */
int pnat_binding_attach(u32 sw_if_index, pnat_attachment_point_t attachment,
u32 binding_index) {
pnat_main_t *pm = &pnat_main;
diff --git a/src/plugins/nat/pnat/pnat.h b/src/plugins/nat/pnat/pnat.h
index af64fceb4d1..7ad87c8161a 100644
--- a/src/plugins/nat/pnat/pnat.h
+++ b/src/plugins/nat/pnat/pnat.h
@@ -24,7 +24,8 @@
/* Definitions from pnat.api */
#include <pnat/pnat.api_types.h>
-typedef vl_api_pnat_5tuple_t pnat_5tuple_t;
+typedef vl_api_pnat_match_tuple_t pnat_match_tuple_t;
+typedef vl_api_pnat_rewrite_tuple_t pnat_rewrite_tuple_t;
typedef vl_api_pnat_mask_t pnat_mask_t;
typedef vl_api_pnat_attachment_point_t pnat_attachment_point_t;
@@ -35,6 +36,8 @@ typedef enum {
PNAT_INSTR_SOURCE_PORT = 1 << 2,
PNAT_INSTR_DESTINATION_ADDRESS = 1 << 3,
PNAT_INSTR_DESTINATION_PORT = 1 << 4,
+ PNAT_INSTR_COPY_BYTE = 1 << 5,
+ PNAT_INSTR_CLEAR_BYTE = 1 << 6,
} pnat_instructions_t;
typedef struct {
@@ -52,9 +55,15 @@ typedef struct {
u16 post_sp;
u16 post_dp;
+ /* Byte copy inside of packet */
+ u8 from_offset;
+ u8 to_offset;
+
+ u8 clear_offset; /* Clear byte */
+
/* Used for trace/show commands */
- pnat_5tuple_t match;
- pnat_5tuple_t rewrite;
+ pnat_match_tuple_t match;
+ pnat_rewrite_tuple_t rewrite;
} pnat_translation_t;
/* Interface object */
@@ -91,11 +100,11 @@ pnat_interface_t *pnat_interface_by_sw_if_index(u32 sw_if_index);
/* Packet trace information */
typedef struct {
u32 pool_index;
- pnat_5tuple_t match;
- pnat_5tuple_t rewrite;
+ pnat_match_tuple_t match;
+ pnat_rewrite_tuple_t rewrite;
} pnat_trace_t;
-int pnat_binding_add(pnat_5tuple_t *match, pnat_5tuple_t *rewrite,
+int pnat_binding_add(pnat_match_tuple_t *match, pnat_rewrite_tuple_t *rewrite,
u32 *binding_index);
int pnat_binding_del(u32 binding_index);
int pnat_binding_attach(u32 sw_if_index, pnat_attachment_point_t attachment,
@@ -103,7 +112,7 @@ int pnat_binding_attach(u32 sw_if_index, pnat_attachment_point_t attachment,
int pnat_binding_detach(u32 sw_if_index, pnat_attachment_point_t attachment,
u32 binding_index);
u32 pnat_flow_lookup(u32 sw_if_index, pnat_attachment_point_t attachment,
- pnat_5tuple_t *match);
+ pnat_match_tuple_t *match);
static inline void
pnat_calc_key(u32 sw_if_index, pnat_attachment_point_t attachment,
diff --git a/src/plugins/nat/pnat/pnat_cli.c b/src/plugins/nat/pnat/pnat_cli.c
index 84dfd8d5fc4..082f0778acb 100644
--- a/src/plugins/nat/pnat/pnat_cli.c
+++ b/src/plugins/nat/pnat/pnat_cli.c
@@ -26,8 +26,8 @@
/*
* This file contains the handlers for the (unsupported) VPP debug CLI.
*/
-u8 *format_pnat_5tuple(u8 *s, va_list *args) {
- pnat_5tuple_t *t = va_arg(*args, pnat_5tuple_t *);
+u8 *format_pnat_match_tuple(u8 *s, va_list *args) {
+ pnat_match_tuple_t *t = va_arg(*args, pnat_match_tuple_t *);
s = format(s, "{");
if (t->mask & PNAT_SA)
s = format(s, "%U", format_ip4_address, &t->src);
@@ -52,12 +52,38 @@ u8 *format_pnat_5tuple(u8 *s, va_list *args) {
s = format(s, "}");
return s;
}
+u8 *format_pnat_rewrite_tuple(u8 *s, va_list *args) {
+ pnat_rewrite_tuple_t *t = va_arg(*args, pnat_rewrite_tuple_t *);
+ s = format(s, "{");
+ if (t->mask & PNAT_SA)
+ s = format(s, "%U", format_ip4_address, &t->src);
+ else
+ s = format(s, "*");
+ if (t->mask & PNAT_SPORT)
+ s = format(s, ":%u,", t->sport);
+ else
+ s = format(s, ":*,");
+ if (t->mask & PNAT_DA)
+ s = format(s, "%U", format_ip4_address, &t->dst);
+ else
+ s = format(s, "*");
+ if (t->mask & PNAT_DPORT)
+ s = format(s, ":%u", t->dport);
+ else
+ s = format(s, ":*");
+ if (t->mask & PNAT_COPY_BYTE)
+ s = format(s, " copy byte@[%d->%d]", t->from_offset, t->to_offset);
+ if (t->mask & PNAT_CLEAR_BYTE)
+ s = format(s, " clear byte@[%d]", t->clear_offset);
+ s = format(s, "}");
+ return s;
+}
u8 *format_pnat_translation(u8 *s, va_list *args) {
u32 index = va_arg(*args, u32);
pnat_translation_t *t = va_arg(*args, pnat_translation_t *);
- s = format(s, "[%d] match: %U rewrite: %U", index, format_pnat_5tuple,
- &t->match, format_pnat_5tuple, &t->rewrite);
+ s = format(s, "[%d] match: %U rewrite: %U", index, format_pnat_match_tuple,
+ &t->match, format_pnat_rewrite_tuple, &t->rewrite);
return s;
}
@@ -88,8 +114,8 @@ static u8 *format_pnat_interface(u8 *s, va_list *args) {
return s;
}
-uword unformat_pnat_5tuple(unformat_input_t *input, va_list *args) {
- pnat_5tuple_t *t = va_arg(*args, pnat_5tuple_t *);
+uword unformat_pnat_match_tuple(unformat_input_t *input, va_list *args) {
+ pnat_match_tuple_t *t = va_arg(*args, pnat_match_tuple_t *);
u32 dport, sport;
while (1) {
if (unformat(input, "src %U", unformat_ip4_address, &t->src))
@@ -114,6 +140,45 @@ uword unformat_pnat_5tuple(unformat_input_t *input, va_list *args) {
return 1;
}
+uword unformat_pnat_rewrite_tuple(unformat_input_t *input, va_list *args) {
+ pnat_rewrite_tuple_t *t = va_arg(*args, pnat_rewrite_tuple_t *);
+ u32 dport, sport;
+ u32 to_offset, from_offset, clear_offset;
+
+ while (1) {
+ if (unformat(input, "src %U", unformat_ip4_address, &t->src))
+ t->mask |= PNAT_SA;
+ else if (unformat(input, "dst %U", unformat_ip4_address, &t->dst))
+ t->mask |= PNAT_DA;
+ else if (unformat(input, "sport %d", &sport)) {
+ if (sport == 0 || sport > 65535)
+ return 0;
+ t->mask |= PNAT_SPORT;
+ t->sport = sport;
+ } else if (unformat(input, "dport %d", &dport)) {
+ if (dport == 0 || dport > 65535)
+ return 0;
+ t->mask |= PNAT_DPORT;
+ t->dport = dport;
+ } else if (unformat(input, "copy-byte-at-offset %d %d", &from_offset,
+ &to_offset)) {
+ if (from_offset == to_offset || to_offset > 255 ||
+ from_offset > 255)
+ return 0;
+ t->mask |= PNAT_COPY_BYTE;
+ t->from_offset = from_offset;
+ t->to_offset = to_offset;
+ } else if (unformat(input, "clear-byte-at-offset %d", &clear_offset)) {
+ if (clear_offset > 255)
+ return 0;
+ t->mask |= PNAT_CLEAR_BYTE;
+ t->clear_offset = clear_offset;
+ } else
+ break;
+ }
+ return 1;
+}
+
static clib_error_t *set_pnat_translation_command_fn(vlib_main_t *vm,
unformat_input_t *input,
vlib_cli_command_t *cmd) {
@@ -123,17 +188,17 @@ static clib_error_t *set_pnat_translation_command_fn(vlib_main_t *vm,
bool match_set = false, rewrite_set = false;
bool add = true;
u32 sw_if_index = ~0;
- pnat_5tuple_t match = {0};
- pnat_5tuple_t rewrite = {0};
+ pnat_match_tuple_t match = {0};
+ pnat_rewrite_tuple_t rewrite = {0};
/* Get a line of input. */
if (!unformat_user(input, unformat_line_input, line_input))
return 0;
while (unformat_check_input(line_input) != UNFORMAT_END_OF_INPUT) {
- if (unformat(line_input, "match %U", unformat_pnat_5tuple, &match))
+ if (unformat(line_input, "match %U", unformat_pnat_match_tuple, &match))
match_set = true;
- else if (unformat(line_input, "rewrite %U", unformat_pnat_5tuple,
+ else if (unformat(line_input, "rewrite %U", unformat_pnat_rewrite_tuple,
&rewrite))
rewrite_set = true;
else if (unformat(line_input, "interface %U",
@@ -228,7 +293,7 @@ done:
VLIB_CLI_COMMAND(set_pnat_translation_command, static) = {
.path = "set pnat translation",
.short_help = "set pnat translation interface <name> match <5-tuple> "
- "rewrite <5-tuple> {in|out} [del]",
+ "rewrite <tuple> {in|out} [del]",
.function = set_pnat_translation_command_fn,
};
diff --git a/src/plugins/nat/pnat/pnat_node.c b/src/plugins/nat/pnat/pnat_node.c
index ec505cfa926..3277e6a65dc 100644
--- a/src/plugins/nat/pnat/pnat_node.c
+++ b/src/plugins/nat/pnat/pnat_node.c
@@ -74,12 +74,10 @@ VLIB_REGISTER_NODE(pnat_output_node) = {
VNET_FEATURE_INIT(pnat_input, static) = {
.arc_name = "ip4-unicast",
.node_name = "pnat-input",
- .runs_after = VNET_FEATURES("acl-plugin-in-ip4-fa",
- "ip4-sv-reassembly-feature"),
+ .runs_after = VNET_FEATURES("ip4-sv-reassembly-feature"),
};
VNET_FEATURE_INIT(pnat_output, static) = {
.arc_name = "ip4-output",
.node_name = "pnat-output",
- .runs_after = VNET_FEATURES("acl-plugin-out-ip4-fa",
- "ip4-sv-reassembly-output-feature"),
+ .runs_after = VNET_FEATURES("ip4-sv-reassembly-output-feature"),
};
diff --git a/src/plugins/nat/pnat/pnat_node.h b/src/plugins/nat/pnat/pnat_node.h
index 89acab0b624..595189c2efb 100644
--- a/src/plugins/nat/pnat/pnat_node.h
+++ b/src/plugins/nat/pnat/pnat_node.h
@@ -25,8 +25,8 @@
/* PNAT next-nodes */
typedef enum { PNAT_NEXT_DROP, PNAT_N_NEXT } pnat_next_t;
-// u8 *format_pnat_key(u8 *s, va_list *args);
-u8 *format_pnat_5tuple(u8 *s, va_list *args);
+u8 *format_pnat_match_tuple(u8 *s, va_list *args);
+u8 *format_pnat_rewrite_tuple(u8 *s, va_list *args);
static inline u8 *format_pnat_trace(u8 *s, va_list *args) {
CLIB_UNUSED(vlib_main_t * vm) = va_arg(*args, vlib_main_t *);
CLIB_UNUSED(vlib_node_t * node) = va_arg(*args, vlib_node_t *);
@@ -34,8 +34,10 @@ static inline u8 *format_pnat_trace(u8 *s, va_list *args) {
s = format(s, "pnat: index %d\n", t->pool_index);
if (t->pool_index != ~0) {
- s = format(s, " match: %U\n", format_pnat_5tuple, &t->match);
- s = format(s, " rewrite: %U", format_pnat_5tuple, &t->rewrite);
+ s = format(s, " match: %U\n", format_pnat_match_tuple,
+ &t->match);
+ s = format(s, " rewrite: %U", format_pnat_rewrite_tuple,
+ &t->rewrite);
}
return s;
}
@@ -43,6 +45,7 @@ static inline u8 *format_pnat_trace(u8 *s, va_list *args) {
/*
* Given a packet and rewrite instructions from a translation modify packet.
*/
+// TODO: Generalize to write with mask
static u32 pnat_rewrite_ip4(u32 pool_index, ip4_header_t *ip) {
pnat_main_t *pm = &pnat_main;
if (pool_is_free_index(pm->translations, pool_index))
@@ -65,10 +68,22 @@ static u32 pnat_rewrite_ip4(u32 pool_index, ip4_header_t *ip) {
ip_csum_t csum = ip->checksum;
csum = ip_csum_sub_even(csum, csumd);
ip->checksum = ip_csum_fold(csum);
+ if (ip->checksum == 0xffff)
+ ip->checksum = 0;
ASSERT(ip->checksum == ip4_header_checksum(ip));
+ u16 plen = clib_net_to_host_u16(ip->length);
+
+ /* Nothing more to do if this is a fragment. */
+ if (ip4_is_fragment(ip))
+ return PNAT_ERROR_NONE;
+
/* L4 ports */
if (ip->protocol == IP_PROTOCOL_TCP) {
+ /* Assume IP4 header is 20 bytes */
+ if (plen < sizeof(ip4_header_t) + sizeof(tcp_header_t))
+ return PNAT_ERROR_TOOSHORT;
+
tcp_header_t *tcp = ip4_next_header(ip);
ip_csum_t l4csum = tcp->checksum;
if (t->instructions & PNAT_INSTR_DESTINATION_PORT) {
@@ -84,6 +99,8 @@ static u32 pnat_rewrite_ip4(u32 pool_index, ip4_header_t *ip) {
l4csum = ip_csum_sub_even(l4csum, csumd);
tcp->checksum = ip_csum_fold(l4csum);
} else if (ip->protocol == IP_PROTOCOL_UDP) {
+ if (plen < sizeof(ip4_header_t) + sizeof(udp_header_t))
+ return PNAT_ERROR_TOOSHORT;
udp_header_t *udp = ip4_next_header(ip);
ip_csum_t l4csum = udp->checksum;
if (t->instructions & PNAT_INSTR_DESTINATION_PORT) {
@@ -101,6 +118,25 @@ static u32 pnat_rewrite_ip4(u32 pool_index, ip4_header_t *ip) {
udp->checksum = ip_csum_fold(l4csum);
}
}
+ if (t->instructions & PNAT_INSTR_COPY_BYTE) {
+ /* Copy byte from somewhere in packet to elsewhere */
+
+ if (t->to_offset >= plen || t->from_offset > plen) {
+ return PNAT_ERROR_TOOSHORT;
+ }
+ u8 *p = (u8 *)ip;
+ p[t->to_offset] = p[t->from_offset];
+ ip->checksum = ip4_header_checksum(ip);
+ // TODO: L4 checksum
+ }
+ if (t->instructions & PNAT_INSTR_CLEAR_BYTE) {
+ /* Clear byte at offset */
+ u8 *p = (u8 *)ip;
+ p[t->clear_offset] = 0;
+ ip->checksum = ip4_header_checksum(ip);
+ // TODO: L4 checksum
+ }
+
return PNAT_ERROR_NONE;
}
@@ -109,6 +145,7 @@ static u32 pnat_rewrite_ip4(u32 pool_index, ip4_header_t *ip) {
* If a binding is found, rewrite the packet according to instructions,
* otherwise follow configured default action (forward, punt or drop)
*/
+// TODO: Make use of SVR configurable
static_always_inline uword pnat_node_inline(vlib_main_t *vm,
vlib_node_runtime_t *node,
vlib_frame_t *frame,
@@ -156,9 +193,9 @@ static_always_inline uword pnat_node_inline(vlib_main_t *vm,
/* Cache miss */
*pi = ~0;
}
- next += 1;
/*next: */
+ next += 1;
n_left_from -= 1;
b += 1;
pi += 1;
diff --git a/src/plugins/nat/pnat/tests/missing_rule.def b/src/plugins/nat/pnat/tests/missing_rule.def
new file mode 100644
index 00000000000..62135492410
--- /dev/null
+++ b/src/plugins/nat/pnat/tests/missing_rule.def
@@ -0,0 +1,8 @@
+[
+ (
+ "hit missing rule",
+ IP(src='1.1.1.1', dst='123.123.123.123')/UDP(chksum=0, sport=80, dport=6871),
+ IP(src='1.1.1.1', dst='123.123.123.123')/UDP(chksum=0, sport=80, dport=6871),
+ 0
+ ),
+]
diff --git a/src/plugins/nat/pnat/tests/packets.def b/src/plugins/nat/pnat/tests/packets.def
new file mode 100644
index 00000000000..b8486642e82
--- /dev/null
+++ b/src/plugins/nat/pnat/tests/packets.def
@@ -0,0 +1,69 @@
+[
+ (
+ "da rewritten",
+ IP(src='1.1.1.1', dst='2.2.2.2')/UDP(sport=80, dport=6871),
+ IP(src='1.1.1.1', dst='1.2.3.4')/UDP(sport=80, dport=6871),
+ 4242
+ ),
+ (
+ "unchanged",
+ IP(src='1.1.1.1', dst='2.2.2.2')/UDP(sport=80, dport=8080),
+ IP(src='1.1.1.1', dst='2.2.2.2')/UDP(sport=80, dport=8080),
+ 4242
+ ),
+ (
+ "tcp da",
+ IP(src='1.1.1.1', dst='2.2.2.2')/TCP(sport=80, dport=6871),
+ IP(src='1.1.1.1', dst='1.2.3.4')/TCP(sport=80, dport=6871),
+ 4242
+ ),
+ (
+ "tcp da ports",
+ IP(src='1.1.1.1', dst='2.2.2.2')/TCP(sport=80, dport=6872),
+ IP(src='1.1.1.1', dst='1.2.3.4')/TCP(sport=53, dport=8000),
+ 4242
+ ),
+ (
+ "tcp da ports -- fragmented packet",
+ IP(src='1.1.1.1', dst='2.2.2.2', flags='MF', frag=100)/Raw('X' * 40),
+ IP(src='1.1.1.1', dst='2.2.2.2', flags='MF', frag=100)/Raw('X' * 40),
+ 4242
+ ),
+ (
+ "short packet",
+ IP(src='1.1.1.1', dst='2.2.2.2'),
+ IP(src='1.1.1.1', dst='2.2.2.2'),
+ 4242
+ ),
+ (
+ "copy byte",
+ IP(src='1.1.1.234', dst='2.2.2.2')/UDP(chksum=0, sport=80, dport=6874),
+ IP(src='1.1.1.234', dst='2.2.2.234')/UDP(chksum=0, sport=80, dport=6874),
+ 4242
+ ),
+ (
+ "copy byte into Geneve reserved field",
+ IP(src='1.1.1.1', dst='2.2.2.2')/UDP(chksum=0, sport=80, dport=6877)/GENEVE(vni=4040),
+ IP(src='1.1.1.1', dst='1.2.3.4')/UDP(chksum=0, sport=80, dport=6877)/Raw(b'\x00\x00\x00\x00\x00\x0F\xC8\x01'),
+ 4242
+ ),
+ (
+ "copy byte overrun",
+ IP(src='1.1.1.234', dst='2.2.2.2')/UDP(chksum=0, sport=80, dport=6875),
+ IP(src='1.1.1.234', dst='2.2.2.2')/UDP(chksum=0, sport=80, dport=6875),
+ 0
+ ),
+ (
+ "clear byte",
+ IP(src='1.1.1.234', dst='2.2.2.2')/UDP(chksum=0, sport=80, dport=6876),
+ IP(src='1.1.1.234', dst='2.2.2.2')/UDP(chksum=0, sport=80, dport=220),
+ 4242
+ ),
+ (
+ "packet with options",
+ IP(src='1.1.1.1', dst='2.2.2.2', options=[IPOption('\x07')])/TCP(sport=80, dport=6872),
+ IP(src='1.1.1.1', dst='1.2.3.4', options=[IPOption('\x07')])/TCP(sport=53, dport=8000),
+ 4242
+ ),
+
+]
diff --git a/src/plugins/nat/pnat/pnat_test.c b/src/plugins/nat/pnat/tests/pnat_test.c
index 762b4bdb3f0..ab55e7ef49d 100644
--- a/src/plugins/nat/pnat/pnat_test.c
+++ b/src/plugins/nat/pnat/tests/pnat_test.c
@@ -23,7 +23,7 @@
#include <vppinfra/bihash_16_8.h>
#include <vppinfra/bihash_template.c>
#include <vnet/fib/ip4_fib.h>
-#include "pnat.h"
+#include "../pnat.h"
#include <pnat/pnat.api_enum.h> /* For error counters */
#include <arpa/inet.h>
#include "pnat_test_stubs.h"
@@ -50,10 +50,10 @@ static u32 *buffer_init(u32 *vector, int count) {
}
return vector;
}
-#define PNAT_TEST_DEBUG 0
u32 *results_bi = 0; /* global vector of result buffers */
u16 *results_next = 0;
+
vlib_node_runtime_t *node;
#define log_info(M, ...) \
@@ -61,13 +61,18 @@ vlib_node_runtime_t *node;
#define log_error(M, ...) \
fprintf(stderr, "\033[31;1m[ERROR] (%s:%d:) " M "\033[0m\n", __FILE__, \
__LINE__, ##__VA_ARGS__)
-#define test_assert(A, M, ...) \
+#define test_assert_log(A, M, ...) \
if (!(A)) { \
log_error(M, ##__VA_ARGS__); \
assert(A); \
} else { \
log_info(M, ##__VA_ARGS__); \
}
+#define test_assert(A, M, ...) \
+ if (!(A)) { \
+ log_error(M, ##__VA_ARGS__); \
+ assert(A); \
+ }
/*
* Always return the frame of generated packets
@@ -113,54 +118,33 @@ vlib_buffer_t *test_vlib_get_buffer(u32 bi) {
}
/* Must be included here to allow the above functions to override */
-#include "pnat_node.h"
+#include "../pnat_node.h"
/*** TESTS ***/
typedef struct {
+ char *name;
+ int nsend;
+ char *send;
+ int nexpect;
+ char *expect;
+ u32 expect_next_index;
+} test_t;
+#include "test_packets.h"
+
+/* Rules */
+typedef struct {
char *src;
char *dst;
u8 proto;
u16 sport;
u16 dport;
+ u8 from_offset;
+ u8 to_offset;
+ u8 clear_offset;
} test_5tuple_t;
typedef struct {
- char *name;
- test_5tuple_t send;
- test_5tuple_t expect;
- u32 expect_next_index;
-} test_t;
-
-test_t tests[] = {
- {
- .name = "da rewritten",
- .send = {"1.1.1.1", "2.2.2.2", 17, 80, 6871},
- .expect = {"1.1.1.1", "1.2.3.4", 17, 80, 6871},
- .expect_next_index = NEXT_PASSTHROUGH,
- },
- {
- .name = "unchanged",
- .send = {"1.1.1.1", "2.2.2.2", 17, 80, 8080},
- .expect = {"1.1.1.1", "2.2.2.2", 17, 80, 8080},
- .expect_next_index = NEXT_PASSTHROUGH,
- },
- {
- .name = "tcp da",
- .send = {"1.1.1.1", "2.2.2.2", 6, 80, 6871},
- .expect = {"1.1.1.1", "1.2.3.4", 6, 80, 6871},
- .expect_next_index = NEXT_PASSTHROUGH,
- },
- {
- .name = "tcp da ports",
- .send = {"1.1.1.1", "2.2.2.2", 6, 80, 6872},
- .expect = {"1.1.1.1", "1.2.3.4", 6, 53, 8000},
- .expect_next_index = NEXT_PASSTHROUGH,
- },
-};
-
-/* Rules */
-typedef struct {
test_5tuple_t match;
test_5tuple_t rewrite;
bool in;
@@ -188,55 +172,55 @@ rule_t rules[] = {
.rewrite = {.dst = "1.2.3.4", .sport = 53, .dport = 8000},
.in = true,
},
+ {
+ .match = {.dst = "2.2.2.2", .proto = 17, .dport = 6874},
+ .rewrite = {.from_offset = 15, .to_offset = 19},
+ .in = true,
+ },
+ {
+ .match = {.dst = "2.2.2.2", .proto = 17, .dport = 6875},
+ .rewrite = {.from_offset = 15, .to_offset = 50},
+ .in = true,
+ },
+ {
+ .match = {.dst = "2.2.2.2", .proto = 17, .dport = 6877},
+ .rewrite = {.dst = "1.2.3.4", .from_offset = 15, .to_offset = 35},
+ .in = true,
+ },
+ {
+ .match = {.dst = "2.2.2.2", .proto = 17, .dport = 6876},
+ .rewrite = {.clear_offset = 22},
+ .in = true,
+ },
};
-static int fill_packets(vlib_main_t *vm, vlib_buffer_t *b,
- test_5tuple_t *test) {
+static int fill_packets(vlib_main_t *vm, vlib_buffer_t *b, int n, char *test) {
b->flags |= VLIB_BUFFER_IS_TRACED;
ip4_header_t *ip = (ip4_header_t *)vlib_buffer_get_current(b);
- memset(ip, 0, sizeof(*ip));
- ip->ip_version_and_header_length = 0x45;
- ip->ttl = 64;
- inet_pton(AF_INET, test->src, &ip->src_address.as_u32);
- inet_pton(AF_INET, test->dst, &ip->dst_address.as_u32);
- ip->protocol = test->proto;
-
- if (test->proto == IP_PROTOCOL_UDP) {
+
+ memcpy(ip, test, n);
+
+ /* Do the work of SVR */
+ vnet_buffer(b)->ip.reass.l4_src_port = 0;
+ vnet_buffer(b)->ip.reass.l4_dst_port = 0;
+ b->current_length = n;
+
+ if (ip4_is_fragment(ip))
+ return 0;
+ if (ip->protocol == IP_PROTOCOL_UDP) {
udp_header_t *udp = ip4_next_header(ip);
- memset(udp, 0, sizeof(*udp));
- udp->dst_port = htons(test->dport);
- udp->src_port = htons(test->sport);
- udp->length = htons(8);
vnet_buffer(b)->ip.reass.l4_src_port = udp->src_port;
vnet_buffer(b)->ip.reass.l4_dst_port = udp->dst_port;
- b->current_length = 28;
- ip->length = htons(b->current_length);
- ip->checksum = ip4_header_checksum(ip);
- udp->checksum = ip4_tcp_udp_compute_checksum(vm, b, ip);
- } else if (test->proto == IP_PROTOCOL_TCP) {
+ } else if (ip->protocol == IP_PROTOCOL_TCP) {
tcp_header_t *tcp = ip4_next_header(ip);
- memset(tcp, 0, sizeof(*tcp));
- tcp->dst_port = htons(test->dport);
- tcp->src_port = htons(test->sport);
vnet_buffer(b)->ip.reass.l4_src_port = tcp->src_port;
vnet_buffer(b)->ip.reass.l4_dst_port = tcp->dst_port;
- b->current_length = sizeof(ip4_header_t) + sizeof(tcp_header_t);
- ip->length = htons(b->current_length);
- ip->checksum = ip4_header_checksum(ip);
- tcp->checksum = ip4_tcp_udp_compute_checksum(vm, b, ip);
- } else {
- b->current_length = sizeof(ip4_header_t);
- ip->length = htons(b->current_length);
- ip->checksum = ip4_header_checksum(ip);
- vnet_buffer(b)->ip.reass.l4_src_port = 0;
- vnet_buffer(b)->ip.reass.l4_dst_port = 0;
}
-
return 0;
}
-static void ruleto5tuple(test_5tuple_t *r, pnat_5tuple_t *t) {
+static void ruletomatch(test_5tuple_t *r, pnat_match_tuple_t *t) {
if (r->src) {
inet_pton(AF_INET, r->src, &t->src);
t->mask |= PNAT_SA;
@@ -256,12 +240,40 @@ static void ruleto5tuple(test_5tuple_t *r, pnat_5tuple_t *t) {
t->proto = r->proto;
}
+static void ruletorewrite(test_5tuple_t *r, pnat_rewrite_tuple_t *t) {
+ if (r->src) {
+ inet_pton(AF_INET, r->src, &t->src);
+ t->mask |= PNAT_SA;
+ }
+ if (r->dst) {
+ inet_pton(AF_INET, r->dst, &t->dst);
+ t->mask |= PNAT_DA;
+ }
+ if (r->dport) {
+ t->dport = r->dport;
+ t->mask |= PNAT_DPORT;
+ }
+ if (r->sport) {
+ t->sport = r->sport;
+ t->mask |= PNAT_SPORT;
+ }
+ if (r->to_offset || r->from_offset) {
+ t->to_offset = r->to_offset;
+ t->from_offset = r->from_offset;
+ t->mask |= PNAT_COPY_BYTE;
+ }
+ if (r->clear_offset) {
+ t->clear_offset = r->clear_offset;
+ t->mask |= PNAT_CLEAR_BYTE;
+ }
+}
+
static void add_translation(rule_t *r) {
- pnat_5tuple_t match = {0};
- pnat_5tuple_t rewrite = {0};
+ pnat_match_tuple_t match = {0};
+ pnat_rewrite_tuple_t rewrite = {0};
- ruleto5tuple(&r->match, &match);
- ruleto5tuple(&r->rewrite, &rewrite);
+ ruletomatch(&r->match, &match);
+ ruletorewrite(&r->rewrite, &rewrite);
int rv = pnat_binding_add(&match, &rewrite, &r->index);
assert(rv == 0);
@@ -287,24 +299,47 @@ static void validate_packet(vlib_main_t *vm, char *name, u32 bi,
ip4_header_t *expected_ip =
(ip4_header_t *)vlib_buffer_get_current(expected_b);
-#if PNAT_TEST_DEBUG
- clib_warning("Received packet: %U", format_ip4_header, ip, 20);
- clib_warning("Expected packet: %U", format_ip4_header, expected_ip, 20);
- tcp_header_t *tcp = ip4_next_header(ip);
- clib_warning("IP: %U TCP: %U", format_ip4_header, ip, sizeof(*ip),
- format_tcp_header, tcp, sizeof(*tcp));
- tcp = ip4_next_header(expected_ip);
- clib_warning("IP: %U TCP: %U", format_ip4_header, expected_ip, sizeof(*ip),
- format_tcp_header, tcp, sizeof(*tcp));
-#endif
-
- u32 flags = ip4_tcp_udp_validate_checksum(vm, b);
- assert((flags & VNET_BUFFER_F_L4_CHECKSUM_CORRECT) != 0);
- flags = ip4_tcp_udp_validate_checksum(vm, expected_b);
- assert((flags & VNET_BUFFER_F_L4_CHECKSUM_CORRECT) != 0);
- assert(b->current_length == expected_b->current_length);
-
- test_assert(memcmp(ip, expected_ip, b->current_length) == 0, "%s", name);
+ if (ip->protocol == IP_PROTOCOL_UDP || ip->protocol == IP_PROTOCOL_TCP) {
+ u32 flags = ip4_tcp_udp_validate_checksum(vm, b);
+ test_assert((flags & VNET_BUFFER_F_L4_CHECKSUM_CORRECT) != 0, "%s",
+ name);
+ flags = ip4_tcp_udp_validate_checksum(vm, expected_b);
+ test_assert((flags & VNET_BUFFER_F_L4_CHECKSUM_CORRECT) != 0, "%s",
+ name);
+ }
+ test_assert(b->current_length == expected_b->current_length, "%s %d vs %d",
+ name, b->current_length, expected_b->current_length);
+
+ if (memcmp(ip, expected_ip, b->current_length) != 0) {
+ if (ip->protocol == IP_PROTOCOL_UDP) {
+ udp_header_t *udp = ip4_next_header(ip);
+ clib_warning("Received: IP: %U UDP: %U", format_ip4_header, ip,
+ sizeof(*ip), format_udp_header, udp, sizeof(*udp));
+ udp = ip4_next_header(expected_ip);
+ clib_warning("%U", format_hexdump, ip, b->current_length);
+ clib_warning("Expected: IP: %U UDP: %U", format_ip4_header,
+ expected_ip, sizeof(*ip), format_udp_header, udp,
+ sizeof(*udp));
+ clib_warning("%U", format_hexdump, expected_ip,
+ expected_b->current_length);
+ } else if (ip->protocol == IP_PROTOCOL_TCP) {
+ tcp_header_t *tcp = ip4_next_header(ip);
+ clib_warning("Received IP: %U TCP: %U", format_ip4_header, ip,
+ sizeof(*ip), format_tcp_header, tcp, sizeof(*tcp));
+ tcp = ip4_next_header(expected_ip);
+ clib_warning("Expected IP: %U TCP: %U", format_ip4_header,
+ expected_ip, sizeof(*ip), format_tcp_header, tcp,
+ sizeof(*tcp));
+ } else {
+ clib_warning("Received: IP: %U", format_ip4_header, ip,
+ sizeof(*ip));
+ clib_warning("Expected: IP: %U", format_ip4_header, expected_ip,
+ sizeof(*ip));
+ }
+ test_assert_log(0, "%s", name);
+ } else {
+ test_assert_log(1, "%s", name);
+ }
}
extern vlib_node_registration_t pnat_input_node;
@@ -317,8 +352,9 @@ static void test_table(test_t *t, int no_tests) {
/* Generate packet data */
for (i = 0; i < no_tests; i++) {
// create input buffer(s)
- fill_packets(vm, (vlib_buffer_t *)&buffers[i], &t[i].send);
- fill_packets(vm, (vlib_buffer_t *)&expected[i], &t[i].expect);
+ fill_packets(vm, (vlib_buffer_t *)&buffers[i], t[i].nsend, t[i].send);
+ fill_packets(vm, (vlib_buffer_t *)&expected[i], t[i].nexpect,
+ t[i].expect);
}
/* send packets through graph node */
@@ -329,7 +365,7 @@ static void test_table(test_t *t, int no_tests) {
/* verify tests */
for (i = 0; i < no_tests; i++) {
- assert(t[i].expect_next_index == results_next[i]);
+ test_assert(t[i].expect_next_index == results_next[i], "%s", t[i].name);
validate_packet(vm, t[i].name, results_bi[i],
(vlib_buffer_t *)&expected[i]);
}
@@ -337,7 +373,7 @@ static void test_table(test_t *t, int no_tests) {
vec_free(results_bi);
}
-static void test_performance(void) {
+void test_performance(void) {
pnat_main_t *pm = &pnat_main;
int i;
vlib_main_t *vm = &vlib_global_main;
@@ -347,12 +383,13 @@ static void test_performance(void) {
}
assert(pool_elts(pm->translations) == sizeof(rules) / sizeof(rules[0]));
- int no_tests = sizeof(tests) / sizeof(tests[0]);
+ int no_tests = sizeof(tests_packets) / sizeof(tests_packets[0]);
/* Generate packet data */
for (i = 0; i < VLIB_FRAME_SIZE; i++) {
// create input buffer(s)
fill_packets(vm, (vlib_buffer_t *)&buffers[i],
- &tests[i % no_tests].send);
+ tests_packets[i % no_tests].nsend,
+ tests_packets[i % no_tests].send);
// fill_packets(vm, (vlib_buffer_t *)&expected[i], &tests[i %
// no_tests].expect);
}
@@ -382,7 +419,7 @@ static void test_performance(void) {
assert(pool_elts(pm->interfaces) == 0);
}
-static void test_packets(void) {
+void test_packets(void) {
pnat_main_t *pm = &pnat_main;
int i;
for (i = 0; i < sizeof(rules) / sizeof(rules[0]); i++) {
@@ -390,7 +427,7 @@ static void test_packets(void) {
}
assert(pool_elts(pm->translations) == sizeof(rules) / sizeof(rules[0]));
- test_table(tests, sizeof(tests) / sizeof(tests[0]));
+ test_table(tests_packets, sizeof(tests_packets) / sizeof(tests_packets[0]));
for (i = 0; i < sizeof(rules) / sizeof(rules[0]); i++) {
del_translation(&rules[i]);
@@ -398,6 +435,7 @@ static void test_packets(void) {
assert(pool_elts(pm->translations) == 0);
assert(pool_elts(pm->interfaces) == 0);
}
+
static void test_attach(void) {
pnat_attachment_point_t attachment = PNAT_IP4_INPUT;
u32 binding_index = 0;
@@ -408,8 +446,8 @@ static void test_attach(void) {
rv = pnat_binding_detach(sw_if_index, attachment, 1234);
test_assert(rv == -1, "binding_detach - nothing to detach");
- pnat_5tuple_t match = {.mask = PNAT_SA};
- pnat_5tuple_t rewrite = {.mask = PNAT_SA};
+ pnat_match_tuple_t match = {.mask = PNAT_SA};
+ pnat_rewrite_tuple_t rewrite = {.mask = PNAT_SA};
rv = pnat_binding_add(&match, &rewrite, &binding_index);
assert(rv == 0);
@@ -441,24 +479,17 @@ static void test_del_before_detach(void) {
int rv = pnat_binding_del(binding_index);
assert(rv == 0);
- test_t test = {
- .name = "hit missing rule",
- .send = {"1.1.1.1", "123.123.123.123", 17, 80, 6871},
- .expect = {"1.1.1.1", "123.123.123.123", 17, 80, 6871},
- .expect_next_index = PNAT_NEXT_DROP,
- };
-
- test_table(&test, 1);
+ test_table(&tests_missing_rule[0], 1);
/* For now if you have deleted before detach, can't find key */
rv = pnat_binding_detach(sw_if_index, attachment, binding_index);
test_assert(rv == -1, "binding_detach - failure");
/* Re-add the rule and try again */
- pnat_5tuple_t match = {0};
- pnat_5tuple_t rewrite = {0};
- ruleto5tuple(&rule.match, &match);
- ruleto5tuple(&rule.rewrite, &rewrite);
+ pnat_match_tuple_t match = {0};
+ pnat_rewrite_tuple_t rewrite = {0};
+ ruletomatch(&rule.match, &match);
+ ruletorewrite(&rule.rewrite, &rewrite);
rv = pnat_binding_add(&match, &rewrite, &binding_index);
assert(rv == 0);
rv = pnat_binding_detach(sw_if_index, attachment, binding_index);
@@ -467,11 +498,56 @@ static void test_del_before_detach(void) {
assert(rv == 0);
}
-static void test_api(void) {
+void test_api(void) {
test_attach();
test_del_before_detach();
}
+void test_checksum(void) {
+ int i;
+ vlib_main_t *vm = &vlib_global_main;
+ pnat_main_t *pm = &pnat_main;
+
+ test_t test = {
+ .name = "checksum",
+ .nsend = 28,
+ .send =
+ (char[]){0x45, 0x00, 0x00, 0x1c, 0x00, 0x01, 0x00, 0x00, 0x40, 0x11,
+ 0x74, 0xcb, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02,
+ 0x00, 0x50, 0x1a, 0xd7, 0x00, 0x08, 0xde, 0xb1},
+ };
+
+ for (i = 0; i < sizeof(rules) / sizeof(rules[0]); i++) {
+ add_translation(&rules[i]);
+ }
+ assert(pool_elts(pm->translations) == sizeof(rules) / sizeof(rules[0]));
+
+ /* send packets through graph node */
+ vlib_frame_t frame = {.n_vectors = 1};
+ node->flags |= VLIB_NODE_FLAG_TRACE;
+
+ ip4_header_t *ip =
+ (ip4_header_t *)vlib_buffer_get_current((vlib_buffer_t *)&buffers[0]);
+
+ for (i = 0; i < 65535; i++) {
+
+ /* Get a buffer. Loop through 64K variations of it to check checksum */
+ memset(&buffers[0], 0, 2048);
+ fill_packets(vm, (vlib_buffer_t *)&buffers[0], test.nsend, test.send);
+
+ ip->src_address.as_u32 = i;
+ ip->checksum = 0;
+ ip->checksum = ip4_header_checksum(ip);
+ pnat_node_inline(vm, node, &frame, PNAT_IP4_INPUT, VLIB_RX);
+ }
+
+ test_assert_log(1, "%s", test.name);
+
+ for (i = 0; i < sizeof(rules) / sizeof(rules[0]); i++) {
+ del_translation(&rules[i]);
+ }
+}
+
/*
* Unit testing:
* 1) Table of packets and expected outcomes. Run through
@@ -497,8 +573,15 @@ int main(int argc, char **argv) {
/* Test API */
test_api();
-
test_packets();
-
+ test_checksum();
test_performance();
}
+
+/*
+ * NEW TESTS:
+ * - Chained buffers. Only do rewrite in first buffer
+ * - No interface. Can that really happen?
+ * - IP length shorter than buffer.
+ * - IP length longer than buffer.
+ */
diff --git a/src/plugins/nat/pnat/pnat_test_stubs.h b/src/plugins/nat/pnat/tests/pnat_test_stubs.h
index 2801398407c..62aa050989a 100644
--- a/src/plugins/nat/pnat/pnat_test_stubs.h
+++ b/src/plugins/nat/pnat/tests/pnat_test_stubs.h
@@ -35,7 +35,8 @@ u8 *format_ip4_address(u8 *s, va_list *args) {
return format(s, "%d.%d.%d.%d", a[0], a[1], a[2], a[3]);
}
-u8 *format_pnat_5tuple(u8 *s, va_list *args) { return 0; }
+u8 *format_pnat_match_tuple(u8 *s, va_list *args) { return 0; }
+u8 *format_pnat_rewrite_tuple(u8 *s, va_list *args) { return 0; }
vl_counter_t pnat_error_counters[10];
@@ -142,6 +143,28 @@ u8 *format_tcp_header(u8 *s, va_list *args) {
clib_net_to_host_u16(tcp->checksum));
return s;
}
+/* Format UDP header. */
+u8 *format_udp_header(u8 *s, va_list *args) {
+ udp_header_t *udp = va_arg(*args, udp_header_t *);
+ u32 max_header_bytes = va_arg(*args, u32);
+ u32 indent;
+
+ /* Nothing to do. */
+ if (max_header_bytes < sizeof(udp[0]))
+ return format(s, "UDP header truncated");
+
+ indent = format_get_indent(s);
+ indent += 2;
+
+ s = format(s, "UDP: %d -> %d", clib_net_to_host_u16(udp->src_port),
+ clib_net_to_host_u16(udp->dst_port));
+
+ s = format(s, "\n%Ulength %d, checksum 0x%04x", format_white_space, indent,
+ clib_net_to_host_u16(udp->length),
+ clib_net_to_host_u16(udp->checksum));
+
+ return s;
+}
/* Format an IP4 header. */
u8 *format_ip4_header(u8 *s, va_list *args) {
diff --git a/src/plugins/nat/pnat/tests/test_genpackets.py b/src/plugins/nat/pnat/tests/test_genpackets.py
new file mode 100755
index 00000000000..9d32d3e3656
--- /dev/null
+++ b/src/plugins/nat/pnat/tests/test_genpackets.py
@@ -0,0 +1,38 @@
+#!/usr/bin/env python3
+
+import sys
+import os
+from importlib.machinery import SourceFileLoader
+from scapy.all import *
+from scapy.contrib.geneve import GENEVE
+
+def hexstring(p):
+ s = bytes(p.__class__(p))
+ return ",".join("0x{:02x}".format(c) for c in s)
+
+def output_test(filename, tests):
+ (name, ext) = os.path.basename(filename).split(".")
+ print('/* DO NOT EDIT: automatically generated by test_genpackets.py */')
+ print('/* clang-format off */')
+ print('test_t tests_{}[] = {{'.format(name))
+
+ for t in tests:
+ print(' {')
+ print(' .name = "{}",'.format(t[0]))
+ print(' .nsend = {},'.format(len(t[1])))
+ print(' .send = (char []){{{}}},'.format(hexstring(t[1])))
+ print(' .nexpect = {},'.format(len(t[2])))
+ print(' .expect = (char []){{{}}},'.format(hexstring(t[2])))
+ print(' .expect_next_index = {}'.format(t[3]))
+ print(' },')
+ print('};')
+ print('/* clang-format on */')
+
+# Read tests from file
+for filename in sys.argv[1:]:
+ with open(filename) as f:
+ content = f.read().replace('\n', '')
+
+ tests = eval(content)
+ output_test(filename, tests)
+
diff --git a/src/plugins/nat/pnat/tests/test_packets.h b/src/plugins/nat/pnat/tests/test_packets.h
new file mode 100644
index 00000000000..b010450d4a6
--- /dev/null
+++ b/src/plugins/nat/pnat/tests/test_packets.h
@@ -0,0 +1,106 @@
+/* DO NOT EDIT: automatically generated by test_genpackets.py */
+/* clang-format off */
+test_t tests_packets[] = {
+ {
+ .name = "da rewritten",
+ .nsend = 28,
+ .send = (char []){0x45,0x00,0x00,0x1c,0x00,0x01,0x00,0x00,0x40,0x11,0x74,0xcb,0x01,0x01,0x01,0x01,0x02,0x02,0x02,0x02,0x00,0x50,0x1a,0xd7,0x00,0x08,0xde,0xb1},
+ .nexpect = 28,
+ .expect = (char []){0x45,0x00,0x00,0x1c,0x00,0x01,0x00,0x00,0x40,0x11,0x74,0xc9,0x01,0x01,0x01,0x01,0x01,0x02,0x03,0x04,0x00,0x50,0x1a,0xd7,0x00,0x08,0xde,0xaf},
+ .expect_next_index = 4242
+ },
+ {
+ .name = "unchanged",
+ .nsend = 28,
+ .send = (char []){0x45,0x00,0x00,0x1c,0x00,0x01,0x00,0x00,0x40,0x11,0x74,0xcb,0x01,0x01,0x01,0x01,0x02,0x02,0x02,0x02,0x00,0x50,0x1f,0x90,0x00,0x08,0xd9,0xf8},
+ .nexpect = 28,
+ .expect = (char []){0x45,0x00,0x00,0x1c,0x00,0x01,0x00,0x00,0x40,0x11,0x74,0xcb,0x01,0x01,0x01,0x01,0x02,0x02,0x02,0x02,0x00,0x50,0x1f,0x90,0x00,0x08,0xd9,0xf8},
+ .expect_next_index = 4242
+ },
+ {
+ .name = "tcp da",
+ .nsend = 40,
+ .send = (char []){0x45,0x00,0x00,0x28,0x00,0x01,0x00,0x00,0x40,0x06,0x74,0xca,0x01,0x01,0x01,0x01,0x02,0x02,0x02,0x02,0x00,0x50,0x1a,0xd7,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x50,0x02,0x20,0x00,0x6e,0xb6,0x00,0x00},
+ .nexpect = 40,
+ .expect = (char []){0x45,0x00,0x00,0x28,0x00,0x01,0x00,0x00,0x40,0x06,0x74,0xc8,0x01,0x01,0x01,0x01,0x01,0x02,0x03,0x04,0x00,0x50,0x1a,0xd7,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x50,0x02,0x20,0x00,0x6e,0xb4,0x00,0x00},
+ .expect_next_index = 4242
+ },
+ {
+ .name = "tcp da ports",
+ .nsend = 40,
+ .send = (char []){0x45,0x00,0x00,0x28,0x00,0x01,0x00,0x00,0x40,0x06,0x74,0xca,0x01,0x01,0x01,0x01,0x02,0x02,0x02,0x02,0x00,0x50,0x1a,0xd8,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x50,0x02,0x20,0x00,0x6e,0xb5,0x00,0x00},
+ .nexpect = 40,
+ .expect = (char []){0x45,0x00,0x00,0x28,0x00,0x01,0x00,0x00,0x40,0x06,0x74,0xc8,0x01,0x01,0x01,0x01,0x01,0x02,0x03,0x04,0x00,0x35,0x1f,0x40,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x50,0x02,0x20,0x00,0x6a,0x66,0x00,0x00},
+ .expect_next_index = 4242
+ },
+ {
+ .name = "tcp da ports -- fragmented packet",
+ .nsend = 60,
+ .send = (char []){0x45,0x00,0x00,0x3c,0x00,0x01,0x20,0x64,0x40,0x00,0x54,0x58,0x01,0x01,0x01,0x01,0x02,0x02,0x02,0x02,0x58,0x58,0x58,0x58,0x58,0x58,0x58,0x58,0x58,0x58,0x58,0x58,0x58,0x58,0x58,0x58,0x58,0x58,0x58,0x58,0x58,0x58,0x58,0x58,0x58,0x58,0x58,0x58,0x58,0x58,0x58,0x58,0x58,0x58,0x58,0x58,0x58,0x58,0x58,0x58},
+ .nexpect = 60,
+ .expect = (char []){0x45,0x00,0x00,0x3c,0x00,0x01,0x20,0x64,0x40,0x00,0x54,0x58,0x01,0x01,0x01,0x01,0x02,0x02,0x02,0x02,0x58,0x58,0x58,0x58,0x58,0x58,0x58,0x58,0x58,0x58,0x58,0x58,0x58,0x58,0x58,0x58,0x58,0x58,0x58,0x58,0x58,0x58,0x58,0x58,0x58,0x58,0x58,0x58,0x58,0x58,0x58,0x58,0x58,0x58,0x58,0x58,0x58,0x58,0x58,0x58},
+ .expect_next_index = 4242
+ },
+ {
+ .name = "short packet",
+ .nsend = 20,
+ .send = (char []){0x45,0x00,0x00,0x14,0x00,0x01,0x00,0x00,0x40,0x00,0x74,0xe4,0x01,0x01,0x01,0x01,0x02,0x02,0x02,0x02},
+ .nexpect = 20,
+ .expect = (char []){0x45,0x00,0x00,0x14,0x00,0x01,0x00,0x00,0x40,0x00,0x74,0xe4,0x01,0x01,0x01,0x01,0x02,0x02,0x02,0x02},
+ .expect_next_index = 4242
+ },
+ {
+ .name = "copy byte",
+ .nsend = 28,
+ .send = (char []){0x45,0x00,0x00,0x1c,0x00,0x01,0x00,0x00,0x40,0x11,0x73,0xe2,0x01,0x01,0x01,0xea,0x02,0x02,0x02,0x02,0x00,0x50,0x1a,0xda,0x00,0x08,0x00,0x00},
+ .nexpect = 28,
+ .expect = (char []){0x45,0x00,0x00,0x1c,0x00,0x01,0x00,0x00,0x40,0x11,0x72,0xfa,0x01,0x01,0x01,0xea,0x02,0x02,0x02,0xea,0x00,0x50,0x1a,0xda,0x00,0x08,0x00,0x00},
+ .expect_next_index = 4242
+ },
+ {
+ .name = "copy byte into Geneve reserved field",
+ .nsend = 36,
+ .send = (char []){0x45,0x00,0x00,0x24,0x00,0x01,0x00,0x00,0x40,0x11,0x74,0xc3,0x01,0x01,0x01,0x01,0x02,0x02,0x02,0x02,0x00,0x50,0x1a,0xdd,0x00,0x10,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0f,0xc8,0x00},
+ .nexpect = 36,
+ .expect = (char []){0x45,0x00,0x00,0x24,0x00,0x01,0x00,0x00,0x40,0x11,0x74,0xc1,0x01,0x01,0x01,0x01,0x01,0x02,0x03,0x04,0x00,0x50,0x1a,0xdd,0x00,0x10,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0f,0xc8,0x01},
+ .expect_next_index = 4242
+ },
+ {
+ .name = "copy byte overrun",
+ .nsend = 28,
+ .send = (char []){0x45,0x00,0x00,0x1c,0x00,0x01,0x00,0x00,0x40,0x11,0x73,0xe2,0x01,0x01,0x01,0xea,0x02,0x02,0x02,0x02,0x00,0x50,0x1a,0xdb,0x00,0x08,0x00,0x00},
+ .nexpect = 28,
+ .expect = (char []){0x45,0x00,0x00,0x1c,0x00,0x01,0x00,0x00,0x40,0x11,0x73,0xe2,0x01,0x01,0x01,0xea,0x02,0x02,0x02,0x02,0x00,0x50,0x1a,0xdb,0x00,0x08,0x00,0x00},
+ .expect_next_index = 0
+ },
+ {
+ .name = "clear byte",
+ .nsend = 28,
+ .send = (char []){0x45,0x00,0x00,0x1c,0x00,0x01,0x00,0x00,0x40,0x11,0x73,0xe2,0x01,0x01,0x01,0xea,0x02,0x02,0x02,0x02,0x00,0x50,0x1a,0xdc,0x00,0x08,0x00,0x00},
+ .nexpect = 28,
+ .expect = (char []){0x45,0x00,0x00,0x1c,0x00,0x01,0x00,0x00,0x40,0x11,0x73,0xe2,0x01,0x01,0x01,0xea,0x02,0x02,0x02,0x02,0x00,0x50,0x00,0xdc,0x00,0x08,0x00,0x00},
+ .expect_next_index = 4242
+ },
+ {
+ .name = "packet with options",
+ .nsend = 44,
+ .send = (char []){0x46,0x00,0x00,0x2c,0x00,0x01,0x00,0x00,0x40,0x06,0x6c,0xc6,0x01,0x01,0x01,0x01,0x02,0x02,0x02,0x02,0x07,0x00,0x00,0x00,0x00,0x50,0x1a,0xd8,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x50,0x02,0x20,0x00,0x6e,0xb5,0x00,0x00},
+ .nexpect = 44,
+ .expect = (char []){0x46,0x00,0x00,0x2c,0x00,0x01,0x00,0x00,0x40,0x06,0x6c,0xc4,0x01,0x01,0x01,0x01,0x01,0x02,0x03,0x04,0x07,0x00,0x00,0x00,0x00,0x35,0x1f,0x40,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x50,0x02,0x20,0x00,0x6a,0x66,0x00,0x00},
+ .expect_next_index = 4242
+ },
+};
+/* clang-format on */
+/* DO NOT EDIT: automatically generated by test_genpackets.py */
+/* clang-format off */
+test_t tests_missing_rule[] = {
+ {
+ .name = "hit missing rule",
+ .nsend = 28,
+ .send = (char []){0x45,0x00,0x00,0x1c,0x00,0x01,0x00,0x00,0x40,0x11,0x81,0xd8,0x01,0x01,0x01,0x01,0x7b,0x7b,0x7b,0x7b,0x00,0x50,0x1a,0xd7,0x00,0x08,0x00,0x00},
+ .nexpect = 28,
+ .expect = (char []){0x45,0x00,0x00,0x1c,0x00,0x01,0x00,0x00,0x40,0x11,0x81,0xd8,0x01,0x01,0x01,0x01,0x7b,0x7b,0x7b,0x7b,0x00,0x50,0x1a,0xd7,0x00,0x08,0x00,0x00},
+ .expect_next_index = 0
+ },
+};
+/* clang-format on */