if (mutable->flags & TNL_F_CSUM)
len += GRE_HEADER_SECTION;
- if (mutable->out_key || mutable->flags & TNL_F_OUT_KEY_ACTION)
+ if (mutable->out_key || mutable->flags & TNL_F_OUT_KEY_ACTION) {
len += GRE_HEADER_SECTION;
-
+ if (mutable->key.tunnel_type & TNL_T_PROTO_GRE64)
+ len += GRE_HEADER_SECTION;
+ }
return len;
}
+
/* Returns the least-significant 32 bits of a __be64. */
static __be32 be64_get_low32(__be64 x)
{
#endif
}
+static __be32 be64_get_high32(__be64 x)
+{
+#ifdef __BIG_ENDIAN
+ return (__force __be32)((__force u64)x >> 32);
+#else
+ return (__force __be32)x;
+#endif
+}
+
static void gre_build_header(const struct vport *vport,
const struct tnl_mutable_config *mutable,
void *header)
options++;
}
- if (mutable->out_key || mutable->flags & TNL_F_OUT_KEY_ACTION)
+ if (mutable->flags & TNL_F_OUT_KEY_ACTION) {
greh->flags |= GRE_KEY;
+ if (mutable->key.tunnel_type & TNL_T_PROTO_GRE64)
+ greh->flags |= GRE_SEQ;
- if (mutable->out_key)
+ } else if (mutable->out_key) {
+ greh->flags |= GRE_KEY;
*options = be64_get_low32(mutable->out_key);
+ if (mutable->key.tunnel_type & TNL_T_PROTO_GRE64) {
+ options++;
+ *options = be64_get_high32(mutable->out_key);
+ greh->flags |= GRE_SEQ;
+ }
+ }
}
static struct sk_buff *gre_update_header(const struct vport *vport,
- GRE_HEADER_SECTION);
/* Work backwards over the options so the checksum is last. */
- if (mutable->flags & TNL_F_OUT_KEY_ACTION)
+ if (mutable->flags & TNL_F_OUT_KEY_ACTION) {
+ if (mutable->key.tunnel_type & TNL_T_PROTO_GRE64) {
+ /* Set higher 32 bits to seq. */
+ *options = be64_get_high32(OVS_CB(skb)->tun_id);
+ options--;
+ }
*options = be64_get_low32(OVS_CB(skb)->tun_id);
-
- if (mutable->out_key || mutable->flags & TNL_F_OUT_KEY_ACTION)
options--;
+ } else if (mutable->out_key) {
+ options--;
+ if (mutable->key.tunnel_type & TNL_T_PROTO_GRE64)
+ options--;
+ }
if (mutable->flags & TNL_F_CSUM)
*(__sum16 *)options = csum_fold(skb_checksum(skb,
return skb;
}
-/* Zero-extends a __be32 into the least-significant 32 bits of a __be64. */
-static __be64 be32_extend_to_be64(__be32 x)
+static __be64 key_to_tunnel_id(__be32 key, __be32 seq)
{
#ifdef __BIG_ENDIAN
- return (__force __be64)x;
+ return (__force __be64)((__force u64)seq << 32 | (__force u32)key);
#else
- return (__force __be64)((__force u64)x << 32);
+ return (__force __be64)((__force u64)key << 32 | (__force u32)seq);
#endif
}
-static int parse_header(struct iphdr *iph, __be16 *flags, __be64 *key)
+static int parse_header(struct iphdr *iph, __be16 *flags, __be64 *tun_id,
+ u32 *tunnel_type)
{
/* IP and ICMP protocol handlers check that the IHL is valid. */
struct gre_base_hdr *greh = (struct gre_base_hdr *)((u8 *)iph + (iph->ihl << 2));
}
if (greh->flags & GRE_KEY) {
- hdr_len += GRE_HEADER_SECTION;
+ __be32 seq;
+ __be32 gre_key;
- *key = be32_extend_to_be64(*options);
+ gre_key = *options;
+ hdr_len += GRE_HEADER_SECTION;
options++;
+
+ if (greh->flags & GRE_SEQ) {
+ seq = *options;
+ *tunnel_type = TNL_T_PROTO_GRE64;
+ } else {
+ seq = 0;
+ *tunnel_type = TNL_T_PROTO_GRE;
+ }
+ *tun_id = key_to_tunnel_id(gre_key, seq);
} else
- *key = 0;
+ *tun_id = 0;
- if (unlikely(greh->flags & GRE_SEQ))
+ if (greh->flags & GRE_SEQ)
hdr_len += GRE_HEADER_SECTION;
return hdr_len;
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;
if (ipv4_is_multicast(iph->daddr))
return;
- tunnel_hdr_len = parse_header(iph, &flags, &key);
+ 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,
- TNL_T_PROTO_GRE, &mutable);
+ tunnel_type, &mutable);
if (!vport)
return;
struct iphdr *iph;
__be16 flags;
__be64 key;
+ u32 tunnel_type;
if (unlikely(!pskb_may_pull(skb, sizeof(struct gre_base_hdr) + ETH_HLEN)))
goto error;
-
if (unlikely(!check_checksum(skb)))
goto error;
- hdr_len = parse_header(ip_hdr(skb), &flags, &key);
+ hdr_len = parse_header(ip_hdr(skb), &flags, &key, &tunnel_type);
if (unlikely(hdr_len < 0))
goto error;
iph = ip_hdr(skb);
vport = ovs_tnl_find_port(dev_net(skb->dev), iph->daddr, iph->saddr, key,
- TNL_T_PROTO_GRE, &mutable);
+ tunnel_type, &mutable);
if (unlikely(!vport)) {
icmp_send(skb, ICMP_DEST_UNREACH, ICMP_PORT_UNREACH, 0);
goto error;
return ovs_tnl_create(parms, &ovs_gre_vport_ops, &gre_tnl_ops);
}
+static const struct tnl_ops gre64_tnl_ops = {
+ .tunnel_type = TNL_T_PROTO_GRE64,
+ .ipproto = IPPROTO_GRE,
+ .hdr_len = gre_hdr_len,
+ .build_header = gre_build_header,
+ .update_header = gre_update_header,
+};
+
+static struct vport *gre_create64(const struct vport_parms *parms)
+{
+ return ovs_tnl_create(parms, &ovs_gre64_vport_ops, &gre64_tnl_ops);
+}
+
static const struct net_protocol gre_protocol_handlers = {
.handler = gre_rcv,
.err_handler = gre_err,
#endif
};
+static bool inited;
+
static int gre_init(void)
{
int err;
+ if (inited)
+ return 0;
+
+ inited = true;
err = inet_add_protocol(&gre_protocol_handlers, IPPROTO_GRE);
if (err)
pr_warn("cannot register gre protocol handler\n");
static void gre_exit(void)
{
+ if (!inited)
+ return;
+
+ inited = false;
+
inet_del_protocol(&gre_protocol_handlers, IPPROTO_GRE);
}
.get_operstate = ovs_vport_gen_get_operstate,
.send = ovs_tnl_send,
};
+
+const struct vport_ops ovs_gre64_vport_ops = {
+ .type = OVS_VPORT_TYPE_GRE64,
+ .flags = VPORT_F_TUN_ID,
+ .init = gre_init,
+ .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,
+ .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,
+};
return (nl_attr_get_u32(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 (nl_attr_get_u32(a[OVS_TUNNEL_ATTR_FLAGS]) & TNL_F_IPSEC
+ ? "ipsec_gre64" : "gre64");
+
case OVS_VPORT_TYPE_CAPWAP:
return "capwap";
uint32_t flags;
flags = TNL_F_DF_DEFAULT | TNL_F_PMTUD | TNL_F_HDR_CACHE;
- if (!strcmp(type, "gre")) {
+ if (!strcmp(type, "gre") || !strcmp(type, "gre64")) {
is_gre = true;
- } else if (!strcmp(type, "ipsec_gre")) {
+ } else if (!strcmp(type, "ipsec_gre") || !strcmp(type, "ipsec_gre64")) {
is_gre = true;
is_ipsec = true;
flags |= TNL_F_IPSEC;
{ "ipsec_gre", VPORT_FUNCTIONS(netdev_vport_get_drv_info) },
parse_tunnel_config, unparse_tunnel_config },
+ { OVS_VPORT_TYPE_GRE64,
+ { "gre64", VPORT_FUNCTIONS(netdev_vport_get_drv_info) },
+ parse_tunnel_config, unparse_tunnel_config },
+
+ { OVS_VPORT_TYPE_GRE64,
+ { "ipsec_gre64", VPORT_FUNCTIONS(netdev_vport_get_drv_info) },
+ parse_tunnel_config, unparse_tunnel_config },
+
{ OVS_VPORT_TYPE_CAPWAP,
{ "capwap", VPORT_FUNCTIONS(netdev_vport_get_drv_info) },
parse_tunnel_config, unparse_tunnel_config },