tpi.seq = htonl(tunnel->o_seqno);
 
        /* Push GRE header. */
-       gre_build_header(skb, &tpi, tunnel->hlen);
+       gre_build_header(skb, &tpi, tunnel->tun_hlen);
 
        ip_tunnel_xmit(skb, dev, tnl_params, tnl_params->protocol);
 }
 static int ipgre_tunnel_ioctl(struct net_device *dev,
                              struct ifreq *ifr, int cmd)
 {
-       int err = 0;
+       int err;
        struct ip_tunnel_parm p;
 
        if (copy_from_user(&p, ifr->ifr_ifru.ifru_data, sizeof(p)))
 static void __gre_tunnel_init(struct net_device *dev)
 {
        struct ip_tunnel *tunnel;
+       int t_hlen;
 
        tunnel = netdev_priv(dev);
-       tunnel->hlen = ip_gre_calc_hlen(tunnel->parms.o_flags);
+       tunnel->tun_hlen = ip_gre_calc_hlen(tunnel->parms.o_flags);
        tunnel->parms.iph.protocol = IPPROTO_GRE;
 
-       dev->needed_headroom    = LL_MAX_HEADER + sizeof(struct iphdr) + 4;
-       dev->mtu                = ETH_DATA_LEN - sizeof(struct iphdr) - 4;
+       tunnel->hlen = tunnel->tun_hlen + tunnel->encap_hlen;
+
+       t_hlen = tunnel->hlen + sizeof(struct iphdr);
+
+       dev->needed_headroom    = LL_MAX_HEADER + t_hlen + 4;
+       dev->mtu                = ETH_DATA_LEN - t_hlen - 4;
 
        dev->features           |= GRE_FEATURES;
        dev->hw_features        |= GRE_FEATURES;
                parms->iph.frag_off = htons(IP_DF);
 }
 
+/* This function returns true when ENCAP attributes are present in the nl msg */
+static bool ipgre_netlink_encap_parms(struct nlattr *data[],
+                                     struct ip_tunnel_encap *ipencap)
+{
+       bool ret = false;
+
+       memset(ipencap, 0, sizeof(*ipencap));
+
+       if (!data)
+               return ret;
+
+       if (data[IFLA_GRE_ENCAP_TYPE]) {
+               ret = true;
+               ipencap->type = nla_get_u16(data[IFLA_GRE_ENCAP_TYPE]);
+       }
+
+       if (data[IFLA_GRE_ENCAP_FLAGS]) {
+               ret = true;
+               ipencap->flags = nla_get_u16(data[IFLA_GRE_ENCAP_FLAGS]);
+       }
+
+       if (data[IFLA_GRE_ENCAP_SPORT]) {
+               ret = true;
+               ipencap->sport = nla_get_u16(data[IFLA_GRE_ENCAP_SPORT]);
+       }
+
+       if (data[IFLA_GRE_ENCAP_DPORT]) {
+               ret = true;
+               ipencap->dport = nla_get_u16(data[IFLA_GRE_ENCAP_DPORT]);
+       }
+
+       return ret;
+}
+
 static int gre_tap_init(struct net_device *dev)
 {
        __gre_tunnel_init(dev);
                         struct nlattr *tb[], struct nlattr *data[])
 {
        struct ip_tunnel_parm p;
+       struct ip_tunnel_encap ipencap;
+
+       if (ipgre_netlink_encap_parms(data, &ipencap)) {
+               struct ip_tunnel *t = netdev_priv(dev);
+               int err = ip_tunnel_encap_setup(t, &ipencap);
+
+               if (err < 0)
+                       return err;
+       }
 
        ipgre_netlink_parms(data, tb, &p);
        return ip_tunnel_newlink(dev, tb, &p);
                            struct nlattr *data[])
 {
        struct ip_tunnel_parm p;
+       struct ip_tunnel_encap ipencap;
+
+       if (ipgre_netlink_encap_parms(data, &ipencap)) {
+               struct ip_tunnel *t = netdev_priv(dev);
+               int err = ip_tunnel_encap_setup(t, &ipencap);
+
+               if (err < 0)
+                       return err;
+       }
 
        ipgre_netlink_parms(data, tb, &p);
        return ip_tunnel_changelink(dev, tb, &p);
                nla_total_size(1) +
                /* IFLA_GRE_PMTUDISC */
                nla_total_size(1) +
+               /* IFLA_GRE_ENCAP_TYPE */
+               nla_total_size(2) +
+               /* IFLA_GRE_ENCAP_FLAGS */
+               nla_total_size(2) +
+               /* IFLA_GRE_ENCAP_SPORT */
+               nla_total_size(2) +
+               /* IFLA_GRE_ENCAP_DPORT */
+               nla_total_size(2) +
                0;
 }
 
            nla_put_u8(skb, IFLA_GRE_PMTUDISC,
                       !!(p->iph.frag_off & htons(IP_DF))))
                goto nla_put_failure;
+
+       if (nla_put_u16(skb, IFLA_GRE_ENCAP_TYPE,
+                       t->encap.type) ||
+           nla_put_u16(skb, IFLA_GRE_ENCAP_SPORT,
+                       t->encap.sport) ||
+           nla_put_u16(skb, IFLA_GRE_ENCAP_DPORT,
+                       t->encap.dport) ||
+           nla_put_u16(skb, IFLA_GRE_ENCAP_FLAGS,
+                       t->encap.dport))
+               goto nla_put_failure;
+
        return 0;
 
 nla_put_failure:
        [IFLA_GRE_TTL]          = { .type = NLA_U8 },
        [IFLA_GRE_TOS]          = { .type = NLA_U8 },
        [IFLA_GRE_PMTUDISC]     = { .type = NLA_U8 },
+       [IFLA_GRE_ENCAP_TYPE]   = { .type = NLA_U16 },
+       [IFLA_GRE_ENCAP_FLAGS]  = { .type = NLA_U16 },
+       [IFLA_GRE_ENCAP_SPORT]  = { .type = NLA_U16 },
+       [IFLA_GRE_ENCAP_DPORT]  = { .type = NLA_U16 },
 };
 
 static struct rtnl_link_ops ipgre_link_ops __read_mostly = {