diff mbox series

[ovs-dev,RFC,1/2] northd: add ACL Sampling

Message ID 20221018155936.1394396-2-amorenoz@redhat.com
State RFC
Headers show
Series ACL Sampling using per-flow IPFIX | expand

Commit Message

Adrián Moreno Oct. 18, 2022, 3:59 p.m. UTC
Introduce a new table called Sample where per-flow IPFIX configuration
can be specified.
Also, reference rows from such table from the ACL table to enable the
configuration of ACL sampling. If enabled, northd will add a sample
action to each ACL-related logical flow.

Signed-off-by: Adrian Moreno <amorenoz@redhat.com>
---
 northd/northd.c  | 31 ++++++++++++++++++++++++++++++-
 ovn-nb.ovsschema | 23 ++++++++++++++++++++++-
 ovn-nb.xml       | 31 +++++++++++++++++++++++++++++++
 3 files changed, 83 insertions(+), 2 deletions(-)

Comments

Dumitru Ceara March 30, 2023, 10:02 a.m. UTC | #1
On 10/18/22 17:59, Adrian Moreno wrote:
> Introduce a new table called Sample where per-flow IPFIX configuration
> can be specified.
> Also, reference rows from such table from the ACL table to enable the
> configuration of ACL sampling. If enabled, northd will add a sample
> action to each ACL-related logical flow.
> 
> Signed-off-by: Adrian Moreno <amorenoz@redhat.com>
> ---
>  northd/northd.c  | 31 ++++++++++++++++++++++++++++++-
>  ovn-nb.ovsschema | 23 ++++++++++++++++++++++-
>  ovn-nb.xml       | 31 +++++++++++++++++++++++++++++++
>  3 files changed, 83 insertions(+), 2 deletions(-)
> 
> diff --git a/northd/northd.c b/northd/northd.c
> index 61d474840..3e09e3a0f 100644
> --- a/northd/northd.c
> +++ b/northd/northd.c
> @@ -6194,6 +6194,27 @@ build_acl_log(struct ds *actions, const struct nbrec_acl *acl,
>      ds_put_cstr(actions, "); ");
>  }
>  
> +static void
> +build_acl_sample(struct ds *actions, const struct nbrec_acl *acl)
> +{
> +    if (!acl->sample) {
> +        return;
> +    }
> +    ds_put_format(actions, "sample(probability=%"PRId16","
> +                           "collector_set=%d,"
> +                           "obs_domain=%hd,",
> +                            (uint16_t) acl->sample->probability,
> +                            (uint32_t) acl->sample->collector_set_id,
> +                            (uint8_t) acl->sample->obs_domain_id);
> +
> +    if (acl->sample->obs_point_id) {
> +        ds_put_format(actions, "obs_point=%"PRId32");",
> +                      (uint32_t) *acl->sample->obs_point_id);
> +    } else {
> +        ds_put_cstr(actions, "obs_point=$cookie);");
> +    }
> +}
> +
>  static void
>  build_reject_acl_rules(struct ovn_datapath *od, struct hmap *lflows,
>                         enum ovn_stage stage, struct nbrec_acl *acl,
> @@ -6260,6 +6281,7 @@ consider_acl(struct hmap *lflows, struct ovn_datapath *od,
>      if (!strcmp(acl->action, "allow-stateless")) {
>          ds_clear(actions);
>          build_acl_log(actions, acl, meter_groups);
> +        build_acl_sample(actions, acl);
>          ds_put_cstr(actions, "next;");
>          ovn_lflow_add_with_hint(lflows, od, stage,
>                                  acl->priority + OVN_ACL_PRI_OFFSET,
> @@ -6275,6 +6297,7 @@ consider_acl(struct hmap *lflows, struct ovn_datapath *od,
>          if (!has_stateful) {
>              ds_clear(actions);
>              build_acl_log(actions, acl, meter_groups);
> +            build_acl_sample(actions, acl);
>              ds_put_cstr(actions, "next;");
>              ovn_lflow_add_with_hint(lflows, od, stage,
>                                      acl->priority + OVN_ACL_PRI_OFFSET,
> @@ -6304,6 +6327,7 @@ consider_acl(struct hmap *lflows, struct ovn_datapath *od,
>                                REG_LABEL" = %"PRId64"; ", acl->label);
>              }
>              build_acl_log(actions, acl, meter_groups);
> +            build_acl_sample(actions, acl);
>              ds_put_cstr(actions, "next;");
>              ovn_lflow_add_with_hint(lflows, od, stage,
>                                      acl->priority + OVN_ACL_PRI_OFFSET,
> @@ -6329,6 +6353,7 @@ consider_acl(struct hmap *lflows, struct ovn_datapath *od,
>                                REG_LABEL" = %"PRId64"; ", acl->label);
>              }
>              build_acl_log(actions, acl, meter_groups);
> +            build_acl_sample(actions, acl);
>              ds_put_cstr(actions, "next;");
>              ovn_lflow_add_with_hint(lflows, od, stage,
>                                      acl->priority + OVN_ACL_PRI_OFFSET,
> @@ -6349,7 +6374,7 @@ consider_acl(struct hmap *lflows, struct ovn_datapath *od,
>               */
>              bool log_related = smap_get_bool(&acl->options, "log-related",
>                                               false);
> -            if (acl->log && acl->label && log_related) {
> +            if ((acl->log || acl->sample) && acl->label && log_related) {
>                  /* Related/reply flows need to be set on the opposite pipeline
>                   * from where the ACL itself is set.
>                   */
> @@ -6365,6 +6390,7 @@ consider_acl(struct hmap *lflows, struct ovn_datapath *od,
>                                use_ct_inv_match ? " && !ct.inv" : "",
>                                ct_blocked_match, acl->label);
>                  build_acl_log(actions, acl, meter_groups);
> +                build_acl_sample(actions, acl);
>                  ds_put_cstr(actions, "next;");
>                  ovn_lflow_add_with_hint(lflows, od, log_related_stage,
>                                          UINT16_MAX - 2,
> @@ -6402,6 +6428,7 @@ consider_acl(struct hmap *lflows, struct ovn_datapath *od,
>              } else {
>                  ds_put_format(match, " && (%s)", acl->match);
>                  build_acl_log(actions, acl, meter_groups);
> +                build_acl_sample(actions, acl);
>                  ds_put_cstr(actions, debug_implicit_drop_action());
>                  ovn_lflow_add_with_hint(lflows, od, stage,
>                                          acl->priority + OVN_ACL_PRI_OFFSET,
> @@ -6430,6 +6457,7 @@ consider_acl(struct hmap *lflows, struct ovn_datapath *od,
>              } else {
>                  ds_put_format(match, " && (%s)", acl->match);
>                  build_acl_log(actions, acl, meter_groups);
> +                build_acl_sample(actions, acl);
>                  ds_put_cstr(actions, debug_implicit_drop_action());
>                  ovn_lflow_add_with_hint(lflows, od, stage,
>                                          acl->priority + OVN_ACL_PRI_OFFSET,
> @@ -6447,6 +6475,7 @@ consider_acl(struct hmap *lflows, struct ovn_datapath *od,
>                                         actions, &acl->header_, meter_groups);
>              } else {
>                  build_acl_log(actions, acl, meter_groups);
> +                build_acl_sample(actions, acl);
>                  ds_put_cstr(actions, debug_implicit_drop_action());
>                  ovn_lflow_add_with_hint(lflows, od, stage,
>                                          acl->priority + OVN_ACL_PRI_OFFSET,
> diff --git a/ovn-nb.ovsschema b/ovn-nb.ovsschema
> index 174364c8b..6178b532e 100644
> --- a/ovn-nb.ovsschema
> +++ b/ovn-nb.ovsschema
> @@ -1,7 +1,7 @@
>  {
>      "name": "OVN_Northbound",
>      "version": "6.3.0",
> -    "cksum": "4042813038 31869",
> +    "cksum": "3795038812 33116",
>      "tables": {
>          "NB_Global": {
>              "columns": {
> @@ -30,6 +30,23 @@
>                  "ipsec": {"type": "boolean"}},
>              "maxRows": 1,
>              "isRoot": true},
> +        "Sample": {
> +            "columns": {
> +                "probability": {"type": {"key": {"type": "integer",
> +                                                      "minInteger": 0,
> +                                                      "maxInteger": 65535}}},
> +                "collector_set_id": {"type": {"key": {"type": "integer",
> +                                                      "minInteger": 0,
> +                                                   "maxInteger": 4294967295}}},
> +                "obs_domain_id": {"type": {"key": {"type": "integer",
> +                                                   "minInteger": 0,
> +                                                   "maxInteger": 255}}},
> +                "obs_point_id": {"type": {"key": {"type": "integer",
> +                                                  "minInteger": 0,
> +                                                  "maxInteger": 4294967295},
> +                                          "min": 0, "max":1}}
> +            }

This seems to match 1:1 the arguments of the OVS SAMPLE action.  That's
fine but do we expect more arguments to be added in the future?

Please add a column that can be used as index and also define a unique
index.  That will save us from trouble like "spurious duplicates" [0]
when the DB is in RAFT.  It also makes it easier for the CMS to track
what it installed or not.  An external_ids column might also make sense.

[0]
https://github.com/openvswitch/ovs/commit/04e5adfedd2a2e4ceac62136671057542a7cf875

> +        },

Nit: By default "isRoot" is false.  Don't we want this as root table?

>          "Copp": {
>              "columns": {
>                  "name": {"type": "string"},
> @@ -267,6 +284,10 @@
>                  "label": {"type": {"key": {"type": "integer",
>                                             "minInteger": 0,
>                                             "maxInteger": 4294967295}}},
> +                "sample": {"type": {"key": {"type": "uuid",
> +                                            "refTable": "Sample",
> +                                            "refType": "strong"},
> +                                    "min": 0, "max": 1}},
>                  "options": {
>                       "type": {"key": "string",
>                                "value": "string",
> diff --git a/ovn-nb.xml b/ovn-nb.xml
> index 9581aef7c..e818fd8d1 100644
> --- a/ovn-nb.xml
> +++ b/ovn-nb.xml
> @@ -387,6 +387,31 @@
>  
>    </table>
>  
> +  <table name="Sample" title="Sample">
> +    <p>
> +      This table describes an IPFIX Sampling Point. Entries in other tables
> +      might be associated with Sample entries to indicate how the sample
> +      should be generated.
> +
> +      For an example, see <ref table="ACL"/>.
> +    </p>
> +    <column name="probability">
> +      Sampling probability. It must be an integer number between 0 and 65535.
> +    </column>
> +    <column name="collector_set_id">
> +      The 32-bit integer identifier of the set of of collectors to send
> +      packets to. See Flow_Sample_Collector_Set Table in ovs-vswitchd's
> +      database schema.
> +    </column>
> +    <column name="obs_domain_id">
> +      The 8 most significant bits of the Observation Domain ID that will be
> +      added to evvery IPFIX sample. The 24 LSB will be the datapath key.
> +    </column>
> +    <column name="obs_point_id">
> +      If set, it'll be use as Observation Point ID in every IPFIX sample.
> +      Otherwise the Logical Flow's coockie will be used.
> +    </column>
> +  </table>
>    <table name="Copp" title="Control plane protection">
>      <p>
>        This table is used to define control plane protection policies, i.e.,
> @@ -2191,6 +2216,12 @@
>        </column>
>      </group>
>  
> +    <column name="sample">
> +      <p>
> +        The entry in the <ref table="Sample"/> table to use for sampling.
> +      </p>
> +    </column>
> +
>      <group title="Common Columns">
>        <column name="options">
>          This column provides general key/value settings. The supported
Adrián Moreno April 5, 2023, 7:20 a.m. UTC | #2
Thanks for the comments Dumitru!

On 3/30/23 12:02, Dumitru Ceara wrote:
> On 10/18/22 17:59, Adrian Moreno wrote:
>> Introduce a new table called Sample where per-flow IPFIX configuration
>> can be specified.
>> Also, reference rows from such table from the ACL table to enable the
>> configuration of ACL sampling. If enabled, northd will add a sample
>> action to each ACL-related logical flow.
>>
>> Signed-off-by: Adrian Moreno <amorenoz@redhat.com>
>> ---
>>   northd/northd.c  | 31 ++++++++++++++++++++++++++++++-
>>   ovn-nb.ovsschema | 23 ++++++++++++++++++++++-
>>   ovn-nb.xml       | 31 +++++++++++++++++++++++++++++++
>>   3 files changed, 83 insertions(+), 2 deletions(-)
>>
>> diff --git a/northd/northd.c b/northd/northd.c
>> index 61d474840..3e09e3a0f 100644
>> --- a/northd/northd.c
>> +++ b/northd/northd.c
>> @@ -6194,6 +6194,27 @@ build_acl_log(struct ds *actions, const struct nbrec_acl *acl,
>>       ds_put_cstr(actions, "); ");
>>   }
>>   
>> +static void
>> +build_acl_sample(struct ds *actions, const struct nbrec_acl *acl)
>> +{
>> +    if (!acl->sample) {
>> +        return;
>> +    }
>> +    ds_put_format(actions, "sample(probability=%"PRId16","
>> +                           "collector_set=%d,"
>> +                           "obs_domain=%hd,",
>> +                            (uint16_t) acl->sample->probability,
>> +                            (uint32_t) acl->sample->collector_set_id,
>> +                            (uint8_t) acl->sample->obs_domain_id);
>> +
>> +    if (acl->sample->obs_point_id) {
>> +        ds_put_format(actions, "obs_point=%"PRId32");",
>> +                      (uint32_t) *acl->sample->obs_point_id);
>> +    } else {
>> +        ds_put_cstr(actions, "obs_point=$cookie);");
>> +    }
>> +}
>> +
>>   static void
>>   build_reject_acl_rules(struct ovn_datapath *od, struct hmap *lflows,
>>                          enum ovn_stage stage, struct nbrec_acl *acl,
>> @@ -6260,6 +6281,7 @@ consider_acl(struct hmap *lflows, struct ovn_datapath *od,
>>       if (!strcmp(acl->action, "allow-stateless")) {
>>           ds_clear(actions);
>>           build_acl_log(actions, acl, meter_groups);
>> +        build_acl_sample(actions, acl);
>>           ds_put_cstr(actions, "next;");
>>           ovn_lflow_add_with_hint(lflows, od, stage,
>>                                   acl->priority + OVN_ACL_PRI_OFFSET,
>> @@ -6275,6 +6297,7 @@ consider_acl(struct hmap *lflows, struct ovn_datapath *od,
>>           if (!has_stateful) {
>>               ds_clear(actions);
>>               build_acl_log(actions, acl, meter_groups);
>> +            build_acl_sample(actions, acl);
>>               ds_put_cstr(actions, "next;");
>>               ovn_lflow_add_with_hint(lflows, od, stage,
>>                                       acl->priority + OVN_ACL_PRI_OFFSET,
>> @@ -6304,6 +6327,7 @@ consider_acl(struct hmap *lflows, struct ovn_datapath *od,
>>                                 REG_LABEL" = %"PRId64"; ", acl->label);
>>               }
>>               build_acl_log(actions, acl, meter_groups);
>> +            build_acl_sample(actions, acl);
>>               ds_put_cstr(actions, "next;");
>>               ovn_lflow_add_with_hint(lflows, od, stage,
>>                                       acl->priority + OVN_ACL_PRI_OFFSET,
>> @@ -6329,6 +6353,7 @@ consider_acl(struct hmap *lflows, struct ovn_datapath *od,
>>                                 REG_LABEL" = %"PRId64"; ", acl->label);
>>               }
>>               build_acl_log(actions, acl, meter_groups);
>> +            build_acl_sample(actions, acl);
>>               ds_put_cstr(actions, "next;");
>>               ovn_lflow_add_with_hint(lflows, od, stage,
>>                                       acl->priority + OVN_ACL_PRI_OFFSET,
>> @@ -6349,7 +6374,7 @@ consider_acl(struct hmap *lflows, struct ovn_datapath *od,
>>                */
>>               bool log_related = smap_get_bool(&acl->options, "log-related",
>>                                                false);
>> -            if (acl->log && acl->label && log_related) {
>> +            if ((acl->log || acl->sample) && acl->label && log_related) {
>>                   /* Related/reply flows need to be set on the opposite pipeline
>>                    * from where the ACL itself is set.
>>                    */
>> @@ -6365,6 +6390,7 @@ consider_acl(struct hmap *lflows, struct ovn_datapath *od,
>>                                 use_ct_inv_match ? " && !ct.inv" : "",
>>                                 ct_blocked_match, acl->label);
>>                   build_acl_log(actions, acl, meter_groups);
>> +                build_acl_sample(actions, acl);
>>                   ds_put_cstr(actions, "next;");
>>                   ovn_lflow_add_with_hint(lflows, od, log_related_stage,
>>                                           UINT16_MAX - 2,
>> @@ -6402,6 +6428,7 @@ consider_acl(struct hmap *lflows, struct ovn_datapath *od,
>>               } else {
>>                   ds_put_format(match, " && (%s)", acl->match);
>>                   build_acl_log(actions, acl, meter_groups);
>> +                build_acl_sample(actions, acl);
>>                   ds_put_cstr(actions, debug_implicit_drop_action());
>>                   ovn_lflow_add_with_hint(lflows, od, stage,
>>                                           acl->priority + OVN_ACL_PRI_OFFSET,
>> @@ -6430,6 +6457,7 @@ consider_acl(struct hmap *lflows, struct ovn_datapath *od,
>>               } else {
>>                   ds_put_format(match, " && (%s)", acl->match);
>>                   build_acl_log(actions, acl, meter_groups);
>> +                build_acl_sample(actions, acl);
>>                   ds_put_cstr(actions, debug_implicit_drop_action());
>>                   ovn_lflow_add_with_hint(lflows, od, stage,
>>                                           acl->priority + OVN_ACL_PRI_OFFSET,
>> @@ -6447,6 +6475,7 @@ consider_acl(struct hmap *lflows, struct ovn_datapath *od,
>>                                          actions, &acl->header_, meter_groups);
>>               } else {
>>                   build_acl_log(actions, acl, meter_groups);
>> +                build_acl_sample(actions, acl);
>>                   ds_put_cstr(actions, debug_implicit_drop_action());
>>                   ovn_lflow_add_with_hint(lflows, od, stage,
>>                                           acl->priority + OVN_ACL_PRI_OFFSET,
>> diff --git a/ovn-nb.ovsschema b/ovn-nb.ovsschema
>> index 174364c8b..6178b532e 100644
>> --- a/ovn-nb.ovsschema
>> +++ b/ovn-nb.ovsschema
>> @@ -1,7 +1,7 @@
>>   {
>>       "name": "OVN_Northbound",
>>       "version": "6.3.0",
>> -    "cksum": "4042813038 31869",
>> +    "cksum": "3795038812 33116",
>>       "tables": {
>>           "NB_Global": {
>>               "columns": {
>> @@ -30,6 +30,23 @@
>>                   "ipsec": {"type": "boolean"}},
>>               "maxRows": 1,
>>               "isRoot": true},
>> +        "Sample": {
>> +            "columns": {
>> +                "probability": {"type": {"key": {"type": "integer",
>> +                                                      "minInteger": 0,
>> +                                                      "maxInteger": 65535}}},
>> +                "collector_set_id": {"type": {"key": {"type": "integer",
>> +                                                      "minInteger": 0,
>> +                                                   "maxInteger": 4294967295}}},
>> +                "obs_domain_id": {"type": {"key": {"type": "integer",
>> +                                                   "minInteger": 0,
>> +                                                   "maxInteger": 255}}},
>> +                "obs_point_id": {"type": {"key": {"type": "integer",
>> +                                                  "minInteger": 0,
>> +                                                  "maxInteger": 4294967295},
>> +                                          "min": 0, "max":1}}
>> +            }
> 
> This seems to match 1:1 the arguments of the OVS SAMPLE action.  That's
> fine but do we expect more arguments to be added in the future?

I can't think of any right now.

> 
> Please add a column that can be used as index and also define a unique
> index.  That will save us from trouble like "spurious duplicates" [0]
> when the DB is in RAFT.  It also makes it easier for the CMS to track
> what it installed or not.

Technically, the only unique element would be (collector_set_id, obs_domain_id, 
obs_point_id) tuple. I can add that as index. Would that work or are you 
referring  to a column called "id"?

  An external_ids column might also make sense.
> 

Yes it does.


> [0]
> https://github.com/openvswitch/ovs/commit/04e5adfedd2a2e4ceac62136671057542a7cf875
> 
>> +        },
> 
> Nit: By default "isRoot" is false.  Don't we want this as root table?
> 

Hmm... I don't know. Maybe we could delete

>>           "Copp": {
>>               "columns": {
>>                   "name": {"type": "string"},
>> @@ -267,6 +284,10 @@
>>                   "label": {"type": {"key": {"type": "integer",
>>                                              "minInteger": 0,
>>                                              "maxInteger": 4294967295}}},
>> +                "sample": {"type": {"key": {"type": "uuid",
>> +                                            "refTable": "Sample",
>> +                                            "refType": "strong"},
>> +                                    "min": 0, "max": 1}},
>>                   "options": {
>>                        "type": {"key": "string",
>>                                 "value": "string",
>> diff --git a/ovn-nb.xml b/ovn-nb.xml
>> index 9581aef7c..e818fd8d1 100644
>> --- a/ovn-nb.xml
>> +++ b/ovn-nb.xml
>> @@ -387,6 +387,31 @@
>>   
>>     </table>
>>   
>> +  <table name="Sample" title="Sample">
>> +    <p>
>> +      This table describes an IPFIX Sampling Point. Entries in other tables
>> +      might be associated with Sample entries to indicate how the sample
>> +      should be generated.
>> +
>> +      For an example, see <ref table="ACL"/>.
>> +    </p>
>> +    <column name="probability">
>> +      Sampling probability. It must be an integer number between 0 and 65535.
>> +    </column>
>> +    <column name="collector_set_id">
>> +      The 32-bit integer identifier of the set of of collectors to send
>> +      packets to. See Flow_Sample_Collector_Set Table in ovs-vswitchd's
>> +      database schema.
>> +    </column>
>> +    <column name="obs_domain_id">
>> +      The 8 most significant bits of the Observation Domain ID that will be
>> +      added to evvery IPFIX sample. The 24 LSB will be the datapath key.
>> +    </column>
>> +    <column name="obs_point_id">
>> +      If set, it'll be use as Observation Point ID in every IPFIX sample.
>> +      Otherwise the Logical Flow's coockie will be used.
>> +    </column>
>> +  </table>
>>     <table name="Copp" title="Control plane protection">
>>       <p>
>>         This table is used to define control plane protection policies, i.e.,
>> @@ -2191,6 +2216,12 @@
>>         </column>
>>       </group>
>>   
>> +    <column name="sample">
>> +      <p>
>> +        The entry in the <ref table="Sample"/> table to use for sampling.
>> +      </p>
>> +    </column>
>> +
>>       <group title="Common Columns">
>>         <column name="options">
>>           This column provides general key/value settings. The supported
>
Dumitru Ceara April 5, 2023, 1:59 p.m. UTC | #3
On 4/5/23 09:20, Adrian Moreno wrote:
> Thanks for the comments Dumitru!
> 
> On 3/30/23 12:02, Dumitru Ceara wrote:
>> On 10/18/22 17:59, Adrian Moreno wrote:
>>> Introduce a new table called Sample where per-flow IPFIX configuration
>>> can be specified.
>>> Also, reference rows from such table from the ACL table to enable the
>>> configuration of ACL sampling. If enabled, northd will add a sample
>>> action to each ACL-related logical flow.
>>>
>>> Signed-off-by: Adrian Moreno <amorenoz@redhat.com>
>>> ---
>>>   northd/northd.c  | 31 ++++++++++++++++++++++++++++++-
>>>   ovn-nb.ovsschema | 23 ++++++++++++++++++++++-
>>>   ovn-nb.xml       | 31 +++++++++++++++++++++++++++++++
>>>   3 files changed, 83 insertions(+), 2 deletions(-)
>>>
>>> diff --git a/northd/northd.c b/northd/northd.c
>>> index 61d474840..3e09e3a0f 100644
>>> --- a/northd/northd.c
>>> +++ b/northd/northd.c
>>> @@ -6194,6 +6194,27 @@ build_acl_log(struct ds *actions, const struct
>>> nbrec_acl *acl,
>>>       ds_put_cstr(actions, "); ");
>>>   }
>>>   +static void
>>> +build_acl_sample(struct ds *actions, const struct nbrec_acl *acl)
>>> +{
>>> +    if (!acl->sample) {
>>> +        return;
>>> +    }
>>> +    ds_put_format(actions, "sample(probability=%"PRId16","
>>> +                           "collector_set=%d,"
>>> +                           "obs_domain=%hd,",
>>> +                            (uint16_t) acl->sample->probability,
>>> +                            (uint32_t) acl->sample->collector_set_id,
>>> +                            (uint8_t) acl->sample->obs_domain_id);
>>> +
>>> +    if (acl->sample->obs_point_id) {
>>> +        ds_put_format(actions, "obs_point=%"PRId32");",
>>> +                      (uint32_t) *acl->sample->obs_point_id);
>>> +    } else {
>>> +        ds_put_cstr(actions, "obs_point=$cookie);");
>>> +    }
>>> +}
>>> +
>>>   static void
>>>   build_reject_acl_rules(struct ovn_datapath *od, struct hmap *lflows,
>>>                          enum ovn_stage stage, struct nbrec_acl *acl,
>>> @@ -6260,6 +6281,7 @@ consider_acl(struct hmap *lflows, struct
>>> ovn_datapath *od,
>>>       if (!strcmp(acl->action, "allow-stateless")) {
>>>           ds_clear(actions);
>>>           build_acl_log(actions, acl, meter_groups);
>>> +        build_acl_sample(actions, acl);
>>>           ds_put_cstr(actions, "next;");
>>>           ovn_lflow_add_with_hint(lflows, od, stage,
>>>                                   acl->priority + OVN_ACL_PRI_OFFSET,
>>> @@ -6275,6 +6297,7 @@ consider_acl(struct hmap *lflows, struct
>>> ovn_datapath *od,
>>>           if (!has_stateful) {
>>>               ds_clear(actions);
>>>               build_acl_log(actions, acl, meter_groups);
>>> +            build_acl_sample(actions, acl);
>>>               ds_put_cstr(actions, "next;");
>>>               ovn_lflow_add_with_hint(lflows, od, stage,
>>>                                       acl->priority +
>>> OVN_ACL_PRI_OFFSET,
>>> @@ -6304,6 +6327,7 @@ consider_acl(struct hmap *lflows, struct
>>> ovn_datapath *od,
>>>                                 REG_LABEL" = %"PRId64"; ", acl->label);
>>>               }
>>>               build_acl_log(actions, acl, meter_groups);
>>> +            build_acl_sample(actions, acl);
>>>               ds_put_cstr(actions, "next;");
>>>               ovn_lflow_add_with_hint(lflows, od, stage,
>>>                                       acl->priority +
>>> OVN_ACL_PRI_OFFSET,
>>> @@ -6329,6 +6353,7 @@ consider_acl(struct hmap *lflows, struct
>>> ovn_datapath *od,
>>>                                 REG_LABEL" = %"PRId64"; ", acl->label);
>>>               }
>>>               build_acl_log(actions, acl, meter_groups);
>>> +            build_acl_sample(actions, acl);
>>>               ds_put_cstr(actions, "next;");
>>>               ovn_lflow_add_with_hint(lflows, od, stage,
>>>                                       acl->priority +
>>> OVN_ACL_PRI_OFFSET,
>>> @@ -6349,7 +6374,7 @@ consider_acl(struct hmap *lflows, struct
>>> ovn_datapath *od,
>>>                */
>>>               bool log_related = smap_get_bool(&acl->options,
>>> "log-related",
>>>                                                false);
>>> -            if (acl->log && acl->label && log_related) {
>>> +            if ((acl->log || acl->sample) && acl->label &&
>>> log_related) {
>>>                   /* Related/reply flows need to be set on the
>>> opposite pipeline
>>>                    * from where the ACL itself is set.
>>>                    */
>>> @@ -6365,6 +6390,7 @@ consider_acl(struct hmap *lflows, struct
>>> ovn_datapath *od,
>>>                                 use_ct_inv_match ? " && !ct.inv" : "",
>>>                                 ct_blocked_match, acl->label);
>>>                   build_acl_log(actions, acl, meter_groups);
>>> +                build_acl_sample(actions, acl);
>>>                   ds_put_cstr(actions, "next;");
>>>                   ovn_lflow_add_with_hint(lflows, od, log_related_stage,
>>>                                           UINT16_MAX - 2,
>>> @@ -6402,6 +6428,7 @@ consider_acl(struct hmap *lflows, struct
>>> ovn_datapath *od,
>>>               } else {
>>>                   ds_put_format(match, " && (%s)", acl->match);
>>>                   build_acl_log(actions, acl, meter_groups);
>>> +                build_acl_sample(actions, acl);
>>>                   ds_put_cstr(actions, debug_implicit_drop_action());
>>>                   ovn_lflow_add_with_hint(lflows, od, stage,
>>>                                           acl->priority +
>>> OVN_ACL_PRI_OFFSET,
>>> @@ -6430,6 +6457,7 @@ consider_acl(struct hmap *lflows, struct
>>> ovn_datapath *od,
>>>               } else {
>>>                   ds_put_format(match, " && (%s)", acl->match);
>>>                   build_acl_log(actions, acl, meter_groups);
>>> +                build_acl_sample(actions, acl);
>>>                   ds_put_cstr(actions, debug_implicit_drop_action());
>>>                   ovn_lflow_add_with_hint(lflows, od, stage,
>>>                                           acl->priority +
>>> OVN_ACL_PRI_OFFSET,
>>> @@ -6447,6 +6475,7 @@ consider_acl(struct hmap *lflows, struct
>>> ovn_datapath *od,
>>>                                          actions, &acl->header_,
>>> meter_groups);
>>>               } else {
>>>                   build_acl_log(actions, acl, meter_groups);
>>> +                build_acl_sample(actions, acl);
>>>                   ds_put_cstr(actions, debug_implicit_drop_action());
>>>                   ovn_lflow_add_with_hint(lflows, od, stage,
>>>                                           acl->priority +
>>> OVN_ACL_PRI_OFFSET,
>>> diff --git a/ovn-nb.ovsschema b/ovn-nb.ovsschema
>>> index 174364c8b..6178b532e 100644
>>> --- a/ovn-nb.ovsschema
>>> +++ b/ovn-nb.ovsschema
>>> @@ -1,7 +1,7 @@
>>>   {
>>>       "name": "OVN_Northbound",
>>>       "version": "6.3.0",
>>> -    "cksum": "4042813038 31869",
>>> +    "cksum": "3795038812 33116",
>>>       "tables": {
>>>           "NB_Global": {
>>>               "columns": {
>>> @@ -30,6 +30,23 @@
>>>                   "ipsec": {"type": "boolean"}},
>>>               "maxRows": 1,
>>>               "isRoot": true},
>>> +        "Sample": {
>>> +            "columns": {
>>> +                "probability": {"type": {"key": {"type": "integer",
>>> +                                                      "minInteger": 0,
>>> +                                                      "maxInteger":
>>> 65535}}},
>>> +                "collector_set_id": {"type": {"key": {"type":
>>> "integer",
>>> +                                                      "minInteger": 0,
>>> +                                                   "maxInteger":
>>> 4294967295}}},
>>> +                "obs_domain_id": {"type": {"key": {"type": "integer",
>>> +                                                   "minInteger": 0,
>>> +                                                   "maxInteger":
>>> 255}}},
>>> +                "obs_point_id": {"type": {"key": {"type": "integer",
>>> +                                                  "minInteger": 0,
>>> +                                                  "maxInteger":
>>> 4294967295},
>>> +                                          "min": 0, "max":1}}
>>> +            }
>>
>> This seems to match 1:1 the arguments of the OVS SAMPLE action.  That's
>> fine but do we expect more arguments to be added in the future?
> 
> I can't think of any right now.
> 

Ok.

>>
>> Please add a column that can be used as index and also define a unique
>> index.  That will save us from trouble like "spurious duplicates" [0]
>> when the DB is in RAFT.  It also makes it easier for the CMS to track
>> what it installed or not.
> 
> Technically, the only unique element would be (collector_set_id,
> obs_domain_id, obs_point_id) tuple. I can add that as index. Would that
> work or are you referring  to a column called "id"?
> 

If the tuple (collector_set_id, obs_domain_id, obs_point_id) is expected
to be unique then let's add it as index.  Otherwise, yes, please add an
"id" or "name" column and mark that as index.

>  An external_ids column might also make sense.
>>
> 
> Yes it does.
> 
> 
>> [0]
>> https://github.com/openvswitch/ovs/commit/04e5adfedd2a2e4ceac62136671057542a7cf875
>>
>>> +        },
>>
>> Nit: By default "isRoot" is false.  Don't we want this as root table?
>>
> 
> Hmm... I don't know. Maybe we could delete
> 

"isRoot: false" means the records are garbage collected when not
referred to anymore.  ACLs, for example, are like that.  It's probably
OK to leave this as non-root.  I just wanted to make sure we considered
both options.
diff mbox series

Patch

diff --git a/northd/northd.c b/northd/northd.c
index 61d474840..3e09e3a0f 100644
--- a/northd/northd.c
+++ b/northd/northd.c
@@ -6194,6 +6194,27 @@  build_acl_log(struct ds *actions, const struct nbrec_acl *acl,
     ds_put_cstr(actions, "); ");
 }
 
+static void
+build_acl_sample(struct ds *actions, const struct nbrec_acl *acl)
+{
+    if (!acl->sample) {
+        return;
+    }
+    ds_put_format(actions, "sample(probability=%"PRId16","
+                           "collector_set=%d,"
+                           "obs_domain=%hd,",
+                            (uint16_t) acl->sample->probability,
+                            (uint32_t) acl->sample->collector_set_id,
+                            (uint8_t) acl->sample->obs_domain_id);
+
+    if (acl->sample->obs_point_id) {
+        ds_put_format(actions, "obs_point=%"PRId32");",
+                      (uint32_t) *acl->sample->obs_point_id);
+    } else {
+        ds_put_cstr(actions, "obs_point=$cookie);");
+    }
+}
+
 static void
 build_reject_acl_rules(struct ovn_datapath *od, struct hmap *lflows,
                        enum ovn_stage stage, struct nbrec_acl *acl,
@@ -6260,6 +6281,7 @@  consider_acl(struct hmap *lflows, struct ovn_datapath *od,
     if (!strcmp(acl->action, "allow-stateless")) {
         ds_clear(actions);
         build_acl_log(actions, acl, meter_groups);
+        build_acl_sample(actions, acl);
         ds_put_cstr(actions, "next;");
         ovn_lflow_add_with_hint(lflows, od, stage,
                                 acl->priority + OVN_ACL_PRI_OFFSET,
@@ -6275,6 +6297,7 @@  consider_acl(struct hmap *lflows, struct ovn_datapath *od,
         if (!has_stateful) {
             ds_clear(actions);
             build_acl_log(actions, acl, meter_groups);
+            build_acl_sample(actions, acl);
             ds_put_cstr(actions, "next;");
             ovn_lflow_add_with_hint(lflows, od, stage,
                                     acl->priority + OVN_ACL_PRI_OFFSET,
@@ -6304,6 +6327,7 @@  consider_acl(struct hmap *lflows, struct ovn_datapath *od,
                               REG_LABEL" = %"PRId64"; ", acl->label);
             }
             build_acl_log(actions, acl, meter_groups);
+            build_acl_sample(actions, acl);
             ds_put_cstr(actions, "next;");
             ovn_lflow_add_with_hint(lflows, od, stage,
                                     acl->priority + OVN_ACL_PRI_OFFSET,
@@ -6329,6 +6353,7 @@  consider_acl(struct hmap *lflows, struct ovn_datapath *od,
                               REG_LABEL" = %"PRId64"; ", acl->label);
             }
             build_acl_log(actions, acl, meter_groups);
+            build_acl_sample(actions, acl);
             ds_put_cstr(actions, "next;");
             ovn_lflow_add_with_hint(lflows, od, stage,
                                     acl->priority + OVN_ACL_PRI_OFFSET,
@@ -6349,7 +6374,7 @@  consider_acl(struct hmap *lflows, struct ovn_datapath *od,
              */
             bool log_related = smap_get_bool(&acl->options, "log-related",
                                              false);
-            if (acl->log && acl->label && log_related) {
+            if ((acl->log || acl->sample) && acl->label && log_related) {
                 /* Related/reply flows need to be set on the opposite pipeline
                  * from where the ACL itself is set.
                  */
@@ -6365,6 +6390,7 @@  consider_acl(struct hmap *lflows, struct ovn_datapath *od,
                               use_ct_inv_match ? " && !ct.inv" : "",
                               ct_blocked_match, acl->label);
                 build_acl_log(actions, acl, meter_groups);
+                build_acl_sample(actions, acl);
                 ds_put_cstr(actions, "next;");
                 ovn_lflow_add_with_hint(lflows, od, log_related_stage,
                                         UINT16_MAX - 2,
@@ -6402,6 +6428,7 @@  consider_acl(struct hmap *lflows, struct ovn_datapath *od,
             } else {
                 ds_put_format(match, " && (%s)", acl->match);
                 build_acl_log(actions, acl, meter_groups);
+                build_acl_sample(actions, acl);
                 ds_put_cstr(actions, debug_implicit_drop_action());
                 ovn_lflow_add_with_hint(lflows, od, stage,
                                         acl->priority + OVN_ACL_PRI_OFFSET,
@@ -6430,6 +6457,7 @@  consider_acl(struct hmap *lflows, struct ovn_datapath *od,
             } else {
                 ds_put_format(match, " && (%s)", acl->match);
                 build_acl_log(actions, acl, meter_groups);
+                build_acl_sample(actions, acl);
                 ds_put_cstr(actions, debug_implicit_drop_action());
                 ovn_lflow_add_with_hint(lflows, od, stage,
                                         acl->priority + OVN_ACL_PRI_OFFSET,
@@ -6447,6 +6475,7 @@  consider_acl(struct hmap *lflows, struct ovn_datapath *od,
                                        actions, &acl->header_, meter_groups);
             } else {
                 build_acl_log(actions, acl, meter_groups);
+                build_acl_sample(actions, acl);
                 ds_put_cstr(actions, debug_implicit_drop_action());
                 ovn_lflow_add_with_hint(lflows, od, stage,
                                         acl->priority + OVN_ACL_PRI_OFFSET,
diff --git a/ovn-nb.ovsschema b/ovn-nb.ovsschema
index 174364c8b..6178b532e 100644
--- a/ovn-nb.ovsschema
+++ b/ovn-nb.ovsschema
@@ -1,7 +1,7 @@ 
 {
     "name": "OVN_Northbound",
     "version": "6.3.0",
-    "cksum": "4042813038 31869",
+    "cksum": "3795038812 33116",
     "tables": {
         "NB_Global": {
             "columns": {
@@ -30,6 +30,23 @@ 
                 "ipsec": {"type": "boolean"}},
             "maxRows": 1,
             "isRoot": true},
+        "Sample": {
+            "columns": {
+                "probability": {"type": {"key": {"type": "integer",
+                                                      "minInteger": 0,
+                                                      "maxInteger": 65535}}},
+                "collector_set_id": {"type": {"key": {"type": "integer",
+                                                      "minInteger": 0,
+                                                   "maxInteger": 4294967295}}},
+                "obs_domain_id": {"type": {"key": {"type": "integer",
+                                                   "minInteger": 0,
+                                                   "maxInteger": 255}}},
+                "obs_point_id": {"type": {"key": {"type": "integer",
+                                                  "minInteger": 0,
+                                                  "maxInteger": 4294967295},
+                                          "min": 0, "max":1}}
+            }
+        },
         "Copp": {
             "columns": {
                 "name": {"type": "string"},
@@ -267,6 +284,10 @@ 
                 "label": {"type": {"key": {"type": "integer",
                                            "minInteger": 0,
                                            "maxInteger": 4294967295}}},
+                "sample": {"type": {"key": {"type": "uuid",
+                                            "refTable": "Sample",
+                                            "refType": "strong"},
+                                    "min": 0, "max": 1}},
                 "options": {
                      "type": {"key": "string",
                               "value": "string",
diff --git a/ovn-nb.xml b/ovn-nb.xml
index 9581aef7c..e818fd8d1 100644
--- a/ovn-nb.xml
+++ b/ovn-nb.xml
@@ -387,6 +387,31 @@ 
 
   </table>
 
+  <table name="Sample" title="Sample">
+    <p>
+      This table describes an IPFIX Sampling Point. Entries in other tables
+      might be associated with Sample entries to indicate how the sample
+      should be generated.
+
+      For an example, see <ref table="ACL"/>.
+    </p>
+    <column name="probability">
+      Sampling probability. It must be an integer number between 0 and 65535.
+    </column>
+    <column name="collector_set_id">
+      The 32-bit integer identifier of the set of of collectors to send
+      packets to. See Flow_Sample_Collector_Set Table in ovs-vswitchd's
+      database schema.
+    </column>
+    <column name="obs_domain_id">
+      The 8 most significant bits of the Observation Domain ID that will be
+      added to evvery IPFIX sample. The 24 LSB will be the datapath key.
+    </column>
+    <column name="obs_point_id">
+      If set, it'll be use as Observation Point ID in every IPFIX sample.
+      Otherwise the Logical Flow's coockie will be used.
+    </column>
+  </table>
   <table name="Copp" title="Control plane protection">
     <p>
       This table is used to define control plane protection policies, i.e.,
@@ -2191,6 +2216,12 @@ 
       </column>
     </group>
 
+    <column name="sample">
+      <p>
+        The entry in the <ref table="Sample"/> table to use for sampling.
+      </p>
+    </column>
+
     <group title="Common Columns">
       <column name="options">
         This column provides general key/value settings. The supported