Re: switch(4): add more input validations

2016-10-31 Thread Rafael Zalamena
On Fri, Oct 28, 2016 at 07:56:12PM +0400, Reyk Floeter wrote:
> > On 28.10.2016, at 19:20, Rafael Zalamena  wrote:
> > This diff teaches switch(4) how to do more validations on dynamic input
> > field types, like: ofp_match (has N oxms), ofp_action_header (might be
> > followed by N actions) and ofp_instruction (might have N actions inside).
> > 
> > This is important because the internal switch structures reuse the ofp_match
> > and friends from the packet and blindly trusts it to be correct, so to be
> > able to do that we must ensure that we are receiving them correctly.
> > We need to pay special attention to the fields that these macros use:
> > - OFP_OXM_FOREACH;
> > - OFP_ACTION_FOREACH;
> > - OFP_FLOW_MOD_MSG_INSTRUCTION_OFFSET;
> > - OFP_FLOW_MOD_INSTRUCTON_FOREACH;
> > - OFP_I_ACTIONS_FOREACH;
> > - OFP_BUCKETS_FOREACH;
> ---snip---

I've commited most of the small diffs, but one of them turned out to be
different so I sent another email ('switch(4): input validation:
swofp_flow_entry_put_instructions').

Here is the diff after all those commits, with just one small change:
I have implemented set_queue action validation that was missing.

ok?

Index: switchofp.c
===
RCS file: /cvs/src/sys/net/switchofp.c,v
retrieving revision 1.23
diff -u -p -r1.23 switchofp.c
--- switchofp.c 31 Oct 2016 08:06:27 -  1.23
+++ switchofp.c 31 Oct 2016 14:52:13 -
@@ -211,8 +211,11 @@ int swofp_flow_cmp_strict(struct swofp_
 int swofp_flow_filter(struct swofp_flow_entry *, uint64_t, uint64_t,
uint32_t, uint32_t);
 voidswofp_flow_timeout(struct switch_softc *);
+int swofp_validate_oxm(struct ofp_ox_match *, int *);
 int swofp_validate_flow_match(struct ofp_match *, int *);
-int swofp_validate_flow_instruction(struct ofp_instruction *, int *);
+int swofp_validate_flow_instruction(struct ofp_instruction *, size_t,
+   int *);
+int swofp_validate_action(struct ofp_action_header *, size_t, int *);
 
 /*
  * OpenFlow protocol compare oxm
@@ -1807,75 +1810,242 @@ swofp_ox_cmp_ether_addr(struct ofp_ox_ma
}
 }
 
-
-/* TODO: validation for match */
 int
-swofp_validate_flow_match(struct ofp_match *om, int *err)
+swofp_validate_oxm(struct ofp_ox_match *oxm, int *err)
 {
-   struct ofp_oxm_class *handler;
-   struct ofp_ox_match *oxm;
+   struct ofp_oxm_class*handler;
+   int  length, hasmask;
+   int  neededlen;
 
-   OFP_OXM_FOREACH(om, ntohs(om->om_length), oxm) {
-   handler = swofp_lookup_oxm_handler(oxm);
-   if (handler == NULL ||
-   handler->oxm_match == NULL) {
-   *err = OFP_ERRMATCH_BAD_FIELD;
-   return (-1);
-   }
+   handler = swofp_lookup_oxm_handler(oxm);
+   if (handler == NULL || handler->oxm_match == NULL) {
+   *err = OFP_ERRMATCH_BAD_FIELD;
+   return (-1);
+   }
+
+   hasmask = OFP_OXM_GET_HASMASK(oxm);
+   length = oxm->oxm_length;
+
+   neededlen = (hasmask) ?
+   (handler->oxm_len * 2) : (handler->oxm_len);
+   if (oxm->oxm_length != neededlen) {
+   *err = OFP_ERRMATCH_BAD_LEN;
+   return (-1);
}
 
return (0);
 }
 
 int
-swofp_validate_flow_action_set_field(struct ofp_action_set_field *oasf)
+swofp_validate_flow_match(struct ofp_match *om, int *err)
 {
-   struct ofp_ox_match *oxm;
-   struct ofp_oxm_class*handler;
-
-   oxm = (struct ofp_ox_match *)oasf->asf_field;
+   struct ofp_ox_match *oxm;
 
-   handler = swofp_lookup_oxm_handler(oxm);
-   if (handler == NULL)
-   return (OFP_ERRACTION_SET_TYPE);
-   if (handler->oxm_set == NULL)
-   return (OFP_ERRACTION_SET_TYPE);
+   /*
+* TODO this function is missing checks for:
+* - OFP_ERRMATCH_BAD_TAG;
+* - OFP_ERRMATCH_BAD_VALUE;
+* - OFP_ERRMATCH_BAD_MASK;
+* - OFP_ERRMATCH_BAD_PREREQ;
+* - OFP_ERRMATCH_DUP_FIELD;
+*/
+   OFP_OXM_FOREACH(om, ntohs(om->om_length), oxm) {
+   if (swofp_validate_oxm(oxm, err))
+   return (*err);
+   }
 
return (0);
 }
 
-/* TODO: validation for instruction */
 int
-swofp_validate_flow_instruction(struct ofp_instruction *oi, int *error)
+swofp_validate_flow_instruction(struct ofp_instruction *oi, size_t total,
+int *err)
 {
struct ofp_action_header*oah;
struct ofp_instruction_actions  *oia;
+   int  ilen;
+
+   ilen = ntohs(oi->i_len);
+   /* Check for bigger than packet or smaller than header. */
+   if (ilen > total || ilen < sizeof(*oi)) {
+   *err = OFP_ERRINST_BAD_LEN;
+   return (-1);
+   }
 
switch (ntohs(oi->i_type)) {
case OFP_INSTRUCTION_T_GOTO

Re: switch(4): add more input validations

2016-10-28 Thread Reyk Floeter

> On 28.10.2016, at 19:20, Rafael Zalamena  wrote:
> 
> This diff teaches switch(4) how to do more validations on dynamic input
> field types, like: ofp_match (has N oxms), ofp_action_header (might be
> followed by N actions) and ofp_instruction (might have N actions inside).
> 
> This is important because the internal switch structures reuse the ofp_match
> and friends from the packet and blindly trusts it to be correct, so to be
> able to do that we must ensure that we are receiving them correctly.
> We need to pay special attention to the fields that these macros use:
> - OFP_OXM_FOREACH;
> - OFP_ACTION_FOREACH;
> - OFP_FLOW_MOD_MSG_INSTRUCTION_OFFSET;
> - OFP_FLOW_MOD_INSTRUCTON_FOREACH;
> - OFP_I_ACTIONS_FOREACH;
> - OFP_BUCKETS_FOREACH;
> 

Yes, adding the validation step is the right approach!

And this approach is what we also do in switchd (and iked's ikev2 parser).

> Other small fixes:

Small fixes should go in separately:

> - Simplify OFP_FLOW_MOD_MSG_INSTRUCTION_OFFSET() macro;

OK

> - Change swofp_flow_table_add() malloc behaviour to be like all the rest
>   of the code (don't wait for memory);

OK

> - swofp_flow_entry_put_instructions() doesn't need a pointer to an mbuf
>   pointer, we only read it;

OK

> - Change the validation function parameters: we can't check for non-errors
>   using the value 0, because the spec actually uses it for some errors.
>   Instead use int pointer to get error value and use the return value only
>   to find out if the validation succeeded or not;

OK

> - Return more accurate error messages: some error messages were being sent
>   with the wrong type/code combination;

OK

> - Removed swofp_validate_flow_action_set_field() as it was incorporated
>   in action validation;
> 

This is part of the validation diff.

Could you commit the small fixes first and send an updated validation diff?

> TODO for next diffs:
> - We still need to code the validation for group messages, but since I
>   haven't started using it yet, I didn't touch it;
> - We still need to implement validation for some specific OXM errors:
>   duplicated OXM, invalid wildcard, invalid value or missing prereq;
> - swofp_validate_buckets() could use validate_action() with some effort;
> 

Sure

> Even though switchd(8) does packet validation (both on input and output),
> we should commit this to enable other vendors/software to use the OpenBSD
> switch(4) directly, otherwise we will need more effort to implement this
> for switchd(8) relaying and force people to use switchd(8) needlessly.
> 

Full ack,

a) it is a very important design decision that switch(4) does NOT depend
on switchd(8). This is not 100% true at the moment (as switch(4) doesn't fully
behave like a normal socket, eg. it only supports full single packet read/write 
I/O),
but it should be possible to connect any user space controller to /dev/switch0,
eg. by using a tool like socat to connect it to ryu instead of switchd.

b) The kernel should never "trust" user land to do the right thing. Crashing it 
from
user input via /dev/switch* would be a bad thing. And we cannot even guarantee
that switchd(8) is absolutely safe.

> ok?
> 

Small fixes OK first, see above.

Reyk

> Index: net/ofp.h
> ===
> RCS file: /home/obsdcvs/src/sys/net/ofp.h,v
> retrieving revision 1.2
> diff -u -p -r1.2 ofp.h
> --- net/ofp.h 30 Sep 2016 12:40:00 -  1.2
> +++ net/ofp.h 25 Oct 2016 08:50:14 -
> @@ -95,7 +95,7 @@ struct ofp_hello_element_versionbitmap {
> 
> /* Ports */
> #define OFP_PORT_MAX  0xff00  /* Maximum number of physical 
> ports */
> -#define  OFP_PORT_INPUT  0xfff8  /* Send back to input 
> port */
> +#define  OFP_PORT_INPUT  0xfff8  /* Send back to input 
> port */
> #define OFP_PORT_FLOWTABLE0xfff9  /* Perform actions in flow 
> table */
> #define OFP_PORT_NORMAL   0xfffa  /* Let switch decide */
> #define OFP_PORT_FLOOD0xfffb  /* All non-block ports 
> except input */
> @@ -179,9 +179,9 @@ struct ofp_switch_features {
> 
> /* Switch capabilities */
> #define OFP_SWCAP_FLOW_STATS  0x1 /* Flow statistics */
> -#define OFP_SWCAP_TABLE_STATS0x2 /* Table statistics */
> -#define OFP_SWCAP_PORT_STATS 0x4 /* Port statistics */
> -#define OFP_SWCAP_GROUP_STATS0x8 /* Group statistics */
> +#define OFP_SWCAP_TABLE_STATS0x2 /* Table statistics */
> +#define OFP_SWCAP_PORT_STATS 0x4 /* Port statistics */
> +#define OFP_SWCAP_GROUP_STATS0x8 /* Group statistics */
> #define OFP_SWCAP_IP_REASM0x20/* Can reassemble IP frags */
> #define OFP_SWCAP_QUEUE_STATS 0x40/* Queue statistics */
> #define OFP_SWCAP_ARP_MATCH_IP0x80/* Match IP addresses 
> in ARP pkts */
> @@ -314,15 +314,15 @@ struct ofp_acti

switch(4): add more input validations

2016-10-28 Thread Rafael Zalamena
This diff teaches switch(4) how to do more validations on dynamic input
field types, like: ofp_match (has N oxms), ofp_action_header (might be
followed by N actions) and ofp_instruction (might have N actions inside).

This is important because the internal switch structures reuse the ofp_match
and friends from the packet and blindly trusts it to be correct, so to be
able to do that we must ensure that we are receiving them correctly.
We need to pay special attention to the fields that these macros use:
 - OFP_OXM_FOREACH;
 - OFP_ACTION_FOREACH;
 - OFP_FLOW_MOD_MSG_INSTRUCTION_OFFSET;
 - OFP_FLOW_MOD_INSTRUCTON_FOREACH;
 - OFP_I_ACTIONS_FOREACH;
 - OFP_BUCKETS_FOREACH;

Other small fixes:
 - Simplify OFP_FLOW_MOD_MSG_INSTRUCTION_OFFSET() macro;
 - Change swofp_flow_table_add() malloc behaviour to be like all the rest
   of the code (don't wait for memory);
 - swofp_flow_entry_put_instructions() doesn't need a pointer to an mbuf
   pointer, we only read it;
 - Change the validation function parameters: we can't check for non-errors
   using the value 0, because the spec actually uses it for some errors.
   Instead use int pointer to get error value and use the return value only
   to find out if the validation succeeded or not;
 - Return more accurate error messages: some error messages were being sent
   with the wrong type/code combination;
 - Removed swofp_validate_flow_action_set_field() as it was incorporated
   in action validation;

TODO for next diffs:
 - We still need to code the validation for group messages, but since I
   haven't started using it yet, I didn't touch it;
 - We still need to implement validation for some specific OXM errors:
   duplicated OXM, invalid wildcard, invalid value or missing prereq;
 - swofp_validate_buckets() could use validate_action() with some effort;

Even though switchd(8) does packet validation (both on input and output),
we should commit this to enable other vendors/software to use the OpenBSD
switch(4) directly, otherwise we will need more effort to implement this
for switchd(8) relaying and force people to use switchd(8) needlessly.

ok?

Index: net/ofp.h
===
RCS file: /home/obsdcvs/src/sys/net/ofp.h,v
retrieving revision 1.2
diff -u -p -r1.2 ofp.h
--- net/ofp.h   30 Sep 2016 12:40:00 -  1.2
+++ net/ofp.h   25 Oct 2016 08:50:14 -
@@ -95,7 +95,7 @@ struct ofp_hello_element_versionbitmap {
 
 /* Ports */
 #define OFP_PORT_MAX   0xff00  /* Maximum number of physical 
ports */
-#defineOFP_PORT_INPUT  0xfff8  /* Send back to input 
port */
+#defineOFP_PORT_INPUT  0xfff8  /* Send back to input 
port */
 #define OFP_PORT_FLOWTABLE 0xfff9  /* Perform actions in flow 
table */
 #define OFP_PORT_NORMAL0xfffa  /* Let switch decide */
 #define OFP_PORT_FLOOD 0xfffb  /* All non-block ports except 
input */
@@ -179,9 +179,9 @@ struct ofp_switch_features {
 
 /* Switch capabilities */
 #define OFP_SWCAP_FLOW_STATS   0x1 /* Flow statistics */
-#define OFP_SWCAP_TABLE_STATS  0x2 /* Table statistics */
-#define OFP_SWCAP_PORT_STATS   0x4 /* Port statistics */
-#define OFP_SWCAP_GROUP_STATS  0x8 /* Group statistics */
+#define OFP_SWCAP_TABLE_STATS  0x2 /* Table statistics */
+#define OFP_SWCAP_PORT_STATS   0x4 /* Port statistics */
+#define OFP_SWCAP_GROUP_STATS  0x8 /* Group statistics */
 #define OFP_SWCAP_IP_REASM 0x20/* Can reassemble IP frags */
 #define OFP_SWCAP_QUEUE_STATS  0x40/* Queue statistics */
 #define OFP_SWCAP_ARP_MATCH_IP 0x80/* Match IP addresses in ARP 
pkts */
@@ -314,15 +314,15 @@ struct ofp_action_mpls_ttl {
 struct ofp_action_push {
uint16_tap_type;
uint16_tap_len;
-   uint16_tap_ethertype;
-   uint8_t pad[2];
+   uint16_tap_ethertype;
+   uint8_t ap_pad[2];
 } __packed;
 
 struct ofp_action_pop_mpls {
uint16_tapm_type;
uint16_tapm_len;
uint16_tapm_ethertype;
-   uint8_t pad[2];
+   uint8_t apm_pad[2];
 } __packed;
 
 struct ofp_action_group {
@@ -342,6 +342,12 @@ struct ofp_action_set_field {
uint16_tasf_type;
uint16_tasf_len;
uint8_t asf_field[4];
+} __packed;
+
+struct ofp_action_set_queue {
+   uint16_tasq_type;
+   uint16_tasq_len;
+   uint32_tasq_queue_id;
 } __packed;
 
 /* Packet-Out Message */
Index: net/switchofp.c
===
RCS file: /home/obsdcvs/src/sys/net/switchofp.c,v
retrieving revision 1.18
diff -u -p -r1.18 switchofp.c
--- net/switchofp.c 28 Oct 2016 09:01:49 -  1.18
+++ net/switchofp.c 28 Oct 2016 14:48:49