|
36 | 36 | #include <net/switchdev.h>
|
37 | 37 | #include <net/tc_act/tc_gact.h>
|
38 | 38 | #include <net/tc_act/tc_mirred.h>
|
| 39 | +#include <net/tc_act/tc_pedit.h> |
39 | 40 | #include <net/tc_act/tc_vlan.h>
|
40 | 41 | #include <net/tc_act/tc_tunnel_key.h>
|
41 | 42 |
|
@@ -223,6 +224,247 @@ nfp_fl_set_vxlan(struct nfp_fl_set_vxlan *set_vxlan,
|
223 | 224 | return 0;
|
224 | 225 | }
|
225 | 226 |
|
| 227 | +static void nfp_fl_set_helper32(u32 value, u32 mask, u8 *p_exact, u8 *p_mask) |
| 228 | +{ |
| 229 | + u32 oldvalue = get_unaligned((u32 *)p_exact); |
| 230 | + u32 oldmask = get_unaligned((u32 *)p_mask); |
| 231 | + |
| 232 | + value &= mask; |
| 233 | + value |= oldvalue & ~mask; |
| 234 | + |
| 235 | + put_unaligned(oldmask | mask, (u32 *)p_mask); |
| 236 | + put_unaligned(value, (u32 *)p_exact); |
| 237 | +} |
| 238 | + |
| 239 | +static int |
| 240 | +nfp_fl_set_eth(const struct tc_action *action, int idx, u32 off, |
| 241 | + struct nfp_fl_set_eth *set_eth) |
| 242 | +{ |
| 243 | + u16 tmp_set_eth_op; |
| 244 | + u32 exact, mask; |
| 245 | + |
| 246 | + if (off + 4 > ETH_ALEN * 2) |
| 247 | + return -EOPNOTSUPP; |
| 248 | + |
| 249 | + mask = ~tcf_pedit_mask(action, idx); |
| 250 | + exact = tcf_pedit_val(action, idx); |
| 251 | + |
| 252 | + if (exact & ~mask) |
| 253 | + return -EOPNOTSUPP; |
| 254 | + |
| 255 | + nfp_fl_set_helper32(exact, mask, &set_eth->eth_addr_val[off], |
| 256 | + &set_eth->eth_addr_mask[off]); |
| 257 | + |
| 258 | + set_eth->reserved = cpu_to_be16(0); |
| 259 | + tmp_set_eth_op = FIELD_PREP(NFP_FL_ACT_LEN_LW, |
| 260 | + sizeof(*set_eth) >> NFP_FL_LW_SIZ) | |
| 261 | + FIELD_PREP(NFP_FL_ACT_JMP_ID, |
| 262 | + NFP_FL_ACTION_OPCODE_SET_ETHERNET); |
| 263 | + set_eth->a_op = cpu_to_be16(tmp_set_eth_op); |
| 264 | + |
| 265 | + return 0; |
| 266 | +} |
| 267 | + |
| 268 | +static int |
| 269 | +nfp_fl_set_ip4(const struct tc_action *action, int idx, u32 off, |
| 270 | + struct nfp_fl_set_ip4_addrs *set_ip_addr) |
| 271 | +{ |
| 272 | + u16 tmp_set_ipv4_op; |
| 273 | + __be32 exact, mask; |
| 274 | + |
| 275 | + /* We are expecting tcf_pedit to return a big endian value */ |
| 276 | + mask = (__force __be32)~tcf_pedit_mask(action, idx); |
| 277 | + exact = (__force __be32)tcf_pedit_val(action, idx); |
| 278 | + |
| 279 | + if (exact & ~mask) |
| 280 | + return -EOPNOTSUPP; |
| 281 | + |
| 282 | + switch (off) { |
| 283 | + case offsetof(struct iphdr, daddr): |
| 284 | + set_ip_addr->ipv4_dst_mask = mask; |
| 285 | + set_ip_addr->ipv4_dst = exact; |
| 286 | + break; |
| 287 | + case offsetof(struct iphdr, saddr): |
| 288 | + set_ip_addr->ipv4_src_mask = mask; |
| 289 | + set_ip_addr->ipv4_src = exact; |
| 290 | + break; |
| 291 | + default: |
| 292 | + return -EOPNOTSUPP; |
| 293 | + } |
| 294 | + |
| 295 | + set_ip_addr->reserved = cpu_to_be16(0); |
| 296 | + tmp_set_ipv4_op = FIELD_PREP(NFP_FL_ACT_LEN_LW, |
| 297 | + sizeof(*set_ip_addr) >> NFP_FL_LW_SIZ) | |
| 298 | + FIELD_PREP(NFP_FL_ACT_JMP_ID, |
| 299 | + NFP_FL_ACTION_OPCODE_SET_IPV4_ADDRS); |
| 300 | + set_ip_addr->a_op = cpu_to_be16(tmp_set_ipv4_op); |
| 301 | + |
| 302 | + return 0; |
| 303 | +} |
| 304 | + |
| 305 | +static void |
| 306 | +nfp_fl_set_ip6_helper(int opcode_tag, int idx, __be32 exact, __be32 mask, |
| 307 | + struct nfp_fl_set_ipv6_addr *ip6) |
| 308 | +{ |
| 309 | + u16 tmp_set_op; |
| 310 | + |
| 311 | + ip6->ipv6[idx % 4].mask = mask; |
| 312 | + ip6->ipv6[idx % 4].exact = exact; |
| 313 | + |
| 314 | + ip6->reserved = cpu_to_be16(0); |
| 315 | + tmp_set_op = FIELD_PREP(NFP_FL_ACT_LEN_LW, sizeof(*ip6) >> |
| 316 | + NFP_FL_LW_SIZ) | |
| 317 | + FIELD_PREP(NFP_FL_ACT_JMP_ID, opcode_tag); |
| 318 | + ip6->a_op = cpu_to_be16(tmp_set_op); |
| 319 | +} |
| 320 | + |
| 321 | +static int |
| 322 | +nfp_fl_set_ip6(const struct tc_action *action, int idx, u32 off, |
| 323 | + struct nfp_fl_set_ipv6_addr *ip_dst, |
| 324 | + struct nfp_fl_set_ipv6_addr *ip_src) |
| 325 | +{ |
| 326 | + __be32 exact, mask; |
| 327 | + |
| 328 | + /* We are expecting tcf_pedit to return a big endian value */ |
| 329 | + mask = (__force __be32)~tcf_pedit_mask(action, idx); |
| 330 | + exact = (__force __be32)tcf_pedit_val(action, idx); |
| 331 | + |
| 332 | + if (exact & ~mask) |
| 333 | + return -EOPNOTSUPP; |
| 334 | + |
| 335 | + if (off < offsetof(struct ipv6hdr, saddr)) |
| 336 | + return -EOPNOTSUPP; |
| 337 | + else if (off < offsetof(struct ipv6hdr, daddr)) |
| 338 | + nfp_fl_set_ip6_helper(NFP_FL_ACTION_OPCODE_SET_IPV6_SRC, idx, |
| 339 | + exact, mask, ip_src); |
| 340 | + else if (off < offsetof(struct ipv6hdr, daddr) + |
| 341 | + sizeof(struct in6_addr)) |
| 342 | + nfp_fl_set_ip6_helper(NFP_FL_ACTION_OPCODE_SET_IPV6_DST, idx, |
| 343 | + exact, mask, ip_dst); |
| 344 | + else |
| 345 | + return -EOPNOTSUPP; |
| 346 | + |
| 347 | + return 0; |
| 348 | +} |
| 349 | + |
| 350 | +static int |
| 351 | +nfp_fl_set_tport(const struct tc_action *action, int idx, u32 off, |
| 352 | + struct nfp_fl_set_tport *set_tport, int opcode) |
| 353 | +{ |
| 354 | + u32 exact, mask; |
| 355 | + u16 tmp_set_op; |
| 356 | + |
| 357 | + if (off) |
| 358 | + return -EOPNOTSUPP; |
| 359 | + |
| 360 | + mask = ~tcf_pedit_mask(action, idx); |
| 361 | + exact = tcf_pedit_val(action, idx); |
| 362 | + |
| 363 | + if (exact & ~mask) |
| 364 | + return -EOPNOTSUPP; |
| 365 | + |
| 366 | + nfp_fl_set_helper32(exact, mask, set_tport->tp_port_val, |
| 367 | + set_tport->tp_port_mask); |
| 368 | + |
| 369 | + set_tport->reserved = cpu_to_be16(0); |
| 370 | + tmp_set_op = FIELD_PREP(NFP_FL_ACT_LEN_LW, |
| 371 | + sizeof(*set_tport) >> NFP_FL_LW_SIZ); |
| 372 | + tmp_set_op |= FIELD_PREP(NFP_FL_ACT_JMP_ID, opcode); |
| 373 | + set_tport->a_op = cpu_to_be16(tmp_set_op); |
| 374 | + |
| 375 | + return 0; |
| 376 | +} |
| 377 | + |
| 378 | +static int |
| 379 | +nfp_fl_pedit(const struct tc_action *action, char *nfp_action, int *a_len) |
| 380 | +{ |
| 381 | + struct nfp_fl_set_ipv6_addr set_ip6_dst, set_ip6_src; |
| 382 | + struct nfp_fl_set_ip4_addrs set_ip_addr; |
| 383 | + struct nfp_fl_set_tport set_tport; |
| 384 | + struct nfp_fl_set_eth set_eth; |
| 385 | + enum pedit_header_type htype; |
| 386 | + int idx, nkeys, err; |
| 387 | + size_t act_size; |
| 388 | + u32 offset, cmd; |
| 389 | + |
| 390 | + memset(&set_ip6_dst, 0, sizeof(set_ip6_dst)); |
| 391 | + memset(&set_ip6_src, 0, sizeof(set_ip6_src)); |
| 392 | + memset(&set_ip_addr, 0, sizeof(set_ip_addr)); |
| 393 | + memset(&set_tport, 0, sizeof(set_tport)); |
| 394 | + memset(&set_eth, 0, sizeof(set_eth)); |
| 395 | + nkeys = tcf_pedit_nkeys(action); |
| 396 | + |
| 397 | + for (idx = 0; idx < nkeys; idx++) { |
| 398 | + cmd = tcf_pedit_cmd(action, idx); |
| 399 | + htype = tcf_pedit_htype(action, idx); |
| 400 | + offset = tcf_pedit_offset(action, idx); |
| 401 | + |
| 402 | + if (cmd != TCA_PEDIT_KEY_EX_CMD_SET) |
| 403 | + return -EOPNOTSUPP; |
| 404 | + |
| 405 | + switch (htype) { |
| 406 | + case TCA_PEDIT_KEY_EX_HDR_TYPE_ETH: |
| 407 | + err = nfp_fl_set_eth(action, idx, offset, &set_eth); |
| 408 | + break; |
| 409 | + case TCA_PEDIT_KEY_EX_HDR_TYPE_IP4: |
| 410 | + err = nfp_fl_set_ip4(action, idx, offset, &set_ip_addr); |
| 411 | + break; |
| 412 | + case TCA_PEDIT_KEY_EX_HDR_TYPE_IP6: |
| 413 | + err = nfp_fl_set_ip6(action, idx, offset, &set_ip6_dst, |
| 414 | + &set_ip6_src); |
| 415 | + break; |
| 416 | + case TCA_PEDIT_KEY_EX_HDR_TYPE_TCP: |
| 417 | + err = nfp_fl_set_tport(action, idx, offset, &set_tport, |
| 418 | + NFP_FL_ACTION_OPCODE_SET_TCP); |
| 419 | + break; |
| 420 | + case TCA_PEDIT_KEY_EX_HDR_TYPE_UDP: |
| 421 | + err = nfp_fl_set_tport(action, idx, offset, &set_tport, |
| 422 | + NFP_FL_ACTION_OPCODE_SET_UDP); |
| 423 | + break; |
| 424 | + default: |
| 425 | + return -EOPNOTSUPP; |
| 426 | + } |
| 427 | + if (err) |
| 428 | + return err; |
| 429 | + } |
| 430 | + |
| 431 | + if (set_eth.a_op) { |
| 432 | + act_size = sizeof(set_eth); |
| 433 | + memcpy(nfp_action, &set_eth, act_size); |
| 434 | + *a_len += act_size; |
| 435 | + } else if (set_ip_addr.a_op) { |
| 436 | + act_size = sizeof(set_ip_addr); |
| 437 | + memcpy(nfp_action, &set_ip_addr, act_size); |
| 438 | + *a_len += act_size; |
| 439 | + } else if (set_ip6_dst.a_op && set_ip6_src.a_op) { |
| 440 | + /* TC compiles set src and dst IPv6 address as a single action, |
| 441 | + * the hardware requires this to be 2 separate actions. |
| 442 | + */ |
| 443 | + act_size = sizeof(set_ip6_src); |
| 444 | + memcpy(nfp_action, &set_ip6_src, act_size); |
| 445 | + *a_len += act_size; |
| 446 | + |
| 447 | + act_size = sizeof(set_ip6_dst); |
| 448 | + memcpy(&nfp_action[sizeof(set_ip6_src)], &set_ip6_dst, |
| 449 | + act_size); |
| 450 | + *a_len += act_size; |
| 451 | + } else if (set_ip6_dst.a_op) { |
| 452 | + act_size = sizeof(set_ip6_dst); |
| 453 | + memcpy(nfp_action, &set_ip6_dst, act_size); |
| 454 | + *a_len += act_size; |
| 455 | + } else if (set_ip6_src.a_op) { |
| 456 | + act_size = sizeof(set_ip6_src); |
| 457 | + memcpy(nfp_action, &set_ip6_src, act_size); |
| 458 | + *a_len += act_size; |
| 459 | + } else if (set_tport.a_op) { |
| 460 | + act_size = sizeof(set_tport); |
| 461 | + memcpy(nfp_action, &set_tport, act_size); |
| 462 | + *a_len += act_size; |
| 463 | + } |
| 464 | + |
| 465 | + return 0; |
| 466 | +} |
| 467 | + |
226 | 468 | static int
|
227 | 469 | nfp_flower_loop_action(const struct tc_action *a,
|
228 | 470 | struct nfp_fl_payload *nfp_fl, int *a_len,
|
@@ -301,6 +543,9 @@ nfp_flower_loop_action(const struct tc_action *a,
|
301 | 543 | } else if (is_tcf_tunnel_release(a)) {
|
302 | 544 | /* Tunnel decap is handled by default so accept action. */
|
303 | 545 | return 0;
|
| 546 | + } else if (is_tcf_pedit(a)) { |
| 547 | + if (nfp_fl_pedit(a, &nfp_fl->action_data[*a_len], a_len)) |
| 548 | + return -EOPNOTSUPP; |
304 | 549 | } else {
|
305 | 550 | /* Currently we do not handle any other actions. */
|
306 | 551 | return -EOPNOTSUPP;
|
|
0 commit comments