3434#include <linux/bitfield.h>
3535#include <net/pkt_cls.h>
3636#include <net/switchdev.h>
37+ #include <net/tc_act/tc_csum.h>
3738#include <net/tc_act/tc_gact.h>
3839#include <net/tc_act/tc_mirred.h>
3940#include <net/tc_act/tc_pedit.h>
4445#include "main.h"
4546#include "../nfp_net_repr.h"
4647
48+ #define NFP_FL_SUPPORTED_IPV4_UDP_TUN_FLAGS (TUNNEL_CSUM | TUNNEL_KEY)
49+
4750static void nfp_fl_pop_vlan (struct nfp_fl_pop_vlan * pop_vlan )
4851{
4952 size_t act_size = sizeof (struct nfp_fl_pop_vlan );
@@ -235,9 +238,12 @@ nfp_fl_set_ipv4_udp_tun(struct nfp_fl_set_ipv4_udp_tun *set_tun,
235238 size_t act_size = sizeof (struct nfp_fl_set_ipv4_udp_tun );
236239 struct ip_tunnel_info * ip_tun = tcf_tunnel_info (action );
237240 u32 tmp_set_ip_tun_type_index = 0 ;
241+ struct flowi4 flow = {};
238242 /* Currently support one pre-tunnel so index is always 0. */
239243 int pretun_idx = 0 ;
244+ struct rtable * rt ;
240245 struct net * net ;
246+ int err ;
241247
242248 if (ip_tun -> options_len )
243249 return - EOPNOTSUPP ;
@@ -254,7 +260,28 @@ nfp_fl_set_ipv4_udp_tun(struct nfp_fl_set_ipv4_udp_tun *set_tun,
254260
255261 set_tun -> tun_type_index = cpu_to_be32 (tmp_set_ip_tun_type_index );
256262 set_tun -> tun_id = ip_tun -> key .tun_id ;
257- set_tun -> ttl = net -> ipv4 .sysctl_ip_default_ttl ;
263+
264+ /* Do a route lookup to determine ttl - if fails then use default.
265+ * Note that CONFIG_INET is a requirement of CONFIG_NET_SWITCHDEV so
266+ * must be defined here.
267+ */
268+ flow .daddr = ip_tun -> key .u .ipv4 .dst ;
269+ flow .flowi4_proto = IPPROTO_UDP ;
270+ rt = ip_route_output_key (net , & flow );
271+ err = PTR_ERR_OR_ZERO (rt );
272+ if (!err ) {
273+ set_tun -> ttl = ip4_dst_hoplimit (& rt -> dst );
274+ ip_rt_put (rt );
275+ } else {
276+ set_tun -> ttl = net -> ipv4 .sysctl_ip_default_ttl ;
277+ }
278+
279+ set_tun -> tos = ip_tun -> key .tos ;
280+
281+ if (!(ip_tun -> key .tun_flags & TUNNEL_KEY ) ||
282+ ip_tun -> key .tun_flags & ~NFP_FL_SUPPORTED_IPV4_UDP_TUN_FLAGS )
283+ return - EOPNOTSUPP ;
284+ set_tun -> tun_flags = ip_tun -> key .tun_flags ;
258285
259286 /* Complete pre_tunnel action. */
260287 pre_tun -> ipv4_dst = ip_tun -> key .u .ipv4 .dst ;
@@ -398,8 +425,27 @@ nfp_fl_set_tport(const struct tc_action *action, int idx, u32 off,
398425 return 0 ;
399426}
400427
428+ static u32 nfp_fl_csum_l4_to_flag (u8 ip_proto )
429+ {
430+ switch (ip_proto ) {
431+ case 0 :
432+ /* Filter doesn't force proto match,
433+ * both TCP and UDP will be updated if encountered
434+ */
435+ return TCA_CSUM_UPDATE_FLAG_TCP | TCA_CSUM_UPDATE_FLAG_UDP ;
436+ case IPPROTO_TCP :
437+ return TCA_CSUM_UPDATE_FLAG_TCP ;
438+ case IPPROTO_UDP :
439+ return TCA_CSUM_UPDATE_FLAG_UDP ;
440+ default :
441+ /* All other protocols will be ignored by FW */
442+ return 0 ;
443+ }
444+ }
445+
401446static int
402- nfp_fl_pedit (const struct tc_action * action , char * nfp_action , int * a_len )
447+ nfp_fl_pedit (const struct tc_action * action , struct tc_cls_flower_offload * flow ,
448+ char * nfp_action , int * a_len , u32 * csum_updated )
403449{
404450 struct nfp_fl_set_ipv6_addr set_ip6_dst , set_ip6_src ;
405451 struct nfp_fl_set_ip4_addrs set_ip_addr ;
@@ -409,6 +455,7 @@ nfp_fl_pedit(const struct tc_action *action, char *nfp_action, int *a_len)
409455 int idx , nkeys , err ;
410456 size_t act_size ;
411457 u32 offset , cmd ;
458+ u8 ip_proto = 0 ;
412459
413460 memset (& set_ip6_dst , 0 , sizeof (set_ip6_dst ));
414461 memset (& set_ip6_src , 0 , sizeof (set_ip6_src ));
@@ -451,6 +498,15 @@ nfp_fl_pedit(const struct tc_action *action, char *nfp_action, int *a_len)
451498 return err ;
452499 }
453500
501+ if (dissector_uses_key (flow -> dissector , FLOW_DISSECTOR_KEY_BASIC )) {
502+ struct flow_dissector_key_basic * basic ;
503+
504+ basic = skb_flow_dissector_target (flow -> dissector ,
505+ FLOW_DISSECTOR_KEY_BASIC ,
506+ flow -> key );
507+ ip_proto = basic -> ip_proto ;
508+ }
509+
454510 if (set_eth .head .len_lw ) {
455511 act_size = sizeof (set_eth );
456512 memcpy (nfp_action , & set_eth , act_size );
@@ -459,6 +515,10 @@ nfp_fl_pedit(const struct tc_action *action, char *nfp_action, int *a_len)
459515 act_size = sizeof (set_ip_addr );
460516 memcpy (nfp_action , & set_ip_addr , act_size );
461517 * a_len += act_size ;
518+
519+ /* Hardware will automatically fix IPv4 and TCP/UDP checksum. */
520+ * csum_updated |= TCA_CSUM_UPDATE_FLAG_IPV4HDR |
521+ nfp_fl_csum_l4_to_flag (ip_proto );
462522 } else if (set_ip6_dst .head .len_lw && set_ip6_src .head .len_lw ) {
463523 /* TC compiles set src and dst IPv6 address as a single action,
464524 * the hardware requires this to be 2 separate actions.
@@ -471,18 +531,30 @@ nfp_fl_pedit(const struct tc_action *action, char *nfp_action, int *a_len)
471531 memcpy (& nfp_action [sizeof (set_ip6_src )], & set_ip6_dst ,
472532 act_size );
473533 * a_len += act_size ;
534+
535+ /* Hardware will automatically fix TCP/UDP checksum. */
536+ * csum_updated |= nfp_fl_csum_l4_to_flag (ip_proto );
474537 } else if (set_ip6_dst .head .len_lw ) {
475538 act_size = sizeof (set_ip6_dst );
476539 memcpy (nfp_action , & set_ip6_dst , act_size );
477540 * a_len += act_size ;
541+
542+ /* Hardware will automatically fix TCP/UDP checksum. */
543+ * csum_updated |= nfp_fl_csum_l4_to_flag (ip_proto );
478544 } else if (set_ip6_src .head .len_lw ) {
479545 act_size = sizeof (set_ip6_src );
480546 memcpy (nfp_action , & set_ip6_src , act_size );
481547 * a_len += act_size ;
548+
549+ /* Hardware will automatically fix TCP/UDP checksum. */
550+ * csum_updated |= nfp_fl_csum_l4_to_flag (ip_proto );
482551 } else if (set_tport .head .len_lw ) {
483552 act_size = sizeof (set_tport );
484553 memcpy (nfp_action , & set_tport , act_size );
485554 * a_len += act_size ;
555+
556+ /* Hardware will automatically fix TCP/UDP checksum. */
557+ * csum_updated |= nfp_fl_csum_l4_to_flag (ip_proto );
486558 }
487559
488560 return 0 ;
@@ -493,12 +565,18 @@ nfp_flower_output_action(struct nfp_app *app, const struct tc_action *a,
493565 struct nfp_fl_payload * nfp_fl , int * a_len ,
494566 struct net_device * netdev , bool last ,
495567 enum nfp_flower_tun_type * tun_type , int * tun_out_cnt ,
496- int * out_cnt )
568+ int * out_cnt , u32 * csum_updated )
497569{
498570 struct nfp_flower_priv * priv = app -> priv ;
499571 struct nfp_fl_output * output ;
500572 int err , prelag_size ;
501573
574+ /* If csum_updated has not been reset by now, it means HW will
575+ * incorrectly update csums when they are not requested.
576+ */
577+ if (* csum_updated )
578+ return - EOPNOTSUPP ;
579+
502580 if (* a_len + sizeof (struct nfp_fl_output ) > NFP_FL_MAX_A_SIZ )
503581 return - EOPNOTSUPP ;
504582
@@ -529,10 +607,11 @@ nfp_flower_output_action(struct nfp_app *app, const struct tc_action *a,
529607
530608static int
531609nfp_flower_loop_action (struct nfp_app * app , const struct tc_action * a ,
610+ struct tc_cls_flower_offload * flow ,
532611 struct nfp_fl_payload * nfp_fl , int * a_len ,
533612 struct net_device * netdev ,
534613 enum nfp_flower_tun_type * tun_type , int * tun_out_cnt ,
535- int * out_cnt )
614+ int * out_cnt , u32 * csum_updated )
536615{
537616 struct nfp_fl_set_ipv4_udp_tun * set_tun ;
538617 struct nfp_fl_pre_tunnel * pre_tun ;
@@ -545,14 +624,14 @@ nfp_flower_loop_action(struct nfp_app *app, const struct tc_action *a,
545624 } else if (is_tcf_mirred_egress_redirect (a )) {
546625 err = nfp_flower_output_action (app , a , nfp_fl , a_len , netdev ,
547626 true, tun_type , tun_out_cnt ,
548- out_cnt );
627+ out_cnt , csum_updated );
549628 if (err )
550629 return err ;
551630
552631 } else if (is_tcf_mirred_egress_mirror (a )) {
553632 err = nfp_flower_output_action (app , a , nfp_fl , a_len , netdev ,
554633 false, tun_type , tun_out_cnt ,
555- out_cnt );
634+ out_cnt , csum_updated );
556635 if (err )
557636 return err ;
558637
@@ -602,8 +681,17 @@ nfp_flower_loop_action(struct nfp_app *app, const struct tc_action *a,
602681 /* Tunnel decap is handled by default so accept action. */
603682 return 0 ;
604683 } else if (is_tcf_pedit (a )) {
605- if (nfp_fl_pedit (a , & nfp_fl -> action_data [* a_len ], a_len ))
684+ if (nfp_fl_pedit (a , flow , & nfp_fl -> action_data [* a_len ],
685+ a_len , csum_updated ))
686+ return - EOPNOTSUPP ;
687+ } else if (is_tcf_csum (a )) {
688+ /* csum action requests recalc of something we have not fixed */
689+ if (tcf_csum_update_flags (a ) & ~* csum_updated )
606690 return - EOPNOTSUPP ;
691+ /* If we will correctly fix the csum we can remove it from the
692+ * csum update list. Which will later be used to check support.
693+ */
694+ * csum_updated &= ~tcf_csum_update_flags (a );
607695 } else {
608696 /* Currently we do not handle any other actions. */
609697 return - EOPNOTSUPP ;
@@ -620,6 +708,7 @@ int nfp_flower_compile_action(struct nfp_app *app,
620708 int act_len , act_cnt , err , tun_out_cnt , out_cnt ;
621709 enum nfp_flower_tun_type tun_type ;
622710 const struct tc_action * a ;
711+ u32 csum_updated = 0 ;
623712 LIST_HEAD (actions );
624713
625714 memset (nfp_flow -> action_data , 0 , NFP_FL_MAX_A_SIZ );
@@ -632,8 +721,9 @@ int nfp_flower_compile_action(struct nfp_app *app,
632721
633722 tcf_exts_to_list (flow -> exts , & actions );
634723 list_for_each_entry (a , & actions , list ) {
635- err = nfp_flower_loop_action (app , a , nfp_flow , & act_len , netdev ,
636- & tun_type , & tun_out_cnt , & out_cnt );
724+ err = nfp_flower_loop_action (app , a , flow , nfp_flow , & act_len ,
725+ netdev , & tun_type , & tun_out_cnt ,
726+ & out_cnt , & csum_updated );
637727 if (err )
638728 return err ;
639729 act_cnt ++ ;
0 commit comments