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) 33295219afbSAaron Conole int err; 333b57dc7c1SPaul Blakey enum nf_nat_manip_type maniptype; 334b57dc7c1SPaul Blakey 335b57dc7c1SPaul Blakey if (!(ct_action & TCA_CT_ACT_NAT)) 336b57dc7c1SPaul Blakey return NF_ACCEPT; 337b57dc7c1SPaul Blakey 338b57dc7c1SPaul Blakey /* Add NAT extension if not confirmed yet. */ 339b57dc7c1SPaul Blakey if (!nf_ct_is_confirmed(ct) && !nf_ct_nat_ext_add(ct)) 340b57dc7c1SPaul Blakey return NF_DROP; /* Can't NAT. */ 341b57dc7c1SPaul Blakey 342b57dc7c1SPaul Blakey if (ctinfo != IP_CT_NEW && (ct->status & IPS_NAT_MASK) && 343b57dc7c1SPaul Blakey (ctinfo != IP_CT_RELATED || commit)) { 344b57dc7c1SPaul Blakey /* NAT an established or related connection like before. */ 345b57dc7c1SPaul Blakey if (CTINFO2DIR(ctinfo) == IP_CT_DIR_REPLY) 346b57dc7c1SPaul Blakey /* This is the REPLY direction for a connection 347b57dc7c1SPaul Blakey * for which NAT was applied in the forward 348b57dc7c1SPaul Blakey * direction. Do the reverse NAT. 349b57dc7c1SPaul Blakey */ 350b57dc7c1SPaul Blakey maniptype = ct->status & IPS_SRC_NAT 351b57dc7c1SPaul Blakey ? NF_NAT_MANIP_DST : NF_NAT_MANIP_SRC; 352b57dc7c1SPaul Blakey else 353b57dc7c1SPaul Blakey maniptype = ct->status & IPS_SRC_NAT 354b57dc7c1SPaul Blakey ? NF_NAT_MANIP_SRC : NF_NAT_MANIP_DST; 355b57dc7c1SPaul Blakey } else if (ct_action & TCA_CT_ACT_NAT_SRC) { 356b57dc7c1SPaul Blakey maniptype = NF_NAT_MANIP_SRC; 357b57dc7c1SPaul Blakey } else if (ct_action & TCA_CT_ACT_NAT_DST) { 358b57dc7c1SPaul Blakey maniptype = NF_NAT_MANIP_DST; 359b57dc7c1SPaul Blakey } else { 360b57dc7c1SPaul Blakey return NF_ACCEPT; 361b57dc7c1SPaul Blakey } 362b57dc7c1SPaul Blakey 36395219afbSAaron Conole err = ct_nat_execute(skb, ct, ctinfo, range, maniptype); 36495219afbSAaron Conole if (err == NF_ACCEPT && 36595219afbSAaron Conole ct->status & IPS_SRC_NAT && ct->status & IPS_DST_NAT) { 36695219afbSAaron Conole if (maniptype == NF_NAT_MANIP_SRC) 36795219afbSAaron Conole maniptype = NF_NAT_MANIP_DST; 36895219afbSAaron Conole else 36995219afbSAaron Conole maniptype = NF_NAT_MANIP_SRC; 37095219afbSAaron Conole 37195219afbSAaron Conole err = ct_nat_execute(skb, ct, ctinfo, range, maniptype); 37295219afbSAaron Conole } 37395219afbSAaron Conole return err; 374b57dc7c1SPaul Blakey #else 375b57dc7c1SPaul Blakey return NF_ACCEPT; 376b57dc7c1SPaul Blakey #endif 377b57dc7c1SPaul Blakey } 378b57dc7c1SPaul Blakey 379b57dc7c1SPaul Blakey static int tcf_ct_act(struct sk_buff *skb, const struct tc_action *a, 380b57dc7c1SPaul Blakey struct tcf_result *res) 381b57dc7c1SPaul Blakey { 382b57dc7c1SPaul Blakey struct net *net = dev_net(skb->dev); 383b57dc7c1SPaul Blakey bool cached, commit, clear, force; 384b57dc7c1SPaul Blakey enum ip_conntrack_info ctinfo; 385b57dc7c1SPaul Blakey struct tcf_ct *c = to_ct(a); 386b57dc7c1SPaul Blakey struct nf_conn *tmpl = NULL; 387b57dc7c1SPaul Blakey struct nf_hook_state state; 388b57dc7c1SPaul Blakey int nh_ofs, err, retval; 389b57dc7c1SPaul Blakey struct tcf_ct_params *p; 390b57dc7c1SPaul Blakey struct nf_conn *ct; 391b57dc7c1SPaul Blakey u8 family; 392b57dc7c1SPaul Blakey 393b57dc7c1SPaul Blakey p = rcu_dereference_bh(c->params); 394b57dc7c1SPaul Blakey 395b57dc7c1SPaul Blakey retval = READ_ONCE(c->tcf_action); 396b57dc7c1SPaul Blakey commit = p->ct_action & TCA_CT_ACT_COMMIT; 397b57dc7c1SPaul Blakey clear = p->ct_action & TCA_CT_ACT_CLEAR; 398b57dc7c1SPaul Blakey force = p->ct_action & TCA_CT_ACT_FORCE; 399b57dc7c1SPaul Blakey tmpl = p->tmpl; 400b57dc7c1SPaul Blakey 401b57dc7c1SPaul Blakey if (clear) { 402b57dc7c1SPaul Blakey ct = nf_ct_get(skb, &ctinfo); 403b57dc7c1SPaul Blakey if (ct) { 404b57dc7c1SPaul Blakey nf_conntrack_put(&ct->ct_general); 405b57dc7c1SPaul Blakey nf_ct_set(skb, NULL, IP_CT_UNTRACKED); 406b57dc7c1SPaul Blakey } 407b57dc7c1SPaul Blakey 408b57dc7c1SPaul Blakey goto out; 409b57dc7c1SPaul Blakey } 410b57dc7c1SPaul Blakey 411b57dc7c1SPaul Blakey family = tcf_ct_skb_nf_family(skb); 412b57dc7c1SPaul Blakey if (family == NFPROTO_UNSPEC) 413b57dc7c1SPaul Blakey goto drop; 414b57dc7c1SPaul Blakey 415b57dc7c1SPaul Blakey /* The conntrack module expects to be working at L3. 416b57dc7c1SPaul Blakey * We also try to pull the IPv4/6 header to linear area 417b57dc7c1SPaul Blakey */ 418b57dc7c1SPaul Blakey nh_ofs = skb_network_offset(skb); 419b57dc7c1SPaul Blakey skb_pull_rcsum(skb, nh_ofs); 420b57dc7c1SPaul Blakey err = tcf_ct_handle_fragments(net, skb, family, p->zone); 421b57dc7c1SPaul Blakey if (err == -EINPROGRESS) { 422b57dc7c1SPaul Blakey retval = TC_ACT_STOLEN; 423b57dc7c1SPaul Blakey goto out; 424b57dc7c1SPaul Blakey } 425b57dc7c1SPaul Blakey if (err) 426b57dc7c1SPaul Blakey goto drop; 427b57dc7c1SPaul Blakey 428b57dc7c1SPaul Blakey err = tcf_ct_skb_network_trim(skb, family); 429b57dc7c1SPaul Blakey if (err) 430b57dc7c1SPaul Blakey goto drop; 431b57dc7c1SPaul Blakey 432b57dc7c1SPaul Blakey /* If we are recirculating packets to match on ct fields and 433b57dc7c1SPaul Blakey * committing with a separate ct action, then we don't need to 434b57dc7c1SPaul Blakey * actually run the packet through conntrack twice unless it's for a 435b57dc7c1SPaul Blakey * different zone. 436b57dc7c1SPaul Blakey */ 437b57dc7c1SPaul Blakey cached = tcf_ct_skb_nfct_cached(net, skb, p->zone, force); 438b57dc7c1SPaul Blakey if (!cached) { 439b57dc7c1SPaul Blakey /* Associate skb with specified zone. */ 440b57dc7c1SPaul Blakey if (tmpl) { 441b57dc7c1SPaul Blakey ct = nf_ct_get(skb, &ctinfo); 442b57dc7c1SPaul Blakey if (skb_nfct(skb)) 443b57dc7c1SPaul Blakey nf_conntrack_put(skb_nfct(skb)); 444b57dc7c1SPaul Blakey nf_conntrack_get(&tmpl->ct_general); 445b57dc7c1SPaul Blakey nf_ct_set(skb, tmpl, IP_CT_NEW); 446b57dc7c1SPaul Blakey } 447b57dc7c1SPaul Blakey 448b57dc7c1SPaul Blakey state.hook = NF_INET_PRE_ROUTING; 449b57dc7c1SPaul Blakey state.net = net; 450b57dc7c1SPaul Blakey state.pf = family; 451b57dc7c1SPaul Blakey err = nf_conntrack_in(skb, &state); 452b57dc7c1SPaul Blakey if (err != NF_ACCEPT) 453b57dc7c1SPaul Blakey goto out_push; 454b57dc7c1SPaul Blakey } 455b57dc7c1SPaul Blakey 456b57dc7c1SPaul Blakey ct = nf_ct_get(skb, &ctinfo); 457b57dc7c1SPaul Blakey if (!ct) 458b57dc7c1SPaul Blakey goto out_push; 459b57dc7c1SPaul Blakey nf_ct_deliver_cached_events(ct); 460b57dc7c1SPaul Blakey 461b57dc7c1SPaul Blakey err = tcf_ct_act_nat(skb, ct, ctinfo, p->ct_action, &p->range, commit); 462b57dc7c1SPaul Blakey if (err != NF_ACCEPT) 463b57dc7c1SPaul Blakey goto drop; 464b57dc7c1SPaul Blakey 465b57dc7c1SPaul Blakey if (commit) { 466b57dc7c1SPaul Blakey tcf_ct_act_set_mark(ct, p->mark, p->mark_mask); 467b57dc7c1SPaul Blakey tcf_ct_act_set_labels(ct, p->labels, p->labels_mask); 468b57dc7c1SPaul Blakey 469b57dc7c1SPaul Blakey /* This will take care of sending queued events 470b57dc7c1SPaul Blakey * even if the connection is already confirmed. 471b57dc7c1SPaul Blakey */ 472b57dc7c1SPaul Blakey nf_conntrack_confirm(skb); 473b57dc7c1SPaul Blakey } 474b57dc7c1SPaul Blakey 475b57dc7c1SPaul Blakey out_push: 476b57dc7c1SPaul Blakey skb_push_rcsum(skb, nh_ofs); 477b57dc7c1SPaul Blakey 478b57dc7c1SPaul Blakey out: 4795e1ad95bSVlad Buslov tcf_action_update_bstats(&c->common, skb); 480b57dc7c1SPaul Blakey return retval; 481b57dc7c1SPaul Blakey 482b57dc7c1SPaul Blakey drop: 48326b537a8SVlad Buslov tcf_action_inc_drop_qstats(&c->common); 484b57dc7c1SPaul Blakey return TC_ACT_SHOT; 485b57dc7c1SPaul Blakey } 486b57dc7c1SPaul Blakey 487b57dc7c1SPaul Blakey static const struct nla_policy ct_policy[TCA_CT_MAX + 1] = { 488b57dc7c1SPaul Blakey [TCA_CT_ACTION] = { .type = NLA_U16 }, 489b57dc7c1SPaul Blakey [TCA_CT_PARMS] = { .type = NLA_EXACT_LEN, .len = sizeof(struct tc_ct) }, 490b57dc7c1SPaul Blakey [TCA_CT_ZONE] = { .type = NLA_U16 }, 491b57dc7c1SPaul Blakey [TCA_CT_MARK] = { .type = NLA_U32 }, 492b57dc7c1SPaul Blakey [TCA_CT_MARK_MASK] = { .type = NLA_U32 }, 493b57dc7c1SPaul Blakey [TCA_CT_LABELS] = { .type = NLA_BINARY, 494b57dc7c1SPaul Blakey .len = 128 / BITS_PER_BYTE }, 495b57dc7c1SPaul Blakey [TCA_CT_LABELS_MASK] = { .type = NLA_BINARY, 496b57dc7c1SPaul Blakey .len = 128 / BITS_PER_BYTE }, 497b57dc7c1SPaul Blakey [TCA_CT_NAT_IPV4_MIN] = { .type = NLA_U32 }, 498b57dc7c1SPaul Blakey [TCA_CT_NAT_IPV4_MAX] = { .type = NLA_U32 }, 499b57dc7c1SPaul Blakey [TCA_CT_NAT_IPV6_MIN] = { .type = NLA_EXACT_LEN, 500b57dc7c1SPaul Blakey .len = sizeof(struct in6_addr) }, 501b57dc7c1SPaul Blakey [TCA_CT_NAT_IPV6_MAX] = { .type = NLA_EXACT_LEN, 502b57dc7c1SPaul Blakey .len = sizeof(struct in6_addr) }, 503b57dc7c1SPaul Blakey [TCA_CT_NAT_PORT_MIN] = { .type = NLA_U16 }, 504b57dc7c1SPaul Blakey [TCA_CT_NAT_PORT_MAX] = { .type = NLA_U16 }, 505b57dc7c1SPaul Blakey }; 506b57dc7c1SPaul Blakey 507b57dc7c1SPaul Blakey static int tcf_ct_fill_params_nat(struct tcf_ct_params *p, 508b57dc7c1SPaul Blakey struct tc_ct *parm, 509b57dc7c1SPaul Blakey struct nlattr **tb, 510b57dc7c1SPaul Blakey struct netlink_ext_ack *extack) 511b57dc7c1SPaul Blakey { 512b57dc7c1SPaul Blakey struct nf_nat_range2 *range; 513b57dc7c1SPaul Blakey 514b57dc7c1SPaul Blakey if (!(p->ct_action & TCA_CT_ACT_NAT)) 515b57dc7c1SPaul Blakey return 0; 516b57dc7c1SPaul Blakey 517b57dc7c1SPaul Blakey if (!IS_ENABLED(CONFIG_NF_NAT)) { 518b57dc7c1SPaul Blakey NL_SET_ERR_MSG_MOD(extack, "Netfilter nat isn't enabled in kernel"); 519b57dc7c1SPaul Blakey return -EOPNOTSUPP; 520b57dc7c1SPaul Blakey } 521b57dc7c1SPaul Blakey 522b57dc7c1SPaul Blakey if (!(p->ct_action & (TCA_CT_ACT_NAT_SRC | TCA_CT_ACT_NAT_DST))) 523b57dc7c1SPaul Blakey return 0; 524b57dc7c1SPaul Blakey 525b57dc7c1SPaul Blakey if ((p->ct_action & TCA_CT_ACT_NAT_SRC) && 526b57dc7c1SPaul Blakey (p->ct_action & TCA_CT_ACT_NAT_DST)) { 527b57dc7c1SPaul Blakey NL_SET_ERR_MSG_MOD(extack, "dnat and snat can't be enabled at the same time"); 528b57dc7c1SPaul Blakey return -EOPNOTSUPP; 529b57dc7c1SPaul Blakey } 530b57dc7c1SPaul Blakey 531b57dc7c1SPaul Blakey range = &p->range; 532b57dc7c1SPaul Blakey if (tb[TCA_CT_NAT_IPV4_MIN]) { 533b57dc7c1SPaul Blakey struct nlattr *max_attr = tb[TCA_CT_NAT_IPV4_MAX]; 534b57dc7c1SPaul Blakey 535b57dc7c1SPaul Blakey p->ipv4_range = true; 536b57dc7c1SPaul Blakey range->flags |= NF_NAT_RANGE_MAP_IPS; 537b57dc7c1SPaul Blakey range->min_addr.ip = 538b57dc7c1SPaul Blakey nla_get_in_addr(tb[TCA_CT_NAT_IPV4_MIN]); 539b57dc7c1SPaul Blakey 540b57dc7c1SPaul Blakey range->max_addr.ip = max_attr ? 541b57dc7c1SPaul Blakey nla_get_in_addr(max_attr) : 542b57dc7c1SPaul Blakey range->min_addr.ip; 543b57dc7c1SPaul Blakey } else if (tb[TCA_CT_NAT_IPV6_MIN]) { 544b57dc7c1SPaul Blakey struct nlattr *max_attr = tb[TCA_CT_NAT_IPV6_MAX]; 545b57dc7c1SPaul Blakey 546b57dc7c1SPaul Blakey p->ipv4_range = false; 547b57dc7c1SPaul Blakey range->flags |= NF_NAT_RANGE_MAP_IPS; 548b57dc7c1SPaul Blakey range->min_addr.in6 = 549b57dc7c1SPaul Blakey nla_get_in6_addr(tb[TCA_CT_NAT_IPV6_MIN]); 550b57dc7c1SPaul Blakey 551b57dc7c1SPaul Blakey range->max_addr.in6 = max_attr ? 552b57dc7c1SPaul Blakey nla_get_in6_addr(max_attr) : 553b57dc7c1SPaul Blakey range->min_addr.in6; 554b57dc7c1SPaul Blakey } 555b57dc7c1SPaul Blakey 556b57dc7c1SPaul Blakey if (tb[TCA_CT_NAT_PORT_MIN]) { 557b57dc7c1SPaul Blakey range->flags |= NF_NAT_RANGE_PROTO_SPECIFIED; 558b57dc7c1SPaul Blakey range->min_proto.all = nla_get_be16(tb[TCA_CT_NAT_PORT_MIN]); 559b57dc7c1SPaul Blakey 560b57dc7c1SPaul Blakey range->max_proto.all = tb[TCA_CT_NAT_PORT_MAX] ? 561b57dc7c1SPaul Blakey nla_get_be16(tb[TCA_CT_NAT_PORT_MAX]) : 562b57dc7c1SPaul Blakey range->min_proto.all; 563b57dc7c1SPaul Blakey } 564b57dc7c1SPaul Blakey 565b57dc7c1SPaul Blakey return 0; 566b57dc7c1SPaul Blakey } 567b57dc7c1SPaul Blakey 568b57dc7c1SPaul Blakey static void tcf_ct_set_key_val(struct nlattr **tb, 569b57dc7c1SPaul Blakey void *val, int val_type, 570b57dc7c1SPaul Blakey void *mask, int mask_type, 571b57dc7c1SPaul Blakey int len) 572b57dc7c1SPaul Blakey { 573b57dc7c1SPaul Blakey if (!tb[val_type]) 574b57dc7c1SPaul Blakey return; 575b57dc7c1SPaul Blakey nla_memcpy(val, tb[val_type], len); 576b57dc7c1SPaul Blakey 577b57dc7c1SPaul Blakey if (!mask) 578b57dc7c1SPaul Blakey return; 579b57dc7c1SPaul Blakey 580b57dc7c1SPaul Blakey if (mask_type == TCA_CT_UNSPEC || !tb[mask_type]) 581b57dc7c1SPaul Blakey memset(mask, 0xff, len); 582b57dc7c1SPaul Blakey else 583b57dc7c1SPaul Blakey nla_memcpy(mask, tb[mask_type], len); 584b57dc7c1SPaul Blakey } 585b57dc7c1SPaul Blakey 586b57dc7c1SPaul Blakey static int tcf_ct_fill_params(struct net *net, 587b57dc7c1SPaul Blakey struct tcf_ct_params *p, 588b57dc7c1SPaul Blakey struct tc_ct *parm, 589b57dc7c1SPaul Blakey struct nlattr **tb, 590b57dc7c1SPaul Blakey struct netlink_ext_ack *extack) 591b57dc7c1SPaul Blakey { 592b57dc7c1SPaul Blakey struct tc_ct_action_net *tn = net_generic(net, ct_net_id); 593b57dc7c1SPaul Blakey struct nf_conntrack_zone zone; 594b57dc7c1SPaul Blakey struct nf_conn *tmpl; 595b57dc7c1SPaul Blakey int err; 596b57dc7c1SPaul Blakey 597b57dc7c1SPaul Blakey p->zone = NF_CT_DEFAULT_ZONE_ID; 598b57dc7c1SPaul Blakey 599b57dc7c1SPaul Blakey tcf_ct_set_key_val(tb, 600b57dc7c1SPaul Blakey &p->ct_action, TCA_CT_ACTION, 601b57dc7c1SPaul Blakey NULL, TCA_CT_UNSPEC, 602b57dc7c1SPaul Blakey sizeof(p->ct_action)); 603b57dc7c1SPaul Blakey 604b57dc7c1SPaul Blakey if (p->ct_action & TCA_CT_ACT_CLEAR) 605b57dc7c1SPaul Blakey return 0; 606b57dc7c1SPaul Blakey 607b57dc7c1SPaul Blakey err = tcf_ct_fill_params_nat(p, parm, tb, extack); 608b57dc7c1SPaul Blakey if (err) 609b57dc7c1SPaul Blakey return err; 610b57dc7c1SPaul Blakey 611b57dc7c1SPaul Blakey if (tb[TCA_CT_MARK]) { 612b57dc7c1SPaul Blakey if (!IS_ENABLED(CONFIG_NF_CONNTRACK_MARK)) { 613b57dc7c1SPaul Blakey NL_SET_ERR_MSG_MOD(extack, "Conntrack mark isn't enabled."); 614b57dc7c1SPaul Blakey return -EOPNOTSUPP; 615b57dc7c1SPaul Blakey } 616b57dc7c1SPaul Blakey tcf_ct_set_key_val(tb, 617b57dc7c1SPaul Blakey &p->mark, TCA_CT_MARK, 618b57dc7c1SPaul Blakey &p->mark_mask, TCA_CT_MARK_MASK, 619b57dc7c1SPaul Blakey sizeof(p->mark)); 620b57dc7c1SPaul Blakey } 621b57dc7c1SPaul Blakey 622b57dc7c1SPaul Blakey if (tb[TCA_CT_LABELS]) { 623b57dc7c1SPaul Blakey if (!IS_ENABLED(CONFIG_NF_CONNTRACK_LABELS)) { 624b57dc7c1SPaul Blakey NL_SET_ERR_MSG_MOD(extack, "Conntrack labels isn't enabled."); 625b57dc7c1SPaul Blakey return -EOPNOTSUPP; 626b57dc7c1SPaul Blakey } 627b57dc7c1SPaul Blakey 628b57dc7c1SPaul Blakey if (!tn->labels) { 629b57dc7c1SPaul Blakey NL_SET_ERR_MSG_MOD(extack, "Failed to set connlabel length"); 630b57dc7c1SPaul Blakey return -EOPNOTSUPP; 631b57dc7c1SPaul Blakey } 632b57dc7c1SPaul Blakey tcf_ct_set_key_val(tb, 633b57dc7c1SPaul Blakey p->labels, TCA_CT_LABELS, 634b57dc7c1SPaul Blakey p->labels_mask, TCA_CT_LABELS_MASK, 635b57dc7c1SPaul Blakey sizeof(p->labels)); 636b57dc7c1SPaul Blakey } 637b57dc7c1SPaul Blakey 638b57dc7c1SPaul Blakey if (tb[TCA_CT_ZONE]) { 639b57dc7c1SPaul Blakey if (!IS_ENABLED(CONFIG_NF_CONNTRACK_ZONES)) { 640b57dc7c1SPaul Blakey NL_SET_ERR_MSG_MOD(extack, "Conntrack zones isn't enabled."); 641b57dc7c1SPaul Blakey return -EOPNOTSUPP; 642b57dc7c1SPaul Blakey } 643b57dc7c1SPaul Blakey 644b57dc7c1SPaul Blakey tcf_ct_set_key_val(tb, 645b57dc7c1SPaul Blakey &p->zone, TCA_CT_ZONE, 646b57dc7c1SPaul Blakey NULL, TCA_CT_UNSPEC, 647b57dc7c1SPaul Blakey sizeof(p->zone)); 648b57dc7c1SPaul Blakey } 649b57dc7c1SPaul Blakey 650b57dc7c1SPaul Blakey if (p->zone == NF_CT_DEFAULT_ZONE_ID) 651b57dc7c1SPaul Blakey return 0; 652b57dc7c1SPaul Blakey 653b57dc7c1SPaul Blakey nf_ct_zone_init(&zone, p->zone, NF_CT_DEFAULT_ZONE_DIR, 0); 654b57dc7c1SPaul Blakey tmpl = nf_ct_tmpl_alloc(net, &zone, GFP_KERNEL); 655b57dc7c1SPaul Blakey if (!tmpl) { 656b57dc7c1SPaul Blakey NL_SET_ERR_MSG_MOD(extack, "Failed to allocate conntrack template"); 657b57dc7c1SPaul Blakey return -ENOMEM; 658b57dc7c1SPaul Blakey } 659b57dc7c1SPaul Blakey __set_bit(IPS_CONFIRMED_BIT, &tmpl->status); 660b57dc7c1SPaul Blakey nf_conntrack_get(&tmpl->ct_general); 661b57dc7c1SPaul Blakey p->tmpl = tmpl; 662b57dc7c1SPaul Blakey 663b57dc7c1SPaul Blakey return 0; 664b57dc7c1SPaul Blakey } 665b57dc7c1SPaul Blakey 666b57dc7c1SPaul Blakey static int tcf_ct_init(struct net *net, struct nlattr *nla, 667b57dc7c1SPaul Blakey struct nlattr *est, struct tc_action **a, 668b57dc7c1SPaul Blakey int replace, int bind, bool rtnl_held, 669abbb0d33SVlad Buslov struct tcf_proto *tp, u32 flags, 670b57dc7c1SPaul Blakey struct netlink_ext_ack *extack) 671b57dc7c1SPaul Blakey { 672b57dc7c1SPaul Blakey struct tc_action_net *tn = net_generic(net, ct_net_id); 673b57dc7c1SPaul Blakey struct tcf_ct_params *params = NULL; 674b57dc7c1SPaul Blakey struct nlattr *tb[TCA_CT_MAX + 1]; 675b57dc7c1SPaul Blakey struct tcf_chain *goto_ch = NULL; 676b57dc7c1SPaul Blakey struct tc_ct *parm; 677b57dc7c1SPaul Blakey struct tcf_ct *c; 678b57dc7c1SPaul Blakey int err, res = 0; 6797be8ef2cSDmytro Linkin u32 index; 680b57dc7c1SPaul Blakey 681b57dc7c1SPaul Blakey if (!nla) { 682b57dc7c1SPaul Blakey NL_SET_ERR_MSG_MOD(extack, "Ct requires attributes to be passed"); 683b57dc7c1SPaul Blakey return -EINVAL; 684b57dc7c1SPaul Blakey } 685b57dc7c1SPaul Blakey 686b57dc7c1SPaul Blakey err = nla_parse_nested(tb, TCA_CT_MAX, nla, ct_policy, extack); 687b57dc7c1SPaul Blakey if (err < 0) 688b57dc7c1SPaul Blakey return err; 689b57dc7c1SPaul Blakey 690b57dc7c1SPaul Blakey if (!tb[TCA_CT_PARMS]) { 691b57dc7c1SPaul Blakey NL_SET_ERR_MSG_MOD(extack, "Missing required ct parameters"); 692b57dc7c1SPaul Blakey return -EINVAL; 693b57dc7c1SPaul Blakey } 694b57dc7c1SPaul Blakey parm = nla_data(tb[TCA_CT_PARMS]); 6957be8ef2cSDmytro Linkin index = parm->index; 6967be8ef2cSDmytro Linkin err = tcf_idr_check_alloc(tn, &index, a, bind); 697b57dc7c1SPaul Blakey if (err < 0) 698b57dc7c1SPaul Blakey return err; 699b57dc7c1SPaul Blakey 700b57dc7c1SPaul Blakey if (!err) { 701e3822678SVlad Buslov err = tcf_idr_create_from_flags(tn, index, est, a, 702e3822678SVlad Buslov &act_ct_ops, bind, flags); 703b57dc7c1SPaul Blakey if (err) { 7047be8ef2cSDmytro Linkin tcf_idr_cleanup(tn, index); 705b57dc7c1SPaul Blakey return err; 706b57dc7c1SPaul Blakey } 707b57dc7c1SPaul Blakey res = ACT_P_CREATED; 708b57dc7c1SPaul Blakey } else { 709b57dc7c1SPaul Blakey if (bind) 710b57dc7c1SPaul Blakey return 0; 711b57dc7c1SPaul Blakey 712b57dc7c1SPaul Blakey if (!replace) { 713b57dc7c1SPaul Blakey tcf_idr_release(*a, bind); 714b57dc7c1SPaul Blakey return -EEXIST; 715b57dc7c1SPaul Blakey } 716b57dc7c1SPaul Blakey } 717b57dc7c1SPaul Blakey err = tcf_action_check_ctrlact(parm->action, tp, &goto_ch, extack); 718b57dc7c1SPaul Blakey if (err < 0) 719b57dc7c1SPaul Blakey goto cleanup; 720b57dc7c1SPaul Blakey 721b57dc7c1SPaul Blakey c = to_ct(*a); 722b57dc7c1SPaul Blakey 723b57dc7c1SPaul Blakey params = kzalloc(sizeof(*params), GFP_KERNEL); 724b57dc7c1SPaul Blakey if (unlikely(!params)) { 725b57dc7c1SPaul Blakey err = -ENOMEM; 726b57dc7c1SPaul Blakey goto cleanup; 727b57dc7c1SPaul Blakey } 728b57dc7c1SPaul Blakey 729b57dc7c1SPaul Blakey err = tcf_ct_fill_params(net, params, parm, tb, extack); 730b57dc7c1SPaul Blakey if (err) 731b57dc7c1SPaul Blakey goto cleanup; 732b57dc7c1SPaul Blakey 733b57dc7c1SPaul Blakey spin_lock_bh(&c->tcf_lock); 734b57dc7c1SPaul Blakey goto_ch = tcf_action_set_ctrlact(*a, parm->action, goto_ch); 735445d3749SPaul E. McKenney params = rcu_replace_pointer(c->params, params, 736445d3749SPaul E. McKenney lockdep_is_held(&c->tcf_lock)); 737b57dc7c1SPaul Blakey spin_unlock_bh(&c->tcf_lock); 738b57dc7c1SPaul Blakey 739b57dc7c1SPaul Blakey if (goto_ch) 740b57dc7c1SPaul Blakey tcf_chain_put_by_act(goto_ch); 741b57dc7c1SPaul Blakey if (params) 742b57dc7c1SPaul Blakey kfree_rcu(params, rcu); 743b57dc7c1SPaul Blakey if (res == ACT_P_CREATED) 744b57dc7c1SPaul Blakey tcf_idr_insert(tn, *a); 745b57dc7c1SPaul Blakey 746b57dc7c1SPaul Blakey return res; 747b57dc7c1SPaul Blakey 748b57dc7c1SPaul Blakey cleanup: 749b57dc7c1SPaul Blakey if (goto_ch) 750b57dc7c1SPaul Blakey tcf_chain_put_by_act(goto_ch); 751b57dc7c1SPaul Blakey kfree(params); 752b57dc7c1SPaul Blakey tcf_idr_release(*a, bind); 753b57dc7c1SPaul Blakey return err; 754b57dc7c1SPaul Blakey } 755b57dc7c1SPaul Blakey 756b57dc7c1SPaul Blakey static void tcf_ct_cleanup(struct tc_action *a) 757b57dc7c1SPaul Blakey { 758b57dc7c1SPaul Blakey struct tcf_ct_params *params; 759b57dc7c1SPaul Blakey struct tcf_ct *c = to_ct(a); 760b57dc7c1SPaul Blakey 761b57dc7c1SPaul Blakey params = rcu_dereference_protected(c->params, 1); 762b57dc7c1SPaul Blakey if (params) 763b57dc7c1SPaul Blakey call_rcu(¶ms->rcu, tcf_ct_params_free); 764b57dc7c1SPaul Blakey } 765b57dc7c1SPaul Blakey 766b57dc7c1SPaul Blakey static int tcf_ct_dump_key_val(struct sk_buff *skb, 767b57dc7c1SPaul Blakey void *val, int val_type, 768b57dc7c1SPaul Blakey void *mask, int mask_type, 769b57dc7c1SPaul Blakey int len) 770b57dc7c1SPaul Blakey { 771b57dc7c1SPaul Blakey int err; 772b57dc7c1SPaul Blakey 773b57dc7c1SPaul Blakey if (mask && !memchr_inv(mask, 0, len)) 774b57dc7c1SPaul Blakey return 0; 775b57dc7c1SPaul Blakey 776b57dc7c1SPaul Blakey err = nla_put(skb, val_type, len, val); 777b57dc7c1SPaul Blakey if (err) 778b57dc7c1SPaul Blakey return err; 779b57dc7c1SPaul Blakey 780b57dc7c1SPaul Blakey if (mask_type != TCA_CT_UNSPEC) { 781b57dc7c1SPaul Blakey err = nla_put(skb, mask_type, len, mask); 782b57dc7c1SPaul Blakey if (err) 783b57dc7c1SPaul Blakey return err; 784b57dc7c1SPaul Blakey } 785b57dc7c1SPaul Blakey 786b57dc7c1SPaul Blakey return 0; 787b57dc7c1SPaul Blakey } 788b57dc7c1SPaul Blakey 789b57dc7c1SPaul Blakey static int tcf_ct_dump_nat(struct sk_buff *skb, struct tcf_ct_params *p) 790b57dc7c1SPaul Blakey { 791b57dc7c1SPaul Blakey struct nf_nat_range2 *range = &p->range; 792b57dc7c1SPaul Blakey 793b57dc7c1SPaul Blakey if (!(p->ct_action & TCA_CT_ACT_NAT)) 794b57dc7c1SPaul Blakey return 0; 795b57dc7c1SPaul Blakey 796b57dc7c1SPaul Blakey if (!(p->ct_action & (TCA_CT_ACT_NAT_SRC | TCA_CT_ACT_NAT_DST))) 797b57dc7c1SPaul Blakey return 0; 798b57dc7c1SPaul Blakey 799b57dc7c1SPaul Blakey if (range->flags & NF_NAT_RANGE_MAP_IPS) { 800b57dc7c1SPaul Blakey if (p->ipv4_range) { 801b57dc7c1SPaul Blakey if (nla_put_in_addr(skb, TCA_CT_NAT_IPV4_MIN, 802b57dc7c1SPaul Blakey range->min_addr.ip)) 803b57dc7c1SPaul Blakey return -1; 804b57dc7c1SPaul Blakey if (nla_put_in_addr(skb, TCA_CT_NAT_IPV4_MAX, 805b57dc7c1SPaul Blakey range->max_addr.ip)) 806b57dc7c1SPaul Blakey return -1; 807b57dc7c1SPaul Blakey } else { 808b57dc7c1SPaul Blakey if (nla_put_in6_addr(skb, TCA_CT_NAT_IPV6_MIN, 809b57dc7c1SPaul Blakey &range->min_addr.in6)) 810b57dc7c1SPaul Blakey return -1; 811b57dc7c1SPaul Blakey if (nla_put_in6_addr(skb, TCA_CT_NAT_IPV6_MAX, 812b57dc7c1SPaul Blakey &range->max_addr.in6)) 813b57dc7c1SPaul Blakey return -1; 814b57dc7c1SPaul Blakey } 815b57dc7c1SPaul Blakey } 816b57dc7c1SPaul Blakey 817b57dc7c1SPaul Blakey if (range->flags & NF_NAT_RANGE_PROTO_SPECIFIED) { 818b57dc7c1SPaul Blakey if (nla_put_be16(skb, TCA_CT_NAT_PORT_MIN, 819b57dc7c1SPaul Blakey range->min_proto.all)) 820b57dc7c1SPaul Blakey return -1; 821b57dc7c1SPaul Blakey if (nla_put_be16(skb, TCA_CT_NAT_PORT_MAX, 822b57dc7c1SPaul Blakey range->max_proto.all)) 823b57dc7c1SPaul Blakey return -1; 824b57dc7c1SPaul Blakey } 825b57dc7c1SPaul Blakey 826b57dc7c1SPaul Blakey return 0; 827b57dc7c1SPaul Blakey } 828b57dc7c1SPaul Blakey 829b57dc7c1SPaul Blakey static inline int tcf_ct_dump(struct sk_buff *skb, struct tc_action *a, 830b57dc7c1SPaul Blakey int bind, int ref) 831b57dc7c1SPaul Blakey { 832b57dc7c1SPaul Blakey unsigned char *b = skb_tail_pointer(skb); 833b57dc7c1SPaul Blakey struct tcf_ct *c = to_ct(a); 834b57dc7c1SPaul Blakey struct tcf_ct_params *p; 835b57dc7c1SPaul Blakey 836b57dc7c1SPaul Blakey struct tc_ct opt = { 837b57dc7c1SPaul Blakey .index = c->tcf_index, 838b57dc7c1SPaul Blakey .refcnt = refcount_read(&c->tcf_refcnt) - ref, 839b57dc7c1SPaul Blakey .bindcnt = atomic_read(&c->tcf_bindcnt) - bind, 840b57dc7c1SPaul Blakey }; 841b57dc7c1SPaul Blakey struct tcf_t t; 842b57dc7c1SPaul Blakey 843b57dc7c1SPaul Blakey spin_lock_bh(&c->tcf_lock); 844b57dc7c1SPaul Blakey p = rcu_dereference_protected(c->params, 845b57dc7c1SPaul Blakey lockdep_is_held(&c->tcf_lock)); 846b57dc7c1SPaul Blakey opt.action = c->tcf_action; 847b57dc7c1SPaul Blakey 848b57dc7c1SPaul Blakey if (tcf_ct_dump_key_val(skb, 849b57dc7c1SPaul Blakey &p->ct_action, TCA_CT_ACTION, 850b57dc7c1SPaul Blakey NULL, TCA_CT_UNSPEC, 851b57dc7c1SPaul Blakey sizeof(p->ct_action))) 852b57dc7c1SPaul Blakey goto nla_put_failure; 853b57dc7c1SPaul Blakey 854b57dc7c1SPaul Blakey if (p->ct_action & TCA_CT_ACT_CLEAR) 855b57dc7c1SPaul Blakey goto skip_dump; 856b57dc7c1SPaul Blakey 857b57dc7c1SPaul Blakey if (IS_ENABLED(CONFIG_NF_CONNTRACK_MARK) && 858b57dc7c1SPaul Blakey tcf_ct_dump_key_val(skb, 859b57dc7c1SPaul Blakey &p->mark, TCA_CT_MARK, 860b57dc7c1SPaul Blakey &p->mark_mask, TCA_CT_MARK_MASK, 861b57dc7c1SPaul Blakey sizeof(p->mark))) 862b57dc7c1SPaul Blakey goto nla_put_failure; 863b57dc7c1SPaul Blakey 864b57dc7c1SPaul Blakey if (IS_ENABLED(CONFIG_NF_CONNTRACK_LABELS) && 865b57dc7c1SPaul Blakey tcf_ct_dump_key_val(skb, 866b57dc7c1SPaul Blakey p->labels, TCA_CT_LABELS, 867b57dc7c1SPaul Blakey p->labels_mask, TCA_CT_LABELS_MASK, 868b57dc7c1SPaul Blakey sizeof(p->labels))) 869b57dc7c1SPaul Blakey goto nla_put_failure; 870b57dc7c1SPaul Blakey 871b57dc7c1SPaul Blakey if (IS_ENABLED(CONFIG_NF_CONNTRACK_ZONES) && 872b57dc7c1SPaul Blakey tcf_ct_dump_key_val(skb, 873b57dc7c1SPaul Blakey &p->zone, TCA_CT_ZONE, 874b57dc7c1SPaul Blakey NULL, TCA_CT_UNSPEC, 875b57dc7c1SPaul Blakey sizeof(p->zone))) 876b57dc7c1SPaul Blakey goto nla_put_failure; 877b57dc7c1SPaul Blakey 878b57dc7c1SPaul Blakey if (tcf_ct_dump_nat(skb, p)) 879b57dc7c1SPaul Blakey goto nla_put_failure; 880b57dc7c1SPaul Blakey 881b57dc7c1SPaul Blakey skip_dump: 882b57dc7c1SPaul Blakey if (nla_put(skb, TCA_CT_PARMS, sizeof(opt), &opt)) 883b57dc7c1SPaul Blakey goto nla_put_failure; 884b57dc7c1SPaul Blakey 885b57dc7c1SPaul Blakey tcf_tm_dump(&t, &c->tcf_tm); 886b57dc7c1SPaul Blakey if (nla_put_64bit(skb, TCA_CT_TM, sizeof(t), &t, TCA_CT_PAD)) 887b57dc7c1SPaul Blakey goto nla_put_failure; 888b57dc7c1SPaul Blakey spin_unlock_bh(&c->tcf_lock); 889b57dc7c1SPaul Blakey 890b57dc7c1SPaul Blakey return skb->len; 891b57dc7c1SPaul Blakey nla_put_failure: 892b57dc7c1SPaul Blakey spin_unlock_bh(&c->tcf_lock); 893b57dc7c1SPaul Blakey nlmsg_trim(skb, b); 894b57dc7c1SPaul Blakey return -1; 895b57dc7c1SPaul Blakey } 896b57dc7c1SPaul Blakey 897b57dc7c1SPaul Blakey static int tcf_ct_walker(struct net *net, struct sk_buff *skb, 898b57dc7c1SPaul Blakey struct netlink_callback *cb, int type, 899b57dc7c1SPaul Blakey const struct tc_action_ops *ops, 900b57dc7c1SPaul Blakey struct netlink_ext_ack *extack) 901b57dc7c1SPaul Blakey { 902b57dc7c1SPaul Blakey struct tc_action_net *tn = net_generic(net, ct_net_id); 903b57dc7c1SPaul Blakey 904b57dc7c1SPaul Blakey return tcf_generic_walker(tn, skb, cb, type, ops, extack); 905b57dc7c1SPaul Blakey } 906b57dc7c1SPaul Blakey 907b57dc7c1SPaul Blakey static int tcf_ct_search(struct net *net, struct tc_action **a, u32 index) 908b57dc7c1SPaul Blakey { 909b57dc7c1SPaul Blakey struct tc_action_net *tn = net_generic(net, ct_net_id); 910b57dc7c1SPaul Blakey 911b57dc7c1SPaul Blakey return tcf_idr_search(tn, a, index); 912b57dc7c1SPaul Blakey } 913b57dc7c1SPaul Blakey 914b57dc7c1SPaul Blakey static void tcf_stats_update(struct tc_action *a, u64 bytes, u32 packets, 915b57dc7c1SPaul Blakey u64 lastuse, bool hw) 916b57dc7c1SPaul Blakey { 917b57dc7c1SPaul Blakey struct tcf_ct *c = to_ct(a); 918b57dc7c1SPaul Blakey 919c8ecebd0SVlad Buslov tcf_action_update_stats(a, bytes, packets, false, hw); 920b57dc7c1SPaul Blakey c->tcf_tm.lastuse = max_t(u64, c->tcf_tm.lastuse, lastuse); 921b57dc7c1SPaul Blakey } 922b57dc7c1SPaul Blakey 923b57dc7c1SPaul Blakey static struct tc_action_ops act_ct_ops = { 924b57dc7c1SPaul Blakey .kind = "ct", 925b57dc7c1SPaul Blakey .id = TCA_ID_CT, 926b57dc7c1SPaul Blakey .owner = THIS_MODULE, 927b57dc7c1SPaul Blakey .act = tcf_ct_act, 928b57dc7c1SPaul Blakey .dump = tcf_ct_dump, 929b57dc7c1SPaul Blakey .init = tcf_ct_init, 930b57dc7c1SPaul Blakey .cleanup = tcf_ct_cleanup, 931b57dc7c1SPaul Blakey .walk = tcf_ct_walker, 932b57dc7c1SPaul Blakey .lookup = tcf_ct_search, 933b57dc7c1SPaul Blakey .stats_update = tcf_stats_update, 934b57dc7c1SPaul Blakey .size = sizeof(struct tcf_ct), 935b57dc7c1SPaul Blakey }; 936b57dc7c1SPaul Blakey 937b57dc7c1SPaul Blakey static __net_init int ct_init_net(struct net *net) 938b57dc7c1SPaul Blakey { 939b57dc7c1SPaul Blakey unsigned int n_bits = FIELD_SIZEOF(struct tcf_ct_params, labels) * 8; 940b57dc7c1SPaul Blakey struct tc_ct_action_net *tn = net_generic(net, ct_net_id); 941b57dc7c1SPaul Blakey 942b57dc7c1SPaul Blakey if (nf_connlabels_get(net, n_bits - 1)) { 943b57dc7c1SPaul Blakey tn->labels = false; 944b57dc7c1SPaul Blakey pr_err("act_ct: Failed to set connlabels length"); 945b57dc7c1SPaul Blakey } else { 946b57dc7c1SPaul Blakey tn->labels = true; 947b57dc7c1SPaul Blakey } 948b57dc7c1SPaul Blakey 949981471bdSCong Wang return tc_action_net_init(net, &tn->tn, &act_ct_ops); 950b57dc7c1SPaul Blakey } 951b57dc7c1SPaul Blakey 952b57dc7c1SPaul Blakey static void __net_exit ct_exit_net(struct list_head *net_list) 953b57dc7c1SPaul Blakey { 954b57dc7c1SPaul Blakey struct net *net; 955b57dc7c1SPaul Blakey 956b57dc7c1SPaul Blakey rtnl_lock(); 957b57dc7c1SPaul Blakey list_for_each_entry(net, net_list, exit_list) { 958b57dc7c1SPaul Blakey struct tc_ct_action_net *tn = net_generic(net, ct_net_id); 959b57dc7c1SPaul Blakey 960b57dc7c1SPaul Blakey if (tn->labels) 961b57dc7c1SPaul Blakey nf_connlabels_put(net); 962b57dc7c1SPaul Blakey } 963b57dc7c1SPaul Blakey rtnl_unlock(); 964b57dc7c1SPaul Blakey 965b57dc7c1SPaul Blakey tc_action_net_exit(net_list, ct_net_id); 966b57dc7c1SPaul Blakey } 967b57dc7c1SPaul Blakey 968b57dc7c1SPaul Blakey static struct pernet_operations ct_net_ops = { 969b57dc7c1SPaul Blakey .init = ct_init_net, 970b57dc7c1SPaul Blakey .exit_batch = ct_exit_net, 971b57dc7c1SPaul Blakey .id = &ct_net_id, 972b57dc7c1SPaul Blakey .size = sizeof(struct tc_ct_action_net), 973b57dc7c1SPaul Blakey }; 974b57dc7c1SPaul Blakey 975b57dc7c1SPaul Blakey static int __init ct_init_module(void) 976b57dc7c1SPaul Blakey { 977b57dc7c1SPaul Blakey return tcf_register_action(&act_ct_ops, &ct_net_ops); 978b57dc7c1SPaul Blakey } 979b57dc7c1SPaul Blakey 980b57dc7c1SPaul Blakey static void __exit ct_cleanup_module(void) 981b57dc7c1SPaul Blakey { 982b57dc7c1SPaul Blakey tcf_unregister_action(&act_ct_ops, &ct_net_ops); 983b57dc7c1SPaul Blakey } 984b57dc7c1SPaul Blakey 985b57dc7c1SPaul Blakey module_init(ct_init_module); 986b57dc7c1SPaul Blakey module_exit(ct_cleanup_module); 987b57dc7c1SPaul Blakey MODULE_AUTHOR("Paul Blakey <paulb@mellanox.com>"); 988b57dc7c1SPaul Blakey MODULE_AUTHOR("Yossi Kuperman <yossiku@mellanox.com>"); 989b57dc7c1SPaul Blakey MODULE_AUTHOR("Marcelo Ricardo Leitner <marcelo.leitner@gmail.com>"); 990b57dc7c1SPaul Blakey MODULE_DESCRIPTION("Connection tracking action"); 991b57dc7c1SPaul Blakey MODULE_LICENSE("GPL v2"); 992b57dc7c1SPaul Blakey 993