This change implements support for the check_pkt_len action using the TC police action, which supports MTU checking.
Signed-off-by: Eelco Chaudron <[email protected]> Acked-by: Mike Pattrick <[email protected]> --- v5: - Sync with upstream branch. - Merged nl_parse_act_police_mtu() function with the nl_parse_act_police(). v3: - Was using TCA_CSUM_PARMS instead of TCA_POLICE_TBF. lib/netdev-offload-tc.c | 174 ++++++++++++++++++ lib/ofp-actions.c | 2 lib/tc.c | 443 ++++++++++++++++++++++++++++++++++++++++++----- lib/tc.h | 22 ++ 4 files changed, 587 insertions(+), 54 deletions(-) diff --git a/lib/netdev-offload-tc.c b/lib/netdev-offload-tc.c index 81f06cdb7..8516ac156 100644 --- a/lib/netdev-offload-tc.c +++ b/lib/netdev-offload-tc.c @@ -76,8 +76,6 @@ struct policer_node { uint32_t police_idx; }; -#define METER_POLICE_IDS_BASE 0x10000000 -#define METER_POLICE_IDS_MAX 0x1FFFFFFF /* Protects below meter police ids pool. */ static struct ovs_mutex meter_police_ids_mutex = OVS_MUTEX_INITIALIZER; static struct id_pool *meter_police_ids OVS_GUARDED_BY(meter_police_ids_mutex); @@ -91,6 +89,14 @@ static struct hmap police_idx_to_meter_id OVS_GUARDED_BY(meter_mutex) static int meter_id_lookup(uint32_t meter_id, uint32_t *police_idx); static int police_idx_lookup(uint32_t police_idx, uint32_t *meter_id); +static int netdev_tc_parse_nl_actions(struct netdev *netdev, + struct tc_flower *flower, + struct offload_info *info, + const struct nlattr *actions, + size_t actions_len, + bool *recirc_act, bool more_actions, + struct tc_action **need_jump_update); + static bool is_internal_port(const char *type) { @@ -863,6 +869,45 @@ _parse_tc_flower_to_actions(struct tc_flower *flower, struct ofpbuf *buf, nl_msg_put_u32(buf, OVS_ACTION_ATTR_METER, meter_id); } break; + case TC_ACT_POLICE_MTU: { + size_t offset, act_offset; + uint32_t jump; + + offset = nl_msg_start_nested(buf, + OVS_ACTION_ATTR_CHECK_PKT_LEN); + + nl_msg_put_u16(buf, OVS_CHECK_PKT_LEN_ATTR_PKT_LEN, + action->police.mtu); + + act_offset = nl_msg_start_nested( + buf, OVS_CHECK_PKT_LEN_ATTR_ACTIONS_IF_GREATER); + i = _parse_tc_flower_to_actions(flower, buf, i + 1, + action->police.result_jump); + nl_msg_end_nested(buf, act_offset); + + act_offset = nl_msg_start_nested( + buf, OVS_CHECK_PKT_LEN_ATTR_ACTIONS_IF_LESS_EQUAL); + + jump = flower->actions[i - 1].jump_action; + if (jump == JUMP_ACTION_STOP) { + jump = max_index; + } + if (jump != 0) { + i = _parse_tc_flower_to_actions(flower, buf, i, jump); + } + nl_msg_end_nested(buf, act_offset); + + i--; + nl_msg_end_nested(buf, offset); + } + break; + } + + if (action->jump_action && action->type != TC_ACT_POLICE_MTU) { + /* If there is a jump, it means this was the end of an action + * set and we need to end this branch. */ + i++; + break; } } return i; @@ -1622,11 +1667,121 @@ parse_match_ct_state_to_flower(struct tc_flower *flower, struct match *match) } } + +static int +parse_check_pkt_len_action(struct netdev *netdev, struct tc_flower *flower, + struct offload_info *info, struct tc_action *action, + const struct nlattr *nla, bool last_action, + struct tc_action **need_jump_update, + bool *recirc_act) +{ + struct tc_action *ge_jump_update = NULL, *le_jump_update = NULL; + const struct nlattr *nl_actions; + int err, le_offset, gt_offset; + uint16_t pkt_len; + + static const struct nl_policy ovs_cpl_policy[] = { + [OVS_CHECK_PKT_LEN_ATTR_PKT_LEN] = { .type = NL_A_U16 }, + [OVS_CHECK_PKT_LEN_ATTR_ACTIONS_IF_GREATER] = { .type = NL_A_NESTED }, + [OVS_CHECK_PKT_LEN_ATTR_ACTIONS_IF_LESS_EQUAL] + = { .type = NL_A_NESTED }, + }; + struct nlattr *a[ARRAY_SIZE(ovs_cpl_policy)]; + + if (!nl_parse_nested(nla, ovs_cpl_policy, a, ARRAY_SIZE(a))) { + VLOG_INFO("Received invalid formatted OVS_ACTION_ATTR_CHECK_PKT_LEN!"); + return EOPNOTSUPP; + } + + if (!a[OVS_CHECK_PKT_LEN_ATTR_ACTIONS_IF_GREATER] || + !a[OVS_CHECK_PKT_LEN_ATTR_ACTIONS_IF_LESS_EQUAL]) { + VLOG_INFO("Received invalid OVS_CHECK_PKT_LEN_ATTR_ACTION_IF_*!"); + return EOPNOTSUPP; + } + + pkt_len = nl_attr_get_u16(a[OVS_CHECK_PKT_LEN_ATTR_PKT_LEN]); + + /* Add the police mtu action first in the allocated slot. */ + action->police.mtu = pkt_len; + action->type = TC_ACT_POLICE_MTU; + le_offset = flower->action_count++; + + /* Parse and add the greater than action(s). + * NOTE: The last_action parameter means that there are no more actions + * after the if () then ... else () case. */ + nl_actions = a[OVS_CHECK_PKT_LEN_ATTR_ACTIONS_IF_GREATER]; + err = netdev_tc_parse_nl_actions(netdev, flower, info, + nl_attr_get(nl_actions), + nl_attr_get_size(nl_actions), + recirc_act, !last_action, + &ge_jump_update); + if (err) { + return err; + } + + /* Update goto offset for le actions */ + flower->actions[le_offset].police.result_jump = flower->action_count; + + gt_offset = flower->action_count; + + /* Parse and add the less than action(s). */ + nl_actions = a[OVS_CHECK_PKT_LEN_ATTR_ACTIONS_IF_LESS_EQUAL]; + err = netdev_tc_parse_nl_actions(netdev, flower, info, + nl_attr_get(nl_actions), + nl_attr_get_size(nl_actions), + recirc_act, !last_action, + &le_jump_update); + + if (gt_offset == flower->action_count && last_action) { + /* No le actions where added, fix gt offset */ + flower->actions[le_offset].police.result_jump = JUMP_ACTION_STOP; + } + + /* Update goto offset for gt actions to skip the le ones. */ + if (last_action) { + flower->actions[gt_offset - 1].jump_action = JUMP_ACTION_STOP; + + if (need_jump_update) { + *need_jump_update = NULL; + } + } else { + if (gt_offset == flower->action_count) { + flower->actions[gt_offset - 1].jump_action = 0; + } else { + flower->actions[gt_offset - 1].jump_action = flower->action_count; + } + /* If we have nested if() else () the if actions jump over the else + * and will end-up in the outer else () case, which it should have + * skipped. To void this we return the "potential" inner if() goto to + * need_jump_update, so it can be updated on return! + */ + if (need_jump_update) { + *need_jump_update = &flower->actions[gt_offset - 1]; + } + } + + if (le_jump_update != NULL) { + le_jump_update->jump_action = + flower->actions[gt_offset - 1].jump_action; + } + if (ge_jump_update != NULL) { + ge_jump_update->jump_action = + flower->actions[gt_offset - 1].jump_action; + } + + if (err) { + return err; + } + + return 0; +} + static int netdev_tc_parse_nl_actions(struct netdev *netdev, struct tc_flower *flower, struct offload_info *info, const struct nlattr *actions, size_t actions_len, - bool *recirc_act) + bool *recirc_act, bool more_actions, + struct tc_action **need_jump_update) { static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 20); const struct nlattr *nla; @@ -1756,6 +1911,16 @@ netdev_tc_parse_nl_actions(struct netdev *netdev, struct tc_flower *flower, action->type = TC_ACT_POLICE; action->police.index = police_index; flower->action_count++; + } else if (nl_attr_type(nla) == OVS_ACTION_ATTR_CHECK_PKT_LEN) { + err = parse_check_pkt_len_action(netdev, flower, info, action, nla, + nl_attr_len_pad(nla, + left) >= left + && !more_actions, + need_jump_update, + recirc_act); + if (err) { + return err; + } } else { VLOG_DBG_RL(&rl, "unsupported put action type: %d", nl_attr_type(nla)); @@ -2028,7 +2193,8 @@ netdev_tc_flow_put(struct netdev *netdev, struct match *match, /* Parse all (nested) actions. */ err = netdev_tc_parse_nl_actions(netdev, &flower, info, - actions, actions_len, &recirc_act); + actions, actions_len, &recirc_act, + false, NULL); if (err) { return err; } diff --git a/lib/ofp-actions.c b/lib/ofp-actions.c index d7e5f542a..cd10121e9 100644 --- a/lib/ofp-actions.c +++ b/lib/ofp-actions.c @@ -1,4 +1,4 @@ -/* + /* * Copyright (c) 2008-2017, 2019-2020 Nicira, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/lib/tc.c b/lib/tc.c index 392dee632..874057ac2 100644 --- a/lib/tc.c +++ b/lib/tc.c @@ -1008,6 +1008,18 @@ nl_parse_flower_flags(struct nlattr **attrs, struct tc_flower *flower) flower->offloaded_state = nl_get_flower_offloaded_state(attrs); } +static void +nl_parse_action_pc(uint32_t action_pc, struct tc_action *action) +{ + if (action_pc == TC_ACT_STOLEN) { + action->jump_action = JUMP_ACTION_STOP; + } else if (action_pc & TC_ACT_JUMP) { + action->jump_action = action_pc & TC_ACT_EXT_VAL_MASK; + } else { + action->jump_action = 0; + } +} + static const struct nl_policy pedit_policy[] = { [TCA_PEDIT_PARMS_EX] = { .type = NL_A_UNSPEC, .min_len = sizeof(struct tc_pedit), @@ -1107,6 +1119,7 @@ nl_parse_act_pedit(struct nlattr *options, struct tc_flower *flower) action->type = TC_ACT_PEDIT; + nl_parse_action_pc(pe->action, action); return 0; } @@ -1281,6 +1294,7 @@ nl_parse_act_tunnel_key(struct nlattr *options, struct tc_flower *flower) if (err) { return err; } + nl_parse_action_pc(tun->action, action); } else if (tun->t_action == TCA_TUNNEL_KEY_ACT_RELEASE) { flower->tunnel = true; } else { @@ -1317,7 +1331,11 @@ get_user_hz(void) static void nl_parse_tcf(const struct tcf_t *tm, struct tc_flower *flower) { - flower->lastused = time_msec() - (tm->lastuse * 1000 / get_user_hz()); + uint64_t lastused = time_msec() - (tm->lastuse * 1000 / get_user_hz()); + + if (flower->lastused < lastused) { + flower->lastused = lastused; + } } static int @@ -1342,6 +1360,7 @@ nl_parse_act_gact(struct nlattr *options, struct tc_flower *flower) action = &flower->actions[flower->action_count++]; action->chain = p->action & TC_ACT_EXT_VAL_MASK; action->type = TC_ACT_GOTO; + nl_parse_action_pc(p->action, action); } else if (p->action != TC_ACT_SHOT) { VLOG_ERR_RL(&error_rl, "unknown gact action: %d", p->action); return EINVAL; @@ -1378,6 +1397,7 @@ nl_parse_act_police(const struct nlattr *options, struct tc_flower *flower) { struct nlattr *police_attrs[ARRAY_SIZE(police_policy)] = {}; const struct tc_police *police; + struct nlattr *police_result; struct tc_action *action; struct nlattr *police_tm; const struct tcf_t *tm; @@ -1390,8 +1410,22 @@ nl_parse_act_police(const struct nlattr *options, struct tc_flower *flower) police = nl_attr_get_unspec(police_attrs[TCA_POLICE_TBF], sizeof *police); action = &flower->actions[flower->action_count++]; - action->type = TC_ACT_POLICE; - action->police.index = police->index; + + police_result = police_attrs[TCA_POLICE_RESULT]; + if (police_result && !tc_is_meter_index(police->index)) { + action->type = TC_ACT_POLICE_MTU; + action->police.mtu = police->mtu; + + uint32_t action_pc = nl_attr_get_u32(police_result); + if (action_pc & TC_ACT_JUMP) { + action->police.result_jump = action_pc & TC_ACT_EXT_VAL_MASK; + } else { + action->police.result_jump = JUMP_ACTION_STOP; + } + } else { + action->type = TC_ACT_POLICE; + action->police.index = police->index; + } police_tm = police_attrs[TCA_POLICE_TM]; if (police_tm) { @@ -1399,6 +1433,7 @@ nl_parse_act_police(const struct nlattr *options, struct tc_flower *flower) nl_parse_tcf(tm, flower); } + nl_parse_action_pc(police->action, action); return 0; } @@ -1449,8 +1484,9 @@ nl_parse_act_mirred(struct nlattr *options, struct tc_flower *flower) mirred_tm = mirred_attrs[TCA_MIRRED_TM]; memcpy(&tm, nl_attr_get_unspec(mirred_tm, sizeof tm), sizeof tm); - nl_parse_tcf(&tm, flower); + nl_parse_tcf(&tm, flower); + nl_parse_action_pc(m->action, action); return 0; } @@ -1579,6 +1615,7 @@ nl_parse_act_ct(struct nlattr *options, struct tc_flower *flower) } action->type = TC_ACT_CT; + nl_parse_action_pc(ct->action, action); return 0; } @@ -1624,6 +1661,8 @@ nl_parse_act_vlan(struct nlattr *options, struct tc_flower *flower) v->action, v->v_action); return EINVAL; } + + nl_parse_action_pc(v->action, action); return 0; } @@ -1717,6 +1756,7 @@ nl_parse_act_mpls(struct nlattr *options, struct tc_flower *flower) return EINVAL; } + nl_parse_action_pc(m->action, action); return 0; } @@ -1757,6 +1797,12 @@ nl_parse_act_csum(struct nlattr *options, struct tc_flower *flower) return EINVAL; } + /* The action_pc should be set on the previous action. */ + if (flower->action_count < TCA_ACT_MAX_NUM) { + struct tc_action *action = &flower->actions[flower->action_count]; + + nl_parse_action_pc(c->action, action); + } return 0; } @@ -1831,7 +1877,7 @@ nl_parse_action_stats(struct nlattr *act_stats, static int nl_parse_single_action(struct nlattr *action, struct tc_flower *flower, - bool terse) + bool terse, bool *csum) { struct nlattr *act_options; struct nlattr *act_cookie; @@ -1846,6 +1892,7 @@ nl_parse_single_action(struct nlattr *action, struct tc_flower *flower, return EPROTO; } + *csum = false; act_kind = nl_attr_get_string(action_attrs[TCA_ACT_KIND]); act_options = action_attrs[TCA_ACT_OPTIONS]; act_cookie = action_attrs[TCA_ACT_COOKIE]; @@ -1866,6 +1913,7 @@ nl_parse_single_action(struct nlattr *action, struct tc_flower *flower, err = nl_parse_act_pedit(act_options, flower); } else if (!strcmp(act_kind, "csum")) { nl_parse_act_csum(act_options, flower); + *csum = true; } else if (!strcmp(act_kind, "skbedit")) { /* Added for TC rule only (not in OvS rule) so ignore. */ } else if (!strcmp(act_kind, "ct")) { @@ -1917,6 +1965,10 @@ nl_parse_flower_actions(struct nlattr **attrs, struct tc_flower *flower, static struct nl_policy actions_orders_policy[TCA_ACT_MAX_NUM + 1] = {}; struct nlattr *actions_orders[ARRAY_SIZE(actions_orders_policy)]; const int max_size = ARRAY_SIZE(actions_orders_policy); + int previous_action_count = 0; + bool need_jump_adjust = false; + int jump_adjust = 0; + bool csum = false; for (int i = TCA_ACT_MIN_PRIO; i < max_size; i++) { actions_orders_policy[i].type = NL_A_NESTED; @@ -1937,7 +1989,70 @@ nl_parse_flower_actions(struct nlattr **attrs, struct tc_flower *flower, VLOG_DBG_RL(&error_rl, "Can only support %d actions", TCA_ACT_MAX_NUM); return EOPNOTSUPP; } - err = nl_parse_single_action(actions_orders[i], flower, terse); + err = nl_parse_single_action(actions_orders[i], flower, terse, + &csum); + + if (flower->action_count == previous_action_count) { + + struct tc_action *action; + + /* We had no update on the TC action count, which means + * we had a none TC type action. So need to adjust existing + * jump offsets. */ + jump_adjust++; + + if (need_jump_adjust || (csum && flower->action_count > 0)) { + + if (csum && flower->action_count > 0) { + /* The csum action is special as it might carry + * a jump count for the previous TC_ACT and therefore + * should be adjusted with jump_adjust as it got + * copied. */ + action = &flower->actions[flower->action_count - 1]; + if (action->jump_action + && action->jump_action != JUMP_ACTION_STOP) { + action->jump_action -= (jump_adjust - 1); + } + } + + for (int j = 0; j < flower->action_count; j++) { + action = &flower->actions[j]; + + if (action->type == TC_ACT_POLICE_MTU + && action->police.result_jump != JUMP_ACTION_STOP + && (action->police.result_jump - 1) > + flower->action_count) { + + action->police.result_jump--; + } + + if (action->jump_action + && action->jump_action != JUMP_ACTION_STOP + && (action->jump_action - 1) > + flower->action_count) { + + action->jump_action--; + } + } + } + } else { + struct tc_action *action; + + action = &flower->actions[previous_action_count]; + if (action->type == TC_ACT_POLICE_MTU && + action->police.result_jump != JUMP_ACTION_STOP) { + action->police.result_jump -= jump_adjust; + need_jump_adjust = true; + } + + if (action->jump_action + && action->jump_action != JUMP_ACTION_STOP) { + action->jump_action -= jump_adjust; + need_jump_adjust = true; + } + + previous_action_count = flower->action_count; + } if (err) { return err; @@ -2137,8 +2252,11 @@ parse_netlink_to_tc_policer(struct ofpbuf *reply, uint32_t police_idx[]) for (i = 0; i < TCA_ACT_MAX_PRIO; i++) { if (actions_orders[i]) { + bool csum; + memset(&flower, 0, sizeof(struct tc_flower)); - err = nl_parse_single_action(actions_orders[i], &flower, false); + err = nl_parse_single_action(actions_orders[i], &flower, false, + &csum); if (err || flower.actions[0].type != TC_ACT_POLICE) { continue; } @@ -2191,14 +2309,14 @@ tc_get_tc_cls_policy(enum tc_offload_policy policy) } static void -nl_msg_put_act_csum(struct ofpbuf *request, uint32_t flags) +nl_msg_put_act_csum(struct ofpbuf *request, uint32_t flags, uint32_t action_pc) { size_t offset; nl_msg_put_string(request, TCA_ACT_KIND, "csum"); offset = nl_msg_start_nested(request, TCA_ACT_OPTIONS); { - struct tc_csum parm = { .action = TC_ACT_PIPE, + struct tc_csum parm = { .action = action_pc, .update_flags = flags }; nl_msg_put_unspec(request, TCA_CSUM_PARMS, &parm, sizeof parm); @@ -2208,7 +2326,7 @@ nl_msg_put_act_csum(struct ofpbuf *request, uint32_t flags) static void nl_msg_put_act_pedit(struct ofpbuf *request, struct tc_pedit *parm, - struct tc_pedit_key_ex *ex) + struct tc_pedit_key_ex *ex, uint32_t action_pc) { size_t ksize = sizeof *parm + parm->nkeys * sizeof(struct tc_pedit_key); size_t offset, offset_keys_ex, offset_key; @@ -2217,7 +2335,7 @@ nl_msg_put_act_pedit(struct ofpbuf *request, struct tc_pedit *parm, nl_msg_put_string(request, TCA_ACT_KIND, "pedit"); offset = nl_msg_start_nested(request, TCA_ACT_OPTIONS); { - parm->action = TC_ACT_PIPE; + parm->action = action_pc; nl_msg_put_unspec(request, TCA_PEDIT_PARMS_EX, parm, ksize); offset_keys_ex = nl_msg_start_nested(request, TCA_PEDIT_KEYS_EX); @@ -2234,14 +2352,14 @@ nl_msg_put_act_pedit(struct ofpbuf *request, struct tc_pedit *parm, static void nl_msg_put_act_push_vlan(struct ofpbuf *request, ovs_be16 tpid, - uint16_t vid, uint8_t prio) + uint16_t vid, uint8_t prio, uint32_t action_pc) { size_t offset; nl_msg_put_string(request, TCA_ACT_KIND, "vlan"); offset = nl_msg_start_nested(request, TCA_ACT_OPTIONS); { - struct tc_vlan parm = { .action = TC_ACT_PIPE, + struct tc_vlan parm = { .action = action_pc, .v_action = TCA_VLAN_ACT_PUSH }; nl_msg_put_unspec(request, TCA_VLAN_PARMS, &parm, sizeof parm); @@ -2253,14 +2371,14 @@ nl_msg_put_act_push_vlan(struct ofpbuf *request, ovs_be16 tpid, } static void -nl_msg_put_act_pop_vlan(struct ofpbuf *request) +nl_msg_put_act_pop_vlan(struct ofpbuf *request, uint32_t action_pc) { size_t offset; nl_msg_put_string(request, TCA_ACT_KIND, "vlan"); offset = nl_msg_start_nested(request, TCA_ACT_OPTIONS); { - struct tc_vlan parm = { .action = TC_ACT_PIPE, + struct tc_vlan parm = { .action = action_pc, .v_action = TCA_VLAN_ACT_POP }; nl_msg_put_unspec(request, TCA_VLAN_PARMS, &parm, sizeof parm); @@ -2269,14 +2387,15 @@ nl_msg_put_act_pop_vlan(struct ofpbuf *request) } static void -nl_msg_put_act_pop_mpls(struct ofpbuf *request, ovs_be16 proto) +nl_msg_put_act_pop_mpls(struct ofpbuf *request, ovs_be16 proto, + uint32_t action_pc) { size_t offset; nl_msg_put_string(request, TCA_ACT_KIND, "mpls"); offset = nl_msg_start_nested(request, TCA_ACT_OPTIONS | NLA_F_NESTED); { - struct tc_mpls parm = { .action = TC_ACT_PIPE, + struct tc_mpls parm = { .action = action_pc, .m_action = TCA_MPLS_ACT_POP }; nl_msg_put_unspec(request, TCA_MPLS_PARMS, &parm, sizeof parm); @@ -2287,14 +2406,15 @@ nl_msg_put_act_pop_mpls(struct ofpbuf *request, ovs_be16 proto) static void nl_msg_put_act_push_mpls(struct ofpbuf *request, ovs_be16 proto, - uint32_t label, uint8_t tc, uint8_t ttl, uint8_t bos) + uint32_t label, uint8_t tc, uint8_t ttl, uint8_t bos, + uint32_t action_pc) { size_t offset; nl_msg_put_string(request, TCA_ACT_KIND, "mpls"); offset = nl_msg_start_nested(request, TCA_ACT_OPTIONS | NLA_F_NESTED); { - struct tc_mpls parm = { .action = TC_ACT_PIPE, + struct tc_mpls parm = { .action = action_pc, .m_action = TCA_MPLS_ACT_PUSH }; nl_msg_put_unspec(request, TCA_MPLS_PARMS, &parm, sizeof parm); @@ -2309,14 +2429,14 @@ nl_msg_put_act_push_mpls(struct ofpbuf *request, ovs_be16 proto, static void nl_msg_put_act_set_mpls(struct ofpbuf *request, uint32_t label, uint8_t tc, - uint8_t ttl, uint8_t bos) + uint8_t ttl, uint8_t bos, uint32_t action_pc) { size_t offset; nl_msg_put_string(request, TCA_ACT_KIND, "mpls"); offset = nl_msg_start_nested(request, TCA_ACT_OPTIONS | NLA_F_NESTED); { - struct tc_mpls parm = { .action = TC_ACT_PIPE, + struct tc_mpls parm = { .action = action_pc, .m_action = TCA_MPLS_ACT_MODIFY }; nl_msg_put_unspec(request, TCA_MPLS_PARMS, &parm, sizeof parm); @@ -2385,14 +2505,14 @@ nl_msg_put_act_tunnel_key_set(struct ofpbuf *request, bool id_present, struct in6_addr *ipv6_dst, ovs_be16 tp_dst, uint8_t tos, uint8_t ttl, struct tun_metadata tun_metadata, - uint8_t no_csum) + uint8_t no_csum, uint32_t action_pc) { size_t offset; nl_msg_put_string(request, TCA_ACT_KIND, "tunnel_key"); offset = nl_msg_start_nested(request, TCA_ACT_OPTIONS); { - struct tc_tunnel_key tun = { .action = TC_ACT_PIPE, + struct tc_tunnel_key tun = { .action = action_pc, .t_action = TCA_TUNNEL_KEY_ACT_SET }; nl_msg_put_unspec(request, TCA_TUNNEL_KEY_PARMS, &tun, sizeof tun); @@ -2445,23 +2565,26 @@ nl_msg_put_act_gact(struct ofpbuf *request, uint32_t chain) } static void -nl_msg_put_act_police_index(struct ofpbuf *request, uint32_t police_idx) +nl_msg_put_act_police_index(struct ofpbuf *request, uint32_t police_idx, + uint32_t action_pc) { struct tc_police police; size_t offset; memset(&police, 0, sizeof police); police.index = police_idx; + police.action = action_pc; nl_msg_put_string(request, TCA_ACT_KIND, "police"); offset = nl_msg_start_nested(request, TCA_ACT_OPTIONS); nl_msg_put_unspec(request, TCA_POLICE_TBF, &police, sizeof police); - nl_msg_put_u32(request, TCA_POLICE_RESULT, TC_ACT_PIPE); + nl_msg_put_u32(request, TCA_POLICE_RESULT, action_pc); nl_msg_end_nested(request, offset); } static void -nl_msg_put_act_ct(struct ofpbuf *request, struct tc_action *action) +nl_msg_put_act_ct(struct ofpbuf *request, struct tc_action *action, + uint32_t action_pc) { uint16_t ct_action = 0; size_t offset; @@ -2470,7 +2593,7 @@ nl_msg_put_act_ct(struct ofpbuf *request, struct tc_action *action) offset = nl_msg_start_nested(request, TCA_ACT_OPTIONS | NLA_F_NESTED); { struct tc_ct ct = { - .action = TC_ACT_PIPE, + .action = action_pc, }; if (!action->ct.clear) { @@ -2675,10 +2798,57 @@ csum_update_flag(struct tc_flower *flower, return EOPNOTSUPP; } +static bool +rewrite_pedits_need_csum_update(struct tc_action *action) +{ + int i, j; + + for (i = 0; i < ARRAY_SIZE(flower_pedit_map); i++) { + struct flower_key_to_pedit *m = &flower_pedit_map[i]; + ovs_be32 *mask, *data, first_word_mask, last_word_mask; + int cnt = 0, cur_offset = 0; + + if (!m->size) { + continue; + } + + calc_offsets(action, m, &cur_offset, &cnt, &last_word_mask, + &first_word_mask, &mask, &data); + + for (j = 0; j < cnt; j++, mask++) { + ovs_be32 mask_word = *mask; + + if (j == 0) { + mask_word &= first_word_mask; + } + if (j == cnt - 1) { + mask_word &= last_word_mask; + } + if (!mask_word) { + continue; + } + + switch (m->htype) { + case TCA_PEDIT_KEY_EX_HDR_TYPE_IP4: + case TCA_PEDIT_KEY_EX_HDR_TYPE_IP6: + case TCA_PEDIT_KEY_EX_HDR_TYPE_TCP: + case TCA_PEDIT_KEY_EX_HDR_TYPE_UDP: + return true; + case TCA_PEDIT_KEY_EX_HDR_TYPE_NETWORK: + case TCA_PEDIT_KEY_EX_HDR_TYPE_ETH: + case __PEDIT_HDR_TYPE_MAX: + break; + } + } + } + return false; +} + static int nl_msg_put_flower_rewrite_pedits(struct ofpbuf *request, struct tc_flower *flower, - struct tc_action *action) + struct tc_action *action, + uint32_t action_pc) { struct { struct tc_pedit sel; @@ -2745,7 +2915,8 @@ nl_msg_put_flower_rewrite_pedits(struct ofpbuf *request, } } } - nl_msg_put_act_pedit(request, &sel.sel, sel.keys_ex); + nl_msg_put_act_pedit(request, &sel.sel, sel.keys_ex, + flower->csum_update_flags ? TC_ACT_PIPE : action_pc); return 0; } @@ -2766,7 +2937,7 @@ nl_msg_put_flower_acts_release(struct ofpbuf *request, uint16_t act_index) * last pedit action. */ static void nl_msg_put_csum_act(struct ofpbuf *request, struct tc_flower *flower, - uint16_t *act_index) + uint32_t action_pc, uint16_t *act_index) { size_t act_offset; @@ -2776,7 +2947,7 @@ nl_msg_put_csum_act(struct ofpbuf *request, struct tc_flower *flower, } act_offset = nl_msg_start_nested(request, (*act_index)++); - nl_msg_put_act_csum(request, flower->csum_update_flags); + nl_msg_put_act_csum(request, flower->csum_update_flags, action_pc); nl_msg_put_act_flags(request); nl_msg_end_nested(request, act_offset); @@ -2784,6 +2955,125 @@ nl_msg_put_csum_act(struct ofpbuf *request, struct tc_flower *flower, flower->csum_update_flags = 0; } +static int +get_action_index_for_tc_actions(struct tc_flower *flower, uint16_t act_index, + struct tc_action *action, int action_count, + bool tunnel_key_released) +{ + bool need_csum = false; + + if (action_count < 0) { + /* Only forward jumps are supported */ + return -EINVAL; + } + + for (int i = 0; i < action_count; i++, action++) { + if (action->type != TC_ACT_PEDIT && need_csum) { + need_csum = false; + act_index++; + } + + switch (action->type) { + + case TC_ACT_OUTPUT: + if (!tunnel_key_released && flower->tunnel) { + act_index++; + tunnel_key_released = true; + } + if (action->out.ingress) { + act_index++; + } + act_index++; + break; + + case TC_ACT_ENCAP: + if (!tunnel_key_released && flower->tunnel) { + act_index++; + tunnel_key_released = true; + } + act_index++; + break; + + case TC_ACT_PEDIT: + if (!need_csum) { + need_csum = rewrite_pedits_need_csum_update(action); + } + if (i == (action_count - 1) && need_csum) { + need_csum = false; + act_index++; + } + act_index++; + break; + + case TC_ACT_POLICE: + case TC_ACT_POLICE_MTU: + case TC_ACT_VLAN_POP: + case TC_ACT_VLAN_PUSH: + case TC_ACT_MPLS_POP: + case TC_ACT_MPLS_PUSH: + case TC_ACT_MPLS_SET: + case TC_ACT_GOTO: + case TC_ACT_CT: + /* Increase act_index by one if we are sure this type of action + * will only add one tc action in the kernel. */ + act_index++; + break; + + /* If we can't determine how many tc actions will be added by the + * kernel return -EOPNOTSUPP. + * + * Please do NOT add a default case. */ + } + } + + return act_index; +} + +static int +nl_msg_put_act_police_mtu(struct ofpbuf *request, struct tc_flower *flower, + struct tc_action *action, uint32_t action_pc, + int action_index, uint16_t act_index, bool released) +{ + uint32_t tc_action; + size_t offset; + + if (action->police.result_jump != JUMP_ACTION_STOP) { + int jump_index; + int action_count = action->police.result_jump - action_index - 1; + + jump_index = get_action_index_for_tc_actions(flower, + act_index - 1, + action + 1, + action_count, + released); + if (jump_index < 0) { + return -jump_index; + } + tc_action = TC_ACT_JUMP | (jump_index & TC_ACT_EXT_VAL_MASK); + } else { + tc_action = TC_ACT_STOLEN; + } + + nl_msg_put_string(request, TCA_ACT_KIND, "police"); + offset = nl_msg_start_nested(request, TCA_ACT_OPTIONS); + { + struct tc_police p = { .action = action_pc, + .mtu = action->police.mtu }; + + nl_msg_put_unspec(request, TCA_POLICE_TBF, &p, sizeof p); + + /* The value in jump_action is the total number of TC_ACT_* + * we need to jump, not the actual number of TCA_ACT_KIND + * (act_index) actions. As certain TC_ACT_* actions can be + * translated into multiple TCA_ACT_KIND ones. + * + * See nl_msg_put_flower_acts() below for more details. */ + nl_msg_put_u32(request, TCA_POLICE_RESULT, tc_action); + } + nl_msg_end_nested(request, offset); + return 0; +} + static int nl_msg_put_flower_acts(struct ofpbuf *request, struct tc_flower *flower) { @@ -2797,17 +3087,59 @@ nl_msg_put_flower_acts(struct ofpbuf *request, struct tc_flower *flower) offset = nl_msg_start_nested(request, TCA_FLOWER_ACT); { int error; + uint32_t prev_action_pc = TC_ACT_PIPE; action = flower->actions; for (i = 0; i < flower->action_count; i++, action++) { - if (action->type != TC_ACT_PEDIT) { - nl_msg_put_csum_act(request, flower, &act_index); + uint32_t action_pc; /* Programmatic Control */ + + if (!action->jump_action) { + action_pc = TC_ACT_PIPE; + } else if (action->jump_action == JUMP_ACTION_STOP) { + action_pc = TC_ACT_STOLEN; + } else { + /* The value in jump_action is the total number of TC_ACT_* + * we need to jump, not the actual number of TCA_ACT_KIND + * (act_index) actions. As certain TC_ACT_* actions can be + * translated into multiple TCA_ACT_KIND ones. + * + * If we can determine the number of actual actions being + * inserted we will update the count, if not we will return + * -EOPNOTSUPP. + */ + int jump_index; + int act_index_start = act_index - 1; + int action_count = (action->jump_action & + TC_ACT_EXT_VAL_MASK) - i; + + if (flower->csum_update_flags && + (action->type != TC_ACT_PEDIT + || prev_action_pc & TC_ACT_JUMP)) { + act_index_start++; + } + + jump_index = get_action_index_for_tc_actions(flower, + act_index_start, + action, + action_count, + released); + if (jump_index < 0) { + return -jump_index; + } + + action_pc = TC_ACT_JUMP | jump_index; + } + + if (action->type != TC_ACT_PEDIT || prev_action_pc & TC_ACT_JUMP) { + nl_msg_put_csum_act(request, flower, prev_action_pc, + &act_index); } + switch (action->type) { case TC_ACT_PEDIT: { act_offset = nl_msg_start_nested(request, act_index++); error = nl_msg_put_flower_rewrite_pedits(request, flower, - action); + action, action_pc); if (error) { return error; } @@ -2815,7 +3147,8 @@ nl_msg_put_flower_acts(struct ofpbuf *request, struct tc_flower *flower) if (i == flower->action_count - 1) { /* If this is the last action check csum calc again. */ - nl_msg_put_csum_act(request, flower, &act_index); + nl_msg_put_csum_act(request, flower, action_pc, + &act_index); } } break; @@ -2836,14 +3169,15 @@ nl_msg_put_flower_acts(struct ofpbuf *request, struct tc_flower *flower) action->encap.tos, action->encap.ttl, action->encap.data, - action->encap.no_csum); + action->encap.no_csum, + action_pc); nl_msg_put_act_flags(request); nl_msg_end_nested(request, act_offset); } break; case TC_ACT_VLAN_POP: { act_offset = nl_msg_start_nested(request, act_index++); - nl_msg_put_act_pop_vlan(request); + nl_msg_put_act_pop_vlan(request, action_pc); nl_msg_put_act_flags(request); nl_msg_end_nested(request, act_offset); } @@ -2853,14 +3187,16 @@ nl_msg_put_flower_acts(struct ofpbuf *request, struct tc_flower *flower) nl_msg_put_act_push_vlan(request, action->vlan.vlan_push_tpid, action->vlan.vlan_push_id, - action->vlan.vlan_push_prio); + action->vlan.vlan_push_prio, + action_pc); nl_msg_put_act_flags(request); nl_msg_end_nested(request, act_offset); } break; case TC_ACT_MPLS_POP: { act_offset = nl_msg_start_nested(request, act_index++); - nl_msg_put_act_pop_mpls(request, action->mpls.proto); + nl_msg_put_act_pop_mpls(request, action->mpls.proto, + action_pc); nl_msg_end_nested(request, act_offset); } break; @@ -2868,7 +3204,8 @@ nl_msg_put_flower_acts(struct ofpbuf *request, struct tc_flower *flower) act_offset = nl_msg_start_nested(request, act_index++); nl_msg_put_act_push_mpls(request, action->mpls.proto, action->mpls.label, action->mpls.tc, - action->mpls.ttl, action->mpls.bos); + action->mpls.ttl, action->mpls.bos, + action_pc); nl_msg_end_nested(request, act_offset); } break; @@ -2876,7 +3213,7 @@ nl_msg_put_flower_acts(struct ofpbuf *request, struct tc_flower *flower) act_offset = nl_msg_start_nested(request, act_index++); nl_msg_put_act_set_mpls(request, action->mpls.label, action->mpls.tc, action->mpls.ttl, - action->mpls.bos); + action->mpls.bos, action_pc); nl_msg_end_nested(request, act_offset); } break; @@ -2911,12 +3248,13 @@ nl_msg_put_flower_acts(struct ofpbuf *request, struct tc_flower *flower) nl_msg_put_act_mirred(request, ifindex, TC_ACT_STOLEN, TCA_EGRESS_REDIR); } + action->jump_action = JUMP_ACTION_STOP; } else { if (ingress) { - nl_msg_put_act_mirred(request, ifindex, TC_ACT_PIPE, + nl_msg_put_act_mirred(request, ifindex, action_pc, TCA_INGRESS_MIRROR); } else { - nl_msg_put_act_mirred(request, ifindex, TC_ACT_PIPE, + nl_msg_put_act_mirred(request, ifindex, action_pc, TCA_EGRESS_MIRROR); } } @@ -2944,18 +3282,33 @@ nl_msg_put_flower_acts(struct ofpbuf *request, struct tc_flower *flower) break; case TC_ACT_CT: { act_offset = nl_msg_start_nested(request, act_index++); - nl_msg_put_act_ct(request, action); + nl_msg_put_act_ct(request, action, action_pc); nl_msg_put_act_cookie(request, &flower->act_cookie); nl_msg_end_nested(request, act_offset); } break; case TC_ACT_POLICE: { act_offset = nl_msg_start_nested(request, act_index++); - nl_msg_put_act_police_index(request, action->police.index); + nl_msg_put_act_police_index(request, action->police.index, + action_pc); nl_msg_end_nested(request, act_offset); } break; + case TC_ACT_POLICE_MTU: { + act_offset = nl_msg_start_nested(request, act_index++); + if (nl_msg_put_act_police_mtu(request, flower, action, + action_pc, i, act_index, + released)) { + return -EOPNOTSUPP; + } + nl_msg_put_act_cookie(request, &flower->act_cookie); + nl_msg_put_act_flags(request); + nl_msg_end_nested(request, act_offset); } + break; + } + + prev_action_pc = action_pc; } } diff --git a/lib/tc.h b/lib/tc.h index 4690dce4f..2e64ad372 100644 --- a/lib/tc.h +++ b/lib/tc.h @@ -58,6 +58,17 @@ enum tc_qdisc_hook { TC_EGRESS, }; +#define METER_POLICE_IDS_BASE 0x10000000 +#define METER_POLICE_IDS_MAX 0x1FFFFFFF + +static inline bool +tc_is_meter_index(uint32_t index) { + if (index >= METER_POLICE_IDS_BASE && index <= METER_POLICE_IDS_MAX) { + return true; + } + return false; +} + /* Returns tc handle 'major':'minor'. */ static inline unsigned int tc_make_handle(unsigned int major, unsigned int minor) @@ -178,6 +189,7 @@ enum tc_action_type { TC_ACT_GOTO, TC_ACT_CT, TC_ACT_POLICE, + TC_ACT_POLICE_MTU, }; enum nat_type { @@ -260,18 +272,20 @@ struct tc_action { bool force; bool commit; } ct; - struct { struct tc_flower_key key; struct tc_flower_key mask; } rewrite; - struct { uint32_t index; + uint32_t result_jump; + uint16_t mtu; } police; - }; + }; - enum tc_action_type type; + enum tc_action_type type; + uint32_t jump_action; +#define JUMP_ACTION_STOP 0xffffffff }; /* assert that if we overflow with a masked write of uint32_t to the last byte _______________________________________________ dev mailing list [email protected] https://mail.openvswitch.org/mailman/listinfo/ovs-dev
