[ovs-dev] [PATCH v2] datapath: Add a new action dec_ttl

Eelco Chaudron echaudro at redhat.com
Fri Nov 13 10:04:53 UTC 2020


Add support for the dec_ttl action. Instead of programming the datapath with
a flow that matches the packet TTL and an IP set, use a single dec_ttl action.

The old behavior is kept if the new action is not supported by the datapath.

  # ovs-ofctl dump-flows br0
   cookie=0x0, duration=12.538s, table=0, n_packets=4, n_bytes=392, ip actions=dec_ttl,NORMAL
   cookie=0x0, duration=12.536s, table=0, n_packets=4, n_bytes=168, actions=NORMAL

  # ping -c1 -t 20 192.168.0.2
  PING 192.168.0.2 (192.168.0.2) 56(84) bytes of data.
  IP (tos 0x0, ttl 19, id 45336, offset 0, flags [DF], proto ICMP (1), length 84)
      192.168.0.1 > 192.168.0.2: ICMP echo request, id 8865, seq 1, length 64

Linux netlink datapath support depends on upstream Linux commit:
  744676e77720 ("openvswitch: add TTL decrement action")


Note that in the Linux kernel tree the OVS_ACTION_ATTR_ADD_MPLS has been
defined, and to make sure the IDs are in sync, it had to be added to the
OVS source tree. This required some additional case statements, which
should be revisited once the OVS implementation is added.


Co-developed-by: Matteo Croce <mcroce at linux.microsoft.com>
Co-developed-by: Bindiya Kurle <bindiyakurle at gmail.com>
Signed-off-by: Eelco Chaudron <echaudro at redhat.com>

---
v2: - Used definition instead of numeric value in format_dec_ttl_action()
    - Changed format from "dec_ttl(ttl<=1(<actions>)) to
      "dec_ttl(le_1(<actions>))" to be more in line with the check_pkt_len action.
    - Fixed parsing of "dec_ttl()" action for adding a dp flow.
    - Cleaned up format_dec_ttl_action()

 datapath/linux/compat/include/linux/openvswitch.h |    8 ++
 lib/dpif-netdev.c                                 |    4 +
 lib/dpif.c                                        |    4 +
 lib/odp-execute.c                                 |  102 ++++++++++++++++++++-
 lib/odp-execute.h                                 |    2 
 lib/odp-util.c                                    |   42 +++++++++
 lib/packets.h                                     |   13 ++-
 ofproto/ofproto-dpif-ipfix.c                      |    2 
 ofproto/ofproto-dpif-sflow.c                      |    2 
 ofproto/ofproto-dpif-xlate.c                      |   54 +++++++++--
 ofproto/ofproto-dpif.c                            |   37 ++++++++
 ofproto/ofproto-dpif.h                            |    6 +
 12 files changed, 253 insertions(+), 23 deletions(-)

