drm/panel: support transmit DSI packet
[firefly-linux-kernel-4.4.55.git] / net / ipv4 / ipip.c
index 77bfcce64fe568b3a162a9d36324061553275a5e..a09fb0dec725a8827ba5d1e928597c103d1f9322 100644 (file)
@@ -144,18 +144,18 @@ static int ipip_err(struct sk_buff *skb, u32 info)
        err = -ENOENT;
        t = ip_tunnel_lookup(itn, skb->dev->ifindex, TUNNEL_NO_KEY,
                             iph->daddr, iph->saddr, 0);
-       if (t == NULL)
+       if (!t)
                goto out;
 
        if (type == ICMP_DEST_UNREACH && code == ICMP_FRAG_NEEDED) {
                ipv4_update_pmtu(skb, dev_net(skb->dev), info,
-                                t->dev->ifindex, 0, IPPROTO_IPIP, 0);
+                                t->parms.link, 0, IPPROTO_IPIP, 0);
                err = 0;
                goto out;
        }
 
        if (type == ICMP_REDIRECT) {
-               ipv4_redirect(skb, dev_net(skb->dev), t->dev->ifindex, 0,
+               ipv4_redirect(skb, dev_net(skb->dev), t->parms.link, 0,
                              IPPROTO_IPIP, 0);
                err = 0;
                goto out;
@@ -188,14 +188,17 @@ static int ipip_rcv(struct sk_buff *skb)
        struct net *net = dev_net(skb->dev);
        struct ip_tunnel_net *itn = net_generic(net, ipip_net_id);
        struct ip_tunnel *tunnel;
-       const struct iphdr *iph = ip_hdr(skb);
+       const struct iphdr *iph;
 
+       iph = ip_hdr(skb);
        tunnel = ip_tunnel_lookup(itn, skb->dev->ifindex, TUNNEL_NO_KEY,
                        iph->saddr, iph->daddr, 0);
        if (tunnel) {
                if (!xfrm4_policy_check(NULL, XFRM_POLICY_IN, skb))
                        goto drop;
-               return ip_tunnel_rcv(tunnel, skb, &tpi, log_ecn_error);
+               if (iptunnel_pull_header(skb, 0, tpi.proto))
+                       goto drop;
+               return ip_tunnel_rcv(tunnel, skb, &tpi, NULL, log_ecn_error);
        }
 
        return -1;
@@ -217,17 +220,19 @@ static netdev_tx_t ipip_tunnel_xmit(struct sk_buff *skb, struct net_device *dev)
        if (unlikely(skb->protocol != htons(ETH_P_IP)))
                goto tx_error;
 
-       if (likely(!skb->encapsulation)) {
-               skb_reset_inner_headers(skb);
-               skb->encapsulation = 1;
-       }
+       skb = iptunnel_handle_offloads(skb, false, SKB_GSO_IPIP);
+       if (IS_ERR(skb))
+               goto out;
 
-       ip_tunnel_xmit(skb, dev, tiph);
+       skb_set_inner_ipproto(skb, IPPROTO_IPIP);
+
+       ip_tunnel_xmit(skb, dev, tiph, tiph->protocol);
        return NETDEV_TX_OK;
 
 tx_error:
+       kfree_skb(skb);
+out:
        dev->stats.tx_errors++;
-       dev_kfree_skb(skb);
        return NETDEV_TX_OK;
 }
 
@@ -240,14 +245,14 @@ ipip_tunnel_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
        if (copy_from_user(&p, ifr->ifr_ifru.ifru_data, sizeof(p)))
                return -EFAULT;
 
-       if (p.iph.version != 4 || p.iph.protocol != IPPROTO_IPIP ||
-                       p.iph.ihl != 5 || (p.iph.frag_off&htons(~IP_DF)))
-               return -EINVAL;
-       if (p.i_key || p.o_key || p.i_flags || p.o_flags)
-               return -EINVAL;
-       if (p.iph.ttl)
-               p.iph.frag_off |= htons(IP_DF);
+       if (cmd == SIOCADDTUNNEL || cmd == SIOCCHGTUNNEL) {
+               if (p.iph.version != 4 || p.iph.protocol != IPPROTO_IPIP ||
+                   p.iph.ihl != 5 || (p.iph.frag_off&htons(~IP_DF)))
+                       return -EINVAL;
+       }
 
+       p.i_key = p.o_key = 0;
+       p.i_flags = p.o_flags = 0;
        err = ip_tunnel_ioctl(dev, &p, cmd);
        if (err)
                return err;
@@ -265,11 +270,13 @@ static const struct net_device_ops ipip_netdev_ops = {
        .ndo_do_ioctl   = ipip_tunnel_ioctl,
        .ndo_change_mtu = ip_tunnel_change_mtu,
        .ndo_get_stats64 = ip_tunnel_get_stats64,
+       .ndo_get_iflink = ip_tunnel_get_iflink,
 };
 
 #define IPIP_FEATURES (NETIF_F_SG |            \
                       NETIF_F_FRAGLIST |       \
                       NETIF_F_HIGHDMA |        \
+                      NETIF_F_GSO_SOFTWARE |   \
                       NETIF_F_HW_CSUM)
 
 static void ipip_tunnel_setup(struct net_device *dev)
