17f8a436eSJoe Stringer /* 27f8a436eSJoe Stringer * Copyright (c) 2015 Nicira, Inc. 37f8a436eSJoe Stringer * 47f8a436eSJoe Stringer * This program is free software; you can redistribute it and/or 57f8a436eSJoe Stringer * modify it under the terms of version 2 of the GNU General Public 67f8a436eSJoe Stringer * License as published by the Free Software Foundation. 77f8a436eSJoe Stringer * 87f8a436eSJoe Stringer * This program is distributed in the hope that it will be useful, but 97f8a436eSJoe Stringer * WITHOUT ANY WARRANTY; without even the implied warranty of 107f8a436eSJoe Stringer * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 117f8a436eSJoe Stringer * General Public License for more details. 127f8a436eSJoe Stringer */ 137f8a436eSJoe Stringer 147f8a436eSJoe Stringer #include <linux/module.h> 157f8a436eSJoe Stringer #include <linux/openvswitch.h> 167f8a436eSJoe Stringer #include <net/ip.h> 177f8a436eSJoe Stringer #include <net/netfilter/nf_conntrack_core.h> 18cae3a262SJoe Stringer #include <net/netfilter/nf_conntrack_helper.h> 19c2ac6673SJoe Stringer #include <net/netfilter/nf_conntrack_labels.h> 207f8a436eSJoe Stringer #include <net/netfilter/nf_conntrack_zones.h> 217f8a436eSJoe Stringer #include <net/netfilter/ipv6/nf_defrag_ipv6.h> 227f8a436eSJoe Stringer 237f8a436eSJoe Stringer #include "datapath.h" 247f8a436eSJoe Stringer #include "conntrack.h" 257f8a436eSJoe Stringer #include "flow.h" 267f8a436eSJoe Stringer #include "flow_netlink.h" 277f8a436eSJoe Stringer 287f8a436eSJoe Stringer struct ovs_ct_len_tbl { 297f8a436eSJoe Stringer size_t maxlen; 307f8a436eSJoe Stringer size_t minlen; 317f8a436eSJoe Stringer }; 327f8a436eSJoe Stringer 33182e3042SJoe Stringer /* Metadata mark for masked write to conntrack mark */ 34182e3042SJoe Stringer struct md_mark { 35182e3042SJoe Stringer u32 value; 36182e3042SJoe Stringer u32 mask; 37182e3042SJoe Stringer }; 38182e3042SJoe Stringer 39c2ac6673SJoe Stringer /* Metadata label for masked write to conntrack label. */ 4033db4125SJoe Stringer struct md_labels { 4133db4125SJoe Stringer struct ovs_key_ct_labels value; 4233db4125SJoe Stringer struct ovs_key_ct_labels mask; 43c2ac6673SJoe Stringer }; 44c2ac6673SJoe Stringer 457f8a436eSJoe Stringer /* Conntrack action context for execution. */ 467f8a436eSJoe Stringer struct ovs_conntrack_info { 47cae3a262SJoe Stringer struct nf_conntrack_helper *helper; 487f8a436eSJoe Stringer struct nf_conntrack_zone zone; 497f8a436eSJoe Stringer struct nf_conn *ct; 50ab38a7b5SJoe Stringer u8 commit : 1; 517f8a436eSJoe Stringer u16 family; 52182e3042SJoe Stringer struct md_mark mark; 5333db4125SJoe Stringer struct md_labels labels; 547f8a436eSJoe Stringer }; 557f8a436eSJoe Stringer 567f8a436eSJoe Stringer static u16 key_to_nfproto(const struct sw_flow_key *key) 577f8a436eSJoe Stringer { 587f8a436eSJoe Stringer switch (ntohs(key->eth.type)) { 597f8a436eSJoe Stringer case ETH_P_IP: 607f8a436eSJoe Stringer return NFPROTO_IPV4; 617f8a436eSJoe Stringer case ETH_P_IPV6: 627f8a436eSJoe Stringer return NFPROTO_IPV6; 637f8a436eSJoe Stringer default: 647f8a436eSJoe Stringer return NFPROTO_UNSPEC; 657f8a436eSJoe Stringer } 667f8a436eSJoe Stringer } 677f8a436eSJoe Stringer 687f8a436eSJoe Stringer /* Map SKB connection state into the values used by flow definition. */ 697f8a436eSJoe Stringer static u8 ovs_ct_get_state(enum ip_conntrack_info ctinfo) 707f8a436eSJoe Stringer { 717f8a436eSJoe Stringer u8 ct_state = OVS_CS_F_TRACKED; 727f8a436eSJoe Stringer 737f8a436eSJoe Stringer switch (ctinfo) { 747f8a436eSJoe Stringer case IP_CT_ESTABLISHED_REPLY: 757f8a436eSJoe Stringer case IP_CT_RELATED_REPLY: 767f8a436eSJoe Stringer case IP_CT_NEW_REPLY: 777f8a436eSJoe Stringer ct_state |= OVS_CS_F_REPLY_DIR; 787f8a436eSJoe Stringer break; 797f8a436eSJoe Stringer default: 807f8a436eSJoe Stringer break; 817f8a436eSJoe Stringer } 827f8a436eSJoe Stringer 837f8a436eSJoe Stringer switch (ctinfo) { 847f8a436eSJoe Stringer case IP_CT_ESTABLISHED: 857f8a436eSJoe Stringer case IP_CT_ESTABLISHED_REPLY: 867f8a436eSJoe Stringer ct_state |= OVS_CS_F_ESTABLISHED; 877f8a436eSJoe Stringer break; 887f8a436eSJoe Stringer case IP_CT_RELATED: 897f8a436eSJoe Stringer case IP_CT_RELATED_REPLY: 907f8a436eSJoe Stringer ct_state |= OVS_CS_F_RELATED; 917f8a436eSJoe Stringer break; 927f8a436eSJoe Stringer case IP_CT_NEW: 937f8a436eSJoe Stringer case IP_CT_NEW_REPLY: 947f8a436eSJoe Stringer ct_state |= OVS_CS_F_NEW; 957f8a436eSJoe Stringer break; 967f8a436eSJoe Stringer default: 977f8a436eSJoe Stringer break; 987f8a436eSJoe Stringer } 997f8a436eSJoe Stringer 1007f8a436eSJoe Stringer return ct_state; 1017f8a436eSJoe Stringer } 1027f8a436eSJoe Stringer 1030d5cdef8SJoe Stringer static u32 ovs_ct_get_mark(const struct nf_conn *ct) 1040d5cdef8SJoe Stringer { 1050d5cdef8SJoe Stringer #if IS_ENABLED(CONFIG_NF_CONNTRACK_MARK) 1060d5cdef8SJoe Stringer return ct ? ct->mark : 0; 1070d5cdef8SJoe Stringer #else 1080d5cdef8SJoe Stringer return 0; 1090d5cdef8SJoe Stringer #endif 1100d5cdef8SJoe Stringer } 1110d5cdef8SJoe Stringer 11233db4125SJoe Stringer static void ovs_ct_get_labels(const struct nf_conn *ct, 11333db4125SJoe Stringer struct ovs_key_ct_labels *labels) 114c2ac6673SJoe Stringer { 115c2ac6673SJoe Stringer struct nf_conn_labels *cl = ct ? nf_ct_labels_find(ct) : NULL; 116c2ac6673SJoe Stringer 117c2ac6673SJoe Stringer if (cl) { 118c2ac6673SJoe Stringer size_t len = cl->words * sizeof(long); 119c2ac6673SJoe Stringer 12033db4125SJoe Stringer if (len > OVS_CT_LABELS_LEN) 12133db4125SJoe Stringer len = OVS_CT_LABELS_LEN; 12233db4125SJoe Stringer else if (len < OVS_CT_LABELS_LEN) 12333db4125SJoe Stringer memset(labels, 0, OVS_CT_LABELS_LEN); 12433db4125SJoe Stringer memcpy(labels, cl->bits, len); 125c2ac6673SJoe Stringer } else { 12633db4125SJoe Stringer memset(labels, 0, OVS_CT_LABELS_LEN); 127c2ac6673SJoe Stringer } 128c2ac6673SJoe Stringer } 129c2ac6673SJoe Stringer 1307f8a436eSJoe Stringer static void __ovs_ct_update_key(struct sw_flow_key *key, u8 state, 131182e3042SJoe Stringer const struct nf_conntrack_zone *zone, 132182e3042SJoe Stringer const struct nf_conn *ct) 1337f8a436eSJoe Stringer { 1347f8a436eSJoe Stringer key->ct.state = state; 1357f8a436eSJoe Stringer key->ct.zone = zone->id; 1360d5cdef8SJoe Stringer key->ct.mark = ovs_ct_get_mark(ct); 13733db4125SJoe Stringer ovs_ct_get_labels(ct, &key->ct.labels); 1387f8a436eSJoe Stringer } 1397f8a436eSJoe Stringer 1407f8a436eSJoe Stringer /* Update 'key' based on skb->nfct. If 'post_ct' is true, then OVS has 1417f8a436eSJoe Stringer * previously sent the packet to conntrack via the ct action. 1427f8a436eSJoe Stringer */ 1437f8a436eSJoe Stringer static void ovs_ct_update_key(const struct sk_buff *skb, 1447f8a436eSJoe Stringer struct sw_flow_key *key, bool post_ct) 1457f8a436eSJoe Stringer { 1467f8a436eSJoe Stringer const struct nf_conntrack_zone *zone = &nf_ct_zone_dflt; 1477f8a436eSJoe Stringer enum ip_conntrack_info ctinfo; 1487f8a436eSJoe Stringer struct nf_conn *ct; 1497f8a436eSJoe Stringer u8 state = 0; 1507f8a436eSJoe Stringer 1517f8a436eSJoe Stringer ct = nf_ct_get(skb, &ctinfo); 1527f8a436eSJoe Stringer if (ct) { 1537f8a436eSJoe Stringer state = ovs_ct_get_state(ctinfo); 1547f8a436eSJoe Stringer if (ct->master) 1557f8a436eSJoe Stringer state |= OVS_CS_F_RELATED; 1567f8a436eSJoe Stringer zone = nf_ct_zone(ct); 1577f8a436eSJoe Stringer } else if (post_ct) { 1587f8a436eSJoe Stringer state = OVS_CS_F_TRACKED | OVS_CS_F_INVALID; 1597f8a436eSJoe Stringer } 160182e3042SJoe Stringer __ovs_ct_update_key(key, state, zone, ct); 1617f8a436eSJoe Stringer } 1627f8a436eSJoe Stringer 1637f8a436eSJoe Stringer void ovs_ct_fill_key(const struct sk_buff *skb, struct sw_flow_key *key) 1647f8a436eSJoe Stringer { 1657f8a436eSJoe Stringer ovs_ct_update_key(skb, key, false); 1667f8a436eSJoe Stringer } 1677f8a436eSJoe Stringer 1687f8a436eSJoe Stringer int ovs_ct_put_key(const struct sw_flow_key *key, struct sk_buff *skb) 1697f8a436eSJoe Stringer { 170fbccce59SJoe Stringer if (nla_put_u32(skb, OVS_KEY_ATTR_CT_STATE, key->ct.state)) 1717f8a436eSJoe Stringer return -EMSGSIZE; 1727f8a436eSJoe Stringer 1737f8a436eSJoe Stringer if (IS_ENABLED(CONFIG_NF_CONNTRACK_ZONES) && 1747f8a436eSJoe Stringer nla_put_u16(skb, OVS_KEY_ATTR_CT_ZONE, key->ct.zone)) 1757f8a436eSJoe Stringer return -EMSGSIZE; 1767f8a436eSJoe Stringer 177182e3042SJoe Stringer if (IS_ENABLED(CONFIG_NF_CONNTRACK_MARK) && 178182e3042SJoe Stringer nla_put_u32(skb, OVS_KEY_ATTR_CT_MARK, key->ct.mark)) 179182e3042SJoe Stringer return -EMSGSIZE; 180182e3042SJoe Stringer 1819723e6abSValentin Rothberg if (IS_ENABLED(CONFIG_NF_CONNTRACK_LABELS) && 18233db4125SJoe Stringer nla_put(skb, OVS_KEY_ATTR_CT_LABELS, sizeof(key->ct.labels), 18333db4125SJoe Stringer &key->ct.labels)) 184c2ac6673SJoe Stringer return -EMSGSIZE; 185c2ac6673SJoe Stringer 186182e3042SJoe Stringer return 0; 187182e3042SJoe Stringer } 188182e3042SJoe Stringer 189182e3042SJoe Stringer static int ovs_ct_set_mark(struct sk_buff *skb, struct sw_flow_key *key, 190182e3042SJoe Stringer u32 ct_mark, u32 mask) 191182e3042SJoe Stringer { 1920d5cdef8SJoe Stringer #if IS_ENABLED(CONFIG_NF_CONNTRACK_MARK) 193182e3042SJoe Stringer enum ip_conntrack_info ctinfo; 194182e3042SJoe Stringer struct nf_conn *ct; 195182e3042SJoe Stringer u32 new_mark; 196182e3042SJoe Stringer 197182e3042SJoe Stringer 198182e3042SJoe Stringer /* The connection could be invalid, in which case set_mark is no-op. */ 199182e3042SJoe Stringer ct = nf_ct_get(skb, &ctinfo); 200182e3042SJoe Stringer if (!ct) 201182e3042SJoe Stringer return 0; 202182e3042SJoe Stringer 203182e3042SJoe Stringer new_mark = ct_mark | (ct->mark & ~(mask)); 204182e3042SJoe Stringer if (ct->mark != new_mark) { 205182e3042SJoe Stringer ct->mark = new_mark; 206182e3042SJoe Stringer nf_conntrack_event_cache(IPCT_MARK, ct); 207182e3042SJoe Stringer key->ct.mark = new_mark; 208182e3042SJoe Stringer } 209182e3042SJoe Stringer 2107f8a436eSJoe Stringer return 0; 2110d5cdef8SJoe Stringer #else 2120d5cdef8SJoe Stringer return -ENOTSUPP; 2130d5cdef8SJoe Stringer #endif 2147f8a436eSJoe Stringer } 2157f8a436eSJoe Stringer 21633db4125SJoe Stringer static int ovs_ct_set_labels(struct sk_buff *skb, struct sw_flow_key *key, 21733db4125SJoe Stringer const struct ovs_key_ct_labels *labels, 21833db4125SJoe Stringer const struct ovs_key_ct_labels *mask) 219c2ac6673SJoe Stringer { 220c2ac6673SJoe Stringer enum ip_conntrack_info ctinfo; 221c2ac6673SJoe Stringer struct nf_conn_labels *cl; 222c2ac6673SJoe Stringer struct nf_conn *ct; 223c2ac6673SJoe Stringer int err; 224c2ac6673SJoe Stringer 225c2ac6673SJoe Stringer if (!IS_ENABLED(CONFIG_NF_CONNTRACK_LABELS)) 226c2ac6673SJoe Stringer return -ENOTSUPP; 227c2ac6673SJoe Stringer 228c2ac6673SJoe Stringer /* The connection could be invalid, in which case set_label is no-op.*/ 229c2ac6673SJoe Stringer ct = nf_ct_get(skb, &ctinfo); 230c2ac6673SJoe Stringer if (!ct) 231c2ac6673SJoe Stringer return 0; 232c2ac6673SJoe Stringer 233c2ac6673SJoe Stringer cl = nf_ct_labels_find(ct); 234c2ac6673SJoe Stringer if (!cl) { 235c2ac6673SJoe Stringer nf_ct_labels_ext_add(ct); 236c2ac6673SJoe Stringer cl = nf_ct_labels_find(ct); 237c2ac6673SJoe Stringer } 23833db4125SJoe Stringer if (!cl || cl->words * sizeof(long) < OVS_CT_LABELS_LEN) 239c2ac6673SJoe Stringer return -ENOSPC; 240c2ac6673SJoe Stringer 24133db4125SJoe Stringer err = nf_connlabels_replace(ct, (u32 *)labels, (u32 *)mask, 24233db4125SJoe Stringer OVS_CT_LABELS_LEN / sizeof(u32)); 243c2ac6673SJoe Stringer if (err) 244c2ac6673SJoe Stringer return err; 245c2ac6673SJoe Stringer 24633db4125SJoe Stringer ovs_ct_get_labels(ct, &key->ct.labels); 247c2ac6673SJoe Stringer return 0; 248c2ac6673SJoe Stringer } 249c2ac6673SJoe Stringer 250cae3a262SJoe Stringer /* 'skb' should already be pulled to nh_ofs. */ 251cae3a262SJoe Stringer static int ovs_ct_helper(struct sk_buff *skb, u16 proto) 252cae3a262SJoe Stringer { 253cae3a262SJoe Stringer const struct nf_conntrack_helper *helper; 254cae3a262SJoe Stringer const struct nf_conn_help *help; 255cae3a262SJoe Stringer enum ip_conntrack_info ctinfo; 256cae3a262SJoe Stringer unsigned int protoff; 257cae3a262SJoe Stringer struct nf_conn *ct; 258cae3a262SJoe Stringer 259cae3a262SJoe Stringer ct = nf_ct_get(skb, &ctinfo); 260cae3a262SJoe Stringer if (!ct || ctinfo == IP_CT_RELATED_REPLY) 261cae3a262SJoe Stringer return NF_ACCEPT; 262cae3a262SJoe Stringer 263cae3a262SJoe Stringer help = nfct_help(ct); 264cae3a262SJoe Stringer if (!help) 265cae3a262SJoe Stringer return NF_ACCEPT; 266cae3a262SJoe Stringer 267cae3a262SJoe Stringer helper = rcu_dereference(help->helper); 268cae3a262SJoe Stringer if (!helper) 269cae3a262SJoe Stringer return NF_ACCEPT; 270cae3a262SJoe Stringer 271cae3a262SJoe Stringer switch (proto) { 272cae3a262SJoe Stringer case NFPROTO_IPV4: 273cae3a262SJoe Stringer protoff = ip_hdrlen(skb); 274cae3a262SJoe Stringer break; 275cae3a262SJoe Stringer case NFPROTO_IPV6: { 276cae3a262SJoe Stringer u8 nexthdr = ipv6_hdr(skb)->nexthdr; 277cae3a262SJoe Stringer __be16 frag_off; 278cc570605SJoe Stringer int ofs; 279cae3a262SJoe Stringer 280cc570605SJoe Stringer ofs = ipv6_skip_exthdr(skb, sizeof(struct ipv6hdr), &nexthdr, 281cc570605SJoe Stringer &frag_off); 282cc570605SJoe Stringer if (ofs < 0 || (frag_off & htons(~0x7)) != 0) { 283cae3a262SJoe Stringer pr_debug("proto header not found\n"); 284cae3a262SJoe Stringer return NF_ACCEPT; 285cae3a262SJoe Stringer } 286cc570605SJoe Stringer protoff = ofs; 287cae3a262SJoe Stringer break; 288cae3a262SJoe Stringer } 289cae3a262SJoe Stringer default: 290cae3a262SJoe Stringer WARN_ONCE(1, "helper invoked on non-IP family!"); 291cae3a262SJoe Stringer return NF_DROP; 292cae3a262SJoe Stringer } 293cae3a262SJoe Stringer 294cae3a262SJoe Stringer return helper->help(skb, protoff, ct, ctinfo); 295cae3a262SJoe Stringer } 296cae3a262SJoe Stringer 2977f8a436eSJoe Stringer static int handle_fragments(struct net *net, struct sw_flow_key *key, 2987f8a436eSJoe Stringer u16 zone, struct sk_buff *skb) 2997f8a436eSJoe Stringer { 3007f8a436eSJoe Stringer struct ovs_skb_cb ovs_cb = *OVS_CB(skb); 3017f8a436eSJoe Stringer 3027f8a436eSJoe Stringer if (key->eth.type == htons(ETH_P_IP)) { 3037f8a436eSJoe Stringer enum ip_defrag_users user = IP_DEFRAG_CONNTRACK_IN + zone; 3047f8a436eSJoe Stringer int err; 3057f8a436eSJoe Stringer 3067f8a436eSJoe Stringer memset(IPCB(skb), 0, sizeof(struct inet_skb_parm)); 3077f8a436eSJoe Stringer err = ip_defrag(skb, user); 3087f8a436eSJoe Stringer if (err) 3097f8a436eSJoe Stringer return err; 3107f8a436eSJoe Stringer 3117f8a436eSJoe Stringer ovs_cb.mru = IPCB(skb)->frag_max_size; 3127f8a436eSJoe Stringer } else if (key->eth.type == htons(ETH_P_IPV6)) { 3137f8a436eSJoe Stringer #if IS_ENABLED(CONFIG_NF_DEFRAG_IPV6) 3147f8a436eSJoe Stringer enum ip6_defrag_users user = IP6_DEFRAG_CONNTRACK_IN + zone; 3157f8a436eSJoe Stringer struct sk_buff *reasm; 3167f8a436eSJoe Stringer 3177f8a436eSJoe Stringer memset(IP6CB(skb), 0, sizeof(struct inet6_skb_parm)); 3187f8a436eSJoe Stringer reasm = nf_ct_frag6_gather(skb, user); 3197f8a436eSJoe Stringer if (!reasm) 3207f8a436eSJoe Stringer return -EINPROGRESS; 3217f8a436eSJoe Stringer 3227f8a436eSJoe Stringer if (skb == reasm) 3237f8a436eSJoe Stringer return -EINVAL; 3247f8a436eSJoe Stringer 3257f8a436eSJoe Stringer key->ip.proto = ipv6_hdr(reasm)->nexthdr; 3267f8a436eSJoe Stringer skb_morph(skb, reasm); 3277f8a436eSJoe Stringer consume_skb(reasm); 3287f8a436eSJoe Stringer ovs_cb.mru = IP6CB(skb)->frag_max_size; 3297f8a436eSJoe Stringer #else 3307f8a436eSJoe Stringer return -EPFNOSUPPORT; 3317f8a436eSJoe Stringer #endif 3327f8a436eSJoe Stringer } else { 3337f8a436eSJoe Stringer return -EPFNOSUPPORT; 3347f8a436eSJoe Stringer } 3357f8a436eSJoe Stringer 3367f8a436eSJoe Stringer key->ip.frag = OVS_FRAG_TYPE_NONE; 3377f8a436eSJoe Stringer skb_clear_hash(skb); 3387f8a436eSJoe Stringer skb->ignore_df = 1; 3397f8a436eSJoe Stringer *OVS_CB(skb) = ovs_cb; 3407f8a436eSJoe Stringer 3417f8a436eSJoe Stringer return 0; 3427f8a436eSJoe Stringer } 3437f8a436eSJoe Stringer 3447f8a436eSJoe Stringer static struct nf_conntrack_expect * 3457f8a436eSJoe Stringer ovs_ct_expect_find(struct net *net, const struct nf_conntrack_zone *zone, 3467f8a436eSJoe Stringer u16 proto, const struct sk_buff *skb) 3477f8a436eSJoe Stringer { 3487f8a436eSJoe Stringer struct nf_conntrack_tuple tuple; 3497f8a436eSJoe Stringer 3507f8a436eSJoe Stringer if (!nf_ct_get_tuplepr(skb, skb_network_offset(skb), proto, &tuple)) 3517f8a436eSJoe Stringer return NULL; 3527f8a436eSJoe Stringer return __nf_ct_expect_find(net, zone, &tuple); 3537f8a436eSJoe Stringer } 3547f8a436eSJoe Stringer 3557f8a436eSJoe Stringer /* Determine whether skb->nfct is equal to the result of conntrack lookup. */ 3567f8a436eSJoe Stringer static bool skb_nfct_cached(const struct net *net, const struct sk_buff *skb, 3577f8a436eSJoe Stringer const struct ovs_conntrack_info *info) 3587f8a436eSJoe Stringer { 3597f8a436eSJoe Stringer enum ip_conntrack_info ctinfo; 3607f8a436eSJoe Stringer struct nf_conn *ct; 3617f8a436eSJoe Stringer 3627f8a436eSJoe Stringer ct = nf_ct_get(skb, &ctinfo); 3637f8a436eSJoe Stringer if (!ct) 3647f8a436eSJoe Stringer return false; 3657f8a436eSJoe Stringer if (!net_eq(net, read_pnet(&ct->ct_net))) 3667f8a436eSJoe Stringer return false; 3677f8a436eSJoe Stringer if (!nf_ct_zone_equal_any(info->ct, nf_ct_zone(ct))) 3687f8a436eSJoe Stringer return false; 369cae3a262SJoe Stringer if (info->helper) { 370cae3a262SJoe Stringer struct nf_conn_help *help; 371cae3a262SJoe Stringer 372cae3a262SJoe Stringer help = nf_ct_ext_find(ct, NF_CT_EXT_HELPER); 373cae3a262SJoe Stringer if (help && rcu_access_pointer(help->helper) != info->helper) 374cae3a262SJoe Stringer return false; 375cae3a262SJoe Stringer } 3767f8a436eSJoe Stringer 3777f8a436eSJoe Stringer return true; 3787f8a436eSJoe Stringer } 3797f8a436eSJoe Stringer 3807f8a436eSJoe Stringer static int __ovs_ct_lookup(struct net *net, const struct sw_flow_key *key, 3817f8a436eSJoe Stringer const struct ovs_conntrack_info *info, 3827f8a436eSJoe Stringer struct sk_buff *skb) 3837f8a436eSJoe Stringer { 3847f8a436eSJoe Stringer /* If we are recirculating packets to match on conntrack fields and 3857f8a436eSJoe Stringer * committing with a separate conntrack action, then we don't need to 3867f8a436eSJoe Stringer * actually run the packet through conntrack twice unless it's for a 3877f8a436eSJoe Stringer * different zone. 3887f8a436eSJoe Stringer */ 3897f8a436eSJoe Stringer if (!skb_nfct_cached(net, skb, info)) { 3907f8a436eSJoe Stringer struct nf_conn *tmpl = info->ct; 3917f8a436eSJoe Stringer 3927f8a436eSJoe Stringer /* Associate skb with specified zone. */ 3937f8a436eSJoe Stringer if (tmpl) { 3947f8a436eSJoe Stringer if (skb->nfct) 3957f8a436eSJoe Stringer nf_conntrack_put(skb->nfct); 3967f8a436eSJoe Stringer nf_conntrack_get(&tmpl->ct_general); 3977f8a436eSJoe Stringer skb->nfct = &tmpl->ct_general; 3987f8a436eSJoe Stringer skb->nfctinfo = IP_CT_NEW; 3997f8a436eSJoe Stringer } 4007f8a436eSJoe Stringer 4017f8a436eSJoe Stringer if (nf_conntrack_in(net, info->family, NF_INET_PRE_ROUTING, 4027f8a436eSJoe Stringer skb) != NF_ACCEPT) 4037f8a436eSJoe Stringer return -ENOENT; 404cae3a262SJoe Stringer 405cae3a262SJoe Stringer if (ovs_ct_helper(skb, info->family) != NF_ACCEPT) { 406cae3a262SJoe Stringer WARN_ONCE(1, "helper rejected packet"); 407cae3a262SJoe Stringer return -EINVAL; 408cae3a262SJoe Stringer } 4097f8a436eSJoe Stringer } 4107f8a436eSJoe Stringer 4117f8a436eSJoe Stringer return 0; 4127f8a436eSJoe Stringer } 4137f8a436eSJoe Stringer 4147f8a436eSJoe Stringer /* Lookup connection and read fields into key. */ 4157f8a436eSJoe Stringer static int ovs_ct_lookup(struct net *net, struct sw_flow_key *key, 4167f8a436eSJoe Stringer const struct ovs_conntrack_info *info, 4177f8a436eSJoe Stringer struct sk_buff *skb) 4187f8a436eSJoe Stringer { 4197f8a436eSJoe Stringer struct nf_conntrack_expect *exp; 4207f8a436eSJoe Stringer 4217f8a436eSJoe Stringer exp = ovs_ct_expect_find(net, &info->zone, info->family, skb); 4227f8a436eSJoe Stringer if (exp) { 4237f8a436eSJoe Stringer u8 state; 4247f8a436eSJoe Stringer 4257f8a436eSJoe Stringer state = OVS_CS_F_TRACKED | OVS_CS_F_NEW | OVS_CS_F_RELATED; 426182e3042SJoe Stringer __ovs_ct_update_key(key, state, &info->zone, exp->master); 4277f8a436eSJoe Stringer } else { 4287f8a436eSJoe Stringer int err; 4297f8a436eSJoe Stringer 4307f8a436eSJoe Stringer err = __ovs_ct_lookup(net, key, info, skb); 4317f8a436eSJoe Stringer if (err) 4327f8a436eSJoe Stringer return err; 4337f8a436eSJoe Stringer 4347f8a436eSJoe Stringer ovs_ct_update_key(skb, key, true); 4357f8a436eSJoe Stringer } 4367f8a436eSJoe Stringer 4377f8a436eSJoe Stringer return 0; 4387f8a436eSJoe Stringer } 4397f8a436eSJoe Stringer 4407f8a436eSJoe Stringer /* Lookup connection and confirm if unconfirmed. */ 4417f8a436eSJoe Stringer static int ovs_ct_commit(struct net *net, struct sw_flow_key *key, 4427f8a436eSJoe Stringer const struct ovs_conntrack_info *info, 4437f8a436eSJoe Stringer struct sk_buff *skb) 4447f8a436eSJoe Stringer { 4457f8a436eSJoe Stringer u8 state; 4467f8a436eSJoe Stringer int err; 4477f8a436eSJoe Stringer 4487f8a436eSJoe Stringer state = key->ct.state; 4497f8a436eSJoe Stringer if (key->ct.zone == info->zone.id && 4507f8a436eSJoe Stringer ((state & OVS_CS_F_TRACKED) && !(state & OVS_CS_F_NEW))) { 4517f8a436eSJoe Stringer /* Previous lookup has shown that this connection is already 4527f8a436eSJoe Stringer * tracked and committed. Skip committing. 4537f8a436eSJoe Stringer */ 4547f8a436eSJoe Stringer return 0; 4557f8a436eSJoe Stringer } 4567f8a436eSJoe Stringer 4577f8a436eSJoe Stringer err = __ovs_ct_lookup(net, key, info, skb); 4587f8a436eSJoe Stringer if (err) 4597f8a436eSJoe Stringer return err; 4607f8a436eSJoe Stringer if (nf_conntrack_confirm(skb) != NF_ACCEPT) 4617f8a436eSJoe Stringer return -EINVAL; 4627f8a436eSJoe Stringer 4637f8a436eSJoe Stringer ovs_ct_update_key(skb, key, true); 4647f8a436eSJoe Stringer 4657f8a436eSJoe Stringer return 0; 4667f8a436eSJoe Stringer } 4677f8a436eSJoe Stringer 46833db4125SJoe Stringer static bool labels_nonzero(const struct ovs_key_ct_labels *labels) 469c2ac6673SJoe Stringer { 470c2ac6673SJoe Stringer size_t i; 471c2ac6673SJoe Stringer 47233db4125SJoe Stringer for (i = 0; i < sizeof(*labels); i++) 47333db4125SJoe Stringer if (labels->ct_labels[i]) 474c2ac6673SJoe Stringer return true; 475c2ac6673SJoe Stringer 476c2ac6673SJoe Stringer return false; 477c2ac6673SJoe Stringer } 478c2ac6673SJoe Stringer 4797f8a436eSJoe Stringer int ovs_ct_execute(struct net *net, struct sk_buff *skb, 4807f8a436eSJoe Stringer struct sw_flow_key *key, 4817f8a436eSJoe Stringer const struct ovs_conntrack_info *info) 4827f8a436eSJoe Stringer { 4837f8a436eSJoe Stringer int nh_ofs; 4847f8a436eSJoe Stringer int err; 4857f8a436eSJoe Stringer 4867f8a436eSJoe Stringer /* The conntrack module expects to be working at L3. */ 4877f8a436eSJoe Stringer nh_ofs = skb_network_offset(skb); 4887f8a436eSJoe Stringer skb_pull(skb, nh_ofs); 4897f8a436eSJoe Stringer 4907f8a436eSJoe Stringer if (key->ip.frag != OVS_FRAG_TYPE_NONE) { 4917f8a436eSJoe Stringer err = handle_fragments(net, key, info->zone.id, skb); 4927f8a436eSJoe Stringer if (err) 4937f8a436eSJoe Stringer return err; 4947f8a436eSJoe Stringer } 4957f8a436eSJoe Stringer 496ab38a7b5SJoe Stringer if (info->commit) 4977f8a436eSJoe Stringer err = ovs_ct_commit(net, key, info, skb); 4987f8a436eSJoe Stringer else 4997f8a436eSJoe Stringer err = ovs_ct_lookup(net, key, info, skb); 500182e3042SJoe Stringer if (err) 501182e3042SJoe Stringer goto err; 5027f8a436eSJoe Stringer 503c2ac6673SJoe Stringer if (info->mark.mask) { 504182e3042SJoe Stringer err = ovs_ct_set_mark(skb, key, info->mark.value, 505182e3042SJoe Stringer info->mark.mask); 506c2ac6673SJoe Stringer if (err) 507c2ac6673SJoe Stringer goto err; 508c2ac6673SJoe Stringer } 50933db4125SJoe Stringer if (labels_nonzero(&info->labels.mask)) 51033db4125SJoe Stringer err = ovs_ct_set_labels(skb, key, &info->labels.value, 51133db4125SJoe Stringer &info->labels.mask); 512182e3042SJoe Stringer err: 5137f8a436eSJoe Stringer skb_push(skb, nh_ofs); 5147f8a436eSJoe Stringer return err; 5157f8a436eSJoe Stringer } 5167f8a436eSJoe Stringer 517cae3a262SJoe Stringer static int ovs_ct_add_helper(struct ovs_conntrack_info *info, const char *name, 518cae3a262SJoe Stringer const struct sw_flow_key *key, bool log) 519cae3a262SJoe Stringer { 520cae3a262SJoe Stringer struct nf_conntrack_helper *helper; 521cae3a262SJoe Stringer struct nf_conn_help *help; 522cae3a262SJoe Stringer 523cae3a262SJoe Stringer helper = nf_conntrack_helper_try_module_get(name, info->family, 524cae3a262SJoe Stringer key->ip.proto); 525cae3a262SJoe Stringer if (!helper) { 526cae3a262SJoe Stringer OVS_NLERR(log, "Unknown helper \"%s\"", name); 527cae3a262SJoe Stringer return -EINVAL; 528cae3a262SJoe Stringer } 529cae3a262SJoe Stringer 530cae3a262SJoe Stringer help = nf_ct_helper_ext_add(info->ct, helper, GFP_KERNEL); 531cae3a262SJoe Stringer if (!help) { 532cae3a262SJoe Stringer module_put(helper->me); 533cae3a262SJoe Stringer return -ENOMEM; 534cae3a262SJoe Stringer } 535cae3a262SJoe Stringer 536cae3a262SJoe Stringer rcu_assign_pointer(help->helper, helper); 537cae3a262SJoe Stringer info->helper = helper; 538cae3a262SJoe Stringer return 0; 539cae3a262SJoe Stringer } 540cae3a262SJoe Stringer 5417f8a436eSJoe Stringer static const struct ovs_ct_len_tbl ovs_ct_attr_lens[OVS_CT_ATTR_MAX + 1] = { 542ab38a7b5SJoe Stringer [OVS_CT_ATTR_COMMIT] = { .minlen = 0, .maxlen = 0 }, 5437f8a436eSJoe Stringer [OVS_CT_ATTR_ZONE] = { .minlen = sizeof(u16), 5447f8a436eSJoe Stringer .maxlen = sizeof(u16) }, 545182e3042SJoe Stringer [OVS_CT_ATTR_MARK] = { .minlen = sizeof(struct md_mark), 546182e3042SJoe Stringer .maxlen = sizeof(struct md_mark) }, 54733db4125SJoe Stringer [OVS_CT_ATTR_LABELS] = { .minlen = sizeof(struct md_labels), 54833db4125SJoe Stringer .maxlen = sizeof(struct md_labels) }, 549cae3a262SJoe Stringer [OVS_CT_ATTR_HELPER] = { .minlen = 1, 550cae3a262SJoe Stringer .maxlen = NF_CT_HELPER_NAME_LEN } 5517f8a436eSJoe Stringer }; 5527f8a436eSJoe Stringer 5537f8a436eSJoe Stringer static int parse_ct(const struct nlattr *attr, struct ovs_conntrack_info *info, 554cae3a262SJoe Stringer const char **helper, bool log) 5557f8a436eSJoe Stringer { 5567f8a436eSJoe Stringer struct nlattr *a; 5577f8a436eSJoe Stringer int rem; 5587f8a436eSJoe Stringer 5597f8a436eSJoe Stringer nla_for_each_nested(a, attr, rem) { 5607f8a436eSJoe Stringer int type = nla_type(a); 5617f8a436eSJoe Stringer int maxlen = ovs_ct_attr_lens[type].maxlen; 5627f8a436eSJoe Stringer int minlen = ovs_ct_attr_lens[type].minlen; 5637f8a436eSJoe Stringer 5647f8a436eSJoe Stringer if (type > OVS_CT_ATTR_MAX) { 5657f8a436eSJoe Stringer OVS_NLERR(log, 5667f8a436eSJoe Stringer "Unknown conntrack attr (type=%d, max=%d)", 5677f8a436eSJoe Stringer type, OVS_CT_ATTR_MAX); 5687f8a436eSJoe Stringer return -EINVAL; 5697f8a436eSJoe Stringer } 5707f8a436eSJoe Stringer if (nla_len(a) < minlen || nla_len(a) > maxlen) { 5717f8a436eSJoe Stringer OVS_NLERR(log, 5727f8a436eSJoe Stringer "Conntrack attr type has unexpected length (type=%d, length=%d, expected=%d)", 5737f8a436eSJoe Stringer type, nla_len(a), maxlen); 5747f8a436eSJoe Stringer return -EINVAL; 5757f8a436eSJoe Stringer } 5767f8a436eSJoe Stringer 5777f8a436eSJoe Stringer switch (type) { 578ab38a7b5SJoe Stringer case OVS_CT_ATTR_COMMIT: 579ab38a7b5SJoe Stringer info->commit = true; 5807f8a436eSJoe Stringer break; 5817f8a436eSJoe Stringer #ifdef CONFIG_NF_CONNTRACK_ZONES 5827f8a436eSJoe Stringer case OVS_CT_ATTR_ZONE: 5837f8a436eSJoe Stringer info->zone.id = nla_get_u16(a); 5847f8a436eSJoe Stringer break; 5857f8a436eSJoe Stringer #endif 586182e3042SJoe Stringer #ifdef CONFIG_NF_CONNTRACK_MARK 587182e3042SJoe Stringer case OVS_CT_ATTR_MARK: { 588182e3042SJoe Stringer struct md_mark *mark = nla_data(a); 589182e3042SJoe Stringer 590182e3042SJoe Stringer info->mark = *mark; 591182e3042SJoe Stringer break; 592182e3042SJoe Stringer } 593182e3042SJoe Stringer #endif 594c2ac6673SJoe Stringer #ifdef CONFIG_NF_CONNTRACK_LABELS 59533db4125SJoe Stringer case OVS_CT_ATTR_LABELS: { 59633db4125SJoe Stringer struct md_labels *labels = nla_data(a); 597c2ac6673SJoe Stringer 59833db4125SJoe Stringer info->labels = *labels; 599c2ac6673SJoe Stringer break; 600c2ac6673SJoe Stringer } 601c2ac6673SJoe Stringer #endif 602cae3a262SJoe Stringer case OVS_CT_ATTR_HELPER: 603cae3a262SJoe Stringer *helper = nla_data(a); 604cae3a262SJoe Stringer if (!memchr(*helper, '\0', nla_len(a))) { 605cae3a262SJoe Stringer OVS_NLERR(log, "Invalid conntrack helper"); 606cae3a262SJoe Stringer return -EINVAL; 607cae3a262SJoe Stringer } 608cae3a262SJoe Stringer break; 6097f8a436eSJoe Stringer default: 6107f8a436eSJoe Stringer OVS_NLERR(log, "Unknown conntrack attr (%d)", 6117f8a436eSJoe Stringer type); 6127f8a436eSJoe Stringer return -EINVAL; 6137f8a436eSJoe Stringer } 6147f8a436eSJoe Stringer } 6157f8a436eSJoe Stringer 6167f8a436eSJoe Stringer if (rem > 0) { 6177f8a436eSJoe Stringer OVS_NLERR(log, "Conntrack attr has %d unknown bytes", rem); 6187f8a436eSJoe Stringer return -EINVAL; 6197f8a436eSJoe Stringer } 6207f8a436eSJoe Stringer 6217f8a436eSJoe Stringer return 0; 6227f8a436eSJoe Stringer } 6237f8a436eSJoe Stringer 624c2ac6673SJoe Stringer bool ovs_ct_verify(struct net *net, enum ovs_key_attr attr) 6257f8a436eSJoe Stringer { 6267f8a436eSJoe Stringer if (attr == OVS_KEY_ATTR_CT_STATE) 6277f8a436eSJoe Stringer return true; 6287f8a436eSJoe Stringer if (IS_ENABLED(CONFIG_NF_CONNTRACK_ZONES) && 6297f8a436eSJoe Stringer attr == OVS_KEY_ATTR_CT_ZONE) 6307f8a436eSJoe Stringer return true; 631182e3042SJoe Stringer if (IS_ENABLED(CONFIG_NF_CONNTRACK_MARK) && 632182e3042SJoe Stringer attr == OVS_KEY_ATTR_CT_MARK) 633182e3042SJoe Stringer return true; 634c2ac6673SJoe Stringer if (IS_ENABLED(CONFIG_NF_CONNTRACK_LABELS) && 63533db4125SJoe Stringer attr == OVS_KEY_ATTR_CT_LABELS) { 636c2ac6673SJoe Stringer struct ovs_net *ovs_net = net_generic(net, ovs_net_id); 637c2ac6673SJoe Stringer 638c2ac6673SJoe Stringer return ovs_net->xt_label; 639c2ac6673SJoe Stringer } 6407f8a436eSJoe Stringer 6417f8a436eSJoe Stringer return false; 6427f8a436eSJoe Stringer } 6437f8a436eSJoe Stringer 6447f8a436eSJoe Stringer int ovs_ct_copy_action(struct net *net, const struct nlattr *attr, 6457f8a436eSJoe Stringer const struct sw_flow_key *key, 6467f8a436eSJoe Stringer struct sw_flow_actions **sfa, bool log) 6477f8a436eSJoe Stringer { 6487f8a436eSJoe Stringer struct ovs_conntrack_info ct_info; 649cae3a262SJoe Stringer const char *helper = NULL; 6507f8a436eSJoe Stringer u16 family; 6517f8a436eSJoe Stringer int err; 6527f8a436eSJoe Stringer 6537f8a436eSJoe Stringer family = key_to_nfproto(key); 6547f8a436eSJoe Stringer if (family == NFPROTO_UNSPEC) { 6557f8a436eSJoe Stringer OVS_NLERR(log, "ct family unspecified"); 6567f8a436eSJoe Stringer return -EINVAL; 6577f8a436eSJoe Stringer } 6587f8a436eSJoe Stringer 6597f8a436eSJoe Stringer memset(&ct_info, 0, sizeof(ct_info)); 6607f8a436eSJoe Stringer ct_info.family = family; 6617f8a436eSJoe Stringer 6627f8a436eSJoe Stringer nf_ct_zone_init(&ct_info.zone, NF_CT_DEFAULT_ZONE_ID, 6637f8a436eSJoe Stringer NF_CT_DEFAULT_ZONE_DIR, 0); 6647f8a436eSJoe Stringer 665cae3a262SJoe Stringer err = parse_ct(attr, &ct_info, &helper, log); 6667f8a436eSJoe Stringer if (err) 6677f8a436eSJoe Stringer return err; 6687f8a436eSJoe Stringer 6697f8a436eSJoe Stringer /* Set up template for tracking connections in specific zones. */ 6707f8a436eSJoe Stringer ct_info.ct = nf_ct_tmpl_alloc(net, &ct_info.zone, GFP_KERNEL); 6717f8a436eSJoe Stringer if (!ct_info.ct) { 6727f8a436eSJoe Stringer OVS_NLERR(log, "Failed to allocate conntrack template"); 6737f8a436eSJoe Stringer return -ENOMEM; 6747f8a436eSJoe Stringer } 675cae3a262SJoe Stringer if (helper) { 676cae3a262SJoe Stringer err = ovs_ct_add_helper(&ct_info, helper, key, log); 677cae3a262SJoe Stringer if (err) 678cae3a262SJoe Stringer goto err_free_ct; 679cae3a262SJoe Stringer } 6807f8a436eSJoe Stringer 6817f8a436eSJoe Stringer err = ovs_nla_add_action(sfa, OVS_ACTION_ATTR_CT, &ct_info, 6827f8a436eSJoe Stringer sizeof(ct_info), log); 6837f8a436eSJoe Stringer if (err) 6847f8a436eSJoe Stringer goto err_free_ct; 6857f8a436eSJoe Stringer 6867f8a436eSJoe Stringer __set_bit(IPS_CONFIRMED_BIT, &ct_info.ct->status); 6877f8a436eSJoe Stringer nf_conntrack_get(&ct_info.ct->ct_general); 6887f8a436eSJoe Stringer return 0; 6897f8a436eSJoe Stringer err_free_ct: 6907f8a436eSJoe Stringer nf_conntrack_free(ct_info.ct); 6917f8a436eSJoe Stringer return err; 6927f8a436eSJoe Stringer } 6937f8a436eSJoe Stringer 6947f8a436eSJoe Stringer int ovs_ct_action_to_attr(const struct ovs_conntrack_info *ct_info, 6957f8a436eSJoe Stringer struct sk_buff *skb) 6967f8a436eSJoe Stringer { 6977f8a436eSJoe Stringer struct nlattr *start; 6987f8a436eSJoe Stringer 6997f8a436eSJoe Stringer start = nla_nest_start(skb, OVS_ACTION_ATTR_CT); 7007f8a436eSJoe Stringer if (!start) 7017f8a436eSJoe Stringer return -EMSGSIZE; 7027f8a436eSJoe Stringer 703ab38a7b5SJoe Stringer if (ct_info->commit && nla_put_flag(skb, OVS_CT_ATTR_COMMIT)) 7047f8a436eSJoe Stringer return -EMSGSIZE; 7057f8a436eSJoe Stringer if (IS_ENABLED(CONFIG_NF_CONNTRACK_ZONES) && 7067f8a436eSJoe Stringer nla_put_u16(skb, OVS_CT_ATTR_ZONE, ct_info->zone.id)) 7077f8a436eSJoe Stringer return -EMSGSIZE; 708182e3042SJoe Stringer if (IS_ENABLED(CONFIG_NF_CONNTRACK_MARK) && 709182e3042SJoe Stringer nla_put(skb, OVS_CT_ATTR_MARK, sizeof(ct_info->mark), 710182e3042SJoe Stringer &ct_info->mark)) 711182e3042SJoe Stringer return -EMSGSIZE; 712c2ac6673SJoe Stringer if (IS_ENABLED(CONFIG_NF_CONNTRACK_LABELS) && 71333db4125SJoe Stringer nla_put(skb, OVS_CT_ATTR_LABELS, sizeof(ct_info->labels), 71433db4125SJoe Stringer &ct_info->labels)) 715c2ac6673SJoe Stringer return -EMSGSIZE; 716cae3a262SJoe Stringer if (ct_info->helper) { 717cae3a262SJoe Stringer if (nla_put_string(skb, OVS_CT_ATTR_HELPER, 718cae3a262SJoe Stringer ct_info->helper->name)) 719cae3a262SJoe Stringer return -EMSGSIZE; 720cae3a262SJoe Stringer } 7217f8a436eSJoe Stringer 7227f8a436eSJoe Stringer nla_nest_end(skb, start); 7237f8a436eSJoe Stringer 7247f8a436eSJoe Stringer return 0; 7257f8a436eSJoe Stringer } 7267f8a436eSJoe Stringer 7277f8a436eSJoe Stringer void ovs_ct_free_action(const struct nlattr *a) 7287f8a436eSJoe Stringer { 7297f8a436eSJoe Stringer struct ovs_conntrack_info *ct_info = nla_data(a); 7307f8a436eSJoe Stringer 731cae3a262SJoe Stringer if (ct_info->helper) 732cae3a262SJoe Stringer module_put(ct_info->helper->me); 7337f8a436eSJoe Stringer if (ct_info->ct) 7347f8a436eSJoe Stringer nf_ct_put(ct_info->ct); 7357f8a436eSJoe Stringer } 736c2ac6673SJoe Stringer 737c2ac6673SJoe Stringer void ovs_ct_init(struct net *net) 738c2ac6673SJoe Stringer { 73933db4125SJoe Stringer unsigned int n_bits = sizeof(struct ovs_key_ct_labels) * BITS_PER_BYTE; 740c2ac6673SJoe Stringer struct ovs_net *ovs_net = net_generic(net, ovs_net_id); 741c2ac6673SJoe Stringer 742c2ac6673SJoe Stringer if (nf_connlabels_get(net, n_bits)) { 743c2ac6673SJoe Stringer ovs_net->xt_label = false; 744c2ac6673SJoe Stringer OVS_NLERR(true, "Failed to set connlabel length"); 745c2ac6673SJoe Stringer } else { 746c2ac6673SJoe Stringer ovs_net->xt_label = true; 747c2ac6673SJoe Stringer } 748c2ac6673SJoe Stringer } 749c2ac6673SJoe Stringer 750c2ac6673SJoe Stringer void ovs_ct_exit(struct net *net) 751c2ac6673SJoe Stringer { 752c2ac6673SJoe Stringer struct ovs_net *ovs_net = net_generic(net, ovs_net_id); 753c2ac6673SJoe Stringer 754c2ac6673SJoe Stringer if (ovs_net->xt_label) 755c2ac6673SJoe Stringer nf_connlabels_put(net); 756c2ac6673SJoe Stringer } 757