diff --git a/datapath/linux/compat/include/linux/openvswitch.h b/datapath/linux/compat/include/linux/openvswitch.h
index 2d884312f..66c386462 100644
--- a/datapath/linux/compat/include/linux/openvswitch.h
+++ b/datapath/linux/compat/include/linux/openvswitch.h
@@ -1021,6 +1021,8 @@ enum ovs_action_attr {
 	OVS_ACTION_ATTR_METER,        /* u32 meter number. */
 	OVS_ACTION_ATTR_CLONE,        /* Nested OVS_CLONE_ATTR_*.  */
 	OVS_ACTION_ATTR_CHECK_PKT_LEN, /* Nested OVS_CHECK_PKT_LEN_ATTR_*. */
+	OVS_ACTION_ATTR_ADD_MPLS,      /* struct ovs_action_add_mpls. */
+	OVS_ACTION_ATTR_DEC_TTL,       /* Nested OVS_DEC_TTL_ATTR_*. */
 
 #ifndef __KERNEL__
 	OVS_ACTION_ATTR_TUNNEL_PUSH,   /* struct ovs_action_push_tnl*/
@@ -1040,6 +1042,12 @@ enum ovs_action_attr {
 
 #define OVS_ACTION_ATTR_MAX (__OVS_ACTION_ATTR_MAX - 1)
 
+enum ovs_dec_ttl_attr {
+	OVS_DEC_TTL_ATTR_UNSPEC,
+	OVS_DEC_TTL_ATTR_ACTION,	/* Nested struct nlattr. */
+	__OVS_DEC_TTL_ATTR_MAX
+};
+
 /* Meters. */
 #define OVS_METER_FAMILY  "ovs_meter"
 #define OVS_METER_MCGROUP "ovs_meter"
diff --git a/lib/dpif-netdev.c b/lib/dpif-netdev.c
index 300861ca5..35c6560c3 100644
--- a/lib/dpif-netdev.c
+++ b/lib/dpif-netdev.c
@@ -7975,6 +7975,8 @@ dp_execute_cb(void *aux_, struct dp_packet_batch *packets_,
     case OVS_ACTION_ATTR_CT_CLEAR:
     case OVS_ACTION_ATTR_CHECK_PKT_LEN:
     case OVS_ACTION_ATTR_DROP:
+    case OVS_ACTION_ATTR_DEC_TTL:
+    case OVS_ACTION_ATTR_ADD_MPLS:
     case __OVS_ACTION_ATTR_MAX:
         OVS_NOT_REACHED();
     }
@@ -7991,7 +7993,7 @@ dp_netdev_execute_actions(struct dp_netdev_pmd_thread *pmd,
     struct dp_netdev_execute_aux aux = { pmd, flow };
 
     odp_execute_actions(&aux, packets, should_steal, actions,
-                        actions_len, dp_execute_cb);
+                        actions_len, dp_execute_cb, false);
 }
 
 struct dp_netdev_ct_dump {
diff --git a/lib/dpif.c b/lib/dpif.c
index ac2860764..644d32d47 100644
--- a/lib/dpif.c
+++ b/lib/dpif.c
@@ -1273,6 +1273,8 @@ dpif_execute_helper_cb(void *aux_, struct dp_packet_batch *packets_,
     case OVS_ACTION_ATTR_UNSPEC:
     case OVS_ACTION_ATTR_CHECK_PKT_LEN:
     case OVS_ACTION_ATTR_DROP:
+    case OVS_ACTION_ATTR_ADD_MPLS:
+    case OVS_ACTION_ATTR_DEC_TTL:
     case __OVS_ACTION_ATTR_MAX:
         OVS_NOT_REACHED();
     }
@@ -1294,7 +1296,7 @@ dpif_execute_with_help(struct dpif *dpif, struct dpif_execute *execute)
 
     dp_packet_batch_init_packet(&pb, execute->packet);
     odp_execute_actions(&aux, &pb, false, execute->actions,
-                        execute->actions_len, dpif_execute_helper_cb);
+                        execute->actions_len, dpif_execute_helper_cb, false);
     return aux.error;
 }
 
diff --git a/lib/odp-execute.c b/lib/odp-execute.c
index 6eeda2a61..237776448 100644
--- a/lib/odp-execute.c
+++ b/lib/odp-execute.c
@@ -716,7 +716,58 @@ odp_execute_sample(void *dp, struct dp_packet *packet, bool steal,
     }
     dp_packet_batch_init_packet(&pb, packet);
     odp_execute_actions(dp, &pb, true, nl_attr_get(subactions),
-                        nl_attr_get_size(subactions), dp_execute_action);
+                        nl_attr_get_size(subactions), dp_execute_action,
+                        false);
+}
+
+static bool execute_dec_ttl(struct dp_packet *packet)
+{
+    struct eth_header *eth = dp_packet_eth(packet);
+
+    if (dl_type_is_ipv4(eth->eth_type)) {
+        struct ip_header *nh = dp_packet_l3(packet);
+        uint8_t old_ttl = nh->ip_ttl;
+
+        if (old_ttl <= 1) {
+            return true;
+        }
+
+        nh->ip_ttl--;
+        nh->ip_csum = recalc_csum16(nh->ip_csum, htons(old_ttl << 8),
+                                    htons(nh->ip_ttl << 8));
+    } else if (dl_type_is_ipv6(eth->eth_type)) {
+        struct ovs_16aligned_ip6_hdr *nh = dp_packet_l3(packet);
+
+        if (nh->ip6_hlim <= 1) {
+            return true;
+        }
+
+        nh->ip6_hlim--;
+    }
+
+    return false;
+}
+
+static void odp_dec_ttl_exception_handler(void *dp,
+                                          struct dp_packet_batch *batch,
+                                          const struct nlattr *action,
+                                          odp_execute_cb dp_execute_action)
+{
+    const struct nlattr *a;
+    const struct nlattr *subaction = NULL;
+    size_t left;
+
+    if (nl_attr_is_valid(action, nl_attr_get_size(action))) {
+        dp_packet_delete_batch(batch, true);
+        return;
+    }
+
+    NL_NESTED_FOR_EACH_UNSAFE (a, left, action) {
+        subaction = a;
+        odp_execute_actions(dp, batch, true, subaction,
+                            nl_attr_get_size(subaction),
+                            dp_execute_action, true);
+    }
 }
 
 static void