@@ -278,11 +285,9 @@ static void ipip_tunnel_setup(struct net_device *dev)
 
        dev->type               = ARPHRD_TUNNEL;
        dev->flags              = IFF_NOARP;
-       dev->iflink             = 0;
        dev->addr_len           = 4;
-       dev->features           |= NETIF_F_NETNS_LOCAL;
        dev->features           |= NETIF_F_LLTX;
-       dev->priv_flags         &= ~IFF_XMIT_DST_RELEASE;
+       netif_keep_dst(dev);
 
        dev->features           |= IPIP_FEATURES;
        dev->hw_features        |= IPIP_FEATURES;
@@ -296,7 +301,8 @@ static int ipip_tunnel_init(struct net_device *dev)
        memcpy(dev->dev_addr, &tunnel->parms.iph.saddr, 4);
        memcpy(dev->broadcast, &tunnel->parms.iph.daddr, 4);
 
-       tunnel->hlen = 0;
+       tunnel->tun_hlen = 0;
+       tunnel->hlen = tunnel->tun_hlen + tunnel->encap_hlen;
        tunnel->parms.iph.protocol = IPPROTO_IPIP;
        return ip_tunnel_init(dev);
 }
@@ -317,10 +323,10 @@ static void ipip_netlink_parms(struct nlattr *data[],
                parms->link = nla_get_u32(data[IFLA_IPTUN_LINK]);
 
        if (data[IFLA_IPTUN_LOCAL])
-               parms->iph.saddr = nla_get_be32(data[IFLA_IPTUN_LOCAL]);
+               parms->iph.saddr = nla_get_in_addr(data[IFLA_IPTUN_LOCAL]);
 
        if (data[IFLA_IPTUN_REMOTE])
-               parms->iph.daddr = nla_get_be32(data[IFLA_IPTUN_REMOTE]);
+               parms->iph.daddr = nla_get_in_addr(data[IFLA_IPTUN_REMOTE]);
 
        if (data[IFLA_IPTUN_TTL]) {
                parms->iph.ttl = nla_get_u8(data[IFLA_IPTUN_TTL]);
@@ -335,10 +341,53 @@ static void ipip_netlink_parms(struct nlattr *data[],
                parms->iph.frag_off = htons(IP_DF);
 }
 
+/* This function returns true when ENCAP attributes are present in the nl msg */
+static bool ipip_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_IPTUN_ENCAP_TYPE]) {
+               ret = true;
+               ipencap->type = nla_get_u16(data[IFLA_IPTUN_ENCAP_TYPE]);
+       }
+
+       if (data[IFLA_IPTUN_ENCAP_FLAGS]) {
+               ret = true;
+               ipencap->flags = nla_get_u16(data[IFLA_IPTUN_ENCAP_FLAGS]);
+       }
+
+       if (data[IFLA_IPTUN_ENCAP_SPORT]) {
+               ret = true;
+               ipencap->sport = nla_get_be16(data[IFLA_IPTUN_ENCAP_SPORT]);
+       }
+
+       if (data[IFLA_IPTUN_ENCAP_DPORT]) {
+               ret = true;
+               ipencap->dport = nla_get_be16(data[IFLA_IPTUN_ENCAP_DPORT]);
+       }
+
+       return ret;
+}
+
 static int ipip_newlink(struct net *src_net, struct net_device *dev,
                        struct nlattr *tb[], struct nlattr *data[])
 {
        struct ip_tunnel_parm p;
+       struct ip_tunnel_encap ipencap;
+
+       if (ipip_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;
+       }
 
        ipip_netlink_parms(data, &p);
        return ip_tunnel_newlink(dev, tb, &p);
@@ -348,6 +397,15 @@ static int ipip_changelink(struct net_device *dev, struct nlattr *tb[],
                           struct nlattr *data[])
 {
        struct ip_tunnel_parm p;
+       struct ip_tunnel_encap ipencap;
+
+       if (ipip_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;
+       }
 
        ipip_netlink_parms(data, &p);
 
@@ -373,6 +431,14 @@ static size_t ipip_get_size(const struct net_device *dev)
                nla_total_size(1) +
                /* IFLA_IPTUN_PMTUDISC */
                nla_total_size(1) +
+               /* IFLA_IPTUN_ENCAP_TYPE */
+               nla_total_size(2) +
+               /* IFLA_IPTUN_ENCAP_FLAGS */
+               nla_total_size(2) +
+               /* IFLA_IPTUN_ENCAP_SPORT */
+               nla_total_size(2) +
+               /* IFLA_IPTUN_ENCAP_DPORT */
+               nla_total_size(2) +
                0;
 }
 
@@ -382,13 +448,24 @@ static int ipip_fill_info(struct sk_buff *skb, const struct net_device *dev)
        struct ip_tunnel_parm *parm = &tunnel->parms;
 
        if (nla_put_u32(skb, IFLA_IPTUN_LINK, parm->link) ||
-           nla_put_be32(skb, IFLA_IPTUN_LOCAL, parm->iph.saddr) ||
-           nla_put_be32(skb, IFLA_IPTUN_REMOTE, parm->iph.daddr) ||
+           nla_put_in_addr(skb, IFLA_IPTUN_LOCAL, parm->iph.saddr) ||
+           nla_put_in_addr(skb, IFLA_IPTUN_REMOTE, parm->iph.daddr) ||
            nla_put_u8(skb, IFLA_IPTUN_TTL, parm->iph.ttl) ||
            nla_put_u8(skb, IFLA_IPTUN_TOS, parm->iph.tos) ||
            nla_put_u8(skb, IFLA_IPTUN_PMTUDISC,
                       !!(parm->iph.frag_off & htons(IP_DF))))
                goto nla_put_failure;
+
+       if (nla_put_u16(skb, IFLA_IPTUN_ENCAP_TYPE,
+                       tunnel->encap.type) ||
+           nla_put_be16(skb, IFLA_IPTUN_ENCAP_SPORT,
+                        tunnel->encap.sport) ||
+           nla_put_be16(skb, IFLA_IPTUN_ENCAP_DPORT,
+                        tunnel->encap.dport) ||
+           nla_put_u16(skb, IFLA_IPTUN_ENCAP_FLAGS,
+                       tunnel->encap.flags))
+               goto nla_put_failure;
+
        return 0;
 
 nla_put_failure:
@@ -402,6 +479,10 @@ static const struct nla_policy ipip_policy[IFLA_IPTUN_MAX + 1] = {
        [IFLA_IPTUN_TTL]                = { .type = NLA_U8 },
        [IFLA_IPTUN_TOS]                = { .type = NLA_U8 },
        [IFLA_IPTUN_PMTUDISC]           = { .type = NLA_U8 },
+       [IFLA_IPTUN_ENCAP_TYPE]         = { .type = NLA_U16 },
+       [IFLA_IPTUN_ENCAP_FLAGS]        = { .type = NLA_U16 },
+       [IFLA_IPTUN_ENCAP_SPORT]        = { .type = NLA_U16 },
+       [IFLA_IPTUN_ENCAP_DPORT]        = { .type = NLA_U16 },
 };
 
 static struct rtnl_link_ops ipip_link_ops __read_mostly = {
@@ -415,6 +496,7 @@ static struct rtnl_link_ops ipip_link_ops __read_mostly = {
        .dellink        = ip_tunnel_dellink,
        .get_size       = ipip_get_size,
        .fill_info      = ipip_fill_info,
+       .get_link_net   = ip_tunnel_get_link_net,
 };
 
 static struct xfrm_tunnel ipip_handler __read_mostly = {
@@ -431,7 +513,7 @@ static int __net_init ipip_init_net(struct net *net)
 static void __net_exit ipip_exit_net(struct net *net)
 {
        struct ip_tunnel_net *itn = net_generic(net, ipip_net_id);
-       ip_tunnel_delete_net(itn);
+       ip_tunnel_delete_net(itn, &ipip_link_ops);
 }
 
 static struct pernet_operations ipip_net_ops = {
@@ -481,4 +563,5 @@ static void __exit ipip_fini(void)
 module_init(ipip_init);
 module_exit(ipip_fini);
 MODULE_LICENSE("GPL");
+MODULE_ALIAS_RTNL_LINK("ipip");
 MODULE_ALIAS_NETDEV("tunl0");