2
0
mirror of https://github.com/openvswitch/ovs synced 2025-10-25 15:07:05 +00:00

Add connection tracking mark support.

This patch adds a new 32-bit metadata field to the connection tracking
interface. When a mark is specified as part of the ct action and the
connection is committed, the value is saved with the current connection.
Subsequent ct lookups with the table specified will expose this metadata
as the "ct_mark" field in the flow.

For example, to allow new TCP connections from port 1->2 and only allow
established connections from port 2->1, and to associate a mark with those
connections:

    table=0,priority=1,action=drop
    table=0,arp,action=normal
    table=0,in_port=1,tcp,action=ct(commit,exec(set_field:1->ct_mark)),2
    table=0,in_port=2,ct_state=-trk,tcp,action=ct(table=1)
    table=1,in_port=2,ct_state=+trk,ct_mark=1,tcp,action=1

Signed-off-by: Joe Stringer <joestringer@nicira.com>
Acked-by: Jarno Rajahalme <jrajahalme@nicira.com>
Acked-by: Ben Pfaff <blp@nicira.com>
This commit is contained in:
Joe Stringer
2015-09-18 13:58:00 -07:00
parent 07659514c3
commit 8e53fe8cf7
30 changed files with 550 additions and 43 deletions

View File

@@ -353,6 +353,10 @@ static enum ofperr ofpacts_pull_openflow_actions__(
struct ofpbuf *openflow, unsigned int actions_len,
enum ofp_version version, uint32_t allowed_ovsinsts,
struct ofpbuf *ofpacts, enum ofpact_type outer_action);
static char * OVS_WARN_UNUSED_RESULT ofpacts_parse_copy(
const char *s_, struct ofpbuf *ofpacts,
enum ofputil_protocol *usable_protocols,
bool allow_instructions, enum ofpact_type outer_action);
/* Pull off existing actions or instructions. Used by nesting actions to keep
* ofpacts_parse() oblivious of actions nesting.
@@ -4657,6 +4661,10 @@ format_DEBUG_RECIRC(const struct ofpact_null *a OVS_UNUSED, struct ds *s)
*
* It is strongly recommended that this table is later than the current
* table, to prevent loops.
*
* Zero or more actions may immediately follow this action. These actions will
* be executed within the context of the connection tracker, and they require
* the NX_CT_F_COMMIT flag to be set.
*/
struct nx_action_conntrack {
ovs_be16 type; /* OFPAT_VENDOR. */
@@ -4708,10 +4716,11 @@ decode_ct_zone(const struct nx_action_conntrack *nac,
static enum ofperr
decode_NXAST_RAW_CT(const struct nx_action_conntrack *nac,
enum ofp_version ofp_version OVS_UNUSED,
struct ofpbuf *out)
enum ofp_version ofp_version, struct ofpbuf *out)
{
const size_t ct_offset = ofpacts_pull(out);
struct ofpact_conntrack *conntrack;
struct ofpbuf openflow;
int error = 0;
conntrack = ofpact_put_CT(out);
@@ -4722,15 +4731,40 @@ decode_NXAST_RAW_CT(const struct nx_action_conntrack *nac,
}
conntrack->recirc_table = nac->recirc_table;
ofpbuf_pull(out, sizeof(*conntrack));
ofpbuf_use_const(&openflow, nac + 1, ntohs(nac->len) - sizeof(*nac));
error = ofpacts_pull_openflow_actions__(&openflow, openflow.size,
ofp_version,
1u << OVSINST_OFPIT11_APPLY_ACTIONS,
out, OFPACT_CT);
if (error) {
goto out;
}
conntrack = ofpbuf_push_uninit(out, sizeof(*conntrack));
out->header = &conntrack->ofpact;
ofpact_update_len(out, &conntrack->ofpact);
if (conntrack->ofpact.len > sizeof(*conntrack)
&& !(conntrack->flags & NX_CT_F_COMMIT)) {
VLOG_WARN_RL(&rl, "CT action requires commit flag if actions are "
"specified.");
error = OFPERR_OFPBAC_BAD_ARGUMENT;
}
out:
ofpbuf_push_uninit(out, ct_offset);
return error;
}
static void
encode_CT(const struct ofpact_conntrack *conntrack,
enum ofp_version ofp_version OVS_UNUSED, struct ofpbuf *out)
enum ofp_version ofp_version, struct ofpbuf *out)
{
struct nx_action_conntrack *nac;
const size_t ofs = out->size;
size_t len;
nac = put_NXAST_CT(out);
nac->flags = htons(conntrack->flags);
@@ -4743,6 +4777,13 @@ encode_CT(const struct ofpact_conntrack *conntrack,
nac->zone_imm = htons(conntrack->zone_imm);
}
nac->recirc_table = conntrack->recirc_table;
len = ofpacts_put_openflow_actions(conntrack->actions,
ofpact_ct_get_action_len(conntrack),
out, ofp_version);
len += sizeof(*nac);
nac = ofpbuf_at(out, ofs, sizeof(*nac));
nac->len = htons(len);
}
/* Parses 'arg' as the argument to a "ct" action, and appends such an
@@ -4752,8 +4793,9 @@ encode_CT(const struct ofpact_conntrack *conntrack,
* error. The caller is responsible for freeing the returned string. */
static char * OVS_WARN_UNUSED_RESULT
parse_CT(char *arg, struct ofpbuf *ofpacts,
enum ofputil_protocol *usable_protocols OVS_UNUSED)
enum ofputil_protocol *usable_protocols)
{
const size_t ct_offset = ofpacts_pull(ofpacts);
struct ofpact_conntrack *oc;
char *error = NULL;
char *key, *value;
@@ -4779,6 +4821,15 @@ parse_CT(char *arg, struct ofpbuf *ofpacts,
return error;
}
}
} else if (!strcmp(key, "exec")) {
/* Hide existing actions from ofpacts_parse_copy(), so the
* nesting can be handled transparently. */
ofpbuf_pull(ofpacts, sizeof(*oc));
error = ofpacts_parse_copy(value, ofpacts, usable_protocols, false,
OFPACT_CT);
ofpact_pad(ofpacts);
ofpacts->header = ofpbuf_push_uninit(ofpacts, sizeof(*oc));
oc = ofpacts->header;
} else {
error = xasprintf("invalid argument to \"ct\" action: `%s'", key);
}
@@ -4787,6 +4838,8 @@ parse_CT(char *arg, struct ofpbuf *ofpacts,
}
}
ofpact_update_len(ofpacts, &oc->ofpact);
ofpbuf_push_uninit(ofpacts, ct_offset);
return error;
}
@@ -4807,6 +4860,11 @@ format_CT(const struct ofpact_conntrack *a, struct ds *s)
} else if (a->zone_imm) {
ds_put_format(s, "zone=%"PRIu16",", a->zone_imm);
}
if (ofpact_ct_get_action_len(a)) {
ds_put_cstr(s, "exec(");
ofpacts_format(a->actions, ofpact_ct_get_action_len(a), s);
ds_put_format(s, "),");
}
ds_chomp(s, ',');
ds_put_char(s, ')');
}
@@ -6054,6 +6112,7 @@ ofpact_check__(enum ofputil_protocol *usable_protocols, struct ofpact *a,
case OFPACT_CT: {
struct ofpact_conntrack *oc = ofpact_get_CT(a);
enum ofputil_protocol p = *usable_protocols;
if (!dl_type_is_ip_any(flow->dl_type)
|| (flow->ct_state & CS_INVALID && oc->flags & NX_CT_F_COMMIT)) {
@@ -6063,7 +6122,9 @@ ofpact_check__(enum ofputil_protocol *usable_protocols, struct ofpact *a,
if (oc->zone_src.field) {
return mf_check_src(&oc->zone_src, flow);
}
return 0;
return ofpacts_check(oc->actions, ofpact_ct_get_action_len(oc),
flow, max_ports, table_id, n_tables, &p);
}
case OFPACT_CLEAR_ACTIONS:
@@ -6170,13 +6231,60 @@ ofpacts_check_consistency(struct ofpact ofpacts[], size_t ofpacts_len,
: 0);
}
static const struct mf_field *
ofpact_get_mf_field(enum ofpact_type type, const void *ofpact)
{
if (type == OFPACT_SET_FIELD) {
const struct ofpact_set_field *orl = ofpact;
return orl->field;
} else if (type == OFPACT_REG_MOVE) {
const struct ofpact_reg_move *orm = ofpact;
return orm->dst.field;
}
return NULL;
}
static enum ofperr
unsupported_nesting(enum ofpact_type action, enum ofpact_type outer_action)
{
VLOG_WARN("%s action doesn't support nested action %s",
ofpact_name(outer_action), ofpact_name(action));
return OFPERR_OFPBAC_BAD_ARGUMENT;
}
static bool
field_requires_ct(enum mf_field_id field)
{
return field == MFF_CT_MARK;
}
/* Apply nesting constraints for actions */
static enum ofperr
ofpacts_verify_nested(const struct ofpact *a, enum ofpact_type outer_action)
{
if (outer_action != OFPACT_WRITE_ACTIONS) {
VLOG_WARN("\"%s\" action doesn't support nested action \"%s\"",
ofpact_name(outer_action), ofpact_name(a->type));
return OFPERR_OFPBAC_BAD_ARGUMENT;
const struct mf_field *field = ofpact_get_mf_field(a->type, a);
if (field && field_requires_ct(field->id) && outer_action != OFPACT_CT) {
VLOG_WARN("cannot set CT fields outside of ct action");
return OFPERR_OFPBAC_BAD_SET_ARGUMENT;
}
if (outer_action) {
ovs_assert(outer_action == OFPACT_WRITE_ACTIONS
|| outer_action == OFPACT_CT);
if (outer_action == OFPACT_CT) {
if (!field) {
return unsupported_nesting(a->type, outer_action);
} else if (!field_requires_ct(field->id)) {
VLOG_WARN("%s action doesn't support nested modification "
"of %s", ofpact_name(outer_action), field->name);
return OFPERR_OFPBAC_BAD_ARGUMENT;
}
}
}
return 0;
@@ -6201,6 +6309,7 @@ ofpacts_verify(const struct ofpact ofpacts[], size_t ofpacts_len,
inst = OVSINST_OFPIT13_METER;
OFPACT_FOR_EACH (a, ofpacts, ofpacts_len) {
enum ovs_instruction_type next;
enum ofperr error;
if (a->type == OFPACT_CONJUNCTION) {
OFPACT_FOR_EACH (a, ofpacts, ofpacts_len) {
@@ -6215,12 +6324,9 @@ ofpacts_verify(const struct ofpact ofpacts[], size_t ofpacts_len,
return 0;
}
if (outer_action) {
enum ofperr error = ofpacts_verify_nested(a, outer_action);
if (error) {
return error;
}
error = ofpacts_verify_nested(a, outer_action);
if (error) {
return error;
}
next = ovs_instruction_type_from_ofpact_type(a->type);