History

openvswitch: Allow matching on conntrack mark

Allow matching and setting the ct_mark field. As with ct_state and ct_zone, these fields are populated when the CT action is executed. To write to this field, a value and mask can be specified as a nested attribute under the CT action. This data is stored with the conntrack entry, and is executed after the lookup occurs for the CT action. The conntrack entry itself must be committed using the COMMIT flag in the CT action flags for this change to persist.

Joe Stringer committed 11 years ago

Commit 182e3041 parent 7f8a4366 files · +83 −4Browse files

Unified · Split

6 files
  1. include/uapi/linux/openvswitch.h +5 −0
  2. net/openvswitch/actions.c +1 −0
  3. net/openvswitch/conntrack.c +64 −3
  4. net/openvswitch/conntrack.h +1 −0
  5. net/openvswitch/flow.h +1 −0
  6. net/openvswitch/flow_netlink.c +11 −1
include/uapi/linux/openvswitch.h+5−0
@@ -325,6 +325,7 @@ enum ovs_key_attr { · 324 unmodified lines
325325 * the accepted length of the array. */
326326 OVS_KEY_ATTR_CT_STATE, /* u8 bitmask of OVS_CS_F_* */
327327 OVS_KEY_ATTR_CT_ZONE, /* u16 connection tracking zone. */
328+ OVS_KEY_ATTR_CT_MARK, /* u32 connection tracking mark */
328329
329330 #ifdef __KERNEL__
330331 OVS_KEY_ATTR_TUNNEL_INFO, /* struct ip_tunnel_info */
@@ -613,11 +614,15 @@ struct ovs_action_hash { · 282 unmodified lines
613614 * enum ovs_ct_attr - Attributes for %OVS_ACTION_ATTR_CT action.
614615 * @OVS_CT_ATTR_FLAGS: u32 connection tracking flags.
615616 * @OVS_CT_ATTR_ZONE: u16 connection tracking zone.
617+ * @OVS_CT_ATTR_MARK: u32 value followed by u32 mask. For each bit set in the
618+ * mask, the corresponding bit in the value is copied to the connection
619+ * tracking mark field in the connection.
616620 */
617621 enum ovs_ct_attr {
618622 OVS_CT_ATTR_UNSPEC,
619623 OVS_CT_ATTR_FLAGS, /* u8 bitmask of OVS_CT_F_*. */
620624 OVS_CT_ATTR_ZONE, /* u16 zone id. */
625+ OVS_CT_ATTR_MARK, /* mark to associate with this connection. */
621626 __OVS_CT_ATTR_MAX
622627 };
623628
net/openvswitch/actions.c+1−0
@@ -968,6 +968,7 @@ static int execute_masked_set_action(struct sk_buff *skb, · 967 unmodified lines
968968
969969 case OVS_KEY_ATTR_CT_STATE:
970970 case OVS_KEY_ATTR_CT_ZONE:
971+ case OVS_KEY_ATTR_CT_MARK:
971972 err = -EINVAL;
972973 break;
973974 }
net/openvswitch/conntrack.c+64−3
@@ -28,12 +28,19 @@ struct ovs_ct_len_tbl { · 27 unmodified lines
2828 size_t minlen;
2929 };
3030
31+/* Metadata mark for masked write to conntrack mark */
32+struct md_mark {
33+ u32 value;
34+ u32 mask;
35+};
36+
3137 /* Conntrack action context for execution. */
3238 struct ovs_conntrack_info {
3339 struct nf_conntrack_zone zone;
3440 struct nf_conn *ct;
3541 u32 flags;
3642 u16 family;
43+ struct md_mark mark;
3744 };
3845
3946 static u16 key_to_nfproto(const struct sw_flow_key *key)
@@ -84,10 +91,12 @@ static u8 ovs_ct_get_state(enum ip_conntrack_info ctinfo) · 44 unmodified lines
8491 }
8592
8693 static void __ovs_ct_update_key(struct sw_flow_key *key, u8 state,
87− const struct nf_conntrack_zone *zone)
94+ const struct nf_conntrack_zone *zone,
95+ const struct nf_conn *ct)
8896 {
8997 key->ct.state = state;
9098 key->ct.zone = zone->id;
99+ key->ct.mark = ct ? ct->mark : 0;
91100 }
92101
93102 /* Update 'key' based on skb->nfct. If 'post_ct' is true, then OVS has
@@ -110,7 +119,7 @@ static void ovs_ct_update_key(const struct sk_buff *skb, · 16 unmodified lines
110119 } else if (post_ct) {
111120 state = OVS_CS_F_TRACKED | OVS_CS_F_INVALID;
112121 }
113− __ovs_ct_update_key(key, state, zone);
122+ __ovs_ct_update_key(key, state, zone, ct);
114123 }
115124
116125 void ovs_ct_fill_key(const struct sk_buff *skb, struct sw_flow_key *key)
@@ -127,6 +136,35 @@ int ovs_ct_put_key(const struct sw_flow_key *key, struct sk_buff *skb) · 10 unmodified lines
127136 nla_put_u16(skb, OVS_KEY_ATTR_CT_ZONE, key->ct.zone))
128137 return -EMSGSIZE;
129138
139+ if (IS_ENABLED(CONFIG_NF_CONNTRACK_MARK) &&
140+ nla_put_u32(skb, OVS_KEY_ATTR_CT_MARK, key->ct.mark))
141+ return -EMSGSIZE;
142+
143+ return 0;
144+}
145+
146+static int ovs_ct_set_mark(struct sk_buff *skb, struct sw_flow_key *key,
147+ u32 ct_mark, u32 mask)
148+{
149+ enum ip_conntrack_info ctinfo;
150+ struct nf_conn *ct;
151+ u32 new_mark;
152+
153+ if (!IS_ENABLED(CONFIG_NF_CONNTRACK_MARK))
154+ return -ENOTSUPP;
155+
156+ /* The connection could be invalid, in which case set_mark is no-op. */
157+ ct = nf_ct_get(skb, &ctinfo);
158+ if (!ct)
159+ return 0;
160+
161+ new_mark = ct_mark | (ct->mark & ~(mask));
162+ if (ct->mark != new_mark) {
163+ ct->mark = new_mark;
164+ nf_conntrack_event_cache(IPCT_MARK, ct);
165+ key->ct.mark = new_mark;
166+ }
167+
130168 return 0;
131169 }
132170
@@ -247,7 +285,7 @@ static int ovs_ct_lookup(struct net *net, struct sw_flow_key *key, · 114 unmodified lines
247285 u8 state;
248286
249287 state = OVS_CS_F_TRACKED | OVS_CS_F_NEW | OVS_CS_F_RELATED;
250− __ovs_ct_update_key(key, state, &info->zone);
288+ __ovs_ct_update_key(key, state, &info->zone, exp->master);
251289 } else {
252290 int err;
253291
@@ -310,7 +348,13 @@ int ovs_ct_execute(struct net *net, struct sk_buff *skb, · 56 unmodified lines
310348 err = ovs_ct_commit(net, key, info, skb);
311349 else
312350 err = ovs_ct_lookup(net, key, info, skb);
351+ if (err)
352+ goto err;
313353
354+ if (info->mark.mask)
355+ err = ovs_ct_set_mark(skb, key, info->mark.value,
356+ info->mark.mask);
357+err:
314358 skb_push(skb, nh_ofs);
315359 return err;
316360 }
@@ -320,6 +364,8 @@ static const struct ovs_ct_len_tbl ovs_ct_attr_lens[OVS_CT_ATTR_MAX + 1] = { · 3 unmodified lines
320364 .maxlen = sizeof(u32) },
321365 [OVS_CT_ATTR_ZONE] = { .minlen = sizeof(u16),
322366 .maxlen = sizeof(u16) },
367+ [OVS_CT_ATTR_MARK] = { .minlen = sizeof(struct md_mark),
368+ .maxlen = sizeof(struct md_mark) },
323369 };
324370
325371 static int parse_ct(const struct nlattr *attr, struct ovs_conntrack_info *info,
@@ -354,6 +400,14 @@ static int parse_ct(const struct nlattr *attr, struct ovs_conntrack_info *info, · 28 unmodified lines
354400 case OVS_CT_ATTR_ZONE:
355401 info->zone.id = nla_get_u16(a);
356402 break;
403+#endif
404+#ifdef CONFIG_NF_CONNTRACK_MARK
405+ case OVS_CT_ATTR_MARK: {
406+ struct md_mark *mark = nla_data(a);
407+
408+ info->mark = *mark;
409+ break;
410+ }
357411 #endif
358412 default:
359413 OVS_NLERR(log, "Unknown conntrack attr (%d)",
@@ -377,6 +431,9 @@ bool ovs_ct_verify(enum ovs_key_attr attr) · 17 unmodified lines
377431 if (IS_ENABLED(CONFIG_NF_CONNTRACK_ZONES) &&
378432 attr == OVS_KEY_ATTR_CT_ZONE)
379433 return true;
434+ if (IS_ENABLED(CONFIG_NF_CONNTRACK_MARK) &&
435+ attr == OVS_KEY_ATTR_CT_MARK)
436+ return true;
380437
381438 return false;
382439 }
@@ -439,6 +496,10 @@ int ovs_ct_action_to_attr(const struct ovs_conntrack_info *ct_info, · 56 unmodified lines
439496 if (IS_ENABLED(CONFIG_NF_CONNTRACK_ZONES) &&
440497 nla_put_u16(skb, OVS_CT_ATTR_ZONE, ct_info->zone.id))
441498 return -EMSGSIZE;
499+ if (IS_ENABLED(CONFIG_NF_CONNTRACK_MARK) &&
500+ nla_put(skb, OVS_CT_ATTR_MARK, sizeof(ct_info->mark),
501+ &ct_info->mark))
502+ return -EMSGSIZE;
442503
443504 nla_nest_end(skb, start);
444505
net/openvswitch/conntrack.h+1−0
@@ -65,6 +65,7 @@ static inline void ovs_ct_fill_key(const struct sk_buff *skb, · 64 unmodified lines
6565 {
6666 key->ct.state = 0;
6767 key->ct.zone = 0;
68+ key->ct.mark = 0;
6869 }
6970
7071 static inline int ovs_ct_put_key(const struct sw_flow_key *key,
net/openvswitch/flow.h+1−0
@@ -114,6 +114,7 @@ struct sw_flow_key { · 113 unmodified lines
114114 struct {
115115 /* Connection tracking fields. */
116116 u16 zone;
117+ u32 mark;
117118 u8 state;
118119 } ct;
119120
net/openvswitch/flow_netlink.c+11−1
@@ -281,7 +281,7 @@ size_t ovs_key_attr_size(void) · 280 unmodified lines
281281 /* Whenever adding new OVS_KEY_ FIELDS, we should consider
282282 * updating this function.
283283 */
284− BUILD_BUG_ON(OVS_KEY_ATTR_TUNNEL_INFO != 24);
284+ BUILD_BUG_ON(OVS_KEY_ATTR_TUNNEL_INFO != 25);
285285
286286 return nla_total_size(4) /* OVS_KEY_ATTR_PRIORITY */
287287 + nla_total_size(0) /* OVS_KEY_ATTR_TUNNEL */
@@ -292,6 +292,7 @@ size_t ovs_key_attr_size(void) · 4 unmodified lines
292292 + nla_total_size(4) /* OVS_KEY_ATTR_RECIRC_ID */
293293 + nla_total_size(1) /* OVS_KEY_ATTR_CT_STATE */
294294 + nla_total_size(2) /* OVS_KEY_ATTR_CT_ZONE */
295+ + nla_total_size(4) /* OVS_KEY_ATTR_CT_MARK */
295296 + nla_total_size(12) /* OVS_KEY_ATTR_ETHERNET */
296297 + nla_total_size(2) /* OVS_KEY_ATTR_ETHERTYPE */
297298 + nla_total_size(4) /* OVS_KEY_ATTR_VLAN */
@@ -343,6 +344,7 @@ static const struct ovs_len_tbl ovs_key_lens[OVS_KEY_ATTR_MAX + 1] = { · 45 unmodified lines
343344 [OVS_KEY_ATTR_MPLS] = { .len = sizeof(struct ovs_key_mpls) },
344345 [OVS_KEY_ATTR_CT_STATE] = { .len = sizeof(u8) },
345346 [OVS_KEY_ATTR_CT_ZONE] = { .len = sizeof(u16) },
347+ [OVS_KEY_ATTR_CT_MARK] = { .len = sizeof(u32) },
346348 };
347349
348350 static bool is_all_zero(const u8 *fp, size_t size)
@@ -787,6 +789,13 @@ static int metadata_from_nlattrs(struct sw_flow_match *match, u64 *attrs, · 438 unmodified lines
787789 SW_FLOW_KEY_PUT(match, ct.zone, ct_zone, is_mask);
788790 *attrs &= ~(1ULL << OVS_KEY_ATTR_CT_ZONE);
789791 }
792+ if (*attrs & (1 << OVS_KEY_ATTR_CT_MARK) &&
793+ ovs_ct_verify(OVS_KEY_ATTR_CT_MARK)) {
794+ u32 mark = nla_get_u32(a[OVS_KEY_ATTR_CT_MARK]);
795+
796+ SW_FLOW_KEY_PUT(match, ct.mark, mark, is_mask);
797+ *attrs &= ~(1ULL << OVS_KEY_ATTR_CT_MARK);
798+ }
790799 return 0;
791800 }
792801
@@ -1919,6 +1928,7 @@ static int validate_set(const struct nlattr *a, · 1126 unmodified lines
19191928
19201929 case OVS_KEY_ATTR_PRIORITY:
19211930 case OVS_KEY_ATTR_SKB_MARK:
1931+ case OVS_KEY_ATTR_CT_MARK:
19221932 case OVS_KEY_ATTR_ETHERNET:
19231933 break;
19241934