Tadaaki Nagao nagao@stratosphere.co.jp
Tetsuo NAKAGAWA nakagawa@mxc.nes.nec.co.jp
Thomas Goirand zigo@debian.org
+Thomas Graf tgraf@redhat.com
Thomas Lacroix thomas.lacroix@citrix.com
Todd Deshane deshantm@gmail.com
Tom Everman teverman@google.com
It should build against almost any kernel, certainly against 2.6.18
and later.
+Q: Should userspace or kernel be upgraded first to minimize downtime?
+
+ In general, the Open vSwitch userspace should be used with the
+ kernel version included in the same release or with the version
+ from upstream Linux. However, when upgrading between two releases
+ of Open vSwitch it is best to migrate userspace first to reduce
+ the possbility of incompatibilities.
+
Q: What features are not available in the Open vSwitch kernel datapath
that ships as part of the upstream Linux kernel?
A: The kernel module in upstream Linux 3.3 and later does not include
- the following features:
-
- - Tunnel virtual ports, that is, interfaces with type "gre",
- "ipsec_gre", "capwap". It is possible to create tunnels in
- Linux and attach them to Open vSwitch as system devices.
- However, they cannot be dynamically created through the OVSDB
- protocol or set the tunnel ids as a flow action.
-
- Work is in progress in adding these features to the upstream
- Linux version of the Open vSwitch kernel module. For now, if
- you need these features, use the kernel module from the Open
- vSwitch distribution instead of the upstream Linux kernel
- module.
-
- - Patch virtual ports, that is, interfaces with type "patch".
- You can use Linux "veth" devices as a substitute.
-
- We don't have any plans to add patch ports upstream.
+ tunnel virtual ports, that is, interfaces with type "gre",
+ "ipsec_gre", "gre64", "ipsec_gre64", "vxlan", or "capwap". It is
+ possible to create tunnels in Linux and attach them to Open vSwitch
+ as system devices. However, they cannot be dynamically created
+ through the OVSDB protocol or set the tunnel ids as a flow action.
+
+ Work is in progress in adding tunnel virtual ports to the upstream
+ Linux version of the Open vSwitch kernel module. For now, if you
+ need these features, use the kernel module from the Open vSwitch
+ distribution instead of the upstream Linux kernel module.
+
+ The upstream kernel module does not include patch ports, but this
+ only matters for Open vSwitch 1.9 and earlier, because Open vSwitch
+ 1.10 and later implement patch ports without using this kernel
+ feature.
Q: What features are not available when using the userspace datapath?
-A: Tunnel and patch virtual ports are not supported, as described in the
+A: Tunnel virtual ports are not supported, as described in the
previous answer. It is also not possible to use queue-related
actions. On Linux kernels before 2.6.39, maximum-sized VLAN packets
may not be transmitted.
retire that meaning of ANY in favor of the OpenFlow 1.1 meaning.
- Inheritance of the Don't Fragment bit in IP tunnels (df_inherit) is
no longer supported.
+ - Patch ports are implemented in userspace.
+ - Tunneling requires the version of the kernel module paired with Open
+ vSwitch 1.9.0 or later.
v1.9.0 - xx xxx xxxx
vport-gre.c \
vport-internal_dev.c \
vport-netdev.c \
- vport-patch.c \
vport-vxlan.c
openvswitch_headers = \
#ifdef HAVE_NLA_NUL_STRING
[OVS_VPORT_ATTR_NAME] = { .type = NLA_NUL_STRING, .len = IFNAMSIZ - 1 },
[OVS_VPORT_ATTR_STATS] = { .len = sizeof(struct ovs_vport_stats) },
- [OVS_VPORT_ATTR_ADDRESS] = { .len = ETH_ALEN },
#else
[OVS_VPORT_ATTR_STATS] = { .minlen = sizeof(struct ovs_vport_stats) },
- [OVS_VPORT_ATTR_ADDRESS] = { .minlen = ETH_ALEN },
#endif
[OVS_VPORT_ATTR_PORT_NO] = { .type = NLA_U32 },
[OVS_VPORT_ATTR_TYPE] = { .type = NLA_U32 },
&vport_stats))
goto nla_put_failure;
- if (nla_put(skb, OVS_VPORT_ATTR_ADDRESS, ETH_ALEN,
- vport->ops->get_addr(vport)))
- goto nla_put_failure;
-
err = ovs_vport_get_options(vport, skb);
if (err == -EMSGSIZE)
goto error;
return ERR_PTR(-EINVAL);
}
-/* Called with RTNL lock. */
-static int change_vport(struct vport *vport,
- struct nlattr *a[OVS_VPORT_ATTR_MAX + 1])
-{
- int err = 0;
-
- if (a[OVS_VPORT_ATTR_STATS])
- ovs_vport_set_stats(vport, nla_data(a[OVS_VPORT_ATTR_STATS]));
-
- if (a[OVS_VPORT_ATTR_ADDRESS])
- err = ovs_vport_set_addr(vport, nla_data(a[OVS_VPORT_ATTR_ADDRESS]));
-
- return err;
-}
-
static int ovs_vport_cmd_new(struct sk_buff *skb, struct genl_info *info)
{
struct nlattr **a = info->attrs;
if (IS_ERR(vport))
goto exit_unlock;
- err = change_vport(vport, a);
- if (!err) {
- reply = ovs_vport_cmd_build_info(vport, info->snd_portid,
- info->snd_seq,
- OVS_VPORT_CMD_NEW);
- if (IS_ERR(reply))
- err = PTR_ERR(reply);
- }
- if (err) {
+ if (a[OVS_VPORT_ATTR_STATS])
+ ovs_vport_set_stats(vport, nla_data(a[OVS_VPORT_ATTR_STATS]));
+
+ reply = ovs_vport_cmd_build_info(vport, info->snd_portid, info->snd_seq,
+ OVS_VPORT_CMD_NEW);
+ if (IS_ERR(reply)) {
+ err = PTR_ERR(reply);
ovs_dp_detach_port(vport);
goto exit_unlock;
}
if (!err && a[OVS_VPORT_ATTR_OPTIONS])
err = ovs_vport_set_options(vport, a[OVS_VPORT_ATTR_OPTIONS]);
- if (!err)
- err = change_vport(vport, a);
- else
+ if (err)
goto exit_unlock;
- if (!err && a[OVS_VPORT_ATTR_UPCALL_PID])
+
+ if (a[OVS_VPORT_ATTR_STATS])
+ ovs_vport_set_stats(vport, nla_data(a[OVS_VPORT_ATTR_STATS]));
+
+ if (a[OVS_VPORT_ATTR_UPCALL_PID])
vport->upcall_portid = nla_get_u32(a[OVS_VPORT_ATTR_UPCALL_PID]);
reply = ovs_vport_cmd_build_info(vport, info->snd_portid,
#define OVS_TNL_F_CSUM (1 << 1)
#define OVS_TNL_F_KEY (1 << 2)
+/* Used to memset ovs_key_ipv4_tunnel padding. */
+#define OVS_TUNNEL_KEY_SIZE \
+ (offsetof(struct ovs_key_ipv4_tunnel, ipv4_ttl) + \
+ FIELD_SIZEOF(struct ovs_key_ipv4_tunnel, ipv4_ttl))
+
struct ovs_key_ipv4_tunnel {
__be64 tun_id;
__be32 ipv4_src;
ovs_vport_receive(vport, skb);
}
-static bool check_ipv4_address(__be32 addr)
-{
- if (ipv4_is_multicast(addr) || ipv4_is_lbcast(addr)
- || ipv4_is_loopback(addr) || ipv4_is_zeronet(addr))
- return false;
-
- return true;
-}
-
-static bool ipv4_should_icmp(struct sk_buff *skb)
-{
- struct iphdr *old_iph = ip_hdr(skb);
-
- /* Don't respond to L2 broadcast. */
- if (is_multicast_ether_addr(eth_hdr(skb)->h_dest))
- return false;
-
- /* Don't respond to L3 broadcast or invalid addresses. */
- if (!check_ipv4_address(old_iph->daddr) ||
- !check_ipv4_address(old_iph->saddr))
- return false;
-
- /* Only respond to the first fragment. */
- if (old_iph->frag_off & htons(IP_OFFSET))
- return false;
-
- /* Don't respond to ICMP error messages. */
- if (old_iph->protocol == IPPROTO_ICMP) {
- u8 icmp_type, *icmp_typep;
-
- icmp_typep = skb_header_pointer(skb, (u8 *)old_iph +
- (old_iph->ihl << 2) +
- offsetof(struct icmphdr, type) -
- skb->data, sizeof(icmp_type),
- &icmp_type);
-
- if (!icmp_typep)
- return false;
-
- if (*icmp_typep > NR_ICMP_TYPES
- || (*icmp_typep <= ICMP_PARAMETERPROB
- && *icmp_typep != ICMP_ECHOREPLY
- && *icmp_typep != ICMP_ECHO))
- return false;
- }
-
- return true;
-}
-
-static void ipv4_build_icmp(struct sk_buff *skb, struct sk_buff *nskb,
- unsigned int mtu, unsigned int payload_length)
-{
- struct iphdr *iph, *old_iph = ip_hdr(skb);
- struct icmphdr *icmph;
- u8 *payload;
-
- iph = (struct iphdr *)skb_put(nskb, sizeof(struct iphdr));
- icmph = (struct icmphdr *)skb_put(nskb, sizeof(struct icmphdr));
- payload = skb_put(nskb, payload_length);
-
- /* IP */
- iph->version = 4;
- iph->ihl = sizeof(struct iphdr) >> 2;
- iph->tos = (old_iph->tos & IPTOS_TOS_MASK) |
- IPTOS_PREC_INTERNETCONTROL;
- iph->tot_len = htons(sizeof(struct iphdr)
- + sizeof(struct icmphdr)
- + payload_length);
- get_random_bytes(&iph->id, sizeof(iph->id));
- iph->frag_off = 0;
- iph->ttl = IPDEFTTL;
- iph->protocol = IPPROTO_ICMP;
- iph->daddr = old_iph->saddr;
- iph->saddr = old_iph->daddr;
-
- ip_send_check(iph);
-
- /* ICMP */
- icmph->type = ICMP_DEST_UNREACH;
- icmph->code = ICMP_FRAG_NEEDED;
- icmph->un.gateway = htonl(mtu);
- icmph->checksum = 0;
-
- nskb->csum = csum_partial((u8 *)icmph, sizeof(struct icmphdr), 0);
- nskb->csum = skb_copy_and_csum_bits(skb, (u8 *)old_iph - skb->data,
- payload, payload_length,
- nskb->csum);
- icmph->checksum = csum_fold(nskb->csum);
-}
-
-#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
-static bool ipv6_should_icmp(struct sk_buff *skb)
-{
- struct ipv6hdr *old_ipv6h = ipv6_hdr(skb);
- int addr_type;
- int payload_off = (u8 *)(old_ipv6h + 1) - skb->data;
- u8 nexthdr = ipv6_hdr(skb)->nexthdr;
- __be16 frag_off;
-
- /* Check source address is valid. */
- addr_type = ipv6_addr_type(&old_ipv6h->saddr);
- if (addr_type & IPV6_ADDR_MULTICAST || addr_type == IPV6_ADDR_ANY)
- return false;
-
- /* Don't reply to unspecified addresses. */
- if (ipv6_addr_type(&old_ipv6h->daddr) == IPV6_ADDR_ANY)
- return false;
-
- /* Don't respond to ICMP error messages. */
- payload_off = ipv6_skip_exthdr(skb, payload_off, &nexthdr, &frag_off);
- if (payload_off < 0)
- return false;
-
- if (nexthdr == NEXTHDR_ICMP) {
- u8 icmp_type, *icmp_typep;
-
- icmp_typep = skb_header_pointer(skb, payload_off +
- offsetof(struct icmp6hdr,
- icmp6_type),
- sizeof(icmp_type), &icmp_type);
-
- if (!icmp_typep || !(*icmp_typep & ICMPV6_INFOMSG_MASK))
- return false;
- }
-
- return true;
-}
-
-static void ipv6_build_icmp(struct sk_buff *skb, struct sk_buff *nskb,
- unsigned int mtu, unsigned int payload_length)
-{
- struct ipv6hdr *ipv6h, *old_ipv6h = ipv6_hdr(skb);
- struct icmp6hdr *icmp6h;
- u8 *payload;
-
- ipv6h = (struct ipv6hdr *)skb_put(nskb, sizeof(struct ipv6hdr));
- icmp6h = (struct icmp6hdr *)skb_put(nskb, sizeof(struct icmp6hdr));
- payload = skb_put(nskb, payload_length);
-
- /* IPv6 */
- ipv6h->version = 6;
- ipv6h->priority = 0;
- memset(&ipv6h->flow_lbl, 0, sizeof(ipv6h->flow_lbl));
- ipv6h->payload_len = htons(sizeof(struct icmp6hdr)
- + payload_length);
- ipv6h->nexthdr = NEXTHDR_ICMP;
- ipv6h->hop_limit = IPV6_DEFAULT_HOPLIMIT;
- ipv6h->daddr = old_ipv6h->saddr;
- ipv6h->saddr = old_ipv6h->daddr;
-
- /* ICMPv6 */
- icmp6h->icmp6_type = ICMPV6_PKT_TOOBIG;
- icmp6h->icmp6_code = 0;
- icmp6h->icmp6_cksum = 0;
- icmp6h->icmp6_mtu = htonl(mtu);
-
- nskb->csum = csum_partial((u8 *)icmp6h, sizeof(struct icmp6hdr), 0);
- nskb->csum = skb_copy_and_csum_bits(skb, (u8 *)old_ipv6h - skb->data,
- payload, payload_length,
- nskb->csum);
- icmp6h->icmp6_cksum = csum_ipv6_magic(&ipv6h->saddr, &ipv6h->daddr,
- sizeof(struct icmp6hdr)
- + payload_length,
- ipv6h->nexthdr, nskb->csum);
-}
-#endif /* IPv6 */
-
-bool ovs_tnl_frag_needed(struct vport *vport,
- const struct tnl_mutable_config *mutable,
- struct sk_buff *skb, unsigned int mtu)
-{
- unsigned int eth_hdr_len = ETH_HLEN;
- unsigned int total_length = 0, header_length = 0, payload_length;
- struct ethhdr *eh, *old_eh = eth_hdr(skb);
- struct sk_buff *nskb;
-
- /* Sanity check */
- if (skb->protocol == htons(ETH_P_IP)) {
- if (mtu < IP_MIN_MTU)
- return false;
-
- if (!ipv4_should_icmp(skb))
- return true;
- }
-#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
- else if (skb->protocol == htons(ETH_P_IPV6)) {
- if (mtu < IPV6_MIN_MTU)
- return false;
-
- /*
- * In theory we should do PMTUD on IPv6 multicast messages but
- * we don't have an address to send from so just fragment.
- */
- if (ipv6_addr_type(&ipv6_hdr(skb)->daddr) & IPV6_ADDR_MULTICAST)
- return false;
-
- if (!ipv6_should_icmp(skb))
- return true;
- }
-#endif
- else
- return false;
-
- /* Allocate */
- if (old_eh->h_proto == htons(ETH_P_8021Q))
- eth_hdr_len = VLAN_ETH_HLEN;
-
- payload_length = skb->len - eth_hdr_len;
- if (skb->protocol == htons(ETH_P_IP)) {
- header_length = sizeof(struct iphdr) + sizeof(struct icmphdr);
- total_length = min_t(unsigned int, header_length +
- payload_length, 576);
- }
-#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
- else {
- header_length = sizeof(struct ipv6hdr) +
- sizeof(struct icmp6hdr);
- total_length = min_t(unsigned int, header_length +
- payload_length, IPV6_MIN_MTU);
- }
-#endif
-
- payload_length = total_length - header_length;
-
- nskb = dev_alloc_skb(NET_IP_ALIGN + eth_hdr_len + header_length +
- payload_length);
- if (!nskb)
- return false;
-
- skb_reserve(nskb, NET_IP_ALIGN);
-
- /* Ethernet / VLAN */
- eh = (struct ethhdr *)skb_put(nskb, eth_hdr_len);
- memcpy(eh->h_dest, old_eh->h_source, ETH_ALEN);
- memcpy(eh->h_source, mutable->eth_addr, ETH_ALEN);
- nskb->protocol = eh->h_proto = old_eh->h_proto;
- if (old_eh->h_proto == htons(ETH_P_8021Q)) {
- struct vlan_ethhdr *vh = (struct vlan_ethhdr *)eh;
-
- vh->h_vlan_TCI = vlan_eth_hdr(skb)->h_vlan_TCI;
- vh->h_vlan_encapsulated_proto = skb->protocol;
- } else
- vlan_set_tci(nskb, vlan_get_tci(skb));
- skb_reset_mac_header(nskb);
-
- /* Protocol */
- if (skb->protocol == htons(ETH_P_IP))
- ipv4_build_icmp(skb, nskb, mtu, payload_length);
-#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
- else
- ipv6_build_icmp(skb, nskb, mtu, payload_length);
-#endif
-
- if (unlikely(compute_ip_summed(nskb, false))) {
- kfree_skb(nskb);
- return false;
- }
-
- ovs_vport_receive(vport, nskb);
-
- return true;
-}
-
-static bool check_mtu(struct sk_buff *skb,
- struct vport *vport,
- const struct tnl_mutable_config *mutable,
- const struct rtable *rt, __be16 *frag_offp,
- int tunnel_hlen)
-{
- bool df_inherit;
- bool pmtud;
- __be16 frag_off;
- int mtu = 0;
- unsigned int packet_length = skb->len - ETH_HLEN;
-
- if (OVS_CB(skb)->tun_key->ipv4_dst) {
- df_inherit = false;
- pmtud = false;
- frag_off = OVS_CB(skb)->tun_key->tun_flags & OVS_TNL_F_DONT_FRAGMENT ?
- htons(IP_DF) : 0;
- } else {
- df_inherit = mutable->flags & TNL_F_DF_INHERIT;
- pmtud = mutable->flags & TNL_F_PMTUD;
- frag_off = mutable->flags & TNL_F_DF_DEFAULT ? htons(IP_DF) : 0;
- }
-
- /* Allow for one level of tagging in the packet length. */
- if (!vlan_tx_tag_present(skb) &&
- eth_hdr(skb)->h_proto == htons(ETH_P_8021Q))
- packet_length -= VLAN_HLEN;
-
- if (pmtud) {
- int vlan_header = 0;
-
- /* The tag needs to go in packet regardless of where it
- * currently is, so subtract it from the MTU.
- */
- if (vlan_tx_tag_present(skb) ||
- eth_hdr(skb)->h_proto == htons(ETH_P_8021Q))
- vlan_header = VLAN_HLEN;
-
- mtu = dst_mtu(&rt_dst(rt))
- - ETH_HLEN
- - tunnel_hlen
- - vlan_header;
- }
-
- if (skb->protocol == htons(ETH_P_IP)) {
- struct iphdr *iph = ip_hdr(skb);
-
- if (df_inherit)
- frag_off = iph->frag_off & htons(IP_DF);
-
- if (pmtud && iph->frag_off & htons(IP_DF)) {
- mtu = max(mtu, IP_MIN_MTU);
-
- if (packet_length > mtu &&
- ovs_tnl_frag_needed(vport, mutable, skb, mtu))
- return false;
- }
- }
-#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
- else if (skb->protocol == htons(ETH_P_IPV6)) {
- /* IPv6 requires end hosts to do fragmentation
- * if the packet is above the minimum MTU.
- */
- if (df_inherit && packet_length > IPV6_MIN_MTU)
- frag_off = htons(IP_DF);
-
- if (pmtud) {
- mtu = max(mtu, IPV6_MIN_MTU);
-
- if (packet_length > mtu &&
- ovs_tnl_frag_needed(vport, mutable, skb, mtu))
- return false;
- }
- }
-#endif
-
- *frag_offp = frag_off;
- return true;
-}
-
static struct rtable *find_route(struct net *net,
__be32 *saddr, __be32 daddr, u8 ipproto,
u8 tos)
struct ovs_key_ipv4_tunnel tun_key;
int sent_len = 0;
int tunnel_hlen;
- __be16 frag_off = 0;
+ __be16 frag_off;
__be32 daddr;
__be32 saddr;
u8 ttl;
saddr = OVS_CB(skb)->tun_key->ipv4_src;
tos = OVS_CB(skb)->tun_key->ipv4_tos;
ttl = OVS_CB(skb)->tun_key->ipv4_ttl;
+ frag_off = OVS_CB(skb)->tun_key->tun_flags &
+ OVS_TNL_F_DONT_FRAGMENT ? htons(IP_DF) : 0;
} else {
u8 inner_tos;
daddr = mutable->key.daddr;
#endif
}
+ frag_off = mutable->flags & TNL_F_DF_DEFAULT ? htons(IP_DF) : 0;
}
/* Route lookup */
goto err_free_rt;
}
- /* MTU */
- if (unlikely(!check_mtu(skb, vport, mutable, rt, &frag_off, tunnel_hlen))) {
- err = VPORT_E_TX_DROPPED;
- goto err_free_rt;
- }
-
/* TTL Fixup. */
if (!OVS_CB(skb)->tun_key->ipv4_dst) {
if (!(mutable->flags & TNL_F_TTL_INHERIT)) {
goto error_free_vport;
}
- random_ether_addr(mutable->eth_addr);
-
get_random_bytes(&initial_frag_id, sizeof(int));
atomic_set(&tnl_vport->frag_id, initial_frag_id);
goto error;
}
- /* Copy fields whose values should be retained. */
- mutable->seq = old_mutable->seq + 1;
- memcpy(mutable->eth_addr, old_mutable->eth_addr, ETH_ALEN);
-
/* Parse the others configured by userspace. */
err = tnl_set_config(ovs_dp_get_net(vport->dp), options, tnl_vport->tnl_ops,
vport, mutable);
call_rcu(&tnl_vport->rcu, free_port_rcu);
}
-int ovs_tnl_set_addr(struct vport *vport, const unsigned char *addr)
-{
- struct tnl_vport *tnl_vport = tnl_vport_priv(vport);
- struct tnl_mutable_config *old_mutable, *mutable;
-
- old_mutable = rtnl_dereference(tnl_vport->mutable);
- mutable = kmemdup(old_mutable, sizeof(struct tnl_mutable_config), GFP_KERNEL);
- if (!mutable)
- return -ENOMEM;
-
- old_mutable->mlink = 0;
-
- memcpy(mutable->eth_addr, addr, ETH_ALEN);
- assign_config_rcu(vport, mutable);
-
- return 0;
-}
-
const char *ovs_tnl_get_name(const struct vport *vport)
{
const struct tnl_vport *tnl_vport = tnl_vport_priv(vport);
return tnl_vport->name;
}
-const unsigned char *ovs_tnl_get_addr(const struct vport *vport)
-{
- const struct tnl_vport *tnl_vport = tnl_vport_priv(vport);
- return rcu_dereference_rtnl(tnl_vport->mutable)->eth_addr;
-}
-
void ovs_tnl_free_linked_skbs(struct sk_buff *skb)
{
while (skb) {
/* All public tunnel flags. */
#define TNL_F_PUBLIC (TNL_F_CSUM | TNL_F_TOS_INHERIT | TNL_F_TTL_INHERIT | \
- TNL_F_DF_INHERIT | TNL_F_DF_DEFAULT | TNL_F_PMTUD | \
- TNL_F_IPSEC)
+ TNL_F_DF_DEFAULT | TNL_F_IPSEC)
/**
* struct port_lookup_key - Tunnel port key, used as hash table key.
* @key: Used as key for tunnel port. Configured via OVS_TUNNEL_ATTR_*
* attributes.
* @rcu: RCU callback head for deferred destruction.
- * @seq: Sequence number for distinguishing configuration versions.
* @tunnel_hlen: Tunnel header length.
- * @eth_addr: Source address for packets generated by tunnel itself
- * (e.g. ICMP fragmentation needed messages).
* @out_key: Key to use on output, 0 if this tunnel has no fixed output key.
* @flags: TNL_F_* flags.
* @tos: IPv4 TOS value to use for tunnel, 0 if no fixed TOS.
struct port_lookup_key key;
struct rcu_head rcu;
- unsigned seq;
-
- unsigned char eth_addr[ETH_ALEN];
-
/* Configured via OVS_TUNNEL_ATTR_* attributes. */
__be64 out_key;
u32 flags;
int ovs_tnl_set_options(struct vport *, struct nlattr *);
int ovs_tnl_get_options(const struct vport *, struct sk_buff *);
-int ovs_tnl_set_addr(struct vport *vport, const unsigned char *addr);
const char *ovs_tnl_get_name(const struct vport *vport);
-const unsigned char *ovs_tnl_get_addr(const struct vport *vport);
int ovs_tnl_send(struct vport *vport, struct sk_buff *skb);
void ovs_tnl_rcv(struct vport *vport, struct sk_buff *skb);
tun_key->ipv4_tos = iph->tos;
tun_key->ipv4_ttl = iph->ttl;
tun_key->tun_flags = tun_flags;
+
+ /* clear struct padding. */
+ memset((unsigned char*) tun_key + OVS_TUNNEL_KEY_SIZE, 0,
+ sizeof(*tun_key) - OVS_TUNNEL_KEY_SIZE);
}
static inline void tnl_get_param(const struct tnl_mutable_config *mutable,
.exit = capwap_exit,
.create = capwap_create,
.destroy = capwap_destroy,
- .set_addr = ovs_tnl_set_addr,
.get_name = ovs_tnl_get_name,
- .get_addr = ovs_tnl_get_addr,
.get_options = ovs_tnl_get_options,
.set_options = ovs_tnl_set_options,
.send = ovs_tnl_send,
return hdr_len;
}
-/* Called with rcu_read_lock and BH disabled. */
-static void gre_err(struct sk_buff *skb, u32 info)
-{
- struct vport *vport;
- const struct tnl_mutable_config *mutable;
- const int type = icmp_hdr(skb)->type;
- const int code = icmp_hdr(skb)->code;
- int mtu = ntohs(icmp_hdr(skb)->un.frag.mtu);
- u32 tunnel_type;
-
- struct iphdr *iph;
- __be16 flags;
- __be64 key;
- int tunnel_hdr_len, tot_hdr_len;
- unsigned int orig_mac_header;
- unsigned int orig_nw_header;
-
- if (type != ICMP_DEST_UNREACH || code != ICMP_FRAG_NEEDED)
- return;
-
- /*
- * The mimimum size packet that we would actually be able to process:
- * encapsulating IP header, minimum GRE header, Ethernet header,
- * inner IPv4 header.
- */
- if (!pskb_may_pull(skb, sizeof(struct iphdr) + GRE_HEADER_SECTION +
- ETH_HLEN + sizeof(struct iphdr)))
- return;
-
- iph = (struct iphdr *)skb->data;
- if (ipv4_is_multicast(iph->daddr))
- return;
-
- tunnel_hdr_len = parse_header(iph, &flags, &key, &tunnel_type);
- if (tunnel_hdr_len < 0)
- return;
-
- vport = ovs_tnl_find_port(dev_net(skb->dev), iph->saddr, iph->daddr, key,
- tunnel_type, &mutable);
- if (!vport)
- return;
-
- /*
- * Packets received by this function were previously sent by us, so
- * any comparisons should be to the output values, not the input.
- * However, it's not really worth it to have a hash table based on
- * output keys (especially since ICMP error handling of tunneled packets
- * isn't that reliable anyways). Therefore, we do a lookup based on the
- * out key as if it were the in key and then check to see if the input
- * and output keys are the same.
- */
- if (mutable->key.in_key != mutable->out_key)
- return;
-
- if (!!(mutable->flags & TNL_F_IN_KEY_MATCH) !=
- !!(mutable->flags & TNL_F_OUT_KEY_ACTION))
- return;
-
- if ((mutable->flags & TNL_F_CSUM) && !(flags & GRE_CSUM))
- return;
-
- tunnel_hdr_len += iph->ihl << 2;
-
- orig_mac_header = skb_mac_header(skb) - skb->data;
- orig_nw_header = skb_network_header(skb) - skb->data;
- skb_set_mac_header(skb, tunnel_hdr_len);
-
- tot_hdr_len = tunnel_hdr_len + ETH_HLEN;
-
- skb->protocol = eth_hdr(skb)->h_proto;
- if (skb->protocol == htons(ETH_P_8021Q)) {
- tot_hdr_len += VLAN_HLEN;
- skb->protocol = vlan_eth_hdr(skb)->h_vlan_encapsulated_proto;
- }
-
- skb_set_network_header(skb, tot_hdr_len);
- mtu -= tot_hdr_len;
-
- if (skb->protocol == htons(ETH_P_IP))
- tot_hdr_len += sizeof(struct iphdr);
-#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
- else if (skb->protocol == htons(ETH_P_IPV6))
- tot_hdr_len += sizeof(struct ipv6hdr);
-#endif
- else
- goto out;
-
- if (!pskb_may_pull(skb, tot_hdr_len))
- goto out;
-
- if (skb->protocol == htons(ETH_P_IP)) {
- if (mtu < IP_MIN_MTU) {
- if (ntohs(ip_hdr(skb)->tot_len) >= IP_MIN_MTU)
- mtu = IP_MIN_MTU;
- else
- goto out;
- }
-
- }
-#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
- else if (skb->protocol == htons(ETH_P_IPV6)) {
- if (mtu < IPV6_MIN_MTU) {
- unsigned int packet_length = sizeof(struct ipv6hdr) +
- ntohs(ipv6_hdr(skb)->payload_len);
-
- if (packet_length >= IPV6_MIN_MTU
- || ntohs(ipv6_hdr(skb)->payload_len) == 0)
- mtu = IPV6_MIN_MTU;
- else
- goto out;
- }
- }
-#endif
-
- __skb_pull(skb, tunnel_hdr_len);
- ovs_tnl_frag_needed(vport, mutable, skb, mtu);
- __skb_push(skb, tunnel_hdr_len);
-
-out:
- skb_set_mac_header(skb, orig_mac_header);
- skb_set_network_header(skb, orig_nw_header);
- skb->protocol = htons(ETH_P_IP);
-}
-
static bool check_checksum(struct sk_buff *skb)
{
struct iphdr *iph = ip_hdr(skb);
return ovs_tnl_create(parms, &ovs_gre_vport_ops, &gre_tnl_ops);
}
-static struct vport *gre_create_ft(const struct vport_parms *parms)
-{
- return ovs_tnl_create(parms, &ovs_gre_ft_vport_ops, &gre_tnl_ops);
-}
-
static const struct tnl_ops gre64_tnl_ops = {
.tunnel_type = TNL_T_PROTO_GRE64,
.ipproto = IPPROTO_GRE,
static const struct net_protocol gre_protocol_handlers = {
.handler = gre_rcv,
- .err_handler = gre_err,
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,32)
.netns_ok = 1,
#endif
inet_del_protocol(&gre_protocol_handlers, IPPROTO_GRE);
}
-const struct vport_ops ovs_gre_ft_vport_ops = {
- .type = OVS_VPORT_TYPE_FT_GRE,
- .flags = VPORT_F_TUN_ID,
- .init = gre_init,
- .exit = gre_exit,
- .create = gre_create_ft,
- .destroy = ovs_tnl_destroy,
- .set_addr = ovs_tnl_set_addr,
- .get_name = ovs_tnl_get_name,
- .get_addr = ovs_tnl_get_addr,
- .get_options = ovs_tnl_get_options,
- .set_options = ovs_tnl_set_options,
- .send = ovs_tnl_send,
-};
-
const struct vport_ops ovs_gre_vport_ops = {
.type = OVS_VPORT_TYPE_GRE,
.flags = VPORT_F_TUN_ID,
.exit = gre_exit,
.create = gre_create,
.destroy = ovs_tnl_destroy,
- .set_addr = ovs_tnl_set_addr,
.get_name = ovs_tnl_get_name,
- .get_addr = ovs_tnl_get_addr,
.get_options = ovs_tnl_get_options,
.set_options = ovs_tnl_set_options,
.send = ovs_tnl_send,
.exit = gre_exit,
.create = gre_create64,
.destroy = ovs_tnl_destroy,
- .set_addr = ovs_tnl_set_addr,
.get_name = ovs_tnl_get_name,
- .get_addr = ovs_tnl_get_addr,
.get_options = ovs_tnl_get_options,
.set_options = ovs_tnl_set_options,
.send = ovs_tnl_send,
.flags = VPORT_F_REQUIRED | VPORT_F_FLOW,
.create = internal_dev_create,
.destroy = internal_dev_destroy,
- .set_addr = ovs_netdev_set_addr,
.get_name = ovs_netdev_get_name,
- .get_addr = ovs_netdev_get_addr,
.get_ifindex = ovs_netdev_get_ifindex,
.send = internal_dev_recv,
};
call_rcu(&netdev_vport->rcu, free_port_rcu);
}
-int ovs_netdev_set_addr(struct vport *vport, const unsigned char *addr)
-{
- struct netdev_vport *netdev_vport = netdev_vport_priv(vport);
- struct sockaddr sa;
-
- sa.sa_family = ARPHRD_ETHER;
- memcpy(sa.sa_data, addr, ETH_ALEN);
-
- return dev_set_mac_address(netdev_vport->dev, &sa);
-}
-
const char *ovs_netdev_get_name(const struct vport *vport)
{
const struct netdev_vport *netdev_vport = netdev_vport_priv(vport);
return netdev_vport->dev->name;
}
-const unsigned char *ovs_netdev_get_addr(const struct vport *vport)
-{
- const struct netdev_vport *netdev_vport = netdev_vport_priv(vport);
- return netdev_vport->dev->dev_addr;
-}
-
int ovs_netdev_get_ifindex(const struct vport *vport)
{
const struct netdev_vport *netdev_vport = netdev_vport_priv(vport);
.exit = netdev_exit,
.create = netdev_create,
.destroy = netdev_destroy,
- .set_addr = ovs_netdev_set_addr,
.get_name = ovs_netdev_get_name,
- .get_addr = ovs_netdev_get_addr,
.get_ifindex = ovs_netdev_get_ifindex,
.send = netdev_send,
};
return vport_priv(vport);
}
-int ovs_netdev_set_addr(struct vport *, const unsigned char *addr);
const char *ovs_netdev_get_name(const struct vport *);
-const unsigned char *ovs_netdev_get_addr(const struct vport *);
const char *ovs_netdev_get_config(const struct vport *);
int ovs_netdev_get_ifindex(const struct vport *);
+++ /dev/null
-/*
- * Copyright (c) 2007-2012 Nicira, Inc.
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of version 2 of the GNU General Public
- * License as published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
- * 02110-1301, USA
- */
-
-#include <linux/kernel.h>
-#include <linux/jhash.h>
-#include <linux/list.h>
-#include <linux/rtnetlink.h>
-#include <net/net_namespace.h>
-
-#include "compat.h"
-#include "datapath.h"
-#include "vport.h"
-
-struct patch_config {
- struct rcu_head rcu;
-
- char peer_name[IFNAMSIZ];
- unsigned char eth_addr[ETH_ALEN];
-};
-
-struct patch_vport {
- struct rcu_head rcu;
-
- char name[IFNAMSIZ];
-
- /* Protected by RTNL lock. */
- struct hlist_node hash_node;
-
- struct vport __rcu *peer;
- struct patch_config __rcu *patchconf;
-};
-
-/* Protected by RTNL lock. */
-static struct hlist_head *peer_table;
-#define PEER_HASH_BUCKETS 256
-
-static void update_peers(struct net *, const char *name, struct vport *);
-
-static struct patch_vport *patch_vport_priv(const struct vport *vport)
-{
- return vport_priv(vport);
-}
-
-/* RCU callback. */
-static void free_config(struct rcu_head *rcu)
-{
- struct patch_config *c = container_of(rcu, struct patch_config, rcu);
- kfree(c);
-}
-
-static void assign_config_rcu(struct vport *vport,
- struct patch_config *new_config)
-{
- struct patch_vport *patch_vport = patch_vport_priv(vport);
- struct patch_config *old_config;
-
- old_config = rtnl_dereference(patch_vport->patchconf);
- rcu_assign_pointer(patch_vport->patchconf, new_config);
- call_rcu(&old_config->rcu, free_config);
-}
-
-static struct hlist_head *hash_bucket(struct net *net, const char *name)
-{
- unsigned int hash = jhash(name, strlen(name), (unsigned long) net);
- return &peer_table[hash & (PEER_HASH_BUCKETS - 1)];
-}
-
-static int patch_init(void)
-{
- peer_table = kzalloc(PEER_HASH_BUCKETS * sizeof(struct hlist_head),
- GFP_KERNEL);
- if (!peer_table)
- return -ENOMEM;
-
- return 0;
-}
-
-static void patch_exit(void)
-{
- kfree(peer_table);
-}
-
-static const struct nla_policy patch_policy[OVS_PATCH_ATTR_MAX + 1] = {
-#ifdef HAVE_NLA_NUL_STRING
- [OVS_PATCH_ATTR_PEER] = { .type = NLA_NUL_STRING, .len = IFNAMSIZ - 1 },
-#endif
-};
-
-static int patch_set_config(struct vport *vport, const struct nlattr *options,
- struct patch_config *patchconf)
-{
- struct patch_vport *patch_vport = patch_vport_priv(vport);
- struct nlattr *a[OVS_PATCH_ATTR_MAX + 1];
- const char *peer_name;
- int err;
-
- if (!options)
- return -EINVAL;
-
- err = nla_parse_nested(a, OVS_PATCH_ATTR_MAX, options, patch_policy);
- if (err)
- return err;
-
- if (!a[OVS_PATCH_ATTR_PEER] ||
- CHECK_NUL_STRING(a[OVS_PATCH_ATTR_PEER], IFNAMSIZ - 1))
- return -EINVAL;
-
- peer_name = nla_data(a[OVS_PATCH_ATTR_PEER]);
- if (!strcmp(patch_vport->name, peer_name))
- return -EINVAL;
-
- strcpy(patchconf->peer_name, peer_name);
-
- return 0;
-}
-
-static struct vport *patch_create(const struct vport_parms *parms)
-{
- struct vport *vport;
- struct patch_vport *patch_vport;
- const char *peer_name;
- struct patch_config *patchconf;
- struct net *net = ovs_dp_get_net(parms->dp);
- int err;
-
- vport = ovs_vport_alloc(sizeof(struct patch_vport),
- &ovs_patch_vport_ops, parms);
- if (IS_ERR(vport)) {
- err = PTR_ERR(vport);
- goto error;
- }
-
- patch_vport = patch_vport_priv(vport);
-
- strcpy(patch_vport->name, parms->name);
-
- patchconf = kmalloc(sizeof(struct patch_config), GFP_KERNEL);
- if (!patchconf) {
- err = -ENOMEM;
- goto error_free_vport;
- }
-
- err = patch_set_config(vport, parms->options, patchconf);
- if (err)
- goto error_free_patchconf;
-
- random_ether_addr(patchconf->eth_addr);
-
- rcu_assign_pointer(patch_vport->patchconf, patchconf);
-
- peer_name = patchconf->peer_name;
- hlist_add_head(&patch_vport->hash_node, hash_bucket(net, peer_name));
- rcu_assign_pointer(patch_vport->peer, ovs_vport_locate(net, peer_name));
- update_peers(net, patch_vport->name, vport);
-
- return vport;
-
-error_free_patchconf:
- kfree(patchconf);
-error_free_vport:
- ovs_vport_free(vport);
-error:
- return ERR_PTR(err);
-}
-
-static void free_port_rcu(struct rcu_head *rcu)
-{
- struct patch_vport *patch_vport = container_of(rcu,
- struct patch_vport, rcu);
-
- kfree((struct patch_config __force *)patch_vport->patchconf);
- ovs_vport_free(vport_from_priv(patch_vport));
-}
-
-static void patch_destroy(struct vport *vport)
-{
- struct patch_vport *patch_vport = patch_vport_priv(vport);
-
- update_peers(ovs_dp_get_net(vport->dp), patch_vport->name, NULL);
- hlist_del(&patch_vport->hash_node);
- call_rcu(&patch_vport->rcu, free_port_rcu);
-}
-
-static int patch_set_options(struct vport *vport, struct nlattr *options)
-{
- struct patch_vport *patch_vport = patch_vport_priv(vport);
- struct patch_config *patchconf;
- int err;
-
- patchconf = kmemdup(rtnl_dereference(patch_vport->patchconf),
- sizeof(struct patch_config), GFP_KERNEL);
- if (!patchconf) {
- err = -ENOMEM;
- goto error;
- }
-
- err = patch_set_config(vport, options, patchconf);
- if (err)
- goto error_free;
-
- assign_config_rcu(vport, patchconf);
-
- hlist_del(&patch_vport->hash_node);
-
- rcu_assign_pointer(patch_vport->peer,
- ovs_vport_locate(ovs_dp_get_net(vport->dp), patchconf->peer_name));
-
- hlist_add_head(&patch_vport->hash_node,
- hash_bucket(ovs_dp_get_net(vport->dp), patchconf->peer_name));
-
- return 0;
-error_free:
- kfree(patchconf);
-error:
- return err;
-}
-
-static void update_peers(struct net *net, const char *name, struct vport *vport)
-{
- struct hlist_head *bucket = hash_bucket(net, name);
- struct patch_vport *peer_vport;
- struct hlist_node *node;
-
- hlist_for_each_entry(peer_vport, node, bucket, hash_node) {
- struct vport *curr_vport = vport_from_priv(peer_vport);
- const char *peer_name;
-
- peer_name = rtnl_dereference(peer_vport->patchconf)->peer_name;
- if (!strcmp(peer_name, name) && net_eq(ovs_dp_get_net(curr_vport->dp), net))
- rcu_assign_pointer(peer_vport->peer, vport);
- }
-}
-
-static int patch_set_addr(struct vport *vport, const unsigned char *addr)
-{
- struct patch_vport *patch_vport = patch_vport_priv(vport);
- struct patch_config *patchconf;
-
- patchconf = kmemdup(rtnl_dereference(patch_vport->patchconf),
- sizeof(struct patch_config), GFP_KERNEL);
- if (!patchconf)
- return -ENOMEM;
-
- memcpy(patchconf->eth_addr, addr, ETH_ALEN);
- assign_config_rcu(vport, patchconf);
-
- return 0;
-}
-
-
-static const char *patch_get_name(const struct vport *vport)
-{
- const struct patch_vport *patch_vport = patch_vport_priv(vport);
- return patch_vport->name;
-}
-
-static const unsigned char *patch_get_addr(const struct vport *vport)
-{
- const struct patch_vport *patch_vport = patch_vport_priv(vport);
- return rcu_dereference_rtnl(patch_vport->patchconf)->eth_addr;
-}
-
-static int patch_get_options(const struct vport *vport, struct sk_buff *skb)
-{
- struct patch_vport *patch_vport = patch_vport_priv(vport);
- struct patch_config *patchconf = rcu_dereference_rtnl(patch_vport->patchconf);
-
- return nla_put_string(skb, OVS_PATCH_ATTR_PEER, patchconf->peer_name);
-}
-
-static int patch_send(struct vport *vport, struct sk_buff *skb)
-{
- struct patch_vport *patch_vport = patch_vport_priv(vport);
- struct vport *peer = rcu_dereference(patch_vport->peer);
- int skb_len = skb->len;
-
- if (!peer) {
- kfree_skb(skb);
- ovs_vport_record_error(vport, VPORT_E_TX_DROPPED);
-
- return 0;
- }
-
- ovs_vport_receive(peer, skb);
- return skb_len;
-}
-
-const struct vport_ops ovs_patch_vport_ops = {
- .type = OVS_VPORT_TYPE_PATCH,
- .init = patch_init,
- .exit = patch_exit,
- .create = patch_create,
- .destroy = patch_destroy,
- .set_addr = patch_set_addr,
- .get_name = patch_get_name,
- .get_addr = patch_get_addr,
- .get_options = patch_get_options,
- .set_options = patch_set_options,
- .send = patch_send,
-};
.flags = VPORT_F_TUN_ID,
.create = vxlan_tnl_create,
.destroy = vxlan_tnl_destroy,
- .set_addr = ovs_tnl_set_addr,
.get_name = ovs_tnl_get_name,
- .get_addr = ovs_tnl_get_addr,
.get_options = ovs_tnl_get_options,
.set_options = vxlan_set_options,
.send = ovs_tnl_send,
static const struct vport_ops *base_vport_ops_list[] = {
&ovs_netdev_vport_ops,
&ovs_internal_vport_ops,
- &ovs_patch_vport_ops,
&ovs_gre_vport_ops,
- &ovs_gre_ft_vport_ops,
&ovs_gre64_vport_ops,
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,26)
&ovs_capwap_vport_ops,
vport->ops->destroy(vport);
}
-/**
- * ovs_vport_set_addr - set device Ethernet address (for kernel callers)
- *
- * @vport: vport on which to set Ethernet address.
- * @addr: New address.
- *
- * Sets the Ethernet address of the given device. Some devices may not support
- * setting the Ethernet address, in which case the result will always be
- * -EOPNOTSUPP. RTNL lock must be held.
- */
-int ovs_vport_set_addr(struct vport *vport, const unsigned char *addr)
-{
- ASSERT_RTNL();
-
- if (!is_valid_ether_addr(addr))
- return -EADDRNOTAVAIL;
-
- if (vport->ops->set_addr)
- return vport->ops->set_addr(vport, addr);
- else
- return -EOPNOTSUPP;
-}
-
/**
* ovs_vport_set_stats - sets offset device stats
*
struct vport *ovs_vport_locate(struct net *net, const char *name);
-int ovs_vport_set_addr(struct vport *, const unsigned char *);
void ovs_vport_set_stats(struct vport *, struct ovs_vport_stats *);
void ovs_vport_get_stats(struct vport *, struct ovs_vport_stats *);
* @get_options: Appends vport-specific attributes for the configuration of an
* existing vport to a &struct sk_buff. May be %NULL for a vport that does not
* have any configuration.
- * @set_addr: Set the device's MAC address. May be null if not supported.
* @get_name: Get the device's name.
- * @get_addr: Get the device's MAC address.
* @get_config: Get the device's configuration.
* @get_ifindex: Get the system interface index associated with the device.
* May be null if the device does not have an ifindex.
int (*set_options)(struct vport *, struct nlattr *);
int (*get_options)(const struct vport *, struct sk_buff *);
- int (*set_addr)(struct vport *, const unsigned char *);
-
/* Called with rcu_read_lock or RTNL lock. */
const char *(*get_name)(const struct vport *);
- const unsigned char *(*get_addr)(const struct vport *);
void (*get_config)(const struct vport *, void *);
int (*get_ifindex)(const struct vport *);
int (*send)(struct vport *, struct sk_buff *);
* add yours to the list at the top of vport.c. */
extern const struct vport_ops ovs_netdev_vport_ops;
extern const struct vport_ops ovs_internal_vport_ops;
-extern const struct vport_ops ovs_patch_vport_ops;
extern const struct vport_ops ovs_gre_vport_ops;
-extern const struct vport_ops ovs_gre_ft_vport_ops;
extern const struct vport_ops ovs_gre64_vport_ops;
extern const struct vport_ops ovs_capwap_vport_ops;
extern const struct vport_ops ovs_vxlan_vport_ops;
OVS_VPORT_TYPE_UNSPEC,
OVS_VPORT_TYPE_NETDEV, /* network device */
OVS_VPORT_TYPE_INTERNAL, /* network device implemented by datapath */
- OVS_VPORT_TYPE_FT_GRE, /* Flow based GRE tunnel. */
+ OVS_VPORT_TYPE_GRE, /* GRE tunnel. */
OVS_VPORT_TYPE_VXLAN, /* VXLAN tunnel */
- OVS_VPORT_TYPE_PATCH = 100, /* virtual tunnel connecting two vports */
- OVS_VPORT_TYPE_GRE, /* GRE tunnel */
- OVS_VPORT_TYPE_CAPWAP, /* CAPWAP tunnel */
+ OVS_VPORT_TYPE_CAPWAP = 102, /* CAPWAP tunnel */
OVS_VPORT_TYPE_GRE64 = 104, /* GRE tunnel with 64-bit keys */
__OVS_VPORT_TYPE_MAX
};
* this port. A value of zero indicates that upcalls should not be sent.
* @OVS_VPORT_ATTR_STATS: A &struct ovs_vport_stats giving statistics for
* packets sent or received through the vport.
- * @OVS_VPORT_ATTR_ADDRESS: A 6-byte Ethernet address for the vport.
*
* These attributes follow the &struct ovs_header within the Generic Netlink
* payload for %OVS_VPORT_* commands.
* %OVS_VPORT_ATTR_NAME attributes are required. %OVS_VPORT_ATTR_PORT_NO is
* optional; if not specified a free port number is automatically selected.
* Whether %OVS_VPORT_ATTR_OPTIONS is required or optional depends on the type
- * of vport. %OVS_VPORT_ATTR_STATS and %OVS_VPORT_ATTR_ADDRESS are optional,
- * and other attributes are ignored.
+ * of vport. %OVS_VPORT_ATTR_STATS is optional and other attributes are
+ * ignored.
*
* For other requests, if %OVS_VPORT_ATTR_NAME is specified then it is used to
* look up the vport to operate on; otherwise dp_idx from the &struct
OVS_VPORT_ATTR_OPTIONS, /* nested attributes, varies by vport type */
OVS_VPORT_ATTR_UPCALL_PID, /* u32 Netlink PID to receive upcalls */
OVS_VPORT_ATTR_STATS, /* struct ovs_vport_stats */
- OVS_VPORT_ATTR_ADDRESS = 100, /* hardware address */
__OVS_VPORT_ATTR_MAX
};
#define TNL_F_CSUM (1 << 0) /* Checksum packets. */
#define TNL_F_TOS_INHERIT (1 << 1) /* Inherit ToS from inner packet. */
#define TNL_F_TTL_INHERIT (1 << 2) /* Inherit TTL from inner packet. */
-#define TNL_F_DF_INHERIT (1 << 3) /* Inherit DF bit from inner packet. */
+/* Bit 3 was previously used for Don't Fragment inheritance. " */
#define TNL_F_DF_DEFAULT (1 << 4) /* Set DF bit if inherit off or
* not IP. */
+/* Bit 5 was previously used for path MTU discovery. " */
/* Bit 6 is reserved since it was previously used for Tunnel header caching. */
-#define TNL_F_PMTUD (1 << 5) /* Enable path MTU discovery. */
#define TNL_F_IPSEC (1 << 7) /* Traffic is IPsec encrypted. */
#endif /* openvswitch/tunnel.h */
lib/netdev-tunnel.c \
lib/netdev-pltap.c \
lib/netdev-provider.h \
+ lib/netdev-vport.c \
+ lib/netdev-vport.h \
lib/netdev.c \
lib/netdev.h \
lib/netflow.h \
lib/dpif-linux.h \
lib/netdev-linux.c \
lib/netdev-linux.h \
- lib/netdev-vport.c \
- lib/netdev-vport.h \
lib/netlink-notifier.c \
lib/netlink-notifier.h \
lib/netlink-protocol.h \
/*
- * Copyright (c) 2008, 2009, 2010, 2011, 2012 Nicira, Inc.
+ * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013 Nicira, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
return error;
}
+static const char *
+get_vport_type(const struct dpif_linux_vport *vport)
+{
+ static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 20);
+
+ switch (vport->type) {
+ case OVS_VPORT_TYPE_NETDEV:
+ return "system";
+
+ case OVS_VPORT_TYPE_INTERNAL:
+ return "internal";
+
+ case OVS_VPORT_TYPE_GRE:
+ return "gre";
+
+ case OVS_VPORT_TYPE_GRE64:
+ return "gre64";
+
+ case OVS_VPORT_TYPE_CAPWAP:
+ return "capwap";
+
+ case OVS_VPORT_TYPE_VXLAN:
+ return "vxlan";
+
+ case OVS_VPORT_TYPE_UNSPEC:
+ case __OVS_VPORT_TYPE_MAX:
+ break;
+ }
+
+ VLOG_WARN_RL(&rl, "dp%d: port `%s' has unsupported type %u",
+ vport->dp_ifindex, vport->name, (unsigned int) vport->type);
+ return "unknown";
+}
+
+static enum ovs_vport_type
+netdev_to_ovs_vport_type(const struct netdev *netdev)
+{
+ const char *type = netdev_get_type(netdev);
+
+ if (!strcmp(type, "tap") || !strcmp(type, "system")) {
+ return OVS_VPORT_TYPE_NETDEV;
+ } else if (!strcmp(type, "internal")) {
+ return OVS_VPORT_TYPE_INTERNAL;
+ } else if (strstr(type, "gre64")) {
+ return OVS_VPORT_TYPE_GRE64;
+ } else if (strstr(type, "gre")) {
+ return OVS_VPORT_TYPE_GRE;
+ } else if (!strcmp(type, "capwap")) {
+ return OVS_VPORT_TYPE_CAPWAP;
+ } else if (!strcmp(type, "vxlan")) {
+ return OVS_VPORT_TYPE_VXLAN;
+ } else {
+ return OVS_VPORT_TYPE_UNSPEC;
+ }
+}
+
static int
dpif_linux_port_add(struct dpif *dpif_, struct netdev *netdev,
uint32_t *port_nop)
{
struct dpif_linux *dpif = dpif_linux_cast(dpif_);
- const char *name = netdev_get_name(netdev);
+ const char *name = netdev_vport_get_dpif_port(netdev);
const char *type = netdev_get_type(netdev);
struct dpif_linux_vport request, reply;
- const struct ofpbuf *options;
struct nl_sock *sock = NULL;
uint32_t upcall_pid;
struct ofpbuf *buf;
dpif_linux_vport_init(&request);
request.cmd = OVS_VPORT_CMD_NEW;
request.dp_ifindex = dpif->dp_ifindex;
- request.type = netdev_vport_get_vport_type(netdev);
+ request.type = netdev_to_ovs_vport_type(netdev);
if (request.type == OVS_VPORT_TYPE_UNSPEC) {
VLOG_WARN_RL(&error_rl, "%s: cannot create port `%s' because it has "
"unsupported type `%s'",
}
request.name = name;
- options = netdev_vport_get_options(netdev);
- if (options && options->size) {
- request.options = options->data;
- request.options_len = options->size;
- }
-
if (request.type == OVS_VPORT_TYPE_NETDEV) {
netdev_linux_ethtool_set_flag(netdev, ETH_FLAG_LRO, "LRO", false);
}
error = ENODEV;
} else if (dpif_port) {
dpif_port->name = xstrdup(reply.name);
- dpif_port->type = xstrdup(netdev_vport_get_netdev_type(&reply));
+ dpif_port->type = xstrdup(get_vport_type(&reply));
dpif_port->port_no = reply.port_no;
}
ofpbuf_delete(buf);
}
dpif_port->name = CONST_CAST(char *, vport.name);
- dpif_port->type = CONST_CAST(char *, netdev_vport_get_netdev_type(&vport));
+ dpif_port->type = CONST_CAST(char *, get_vport_type(&vport));
dpif_port->port_no = vport.port_no;
return 0;
}
[OVS_VPORT_ATTR_UPCALL_PID] = { .type = NL_A_U32 },
[OVS_VPORT_ATTR_STATS] = { NL_POLICY_FOR(struct ovs_vport_stats),
.optional = true },
- [OVS_VPORT_ATTR_ADDRESS] = { .type = NL_A_UNSPEC,
- .min_len = ETH_ADDR_LEN,
- .max_len = ETH_ADDR_LEN,
- .optional = true },
[OVS_VPORT_ATTR_OPTIONS] = { .type = NL_A_NESTED, .optional = true },
};
if (a[OVS_VPORT_ATTR_STATS]) {
vport->stats = nl_attr_get(a[OVS_VPORT_ATTR_STATS]);
}
- if (a[OVS_VPORT_ATTR_ADDRESS]) {
- vport->address = nl_attr_get(a[OVS_VPORT_ATTR_ADDRESS]);
- }
if (a[OVS_VPORT_ATTR_OPTIONS]) {
vport->options = nl_attr_get(a[OVS_VPORT_ATTR_OPTIONS]);
vport->options_len = nl_attr_get_size(a[OVS_VPORT_ATTR_OPTIONS]);
vport->stats, sizeof *vport->stats);
}
- if (vport->address) {
- nl_msg_put_unspec(buf, OVS_VPORT_ATTR_ADDRESS,
- vport->address, ETH_ADDR_LEN);
- }
-
if (vport->options) {
nl_msg_put_nested(buf, OVS_VPORT_ATTR_OPTIONS,
vport->options, vport->options_len);
static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 5);
struct ds s;
- if (VLOG_DROP_ERR(&rl)) {
+ if (VLOG_DROP_WARN(&rl)) {
return;
}
const char *name; /* OVS_VPORT_ATTR_NAME. */
const uint32_t *upcall_pid; /* OVS_VPORT_ATTR_UPCALL_PID. */
const struct ovs_vport_stats *stats; /* OVS_VPORT_ATTR_STATS. */
- const uint8_t *address; /* OVS_VPORT_ATTR_ADDRESS. */
const struct nlattr *options; /* OVS_VPORT_ATTR_OPTIONS. */
size_t options_len;
};
/*
- * Copyright (c) 2009, 2010, 2011, 2012 Nicira, Inc.
+ * Copyright (c) 2009, 2010, 2011, 2012, 2013 Nicira, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
#include "hmap.h"
#include "list.h"
#include "netdev.h"
+#include "netdev-vport.h"
#include "netlink.h"
#include "odp-util.h"
#include "ofp-print.h"
return 0;
}
+static bool
+dpif_netdev_class_is_dummy(const struct dpif_class *class)
+{
+ return class != &dpif_netdev_class;
+}
+
+static bool
+dpif_netdev_class_is_planetlab(const struct dpif_class *class)
+{
+ return class == &dpif_planetlab_class;
+}
+
static const char *
dpif_netdev_port_open_type(const struct dpif_class *class, const char *type)
{
return strcmp(type, "internal") ? type
- : class == &dpif_netdev_class ? "tap"
- : class == &dpif_planetlab_class ? "pltap"
- : "dummy";
+ : dpif_netdev_class_is_planetlab(class) ? "pltap"
+ : dpif_netdev_class_is_dummy(class) ? "dummy"
+ : "tap";
}
static struct dpif *
/* XXX reject non-Ethernet devices */
error = netdev_listen(netdev);
- if (error) {
+ if (error
+ && !(error == EOPNOTSUPP && dpif_netdev_class_is_dummy(dp->class))) {
VLOG_ERR("%s: cannot receive packets on this network device (%s)",
devname, strerror(errno));
netdev_close(netdev);
}
port_no = *port_nop;
} else {
- port_no = choose_port(dp, netdev_get_name(netdev));
+ port_no = choose_port(dp, netdev_vport_get_dpif_port(netdev));
}
if (port_no >= 0) {
*port_nop = port_no;
- return do_add_port(dp, netdev_get_name(netdev),
+ return do_add_port(dp, netdev_vport_get_dpif_port(netdev),
netdev_get_type(netdev), port_no);
}
return EFBIG;
struct dp_netdev_port *port;
LIST_FOR_EACH (port, node, &dp->port_list) {
- if (!strcmp(netdev_get_name(port->netdev), devname)) {
+ if (!strcmp(netdev_vport_get_dpif_port(port->netdev), devname)) {
*portp = port;
return 0;
}
dp->ports[port->port_no] = NULL;
dp->serial++;
- name = xstrdup(netdev_get_name(port->netdev));
+ name = xstrdup(netdev_vport_get_dpif_port(port->netdev));
netdev_close(port->netdev);
free(port->type);
answer_port_query(const struct dp_netdev_port *port,
struct dpif_port *dpif_port)
{
- dpif_port->name = xstrdup(netdev_get_name(port->netdev));
+ dpif_port->name = xstrdup(netdev_vport_get_dpif_port(port->netdev));
dpif_port->type = xstrdup(port->type);
dpif_port->port_no = port->port_no;
}
struct dp_netdev_port *port = dp->ports[port_no];
if (port) {
free(state->name);
- state->name = xstrdup(netdev_get_name(port->netdev));
+ state->name = xstrdup(netdev_vport_get_dpif_port(port->netdev));
dpif_port->name = state->name;
dpif_port->type = port->type;
dpif_port->port_no = port->port_no;
} else if (error != EAGAIN && error != EOPNOTSUPP) {
static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
VLOG_ERR_RL(&rl, "error receiving data from %s: %s",
- netdev_get_name(port->netdev), strerror(error));
+ netdev_vport_get_dpif_port(port->netdev),
+ strerror(error));
}
}
ofpbuf_uninit(&packet);
#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>
-#include <linux/openvswitch.h>
#include "openflow/openflow.h"
#include "netdev.h"
#include "util.h"
/*
- * Copyright (c) 2009, 2010, 2011, 2012 Nicira, Inc.
+ * Copyright (c) 2009, 2010, 2011, 2012, 2013 Nicira, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
memset(&wc->masks.arp_tha, 0xff, sizeof wc->masks.arp_tha);
}
- if (flow->dl_type == htons(ETH_TYPE_IPV6) ||
- flow->dl_type == htons(ETH_TYPE_IP)) {
+ if (is_ip_any(flow)) {
memset(&wc->masks.nw_tos, 0xff, sizeof wc->masks.nw_tos);
memset(&wc->masks.nw_ttl, 0xff, sizeof wc->masks.nw_ttl);
}
NOT_REACHED();
}
-static bool
-is_ip_any(const struct flow *flow)
-{
- return (flow->dl_type == htons(ETH_TYPE_IP) ||
- flow->dl_type == htons(ETH_TYPE_IPV6));
-}
-
static bool
is_icmpv4(const struct flow *flow)
{
#include "flow.h"
#include "list.h"
#include "netdev-provider.h"
+#include "netdev-vport.h"
#include "odp-util.h"
#include "ofp-print.h"
#include "ofpbuf.h"
VLOG_DEFINE_THIS_MODULE(netdev_dummy);
+#ifdef __FreeBSD__
+#define FREE_BSD 1
+#else
+#define FREE_BSD 0
+#endif
+
struct netdev_dev_dummy {
struct netdev_dev netdev_dev;
uint8_t hwaddr[ETH_ADDR_LEN];
sset_destroy(&types);
}
netdev_register_provider(&dummy_class);
+
+ if (FREE_BSD) {
+ netdev_vport_tunnel_register();
+ }
}
#include "hmap.h"
#include "netdev-provider.h"
#include "netdev-vport.h"
-#include "netlink.h"
#include "netlink-notifier.h"
#include "netlink-socket.h"
+#include "netlink.h"
#include "ofpbuf.h"
#include "openflow/openflow.h"
#include "packets.h"
#include "poll-loop.h"
#include "rtnetlink-link.h"
-#include "socket-util.h"
#include "shash.h"
+#include "socket-util.h"
#include "sset.h"
#include "timer.h"
+#include "unaligned.h"
#include "vlog.h"
VLOG_DEFINE_THIS_MODULE(netdev_linux);
*b = tmp;
}
+/* Copies 'src' into 'dst', performing format conversion in the process.
+ *
+ * 'src' is allowed to be misaligned. */
+static void
+netdev_stats_from_ovs_vport_stats(struct netdev_stats *dst,
+ const struct ovs_vport_stats *src)
+{
+ dst->rx_packets = get_unaligned_u64(&src->rx_packets);
+ dst->tx_packets = get_unaligned_u64(&src->tx_packets);
+ dst->rx_bytes = get_unaligned_u64(&src->rx_bytes);
+ dst->tx_bytes = get_unaligned_u64(&src->tx_bytes);
+ dst->rx_errors = get_unaligned_u64(&src->rx_errors);
+ dst->tx_errors = get_unaligned_u64(&src->tx_errors);
+ dst->rx_dropped = get_unaligned_u64(&src->rx_dropped);
+ dst->tx_dropped = get_unaligned_u64(&src->tx_dropped);
+ dst->multicast = 0;
+ dst->collisions = 0;
+ dst->rx_length_errors = 0;
+ dst->rx_over_errors = 0;
+ dst->rx_crc_errors = 0;
+ dst->rx_frame_errors = 0;
+ dst->rx_fifo_errors = 0;
+ dst->rx_missed_errors = 0;
+ dst->tx_aborted_errors = 0;
+ dst->tx_carrier_errors = 0;
+ dst->tx_fifo_errors = 0;
+ dst->tx_heartbeat_errors = 0;
+ dst->tx_window_errors = 0;
+}
+
+static int
+get_stats_via_vport__(const struct netdev *netdev, struct netdev_stats *stats)
+{
+ struct dpif_linux_vport reply;
+ struct ofpbuf *buf;
+ int error;
+
+ error = dpif_linux_vport_get(netdev_get_name(netdev), &reply, &buf);
+ if (error) {
+ return error;
+ } else if (!reply.stats) {
+ ofpbuf_delete(buf);
+ return EOPNOTSUPP;
+ }
+
+ netdev_stats_from_ovs_vport_stats(stats, reply.stats);
+
+ ofpbuf_delete(buf);
+
+ return 0;
+}
+
static void
get_stats_via_vport(const struct netdev *netdev_,
struct netdev_stats *stats)
!(netdev_dev->cache_valid & VALID_VPORT_STAT_ERROR)) {
int error;
- error = netdev_vport_get_stats(netdev_, stats);
+ error = get_stats_via_vport__(netdev_, stats);
if (error && error != ENOENT) {
VLOG_WARN_RL(&rl, "%s: obtaining netdev stats via vport failed "
"(%s)", netdev_get_name(netdev_), strerror(error));
memset(&tc_police, 0, sizeof tc_police);
tc_police.action = TC_POLICE_SHOT;
tc_police.mtu = mtu;
- tc_fill_rate(&tc_police.rate, kbits_rate/8 * 1000, mtu);
+ tc_fill_rate(&tc_police.rate, (kbits_rate * 1000)/8, mtu);
tc_police.burst = tc_bytes_to_ticks(tc_police.rate.rate,
kbits_burst * 1024);
#include <errno.h>
#include <fcntl.h>
#include <sys/socket.h>
-#include <linux/openvswitch.h>
-#include <linux/rtnetlink.h>
#include <net/if.h>
#include <sys/ioctl.h>
#include "byte-order.h"
#include "daemon.h"
#include "dirs.h"
-#include "dpif-linux.h"
+#include "dpif.h"
#include "hash.h"
#include "hmap.h"
#include "list.h"
-#include "netdev-linux.h"
#include "netdev-provider.h"
-#include "netlink.h"
-#include "netlink-notifier.h"
-#include "netlink-socket.h"
#include "ofpbuf.h"
-#include "openvswitch/tunnel.h"
#include "packets.h"
#include "route-table.h"
#include "shash.h"
#include "socket-util.h"
-#include "unaligned.h"
#include "vlog.h"
VLOG_DEFINE_THIS_MODULE(netdev_vport);
struct netdev_dev_vport {
struct netdev_dev netdev_dev;
- struct ofpbuf *options;
unsigned int change_seq;
uint8_t etheraddr[ETH_ADDR_LEN];
+ struct netdev_stats stats;
+
+ /* Tunnels. */
struct netdev_tunnel_config tnl_cfg;
+
+ /* Patch Ports. */
+ char *peer;
};
struct vport_class {
- enum ovs_vport_type type;
+ const char *dpif_port;
struct netdev_class netdev_class;
- int (*parse_config)(const char *name, const char *type,
- const struct smap *args, struct ofpbuf *options,
- struct netdev_tunnel_config *tnl_cfg);
- int (*unparse_config)(const char *name, const char *type,
- const struct nlattr *options, size_t options_len,
- struct smap *args);
};
-static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 20);
-
static int netdev_vport_create(const struct netdev_class *, const char *,
struct netdev_dev **);
-static void netdev_vport_poll_notify(const struct netdev *);
-static int tnl_port_config_from_nlattr(const struct nlattr *options,
- size_t options_len,
- struct nlattr *a[OVS_TUNNEL_ATTR_MAX + 1]);
+static int get_patch_config(struct netdev_dev *, struct smap *args);
+static void netdev_vport_poll_notify(struct netdev_dev_vport *);
static bool
is_vport_class(const struct netdev_class *class)
return &netdev_dev_vport_cast(netdev_dev)->tnl_cfg;
}
-/* If 'netdev' is a vport netdev, returns an ofpbuf that contains Netlink
- * options to include in OVS_VPORT_ATTR_OPTIONS for configuring that vport.
- * Otherwise returns NULL. */
-const struct ofpbuf *
-netdev_vport_get_options(const struct netdev *netdev)
-{
- const struct netdev_dev *dev = netdev_get_dev(netdev);
-
- return (is_vport_class(netdev_dev_get_class(dev))
- ? netdev_dev_vport_cast(dev)->options
- : NULL);
-}
-
-enum ovs_vport_type
-netdev_vport_get_vport_type(const struct netdev *netdev)
+bool
+netdev_vport_is_patch(const struct netdev *netdev)
{
const struct netdev_dev *dev = netdev_get_dev(netdev);
const struct netdev_class *class = netdev_dev_get_class(dev);
- return (is_vport_class(class) ? vport_class_cast(class)->type
- : class == &netdev_internal_class ? OVS_VPORT_TYPE_INTERNAL
- : (class == &netdev_linux_class ||
- class == &netdev_tap_class) ? OVS_VPORT_TYPE_NETDEV
- : OVS_VPORT_TYPE_UNSPEC);
-}
-
-static uint32_t
-get_u32_or_zero(const struct nlattr *a)
-{
- return a ? nl_attr_get_u32(a) : 0;
+ return class->get_config == get_patch_config;
}
const char *
-netdev_vport_get_netdev_type(const struct dpif_linux_vport *vport)
+netdev_vport_get_dpif_port(const struct netdev *netdev)
{
- struct nlattr *a[OVS_TUNNEL_ATTR_MAX + 1];
-
- switch (vport->type) {
- case OVS_VPORT_TYPE_UNSPEC:
- break;
-
- case OVS_VPORT_TYPE_NETDEV:
- return "system";
-
- case OVS_VPORT_TYPE_INTERNAL:
- return "internal";
-
- case OVS_VPORT_TYPE_PATCH:
- return "patch";
-
- case OVS_VPORT_TYPE_GRE:
- if (tnl_port_config_from_nlattr(vport->options, vport->options_len,
- a)) {
- break;
- }
- return (get_u32_or_zero(a[OVS_TUNNEL_ATTR_FLAGS]) & TNL_F_IPSEC
- ? "ipsec_gre" : "gre");
-
- case OVS_VPORT_TYPE_GRE64:
- if (tnl_port_config_from_nlattr(vport->options, vport->options_len,
- a)) {
- break;
- }
- return (get_u32_or_zero(a[OVS_TUNNEL_ATTR_FLAGS]) & TNL_F_IPSEC
- ? "ipsec_gre64" : "gre64");
-
- case OVS_VPORT_TYPE_CAPWAP:
- return "capwap";
-
- case OVS_VPORT_TYPE_VXLAN:
- return "vxlan";
-
- case OVS_VPORT_TYPE_FT_GRE:
- case __OVS_VPORT_TYPE_MAX:
- break;
- }
+ const struct netdev_dev *dev = netdev_get_dev(netdev);
+ const struct netdev_class *class = netdev_dev_get_class(dev);
+ const char *dpif_port;
- VLOG_WARN_RL(&rl, "dp%d: port `%s' has unsupported type %u",
- vport->dp_ifindex, vport->name, (unsigned int) vport->type);
- return "unknown";
+ dpif_port = (is_vport_class(class)
+ ? vport_class_cast(class)->dpif_port
+ : NULL);
+ return dpif_port ? dpif_port : netdev_get_name(netdev);
}
static int
{
struct netdev_dev_vport *netdev_dev = netdev_dev_vport_cast(netdev_dev_);
- ofpbuf_delete(netdev_dev->options);
route_table_unregister();
+ free(netdev_dev->peer);
free(netdev_dev);
}
free(netdev);
}
-static int
-netdev_vport_get_config(struct netdev_dev *dev_, struct smap *args)
-{
- const struct netdev_class *netdev_class = netdev_dev_get_class(dev_);
- const struct vport_class *vport_class = vport_class_cast(netdev_class);
- struct netdev_dev_vport *dev = netdev_dev_vport_cast(dev_);
- const char *name = netdev_dev_get_name(dev_);
- int error;
-
- if (!dev->options) {
- struct dpif_linux_vport reply;
- struct ofpbuf *buf;
-
- error = dpif_linux_vport_get(name, &reply, &buf);
- if (error) {
- VLOG_ERR_RL(&rl, "%s: vport query failed (%s)",
- name, strerror(error));
- return error;
- }
-
- dev->options = ofpbuf_clone_data(reply.options, reply.options_len);
- ofpbuf_delete(buf);
- }
-
- error = vport_class->unparse_config(name, netdev_class->type,
- dev->options->data,
- dev->options->size,
- args);
- if (error) {
- VLOG_ERR_RL(&rl, "%s: failed to parse kernel config (%s)",
- name, strerror(error));
- }
- return error;
-}
-
-static int
-netdev_vport_set_config(struct netdev_dev *dev_, const struct smap *args)
-{
- const struct netdev_class *netdev_class = netdev_dev_get_class(dev_);
- const struct vport_class *vport_class = vport_class_cast(netdev_class);
- struct netdev_dev_vport *dev = netdev_dev_vport_cast(dev_);
- const char *name = netdev_dev_get_name(dev_);
- struct netdev_tunnel_config tnl_cfg;
- struct ofpbuf *options;
- int error;
-
- options = ofpbuf_new(64);
- error = vport_class->parse_config(name, netdev_dev_get_type(dev_),
- args, options, &tnl_cfg);
- if (!error
- && (!dev->options
- || options->size != dev->options->size
- || memcmp(options->data, dev->options->data, options->size))) {
- struct dpif_linux_vport vport;
-
- dpif_linux_vport_init(&vport);
- vport.cmd = OVS_VPORT_CMD_SET;
- vport.name = name;
- vport.options = options->data;
- vport.options_len = options->size;
- error = dpif_linux_vport_transact(&vport, NULL, NULL);
- if (!error || error == ENODEV) {
- /* Either reconfiguration succeeded or this vport is not installed
- * in the kernel (e.g. it hasn't been added to a dpif yet with
- * dpif_port_add()). */
- ofpbuf_delete(dev->options);
- dev->options = options;
- dev->tnl_cfg = tnl_cfg;
- options = NULL;
- error = 0;
- }
- }
- ofpbuf_delete(options);
-
- return error;
-}
-
static int
netdev_vport_set_etheraddr(struct netdev *netdev,
const uint8_t mac[ETH_ADDR_LEN])
{
- memcpy(netdev_vport_get_dev(netdev)->etheraddr, mac, ETH_ADDR_LEN);
- netdev_vport_poll_notify(netdev);
+ struct netdev_dev_vport *dev = netdev_vport_get_dev(netdev);
+ memcpy(dev->etheraddr, mac, ETH_ADDR_LEN);
+ netdev_vport_poll_notify(dev);
return 0;
}
return 0;
}
-/* Copies 'src' into 'dst', performing format conversion in the process.
- *
- * 'src' is allowed to be misaligned. */
-static void
-netdev_stats_from_ovs_vport_stats(struct netdev_stats *dst,
- const struct ovs_vport_stats *src)
-{
- dst->rx_packets = get_unaligned_u64(&src->rx_packets);
- dst->tx_packets = get_unaligned_u64(&src->tx_packets);
- dst->rx_bytes = get_unaligned_u64(&src->rx_bytes);
- dst->tx_bytes = get_unaligned_u64(&src->tx_bytes);
- dst->rx_errors = get_unaligned_u64(&src->rx_errors);
- dst->tx_errors = get_unaligned_u64(&src->tx_errors);
- dst->rx_dropped = get_unaligned_u64(&src->rx_dropped);
- dst->tx_dropped = get_unaligned_u64(&src->tx_dropped);
- dst->multicast = 0;
- dst->collisions = 0;
- dst->rx_length_errors = 0;
- dst->rx_over_errors = 0;
- dst->rx_crc_errors = 0;
- dst->rx_frame_errors = 0;
- dst->rx_fifo_errors = 0;
- dst->rx_missed_errors = 0;
- dst->tx_aborted_errors = 0;
- dst->tx_carrier_errors = 0;
- dst->tx_fifo_errors = 0;
- dst->tx_heartbeat_errors = 0;
- dst->tx_window_errors = 0;
-}
-
-int
-netdev_vport_get_stats(const struct netdev *netdev, struct netdev_stats *stats)
-{
- struct dpif_linux_vport reply;
- struct ofpbuf *buf;
- int error;
-
- error = dpif_linux_vport_get(netdev_get_name(netdev), &reply, &buf);
- if (error) {
- return error;
- } else if (!reply.stats) {
- ofpbuf_delete(buf);
- return EOPNOTSUPP;
- }
-
- netdev_stats_from_ovs_vport_stats(stats, reply.stats);
-
- ofpbuf_delete(buf);
-
- return 0;
-}
-
static int
tunnel_get_status(const struct netdev *netdev, struct smap *smap)
{
/* Helper functions. */
static void
-netdev_vport_poll_notify(const struct netdev *netdev)
+netdev_vport_poll_notify(struct netdev_dev_vport *ndv)
{
- struct netdev_dev_vport *ndv = netdev_vport_get_dev(netdev);
-
ndv->change_seq++;
if (!ndv->change_seq) {
ndv->change_seq++;
}
}
\f
-/* Code specific to individual vport types. */
+/* Code specific to tunnel types. */
static ovs_be64
parse_key(const struct smap *args, const char *name,
}
static int
-parse_tunnel_config(const char *name, const char *type,
- const struct smap *args, struct ofpbuf *options,
- struct netdev_tunnel_config *tnl_cfg_)
+set_tunnel_config(struct netdev_dev *dev_, const struct smap *args)
{
+ struct netdev_dev_vport *dev = netdev_dev_vport_cast(dev_);
+ const char *name = netdev_dev_get_name(dev_);
+ const char *type = netdev_dev_get_type(dev_);
bool ipsec_mech_set, needs_dst_port, has_csum;
struct netdev_tunnel_config tnl_cfg;
struct smap_node *node;
- uint8_t flags;
- flags = TNL_F_DF_DEFAULT;
has_csum = strstr(type, "gre");
ipsec_mech_set = false;
memset(&tnl_cfg, 0, sizeof tnl_cfg);
needs_dst_port = !strcmp(type, "vxlan");
tnl_cfg.ipsec = strstr(type, "ipsec");
- if (tnl_cfg.ipsec) {
- flags |= TNL_F_IPSEC;
- }
tnl_cfg.dont_fragment = true;
SMAP_FOR_EACH (node, args) {
}
} else if (!strcmp(node->key, "tos")) {
if (!strcmp(node->value, "inherit")) {
- flags |= TNL_F_TOS_INHERIT;
tnl_cfg.tos_inherit = true;
} else {
char *endptr;
int tos;
tos = strtol(node->value, &endptr, 0);
if (*endptr == '\0' && tos == (tos & IP_DSCP_MASK)) {
- nl_msg_put_u8(options, OVS_TUNNEL_ATTR_TOS, tos);
tnl_cfg.tos = tos;
} else {
VLOG_WARN("%s: invalid TOS %s", name, node->value);
}
} else if (!strcmp(node->key, "ttl")) {
if (!strcmp(node->value, "inherit")) {
- flags |= TNL_F_TTL_INHERIT;
tnl_cfg.ttl_inherit = true;
} else {
- nl_msg_put_u8(options, OVS_TUNNEL_ATTR_TTL, atoi(node->value));
tnl_cfg.ttl = atoi(node->value);
}
} else if (!strcmp(node->key, "dst_port") && needs_dst_port) {
tnl_cfg.dst_port = htons(atoi(node->value));
- nl_msg_put_u16(options, OVS_TUNNEL_ATTR_DST_PORT,
- atoi(node->value));
} else if (!strcmp(node->key, "csum") && has_csum) {
if (!strcmp(node->value, "true")) {
- flags |= TNL_F_CSUM;
tnl_cfg.csum = true;
}
} else if (!strcmp(node->key, "df_default")) {
if (!strcmp(node->value, "false")) {
- flags &= ~TNL_F_DF_DEFAULT;
tnl_cfg.dont_fragment = false;
}
} else if (!strcmp(node->key, "peer_cert") && tnl_cfg.ipsec) {
/* Add a default destination port for VXLAN if none specified. */
if (needs_dst_port && !tnl_cfg.dst_port) {
- nl_msg_put_u16(options, OVS_TUNNEL_ATTR_DST_PORT, VXLAN_DST_PORT);
tnl_cfg.dst_port = htons(VXLAN_DST_PORT);
}
name, type);
return EINVAL;
}
- nl_msg_put_be32(options, OVS_TUNNEL_ATTR_DST_IPV4, tnl_cfg.ip_dst);
if (tnl_cfg.ip_src) {
if (ip_is_multicast(tnl_cfg.ip_dst)) {
VLOG_WARN("%s: remote_ip is multicast, ignoring local_ip", name);
tnl_cfg.ip_src = 0;
- } else {
- nl_msg_put_be32(options, OVS_TUNNEL_ATTR_SRC_IPV4, tnl_cfg.ip_src);
}
}
tnl_cfg.in_key = parse_key(args, "in_key",
&tnl_cfg.in_key_present,
&tnl_cfg.in_key_flow);
- if (tnl_cfg.in_key_present && !tnl_cfg.in_key_flow) {
- nl_msg_put_be64(options, OVS_TUNNEL_ATTR_IN_KEY, tnl_cfg.in_key);
- }
tnl_cfg.out_key = parse_key(args, "out_key",
&tnl_cfg.out_key_present,
&tnl_cfg.out_key_flow);
- if (tnl_cfg.out_key_present && !tnl_cfg.out_key_flow) {
- nl_msg_put_be64(options, OVS_TUNNEL_ATTR_OUT_KEY, tnl_cfg.out_key);
- }
- nl_msg_put_u32(options, OVS_TUNNEL_ATTR_FLAGS, flags);
- *tnl_cfg_ = tnl_cfg;
+ dev->tnl_cfg = tnl_cfg;
+ netdev_vport_poll_notify(dev);
return 0;
}
static int
-tnl_port_config_from_nlattr(const struct nlattr *options, size_t options_len,
- struct nlattr *a[OVS_TUNNEL_ATTR_MAX + 1])
-{
- static const struct nl_policy ovs_tunnel_policy[] = {
- [OVS_TUNNEL_ATTR_FLAGS] = { .type = NL_A_U32, .optional = true },
- [OVS_TUNNEL_ATTR_DST_IPV4] = { .type = NL_A_BE32, .optional = true },
- [OVS_TUNNEL_ATTR_SRC_IPV4] = { .type = NL_A_BE32, .optional = true },
- [OVS_TUNNEL_ATTR_IN_KEY] = { .type = NL_A_BE64, .optional = true },
- [OVS_TUNNEL_ATTR_OUT_KEY] = { .type = NL_A_BE64, .optional = true },
- [OVS_TUNNEL_ATTR_TOS] = { .type = NL_A_U8, .optional = true },
- [OVS_TUNNEL_ATTR_TTL] = { .type = NL_A_U8, .optional = true },
- [OVS_TUNNEL_ATTR_DST_PORT] = { .type = NL_A_U16, .optional = true },
- };
- struct ofpbuf buf;
-
- ofpbuf_use_const(&buf, options, options_len);
- if (!nl_policy_parse(&buf, 0, ovs_tunnel_policy,
- a, ARRAY_SIZE(ovs_tunnel_policy))) {
- return EINVAL;
- }
- return 0;
-}
-
-static uint64_t
-get_be64_or_zero(const struct nlattr *a)
-{
- return a ? ntohll(nl_attr_get_be64(a)) : 0;
-}
-
-static int
-unparse_tunnel_config(const char *name OVS_UNUSED, const char *type OVS_UNUSED,
- const struct nlattr *options, size_t options_len,
- struct smap *args)
+get_tunnel_config(struct netdev_dev *dev, struct smap *args)
{
- struct nlattr *a[OVS_TUNNEL_ATTR_MAX + 1];
- uint32_t flags;
- int error;
+ const struct netdev_tunnel_config *tnl_cfg =
+ &netdev_dev_vport_cast(dev)->tnl_cfg;
- error = tnl_port_config_from_nlattr(options, options_len, a);
- if (error) {
- return error;
+ if (tnl_cfg->ip_dst) {
+ smap_add_format(args, "remote_ip", IP_FMT, IP_ARGS(tnl_cfg->ip_dst));
}
- if (a[OVS_TUNNEL_ATTR_DST_IPV4]) {
- ovs_be32 daddr = nl_attr_get_be32(a[OVS_TUNNEL_ATTR_DST_IPV4]);
- smap_add_format(args, "remote_ip", IP_FMT, IP_ARGS(daddr));
+ if (tnl_cfg->ip_src) {
+ smap_add_format(args, "local_ip", IP_FMT, IP_ARGS(tnl_cfg->ip_src));
}
- if (a[OVS_TUNNEL_ATTR_SRC_IPV4]) {
- ovs_be32 saddr = nl_attr_get_be32(a[OVS_TUNNEL_ATTR_SRC_IPV4]);
- smap_add_format(args, "local_ip", IP_FMT, IP_ARGS(saddr));
- }
-
- if (!a[OVS_TUNNEL_ATTR_IN_KEY] && !a[OVS_TUNNEL_ATTR_OUT_KEY]) {
+ if (tnl_cfg->in_key_flow && tnl_cfg->out_key_flow) {
smap_add(args, "key", "flow");
+ } else if (tnl_cfg->in_key_present && tnl_cfg->out_key_present
+ && tnl_cfg->in_key == tnl_cfg->out_key) {
+ smap_add_format(args, "key", "%"PRIu64, ntohll(tnl_cfg->in_key));
} else {
- uint64_t in_key = get_be64_or_zero(a[OVS_TUNNEL_ATTR_IN_KEY]);
- uint64_t out_key = get_be64_or_zero(a[OVS_TUNNEL_ATTR_OUT_KEY]);
-
- if (in_key && in_key == out_key) {
- smap_add_format(args, "key", "%"PRIu64, in_key);
- } else {
- if (!a[OVS_TUNNEL_ATTR_IN_KEY]) {
- smap_add(args, "in_key", "flow");
- } else if (in_key) {
- smap_add_format(args, "in_key", "%"PRIu64, in_key);
- }
+ if (tnl_cfg->in_key_flow) {
+ smap_add(args, "in_key", "flow");
+ } else if (tnl_cfg->in_key_present) {
+ smap_add_format(args, "in_key", "%"PRIu64,
+ ntohll(tnl_cfg->in_key));
+ }
- if (!a[OVS_TUNNEL_ATTR_OUT_KEY]) {
- smap_add(args, "out_key", "flow");
- } else if (out_key) {
- smap_add_format(args, "out_key", "%"PRIu64, out_key);
- }
+ if (tnl_cfg->out_key_flow) {
+ smap_add(args, "out_key", "flow");
+ } else if (tnl_cfg->out_key_present) {
+ smap_add_format(args, "out_key", "%"PRIu64,
+ ntohll(tnl_cfg->out_key));
}
}
- flags = get_u32_or_zero(a[OVS_TUNNEL_ATTR_FLAGS]);
-
- if (flags & TNL_F_TTL_INHERIT) {
+ if (tnl_cfg->ttl_inherit) {
smap_add(args, "ttl", "inherit");
- } else if (a[OVS_TUNNEL_ATTR_TTL]) {
- int ttl = nl_attr_get_u8(a[OVS_TUNNEL_ATTR_TTL]);
- smap_add_format(args, "ttl", "%d", ttl);
+ } else if (tnl_cfg->ttl != DEFAULT_TTL) {
+ smap_add_format(args, "ttl", "%"PRIu8, tnl_cfg->ttl);
}
- if (flags & TNL_F_TOS_INHERIT) {
+ if (tnl_cfg->tos_inherit) {
smap_add(args, "tos", "inherit");
- } else if (a[OVS_TUNNEL_ATTR_TOS]) {
- int tos = nl_attr_get_u8(a[OVS_TUNNEL_ATTR_TOS]);
- smap_add_format(args, "tos", "0x%x", tos);
+ } else if (tnl_cfg->tos) {
+ smap_add_format(args, "tos", "0x%x", tnl_cfg->tos);
}
- if (a[OVS_TUNNEL_ATTR_DST_PORT]) {
- uint16_t dst_port = nl_attr_get_u16(a[OVS_TUNNEL_ATTR_DST_PORT]);
+ if (tnl_cfg->dst_port) {
+ uint16_t dst_port = ntohs(tnl_cfg->dst_port);
if (dst_port != VXLAN_DST_PORT) {
smap_add_format(args, "dst_port", "%d", dst_port);
}
}
- if (flags & TNL_F_CSUM) {
+ if (tnl_cfg->csum) {
smap_add(args, "csum", "true");
}
- if (flags & TNL_F_DF_INHERIT) {
- /* Shouldn't happen as "df_inherit" is no longer supported. However,
- * for completeness we report it if it's there. */
- smap_add(args, "df_inherit", "true");
- }
- if (!(flags & TNL_F_DF_DEFAULT)) {
+
+ if (!tnl_cfg->dont_fragment) {
smap_add(args, "df_default", "false");
}
return 0;
}
+\f
+/* Code specific to patch ports. */
+
+const char *
+netdev_vport_patch_peer(const struct netdev *netdev)
+{
+ return netdev_vport_is_patch(netdev)
+ ? netdev_vport_get_dev(netdev)->peer
+ : NULL;
+}
+
+void
+netdev_vport_inc_rx(const struct netdev *netdev,
+ const struct dpif_flow_stats *stats)
+{
+ if (is_vport_class(netdev_dev_get_class(netdev_get_dev(netdev)))) {
+ struct netdev_dev_vport *dev = netdev_vport_get_dev(netdev);
+ dev->stats.rx_packets += stats->n_packets;
+ dev->stats.rx_bytes += stats->n_bytes;
+ }
+}
+
+void
+netdev_vport_inc_tx(const struct netdev *netdev,
+ const struct dpif_flow_stats *stats)
+{
+ if (is_vport_class(netdev_dev_get_class(netdev_get_dev(netdev)))) {
+ struct netdev_dev_vport *dev = netdev_vport_get_dev(netdev);
+ dev->stats.tx_packets += stats->n_packets;
+ dev->stats.tx_bytes += stats->n_bytes;
+ }
+}
static int
-parse_patch_config(const char *name, const char *type OVS_UNUSED,
- const struct smap *args, struct ofpbuf *options,
- struct netdev_tunnel_config *tnl_cfg)
+get_patch_config(struct netdev_dev *dev_, struct smap *args)
{
- const char *peer;
+ struct netdev_dev_vport *dev = netdev_dev_vport_cast(dev_);
+
+ if (dev->peer) {
+ smap_add(args, "peer", dev->peer);
+ }
+ return 0;
+}
- memset(tnl_cfg, 0, sizeof *tnl_cfg);
+static int
+set_patch_config(struct netdev_dev *dev_, const struct smap *args)
+{
+ struct netdev_dev_vport *dev = netdev_dev_vport_cast(dev_);
+ const char *name = netdev_dev_get_name(dev_);
+ const char *peer;
peer = smap_get(args, "peer");
if (!peer) {
return EINVAL;
}
- if (strlen(peer) >= IFNAMSIZ) {
- VLOG_ERR("%s: patch 'peer' arg too long", name);
- return EINVAL;
- }
-
if (!strcmp(name, peer)) {
VLOG_ERR("%s: patch peer must not be self", name);
return EINVAL;
}
- nl_msg_put_string(options, OVS_PATCH_ATTR_PEER, peer);
+ free(dev->peer);
+ dev->peer = xstrdup(peer);
return 0;
}
static int
-unparse_patch_config(const char *name OVS_UNUSED, const char *type OVS_UNUSED,
- const struct nlattr *options, size_t options_len,
- struct smap *args)
+get_stats(const struct netdev *netdev, struct netdev_stats *stats)
{
- static const struct nl_policy ovs_patch_policy[] = {
- [OVS_PATCH_ATTR_PEER] = { .type = NL_A_STRING,
- .max_len = IFNAMSIZ,
- .optional = false }
- };
-
- struct nlattr *a[ARRAY_SIZE(ovs_patch_policy)];
- struct ofpbuf buf;
-
- ofpbuf_use_const(&buf, options, options_len);
- if (!nl_policy_parse(&buf, 0, ovs_patch_policy,
- a, ARRAY_SIZE(ovs_patch_policy))) {
- return EINVAL;
- }
-
- smap_add(args, "peer", nl_attr_get_string(a[OVS_PATCH_ATTR_PEER]));
+ struct netdev_dev_vport *dev = netdev_vport_get_dev(netdev);
+ memcpy(stats, &dev->stats, sizeof *stats);
return 0;
}
\f
-#define VPORT_FUNCTIONS(GET_TUNNEL_CONFIG, GET_STATUS) \
+#define VPORT_FUNCTIONS(GET_CONFIG, SET_CONFIG, \
+ GET_TUNNEL_CONFIG, GET_STATUS) \
NULL, \
netdev_vport_run, \
netdev_vport_wait, \
\
netdev_vport_create, \
netdev_vport_destroy, \
- netdev_vport_get_config, \
- netdev_vport_set_config, \
+ GET_CONFIG, \
+ SET_CONFIG, \
GET_TUNNEL_CONFIG, \
\
netdev_vport_open, \
NULL, /* get_carrier */ \
NULL, /* get_carrier_resets */ \
NULL, /* get_miimon */ \
- netdev_vport_get_stats, \
+ get_stats, \
NULL, /* set_stats */ \
\
NULL, /* get_features */ \
\
netdev_vport_change_seq
-#define TUNNEL_CLASS(NAME, VPORT_TYPE) \
- { VPORT_TYPE, \
- { NAME, VPORT_FUNCTIONS(get_netdev_tunnel_config, \
- tunnel_get_status) }, \
- parse_tunnel_config, unparse_tunnel_config }
+#define TUNNEL_CLASS(NAME, DPIF_PORT) \
+ { DPIF_PORT, \
+ { NAME, VPORT_FUNCTIONS(get_tunnel_config, \
+ set_tunnel_config, \
+ get_netdev_tunnel_config, \
+ tunnel_get_status) }}
void
-netdev_vport_register(void)
+netdev_vport_tunnel_register(void)
{
static const struct vport_class vport_classes[] = {
- TUNNEL_CLASS("gre", OVS_VPORT_TYPE_GRE),
- TUNNEL_CLASS("ipsec_gre", OVS_VPORT_TYPE_GRE),
- TUNNEL_CLASS("gre64", OVS_VPORT_TYPE_GRE64),
- TUNNEL_CLASS("ipsec_gre64", OVS_VPORT_TYPE_GRE64),
- TUNNEL_CLASS("capwap", OVS_VPORT_TYPE_CAPWAP),
- TUNNEL_CLASS("vxlan", OVS_VPORT_TYPE_VXLAN),
-
- { OVS_VPORT_TYPE_PATCH,
- { "patch", VPORT_FUNCTIONS(NULL, NULL) },
- parse_patch_config, unparse_patch_config }
+ TUNNEL_CLASS("gre", "gre_system"),
+ TUNNEL_CLASS("ipsec_gre", "gre_system"),
+ TUNNEL_CLASS("gre64", "gre64_system"),
+ TUNNEL_CLASS("ipsec_gre64", "gre64_system"),
+ TUNNEL_CLASS("capwap", "capwap_system"),
+ TUNNEL_CLASS("vxlan", "vxlan_system")
};
int i;
netdev_register_provider(&vport_classes[i].netdev_class);
}
}
+
+void
+netdev_vport_patch_register(void)
+{
+ static const struct vport_class patch_class =
+ { NULL,
+ { "patch", VPORT_FUNCTIONS(get_patch_config,
+ set_patch_config,
+ NULL,
+ NULL) }};
+ netdev_register_provider(&patch_class.netdev_class);
+}
#define NETDEV_VPORT_H 1
#include <stdbool.h>
-#include "openvswitch/types.h"
struct dpif_linux_vport;
+struct dpif_flow_stats;
struct netdev;
struct netdev_stats;
-void netdev_vport_register(void);
+void netdev_vport_tunnel_register(void);
+void netdev_vport_patch_register(void);
-const struct ofpbuf *netdev_vport_get_options(const struct netdev *);
+bool netdev_vport_is_patch(const struct netdev *);
-enum ovs_vport_type netdev_vport_get_vport_type(const struct netdev *);
-const char *netdev_vport_get_netdev_type(const struct dpif_linux_vport *);
+const char *netdev_vport_patch_peer(const struct netdev *netdev);
-int netdev_vport_get_stats(const struct netdev *, struct netdev_stats *);
+void netdev_vport_inc_rx(const struct netdev *,
+ const struct dpif_flow_stats *);
+void netdev_vport_inc_tx(const struct netdev *,
+ const struct dpif_flow_stats *);
+
+const char *netdev_vport_get_dpif_port(const struct netdev *);
#endif /* netdev-vport.h */
inited = true;
fatal_signal_add_hook(close_all_netdevs, NULL, NULL, true);
+ netdev_vport_patch_register();
#ifdef LINUX_DATAPATH
netdev_register_provider(&netdev_linux_class);
netdev_register_provider(&netdev_internal_class);
netdev_register_provider(&netdev_tap_class);
- netdev_vport_register();
+ netdev_vport_tunnel_register();
#endif
#ifdef __FreeBSD__
netdev_register_provider(&netdev_tap_class);
return netdev_get_dev(netdev)->netdev_class->type;
}
+
+const char *
+netdev_get_type_from_name(const char *name)
+{
+ const struct netdev_dev *dev = netdev_dev_from_name(name);
+ return dev ? netdev_dev_get_type(dev) : NULL;
+}
+
struct netdev_dev *
netdev_get_dev(const struct netdev *netdev)
{
/* Basic properties. */
const char *netdev_get_name(const struct netdev *);
const char *netdev_get_type(const struct netdev *);
+const char *netdev_get_type_from_name(const char *);
int netdev_get_mtu(const struct netdev *, int *mtup);
int netdev_set_mtu(const struct netdev *, int mtu);
int netdev_get_ifindex(const struct netdev *);
/*
- * Copyright (c) 2009, 2010, 2011, 2012 Nicira, Inc.
+ * Copyright (c) 2009, 2010, 2011, 2012, 2013 Nicira, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
memcpy(arp_key->arp_tha, flow->arp_tha, ETH_ADDR_LEN);
}
- if ((flow->dl_type == htons(ETH_TYPE_IP)
- || flow->dl_type == htons(ETH_TYPE_IPV6))
- && !(flow->nw_frag & FLOW_NW_FRAG_LATER)) {
-
+ if (is_ip_any(flow) && !(flow->nw_frag & FLOW_NW_FRAG_LATER)) {
if (flow->nw_proto == IPPROTO_TCP) {
struct ovs_key_tcp *tcp_key;
}
if (flow->nw_proto == IPPROTO_TCP
- && (flow->dl_type == htons(ETH_TYPE_IP) ||
- flow->dl_type == htons(ETH_TYPE_IPV6))
+ && is_ip_any(flow)
&& !(flow->nw_frag & FLOW_NW_FRAG_LATER)) {
expected_attrs |= UINT64_C(1) << OVS_KEY_ATTR_TCP;
if (present_attrs & (UINT64_C(1) << OVS_KEY_ATTR_TCP)) {
flow->tp_dst = tcp_key->tcp_dst;
}
} else if (flow->nw_proto == IPPROTO_UDP
- && (flow->dl_type == htons(ETH_TYPE_IP) ||
- flow->dl_type == htons(ETH_TYPE_IPV6))
+ && is_ip_any(flow)
&& !(flow->nw_frag & FLOW_NW_FRAG_LATER)) {
expected_attrs |= UINT64_C(1) << OVS_KEY_ATTR_UDP;
if (present_attrs & (UINT64_C(1) << OVS_KEY_ATTR_UDP)) {
return cookie ? odp_actions->size - NLA_ALIGN(sizeof *cookie) : 0;
}
+
+void
+odp_put_tunnel_action(const struct flow_tnl *tunnel,
+ struct ofpbuf *odp_actions)
+{
+ size_t offset = nl_msg_start_nested(odp_actions, OVS_ACTION_ATTR_SET);
+ tun_key_to_attr(odp_actions, tunnel);
+ nl_msg_end_nested(odp_actions, offset);
+}
\f
/* The commit_odp_actions() function and its helpers. */
nl_msg_end_nested(odp_actions, offset);
}
-static void
-commit_set_tunnel_action(const struct flow *flow, struct flow *base,
+/* If any of the flow key data that ODP actions can modify are different in
+ * 'base->tunnel' and 'flow->tunnel', appends a set_tunnel ODP action to
+ * 'odp_actions' that change the flow tunneling information in key from
+ * 'base->tunnel' into 'flow->tunnel', and then changes 'base->tunnel' in the
+ * same way. In other words, operates the same as commit_odp_actions(), but
+ * only on tunneling information. */
+void
+commit_odp_tunnel_action(const struct flow *flow, struct flow *base,
struct ofpbuf *odp_actions)
{
if (!memcmp(&base->tunnel, &flow->tunnel, sizeof base->tunnel)) {
/* A valid IPV4_TUNNEL must have non-zero ip_dst. */
if (flow->tunnel.ip_dst) {
- size_t offset;
-
- offset = nl_msg_start_nested(odp_actions, OVS_ACTION_ATTR_SET);
- tun_key_to_attr(odp_actions, &base->tunnel);
- nl_msg_end_nested(odp_actions, offset);
+ odp_put_tunnel_action(&base->tunnel, odp_actions);
} else {
commit_set_action(odp_actions, OVS_KEY_ATTR_TUN_ID,
&base->tunnel.tun_id, sizeof base->tunnel.tun_id);
}
/* If any of the flow key data that ODP actions can modify are different in
* 'base' and 'flow', appends ODP actions to 'odp_actions' that change the flow
- * key from 'base' into 'flow', and then changes 'base' the same way. */
+ * key from 'base' into 'flow', and then changes 'base' the same way. Does not
+ * commit set_tunnel actions. Users should call commit_odp_tunnel_action()
+ * in addition to this function if needed. */
void
commit_odp_actions(const struct flow *flow, struct flow *base,
struct ofpbuf *odp_actions)
{
- commit_set_tunnel_action(flow, base, odp_actions);
commit_set_ether_addr_action(flow, base, odp_actions);
commit_vlan_action(flow, base, odp_actions);
commit_set_nw_action(flow, base, odp_actions);
/*
- * Copyright (c) 2009, 2010, 2011, 2012 Nicira, Inc.
+ * Copyright (c) 2009, 2010, 2011, 2012, 2013 Nicira, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
struct ds;
struct flow;
+struct flow_tnl;
struct nlattr;
struct ofpbuf;
struct simap;
struct flow *);
const char *odp_key_fitness_to_string(enum odp_key_fitness);
+void commit_odp_tunnel_action(const struct flow *, struct flow *base,
+ struct ofpbuf *odp_actions);
void commit_odp_actions(const struct flow *, struct flow *base,
struct ofpbuf *odp_actions);
\f
size_t odp_put_userspace_action(uint32_t pid,
const union user_action_cookie *,
struct ofpbuf *odp_actions);
+void odp_put_tunnel_action(const struct flow_tnl *tunnel,
+ struct ofpbuf *odp_actions);
/* Reasons why a subfacet might not be fast-pathable. */
enum slow_path_reason {
}
}
+ if (!flow->in_port) {
+ flow->in_port = OFPP_NONE;
+ }
+
exit:
free(copy);
/*
- * Copyright (c) 2009, 2010, 2011, 2012 Nicira, Inc.
+ * Copyright (c) 2009, 2010, 2011, 2012, 2013 Nicira, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
uint8_t
packet_get_tcp_flags(const struct ofpbuf *packet, const struct flow *flow)
{
- if ((flow->dl_type == htons(ETH_TYPE_IP) ||
- flow->dl_type == htons(ETH_TYPE_IPV6)) &&
- flow->nw_proto == IPPROTO_TCP && packet->l7) {
+ if (is_ip_any(flow) && flow->nw_proto == IPPROTO_TCP && packet->l7) {
const struct tcp_header *tcp = packet->l4;
return TCP_FLAGS(tcp->tcp_ctl);
} else {
#include <stdint.h>
#include <string.h>
#include "compiler.h"
+#include "flow.h"
#include "openvswitch/types.h"
#include "random.h"
#include "util.h"
struct ofpbuf;
struct ds;
-struct flow;
bool dpid_from_string(const char *s, uint64_t *dpidp);
return ipv6_addr_equals(mask, &in6addr_exact);
}
+static inline bool is_ip_any(const struct flow *flow)
+{
+ return flow->dl_type == htons(ETH_TYPE_IP)
+ || flow->dl_type == htons(ETH_TYPE_IPV6);
+}
+
void format_ipv6_addr(char *addr_str, const struct in6_addr *addr);
void print_ipv6_addr(struct ds *string, const struct in6_addr *addr);
void print_ipv6_masked(struct ds *string, const struct in6_addr *addr,
rconn_add_monitor(struct rconn *rc, struct vconn *vconn)
{
if (rc->n_monitors < ARRAY_SIZE(rc->monitors)) {
- int version = vconn_get_version(rc->vconn);
-
- /* Override the allowed versions of the snoop vconn so that
- * only the version of the controller connection is allowed.
- * This is because the snoop will see the same messages as the
- * controller */
- vconn_set_allowed_versions(vconn, 1u << version);
-
VLOG_INFO("new monitor connection from %s", vconn_get_name(vconn));
rc->monitors[rc->n_monitors++] = vconn;
} else {
route_table_run(void)
{
}
+
+void
+route_table_wait(void)
+{
+}
/*
- * Copyright (c) 2008, 2009, 2010, 2012 Nicira, Inc.
+ * Copyright (c) 2008, 2009, 2010, 2012, 2013 Nicira, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
struct vconn_class *class;
int state;
int error;
- uint32_t allowed_versions;
- uint32_t peer_versions;
- enum ofp_version version;
+
+ /* OpenFlow versions. */
+ uint32_t allowed_versions; /* Bitmap of versions we will accept. */
+ uint32_t peer_versions; /* Peer's bitmap of versions it will accept. */
+ enum ofp_version version; /* Negotiated version (or 0). */
+ bool recv_any_version; /* True to receive a message of any version. */
+
ovs_be32 remote_ip;
ovs_be16 remote_port;
ovs_be32 local_ip;
ovs_be16 local_port;
+
char *name;
};
return vconn->version ? vconn->version : -1;
}
+/* By default, a vconn accepts only OpenFlow messages whose version matches the
+ * one negotiated for the connection. A message received with a different
+ * version is an error that causes the vconn to drop the connection.
+ *
+ * This functions allows 'vconn' to accept messages with any OpenFlow version.
+ * This is useful in the special case where 'vconn' is used as an rconn
+ * "monitor" connection (see rconn_add_monitor()), that is, where 'vconn' is
+ * used as a target for mirroring OpenFlow messages for debugging and
+ * troubleshooting.
+ *
+ * This function should be called after a successful vconn_open() or
+ * pvconn_accept() but before the connection completes, that is, before
+ * vconn_connect() returns success. Otherwise, messages that arrive on 'vconn'
+ * beforehand with an unexpected version will the vconn to drop the
+ * connection. */
+void
+vconn_set_recv_any_version(struct vconn *vconn)
+{
+ vconn->recv_any_version = true;
+}
+
static void
vcs_connecting(struct vconn *vconn)
{
if (!retval) {
retval = do_recv(vconn, &msg);
}
- if (!retval) {
+ if (!retval && !vconn->recv_any_version) {
const struct ofp_header *oh = msg->data;
if (oh->version != vconn->version) {
enum ofptype type;
vconn_init(struct vconn *vconn, struct vconn_class *class, int connect_status,
const char *name, uint32_t allowed_versions)
{
+ memset(vconn, 0, sizeof *vconn);
vconn->class = class;
vconn->state = (connect_status == EAGAIN ? VCS_CONNECTING
: !connect_status ? VCS_SEND_HELLO
: VCS_DISCONNECTED);
vconn->error = connect_status;
- vconn->version = 0;
vconn->allowed_versions = allowed_versions;
- vconn->remote_ip = 0;
- vconn->remote_port = 0;
- vconn->local_ip = 0;
- vconn->local_port = 0;
vconn->name = xstrdup(name);
ovs_assert(vconn->state != VCS_CONNECTING || class->connect);
}
/*
- * Copyright (c) 2008, 2009, 2010, 2011, 2012 Nicira, Inc.
+ * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013 Nicira, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
struct vconn **vconnp);
void vconn_close(struct vconn *);
const char *vconn_get_name(const struct vconn *);
+
uint32_t vconn_get_allowed_versions(const struct vconn *vconn);
void vconn_set_allowed_versions(struct vconn *vconn,
uint32_t allowed_versions);
+int vconn_get_version(const struct vconn *);
+void vconn_set_recv_any_version(struct vconn *);
+
ovs_be32 vconn_get_remote_ip(const struct vconn *);
ovs_be16 vconn_get_remote_port(const struct vconn *);
ovs_be32 vconn_get_local_ip(const struct vconn *);
ovs_be16 vconn_get_local_port(const struct vconn *);
-int vconn_get_version(const struct vconn *);
+
int vconn_connect(struct vconn *);
int vconn_recv(struct vconn *, struct ofpbuf **);
int vconn_send(struct vconn *, struct ofpbuf *);
* to avoid missing log messages. */
VLOG_INFO("send failed (%s)", strerror(error));
} else if (error) {
- VLOG_ABORT("send failed (%s)", strerror(error));
+ VLOG_FATAL("send failed (%s)", strerror(error));
}
free(all_iovs);
/* Main process closed the IPC socket. Exit cleanly. */
break;
} else if (error != EAGAIN) {
- VLOG_ABORT("RPC receive failed (%s)", strerror(error));
+ VLOG_FATAL("RPC receive failed (%s)", strerror(error));
}
poll_fd_wait(server_sock, POLLIN);
ofproto/pktbuf.c \
ofproto/pktbuf.h \
ofproto/pinsched.c \
- ofproto/pinsched.h
+ ofproto/pinsched.h \
+ ofproto/tunnel.c \
+ ofproto/tunnel.h
MAN_FRAGMENTS += ofproto/ofproto-unixctl.man ofproto/ofproto-dpif-unixctl.man
#include "mac-learning.h"
#include "meta-flow.h"
#include "multipath.h"
+#include "netdev-vport.h"
#include "netdev.h"
#include "netlink.h"
#include "nx-match.h"
#include "simap.h"
#include "smap.h"
#include "timer.h"
+#include "tunnel.h"
#include "unaligned.h"
#include "unixctl.h"
#include "vlan-bitmap.h"
static void xlate_actions_for_side_effects(struct action_xlate_ctx *,
const struct ofpact *ofpacts,
size_t ofpacts_len);
+static void xlate_table_action(struct action_xlate_ctx *, uint16_t in_port,
+ uint8_t table_id, bool may_packet_in);
static size_t put_userspace_action(const struct ofproto_dpif *,
struct ofpbuf *odp_actions,
uint32_t bond_stable_id; /* stable_id to use as bond slave, or 0. */
bool may_enable; /* May be enabled in bonds. */
long long int carrier_seq; /* Carrier status changes. */
+ struct tnl_port *tnl_port; /* Tunnel handle, or null. */
/* Spanning tree. */
struct stp_port *stp_port; /* Spanning Tree Protocol, if any. */
COVERAGE_DEFINE(rev_flow_table);
COVERAGE_DEFINE(rev_inconsistency);
+/* Drop keys are odp flow keys which have drop flows installed in the kernel.
+ * These are datapath flows which have no associated ofproto, if they did we
+ * would use facets. */
+struct drop_key {
+ struct hmap_node hmap_node;
+ struct nlattr *key;
+ size_t key_len;
+};
+
/* All datapaths of a given type share a single dpif backer instance. */
struct dpif_backer {
char *type;
struct dpif *dpif;
struct timer next_expiration;
struct hmap odp_to_ofport_map; /* ODP port to ofport mapping. */
+
+ struct sset tnl_backers; /* Set of dpif ports backing tunnels. */
+
+ /* Facet revalidation flags applying to facets which use this backer. */
+ enum revalidate_reason need_revalidate; /* Revalidate every facet. */
+ struct tag_set revalidate_set; /* Revalidate only matching facets. */
+
+ struct hmap drop_keys; /* Set of dropped odp keys. */
};
/* All existing ofproto_backer instances, indexed by ofproto->up.type. */
static struct shash all_dpif_backers = SHASH_INITIALIZER(&all_dpif_backers);
+static void drop_key_clear(struct dpif_backer *);
static struct ofport_dpif *
odp_port_to_ofport(const struct dpif_backer *, uint32_t odp_port);
/* Revalidation. */
struct table_dpif tables[N_TABLES];
- enum revalidate_reason need_revalidate;
- struct tag_set revalidate_set;
/* Support for debugging async flow mods. */
struct list completions;
struct hmap vlandev_map; /* vlandev -> (realdev,vid). */
/* Ports. */
- struct sset ports; /* Set of port names. */
+ struct sset ports; /* Set of standard port names. */
+ struct sset ghost_ports; /* Ports with no datapath port. */
struct sset port_poll_set; /* Queued names for port_poll() reply. */
int port_poll_errno; /* Last errno for port_poll() reply. */
};
static void ofproto_trace(struct ofproto_dpif *, const struct flow *,
const struct ofpbuf *, ovs_be16 initial_tci,
struct ds *);
+static bool may_dpif_port_del(struct ofport_dpif *);
/* Packet processing. */
static void update_learning_table(struct ofproto_dpif *,
dpif_run(backer->dpif);
+ if (backer->need_revalidate
+ || !tag_set_is_empty(&backer->revalidate_set)) {
+ struct tag_set revalidate_set = backer->revalidate_set;
+ bool need_revalidate = backer->need_revalidate;
+ struct ofproto_dpif *ofproto;
+
+ switch (backer->need_revalidate) {
+ case REV_RECONFIGURE: COVERAGE_INC(rev_reconfigure); break;
+ case REV_STP: COVERAGE_INC(rev_stp); break;
+ case REV_PORT_TOGGLED: COVERAGE_INC(rev_port_toggled); break;
+ case REV_FLOW_TABLE: COVERAGE_INC(rev_flow_table); break;
+ case REV_INCONSISTENCY: COVERAGE_INC(rev_inconsistency); break;
+ }
+
+ if (backer->need_revalidate) {
+ /* Clear the drop_keys in case we should now be accepting some
+ * formerly dropped flows. */
+ drop_key_clear(backer);
+ }
+
+ HMAP_FOR_EACH (ofproto, all_ofproto_dpifs_node, &all_ofproto_dpifs) {
+ struct facet *facet;
+
+ if (ofproto->backer != backer) {
+ continue;
+ }
+
+ /* Clear the revalidation flags. */
+ tag_set_init(&backer->revalidate_set);
+ backer->need_revalidate = 0;
+
+ HMAP_FOR_EACH (facet, hmap_node, &ofproto->facets) {
+ if (need_revalidate
+ || tag_set_intersects(&revalidate_set, facet->tags)) {
+ facet_revalidate(facet);
+ }
+ }
+ }
+ }
+
if (timer_expired(&backer->next_expiration)) {
int delay = expire(backer);
timer_set_duration(&backer->next_expiration, delay);
goto next;
}
+ HMAP_FOR_EACH (ofproto, all_ofproto_dpifs_node,
+ &all_ofproto_dpifs) {
+ if (sset_contains(&ofproto->backer->tnl_backers, devname)) {
+ goto next;
+ }
+ }
+
ofproto = lookup_ofproto_dpif_by_port_name(devname);
if (dpif_port_query_by_name(backer->dpif, devname, &port)) {
/* The port was removed. If we know the datapath,
return;
}
+ drop_key_clear(backer);
+ hmap_destroy(&backer->drop_keys);
+
+ sset_destroy(&backer->tnl_backers);
hmap_destroy(&backer->odp_to_ofport_map);
node = shash_find(&all_dpif_backers, backer->type);
free(backer->type);
backer->type = xstrdup(type);
backer->refcount = 1;
hmap_init(&backer->odp_to_ofport_map);
+ hmap_init(&backer->drop_keys);
timer_set_duration(&backer->next_expiration, 1000);
+ backer->need_revalidate = 0;
+ sset_init(&backer->tnl_backers);
+ tag_set_init(&backer->revalidate_set);
*backerp = backer;
dpif_flow_flush(backer->dpif);
table->other_table = NULL;
table->basis = random_uint32();
}
- ofproto->need_revalidate = 0;
- tag_set_init(&ofproto->revalidate_set);
list_init(&ofproto->completions);
hmap_init(&ofproto->realdev_vid_map);
sset_init(&ofproto->ports);
+ sset_init(&ofproto->ghost_ports);
sset_init(&ofproto->port_poll_set);
ofproto->port_poll_errno = 0;
hmap_destroy(&ofproto->realdev_vid_map);
sset_destroy(&ofproto->ports);
+ sset_destroy(&ofproto->ghost_ports);
sset_destroy(&ofproto->port_poll_set);
close_dpif_backer(ofproto->backer);
}
stp_run(ofproto);
- mac_learning_run(ofproto->ml, &ofproto->revalidate_set);
-
- /* Now revalidate if there's anything to do. */
- if (ofproto->need_revalidate
- || !tag_set_is_empty(&ofproto->revalidate_set)) {
- struct tag_set revalidate_set = ofproto->revalidate_set;
- bool revalidate_all = ofproto->need_revalidate;
- struct facet *facet;
-
- switch (ofproto->need_revalidate) {
- case REV_RECONFIGURE: COVERAGE_INC(rev_reconfigure); break;
- case REV_STP: COVERAGE_INC(rev_stp); break;
- case REV_PORT_TOGGLED: COVERAGE_INC(rev_port_toggled); break;
- case REV_FLOW_TABLE: COVERAGE_INC(rev_flow_table); break;
- case REV_INCONSISTENCY: COVERAGE_INC(rev_inconsistency); break;
- }
-
- /* Clear the revalidation flags. */
- tag_set_init(&ofproto->revalidate_set);
- ofproto->need_revalidate = 0;
-
- HMAP_FOR_EACH (facet, hmap_node, &ofproto->facets) {
- if (revalidate_all
- || tag_set_intersects(&revalidate_set, facet->tags)) {
- facet_revalidate(facet);
- }
- }
- }
+ mac_learning_run(ofproto->ml, &ofproto->backer->revalidate_set);
/* Check the consistency of a random facet, to aid debugging. */
- if (!hmap_is_empty(&ofproto->facets) && !ofproto->need_revalidate) {
+ if (!hmap_is_empty(&ofproto->facets)
+ && !ofproto->backer->need_revalidate) {
struct facet *facet;
facet = CONTAINER_OF(hmap_random_node(&ofproto->facets),
struct facet, hmap_node);
- if (!tag_set_intersects(&ofproto->revalidate_set, facet->tags)) {
+ if (!tag_set_intersects(&ofproto->backer->revalidate_set,
+ facet->tags)) {
if (!facet_check_consistency(facet)) {
- ofproto->need_revalidate = REV_INCONSISTENCY;
+ ofproto->backer->need_revalidate = REV_INCONSISTENCY;
}
}
}
if (ofproto->sflow) {
dpif_sflow_wait(ofproto->sflow);
}
- if (!tag_set_is_empty(&ofproto->revalidate_set)) {
+ if (!tag_set_is_empty(&ofproto->backer->revalidate_set)) {
poll_immediate_wake();
}
HMAP_FOR_EACH (ofport, up.hmap_node, &ofproto->up.ports) {
}
mac_learning_wait(ofproto->ml);
stp_wait(ofproto);
- if (ofproto->need_revalidate) {
+ if (ofproto->backer->need_revalidate) {
/* Shouldn't happen, but if it does just go around again. */
VLOG_DBG_RL(&rl, "need revalidate in ofproto_wait_cb()");
poll_immediate_wake();
{
struct ofport_dpif *port = ofport_dpif_cast(port_);
struct ofproto_dpif *ofproto = ofproto_dpif_cast(port->up.ofproto);
+ const struct netdev *netdev = port->up.netdev;
struct dpif_port dpif_port;
int error;
- ofproto->need_revalidate = REV_RECONFIGURE;
+ ofproto->backer->need_revalidate = REV_RECONFIGURE;
port->bundle = NULL;
port->cfm = NULL;
port->tag = tag_create_random();
port->may_enable = true;
port->stp_port = NULL;
port->stp_state = STP_DISABLED;
+ port->tnl_port = NULL;
hmap_init(&port->priorities);
port->realdev_ofp_port = 0;
port->vlandev_vid = 0;
- port->carrier_seq = netdev_get_carrier_resets(port->up.netdev);
+ port->carrier_seq = netdev_get_carrier_resets(netdev);
+
+ if (netdev_vport_is_patch(netdev)) {
+ /* XXX By bailing out here, we don't do required sFlow work. */
+ port->odp_port = OVSP_NONE;
+ return 0;
+ }
error = dpif_port_query_by_name(ofproto->backer->dpif,
- netdev_get_name(port->up.netdev),
+ netdev_vport_get_dpif_port(netdev),
&dpif_port);
if (error) {
return error;
port->odp_port = dpif_port.port_no;
- /* Sanity-check that a mapping doesn't already exist. This
- * shouldn't happen. */
- if (odp_port_to_ofp_port(ofproto, port->odp_port) != OFPP_NONE) {
- VLOG_ERR("port %s already has an OpenFlow port number\n",
- dpif_port.name);
- return EBUSY;
- }
+ if (netdev_get_tunnel_config(netdev)) {
+ port->tnl_port = tnl_port_add(&port->up, port->odp_port);
+ } else {
+ /* Sanity-check that a mapping doesn't already exist. This
+ * shouldn't happen for non-tunnel ports. */
+ if (odp_port_to_ofp_port(ofproto, port->odp_port) != OFPP_NONE) {
+ VLOG_ERR("port %s already has an OpenFlow port number",
+ dpif_port.name);
+ return EBUSY;
+ }
- hmap_insert(&ofproto->backer->odp_to_ofport_map, &port->odp_port_node,
- hash_int(port->odp_port, 0));
+ hmap_insert(&ofproto->backer->odp_to_ofport_map, &port->odp_port_node,
+ hash_int(port->odp_port, 0));
+ }
if (ofproto->sflow) {
dpif_sflow_add_port(ofproto->sflow, port_, port->odp_port);
{
struct ofport_dpif *port = ofport_dpif_cast(port_);
struct ofproto_dpif *ofproto = ofproto_dpif_cast(port->up.ofproto);
+ const char *dp_port_name = netdev_vport_get_dpif_port(port->up.netdev);
const char *devname = netdev_get_name(port->up.netdev);
- if (dpif_port_exists(ofproto->backer->dpif, devname)) {
+ if (dpif_port_exists(ofproto->backer->dpif, dp_port_name)
+ && may_dpif_port_del(port)) {
/* The underlying device is still there, so delete it. This
* happens when the ofproto is being destroyed, since the caller
* assumes that removal of attached ports will happen as part of
* destruction. */
dpif_port_del(ofproto->backer->dpif, port->odp_port);
+ sset_find_and_delete(&ofproto->backer->tnl_backers, dp_port_name);
}
+ if (port->odp_port != OVSP_NONE && !port->tnl_port) {
+ hmap_remove(&ofproto->backer->odp_to_ofport_map, &port->odp_port_node);
+ }
+
+ tnl_port_del(port->tnl_port);
sset_find_and_delete(&ofproto->ports, devname);
- hmap_remove(&ofproto->backer->odp_to_ofport_map, &port->odp_port_node);
- ofproto->need_revalidate = REV_RECONFIGURE;
+ sset_find_and_delete(&ofproto->ghost_ports, devname);
+ ofproto->backer->need_revalidate = REV_RECONFIGURE;
bundle_remove(port_);
set_cfm(port_, NULL);
if (ofproto->sflow) {
if (changed & (OFPUTIL_PC_NO_RECV | OFPUTIL_PC_NO_RECV_STP |
OFPUTIL_PC_NO_FWD | OFPUTIL_PC_NO_FLOOD |
OFPUTIL_PC_NO_PACKET_IN)) {
- ofproto->need_revalidate = REV_RECONFIGURE;
+ ofproto->backer->need_revalidate = REV_RECONFIGURE;
if (changed & OFPUTIL_PC_NO_FLOOD && port->bundle) {
bundle_update(port->bundle);
HMAP_FOR_EACH (ofport, up.hmap_node, &ofproto->up.ports) {
dpif_sflow_add_port(ds, &ofport->up, ofport->odp_port);
}
- ofproto->need_revalidate = REV_RECONFIGURE;
+ ofproto->backer->need_revalidate = REV_RECONFIGURE;
}
dpif_sflow_set_options(ds, sflow_options);
} else {
if (ds) {
dpif_sflow_destroy(ds);
- ofproto->need_revalidate = REV_RECONFIGURE;
+ ofproto->backer->need_revalidate = REV_RECONFIGURE;
ofproto->sflow = NULL;
}
}
struct ofproto_dpif *ofproto;
ofproto = ofproto_dpif_cast(ofport->up.ofproto);
- ofproto->need_revalidate = REV_RECONFIGURE;
+ ofproto->backer->need_revalidate = REV_RECONFIGURE;
ofport->cfm = cfm_create(netdev_get_name(ofport->up.netdev));
}
/* Only revalidate flows if the configuration changed. */
if (!s != !ofproto->stp) {
- ofproto->need_revalidate = REV_RECONFIGURE;
+ ofproto->backer->need_revalidate = REV_RECONFIGURE;
}
if (s) {
if (stp_learn_in_state(ofport->stp_state)
!= stp_learn_in_state(state)) {
/* xxx Learning action flows should also be flushed. */
- mac_learning_flush(ofproto->ml, &ofproto->revalidate_set);
+ mac_learning_flush(ofproto->ml,
+ &ofproto->backer->revalidate_set);
}
fwd_change = stp_forward_in_state(ofport->stp_state)
!= stp_forward_in_state(state);
- ofproto->need_revalidate = REV_STP;
+ ofproto->backer->need_revalidate = REV_STP;
ofport->stp_state = state;
ofport->stp_state_entered = time_msec();
}
if (stp_check_and_reset_fdb_flush(ofproto->stp)) {
- mac_learning_flush(ofproto->ml, &ofproto->revalidate_set);
+ mac_learning_flush(ofproto->ml, &ofproto->backer->revalidate_set);
}
}
}
pdscp = xmalloc(sizeof *pdscp);
pdscp->priority = priority;
pdscp->dscp = dscp;
- ofproto->need_revalidate = REV_RECONFIGURE;
+ ofproto->backer->need_revalidate = REV_RECONFIGURE;
}
if (pdscp->dscp != dscp) {
pdscp->dscp = dscp;
- ofproto->need_revalidate = REV_RECONFIGURE;
+ ofproto->backer->need_revalidate = REV_RECONFIGURE;
}
hmap_insert(&new, &pdscp->hmap_node, hash_int(pdscp->priority, 0));
if (!hmap_is_empty(&ofport->priorities)) {
ofport_clear_priorities(ofport);
- ofproto->need_revalidate = REV_RECONFIGURE;
+ ofproto->backer->need_revalidate = REV_RECONFIGURE;
}
hmap_swap(&new, &ofport->priorities);
struct mac_learning *ml = ofproto->ml;
struct mac_entry *mac, *next_mac;
- ofproto->need_revalidate = REV_RECONFIGURE;
+ ofproto->backer->need_revalidate = REV_RECONFIGURE;
LIST_FOR_EACH_SAFE (mac, next_mac, lru_node, &ml->lrus) {
if (mac->port.p == bundle) {
if (all_ofprotos) {
e = mac_learning_lookup(o->ml, mac->mac, mac->vlan,
NULL);
if (e) {
- tag_set_add(&o->revalidate_set, e->tag);
mac_learning_expire(o->ml, e);
}
}
{
struct ofbundle *bundle = port->bundle;
- bundle->ofproto->need_revalidate = REV_RECONFIGURE;
+ bundle->ofproto->backer->need_revalidate = REV_RECONFIGURE;
list_remove(&port->bundle_node);
port->bundle = NULL;
}
if (port->bundle != bundle) {
- bundle->ofproto->need_revalidate = REV_RECONFIGURE;
+ bundle->ofproto->backer->need_revalidate = REV_RECONFIGURE;
if (port->bundle) {
bundle_del_port(port);
}
}
}
if (lacp) {
- port->bundle->ofproto->need_revalidate = REV_RECONFIGURE;
+ bundle->ofproto->backer->need_revalidate = REV_RECONFIGURE;
lacp_slave_register(bundle->lacp, port, lacp);
}
mirror_destroy(m);
} else if (hmapx_find_and_delete(&m->srcs, bundle)
|| hmapx_find_and_delete(&m->dsts, bundle)) {
- ofproto->need_revalidate = REV_RECONFIGURE;
+ ofproto->backer->need_revalidate = REV_RECONFIGURE;
}
}
}
/* LACP. */
if (s->lacp) {
if (!bundle->lacp) {
- ofproto->need_revalidate = REV_RECONFIGURE;
+ ofproto->backer->need_revalidate = REV_RECONFIGURE;
bundle->lacp = lacp_create();
}
lacp_configure(bundle->lacp, s->lacp);
bundle->ofproto->has_bonded_bundles = true;
if (bundle->bond) {
if (bond_reconfigure(bundle->bond, s->bond)) {
- ofproto->need_revalidate = REV_RECONFIGURE;
+ ofproto->backer->need_revalidate = REV_RECONFIGURE;
}
} else {
bundle->bond = bond_create(s->bond);
- ofproto->need_revalidate = REV_RECONFIGURE;
+ ofproto->backer->need_revalidate = REV_RECONFIGURE;
}
LIST_FOR_EACH (port, bundle_node, &bundle->ports) {
bond_slave_set_may_enable(bundle->bond, port, port->may_enable);
}
- bond_run(bundle->bond, &bundle->ofproto->revalidate_set,
+ bond_run(bundle->bond, &bundle->ofproto->backer->revalidate_set,
lacp_status(bundle->lacp));
if (bond_should_send_learning_packets(bundle->bond)) {
bundle_send_learning_packets(bundle);
}
}
- ofproto->need_revalidate = REV_RECONFIGURE;
+ ofproto->backer->need_revalidate = REV_RECONFIGURE;
ofproto->has_mirrors = true;
- mac_learning_flush(ofproto->ml, &ofproto->revalidate_set);
+ mac_learning_flush(ofproto->ml,
+ &ofproto->backer->revalidate_set);
mirror_update_dups(ofproto);
return 0;
}
ofproto = mirror->ofproto;
- ofproto->need_revalidate = REV_RECONFIGURE;
- mac_learning_flush(ofproto->ml, &ofproto->revalidate_set);
+ ofproto->backer->need_revalidate = REV_RECONFIGURE;
+ mac_learning_flush(ofproto->ml, &ofproto->backer->revalidate_set);
mirror_bit = MIRROR_MASK_C(1) << mirror->idx;
HMAP_FOR_EACH (bundle, hmap_node, &ofproto->bundles) {
{
struct ofproto_dpif *ofproto = ofproto_dpif_cast(ofproto_);
if (mac_learning_set_flood_vlans(ofproto->ml, flood_vlans)) {
- mac_learning_flush(ofproto->ml, &ofproto->revalidate_set);
+ mac_learning_flush(ofproto->ml, &ofproto->backer->revalidate_set);
}
return 0;
}
forward_bpdu_changed(struct ofproto *ofproto_)
{
struct ofproto_dpif *ofproto = ofproto_dpif_cast(ofproto_);
- ofproto->need_revalidate = REV_RECONFIGURE;
+ ofproto->backer->need_revalidate = REV_RECONFIGURE;
}
static void
ofproto_port->ofp_port = odp_port_to_ofp_port(ofproto, dpif_port->port_no);
}
+static struct ofport_dpif *
+ofport_get_peer(const struct ofport_dpif *ofport_dpif)
+{
+ const struct ofproto_dpif *ofproto;
+ const char *peer;
+
+ peer = netdev_vport_patch_peer(ofport_dpif->up.netdev);
+ if (!peer) {
+ return NULL;
+ }
+
+ HMAP_FOR_EACH (ofproto, all_ofproto_dpifs_node, &all_ofproto_dpifs) {
+ struct ofport *ofport;
+
+ ofport = shash_find_data(&ofproto->up.port_by_name, peer);
+ if (ofport && ofport->ofproto->ofproto_class == &ofproto_dpif_class) {
+ return ofport_dpif_cast(ofport);
+ }
+ }
+ return NULL;
+}
+
static void
port_run_fast(struct ofport_dpif *ofport)
{
ofport->carrier_seq = carrier_seq;
port_run_fast(ofport);
+
+ if (ofport->tnl_port
+ && tnl_port_reconfigure(&ofport->up, ofport->odp_port,
+ &ofport->tnl_port)) {
+ ofproto_dpif_cast(ofport->up.ofproto)->backer->need_revalidate = true;
+ }
+
if (ofport->cfm) {
int cfm_opup = cfm_get_opup(ofport->cfm);
struct ofproto_dpif *ofproto = ofproto_dpif_cast(ofport->up.ofproto);
if (ofproto->has_bundle_action) {
- ofproto->need_revalidate = REV_PORT_TOGGLED;
+ ofproto->backer->need_revalidate = REV_PORT_TOGGLED;
}
}
struct dpif_port dpif_port;
int error;
+ if (sset_contains(&ofproto->ghost_ports, devname)) {
+ const char *type = netdev_get_type_from_name(devname);
+
+ /* We may be called before ofproto->up.port_by_name is populated with
+ * the appropriate ofport. For this reason, we must get the name and
+ * type from the netdev layer directly. */
+ if (type) {
+ const struct ofport *ofport;
+
+ ofport = shash_find_data(&ofproto->up.port_by_name, devname);
+ ofproto_port->ofp_port = ofport ? ofport->ofp_port : OFPP_NONE;
+ ofproto_port->name = xstrdup(devname);
+ ofproto_port->type = xstrdup(type);
+ return 0;
+ }
+ return ENODEV;
+ }
+
if (!sset_contains(&ofproto->ports, devname)) {
return ENODEV;
}
port_add(struct ofproto *ofproto_, struct netdev *netdev)
{
struct ofproto_dpif *ofproto = ofproto_dpif_cast(ofproto_);
- uint32_t odp_port = UINT32_MAX;
- int error;
+ const char *dp_port_name = netdev_vport_get_dpif_port(netdev);
+ const char *devname = netdev_get_name(netdev);
- error = dpif_port_add(ofproto->backer->dpif, netdev, &odp_port);
- if (!error) {
- sset_add(&ofproto->ports, netdev_get_name(netdev));
+ if (netdev_vport_is_patch(netdev)) {
+ sset_add(&ofproto->ghost_ports, netdev_get_name(netdev));
+ return 0;
}
- return error;
+
+ if (!dpif_port_exists(ofproto->backer->dpif, dp_port_name)) {
+ int error = dpif_port_add(ofproto->backer->dpif, netdev, NULL);
+ if (error) {
+ return error;
+ }
+ }
+
+ if (netdev_get_tunnel_config(netdev)) {
+ sset_add(&ofproto->ghost_ports, devname);
+ sset_add(&ofproto->backer->tnl_backers, dp_port_name);
+ } else {
+ sset_add(&ofproto->ports, devname);
+ }
+ return 0;
+}
+
+/* Returns true if the odp_port backing 'ofport' may be deleted from the
+ * datapath. In most cases, this function simply returns true. However, for
+ * tunnels it's possible that multiple ofports use the same odp_port, in which
+ * case we need to keep the odp_port backer around until the last ofport is
+ * deleted. */
+static bool
+may_dpif_port_del(struct ofport_dpif *ofport)
+{
+ struct dpif_backer *backer = ofproto_dpif_cast(ofport->up.ofproto)->backer;
+ struct ofproto_dpif *ofproto_iter;
+
+ if (!ofport->tnl_port) {
+ return true;
+ }
+
+ HMAP_FOR_EACH (ofproto_iter, all_ofproto_dpifs_node, &all_ofproto_dpifs) {
+ struct ofport_dpif *iter;
+
+ if (backer != ofproto_iter->backer) {
+ continue;
+ }
+
+ HMAP_FOR_EACH (iter, up.hmap_node, &ofproto_iter->up.ports) {
+ if (ofport == iter) {
+ continue;
+ }
+
+ if (!strcmp(netdev_vport_get_dpif_port(ofport->up.netdev),
+ netdev_vport_get_dpif_port(iter->up.netdev))) {
+ return false;
+ }
+ }
+ }
+
+ return true;
}
static int
port_del(struct ofproto *ofproto_, uint16_t ofp_port)
{
struct ofproto_dpif *ofproto = ofproto_dpif_cast(ofproto_);
- uint32_t odp_port = ofp_port_to_odp_port(ofproto, ofp_port);
+ struct ofport_dpif *ofport = get_ofp_port(ofproto, ofp_port);
int error = 0;
- if (odp_port != OFPP_NONE) {
- error = dpif_port_del(ofproto->backer->dpif, odp_port);
+ if (!ofport) {
+ return 0;
}
- if (!error) {
- struct ofport_dpif *ofport = get_ofp_port(ofproto, ofp_port);
- if (ofport) {
+
+ sset_find_and_delete(&ofproto->ghost_ports,
+ netdev_get_name(ofport->up.netdev));
+ if (may_dpif_port_del(ofport)) {
+ error = dpif_port_del(ofproto->backer->dpif, ofport->odp_port);
+ if (!error) {
+ const char *dpif_port;
+
/* The caller is going to close ofport->up.netdev. If this is a
* bonded port, then the bond is using that netdev, so remove it
* from the bond. The client will need to reconfigure everything
* after deleting ports, so then the slave will get re-added. */
+ dpif_port = netdev_vport_get_dpif_port(ofport->up.netdev);
+ sset_find_and_delete(&ofproto->backer->tnl_backers, dpif_port);
bundle_remove(&ofport->up);
}
}
struct port_dump_state {
uint32_t bucket;
uint32_t offset;
+ bool ghost;
};
static int
port_dump_start(const struct ofproto *ofproto_ OVS_UNUSED, void **statep)
{
- struct port_dump_state *state;
-
- *statep = state = xmalloc(sizeof *state);
- state->bucket = 0;
- state->offset = 0;
+ *statep = xzalloc(sizeof(struct port_dump_state));
return 0;
}
static int
-port_dump_next(const struct ofproto *ofproto_ OVS_UNUSED, void *state_,
+port_dump_next(const struct ofproto *ofproto_, void *state_,
struct ofproto_port *port)
{
struct ofproto_dpif *ofproto = ofproto_dpif_cast(ofproto_);
struct port_dump_state *state = state_;
+ const struct sset *sset;
struct sset_node *node;
- while ((node = sset_at_position(&ofproto->ports, &state->bucket,
- &state->offset))) {
+ sset = state->ghost ? &ofproto->ghost_ports : &ofproto->ports;
+ while ((node = sset_at_position(sset, &state->bucket, &state->offset))) {
int error;
error = port_query_by_name(ofproto_, node->name, port);
}
}
+ if (!state->ghost) {
+ state->ghost = true;
+ state->bucket = 0;
+ state->offset = 0;
+ return port_dump_next(ofproto_, state_, port);
+ }
+
return EOF;
}
handle_flow_miss_with_facet(miss, facet, now, ops, n_ops);
}
+static struct drop_key *
+drop_key_lookup(const struct dpif_backer *backer, const struct nlattr *key,
+ size_t key_len)
+{
+ struct drop_key *drop_key;
+
+ HMAP_FOR_EACH_WITH_HASH (drop_key, hmap_node, hash_bytes(key, key_len, 0),
+ &backer->drop_keys) {
+ if (drop_key->key_len == key_len
+ && !memcmp(drop_key->key, key, key_len)) {
+ return drop_key;
+ }
+ }
+ return NULL;
+}
+
+static void
+drop_key_clear(struct dpif_backer *backer)
+{
+ static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 15);
+ struct drop_key *drop_key, *next;
+
+ HMAP_FOR_EACH_SAFE (drop_key, next, hmap_node, &backer->drop_keys) {
+ int error;
+
+ error = dpif_flow_del(backer->dpif, drop_key->key, drop_key->key_len,
+ NULL);
+ if (error && !VLOG_DROP_WARN(&rl)) {
+ struct ds ds = DS_EMPTY_INITIALIZER;
+ odp_flow_key_format(drop_key->key, drop_key->key_len, &ds);
+ VLOG_WARN("Failed to delete drop key (%s) (%s)", strerror(error),
+ ds_cstr(&ds));
+ ds_destroy(&ds);
+ }
+
+ hmap_remove(&backer->drop_keys, &drop_key->hmap_node);
+ free(drop_key->key);
+ free(drop_key);
+ }
+}
+
/* Given a datpath, packet, and flow metadata ('backer', 'packet', and 'key'
* respectively), populates 'flow' with the result of odp_flow_key_to_flow().
* Optionally, if nonnull, populates 'fitnessp' with the fitness of 'flow' as
* odp_flow_key_to_flow(). (This differs from the value returned in
* flow->vlan_tci only for packets received on VLAN splinters.)
*
+ * Similarly, this function also includes some logic to help with tunnels. It
+ * may modify 'flow' as necessary to make the tunneling implementation
+ * transparent to the upcall processing logic.
+ *
* Returns 0 if successful, ENODEV if the parsed flow has no associated ofport,
* or some other positive errno if there are other problems. */
static int
{
const struct ofport_dpif *port;
enum odp_key_fitness fitness;
- int error;
+ int error = ENODEV;
fitness = odp_flow_key_to_flow(key, key_len, flow);
if (fitness == ODP_FIT_ERROR) {
*odp_in_port = flow->in_port;
}
- port = odp_port_to_ofport(backer, flow->in_port);
- if (!port) {
- flow->in_port = OFPP_NONE;
- error = ofproto ? ENODEV : 0;
- goto exit;
- }
+ if (tnl_port_should_receive(flow)) {
+ const struct ofport *ofport = tnl_port_receive(flow);
+ if (!ofport) {
+ flow->in_port = OFPP_NONE;
+ goto exit;
+ }
+ port = ofport_dpif_cast(ofport);
- if (ofproto) {
- *ofproto = ofproto_dpif_cast(port->up.ofproto);
- }
+ /* We can't reproduce 'key' from 'flow'. */
+ fitness = fitness == ODP_FIT_PERFECT ? ODP_FIT_TOO_MUCH : fitness;
- flow->in_port = port->up.ofp_port;
- if (vsp_adjust_flow(ofproto_dpif_cast(port->up.ofproto), flow)) {
- if (packet) {
- /* Make the packet resemble the flow, so that it gets sent to an
- * OpenFlow controller properly, so that it looks correct for
- * sFlow, and so that flow_extract() will get the correct vlan_tci
- * if it is called on 'packet'.
- *
- * The allocated space inside 'packet' probably also contains
- * 'key', that is, both 'packet' and 'key' are probably part of a
- * struct dpif_upcall (see the large comment on that structure
- * definition), so pushing data on 'packet' is in general not a
- * good idea since it could overwrite 'key' or free it as a side
- * effect. However, it's OK in this special case because we know
- * that 'packet' is inside a Netlink attribute: pushing 4 bytes
- * will just overwrite the 4-byte "struct nlattr", which is fine
- * since we don't need that header anymore. */
- eth_push_vlan(packet, flow->vlan_tci);
- }
-
- /* Let the caller know that we can't reproduce 'key' from 'flow'. */
- if (fitness == ODP_FIT_PERFECT) {
- fitness = ODP_FIT_TOO_MUCH;
+ /* XXX: Since the tunnel module is not scoped per backer, it's
+ * theoretically possible that we'll receive an ofport belonging to an
+ * entirely different datapath. In practice, this can't happen because
+ * no platforms has two separate datapaths which each support
+ * tunneling. */
+ ovs_assert(ofproto_dpif_cast(port->up.ofproto)->backer == backer);
+ } else {
+ port = odp_port_to_ofport(backer, flow->in_port);
+ if (!port) {
+ flow->in_port = OFPP_NONE;
+ goto exit;
+ }
+
+ flow->in_port = port->up.ofp_port;
+ if (vsp_adjust_flow(ofproto_dpif_cast(port->up.ofproto), flow)) {
+ if (packet) {
+ /* Make the packet resemble the flow, so that it gets sent to
+ * an OpenFlow controller properly, so that it looks correct
+ * for sFlow, and so that flow_extract() will get the correct
+ * vlan_tci if it is called on 'packet'.
+ *
+ * The allocated space inside 'packet' probably also contains
+ * 'key', that is, both 'packet' and 'key' are probably part of
+ * a struct dpif_upcall (see the large comment on that
+ * structure definition), so pushing data on 'packet' is in
+ * general not a good idea since it could overwrite 'key' or
+ * free it as a side effect. However, it's OK in this special
+ * case because we know that 'packet' is inside a Netlink
+ * attribute: pushing 4 bytes will just overwrite the 4-byte
+ * "struct nlattr", which is fine since we don't need that
+ * header anymore. */
+ eth_push_vlan(packet, flow->vlan_tci);
+ }
+ /* We can't reproduce 'key' from 'flow'. */
+ fitness = fitness == ODP_FIT_PERFECT ? ODP_FIT_TOO_MUCH : fitness;
}
}
error = 0;
+ if (ofproto) {
+ *ofproto = ofproto_dpif_cast(port->up.ofproto);
+ }
+
exit:
if (fitnessp) {
*fitnessp = fitness;
upcall->key_len, &flow, &miss->key_fitness,
&ofproto, &odp_in_port, &miss->initial_tci);
if (error == ENODEV) {
+ struct drop_key *drop_key;
+
/* Received packet on port for which we couldn't associate
* an ofproto. This can happen if a port is removed while
* traffic is being received. Print a rate-limited message
- * in case it happens frequently. */
+ * in case it happens frequently. Install a drop flow so
+ * that future packets of the flow are inexpensively dropped
+ * in the kernel. */
VLOG_INFO_RL(&rl, "received packet on unassociated port %"PRIu32,
flow.in_port);
+
+ drop_key = drop_key_lookup(backer, upcall->key, upcall->key_len);
+ if (!drop_key) {
+ drop_key = xmalloc(sizeof *drop_key);
+ drop_key->key = xmemdup(upcall->key, upcall->key_len);
+ drop_key->key_len = upcall->key_len;
+
+ hmap_insert(&backer->drop_keys, &drop_key->hmap_node,
+ hash_bytes(drop_key->key, drop_key->key_len, 0));
+ dpif_flow_put(backer->dpif, DPIF_FP_CREATE | DPIF_FP_MODIFY,
+ drop_key->key, drop_key->key_len, NULL, 0, NULL);
+ }
+ continue;
}
if (error) {
continue;
struct ofproto_dpif *ofproto;
int max_idle = INT32_MAX;
+ /* Periodically clear out the drop keys in an effort to keep them
+ * relatively few. */
+ drop_key_clear(backer);
+
/* Update stats for each flow in the backer. */
update_stats(backer);
HMAP_FOR_EACH (ofproto, all_ofproto_dpifs_node, &all_ofproto_dpifs) {
- struct rule_dpif *rule, *next_rule;
- struct oftable *table;
+ struct rule *rule, *next_rule;
int dp_max_idle;
if (ofproto->backer != backer) {
/* Expire OpenFlow flows whose idle_timeout or hard_timeout
* has passed. */
- OFPROTO_FOR_EACH_TABLE (table, &ofproto->up) {
- struct cls_cursor cursor;
-
- cls_cursor_init(&cursor, &table->cls, NULL);
- CLS_CURSOR_FOR_EACH_SAFE (rule, next_rule, up.cr, &cursor) {
- rule_expire(rule);
- }
+ LIST_FOR_EACH_SAFE (rule, next_rule, expirable,
+ &ofproto->up.expirable) {
+ rule_expire(rule_dpif_cast(rule));
}
/* All outstanding data in existing flows has been accounted, so it's a
HMAP_FOR_EACH (bundle, hmap_node, &ofproto->bundles) {
if (bundle->bond) {
- bond_rebalance(bundle->bond, &ofproto->revalidate_set);
+ bond_rebalance(bundle->bond, &backer->revalidate_set);
}
}
}
while (dpif_flow_dump_next(&dump, &key, &key_len, NULL, NULL, &stats)) {
struct flow flow;
struct subfacet *subfacet;
- enum odp_key_fitness fitness;
struct ofproto_dpif *ofproto;
- struct ofport_dpif *port;
+ struct ofport_dpif *ofport;
uint32_t key_hash;
- fitness = odp_flow_key_to_flow(key, key_len, &flow);
- if (fitness == ODP_FIT_ERROR) {
+ if (ofproto_receive(backer, NULL, key, key_len, &flow, NULL, &ofproto,
+ NULL, NULL)) {
continue;
}
- port = odp_port_to_ofport(backer, flow.in_port);
- if (!port) {
- /* This flow is for a port for which we couldn't associate an
- * ofproto. This can happen if a port is removed while
- * traffic is being received. Ignore this flow, since it
- * will get timed out. */
- continue;
+ ofport = get_ofp_port(ofproto, flow.in_port);
+ if (ofport && ofport->tnl_port) {
+ netdev_vport_inc_rx(ofport->up.netdev, stats);
}
- ofproto = ofproto_dpif_cast(port->up.ofproto);
- flow.in_port = port->up.ofp_port;
key_hash = odp_flow_key_hash(key, key_len);
-
subfacet = subfacet_find(ofproto, key, key_len, key_hash, &flow);
switch (subfacet ? subfacet->path : SF_NOT_INSTALLED) {
case SF_FAST_PATH:
}
/* Executes, within 'ofproto', the 'n_actions' actions in 'actions' on
- * 'packet', which arrived on 'in_port'.
- *
- * Takes ownership of 'packet'. */
+ * 'packet', which arrived on 'in_port'. */
static bool
execute_odp_actions(struct ofproto_dpif *ofproto, const struct flow *flow,
const struct nlattr *odp_actions, size_t actions_len,
error = dpif_execute(ofproto->backer->dpif, key.data, key.size,
odp_actions, actions_len, packet);
-
- ofpbuf_delete(packet);
return !error;
}
facet = facet_find(ofproto, flow, hash);
if (facet
- && (ofproto->need_revalidate
- || tag_set_intersects(&ofproto->revalidate_set, facet->tags))) {
+ && (ofproto->backer->need_revalidate
+ || tag_set_intersects(&ofproto->backer->revalidate_set,
+ facet->tags))) {
facet_revalidate(facet);
}
}
}
-static enum ofperr
-rule_execute(struct rule *rule_, const struct flow *flow,
- struct ofpbuf *packet)
+static void
+rule_dpif_execute(struct rule_dpif *rule, const struct flow *flow,
+ struct ofpbuf *packet)
{
- struct rule_dpif *rule = rule_dpif_cast(rule_);
struct ofproto_dpif *ofproto = ofproto_dpif_cast(rule->up.ofproto);
struct dpif_flow_stats stats;
odp_actions.size, packet);
ofpbuf_uninit(&odp_actions);
+}
+static enum ofperr
+rule_execute(struct rule *rule, const struct flow *flow,
+ struct ofpbuf *packet)
+{
+ rule_dpif_execute(rule_dpif_cast(rule), flow, packet);
+ ofpbuf_delete(packet);
return 0;
}
send_packet(const struct ofport_dpif *ofport, struct ofpbuf *packet)
{
const struct ofproto_dpif *ofproto = ofproto_dpif_cast(ofport->up.ofproto);
+ uint64_t odp_actions_stub[1024 / 8];
struct ofpbuf key, odp_actions;
struct odputil_keybuf keybuf;
uint32_t odp_port;
int error;
flow_extract(packet, 0, 0, NULL, OFPP_LOCAL, &flow);
- odp_port = vsp_realdev_to_vlandev(ofproto, ofport->odp_port,
- flow.vlan_tci);
- if (odp_port != ofport->odp_port) {
- eth_pop_vlan(packet);
- flow.vlan_tci = htons(0);
+ if (netdev_vport_is_patch(ofport->up.netdev)) {
+ struct ofproto_dpif *peer_ofproto;
+ struct dpif_flow_stats stats;
+ struct ofport_dpif *peer;
+ struct rule_dpif *rule;
+
+ peer = ofport_get_peer(ofport);
+ if (!peer) {
+ return ENODEV;
+ }
+
+ dpif_flow_stats_extract(&flow, packet, time_msec(), &stats);
+ netdev_vport_inc_tx(ofport->up.netdev, &stats);
+ netdev_vport_inc_rx(peer->up.netdev, &stats);
+
+ flow.in_port = peer->up.ofp_port;
+ peer_ofproto = ofproto_dpif_cast(peer->up.ofproto);
+ rule = rule_dpif_lookup(peer_ofproto, &flow);
+ rule_dpif_execute(rule, &flow, packet);
+
+ return 0;
+ }
+
+ ofpbuf_use_stub(&odp_actions, odp_actions_stub, sizeof odp_actions_stub);
+
+ if (ofport->tnl_port) {
+ struct dpif_flow_stats stats;
+
+ odp_port = tnl_port_send(ofport->tnl_port, &flow);
+ if (odp_port == OVSP_NONE) {
+ return ENODEV;
+ }
+
+ dpif_flow_stats_extract(&flow, packet, time_msec(), &stats);
+ netdev_vport_inc_tx(ofport->up.netdev, &stats);
+ odp_put_tunnel_action(&flow.tunnel, &odp_actions);
+ } else {
+ odp_port = vsp_realdev_to_vlandev(ofproto, ofport->odp_port,
+ flow.vlan_tci);
+ if (odp_port != ofport->odp_port) {
+ eth_pop_vlan(packet);
+ flow.vlan_tci = htons(0);
+ }
}
ofpbuf_use_stack(&key, &keybuf, sizeof keybuf);
odp_flow_key_from_flow(&key, &flow,
ofp_port_to_odp_port(ofproto, flow.in_port));
- ofpbuf_init(&odp_actions, 32);
compose_sflow_action(ofproto, &odp_actions, &flow, odp_port);
nl_msg_put_u32(&odp_actions, OVS_ACTION_ATTR_OUTPUT, odp_port);
bool check_stp)
{
const struct ofport_dpif *ofport = get_ofp_port(ctx->ofproto, ofp_port);
- uint32_t odp_port = ofp_port_to_odp_port(ctx->ofproto, ofp_port);
ovs_be16 flow_vlan_tci = ctx->flow.vlan_tci;
+ ovs_be64 flow_tun_id = ctx->flow.tunnel.tun_id;
uint8_t flow_nw_tos = ctx->flow.nw_tos;
struct priority_to_dscp *pdscp;
- uint32_t out_port;
+ uint32_t out_port, odp_port;
+
+ /* If 'struct flow' gets additional metadata, we'll need to zero it out
+ * before traversing a patch port. */
+ BUILD_ASSERT_DECL(FLOW_WC_SEQ == 18);
if (!ofport) {
xlate_report(ctx, "Nonexistent output port");
return;
}
+ if (netdev_vport_is_patch(ofport->up.netdev)) {
+ struct ofport_dpif *peer = ofport_get_peer(ofport);
+ struct flow old_flow = ctx->flow;
+ const struct ofproto_dpif *peer_ofproto;
+
+ if (!peer) {
+ xlate_report(ctx, "Nonexistent patch port peer");
+ return;
+ }
+
+ peer_ofproto = ofproto_dpif_cast(peer->up.ofproto);
+ if (peer_ofproto->backer != ctx->ofproto->backer) {
+ xlate_report(ctx, "Patch port peer on a different datapath");
+ return;
+ }
+
+ ctx->ofproto = ofproto_dpif_cast(peer->up.ofproto);
+ ctx->flow.in_port = peer->up.ofp_port;
+ ctx->flow.metadata = htonll(0);
+ memset(&ctx->flow.tunnel, 0, sizeof ctx->flow.tunnel);
+ memset(ctx->flow.regs, 0, sizeof ctx->flow.regs);
+ xlate_table_action(ctx, ctx->flow.in_port, 0, true);
+ ctx->flow = old_flow;
+ ctx->ofproto = ofproto_dpif_cast(ofport->up.ofproto);
+
+ if (ctx->resubmit_stats) {
+ netdev_vport_inc_tx(ofport->up.netdev, ctx->resubmit_stats);
+ netdev_vport_inc_rx(peer->up.netdev, ctx->resubmit_stats);
+ }
+
+ return;
+ }
+
pdscp = get_priority(ofport, ctx->flow.skb_priority);
if (pdscp) {
ctx->flow.nw_tos &= ~IP_DSCP_MASK;
ctx->flow.nw_tos |= pdscp->dscp;
}
- out_port = vsp_realdev_to_vlandev(ctx->ofproto, odp_port,
- ctx->flow.vlan_tci);
- if (out_port != odp_port) {
- ctx->flow.vlan_tci = htons(0);
+ odp_port = ofp_port_to_odp_port(ctx->ofproto, ofp_port);
+ if (ofport->tnl_port) {
+ odp_port = tnl_port_send(ofport->tnl_port, &ctx->flow);
+ if (odp_port == OVSP_NONE) {
+ xlate_report(ctx, "Tunneling decided against output");
+ return;
+ }
+
+ if (ctx->resubmit_stats) {
+ netdev_vport_inc_tx(ofport->up.netdev, ctx->resubmit_stats);
+ }
+ out_port = odp_port;
+ commit_odp_tunnel_action(&ctx->flow, &ctx->base_flow,
+ ctx->odp_actions);
+ } else {
+ out_port = vsp_realdev_to_vlandev(ctx->ofproto, odp_port,
+ ctx->flow.vlan_tci);
+ if (out_port != odp_port) {
+ ctx->flow.vlan_tci = htons(0);
+ }
}
commit_odp_actions(&ctx->flow, &ctx->base_flow, ctx->odp_actions);
nl_msg_put_u32(ctx->odp_actions, OVS_ACTION_ATTR_OUTPUT, out_port);
ctx->sflow_odp_port = odp_port;
ctx->sflow_n_outputs++;
ctx->nf_output_iface = ofp_port;
+ ctx->flow.tunnel.tun_id = flow_tun_id;
ctx->flow.vlan_tci = flow_vlan_tci;
ctx->flow.nw_tos = flow_nw_tos;
}
in_bundle->name, vlan);
mac->port.p = in_bundle;
- tag_set_add(&ofproto->revalidate_set,
+ tag_set_add(&ofproto->backer->revalidate_set,
mac_learning_changed(ofproto->ml, mac));
}
}
if (table->catchall_table != catchall || table->other_table != other) {
table->catchall_table = catchall;
table->other_table = other;
- ofproto->need_revalidate = REV_FLOW_TABLE;
+ ofproto->backer->need_revalidate = REV_FLOW_TABLE;
}
}
table_update_taggable(ofproto, rule->up.table_id);
- if (!ofproto->need_revalidate) {
+ if (!ofproto->backer->need_revalidate) {
struct table_dpif *table = &ofproto->tables[rule->up.table_id];
if (table->other_table && rule->tag) {
- tag_set_add(&ofproto->revalidate_set, rule->tag);
+ tag_set_add(&ofproto->backer->revalidate_set, rule->tag);
} else {
- ofproto->need_revalidate = REV_FLOW_TABLE;
+ ofproto->backer->need_revalidate = REV_FLOW_TABLE;
}
}
}
enum ofp_config_flags frag_handling)
{
struct ofproto_dpif *ofproto = ofproto_dpif_cast(ofproto_);
-
if (frag_handling != OFPC_FRAG_REASM) {
- ofproto->need_revalidate = REV_RECONFIGURE;
+ ofproto->backer->need_revalidate = REV_RECONFIGURE;
return true;
} else {
return false;
unixctl_command_reply_error(conn, "no such bridge");
return;
}
- mac_learning_flush(ofproto->ml, &ofproto->revalidate_set);
+ mac_learning_flush(ofproto->ml, &ofproto->backer->revalidate_set);
} else {
HMAP_FOR_EACH (ofproto, all_ofproto_dpifs_node, &all_ofproto_dpifs) {
- mac_learning_flush(ofproto->ml, &ofproto->revalidate_set);
+ mac_learning_flush(ofproto->ml, &ofproto->backer->revalidate_set);
}
}
}
}
if (errors) {
- ofproto->need_revalidate = REV_INCONSISTENCY;
+ ofproto->backer->need_revalidate = REV_INCONSISTENCY;
}
if (errors) {
struct ofport *ofport = node->data;
const char *name = netdev_get_name(ofport->netdev);
const char *type = netdev_get_type(ofport->netdev);
+ uint32_t odp_port;
+
+ ds_put_format(ds, "\t%s %u/", name, ofport->ofp_port);
+
+ odp_port = ofp_port_to_odp_port(ofproto, ofport->ofp_port);
+ if (odp_port != OVSP_NONE) {
+ ds_put_format(ds, "%"PRIu32":", odp_port);
+ } else {
+ ds_put_cstr(ds, "none:");
+ }
- ds_put_format(ds, "\t%s %u/%u:", name, ofport->ofp_port,
- ofp_port_to_odp_port(ofproto, ofport->ofp_port));
if (strcmp(type, "system")) {
struct netdev *netdev;
int error;
return;
}
+ update_stats(ofproto->backer);
+
HMAP_FOR_EACH (subfacet, hmap_node, &ofproto->subfacets) {
struct odputil_keybuf keybuf;
struct ofpbuf key;
return 0;
}
- ofproto->need_revalidate = REV_RECONFIGURE;
+ ofproto->backer->need_revalidate = REV_RECONFIGURE;
if (ofport->realdev_ofp_port) {
vsp_remove(ofport);
struct oftable *tables;
int n_tables;
+ /* Optimisation for flow expiry.
+ * These flows should all be present in tables. */
+ struct list expirable; /* Expirable 'struct rule"s in all tables. */
+
/* OpenFlow connections. */
struct connmgr *connmgr;
enum nx_flow_monitor_flags monitor_flags;
uint64_t add_seqno; /* Sequence number when added. */
uint64_t modify_seqno; /* Sequence number when changed. */
+
+ /* Optimisation for flow expiry. */
+ struct list expirable; /* In ofproto's 'expirable' list if this rule
+ * is expirable, otherwise empty. */
};
static inline struct rule *
ofproto->max_ports = OFPP_MAX;
ofproto->tables = NULL;
ofproto->n_tables = 0;
+ list_init(&ofproto->expirable);
ofproto->connmgr = connmgr_create(ofproto, datapath_name, datapath_name);
ofproto->state = S_OPENFLOW;
list_init(&ofproto->pending);
static void
dealloc_ofp_port(const struct ofproto *ofproto, uint16_t ofp_port)
{
- bitmap_set0(ofproto->ofp_port_ids, ofp_port);
+ if (ofp_port < ofproto->max_ports) {
+ bitmap_set0(ofproto->ofp_port_ids, ofp_port);
+ }
}
/* Opens and returns a netdev for 'ofproto_port' in 'ofproto', or a null
rule->ofpacts_len = fm->ofpacts_len;
rule->evictable = true;
rule->eviction_group = NULL;
+ list_init(&rule->expirable);
rule->monitor_flags = 0;
rule->add_seqno = 0;
rule->modify_seqno = 0;
new_cookie = (fm->new_cookie != htonll(UINT64_MAX)
? fm->new_cookie
: rule->flow_cookie);
- if (!actions_changed && new_cookie == rule->flow_cookie) {
- /* No change at all. */
- continue;
- }
op = ofoperation_create(group, rule, OFOPERATION_MODIFY, 0);
rule->flow_cookie = new_cookie;
LIST_FOR_EACH_SAFE (op, next_op, group_node, &group->ops) {
struct rule *rule = op->rule;
- if (!op->error && !ofproto_rule_is_hidden(rule)) {
+ /* We generally want to report the change to active OpenFlow flow
+ monitors (e.g. NXST_FLOW_MONITOR). There are three exceptions:
+
+ - The operation failed.
+
+ - The affected rule is not visible to controllers.
+
+ - The operation's only effect was to update rule->modified. */
+ if (!(op->error
+ || ofproto_rule_is_hidden(rule)
+ || (op->type == OFOPERATION_MODIFY
+ && op->ofpacts
+ && rule->flow_cookie == op->flow_cookie))) {
/* Check that we can just cast from ofoperation_type to
* nx_flow_update_event. */
BUILD_ASSERT_DECL((enum nx_flow_update_event) OFOPERATION_ADD
classifier_remove(&table->cls, &rule->cr);
eviction_group_remove_rule(rule);
+ if (!list_is_empty(&rule->expirable)) {
+ list_remove(&rule->expirable);
+ }
}
/* Inserts 'rule' into its oftable. Removes any existing rule from 'rule''s
struct ofproto *ofproto = rule->ofproto;
struct oftable *table = &ofproto->tables[rule->table_id];
struct rule *victim;
+ bool may_expire = rule->hard_timeout || rule->idle_timeout;
+
+ if (may_expire) {
+ list_insert(&ofproto->expirable, &rule->expirable);
+ }
victim = rule_from_cls_rule(classifier_replace(&table->cls, &rule->cr));
if (victim) {
+ if (!list_is_empty(&victim->expirable)) {
+ list_remove(&victim->expirable);
+ }
eviction_group_remove_rule(victim);
}
eviction_group_add_rule(rule);
--- /dev/null
+/* Copyright (c) 2013 Nicira, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License. */
+
+#include <config.h>
+#include "tunnel.h"
+
+#include <errno.h>
+
+#include "ofproto/ofproto-provider.h"
+#include "byte-order.h"
+#include "dynamic-string.h"
+#include "hash.h"
+#include "hmap.h"
+#include "netdev-vport.h"
+#include "odp-util.h"
+#include "packets.h"
+#include "smap.h"
+#include "socket-util.h"
+#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;
+ bool in_key_flow;
+};
+
+struct tnl_port {
+ struct hmap_node match_node;
+
+ const struct ofport *ofport;
+ unsigned int netdev_seq;
+ struct tnl_match match;
+};
+
+static struct hmap tnl_match_map = HMAP_INITIALIZER(&tnl_match_map);
+
+/* 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 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 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)
+{
+ const struct netdev_tunnel_config *cfg;
+ struct tnl_port *existing_port;
+ struct tnl_port *tnl_port;
+
+ cfg = netdev_get_tunnel_config(ofport->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->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.in_key_flow = cfg->in_key_flow;
+ tnl_port->match.odp_port = odp_port;
+
+ existing_port = tnl_find_exact(&tnl_port->match);
+ if (existing_port) {
+ if (warn) {
+ struct ds ds = DS_EMPTY_INITIALIZER;
+ tnl_match_fmt(&tnl_port->match, &ds);
+ VLOG_WARN("%s: attempting to add tunnel port with same config as "
+ "port '%s' (%s)", tnl_port_get_name(tnl_port),
+ tnl_port_get_name(existing_port), ds_cstr(&ds));
+ ds_destroy(&ds);
+ free(tnl_port);
+ }
+ return &void_tnl_port;
+ }
+
+ hmap_insert(&tnl_match_map, &tnl_port->match_node,
+ tnl_hash(&tnl_port->match));
+ tnl_port_mod_log(tnl_port, "adding");
+ return tnl_port;
+}
+
+/* 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)
+{
+ return tnl_port_add__(ofport, odp_port, true);
+}
+
+/* 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
+ * tnl_port_add(). */
+bool
+tnl_port_reconfigure(const struct ofport *ofport, uint32_t odp_port,
+ struct tnl_port **tnl_portp)
+{
+ struct tnl_port *tnl_port = *tnl_portp;
+
+ 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
+ || tnl_port->match.odp_port != odp_port
+ || tnl_port->netdev_seq != netdev_change_seq(ofport->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;
+ }
+ return false;
+}
+
+/* Removes 'tnl_port' from the module. */
+void
+tnl_port_del(struct tnl_port *tnl_port)
+{
+ if (tnl_port && tnl_port != &void_tnl_port) {
+ tnl_port_mod_log(tnl_port, "removing");
+ hmap_remove(&tnl_match_map, &tnl_port->match_node);
+ 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.
+ *
+ * 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)
+{
+ char *pre_flow_str = NULL;
+ struct tnl_port *tnl_port;
+ struct tnl_match match;
+
+ memset(&match, 0, sizeof match);
+ match.odp_port = flow->in_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;
+
+ tnl_port = tnl_find(&match);
+ 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;
+ }
+
+ 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);
+ VLOG_DBG("flow received\n"
+ "%s"
+ " pre: %s\n"
+ "post: %s",
+ tnl_str, pre_flow_str, post_flow_str);
+ free(tnl_str);
+ free(pre_flow_str);
+ free(post_flow_str);
+ }
+ return tnl_port->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
+ * shouldn't occur. */
+uint32_t
+tnl_port_send(const struct tnl_port *tnl_port, struct flow *flow)
+{
+ const struct netdev_tunnel_config *cfg;
+ char *pre_flow_str = NULL;
+
+ if (tnl_port == &void_tnl_port) {
+ return OVSP_NONE;
+ }
+
+ cfg = netdev_get_tunnel_config(tnl_port->ofport->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->out_key_flow) {
+ flow->tunnel.tun_id = cfg->out_key;
+ }
+
+ if (cfg->ttl_inherit && is_ip_any(flow)) {
+ flow->tunnel.ip_ttl = flow->nw_ttl;
+ } else {
+ flow->tunnel.ip_ttl = cfg->ttl;
+ }
+
+ if (cfg->tos_inherit && is_ip_any(flow)) {
+ flow->tunnel.ip_tos = flow->nw_tos & IP_DSCP_MASK;
+ } else {
+ flow->tunnel.ip_tos = cfg->tos;
+ }
+
+ if ((flow->nw_tos & IP_ECN_MASK) == IP_ECN_CE) {
+ flow->tunnel.ip_tos |= IP_ECN_ECT_0;
+ } else {
+ flow->tunnel.ip_tos |= flow->nw_tos & IP_ECN_MASK;
+ }
+
+ flow->tunnel.flags = (cfg->dont_fragment ? FLOW_TNL_F_DONT_FRAGMENT : 0)
+ | (cfg->csum ? FLOW_TNL_F_CSUM : 0)
+ | (cfg->out_key_present ? FLOW_TNL_F_KEY : 0);
+
+ if (pre_flow_str) {
+ char *post_flow_str = flow_to_string(flow);
+ char *tnl_str = tnl_port_fmt(tnl_port);
+ VLOG_DBG("flow sent\n"
+ "%s"
+ " pre: %s\n"
+ "post: %s",
+ tnl_str, pre_flow_str, post_flow_str);
+ free(tnl_str);
+ free(pre_flow_str);
+ free(post_flow_str);
+ }
+
+ return tnl_port->match.odp_port;
+}
+
+static uint32_t
+tnl_hash(struct tnl_match *match)
+{
+ BUILD_ASSERT_DECL(sizeof *match % sizeof(uint32_t) == 0);
+ return hash_words((uint32_t *) match, sizeof *match / sizeof(uint32_t), 0);
+}
+
+static struct tnl_port *
+tnl_find_exact(struct tnl_match *match)
+{
+ struct tnl_port *tnl_port;
+
+ HMAP_FOR_EACH_WITH_HASH (tnl_port, match_node, tnl_hash(match),
+ &tnl_match_map) {
+ if (!memcmp(match, &tnl_port->match, sizeof *match)) {
+ return tnl_port;
+ }
+ }
+ return NULL;
+}
+
+static struct tnl_port *
+tnl_find(struct tnl_match *match_)
+{
+ 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;
+ }
+ }
+
+ /* remote_ip, in_key */
+ match.ip_src = 0;
+ tnl_port = tnl_find_exact(&match);
+ if (tnl_port) {
+ return tnl_port;
+ }
+ 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;
+ }
+
+ /* 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;
+ }
+
+ /* multicast remote_ip */
+ match.in_key = 0;
+ match.in_key_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)
+{
+ ds_put_format(ds, IP_FMT"->"IP_FMT, IP_ARGS(match->ip_src),
+ IP_ARGS(match->ip_dst));
+
+ 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));
+ }
+ }
+
+ ds_put_format(ds, ", dp port=%"PRIu32, match->odp_port);
+}
+
+static void
+tnl_port_mod_log(const struct tnl_port *tnl_port, const char *action)
+{
+ if (VLOG_IS_DBG_ENABLED()) {
+ struct ds ds = DS_EMPTY_INITIALIZER;
+
+ tnl_match_fmt(&tnl_port->match, &ds);
+ VLOG_DBG("%s tunnel port %s (%s)", action, tnl_port_get_name(tnl_port),
+ ds_cstr(&ds));
+ ds_destroy(&ds);
+ }
+}
+
+static char *
+tnl_port_fmt(const struct tnl_port *tnl_port)
+{
+ const struct netdev_tunnel_config *cfg =
+ netdev_get_tunnel_config(tnl_port->ofport->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));
+ tnl_match_fmt(&tnl_port->match, &ds);
+
+ if (cfg->out_key != cfg->in_key ||
+ cfg->out_key_present != cfg->in_key_present ||
+ cfg->out_key_flow != cfg->in_key_flow) {
+ ds_put_cstr(&ds, ", out_key=");
+ if (!cfg->out_key_present) {
+ ds_put_cstr(&ds, "none");
+ } else if (cfg->out_key_flow) {
+ ds_put_cstr(&ds, "flow");
+ } else {
+ ds_put_format(&ds, "%#"PRIx64, ntohll(cfg->out_key));
+ }
+ }
+
+ if (cfg->ttl_inherit) {
+ ds_put_cstr(&ds, ", ttl=inherit");
+ } else {
+ ds_put_format(&ds, ", ttl=%"PRIu8, cfg->ttl);
+ }
+
+ if (cfg->tos_inherit) {
+ ds_put_cstr(&ds, ", tos=inherit");
+ } else if (cfg->tos) {
+ ds_put_format(&ds, ", tos=%#"PRIx8, cfg->tos);
+ }
+
+ if (!cfg->dont_fragment) {
+ ds_put_cstr(&ds, ", df=false");
+ }
+
+ if (cfg->csum) {
+ ds_put_cstr(&ds, ", csum=true");
+ }
+
+ ds_put_cstr(&ds, ")\n");
+
+ return ds_steal_cstr(&ds);
+}
+
+static const char *
+tnl_port_get_name(const struct tnl_port *tnl_port)
+{
+ return netdev_get_name(tnl_port->ofport->netdev);
+}
--- /dev/null
+/* Copyright (c) 2013 Nicira, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef TUNNEL_H
+#define TUNNEL_H 1
+
+#include <stdbool.h>
+#include <stdint.h>
+#include "flow.h"
+
+/* Tunnel port emulation layer.
+ *
+ * These functions emulate tunnel virtual ports based on the outer
+ * header information from the kernel. */
+
+struct ofport;
+struct tnl_port;
+
+bool tnl_port_reconfigure(const struct ofport *, uint32_t odp_port,
+ struct tnl_port **);
+
+struct tnl_port *tnl_port_add(const struct ofport *, uint32_t odp_port);
+void tnl_port_del(struct tnl_port *);
+
+const struct ofport *tnl_port_receive(struct flow *);
+uint32_t tnl_port_send(const struct tnl_port *, struct flow *);
+
+/* Returns true if 'flow' should be submitted to tnl_port_receive(). */
+static inline bool
+tnl_port_should_receive(const struct flow *flow)
+{
+ return flow->tunnel.ip_dst != 0;
+}
+
+#endif /* tunnel.h */
-/* Copyright (c) 2009, 2010, 2011, 2012 Nicira, Inc.
+/* Copyright (c) 2009, 2010, 2011, 2012, 2013 Nicira, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
ovsdb_jsonrpc_session_unlock_all(s);
jsonrpc_session_close(s->js);
list_remove(&s->node);
- ovsdb_session_destroy(&s->up);
s->remote->server->n_sessions--;
ovsdb_session_destroy(&s->up);
free(s);
OTHERSCRIPT="/etc/sysconfig/network-scripts/ifdown-eth"
fi
+[ -f /var/lock/subsys/openvswitch ] || /sbin/service openvswitch start
+
case "$TYPE" in
OVSBridge)
${OTHERSCRIPT} ${CONFIG} $2
OTHERSCRIPT="/etc/sysconfig/network-scripts/ifup-eth"
fi
+check_recursion()
+{
+ [ -n "${UPPEDSTACK}" ] && for _r in ${UPPEDSTACK}; do
+ [ "$_r" = "$1" ] && return 1
+ done
+
+ return 0
+}
+
+if [ -z "${UPPEDSTACK}" ]; then
+ UPPEDSTACK="${DEVICE}"
+fi
+
+[ -n "${OVSREQUIRES}" ] && for _i in ${OVSREQUIRES}; do
+ if ( check_recursion "$_i" ); then
+ UPPEDSTACK="${UPPEDSTACK} $_i" /sbin/ifup "$_i"
+ fi
+done
+
+[ -f /var/lock/subsys/openvswitch ] || /sbin/service openvswitch start
+
case "$TYPE" in
OVSBridge)
ovs-vsctl -t ${TIMEOUT} -- --may-exist add-br "$DEVICE" $OVS_OPTIONS ${OVS_EXTRA+-- $OVS_EXTRA}
tests/jsonrpc.at \
tests/jsonrpc-py.at \
tests/timeval.at \
+ tests/tunnel.at \
tests/lockfile.at \
tests/reconnect.at \
tests/ofproto-dpif.at \
])
OVS_VSWITCHD_STOP
AT_CLEANUP
+
+AT_SETUP([lacp - negotiation])
+# Create bond0 on br0 with interfaces p0 and p1
+# and bond1 on br1 with interfaces p2 and p3
+# with p0 patched to p2 and p1 patched to p3.
+OVS_VSWITCHD_START(
+ [add-bond br0 bond0 p0 p1 bond_mode=balance-tcp lacp=active \
+ other-config:lacp-time=fast \
+ other-config:bond-rebalance-interval=0 -- \
+ set interface p0 type=patch options:peer=p2 ofport_request=1 -- \
+ set interface p1 type=patch options:peer=p3 ofport_request=2 -- \
+ add-br br1 -- \
+ set bridge br1 other-config:hwaddr=aa:66:aa:66:00:00 -- \
+ set bridge br1 datapath-type=dummy other-config:datapath-id=1234 \
+ fail-mode=secure -- \
+ add-bond br1 bond1 p2 p3 bond_mode=balance-tcp lacp=active \
+ other-config:lacp-time=fast \
+ other-config:bond-rebalance-interval=0 -- \
+ set interface p2 type=patch options:peer=p0 ofport_request=3 -- \
+ set interface p3 type=patch options:peer=p1 ofport_request=4 --])
+
+AT_CHECK([ovs-appctl netdev-dummy/set-admin-state up], 0, [OK
+])
+
+# Wait for up to 5 (simulated) seconds, until LACP negotiation finishes.
+i=0
+while :; do
+ ovs-appctl lacp/show bond0 > bond0
+ AT_CAPTURE_FILE([bond0])
+ ovs-appctl lacp/show bond1 > bond1
+ AT_CAPTURE_FILE([bond1])
+ if grep negotiated bond0 && grep negotiated bond1; then
+ if grep expired bond0 || grep expired bond1; then
+ :
+ else
+ break
+ fi
+ fi
+ i=`expr $i + 1`
+ if test $i = 50; then
+ AT_FAIL_IF([:])
+ fi
+ ovs-appctl time/warp 100
+done
+
+# Now check the correctly negotiated configuration.
+AT_CHECK(
+ [ovs-appctl lacp/show bond0
+ovs-appctl lacp/show bond1
+ovs-appctl bond/show bond0
+ovs-appctl bond/show bond1], [0], [stdout])
+AT_CHECK([sed '/active slave/d' stdout], [0], [dnl
+---- bond0 ----
+ status: active negotiated
+ sys_id: aa:55:aa:55:00:00
+ sys_priority: 65534
+ aggregation key: 2
+ lacp_time: fast
+
+slave: p0: current attached
+ port_id: 1
+ port_priority: 65535
+ may_enable: true
+
+ actor sys_id: aa:55:aa:55:00:00
+ actor sys_priority: 65534
+ actor port_id: 1
+ actor port_priority: 65535
+ actor key: 2
+ actor state: activity timeout aggregation synchronized collecting distributing
+
+ partner sys_id: aa:66:aa:66:00:00
+ partner sys_priority: 65534
+ partner port_id: 3
+ partner port_priority: 65535
+ partner key: 4
+ partner state: activity timeout aggregation synchronized collecting distributing
+
+slave: p1: current attached
+ port_id: 2
+ port_priority: 65535
+ may_enable: true
+
+ actor sys_id: aa:55:aa:55:00:00
+ actor sys_priority: 65534
+ actor port_id: 2
+ actor port_priority: 65535
+ actor key: 2
+ actor state: activity timeout aggregation synchronized collecting distributing
+
+ partner sys_id: aa:66:aa:66:00:00
+ partner sys_priority: 65534
+ partner port_id: 4
+ partner port_priority: 65535
+ partner key: 4
+ partner state: activity timeout aggregation synchronized collecting distributing
+---- bond1 ----
+ status: active negotiated
+ sys_id: aa:66:aa:66:00:00
+ sys_priority: 65534
+ aggregation key: 4
+ lacp_time: fast
+
+slave: p2: current attached
+ port_id: 3
+ port_priority: 65535
+ may_enable: true
+
+ actor sys_id: aa:66:aa:66:00:00
+ actor sys_priority: 65534
+ actor port_id: 3
+ actor port_priority: 65535
+ actor key: 4
+ actor state: activity timeout aggregation synchronized collecting distributing
+
+ partner sys_id: aa:55:aa:55:00:00
+ partner sys_priority: 65534
+ partner port_id: 1
+ partner port_priority: 65535
+ partner key: 2
+ partner state: activity timeout aggregation synchronized collecting distributing
+
+slave: p3: current attached
+ port_id: 4
+ port_priority: 65535
+ may_enable: true
+
+ actor sys_id: aa:66:aa:66:00:00
+ actor sys_priority: 65534
+ actor port_id: 4
+ actor port_priority: 65535
+ actor key: 4
+ actor state: activity timeout aggregation synchronized collecting distributing
+
+ partner sys_id: aa:55:aa:55:00:00
+ partner sys_priority: 65534
+ partner port_id: 2
+ partner port_priority: 65535
+ partner key: 2
+ partner state: activity timeout aggregation synchronized collecting distributing
+---- bond0 ----
+bond_mode: balance-tcp
+bond-hash-basis: 0
+updelay: 0 ms
+downdelay: 0 ms
+lacp_status: negotiated
+
+slave p0: enabled
+ may_enable: true
+
+slave p1: enabled
+ may_enable: true
+
+---- bond1 ----
+bond_mode: balance-tcp
+bond-hash-basis: 0
+updelay: 0 ms
+downdelay: 0 ms
+lacp_status: negotiated
+
+slave p2: enabled
+ may_enable: true
+
+slave p3: enabled
+ may_enable: true
+
+])
+AT_CHECK([grep 'active slave' stdout], [0], [dnl
+ active slave
+ active slave
+])
+
+# Redirect the patch link between p0 and p2 so that no packets get
+# back and forth across them anymore. Then wait 4 simulated
+# seconds. The LACP state should become "expired" for p0 and p2.
+AT_CHECK([ovs-vsctl \
+-- add-port br0 null0 -- set int null0 type=patch options:peer=p2 -- set int p2 options:peer=null0 \
+-- add-port br1 null1 -- set int null1 type=patch options:peer=p0 -- set int p0 options:peer=null1])
+
+for i in `seq 0 40`; do ovs-appctl time/warp 100; done
+AT_CHECK(
+ [ovs-appctl lacp/show bond0
+ovs-appctl lacp/show bond1
+ovs-appctl bond/show bond0
+ovs-appctl bond/show bond1], [0], [dnl
+---- bond0 ----
+ status: active negotiated
+ sys_id: aa:55:aa:55:00:00
+ sys_priority: 65534
+ aggregation key: 2
+ lacp_time: fast
+
+slave: p0: expired attached
+ port_id: 1
+ port_priority: 65535
+ may_enable: false
+
+ actor sys_id: aa:55:aa:55:00:00
+ actor sys_priority: 65534
+ actor port_id: 1
+ actor port_priority: 65535
+ actor key: 2
+ actor state: activity timeout aggregation synchronized collecting distributing expired
+
+ partner sys_id: aa:66:aa:66:00:00
+ partner sys_priority: 65534
+ partner port_id: 3
+ partner port_priority: 65535
+ partner key: 4
+ partner state: activity timeout aggregation collecting distributing
+
+slave: p1: current attached
+ port_id: 2
+ port_priority: 65535
+ may_enable: true
+
+ actor sys_id: aa:55:aa:55:00:00
+ actor sys_priority: 65534
+ actor port_id: 2
+ actor port_priority: 65535
+ actor key: 2
+ actor state: activity timeout aggregation synchronized collecting distributing
+
+ partner sys_id: aa:66:aa:66:00:00
+ partner sys_priority: 65534
+ partner port_id: 4
+ partner port_priority: 65535
+ partner key: 4
+ partner state: activity timeout aggregation synchronized collecting distributing
+---- bond1 ----
+ status: active negotiated
+ sys_id: aa:66:aa:66:00:00
+ sys_priority: 65534
+ aggregation key: 4
+ lacp_time: fast
+
+slave: p2: expired attached
+ port_id: 3
+ port_priority: 65535
+ may_enable: false
+
+ actor sys_id: aa:66:aa:66:00:00
+ actor sys_priority: 65534
+ actor port_id: 3
+ actor port_priority: 65535
+ actor key: 4
+ actor state: activity timeout aggregation synchronized collecting distributing expired
+
+ partner sys_id: aa:55:aa:55:00:00
+ partner sys_priority: 65534
+ partner port_id: 1
+ partner port_priority: 65535
+ partner key: 2
+ partner state: activity timeout aggregation collecting distributing
+
+slave: p3: current attached
+ port_id: 4
+ port_priority: 65535
+ may_enable: true
+
+ actor sys_id: aa:66:aa:66:00:00
+ actor sys_priority: 65534
+ actor port_id: 4
+ actor port_priority: 65535
+ actor key: 4
+ actor state: activity timeout aggregation synchronized collecting distributing
+
+ partner sys_id: aa:55:aa:55:00:00
+ partner sys_priority: 65534
+ partner port_id: 2
+ partner port_priority: 65535
+ partner key: 2
+ partner state: activity timeout aggregation synchronized collecting distributing
+---- bond0 ----
+bond_mode: balance-tcp
+bond-hash-basis: 0
+updelay: 0 ms
+downdelay: 0 ms
+lacp_status: negotiated
+
+slave p0: disabled
+ may_enable: false
+
+slave p1: enabled
+ active slave
+ may_enable: true
+
+---- bond1 ----
+bond_mode: balance-tcp
+bond-hash-basis: 0
+updelay: 0 ms
+downdelay: 0 ms
+lacp_status: negotiated
+
+slave p2: disabled
+ may_enable: false
+
+slave p3: enabled
+ active slave
+ may_enable: true
+
+])
+
+# Wait 4 more simulated seconds. The LACP state should become
+# "defaulted" for p0 and p2.
+for i in `seq 0 40`; do ovs-appctl time/warp 100; done
+AT_CHECK(
+ [ovs-appctl lacp/show bond0
+ovs-appctl lacp/show bond1
+ovs-appctl bond/show bond0
+ovs-appctl bond/show bond1], [0], [dnl
+---- bond0 ----
+ status: active negotiated
+ sys_id: aa:55:aa:55:00:00
+ sys_priority: 65534
+ aggregation key: 2
+ lacp_time: fast
+
+slave: p0: defaulted detached
+ port_id: 1
+ port_priority: 65535
+ may_enable: false
+
+ actor sys_id: aa:55:aa:55:00:00
+ actor sys_priority: 65534
+ actor port_id: 1
+ actor port_priority: 65535
+ actor key: 2
+ actor state: activity timeout aggregation defaulted
+
+ partner sys_id: 00:00:00:00:00:00
+ partner sys_priority: 0
+ partner port_id: 0
+ partner port_priority: 0
+ partner key: 0
+ partner state:
+
+slave: p1: current attached
+ port_id: 2
+ port_priority: 65535
+ may_enable: true
+
+ actor sys_id: aa:55:aa:55:00:00
+ actor sys_priority: 65534
+ actor port_id: 2
+ actor port_priority: 65535
+ actor key: 2
+ actor state: activity timeout aggregation synchronized collecting distributing
+
+ partner sys_id: aa:66:aa:66:00:00
+ partner sys_priority: 65534
+ partner port_id: 4
+ partner port_priority: 65535
+ partner key: 4
+ partner state: activity timeout aggregation synchronized collecting distributing
+---- bond1 ----
+ status: active negotiated
+ sys_id: aa:66:aa:66:00:00
+ sys_priority: 65534
+ aggregation key: 4
+ lacp_time: fast
+
+slave: p2: defaulted detached
+ port_id: 3
+ port_priority: 65535
+ may_enable: false
+
+ actor sys_id: aa:66:aa:66:00:00
+ actor sys_priority: 65534
+ actor port_id: 3
+ actor port_priority: 65535
+ actor key: 4
+ actor state: activity timeout aggregation defaulted
+
+ partner sys_id: 00:00:00:00:00:00
+ partner sys_priority: 0
+ partner port_id: 0
+ partner port_priority: 0
+ partner key: 0
+ partner state:
+
+slave: p3: current attached
+ port_id: 4
+ port_priority: 65535
+ may_enable: true
+
+ actor sys_id: aa:66:aa:66:00:00
+ actor sys_priority: 65534
+ actor port_id: 4
+ actor port_priority: 65535
+ actor key: 4
+ actor state: activity timeout aggregation synchronized collecting distributing
+
+ partner sys_id: aa:55:aa:55:00:00
+ partner sys_priority: 65534
+ partner port_id: 2
+ partner port_priority: 65535
+ partner key: 2
+ partner state: activity timeout aggregation synchronized collecting distributing
+---- bond0 ----
+bond_mode: balance-tcp
+bond-hash-basis: 0
+updelay: 0 ms
+downdelay: 0 ms
+lacp_status: negotiated
+
+slave p0: disabled
+ may_enable: false
+
+slave p1: enabled
+ active slave
+ may_enable: true
+
+---- bond1 ----
+bond_mode: balance-tcp
+bond-hash-basis: 0
+updelay: 0 ms
+downdelay: 0 ms
+lacp_status: negotiated
+
+slave p2: disabled
+ may_enable: false
+
+slave p3: enabled
+ active slave
+ may_enable: true
+
+])
+OVS_VSWITCHD_STOP
+AT_CLEANUP
OVS_VSWITCHD_STOP
AT_CLEANUP
+dnl This test checks that repeated uses of a "learn" action cause the
+dnl modified time of the learned flow to advance. Otherwise, the
+dnl learned flow will expire after its hard timeout even though it's
+dnl supposed to be refreshed. (The expiration can be hard to see since
+dnl it gets re-learned again the next time a packet appears, but
+dnl sometimes the expiration can cause temporary flooding etc.)
+AT_SETUP([learning action - learn refreshes hard_age])
+OVS_VSWITCHD_START(
+ [add-port br0 p1 -- set Interface p1 type=dummy ofport_request=1 -- \
+ add-port br0 p2 -- set Interface p2 type=dummy ofport_request=2 -- \
+ add-port br0 p3 -- set Interface p3 type=dummy ofport_request=3])
+
+ovs-appctl time/stop
+
+# Set up flow table for MAC learning.
+AT_DATA([flows.txt], [[
+table=0 actions=learn(table=1, hard_timeout=10, NXM_OF_ETH_DST[]=NXM_OF_ETH_SRC[], output:NXM_OF_IN_PORT[]), resubmit(,1)
+table=1 priority=0 actions=flood
+]])
+AT_CHECK([ovs-ofctl add-flows br0 flows.txt])
+
+# Trace an ICMP packet arriving on port 3, to create a MAC learning entry.
+flow="in_port(3),eth(src=50:54:00:00:00:07,dst=50:54:00:00:00:05),eth_type(0x0800),ipv4(src=192.168.0.2,dst=192.168.0.1,proto=1,tos=0,ttl=64,frag=no),icmp(type=0,code=0)"
+AT_CHECK([ovs-appctl ofproto/trace br0 "$flow" -generate], [0], [stdout])
+actual=`tail -1 stdout | sed 's/Datapath actions: //'`
+
+expected="1,2,100"
+AT_CHECK([ovs-dpctl normalize-actions "$flow" "$expected"], [0], [stdout])
+mv stdout expout
+AT_CHECK([ovs-dpctl normalize-actions "$flow" "$actual"], [0], [expout])
+
+# Check that the MAC learning entry appeared.
+AT_CHECK([ovs-ofctl dump-flows br0 table=1 | ofctl_strip | sort], [0], [dnl
+ table=1, hard_timeout=10, dl_dst=50:54:00:00:00:07 actions=output:3
+ table=1, priority=0 actions=FLOOD
+NXST_FLOW reply:
+])
+
+# For 25 seconds, make sure that the MAC learning entry doesn't
+# disappear as long as we refresh it every second.
+for i in 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25; do
+ ovs-appctl time/warp 1000
+ AT_CHECK([ovs-appctl ofproto/trace br0 "$flow" -generate], [0], [stdout])
+
+ # Check that the entry is there.
+ AT_CHECK([ovs-ofctl dump-flows br0 table=1], [0], [stdout])
+ AT_CHECK([ofctl_strip < stdout | sort], [0], [dnl
+ table=1, hard_timeout=10, dl_dst=50:54:00:00:00:07 actions=output:3
+ table=1, priority=0 actions=FLOOD
+NXST_FLOW reply:
+])
+
+ if test $i != 1; then
+ # Check that hard_age has appeared. We need to do this separately
+ # from the above check because ofctl_strip removes it. dump-flows
+ # only prints hard_age when it is different from the flow's duration
+ # (that is, the number of seconds from the time it was created),
+ # so we only check for it after we've refreshed the flow once.
+ AT_CHECK([grep dl_dst=50:54:00:00:00:07 stdout | grep -c hard_age],
+ [0], [1
+])
+ fi
+done
+
+# Make sure that 15 seconds without refreshing makes the flow time out.
+ovs-appctl time/warp 5000
+ovs-appctl time/warp 5000
+ovs-appctl time/warp 5000
+ AT_CHECK([ovs-ofctl dump-flows br0 table=1 | ofctl_strip | sort], [0], [dnl
+ table=1, priority=0 actions=FLOOD
+NXST_FLOW reply:
+])
+OVS_VSWITCHD_STOP
+AT_CLEANUP
+
AT_SETUP([learning action - TCPv4 port learning])
OVS_VSWITCHD_START(
[add-port br0 p1 -- set Interface p1 type=dummy -- \
AT_CHECK([ovs-ofctl add-flow br0 action=normal])
# "in_port" defaults to OFPP_NONE if it's not specified.
-flow="eth(src=50:54:00:00:00:05,dst=50:54:00:00:00:07),eth_type(0x0800),ipv4(src=192.168.0.1,dst=192.168.0.2,proto=1,tos=0,ttl=128,frag=no),icmp(type=8,code=0)"
+flow="icmp,dl_src=50:54:00:00:00:05,dl_dst=50:54:00:00:00:07,nw_src=192.168.0.1,nw_dst=192.168.0.2,icmp_type=8,icmp_code=0"
AT_CHECK([ovs-appctl ofproto/trace br0 "$flow"], [0], [stdout])
-actual=`tail -1 stdout | sed 's/Datapath actions: //'`
-
-expected="1,2,100"
-AT_CHECK([ovs-dpctl normalize-actions "$flow" "$expected"], [0], [stdout])
-mv stdout expout
-AT_CHECK([ovs-dpctl normalize-actions "$flow" "$actual"], [0], [expout])
+AT_CHECK([tail -1 stdout | sed 's/Datapath actions: //' | tr "," "\n" | sort -n], [0], [dnl
+1
+2
+100
+])
OVS_VSWITCHD_STOP
AT_CLEANUP
AT_SETUP([ofproto-dpif - DSCP])
OVS_VSWITCHD_START([add-port br0 p1 -- set Interface p1 type=dummy])
+ADD_OF_PORTS([br0], [9])
AT_DATA([flows.txt], [dnl
actions=output:65534,enqueue:1:1,enqueue:1:2,enqueue:1:2,enqueue:1:1,output:1,mod_nw_tos:0,output:1,output:65534
])
OVS_VSWITCHD_STOP
AT_CLEANUP
-AT_SETUP([ofproto-dpif - set_tunnel])
-OVS_VSWITCHD_START
-ADD_OF_PORTS([br0], [1], [2], [3], [4], [5], [90])
-AT_DATA([flows.txt], [dnl
-in_port=90 actions=resubmit:1,resubmit:2,resubmit:3,resubmit:4,resubmit:5
-in_port=1 actions=set_tunnel:1,output:1
-in_port=2 actions=set_tunnel:1,output:2
-in_port=3 actions=set_tunnel:2,set_tunnel:3,output:3
-in_port=4 actions=set_tunnel:4,set_tunnel:3,output:4
-in_port=5 actions=set_tunnel:5
-])
-AT_CHECK([ovs-ofctl add-flows br0 flows.txt])
-AT_CHECK([ovs-appctl ofproto/trace br0 'tun_id(0x1),in_port(90),eth(src=50:54:00:00:00:05,dst=50:54:00:00:00:07),eth_type(0x0800),ipv4(src=192.168.0.1,dst=192.168.0.2,proto=1,tos=0,ttl=128,frag=no),icmp(type=8,code=0)'], [0], [stdout])
-AT_CHECK([tail -1 stdout], [0],
- [Datapath actions: set(tun_id(0x1)),1,2,set(tun_id(0x3)),3,4
-])
-OVS_VSWITCHD_STOP
-AT_CLEANUP
-
AT_SETUP([ofproto-dpif - controller])
OVS_VSWITCHD_START([dnl
add-port br0 p1 -- set Interface p1 type=dummy
AT_SETUP([ofproto-dpif - fragment handling])
OVS_VSWITCHD_START
-ADD_OF_PORTS([br0], [1], [2], [3], [4], [5], [6])
+ADD_OF_PORTS([br0], [1], [2], [3], [4], [5], [6], [90])
AT_DATA([flows.txt], [dnl
priority=75 tcp ip_frag=no tp_dst=80 actions=output:1
priority=75 tcp ip_frag=first tp_dst=80 actions=output:2
AT_CHECK([ovs-ofctl add-flow br0 action=output:1])
# "in_port" defaults to OFPP_NONE if it's not specified.
-flow="eth(src=50:54:00:00:00:05,dst=50:54:00:00:00:07),eth_type(0x0800),ipv4(src=192.168.0.1,dst=192.168.0.2,proto=1,tos=0,ttl=128,frag=no),icmp(type=8,code=0)"
+flow="icmp,dl_src=50:54:00:00:00:05,dl_dst=50:54:00:00:00:07,nw_src=192.168.0.1,nw_dst=192.168.0.2,nw_ttl=128,icmp_type=8,icmp_code=0"
AT_CHECK([ovs-appctl ofproto/trace br0 "$flow"], [0], [stdout])
AT_CHECK_UNQUOTED([tail -1 stdout], [0],
[Datapath actions: 1,2
OVS_VSWITCHD_STOP
AT_CLEANUP
+
+AT_SETUP([ofproto-dpif - patch ports])
+OVS_VSWITCHD_START([add-br br1 \
+-- set bridge br1 datapath-type=dummy fail-mode=secure \
+-- add-port br1 pbr1 -- set int pbr1 type=patch options:peer=pbr0 \
+-- add-port br0 pbr0 -- set int pbr0 type=patch options:peer=pbr1])
+
+ADD_OF_PORTS([br0], [2])
+ADD_OF_PORTS([br1], [3])
+
+AT_CHECK([ovs-ofctl add-flow br0 actions=LOCAL,output:1,output:2])
+AT_CHECK([ovs-ofctl add-flow br1 actions=LOCAL,output:1,output:3])
+
+for i in $(seq 1 10); do
+ ovs-appctl netdev-dummy/receive br0 'in_port(100),eth(src=50:54:00:00:00:05,dst=50:54:00:00:00:07),eth_type(0x0800),ipv4(src=192.168.0.1,dst=192.168.0.2,proto=1,tos=0,ttl=64,frag=no),icmp(type=8,code=0)'
+done
+
+for i in $(seq 1 5); do
+ ovs-appctl netdev-dummy/receive br1 'in_port(101),eth(src=50:54:00:00:00:07,dst=50:54:00:00:00:05),eth_type(0x0800),ipv4(src=192.168.0.2,dst=192.168.0.1,proto=1,tos=0,ttl=64,frag=no),icmp(type=8,code=0)'
+done
+
+AT_CHECK([ovs-appctl dpif/show], [0], [dnl
+br0 (dummy@ovs-dummy):
+ lookups: hit:13 missed:2 lost:0
+ flows: 1
+ br0 65534/100: (dummy)
+ p2 2/2: (dummy)
+ pbr0 1/none: (patch: peer=pbr1)
+br1 (dummy@ovs-dummy):
+ lookups: hit:13 missed:2 lost:0
+ flows: 1
+ br1 65534/101: (dummy)
+ p3 3/3: (dummy)
+ pbr1 1/none: (patch: peer=pbr0)
+])
+
+AT_CHECK([ovs-appctl dpif/dump-flows br0 | STRIP_USED], [0], [dnl
+in_port(100),eth(src=50:54:00:00:00:05,dst=50:54:00:00:00:07),eth_type(0x0800),ipv4(src=192.168.0.1,dst=192.168.0.2,proto=1,tos=0,ttl=64,frag=no),icmp(type=8,code=0), packets:9, bytes:540, used:0.0s, actions:101,3,2
+]),
+AT_CHECK([ovs-appctl dpif/dump-flows br1 | STRIP_USED], [0], [dnl
+in_port(101),eth(src=50:54:00:00:00:07,dst=50:54:00:00:00:05),eth_type(0x0800),ipv4(src=192.168.0.2,dst=192.168.0.1,proto=1,tos=0,ttl=64,frag=no),icmp(type=8,code=0), packets:4, bytes:240, used:0.0s, actions:100,2,3
+])
+
+AT_CHECK([ovs-ofctl dump-ports br0 pbr0], [0], [dnl
+OFPST_PORT reply (xid=0x4): 1 ports
+ port 1: rx pkts=5, bytes=300, drop=0, errs=0, frame=0, over=0, crc=0
+ tx pkts=10, bytes=600, drop=0, errs=0, coll=0
+])
+
+AT_CHECK([ovs-ofctl dump-ports br1 pbr1], [0], [dnl
+OFPST_PORT reply (xid=0x4): 1 ports
+ port 1: rx pkts=10, bytes=600, drop=0, errs=0, frame=0, over=0, crc=0
+ tx pkts=5, bytes=300, drop=0, errs=0, coll=0
+])
+
+OVS_VSWITCHD_STOP
+AT_CLEANUP
m4_include([tests/jsonrpc.at])
m4_include([tests/jsonrpc-py.at])
m4_include([tests/timeval.at])
+m4_include([tests/tunnel.at])
m4_include([tests/lockfile.at])
m4_include([tests/reconnect.at])
m4_include([tests/ofproto.at])
--- /dev/null
+AT_BANNER([tunnel])
+
+AT_SETUP([tunnel - input])
+OVS_VSWITCHD_START([add-port br0 p1 -- set Interface p1 type=gre \
+ options:remote_ip=1.1.1.1 ofport_request=1\
+ -- add-port br0 p2 -- set Interface p2 type=gre \
+ options:local_ip=2.2.2.2 options:remote_ip=1.1.1.1 \
+ ofport_request=2 \
+ -- add-port br0 p3 -- set Interface p3 type=gre \
+ options:remote_ip=2.2.2.2 ofport_request=3])
+AT_DATA([flows.txt], [dnl
+actions=IN_PORT
+])
+
+AT_CHECK([ovs-ofctl add-flows br0 flows.txt])
+
+AT_CHECK([ovs-appctl dpif/show], [0], [dnl
+br0 (dummy@ovs-dummy):
+ lookups: hit:0 missed:0 lost:0
+ flows: 0
+ br0 65534/100: (dummy)
+ p1 1/1: (gre: remote_ip=1.1.1.1)
+ p2 2/1: (gre: local_ip=2.2.2.2, remote_ip=1.1.1.1)
+ p3 3/1: (gre: remote_ip=2.2.2.2)
+])
+
+dnl remote_ip
+AT_CHECK([ovs-appctl ofproto/trace br0 'tunnel(tun_id=0x0,src=1.1.1.1,dst=1.2.3.4,tos=0x0,ttl=64,flags()),in_port(1),eth(src=50:54:00:00:00:05,dst=50:54:00:00:00:07),eth_type(0x0800),ipv4(src=192.168.0.1,dst=192.168.0.2,proto=6,tos=0,ttl=64,frag=no),tcp(src=8,dst=9)'], [0], [stdout])
+AT_CHECK([tail -1 stdout], [0],
+ [Datapath actions: set(tunnel(tun_id=0x0,src=0.0.0.0,dst=1.1.1.1,tos=0x0,ttl=64,flags(df))),1
+])
+
+dnl local_ip, remote_ip
+AT_CHECK([ovs-appctl ofproto/trace br0 'tunnel(tun_id=0x0,src=1.1.1.1,dst=2.2.2.2,tos=0x0,ttl=64,flags()),in_port(1),eth(src=50:54:00:00:00:05,dst=50:54:00:00:00:07),eth_type(0x0800),ipv4(src=192.168.0.1,dst=192.168.0.2,proto=6,tos=0,ttl=64,frag=no),tcp(src=8,dst=9)'], [0], [stdout])
+AT_CHECK([tail -1 stdout], [0],
+ [Datapath actions: set(tunnel(tun_id=0x0,src=2.2.2.2,dst=1.1.1.1,tos=0x0,ttl=64,flags(df))),1
+])
+
+dnl reconfigure, local_ip, remote_ip
+AT_CHECK([ovs-vsctl set Interface p2 type=gre options:local_ip=2.2.2.3 \
+ options:df_default=false options:ttl=1 options:csum=true \
+ -- set Interface p3 type=gre64])
+AT_CHECK([ovs-appctl dpif/show], [0], [dnl
+br0 (dummy@ovs-dummy):
+ lookups: hit:0 missed:0 lost:0
+ flows: 0
+ br0 65534/100: (dummy)
+ p1 1/1: (gre: remote_ip=1.1.1.1)
+ p2 2/1: (gre: csum=true, df_default=false, local_ip=2.2.2.3, remote_ip=1.1.1.1, ttl=1)
+ p3 3/64: (gre64: remote_ip=2.2.2.2)
+])
+AT_CHECK([ovs-appctl ofproto/trace br0 'tunnel(tun_id=0x0,src=1.1.1.1,dst=2.2.2.2,tos=0x0,ttl=64,flags()),in_port(1),eth(src=50:54:00:00:00:05,dst=50:54:00:00:00:07),eth_type(0x0800),ipv4(src=192.168.0.1,dst=192.168.0.2,proto=6,tos=0,ttl=64,frag=no),tcp(src=8,dst=9)'], [0], [stdout])
+AT_CHECK([tail -1 stdout], [0],
+ [Datapath actions: set(tunnel(tun_id=0x0,src=0.0.0.0,dst=1.1.1.1,tos=0x0,ttl=64,flags(df))),1
+])
+AT_CHECK([ovs-appctl ofproto/trace br0 'tunnel(tun_id=0x0,src=1.1.1.1,dst=2.2.2.3,tos=0x0,ttl=64,flags()),in_port(1),eth(src=50:54:00:00:00:05,dst=50:54:00:00:00:07),eth_type(0x0800),ipv4(src=192.168.0.1,dst=192.168.0.2,proto=6,tos=0,ttl=64,frag=no),tcp(src=8,dst=9)'], [0], [stdout])
+AT_CHECK([tail -1 stdout], [0],
+ [Datapath actions: set(tunnel(tun_id=0x0,src=2.2.2.3,dst=1.1.1.1,tos=0x0,ttl=1,flags(csum))),1
+])
+
+dnl nonexistent tunnel
+AT_CHECK([ovs-appctl ofproto/trace br0 'tunnel(tun_id=0x0,src=5.5.5.5,dst=6.6.6.6,tos=0x0,ttl=64,flags()),in_port(1),eth(src=50:54:00:00:00:05,dst=50:54:00:00:00:07),eth_type(0x0800),ipv4(src=192.168.0.1,dst=192.168.0.2,proto=6,tos=0,ttl=64,frag=no),tcp(src=8,dst=9)'], [2], [ignore], [dnl
+Invalid flow
+ovs-appctl: ovs-vswitchd: server returned an error
+])
+
+OVS_VSWITCHD_STOP
+AT_CLEANUP
+
+AT_SETUP([tunnel - ECN decapsulation])
+OVS_VSWITCHD_START([add-port br0 p1 -- set Interface p1 type=gre \
+ options:remote_ip=1.1.1.1 ofport_request=1 \
+ -- add-port br0 p2 -- set Interface p2 type=dummy \
+ ofport_request=2])
+AT_DATA([flows.txt], [dnl
+actions=2
+])
+
+AT_CHECK([ovs-ofctl add-flows br0 flows.txt])
+
+AT_CHECK([ovs-appctl dpif/show], [0], [dnl
+br0 (dummy@ovs-dummy):
+ lookups: hit:0 missed:0 lost:0
+ flows: 0
+ br0 65534/100: (dummy)
+ p1 1/1: (gre: remote_ip=1.1.1.1)
+ p2 2/2: (dummy)
+])
+
+AT_CHECK([ovs-appctl ofproto/trace br0 'tunnel(tun_id=0x0,src=1.1.1.1,dst=2.2.2.2,tos=0x3,ttl=64,flags()),in_port(1),eth(src=50:54:00:00:00:05,dst=50:54:00:00:00:07),eth_type(0x0800),ipv4(src=192.168.0.1,dst=192.168.0.2,proto=6,tos=1,ttl=64,frag=no),tcp(src=8,dst=9)'], [0], [stdout])
+AT_CHECK([tail -1 stdout], [0],
+ [Datapath actions: 2
+])
+OVS_VSWITCHD_STOP
+AT_CLEANUP
+
+AT_SETUP([tunnel - output])
+OVS_VSWITCHD_START([add-port br0 p1 -- set Interface p1 type=gre \
+ options:remote_ip=1.1.1.1 options:local_ip=2.2.2.2 \
+ options:key=5 ofport_request=1\
+ -- add-port br0 p2 -- set Interface p2 type=dummy \
+ ofport_request=2 ofport_request=2])
+AT_DATA([flows.txt], [dnl
+actions=output:1
+])
+
+AT_CHECK([ovs-ofctl add-flows br0 flows.txt])
+
+AT_CHECK([ovs-appctl dpif/show], [0], [dnl
+br0 (dummy@ovs-dummy):
+ lookups: hit:0 missed:0 lost:0
+ flows: 0
+ br0 65534/100: (dummy)
+ p1 1/1: (gre: key=5, local_ip=2.2.2.2, remote_ip=1.1.1.1)
+ p2 2/2: (dummy)
+])
+
+dnl Basic
+AT_CHECK([ovs-appctl ofproto/trace br0 'in_port(2),eth(src=50:54:00:00:00:05,dst=50:54:00:00:00:07),eth_type(0x0800),ipv4(src=192.168.0.1,dst=192.168.0.2,proto=6,tos=4,ttl=128,frag=no),tcp(src=8,dst=9)'], [0], [stdout])
+AT_CHECK([tail -1 stdout], [0],
+ [Datapath actions: set(tunnel(tun_id=0x5,src=2.2.2.2,dst=1.1.1.1,tos=0x0,ttl=64,flags(df,key))),1
+])
+
+dnl ECN
+AT_CHECK([ovs-appctl ofproto/trace br0 'in_port(2),eth(src=50:54:00:00:00:05,dst=50:54:00:00:00:07),eth_type(0x0800),ipv4(src=192.168.0.1,dst=192.168.0.2,proto=6,tos=1,ttl=64,frag=no),tcp(src=8,dst=9)'], [0], [stdout])
+AT_CHECK([tail -1 stdout], [0],
+ [Datapath actions: set(tunnel(tun_id=0x5,src=2.2.2.2,dst=1.1.1.1,tos=0x1,ttl=64,flags(df,key))),1
+])
+OVS_VSWITCHD_STOP
+AT_CLEANUP
+
+AT_SETUP([tunnel - ToS and TTL inheritance])
+OVS_VSWITCHD_START([add-port br0 p1 -- set Interface p1 type=gre \
+ options:remote_ip=1.1.1.1 options:tos=inherit \
+ options:ttl=inherit ofport_request=1 \
+ -- add-port br0 p2 -- set Interface p2 type=dummy \
+ ofport_request=2 ofport_request=2])
+AT_DATA([flows.txt], [dnl
+actions=output:1
+])
+
+AT_CHECK([ovs-ofctl add-flows br0 flows.txt])
+
+AT_CHECK([ovs-appctl dpif/show], [0], [dnl
+br0 (dummy@ovs-dummy):
+ lookups: hit:0 missed:0 lost:0
+ flows: 0
+ br0 65534/100: (dummy)
+ p1 1/1: (gre: remote_ip=1.1.1.1, tos=inherit, ttl=inherit)
+ p2 2/2: (dummy)
+])
+
+dnl Basic
+AT_CHECK([ovs-appctl ofproto/trace br0 'in_port(2),eth(src=50:54:00:00:00:05,dst=50:54:00:00:00:07),eth_type(0x0800),ipv4(src=192.168.0.1,dst=192.168.0.2,proto=6,tos=4,ttl=128,frag=no),tcp(src=8,dst=9)'], [0], [stdout])
+AT_CHECK([tail -1 stdout], [0],
+ [Datapath actions: set(tunnel(tun_id=0x0,src=0.0.0.0,dst=1.1.1.1,tos=0x4,ttl=128,flags(df))),1
+])
+
+dnl ECN
+AT_CHECK([ovs-appctl ofproto/trace br0 'in_port(2),eth(src=50:54:00:00:00:05,dst=50:54:00:00:00:07),eth_type(0x0800),ipv4(src=192.168.0.1,dst=192.168.0.2,proto=6,tos=5,ttl=128,frag=no),tcp(src=8,dst=9)'], [0], [stdout])
+AT_CHECK([tail -1 stdout], [0],
+ [Datapath actions: set(tunnel(tun_id=0x0,src=0.0.0.0,dst=1.1.1.1,tos=0x5,ttl=128,flags(df))),1
+])
+
+dnl non-IP
+AT_CHECK([ovs-appctl ofproto/trace br0 'in_port(2),eth(src=50:54:00:00:00:05,dst=50:54:00:00:00:07),eth_type(0x0806),arp(sip=1.2.3.4,tip=5.6.7.8,op=1,sha=00:0f:10:11:12:13,tha=00:14:15:16:17:18)'], [0], [stdout])
+AT_CHECK([tail -1 stdout], [0],
+ [Datapath actions: set(tunnel(tun_id=0x0,src=0.0.0.0,dst=1.1.1.1,tos=0x0,ttl=64,flags(df))),1
+])
+OVS_VSWITCHD_STOP
+AT_CLEANUP
+
+AT_SETUP([tunnel - set_tunnel])
+OVS_VSWITCHD_START([dnl
+ add-port br0 p1 -- set Interface p1 type=gre options:key=flow \
+ options:remote_ip=1.1.1.1 ofport_request=1 \
+ -- add-port br0 p2 -- set Interface p2 type=gre options:key=flow \
+ options:remote_ip=2.2.2.2 ofport_request=2 \
+ -- add-port br0 p3 -- set Interface p3 type=gre options:key=flow \
+ options:remote_ip=3.3.3.3 ofport_request=3 \
+ -- add-port br0 p4 -- set Interface p4 type=gre options:key=flow \
+ options:remote_ip=4.4.4.4 ofport_request=4])
+AT_DATA([flows.txt], [dnl
+actions=set_tunnel:1,output:1,set_tunnel:2,output:2,set_tunnel:3,output:3,set_tunnel:5,output:4
+])
+
+AT_CHECK([ovs-ofctl add-flows br0 flows.txt])
+
+AT_CHECK([ovs-appctl dpif/show], [0], [dnl
+br0 (dummy@ovs-dummy):
+ lookups: hit:0 missed:0 lost:0
+ flows: 0
+ br0 65534/100: (dummy)
+ p1 1/1: (gre: key=flow, remote_ip=1.1.1.1)
+ p2 2/1: (gre: key=flow, remote_ip=2.2.2.2)
+ p3 3/1: (gre: key=flow, remote_ip=3.3.3.3)
+ p4 4/1: (gre: key=flow, remote_ip=4.4.4.4)
+])
+
+AT_CHECK([ovs-appctl ofproto/trace br0 'in_port(100),eth(src=50:54:00:00:00:05,dst=50:54:00:00:00:07),eth_type(0x0800),ipv4(src=192.168.0.1,dst=192.168.0.2,proto=1,tos=0,ttl=128,frag=no),icmp(type=8,code=0)'], [0], [stdout])
+AT_CHECK([tail -1 stdout], [0], [Datapath actions: dnl
+set(tunnel(tun_id=0x1,src=0.0.0.0,dst=1.1.1.1,tos=0x0,ttl=64,flags(df,key))),1,dnl
+set(tunnel(tun_id=0x2,src=0.0.0.0,dst=2.2.2.2,tos=0x0,ttl=64,flags(df,key))),1,dnl
+set(tunnel(tun_id=0x3,src=0.0.0.0,dst=3.3.3.3,tos=0x0,ttl=64,flags(df,key))),1,dnl
+set(tunnel(tun_id=0x5,src=0.0.0.0,dst=4.4.4.4,tos=0x0,ttl=64,flags(df,key))),1
+])
+OVS_VSWITCHD_STOP
+AT_CLEANUP
+
+AT_SETUP([tunnel - key])
+OVS_VSWITCHD_START([dnl
+ add-port br0 p1 -- set Interface p1 type=gre options:key=1 \
+ options:remote_ip=1.1.1.1 ofport_request=1 \
+ -- add-port br0 p2 -- set Interface p2 type=gre options:in_key=2 \
+ options:out_key=3 options:remote_ip=1.1.1.1 ofport_request=2 \
+ -- add-port br0 p3 -- set Interface p3 type=gre options:out_key=5 \
+ options:remote_ip=1.1.1.1 ofport_request=3])
+AT_DATA([flows.txt], [dnl
+actions=IN_PORT,output:1,output:2,output:3
+])
+
+AT_CHECK([ovs-ofctl add-flows br0 flows.txt])
+
+AT_CHECK([ovs-appctl dpif/show], [0], [dnl
+br0 (dummy@ovs-dummy):
+ lookups: hit:0 missed:0 lost:0
+ flows: 0
+ br0 65534/100: (dummy)
+ p1 1/1: (gre: key=1, remote_ip=1.1.1.1)
+ p2 2/1: (gre: in_key=2, out_key=3, remote_ip=1.1.1.1)
+ p3 3/1: (gre: out_key=5, remote_ip=1.1.1.1)
+])
+
+AT_CHECK([ovs-appctl ofproto/trace br0 'tunnel(tun_id=0x1,src=1.1.1.1,dst=2.2.2.2,tos=0x0,ttl=64,flags(key)),in_port(1),eth(src=50:54:00:00:00:05,dst=50:54:00:00:00:07),eth_type(0x0800),ipv4(src=192.168.0.1,dst=192.168.0.2,proto=6,tos=0,ttl=64,frag=no),tcp(src=8,dst=9)'], [0], [stdout])
+AT_CHECK([tail -1 stdout], [0], [Datapath actions: dnl
+set(tunnel(tun_id=0x1,src=0.0.0.0,dst=1.1.1.1,tos=0x0,ttl=64,flags(df,key))),1,dnl
+set(tunnel(tun_id=0x3,src=0.0.0.0,dst=1.1.1.1,tos=0x0,ttl=64,flags(df,key))),1,dnl
+set(tunnel(tun_id=0x5,src=0.0.0.0,dst=1.1.1.1,tos=0x0,ttl=64,flags(df,key))),1
+])
+
+AT_CHECK([ovs-appctl ofproto/trace br0 'tunnel(tun_id=0x2,src=1.1.1.1,dst=2.2.2.2,tos=0x0,ttl=64,flags(key)),in_port(1),eth(src=50:54:00:00:00:05,dst=50:54:00:00:00:07),eth_type(0x0800),ipv4(src=192.168.0.1,dst=192.168.0.2,proto=6,tos=0,ttl=64,frag=no),tcp(src=8,dst=9)'], [0], [stdout])
+AT_CHECK([tail -1 stdout], [0], [Datapath actions: dnl
+set(tunnel(tun_id=0x3,src=0.0.0.0,dst=1.1.1.1,tos=0x0,ttl=64,flags(df,key))),1,dnl
+set(tunnel(tun_id=0x1,src=0.0.0.0,dst=1.1.1.1,tos=0x0,ttl=64,flags(df,key))),1,dnl
+set(tunnel(tun_id=0x5,src=0.0.0.0,dst=1.1.1.1,tos=0x0,ttl=64,flags(df,key))),1
+])
+
+AT_CHECK([ovs-appctl ofproto/trace br0 'tunnel(tun_id=0x0,src=1.1.1.1,dst=2.2.2.2,tos=0x0,ttl=64,flags()),in_port(1),eth(src=50:54:00:00:00:05,dst=50:54:00:00:00:07),eth_type(0x0800),ipv4(src=192.168.0.1,dst=192.168.0.2,proto=6,tos=0,ttl=64,frag=no),tcp(src=8,dst=9)'], [0], [stdout])
+AT_CHECK([tail -1 stdout], [0], [Datapath actions: dnl
+set(tunnel(tun_id=0x5,src=0.0.0.0,dst=1.1.1.1,tos=0x0,ttl=64,flags(df,key))),1,dnl
+set(tunnel(tun_id=0x1,src=0.0.0.0,dst=1.1.1.1,tos=0x0,ttl=64,flags(df,key))),1,dnl
+set(tunnel(tun_id=0x3,src=0.0.0.0,dst=1.1.1.1,tos=0x0,ttl=64,flags(df,key))),1
+])
+
+AT_CHECK([ovs-appctl ofproto/trace br0 'tunnel(tun_id=0xf,src=1.1.1.1,dst=2.2.2.2,tos=0x0,ttl=64,flags(key)),in_port(1),eth(src=50:54:00:00:00:05,dst=50:54:00:00:00:07),eth_type(0x0800),ipv4(src=192.168.0.1,dst=192.168.0.2,proto=6,tos=0,ttl=64,frag=no),tcp(src=8,dst=9)'], [2], [ignore], [dnl
+Invalid flow
+ovs-appctl: ovs-vswitchd: server returned an error
+])
+OVS_VSWITCHD_STOP
+AT_CLEANUP
+
+AT_SETUP([tunnel - key match])
+OVS_VSWITCHD_START([dnl
+ add-port br0 p1 -- set Interface p1 type=gre options:key=flow \
+ options:remote_ip=1.1.1.1 ofport_request=1 \
+ -- add-port br0 p2 -- set Interface p2 type=gre options:key=3 \
+ options:remote_ip=3.3.3.3 ofport_request=2 \
+ -- add-port br0 p3 -- set Interface p3 type=dummy ofport_request=3 \
+ -- add-port br0 p4 -- set Interface p4 type=dummy ofport_request=4 \
+ -- add-port br0 p5 -- set Interface p5 type=dummy ofport_request=5])
+AT_DATA([flows.txt], [dnl
+tun_id=2,actions=output:3
+tun_id=3,actions=output:4,set_tunnel:2,resubmit:99,set_tunnel:4,output:2,resubmit:99
+tun_id=4,actions=output:5
+])
+
+AT_CHECK([ovs-ofctl add-flows br0 flows.txt])
+
+AT_CHECK([ovs-appctl dpif/show], [0], [dnl
+br0 (dummy@ovs-dummy):
+ lookups: hit:0 missed:0 lost:0
+ flows: 0
+ br0 65534/100: (dummy)
+ p1 1/1: (gre: key=flow, remote_ip=1.1.1.1)
+ p2 2/1: (gre: key=3, remote_ip=3.3.3.3)
+ p3 3/3: (dummy)
+ p4 4/4: (dummy)
+ p5 5/5: (dummy)
+])
+
+AT_CHECK([ovs-appctl ofproto/trace br0 'tunnel(tun_id=0x2,src=1.1.1.1,dst=2.2.2.2,tos=0x0,ttl=64,flags(key)),in_port(1),eth(src=50:54:00:00:00:05,dst=50:54:00:00:00:07),eth_type(0x0800),ipv4(src=192.168.0.1,dst=192.168.0.2,proto=6,tos=0,ttl=64,frag=no),tcp(src=8,dst=9)'], [0], [stdout])
+AT_CHECK([tail -1 stdout], [0], [dnl
+Datapath actions: 3
+])
+
+AT_CHECK([ovs-appctl ofproto/trace br0 'tunnel(tun_id=0x3,src=1.1.1.1,dst=2.2.2.2,tos=0x0,ttl=64,flags(key)),in_port(1),eth(src=50:54:00:00:00:05,dst=50:54:00:00:00:07),eth_type(0x0800),ipv4(src=192.168.0.1,dst=192.168.0.2,proto=6,tos=0,ttl=64,frag=no),tcp(src=8,dst=9)'], [0], [stdout])
+AT_CHECK([tail -1 stdout], [0], [dnl
+Datapath actions: 4,3,set(tunnel(tun_id=0x3,src=0.0.0.0,dst=3.3.3.3,tos=0x0,ttl=64,flags(df,key))),1,5
+])
+
+AT_CHECK([ovs-appctl ofproto/trace br0 'tunnel(tun_id=0x3,src=3.3.3.3,dst=2.2.2.2,tos=0x0,ttl=64,flags(key)),in_port(1),eth(src=50:54:00:00:00:05,dst=50:54:00:00:00:07),eth_type(0x0800),ipv4(src=192.168.0.1,dst=192.168.0.2,proto=6,tos=0,ttl=64,frag=no),tcp(src=8,dst=9)'], [0], [stdout])
+AT_CHECK([tail -1 stdout], [0], [dnl
+Datapath actions: 4,3,5
+])
+
+AT_CHECK([ovs-appctl ofproto/trace br0 'tunnel(tun_id=0x0,src=1.1.1.1,dst=2.2.2.2,tos=0x0,ttl=64,flags(key)),in_port(1),eth(src=50:54:00:00:00:05,dst=50:54:00:00:00:07),eth_type(0x0800),ipv4(src=192.168.0.1,dst=192.168.0.2,proto=6,tos=0,ttl=64,frag=no),tcp(src=8,dst=9)'], [0], [stdout])
+AT_CHECK([tail -1 stdout], [0], [dnl
+ - Sends "packet-in" messages to the OpenFlow controller.
+])
+
+OVS_VSWITCHD_STOP
+AT_CLEANUP
.
.IP
Each of the wrappers can expose bugs in Open vSwitch that lead to
-incorrect operation, including crashes. The \fBvalgring\fR and
+incorrect operation, including crashes. The \fBvalgrind\fR and
\fBstrace\fR wrappers greatly slow daemon operations so they should
not be used in production. They also produce voluminous logs that can
quickly fill small disk partitions. The \fBglibc\fR wrapper is less
.
\fBovs\-ctl\fR uses the following files:
.
-.IP "\fBovs\-lib.sh"
+.IP "\fBovs\-lib"
Shell function library used internally by \fBovs\-ctl\fR. It must be
installed in the same directory as \fBovs\-ctl\fR.
.
return error;
}
+enum open_target { MGMT, SNOOP };
+
static enum ofputil_protocol
-open_vconn__(const char *name, const char *default_suffix,
+open_vconn__(const char *name, enum open_target target,
struct vconn **vconnp)
{
+ const char *suffix = target == MGMT ? "mgmt" : "snoop";
char *datapath_name, *datapath_type, *socket_name;
enum ofputil_protocol protocol;
char *bridge_path;
int ofp_version;
int error;
- bridge_path = xasprintf("%s/%s.%s", ovs_rundir(), name, default_suffix);
+ bridge_path = xasprintf("%s/%s.%s", ovs_rundir(), name, suffix);
ofproto_parse_name(name, &datapath_name, &datapath_type);
- socket_name = xasprintf("%s/%s.%s",
- ovs_rundir(), datapath_name, default_suffix);
+ socket_name = xasprintf("%s/%s.%s", ovs_rundir(), datapath_name, suffix);
free(datapath_name);
free(datapath_type);
if (strchr(name, ':')) {
- run(vconn_open_block(name, get_allowed_ofp_versions(), DSCP_DEFAULT,
- vconnp),
+ run(vconn_open(name, get_allowed_ofp_versions(), DSCP_DEFAULT, vconnp),
"connecting to %s", name);
} else if (!open_vconn_socket(name, vconnp)) {
/* Fall Through. */
ovs_fatal(0, "%s is not a bridge or a socket", name);
}
+ if (target == SNOOP) {
+ vconn_set_recv_any_version(*vconnp);
+ }
+
free(bridge_path);
free(socket_name);
static enum ofputil_protocol
open_vconn(const char *name, struct vconn **vconnp)
{
- return open_vconn__(name, "mgmt", vconnp);
+ return open_vconn__(name, MGMT, vconnp);
}
static void
{
struct vconn *vconn;
- open_vconn__(argv[1], "snoop", &vconn);
+ open_vconn__(argv[1], SNOOP, &vconn);
monitor_vconn(vconn);
}