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 562f3ab9f9SJoe Stringer static void __ovs_ct_free_action(struct ovs_conntrack_info *ct_info); 572f3ab9f9SJoe Stringer 587f8a436eSJoe Stringer static u16 key_to_nfproto(const struct sw_flow_key *key) 597f8a436eSJoe Stringer { 607f8a436eSJoe Stringer switch (ntohs(key->eth.type)) { 617f8a436eSJoe Stringer case ETH_P_IP: 627f8a436eSJoe Stringer return NFPROTO_IPV4; 637f8a436eSJoe Stringer case ETH_P_IPV6: 647f8a436eSJoe Stringer return NFPROTO_IPV6; 657f8a436eSJoe Stringer default: 667f8a436eSJoe Stringer return NFPROTO_UNSPEC; 677f8a436eSJoe Stringer } 687f8a436eSJoe Stringer } 697f8a436eSJoe Stringer 707f8a436eSJoe Stringer /* Map SKB connection state into the values used by flow definition. */ 717f8a436eSJoe Stringer static u8 ovs_ct_get_state(enum ip_conntrack_info ctinfo) 727f8a436eSJoe Stringer { 737f8a436eSJoe Stringer u8 ct_state = OVS_CS_F_TRACKED; 747f8a436eSJoe Stringer 757f8a436eSJoe Stringer switch (ctinfo) { 767f8a436eSJoe Stringer case IP_CT_ESTABLISHED_REPLY: 777f8a436eSJoe Stringer case IP_CT_RELATED_REPLY: 787f8a436eSJoe Stringer ct_state |= OVS_CS_F_REPLY_DIR; 797f8a436eSJoe Stringer break; 807f8a436eSJoe Stringer default: 817f8a436eSJoe Stringer break; 827f8a436eSJoe Stringer } 837f8a436eSJoe Stringer 847f8a436eSJoe Stringer switch (ctinfo) { 857f8a436eSJoe Stringer case IP_CT_ESTABLISHED: 867f8a436eSJoe Stringer case IP_CT_ESTABLISHED_REPLY: 877f8a436eSJoe Stringer ct_state |= OVS_CS_F_ESTABLISHED; 887f8a436eSJoe Stringer break; 897f8a436eSJoe Stringer case IP_CT_RELATED: 907f8a436eSJoe Stringer case IP_CT_RELATED_REPLY: 917f8a436eSJoe Stringer ct_state |= OVS_CS_F_RELATED; 927f8a436eSJoe Stringer break; 937f8a436eSJoe Stringer case IP_CT_NEW: 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, 144d110986cSJoe Stringer const struct ovs_conntrack_info *info, 1457f8a436eSJoe Stringer struct sw_flow_key *key, bool post_ct) 1467f8a436eSJoe Stringer { 1477f8a436eSJoe Stringer const struct nf_conntrack_zone *zone = &nf_ct_zone_dflt; 1487f8a436eSJoe Stringer enum ip_conntrack_info ctinfo; 1497f8a436eSJoe Stringer struct nf_conn *ct; 1507f8a436eSJoe Stringer u8 state = 0; 1517f8a436eSJoe Stringer 1527f8a436eSJoe Stringer ct = nf_ct_get(skb, &ctinfo); 1537f8a436eSJoe Stringer if (ct) { 1547f8a436eSJoe Stringer state = ovs_ct_get_state(ctinfo); 1559f13ded8SJarno Rajahalme /* All unconfirmed entries are NEW connections. */ 1564f0909eeSJoe Stringer if (!nf_ct_is_confirmed(ct)) 1574f0909eeSJoe Stringer state |= OVS_CS_F_NEW; 1589f13ded8SJarno Rajahalme /* OVS persists the related flag for the duration of the 1599f13ded8SJarno Rajahalme * connection. 1609f13ded8SJarno Rajahalme */ 1617f8a436eSJoe Stringer if (ct->master) 1627f8a436eSJoe Stringer state |= OVS_CS_F_RELATED; 1637f8a436eSJoe Stringer zone = nf_ct_zone(ct); 1647f8a436eSJoe Stringer } else if (post_ct) { 1657f8a436eSJoe Stringer state = OVS_CS_F_TRACKED | OVS_CS_F_INVALID; 166d110986cSJoe Stringer if (info) 167d110986cSJoe Stringer zone = &info->zone; 1687f8a436eSJoe Stringer } 169182e3042SJoe Stringer __ovs_ct_update_key(key, state, zone, ct); 1707f8a436eSJoe Stringer } 1717f8a436eSJoe Stringer 1729f13ded8SJarno Rajahalme /* This is called to initialize CT key fields possibly coming in from the local 1739f13ded8SJarno Rajahalme * stack. 1749f13ded8SJarno Rajahalme */ 1757f8a436eSJoe Stringer void ovs_ct_fill_key(const struct sk_buff *skb, struct sw_flow_key *key) 1767f8a436eSJoe Stringer { 177d110986cSJoe Stringer ovs_ct_update_key(skb, NULL, key, false); 1787f8a436eSJoe Stringer } 1797f8a436eSJoe Stringer 1807f8a436eSJoe Stringer int ovs_ct_put_key(const struct sw_flow_key *key, struct sk_buff *skb) 1817f8a436eSJoe Stringer { 182fbccce59SJoe Stringer if (nla_put_u32(skb, OVS_KEY_ATTR_CT_STATE, key->ct.state)) 1837f8a436eSJoe Stringer return -EMSGSIZE; 1847f8a436eSJoe Stringer 1857f8a436eSJoe Stringer if (IS_ENABLED(CONFIG_NF_CONNTRACK_ZONES) && 1867f8a436eSJoe Stringer nla_put_u16(skb, OVS_KEY_ATTR_CT_ZONE, key->ct.zone)) 1877f8a436eSJoe Stringer return -EMSGSIZE; 1887f8a436eSJoe Stringer 189182e3042SJoe Stringer if (IS_ENABLED(CONFIG_NF_CONNTRACK_MARK) && 190182e3042SJoe Stringer nla_put_u32(skb, OVS_KEY_ATTR_CT_MARK, key->ct.mark)) 191182e3042SJoe Stringer return -EMSGSIZE; 192182e3042SJoe Stringer 1939723e6abSValentin Rothberg if (IS_ENABLED(CONFIG_NF_CONNTRACK_LABELS) && 19433db4125SJoe Stringer nla_put(skb, OVS_KEY_ATTR_CT_LABELS, sizeof(key->ct.labels), 19533db4125SJoe Stringer &key->ct.labels)) 196c2ac6673SJoe Stringer return -EMSGSIZE; 197c2ac6673SJoe Stringer 198182e3042SJoe Stringer return 0; 199182e3042SJoe Stringer } 200182e3042SJoe Stringer 201182e3042SJoe Stringer static int ovs_ct_set_mark(struct sk_buff *skb, struct sw_flow_key *key, 202182e3042SJoe Stringer u32 ct_mark, u32 mask) 203182e3042SJoe Stringer { 2040d5cdef8SJoe Stringer #if IS_ENABLED(CONFIG_NF_CONNTRACK_MARK) 205182e3042SJoe Stringer enum ip_conntrack_info ctinfo; 206182e3042SJoe Stringer struct nf_conn *ct; 207182e3042SJoe Stringer u32 new_mark; 208182e3042SJoe Stringer 209182e3042SJoe Stringer /* The connection could be invalid, in which case set_mark is no-op. */ 210182e3042SJoe Stringer ct = nf_ct_get(skb, &ctinfo); 211182e3042SJoe Stringer if (!ct) 212182e3042SJoe Stringer return 0; 213182e3042SJoe Stringer 214182e3042SJoe Stringer new_mark = ct_mark | (ct->mark & ~(mask)); 215182e3042SJoe Stringer if (ct->mark != new_mark) { 216182e3042SJoe Stringer ct->mark = new_mark; 217182e3042SJoe Stringer nf_conntrack_event_cache(IPCT_MARK, ct); 218182e3042SJoe Stringer key->ct.mark = new_mark; 219182e3042SJoe Stringer } 220182e3042SJoe Stringer 2217f8a436eSJoe Stringer return 0; 2220d5cdef8SJoe Stringer #else 2230d5cdef8SJoe Stringer return -ENOTSUPP; 2240d5cdef8SJoe Stringer #endif 2257f8a436eSJoe Stringer } 2267f8a436eSJoe Stringer 22733db4125SJoe Stringer static int ovs_ct_set_labels(struct sk_buff *skb, struct sw_flow_key *key, 22833db4125SJoe Stringer const struct ovs_key_ct_labels *labels, 22933db4125SJoe Stringer const struct ovs_key_ct_labels *mask) 230c2ac6673SJoe Stringer { 231c2ac6673SJoe Stringer enum ip_conntrack_info ctinfo; 232c2ac6673SJoe Stringer struct nf_conn_labels *cl; 233c2ac6673SJoe Stringer struct nf_conn *ct; 234c2ac6673SJoe Stringer int err; 235c2ac6673SJoe Stringer 236c2ac6673SJoe Stringer /* The connection could be invalid, in which case set_label is no-op.*/ 237c2ac6673SJoe Stringer ct = nf_ct_get(skb, &ctinfo); 238c2ac6673SJoe Stringer if (!ct) 239c2ac6673SJoe Stringer return 0; 240c2ac6673SJoe Stringer 241c2ac6673SJoe Stringer cl = nf_ct_labels_find(ct); 242c2ac6673SJoe Stringer if (!cl) { 243c2ac6673SJoe Stringer nf_ct_labels_ext_add(ct); 244c2ac6673SJoe Stringer cl = nf_ct_labels_find(ct); 245c2ac6673SJoe Stringer } 24633db4125SJoe Stringer if (!cl || cl->words * sizeof(long) < OVS_CT_LABELS_LEN) 247c2ac6673SJoe Stringer return -ENOSPC; 248c2ac6673SJoe Stringer 24933db4125SJoe Stringer err = nf_connlabels_replace(ct, (u32 *)labels, (u32 *)mask, 25033db4125SJoe Stringer OVS_CT_LABELS_LEN / sizeof(u32)); 251c2ac6673SJoe Stringer if (err) 252c2ac6673SJoe Stringer return err; 253c2ac6673SJoe Stringer 25433db4125SJoe Stringer ovs_ct_get_labels(ct, &key->ct.labels); 255c2ac6673SJoe Stringer return 0; 256c2ac6673SJoe Stringer } 257c2ac6673SJoe Stringer 258cae3a262SJoe Stringer /* 'skb' should already be pulled to nh_ofs. */ 259cae3a262SJoe Stringer static int ovs_ct_helper(struct sk_buff *skb, u16 proto) 260cae3a262SJoe Stringer { 261cae3a262SJoe Stringer const struct nf_conntrack_helper *helper; 262cae3a262SJoe Stringer const struct nf_conn_help *help; 263cae3a262SJoe Stringer enum ip_conntrack_info ctinfo; 264cae3a262SJoe Stringer unsigned int protoff; 265cae3a262SJoe Stringer struct nf_conn *ct; 266cae3a262SJoe Stringer 267cae3a262SJoe Stringer ct = nf_ct_get(skb, &ctinfo); 268cae3a262SJoe Stringer if (!ct || ctinfo == IP_CT_RELATED_REPLY) 269cae3a262SJoe Stringer return NF_ACCEPT; 270cae3a262SJoe Stringer 271cae3a262SJoe Stringer help = nfct_help(ct); 272cae3a262SJoe Stringer if (!help) 273cae3a262SJoe Stringer return NF_ACCEPT; 274cae3a262SJoe Stringer 275cae3a262SJoe Stringer helper = rcu_dereference(help->helper); 276cae3a262SJoe Stringer if (!helper) 277cae3a262SJoe Stringer return NF_ACCEPT; 278cae3a262SJoe Stringer 279cae3a262SJoe Stringer switch (proto) { 280cae3a262SJoe Stringer case NFPROTO_IPV4: 281cae3a262SJoe Stringer protoff = ip_hdrlen(skb); 282cae3a262SJoe Stringer break; 283cae3a262SJoe Stringer case NFPROTO_IPV6: { 284cae3a262SJoe Stringer u8 nexthdr = ipv6_hdr(skb)->nexthdr; 285cae3a262SJoe Stringer __be16 frag_off; 286cc570605SJoe Stringer int ofs; 287cae3a262SJoe Stringer 288cc570605SJoe Stringer ofs = ipv6_skip_exthdr(skb, sizeof(struct ipv6hdr), &nexthdr, 289cc570605SJoe Stringer &frag_off); 290cc570605SJoe Stringer if (ofs < 0 || (frag_off & htons(~0x7)) != 0) { 291cae3a262SJoe Stringer pr_debug("proto header not found\n"); 292cae3a262SJoe Stringer return NF_ACCEPT; 293cae3a262SJoe Stringer } 294cc570605SJoe Stringer protoff = ofs; 295cae3a262SJoe Stringer break; 296cae3a262SJoe Stringer } 297cae3a262SJoe Stringer default: 298cae3a262SJoe Stringer WARN_ONCE(1, "helper invoked on non-IP family!"); 299cae3a262SJoe Stringer return NF_DROP; 300cae3a262SJoe Stringer } 301cae3a262SJoe Stringer 302cae3a262SJoe Stringer return helper->help(skb, protoff, ct, ctinfo); 303cae3a262SJoe Stringer } 304cae3a262SJoe Stringer 30574c16618SJoe Stringer /* Returns 0 on success, -EINPROGRESS if 'skb' is stolen, or other nonzero 30674c16618SJoe Stringer * value if 'skb' is freed. 30774c16618SJoe Stringer */ 3087f8a436eSJoe Stringer static int handle_fragments(struct net *net, struct sw_flow_key *key, 3097f8a436eSJoe Stringer u16 zone, struct sk_buff *skb) 3107f8a436eSJoe Stringer { 3117f8a436eSJoe Stringer struct ovs_skb_cb ovs_cb = *OVS_CB(skb); 312daaa7d64SFlorian Westphal int err; 3137f8a436eSJoe Stringer 3147f8a436eSJoe Stringer if (key->eth.type == htons(ETH_P_IP)) { 3157f8a436eSJoe Stringer enum ip_defrag_users user = IP_DEFRAG_CONNTRACK_IN + zone; 3167f8a436eSJoe Stringer 3177f8a436eSJoe Stringer memset(IPCB(skb), 0, sizeof(struct inet_skb_parm)); 31819bcf9f2SEric W. Biederman err = ip_defrag(net, skb, user); 3197f8a436eSJoe Stringer if (err) 3207f8a436eSJoe Stringer return err; 3217f8a436eSJoe Stringer 3227f8a436eSJoe Stringer ovs_cb.mru = IPCB(skb)->frag_max_size; 3237f8a436eSJoe Stringer #if IS_ENABLED(CONFIG_NF_DEFRAG_IPV6) 32474c16618SJoe Stringer } else if (key->eth.type == htons(ETH_P_IPV6)) { 3257f8a436eSJoe Stringer enum ip6_defrag_users user = IP6_DEFRAG_CONNTRACK_IN + zone; 3267f8a436eSJoe Stringer 3277f8a436eSJoe Stringer memset(IP6CB(skb), 0, sizeof(struct inet6_skb_parm)); 328daaa7d64SFlorian Westphal err = nf_ct_frag6_gather(net, skb, user); 329daaa7d64SFlorian Westphal if (err) 330daaa7d64SFlorian Westphal return err; 3317f8a436eSJoe Stringer 332daaa7d64SFlorian Westphal key->ip.proto = ipv6_hdr(skb)->nexthdr; 3337f8a436eSJoe Stringer ovs_cb.mru = IP6CB(skb)->frag_max_size; 3347f8a436eSJoe Stringer #endif 3357f8a436eSJoe Stringer } else { 33674c16618SJoe Stringer kfree_skb(skb); 3377f8a436eSJoe Stringer return -EPFNOSUPPORT; 3387f8a436eSJoe Stringer } 3397f8a436eSJoe Stringer 3407f8a436eSJoe Stringer key->ip.frag = OVS_FRAG_TYPE_NONE; 3417f8a436eSJoe Stringer skb_clear_hash(skb); 3427f8a436eSJoe Stringer skb->ignore_df = 1; 3437f8a436eSJoe Stringer *OVS_CB(skb) = ovs_cb; 3447f8a436eSJoe Stringer 3457f8a436eSJoe Stringer return 0; 3467f8a436eSJoe Stringer } 3477f8a436eSJoe Stringer 3487f8a436eSJoe Stringer static struct nf_conntrack_expect * 3497f8a436eSJoe Stringer ovs_ct_expect_find(struct net *net, const struct nf_conntrack_zone *zone, 3507f8a436eSJoe Stringer u16 proto, const struct sk_buff *skb) 3517f8a436eSJoe Stringer { 3527f8a436eSJoe Stringer struct nf_conntrack_tuple tuple; 3537f8a436eSJoe Stringer 354a31f1adcSEric W. Biederman if (!nf_ct_get_tuplepr(skb, skb_network_offset(skb), proto, net, &tuple)) 3557f8a436eSJoe Stringer return NULL; 3567f8a436eSJoe Stringer return __nf_ct_expect_find(net, zone, &tuple); 3577f8a436eSJoe Stringer } 3587f8a436eSJoe Stringer 3597f8a436eSJoe Stringer /* Determine whether skb->nfct is equal to the result of conntrack lookup. */ 3607f8a436eSJoe Stringer static bool skb_nfct_cached(const struct net *net, const struct sk_buff *skb, 3617f8a436eSJoe Stringer const struct ovs_conntrack_info *info) 3627f8a436eSJoe Stringer { 3637f8a436eSJoe Stringer enum ip_conntrack_info ctinfo; 3647f8a436eSJoe Stringer struct nf_conn *ct; 3657f8a436eSJoe Stringer 3667f8a436eSJoe Stringer ct = nf_ct_get(skb, &ctinfo); 3677f8a436eSJoe Stringer if (!ct) 3687f8a436eSJoe Stringer return false; 3697f8a436eSJoe Stringer if (!net_eq(net, read_pnet(&ct->ct_net))) 3707f8a436eSJoe Stringer return false; 3717f8a436eSJoe Stringer if (!nf_ct_zone_equal_any(info->ct, nf_ct_zone(ct))) 3727f8a436eSJoe Stringer return false; 373cae3a262SJoe Stringer if (info->helper) { 374cae3a262SJoe Stringer struct nf_conn_help *help; 375cae3a262SJoe Stringer 376cae3a262SJoe Stringer help = nf_ct_ext_find(ct, NF_CT_EXT_HELPER); 377cae3a262SJoe Stringer if (help && rcu_access_pointer(help->helper) != info->helper) 378cae3a262SJoe Stringer return false; 379cae3a262SJoe Stringer } 3807f8a436eSJoe Stringer 3817f8a436eSJoe Stringer return true; 3827f8a436eSJoe Stringer } 3837f8a436eSJoe Stringer 3849f13ded8SJarno Rajahalme /* Pass 'skb' through conntrack in 'net', using zone configured in 'info', if 3859f13ded8SJarno Rajahalme * not done already. Update key with new CT state. 3869f13ded8SJarno Rajahalme * Note that if the packet is deemed invalid by conntrack, skb->nfct will be 3879f13ded8SJarno Rajahalme * set to NULL and 0 will be returned. 3889f13ded8SJarno Rajahalme */ 3894f0909eeSJoe Stringer static int __ovs_ct_lookup(struct net *net, struct sw_flow_key *key, 3907f8a436eSJoe Stringer const struct ovs_conntrack_info *info, 3917f8a436eSJoe Stringer struct sk_buff *skb) 3927f8a436eSJoe Stringer { 3937f8a436eSJoe Stringer /* If we are recirculating packets to match on conntrack fields and 3947f8a436eSJoe Stringer * committing with a separate conntrack action, then we don't need to 3957f8a436eSJoe Stringer * actually run the packet through conntrack twice unless it's for a 3967f8a436eSJoe Stringer * different zone. 3977f8a436eSJoe Stringer */ 3987f8a436eSJoe Stringer if (!skb_nfct_cached(net, skb, info)) { 3997f8a436eSJoe Stringer struct nf_conn *tmpl = info->ct; 4007f8a436eSJoe Stringer 4017f8a436eSJoe Stringer /* Associate skb with specified zone. */ 4027f8a436eSJoe Stringer if (tmpl) { 4037f8a436eSJoe Stringer if (skb->nfct) 4047f8a436eSJoe Stringer nf_conntrack_put(skb->nfct); 4057f8a436eSJoe Stringer nf_conntrack_get(&tmpl->ct_general); 4067f8a436eSJoe Stringer skb->nfct = &tmpl->ct_general; 4077f8a436eSJoe Stringer skb->nfctinfo = IP_CT_NEW; 4087f8a436eSJoe Stringer } 4097f8a436eSJoe Stringer 4107f8a436eSJoe Stringer if (nf_conntrack_in(net, info->family, NF_INET_PRE_ROUTING, 4117f8a436eSJoe Stringer skb) != NF_ACCEPT) 4127f8a436eSJoe Stringer return -ENOENT; 413cae3a262SJoe Stringer 414cae3a262SJoe Stringer if (ovs_ct_helper(skb, info->family) != NF_ACCEPT) { 415cae3a262SJoe Stringer WARN_ONCE(1, "helper rejected packet"); 416cae3a262SJoe Stringer return -EINVAL; 417cae3a262SJoe Stringer } 4187f8a436eSJoe Stringer } 4197f8a436eSJoe Stringer 420d110986cSJoe Stringer ovs_ct_update_key(skb, info, key, true); 4214f0909eeSJoe Stringer 4227f8a436eSJoe Stringer return 0; 4237f8a436eSJoe Stringer } 4247f8a436eSJoe Stringer 4257f8a436eSJoe Stringer /* Lookup connection and read fields into key. */ 4267f8a436eSJoe Stringer static int ovs_ct_lookup(struct net *net, struct sw_flow_key *key, 4277f8a436eSJoe Stringer const struct ovs_conntrack_info *info, 4287f8a436eSJoe Stringer struct sk_buff *skb) 4297f8a436eSJoe Stringer { 4307f8a436eSJoe Stringer struct nf_conntrack_expect *exp; 4317f8a436eSJoe Stringer 4329f13ded8SJarno Rajahalme /* If we pass an expected packet through nf_conntrack_in() the 4339f13ded8SJarno Rajahalme * expectation is typically removed, but the packet could still be 4349f13ded8SJarno Rajahalme * lost in upcall processing. To prevent this from happening we 4359f13ded8SJarno Rajahalme * perform an explicit expectation lookup. Expected connections are 4369f13ded8SJarno Rajahalme * always new, and will be passed through conntrack only when they are 4379f13ded8SJarno Rajahalme * committed, as it is OK to remove the expectation at that time. 4389f13ded8SJarno Rajahalme */ 4397f8a436eSJoe Stringer exp = ovs_ct_expect_find(net, &info->zone, info->family, skb); 4407f8a436eSJoe Stringer if (exp) { 4417f8a436eSJoe Stringer u8 state; 4427f8a436eSJoe Stringer 4437f8a436eSJoe Stringer state = OVS_CS_F_TRACKED | OVS_CS_F_NEW | OVS_CS_F_RELATED; 444182e3042SJoe Stringer __ovs_ct_update_key(key, state, &info->zone, exp->master); 4457f8a436eSJoe Stringer } else { 4467f8a436eSJoe Stringer int err; 4477f8a436eSJoe Stringer 4487f8a436eSJoe Stringer err = __ovs_ct_lookup(net, key, info, skb); 4497f8a436eSJoe Stringer if (err) 4507f8a436eSJoe Stringer return err; 4517f8a436eSJoe Stringer } 4527f8a436eSJoe Stringer 4537f8a436eSJoe Stringer return 0; 4547f8a436eSJoe Stringer } 4557f8a436eSJoe Stringer 4567f8a436eSJoe Stringer /* Lookup connection and confirm if unconfirmed. */ 4577f8a436eSJoe Stringer static int ovs_ct_commit(struct net *net, struct sw_flow_key *key, 4587f8a436eSJoe Stringer const struct ovs_conntrack_info *info, 4597f8a436eSJoe Stringer struct sk_buff *skb) 4607f8a436eSJoe Stringer { 4617f8a436eSJoe Stringer u8 state; 4627f8a436eSJoe Stringer int err; 4637f8a436eSJoe Stringer 4647f8a436eSJoe Stringer state = key->ct.state; 4657f8a436eSJoe Stringer if (key->ct.zone == info->zone.id && 4667f8a436eSJoe Stringer ((state & OVS_CS_F_TRACKED) && !(state & OVS_CS_F_NEW))) { 4677f8a436eSJoe Stringer /* Previous lookup has shown that this connection is already 4687f8a436eSJoe Stringer * tracked and committed. Skip committing. 4697f8a436eSJoe Stringer */ 4707f8a436eSJoe Stringer return 0; 4717f8a436eSJoe Stringer } 4727f8a436eSJoe Stringer 4737f8a436eSJoe Stringer err = __ovs_ct_lookup(net, key, info, skb); 4747f8a436eSJoe Stringer if (err) 4757f8a436eSJoe Stringer return err; 4769f13ded8SJarno Rajahalme /* This is a no-op if the connection has already been confirmed. */ 4777f8a436eSJoe Stringer if (nf_conntrack_confirm(skb) != NF_ACCEPT) 4787f8a436eSJoe Stringer return -EINVAL; 4797f8a436eSJoe Stringer 4807f8a436eSJoe Stringer return 0; 4817f8a436eSJoe Stringer } 4827f8a436eSJoe Stringer 48333db4125SJoe Stringer static bool labels_nonzero(const struct ovs_key_ct_labels *labels) 484c2ac6673SJoe Stringer { 485c2ac6673SJoe Stringer size_t i; 486c2ac6673SJoe Stringer 48733db4125SJoe Stringer for (i = 0; i < sizeof(*labels); i++) 48833db4125SJoe Stringer if (labels->ct_labels[i]) 489c2ac6673SJoe Stringer return true; 490c2ac6673SJoe Stringer 491c2ac6673SJoe Stringer return false; 492c2ac6673SJoe Stringer } 493c2ac6673SJoe Stringer 49474c16618SJoe Stringer /* Returns 0 on success, -EINPROGRESS if 'skb' is stolen, or other nonzero 49574c16618SJoe Stringer * value if 'skb' is freed. 49674c16618SJoe Stringer */ 4977f8a436eSJoe Stringer int ovs_ct_execute(struct net *net, struct sk_buff *skb, 4987f8a436eSJoe Stringer struct sw_flow_key *key, 4997f8a436eSJoe Stringer const struct ovs_conntrack_info *info) 5007f8a436eSJoe Stringer { 5017f8a436eSJoe Stringer int nh_ofs; 5027f8a436eSJoe Stringer int err; 5037f8a436eSJoe Stringer 5047f8a436eSJoe Stringer /* The conntrack module expects to be working at L3. */ 5057f8a436eSJoe Stringer nh_ofs = skb_network_offset(skb); 5067f8a436eSJoe Stringer skb_pull(skb, nh_ofs); 5077f8a436eSJoe Stringer 5087f8a436eSJoe Stringer if (key->ip.frag != OVS_FRAG_TYPE_NONE) { 5097f8a436eSJoe Stringer err = handle_fragments(net, key, info->zone.id, skb); 5107f8a436eSJoe Stringer if (err) 5117f8a436eSJoe Stringer return err; 5127f8a436eSJoe Stringer } 5137f8a436eSJoe Stringer 514ab38a7b5SJoe Stringer if (info->commit) 5157f8a436eSJoe Stringer err = ovs_ct_commit(net, key, info, skb); 5167f8a436eSJoe Stringer else 5177f8a436eSJoe Stringer err = ovs_ct_lookup(net, key, info, skb); 518182e3042SJoe Stringer if (err) 519182e3042SJoe Stringer goto err; 5207f8a436eSJoe Stringer 521c2ac6673SJoe Stringer if (info->mark.mask) { 522182e3042SJoe Stringer err = ovs_ct_set_mark(skb, key, info->mark.value, 523182e3042SJoe Stringer info->mark.mask); 524c2ac6673SJoe Stringer if (err) 525c2ac6673SJoe Stringer goto err; 526c2ac6673SJoe Stringer } 52733db4125SJoe Stringer if (labels_nonzero(&info->labels.mask)) 52833db4125SJoe Stringer err = ovs_ct_set_labels(skb, key, &info->labels.value, 52933db4125SJoe Stringer &info->labels.mask); 530182e3042SJoe Stringer err: 5317f8a436eSJoe Stringer skb_push(skb, nh_ofs); 53274c16618SJoe Stringer if (err) 53374c16618SJoe Stringer kfree_skb(skb); 5347f8a436eSJoe Stringer return err; 5357f8a436eSJoe Stringer } 5367f8a436eSJoe Stringer 537cae3a262SJoe Stringer static int ovs_ct_add_helper(struct ovs_conntrack_info *info, const char *name, 538cae3a262SJoe Stringer const struct sw_flow_key *key, bool log) 539cae3a262SJoe Stringer { 540cae3a262SJoe Stringer struct nf_conntrack_helper *helper; 541cae3a262SJoe Stringer struct nf_conn_help *help; 542cae3a262SJoe Stringer 543cae3a262SJoe Stringer helper = nf_conntrack_helper_try_module_get(name, info->family, 544cae3a262SJoe Stringer key->ip.proto); 545cae3a262SJoe Stringer if (!helper) { 546cae3a262SJoe Stringer OVS_NLERR(log, "Unknown helper \"%s\"", name); 547cae3a262SJoe Stringer return -EINVAL; 548cae3a262SJoe Stringer } 549cae3a262SJoe Stringer 550cae3a262SJoe Stringer help = nf_ct_helper_ext_add(info->ct, helper, GFP_KERNEL); 551cae3a262SJoe Stringer if (!help) { 552cae3a262SJoe Stringer module_put(helper->me); 553cae3a262SJoe Stringer return -ENOMEM; 554cae3a262SJoe Stringer } 555cae3a262SJoe Stringer 556cae3a262SJoe Stringer rcu_assign_pointer(help->helper, helper); 557cae3a262SJoe Stringer info->helper = helper; 558cae3a262SJoe Stringer return 0; 559cae3a262SJoe Stringer } 560cae3a262SJoe Stringer 5617f8a436eSJoe Stringer static const struct ovs_ct_len_tbl ovs_ct_attr_lens[OVS_CT_ATTR_MAX + 1] = { 562ab38a7b5SJoe Stringer [OVS_CT_ATTR_COMMIT] = { .minlen = 0, .maxlen = 0 }, 5637f8a436eSJoe Stringer [OVS_CT_ATTR_ZONE] = { .minlen = sizeof(u16), 5647f8a436eSJoe Stringer .maxlen = sizeof(u16) }, 565182e3042SJoe Stringer [OVS_CT_ATTR_MARK] = { .minlen = sizeof(struct md_mark), 566182e3042SJoe Stringer .maxlen = sizeof(struct md_mark) }, 56733db4125SJoe Stringer [OVS_CT_ATTR_LABELS] = { .minlen = sizeof(struct md_labels), 56833db4125SJoe Stringer .maxlen = sizeof(struct md_labels) }, 569cae3a262SJoe Stringer [OVS_CT_ATTR_HELPER] = { .minlen = 1, 570cae3a262SJoe Stringer .maxlen = NF_CT_HELPER_NAME_LEN } 5717f8a436eSJoe Stringer }; 5727f8a436eSJoe Stringer 5737f8a436eSJoe Stringer static int parse_ct(const struct nlattr *attr, struct ovs_conntrack_info *info, 574cae3a262SJoe Stringer const char **helper, bool log) 5757f8a436eSJoe Stringer { 5767f8a436eSJoe Stringer struct nlattr *a; 5777f8a436eSJoe Stringer int rem; 5787f8a436eSJoe Stringer 5797f8a436eSJoe Stringer nla_for_each_nested(a, attr, rem) { 5807f8a436eSJoe Stringer int type = nla_type(a); 5817f8a436eSJoe Stringer int maxlen = ovs_ct_attr_lens[type].maxlen; 5827f8a436eSJoe Stringer int minlen = ovs_ct_attr_lens[type].minlen; 5837f8a436eSJoe Stringer 5847f8a436eSJoe Stringer if (type > OVS_CT_ATTR_MAX) { 5857f8a436eSJoe Stringer OVS_NLERR(log, 5867f8a436eSJoe Stringer "Unknown conntrack attr (type=%d, max=%d)", 5877f8a436eSJoe Stringer type, OVS_CT_ATTR_MAX); 5887f8a436eSJoe Stringer return -EINVAL; 5897f8a436eSJoe Stringer } 5907f8a436eSJoe Stringer if (nla_len(a) < minlen || nla_len(a) > maxlen) { 5917f8a436eSJoe Stringer OVS_NLERR(log, 5927f8a436eSJoe Stringer "Conntrack attr type has unexpected length (type=%d, length=%d, expected=%d)", 5937f8a436eSJoe Stringer type, nla_len(a), maxlen); 5947f8a436eSJoe Stringer return -EINVAL; 5957f8a436eSJoe Stringer } 5967f8a436eSJoe Stringer 5977f8a436eSJoe Stringer switch (type) { 598ab38a7b5SJoe Stringer case OVS_CT_ATTR_COMMIT: 599ab38a7b5SJoe Stringer info->commit = true; 6007f8a436eSJoe Stringer break; 6017f8a436eSJoe Stringer #ifdef CONFIG_NF_CONNTRACK_ZONES 6027f8a436eSJoe Stringer case OVS_CT_ATTR_ZONE: 6037f8a436eSJoe Stringer info->zone.id = nla_get_u16(a); 6047f8a436eSJoe Stringer break; 6057f8a436eSJoe Stringer #endif 606182e3042SJoe Stringer #ifdef CONFIG_NF_CONNTRACK_MARK 607182e3042SJoe Stringer case OVS_CT_ATTR_MARK: { 608182e3042SJoe Stringer struct md_mark *mark = nla_data(a); 609182e3042SJoe Stringer 610e754ec69SJoe Stringer if (!mark->mask) { 611e754ec69SJoe Stringer OVS_NLERR(log, "ct_mark mask cannot be 0"); 612e754ec69SJoe Stringer return -EINVAL; 613e754ec69SJoe Stringer } 614182e3042SJoe Stringer info->mark = *mark; 615182e3042SJoe Stringer break; 616182e3042SJoe Stringer } 617182e3042SJoe Stringer #endif 618c2ac6673SJoe Stringer #ifdef CONFIG_NF_CONNTRACK_LABELS 61933db4125SJoe Stringer case OVS_CT_ATTR_LABELS: { 62033db4125SJoe Stringer struct md_labels *labels = nla_data(a); 621c2ac6673SJoe Stringer 622e754ec69SJoe Stringer if (!labels_nonzero(&labels->mask)) { 623e754ec69SJoe Stringer OVS_NLERR(log, "ct_labels mask cannot be 0"); 624e754ec69SJoe Stringer return -EINVAL; 625e754ec69SJoe Stringer } 62633db4125SJoe Stringer info->labels = *labels; 627c2ac6673SJoe Stringer break; 628c2ac6673SJoe Stringer } 629c2ac6673SJoe Stringer #endif 630cae3a262SJoe Stringer case OVS_CT_ATTR_HELPER: 631cae3a262SJoe Stringer *helper = nla_data(a); 632cae3a262SJoe Stringer if (!memchr(*helper, '\0', nla_len(a))) { 633cae3a262SJoe Stringer OVS_NLERR(log, "Invalid conntrack helper"); 634cae3a262SJoe Stringer return -EINVAL; 635cae3a262SJoe Stringer } 636cae3a262SJoe Stringer break; 6377f8a436eSJoe Stringer default: 6387f8a436eSJoe Stringer OVS_NLERR(log, "Unknown conntrack attr (%d)", 6397f8a436eSJoe Stringer type); 6407f8a436eSJoe Stringer return -EINVAL; 6417f8a436eSJoe Stringer } 6427f8a436eSJoe Stringer } 6437f8a436eSJoe Stringer 6447f8a436eSJoe Stringer if (rem > 0) { 6457f8a436eSJoe Stringer OVS_NLERR(log, "Conntrack attr has %d unknown bytes", rem); 6467f8a436eSJoe Stringer return -EINVAL; 6477f8a436eSJoe Stringer } 6487f8a436eSJoe Stringer 6497f8a436eSJoe Stringer return 0; 6507f8a436eSJoe Stringer } 6517f8a436eSJoe Stringer 652c2ac6673SJoe Stringer bool ovs_ct_verify(struct net *net, enum ovs_key_attr attr) 6537f8a436eSJoe Stringer { 6547f8a436eSJoe Stringer if (attr == OVS_KEY_ATTR_CT_STATE) 6557f8a436eSJoe Stringer return true; 6567f8a436eSJoe Stringer if (IS_ENABLED(CONFIG_NF_CONNTRACK_ZONES) && 6577f8a436eSJoe Stringer attr == OVS_KEY_ATTR_CT_ZONE) 6587f8a436eSJoe Stringer return true; 659182e3042SJoe Stringer if (IS_ENABLED(CONFIG_NF_CONNTRACK_MARK) && 660182e3042SJoe Stringer attr == OVS_KEY_ATTR_CT_MARK) 661182e3042SJoe Stringer return true; 662c2ac6673SJoe Stringer if (IS_ENABLED(CONFIG_NF_CONNTRACK_LABELS) && 66333db4125SJoe Stringer attr == OVS_KEY_ATTR_CT_LABELS) { 664c2ac6673SJoe Stringer struct ovs_net *ovs_net = net_generic(net, ovs_net_id); 665c2ac6673SJoe Stringer 666c2ac6673SJoe Stringer return ovs_net->xt_label; 667c2ac6673SJoe Stringer } 6687f8a436eSJoe Stringer 6697f8a436eSJoe Stringer return false; 6707f8a436eSJoe Stringer } 6717f8a436eSJoe Stringer 6727f8a436eSJoe Stringer int ovs_ct_copy_action(struct net *net, const struct nlattr *attr, 6737f8a436eSJoe Stringer const struct sw_flow_key *key, 6747f8a436eSJoe Stringer struct sw_flow_actions **sfa, bool log) 6757f8a436eSJoe Stringer { 6767f8a436eSJoe Stringer struct ovs_conntrack_info ct_info; 677cae3a262SJoe Stringer const char *helper = NULL; 6787f8a436eSJoe Stringer u16 family; 6797f8a436eSJoe Stringer int err; 6807f8a436eSJoe Stringer 6817f8a436eSJoe Stringer family = key_to_nfproto(key); 6827f8a436eSJoe Stringer if (family == NFPROTO_UNSPEC) { 6837f8a436eSJoe Stringer OVS_NLERR(log, "ct family unspecified"); 6847f8a436eSJoe Stringer return -EINVAL; 6857f8a436eSJoe Stringer } 6867f8a436eSJoe Stringer 6877f8a436eSJoe Stringer memset(&ct_info, 0, sizeof(ct_info)); 6887f8a436eSJoe Stringer ct_info.family = family; 6897f8a436eSJoe Stringer 6907f8a436eSJoe Stringer nf_ct_zone_init(&ct_info.zone, NF_CT_DEFAULT_ZONE_ID, 6917f8a436eSJoe Stringer NF_CT_DEFAULT_ZONE_DIR, 0); 6927f8a436eSJoe Stringer 693cae3a262SJoe Stringer err = parse_ct(attr, &ct_info, &helper, log); 6947f8a436eSJoe Stringer if (err) 6957f8a436eSJoe Stringer return err; 6967f8a436eSJoe Stringer 6977f8a436eSJoe Stringer /* Set up template for tracking connections in specific zones. */ 6987f8a436eSJoe Stringer ct_info.ct = nf_ct_tmpl_alloc(net, &ct_info.zone, GFP_KERNEL); 6997f8a436eSJoe Stringer if (!ct_info.ct) { 7007f8a436eSJoe Stringer OVS_NLERR(log, "Failed to allocate conntrack template"); 7017f8a436eSJoe Stringer return -ENOMEM; 7027f8a436eSJoe Stringer } 70390c7afc9SJoe Stringer 70490c7afc9SJoe Stringer __set_bit(IPS_CONFIRMED_BIT, &ct_info.ct->status); 70590c7afc9SJoe Stringer nf_conntrack_get(&ct_info.ct->ct_general); 70690c7afc9SJoe Stringer 707cae3a262SJoe Stringer if (helper) { 708cae3a262SJoe Stringer err = ovs_ct_add_helper(&ct_info, helper, key, log); 709cae3a262SJoe Stringer if (err) 710cae3a262SJoe Stringer goto err_free_ct; 711cae3a262SJoe Stringer } 7127f8a436eSJoe Stringer 7137f8a436eSJoe Stringer err = ovs_nla_add_action(sfa, OVS_ACTION_ATTR_CT, &ct_info, 7147f8a436eSJoe Stringer sizeof(ct_info), log); 7157f8a436eSJoe Stringer if (err) 7167f8a436eSJoe Stringer goto err_free_ct; 7177f8a436eSJoe Stringer 7187f8a436eSJoe Stringer return 0; 7197f8a436eSJoe Stringer err_free_ct: 7202f3ab9f9SJoe Stringer __ovs_ct_free_action(&ct_info); 7217f8a436eSJoe Stringer return err; 7227f8a436eSJoe Stringer } 7237f8a436eSJoe Stringer 7247f8a436eSJoe Stringer int ovs_ct_action_to_attr(const struct ovs_conntrack_info *ct_info, 7257f8a436eSJoe Stringer struct sk_buff *skb) 7267f8a436eSJoe Stringer { 7277f8a436eSJoe Stringer struct nlattr *start; 7287f8a436eSJoe Stringer 7297f8a436eSJoe Stringer start = nla_nest_start(skb, OVS_ACTION_ATTR_CT); 7307f8a436eSJoe Stringer if (!start) 7317f8a436eSJoe Stringer return -EMSGSIZE; 7327f8a436eSJoe Stringer 733ab38a7b5SJoe Stringer if (ct_info->commit && nla_put_flag(skb, OVS_CT_ATTR_COMMIT)) 7347f8a436eSJoe Stringer return -EMSGSIZE; 7357f8a436eSJoe Stringer if (IS_ENABLED(CONFIG_NF_CONNTRACK_ZONES) && 7367f8a436eSJoe Stringer nla_put_u16(skb, OVS_CT_ATTR_ZONE, ct_info->zone.id)) 7377f8a436eSJoe Stringer return -EMSGSIZE; 738e754ec69SJoe Stringer if (IS_ENABLED(CONFIG_NF_CONNTRACK_MARK) && ct_info->mark.mask && 739182e3042SJoe Stringer nla_put(skb, OVS_CT_ATTR_MARK, sizeof(ct_info->mark), 740182e3042SJoe Stringer &ct_info->mark)) 741182e3042SJoe Stringer return -EMSGSIZE; 742c2ac6673SJoe Stringer if (IS_ENABLED(CONFIG_NF_CONNTRACK_LABELS) && 743e754ec69SJoe Stringer labels_nonzero(&ct_info->labels.mask) && 74433db4125SJoe Stringer nla_put(skb, OVS_CT_ATTR_LABELS, sizeof(ct_info->labels), 74533db4125SJoe Stringer &ct_info->labels)) 746c2ac6673SJoe Stringer return -EMSGSIZE; 747cae3a262SJoe Stringer if (ct_info->helper) { 748cae3a262SJoe Stringer if (nla_put_string(skb, OVS_CT_ATTR_HELPER, 749cae3a262SJoe Stringer ct_info->helper->name)) 750cae3a262SJoe Stringer return -EMSGSIZE; 751cae3a262SJoe Stringer } 7527f8a436eSJoe Stringer 7537f8a436eSJoe Stringer nla_nest_end(skb, start); 7547f8a436eSJoe Stringer 7557f8a436eSJoe Stringer return 0; 7567f8a436eSJoe Stringer } 7577f8a436eSJoe Stringer 7587f8a436eSJoe Stringer void ovs_ct_free_action(const struct nlattr *a) 7597f8a436eSJoe Stringer { 7607f8a436eSJoe Stringer struct ovs_conntrack_info *ct_info = nla_data(a); 7617f8a436eSJoe Stringer 7622f3ab9f9SJoe Stringer __ovs_ct_free_action(ct_info); 7632f3ab9f9SJoe Stringer } 7642f3ab9f9SJoe Stringer 7652f3ab9f9SJoe Stringer static void __ovs_ct_free_action(struct ovs_conntrack_info *ct_info) 7662f3ab9f9SJoe Stringer { 767cae3a262SJoe Stringer if (ct_info->helper) 768cae3a262SJoe Stringer module_put(ct_info->helper->me); 7697f8a436eSJoe Stringer if (ct_info->ct) 7707f8a436eSJoe Stringer nf_ct_put(ct_info->ct); 7717f8a436eSJoe Stringer } 772c2ac6673SJoe Stringer 773c2ac6673SJoe Stringer void ovs_ct_init(struct net *net) 774c2ac6673SJoe Stringer { 77533db4125SJoe Stringer unsigned int n_bits = sizeof(struct ovs_key_ct_labels) * BITS_PER_BYTE; 776c2ac6673SJoe Stringer struct ovs_net *ovs_net = net_generic(net, ovs_net_id); 777c2ac6673SJoe Stringer 778c2ac6673SJoe Stringer if (nf_connlabels_get(net, n_bits)) { 779c2ac6673SJoe Stringer ovs_net->xt_label = false; 780c2ac6673SJoe Stringer OVS_NLERR(true, "Failed to set connlabel length"); 781c2ac6673SJoe Stringer } else { 782c2ac6673SJoe Stringer ovs_net->xt_label = true; 783c2ac6673SJoe Stringer } 784c2ac6673SJoe Stringer } 785c2ac6673SJoe Stringer 786c2ac6673SJoe Stringer void ovs_ct_exit(struct net *net) 787c2ac6673SJoe Stringer { 788c2ac6673SJoe Stringer struct ovs_net *ovs_net = net_generic(net, ovs_net_id); 789c2ac6673SJoe Stringer 790c2ac6673SJoe Stringer if (ovs_net->xt_label) 791c2ac6673SJoe Stringer nf_connlabels_put(net); 792c2ac6673SJoe Stringer } 793