Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

net: sched: add erspan option support to act_tunnel_key

This patch is to allow setting erspan options using the
act_tunnel_key action. Different from geneve options,
only one option can be set. And also, geneve options,
vxlan options or erspan options can't be set at the
same time.

Options are expressed as ver:index:dir:hwid, when ver
is set to 1, index will be applied while dir and hwid
will be ignored, and when ver is set to 2, dir and
hwid will be used while index will be ignored.

# ip link add name erspan1 type erspan external
# tc qdisc add dev eth0 ingress
# tc filter add dev eth0 protocol ip parent ffff: \
flower indev eth0 \
ip_proto udp \
action tunnel_key \
set src_ip 10.0.99.192 \
dst_ip 10.0.99.193 \
dst_port 6081 \
id 11 \
erspan_opts 1:2:0:0 \
action mirred egress redirect dev erspan1

v1->v2:
- do the validation when dst is not yet allocated as Jakub suggested.
- use Duplicate instead of Wrong in err msg for extack.

Signed-off-by: Xin Long <lucien.xin@gmail.com>
Signed-off-by: David S. Miller <davem@davemloft.net>

authored by

Xin Long and committed by
David S. Miller
e20d4ff2 fca3f91c

+134
+16
include/uapi/linux/tc_act/tc_tunnel_key.h
··· 54 54 * TCA_TUNNEL_KEY_ENC_OPTS_ 55 55 * attributes 56 56 */ 57 + TCA_TUNNEL_KEY_ENC_OPTS_ERSPAN, /* Nested 58 + * TCA_TUNNEL_KEY_ENC_OPTS_ 59 + * attributes 60 + */ 57 61 __TCA_TUNNEL_KEY_ENC_OPTS_MAX, 58 62 }; 59 63 ··· 83 79 84 80 #define TCA_TUNNEL_KEY_ENC_OPT_VXLAN_MAX \ 85 81 (__TCA_TUNNEL_KEY_ENC_OPT_VXLAN_MAX - 1) 82 + 83 + enum { 84 + TCA_TUNNEL_KEY_ENC_OPT_ERSPAN_UNSPEC, 85 + TCA_TUNNEL_KEY_ENC_OPT_ERSPAN_VER, /* u8 */ 86 + TCA_TUNNEL_KEY_ENC_OPT_ERSPAN_INDEX, /* be32 */ 87 + TCA_TUNNEL_KEY_ENC_OPT_ERSPAN_DIR, /* u8 */ 88 + TCA_TUNNEL_KEY_ENC_OPT_ERSPAN_HWID, /* u8 */ 89 + __TCA_TUNNEL_KEY_ENC_OPT_ERSPAN_MAX, 90 + }; 91 + 92 + #define TCA_TUNNEL_KEY_ENC_OPT_ERSPAN_MAX \ 93 + (__TCA_TUNNEL_KEY_ENC_OPT_ERSPAN_MAX - 1) 86 94 87 95 #endif
+118
net/sched/act_tunnel_key.c
··· 11 11 #include <linux/rtnetlink.h> 12 12 #include <net/geneve.h> 13 13 #include <net/vxlan.h> 14 + #include <net/erspan.h> 14 15 #include <net/netlink.h> 15 16 #include <net/pkt_sched.h> 16 17 #include <net/dst.h> ··· 59 58 .strict_start_type = TCA_TUNNEL_KEY_ENC_OPTS_VXLAN }, 60 59 [TCA_TUNNEL_KEY_ENC_OPTS_GENEVE] = { .type = NLA_NESTED }, 61 60 [TCA_TUNNEL_KEY_ENC_OPTS_VXLAN] = { .type = NLA_NESTED }, 61 + [TCA_TUNNEL_KEY_ENC_OPTS_ERSPAN] = { .type = NLA_NESTED }, 62 62 }; 63 63 64 64 static const struct nla_policy ··· 73 71 static const struct nla_policy 74 72 vxlan_opt_policy[TCA_TUNNEL_KEY_ENC_OPT_VXLAN_MAX + 1] = { 75 73 [TCA_TUNNEL_KEY_ENC_OPT_VXLAN_GBP] = { .type = NLA_U32 }, 74 + }; 75 + 76 + static const struct nla_policy 77 + erspan_opt_policy[TCA_TUNNEL_KEY_ENC_OPT_ERSPAN_MAX + 1] = { 78 + [TCA_TUNNEL_KEY_ENC_OPT_ERSPAN_VER] = { .type = NLA_U8 }, 79 + [TCA_TUNNEL_KEY_ENC_OPT_ERSPAN_INDEX] = { .type = NLA_U32 }, 80 + [TCA_TUNNEL_KEY_ENC_OPT_ERSPAN_DIR] = { .type = NLA_U8 }, 81 + [TCA_TUNNEL_KEY_ENC_OPT_ERSPAN_HWID] = { .type = NLA_U8 }, 76 82 }; 77 83 78 84 static int ··· 161 151 return sizeof(struct vxlan_metadata); 162 152 } 163 153 154 + static int 155 + tunnel_key_copy_erspan_opt(const struct nlattr *nla, void *dst, int dst_len, 156 + struct netlink_ext_ack *extack) 157 + { 158 + struct nlattr *tb[TCA_TUNNEL_KEY_ENC_OPT_ERSPAN_MAX + 1]; 159 + int err; 160 + u8 ver; 161 + 162 + err = nla_parse_nested(tb, TCA_TUNNEL_KEY_ENC_OPT_ERSPAN_MAX, nla, 163 + erspan_opt_policy, extack); 164 + if (err < 0) 165 + return err; 166 + 167 + if (!tb[TCA_TUNNEL_KEY_ENC_OPT_ERSPAN_VER]) { 168 + NL_SET_ERR_MSG(extack, "Missing tunnel key erspan option ver"); 169 + return -EINVAL; 170 + } 171 + 172 + ver = nla_get_u8(tb[TCA_TUNNEL_KEY_ENC_OPT_ERSPAN_VER]); 173 + if (ver == 1) { 174 + if (!tb[TCA_TUNNEL_KEY_ENC_OPT_ERSPAN_INDEX]) { 175 + NL_SET_ERR_MSG(extack, "Missing tunnel key erspan option index"); 176 + return -EINVAL; 177 + } 178 + } else if (ver == 2) { 179 + if (!tb[TCA_TUNNEL_KEY_ENC_OPT_ERSPAN_DIR] || 180 + !tb[TCA_TUNNEL_KEY_ENC_OPT_ERSPAN_HWID]) { 181 + NL_SET_ERR_MSG(extack, "Missing tunnel key erspan option dir or hwid"); 182 + return -EINVAL; 183 + } 184 + } else { 185 + NL_SET_ERR_MSG(extack, "Tunnel key erspan option ver is incorrect"); 186 + return -EINVAL; 187 + } 188 + 189 + if (dst) { 190 + struct erspan_metadata *md = dst; 191 + 192 + md->version = ver; 193 + if (ver == 1) { 194 + nla = tb[TCA_TUNNEL_KEY_ENC_OPT_ERSPAN_INDEX]; 195 + md->u.index = nla_get_be32(nla); 196 + } else { 197 + nla = tb[TCA_TUNNEL_KEY_ENC_OPT_ERSPAN_DIR]; 198 + md->u.md2.dir = nla_get_u8(nla); 199 + nla = tb[TCA_TUNNEL_KEY_ENC_OPT_ERSPAN_HWID]; 200 + set_hwid(&md->u.md2, nla_get_u8(nla)); 201 + } 202 + } 203 + 204 + return sizeof(struct erspan_metadata); 205 + } 206 + 164 207 static int tunnel_key_copy_opts(const struct nlattr *nla, u8 *dst, 165 208 int dst_len, struct netlink_ext_ack *extack) 166 209 { ··· 255 192 opts_len += opt_len; 256 193 type = TUNNEL_VXLAN_OPT; 257 194 break; 195 + case TCA_TUNNEL_KEY_ENC_OPTS_ERSPAN: 196 + if (type) { 197 + NL_SET_ERR_MSG(extack, "Duplicate type for erspan options"); 198 + return -EINVAL; 199 + } 200 + opt_len = tunnel_key_copy_erspan_opt(attr, dst, 201 + dst_len, extack); 202 + if (opt_len < 0) 203 + return opt_len; 204 + opts_len += opt_len; 205 + type = TUNNEL_ERSPAN_OPT; 206 + break; 258 207 } 259 208 } 260 209 ··· 305 230 case TCA_TUNNEL_KEY_ENC_OPTS_VXLAN: 306 231 #if IS_ENABLED(CONFIG_INET) 307 232 info->key.tun_flags |= TUNNEL_VXLAN_OPT; 233 + return tunnel_key_copy_opts(nla, ip_tunnel_info_opts(info), 234 + opts_len, extack); 235 + #else 236 + return -EAFNOSUPPORT; 237 + #endif 238 + case TCA_TUNNEL_KEY_ENC_OPTS_ERSPAN: 239 + #if IS_ENABLED(CONFIG_INET) 240 + info->key.tun_flags |= TUNNEL_ERSPAN_OPT; 308 241 return tunnel_key_copy_opts(nla, ip_tunnel_info_opts(info), 309 242 opts_len, extack); 310 243 #else ··· 613 530 return 0; 614 531 } 615 532 533 + static int tunnel_key_erspan_opts_dump(struct sk_buff *skb, 534 + const struct ip_tunnel_info *info) 535 + { 536 + struct erspan_metadata *md = (struct erspan_metadata *)(info + 1); 537 + struct nlattr *start; 538 + 539 + start = nla_nest_start_noflag(skb, TCA_TUNNEL_KEY_ENC_OPTS_ERSPAN); 540 + if (!start) 541 + return -EMSGSIZE; 542 + 543 + if (nla_put_u8(skb, TCA_TUNNEL_KEY_ENC_OPT_ERSPAN_VER, md->version)) 544 + goto err; 545 + 546 + if (md->version == 1 && 547 + nla_put_be32(skb, TCA_TUNNEL_KEY_ENC_OPT_ERSPAN_INDEX, md->u.index)) 548 + goto err; 549 + 550 + if (md->version == 2 && 551 + (nla_put_u8(skb, TCA_TUNNEL_KEY_ENC_OPT_ERSPAN_DIR, 552 + md->u.md2.dir) || 553 + nla_put_u8(skb, TCA_TUNNEL_KEY_ENC_OPT_ERSPAN_HWID, 554 + get_hwid(&md->u.md2)))) 555 + goto err; 556 + 557 + nla_nest_end(skb, start); 558 + return 0; 559 + err: 560 + nla_nest_cancel(skb, start); 561 + return -EMSGSIZE; 562 + } 563 + 616 564 static int tunnel_key_opts_dump(struct sk_buff *skb, 617 565 const struct ip_tunnel_info *info) 618 566 { ··· 663 549 goto err_out; 664 550 } else if (info->key.tun_flags & TUNNEL_VXLAN_OPT) { 665 551 err = tunnel_key_vxlan_opts_dump(skb, info); 552 + if (err) 553 + goto err_out; 554 + } else if (info->key.tun_flags & TUNNEL_ERSPAN_OPT) { 555 + err = tunnel_key_erspan_opts_dump(skb, info); 666 556 if (err) 667 557 goto err_out; 668 558 } else {