15b497af4SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only 27f8a436eSJoe Stringer /* 37f8a436eSJoe Stringer * Copyright (c) 2015 Nicira, Inc. 47f8a436eSJoe Stringer */ 57f8a436eSJoe Stringer 67f8a436eSJoe Stringer #include <linux/module.h> 77f8a436eSJoe Stringer #include <linux/openvswitch.h> 805752523SJarno Rajahalme #include <linux/tcp.h> 905752523SJarno Rajahalme #include <linux/udp.h> 1005752523SJarno Rajahalme #include <linux/sctp.h> 1111efd5cbSYi-Hung Wei #include <linux/static_key.h> 125c72b4c6SAndy Shevchenko #include <linux/string_helpers.h> 137f8a436eSJoe Stringer #include <net/ip.h> 1411efd5cbSYi-Hung Wei #include <net/genetlink.h> 157f8a436eSJoe Stringer #include <net/netfilter/nf_conntrack_core.h> 1611efd5cbSYi-Hung Wei #include <net/netfilter/nf_conntrack_count.h> 17cae3a262SJoe Stringer #include <net/netfilter/nf_conntrack_helper.h> 18c2ac6673SJoe Stringer #include <net/netfilter/nf_conntrack_labels.h> 1905752523SJarno Rajahalme #include <net/netfilter/nf_conntrack_seqadj.h> 2006bd2bdfSYi-Hung Wei #include <net/netfilter/nf_conntrack_timeout.h> 217f8a436eSJoe Stringer #include <net/netfilter/nf_conntrack_zones.h> 227f8a436eSJoe Stringer #include <net/netfilter/ipv6/nf_defrag_ipv6.h> 2370b095c8SFlorian Westphal #include <net/ipv6_frag.h> 247f8a436eSJoe Stringer 254806e975SFlorian Westphal #if IS_ENABLED(CONFIG_NF_NAT) 26d2c5c103SFlorian Westphal #include <net/netfilter/nf_nat.h> 2705752523SJarno Rajahalme #endif 2805752523SJarno Rajahalme 29b702436aSPaul Blakey #include <net/netfilter/nf_conntrack_act_ct.h> 30b702436aSPaul Blakey 317f8a436eSJoe Stringer #include "datapath.h" 327f8a436eSJoe Stringer #include "conntrack.h" 337f8a436eSJoe Stringer #include "flow.h" 347f8a436eSJoe Stringer #include "flow_netlink.h" 357f8a436eSJoe Stringer 367f8a436eSJoe Stringer struct ovs_ct_len_tbl { 3705752523SJarno Rajahalme int maxlen; 3805752523SJarno Rajahalme int minlen; 397f8a436eSJoe Stringer }; 407f8a436eSJoe Stringer 41182e3042SJoe Stringer /* Metadata mark for masked write to conntrack mark */ 42182e3042SJoe Stringer struct md_mark { 43182e3042SJoe Stringer u32 value; 44182e3042SJoe Stringer u32 mask; 45182e3042SJoe Stringer }; 46182e3042SJoe Stringer 47c2ac6673SJoe Stringer /* Metadata label for masked write to conntrack label. */ 4833db4125SJoe Stringer struct md_labels { 4933db4125SJoe Stringer struct ovs_key_ct_labels value; 5033db4125SJoe Stringer struct ovs_key_ct_labels mask; 51c2ac6673SJoe Stringer }; 52c2ac6673SJoe Stringer 5305752523SJarno Rajahalme enum ovs_ct_nat { 5405752523SJarno Rajahalme OVS_CT_NAT = 1 << 0, /* NAT for committed connections only. */ 5505752523SJarno Rajahalme OVS_CT_SRC_NAT = 1 << 1, /* Source NAT for NEW connections. */ 5605752523SJarno Rajahalme OVS_CT_DST_NAT = 1 << 2, /* Destination NAT for NEW connections. */ 5705752523SJarno Rajahalme }; 5805752523SJarno Rajahalme 597f8a436eSJoe Stringer /* Conntrack action context for execution. */ 607f8a436eSJoe Stringer struct ovs_conntrack_info { 61cae3a262SJoe Stringer struct nf_conntrack_helper *helper; 627f8a436eSJoe Stringer struct nf_conntrack_zone zone; 637f8a436eSJoe Stringer struct nf_conn *ct; 64ab38a7b5SJoe Stringer u8 commit : 1; 6505752523SJarno Rajahalme u8 nat : 3; /* enum ovs_ct_nat */ 66dd41d33fSJarno Rajahalme u8 force : 1; 6712064551SJarno Rajahalme u8 have_eventmask : 1; 687f8a436eSJoe Stringer u16 family; 6912064551SJarno Rajahalme u32 eventmask; /* Mask of 1 << IPCT_*. */ 70182e3042SJoe Stringer struct md_mark mark; 7133db4125SJoe Stringer struct md_labels labels; 7206bd2bdfSYi-Hung Wei char timeout[CTNL_TIMEOUT_NAME_MAX]; 7371778951SYi-Hung Wei struct nf_ct_timeout *nf_ct_timeout; 744806e975SFlorian Westphal #if IS_ENABLED(CONFIG_NF_NAT) 752eb0f624SThierry Du Tre struct nf_nat_range2 range; /* Only present for SRC NAT and DST NAT. */ 7605752523SJarno Rajahalme #endif 777f8a436eSJoe Stringer }; 787f8a436eSJoe Stringer 7911efd5cbSYi-Hung Wei #if IS_ENABLED(CONFIG_NETFILTER_CONNCOUNT) 8011efd5cbSYi-Hung Wei #define OVS_CT_LIMIT_UNLIMITED 0 8111efd5cbSYi-Hung Wei #define OVS_CT_LIMIT_DEFAULT OVS_CT_LIMIT_UNLIMITED 8211efd5cbSYi-Hung Wei #define CT_LIMIT_HASH_BUCKETS 512 8311efd5cbSYi-Hung Wei static DEFINE_STATIC_KEY_FALSE(ovs_ct_limit_enabled); 8411efd5cbSYi-Hung Wei 8511efd5cbSYi-Hung Wei struct ovs_ct_limit { 8611efd5cbSYi-Hung Wei /* Elements in ovs_ct_limit_info->limits hash table */ 8711efd5cbSYi-Hung Wei struct hlist_node hlist_node; 8811efd5cbSYi-Hung Wei struct rcu_head rcu; 8911efd5cbSYi-Hung Wei u16 zone; 9011efd5cbSYi-Hung Wei u32 limit; 9111efd5cbSYi-Hung Wei }; 9211efd5cbSYi-Hung Wei 9311efd5cbSYi-Hung Wei struct ovs_ct_limit_info { 9411efd5cbSYi-Hung Wei u32 default_limit; 9511efd5cbSYi-Hung Wei struct hlist_head *limits; 9611efd5cbSYi-Hung Wei struct nf_conncount_data *data; 9711efd5cbSYi-Hung Wei }; 9811efd5cbSYi-Hung Wei 9911efd5cbSYi-Hung Wei static const struct nla_policy ct_limit_policy[OVS_CT_LIMIT_ATTR_MAX + 1] = { 10011efd5cbSYi-Hung Wei [OVS_CT_LIMIT_ATTR_ZONE_LIMIT] = { .type = NLA_NESTED, }, 10111efd5cbSYi-Hung Wei }; 10211efd5cbSYi-Hung Wei #endif 10311efd5cbSYi-Hung Wei 10409aa98adSJarno Rajahalme static bool labels_nonzero(const struct ovs_key_ct_labels *labels); 10509aa98adSJarno Rajahalme 1062f3ab9f9SJoe Stringer static void __ovs_ct_free_action(struct ovs_conntrack_info *ct_info); 1072f3ab9f9SJoe Stringer 1087f8a436eSJoe Stringer static u16 key_to_nfproto(const struct sw_flow_key *key) 1097f8a436eSJoe Stringer { 1107f8a436eSJoe Stringer switch (ntohs(key->eth.type)) { 1117f8a436eSJoe Stringer case ETH_P_IP: 1127f8a436eSJoe Stringer return NFPROTO_IPV4; 1137f8a436eSJoe Stringer case ETH_P_IPV6: 1147f8a436eSJoe Stringer return NFPROTO_IPV6; 1157f8a436eSJoe Stringer default: 1167f8a436eSJoe Stringer return NFPROTO_UNSPEC; 1177f8a436eSJoe Stringer } 1187f8a436eSJoe Stringer } 1197f8a436eSJoe Stringer 1207f8a436eSJoe Stringer /* Map SKB connection state into the values used by flow definition. */ 1217f8a436eSJoe Stringer static u8 ovs_ct_get_state(enum ip_conntrack_info ctinfo) 1227f8a436eSJoe Stringer { 1237f8a436eSJoe Stringer u8 ct_state = OVS_CS_F_TRACKED; 1247f8a436eSJoe Stringer 1257f8a436eSJoe Stringer switch (ctinfo) { 1267f8a436eSJoe Stringer case IP_CT_ESTABLISHED_REPLY: 1277f8a436eSJoe Stringer case IP_CT_RELATED_REPLY: 1287f8a436eSJoe Stringer ct_state |= OVS_CS_F_REPLY_DIR; 1297f8a436eSJoe Stringer break; 1307f8a436eSJoe Stringer default: 1317f8a436eSJoe Stringer break; 1327f8a436eSJoe Stringer } 1337f8a436eSJoe Stringer 1347f8a436eSJoe Stringer switch (ctinfo) { 1357f8a436eSJoe Stringer case IP_CT_ESTABLISHED: 1367f8a436eSJoe Stringer case IP_CT_ESTABLISHED_REPLY: 1377f8a436eSJoe Stringer ct_state |= OVS_CS_F_ESTABLISHED; 1387f8a436eSJoe Stringer break; 1397f8a436eSJoe Stringer case IP_CT_RELATED: 1407f8a436eSJoe Stringer case IP_CT_RELATED_REPLY: 1417f8a436eSJoe Stringer ct_state |= OVS_CS_F_RELATED; 1427f8a436eSJoe Stringer break; 1437f8a436eSJoe Stringer case IP_CT_NEW: 1447f8a436eSJoe Stringer ct_state |= OVS_CS_F_NEW; 1457f8a436eSJoe Stringer break; 1467f8a436eSJoe Stringer default: 1477f8a436eSJoe Stringer break; 1487f8a436eSJoe Stringer } 1497f8a436eSJoe Stringer 1507f8a436eSJoe Stringer return ct_state; 1517f8a436eSJoe Stringer } 1527f8a436eSJoe Stringer 1530d5cdef8SJoe Stringer static u32 ovs_ct_get_mark(const struct nf_conn *ct) 1540d5cdef8SJoe Stringer { 1550d5cdef8SJoe Stringer #if IS_ENABLED(CONFIG_NF_CONNTRACK_MARK) 15652d1aa8bSDaniel Xu return ct ? READ_ONCE(ct->mark) : 0; 1570d5cdef8SJoe Stringer #else 1580d5cdef8SJoe Stringer return 0; 1590d5cdef8SJoe Stringer #endif 1600d5cdef8SJoe Stringer } 1610d5cdef8SJoe Stringer 162b87cec38SJarno Rajahalme /* Guard against conntrack labels max size shrinking below 128 bits. */ 163b87cec38SJarno Rajahalme #if NF_CT_LABELS_MAX_SIZE < 16 164b87cec38SJarno Rajahalme #error NF_CT_LABELS_MAX_SIZE must be at least 16 bytes 165b87cec38SJarno Rajahalme #endif 166b87cec38SJarno Rajahalme 16733db4125SJoe Stringer static void ovs_ct_get_labels(const struct nf_conn *ct, 16833db4125SJoe Stringer struct ovs_key_ct_labels *labels) 169c2ac6673SJoe Stringer { 170c2ac6673SJoe Stringer struct nf_conn_labels *cl = ct ? nf_ct_labels_find(ct) : NULL; 171c2ac6673SJoe Stringer 172b87cec38SJarno Rajahalme if (cl) 173b87cec38SJarno Rajahalme memcpy(labels, cl->bits, OVS_CT_LABELS_LEN); 174b87cec38SJarno Rajahalme else 17533db4125SJoe Stringer memset(labels, 0, OVS_CT_LABELS_LEN); 176c2ac6673SJoe Stringer } 177c2ac6673SJoe Stringer 1789dd7f890SJarno Rajahalme static void __ovs_ct_update_key_orig_tp(struct sw_flow_key *key, 1799dd7f890SJarno Rajahalme const struct nf_conntrack_tuple *orig, 1809dd7f890SJarno Rajahalme u8 icmp_proto) 1819dd7f890SJarno Rajahalme { 182316d4d78SJarno Rajahalme key->ct_orig_proto = orig->dst.protonum; 1839dd7f890SJarno Rajahalme if (orig->dst.protonum == icmp_proto) { 1849dd7f890SJarno Rajahalme key->ct.orig_tp.src = htons(orig->dst.u.icmp.type); 1859dd7f890SJarno Rajahalme key->ct.orig_tp.dst = htons(orig->dst.u.icmp.code); 1869dd7f890SJarno Rajahalme } else { 1879dd7f890SJarno Rajahalme key->ct.orig_tp.src = orig->src.u.all; 1889dd7f890SJarno Rajahalme key->ct.orig_tp.dst = orig->dst.u.all; 1899dd7f890SJarno Rajahalme } 1909dd7f890SJarno Rajahalme } 1919dd7f890SJarno Rajahalme 1927f8a436eSJoe Stringer static void __ovs_ct_update_key(struct sw_flow_key *key, u8 state, 193182e3042SJoe Stringer const struct nf_conntrack_zone *zone, 194182e3042SJoe Stringer const struct nf_conn *ct) 1957f8a436eSJoe Stringer { 196316d4d78SJarno Rajahalme key->ct_state = state; 197316d4d78SJarno Rajahalme key->ct_zone = zone->id; 1980d5cdef8SJoe Stringer key->ct.mark = ovs_ct_get_mark(ct); 19933db4125SJoe Stringer ovs_ct_get_labels(ct, &key->ct.labels); 2009dd7f890SJarno Rajahalme 2019dd7f890SJarno Rajahalme if (ct) { 2029dd7f890SJarno Rajahalme const struct nf_conntrack_tuple *orig; 2039dd7f890SJarno Rajahalme 2049dd7f890SJarno Rajahalme /* Use the master if we have one. */ 2059dd7f890SJarno Rajahalme if (ct->master) 2069dd7f890SJarno Rajahalme ct = ct->master; 2079dd7f890SJarno Rajahalme orig = &ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple; 2089dd7f890SJarno Rajahalme 2099dd7f890SJarno Rajahalme /* IP version must match with the master connection. */ 2109dd7f890SJarno Rajahalme if (key->eth.type == htons(ETH_P_IP) && 2119dd7f890SJarno Rajahalme nf_ct_l3num(ct) == NFPROTO_IPV4) { 2129dd7f890SJarno Rajahalme key->ipv4.ct_orig.src = orig->src.u3.ip; 2139dd7f890SJarno Rajahalme key->ipv4.ct_orig.dst = orig->dst.u3.ip; 2149dd7f890SJarno Rajahalme __ovs_ct_update_key_orig_tp(key, orig, IPPROTO_ICMP); 2159dd7f890SJarno Rajahalme return; 2169dd7f890SJarno Rajahalme } else if (key->eth.type == htons(ETH_P_IPV6) && 2179dd7f890SJarno Rajahalme !sw_flow_key_is_nd(key) && 2189dd7f890SJarno Rajahalme nf_ct_l3num(ct) == NFPROTO_IPV6) { 2199dd7f890SJarno Rajahalme key->ipv6.ct_orig.src = orig->src.u3.in6; 2209dd7f890SJarno Rajahalme key->ipv6.ct_orig.dst = orig->dst.u3.in6; 2219dd7f890SJarno Rajahalme __ovs_ct_update_key_orig_tp(key, orig, NEXTHDR_ICMP); 2229dd7f890SJarno Rajahalme return; 2239dd7f890SJarno Rajahalme } 2249dd7f890SJarno Rajahalme } 225316d4d78SJarno Rajahalme /* Clear 'ct_orig_proto' to mark the non-existence of conntrack 2269dd7f890SJarno Rajahalme * original direction key fields. 2279dd7f890SJarno Rajahalme */ 228316d4d78SJarno Rajahalme key->ct_orig_proto = 0; 2297f8a436eSJoe Stringer } 2307f8a436eSJoe Stringer 2315e17da63SJarno Rajahalme /* Update 'key' based on skb->_nfct. If 'post_ct' is true, then OVS has 23205752523SJarno Rajahalme * previously sent the packet to conntrack via the ct action. If 23305752523SJarno Rajahalme * 'keep_nat_flags' is true, the existing NAT flags retained, else they are 23405752523SJarno Rajahalme * initialized from the connection status. 2357f8a436eSJoe Stringer */ 2367f8a436eSJoe Stringer static void ovs_ct_update_key(const struct sk_buff *skb, 237d110986cSJoe Stringer const struct ovs_conntrack_info *info, 23805752523SJarno Rajahalme struct sw_flow_key *key, bool post_ct, 23905752523SJarno Rajahalme bool keep_nat_flags) 2407f8a436eSJoe Stringer { 2417f8a436eSJoe Stringer const struct nf_conntrack_zone *zone = &nf_ct_zone_dflt; 2427f8a436eSJoe Stringer enum ip_conntrack_info ctinfo; 2437f8a436eSJoe Stringer struct nf_conn *ct; 2447f8a436eSJoe Stringer u8 state = 0; 2457f8a436eSJoe Stringer 2467f8a436eSJoe Stringer ct = nf_ct_get(skb, &ctinfo); 2477f8a436eSJoe Stringer if (ct) { 2487f8a436eSJoe Stringer state = ovs_ct_get_state(ctinfo); 2499f13ded8SJarno Rajahalme /* All unconfirmed entries are NEW connections. */ 2504f0909eeSJoe Stringer if (!nf_ct_is_confirmed(ct)) 2514f0909eeSJoe Stringer state |= OVS_CS_F_NEW; 2529f13ded8SJarno Rajahalme /* OVS persists the related flag for the duration of the 2539f13ded8SJarno Rajahalme * connection. 2549f13ded8SJarno Rajahalme */ 2557f8a436eSJoe Stringer if (ct->master) 2567f8a436eSJoe Stringer state |= OVS_CS_F_RELATED; 25705752523SJarno Rajahalme if (keep_nat_flags) { 258316d4d78SJarno Rajahalme state |= key->ct_state & OVS_CS_F_NAT_MASK; 25905752523SJarno Rajahalme } else { 26005752523SJarno Rajahalme if (ct->status & IPS_SRC_NAT) 26105752523SJarno Rajahalme state |= OVS_CS_F_SRC_NAT; 26205752523SJarno Rajahalme if (ct->status & IPS_DST_NAT) 26305752523SJarno Rajahalme state |= OVS_CS_F_DST_NAT; 26405752523SJarno Rajahalme } 2657f8a436eSJoe Stringer zone = nf_ct_zone(ct); 2667f8a436eSJoe Stringer } else if (post_ct) { 2677f8a436eSJoe Stringer state = OVS_CS_F_TRACKED | OVS_CS_F_INVALID; 268d110986cSJoe Stringer if (info) 269d110986cSJoe Stringer zone = &info->zone; 2707f8a436eSJoe Stringer } 271182e3042SJoe Stringer __ovs_ct_update_key(key, state, zone, ct); 2727f8a436eSJoe Stringer } 2737f8a436eSJoe Stringer 2749f13ded8SJarno Rajahalme /* This is called to initialize CT key fields possibly coming in from the local 2759f13ded8SJarno Rajahalme * stack. 2769f13ded8SJarno Rajahalme */ 277d29334c1Swenxu void ovs_ct_fill_key(const struct sk_buff *skb, 278d29334c1Swenxu struct sw_flow_key *key, 279d29334c1Swenxu bool post_ct) 2807f8a436eSJoe Stringer { 281d29334c1Swenxu ovs_ct_update_key(skb, NULL, key, post_ct, false); 2827f8a436eSJoe Stringer } 2837f8a436eSJoe Stringer 2849dd7f890SJarno Rajahalme int ovs_ct_put_key(const struct sw_flow_key *swkey, 2859dd7f890SJarno Rajahalme const struct sw_flow_key *output, struct sk_buff *skb) 2867f8a436eSJoe Stringer { 287316d4d78SJarno Rajahalme if (nla_put_u32(skb, OVS_KEY_ATTR_CT_STATE, output->ct_state)) 2887f8a436eSJoe Stringer return -EMSGSIZE; 2897f8a436eSJoe Stringer 2907f8a436eSJoe Stringer if (IS_ENABLED(CONFIG_NF_CONNTRACK_ZONES) && 291316d4d78SJarno Rajahalme nla_put_u16(skb, OVS_KEY_ATTR_CT_ZONE, output->ct_zone)) 2927f8a436eSJoe Stringer return -EMSGSIZE; 2937f8a436eSJoe Stringer 294182e3042SJoe Stringer if (IS_ENABLED(CONFIG_NF_CONNTRACK_MARK) && 2959dd7f890SJarno Rajahalme nla_put_u32(skb, OVS_KEY_ATTR_CT_MARK, output->ct.mark)) 296182e3042SJoe Stringer return -EMSGSIZE; 297182e3042SJoe Stringer 2989723e6abSValentin Rothberg if (IS_ENABLED(CONFIG_NF_CONNTRACK_LABELS) && 2999dd7f890SJarno Rajahalme nla_put(skb, OVS_KEY_ATTR_CT_LABELS, sizeof(output->ct.labels), 3009dd7f890SJarno Rajahalme &output->ct.labels)) 301c2ac6673SJoe Stringer return -EMSGSIZE; 302c2ac6673SJoe Stringer 303316d4d78SJarno Rajahalme if (swkey->ct_orig_proto) { 3049dd7f890SJarno Rajahalme if (swkey->eth.type == htons(ETH_P_IP)) { 3059aba6c5bSPeilin Ye struct ovs_key_ct_tuple_ipv4 orig; 3069aba6c5bSPeilin Ye 3079aba6c5bSPeilin Ye memset(&orig, 0, sizeof(orig)); 3089aba6c5bSPeilin Ye orig.ipv4_src = output->ipv4.ct_orig.src; 3099aba6c5bSPeilin Ye orig.ipv4_dst = output->ipv4.ct_orig.dst; 3109aba6c5bSPeilin Ye orig.src_port = output->ct.orig_tp.src; 3119aba6c5bSPeilin Ye orig.dst_port = output->ct.orig_tp.dst; 3129aba6c5bSPeilin Ye orig.ipv4_proto = output->ct_orig_proto; 3139aba6c5bSPeilin Ye 3149dd7f890SJarno Rajahalme if (nla_put(skb, OVS_KEY_ATTR_CT_ORIG_TUPLE_IPV4, 3159dd7f890SJarno Rajahalme sizeof(orig), &orig)) 3169dd7f890SJarno Rajahalme return -EMSGSIZE; 3179dd7f890SJarno Rajahalme } else if (swkey->eth.type == htons(ETH_P_IPV6)) { 3189aba6c5bSPeilin Ye struct ovs_key_ct_tuple_ipv6 orig; 3199aba6c5bSPeilin Ye 3209aba6c5bSPeilin Ye memset(&orig, 0, sizeof(orig)); 3219aba6c5bSPeilin Ye memcpy(orig.ipv6_src, output->ipv6.ct_orig.src.s6_addr32, 3229aba6c5bSPeilin Ye sizeof(orig.ipv6_src)); 3239aba6c5bSPeilin Ye memcpy(orig.ipv6_dst, output->ipv6.ct_orig.dst.s6_addr32, 3249aba6c5bSPeilin Ye sizeof(orig.ipv6_dst)); 3259aba6c5bSPeilin Ye orig.src_port = output->ct.orig_tp.src; 3269aba6c5bSPeilin Ye orig.dst_port = output->ct.orig_tp.dst; 3279aba6c5bSPeilin Ye orig.ipv6_proto = output->ct_orig_proto; 3289aba6c5bSPeilin Ye 3299dd7f890SJarno Rajahalme if (nla_put(skb, OVS_KEY_ATTR_CT_ORIG_TUPLE_IPV6, 3309dd7f890SJarno Rajahalme sizeof(orig), &orig)) 3319dd7f890SJarno Rajahalme return -EMSGSIZE; 3329dd7f890SJarno Rajahalme } 3339dd7f890SJarno Rajahalme } 3349dd7f890SJarno Rajahalme 335182e3042SJoe Stringer return 0; 336182e3042SJoe Stringer } 337182e3042SJoe Stringer 3386ffcea79SJarno Rajahalme static int ovs_ct_set_mark(struct nf_conn *ct, struct sw_flow_key *key, 339182e3042SJoe Stringer u32 ct_mark, u32 mask) 340182e3042SJoe Stringer { 3410d5cdef8SJoe Stringer #if IS_ENABLED(CONFIG_NF_CONNTRACK_MARK) 342182e3042SJoe Stringer u32 new_mark; 343182e3042SJoe Stringer 34452d1aa8bSDaniel Xu new_mark = ct_mark | (READ_ONCE(ct->mark) & ~(mask)); 34552d1aa8bSDaniel Xu if (READ_ONCE(ct->mark) != new_mark) { 34652d1aa8bSDaniel Xu WRITE_ONCE(ct->mark, new_mark); 347193e3096SJarno Rajahalme if (nf_ct_is_confirmed(ct)) 348182e3042SJoe Stringer nf_conntrack_event_cache(IPCT_MARK, ct); 349182e3042SJoe Stringer key->ct.mark = new_mark; 350182e3042SJoe Stringer } 351182e3042SJoe Stringer 3527f8a436eSJoe Stringer return 0; 3530d5cdef8SJoe Stringer #else 3540d5cdef8SJoe Stringer return -ENOTSUPP; 3550d5cdef8SJoe Stringer #endif 3567f8a436eSJoe Stringer } 3577f8a436eSJoe Stringer 3586ffcea79SJarno Rajahalme static struct nf_conn_labels *ovs_ct_get_conn_labels(struct nf_conn *ct) 359c2ac6673SJoe Stringer { 360c2ac6673SJoe Stringer struct nf_conn_labels *cl; 361c2ac6673SJoe Stringer 362c2ac6673SJoe Stringer cl = nf_ct_labels_find(ct); 363c2ac6673SJoe Stringer if (!cl) { 364c2ac6673SJoe Stringer nf_ct_labels_ext_add(ct); 365c2ac6673SJoe Stringer cl = nf_ct_labels_find(ct); 366c2ac6673SJoe Stringer } 3676ffcea79SJarno Rajahalme 3686ffcea79SJarno Rajahalme return cl; 3696ffcea79SJarno Rajahalme } 3706ffcea79SJarno Rajahalme 3716ffcea79SJarno Rajahalme /* Initialize labels for a new, yet to be committed conntrack entry. Note that 3726ffcea79SJarno Rajahalme * since the new connection is not yet confirmed, and thus no-one else has 3732317c6b5SJarno Rajahalme * access to it's labels, we simply write them over. 3746ffcea79SJarno Rajahalme */ 3756ffcea79SJarno Rajahalme static int ovs_ct_init_labels(struct nf_conn *ct, struct sw_flow_key *key, 3766ffcea79SJarno Rajahalme const struct ovs_key_ct_labels *labels, 3776ffcea79SJarno Rajahalme const struct ovs_key_ct_labels *mask) 3786ffcea79SJarno Rajahalme { 37909aa98adSJarno Rajahalme struct nf_conn_labels *cl, *master_cl; 38009aa98adSJarno Rajahalme bool have_mask = labels_nonzero(mask); 38109aa98adSJarno Rajahalme 38209aa98adSJarno Rajahalme /* Inherit master's labels to the related connection? */ 38309aa98adSJarno Rajahalme master_cl = ct->master ? nf_ct_labels_find(ct->master) : NULL; 38409aa98adSJarno Rajahalme 38509aa98adSJarno Rajahalme if (!master_cl && !have_mask) 38609aa98adSJarno Rajahalme return 0; /* Nothing to do. */ 3876ffcea79SJarno Rajahalme 3886ffcea79SJarno Rajahalme cl = ovs_ct_get_conn_labels(ct); 389b87cec38SJarno Rajahalme if (!cl) 390c2ac6673SJoe Stringer return -ENOSPC; 391c2ac6673SJoe Stringer 39209aa98adSJarno Rajahalme /* Inherit the master's labels, if any. */ 39309aa98adSJarno Rajahalme if (master_cl) 39409aa98adSJarno Rajahalme *cl = *master_cl; 39509aa98adSJarno Rajahalme 39609aa98adSJarno Rajahalme if (have_mask) { 39709aa98adSJarno Rajahalme u32 *dst = (u32 *)cl->bits; 39809aa98adSJarno Rajahalme int i; 39909aa98adSJarno Rajahalme 4006ffcea79SJarno Rajahalme for (i = 0; i < OVS_CT_LABELS_LEN_32; i++) 4016ffcea79SJarno Rajahalme dst[i] = (dst[i] & ~mask->ct_labels_32[i]) | 40209aa98adSJarno Rajahalme (labels->ct_labels_32[i] 40309aa98adSJarno Rajahalme & mask->ct_labels_32[i]); 40409aa98adSJarno Rajahalme } 4056ffcea79SJarno Rajahalme 4062317c6b5SJarno Rajahalme /* Labels are included in the IPCTNL_MSG_CT_NEW event only if the 407abd0a4f2SJarno Rajahalme * IPCT_LABEL bit is set in the event cache. 4082317c6b5SJarno Rajahalme */ 4092317c6b5SJarno Rajahalme nf_conntrack_event_cache(IPCT_LABEL, ct); 4102317c6b5SJarno Rajahalme 4116ffcea79SJarno Rajahalme memcpy(&key->ct.labels, cl->bits, OVS_CT_LABELS_LEN); 4126ffcea79SJarno Rajahalme 4136ffcea79SJarno Rajahalme return 0; 4146ffcea79SJarno Rajahalme } 4156ffcea79SJarno Rajahalme 4166ffcea79SJarno Rajahalme static int ovs_ct_set_labels(struct nf_conn *ct, struct sw_flow_key *key, 4176ffcea79SJarno Rajahalme const struct ovs_key_ct_labels *labels, 4186ffcea79SJarno Rajahalme const struct ovs_key_ct_labels *mask) 4196ffcea79SJarno Rajahalme { 4206ffcea79SJarno Rajahalme struct nf_conn_labels *cl; 4216ffcea79SJarno Rajahalme int err; 4226ffcea79SJarno Rajahalme 4236ffcea79SJarno Rajahalme cl = ovs_ct_get_conn_labels(ct); 4246ffcea79SJarno Rajahalme if (!cl) 4256ffcea79SJarno Rajahalme return -ENOSPC; 4266ffcea79SJarno Rajahalme 4276ffcea79SJarno Rajahalme err = nf_connlabels_replace(ct, labels->ct_labels_32, 428cb80d58fSJarno Rajahalme mask->ct_labels_32, 429cb80d58fSJarno Rajahalme OVS_CT_LABELS_LEN_32); 430c2ac6673SJoe Stringer if (err) 431c2ac6673SJoe Stringer return err; 432193e3096SJarno Rajahalme 4336ffcea79SJarno Rajahalme memcpy(&key->ct.labels, cl->bits, OVS_CT_LABELS_LEN); 434c2ac6673SJoe Stringer 435c2ac6673SJoe Stringer return 0; 436c2ac6673SJoe Stringer } 437c2ac6673SJoe Stringer 43874c16618SJoe Stringer /* Returns 0 on success, -EINPROGRESS if 'skb' is stolen, or other nonzero 43974c16618SJoe Stringer * value if 'skb' is freed. 44074c16618SJoe Stringer */ 441*1b83bf44SXin Long static int handle_fragments(struct net *net, struct sk_buff *skb, 442*1b83bf44SXin Long u16 zone, u8 family, u8 *proto, u16 *mru) 4437f8a436eSJoe Stringer { 444daaa7d64SFlorian Westphal int err; 4457f8a436eSJoe Stringer 446*1b83bf44SXin Long if (family == NFPROTO_IPV4) { 4477f8a436eSJoe Stringer enum ip_defrag_users user = IP_DEFRAG_CONNTRACK_IN + zone; 4487f8a436eSJoe Stringer 4497f8a436eSJoe Stringer memset(IPCB(skb), 0, sizeof(struct inet_skb_parm)); 45019bcf9f2SEric W. Biederman err = ip_defrag(net, skb, user); 4517f8a436eSJoe Stringer if (err) 4527f8a436eSJoe Stringer return err; 4537f8a436eSJoe Stringer 454*1b83bf44SXin Long *mru = IPCB(skb)->frag_max_size; 4557f8a436eSJoe Stringer #if IS_ENABLED(CONFIG_NF_DEFRAG_IPV6) 456*1b83bf44SXin Long } else if (family == NFPROTO_IPV6) { 4577f8a436eSJoe Stringer enum ip6_defrag_users user = IP6_DEFRAG_CONNTRACK_IN + zone; 4587f8a436eSJoe Stringer 4597f8a436eSJoe Stringer memset(IP6CB(skb), 0, sizeof(struct inet6_skb_parm)); 460daaa7d64SFlorian Westphal err = nf_ct_frag6_gather(net, skb, user); 461f92a80a9SDaniele Di Proietto if (err) { 462f92a80a9SDaniele Di Proietto if (err != -EINPROGRESS) 463f92a80a9SDaniele Di Proietto kfree_skb(skb); 464daaa7d64SFlorian Westphal return err; 465f92a80a9SDaniele Di Proietto } 4667f8a436eSJoe Stringer 467*1b83bf44SXin Long *proto = ipv6_hdr(skb)->nexthdr; 468*1b83bf44SXin Long *mru = IP6CB(skb)->frag_max_size; 4697f8a436eSJoe Stringer #endif 4707f8a436eSJoe Stringer } else { 47174c16618SJoe Stringer kfree_skb(skb); 4727f8a436eSJoe Stringer return -EPFNOSUPPORT; 4737f8a436eSJoe Stringer } 4747f8a436eSJoe Stringer 475*1b83bf44SXin Long skb_clear_hash(skb); 476*1b83bf44SXin Long skb->ignore_df = 1; 477*1b83bf44SXin Long 478*1b83bf44SXin Long return 0; 479*1b83bf44SXin Long } 480*1b83bf44SXin Long 481*1b83bf44SXin Long static int ovs_ct_handle_fragments(struct net *net, struct sw_flow_key *key, 482*1b83bf44SXin Long u16 zone, int family, struct sk_buff *skb) 483*1b83bf44SXin Long { 484*1b83bf44SXin Long struct ovs_skb_cb ovs_cb = *OVS_CB(skb); 485*1b83bf44SXin Long int err; 486*1b83bf44SXin Long 487*1b83bf44SXin Long err = handle_fragments(net, skb, zone, family, &key->ip.proto, &ovs_cb.mru); 488*1b83bf44SXin Long if (err) 489*1b83bf44SXin Long return err; 490*1b83bf44SXin Long 491ad06a566SGreg Rose /* The key extracted from the fragment that completed this datagram 492ad06a566SGreg Rose * likely didn't have an L4 header, so regenerate it. 493ad06a566SGreg Rose */ 494ad06a566SGreg Rose ovs_flow_key_update_l3l4(skb, key); 4957f8a436eSJoe Stringer key->ip.frag = OVS_FRAG_TYPE_NONE; 4967f8a436eSJoe Stringer *OVS_CB(skb) = ovs_cb; 4977f8a436eSJoe Stringer 4987f8a436eSJoe Stringer return 0; 4997f8a436eSJoe Stringer } 5007f8a436eSJoe Stringer 5017f8a436eSJoe Stringer static struct nf_conntrack_expect * 5027f8a436eSJoe Stringer ovs_ct_expect_find(struct net *net, const struct nf_conntrack_zone *zone, 5037f8a436eSJoe Stringer u16 proto, const struct sk_buff *skb) 5047f8a436eSJoe Stringer { 5057f8a436eSJoe Stringer struct nf_conntrack_tuple tuple; 506cf5d7091SJarno Rajahalme struct nf_conntrack_expect *exp; 5077f8a436eSJoe Stringer 508a31f1adcSEric W. Biederman if (!nf_ct_get_tuplepr(skb, skb_network_offset(skb), proto, net, &tuple)) 5097f8a436eSJoe Stringer return NULL; 510cf5d7091SJarno Rajahalme 511cf5d7091SJarno Rajahalme exp = __nf_ct_expect_find(net, zone, &tuple); 512cf5d7091SJarno Rajahalme if (exp) { 513cf5d7091SJarno Rajahalme struct nf_conntrack_tuple_hash *h; 514cf5d7091SJarno Rajahalme 515cf5d7091SJarno Rajahalme /* Delete existing conntrack entry, if it clashes with the 516cf5d7091SJarno Rajahalme * expectation. This can happen since conntrack ALGs do not 517cf5d7091SJarno Rajahalme * check for clashes between (new) expectations and existing 518cf5d7091SJarno Rajahalme * conntrack entries. nf_conntrack_in() will check the 519cf5d7091SJarno Rajahalme * expectations only if a conntrack entry can not be found, 520cf5d7091SJarno Rajahalme * which can lead to OVS finding the expectation (here) in the 521cf5d7091SJarno Rajahalme * init direction, but which will not be removed by the 522cf5d7091SJarno Rajahalme * nf_conntrack_in() call, if a matching conntrack entry is 523cf5d7091SJarno Rajahalme * found instead. In this case all init direction packets 524cf5d7091SJarno Rajahalme * would be reported as new related packets, while reply 525cf5d7091SJarno Rajahalme * direction packets would be reported as un-related 526cf5d7091SJarno Rajahalme * established packets. 527cf5d7091SJarno Rajahalme */ 528cf5d7091SJarno Rajahalme h = nf_conntrack_find_get(net, zone, &tuple); 529cf5d7091SJarno Rajahalme if (h) { 530cf5d7091SJarno Rajahalme struct nf_conn *ct = nf_ct_tuplehash_to_ctrack(h); 531cf5d7091SJarno Rajahalme 532cf5d7091SJarno Rajahalme nf_ct_delete(ct, 0, 0); 533408bdcfcSFlorian Westphal nf_ct_put(ct); 534cf5d7091SJarno Rajahalme } 535cf5d7091SJarno Rajahalme } 536cf5d7091SJarno Rajahalme 537cf5d7091SJarno Rajahalme return exp; 5387f8a436eSJoe Stringer } 5397f8a436eSJoe Stringer 540289f2253SJarno Rajahalme /* This replicates logic from nf_conntrack_core.c that is not exported. */ 541289f2253SJarno Rajahalme static enum ip_conntrack_info 542289f2253SJarno Rajahalme ovs_ct_get_info(const struct nf_conntrack_tuple_hash *h) 543289f2253SJarno Rajahalme { 544289f2253SJarno Rajahalme const struct nf_conn *ct = nf_ct_tuplehash_to_ctrack(h); 545289f2253SJarno Rajahalme 546289f2253SJarno Rajahalme if (NF_CT_DIRECTION(h) == IP_CT_DIR_REPLY) 547289f2253SJarno Rajahalme return IP_CT_ESTABLISHED_REPLY; 548289f2253SJarno Rajahalme /* Once we've had two way comms, always ESTABLISHED. */ 549289f2253SJarno Rajahalme if (test_bit(IPS_SEEN_REPLY_BIT, &ct->status)) 550289f2253SJarno Rajahalme return IP_CT_ESTABLISHED; 551289f2253SJarno Rajahalme if (test_bit(IPS_EXPECTED_BIT, &ct->status)) 552289f2253SJarno Rajahalme return IP_CT_RELATED; 553289f2253SJarno Rajahalme return IP_CT_NEW; 554289f2253SJarno Rajahalme } 555289f2253SJarno Rajahalme 556289f2253SJarno Rajahalme /* Find an existing connection which this packet belongs to without 557289f2253SJarno Rajahalme * re-attributing statistics or modifying the connection state. This allows an 5585e17da63SJarno Rajahalme * skb->_nfct lost due to an upcall to be recovered during actions execution. 559289f2253SJarno Rajahalme * 560289f2253SJarno Rajahalme * Must be called with rcu_read_lock. 561289f2253SJarno Rajahalme * 5625e17da63SJarno Rajahalme * On success, populates skb->_nfct and returns the connection. Returns NULL 5635e17da63SJarno Rajahalme * if there is no existing entry. 564289f2253SJarno Rajahalme */ 565289f2253SJarno Rajahalme static struct nf_conn * 566289f2253SJarno Rajahalme ovs_ct_find_existing(struct net *net, const struct nf_conntrack_zone *zone, 5679ff464dbSJarno Rajahalme u8 l3num, struct sk_buff *skb, bool natted) 568289f2253SJarno Rajahalme { 569289f2253SJarno Rajahalme struct nf_conntrack_tuple tuple; 570289f2253SJarno Rajahalme struct nf_conntrack_tuple_hash *h; 571289f2253SJarno Rajahalme struct nf_conn *ct; 572289f2253SJarno Rajahalme 57360e3be94SFlorian Westphal if (!nf_ct_get_tuplepr(skb, skb_network_offset(skb), l3num, 57460e3be94SFlorian Westphal net, &tuple)) { 575289f2253SJarno Rajahalme pr_debug("ovs_ct_find_existing: Can't get tuple\n"); 576289f2253SJarno Rajahalme return NULL; 577289f2253SJarno Rajahalme } 578289f2253SJarno Rajahalme 5799ff464dbSJarno Rajahalme /* Must invert the tuple if skb has been transformed by NAT. */ 5809ff464dbSJarno Rajahalme if (natted) { 5819ff464dbSJarno Rajahalme struct nf_conntrack_tuple inverse; 5829ff464dbSJarno Rajahalme 583303e0c55SFlorian Westphal if (!nf_ct_invert_tuple(&inverse, &tuple)) { 5849ff464dbSJarno Rajahalme pr_debug("ovs_ct_find_existing: Inversion failed!\n"); 5859ff464dbSJarno Rajahalme return NULL; 5869ff464dbSJarno Rajahalme } 5879ff464dbSJarno Rajahalme tuple = inverse; 5889ff464dbSJarno Rajahalme } 5899ff464dbSJarno Rajahalme 590289f2253SJarno Rajahalme /* look for tuple match */ 591289f2253SJarno Rajahalme h = nf_conntrack_find_get(net, zone, &tuple); 592289f2253SJarno Rajahalme if (!h) 593289f2253SJarno Rajahalme return NULL; /* Not found. */ 594289f2253SJarno Rajahalme 595289f2253SJarno Rajahalme ct = nf_ct_tuplehash_to_ctrack(h); 596289f2253SJarno Rajahalme 5979ff464dbSJarno Rajahalme /* Inverted packet tuple matches the reverse direction conntrack tuple, 5989ff464dbSJarno Rajahalme * select the other tuplehash to get the right 'ctinfo' bits for this 5999ff464dbSJarno Rajahalme * packet. 6009ff464dbSJarno Rajahalme */ 6019ff464dbSJarno Rajahalme if (natted) 6029ff464dbSJarno Rajahalme h = &ct->tuplehash[!h->tuple.dst.dir]; 6039ff464dbSJarno Rajahalme 604c74454faSFlorian Westphal nf_ct_set(skb, ct, ovs_ct_get_info(h)); 605289f2253SJarno Rajahalme return ct; 606289f2253SJarno Rajahalme } 607289f2253SJarno Rajahalme 6088b97ac5bSGreg Rose static 6098b97ac5bSGreg Rose struct nf_conn *ovs_ct_executed(struct net *net, 6108b97ac5bSGreg Rose const struct sw_flow_key *key, 6118b97ac5bSGreg Rose const struct ovs_conntrack_info *info, 6128b97ac5bSGreg Rose struct sk_buff *skb, 6138b97ac5bSGreg Rose bool *ct_executed) 6148b97ac5bSGreg Rose { 6158b97ac5bSGreg Rose struct nf_conn *ct = NULL; 6168b97ac5bSGreg Rose 6178b97ac5bSGreg Rose /* If no ct, check if we have evidence that an existing conntrack entry 6188b97ac5bSGreg Rose * might be found for this skb. This happens when we lose a skb->_nfct 6198b97ac5bSGreg Rose * due to an upcall, or if the direction is being forced. If the 6208b97ac5bSGreg Rose * connection was not confirmed, it is not cached and needs to be run 6218b97ac5bSGreg Rose * through conntrack again. 6228b97ac5bSGreg Rose */ 6238b97ac5bSGreg Rose *ct_executed = (key->ct_state & OVS_CS_F_TRACKED) && 6248b97ac5bSGreg Rose !(key->ct_state & OVS_CS_F_INVALID) && 6258b97ac5bSGreg Rose (key->ct_zone == info->zone.id); 6268b97ac5bSGreg Rose 6278b97ac5bSGreg Rose if (*ct_executed || (!key->ct_state && info->force)) { 6288b97ac5bSGreg Rose ct = ovs_ct_find_existing(net, &info->zone, info->family, skb, 6298b97ac5bSGreg Rose !!(key->ct_state & 6308b97ac5bSGreg Rose OVS_CS_F_NAT_MASK)); 6318b97ac5bSGreg Rose } 6328b97ac5bSGreg Rose 6338b97ac5bSGreg Rose return ct; 6348b97ac5bSGreg Rose } 6358b97ac5bSGreg Rose 6365e17da63SJarno Rajahalme /* Determine whether skb->_nfct is equal to the result of conntrack lookup. */ 637289f2253SJarno Rajahalme static bool skb_nfct_cached(struct net *net, 638289f2253SJarno Rajahalme const struct sw_flow_key *key, 639289f2253SJarno Rajahalme const struct ovs_conntrack_info *info, 640289f2253SJarno Rajahalme struct sk_buff *skb) 6417f8a436eSJoe Stringer { 6427f8a436eSJoe Stringer enum ip_conntrack_info ctinfo; 6437f8a436eSJoe Stringer struct nf_conn *ct; 6448b97ac5bSGreg Rose bool ct_executed = true; 6457f8a436eSJoe Stringer 6467f8a436eSJoe Stringer ct = nf_ct_get(skb, &ctinfo); 6478b97ac5bSGreg Rose if (!ct) 6488b97ac5bSGreg Rose ct = ovs_ct_executed(net, key, info, skb, &ct_executed); 6498b97ac5bSGreg Rose 650dd41d33fSJarno Rajahalme if (ct) 651dd41d33fSJarno Rajahalme nf_ct_get(skb, &ctinfo); 6528b97ac5bSGreg Rose else 6537f8a436eSJoe Stringer return false; 6548b97ac5bSGreg Rose 6557f8a436eSJoe Stringer if (!net_eq(net, read_pnet(&ct->ct_net))) 6567f8a436eSJoe Stringer return false; 6577f8a436eSJoe Stringer if (!nf_ct_zone_equal_any(info->ct, nf_ct_zone(ct))) 6587f8a436eSJoe Stringer return false; 659cae3a262SJoe Stringer if (info->helper) { 660cae3a262SJoe Stringer struct nf_conn_help *help; 661cae3a262SJoe Stringer 662cae3a262SJoe Stringer help = nf_ct_ext_find(ct, NF_CT_EXT_HELPER); 663cae3a262SJoe Stringer if (help && rcu_access_pointer(help->helper) != info->helper) 664cae3a262SJoe Stringer return false; 665cae3a262SJoe Stringer } 66671778951SYi-Hung Wei if (info->nf_ct_timeout) { 66771778951SYi-Hung Wei struct nf_conn_timeout *timeout_ext; 66871778951SYi-Hung Wei 66971778951SYi-Hung Wei timeout_ext = nf_ct_timeout_find(ct); 67071778951SYi-Hung Wei if (!timeout_ext || info->nf_ct_timeout != 67171778951SYi-Hung Wei rcu_dereference(timeout_ext->timeout)) 67271778951SYi-Hung Wei return false; 67371778951SYi-Hung Wei } 674dd41d33fSJarno Rajahalme /* Force conntrack entry direction to the current packet? */ 675dd41d33fSJarno Rajahalme if (info->force && CTINFO2DIR(ctinfo) != IP_CT_DIR_ORIGINAL) { 676dd41d33fSJarno Rajahalme /* Delete the conntrack entry if confirmed, else just release 677dd41d33fSJarno Rajahalme * the reference. 678dd41d33fSJarno Rajahalme */ 679dd41d33fSJarno Rajahalme if (nf_ct_is_confirmed(ct)) 680dd41d33fSJarno Rajahalme nf_ct_delete(ct, 0, 0); 681b768b16dSJarno Rajahalme 682408bdcfcSFlorian Westphal nf_ct_put(ct); 683dd41d33fSJarno Rajahalme nf_ct_set(skb, NULL, 0); 684dd41d33fSJarno Rajahalme return false; 685dd41d33fSJarno Rajahalme } 6867f8a436eSJoe Stringer 6878b97ac5bSGreg Rose return ct_executed; 6887f8a436eSJoe Stringer } 6897f8a436eSJoe Stringer 6904806e975SFlorian Westphal #if IS_ENABLED(CONFIG_NF_NAT) 69160b44ca6SAaron Conole static void ovs_nat_update_key(struct sw_flow_key *key, 69260b44ca6SAaron Conole const struct sk_buff *skb, 69360b44ca6SAaron Conole enum nf_nat_manip_type maniptype) 69460b44ca6SAaron Conole { 69560b44ca6SAaron Conole if (maniptype == NF_NAT_MANIP_SRC) { 69660b44ca6SAaron Conole __be16 src; 69760b44ca6SAaron Conole 69860b44ca6SAaron Conole key->ct_state |= OVS_CS_F_SRC_NAT; 69960b44ca6SAaron Conole if (key->eth.type == htons(ETH_P_IP)) 70060b44ca6SAaron Conole key->ipv4.addr.src = ip_hdr(skb)->saddr; 70160b44ca6SAaron Conole else if (key->eth.type == htons(ETH_P_IPV6)) 70260b44ca6SAaron Conole memcpy(&key->ipv6.addr.src, &ipv6_hdr(skb)->saddr, 70360b44ca6SAaron Conole sizeof(key->ipv6.addr.src)); 70460b44ca6SAaron Conole else 70560b44ca6SAaron Conole return; 70660b44ca6SAaron Conole 70760b44ca6SAaron Conole if (key->ip.proto == IPPROTO_UDP) 70860b44ca6SAaron Conole src = udp_hdr(skb)->source; 70960b44ca6SAaron Conole else if (key->ip.proto == IPPROTO_TCP) 71060b44ca6SAaron Conole src = tcp_hdr(skb)->source; 71160b44ca6SAaron Conole else if (key->ip.proto == IPPROTO_SCTP) 71260b44ca6SAaron Conole src = sctp_hdr(skb)->source; 71360b44ca6SAaron Conole else 71460b44ca6SAaron Conole return; 71560b44ca6SAaron Conole 71660b44ca6SAaron Conole key->tp.src = src; 71760b44ca6SAaron Conole } else { 71860b44ca6SAaron Conole __be16 dst; 71960b44ca6SAaron Conole 72060b44ca6SAaron Conole key->ct_state |= OVS_CS_F_DST_NAT; 72160b44ca6SAaron Conole if (key->eth.type == htons(ETH_P_IP)) 72260b44ca6SAaron Conole key->ipv4.addr.dst = ip_hdr(skb)->daddr; 72360b44ca6SAaron Conole else if (key->eth.type == htons(ETH_P_IPV6)) 72460b44ca6SAaron Conole memcpy(&key->ipv6.addr.dst, &ipv6_hdr(skb)->daddr, 72560b44ca6SAaron Conole sizeof(key->ipv6.addr.dst)); 72660b44ca6SAaron Conole else 72760b44ca6SAaron Conole return; 72860b44ca6SAaron Conole 72960b44ca6SAaron Conole if (key->ip.proto == IPPROTO_UDP) 73060b44ca6SAaron Conole dst = udp_hdr(skb)->dest; 73160b44ca6SAaron Conole else if (key->ip.proto == IPPROTO_TCP) 73260b44ca6SAaron Conole dst = tcp_hdr(skb)->dest; 73360b44ca6SAaron Conole else if (key->ip.proto == IPPROTO_SCTP) 73460b44ca6SAaron Conole dst = sctp_hdr(skb)->dest; 73560b44ca6SAaron Conole else 73660b44ca6SAaron Conole return; 73760b44ca6SAaron Conole 73860b44ca6SAaron Conole key->tp.dst = dst; 73960b44ca6SAaron Conole } 74060b44ca6SAaron Conole } 74160b44ca6SAaron Conole 74205752523SJarno Rajahalme /* Returns NF_DROP if the packet should be dropped, NF_ACCEPT otherwise. */ 74305752523SJarno Rajahalme static int ovs_ct_nat(struct net *net, struct sw_flow_key *key, 74405752523SJarno Rajahalme const struct ovs_conntrack_info *info, 74505752523SJarno Rajahalme struct sk_buff *skb, struct nf_conn *ct, 74605752523SJarno Rajahalme enum ip_conntrack_info ctinfo) 74705752523SJarno Rajahalme { 748ebddb140SXin Long int err, action = 0; 74905752523SJarno Rajahalme 75077959289SXin Long if (!(info->nat & OVS_CT_NAT)) 75177959289SXin Long return NF_ACCEPT; 752ebddb140SXin Long if (info->nat & OVS_CT_SRC_NAT) 753ebddb140SXin Long action |= BIT(NF_NAT_MANIP_SRC); 754ebddb140SXin Long if (info->nat & OVS_CT_DST_NAT) 755ebddb140SXin Long action |= BIT(NF_NAT_MANIP_DST); 75677959289SXin Long 757ebddb140SXin Long err = nf_ct_nat(skb, ct, ctinfo, &action, &info->range, info->commit); 75805752523SJarno Rajahalme 759ebddb140SXin Long if (action & BIT(NF_NAT_MANIP_SRC)) 760ebddb140SXin Long ovs_nat_update_key(key, skb, NF_NAT_MANIP_SRC); 761ebddb140SXin Long if (action & BIT(NF_NAT_MANIP_DST)) 762ebddb140SXin Long ovs_nat_update_key(key, skb, NF_NAT_MANIP_DST); 7635d50aa83SAaron Conole 76405752523SJarno Rajahalme return err; 76505752523SJarno Rajahalme } 7664806e975SFlorian Westphal #else /* !CONFIG_NF_NAT */ 76705752523SJarno Rajahalme static int ovs_ct_nat(struct net *net, struct sw_flow_key *key, 76805752523SJarno Rajahalme const struct ovs_conntrack_info *info, 76905752523SJarno Rajahalme struct sk_buff *skb, struct nf_conn *ct, 77005752523SJarno Rajahalme enum ip_conntrack_info ctinfo) 77105752523SJarno Rajahalme { 77205752523SJarno Rajahalme return NF_ACCEPT; 77305752523SJarno Rajahalme } 77405752523SJarno Rajahalme #endif 77505752523SJarno Rajahalme 7769f13ded8SJarno Rajahalme /* Pass 'skb' through conntrack in 'net', using zone configured in 'info', if 777394e910eSJarno Rajahalme * not done already. Update key with new CT state after passing the packet 778394e910eSJarno Rajahalme * through conntrack. 7795e17da63SJarno Rajahalme * Note that if the packet is deemed invalid by conntrack, skb->_nfct will be 7809f13ded8SJarno Rajahalme * set to NULL and 0 will be returned. 7819f13ded8SJarno Rajahalme */ 7824f0909eeSJoe Stringer static int __ovs_ct_lookup(struct net *net, struct sw_flow_key *key, 7837f8a436eSJoe Stringer const struct ovs_conntrack_info *info, 7847f8a436eSJoe Stringer struct sk_buff *skb) 7857f8a436eSJoe Stringer { 7867f8a436eSJoe Stringer /* If we are recirculating packets to match on conntrack fields and 7877f8a436eSJoe Stringer * committing with a separate conntrack action, then we don't need to 7887f8a436eSJoe Stringer * actually run the packet through conntrack twice unless it's for a 7897f8a436eSJoe Stringer * different zone. 7907f8a436eSJoe Stringer */ 79128b6e0c1SJarno Rajahalme bool cached = skb_nfct_cached(net, key, info, skb); 79228b6e0c1SJarno Rajahalme enum ip_conntrack_info ctinfo; 79328b6e0c1SJarno Rajahalme struct nf_conn *ct; 79428b6e0c1SJarno Rajahalme 79528b6e0c1SJarno Rajahalme if (!cached) { 79693e66024SFlorian Westphal struct nf_hook_state state = { 79793e66024SFlorian Westphal .hook = NF_INET_PRE_ROUTING, 79893e66024SFlorian Westphal .pf = info->family, 79993e66024SFlorian Westphal .net = net, 80093e66024SFlorian Westphal }; 8017f8a436eSJoe Stringer struct nf_conn *tmpl = info->ct; 8025b6b9293SJarno Rajahalme int err; 8037f8a436eSJoe Stringer 8047f8a436eSJoe Stringer /* Associate skb with specified zone. */ 8057f8a436eSJoe Stringer if (tmpl) { 806408bdcfcSFlorian Westphal ct = nf_ct_get(skb, &ctinfo); 807408bdcfcSFlorian Westphal nf_ct_put(ct); 8087f8a436eSJoe Stringer nf_conntrack_get(&tmpl->ct_general); 809c74454faSFlorian Westphal nf_ct_set(skb, tmpl, IP_CT_NEW); 8107f8a436eSJoe Stringer } 8117f8a436eSJoe Stringer 81293e66024SFlorian Westphal err = nf_conntrack_in(skb, &state); 8135b6b9293SJarno Rajahalme if (err != NF_ACCEPT) 8147f8a436eSJoe Stringer return -ENOENT; 815cae3a262SJoe Stringer 81605752523SJarno Rajahalme /* Clear CT state NAT flags to mark that we have not yet done 81705752523SJarno Rajahalme * NAT after the nf_conntrack_in() call. We can actually clear 81805752523SJarno Rajahalme * the whole state, as it will be re-initialized below. 81905752523SJarno Rajahalme */ 820316d4d78SJarno Rajahalme key->ct_state = 0; 82105752523SJarno Rajahalme 82205752523SJarno Rajahalme /* Update the key, but keep the NAT flags. */ 82305752523SJarno Rajahalme ovs_ct_update_key(skb, info, key, true, true); 82405752523SJarno Rajahalme } 82505752523SJarno Rajahalme 82605752523SJarno Rajahalme ct = nf_ct_get(skb, &ctinfo); 82705752523SJarno Rajahalme if (ct) { 828248d45f1SYi-Hung Wei bool add_helper = false; 829248d45f1SYi-Hung Wei 83005752523SJarno Rajahalme /* Packets starting a new connection must be NATted before the 83105752523SJarno Rajahalme * helper, so that the helper knows about the NAT. We enforce 83205752523SJarno Rajahalme * this by delaying both NAT and helper calls for unconfirmed 83305752523SJarno Rajahalme * connections until the committing CT action. For later 83405752523SJarno Rajahalme * packets NAT and Helper may be called in either order. 83505752523SJarno Rajahalme * 83605752523SJarno Rajahalme * NAT will be done only if the CT action has NAT, and only 83705752523SJarno Rajahalme * once per packet (per zone), as guarded by the NAT bits in 838316d4d78SJarno Rajahalme * the key->ct_state. 83905752523SJarno Rajahalme */ 840316d4d78SJarno Rajahalme if (info->nat && !(key->ct_state & OVS_CS_F_NAT_MASK) && 84105752523SJarno Rajahalme (nf_ct_is_confirmed(ct) || info->commit) && 84205752523SJarno Rajahalme ovs_ct_nat(net, key, info, skb, ct, ctinfo) != NF_ACCEPT) { 84305752523SJarno Rajahalme return -EINVAL; 84428b6e0c1SJarno Rajahalme } 845394e910eSJarno Rajahalme 84616ec3d4fSJoe Stringer /* Userspace may decide to perform a ct lookup without a helper 847248d45f1SYi-Hung Wei * specified followed by a (recirculate and) commit with one, 848248d45f1SYi-Hung Wei * or attach a helper in a later commit. Therefore, for 849248d45f1SYi-Hung Wei * connections which we will commit, we may need to attach 850248d45f1SYi-Hung Wei * the helper here. 85116ec3d4fSJoe Stringer */ 8523c186054SXin Long if (!nf_ct_is_confirmed(ct) && info->commit && 8533c186054SXin Long info->helper && !nfct_help(ct)) { 85416ec3d4fSJoe Stringer int err = __nf_ct_try_assign_helper(ct, info->ct, 85516ec3d4fSJoe Stringer GFP_ATOMIC); 85616ec3d4fSJoe Stringer if (err) 85716ec3d4fSJoe Stringer return err; 858248d45f1SYi-Hung Wei add_helper = true; 859fa7e428cSFlavio Leitner 860fa7e428cSFlavio Leitner /* helper installed, add seqadj if NAT is required */ 861fa7e428cSFlavio Leitner if (info->nat && !nfct_seqadj(ct)) { 862fa7e428cSFlavio Leitner if (!nfct_seqadj_ext_add(ct)) 863fa7e428cSFlavio Leitner return -EINVAL; 864fa7e428cSFlavio Leitner } 86516ec3d4fSJoe Stringer } 86616ec3d4fSJoe Stringer 86728b6e0c1SJarno Rajahalme /* Call the helper only if: 868248d45f1SYi-Hung Wei * - nf_conntrack_in() was executed above ("!cached") or a 869248d45f1SYi-Hung Wei * helper was just attached ("add_helper") for a confirmed 870248d45f1SYi-Hung Wei * connection, or 87128b6e0c1SJarno Rajahalme * - When committing an unconfirmed connection. 87228b6e0c1SJarno Rajahalme */ 873248d45f1SYi-Hung Wei if ((nf_ct_is_confirmed(ct) ? !cached || add_helper : 874248d45f1SYi-Hung Wei info->commit) && 875ca71277fSXin Long nf_ct_helper(skb, ct, ctinfo, info->family) != NF_ACCEPT) { 876cae3a262SJoe Stringer return -EINVAL; 877cae3a262SJoe Stringer } 878e2ef5203SNuman Siddique 879e2ef5203SNuman Siddique if (nf_ct_protonum(ct) == IPPROTO_TCP && 880e2ef5203SNuman Siddique nf_ct_is_confirmed(ct) && nf_conntrack_tcp_established(ct)) { 881e2ef5203SNuman Siddique /* Be liberal for tcp packets so that out-of-window 882e2ef5203SNuman Siddique * packets are not marked invalid. 883e2ef5203SNuman Siddique */ 884e2ef5203SNuman Siddique nf_ct_set_tcp_be_liberal(ct); 885e2ef5203SNuman Siddique } 886b702436aSPaul Blakey 887b702436aSPaul Blakey nf_conn_act_ct_ext_fill(skb, ct, ctinfo); 88805752523SJarno Rajahalme } 8897f8a436eSJoe Stringer 8907f8a436eSJoe Stringer return 0; 8917f8a436eSJoe Stringer } 8927f8a436eSJoe Stringer 8937f8a436eSJoe Stringer /* Lookup connection and read fields into key. */ 8947f8a436eSJoe Stringer static int ovs_ct_lookup(struct net *net, struct sw_flow_key *key, 8957f8a436eSJoe Stringer const struct ovs_conntrack_info *info, 8967f8a436eSJoe Stringer struct sk_buff *skb) 8977f8a436eSJoe Stringer { 8987f8a436eSJoe Stringer struct nf_conntrack_expect *exp; 8997f8a436eSJoe Stringer 9009f13ded8SJarno Rajahalme /* If we pass an expected packet through nf_conntrack_in() the 9019f13ded8SJarno Rajahalme * expectation is typically removed, but the packet could still be 9029f13ded8SJarno Rajahalme * lost in upcall processing. To prevent this from happening we 9039f13ded8SJarno Rajahalme * perform an explicit expectation lookup. Expected connections are 9049f13ded8SJarno Rajahalme * always new, and will be passed through conntrack only when they are 9059f13ded8SJarno Rajahalme * committed, as it is OK to remove the expectation at that time. 9069f13ded8SJarno Rajahalme */ 9077f8a436eSJoe Stringer exp = ovs_ct_expect_find(net, &info->zone, info->family, skb); 9087f8a436eSJoe Stringer if (exp) { 9097f8a436eSJoe Stringer u8 state; 9107f8a436eSJoe Stringer 91105752523SJarno Rajahalme /* NOTE: New connections are NATted and Helped only when 91205752523SJarno Rajahalme * committed, so we are not calling into NAT here. 91305752523SJarno Rajahalme */ 9147f8a436eSJoe Stringer state = OVS_CS_F_TRACKED | OVS_CS_F_NEW | OVS_CS_F_RELATED; 915182e3042SJoe Stringer __ovs_ct_update_key(key, state, &info->zone, exp->master); 916d913d3a7SSamuel Gauthier } else { 917d913d3a7SSamuel Gauthier struct nf_conn *ct; 918d913d3a7SSamuel Gauthier int err; 919d913d3a7SSamuel Gauthier 920d913d3a7SSamuel Gauthier err = __ovs_ct_lookup(net, key, info, skb); 921d913d3a7SSamuel Gauthier if (err) 922d913d3a7SSamuel Gauthier return err; 923d913d3a7SSamuel Gauthier 924cb9c6836SFlorian Westphal ct = (struct nf_conn *)skb_nfct(skb); 925d913d3a7SSamuel Gauthier if (ct) 926d913d3a7SSamuel Gauthier nf_ct_deliver_cached_events(ct); 927d913d3a7SSamuel Gauthier } 9287f8a436eSJoe Stringer 9297f8a436eSJoe Stringer return 0; 9307f8a436eSJoe Stringer } 9317f8a436eSJoe Stringer 93233db4125SJoe Stringer static bool labels_nonzero(const struct ovs_key_ct_labels *labels) 933c2ac6673SJoe Stringer { 934c2ac6673SJoe Stringer size_t i; 935c2ac6673SJoe Stringer 936cb80d58fSJarno Rajahalme for (i = 0; i < OVS_CT_LABELS_LEN_32; i++) 937cb80d58fSJarno Rajahalme if (labels->ct_labels_32[i]) 938c2ac6673SJoe Stringer return true; 939c2ac6673SJoe Stringer 940c2ac6673SJoe Stringer return false; 941c2ac6673SJoe Stringer } 942c2ac6673SJoe Stringer 94311efd5cbSYi-Hung Wei #if IS_ENABLED(CONFIG_NETFILTER_CONNCOUNT) 94411efd5cbSYi-Hung Wei static struct hlist_head *ct_limit_hash_bucket( 94511efd5cbSYi-Hung Wei const struct ovs_ct_limit_info *info, u16 zone) 94611efd5cbSYi-Hung Wei { 94711efd5cbSYi-Hung Wei return &info->limits[zone & (CT_LIMIT_HASH_BUCKETS - 1)]; 94811efd5cbSYi-Hung Wei } 94911efd5cbSYi-Hung Wei 95011efd5cbSYi-Hung Wei /* Call with ovs_mutex */ 95111efd5cbSYi-Hung Wei static void ct_limit_set(const struct ovs_ct_limit_info *info, 95211efd5cbSYi-Hung Wei struct ovs_ct_limit *new_ct_limit) 95311efd5cbSYi-Hung Wei { 95411efd5cbSYi-Hung Wei struct ovs_ct_limit *ct_limit; 95511efd5cbSYi-Hung Wei struct hlist_head *head; 95611efd5cbSYi-Hung Wei 95711efd5cbSYi-Hung Wei head = ct_limit_hash_bucket(info, new_ct_limit->zone); 95811efd5cbSYi-Hung Wei hlist_for_each_entry_rcu(ct_limit, head, hlist_node) { 95911efd5cbSYi-Hung Wei if (ct_limit->zone == new_ct_limit->zone) { 96011efd5cbSYi-Hung Wei hlist_replace_rcu(&ct_limit->hlist_node, 96111efd5cbSYi-Hung Wei &new_ct_limit->hlist_node); 96211efd5cbSYi-Hung Wei kfree_rcu(ct_limit, rcu); 96311efd5cbSYi-Hung Wei return; 96411efd5cbSYi-Hung Wei } 96511efd5cbSYi-Hung Wei } 96611efd5cbSYi-Hung Wei 96711efd5cbSYi-Hung Wei hlist_add_head_rcu(&new_ct_limit->hlist_node, head); 96811efd5cbSYi-Hung Wei } 96911efd5cbSYi-Hung Wei 97011efd5cbSYi-Hung Wei /* Call with ovs_mutex */ 97111efd5cbSYi-Hung Wei static void ct_limit_del(const struct ovs_ct_limit_info *info, u16 zone) 97211efd5cbSYi-Hung Wei { 97311efd5cbSYi-Hung Wei struct ovs_ct_limit *ct_limit; 97411efd5cbSYi-Hung Wei struct hlist_head *head; 97511efd5cbSYi-Hung Wei struct hlist_node *n; 97611efd5cbSYi-Hung Wei 97711efd5cbSYi-Hung Wei head = ct_limit_hash_bucket(info, zone); 97811efd5cbSYi-Hung Wei hlist_for_each_entry_safe(ct_limit, n, head, hlist_node) { 97911efd5cbSYi-Hung Wei if (ct_limit->zone == zone) { 98011efd5cbSYi-Hung Wei hlist_del_rcu(&ct_limit->hlist_node); 98111efd5cbSYi-Hung Wei kfree_rcu(ct_limit, rcu); 98211efd5cbSYi-Hung Wei return; 98311efd5cbSYi-Hung Wei } 98411efd5cbSYi-Hung Wei } 98511efd5cbSYi-Hung Wei } 98611efd5cbSYi-Hung Wei 98711efd5cbSYi-Hung Wei /* Call with RCU read lock */ 98811efd5cbSYi-Hung Wei static u32 ct_limit_get(const struct ovs_ct_limit_info *info, u16 zone) 98911efd5cbSYi-Hung Wei { 99011efd5cbSYi-Hung Wei struct ovs_ct_limit *ct_limit; 99111efd5cbSYi-Hung Wei struct hlist_head *head; 99211efd5cbSYi-Hung Wei 99311efd5cbSYi-Hung Wei head = ct_limit_hash_bucket(info, zone); 99411efd5cbSYi-Hung Wei hlist_for_each_entry_rcu(ct_limit, head, hlist_node) { 99511efd5cbSYi-Hung Wei if (ct_limit->zone == zone) 99611efd5cbSYi-Hung Wei return ct_limit->limit; 99711efd5cbSYi-Hung Wei } 99811efd5cbSYi-Hung Wei 99911efd5cbSYi-Hung Wei return info->default_limit; 100011efd5cbSYi-Hung Wei } 100111efd5cbSYi-Hung Wei 100211efd5cbSYi-Hung Wei static int ovs_ct_check_limit(struct net *net, 100311efd5cbSYi-Hung Wei const struct ovs_conntrack_info *info, 100411efd5cbSYi-Hung Wei const struct nf_conntrack_tuple *tuple) 100511efd5cbSYi-Hung Wei { 100611efd5cbSYi-Hung Wei struct ovs_net *ovs_net = net_generic(net, ovs_net_id); 100711efd5cbSYi-Hung Wei const struct ovs_ct_limit_info *ct_limit_info = ovs_net->ct_limit_info; 100811efd5cbSYi-Hung Wei u32 per_zone_limit, connections; 100911efd5cbSYi-Hung Wei u32 conncount_key; 101011efd5cbSYi-Hung Wei 101111efd5cbSYi-Hung Wei conncount_key = info->zone.id; 101211efd5cbSYi-Hung Wei 101311efd5cbSYi-Hung Wei per_zone_limit = ct_limit_get(ct_limit_info, info->zone.id); 101411efd5cbSYi-Hung Wei if (per_zone_limit == OVS_CT_LIMIT_UNLIMITED) 101511efd5cbSYi-Hung Wei return 0; 101611efd5cbSYi-Hung Wei 101711efd5cbSYi-Hung Wei connections = nf_conncount_count(net, ct_limit_info->data, 101811efd5cbSYi-Hung Wei &conncount_key, tuple, &info->zone); 101911efd5cbSYi-Hung Wei if (connections > per_zone_limit) 102011efd5cbSYi-Hung Wei return -ENOMEM; 102111efd5cbSYi-Hung Wei 102211efd5cbSYi-Hung Wei return 0; 102311efd5cbSYi-Hung Wei } 102411efd5cbSYi-Hung Wei #endif 102511efd5cbSYi-Hung Wei 10267d904c7bSJarno Rajahalme /* Lookup connection and confirm if unconfirmed. */ 10277d904c7bSJarno Rajahalme static int ovs_ct_commit(struct net *net, struct sw_flow_key *key, 10287d904c7bSJarno Rajahalme const struct ovs_conntrack_info *info, 10297d904c7bSJarno Rajahalme struct sk_buff *skb) 10307d904c7bSJarno Rajahalme { 10316ffcea79SJarno Rajahalme enum ip_conntrack_info ctinfo; 10326ffcea79SJarno Rajahalme struct nf_conn *ct; 10337d904c7bSJarno Rajahalme int err; 10347d904c7bSJarno Rajahalme 10357d904c7bSJarno Rajahalme err = __ovs_ct_lookup(net, key, info, skb); 10367d904c7bSJarno Rajahalme if (err) 10377d904c7bSJarno Rajahalme return err; 10387d904c7bSJarno Rajahalme 10396ffcea79SJarno Rajahalme /* The connection could be invalid, in which case this is a no-op.*/ 10406ffcea79SJarno Rajahalme ct = nf_ct_get(skb, &ctinfo); 10416ffcea79SJarno Rajahalme if (!ct) 10426ffcea79SJarno Rajahalme return 0; 10436ffcea79SJarno Rajahalme 104411efd5cbSYi-Hung Wei #if IS_ENABLED(CONFIG_NETFILTER_CONNCOUNT) 104511efd5cbSYi-Hung Wei if (static_branch_unlikely(&ovs_ct_limit_enabled)) { 104611efd5cbSYi-Hung Wei if (!nf_ct_is_confirmed(ct)) { 104711efd5cbSYi-Hung Wei err = ovs_ct_check_limit(net, info, 104811efd5cbSYi-Hung Wei &ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple); 104911efd5cbSYi-Hung Wei if (err) { 105011efd5cbSYi-Hung Wei net_warn_ratelimited("openvswitch: zone: %u " 105143d0e960SColin Ian King "exceeds conntrack limit\n", 105211efd5cbSYi-Hung Wei info->zone.id); 105311efd5cbSYi-Hung Wei return err; 105411efd5cbSYi-Hung Wei } 105511efd5cbSYi-Hung Wei } 105611efd5cbSYi-Hung Wei } 105711efd5cbSYi-Hung Wei #endif 105811efd5cbSYi-Hung Wei 105912064551SJarno Rajahalme /* Set the conntrack event mask if given. NEW and DELETE events have 106012064551SJarno Rajahalme * their own groups, but the NFNLGRP_CONNTRACK_UPDATE group listener 106112064551SJarno Rajahalme * typically would receive many kinds of updates. Setting the event 106212064551SJarno Rajahalme * mask allows those events to be filtered. The set event mask will 106312064551SJarno Rajahalme * remain in effect for the lifetime of the connection unless changed 106412064551SJarno Rajahalme * by a further CT action with both the commit flag and the eventmask 106512064551SJarno Rajahalme * option. */ 106612064551SJarno Rajahalme if (info->have_eventmask) { 106712064551SJarno Rajahalme struct nf_conntrack_ecache *cache = nf_ct_ecache_find(ct); 106812064551SJarno Rajahalme 106912064551SJarno Rajahalme if (cache) 107012064551SJarno Rajahalme cache->ctmask = info->eventmask; 107112064551SJarno Rajahalme } 107212064551SJarno Rajahalme 10737d904c7bSJarno Rajahalme /* Apply changes before confirming the connection so that the initial 10747d904c7bSJarno Rajahalme * conntrack NEW netlink event carries the values given in the CT 10757d904c7bSJarno Rajahalme * action. 10767d904c7bSJarno Rajahalme */ 10777d904c7bSJarno Rajahalme if (info->mark.mask) { 10786ffcea79SJarno Rajahalme err = ovs_ct_set_mark(ct, key, info->mark.value, 10797d904c7bSJarno Rajahalme info->mark.mask); 10807d904c7bSJarno Rajahalme if (err) 10817d904c7bSJarno Rajahalme return err; 10827d904c7bSJarno Rajahalme } 108309aa98adSJarno Rajahalme if (!nf_ct_is_confirmed(ct)) { 10846ffcea79SJarno Rajahalme err = ovs_ct_init_labels(ct, key, &info->labels.value, 10856ffcea79SJarno Rajahalme &info->labels.mask); 108609aa98adSJarno Rajahalme if (err) 108709aa98adSJarno Rajahalme return err; 1088b702436aSPaul Blakey 1089b702436aSPaul Blakey nf_conn_act_ct_ext_add(ct); 1090a277d516SArnd Bergmann } else if (IS_ENABLED(CONFIG_NF_CONNTRACK_LABELS) && 1091a277d516SArnd Bergmann labels_nonzero(&info->labels.mask)) { 10926ffcea79SJarno Rajahalme err = ovs_ct_set_labels(ct, key, &info->labels.value, 10937d904c7bSJarno Rajahalme &info->labels.mask); 10947d904c7bSJarno Rajahalme if (err) 10957d904c7bSJarno Rajahalme return err; 10967d904c7bSJarno Rajahalme } 10977d904c7bSJarno Rajahalme /* This will take care of sending queued events even if the connection 10987d904c7bSJarno Rajahalme * is already confirmed. 10997d904c7bSJarno Rajahalme */ 11007d904c7bSJarno Rajahalme if (nf_conntrack_confirm(skb) != NF_ACCEPT) 11017d904c7bSJarno Rajahalme return -EINVAL; 11027d904c7bSJarno Rajahalme 11037d904c7bSJarno Rajahalme return 0; 11047d904c7bSJarno Rajahalme } 11057d904c7bSJarno Rajahalme 110674c16618SJoe Stringer /* Returns 0 on success, -EINPROGRESS if 'skb' is stolen, or other nonzero 110774c16618SJoe Stringer * value if 'skb' is freed. 110874c16618SJoe Stringer */ 11097f8a436eSJoe Stringer int ovs_ct_execute(struct net *net, struct sk_buff *skb, 11107f8a436eSJoe Stringer struct sw_flow_key *key, 11117f8a436eSJoe Stringer const struct ovs_conntrack_info *info) 11127f8a436eSJoe Stringer { 11137f8a436eSJoe Stringer int nh_ofs; 11147f8a436eSJoe Stringer int err; 11157f8a436eSJoe Stringer 11167f8a436eSJoe Stringer /* The conntrack module expects to be working at L3. */ 11177f8a436eSJoe Stringer nh_ofs = skb_network_offset(skb); 111875f01a4cSLance Richardson skb_pull_rcsum(skb, nh_ofs); 11197f8a436eSJoe Stringer 112067fc5d7fSXin Long err = nf_ct_skb_network_trim(skb, info->family); 112167fc5d7fSXin Long if (err) { 112267fc5d7fSXin Long kfree_skb(skb); 11239382fe71SEd Swierk return err; 112467fc5d7fSXin Long } 11259382fe71SEd Swierk 11267f8a436eSJoe Stringer if (key->ip.frag != OVS_FRAG_TYPE_NONE) { 1127*1b83bf44SXin Long err = ovs_ct_handle_fragments(net, key, info->zone.id, 1128*1b83bf44SXin Long info->family, skb); 11297f8a436eSJoe Stringer if (err) 11307f8a436eSJoe Stringer return err; 11317f8a436eSJoe Stringer } 11327f8a436eSJoe Stringer 1133ab38a7b5SJoe Stringer if (info->commit) 11347d904c7bSJarno Rajahalme err = ovs_ct_commit(net, key, info, skb); 11357f8a436eSJoe Stringer else 11367f8a436eSJoe Stringer err = ovs_ct_lookup(net, key, info, skb); 11377f8a436eSJoe Stringer 11387d42e84eSChristophe JAILLET skb_push_rcsum(skb, nh_ofs); 113974c16618SJoe Stringer if (err) 114074c16618SJoe Stringer kfree_skb(skb); 11417f8a436eSJoe Stringer return err; 11427f8a436eSJoe Stringer } 11437f8a436eSJoe Stringer 1144b8226962SEric Garver int ovs_ct_clear(struct sk_buff *skb, struct sw_flow_key *key) 1145b8226962SEric Garver { 1146408bdcfcSFlorian Westphal enum ip_conntrack_info ctinfo; 1147408bdcfcSFlorian Westphal struct nf_conn *ct; 1148408bdcfcSFlorian Westphal 1149408bdcfcSFlorian Westphal ct = nf_ct_get(skb, &ctinfo); 1150408bdcfcSFlorian Westphal 1151408bdcfcSFlorian Westphal nf_ct_put(ct); 1152b8226962SEric Garver nf_ct_set(skb, NULL, IP_CT_UNTRACKED); 11532061ecfdSIlya Maximets 11542061ecfdSIlya Maximets if (key) 1155d29334c1Swenxu ovs_ct_fill_key(skb, key, false); 1156b8226962SEric Garver 1157b8226962SEric Garver return 0; 1158b8226962SEric Garver } 1159b8226962SEric Garver 11604806e975SFlorian Westphal #if IS_ENABLED(CONFIG_NF_NAT) 116105752523SJarno Rajahalme static int parse_nat(const struct nlattr *attr, 116205752523SJarno Rajahalme struct ovs_conntrack_info *info, bool log) 116305752523SJarno Rajahalme { 116405752523SJarno Rajahalme struct nlattr *a; 116505752523SJarno Rajahalme int rem; 116605752523SJarno Rajahalme bool have_ip_max = false; 116705752523SJarno Rajahalme bool have_proto_max = false; 116805752523SJarno Rajahalme bool ip_vers = (info->family == NFPROTO_IPV6); 116905752523SJarno Rajahalme 117005752523SJarno Rajahalme nla_for_each_nested(a, attr, rem) { 117105752523SJarno Rajahalme static const int ovs_nat_attr_lens[OVS_NAT_ATTR_MAX + 1][2] = { 117205752523SJarno Rajahalme [OVS_NAT_ATTR_SRC] = {0, 0}, 117305752523SJarno Rajahalme [OVS_NAT_ATTR_DST] = {0, 0}, 117405752523SJarno Rajahalme [OVS_NAT_ATTR_IP_MIN] = {sizeof(struct in_addr), 117505752523SJarno Rajahalme sizeof(struct in6_addr)}, 117605752523SJarno Rajahalme [OVS_NAT_ATTR_IP_MAX] = {sizeof(struct in_addr), 117705752523SJarno Rajahalme sizeof(struct in6_addr)}, 117805752523SJarno Rajahalme [OVS_NAT_ATTR_PROTO_MIN] = {sizeof(u16), sizeof(u16)}, 117905752523SJarno Rajahalme [OVS_NAT_ATTR_PROTO_MAX] = {sizeof(u16), sizeof(u16)}, 118005752523SJarno Rajahalme [OVS_NAT_ATTR_PERSISTENT] = {0, 0}, 118105752523SJarno Rajahalme [OVS_NAT_ATTR_PROTO_HASH] = {0, 0}, 118205752523SJarno Rajahalme [OVS_NAT_ATTR_PROTO_RANDOM] = {0, 0}, 118305752523SJarno Rajahalme }; 118405752523SJarno Rajahalme int type = nla_type(a); 118505752523SJarno Rajahalme 118605752523SJarno Rajahalme if (type > OVS_NAT_ATTR_MAX) { 11870ed80da5SJoe Perches OVS_NLERR(log, "Unknown NAT attribute (type=%d, max=%d)", 118805752523SJarno Rajahalme type, OVS_NAT_ATTR_MAX); 118905752523SJarno Rajahalme return -EINVAL; 119005752523SJarno Rajahalme } 119105752523SJarno Rajahalme 119205752523SJarno Rajahalme if (nla_len(a) != ovs_nat_attr_lens[type][ip_vers]) { 11930ed80da5SJoe Perches OVS_NLERR(log, "NAT attribute type %d has unexpected length (%d != %d)", 119405752523SJarno Rajahalme type, nla_len(a), 119505752523SJarno Rajahalme ovs_nat_attr_lens[type][ip_vers]); 119605752523SJarno Rajahalme return -EINVAL; 119705752523SJarno Rajahalme } 119805752523SJarno Rajahalme 119905752523SJarno Rajahalme switch (type) { 120005752523SJarno Rajahalme case OVS_NAT_ATTR_SRC: 120105752523SJarno Rajahalme case OVS_NAT_ATTR_DST: 120205752523SJarno Rajahalme if (info->nat) { 12030ed80da5SJoe Perches OVS_NLERR(log, "Only one type of NAT may be specified"); 120405752523SJarno Rajahalme return -ERANGE; 120505752523SJarno Rajahalme } 120605752523SJarno Rajahalme info->nat |= OVS_CT_NAT; 120705752523SJarno Rajahalme info->nat |= ((type == OVS_NAT_ATTR_SRC) 120805752523SJarno Rajahalme ? OVS_CT_SRC_NAT : OVS_CT_DST_NAT); 120905752523SJarno Rajahalme break; 121005752523SJarno Rajahalme 121105752523SJarno Rajahalme case OVS_NAT_ATTR_IP_MIN: 1212ac71b46eSHaishuang Yan nla_memcpy(&info->range.min_addr, a, 1213ac71b46eSHaishuang Yan sizeof(info->range.min_addr)); 121405752523SJarno Rajahalme info->range.flags |= NF_NAT_RANGE_MAP_IPS; 121505752523SJarno Rajahalme break; 121605752523SJarno Rajahalme 121705752523SJarno Rajahalme case OVS_NAT_ATTR_IP_MAX: 121805752523SJarno Rajahalme have_ip_max = true; 121905752523SJarno Rajahalme nla_memcpy(&info->range.max_addr, a, 122005752523SJarno Rajahalme sizeof(info->range.max_addr)); 122105752523SJarno Rajahalme info->range.flags |= NF_NAT_RANGE_MAP_IPS; 122205752523SJarno Rajahalme break; 122305752523SJarno Rajahalme 122405752523SJarno Rajahalme case OVS_NAT_ATTR_PROTO_MIN: 122505752523SJarno Rajahalme info->range.min_proto.all = htons(nla_get_u16(a)); 122605752523SJarno Rajahalme info->range.flags |= NF_NAT_RANGE_PROTO_SPECIFIED; 122705752523SJarno Rajahalme break; 122805752523SJarno Rajahalme 122905752523SJarno Rajahalme case OVS_NAT_ATTR_PROTO_MAX: 123005752523SJarno Rajahalme have_proto_max = true; 123105752523SJarno Rajahalme info->range.max_proto.all = htons(nla_get_u16(a)); 123205752523SJarno Rajahalme info->range.flags |= NF_NAT_RANGE_PROTO_SPECIFIED; 123305752523SJarno Rajahalme break; 123405752523SJarno Rajahalme 123505752523SJarno Rajahalme case OVS_NAT_ATTR_PERSISTENT: 123605752523SJarno Rajahalme info->range.flags |= NF_NAT_RANGE_PERSISTENT; 123705752523SJarno Rajahalme break; 123805752523SJarno Rajahalme 123905752523SJarno Rajahalme case OVS_NAT_ATTR_PROTO_HASH: 124005752523SJarno Rajahalme info->range.flags |= NF_NAT_RANGE_PROTO_RANDOM; 124105752523SJarno Rajahalme break; 124205752523SJarno Rajahalme 124305752523SJarno Rajahalme case OVS_NAT_ATTR_PROTO_RANDOM: 124405752523SJarno Rajahalme info->range.flags |= NF_NAT_RANGE_PROTO_RANDOM_FULLY; 124505752523SJarno Rajahalme break; 124605752523SJarno Rajahalme 124705752523SJarno Rajahalme default: 12480ed80da5SJoe Perches OVS_NLERR(log, "Unknown nat attribute (%d)", type); 124905752523SJarno Rajahalme return -EINVAL; 125005752523SJarno Rajahalme } 125105752523SJarno Rajahalme } 125205752523SJarno Rajahalme 125305752523SJarno Rajahalme if (rem > 0) { 12540ed80da5SJoe Perches OVS_NLERR(log, "NAT attribute has %d unknown bytes", rem); 125505752523SJarno Rajahalme return -EINVAL; 125605752523SJarno Rajahalme } 125705752523SJarno Rajahalme if (!info->nat) { 125805752523SJarno Rajahalme /* Do not allow flags if no type is given. */ 125905752523SJarno Rajahalme if (info->range.flags) { 126005752523SJarno Rajahalme OVS_NLERR(log, 1261e0b10844SJulia Lawall "NAT flags may be given only when NAT range (SRC or DST) is also specified." 126205752523SJarno Rajahalme ); 126305752523SJarno Rajahalme return -EINVAL; 126405752523SJarno Rajahalme } 126505752523SJarno Rajahalme info->nat = OVS_CT_NAT; /* NAT existing connections. */ 126605752523SJarno Rajahalme } else if (!info->commit) { 126705752523SJarno Rajahalme OVS_NLERR(log, 1268e0b10844SJulia Lawall "NAT attributes may be specified only when CT COMMIT flag is also specified." 126905752523SJarno Rajahalme ); 127005752523SJarno Rajahalme return -EINVAL; 127105752523SJarno Rajahalme } 127205752523SJarno Rajahalme /* Allow missing IP_MAX. */ 127305752523SJarno Rajahalme if (info->range.flags & NF_NAT_RANGE_MAP_IPS && !have_ip_max) { 127405752523SJarno Rajahalme memcpy(&info->range.max_addr, &info->range.min_addr, 127505752523SJarno Rajahalme sizeof(info->range.max_addr)); 127605752523SJarno Rajahalme } 127705752523SJarno Rajahalme /* Allow missing PROTO_MAX. */ 127805752523SJarno Rajahalme if (info->range.flags & NF_NAT_RANGE_PROTO_SPECIFIED && 127905752523SJarno Rajahalme !have_proto_max) { 128005752523SJarno Rajahalme info->range.max_proto.all = info->range.min_proto.all; 128105752523SJarno Rajahalme } 128205752523SJarno Rajahalme return 0; 128305752523SJarno Rajahalme } 128405752523SJarno Rajahalme #endif 128505752523SJarno Rajahalme 12867f8a436eSJoe Stringer static const struct ovs_ct_len_tbl ovs_ct_attr_lens[OVS_CT_ATTR_MAX + 1] = { 1287ab38a7b5SJoe Stringer [OVS_CT_ATTR_COMMIT] = { .minlen = 0, .maxlen = 0 }, 1288dd41d33fSJarno Rajahalme [OVS_CT_ATTR_FORCE_COMMIT] = { .minlen = 0, .maxlen = 0 }, 12897f8a436eSJoe Stringer [OVS_CT_ATTR_ZONE] = { .minlen = sizeof(u16), 12907f8a436eSJoe Stringer .maxlen = sizeof(u16) }, 1291182e3042SJoe Stringer [OVS_CT_ATTR_MARK] = { .minlen = sizeof(struct md_mark), 1292182e3042SJoe Stringer .maxlen = sizeof(struct md_mark) }, 129333db4125SJoe Stringer [OVS_CT_ATTR_LABELS] = { .minlen = sizeof(struct md_labels), 129433db4125SJoe Stringer .maxlen = sizeof(struct md_labels) }, 1295cae3a262SJoe Stringer [OVS_CT_ATTR_HELPER] = { .minlen = 1, 129605752523SJarno Rajahalme .maxlen = NF_CT_HELPER_NAME_LEN }, 12974806e975SFlorian Westphal #if IS_ENABLED(CONFIG_NF_NAT) 129805752523SJarno Rajahalme /* NAT length is checked when parsing the nested attributes. */ 129905752523SJarno Rajahalme [OVS_CT_ATTR_NAT] = { .minlen = 0, .maxlen = INT_MAX }, 130005752523SJarno Rajahalme #endif 130112064551SJarno Rajahalme [OVS_CT_ATTR_EVENTMASK] = { .minlen = sizeof(u32), 130212064551SJarno Rajahalme .maxlen = sizeof(u32) }, 130306bd2bdfSYi-Hung Wei [OVS_CT_ATTR_TIMEOUT] = { .minlen = 1, 130406bd2bdfSYi-Hung Wei .maxlen = CTNL_TIMEOUT_NAME_MAX }, 13057f8a436eSJoe Stringer }; 13067f8a436eSJoe Stringer 13077f8a436eSJoe Stringer static int parse_ct(const struct nlattr *attr, struct ovs_conntrack_info *info, 1308cae3a262SJoe Stringer const char **helper, bool log) 13097f8a436eSJoe Stringer { 13107f8a436eSJoe Stringer struct nlattr *a; 13117f8a436eSJoe Stringer int rem; 13127f8a436eSJoe Stringer 13137f8a436eSJoe Stringer nla_for_each_nested(a, attr, rem) { 13147f8a436eSJoe Stringer int type = nla_type(a); 131569ec932eSLiping Zhang int maxlen; 131669ec932eSLiping Zhang int minlen; 13177f8a436eSJoe Stringer 13187f8a436eSJoe Stringer if (type > OVS_CT_ATTR_MAX) { 13197f8a436eSJoe Stringer OVS_NLERR(log, 13207f8a436eSJoe Stringer "Unknown conntrack attr (type=%d, max=%d)", 13217f8a436eSJoe Stringer type, OVS_CT_ATTR_MAX); 13227f8a436eSJoe Stringer return -EINVAL; 13237f8a436eSJoe Stringer } 132469ec932eSLiping Zhang 132569ec932eSLiping Zhang maxlen = ovs_ct_attr_lens[type].maxlen; 132669ec932eSLiping Zhang minlen = ovs_ct_attr_lens[type].minlen; 13277f8a436eSJoe Stringer if (nla_len(a) < minlen || nla_len(a) > maxlen) { 13287f8a436eSJoe Stringer OVS_NLERR(log, 13297f8a436eSJoe Stringer "Conntrack attr type has unexpected length (type=%d, length=%d, expected=%d)", 13307f8a436eSJoe Stringer type, nla_len(a), maxlen); 13317f8a436eSJoe Stringer return -EINVAL; 13327f8a436eSJoe Stringer } 13337f8a436eSJoe Stringer 13347f8a436eSJoe Stringer switch (type) { 1335dd41d33fSJarno Rajahalme case OVS_CT_ATTR_FORCE_COMMIT: 1336dd41d33fSJarno Rajahalme info->force = true; 1337df561f66SGustavo A. R. Silva fallthrough; 1338ab38a7b5SJoe Stringer case OVS_CT_ATTR_COMMIT: 1339ab38a7b5SJoe Stringer info->commit = true; 13407f8a436eSJoe Stringer break; 13417f8a436eSJoe Stringer #ifdef CONFIG_NF_CONNTRACK_ZONES 13427f8a436eSJoe Stringer case OVS_CT_ATTR_ZONE: 13437f8a436eSJoe Stringer info->zone.id = nla_get_u16(a); 13447f8a436eSJoe Stringer break; 13457f8a436eSJoe Stringer #endif 1346182e3042SJoe Stringer #ifdef CONFIG_NF_CONNTRACK_MARK 1347182e3042SJoe Stringer case OVS_CT_ATTR_MARK: { 1348182e3042SJoe Stringer struct md_mark *mark = nla_data(a); 1349182e3042SJoe Stringer 1350e754ec69SJoe Stringer if (!mark->mask) { 1351e754ec69SJoe Stringer OVS_NLERR(log, "ct_mark mask cannot be 0"); 1352e754ec69SJoe Stringer return -EINVAL; 1353e754ec69SJoe Stringer } 1354182e3042SJoe Stringer info->mark = *mark; 1355182e3042SJoe Stringer break; 1356182e3042SJoe Stringer } 1357182e3042SJoe Stringer #endif 1358c2ac6673SJoe Stringer #ifdef CONFIG_NF_CONNTRACK_LABELS 135933db4125SJoe Stringer case OVS_CT_ATTR_LABELS: { 136033db4125SJoe Stringer struct md_labels *labels = nla_data(a); 1361c2ac6673SJoe Stringer 1362e754ec69SJoe Stringer if (!labels_nonzero(&labels->mask)) { 1363e754ec69SJoe Stringer OVS_NLERR(log, "ct_labels mask cannot be 0"); 1364e754ec69SJoe Stringer return -EINVAL; 1365e754ec69SJoe Stringer } 136633db4125SJoe Stringer info->labels = *labels; 1367c2ac6673SJoe Stringer break; 1368c2ac6673SJoe Stringer } 1369c2ac6673SJoe Stringer #endif 1370cae3a262SJoe Stringer case OVS_CT_ATTR_HELPER: 1371cae3a262SJoe Stringer *helper = nla_data(a); 13725c72b4c6SAndy Shevchenko if (!string_is_terminated(*helper, nla_len(a))) { 1373cae3a262SJoe Stringer OVS_NLERR(log, "Invalid conntrack helper"); 1374cae3a262SJoe Stringer return -EINVAL; 1375cae3a262SJoe Stringer } 1376cae3a262SJoe Stringer break; 13774806e975SFlorian Westphal #if IS_ENABLED(CONFIG_NF_NAT) 137805752523SJarno Rajahalme case OVS_CT_ATTR_NAT: { 137905752523SJarno Rajahalme int err = parse_nat(a, info, log); 138005752523SJarno Rajahalme 138105752523SJarno Rajahalme if (err) 138205752523SJarno Rajahalme return err; 138305752523SJarno Rajahalme break; 138405752523SJarno Rajahalme } 138505752523SJarno Rajahalme #endif 138612064551SJarno Rajahalme case OVS_CT_ATTR_EVENTMASK: 138712064551SJarno Rajahalme info->have_eventmask = true; 138812064551SJarno Rajahalme info->eventmask = nla_get_u32(a); 138912064551SJarno Rajahalme break; 139006bd2bdfSYi-Hung Wei #ifdef CONFIG_NF_CONNTRACK_TIMEOUT 139106bd2bdfSYi-Hung Wei case OVS_CT_ATTR_TIMEOUT: 139206bd2bdfSYi-Hung Wei memcpy(info->timeout, nla_data(a), nla_len(a)); 13935c72b4c6SAndy Shevchenko if (!string_is_terminated(info->timeout, nla_len(a))) { 139412c6bc38SYi-Hung Wei OVS_NLERR(log, "Invalid conntrack timeout"); 139506bd2bdfSYi-Hung Wei return -EINVAL; 139606bd2bdfSYi-Hung Wei } 139706bd2bdfSYi-Hung Wei break; 139806bd2bdfSYi-Hung Wei #endif 139912064551SJarno Rajahalme 14007f8a436eSJoe Stringer default: 14017f8a436eSJoe Stringer OVS_NLERR(log, "Unknown conntrack attr (%d)", 14027f8a436eSJoe Stringer type); 14037f8a436eSJoe Stringer return -EINVAL; 14047f8a436eSJoe Stringer } 14057f8a436eSJoe Stringer } 14067f8a436eSJoe Stringer 14077d904c7bSJarno Rajahalme #ifdef CONFIG_NF_CONNTRACK_MARK 14087d904c7bSJarno Rajahalme if (!info->commit && info->mark.mask) { 14097d904c7bSJarno Rajahalme OVS_NLERR(log, 14107d904c7bSJarno Rajahalme "Setting conntrack mark requires 'commit' flag."); 14117d904c7bSJarno Rajahalme return -EINVAL; 14127d904c7bSJarno Rajahalme } 14137d904c7bSJarno Rajahalme #endif 14147d904c7bSJarno Rajahalme #ifdef CONFIG_NF_CONNTRACK_LABELS 14157d904c7bSJarno Rajahalme if (!info->commit && labels_nonzero(&info->labels.mask)) { 14167d904c7bSJarno Rajahalme OVS_NLERR(log, 14177d904c7bSJarno Rajahalme "Setting conntrack labels requires 'commit' flag."); 14187d904c7bSJarno Rajahalme return -EINVAL; 14197d904c7bSJarno Rajahalme } 14207d904c7bSJarno Rajahalme #endif 14217f8a436eSJoe Stringer if (rem > 0) { 14227f8a436eSJoe Stringer OVS_NLERR(log, "Conntrack attr has %d unknown bytes", rem); 14237f8a436eSJoe Stringer return -EINVAL; 14247f8a436eSJoe Stringer } 14257f8a436eSJoe Stringer 14267f8a436eSJoe Stringer return 0; 14277f8a436eSJoe Stringer } 14287f8a436eSJoe Stringer 1429c2ac6673SJoe Stringer bool ovs_ct_verify(struct net *net, enum ovs_key_attr attr) 14307f8a436eSJoe Stringer { 14317f8a436eSJoe Stringer if (attr == OVS_KEY_ATTR_CT_STATE) 14327f8a436eSJoe Stringer return true; 14337f8a436eSJoe Stringer if (IS_ENABLED(CONFIG_NF_CONNTRACK_ZONES) && 14347f8a436eSJoe Stringer attr == OVS_KEY_ATTR_CT_ZONE) 14357f8a436eSJoe Stringer return true; 1436182e3042SJoe Stringer if (IS_ENABLED(CONFIG_NF_CONNTRACK_MARK) && 1437182e3042SJoe Stringer attr == OVS_KEY_ATTR_CT_MARK) 1438182e3042SJoe Stringer return true; 1439c2ac6673SJoe Stringer if (IS_ENABLED(CONFIG_NF_CONNTRACK_LABELS) && 144033db4125SJoe Stringer attr == OVS_KEY_ATTR_CT_LABELS) { 1441c2ac6673SJoe Stringer struct ovs_net *ovs_net = net_generic(net, ovs_net_id); 1442c2ac6673SJoe Stringer 1443c2ac6673SJoe Stringer return ovs_net->xt_label; 1444c2ac6673SJoe Stringer } 14457f8a436eSJoe Stringer 14467f8a436eSJoe Stringer return false; 14477f8a436eSJoe Stringer } 14487f8a436eSJoe Stringer 14497f8a436eSJoe Stringer int ovs_ct_copy_action(struct net *net, const struct nlattr *attr, 14507f8a436eSJoe Stringer const struct sw_flow_key *key, 14517f8a436eSJoe Stringer struct sw_flow_actions **sfa, bool log) 14527f8a436eSJoe Stringer { 14537f8a436eSJoe Stringer struct ovs_conntrack_info ct_info; 1454cae3a262SJoe Stringer const char *helper = NULL; 14557f8a436eSJoe Stringer u16 family; 14567f8a436eSJoe Stringer int err; 14577f8a436eSJoe Stringer 14587f8a436eSJoe Stringer family = key_to_nfproto(key); 14597f8a436eSJoe Stringer if (family == NFPROTO_UNSPEC) { 14607f8a436eSJoe Stringer OVS_NLERR(log, "ct family unspecified"); 14617f8a436eSJoe Stringer return -EINVAL; 14627f8a436eSJoe Stringer } 14637f8a436eSJoe Stringer 14647f8a436eSJoe Stringer memset(&ct_info, 0, sizeof(ct_info)); 14657f8a436eSJoe Stringer ct_info.family = family; 14667f8a436eSJoe Stringer 14677f8a436eSJoe Stringer nf_ct_zone_init(&ct_info.zone, NF_CT_DEFAULT_ZONE_ID, 14687f8a436eSJoe Stringer NF_CT_DEFAULT_ZONE_DIR, 0); 14697f8a436eSJoe Stringer 1470cae3a262SJoe Stringer err = parse_ct(attr, &ct_info, &helper, log); 14717f8a436eSJoe Stringer if (err) 14727f8a436eSJoe Stringer return err; 14737f8a436eSJoe Stringer 14747f8a436eSJoe Stringer /* Set up template for tracking connections in specific zones. */ 14757f8a436eSJoe Stringer ct_info.ct = nf_ct_tmpl_alloc(net, &ct_info.zone, GFP_KERNEL); 14767f8a436eSJoe Stringer if (!ct_info.ct) { 14777f8a436eSJoe Stringer OVS_NLERR(log, "Failed to allocate conntrack template"); 14787f8a436eSJoe Stringer return -ENOMEM; 14797f8a436eSJoe Stringer } 148006bd2bdfSYi-Hung Wei 148106bd2bdfSYi-Hung Wei if (ct_info.timeout[0]) { 148206bd2bdfSYi-Hung Wei if (nf_ct_set_timeout(net, ct_info.ct, family, key->ip.proto, 148306bd2bdfSYi-Hung Wei ct_info.timeout)) 148406bd2bdfSYi-Hung Wei pr_info_ratelimited("Failed to associated timeout " 148506bd2bdfSYi-Hung Wei "policy `%s'\n", ct_info.timeout); 148671778951SYi-Hung Wei else 148771778951SYi-Hung Wei ct_info.nf_ct_timeout = rcu_dereference( 148871778951SYi-Hung Wei nf_ct_timeout_find(ct_info.ct)->timeout); 148971778951SYi-Hung Wei 149006bd2bdfSYi-Hung Wei } 149106bd2bdfSYi-Hung Wei 1492cae3a262SJoe Stringer if (helper) { 1493f96cba2eSXin Long err = nf_ct_add_helper(ct_info.ct, helper, ct_info.family, 1494f96cba2eSXin Long key->ip.proto, ct_info.nat, &ct_info.helper); 1495f96cba2eSXin Long if (err) { 1496f96cba2eSXin Long OVS_NLERR(log, "Failed to add %s helper %d", helper, err); 1497cae3a262SJoe Stringer goto err_free_ct; 1498cae3a262SJoe Stringer } 1499f96cba2eSXin Long } 15007f8a436eSJoe Stringer 15017f8a436eSJoe Stringer err = ovs_nla_add_action(sfa, OVS_ACTION_ATTR_CT, &ct_info, 15027f8a436eSJoe Stringer sizeof(ct_info), log); 15037f8a436eSJoe Stringer if (err) 15047f8a436eSJoe Stringer goto err_free_ct; 15057f8a436eSJoe Stringer 15067f6d6558SFlavio Leitner __set_bit(IPS_CONFIRMED_BIT, &ct_info.ct->status); 15077f8a436eSJoe Stringer return 0; 15087f8a436eSJoe Stringer err_free_ct: 15092f3ab9f9SJoe Stringer __ovs_ct_free_action(&ct_info); 15107f8a436eSJoe Stringer return err; 15117f8a436eSJoe Stringer } 15127f8a436eSJoe Stringer 15134806e975SFlorian Westphal #if IS_ENABLED(CONFIG_NF_NAT) 151405752523SJarno Rajahalme static bool ovs_ct_nat_to_attr(const struct ovs_conntrack_info *info, 151505752523SJarno Rajahalme struct sk_buff *skb) 151605752523SJarno Rajahalme { 151705752523SJarno Rajahalme struct nlattr *start; 151805752523SJarno Rajahalme 1519ae0be8deSMichal Kubecek start = nla_nest_start_noflag(skb, OVS_CT_ATTR_NAT); 152005752523SJarno Rajahalme if (!start) 152105752523SJarno Rajahalme return false; 152205752523SJarno Rajahalme 152305752523SJarno Rajahalme if (info->nat & OVS_CT_SRC_NAT) { 152405752523SJarno Rajahalme if (nla_put_flag(skb, OVS_NAT_ATTR_SRC)) 152505752523SJarno Rajahalme return false; 152605752523SJarno Rajahalme } else if (info->nat & OVS_CT_DST_NAT) { 152705752523SJarno Rajahalme if (nla_put_flag(skb, OVS_NAT_ATTR_DST)) 152805752523SJarno Rajahalme return false; 152905752523SJarno Rajahalme } else { 153005752523SJarno Rajahalme goto out; 153105752523SJarno Rajahalme } 153205752523SJarno Rajahalme 153305752523SJarno Rajahalme if (info->range.flags & NF_NAT_RANGE_MAP_IPS) { 15343bf195aeSFlorian Westphal if (IS_ENABLED(CONFIG_NF_NAT) && 153599b7248eSArnd Bergmann info->family == NFPROTO_IPV4) { 153605752523SJarno Rajahalme if (nla_put_in_addr(skb, OVS_NAT_ATTR_IP_MIN, 153705752523SJarno Rajahalme info->range.min_addr.ip) || 153805752523SJarno Rajahalme (info->range.max_addr.ip 153905752523SJarno Rajahalme != info->range.min_addr.ip && 154005752523SJarno Rajahalme (nla_put_in_addr(skb, OVS_NAT_ATTR_IP_MAX, 154105752523SJarno Rajahalme info->range.max_addr.ip)))) 154205752523SJarno Rajahalme return false; 15433bf195aeSFlorian Westphal } else if (IS_ENABLED(CONFIG_IPV6) && 154499b7248eSArnd Bergmann info->family == NFPROTO_IPV6) { 154505752523SJarno Rajahalme if (nla_put_in6_addr(skb, OVS_NAT_ATTR_IP_MIN, 154605752523SJarno Rajahalme &info->range.min_addr.in6) || 154705752523SJarno Rajahalme (memcmp(&info->range.max_addr.in6, 154805752523SJarno Rajahalme &info->range.min_addr.in6, 154905752523SJarno Rajahalme sizeof(info->range.max_addr.in6)) && 155005752523SJarno Rajahalme (nla_put_in6_addr(skb, OVS_NAT_ATTR_IP_MAX, 155105752523SJarno Rajahalme &info->range.max_addr.in6)))) 155205752523SJarno Rajahalme return false; 155305752523SJarno Rajahalme } else { 155405752523SJarno Rajahalme return false; 155505752523SJarno Rajahalme } 155605752523SJarno Rajahalme } 155705752523SJarno Rajahalme if (info->range.flags & NF_NAT_RANGE_PROTO_SPECIFIED && 155805752523SJarno Rajahalme (nla_put_u16(skb, OVS_NAT_ATTR_PROTO_MIN, 155905752523SJarno Rajahalme ntohs(info->range.min_proto.all)) || 156005752523SJarno Rajahalme (info->range.max_proto.all != info->range.min_proto.all && 156105752523SJarno Rajahalme nla_put_u16(skb, OVS_NAT_ATTR_PROTO_MAX, 156205752523SJarno Rajahalme ntohs(info->range.max_proto.all))))) 156305752523SJarno Rajahalme return false; 156405752523SJarno Rajahalme 156505752523SJarno Rajahalme if (info->range.flags & NF_NAT_RANGE_PERSISTENT && 156605752523SJarno Rajahalme nla_put_flag(skb, OVS_NAT_ATTR_PERSISTENT)) 156705752523SJarno Rajahalme return false; 156805752523SJarno Rajahalme if (info->range.flags & NF_NAT_RANGE_PROTO_RANDOM && 156905752523SJarno Rajahalme nla_put_flag(skb, OVS_NAT_ATTR_PROTO_HASH)) 157005752523SJarno Rajahalme return false; 157105752523SJarno Rajahalme if (info->range.flags & NF_NAT_RANGE_PROTO_RANDOM_FULLY && 157205752523SJarno Rajahalme nla_put_flag(skb, OVS_NAT_ATTR_PROTO_RANDOM)) 157305752523SJarno Rajahalme return false; 157405752523SJarno Rajahalme out: 157505752523SJarno Rajahalme nla_nest_end(skb, start); 157605752523SJarno Rajahalme 157705752523SJarno Rajahalme return true; 157805752523SJarno Rajahalme } 157905752523SJarno Rajahalme #endif 158005752523SJarno Rajahalme 15817f8a436eSJoe Stringer int ovs_ct_action_to_attr(const struct ovs_conntrack_info *ct_info, 15827f8a436eSJoe Stringer struct sk_buff *skb) 15837f8a436eSJoe Stringer { 15847f8a436eSJoe Stringer struct nlattr *start; 15857f8a436eSJoe Stringer 1586ae0be8deSMichal Kubecek start = nla_nest_start_noflag(skb, OVS_ACTION_ATTR_CT); 15877f8a436eSJoe Stringer if (!start) 15887f8a436eSJoe Stringer return -EMSGSIZE; 15897f8a436eSJoe Stringer 1590dd41d33fSJarno Rajahalme if (ct_info->commit && nla_put_flag(skb, ct_info->force 1591dd41d33fSJarno Rajahalme ? OVS_CT_ATTR_FORCE_COMMIT 1592dd41d33fSJarno Rajahalme : OVS_CT_ATTR_COMMIT)) 15937f8a436eSJoe Stringer return -EMSGSIZE; 15947f8a436eSJoe Stringer if (IS_ENABLED(CONFIG_NF_CONNTRACK_ZONES) && 15957f8a436eSJoe Stringer nla_put_u16(skb, OVS_CT_ATTR_ZONE, ct_info->zone.id)) 15967f8a436eSJoe Stringer return -EMSGSIZE; 1597e754ec69SJoe Stringer if (IS_ENABLED(CONFIG_NF_CONNTRACK_MARK) && ct_info->mark.mask && 1598182e3042SJoe Stringer nla_put(skb, OVS_CT_ATTR_MARK, sizeof(ct_info->mark), 1599182e3042SJoe Stringer &ct_info->mark)) 1600182e3042SJoe Stringer return -EMSGSIZE; 1601c2ac6673SJoe Stringer if (IS_ENABLED(CONFIG_NF_CONNTRACK_LABELS) && 1602e754ec69SJoe Stringer labels_nonzero(&ct_info->labels.mask) && 160333db4125SJoe Stringer nla_put(skb, OVS_CT_ATTR_LABELS, sizeof(ct_info->labels), 160433db4125SJoe Stringer &ct_info->labels)) 1605c2ac6673SJoe Stringer return -EMSGSIZE; 1606cae3a262SJoe Stringer if (ct_info->helper) { 1607cae3a262SJoe Stringer if (nla_put_string(skb, OVS_CT_ATTR_HELPER, 1608cae3a262SJoe Stringer ct_info->helper->name)) 1609cae3a262SJoe Stringer return -EMSGSIZE; 1610cae3a262SJoe Stringer } 161112064551SJarno Rajahalme if (ct_info->have_eventmask && 161212064551SJarno Rajahalme nla_put_u32(skb, OVS_CT_ATTR_EVENTMASK, ct_info->eventmask)) 161312064551SJarno Rajahalme return -EMSGSIZE; 161406bd2bdfSYi-Hung Wei if (ct_info->timeout[0]) { 161506bd2bdfSYi-Hung Wei if (nla_put_string(skb, OVS_CT_ATTR_TIMEOUT, ct_info->timeout)) 161606bd2bdfSYi-Hung Wei return -EMSGSIZE; 161706bd2bdfSYi-Hung Wei } 161812064551SJarno Rajahalme 16194806e975SFlorian Westphal #if IS_ENABLED(CONFIG_NF_NAT) 162005752523SJarno Rajahalme if (ct_info->nat && !ovs_ct_nat_to_attr(ct_info, skb)) 162105752523SJarno Rajahalme return -EMSGSIZE; 162205752523SJarno Rajahalme #endif 16237f8a436eSJoe Stringer nla_nest_end(skb, start); 16247f8a436eSJoe Stringer 16257f8a436eSJoe Stringer return 0; 16267f8a436eSJoe Stringer } 16277f8a436eSJoe Stringer 16287f8a436eSJoe Stringer void ovs_ct_free_action(const struct nlattr *a) 16297f8a436eSJoe Stringer { 16307f8a436eSJoe Stringer struct ovs_conntrack_info *ct_info = nla_data(a); 16317f8a436eSJoe Stringer 16322f3ab9f9SJoe Stringer __ovs_ct_free_action(ct_info); 16332f3ab9f9SJoe Stringer } 16342f3ab9f9SJoe Stringer 16352f3ab9f9SJoe Stringer static void __ovs_ct_free_action(struct ovs_conntrack_info *ct_info) 16362f3ab9f9SJoe Stringer { 1637fec9c271SFlavio Leitner if (ct_info->helper) { 1638f319ca65SGeert Uytterhoeven #if IS_ENABLED(CONFIG_NF_NAT) 1639fec9c271SFlavio Leitner if (ct_info->nat) 1640fec9c271SFlavio Leitner nf_nat_helper_put(ct_info->helper); 1641fec9c271SFlavio Leitner #endif 1642d91fc59cSLiping Zhang nf_conntrack_helper_put(ct_info->helper); 1643fec9c271SFlavio Leitner } 164406bd2bdfSYi-Hung Wei if (ct_info->ct) { 164506bd2bdfSYi-Hung Wei if (ct_info->timeout[0]) 164606bd2bdfSYi-Hung Wei nf_ct_destroy_timeout(ct_info->ct); 16476d670497SDan Carpenter nf_ct_tmpl_free(ct_info->ct); 164806bd2bdfSYi-Hung Wei } 16497f8a436eSJoe Stringer } 1650c2ac6673SJoe Stringer 165111efd5cbSYi-Hung Wei #if IS_ENABLED(CONFIG_NETFILTER_CONNCOUNT) 165211efd5cbSYi-Hung Wei static int ovs_ct_limit_init(struct net *net, struct ovs_net *ovs_net) 165311efd5cbSYi-Hung Wei { 165411efd5cbSYi-Hung Wei int i, err; 165511efd5cbSYi-Hung Wei 165611efd5cbSYi-Hung Wei ovs_net->ct_limit_info = kmalloc(sizeof(*ovs_net->ct_limit_info), 165711efd5cbSYi-Hung Wei GFP_KERNEL); 165811efd5cbSYi-Hung Wei if (!ovs_net->ct_limit_info) 165911efd5cbSYi-Hung Wei return -ENOMEM; 166011efd5cbSYi-Hung Wei 166111efd5cbSYi-Hung Wei ovs_net->ct_limit_info->default_limit = OVS_CT_LIMIT_DEFAULT; 166211efd5cbSYi-Hung Wei ovs_net->ct_limit_info->limits = 166311efd5cbSYi-Hung Wei kmalloc_array(CT_LIMIT_HASH_BUCKETS, sizeof(struct hlist_head), 166411efd5cbSYi-Hung Wei GFP_KERNEL); 166511efd5cbSYi-Hung Wei if (!ovs_net->ct_limit_info->limits) { 166611efd5cbSYi-Hung Wei kfree(ovs_net->ct_limit_info); 166711efd5cbSYi-Hung Wei return -ENOMEM; 166811efd5cbSYi-Hung Wei } 166911efd5cbSYi-Hung Wei 167011efd5cbSYi-Hung Wei for (i = 0; i < CT_LIMIT_HASH_BUCKETS; i++) 167111efd5cbSYi-Hung Wei INIT_HLIST_HEAD(&ovs_net->ct_limit_info->limits[i]); 167211efd5cbSYi-Hung Wei 167311efd5cbSYi-Hung Wei ovs_net->ct_limit_info->data = 167411efd5cbSYi-Hung Wei nf_conncount_init(net, NFPROTO_INET, sizeof(u32)); 167511efd5cbSYi-Hung Wei 167611efd5cbSYi-Hung Wei if (IS_ERR(ovs_net->ct_limit_info->data)) { 167711efd5cbSYi-Hung Wei err = PTR_ERR(ovs_net->ct_limit_info->data); 167811efd5cbSYi-Hung Wei kfree(ovs_net->ct_limit_info->limits); 167911efd5cbSYi-Hung Wei kfree(ovs_net->ct_limit_info); 168011efd5cbSYi-Hung Wei pr_err("openvswitch: failed to init nf_conncount %d\n", err); 168111efd5cbSYi-Hung Wei return err; 168211efd5cbSYi-Hung Wei } 168311efd5cbSYi-Hung Wei return 0; 168411efd5cbSYi-Hung Wei } 168511efd5cbSYi-Hung Wei 168611efd5cbSYi-Hung Wei static void ovs_ct_limit_exit(struct net *net, struct ovs_net *ovs_net) 168711efd5cbSYi-Hung Wei { 168811efd5cbSYi-Hung Wei const struct ovs_ct_limit_info *info = ovs_net->ct_limit_info; 168911efd5cbSYi-Hung Wei int i; 169011efd5cbSYi-Hung Wei 169111efd5cbSYi-Hung Wei nf_conncount_destroy(net, NFPROTO_INET, info->data); 169211efd5cbSYi-Hung Wei for (i = 0; i < CT_LIMIT_HASH_BUCKETS; ++i) { 169311efd5cbSYi-Hung Wei struct hlist_head *head = &info->limits[i]; 169411efd5cbSYi-Hung Wei struct ovs_ct_limit *ct_limit; 169511efd5cbSYi-Hung Wei 169627de77ceSTonghao Zhang hlist_for_each_entry_rcu(ct_limit, head, hlist_node, 169727de77ceSTonghao Zhang lockdep_ovsl_is_held()) 169811efd5cbSYi-Hung Wei kfree_rcu(ct_limit, rcu); 169911efd5cbSYi-Hung Wei } 17007b066d17SZeng Tao kfree(info->limits); 17017b066d17SZeng Tao kfree(info); 170211efd5cbSYi-Hung Wei } 170311efd5cbSYi-Hung Wei 170411efd5cbSYi-Hung Wei static struct sk_buff * 170511efd5cbSYi-Hung Wei ovs_ct_limit_cmd_reply_start(struct genl_info *info, u8 cmd, 170611efd5cbSYi-Hung Wei struct ovs_header **ovs_reply_header) 170711efd5cbSYi-Hung Wei { 170811efd5cbSYi-Hung Wei struct ovs_header *ovs_header = info->userhdr; 170911efd5cbSYi-Hung Wei struct sk_buff *skb; 171011efd5cbSYi-Hung Wei 171111efd5cbSYi-Hung Wei skb = genlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); 171211efd5cbSYi-Hung Wei if (!skb) 171311efd5cbSYi-Hung Wei return ERR_PTR(-ENOMEM); 171411efd5cbSYi-Hung Wei 171511efd5cbSYi-Hung Wei *ovs_reply_header = genlmsg_put(skb, info->snd_portid, 171611efd5cbSYi-Hung Wei info->snd_seq, 171711efd5cbSYi-Hung Wei &dp_ct_limit_genl_family, 0, cmd); 171811efd5cbSYi-Hung Wei 171911efd5cbSYi-Hung Wei if (!*ovs_reply_header) { 172011efd5cbSYi-Hung Wei nlmsg_free(skb); 172111efd5cbSYi-Hung Wei return ERR_PTR(-EMSGSIZE); 172211efd5cbSYi-Hung Wei } 172311efd5cbSYi-Hung Wei (*ovs_reply_header)->dp_ifindex = ovs_header->dp_ifindex; 172411efd5cbSYi-Hung Wei 172511efd5cbSYi-Hung Wei return skb; 172611efd5cbSYi-Hung Wei } 172711efd5cbSYi-Hung Wei 172811efd5cbSYi-Hung Wei static bool check_zone_id(int zone_id, u16 *pzone) 172911efd5cbSYi-Hung Wei { 173011efd5cbSYi-Hung Wei if (zone_id >= 0 && zone_id <= 65535) { 173111efd5cbSYi-Hung Wei *pzone = (u16)zone_id; 173211efd5cbSYi-Hung Wei return true; 173311efd5cbSYi-Hung Wei } 173411efd5cbSYi-Hung Wei return false; 173511efd5cbSYi-Hung Wei } 173611efd5cbSYi-Hung Wei 173711efd5cbSYi-Hung Wei static int ovs_ct_limit_set_zone_limit(struct nlattr *nla_zone_limit, 173811efd5cbSYi-Hung Wei struct ovs_ct_limit_info *info) 173911efd5cbSYi-Hung Wei { 174011efd5cbSYi-Hung Wei struct ovs_zone_limit *zone_limit; 174111efd5cbSYi-Hung Wei int rem; 174211efd5cbSYi-Hung Wei u16 zone; 174311efd5cbSYi-Hung Wei 174411efd5cbSYi-Hung Wei rem = NLA_ALIGN(nla_len(nla_zone_limit)); 174511efd5cbSYi-Hung Wei zone_limit = (struct ovs_zone_limit *)nla_data(nla_zone_limit); 174611efd5cbSYi-Hung Wei 174711efd5cbSYi-Hung Wei while (rem >= sizeof(*zone_limit)) { 174811efd5cbSYi-Hung Wei if (unlikely(zone_limit->zone_id == 174911efd5cbSYi-Hung Wei OVS_ZONE_LIMIT_DEFAULT_ZONE)) { 175011efd5cbSYi-Hung Wei ovs_lock(); 175111efd5cbSYi-Hung Wei info->default_limit = zone_limit->limit; 175211efd5cbSYi-Hung Wei ovs_unlock(); 175311efd5cbSYi-Hung Wei } else if (unlikely(!check_zone_id( 175411efd5cbSYi-Hung Wei zone_limit->zone_id, &zone))) { 175511efd5cbSYi-Hung Wei OVS_NLERR(true, "zone id is out of range"); 175611efd5cbSYi-Hung Wei } else { 175711efd5cbSYi-Hung Wei struct ovs_ct_limit *ct_limit; 175811efd5cbSYi-Hung Wei 175959cd7377SMichael Weiß ct_limit = kmalloc(sizeof(*ct_limit), 176059cd7377SMichael Weiß GFP_KERNEL_ACCOUNT); 176111efd5cbSYi-Hung Wei if (!ct_limit) 176211efd5cbSYi-Hung Wei return -ENOMEM; 176311efd5cbSYi-Hung Wei 176411efd5cbSYi-Hung Wei ct_limit->zone = zone; 176511efd5cbSYi-Hung Wei ct_limit->limit = zone_limit->limit; 176611efd5cbSYi-Hung Wei 176711efd5cbSYi-Hung Wei ovs_lock(); 176811efd5cbSYi-Hung Wei ct_limit_set(info, ct_limit); 176911efd5cbSYi-Hung Wei ovs_unlock(); 177011efd5cbSYi-Hung Wei } 177111efd5cbSYi-Hung Wei rem -= NLA_ALIGN(sizeof(*zone_limit)); 177211efd5cbSYi-Hung Wei zone_limit = (struct ovs_zone_limit *)((u8 *)zone_limit + 177311efd5cbSYi-Hung Wei NLA_ALIGN(sizeof(*zone_limit))); 177411efd5cbSYi-Hung Wei } 177511efd5cbSYi-Hung Wei 177611efd5cbSYi-Hung Wei if (rem) 177711efd5cbSYi-Hung Wei OVS_NLERR(true, "set zone limit has %d unknown bytes", rem); 177811efd5cbSYi-Hung Wei 177911efd5cbSYi-Hung Wei return 0; 178011efd5cbSYi-Hung Wei } 178111efd5cbSYi-Hung Wei 178211efd5cbSYi-Hung Wei static int ovs_ct_limit_del_zone_limit(struct nlattr *nla_zone_limit, 178311efd5cbSYi-Hung Wei struct ovs_ct_limit_info *info) 178411efd5cbSYi-Hung Wei { 178511efd5cbSYi-Hung Wei struct ovs_zone_limit *zone_limit; 178611efd5cbSYi-Hung Wei int rem; 178711efd5cbSYi-Hung Wei u16 zone; 178811efd5cbSYi-Hung Wei 178911efd5cbSYi-Hung Wei rem = NLA_ALIGN(nla_len(nla_zone_limit)); 179011efd5cbSYi-Hung Wei zone_limit = (struct ovs_zone_limit *)nla_data(nla_zone_limit); 179111efd5cbSYi-Hung Wei 179211efd5cbSYi-Hung Wei while (rem >= sizeof(*zone_limit)) { 179311efd5cbSYi-Hung Wei if (unlikely(zone_limit->zone_id == 179411efd5cbSYi-Hung Wei OVS_ZONE_LIMIT_DEFAULT_ZONE)) { 179511efd5cbSYi-Hung Wei ovs_lock(); 179611efd5cbSYi-Hung Wei info->default_limit = OVS_CT_LIMIT_DEFAULT; 179711efd5cbSYi-Hung Wei ovs_unlock(); 179811efd5cbSYi-Hung Wei } else if (unlikely(!check_zone_id( 179911efd5cbSYi-Hung Wei zone_limit->zone_id, &zone))) { 180011efd5cbSYi-Hung Wei OVS_NLERR(true, "zone id is out of range"); 180111efd5cbSYi-Hung Wei } else { 180211efd5cbSYi-Hung Wei ovs_lock(); 180311efd5cbSYi-Hung Wei ct_limit_del(info, zone); 180411efd5cbSYi-Hung Wei ovs_unlock(); 180511efd5cbSYi-Hung Wei } 180611efd5cbSYi-Hung Wei rem -= NLA_ALIGN(sizeof(*zone_limit)); 180711efd5cbSYi-Hung Wei zone_limit = (struct ovs_zone_limit *)((u8 *)zone_limit + 180811efd5cbSYi-Hung Wei NLA_ALIGN(sizeof(*zone_limit))); 180911efd5cbSYi-Hung Wei } 181011efd5cbSYi-Hung Wei 181111efd5cbSYi-Hung Wei if (rem) 181211efd5cbSYi-Hung Wei OVS_NLERR(true, "del zone limit has %d unknown bytes", rem); 181311efd5cbSYi-Hung Wei 181411efd5cbSYi-Hung Wei return 0; 181511efd5cbSYi-Hung Wei } 181611efd5cbSYi-Hung Wei 181711efd5cbSYi-Hung Wei static int ovs_ct_limit_get_default_limit(struct ovs_ct_limit_info *info, 181811efd5cbSYi-Hung Wei struct sk_buff *reply) 181911efd5cbSYi-Hung Wei { 18204d51419dSIlya Maximets struct ovs_zone_limit zone_limit = { 18214d51419dSIlya Maximets .zone_id = OVS_ZONE_LIMIT_DEFAULT_ZONE, 18224d51419dSIlya Maximets .limit = info->default_limit, 18234d51419dSIlya Maximets }; 182411efd5cbSYi-Hung Wei 18255e359044SZheng Yongjun return nla_put_nohdr(reply, sizeof(zone_limit), &zone_limit); 182611efd5cbSYi-Hung Wei } 182711efd5cbSYi-Hung Wei 182811efd5cbSYi-Hung Wei static int __ovs_ct_limit_get_zone_limit(struct net *net, 182911efd5cbSYi-Hung Wei struct nf_conncount_data *data, 183011efd5cbSYi-Hung Wei u16 zone_id, u32 limit, 183111efd5cbSYi-Hung Wei struct sk_buff *reply) 183211efd5cbSYi-Hung Wei { 183311efd5cbSYi-Hung Wei struct nf_conntrack_zone ct_zone; 183411efd5cbSYi-Hung Wei struct ovs_zone_limit zone_limit; 183511efd5cbSYi-Hung Wei u32 conncount_key = zone_id; 183611efd5cbSYi-Hung Wei 183711efd5cbSYi-Hung Wei zone_limit.zone_id = zone_id; 183811efd5cbSYi-Hung Wei zone_limit.limit = limit; 183911efd5cbSYi-Hung Wei nf_ct_zone_init(&ct_zone, zone_id, NF_CT_DEFAULT_ZONE_DIR, 0); 184011efd5cbSYi-Hung Wei 184111efd5cbSYi-Hung Wei zone_limit.count = nf_conncount_count(net, data, &conncount_key, NULL, 184211efd5cbSYi-Hung Wei &ct_zone); 184311efd5cbSYi-Hung Wei return nla_put_nohdr(reply, sizeof(zone_limit), &zone_limit); 184411efd5cbSYi-Hung Wei } 184511efd5cbSYi-Hung Wei 184611efd5cbSYi-Hung Wei static int ovs_ct_limit_get_zone_limit(struct net *net, 184711efd5cbSYi-Hung Wei struct nlattr *nla_zone_limit, 184811efd5cbSYi-Hung Wei struct ovs_ct_limit_info *info, 184911efd5cbSYi-Hung Wei struct sk_buff *reply) 185011efd5cbSYi-Hung Wei { 185111efd5cbSYi-Hung Wei struct ovs_zone_limit *zone_limit; 185211efd5cbSYi-Hung Wei int rem, err; 185311efd5cbSYi-Hung Wei u32 limit; 185411efd5cbSYi-Hung Wei u16 zone; 185511efd5cbSYi-Hung Wei 185611efd5cbSYi-Hung Wei rem = NLA_ALIGN(nla_len(nla_zone_limit)); 185711efd5cbSYi-Hung Wei zone_limit = (struct ovs_zone_limit *)nla_data(nla_zone_limit); 185811efd5cbSYi-Hung Wei 185911efd5cbSYi-Hung Wei while (rem >= sizeof(*zone_limit)) { 186011efd5cbSYi-Hung Wei if (unlikely(zone_limit->zone_id == 186111efd5cbSYi-Hung Wei OVS_ZONE_LIMIT_DEFAULT_ZONE)) { 186211efd5cbSYi-Hung Wei err = ovs_ct_limit_get_default_limit(info, reply); 186311efd5cbSYi-Hung Wei if (err) 186411efd5cbSYi-Hung Wei return err; 186511efd5cbSYi-Hung Wei } else if (unlikely(!check_zone_id(zone_limit->zone_id, 186611efd5cbSYi-Hung Wei &zone))) { 186711efd5cbSYi-Hung Wei OVS_NLERR(true, "zone id is out of range"); 186811efd5cbSYi-Hung Wei } else { 186911efd5cbSYi-Hung Wei rcu_read_lock(); 187011efd5cbSYi-Hung Wei limit = ct_limit_get(info, zone); 187111efd5cbSYi-Hung Wei rcu_read_unlock(); 187211efd5cbSYi-Hung Wei 187311efd5cbSYi-Hung Wei err = __ovs_ct_limit_get_zone_limit( 187411efd5cbSYi-Hung Wei net, info->data, zone, limit, reply); 187511efd5cbSYi-Hung Wei if (err) 187611efd5cbSYi-Hung Wei return err; 187711efd5cbSYi-Hung Wei } 187811efd5cbSYi-Hung Wei rem -= NLA_ALIGN(sizeof(*zone_limit)); 187911efd5cbSYi-Hung Wei zone_limit = (struct ovs_zone_limit *)((u8 *)zone_limit + 188011efd5cbSYi-Hung Wei NLA_ALIGN(sizeof(*zone_limit))); 188111efd5cbSYi-Hung Wei } 188211efd5cbSYi-Hung Wei 188311efd5cbSYi-Hung Wei if (rem) 188411efd5cbSYi-Hung Wei OVS_NLERR(true, "get zone limit has %d unknown bytes", rem); 188511efd5cbSYi-Hung Wei 188611efd5cbSYi-Hung Wei return 0; 188711efd5cbSYi-Hung Wei } 188811efd5cbSYi-Hung Wei 188911efd5cbSYi-Hung Wei static int ovs_ct_limit_get_all_zone_limit(struct net *net, 189011efd5cbSYi-Hung Wei struct ovs_ct_limit_info *info, 189111efd5cbSYi-Hung Wei struct sk_buff *reply) 189211efd5cbSYi-Hung Wei { 189311efd5cbSYi-Hung Wei struct ovs_ct_limit *ct_limit; 189411efd5cbSYi-Hung Wei struct hlist_head *head; 189511efd5cbSYi-Hung Wei int i, err = 0; 189611efd5cbSYi-Hung Wei 189711efd5cbSYi-Hung Wei err = ovs_ct_limit_get_default_limit(info, reply); 189811efd5cbSYi-Hung Wei if (err) 189911efd5cbSYi-Hung Wei return err; 190011efd5cbSYi-Hung Wei 190111efd5cbSYi-Hung Wei rcu_read_lock(); 190211efd5cbSYi-Hung Wei for (i = 0; i < CT_LIMIT_HASH_BUCKETS; ++i) { 190311efd5cbSYi-Hung Wei head = &info->limits[i]; 190411efd5cbSYi-Hung Wei hlist_for_each_entry_rcu(ct_limit, head, hlist_node) { 190511efd5cbSYi-Hung Wei err = __ovs_ct_limit_get_zone_limit(net, info->data, 190611efd5cbSYi-Hung Wei ct_limit->zone, ct_limit->limit, reply); 190711efd5cbSYi-Hung Wei if (err) 190811efd5cbSYi-Hung Wei goto exit_err; 190911efd5cbSYi-Hung Wei } 191011efd5cbSYi-Hung Wei } 191111efd5cbSYi-Hung Wei 191211efd5cbSYi-Hung Wei exit_err: 191311efd5cbSYi-Hung Wei rcu_read_unlock(); 191411efd5cbSYi-Hung Wei return err; 191511efd5cbSYi-Hung Wei } 191611efd5cbSYi-Hung Wei 191711efd5cbSYi-Hung Wei static int ovs_ct_limit_cmd_set(struct sk_buff *skb, struct genl_info *info) 191811efd5cbSYi-Hung Wei { 191911efd5cbSYi-Hung Wei struct nlattr **a = info->attrs; 192011efd5cbSYi-Hung Wei struct sk_buff *reply; 192111efd5cbSYi-Hung Wei struct ovs_header *ovs_reply_header; 192211efd5cbSYi-Hung Wei struct ovs_net *ovs_net = net_generic(sock_net(skb->sk), ovs_net_id); 192311efd5cbSYi-Hung Wei struct ovs_ct_limit_info *ct_limit_info = ovs_net->ct_limit_info; 192411efd5cbSYi-Hung Wei int err; 192511efd5cbSYi-Hung Wei 192611efd5cbSYi-Hung Wei reply = ovs_ct_limit_cmd_reply_start(info, OVS_CT_LIMIT_CMD_SET, 192711efd5cbSYi-Hung Wei &ovs_reply_header); 192811efd5cbSYi-Hung Wei if (IS_ERR(reply)) 192911efd5cbSYi-Hung Wei return PTR_ERR(reply); 193011efd5cbSYi-Hung Wei 193111efd5cbSYi-Hung Wei if (!a[OVS_CT_LIMIT_ATTR_ZONE_LIMIT]) { 193211efd5cbSYi-Hung Wei err = -EINVAL; 193311efd5cbSYi-Hung Wei goto exit_err; 193411efd5cbSYi-Hung Wei } 193511efd5cbSYi-Hung Wei 193611efd5cbSYi-Hung Wei err = ovs_ct_limit_set_zone_limit(a[OVS_CT_LIMIT_ATTR_ZONE_LIMIT], 193711efd5cbSYi-Hung Wei ct_limit_info); 193811efd5cbSYi-Hung Wei if (err) 193911efd5cbSYi-Hung Wei goto exit_err; 194011efd5cbSYi-Hung Wei 194111efd5cbSYi-Hung Wei static_branch_enable(&ovs_ct_limit_enabled); 194211efd5cbSYi-Hung Wei 194311efd5cbSYi-Hung Wei genlmsg_end(reply, ovs_reply_header); 194411efd5cbSYi-Hung Wei return genlmsg_reply(reply, info); 194511efd5cbSYi-Hung Wei 194611efd5cbSYi-Hung Wei exit_err: 194711efd5cbSYi-Hung Wei nlmsg_free(reply); 194811efd5cbSYi-Hung Wei return err; 194911efd5cbSYi-Hung Wei } 195011efd5cbSYi-Hung Wei 195111efd5cbSYi-Hung Wei static int ovs_ct_limit_cmd_del(struct sk_buff *skb, struct genl_info *info) 195211efd5cbSYi-Hung Wei { 195311efd5cbSYi-Hung Wei struct nlattr **a = info->attrs; 195411efd5cbSYi-Hung Wei struct sk_buff *reply; 195511efd5cbSYi-Hung Wei struct ovs_header *ovs_reply_header; 195611efd5cbSYi-Hung Wei struct ovs_net *ovs_net = net_generic(sock_net(skb->sk), ovs_net_id); 195711efd5cbSYi-Hung Wei struct ovs_ct_limit_info *ct_limit_info = ovs_net->ct_limit_info; 195811efd5cbSYi-Hung Wei int err; 195911efd5cbSYi-Hung Wei 196011efd5cbSYi-Hung Wei reply = ovs_ct_limit_cmd_reply_start(info, OVS_CT_LIMIT_CMD_DEL, 196111efd5cbSYi-Hung Wei &ovs_reply_header); 196211efd5cbSYi-Hung Wei if (IS_ERR(reply)) 196311efd5cbSYi-Hung Wei return PTR_ERR(reply); 196411efd5cbSYi-Hung Wei 196511efd5cbSYi-Hung Wei if (!a[OVS_CT_LIMIT_ATTR_ZONE_LIMIT]) { 196611efd5cbSYi-Hung Wei err = -EINVAL; 196711efd5cbSYi-Hung Wei goto exit_err; 196811efd5cbSYi-Hung Wei } 196911efd5cbSYi-Hung Wei 197011efd5cbSYi-Hung Wei err = ovs_ct_limit_del_zone_limit(a[OVS_CT_LIMIT_ATTR_ZONE_LIMIT], 197111efd5cbSYi-Hung Wei ct_limit_info); 197211efd5cbSYi-Hung Wei if (err) 197311efd5cbSYi-Hung Wei goto exit_err; 197411efd5cbSYi-Hung Wei 197511efd5cbSYi-Hung Wei genlmsg_end(reply, ovs_reply_header); 197611efd5cbSYi-Hung Wei return genlmsg_reply(reply, info); 197711efd5cbSYi-Hung Wei 197811efd5cbSYi-Hung Wei exit_err: 197911efd5cbSYi-Hung Wei nlmsg_free(reply); 198011efd5cbSYi-Hung Wei return err; 198111efd5cbSYi-Hung Wei } 198211efd5cbSYi-Hung Wei 198311efd5cbSYi-Hung Wei static int ovs_ct_limit_cmd_get(struct sk_buff *skb, struct genl_info *info) 198411efd5cbSYi-Hung Wei { 198511efd5cbSYi-Hung Wei struct nlattr **a = info->attrs; 198611efd5cbSYi-Hung Wei struct nlattr *nla_reply; 198711efd5cbSYi-Hung Wei struct sk_buff *reply; 198811efd5cbSYi-Hung Wei struct ovs_header *ovs_reply_header; 198911efd5cbSYi-Hung Wei struct net *net = sock_net(skb->sk); 199011efd5cbSYi-Hung Wei struct ovs_net *ovs_net = net_generic(net, ovs_net_id); 199111efd5cbSYi-Hung Wei struct ovs_ct_limit_info *ct_limit_info = ovs_net->ct_limit_info; 199211efd5cbSYi-Hung Wei int err; 199311efd5cbSYi-Hung Wei 199411efd5cbSYi-Hung Wei reply = ovs_ct_limit_cmd_reply_start(info, OVS_CT_LIMIT_CMD_GET, 199511efd5cbSYi-Hung Wei &ovs_reply_header); 199611efd5cbSYi-Hung Wei if (IS_ERR(reply)) 199711efd5cbSYi-Hung Wei return PTR_ERR(reply); 199811efd5cbSYi-Hung Wei 1999ae0be8deSMichal Kubecek nla_reply = nla_nest_start_noflag(reply, OVS_CT_LIMIT_ATTR_ZONE_LIMIT); 2000ca965346SColin Ian King if (!nla_reply) { 2001ca965346SColin Ian King err = -EMSGSIZE; 2002ca965346SColin Ian King goto exit_err; 2003ca965346SColin Ian King } 200411efd5cbSYi-Hung Wei 200511efd5cbSYi-Hung Wei if (a[OVS_CT_LIMIT_ATTR_ZONE_LIMIT]) { 200611efd5cbSYi-Hung Wei err = ovs_ct_limit_get_zone_limit( 200711efd5cbSYi-Hung Wei net, a[OVS_CT_LIMIT_ATTR_ZONE_LIMIT], ct_limit_info, 200811efd5cbSYi-Hung Wei reply); 200911efd5cbSYi-Hung Wei if (err) 201011efd5cbSYi-Hung Wei goto exit_err; 201111efd5cbSYi-Hung Wei } else { 201211efd5cbSYi-Hung Wei err = ovs_ct_limit_get_all_zone_limit(net, ct_limit_info, 201311efd5cbSYi-Hung Wei reply); 201411efd5cbSYi-Hung Wei if (err) 201511efd5cbSYi-Hung Wei goto exit_err; 201611efd5cbSYi-Hung Wei } 201711efd5cbSYi-Hung Wei 201811efd5cbSYi-Hung Wei nla_nest_end(reply, nla_reply); 201911efd5cbSYi-Hung Wei genlmsg_end(reply, ovs_reply_header); 202011efd5cbSYi-Hung Wei return genlmsg_reply(reply, info); 202111efd5cbSYi-Hung Wei 202211efd5cbSYi-Hung Wei exit_err: 202311efd5cbSYi-Hung Wei nlmsg_free(reply); 202411efd5cbSYi-Hung Wei return err; 202511efd5cbSYi-Hung Wei } 202611efd5cbSYi-Hung Wei 2027b980b313SRikard Falkeborn static const struct genl_small_ops ct_limit_genl_ops[] = { 202811efd5cbSYi-Hung Wei { .cmd = OVS_CT_LIMIT_CMD_SET, 2029ef6243acSJohannes Berg .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, 203059cd7377SMichael Weiß .flags = GENL_UNS_ADMIN_PERM, /* Requires CAP_NET_ADMIN 203159cd7377SMichael Weiß * privilege. 203259cd7377SMichael Weiß */ 203311efd5cbSYi-Hung Wei .doit = ovs_ct_limit_cmd_set, 203411efd5cbSYi-Hung Wei }, 203511efd5cbSYi-Hung Wei { .cmd = OVS_CT_LIMIT_CMD_DEL, 2036ef6243acSJohannes Berg .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, 203759cd7377SMichael Weiß .flags = GENL_UNS_ADMIN_PERM, /* Requires CAP_NET_ADMIN 203859cd7377SMichael Weiß * privilege. 203959cd7377SMichael Weiß */ 204011efd5cbSYi-Hung Wei .doit = ovs_ct_limit_cmd_del, 204111efd5cbSYi-Hung Wei }, 204211efd5cbSYi-Hung Wei { .cmd = OVS_CT_LIMIT_CMD_GET, 2043ef6243acSJohannes Berg .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, 204411efd5cbSYi-Hung Wei .flags = 0, /* OK for unprivileged users. */ 204511efd5cbSYi-Hung Wei .doit = ovs_ct_limit_cmd_get, 204611efd5cbSYi-Hung Wei }, 204711efd5cbSYi-Hung Wei }; 204811efd5cbSYi-Hung Wei 204911efd5cbSYi-Hung Wei static const struct genl_multicast_group ovs_ct_limit_multicast_group = { 205011efd5cbSYi-Hung Wei .name = OVS_CT_LIMIT_MCGROUP, 205111efd5cbSYi-Hung Wei }; 205211efd5cbSYi-Hung Wei 205311efd5cbSYi-Hung Wei struct genl_family dp_ct_limit_genl_family __ro_after_init = { 205411efd5cbSYi-Hung Wei .hdrsize = sizeof(struct ovs_header), 205511efd5cbSYi-Hung Wei .name = OVS_CT_LIMIT_FAMILY, 205611efd5cbSYi-Hung Wei .version = OVS_CT_LIMIT_VERSION, 205711efd5cbSYi-Hung Wei .maxattr = OVS_CT_LIMIT_ATTR_MAX, 20583b0f31f2SJohannes Berg .policy = ct_limit_policy, 205911efd5cbSYi-Hung Wei .netnsok = true, 206011efd5cbSYi-Hung Wei .parallel_ops = true, 206166a9b928SJakub Kicinski .small_ops = ct_limit_genl_ops, 206266a9b928SJakub Kicinski .n_small_ops = ARRAY_SIZE(ct_limit_genl_ops), 20639c5d03d3SJakub Kicinski .resv_start_op = OVS_CT_LIMIT_CMD_GET + 1, 206411efd5cbSYi-Hung Wei .mcgrps = &ovs_ct_limit_multicast_group, 206511efd5cbSYi-Hung Wei .n_mcgrps = 1, 206611efd5cbSYi-Hung Wei .module = THIS_MODULE, 206711efd5cbSYi-Hung Wei }; 206811efd5cbSYi-Hung Wei #endif 206911efd5cbSYi-Hung Wei 207011efd5cbSYi-Hung Wei int ovs_ct_init(struct net *net) 2071c2ac6673SJoe Stringer { 207233db4125SJoe Stringer unsigned int n_bits = sizeof(struct ovs_key_ct_labels) * BITS_PER_BYTE; 2073c2ac6673SJoe Stringer struct ovs_net *ovs_net = net_generic(net, ovs_net_id); 2074c2ac6673SJoe Stringer 2075adff6c65SFlorian Westphal if (nf_connlabels_get(net, n_bits - 1)) { 2076c2ac6673SJoe Stringer ovs_net->xt_label = false; 2077c2ac6673SJoe Stringer OVS_NLERR(true, "Failed to set connlabel length"); 2078c2ac6673SJoe Stringer } else { 2079c2ac6673SJoe Stringer ovs_net->xt_label = true; 2080c2ac6673SJoe Stringer } 208111efd5cbSYi-Hung Wei 208211efd5cbSYi-Hung Wei #if IS_ENABLED(CONFIG_NETFILTER_CONNCOUNT) 208311efd5cbSYi-Hung Wei return ovs_ct_limit_init(net, ovs_net); 208411efd5cbSYi-Hung Wei #else 208511efd5cbSYi-Hung Wei return 0; 208611efd5cbSYi-Hung Wei #endif 2087c2ac6673SJoe Stringer } 2088c2ac6673SJoe Stringer 2089c2ac6673SJoe Stringer void ovs_ct_exit(struct net *net) 2090c2ac6673SJoe Stringer { 2091c2ac6673SJoe Stringer struct ovs_net *ovs_net = net_generic(net, ovs_net_id); 2092c2ac6673SJoe Stringer 209311efd5cbSYi-Hung Wei #if IS_ENABLED(CONFIG_NETFILTER_CONNCOUNT) 209411efd5cbSYi-Hung Wei ovs_ct_limit_exit(net, ovs_net); 209511efd5cbSYi-Hung Wei #endif 209611efd5cbSYi-Hung Wei 2097c2ac6673SJoe Stringer if (ovs_net->xt_label) 2098c2ac6673SJoe Stringer nf_connlabels_put(net); 2099c2ac6673SJoe Stringer } 2100