X-Git-Url: http://git.onelab.eu/?a=blobdiff_plain;f=ofproto%2Ftunnel.c;h=c23e2d7695db21ee5f3470dad49dc6d36f2343c9;hb=b0fb94a346e52f36aeef238dd5f9bef9a10c14ef;hp=d50cff8052c6ee28411f0bf3bf294629956115c0;hpb=74a99109015a68a469539d7fa04b8f6c13a437bf;p=sliver-openvswitch.git diff --git a/ofproto/tunnel.c b/ofproto/tunnel.c index d50cff805..c23e2d769 100644 --- a/ofproto/tunnel.c +++ b/ofproto/tunnel.c @@ -17,12 +17,11 @@ #include -#include "ofproto/ofproto-provider.h" #include "byte-order.h" #include "dynamic-string.h" #include "hash.h" #include "hmap.h" -#include "netdev-vport.h" +#include "netdev.h" #include "odp-util.h" #include "packets.h" #include "smap.h" @@ -30,72 +29,79 @@ #include "tunnel.h" #include "vlog.h" -/* XXX: - * - * Ability to generate actions on input for ECN - * Ability to generate metadata for packet-outs - * IPsec using skb mark. - * VXLAN. - * Multicast group management (possibly). - * Disallow netdevs with names like "gre64_system" to prevent collisions. */ - VLOG_DEFINE_THIS_MODULE(tunnel); struct tnl_match { ovs_be64 in_key; ovs_be32 ip_src; ovs_be32 ip_dst; - uint32_t odp_port; - bool in_key_present; + odp_port_t odp_port; + uint32_t skb_mark; bool in_key_flow; + bool ip_src_flow; + bool ip_dst_flow; }; struct tnl_port { + struct hmap_node ofport_node; struct hmap_node match_node; - const struct ofport *ofport; + const struct ofport_dpif *ofport; unsigned int netdev_seq; + struct netdev *netdev; + struct tnl_match match; }; -static struct hmap tnl_match_map = HMAP_INITIALIZER(&tnl_match_map); +static struct ovs_rwlock rwlock = OVS_RWLOCK_INITIALIZER; -/* Returned to callers when their ofport will never be used to receive or send - * tunnel traffic. Alternatively, we could ask the caller to delete their - * ofport, but this would be unclean in the reconfguration case. For the first - * time, an ofproto provider would have to call ofproto_port_del() on itself.*/ -static struct tnl_port void_tnl_port; +static struct hmap tnl_match_map__ = HMAP_INITIALIZER(&tnl_match_map__); +static struct hmap *tnl_match_map OVS_GUARDED_BY(rwlock) = &tnl_match_map__; + +static struct hmap ofport_map__ = HMAP_INITIALIZER(&ofport_map__); +static struct hmap *ofport_map OVS_GUARDED_BY(rwlock) = &ofport_map__; static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5); static struct vlog_rate_limit dbg_rl = VLOG_RATE_LIMIT_INIT(60, 60); -static struct tnl_port *tnl_find(struct tnl_match *); -static struct tnl_port *tnl_find_exact(struct tnl_match *); +static struct tnl_port *tnl_find(struct tnl_match *) OVS_REQ_RDLOCK(rwlock); +static struct tnl_port *tnl_find_exact(struct tnl_match *) + OVS_REQ_RDLOCK(rwlock); +static struct tnl_port *tnl_find_ofport(const struct ofport_dpif *) + OVS_REQ_RDLOCK(rwlock); + static uint32_t tnl_hash(struct tnl_match *); static void tnl_match_fmt(const struct tnl_match *, struct ds *); -static char *tnl_port_fmt(const struct tnl_port *); -static void tnl_port_mod_log(const struct tnl_port *, const char *action); -static const char *tnl_port_get_name(const struct tnl_port *); - -static struct tnl_port * -tnl_port_add__(const struct ofport *ofport, uint32_t odp_port, - bool warn) +static char *tnl_port_fmt(const struct tnl_port *) OVS_REQ_RDLOCK(rwlock); +static void tnl_port_mod_log(const struct tnl_port *, const char *action) + OVS_REQ_RDLOCK(rwlock); +static const char *tnl_port_get_name(const struct tnl_port *) + OVS_REQ_RDLOCK(rwlock); +static void tnl_port_del__(const struct ofport_dpif *) OVS_REQ_WRLOCK(rwlock); + +static bool +tnl_port_add__(const struct ofport_dpif *ofport, const struct netdev *netdev, + odp_port_t odp_port, bool warn) + OVS_REQ_WRLOCK(rwlock) { const struct netdev_tunnel_config *cfg; struct tnl_port *existing_port; struct tnl_port *tnl_port; - cfg = netdev_get_tunnel_config(ofport->netdev); + cfg = netdev_get_tunnel_config(netdev); ovs_assert(cfg); tnl_port = xzalloc(sizeof *tnl_port); tnl_port->ofport = ofport; - tnl_port->netdev_seq = netdev_change_seq(tnl_port->ofport->netdev); + tnl_port->netdev = netdev_ref(netdev); + tnl_port->netdev_seq = netdev_change_seq(tnl_port->netdev); tnl_port->match.in_key = cfg->in_key; tnl_port->match.ip_src = cfg->ip_src; tnl_port->match.ip_dst = cfg->ip_dst; - tnl_port->match.in_key_present = cfg->in_key_present; + tnl_port->match.ip_src_flow = cfg->ip_src_flow; + tnl_port->match.ip_dst_flow = cfg->ip_dst_flow; + tnl_port->match.skb_mark = cfg->ipsec ? IPSEC_MARK : 0; tnl_port->match.in_key_flow = cfg->in_key_flow; tnl_port->match.odp_port = odp_port; @@ -110,109 +116,121 @@ tnl_port_add__(const struct ofport *ofport, uint32_t odp_port, ds_destroy(&ds); free(tnl_port); } - return &void_tnl_port; + return false; } - hmap_insert(&tnl_match_map, &tnl_port->match_node, + hmap_insert(ofport_map, &tnl_port->ofport_node, hash_pointer(ofport, 0)); + hmap_insert(tnl_match_map, &tnl_port->match_node, tnl_hash(&tnl_port->match)); tnl_port_mod_log(tnl_port, "adding"); - return tnl_port; + return true; } /* Adds 'ofport' to the module with datapath port number 'odp_port'. 'ofport's * must be added before they can be used by the module. 'ofport' must be a * tunnel. */ -struct tnl_port * -tnl_port_add(const struct ofport *ofport, uint32_t odp_port) +void +tnl_port_add(const struct ofport_dpif *ofport, const struct netdev *netdev, + odp_port_t odp_port) OVS_EXCLUDED(rwlock) { - return tnl_port_add__(ofport, odp_port, true); + ovs_rwlock_wrlock(&rwlock); + tnl_port_add__(ofport, netdev, odp_port, true); + ovs_rwlock_unlock(&rwlock); } -/* Checks if the tnl_port pointed to by 'tnl_portp' needs reconfiguration due - * to changes in its netdev_tunnel_config. If it does, updates 'tnl_portp' to - * point to a new tnl_port and returns true. Otherwise, returns false. - * 'ofport' and 'odp_port' should be the same as would be passed to +/* Checks if the tunnel represented by 'ofport' reconfiguration due to changes + * in its netdev_tunnel_config. If it does, returns true. Otherwise, returns + * false. 'ofport' and 'odp_port' should be the same as would be passed to * tnl_port_add(). */ bool -tnl_port_reconfigure(const struct ofport *ofport, uint32_t odp_port, - struct tnl_port **tnl_portp) +tnl_port_reconfigure(const struct ofport_dpif *ofport, + const struct netdev *netdev, odp_port_t odp_port) + OVS_EXCLUDED(rwlock) { - struct tnl_port *tnl_port = *tnl_portp; + struct tnl_port *tnl_port; + bool changed = false; - if (tnl_port == &void_tnl_port) { - *tnl_portp = tnl_port_add__(ofport, odp_port, false); - return *tnl_portp != &void_tnl_port; - } else if (tnl_port->ofport != ofport + ovs_rwlock_wrlock(&rwlock); + tnl_port = tnl_find_ofport(ofport); + if (!tnl_port) { + changed = tnl_port_add__(ofport, netdev, odp_port, false); + } else if (tnl_port->netdev != netdev || tnl_port->match.odp_port != odp_port - || tnl_port->netdev_seq != netdev_change_seq(ofport->netdev)) { + || tnl_port->netdev_seq != netdev_change_seq(netdev)) { VLOG_DBG("reconfiguring %s", tnl_port_get_name(tnl_port)); - tnl_port_del(tnl_port); - *tnl_portp = tnl_port_add(ofport, odp_port); - return true; + tnl_port_del__(ofport); + tnl_port_add__(ofport, netdev, odp_port, true); + changed = true; } - return false; + ovs_rwlock_unlock(&rwlock); + return changed; } -/* Removes 'tnl_port' from the module. */ -void -tnl_port_del(struct tnl_port *tnl_port) +static void +tnl_port_del__(const struct ofport_dpif *ofport) OVS_REQ_WRLOCK(rwlock) { - if (tnl_port && tnl_port != &void_tnl_port) { + struct tnl_port *tnl_port; + + if (!ofport) { + return; + } + + tnl_port = tnl_find_ofport(ofport); + if (tnl_port) { tnl_port_mod_log(tnl_port, "removing"); - hmap_remove(&tnl_match_map, &tnl_port->match_node); + hmap_remove(tnl_match_map, &tnl_port->match_node); + hmap_remove(ofport_map, &tnl_port->ofport_node); + netdev_close(tnl_port->netdev); free(tnl_port); } } -/* Transforms 'flow' so that it appears to have been received by a tunnel - * OpenFlow port controlled by this module instead of the datapath port it - * actually came in on. Sets 'flow''s in_port to the appropriate OpenFlow port - * number. Returns the 'ofport' corresponding to the new in_port. +/* Removes 'ofport' from the module. */ +void +tnl_port_del(const struct ofport_dpif *ofport) OVS_EXCLUDED(rwlock) +{ + ovs_rwlock_wrlock(&rwlock); + tnl_port_del__(ofport); + ovs_rwlock_unlock(&rwlock); +} + +/* Looks in the table of tunnels for a tunnel matching the metadata in 'flow'. + * Returns the 'ofport' corresponding to the new in_port, or a null pointer if + * none is found. * * Callers should verify that 'flow' needs to be received by calling - * tnl_port_should_receive() before this function. - * - * Leaves 'flow' untouched and returns null if unsuccessful. */ -const struct ofport * -tnl_port_receive(struct flow *flow) + * tnl_port_should_receive() before this function. */ +const struct ofport_dpif * +tnl_port_receive(const struct flow *flow) OVS_EXCLUDED(rwlock) { char *pre_flow_str = NULL; + const struct ofport_dpif *ofport; struct tnl_port *tnl_port; struct tnl_match match; memset(&match, 0, sizeof match); - match.odp_port = flow->in_port; + match.odp_port = flow->in_port.odp_port; match.ip_src = flow->tunnel.ip_dst; match.ip_dst = flow->tunnel.ip_src; match.in_key = flow->tunnel.tun_id; - match.in_key_present = flow->tunnel.flags & FLOW_TNL_F_KEY; + match.skb_mark = flow->skb_mark; + ovs_rwlock_rdlock(&rwlock); tnl_port = tnl_find(&match); + ofport = tnl_port ? tnl_port->ofport : NULL; if (!tnl_port) { struct ds ds = DS_EMPTY_INITIALIZER; tnl_match_fmt(&match, &ds); VLOG_WARN_RL(&rl, "receive tunnel port not found (%s)", ds_cstr(&ds)); ds_destroy(&ds); - return NULL; - } - - if (is_ip_any(flow) - && ((flow->tunnel.ip_tos & IP_ECN_MASK) == IP_ECN_CE) - && (flow->nw_tos & IP_ECN_MASK) == IP_ECN_NOT_ECT) { - VLOG_WARN_RL(&rl, "dropping tunnel packet marked ECN CE but is not ECN" - " capable"); - return NULL; + goto out; } if (!VLOG_DROP_DBG(&dbg_rl)) { pre_flow_str = flow_to_string(flow); } - flow->in_port = tnl_port->ofport->ofp_port; - memset(&flow->tunnel, 0, sizeof flow->tunnel); - flow->tunnel.tun_id = match.in_key; - if (pre_flow_str) { char *post_flow_str = flow_to_string(flow); char *tnl_str = tnl_port_fmt(tnl_port); @@ -225,49 +243,70 @@ tnl_port_receive(struct flow *flow) free(pre_flow_str); free(post_flow_str); } - return tnl_port->ofport; + +out: + ovs_rwlock_unlock(&rwlock); + return ofport; } /* Given that 'flow' should be output to the ofport corresponding to * 'tnl_port', updates 'flow''s tunnel headers and returns the actual datapath - * port that the output should happen on. May return OVSP_NONE if the output + * port that the output should happen on. May return ODPP_NONE if the output * shouldn't occur. */ -uint32_t -tnl_port_send(const struct tnl_port *tnl_port, struct flow *flow) +odp_port_t +tnl_port_send(const struct ofport_dpif *ofport, struct flow *flow, + struct flow_wildcards *wc) OVS_EXCLUDED(rwlock) { const struct netdev_tunnel_config *cfg; + struct tnl_port *tnl_port; char *pre_flow_str = NULL; + odp_port_t out_port; - if (tnl_port == &void_tnl_port) { - return OVSP_NONE; + ovs_rwlock_rdlock(&rwlock); + tnl_port = tnl_find_ofport(ofport); + out_port = tnl_port ? tnl_port->match.odp_port : ODPP_NONE; + if (!tnl_port) { + goto out; } - cfg = netdev_get_tunnel_config(tnl_port->ofport->netdev); + cfg = netdev_get_tunnel_config(tnl_port->netdev); ovs_assert(cfg); if (!VLOG_DROP_DBG(&dbg_rl)) { pre_flow_str = flow_to_string(flow); } - flow->tunnel.ip_src = tnl_port->match.ip_src; - flow->tunnel.ip_dst = tnl_port->match.ip_dst; + if (!cfg->ip_src_flow) { + flow->tunnel.ip_src = tnl_port->match.ip_src; + } + if (!cfg->ip_dst_flow) { + flow->tunnel.ip_dst = tnl_port->match.ip_dst; + } + flow->skb_mark = tnl_port->match.skb_mark; if (!cfg->out_key_flow) { flow->tunnel.tun_id = cfg->out_key; } if (cfg->ttl_inherit && is_ip_any(flow)) { + wc->masks.nw_ttl = 0xff; flow->tunnel.ip_ttl = flow->nw_ttl; } else { flow->tunnel.ip_ttl = cfg->ttl; } if (cfg->tos_inherit && is_ip_any(flow)) { + wc->masks.nw_tos = 0xff; flow->tunnel.ip_tos = flow->nw_tos & IP_DSCP_MASK; } else { flow->tunnel.ip_tos = cfg->tos; } + /* ECN fields are always inherited. */ + if (is_ip_any(flow)) { + wc->masks.nw_tos |= IP_ECN_MASK; + } + if ((flow->nw_tos & IP_ECN_MASK) == IP_ECN_CE) { flow->tunnel.ip_tos |= IP_ECN_ECT_0; } else { @@ -291,7 +330,9 @@ tnl_port_send(const struct tnl_port *tnl_port, struct flow *flow) free(post_flow_str); } - return tnl_port->match.odp_port; +out: + ovs_rwlock_unlock(&rwlock); + return out_port; } static uint32_t @@ -302,12 +343,26 @@ tnl_hash(struct tnl_match *match) } static struct tnl_port * -tnl_find_exact(struct tnl_match *match) +tnl_find_ofport(const struct ofport_dpif *ofport) OVS_REQ_RDLOCK(rwlock) +{ + struct tnl_port *tnl_port; + + HMAP_FOR_EACH_IN_BUCKET (tnl_port, ofport_node, hash_pointer(ofport, 0), + ofport_map) { + if (tnl_port->ofport == ofport) { + return tnl_port; + } + } + return NULL; +} + +static struct tnl_port * +tnl_find_exact(struct tnl_match *match) OVS_REQ_RDLOCK(rwlock) { struct tnl_port *tnl_port; HMAP_FOR_EACH_WITH_HASH (tnl_port, match_node, tnl_hash(match), - &tnl_match_map) { + tnl_match_map) { if (!memcmp(match, &tnl_port->match, sizeof *match)) { return tnl_port; } @@ -316,18 +371,15 @@ tnl_find_exact(struct tnl_match *match) } static struct tnl_port * -tnl_find(struct tnl_match *match_) +tnl_find(struct tnl_match *match_) OVS_REQ_RDLOCK(rwlock) { struct tnl_match match = *match_; - bool is_multicast = ip_is_multicast(match.ip_src); struct tnl_port *tnl_port; /* remote_ip, local_ip, in_key */ - if (!is_multicast) { - tnl_port = tnl_find_exact(&match); - if (tnl_port) { - return tnl_port; - } + tnl_port = tnl_find_exact(&match); + if (tnl_port) { + return tnl_port; } /* remote_ip, in_key */ @@ -339,69 +391,65 @@ tnl_find(struct tnl_match *match_) match.ip_src = match_->ip_src; /* remote_ip, local_ip */ - if (!is_multicast) { - match.in_key = 0; - match.in_key_flow = true; - tnl_port = tnl_find_exact(&match); - if (tnl_port) { - return tnl_port; - } - match.in_key = match_->in_key; - match.in_key_flow = false; + match.in_key = 0; + match.in_key_flow = true; + tnl_port = tnl_find_exact(&match); + if (tnl_port) { + return tnl_port; } /* remote_ip */ match.ip_src = 0; - match.in_key = 0; - match.in_key_flow = true; tnl_port = tnl_find_exact(&match); if (tnl_port) { return tnl_port; } - match.ip_src = match_->ip_src; - match.in_key = match_->in_key; - match.in_key_flow = false; - if (is_multicast) { - match.ip_src = 0; - match.ip_dst = match_->ip_src; - - /* multicast remote_ip, in_key */ - tnl_port = tnl_find_exact(&match); - if (tnl_port) { - return tnl_port; - } + /* Flow-based remote */ + match.ip_dst = 0; + match.ip_dst_flow = true; + tnl_port = tnl_find_exact(&match); + if (tnl_port) { + return tnl_port; + } - /* multicast remote_ip */ - match.in_key = 0; - match.in_key_flow = true; - tnl_port = tnl_find_exact(&match); - if (tnl_port) { - return tnl_port; - } + /* Flow-based everything */ + match.ip_src = 0; + match.ip_src_flow = true; + tnl_port = tnl_find_exact(&match); + if (tnl_port) { + return tnl_port; } + return NULL; } static void tnl_match_fmt(const struct tnl_match *match, struct ds *ds) + OVS_REQ_RDLOCK(rwlock) { - ds_put_format(ds, IP_FMT"->"IP_FMT, IP_ARGS(match->ip_src), - IP_ARGS(match->ip_dst)); + if (!match->ip_dst_flow) { + ds_put_format(ds, IP_FMT"->"IP_FMT, IP_ARGS(match->ip_src), + IP_ARGS(match->ip_dst)); + } else if (!match->ip_src_flow) { + ds_put_format(ds, IP_FMT"->flow", IP_ARGS(match->ip_src)); + } else { + ds_put_cstr(ds, "flow->flow"); + } - if (match->in_key_present) { - if (match->in_key_flow) { - ds_put_cstr(ds, ", key=flow"); - } else { - ds_put_format(ds, ", key=%#"PRIx64, ntohll(match->in_key)); - } + if (match->in_key_flow) { + ds_put_cstr(ds, ", key=flow"); + } else { + ds_put_format(ds, ", key=%#"PRIx64, ntohll(match->in_key)); } ds_put_format(ds, ", dp port=%"PRIu32, match->odp_port); + ds_put_format(ds, ", skb mark=%"PRIu32, match->skb_mark); } static void tnl_port_mod_log(const struct tnl_port *tnl_port, const char *action) + OVS_REQ_RDLOCK(rwlock) { if (VLOG_IS_DBG_ENABLED()) { struct ds ds = DS_EMPTY_INITIALIZER; @@ -414,15 +462,15 @@ tnl_port_mod_log(const struct tnl_port *tnl_port, const char *action) } static char * -tnl_port_fmt(const struct tnl_port *tnl_port) +tnl_port_fmt(const struct tnl_port *tnl_port) OVS_REQ_RDLOCK(rwlock) { const struct netdev_tunnel_config *cfg = - netdev_get_tunnel_config(tnl_port->ofport->netdev); + netdev_get_tunnel_config(tnl_port->netdev); struct ds ds = DS_EMPTY_INITIALIZER; ds_put_format(&ds, "port %"PRIu32": %s (%s: ", tnl_port->match.odp_port, tnl_port_get_name(tnl_port), - netdev_get_type(tnl_port->ofport->netdev)); + netdev_get_type(tnl_port->netdev)); tnl_match_fmt(&tnl_port->match, &ds); if (cfg->out_key != cfg->in_key || @@ -464,7 +512,7 @@ tnl_port_fmt(const struct tnl_port *tnl_port) } static const char * -tnl_port_get_name(const struct tnl_port *tnl_port) +tnl_port_get_name(const struct tnl_port *tnl_port) OVS_REQ_RDLOCK(rwlock) { - return netdev_get_name(tnl_port->ofport->netdev); + return netdev_get_name(tnl_port->netdev); }