v5: implement JDNS{Q,A}MATCHSAFE
Test: TreeHugger
Signed-off-by: Maciej Żenczykowski <maze@google.com>
Change-Id: Icffd679c98e1735183c1c10250f89359c5d9a15f
diff --git a/v5/apf.h b/v5/apf.h
index dd254d9..707519b 100644
--- a/v5/apf.h
+++ b/v5/apf.h
@@ -229,6 +229,7 @@
#define EPKTDATACOPYR1_EXT_OPCODE 42
/* Jumps if the UDP payload content (starting at R0) does not contain the specified QNAME,
* applying MDNS case insensitivity.
+ * SAFE version PASSES corrupt packets, while the other one DROPS.
* R0: Offset to UDP payload content
* imm1: Opcode
* imm2: Label offset
@@ -237,9 +238,11 @@
* e.g.: "jdnsqmatch R0,label,0x0c,\002aa\005local\0\0"
*/
#define JDNSQMATCH_EXT_OPCODE 43
+#define JDNSQMATCHSAFE_EXT_OPCODE 45
/* Jumps if the UDP payload content (starting at R0) does not contain one
* of the specified NAMEs in answers/authority/additional records, applying
* case insensitivity.
+ * SAFE version PASSES corrupt packets, while the other one DROPS.
* R=0/1 meaning 'does not match'/'matches'
* R0: Offset to UDP payload content
* imm1: Opcode
@@ -248,6 +251,7 @@
* e.g.: "jdnsamatch R0,label,0x0c,\002aa\005local\0\0"
*/
#define JDNSAMATCH_EXT_OPCODE 44
+#define JDNSAMATCHSAFE_EXT_OPCODE 46
#define EXTRACT_OPCODE(i) (((i) >> 3) & 31)
#define EXTRACT_REGISTER(i) ((i) & 1)
diff --git a/v5/apf_interpreter.c b/v5/apf_interpreter.c
index 4bec87a..8bfa579 100644
--- a/v5/apf_interpreter.c
+++ b/v5/apf_interpreter.c
@@ -294,6 +294,7 @@
#define EPKTDATACOPYR1_EXT_OPCODE 42
/* Jumps if the UDP payload content (starting at R0) does not contain the specified QNAME,
* applying MDNS case insensitivity.
+ * SAFE version PASSES corrupt packets, while the other one DROPS.
* R0: Offset to UDP payload content
* imm1: Opcode
* imm2: Label offset
@@ -302,9 +303,11 @@
* e.g.: "jdnsqmatch R0,label,0x0c,\002aa\005local\0\0"
*/
#define JDNSQMATCH_EXT_OPCODE 43
+#define JDNSQMATCHSAFE_EXT_OPCODE 45
/* Jumps if the UDP payload content (starting at R0) does not contain one
* of the specified NAMEs in answers/authority/additional records, applying
* case insensitivity.
+ * SAFE version PASSES corrupt packets, while the other one DROPS.
* R=0/1 meaning 'does not match'/'matches'
* R0: Offset to UDP payload content
* imm1: Opcode
@@ -313,6 +316,7 @@
* e.g.: "jdnsamatch R0,label,0x0c,\002aa\005local\0\0"
*/
#define JDNSAMATCH_EXT_OPCODE 44
+#define JDNSAMATCHSAFE_EXT_OPCODE 46
#define EXTRACT_OPCODE(i) (((i) >> 3) & 31)
#define EXTRACT_REGISTER(i) ((i) & 1)
@@ -689,7 +693,7 @@
#define ASSERT_IN_OUTPUT_BOUNDS(p, size) ASSERT_RETURN(IN_OUTPUT_BOUNDS(p, size))
/* Decode the imm length, does not do range checking. */
-/* But note that program is at least 16 bytes shorter than ram, so first few */
+/* But note that program is at least 20 bytes shorter than ram, so first few */
/* immediates can always be safely decoded without exceeding ram buffer. */
#define DECODE_IMM(value, length) \
do { \
@@ -890,31 +894,34 @@
tx_buf_len = 0;
if (ret) { counter[-4]++; return PASS_PACKET; } /* transmit failure */
break;
- case JDNSQMATCH_EXT_OPCODE:
- case JDNSAMATCH_EXT_OPCODE: {
+ case JDNSQMATCH_EXT_OPCODE: /* 43 */
+ case JDNSAMATCH_EXT_OPCODE: /* 44 */
+ case JDNSQMATCHSAFE_EXT_OPCODE: /* 45 */
+ case JDNSAMATCHSAFE_EXT_OPCODE: { /* 46 */
const u32 imm_len = 1 << (len_field - 1);
u32 jump_offs;
DECODE_IMM(jump_offs, imm_len); /* 2nd imm, at worst 8 bytes past prog_len */
int qtype = -1;
- if (imm == JDNSQMATCH_EXT_OPCODE) {
+ if (imm & 1) { /* JDNSQMATCH & JDNSQMATCHSAFE are *odd* extended opcodes */
DECODE_IMM(qtype, 1); /* 3rd imm, at worst 9 bytes past prog_len */
}
u32 udp_payload_offset = registers[0];
- int match_rst = match_names(program + pc,
- program + program_len,
- packet + udp_payload_offset,
- packet_len - udp_payload_offset,
- qtype);
- if (match_rst == -1) return PASS_PACKET;
+ match_result_type match_rst = match_names(program + pc,
+ program + program_len,
+ packet + udp_payload_offset,
+ packet_len - udp_payload_offset,
+ qtype);
+ if (match_rst == error_program) return PASS_PACKET;
+ if (match_rst == error_packet) {
+ counter[-5]++; /* increment error dns packet counter */
+ return (imm >= JDNSQMATCHSAFE_EXT_OPCODE) ? PASS_PACKET : DROP_PACKET;
+ }
while (pc + 1 < program_len && !(program[pc] == 0 && program[pc + 1] == 0)) {
pc++;
}
pc += 2;
- if (reg_num == 0 && match_rst == 0) {
- pc += jump_offs;
- } else if (reg_num == 1 && match_rst == 1) {
- pc += jump_offs;
- }
+ /* relies on reg_num in {0,1} and match_rst being {false=0, true=1} */
+ if (!(reg_num ^ (u32)match_rst)) pc += jump_offs;
break;
}
case EWRITE1_EXT_OPCODE:
@@ -1027,9 +1034,9 @@
/* We also don't want garbage like program_len == 0xFFFFFFFF */
if ((program_len | ram_len) >> 31) return PASS_PACKET;
- /* APFv6 requires at least 4 u32 counters at the end of ram, this makes counter[-4]++ valid */
+ /* APFv6 requires at least 5 u32 counters at the end of ram, this makes counter[-5]++ valid */
/* This cannot wrap due to previous check. */
- if (program_len + 16 > ram_len) return PASS_PACKET;
+ if (program_len + 20 > ram_len) return PASS_PACKET;
return do_apf_run(ctx, (u8*)program, program_len, ram_len, packet, packet_len, filter_age_16384ths);
}
diff --git a/v5/apf_interpreter_source.c b/v5/apf_interpreter_source.c
index 1a3ba9d..16106f4 100644
--- a/v5/apf_interpreter_source.c
+++ b/v5/apf_interpreter_source.c
@@ -115,7 +115,7 @@
#define ASSERT_IN_OUTPUT_BOUNDS(p, size) ASSERT_RETURN(IN_OUTPUT_BOUNDS(p, size))
// Decode the imm length, does not do range checking.
-// But note that program is at least 16 bytes shorter than ram, so first few
+// But note that program is at least 20 bytes shorter than ram, so first few
// immediates can always be safely decoded without exceeding ram buffer.
#define DECODE_IMM(value, length) \
do { \
@@ -316,31 +316,34 @@
tx_buf_len = 0;
if (ret) { counter[-4]++; return PASS_PACKET; } // transmit failure
break;
- case JDNSQMATCH_EXT_OPCODE:
- case JDNSAMATCH_EXT_OPCODE: {
+ case JDNSQMATCH_EXT_OPCODE: // 43
+ case JDNSAMATCH_EXT_OPCODE: // 44
+ case JDNSQMATCHSAFE_EXT_OPCODE: // 45
+ case JDNSAMATCHSAFE_EXT_OPCODE: { // 46
const u32 imm_len = 1 << (len_field - 1);
u32 jump_offs;
DECODE_IMM(jump_offs, imm_len); // 2nd imm, at worst 8 bytes past prog_len
int qtype = -1;
- if (imm == JDNSQMATCH_EXT_OPCODE) {
+ if (imm & 1) { // JDNSQMATCH & JDNSQMATCHSAFE are *odd* extended opcodes
DECODE_IMM(qtype, 1); // 3rd imm, at worst 9 bytes past prog_len
}
u32 udp_payload_offset = registers[0];
- int match_rst = match_names(program + pc,
- program + program_len,
- packet + udp_payload_offset,
- packet_len - udp_payload_offset,
- qtype);
- if (match_rst == -1) return PASS_PACKET;
+ match_result_type match_rst = match_names(program + pc,
+ program + program_len,
+ packet + udp_payload_offset,
+ packet_len - udp_payload_offset,
+ qtype);
+ if (match_rst == error_program) return PASS_PACKET;
+ if (match_rst == error_packet) {
+ counter[-5]++; // increment error dns packet counter
+ return (imm >= JDNSQMATCHSAFE_EXT_OPCODE) ? PASS_PACKET : DROP_PACKET;
+ }
while (pc + 1 < program_len && !(program[pc] == 0 && program[pc + 1] == 0)) {
pc++;
}
pc += 2;
- if (reg_num == 0 && match_rst == 0) {
- pc += jump_offs;
- } else if (reg_num == 1 && match_rst == 1) {
- pc += jump_offs;
- }
+ // relies on reg_num in {0,1} and match_rst being {false=0, true=1}
+ if (!(reg_num ^ (u32)match_rst)) pc += jump_offs;
break;
}
case EWRITE1_EXT_OPCODE:
@@ -453,9 +456,9 @@
// We also don't want garbage like program_len == 0xFFFFFFFF
if ((program_len | ram_len) >> 31) return PASS_PACKET;
- // APFv6 requires at least 4 u32 counters at the end of ram, this makes counter[-4]++ valid
+ // APFv6 requires at least 5 u32 counters at the end of ram, this makes counter[-5]++ valid
// This cannot wrap due to previous check.
- if (program_len + 16 > ram_len) return PASS_PACKET;
+ if (program_len + 20 > ram_len) return PASS_PACKET;
return do_apf_run(ctx, (u8*)program, program_len, ram_len, packet, packet_len, filter_age_16384ths);
}