@@ -734,11 +785,13 @@ odp_execute_clone(void *dp, struct dp_packet_batch *batch, bool steal,
         dp_packet_batch_clone(&clone_pkt_batch, batch);
         dp_packet_batch_reset_cutlen(batch);
         odp_execute_actions(dp, &clone_pkt_batch, true, nl_attr_get(actions),
-                        nl_attr_get_size(actions), dp_execute_action);
+                            nl_attr_get_size(actions), dp_execute_action,
+                            false);
     }
     else {
         odp_execute_actions(dp, batch, true, nl_attr_get(actions),
-                            nl_attr_get_size(actions), dp_execute_action);
+                            nl_attr_get_size(actions), dp_execute_action,
+                            false);
     }
 }
 
@@ -783,7 +836,7 @@ odp_execute_check_pkt_len(void *dp, struct dp_packet *packet, bool steal,
      * odp_execute_actions. */
     dp_packet_batch_init_packet(&pb, packet);
     odp_execute_actions(dp, &pb, true, nl_attr_get(a), nl_attr_get_size(a),
-                        dp_execute_action);
+                        dp_execute_action, false);
 }
 
 static bool
@@ -820,6 +873,8 @@ requires_datapath_assistance(const struct nlattr *a)
     case OVS_ACTION_ATTR_CT_CLEAR:
     case OVS_ACTION_ATTR_CHECK_PKT_LEN:
     case OVS_ACTION_ATTR_DROP:
+    case OVS_ACTION_ATTR_ADD_MPLS:
+    case OVS_ACTION_ATTR_DEC_TTL:
         return false;
 
     case OVS_ACTION_ATTR_UNSPEC:
