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

net: sched: add helper support in act_ct

This patch is to add helper support in act_ct for OVS actions=ct(alg=xxx)
offloading, which is corresponding to Commit cae3a2627520 ("openvswitch:
Allow attaching helpers to ct action") in OVS kernel part.

The difference is when adding TC actions family and proto cannot be got
from the filter/match, other than helper name in tb[TCA_CT_HELPER_NAME],
we also need to send the family in tb[TCA_CT_HELPER_FAMILY] and the
proto in tb[TCA_CT_HELPER_PROTO] to kernel.

Acked-by: Marcelo Ricardo Leitner <marcelo.leitner@gmail.com>
Signed-off-by: Xin Long <lucien.xin@gmail.com>
Signed-off-by: Paolo Abeni <pabeni@redhat.com>

authored by

Xin Long and committed by
Paolo Abeni
a21b06e7 19138941

+85 -8
+1
include/net/tc_act/tc_ct.h
··· 10 10 #include <net/netfilter/nf_conntrack_labels.h> 11 11 12 12 struct tcf_ct_params { 13 + struct nf_conntrack_helper *helper; 13 14 struct nf_conn *tmpl; 14 15 u16 zone; 15 16
+3
include/uapi/linux/tc_act/tc_ct.h
··· 22 22 TCA_CT_NAT_PORT_MIN, /* be16 */ 23 23 TCA_CT_NAT_PORT_MAX, /* be16 */ 24 24 TCA_CT_PAD, 25 + TCA_CT_HELPER_NAME, /* string */ 26 + TCA_CT_HELPER_FAMILY, /* u8 */ 27 + TCA_CT_HELPER_PROTO, /* u8 */ 25 28 __TCA_CT_MAX 26 29 }; 27 30
+81 -8
net/sched/act_ct.c
··· 33 33 #include <net/netfilter/nf_conntrack_acct.h> 34 34 #include <net/netfilter/ipv6/nf_defrag_ipv6.h> 35 35 #include <net/netfilter/nf_conntrack_act_ct.h> 36 + #include <net/netfilter/nf_conntrack_seqadj.h> 36 37 #include <uapi/linux/netfilter/nf_nat.h> 37 38 38 39 static struct workqueue_struct *act_ct_wq; ··· 656 655 657 656 /* Determine whether skb->_nfct is equal to the result of conntrack lookup. */ 658 657 static bool tcf_ct_skb_nfct_cached(struct net *net, struct sk_buff *skb, 659 - u16 zone_id, bool force) 658 + struct tcf_ct_params *p) 660 659 { 661 660 enum ip_conntrack_info ctinfo; 662 661 struct nf_conn *ct; ··· 666 665 return false; 667 666 if (!net_eq(net, read_pnet(&ct->ct_net))) 668 667 goto drop_ct; 669 - if (nf_ct_zone(ct)->id != zone_id) 668 + if (nf_ct_zone(ct)->id != p->zone) 670 669 goto drop_ct; 670 + if (p->helper) { 671 + struct nf_conn_help *help; 672 + 673 + help = nf_ct_ext_find(ct, NF_CT_EXT_HELPER); 674 + if (help && rcu_access_pointer(help->helper) != p->helper) 675 + goto drop_ct; 676 + } 671 677 672 678 /* Force conntrack entry direction. */ 673 - if (force && CTINFO2DIR(ctinfo) != IP_CT_DIR_ORIGINAL) { 679 + if ((p->ct_action & TCA_CT_ACT_FORCE) && 680 + CTINFO2DIR(ctinfo) != IP_CT_DIR_ORIGINAL) { 674 681 if (nf_ct_is_confirmed(ct)) 675 682 nf_ct_kill(ct); 676 683 ··· 841 832 842 833 static void tcf_ct_params_free(struct tcf_ct_params *params) 843 834 { 835 + if (params->helper) { 836 + #if IS_ENABLED(CONFIG_NF_NAT) 837 + if (params->ct_action & TCA_CT_ACT_NAT) 838 + nf_nat_helper_put(params->helper); 839 + #endif 840 + nf_conntrack_helper_put(params->helper); 841 + } 844 842 if (params->ct_ft) 845 843 tcf_ct_flow_table_put(params->ct_ft); 846 844 if (params->tmpl) ··· 1042 1026 struct tcf_result *res) 1043 1027 { 1044 1028 struct net *net = dev_net(skb->dev); 1045 - bool cached, commit, clear, force; 1046 1029 enum ip_conntrack_info ctinfo; 1047 1030 struct tcf_ct *c = to_ct(a); 1048 1031 struct nf_conn *tmpl = NULL; 1049 1032 struct nf_hook_state state; 1033 + bool cached, commit, clear; 1050 1034 int nh_ofs, err, retval; 1051 1035 struct tcf_ct_params *p; 1036 + bool add_helper = false; 1052 1037 bool skip_add = false; 1053 1038 bool defrag = false; 1054 1039 struct nf_conn *ct; ··· 1060 1043 retval = READ_ONCE(c->tcf_action); 1061 1044 commit = p->ct_action & TCA_CT_ACT_COMMIT; 1062 1045 clear = p->ct_action & TCA_CT_ACT_CLEAR; 1063 - force = p->ct_action & TCA_CT_ACT_FORCE; 1064 1046 tmpl = p->tmpl; 1065 1047 1066 1048 tcf_lastuse_update(&c->tcf_tm); ··· 1102 1086 * actually run the packet through conntrack twice unless it's for a 1103 1087 * different zone. 1104 1088 */ 1105 - cached = tcf_ct_skb_nfct_cached(net, skb, p->zone, force); 1089 + cached = tcf_ct_skb_nfct_cached(net, skb, p); 1106 1090 if (!cached) { 1107 1091 if (tcf_ct_flow_table_lookup(p, skb, family)) { 1108 1092 skip_add = true; ··· 1134 1118 err = tcf_ct_act_nat(skb, ct, ctinfo, p->ct_action, &p->range, commit); 1135 1119 if (err != NF_ACCEPT) 1136 1120 goto drop; 1121 + 1122 + if (!nf_ct_is_confirmed(ct) && commit && p->helper && !nfct_help(ct)) { 1123 + err = __nf_ct_try_assign_helper(ct, p->tmpl, GFP_ATOMIC); 1124 + if (err) 1125 + goto drop; 1126 + add_helper = true; 1127 + if (p->ct_action & TCA_CT_ACT_NAT && !nfct_seqadj(ct)) { 1128 + if (!nfct_seqadj_ext_add(ct)) 1129 + goto drop; 1130 + } 1131 + } 1132 + 1133 + if (nf_ct_is_confirmed(ct) ? ((!cached && !skip_add) || add_helper) : commit) { 1134 + if (nf_ct_helper(skb, ct, ctinfo, family) != NF_ACCEPT) 1135 + goto drop; 1136 + } 1137 1137 1138 1138 if (commit) { 1139 1139 tcf_ct_act_set_mark(ct, p->mark, p->mark_mask); ··· 1199 1167 [TCA_CT_NAT_IPV6_MAX] = NLA_POLICY_EXACT_LEN(sizeof(struct in6_addr)), 1200 1168 [TCA_CT_NAT_PORT_MIN] = { .type = NLA_U16 }, 1201 1169 [TCA_CT_NAT_PORT_MAX] = { .type = NLA_U16 }, 1170 + [TCA_CT_HELPER_NAME] = { .type = NLA_STRING, .len = NF_CT_HELPER_NAME_LEN }, 1171 + [TCA_CT_HELPER_FAMILY] = { .type = NLA_U8 }, 1172 + [TCA_CT_HELPER_PROTO] = { .type = NLA_U8 }, 1202 1173 }; 1203 1174 1204 1175 static int tcf_ct_fill_params_nat(struct tcf_ct_params *p, ··· 1291 1256 { 1292 1257 struct tc_ct_action_net *tn = net_generic(net, act_ct_ops.net_id); 1293 1258 struct nf_conntrack_zone zone; 1259 + int err, family, proto, len; 1294 1260 struct nf_conn *tmpl; 1295 - int err; 1261 + char *name; 1296 1262 1297 1263 p->zone = NF_CT_DEFAULT_ZONE_ID; 1298 1264 ··· 1354 1318 NL_SET_ERR_MSG_MOD(extack, "Failed to allocate conntrack template"); 1355 1319 return -ENOMEM; 1356 1320 } 1357 - __set_bit(IPS_CONFIRMED_BIT, &tmpl->status); 1358 1321 p->tmpl = tmpl; 1322 + if (tb[TCA_CT_HELPER_NAME]) { 1323 + name = nla_data(tb[TCA_CT_HELPER_NAME]); 1324 + len = nla_len(tb[TCA_CT_HELPER_NAME]); 1325 + if (len > 16 || name[len - 1] != '\0') { 1326 + NL_SET_ERR_MSG_MOD(extack, "Failed to parse helper name."); 1327 + err = -EINVAL; 1328 + goto err; 1329 + } 1330 + family = tb[TCA_CT_HELPER_FAMILY] ? nla_get_u8(tb[TCA_CT_HELPER_FAMILY]) : AF_INET; 1331 + proto = tb[TCA_CT_HELPER_PROTO] ? nla_get_u8(tb[TCA_CT_HELPER_PROTO]) : IPPROTO_TCP; 1332 + err = nf_ct_add_helper(tmpl, name, family, proto, 1333 + p->ct_action & TCA_CT_ACT_NAT, &p->helper); 1334 + if (err) { 1335 + NL_SET_ERR_MSG_MOD(extack, "Failed to add helper"); 1336 + goto err; 1337 + } 1338 + } 1359 1339 1340 + __set_bit(IPS_CONFIRMED_BIT, &tmpl->status); 1360 1341 return 0; 1342 + err: 1343 + nf_ct_put(p->tmpl); 1344 + p->tmpl = NULL; 1345 + return err; 1361 1346 } 1362 1347 1363 1348 static int tcf_ct_init(struct net *net, struct nlattr *nla, ··· 1547 1490 return 0; 1548 1491 } 1549 1492 1493 + static int tcf_ct_dump_helper(struct sk_buff *skb, struct nf_conntrack_helper *helper) 1494 + { 1495 + if (!helper) 1496 + return 0; 1497 + 1498 + if (nla_put_string(skb, TCA_CT_HELPER_NAME, helper->name) || 1499 + nla_put_u8(skb, TCA_CT_HELPER_FAMILY, helper->tuple.src.l3num) || 1500 + nla_put_u8(skb, TCA_CT_HELPER_PROTO, helper->tuple.dst.protonum)) 1501 + return -1; 1502 + 1503 + return 0; 1504 + } 1505 + 1550 1506 static inline int tcf_ct_dump(struct sk_buff *skb, struct tc_action *a, 1551 1507 int bind, int ref) 1552 1508 { ··· 1610 1540 goto nla_put_failure; 1611 1541 1612 1542 if (tcf_ct_dump_nat(skb, p)) 1543 + goto nla_put_failure; 1544 + 1545 + if (tcf_ct_dump_helper(skb, p->helper)) 1613 1546 goto nla_put_failure; 1614 1547 1615 1548 skip_dump: