datapath: Add Upstream id for GRE type.
[openvswitch] / datapath / vport-gre.c
index 6f406bc9c36e37524c792419ee0d195fafbbaeee..e810173ff95e29cec03f302f377c57a1b54e921c 100644 (file)
@@ -45,18 +45,51 @@ struct gre_base_hdr {
        __be16 protocol;
 };
 
-static int gre_hdr_len(const struct tnl_mutable_config *mutable)
+static void get_gre_param(const struct tnl_mutable_config *mutable,
+                       const struct ovs_key_ipv4_tunnel *tun_key,
+                       u32 *flags, u32 *tunnel_type, __be64 *out_key)
+{
+       if (tun_key->ipv4_dst) {
+               *flags = 0;
+
+               if (tun_key->tun_flags & OVS_FLOW_TNL_F_KEY)
+                       *flags = TNL_F_OUT_KEY_ACTION;
+               if (tun_key->tun_flags & OVS_FLOW_TNL_F_CSUM)
+                       *flags |= TNL_F_CSUM;
+               *tunnel_type = TNL_T_PROTO_GRE;
+               *out_key = tun_key->tun_id;
+       } else {
+               *flags = mutable->flags;
+               *tunnel_type = mutable->key.tunnel_type;
+               if (mutable->flags & TNL_F_OUT_KEY_ACTION)
+                       *out_key = tun_key->tun_id;
+               else
+                       *out_key = mutable->out_key;
+
+       }
+}
+
+static int gre_hdr_len(const struct tnl_mutable_config *mutable,
+                      const struct ovs_key_ipv4_tunnel *tun_key)
 {
        int len;
+       u32 flags;
+       u32 tunnel_type;
+       __be64 out_key;
 
+       get_gre_param(mutable, tun_key, &flags, &tunnel_type, &out_key);
        len = GRE_HEADER_SECTION;
 
-       if (mutable->flags & TNL_F_CSUM)
+       if (flags & TNL_F_CSUM)
                len += GRE_HEADER_SECTION;
 
-       if (mutable->out_key || mutable->flags & TNL_F_OUT_KEY_ACTION) {
+       /* Set key for GRE64 tunnels, even when key if is zero. */
+       if (out_key ||
+           tunnel_type & TNL_T_PROTO_GRE64 ||
+           flags & TNL_F_OUT_KEY_ACTION) {
+
                len += GRE_HEADER_SECTION;
-               if (mutable->key.tunnel_type & TNL_T_PROTO_GRE64)
+               if (tunnel_type & TNL_T_PROTO_GRE64)
                        len += GRE_HEADER_SECTION;
        }
        return len;
@@ -84,31 +117,38 @@ static __be32 be64_get_high32(__be64 x)
 
 static void gre_build_header(const struct vport *vport,
                             const struct tnl_mutable_config *mutable,
+                            const struct ovs_key_ipv4_tunnel *tun_key,
                             void *header)
 {
        struct gre_base_hdr *greh = header;
        __be32 *options = (__be32 *)(greh + 1);
+       u32 flags;
+       u32 tunnel_type;
+       __be64 out_key;
+
+       get_gre_param(mutable, tun_key, &flags, &tunnel_type, &out_key);
 
        greh->protocol = htons(ETH_P_TEB);
        greh->flags = 0;
 
-       if (mutable->flags & TNL_F_CSUM) {
+       if (flags & TNL_F_CSUM) {
                greh->flags |= GRE_CSUM;
                *options = 0;
                options++;
        }
 
-       if (mutable->flags & TNL_F_OUT_KEY_ACTION) {
+       if (flags & TNL_F_OUT_KEY_ACTION) {
                greh->flags |= GRE_KEY;
-               if (mutable->key.tunnel_type & TNL_T_PROTO_GRE64)
+               if (tunnel_type & TNL_T_PROTO_GRE64)
                        greh->flags |= GRE_SEQ;
 
-       } else if (mutable->out_key) {
+       } else if (out_key ||
+                  tunnel_type & TNL_T_PROTO_GRE64) {
                greh->flags |= GRE_KEY;
-               *options = be64_get_low32(mutable->out_key);
-               if (mutable->key.tunnel_type & TNL_T_PROTO_GRE64) {
+               *options = be64_get_low32(out_key);
+               if (tunnel_type & TNL_T_PROTO_GRE64) {
                        options++;
-                       *options = be64_get_high32(mutable->out_key);
+                       *options = be64_get_high32(out_key);
                        greh->flags |= GRE_SEQ;
                }
        }
@@ -117,27 +157,34 @@ static void gre_build_header(const struct vport *vport,
 static struct sk_buff *gre_update_header(const struct vport *vport,
                                         const struct tnl_mutable_config *mutable,
                                         struct dst_entry *dst,
-                                        struct sk_buff *skb)
+                                        struct sk_buff *skb,
+                                        int tunnel_hlen)
 {
-       __be32 *options = (__be32 *)(skb_network_header(skb) + mutable->tunnel_hlen
+       u32 flags;
+       u32 tunnel_type;
+       __be64 out_key;
+       const struct ovs_key_ipv4_tunnel *tun_key = OVS_CB(skb)->tun_key;
+       __be32 *options = (__be32 *)(skb_network_header(skb) + tunnel_hlen
                                               - GRE_HEADER_SECTION);
 
+       get_gre_param(mutable, tun_key, &flags, &tunnel_type, &out_key);
+
        /* Work backwards over the options so the checksum is last. */
-       if (mutable->flags & TNL_F_OUT_KEY_ACTION) {
-               if (mutable->key.tunnel_type & TNL_T_PROTO_GRE64) {
+       if (flags & TNL_F_OUT_KEY_ACTION) {
+               if (tunnel_type & TNL_T_PROTO_GRE64) {
                        /* Set higher 32 bits to seq. */
-                       *options = be64_get_high32(OVS_CB(skb)->tun_id);
+                       *options = be64_get_high32(out_key);
                        options--;
                }
-               *options = be64_get_low32(OVS_CB(skb)->tun_id);
+               *options = be64_get_low32(out_key);
                options--;
-       } else if (mutable->out_key) {
+       } else if (out_key || tunnel_type & TNL_T_PROTO_GRE64) {
                options--;
-               if (mutable->key.tunnel_type & TNL_T_PROTO_GRE64)
+               if (tunnel_type & TNL_T_PROTO_GRE64)
                        options--;
        }
 
-       if (mutable->flags & TNL_F_CSUM)
+       if (flags & TNL_F_CSUM)
                *(__sum16 *)options = csum_fold(skb_checksum(skb,
                                                skb_transport_offset(skb),
                                                skb->len - skb_transport_offset(skb),
@@ -202,8 +249,11 @@ static int parse_header(struct iphdr *iph, __be16 *flags, __be64 *tun_id,
                        *tunnel_type = TNL_T_PROTO_GRE;
                }
                *tun_id = key_to_tunnel_id(gre_key, seq);
-       } else
+       } else {
                *tun_id = 0;
+               /* Ignore GRE seq if there is no key present. */
+               *tunnel_type = TNL_T_PROTO_GRE;
+       }
 
        if (greh->flags & GRE_SEQ)
                hdr_len += GRE_HEADER_SECTION;
@@ -326,7 +376,7 @@ static void gre_err(struct sk_buff *skb, u32 info)
 #endif
 
        __skb_pull(skb, tunnel_hdr_len);
-       ovs_tnl_frag_needed(vport, mutable, skb, mtu, key);
+       ovs_tnl_frag_needed(vport, mutable, skb, mtu);
        __skb_push(skb, tunnel_hdr_len);
 
 out:
@@ -361,6 +411,24 @@ static bool check_checksum(struct sk_buff *skb)
        return (csum == 0);
 }
 
+static u32 gre_flags_to_tunnel_flags(const struct tnl_mutable_config *mutable,
+                                    __be16 gre_flags)
+{
+       u32 tunnel_flags = 0;
+
+       if (gre_flags & GRE_KEY) {
+               if (mutable->key.daddr && (mutable->flags & TNL_F_IN_KEY_MATCH))
+                       tunnel_flags = OVS_FLOW_TNL_F_KEY;
+               else if (!mutable->key.daddr)
+                       tunnel_flags = OVS_FLOW_TNL_F_KEY;
+       }
+
+       if (gre_flags & GRE_CSUM)
+               tunnel_flags |= OVS_FLOW_TNL_F_CSUM;
+
+       return tunnel_flags;
+}
+
 /* Called with rcu_read_lock and BH disabled. */
 static int gre_rcv(struct sk_buff *skb)
 {
@@ -368,6 +436,7 @@ static int gre_rcv(struct sk_buff *skb)
        const struct tnl_mutable_config *mutable;
        int hdr_len;
        struct iphdr *iph;
+       struct ovs_key_ipv4_tunnel tun_key;
        __be16 flags;
        __be64 key;
        u32 tunnel_type;
@@ -392,15 +461,13 @@ static int gre_rcv(struct sk_buff *skb)
                goto error;
        }
 
-       if (mutable->flags & TNL_F_IN_KEY_MATCH)
-               OVS_CB(skb)->tun_id = key;
-       else
-               OVS_CB(skb)->tun_id = 0;
+       tnl_tun_key_init(&tun_key, iph, key, gre_flags_to_tunnel_flags(mutable, flags));
+       OVS_CB(skb)->tun_key = &tun_key;
 
        __skb_pull(skb, hdr_len);
        skb_postpull_rcsum(skb, skb_transport_header(skb), hdr_len + ETH_HLEN);
 
-       ovs_tnl_rcv(vport, skb, iph->tos);
+       ovs_tnl_rcv(vport, skb);
        return 0;
 
 error:
@@ -421,6 +488,11 @@ static struct vport *gre_create(const struct vport_parms *parms)
        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,
@@ -469,6 +541,24 @@ static void gre_exit(void)
        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,
+       .get_dev_flags  = ovs_vport_gen_get_dev_flags,
+       .is_running     = ovs_vport_gen_is_running,
+       .get_operstate  = ovs_vport_gen_get_operstate,
+       .send           = ovs_tnl_send,
+};
+
 const struct vport_ops ovs_gre_vport_ops = {
        .type           = OVS_VPORT_TYPE_GRE,
        .flags          = VPORT_F_TUN_ID,