@@ -846,7 +901,7 @@ requires_datapath_assistance(const struct nlattr *a)
 void
 odp_execute_actions(void *dp, struct dp_packet_batch *batch, bool steal,
                     const struct nlattr *actions, size_t actions_len,
-                    odp_execute_cb dp_execute_action)
+                    odp_execute_cb dp_execute_action, bool force_last)
 {
     struct dp_packet *packet;
     const struct nlattr *a;
@@ -864,7 +919,8 @@ odp_execute_actions(void *dp, struct dp_packet_batch *batch, bool steal,
 
                 dp_execute_action(dp, batch, a, should_steal);
 
-                if (last_action || dp_packet_batch_is_empty(batch)) {
+                if (last_action || dp_packet_batch_is_empty(batch) ||
+                    force_last) {
                     /* We do not need to free the packets.
                      * Either dp_execute_actions() has stolen them
                      * or the batch is freed due to errors. In either
@@ -979,6 +1035,39 @@ odp_execute_actions(void *dp, struct dp_packet_batch *batch, bool steal,
             }
             break;
 
+        case OVS_ACTION_ATTR_DEC_TTL: {
+            const size_t cnt = dp_packet_batch_size(batch);
+            struct dp_packet_batch invalid_ttl;
+            size_t i;
+
+            /* Make batch for invalid ttl packets. */
+            dp_packet_batch_init(&invalid_ttl);
+            invalid_ttl.trunc = batch->trunc;
+            invalid_ttl.do_not_steal = batch->do_not_steal;
+
+            /* Add packets with ttl <=1 to the invalid_ttl batch
+             * and remove it from the batch. */
+            DP_PACKET_BATCH_REFILL_FOR_EACH (i, cnt, packet, batch) {
+                if (execute_dec_ttl(packet)) {
+                    dp_packet_batch_add(&invalid_ttl, packet);
+                } else {
+                    dp_packet_batch_refill(batch, packet, i);
+                }
+            }
+
+            /* Execute action on packets with ttl <= 1. */
+            if (invalid_ttl.count > 0) {
+                odp_dec_ttl_exception_handler(dp, &invalid_ttl, a,
+                                              dp_execute_action);
+            }
+
+            if (last_action || !batch->count) {
+                /* We do not need to free the packets. */
+                return;
+            }
+            break;
+        }
+
         case OVS_ACTION_ATTR_TRUNC: {
             const struct ovs_action_trunc *trunc =
                         nl_attr_get_unspec(a, sizeof *trunc);
@@ -1077,6 +1166,7 @@ odp_execute_actions(void *dp, struct dp_packet_batch *batch, bool steal,
         case OVS_ACTION_ATTR_RECIRC:
         case OVS_ACTION_ATTR_CT:
         case OVS_ACTION_ATTR_UNSPEC:
+        case OVS_ACTION_ATTR_ADD_MPLS:
         case __OVS_ACTION_ATTR_MAX:
             OVS_NOT_REACHED();
         }
diff --git a/lib/odp-execute.h b/lib/odp-execute.h
index a3578a575..3e80ed906 100644
--- a/lib/odp-execute.h
+++ b/lib/odp-execute.h
@@ -38,5 +38,5 @@ typedef void (*odp_execute_cb)(void *dp, struct dp_packet_batch *batch,
 void odp_execute_actions(void *dp, struct dp_packet_batch *batch,
                          bool steal,
                          const struct nlattr *actions, size_t actions_len,
-                         odp_execute_cb dp_execute_action);
+                         odp_execute_cb dp_execute_action, bool force_last);
 #endif
diff --git a/lib/odp-util.c b/lib/odp-util.c
index 0bd2f9aa8..14de187b7 100644
--- a/lib/odp-util.c
+++ b/lib/odp-util.c
@@ -143,8 +143,10 @@ odp_action_len(uint16_t type)
     case OVS_ACTION_ATTR_POP_NSH: return 0;
     case OVS_ACTION_ATTR_CHECK_PKT_LEN: return ATTR_LEN_VARIABLE;
     case OVS_ACTION_ATTR_DROP: return sizeof(uint32_t);
+    case OVS_ACTION_ATTR_DEC_TTL: return ATTR_LEN_VARIABLE;
 
     case OVS_ACTION_ATTR_UNSPEC:
+    case OVS_ACTION_ATTR_ADD_MPLS:
     case __OVS_ACTION_ATTR_MAX:
         return ATTR_LEN_INVALID;
     }
@@ -1109,6 +1111,25 @@ format_odp_check_pkt_len_action(struct ds *ds, const struct nlattr *attr,
     ds_put_cstr(ds, "))");
 }
 
+static void
+format_dec_ttl_action(struct ds *ds,const struct nlattr *attr,
+                      const struct hmap *portno_names)
+{
+    const struct nlattr *nla_acts = nl_attr_get(attr);
+    int len = nl_attr_get_size(attr);
+
+    ds_put_cstr(ds,"dec_ttl(le_1(");
+
+    if (len > 4 && nla_acts->nla_type == OVS_DEC_TTL_ATTR_ACTION) {
+        /* Linux kernel add an additional envelope we should strip. */
+        len -= nl_attr_len_pad(nla_acts, len);
+        nla_acts = nl_attr_next(nla_acts);
+    }
+
+    format_odp_actions(ds, nla_acts, len, portno_names);
+    ds_put_format(ds, "))");
+}
+
 static void
 format_odp_action(struct ds *ds, const struct nlattr *a,
                   const struct hmap *portno_names)
@@ -1256,7 +1277,11 @@ format_odp_action(struct ds *ds, const struct nlattr *a,
     case OVS_ACTION_ATTR_DROP:
         ds_put_cstr(ds, "drop");
         break;
+    case OVS_ACTION_ATTR_DEC_TTL:
+        format_dec_ttl_action(ds, a, portno_names);
+        break;
     case OVS_ACTION_ATTR_UNSPEC:
+    case OVS_ACTION_ATTR_ADD_MPLS:
     case __OVS_ACTION_ATTR_MAX:
     default:
         format_generic_odp_action(ds, a);
@@ -2498,6 +2523,23 @@ parse_odp_action__(struct parse_odp_context *context, const char *s,
             return n + 1;
         }
     }
+    {
+        if (!strncmp(s, "dec_ttl(le_1(", 13)) {
+            size_t actions_ofs;
+            int n = 13;
+
+            actions_ofs = nl_msg_start_nested(actions,
+                                              OVS_ACTION_ATTR_DEC_TTL);
+            int retval = parse_action_list(context, s + n, actions);
+            if (retval < 0) {
+                return retval;
+            }
+
+            n += retval;
+            nl_msg_end_nested(actions, actions_ofs);
+            return n + 2;
+        }
+    }
 
     {
         if (!strncmp(s, "push_nsh(", 9)) {
diff --git a/lib/packets.h b/lib/packets.h
index 481bc22fa..b7fa7b121 100644
--- a/lib/packets.h
+++ b/lib/packets.h
@@ -1211,10 +1211,19 @@ bool in6_is_lla(struct in6_addr *addr);
 void ipv6_multicast_to_ethernet(struct eth_addr *eth,
                                 const struct in6_addr *ip6);
 
+static inline bool dl_type_is_ipv4(ovs_be16 dl_type)
+{
+    return dl_type == htons(ETH_TYPE_IP);
+}
+
+static inline bool dl_type_is_ipv6(ovs_be16 dl_type)
+{
+    return dl_type == htons(ETH_TYPE_IPV6);
+}
+
 static inline bool dl_type_is_ip_any(ovs_be16 dl_type)
 {
-    return dl_type == htons(ETH_TYPE_IP)
-        || dl_type == htons(ETH_TYPE_IPV6);
+    return dl_type_is_ipv4(dl_type) || dl_type_is_ipv6(dl_type);
 }
 
 /* Tunnel header */
diff --git a/ofproto/ofproto-dpif-ipfix.c b/ofproto/ofproto-dpif-ipfix.c
index 796eb6f88..1c8e7ae26 100644
--- a/ofproto/ofproto-dpif-ipfix.c
+++ b/ofproto/ofproto-dpif-ipfix.c
@@ -3018,6 +3018,8 @@ dpif_ipfix_read_actions(const struct flow *flow,
         case OVS_ACTION_ATTR_CHECK_PKT_LEN:
         case OVS_ACTION_ATTR_UNSPEC:
         case OVS_ACTION_ATTR_DROP:
+        case OVS_ACTION_ATTR_ADD_MPLS:
+        case OVS_ACTION_ATTR_DEC_TTL:
         case __OVS_ACTION_ATTR_MAX:
         default:
             break;
diff --git a/ofproto/ofproto-dpif-sflow.c b/ofproto/ofproto-dpif-sflow.c
index fdcb9eabb..c8bf6bee4 100644
--- a/ofproto/ofproto-dpif-sflow.c
+++ b/ofproto/ofproto-dpif-sflow.c
@@ -1178,6 +1178,7 @@ dpif_sflow_read_actions(const struct flow *flow,
         case OVS_ACTION_ATTR_CT_CLEAR:
         case OVS_ACTION_ATTR_METER:
         case OVS_ACTION_ATTR_LB_OUTPUT:
+        case OVS_ACTION_ATTR_DEC_TTL:
             break;
 
         case OVS_ACTION_ATTR_SET_MASKED:
@@ -1226,6 +1227,7 @@ dpif_sflow_read_actions(const struct flow *flow,
         case OVS_ACTION_ATTR_UNSPEC:
         case OVS_ACTION_ATTR_CHECK_PKT_LEN:
         case OVS_ACTION_ATTR_DROP:
+        case OVS_ACTION_ATTR_ADD_MPLS:
         case __OVS_ACTION_ATTR_MAX:
         default:
             break;
diff --git a/ofproto/ofproto-dpif-xlate.c b/ofproto/ofproto-dpif-xlate.c
index 11aa20754..a1fd1c752 100644
--- a/ofproto/ofproto-dpif-xlate.c
+++ b/ofproto/ofproto-dpif-xlate.c
@@ -4839,9 +4839,12 @@ xlate_controller_action(struct xlate_ctx *ctx, int len,
                         enum ofp_packet_in_reason reason,
                         uint16_t controller_id,
                         uint32_t provider_meter_id,
-                        const uint8_t *userdata, size_t userdata_len)
+                        const uint8_t *userdata, size_t userdata_len,
+                        bool commit)
 {
-    xlate_commit_actions(ctx);
+    if (commit) {
+        xlate_commit_actions(ctx);
+    }
 
     /* A packet sent by an action in a table-miss rule is considered an
      * explicit table miss.  OpenFlow before 1.3 doesn't have that concept so
@@ -5087,7 +5090,8 @@ compose_dec_ttl(struct xlate_ctx *ctx, struct ofpact_cnt_ids *ids)
 
         for (i = 0; i < ids->n_controllers; i++) {
             xlate_controller_action(ctx, UINT16_MAX, OFPR_INVALID_TTL,
-                                    ids->cnt_ids[i], UINT32_MAX, NULL, 0);
+                                    ids->cnt_ids[i], UINT32_MAX, NULL, 0,
+                                    true);
         }
 
         /* Stop processing for current table. */
@@ -5128,7 +5132,7 @@ compose_dec_nsh_ttl_action(struct xlate_ctx *ctx)
             return false;
         } else {
             xlate_controller_action(ctx, UINT16_MAX, OFPR_INVALID_TTL,
-                                    0, UINT32_MAX, NULL, 0);
+                                    0, UINT32_MAX, NULL, 0, true);
         }
     }
 
@@ -5161,7 +5165,7 @@ compose_dec_mpls_ttl_action(struct xlate_ctx *ctx)
             return false;
         } else {
             xlate_controller_action(ctx, UINT16_MAX, OFPR_INVALID_TTL, 0,
-                                    UINT32_MAX, NULL, 0);
+                                    UINT32_MAX, NULL, 0, true);
         }
     }
 
@@ -5185,6 +5189,37 @@ xlate_delete_field(struct xlate_ctx *ctx,
     ds_destroy(&s);
 }
 
+/* New handling for dec_ttl action. */
+static void
+xlate_dec_ttl_action(struct xlate_ctx *ctx, struct ofpact_cnt_ids *ids)
+{
+    struct flow *flow = &ctx->xin->flow;
+    struct flow_wildcards *wc = ctx->wc;
+    size_t offset;
+    size_t i;
+
+    if (!is_ip_any(flow)) {
+        return;
+    }
+
+    if (!ctx->xbridge->support.dec_ttl_action) {
+        wc->masks.nw_ttl = 0xff;
+        compose_dec_ttl(ctx, ids);
+        return;
+    }
+
+    xlate_commit_actions(ctx);
+    offset = nl_msg_start_nested(ctx->odp_actions, OVS_ACTION_ATTR_DEC_TTL);
+
+    for (i = 0; i < ids->n_controllers; i++) {
+        xlate_controller_action(ctx, UINT16_MAX, OFPR_INVALID_TTL,
+                                ids->cnt_ids[i], UINT32_MAX, NULL, 0, false);
+    }
+
+    nl_msg_end_nested(ctx->odp_actions, offset);
+    xlate_commit_actions(ctx);
+}
+
 /* Emits an action that outputs to 'port', within 'ctx'.
  *
  * 'controller_len' affects only packets sent to an OpenFlow controller.  It
@@ -5235,7 +5270,7 @@ xlate_output_action(struct xlate_ctx *ctx, ofp_port_t port,
                                  : group_bucket_action ? OFPR_GROUP
                                  : ctx->in_action_set ? OFPR_ACTION_SET
                                  : OFPR_ACTION),
-                                0, UINT32_MAX, NULL, 0);
+                                0, UINT32_MAX, NULL, 0, true);
         break;
     case OFPP_NONE:
         break;
@@ -6796,7 +6831,7 @@ do_xlate_actions(const struct ofpact *ofpacts, size_t ofpacts_len,
                                         controller->controller_id,
                                         controller->provider_meter_id,
                                         controller->userdata,
-                                        controller->userdata_len);
+                                        controller->userdata_len, true);
             }
             break;
 
@@ -7004,10 +7039,7 @@ do_xlate_actions(const struct ofpact *ofpacts, size_t ofpacts_len,
             break;
 
         case OFPACT_DEC_TTL:
-            wc->masks.nw_ttl = 0xff;
-            if (compose_dec_ttl(ctx, ofpact_get_DEC_TTL(a))) {
-                return;
-            }
+            xlate_dec_ttl_action(ctx, ofpact_get_DEC_TTL(a));
             break;
 
         case OFPACT_NOTE:
diff --git a/ofproto/ofproto-dpif.c b/ofproto/ofproto-dpif.c
index fd0b2fdea..68e161e5f 100644
--- a/ofproto/ofproto-dpif.c
+++ b/ofproto/ofproto-dpif.c
@@ -1186,6 +1186,42 @@ check_trunc_action(struct dpif_backer *backer)
     return !error;
 }
 
+/* Tests whether 'dpif' datapath supports decrement of the IP TTL via
+ * OVS_ACTION_DEC_TTL. */
+static bool
+check_dec_ttl_action(struct dpif *dpif)
+{
+    uint8_t actbuf[NL_A_FLAG_SIZE];
+    struct odputil_keybuf keybuf;
+    struct flow flow = { 0 };
+    struct ofpbuf actions;
+    struct ofpbuf key;
+    bool supported;
+
+    struct odp_flow_key_parms odp_parms = {
+        .flow = &flow,
+        .probe = true,
+    };
+
+    ofpbuf_use_stack(&key, &keybuf, sizeof keybuf);
+    odp_flow_key_from_flow(&odp_parms, &key);
+
+    ofpbuf_use_stack(&actions, &actbuf, sizeof actbuf);
+    nl_msg_put_flag(&actions, OVS_ACTION_ATTR_DEC_TTL);
+
+    supported = dpif_probe_feature(dpif, "dec_ttl", &key, &actions, NULL);
+
+    if (supported) {
+        VLOG_INFO("%s: Datapath supports dec_ttl action",
+                  dpif_name(dpif));
+    } else {
+        VLOG_INFO("%s: Datapath does not support dec_ttl action",
+                  dpif_name(dpif));
+    }
+
+    return supported;
+}
+
 /* Tests whether 'backer''s datapath supports the clone action
  * OVS_ACTION_ATTR_CLONE.   */
 static bool
@@ -1590,6 +1626,7 @@ check_support(struct dpif_backer *backer)
         dpif_supports_explicit_drop_action(backer->dpif);
     backer->rt_support.lb_output_action=
         dpif_supports_lb_output_action(backer->dpif);
+    backer->rt_support.dec_ttl_action = check_dec_ttl_action(backer->dpif);
 
     /* Flow fields. */
     backer->rt_support.odp.ct_state = check_ct_state(backer);
diff --git a/ofproto/ofproto-dpif.h b/ofproto/ofproto-dpif.h
index b41c3d82a..f4d1152bc 100644
--- a/ofproto/ofproto-dpif.h
+++ b/ofproto/ofproto-dpif.h
@@ -204,7 +204,11 @@ struct group_dpif *group_dpif_lookup(struct ofproto_dpif *,
     DPIF_SUPPORT_FIELD(bool, explicit_drop_action, "Explicit Drop action")  \
                                                                             \
     /* True if the datapath supports balance_tcp optimization */            \
-    DPIF_SUPPORT_FIELD(bool, lb_output_action, "Optimized Balance TCP mode")
+    DPIF_SUPPORT_FIELD(bool, lb_output_action, "Optimized Balance TCP mode")\
+                                                                            \
+    /* True if the datapath supports dec_ttl action. */                     \
+    DPIF_SUPPORT_FIELD(bool, dec_ttl_action, "Decrement TTL action")
+
 
 
 /* Stores the various features which the corresponding backer supports. */



More information about the dev mailing list