#define MAX_PEDIT_OFFSETS 32
+#ifndef TCM_IFINDEX_MAGIC_BLOCK
+#define TCM_IFINDEX_MAGIC_BLOCK (0xFFFFFFFFU)
+#endif
+
+#if TCA_MAX < 14
+#define TCA_INGRESS_BLOCK 13
+#endif
+
VLOG_DEFINE_THIS_MODULE(tc);
static struct vlog_rate_limit error_rl = VLOG_RATE_LIMIT_INIT(60, 5);
* The configuration and stats may be seen with the following command:
* /sbin/tc -s qdisc show dev <devname>
*
+ * If block_id is greater than 0, then the ingress qdisc is added to a block.
+ * In this case, it is equivalent to running (when 'add' is true):
+ * /sbin/tc qdisc add dev <devname> ingress_block <block_id> ingress
+ *
* Returns 0 if successful, otherwise a positive errno value.
*/
int
-tc_add_del_ingress_qdisc(int ifindex, bool add)
+tc_add_del_ingress_qdisc(int ifindex, bool add, uint32_t block_id)
{
struct ofpbuf request;
struct tcmsg *tcmsg;
tcmsg->tcm_parent = TC_H_INGRESS;
nl_msg_put_string(&request, TCA_KIND, "ingress");
nl_msg_put_unspec(&request, TCA_OPTIONS, NULL, 0);
+ if (block_id) {
+ nl_msg_put_u32(&request, TCA_INGRESS_BLOCK, block_id);
+ }
error = tc_transact(&request, NULL);
if (error) {
.optional = true, },
[TCA_FLOWER_KEY_IP_TTL_MASK] = { .type = NL_A_U8,
.optional = true, },
+ [TCA_FLOWER_KEY_IP_TOS] = { .type = NL_A_U8,
+ .optional = true, },
+ [TCA_FLOWER_KEY_IP_TOS_MASK] = { .type = NL_A_U8,
+ .optional = true, },
[TCA_FLOWER_KEY_TCP_FLAGS] = { .type = NL_A_U16,
.optional = true, },
[TCA_FLOWER_KEY_TCP_FLAGS_MASK] = { .type = NL_A_U16,
.optional = true, },
+ [TCA_FLOWER_KEY_CVLAN_ID] = { .type = NL_A_U16, .optional = true, },
+ [TCA_FLOWER_KEY_CVLAN_PRIO] = { .type = NL_A_U8, .optional = true, },
+ [TCA_FLOWER_KEY_CVLAN_ETH_TYPE] = { .type = NL_A_U16, .optional = true, },
};
static void
static void
nl_parse_flower_vlan(struct nlattr **attrs, struct tc_flower *flower)
{
- if (flower->key.eth_type != htons(ETH_TYPE_VLAN)) {
+ ovs_be16 encap_ethtype;
+
+ if (!eth_type_vlan(flower->key.eth_type)) {
return;
}
- flower->key.encap_eth_type =
+ flower->key.encap_eth_type[0] =
nl_attr_get_be16(attrs[TCA_FLOWER_KEY_ETH_TYPE]);
if (attrs[TCA_FLOWER_KEY_VLAN_ID]) {
- flower->key.vlan_id =
+ flower->key.vlan_id[0] =
nl_attr_get_u16(attrs[TCA_FLOWER_KEY_VLAN_ID]);
}
if (attrs[TCA_FLOWER_KEY_VLAN_PRIO]) {
- flower->key.vlan_prio =
+ flower->key.vlan_prio[0] =
nl_attr_get_u8(attrs[TCA_FLOWER_KEY_VLAN_PRIO]);
}
+
+ if (!attrs[TCA_FLOWER_KEY_VLAN_ETH_TYPE]) {
+ return;
+ }
+
+ encap_ethtype = nl_attr_get_be16(attrs[TCA_FLOWER_KEY_VLAN_ETH_TYPE]);
+ if (!eth_type_vlan(encap_ethtype)) {
+ return;
+ }
+
+ flower->key.encap_eth_type[1] = flower->key.encap_eth_type[0];
+ flower->key.encap_eth_type[0] = encap_ethtype;
+
+ if (attrs[TCA_FLOWER_KEY_CVLAN_ID]) {
+ flower->key.vlan_id[1] =
+ nl_attr_get_u16(attrs[TCA_FLOWER_KEY_CVLAN_ID]);
+ }
+ if (attrs[TCA_FLOWER_KEY_CVLAN_PRIO]) {
+ flower->key.vlan_prio[1] =
+ nl_attr_get_u8(attrs[TCA_FLOWER_KEY_CVLAN_PRIO]);
+ }
}
static void
key->ip_ttl = nl_attr_get_u8(attrs[TCA_FLOWER_KEY_IP_TTL]);
mask->ip_ttl = nl_attr_get_u8(attrs[TCA_FLOWER_KEY_IP_TTL_MASK]);
}
+
+ if (attrs[TCA_FLOWER_KEY_IP_TOS_MASK]) {
+ key->ip_tos = nl_attr_get_u8(attrs[TCA_FLOWER_KEY_IP_TOS]);
+ mask->ip_tos = nl_attr_get_u8(attrs[TCA_FLOWER_KEY_IP_TOS_MASK]);
+ }
}
static enum tc_offloaded_state
vlan_parms = vlan_attrs[TCA_VLAN_PARMS];
v = nl_attr_get_unspec(vlan_parms, sizeof *v);
if (v->v_action == TCA_VLAN_ACT_PUSH) {
+ struct nlattr *vlan_tpid = vlan_attrs[TCA_VLAN_PUSH_VLAN_PROTOCOL];
struct nlattr *vlan_id = vlan_attrs[TCA_VLAN_PUSH_VLAN_ID];
struct nlattr *vlan_prio = vlan_attrs[TCA_VLAN_PUSH_VLAN_PRIORITY];
+ action->vlan.vlan_push_tpid = nl_attr_get_be16(vlan_tpid);
action->vlan.vlan_push_id = nl_attr_get_u16(vlan_id);
action->vlan.vlan_push_prio = vlan_prio ? nl_attr_get_u8(vlan_prio) : 0;
action->type = TC_ACT_VLAN_PUSH;
}
int
-tc_dump_flower_start(int ifindex, struct nl_dump *dump)
+tc_dump_flower_start(int ifindex, struct nl_dump *dump, uint32_t block_id)
{
struct ofpbuf request;
struct tcmsg *tcmsg;
+ int index;
- tcmsg = tc_make_request(ifindex, RTM_GETTFILTER, NLM_F_DUMP, &request);
- tcmsg->tcm_parent = TC_INGRESS_PARENT;
+ index = block_id ? TCM_IFINDEX_MAGIC_BLOCK : ifindex;
+ tcmsg = tc_make_request(index, RTM_GETTFILTER, NLM_F_DUMP, &request);
+ tcmsg->tcm_parent = block_id ? : TC_INGRESS_PARENT;
tcmsg->tcm_info = TC_H_UNSPEC;
tcmsg->tcm_handle = 0;
}
int
-tc_flush(int ifindex)
+tc_flush(int ifindex, uint32_t block_id)
{
struct ofpbuf request;
struct tcmsg *tcmsg;
+ int index;
- tcmsg = tc_make_request(ifindex, RTM_DELTFILTER, NLM_F_ACK, &request);
- tcmsg->tcm_parent = TC_INGRESS_PARENT;
+ index = block_id ? TCM_IFINDEX_MAGIC_BLOCK : ifindex;
+ tcmsg = tc_make_request(index, RTM_DELTFILTER, NLM_F_ACK, &request);
+ tcmsg->tcm_parent = block_id ? : TC_INGRESS_PARENT;
tcmsg->tcm_info = TC_H_UNSPEC;
return tc_transact(&request, NULL);
}
int
-tc_del_filter(int ifindex, int prio, int handle)
+tc_del_filter(int ifindex, int prio, int handle, uint32_t block_id)
{
struct ofpbuf request;
struct tcmsg *tcmsg;
struct ofpbuf *reply;
int error;
+ int index;
- tcmsg = tc_make_request(ifindex, RTM_DELTFILTER, NLM_F_ECHO, &request);
- tcmsg->tcm_parent = TC_INGRESS_PARENT;
+ index = block_id ? TCM_IFINDEX_MAGIC_BLOCK : ifindex;
+ tcmsg = tc_make_request(index, RTM_DELTFILTER, NLM_F_ECHO, &request);
+ tcmsg->tcm_parent = block_id ? : TC_INGRESS_PARENT;
tcmsg->tcm_info = tc_make_handle(prio, 0);
tcmsg->tcm_handle = handle;
}
int
-tc_get_flower(int ifindex, int prio, int handle, struct tc_flower *flower)
+tc_get_flower(int ifindex, int prio, int handle, struct tc_flower *flower,
+ uint32_t block_id)
{
struct ofpbuf request;
struct tcmsg *tcmsg;
struct ofpbuf *reply;
int error;
+ int index;
- tcmsg = tc_make_request(ifindex, RTM_GETTFILTER, NLM_F_ECHO, &request);
- tcmsg->tcm_parent = TC_INGRESS_PARENT;
+ index = block_id ? TCM_IFINDEX_MAGIC_BLOCK : ifindex;
+ tcmsg = tc_make_request(index, RTM_GETTFILTER, NLM_F_ECHO, &request);
+ tcmsg->tcm_parent = block_id ? : TC_INGRESS_PARENT;
tcmsg->tcm_info = tc_make_handle(prio, 0);
tcmsg->tcm_handle = handle;
}
static void
-nl_msg_put_act_push_vlan(struct ofpbuf *request, uint16_t vid, uint8_t prio)
+nl_msg_put_act_push_vlan(struct ofpbuf *request, ovs_be16 tpid,
+ uint16_t vid, uint8_t prio)
{
size_t offset;
.v_action = TCA_VLAN_ACT_PUSH };
nl_msg_put_unspec(request, TCA_VLAN_PARMS, &parm, sizeof parm);
+ nl_msg_put_be16(request, TCA_VLAN_PUSH_VLAN_PROTOCOL, tpid);
nl_msg_put_u16(request, TCA_VLAN_PUSH_VLAN_ID, vid);
nl_msg_put_u8(request, TCA_VLAN_PUSH_VLAN_PRIORITY, prio);
}
case TC_ACT_VLAN_PUSH: {
act_offset = nl_msg_start_nested(request, act_index++);
nl_msg_put_act_push_vlan(request,
+ action->vlan.vlan_push_tpid,
action->vlan.vlan_push_id,
action->vlan.vlan_push_prio);
nl_msg_end_nested(request, act_offset);
{
uint16_t host_eth_type = ntohs(flower->key.eth_type);
- bool is_vlan = (host_eth_type == ETH_TYPE_VLAN);
+ bool is_vlan = eth_type_vlan(flower->key.eth_type);
+ bool is_qinq = is_vlan && eth_type_vlan(flower->key.encap_eth_type[0]);
int err;
/* need to parse acts first as some acts require changing the matching
}
if (is_vlan) {
- host_eth_type = ntohs(flower->key.encap_eth_type);
+ if (is_qinq) {
+ host_eth_type = ntohs(flower->key.encap_eth_type[1]);
+ } else {
+ host_eth_type = ntohs(flower->key.encap_eth_type[0]);
+ }
}
FLOWER_PUT_MASKED_VALUE(dst_mac, TCA_FLOWER_KEY_ETH_DST);
FLOWER_PUT_MASKED_VALUE(src_mac, TCA_FLOWER_KEY_ETH_SRC);
if (host_eth_type == ETH_P_IP || host_eth_type == ETH_P_IPV6) {
+ FLOWER_PUT_MASKED_VALUE(ip_ttl, TCA_FLOWER_KEY_IP_TTL);
+ FLOWER_PUT_MASKED_VALUE(ip_tos, TCA_FLOWER_KEY_IP_TOS);
+
if (flower->mask.ip_proto && flower->key.ip_proto) {
nl_msg_put_u8(request, TCA_FLOWER_KEY_IP_PROTO,
flower->key.ip_proto);
if (host_eth_type == ETH_P_IP) {
FLOWER_PUT_MASKED_VALUE(ipv4.ipv4_src, TCA_FLOWER_KEY_IPV4_SRC);
FLOWER_PUT_MASKED_VALUE(ipv4.ipv4_dst, TCA_FLOWER_KEY_IPV4_DST);
- FLOWER_PUT_MASKED_VALUE(ip_ttl, TCA_FLOWER_KEY_IP_TTL);
} else if (host_eth_type == ETH_P_IPV6) {
FLOWER_PUT_MASKED_VALUE(ipv6.ipv6_src, TCA_FLOWER_KEY_IPV6_SRC);
FLOWER_PUT_MASKED_VALUE(ipv6.ipv6_dst, TCA_FLOWER_KEY_IPV6_DST);
nl_msg_put_be16(request, TCA_FLOWER_KEY_ETH_TYPE, flower->key.eth_type);
if (is_vlan) {
- if (flower->key.vlan_id || flower->key.vlan_prio) {
+ if (flower->key.vlan_id[0] || flower->key.vlan_prio[0]) {
nl_msg_put_u16(request, TCA_FLOWER_KEY_VLAN_ID,
- flower->key.vlan_id);
+ flower->key.vlan_id[0]);
nl_msg_put_u8(request, TCA_FLOWER_KEY_VLAN_PRIO,
- flower->key.vlan_prio);
+ flower->key.vlan_prio[0]);
}
- if (flower->key.encap_eth_type) {
+ if (flower->key.encap_eth_type[0]) {
nl_msg_put_be16(request, TCA_FLOWER_KEY_VLAN_ETH_TYPE,
- flower->key.encap_eth_type);
+ flower->key.encap_eth_type[0]);
+ }
+
+ if (is_qinq) {
+ if (flower->key.vlan_id[1] || flower->key.vlan_prio[1]) {
+ nl_msg_put_u16(request, TCA_FLOWER_KEY_CVLAN_ID,
+ flower->key.vlan_id[1]);
+ nl_msg_put_u8(request, TCA_FLOWER_KEY_CVLAN_PRIO,
+ flower->key.vlan_prio[1]);
+ }
+ if (flower->key.encap_eth_type[1]) {
+ nl_msg_put_be16(request, TCA_FLOWER_KEY_CVLAN_ETH_TYPE,
+ flower->key.encap_eth_type[1]);
+ }
}
}
int
tc_replace_flower(int ifindex, uint16_t prio, uint32_t handle,
- struct tc_flower *flower)
+ struct tc_flower *flower, uint32_t block_id)
{
struct ofpbuf request;
struct tcmsg *tcmsg;
int error = 0;
size_t basic_offset;
uint16_t eth_type = (OVS_FORCE uint16_t) flower->key.eth_type;
+ int index;
- tcmsg = tc_make_request(ifindex, RTM_NEWTFILTER,
- NLM_F_CREATE | NLM_F_ECHO, &request);
- tcmsg->tcm_parent = TC_INGRESS_PARENT;
+ index = block_id ? TCM_IFINDEX_MAGIC_BLOCK : ifindex;
+ tcmsg = tc_make_request(index, RTM_NEWTFILTER, NLM_F_CREATE | NLM_F_ECHO,
+ &request);
+ tcmsg->tcm_parent = block_id ? : TC_INGRESS_PARENT;
tcmsg->tcm_info = tc_make_handle(prio, eth_type);
tcmsg->tcm_handle = handle;