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