diff options
Diffstat (limited to 'lib/ct-dpif.c')
-rw-r--r-- | lib/ct-dpif.c | 298 |
1 files changed, 180 insertions, 118 deletions
diff --git a/lib/ct-dpif.c b/lib/ct-dpif.c index 6f17a26b5..d3b2783ce 100644 --- a/lib/ct-dpif.c +++ b/lib/ct-dpif.c @@ -20,6 +20,7 @@ #include <errno.h> #include "ct-dpif.h" +#include "openvswitch/ofp-ct.h" #include "openvswitch/ofp-parse.h" #include "openvswitch/vlog.h" @@ -101,24 +102,191 @@ ct_dpif_dump_done(struct ct_dpif_dump_state *dump) : EOPNOTSUPP); } +/* Flushing. */ + +static void +ct_dpif_tuple_from_ofp_ct_tuple(const struct ofp_ct_tuple *ofp_tuple, + struct ct_dpif_tuple *tuple, + uint16_t l3_type, uint8_t ip_proto) +{ + if (l3_type == AF_INET) { + tuple->src.ip = in6_addr_get_mapped_ipv4(&ofp_tuple->src); + tuple->dst.ip = in6_addr_get_mapped_ipv4(&ofp_tuple->dst); + } else { + tuple->src.in6 = ofp_tuple->src; + tuple->dst.in6 = ofp_tuple->dst; + } + + tuple->l3_type = l3_type; + tuple->ip_proto = ip_proto; + tuple->src_port = ofp_tuple->src_port; + + if (ip_proto == IPPROTO_ICMP || ip_proto == IPPROTO_ICMPV6) { + tuple->icmp_code = ofp_tuple->icmp_code; + tuple->icmp_type = ofp_tuple->icmp_type; + } else { + tuple->dst_port = ofp_tuple->dst_port; + } +} + +static inline bool +ct_dpif_inet_addr_cmp_partial(const union ct_dpif_inet_addr *addr, + const struct in6_addr *partial, uint16_t l3_type) +{ + if (ipv6_is_zero(partial)) { + return true; + } + + if (l3_type == AF_INET && in6_addr_get_mapped_ipv4(partial) != addr->ip) { + return false; + } + + if (l3_type == AF_INET6 && !ipv6_addr_equals(partial, &addr->in6)) { + return false; + } + + return true; +} + +static inline bool +ct_dpif_tuple_ip_cmp_partial(const struct ct_dpif_tuple *tuple, + const struct ofp_ct_tuple *partial, + uint16_t l3_type, uint8_t ip_proto) +{ + if (!ct_dpif_inet_addr_cmp_partial(&tuple->src, &partial->src, l3_type)) { + return false; + } + + if (!ct_dpif_inet_addr_cmp_partial(&tuple->dst, &partial->dst, l3_type)) { + return false; + } + + if (ip_proto == IPPROTO_ICMP || ip_proto == IPPROTO_ICMPV6) { + if (partial->icmp_id != tuple->icmp_id) { + return false; + } + + if (partial->icmp_type != tuple->icmp_type) { + return false; + } + + if (partial->icmp_code != tuple->icmp_code) { + return false; + } + } else { + if (partial->src_port && partial->src_port != tuple->src_port) { + return false; + } + + if (partial->dst_port && partial->dst_port != tuple->dst_port) { + return false; + } + } + + return true; +} + +/* Returns 'true' if all non-zero members of 'match' equal to corresponding + * members of 'entry'. */ +static bool +ct_dpif_entry_cmp(const struct ct_dpif_entry *entry, + const struct ofp_ct_match *match) +{ + if (match->l3_type && match->l3_type != entry->tuple_orig.l3_type) { + return false; + } + + if (match->ip_proto && match->ip_proto != entry->tuple_orig.ip_proto) { + return false; + } + + if (!ct_dpif_tuple_ip_cmp_partial(&entry->tuple_orig, &match->tuple_orig, + match->l3_type, match->ip_proto)) { + return false; + } + + if (!ct_dpif_tuple_ip_cmp_partial(&entry->tuple_reply, &match->tuple_reply, + match->l3_type, match->ip_proto)) { + return false; + } + + return true; +} + +static int +ct_dpif_flush_tuple(struct dpif *dpif, const uint16_t *zone, + const struct ofp_ct_match *match) +{ + struct ct_dpif_dump_state *dump; + struct ct_dpif_entry cte; + int error; + int tot_bkts; + + if (!dpif->dpif_class->ct_flush) { + return EOPNOTSUPP; + } + + if (VLOG_IS_DBG_ENABLED()) { + struct ds ds = DS_EMPTY_INITIALIZER; + ofp_ct_match_format(&ds, match); + VLOG_DBG("%s: ct_flush: zone=%d %s", dpif_name(dpif), zone ? *zone : 0, + ds_cstr(&ds)); + ds_destroy(&ds); + } + + /* If we have full five tuple in original and empty reply tuple just + * do the flush over original tuple directly. */ + if (ofp_ct_tuple_is_five_tuple(&match->tuple_orig, match->ip_proto) && + ofp_ct_tuple_is_zero(&match->tuple_reply, match->ip_proto)) { + struct ct_dpif_tuple tuple; + + ct_dpif_tuple_from_ofp_ct_tuple(&match->tuple_orig, &tuple, + match->l3_type, match->ip_proto); + return dpif->dpif_class->ct_flush(dpif, zone, &tuple); + } + + error = ct_dpif_dump_start(dpif, &dump, zone, &tot_bkts); + if (error) { + return error; + } + + while (!(error = ct_dpif_dump_next(dump, &cte))) { + if (zone && *zone != cte.zone) { + continue; + } + + if (ct_dpif_entry_cmp(&cte, match)) { + error = dpif->dpif_class->ct_flush(dpif, &cte.zone, + &cte.tuple_orig); + if (error) { + break; + } + } + } + if (error == EOF) { + error = 0; + } + + ct_dpif_dump_done(dump); + return error; +} + /* Flush the entries in the connection tracker used by 'dpif'. The * arguments have the following behavior: * - * - If both 'zone' and 'tuple' are NULL, flush all the conntrack entries. - * - If 'zone' is not NULL, and 'tuple' is NULL, flush all the conntrack + * - If both 'zone' is NULL and 'match' is NULL or zero, flush all the + * conntrack entries. + * - If 'zone' is not NULL, and 'match' is NULL, flush all the conntrack * entries in '*zone'. - * - If 'tuple' is not NULL, flush the conntrack entry specified by 'tuple' - * in '*zone'. If 'zone' is NULL, use the default zone (zone 0). */ + * - If 'match' is not NULL or zero, flush the conntrack entry specified + * by 'match' in '*zone'. If 'zone' is NULL, use the default zone + * (zone 0). */ int ct_dpif_flush(struct dpif *dpif, const uint16_t *zone, - const struct ct_dpif_tuple *tuple) + const struct ofp_ct_match *match) { - if (tuple) { - struct ds ds = DS_EMPTY_INITIALIZER; - ct_dpif_format_tuple(&ds, tuple); - VLOG_DBG("%s: ct_flush: %s in zone %d", dpif_name(dpif), ds_cstr(&ds), - zone ? *zone : 0); - ds_destroy(&ds); + if (match && !ofp_ct_match_is_zero(match)) { + return ct_dpif_flush_tuple(dpif, zone, match); } else if (zone) { VLOG_DBG("%s: ct_flush: zone %"PRIu16, dpif_name(dpif), *zone); } else { @@ -126,7 +294,7 @@ ct_dpif_flush(struct dpif *dpif, const uint16_t *zone, } return (dpif->dpif_class->ct_flush - ? dpif->dpif_class->ct_flush(dpif, zone, tuple) + ? dpif->dpif_class->ct_flush(dpif, zone, NULL) : EOPNOTSUPP); } @@ -583,112 +751,6 @@ ct_dpif_format_tcp_stat(struct ds * ds, int tcp_state, int conn_per_state) ds_put_format(ds, "=%u", conn_per_state); } -/* Parses a specification of a conntrack 5-tuple from 's' into 'tuple'. - * Returns true on success. Otherwise, returns false and puts the error - * message in 'ds'. */ -bool -ct_dpif_parse_tuple(struct ct_dpif_tuple *tuple, const char *s, struct ds *ds) -{ - char *pos, *key, *value, *copy; - memset(tuple, 0, sizeof *tuple); - - pos = copy = xstrdup(s); - while (ofputil_parse_key_value(&pos, &key, &value)) { - if (!*value) { - ds_put_format(ds, "field %s missing value", key); - goto error; - } - - if (!strcmp(key, "ct_nw_src") || !strcmp(key, "ct_nw_dst")) { - if (tuple->l3_type && tuple->l3_type != AF_INET) { - ds_put_cstr(ds, "L3 type set multiple times"); - goto error; - } else { - tuple->l3_type = AF_INET; - } - if (!ip_parse(value, key[6] == 's' ? &tuple->src.ip : - &tuple->dst.ip)) { - goto error_with_msg; - } - } else if (!strcmp(key, "ct_ipv6_src") || - !strcmp(key, "ct_ipv6_dst")) { - if (tuple->l3_type && tuple->l3_type != AF_INET6) { - ds_put_cstr(ds, "L3 type set multiple times"); - goto error; - } else { - tuple->l3_type = AF_INET6; - } - if (!ipv6_parse(value, key[8] == 's' ? &tuple->src.in6 : - &tuple->dst.in6)) { - goto error_with_msg; - } - } else if (!strcmp(key, "ct_nw_proto")) { - char *err = str_to_u8(value, key, &tuple->ip_proto); - if (err) { - free(err); - goto error_with_msg; - } - } else if (!strcmp(key, "ct_tp_src") || !strcmp(key,"ct_tp_dst")) { - uint16_t port; - char *err = str_to_u16(value, key, &port); - if (err) { - free(err); - goto error_with_msg; - } - if (key[6] == 's') { - tuple->src_port = htons(port); - } else { - tuple->dst_port = htons(port); - } - } else if (!strcmp(key, "icmp_type") || !strcmp(key, "icmp_code") || - !strcmp(key, "icmp_id") ) { - if (tuple->ip_proto != IPPROTO_ICMP && - tuple->ip_proto != IPPROTO_ICMPV6) { - ds_put_cstr(ds, "invalid L4 fields"); - goto error; - } - uint16_t icmp_id; - char *err; - if (key[5] == 't') { - err = str_to_u8(value, key, &tuple->icmp_type); - } else if (key[5] == 'c') { - err = str_to_u8(value, key, &tuple->icmp_code); - } else { - err = str_to_u16(value, key, &icmp_id); - tuple->icmp_id = htons(icmp_id); - } - if (err) { - free(err); - goto error_with_msg; - } - } else { - ds_put_format(ds, "invalid conntrack tuple field: %s", key); - goto error; - } - } - - if (ipv6_is_zero(&tuple->src.in6) || ipv6_is_zero(&tuple->dst.in6) || - !tuple->ip_proto) { - /* icmp_type, icmp_code, and icmp_id can be 0. */ - if (tuple->ip_proto != IPPROTO_ICMP && - tuple->ip_proto != IPPROTO_ICMPV6) { - if (!tuple->src_port || !tuple->dst_port) { - ds_put_cstr(ds, "at least one of the conntrack 5-tuple fields " - "is missing."); - goto error; - } - } - } - - free(copy); - return true; - -error_with_msg: - ds_put_format(ds, "failed to parse field %s", key); -error: - free(copy); - return false; -} void ct_dpif_push_zone_limit(struct ovs_list *zone_limits, uint16_t zone, |