1b57dc7c1SPaul Blakey // SPDX-License-Identifier: GPL-2.0 OR Linux-OpenIB 2b57dc7c1SPaul Blakey /* - 3b57dc7c1SPaul Blakey * net/sched/act_ct.c Connection Tracking action 4b57dc7c1SPaul Blakey * 5b57dc7c1SPaul Blakey * Authors: Paul Blakey <paulb@mellanox.com> 6b57dc7c1SPaul Blakey * Yossi Kuperman <yossiku@mellanox.com> 7b57dc7c1SPaul Blakey * Marcelo Ricardo Leitner <marcelo.leitner@gmail.com> 8b57dc7c1SPaul Blakey */ 9b57dc7c1SPaul Blakey 10b57dc7c1SPaul Blakey #include <linux/module.h> 11b57dc7c1SPaul Blakey #include <linux/init.h> 12b57dc7c1SPaul Blakey #include <linux/kernel.h> 13b57dc7c1SPaul Blakey #include <linux/skbuff.h> 14b57dc7c1SPaul Blakey #include <linux/rtnetlink.h> 15b57dc7c1SPaul Blakey #include <linux/pkt_cls.h> 16b57dc7c1SPaul Blakey #include <linux/ip.h> 17b57dc7c1SPaul Blakey #include <linux/ipv6.h> 18c34b961aSPaul Blakey #include <linux/rhashtable.h> 19b57dc7c1SPaul Blakey #include <net/netlink.h> 20b57dc7c1SPaul Blakey #include <net/pkt_sched.h> 21b57dc7c1SPaul Blakey #include <net/pkt_cls.h> 22b57dc7c1SPaul Blakey #include <net/act_api.h> 23b57dc7c1SPaul Blakey #include <net/ip.h> 24b57dc7c1SPaul Blakey #include <net/ipv6_frag.h> 25b57dc7c1SPaul Blakey #include <uapi/linux/tc_act/tc_ct.h> 26b57dc7c1SPaul Blakey #include <net/tc_act/tc_ct.h> 27b57dc7c1SPaul Blakey 28c34b961aSPaul Blakey #include <net/netfilter/nf_flow_table.h> 29b57dc7c1SPaul Blakey #include <net/netfilter/nf_conntrack.h> 30b57dc7c1SPaul Blakey #include <net/netfilter/nf_conntrack_core.h> 31b57dc7c1SPaul Blakey #include <net/netfilter/nf_conntrack_zones.h> 32b57dc7c1SPaul Blakey #include <net/netfilter/nf_conntrack_helper.h> 33b57dc7c1SPaul Blakey #include <net/netfilter/ipv6/nf_defrag_ipv6.h> 3440d102cdSJeremy Sowden #include <uapi/linux/netfilter/nf_nat.h> 35b57dc7c1SPaul Blakey 36c34b961aSPaul Blakey static struct workqueue_struct *act_ct_wq; 37c34b961aSPaul Blakey static struct rhashtable zones_ht; 38c34b961aSPaul Blakey static DEFINE_SPINLOCK(zones_lock); 39c34b961aSPaul Blakey 40c34b961aSPaul Blakey struct tcf_ct_flow_table { 41c34b961aSPaul Blakey struct rhash_head node; /* In zones tables */ 42c34b961aSPaul Blakey 43c34b961aSPaul Blakey struct rcu_work rwork; 44c34b961aSPaul Blakey struct nf_flowtable nf_ft; 45c34b961aSPaul Blakey u16 zone; 46c34b961aSPaul Blakey u32 ref; 47c34b961aSPaul Blakey 48c34b961aSPaul Blakey bool dying; 49c34b961aSPaul Blakey }; 50c34b961aSPaul Blakey 51c34b961aSPaul Blakey static const struct rhashtable_params zones_params = { 52c34b961aSPaul Blakey .head_offset = offsetof(struct tcf_ct_flow_table, node), 53c34b961aSPaul Blakey .key_offset = offsetof(struct tcf_ct_flow_table, zone), 54c34b961aSPaul Blakey .key_len = sizeof_field(struct tcf_ct_flow_table, zone), 55c34b961aSPaul Blakey .automatic_shrinking = true, 56c34b961aSPaul Blakey }; 57c34b961aSPaul Blakey 58c34b961aSPaul Blakey static struct nf_flowtable_type flowtable_ct = { 59c34b961aSPaul Blakey .owner = THIS_MODULE, 60c34b961aSPaul Blakey }; 61c34b961aSPaul Blakey 62c34b961aSPaul Blakey static int tcf_ct_flow_table_get(struct tcf_ct_params *params) 63c34b961aSPaul Blakey { 64c34b961aSPaul Blakey struct tcf_ct_flow_table *ct_ft; 65c34b961aSPaul Blakey int err = -ENOMEM; 66c34b961aSPaul Blakey 67c34b961aSPaul Blakey spin_lock_bh(&zones_lock); 68c34b961aSPaul Blakey ct_ft = rhashtable_lookup_fast(&zones_ht, ¶ms->zone, zones_params); 69c34b961aSPaul Blakey if (ct_ft) 70c34b961aSPaul Blakey goto take_ref; 71c34b961aSPaul Blakey 72c34b961aSPaul Blakey ct_ft = kzalloc(sizeof(*ct_ft), GFP_ATOMIC); 73c34b961aSPaul Blakey if (!ct_ft) 74c34b961aSPaul Blakey goto err_alloc; 75c34b961aSPaul Blakey 76c34b961aSPaul Blakey ct_ft->zone = params->zone; 77c34b961aSPaul Blakey err = rhashtable_insert_fast(&zones_ht, &ct_ft->node, zones_params); 78c34b961aSPaul Blakey if (err) 79c34b961aSPaul Blakey goto err_insert; 80c34b961aSPaul Blakey 81c34b961aSPaul Blakey ct_ft->nf_ft.type = &flowtable_ct; 82c34b961aSPaul Blakey err = nf_flow_table_init(&ct_ft->nf_ft); 83c34b961aSPaul Blakey if (err) 84c34b961aSPaul Blakey goto err_init; 85c34b961aSPaul Blakey 86c34b961aSPaul Blakey __module_get(THIS_MODULE); 87c34b961aSPaul Blakey take_ref: 88c34b961aSPaul Blakey params->ct_ft = ct_ft; 89c34b961aSPaul Blakey ct_ft->ref++; 90c34b961aSPaul Blakey spin_unlock_bh(&zones_lock); 91c34b961aSPaul Blakey 92c34b961aSPaul Blakey return 0; 93c34b961aSPaul Blakey 94c34b961aSPaul Blakey err_init: 95c34b961aSPaul Blakey rhashtable_remove_fast(&zones_ht, &ct_ft->node, zones_params); 96c34b961aSPaul Blakey err_insert: 97c34b961aSPaul Blakey kfree(ct_ft); 98c34b961aSPaul Blakey err_alloc: 99c34b961aSPaul Blakey spin_unlock_bh(&zones_lock); 100c34b961aSPaul Blakey return err; 101c34b961aSPaul Blakey } 102c34b961aSPaul Blakey 103c34b961aSPaul Blakey static void tcf_ct_flow_table_cleanup_work(struct work_struct *work) 104c34b961aSPaul Blakey { 105c34b961aSPaul Blakey struct tcf_ct_flow_table *ct_ft; 106c34b961aSPaul Blakey 107c34b961aSPaul Blakey ct_ft = container_of(to_rcu_work(work), struct tcf_ct_flow_table, 108c34b961aSPaul Blakey rwork); 109c34b961aSPaul Blakey nf_flow_table_free(&ct_ft->nf_ft); 110c34b961aSPaul Blakey kfree(ct_ft); 111c34b961aSPaul Blakey 112c34b961aSPaul Blakey module_put(THIS_MODULE); 113c34b961aSPaul Blakey } 114c34b961aSPaul Blakey 115c34b961aSPaul Blakey static void tcf_ct_flow_table_put(struct tcf_ct_params *params) 116c34b961aSPaul Blakey { 117c34b961aSPaul Blakey struct tcf_ct_flow_table *ct_ft = params->ct_ft; 118c34b961aSPaul Blakey 119c34b961aSPaul Blakey spin_lock_bh(&zones_lock); 120c34b961aSPaul Blakey if (--params->ct_ft->ref == 0) { 121c34b961aSPaul Blakey rhashtable_remove_fast(&zones_ht, &ct_ft->node, zones_params); 122c34b961aSPaul Blakey INIT_RCU_WORK(&ct_ft->rwork, tcf_ct_flow_table_cleanup_work); 123c34b961aSPaul Blakey queue_rcu_work(act_ct_wq, &ct_ft->rwork); 124c34b961aSPaul Blakey } 125c34b961aSPaul Blakey spin_unlock_bh(&zones_lock); 126c34b961aSPaul Blakey } 127c34b961aSPaul Blakey 128*64ff70b8SPaul Blakey static void tcf_ct_flow_table_add(struct tcf_ct_flow_table *ct_ft, 129*64ff70b8SPaul Blakey struct nf_conn *ct, 130*64ff70b8SPaul Blakey bool tcp) 131*64ff70b8SPaul Blakey { 132*64ff70b8SPaul Blakey struct flow_offload *entry; 133*64ff70b8SPaul Blakey int err; 134*64ff70b8SPaul Blakey 135*64ff70b8SPaul Blakey if (test_and_set_bit(IPS_OFFLOAD_BIT, &ct->status)) 136*64ff70b8SPaul Blakey return; 137*64ff70b8SPaul Blakey 138*64ff70b8SPaul Blakey entry = flow_offload_alloc(ct); 139*64ff70b8SPaul Blakey if (!entry) { 140*64ff70b8SPaul Blakey WARN_ON_ONCE(1); 141*64ff70b8SPaul Blakey goto err_alloc; 142*64ff70b8SPaul Blakey } 143*64ff70b8SPaul Blakey 144*64ff70b8SPaul Blakey if (tcp) { 145*64ff70b8SPaul Blakey ct->proto.tcp.seen[0].flags |= IP_CT_TCP_FLAG_BE_LIBERAL; 146*64ff70b8SPaul Blakey ct->proto.tcp.seen[1].flags |= IP_CT_TCP_FLAG_BE_LIBERAL; 147*64ff70b8SPaul Blakey } 148*64ff70b8SPaul Blakey 149*64ff70b8SPaul Blakey err = flow_offload_add(&ct_ft->nf_ft, entry); 150*64ff70b8SPaul Blakey if (err) 151*64ff70b8SPaul Blakey goto err_add; 152*64ff70b8SPaul Blakey 153*64ff70b8SPaul Blakey return; 154*64ff70b8SPaul Blakey 155*64ff70b8SPaul Blakey err_add: 156*64ff70b8SPaul Blakey flow_offload_free(entry); 157*64ff70b8SPaul Blakey err_alloc: 158*64ff70b8SPaul Blakey clear_bit(IPS_OFFLOAD_BIT, &ct->status); 159*64ff70b8SPaul Blakey } 160*64ff70b8SPaul Blakey 161*64ff70b8SPaul Blakey static void tcf_ct_flow_table_process_conn(struct tcf_ct_flow_table *ct_ft, 162*64ff70b8SPaul Blakey struct nf_conn *ct, 163*64ff70b8SPaul Blakey enum ip_conntrack_info ctinfo) 164*64ff70b8SPaul Blakey { 165*64ff70b8SPaul Blakey bool tcp = false; 166*64ff70b8SPaul Blakey 167*64ff70b8SPaul Blakey if (ctinfo != IP_CT_ESTABLISHED && ctinfo != IP_CT_ESTABLISHED_REPLY) 168*64ff70b8SPaul Blakey return; 169*64ff70b8SPaul Blakey 170*64ff70b8SPaul Blakey switch (nf_ct_protonum(ct)) { 171*64ff70b8SPaul Blakey case IPPROTO_TCP: 172*64ff70b8SPaul Blakey tcp = true; 173*64ff70b8SPaul Blakey if (ct->proto.tcp.state != TCP_CONNTRACK_ESTABLISHED) 174*64ff70b8SPaul Blakey return; 175*64ff70b8SPaul Blakey break; 176*64ff70b8SPaul Blakey case IPPROTO_UDP: 177*64ff70b8SPaul Blakey break; 178*64ff70b8SPaul Blakey default: 179*64ff70b8SPaul Blakey return; 180*64ff70b8SPaul Blakey } 181*64ff70b8SPaul Blakey 182*64ff70b8SPaul Blakey if (nf_ct_ext_exist(ct, NF_CT_EXT_HELPER) || 183*64ff70b8SPaul Blakey ct->status & IPS_SEQ_ADJUST) 184*64ff70b8SPaul Blakey return; 185*64ff70b8SPaul Blakey 186*64ff70b8SPaul Blakey tcf_ct_flow_table_add(ct_ft, ct, tcp); 187*64ff70b8SPaul Blakey } 188*64ff70b8SPaul Blakey 189c34b961aSPaul Blakey static int tcf_ct_flow_tables_init(void) 190c34b961aSPaul Blakey { 191c34b961aSPaul Blakey return rhashtable_init(&zones_ht, &zones_params); 192c34b961aSPaul Blakey } 193c34b961aSPaul Blakey 194c34b961aSPaul Blakey static void tcf_ct_flow_tables_uninit(void) 195c34b961aSPaul Blakey { 196c34b961aSPaul Blakey rhashtable_destroy(&zones_ht); 197c34b961aSPaul Blakey } 198c34b961aSPaul Blakey 199b57dc7c1SPaul Blakey static struct tc_action_ops act_ct_ops; 200b57dc7c1SPaul Blakey static unsigned int ct_net_id; 201b57dc7c1SPaul Blakey 202b57dc7c1SPaul Blakey struct tc_ct_action_net { 203b57dc7c1SPaul Blakey struct tc_action_net tn; /* Must be first */ 204b57dc7c1SPaul Blakey bool labels; 205b57dc7c1SPaul Blakey }; 206b57dc7c1SPaul Blakey 207b57dc7c1SPaul Blakey /* Determine whether skb->_nfct is equal to the result of conntrack lookup. */ 208b57dc7c1SPaul Blakey static bool tcf_ct_skb_nfct_cached(struct net *net, struct sk_buff *skb, 209b57dc7c1SPaul Blakey u16 zone_id, bool force) 210b57dc7c1SPaul Blakey { 211b57dc7c1SPaul Blakey enum ip_conntrack_info ctinfo; 212b57dc7c1SPaul Blakey struct nf_conn *ct; 213b57dc7c1SPaul Blakey 214b57dc7c1SPaul Blakey ct = nf_ct_get(skb, &ctinfo); 215b57dc7c1SPaul Blakey if (!ct) 216b57dc7c1SPaul Blakey return false; 217b57dc7c1SPaul Blakey if (!net_eq(net, read_pnet(&ct->ct_net))) 218b57dc7c1SPaul Blakey return false; 219b57dc7c1SPaul Blakey if (nf_ct_zone(ct)->id != zone_id) 220b57dc7c1SPaul Blakey return false; 221b57dc7c1SPaul Blakey 222b57dc7c1SPaul Blakey /* Force conntrack entry direction. */ 223b57dc7c1SPaul Blakey if (force && CTINFO2DIR(ctinfo) != IP_CT_DIR_ORIGINAL) { 224b57dc7c1SPaul Blakey if (nf_ct_is_confirmed(ct)) 225b57dc7c1SPaul Blakey nf_ct_kill(ct); 226b57dc7c1SPaul Blakey 227b57dc7c1SPaul Blakey nf_conntrack_put(&ct->ct_general); 228b57dc7c1SPaul Blakey nf_ct_set(skb, NULL, IP_CT_UNTRACKED); 229b57dc7c1SPaul Blakey 230b57dc7c1SPaul Blakey return false; 231b57dc7c1SPaul Blakey } 232b57dc7c1SPaul Blakey 233b57dc7c1SPaul Blakey return true; 234b57dc7c1SPaul Blakey } 235b57dc7c1SPaul Blakey 236b57dc7c1SPaul Blakey /* Trim the skb to the length specified by the IP/IPv6 header, 237b57dc7c1SPaul Blakey * removing any trailing lower-layer padding. This prepares the skb 238b57dc7c1SPaul Blakey * for higher-layer processing that assumes skb->len excludes padding 239b57dc7c1SPaul Blakey * (such as nf_ip_checksum). The caller needs to pull the skb to the 240b57dc7c1SPaul Blakey * network header, and ensure ip_hdr/ipv6_hdr points to valid data. 241b57dc7c1SPaul Blakey */ 242b57dc7c1SPaul Blakey static int tcf_ct_skb_network_trim(struct sk_buff *skb, int family) 243b57dc7c1SPaul Blakey { 244b57dc7c1SPaul Blakey unsigned int len; 245b57dc7c1SPaul Blakey int err; 246b57dc7c1SPaul Blakey 247b57dc7c1SPaul Blakey switch (family) { 248b57dc7c1SPaul Blakey case NFPROTO_IPV4: 249b57dc7c1SPaul Blakey len = ntohs(ip_hdr(skb)->tot_len); 250b57dc7c1SPaul Blakey break; 251b57dc7c1SPaul Blakey case NFPROTO_IPV6: 252b57dc7c1SPaul Blakey len = sizeof(struct ipv6hdr) 253b57dc7c1SPaul Blakey + ntohs(ipv6_hdr(skb)->payload_len); 254b57dc7c1SPaul Blakey break; 255b57dc7c1SPaul Blakey default: 256b57dc7c1SPaul Blakey len = skb->len; 257b57dc7c1SPaul Blakey } 258b57dc7c1SPaul Blakey 259b57dc7c1SPaul Blakey err = pskb_trim_rcsum(skb, len); 260b57dc7c1SPaul Blakey 261b57dc7c1SPaul Blakey return err; 262b57dc7c1SPaul Blakey } 263b57dc7c1SPaul Blakey 264b57dc7c1SPaul Blakey static u8 tcf_ct_skb_nf_family(struct sk_buff *skb) 265b57dc7c1SPaul Blakey { 266b57dc7c1SPaul Blakey u8 family = NFPROTO_UNSPEC; 267b57dc7c1SPaul Blakey 268b57dc7c1SPaul Blakey switch (skb->protocol) { 269b57dc7c1SPaul Blakey case htons(ETH_P_IP): 270b57dc7c1SPaul Blakey family = NFPROTO_IPV4; 271b57dc7c1SPaul Blakey break; 272b57dc7c1SPaul Blakey case htons(ETH_P_IPV6): 273b57dc7c1SPaul Blakey family = NFPROTO_IPV6; 274b57dc7c1SPaul Blakey break; 275b57dc7c1SPaul Blakey default: 276b57dc7c1SPaul Blakey break; 277b57dc7c1SPaul Blakey } 278b57dc7c1SPaul Blakey 279b57dc7c1SPaul Blakey return family; 280b57dc7c1SPaul Blakey } 281b57dc7c1SPaul Blakey 282b57dc7c1SPaul Blakey static int tcf_ct_ipv4_is_fragment(struct sk_buff *skb, bool *frag) 283b57dc7c1SPaul Blakey { 284b57dc7c1SPaul Blakey unsigned int len; 285b57dc7c1SPaul Blakey 286b57dc7c1SPaul Blakey len = skb_network_offset(skb) + sizeof(struct iphdr); 287b57dc7c1SPaul Blakey if (unlikely(skb->len < len)) 288b57dc7c1SPaul Blakey return -EINVAL; 289b57dc7c1SPaul Blakey if (unlikely(!pskb_may_pull(skb, len))) 290b57dc7c1SPaul Blakey return -ENOMEM; 291b57dc7c1SPaul Blakey 292b57dc7c1SPaul Blakey *frag = ip_is_fragment(ip_hdr(skb)); 293b57dc7c1SPaul Blakey return 0; 294b57dc7c1SPaul Blakey } 295b57dc7c1SPaul Blakey 296b57dc7c1SPaul Blakey static int tcf_ct_ipv6_is_fragment(struct sk_buff *skb, bool *frag) 297b57dc7c1SPaul Blakey { 298b57dc7c1SPaul Blakey unsigned int flags = 0, len, payload_ofs = 0; 299b57dc7c1SPaul Blakey unsigned short frag_off; 300b57dc7c1SPaul Blakey int nexthdr; 301b57dc7c1SPaul Blakey 302b57dc7c1SPaul Blakey len = skb_network_offset(skb) + sizeof(struct ipv6hdr); 303b57dc7c1SPaul Blakey if (unlikely(skb->len < len)) 304b57dc7c1SPaul Blakey return -EINVAL; 305b57dc7c1SPaul Blakey if (unlikely(!pskb_may_pull(skb, len))) 306b57dc7c1SPaul Blakey return -ENOMEM; 307b57dc7c1SPaul Blakey 308b57dc7c1SPaul Blakey nexthdr = ipv6_find_hdr(skb, &payload_ofs, -1, &frag_off, &flags); 309b57dc7c1SPaul Blakey if (unlikely(nexthdr < 0)) 310b57dc7c1SPaul Blakey return -EPROTO; 311b57dc7c1SPaul Blakey 312b57dc7c1SPaul Blakey *frag = flags & IP6_FH_F_FRAG; 313b57dc7c1SPaul Blakey return 0; 314b57dc7c1SPaul Blakey } 315b57dc7c1SPaul Blakey 316b57dc7c1SPaul Blakey static int tcf_ct_handle_fragments(struct net *net, struct sk_buff *skb, 317b57dc7c1SPaul Blakey u8 family, u16 zone) 318b57dc7c1SPaul Blakey { 319b57dc7c1SPaul Blakey enum ip_conntrack_info ctinfo; 320b57dc7c1SPaul Blakey struct nf_conn *ct; 321b57dc7c1SPaul Blakey int err = 0; 322b57dc7c1SPaul Blakey bool frag; 323b57dc7c1SPaul Blakey 324b57dc7c1SPaul Blakey /* Previously seen (loopback)? Ignore. */ 325b57dc7c1SPaul Blakey ct = nf_ct_get(skb, &ctinfo); 326b57dc7c1SPaul Blakey if ((ct && !nf_ct_is_template(ct)) || ctinfo == IP_CT_UNTRACKED) 327b57dc7c1SPaul Blakey return 0; 328b57dc7c1SPaul Blakey 329b57dc7c1SPaul Blakey if (family == NFPROTO_IPV4) 330b57dc7c1SPaul Blakey err = tcf_ct_ipv4_is_fragment(skb, &frag); 331b57dc7c1SPaul Blakey else 332b57dc7c1SPaul Blakey err = tcf_ct_ipv6_is_fragment(skb, &frag); 333b57dc7c1SPaul Blakey if (err || !frag) 334b57dc7c1SPaul Blakey return err; 335b57dc7c1SPaul Blakey 336b57dc7c1SPaul Blakey skb_get(skb); 337b57dc7c1SPaul Blakey 338b57dc7c1SPaul Blakey if (family == NFPROTO_IPV4) { 339b57dc7c1SPaul Blakey enum ip_defrag_users user = IP_DEFRAG_CONNTRACK_IN + zone; 340b57dc7c1SPaul Blakey 341b57dc7c1SPaul Blakey memset(IPCB(skb), 0, sizeof(struct inet_skb_parm)); 342b57dc7c1SPaul Blakey local_bh_disable(); 343b57dc7c1SPaul Blakey err = ip_defrag(net, skb, user); 344b57dc7c1SPaul Blakey local_bh_enable(); 345b57dc7c1SPaul Blakey if (err && err != -EINPROGRESS) 346b57dc7c1SPaul Blakey goto out_free; 347b57dc7c1SPaul Blakey } else { /* NFPROTO_IPV6 */ 348b57dc7c1SPaul Blakey #if IS_ENABLED(CONFIG_NF_DEFRAG_IPV6) 349b57dc7c1SPaul Blakey enum ip6_defrag_users user = IP6_DEFRAG_CONNTRACK_IN + zone; 350b57dc7c1SPaul Blakey 351b57dc7c1SPaul Blakey memset(IP6CB(skb), 0, sizeof(struct inet6_skb_parm)); 352b57dc7c1SPaul Blakey err = nf_ct_frag6_gather(net, skb, user); 353b57dc7c1SPaul Blakey if (err && err != -EINPROGRESS) 354b57dc7c1SPaul Blakey goto out_free; 355b57dc7c1SPaul Blakey #else 356b57dc7c1SPaul Blakey err = -EOPNOTSUPP; 357b57dc7c1SPaul Blakey goto out_free; 358b57dc7c1SPaul Blakey #endif 359b57dc7c1SPaul Blakey } 360b57dc7c1SPaul Blakey 361b57dc7c1SPaul Blakey skb_clear_hash(skb); 362b57dc7c1SPaul Blakey skb->ignore_df = 1; 363b57dc7c1SPaul Blakey return err; 364b57dc7c1SPaul Blakey 365b57dc7c1SPaul Blakey out_free: 366b57dc7c1SPaul Blakey kfree_skb(skb); 367b57dc7c1SPaul Blakey return err; 368b57dc7c1SPaul Blakey } 369b57dc7c1SPaul Blakey 370b57dc7c1SPaul Blakey static void tcf_ct_params_free(struct rcu_head *head) 371b57dc7c1SPaul Blakey { 372b57dc7c1SPaul Blakey struct tcf_ct_params *params = container_of(head, 373b57dc7c1SPaul Blakey struct tcf_ct_params, rcu); 374b57dc7c1SPaul Blakey 375c34b961aSPaul Blakey tcf_ct_flow_table_put(params); 376c34b961aSPaul Blakey 377b57dc7c1SPaul Blakey if (params->tmpl) 378b57dc7c1SPaul Blakey nf_conntrack_put(¶ms->tmpl->ct_general); 379b57dc7c1SPaul Blakey kfree(params); 380b57dc7c1SPaul Blakey } 381b57dc7c1SPaul Blakey 382b57dc7c1SPaul Blakey #if IS_ENABLED(CONFIG_NF_NAT) 383b57dc7c1SPaul Blakey /* Modelled after nf_nat_ipv[46]_fn(). 384b57dc7c1SPaul Blakey * range is only used for new, uninitialized NAT state. 385b57dc7c1SPaul Blakey * Returns either NF_ACCEPT or NF_DROP. 386b57dc7c1SPaul Blakey */ 387b57dc7c1SPaul Blakey static int ct_nat_execute(struct sk_buff *skb, struct nf_conn *ct, 388b57dc7c1SPaul Blakey enum ip_conntrack_info ctinfo, 389b57dc7c1SPaul Blakey const struct nf_nat_range2 *range, 390b57dc7c1SPaul Blakey enum nf_nat_manip_type maniptype) 391b57dc7c1SPaul Blakey { 392b57dc7c1SPaul Blakey int hooknum, err = NF_ACCEPT; 393b57dc7c1SPaul Blakey 394b57dc7c1SPaul Blakey /* See HOOK2MANIP(). */ 395b57dc7c1SPaul Blakey if (maniptype == NF_NAT_MANIP_SRC) 396b57dc7c1SPaul Blakey hooknum = NF_INET_LOCAL_IN; /* Source NAT */ 397b57dc7c1SPaul Blakey else 398b57dc7c1SPaul Blakey hooknum = NF_INET_LOCAL_OUT; /* Destination NAT */ 399b57dc7c1SPaul Blakey 400b57dc7c1SPaul Blakey switch (ctinfo) { 401b57dc7c1SPaul Blakey case IP_CT_RELATED: 402b57dc7c1SPaul Blakey case IP_CT_RELATED_REPLY: 403b57dc7c1SPaul Blakey if (skb->protocol == htons(ETH_P_IP) && 404b57dc7c1SPaul Blakey ip_hdr(skb)->protocol == IPPROTO_ICMP) { 405b57dc7c1SPaul Blakey if (!nf_nat_icmp_reply_translation(skb, ct, ctinfo, 406b57dc7c1SPaul Blakey hooknum)) 407b57dc7c1SPaul Blakey err = NF_DROP; 408b57dc7c1SPaul Blakey goto out; 409b57dc7c1SPaul Blakey } else if (IS_ENABLED(CONFIG_IPV6) && 410b57dc7c1SPaul Blakey skb->protocol == htons(ETH_P_IPV6)) { 411b57dc7c1SPaul Blakey __be16 frag_off; 412b57dc7c1SPaul Blakey u8 nexthdr = ipv6_hdr(skb)->nexthdr; 413b57dc7c1SPaul Blakey int hdrlen = ipv6_skip_exthdr(skb, 414b57dc7c1SPaul Blakey sizeof(struct ipv6hdr), 415b57dc7c1SPaul Blakey &nexthdr, &frag_off); 416b57dc7c1SPaul Blakey 417b57dc7c1SPaul Blakey if (hdrlen >= 0 && nexthdr == IPPROTO_ICMPV6) { 418b57dc7c1SPaul Blakey if (!nf_nat_icmpv6_reply_translation(skb, ct, 419b57dc7c1SPaul Blakey ctinfo, 420b57dc7c1SPaul Blakey hooknum, 421b57dc7c1SPaul Blakey hdrlen)) 422b57dc7c1SPaul Blakey err = NF_DROP; 423b57dc7c1SPaul Blakey goto out; 424b57dc7c1SPaul Blakey } 425b57dc7c1SPaul Blakey } 426b57dc7c1SPaul Blakey /* Non-ICMP, fall thru to initialize if needed. */ 427b57dc7c1SPaul Blakey /* fall through */ 428b57dc7c1SPaul Blakey case IP_CT_NEW: 429b57dc7c1SPaul Blakey /* Seen it before? This can happen for loopback, retrans, 430b57dc7c1SPaul Blakey * or local packets. 431b57dc7c1SPaul Blakey */ 432b57dc7c1SPaul Blakey if (!nf_nat_initialized(ct, maniptype)) { 433b57dc7c1SPaul Blakey /* Initialize according to the NAT action. */ 434b57dc7c1SPaul Blakey err = (range && range->flags & NF_NAT_RANGE_MAP_IPS) 435b57dc7c1SPaul Blakey /* Action is set up to establish a new 436b57dc7c1SPaul Blakey * mapping. 437b57dc7c1SPaul Blakey */ 438b57dc7c1SPaul Blakey ? nf_nat_setup_info(ct, range, maniptype) 439b57dc7c1SPaul Blakey : nf_nat_alloc_null_binding(ct, hooknum); 440b57dc7c1SPaul Blakey if (err != NF_ACCEPT) 441b57dc7c1SPaul Blakey goto out; 442b57dc7c1SPaul Blakey } 443b57dc7c1SPaul Blakey break; 444b57dc7c1SPaul Blakey 445b57dc7c1SPaul Blakey case IP_CT_ESTABLISHED: 446b57dc7c1SPaul Blakey case IP_CT_ESTABLISHED_REPLY: 447b57dc7c1SPaul Blakey break; 448b57dc7c1SPaul Blakey 449b57dc7c1SPaul Blakey default: 450b57dc7c1SPaul Blakey err = NF_DROP; 451b57dc7c1SPaul Blakey goto out; 452b57dc7c1SPaul Blakey } 453b57dc7c1SPaul Blakey 454b57dc7c1SPaul Blakey err = nf_nat_packet(ct, ctinfo, hooknum, skb); 455b57dc7c1SPaul Blakey out: 456b57dc7c1SPaul Blakey return err; 457b57dc7c1SPaul Blakey } 458b57dc7c1SPaul Blakey #endif /* CONFIG_NF_NAT */ 459b57dc7c1SPaul Blakey 460b57dc7c1SPaul Blakey static void tcf_ct_act_set_mark(struct nf_conn *ct, u32 mark, u32 mask) 461b57dc7c1SPaul Blakey { 462b57dc7c1SPaul Blakey #if IS_ENABLED(CONFIG_NF_CONNTRACK_MARK) 463b57dc7c1SPaul Blakey u32 new_mark; 464b57dc7c1SPaul Blakey 465b57dc7c1SPaul Blakey if (!mask) 466b57dc7c1SPaul Blakey return; 467b57dc7c1SPaul Blakey 468b57dc7c1SPaul Blakey new_mark = mark | (ct->mark & ~(mask)); 469b57dc7c1SPaul Blakey if (ct->mark != new_mark) { 470b57dc7c1SPaul Blakey ct->mark = new_mark; 471b57dc7c1SPaul Blakey if (nf_ct_is_confirmed(ct)) 472b57dc7c1SPaul Blakey nf_conntrack_event_cache(IPCT_MARK, ct); 473b57dc7c1SPaul Blakey } 474b57dc7c1SPaul Blakey #endif 475b57dc7c1SPaul Blakey } 476b57dc7c1SPaul Blakey 477b57dc7c1SPaul Blakey static void tcf_ct_act_set_labels(struct nf_conn *ct, 478b57dc7c1SPaul Blakey u32 *labels, 479b57dc7c1SPaul Blakey u32 *labels_m) 480b57dc7c1SPaul Blakey { 481b57dc7c1SPaul Blakey #if IS_ENABLED(CONFIG_NF_CONNTRACK_LABELS) 482c593642cSPankaj Bharadiya size_t labels_sz = sizeof_field(struct tcf_ct_params, labels); 483b57dc7c1SPaul Blakey 484b57dc7c1SPaul Blakey if (!memchr_inv(labels_m, 0, labels_sz)) 485b57dc7c1SPaul Blakey return; 486b57dc7c1SPaul Blakey 487b57dc7c1SPaul Blakey nf_connlabels_replace(ct, labels, labels_m, 4); 488b57dc7c1SPaul Blakey #endif 489b57dc7c1SPaul Blakey } 490b57dc7c1SPaul Blakey 491b57dc7c1SPaul Blakey static int tcf_ct_act_nat(struct sk_buff *skb, 492b57dc7c1SPaul Blakey struct nf_conn *ct, 493b57dc7c1SPaul Blakey enum ip_conntrack_info ctinfo, 494b57dc7c1SPaul Blakey int ct_action, 495b57dc7c1SPaul Blakey struct nf_nat_range2 *range, 496b57dc7c1SPaul Blakey bool commit) 497b57dc7c1SPaul Blakey { 498b57dc7c1SPaul Blakey #if IS_ENABLED(CONFIG_NF_NAT) 49995219afbSAaron Conole int err; 500b57dc7c1SPaul Blakey enum nf_nat_manip_type maniptype; 501b57dc7c1SPaul Blakey 502b57dc7c1SPaul Blakey if (!(ct_action & TCA_CT_ACT_NAT)) 503b57dc7c1SPaul Blakey return NF_ACCEPT; 504b57dc7c1SPaul Blakey 505b57dc7c1SPaul Blakey /* Add NAT extension if not confirmed yet. */ 506b57dc7c1SPaul Blakey if (!nf_ct_is_confirmed(ct) && !nf_ct_nat_ext_add(ct)) 507b57dc7c1SPaul Blakey return NF_DROP; /* Can't NAT. */ 508b57dc7c1SPaul Blakey 509b57dc7c1SPaul Blakey if (ctinfo != IP_CT_NEW && (ct->status & IPS_NAT_MASK) && 510b57dc7c1SPaul Blakey (ctinfo != IP_CT_RELATED || commit)) { 511b57dc7c1SPaul Blakey /* NAT an established or related connection like before. */ 512b57dc7c1SPaul Blakey if (CTINFO2DIR(ctinfo) == IP_CT_DIR_REPLY) 513b57dc7c1SPaul Blakey /* This is the REPLY direction for a connection 514b57dc7c1SPaul Blakey * for which NAT was applied in the forward 515b57dc7c1SPaul Blakey * direction. Do the reverse NAT. 516b57dc7c1SPaul Blakey */ 517b57dc7c1SPaul Blakey maniptype = ct->status & IPS_SRC_NAT 518b57dc7c1SPaul Blakey ? NF_NAT_MANIP_DST : NF_NAT_MANIP_SRC; 519b57dc7c1SPaul Blakey else 520b57dc7c1SPaul Blakey maniptype = ct->status & IPS_SRC_NAT 521b57dc7c1SPaul Blakey ? NF_NAT_MANIP_SRC : NF_NAT_MANIP_DST; 522b57dc7c1SPaul Blakey } else if (ct_action & TCA_CT_ACT_NAT_SRC) { 523b57dc7c1SPaul Blakey maniptype = NF_NAT_MANIP_SRC; 524b57dc7c1SPaul Blakey } else if (ct_action & TCA_CT_ACT_NAT_DST) { 525b57dc7c1SPaul Blakey maniptype = NF_NAT_MANIP_DST; 526b57dc7c1SPaul Blakey } else { 527b57dc7c1SPaul Blakey return NF_ACCEPT; 528b57dc7c1SPaul Blakey } 529b57dc7c1SPaul Blakey 53095219afbSAaron Conole err = ct_nat_execute(skb, ct, ctinfo, range, maniptype); 53195219afbSAaron Conole if (err == NF_ACCEPT && 53295219afbSAaron Conole ct->status & IPS_SRC_NAT && ct->status & IPS_DST_NAT) { 53395219afbSAaron Conole if (maniptype == NF_NAT_MANIP_SRC) 53495219afbSAaron Conole maniptype = NF_NAT_MANIP_DST; 53595219afbSAaron Conole else 53695219afbSAaron Conole maniptype = NF_NAT_MANIP_SRC; 53795219afbSAaron Conole 53895219afbSAaron Conole err = ct_nat_execute(skb, ct, ctinfo, range, maniptype); 53995219afbSAaron Conole } 54095219afbSAaron Conole return err; 541b57dc7c1SPaul Blakey #else 542b57dc7c1SPaul Blakey return NF_ACCEPT; 543b57dc7c1SPaul Blakey #endif 544b57dc7c1SPaul Blakey } 545b57dc7c1SPaul Blakey 546b57dc7c1SPaul Blakey static int tcf_ct_act(struct sk_buff *skb, const struct tc_action *a, 547b57dc7c1SPaul Blakey struct tcf_result *res) 548b57dc7c1SPaul Blakey { 549b57dc7c1SPaul Blakey struct net *net = dev_net(skb->dev); 550b57dc7c1SPaul Blakey bool cached, commit, clear, force; 551b57dc7c1SPaul Blakey enum ip_conntrack_info ctinfo; 552b57dc7c1SPaul Blakey struct tcf_ct *c = to_ct(a); 553b57dc7c1SPaul Blakey struct nf_conn *tmpl = NULL; 554b57dc7c1SPaul Blakey struct nf_hook_state state; 555b57dc7c1SPaul Blakey int nh_ofs, err, retval; 556b57dc7c1SPaul Blakey struct tcf_ct_params *p; 557b57dc7c1SPaul Blakey struct nf_conn *ct; 558b57dc7c1SPaul Blakey u8 family; 559b57dc7c1SPaul Blakey 560b57dc7c1SPaul Blakey p = rcu_dereference_bh(c->params); 561b57dc7c1SPaul Blakey 562b57dc7c1SPaul Blakey retval = READ_ONCE(c->tcf_action); 563b57dc7c1SPaul Blakey commit = p->ct_action & TCA_CT_ACT_COMMIT; 564b57dc7c1SPaul Blakey clear = p->ct_action & TCA_CT_ACT_CLEAR; 565b57dc7c1SPaul Blakey force = p->ct_action & TCA_CT_ACT_FORCE; 566b57dc7c1SPaul Blakey tmpl = p->tmpl; 567b57dc7c1SPaul Blakey 568b57dc7c1SPaul Blakey if (clear) { 569b57dc7c1SPaul Blakey ct = nf_ct_get(skb, &ctinfo); 570b57dc7c1SPaul Blakey if (ct) { 571b57dc7c1SPaul Blakey nf_conntrack_put(&ct->ct_general); 572b57dc7c1SPaul Blakey nf_ct_set(skb, NULL, IP_CT_UNTRACKED); 573b57dc7c1SPaul Blakey } 574b57dc7c1SPaul Blakey 575b57dc7c1SPaul Blakey goto out; 576b57dc7c1SPaul Blakey } 577b57dc7c1SPaul Blakey 578b57dc7c1SPaul Blakey family = tcf_ct_skb_nf_family(skb); 579b57dc7c1SPaul Blakey if (family == NFPROTO_UNSPEC) 580b57dc7c1SPaul Blakey goto drop; 581b57dc7c1SPaul Blakey 582b57dc7c1SPaul Blakey /* The conntrack module expects to be working at L3. 583b57dc7c1SPaul Blakey * We also try to pull the IPv4/6 header to linear area 584b57dc7c1SPaul Blakey */ 585b57dc7c1SPaul Blakey nh_ofs = skb_network_offset(skb); 586b57dc7c1SPaul Blakey skb_pull_rcsum(skb, nh_ofs); 587b57dc7c1SPaul Blakey err = tcf_ct_handle_fragments(net, skb, family, p->zone); 588b57dc7c1SPaul Blakey if (err == -EINPROGRESS) { 589b57dc7c1SPaul Blakey retval = TC_ACT_STOLEN; 590b57dc7c1SPaul Blakey goto out; 591b57dc7c1SPaul Blakey } 592b57dc7c1SPaul Blakey if (err) 593b57dc7c1SPaul Blakey goto drop; 594b57dc7c1SPaul Blakey 595b57dc7c1SPaul Blakey err = tcf_ct_skb_network_trim(skb, family); 596b57dc7c1SPaul Blakey if (err) 597b57dc7c1SPaul Blakey goto drop; 598b57dc7c1SPaul Blakey 599b57dc7c1SPaul Blakey /* If we are recirculating packets to match on ct fields and 600b57dc7c1SPaul Blakey * committing with a separate ct action, then we don't need to 601b57dc7c1SPaul Blakey * actually run the packet through conntrack twice unless it's for a 602b57dc7c1SPaul Blakey * different zone. 603b57dc7c1SPaul Blakey */ 604b57dc7c1SPaul Blakey cached = tcf_ct_skb_nfct_cached(net, skb, p->zone, force); 605b57dc7c1SPaul Blakey if (!cached) { 606b57dc7c1SPaul Blakey /* Associate skb with specified zone. */ 607b57dc7c1SPaul Blakey if (tmpl) { 608b57dc7c1SPaul Blakey ct = nf_ct_get(skb, &ctinfo); 609b57dc7c1SPaul Blakey if (skb_nfct(skb)) 610b57dc7c1SPaul Blakey nf_conntrack_put(skb_nfct(skb)); 611b57dc7c1SPaul Blakey nf_conntrack_get(&tmpl->ct_general); 612b57dc7c1SPaul Blakey nf_ct_set(skb, tmpl, IP_CT_NEW); 613b57dc7c1SPaul Blakey } 614b57dc7c1SPaul Blakey 615b57dc7c1SPaul Blakey state.hook = NF_INET_PRE_ROUTING; 616b57dc7c1SPaul Blakey state.net = net; 617b57dc7c1SPaul Blakey state.pf = family; 618b57dc7c1SPaul Blakey err = nf_conntrack_in(skb, &state); 619b57dc7c1SPaul Blakey if (err != NF_ACCEPT) 620b57dc7c1SPaul Blakey goto out_push; 621b57dc7c1SPaul Blakey } 622b57dc7c1SPaul Blakey 623b57dc7c1SPaul Blakey ct = nf_ct_get(skb, &ctinfo); 624b57dc7c1SPaul Blakey if (!ct) 625b57dc7c1SPaul Blakey goto out_push; 626b57dc7c1SPaul Blakey nf_ct_deliver_cached_events(ct); 627b57dc7c1SPaul Blakey 628b57dc7c1SPaul Blakey err = tcf_ct_act_nat(skb, ct, ctinfo, p->ct_action, &p->range, commit); 629b57dc7c1SPaul Blakey if (err != NF_ACCEPT) 630b57dc7c1SPaul Blakey goto drop; 631b57dc7c1SPaul Blakey 632b57dc7c1SPaul Blakey if (commit) { 633b57dc7c1SPaul Blakey tcf_ct_act_set_mark(ct, p->mark, p->mark_mask); 634b57dc7c1SPaul Blakey tcf_ct_act_set_labels(ct, p->labels, p->labels_mask); 635b57dc7c1SPaul Blakey 636b57dc7c1SPaul Blakey /* This will take care of sending queued events 637b57dc7c1SPaul Blakey * even if the connection is already confirmed. 638b57dc7c1SPaul Blakey */ 639b57dc7c1SPaul Blakey nf_conntrack_confirm(skb); 640b57dc7c1SPaul Blakey } 641b57dc7c1SPaul Blakey 642*64ff70b8SPaul Blakey tcf_ct_flow_table_process_conn(p->ct_ft, ct, ctinfo); 643*64ff70b8SPaul Blakey 644b57dc7c1SPaul Blakey out_push: 645b57dc7c1SPaul Blakey skb_push_rcsum(skb, nh_ofs); 646b57dc7c1SPaul Blakey 647b57dc7c1SPaul Blakey out: 6485e1ad95bSVlad Buslov tcf_action_update_bstats(&c->common, skb); 649b57dc7c1SPaul Blakey return retval; 650b57dc7c1SPaul Blakey 651b57dc7c1SPaul Blakey drop: 65226b537a8SVlad Buslov tcf_action_inc_drop_qstats(&c->common); 653b57dc7c1SPaul Blakey return TC_ACT_SHOT; 654b57dc7c1SPaul Blakey } 655b57dc7c1SPaul Blakey 656b57dc7c1SPaul Blakey static const struct nla_policy ct_policy[TCA_CT_MAX + 1] = { 657b57dc7c1SPaul Blakey [TCA_CT_ACTION] = { .type = NLA_U16 }, 658b57dc7c1SPaul Blakey [TCA_CT_PARMS] = { .type = NLA_EXACT_LEN, .len = sizeof(struct tc_ct) }, 659b57dc7c1SPaul Blakey [TCA_CT_ZONE] = { .type = NLA_U16 }, 660b57dc7c1SPaul Blakey [TCA_CT_MARK] = { .type = NLA_U32 }, 661b57dc7c1SPaul Blakey [TCA_CT_MARK_MASK] = { .type = NLA_U32 }, 662b57dc7c1SPaul Blakey [TCA_CT_LABELS] = { .type = NLA_BINARY, 663b57dc7c1SPaul Blakey .len = 128 / BITS_PER_BYTE }, 664b57dc7c1SPaul Blakey [TCA_CT_LABELS_MASK] = { .type = NLA_BINARY, 665b57dc7c1SPaul Blakey .len = 128 / BITS_PER_BYTE }, 666b57dc7c1SPaul Blakey [TCA_CT_NAT_IPV4_MIN] = { .type = NLA_U32 }, 667b57dc7c1SPaul Blakey [TCA_CT_NAT_IPV4_MAX] = { .type = NLA_U32 }, 668b57dc7c1SPaul Blakey [TCA_CT_NAT_IPV6_MIN] = { .type = NLA_EXACT_LEN, 669b57dc7c1SPaul Blakey .len = sizeof(struct in6_addr) }, 670b57dc7c1SPaul Blakey [TCA_CT_NAT_IPV6_MAX] = { .type = NLA_EXACT_LEN, 671b57dc7c1SPaul Blakey .len = sizeof(struct in6_addr) }, 672b57dc7c1SPaul Blakey [TCA_CT_NAT_PORT_MIN] = { .type = NLA_U16 }, 673b57dc7c1SPaul Blakey [TCA_CT_NAT_PORT_MAX] = { .type = NLA_U16 }, 674b57dc7c1SPaul Blakey }; 675b57dc7c1SPaul Blakey 676b57dc7c1SPaul Blakey static int tcf_ct_fill_params_nat(struct tcf_ct_params *p, 677b57dc7c1SPaul Blakey struct tc_ct *parm, 678b57dc7c1SPaul Blakey struct nlattr **tb, 679b57dc7c1SPaul Blakey struct netlink_ext_ack *extack) 680b57dc7c1SPaul Blakey { 681b57dc7c1SPaul Blakey struct nf_nat_range2 *range; 682b57dc7c1SPaul Blakey 683b57dc7c1SPaul Blakey if (!(p->ct_action & TCA_CT_ACT_NAT)) 684b57dc7c1SPaul Blakey return 0; 685b57dc7c1SPaul Blakey 686b57dc7c1SPaul Blakey if (!IS_ENABLED(CONFIG_NF_NAT)) { 687b57dc7c1SPaul Blakey NL_SET_ERR_MSG_MOD(extack, "Netfilter nat isn't enabled in kernel"); 688b57dc7c1SPaul Blakey return -EOPNOTSUPP; 689b57dc7c1SPaul Blakey } 690b57dc7c1SPaul Blakey 691b57dc7c1SPaul Blakey if (!(p->ct_action & (TCA_CT_ACT_NAT_SRC | TCA_CT_ACT_NAT_DST))) 692b57dc7c1SPaul Blakey return 0; 693b57dc7c1SPaul Blakey 694b57dc7c1SPaul Blakey if ((p->ct_action & TCA_CT_ACT_NAT_SRC) && 695b57dc7c1SPaul Blakey (p->ct_action & TCA_CT_ACT_NAT_DST)) { 696b57dc7c1SPaul Blakey NL_SET_ERR_MSG_MOD(extack, "dnat and snat can't be enabled at the same time"); 697b57dc7c1SPaul Blakey return -EOPNOTSUPP; 698b57dc7c1SPaul Blakey } 699b57dc7c1SPaul Blakey 700b57dc7c1SPaul Blakey range = &p->range; 701b57dc7c1SPaul Blakey if (tb[TCA_CT_NAT_IPV4_MIN]) { 702b57dc7c1SPaul Blakey struct nlattr *max_attr = tb[TCA_CT_NAT_IPV4_MAX]; 703b57dc7c1SPaul Blakey 704b57dc7c1SPaul Blakey p->ipv4_range = true; 705b57dc7c1SPaul Blakey range->flags |= NF_NAT_RANGE_MAP_IPS; 706b57dc7c1SPaul Blakey range->min_addr.ip = 707b57dc7c1SPaul Blakey nla_get_in_addr(tb[TCA_CT_NAT_IPV4_MIN]); 708b57dc7c1SPaul Blakey 709b57dc7c1SPaul Blakey range->max_addr.ip = max_attr ? 710b57dc7c1SPaul Blakey nla_get_in_addr(max_attr) : 711b57dc7c1SPaul Blakey range->min_addr.ip; 712b57dc7c1SPaul Blakey } else if (tb[TCA_CT_NAT_IPV6_MIN]) { 713b57dc7c1SPaul Blakey struct nlattr *max_attr = tb[TCA_CT_NAT_IPV6_MAX]; 714b57dc7c1SPaul Blakey 715b57dc7c1SPaul Blakey p->ipv4_range = false; 716b57dc7c1SPaul Blakey range->flags |= NF_NAT_RANGE_MAP_IPS; 717b57dc7c1SPaul Blakey range->min_addr.in6 = 718b57dc7c1SPaul Blakey nla_get_in6_addr(tb[TCA_CT_NAT_IPV6_MIN]); 719b57dc7c1SPaul Blakey 720b57dc7c1SPaul Blakey range->max_addr.in6 = max_attr ? 721b57dc7c1SPaul Blakey nla_get_in6_addr(max_attr) : 722b57dc7c1SPaul Blakey range->min_addr.in6; 723b57dc7c1SPaul Blakey } 724b57dc7c1SPaul Blakey 725b57dc7c1SPaul Blakey if (tb[TCA_CT_NAT_PORT_MIN]) { 726b57dc7c1SPaul Blakey range->flags |= NF_NAT_RANGE_PROTO_SPECIFIED; 727b57dc7c1SPaul Blakey range->min_proto.all = nla_get_be16(tb[TCA_CT_NAT_PORT_MIN]); 728b57dc7c1SPaul Blakey 729b57dc7c1SPaul Blakey range->max_proto.all = tb[TCA_CT_NAT_PORT_MAX] ? 730b57dc7c1SPaul Blakey nla_get_be16(tb[TCA_CT_NAT_PORT_MAX]) : 731b57dc7c1SPaul Blakey range->min_proto.all; 732b57dc7c1SPaul Blakey } 733b57dc7c1SPaul Blakey 734b57dc7c1SPaul Blakey return 0; 735b57dc7c1SPaul Blakey } 736b57dc7c1SPaul Blakey 737b57dc7c1SPaul Blakey static void tcf_ct_set_key_val(struct nlattr **tb, 738b57dc7c1SPaul Blakey void *val, int val_type, 739b57dc7c1SPaul Blakey void *mask, int mask_type, 740b57dc7c1SPaul Blakey int len) 741b57dc7c1SPaul Blakey { 742b57dc7c1SPaul Blakey if (!tb[val_type]) 743b57dc7c1SPaul Blakey return; 744b57dc7c1SPaul Blakey nla_memcpy(val, tb[val_type], len); 745b57dc7c1SPaul Blakey 746b57dc7c1SPaul Blakey if (!mask) 747b57dc7c1SPaul Blakey return; 748b57dc7c1SPaul Blakey 749b57dc7c1SPaul Blakey if (mask_type == TCA_CT_UNSPEC || !tb[mask_type]) 750b57dc7c1SPaul Blakey memset(mask, 0xff, len); 751b57dc7c1SPaul Blakey else 752b57dc7c1SPaul Blakey nla_memcpy(mask, tb[mask_type], len); 753b57dc7c1SPaul Blakey } 754b57dc7c1SPaul Blakey 755b57dc7c1SPaul Blakey static int tcf_ct_fill_params(struct net *net, 756b57dc7c1SPaul Blakey struct tcf_ct_params *p, 757b57dc7c1SPaul Blakey struct tc_ct *parm, 758b57dc7c1SPaul Blakey struct nlattr **tb, 759b57dc7c1SPaul Blakey struct netlink_ext_ack *extack) 760b57dc7c1SPaul Blakey { 761b57dc7c1SPaul Blakey struct tc_ct_action_net *tn = net_generic(net, ct_net_id); 762b57dc7c1SPaul Blakey struct nf_conntrack_zone zone; 763b57dc7c1SPaul Blakey struct nf_conn *tmpl; 764b57dc7c1SPaul Blakey int err; 765b57dc7c1SPaul Blakey 766b57dc7c1SPaul Blakey p->zone = NF_CT_DEFAULT_ZONE_ID; 767b57dc7c1SPaul Blakey 768b57dc7c1SPaul Blakey tcf_ct_set_key_val(tb, 769b57dc7c1SPaul Blakey &p->ct_action, TCA_CT_ACTION, 770b57dc7c1SPaul Blakey NULL, TCA_CT_UNSPEC, 771b57dc7c1SPaul Blakey sizeof(p->ct_action)); 772b57dc7c1SPaul Blakey 773b57dc7c1SPaul Blakey if (p->ct_action & TCA_CT_ACT_CLEAR) 774b57dc7c1SPaul Blakey return 0; 775b57dc7c1SPaul Blakey 776b57dc7c1SPaul Blakey err = tcf_ct_fill_params_nat(p, parm, tb, extack); 777b57dc7c1SPaul Blakey if (err) 778b57dc7c1SPaul Blakey return err; 779b57dc7c1SPaul Blakey 780b57dc7c1SPaul Blakey if (tb[TCA_CT_MARK]) { 781b57dc7c1SPaul Blakey if (!IS_ENABLED(CONFIG_NF_CONNTRACK_MARK)) { 782b57dc7c1SPaul Blakey NL_SET_ERR_MSG_MOD(extack, "Conntrack mark isn't enabled."); 783b57dc7c1SPaul Blakey return -EOPNOTSUPP; 784b57dc7c1SPaul Blakey } 785b57dc7c1SPaul Blakey tcf_ct_set_key_val(tb, 786b57dc7c1SPaul Blakey &p->mark, TCA_CT_MARK, 787b57dc7c1SPaul Blakey &p->mark_mask, TCA_CT_MARK_MASK, 788b57dc7c1SPaul Blakey sizeof(p->mark)); 789b57dc7c1SPaul Blakey } 790b57dc7c1SPaul Blakey 791b57dc7c1SPaul Blakey if (tb[TCA_CT_LABELS]) { 792b57dc7c1SPaul Blakey if (!IS_ENABLED(CONFIG_NF_CONNTRACK_LABELS)) { 793b57dc7c1SPaul Blakey NL_SET_ERR_MSG_MOD(extack, "Conntrack labels isn't enabled."); 794b57dc7c1SPaul Blakey return -EOPNOTSUPP; 795b57dc7c1SPaul Blakey } 796b57dc7c1SPaul Blakey 797b57dc7c1SPaul Blakey if (!tn->labels) { 798b57dc7c1SPaul Blakey NL_SET_ERR_MSG_MOD(extack, "Failed to set connlabel length"); 799b57dc7c1SPaul Blakey return -EOPNOTSUPP; 800b57dc7c1SPaul Blakey } 801b57dc7c1SPaul Blakey tcf_ct_set_key_val(tb, 802b57dc7c1SPaul Blakey p->labels, TCA_CT_LABELS, 803b57dc7c1SPaul Blakey p->labels_mask, TCA_CT_LABELS_MASK, 804b57dc7c1SPaul Blakey sizeof(p->labels)); 805b57dc7c1SPaul Blakey } 806b57dc7c1SPaul Blakey 807b57dc7c1SPaul Blakey if (tb[TCA_CT_ZONE]) { 808b57dc7c1SPaul Blakey if (!IS_ENABLED(CONFIG_NF_CONNTRACK_ZONES)) { 809b57dc7c1SPaul Blakey NL_SET_ERR_MSG_MOD(extack, "Conntrack zones isn't enabled."); 810b57dc7c1SPaul Blakey return -EOPNOTSUPP; 811b57dc7c1SPaul Blakey } 812b57dc7c1SPaul Blakey 813b57dc7c1SPaul Blakey tcf_ct_set_key_val(tb, 814b57dc7c1SPaul Blakey &p->zone, TCA_CT_ZONE, 815b57dc7c1SPaul Blakey NULL, TCA_CT_UNSPEC, 816b57dc7c1SPaul Blakey sizeof(p->zone)); 817b57dc7c1SPaul Blakey } 818b57dc7c1SPaul Blakey 819b57dc7c1SPaul Blakey if (p->zone == NF_CT_DEFAULT_ZONE_ID) 820b57dc7c1SPaul Blakey return 0; 821b57dc7c1SPaul Blakey 822b57dc7c1SPaul Blakey nf_ct_zone_init(&zone, p->zone, NF_CT_DEFAULT_ZONE_DIR, 0); 823b57dc7c1SPaul Blakey tmpl = nf_ct_tmpl_alloc(net, &zone, GFP_KERNEL); 824b57dc7c1SPaul Blakey if (!tmpl) { 825b57dc7c1SPaul Blakey NL_SET_ERR_MSG_MOD(extack, "Failed to allocate conntrack template"); 826b57dc7c1SPaul Blakey return -ENOMEM; 827b57dc7c1SPaul Blakey } 828b57dc7c1SPaul Blakey __set_bit(IPS_CONFIRMED_BIT, &tmpl->status); 829b57dc7c1SPaul Blakey nf_conntrack_get(&tmpl->ct_general); 830b57dc7c1SPaul Blakey p->tmpl = tmpl; 831b57dc7c1SPaul Blakey 832b57dc7c1SPaul Blakey return 0; 833b57dc7c1SPaul Blakey } 834b57dc7c1SPaul Blakey 835b57dc7c1SPaul Blakey static int tcf_ct_init(struct net *net, struct nlattr *nla, 836b57dc7c1SPaul Blakey struct nlattr *est, struct tc_action **a, 837b57dc7c1SPaul Blakey int replace, int bind, bool rtnl_held, 838abbb0d33SVlad Buslov struct tcf_proto *tp, u32 flags, 839b57dc7c1SPaul Blakey struct netlink_ext_ack *extack) 840b57dc7c1SPaul Blakey { 841b57dc7c1SPaul Blakey struct tc_action_net *tn = net_generic(net, ct_net_id); 842b57dc7c1SPaul Blakey struct tcf_ct_params *params = NULL; 843b57dc7c1SPaul Blakey struct nlattr *tb[TCA_CT_MAX + 1]; 844b57dc7c1SPaul Blakey struct tcf_chain *goto_ch = NULL; 845b57dc7c1SPaul Blakey struct tc_ct *parm; 846b57dc7c1SPaul Blakey struct tcf_ct *c; 847b57dc7c1SPaul Blakey int err, res = 0; 8487be8ef2cSDmytro Linkin u32 index; 849b57dc7c1SPaul Blakey 850b57dc7c1SPaul Blakey if (!nla) { 851b57dc7c1SPaul Blakey NL_SET_ERR_MSG_MOD(extack, "Ct requires attributes to be passed"); 852b57dc7c1SPaul Blakey return -EINVAL; 853b57dc7c1SPaul Blakey } 854b57dc7c1SPaul Blakey 855b57dc7c1SPaul Blakey err = nla_parse_nested(tb, TCA_CT_MAX, nla, ct_policy, extack); 856b57dc7c1SPaul Blakey if (err < 0) 857b57dc7c1SPaul Blakey return err; 858b57dc7c1SPaul Blakey 859b57dc7c1SPaul Blakey if (!tb[TCA_CT_PARMS]) { 860b57dc7c1SPaul Blakey NL_SET_ERR_MSG_MOD(extack, "Missing required ct parameters"); 861b57dc7c1SPaul Blakey return -EINVAL; 862b57dc7c1SPaul Blakey } 863b57dc7c1SPaul Blakey parm = nla_data(tb[TCA_CT_PARMS]); 8647be8ef2cSDmytro Linkin index = parm->index; 8657be8ef2cSDmytro Linkin err = tcf_idr_check_alloc(tn, &index, a, bind); 866b57dc7c1SPaul Blakey if (err < 0) 867b57dc7c1SPaul Blakey return err; 868b57dc7c1SPaul Blakey 869b57dc7c1SPaul Blakey if (!err) { 870e3822678SVlad Buslov err = tcf_idr_create_from_flags(tn, index, est, a, 871e3822678SVlad Buslov &act_ct_ops, bind, flags); 872b57dc7c1SPaul Blakey if (err) { 8737be8ef2cSDmytro Linkin tcf_idr_cleanup(tn, index); 874b57dc7c1SPaul Blakey return err; 875b57dc7c1SPaul Blakey } 876b57dc7c1SPaul Blakey res = ACT_P_CREATED; 877b57dc7c1SPaul Blakey } else { 878b57dc7c1SPaul Blakey if (bind) 879b57dc7c1SPaul Blakey return 0; 880b57dc7c1SPaul Blakey 881b57dc7c1SPaul Blakey if (!replace) { 882b57dc7c1SPaul Blakey tcf_idr_release(*a, bind); 883b57dc7c1SPaul Blakey return -EEXIST; 884b57dc7c1SPaul Blakey } 885b57dc7c1SPaul Blakey } 886b57dc7c1SPaul Blakey err = tcf_action_check_ctrlact(parm->action, tp, &goto_ch, extack); 887b57dc7c1SPaul Blakey if (err < 0) 888b57dc7c1SPaul Blakey goto cleanup; 889b57dc7c1SPaul Blakey 890b57dc7c1SPaul Blakey c = to_ct(*a); 891b57dc7c1SPaul Blakey 892b57dc7c1SPaul Blakey params = kzalloc(sizeof(*params), GFP_KERNEL); 893b57dc7c1SPaul Blakey if (unlikely(!params)) { 894b57dc7c1SPaul Blakey err = -ENOMEM; 895b57dc7c1SPaul Blakey goto cleanup; 896b57dc7c1SPaul Blakey } 897b57dc7c1SPaul Blakey 898b57dc7c1SPaul Blakey err = tcf_ct_fill_params(net, params, parm, tb, extack); 899b57dc7c1SPaul Blakey if (err) 900b57dc7c1SPaul Blakey goto cleanup; 901b57dc7c1SPaul Blakey 902c34b961aSPaul Blakey err = tcf_ct_flow_table_get(params); 903c34b961aSPaul Blakey if (err) 904c34b961aSPaul Blakey goto cleanup; 905c34b961aSPaul Blakey 906b57dc7c1SPaul Blakey spin_lock_bh(&c->tcf_lock); 907b57dc7c1SPaul Blakey goto_ch = tcf_action_set_ctrlact(*a, parm->action, goto_ch); 908445d3749SPaul E. McKenney params = rcu_replace_pointer(c->params, params, 909445d3749SPaul E. McKenney lockdep_is_held(&c->tcf_lock)); 910b57dc7c1SPaul Blakey spin_unlock_bh(&c->tcf_lock); 911b57dc7c1SPaul Blakey 912b57dc7c1SPaul Blakey if (goto_ch) 913b57dc7c1SPaul Blakey tcf_chain_put_by_act(goto_ch); 914b57dc7c1SPaul Blakey if (params) 915b57dc7c1SPaul Blakey kfree_rcu(params, rcu); 916b57dc7c1SPaul Blakey if (res == ACT_P_CREATED) 917b57dc7c1SPaul Blakey tcf_idr_insert(tn, *a); 918b57dc7c1SPaul Blakey 919b57dc7c1SPaul Blakey return res; 920b57dc7c1SPaul Blakey 921b57dc7c1SPaul Blakey cleanup: 922b57dc7c1SPaul Blakey if (goto_ch) 923b57dc7c1SPaul Blakey tcf_chain_put_by_act(goto_ch); 924b57dc7c1SPaul Blakey kfree(params); 925b57dc7c1SPaul Blakey tcf_idr_release(*a, bind); 926b57dc7c1SPaul Blakey return err; 927b57dc7c1SPaul Blakey } 928b57dc7c1SPaul Blakey 929b57dc7c1SPaul Blakey static void tcf_ct_cleanup(struct tc_action *a) 930b57dc7c1SPaul Blakey { 931b57dc7c1SPaul Blakey struct tcf_ct_params *params; 932b57dc7c1SPaul Blakey struct tcf_ct *c = to_ct(a); 933b57dc7c1SPaul Blakey 934b57dc7c1SPaul Blakey params = rcu_dereference_protected(c->params, 1); 935b57dc7c1SPaul Blakey if (params) 936b57dc7c1SPaul Blakey call_rcu(¶ms->rcu, tcf_ct_params_free); 937b57dc7c1SPaul Blakey } 938b57dc7c1SPaul Blakey 939b57dc7c1SPaul Blakey static int tcf_ct_dump_key_val(struct sk_buff *skb, 940b57dc7c1SPaul Blakey void *val, int val_type, 941b57dc7c1SPaul Blakey void *mask, int mask_type, 942b57dc7c1SPaul Blakey int len) 943b57dc7c1SPaul Blakey { 944b57dc7c1SPaul Blakey int err; 945b57dc7c1SPaul Blakey 946b57dc7c1SPaul Blakey if (mask && !memchr_inv(mask, 0, len)) 947b57dc7c1SPaul Blakey return 0; 948b57dc7c1SPaul Blakey 949b57dc7c1SPaul Blakey err = nla_put(skb, val_type, len, val); 950b57dc7c1SPaul Blakey if (err) 951b57dc7c1SPaul Blakey return err; 952b57dc7c1SPaul Blakey 953b57dc7c1SPaul Blakey if (mask_type != TCA_CT_UNSPEC) { 954b57dc7c1SPaul Blakey err = nla_put(skb, mask_type, len, mask); 955b57dc7c1SPaul Blakey if (err) 956b57dc7c1SPaul Blakey return err; 957b57dc7c1SPaul Blakey } 958b57dc7c1SPaul Blakey 959b57dc7c1SPaul Blakey return 0; 960b57dc7c1SPaul Blakey } 961b57dc7c1SPaul Blakey 962b57dc7c1SPaul Blakey static int tcf_ct_dump_nat(struct sk_buff *skb, struct tcf_ct_params *p) 963b57dc7c1SPaul Blakey { 964b57dc7c1SPaul Blakey struct nf_nat_range2 *range = &p->range; 965b57dc7c1SPaul Blakey 966b57dc7c1SPaul Blakey if (!(p->ct_action & TCA_CT_ACT_NAT)) 967b57dc7c1SPaul Blakey return 0; 968b57dc7c1SPaul Blakey 969b57dc7c1SPaul Blakey if (!(p->ct_action & (TCA_CT_ACT_NAT_SRC | TCA_CT_ACT_NAT_DST))) 970b57dc7c1SPaul Blakey return 0; 971b57dc7c1SPaul Blakey 972b57dc7c1SPaul Blakey if (range->flags & NF_NAT_RANGE_MAP_IPS) { 973b57dc7c1SPaul Blakey if (p->ipv4_range) { 974b57dc7c1SPaul Blakey if (nla_put_in_addr(skb, TCA_CT_NAT_IPV4_MIN, 975b57dc7c1SPaul Blakey range->min_addr.ip)) 976b57dc7c1SPaul Blakey return -1; 977b57dc7c1SPaul Blakey if (nla_put_in_addr(skb, TCA_CT_NAT_IPV4_MAX, 978b57dc7c1SPaul Blakey range->max_addr.ip)) 979b57dc7c1SPaul Blakey return -1; 980b57dc7c1SPaul Blakey } else { 981b57dc7c1SPaul Blakey if (nla_put_in6_addr(skb, TCA_CT_NAT_IPV6_MIN, 982b57dc7c1SPaul Blakey &range->min_addr.in6)) 983b57dc7c1SPaul Blakey return -1; 984b57dc7c1SPaul Blakey if (nla_put_in6_addr(skb, TCA_CT_NAT_IPV6_MAX, 985b57dc7c1SPaul Blakey &range->max_addr.in6)) 986b57dc7c1SPaul Blakey return -1; 987b57dc7c1SPaul Blakey } 988b57dc7c1SPaul Blakey } 989b57dc7c1SPaul Blakey 990b57dc7c1SPaul Blakey if (range->flags & NF_NAT_RANGE_PROTO_SPECIFIED) { 991b57dc7c1SPaul Blakey if (nla_put_be16(skb, TCA_CT_NAT_PORT_MIN, 992b57dc7c1SPaul Blakey range->min_proto.all)) 993b57dc7c1SPaul Blakey return -1; 994b57dc7c1SPaul Blakey if (nla_put_be16(skb, TCA_CT_NAT_PORT_MAX, 995b57dc7c1SPaul Blakey range->max_proto.all)) 996b57dc7c1SPaul Blakey return -1; 997b57dc7c1SPaul Blakey } 998b57dc7c1SPaul Blakey 999b57dc7c1SPaul Blakey return 0; 1000b57dc7c1SPaul Blakey } 1001b57dc7c1SPaul Blakey 1002b57dc7c1SPaul Blakey static inline int tcf_ct_dump(struct sk_buff *skb, struct tc_action *a, 1003b57dc7c1SPaul Blakey int bind, int ref) 1004b57dc7c1SPaul Blakey { 1005b57dc7c1SPaul Blakey unsigned char *b = skb_tail_pointer(skb); 1006b57dc7c1SPaul Blakey struct tcf_ct *c = to_ct(a); 1007b57dc7c1SPaul Blakey struct tcf_ct_params *p; 1008b57dc7c1SPaul Blakey 1009b57dc7c1SPaul Blakey struct tc_ct opt = { 1010b57dc7c1SPaul Blakey .index = c->tcf_index, 1011b57dc7c1SPaul Blakey .refcnt = refcount_read(&c->tcf_refcnt) - ref, 1012b57dc7c1SPaul Blakey .bindcnt = atomic_read(&c->tcf_bindcnt) - bind, 1013b57dc7c1SPaul Blakey }; 1014b57dc7c1SPaul Blakey struct tcf_t t; 1015b57dc7c1SPaul Blakey 1016b57dc7c1SPaul Blakey spin_lock_bh(&c->tcf_lock); 1017b57dc7c1SPaul Blakey p = rcu_dereference_protected(c->params, 1018b57dc7c1SPaul Blakey lockdep_is_held(&c->tcf_lock)); 1019b57dc7c1SPaul Blakey opt.action = c->tcf_action; 1020b57dc7c1SPaul Blakey 1021b57dc7c1SPaul Blakey if (tcf_ct_dump_key_val(skb, 1022b57dc7c1SPaul Blakey &p->ct_action, TCA_CT_ACTION, 1023b57dc7c1SPaul Blakey NULL, TCA_CT_UNSPEC, 1024b57dc7c1SPaul Blakey sizeof(p->ct_action))) 1025b57dc7c1SPaul Blakey goto nla_put_failure; 1026b57dc7c1SPaul Blakey 1027b57dc7c1SPaul Blakey if (p->ct_action & TCA_CT_ACT_CLEAR) 1028b57dc7c1SPaul Blakey goto skip_dump; 1029b57dc7c1SPaul Blakey 1030b57dc7c1SPaul Blakey if (IS_ENABLED(CONFIG_NF_CONNTRACK_MARK) && 1031b57dc7c1SPaul Blakey tcf_ct_dump_key_val(skb, 1032b57dc7c1SPaul Blakey &p->mark, TCA_CT_MARK, 1033b57dc7c1SPaul Blakey &p->mark_mask, TCA_CT_MARK_MASK, 1034b57dc7c1SPaul Blakey sizeof(p->mark))) 1035b57dc7c1SPaul Blakey goto nla_put_failure; 1036b57dc7c1SPaul Blakey 1037b57dc7c1SPaul Blakey if (IS_ENABLED(CONFIG_NF_CONNTRACK_LABELS) && 1038b57dc7c1SPaul Blakey tcf_ct_dump_key_val(skb, 1039b57dc7c1SPaul Blakey p->labels, TCA_CT_LABELS, 1040b57dc7c1SPaul Blakey p->labels_mask, TCA_CT_LABELS_MASK, 1041b57dc7c1SPaul Blakey sizeof(p->labels))) 1042b57dc7c1SPaul Blakey goto nla_put_failure; 1043b57dc7c1SPaul Blakey 1044b57dc7c1SPaul Blakey if (IS_ENABLED(CONFIG_NF_CONNTRACK_ZONES) && 1045b57dc7c1SPaul Blakey tcf_ct_dump_key_val(skb, 1046b57dc7c1SPaul Blakey &p->zone, TCA_CT_ZONE, 1047b57dc7c1SPaul Blakey NULL, TCA_CT_UNSPEC, 1048b57dc7c1SPaul Blakey sizeof(p->zone))) 1049b57dc7c1SPaul Blakey goto nla_put_failure; 1050b57dc7c1SPaul Blakey 1051b57dc7c1SPaul Blakey if (tcf_ct_dump_nat(skb, p)) 1052b57dc7c1SPaul Blakey goto nla_put_failure; 1053b57dc7c1SPaul Blakey 1054b57dc7c1SPaul Blakey skip_dump: 1055b57dc7c1SPaul Blakey if (nla_put(skb, TCA_CT_PARMS, sizeof(opt), &opt)) 1056b57dc7c1SPaul Blakey goto nla_put_failure; 1057b57dc7c1SPaul Blakey 1058b57dc7c1SPaul Blakey tcf_tm_dump(&t, &c->tcf_tm); 1059b57dc7c1SPaul Blakey if (nla_put_64bit(skb, TCA_CT_TM, sizeof(t), &t, TCA_CT_PAD)) 1060b57dc7c1SPaul Blakey goto nla_put_failure; 1061b57dc7c1SPaul Blakey spin_unlock_bh(&c->tcf_lock); 1062b57dc7c1SPaul Blakey 1063b57dc7c1SPaul Blakey return skb->len; 1064b57dc7c1SPaul Blakey nla_put_failure: 1065b57dc7c1SPaul Blakey spin_unlock_bh(&c->tcf_lock); 1066b57dc7c1SPaul Blakey nlmsg_trim(skb, b); 1067b57dc7c1SPaul Blakey return -1; 1068b57dc7c1SPaul Blakey } 1069b57dc7c1SPaul Blakey 1070b57dc7c1SPaul Blakey static int tcf_ct_walker(struct net *net, struct sk_buff *skb, 1071b57dc7c1SPaul Blakey struct netlink_callback *cb, int type, 1072b57dc7c1SPaul Blakey const struct tc_action_ops *ops, 1073b57dc7c1SPaul Blakey struct netlink_ext_ack *extack) 1074b57dc7c1SPaul Blakey { 1075b57dc7c1SPaul Blakey struct tc_action_net *tn = net_generic(net, ct_net_id); 1076b57dc7c1SPaul Blakey 1077b57dc7c1SPaul Blakey return tcf_generic_walker(tn, skb, cb, type, ops, extack); 1078b57dc7c1SPaul Blakey } 1079b57dc7c1SPaul Blakey 1080b57dc7c1SPaul Blakey static int tcf_ct_search(struct net *net, struct tc_action **a, u32 index) 1081b57dc7c1SPaul Blakey { 1082b57dc7c1SPaul Blakey struct tc_action_net *tn = net_generic(net, ct_net_id); 1083b57dc7c1SPaul Blakey 1084b57dc7c1SPaul Blakey return tcf_idr_search(tn, a, index); 1085b57dc7c1SPaul Blakey } 1086b57dc7c1SPaul Blakey 1087b57dc7c1SPaul Blakey static void tcf_stats_update(struct tc_action *a, u64 bytes, u32 packets, 1088b57dc7c1SPaul Blakey u64 lastuse, bool hw) 1089b57dc7c1SPaul Blakey { 1090b57dc7c1SPaul Blakey struct tcf_ct *c = to_ct(a); 1091b57dc7c1SPaul Blakey 1092c8ecebd0SVlad Buslov tcf_action_update_stats(a, bytes, packets, false, hw); 1093b57dc7c1SPaul Blakey c->tcf_tm.lastuse = max_t(u64, c->tcf_tm.lastuse, lastuse); 1094b57dc7c1SPaul Blakey } 1095b57dc7c1SPaul Blakey 1096b57dc7c1SPaul Blakey static struct tc_action_ops act_ct_ops = { 1097b57dc7c1SPaul Blakey .kind = "ct", 1098b57dc7c1SPaul Blakey .id = TCA_ID_CT, 1099b57dc7c1SPaul Blakey .owner = THIS_MODULE, 1100b57dc7c1SPaul Blakey .act = tcf_ct_act, 1101b57dc7c1SPaul Blakey .dump = tcf_ct_dump, 1102b57dc7c1SPaul Blakey .init = tcf_ct_init, 1103b57dc7c1SPaul Blakey .cleanup = tcf_ct_cleanup, 1104b57dc7c1SPaul Blakey .walk = tcf_ct_walker, 1105b57dc7c1SPaul Blakey .lookup = tcf_ct_search, 1106b57dc7c1SPaul Blakey .stats_update = tcf_stats_update, 1107b57dc7c1SPaul Blakey .size = sizeof(struct tcf_ct), 1108b57dc7c1SPaul Blakey }; 1109b57dc7c1SPaul Blakey 1110b57dc7c1SPaul Blakey static __net_init int ct_init_net(struct net *net) 1111b57dc7c1SPaul Blakey { 1112c593642cSPankaj Bharadiya unsigned int n_bits = sizeof_field(struct tcf_ct_params, labels) * 8; 1113b57dc7c1SPaul Blakey struct tc_ct_action_net *tn = net_generic(net, ct_net_id); 1114b57dc7c1SPaul Blakey 1115b57dc7c1SPaul Blakey if (nf_connlabels_get(net, n_bits - 1)) { 1116b57dc7c1SPaul Blakey tn->labels = false; 1117b57dc7c1SPaul Blakey pr_err("act_ct: Failed to set connlabels length"); 1118b57dc7c1SPaul Blakey } else { 1119b57dc7c1SPaul Blakey tn->labels = true; 1120b57dc7c1SPaul Blakey } 1121b57dc7c1SPaul Blakey 1122981471bdSCong Wang return tc_action_net_init(net, &tn->tn, &act_ct_ops); 1123b57dc7c1SPaul Blakey } 1124b57dc7c1SPaul Blakey 1125b57dc7c1SPaul Blakey static void __net_exit ct_exit_net(struct list_head *net_list) 1126b57dc7c1SPaul Blakey { 1127b57dc7c1SPaul Blakey struct net *net; 1128b57dc7c1SPaul Blakey 1129b57dc7c1SPaul Blakey rtnl_lock(); 1130b57dc7c1SPaul Blakey list_for_each_entry(net, net_list, exit_list) { 1131b57dc7c1SPaul Blakey struct tc_ct_action_net *tn = net_generic(net, ct_net_id); 1132b57dc7c1SPaul Blakey 1133b57dc7c1SPaul Blakey if (tn->labels) 1134b57dc7c1SPaul Blakey nf_connlabels_put(net); 1135b57dc7c1SPaul Blakey } 1136b57dc7c1SPaul Blakey rtnl_unlock(); 1137b57dc7c1SPaul Blakey 1138b57dc7c1SPaul Blakey tc_action_net_exit(net_list, ct_net_id); 1139b57dc7c1SPaul Blakey } 1140b57dc7c1SPaul Blakey 1141b57dc7c1SPaul Blakey static struct pernet_operations ct_net_ops = { 1142b57dc7c1SPaul Blakey .init = ct_init_net, 1143b57dc7c1SPaul Blakey .exit_batch = ct_exit_net, 1144b57dc7c1SPaul Blakey .id = &ct_net_id, 1145b57dc7c1SPaul Blakey .size = sizeof(struct tc_ct_action_net), 1146b57dc7c1SPaul Blakey }; 1147b57dc7c1SPaul Blakey 1148b57dc7c1SPaul Blakey static int __init ct_init_module(void) 1149b57dc7c1SPaul Blakey { 1150c34b961aSPaul Blakey int err; 1151c34b961aSPaul Blakey 1152c34b961aSPaul Blakey act_ct_wq = alloc_ordered_workqueue("act_ct_workqueue", 0); 1153c34b961aSPaul Blakey if (!act_ct_wq) 1154c34b961aSPaul Blakey return -ENOMEM; 1155c34b961aSPaul Blakey 1156c34b961aSPaul Blakey err = tcf_ct_flow_tables_init(); 1157c34b961aSPaul Blakey if (err) 1158c34b961aSPaul Blakey goto err_tbl_init; 1159c34b961aSPaul Blakey 1160c34b961aSPaul Blakey err = tcf_register_action(&act_ct_ops, &ct_net_ops); 1161c34b961aSPaul Blakey if (err) 1162c34b961aSPaul Blakey goto err_register; 1163c34b961aSPaul Blakey 1164c34b961aSPaul Blakey return 0; 1165c34b961aSPaul Blakey 1166c34b961aSPaul Blakey err_tbl_init: 1167c34b961aSPaul Blakey destroy_workqueue(act_ct_wq); 1168c34b961aSPaul Blakey err_register: 1169c34b961aSPaul Blakey tcf_ct_flow_tables_uninit(); 1170c34b961aSPaul Blakey return err; 1171b57dc7c1SPaul Blakey } 1172b57dc7c1SPaul Blakey 1173b57dc7c1SPaul Blakey static void __exit ct_cleanup_module(void) 1174b57dc7c1SPaul Blakey { 1175b57dc7c1SPaul Blakey tcf_unregister_action(&act_ct_ops, &ct_net_ops); 1176c34b961aSPaul Blakey tcf_ct_flow_tables_uninit(); 1177c34b961aSPaul Blakey destroy_workqueue(act_ct_wq); 1178b57dc7c1SPaul Blakey } 1179b57dc7c1SPaul Blakey 1180b57dc7c1SPaul Blakey module_init(ct_init_module); 1181b57dc7c1SPaul Blakey module_exit(ct_cleanup_module); 1182b57dc7c1SPaul Blakey MODULE_AUTHOR("Paul Blakey <paulb@mellanox.com>"); 1183b57dc7c1SPaul Blakey MODULE_AUTHOR("Yossi Kuperman <yossiku@mellanox.com>"); 1184b57dc7c1SPaul Blakey MODULE_AUTHOR("Marcelo Ricardo Leitner <marcelo.leitner@gmail.com>"); 1185b57dc7c1SPaul Blakey MODULE_DESCRIPTION("Connection tracking action"); 1186b57dc7c1SPaul Blakey MODULE_LICENSE("GPL v2"); 1187b57dc7c1SPaul Blakey 1188