]> git.proxmox.com Git - mirror_ubuntu-bionic-kernel.git/blobdiff - net/ipv6/sit.c
net: better IFF_XMIT_DST_RELEASE support
[mirror_ubuntu-bionic-kernel.git] / net / ipv6 / sit.c
index 6163f851dc014ebd205211829214ce4d0bbc332b..6eab37cf53451b0f8112e51bff2437cb2b4da213 100644 (file)
@@ -812,9 +812,9 @@ static netdev_tx_t ipip6_tunnel_xmit(struct sk_buff *skb,
        const struct ipv6hdr *iph6 = ipv6_hdr(skb);
        u8     tos = tunnel->parms.iph.tos;
        __be16 df = tiph->frag_off;
-       struct rtable *rt;                      /* Route to the other host */
-       struct net_device *tdev;                /* Device to other host */
-       unsigned int max_headroom;              /* The extra header space needed */
+       struct rtable *rt;              /* Route to the other host */
+       struct net_device *tdev;        /* Device to other host */
+       unsigned int max_headroom;      /* The extra header space needed */
        __be32 dst = tiph->daddr;
        struct flowi4 fl4;
        int    mtu;
@@ -822,6 +822,8 @@ static netdev_tx_t ipip6_tunnel_xmit(struct sk_buff *skb,
        int addr_type;
        u8 ttl;
        int err;
+       u8 protocol = IPPROTO_IPV6;
+       int t_hlen = tunnel->hlen + sizeof(struct iphdr);
 
        if (skb->protocol != htons(ETH_P_IPV6))
                goto tx_error;
@@ -911,8 +913,14 @@ static netdev_tx_t ipip6_tunnel_xmit(struct sk_buff *skb,
                goto tx_error;
        }
 
+       skb = iptunnel_handle_offloads(skb, false, SKB_GSO_SIT);
+       if (IS_ERR(skb)) {
+               ip_rt_put(rt);
+               goto out;
+       }
+
        if (df) {
-               mtu = dst_mtu(&rt->dst) - sizeof(struct iphdr);
+               mtu = dst_mtu(&rt->dst) - t_hlen;
 
                if (mtu < 68) {
                        dev->stats.collisions++;
@@ -947,7 +955,7 @@ static netdev_tx_t ipip6_tunnel_xmit(struct sk_buff *skb,
        /*
         * Okay, now see if we can stuff it in the buffer as-is.
         */
-       max_headroom = LL_RESERVED_SPACE(tdev)+sizeof(struct iphdr);
+       max_headroom = LL_RESERVED_SPACE(tdev) + t_hlen;
 
        if (skb_headroom(skb) < max_headroom || skb_shared(skb) ||
            (skb_cloned(skb) && !skb_clone_writable(skb, 0))) {
@@ -969,14 +977,15 @@ static netdev_tx_t ipip6_tunnel_xmit(struct sk_buff *skb,
                ttl = iph6->hop_limit;
        tos = INET_ECN_encapsulate(tos, ipv6_get_dsfield(iph6));
 
-       skb = iptunnel_handle_offloads(skb, false, SKB_GSO_SIT);
-       if (IS_ERR(skb)) {
+       if (ip_tunnel_encap(skb, tunnel, &protocol, &fl4) < 0) {
                ip_rt_put(rt);
-               goto out;
+               goto tx_error;
        }
 
+       skb_set_inner_ipproto(skb, IPPROTO_IPV6);
+
        err = iptunnel_xmit(skb->sk, rt, skb, fl4.saddr, fl4.daddr,
-                           IPPROTO_IPV6, tos, ttl, df,
+                           protocol, tos, ttl, df,
                            !net_eq(tunnel->net, dev_net(dev)));
        iptunnel_xmit_stats(err, &dev->stats, dev->tstats);
        return NETDEV_TX_OK;
@@ -999,6 +1008,8 @@ static netdev_tx_t ipip_tunnel_xmit(struct sk_buff *skb, struct net_device *dev)
        if (IS_ERR(skb))
                goto out;
 
+       skb_set_inner_ipproto(skb, IPPROTO_IPIP);
+
        ip_tunnel_xmit(skb, dev, tiph, IPPROTO_IPIP);
        return NETDEV_TX_OK;
 out:
@@ -1059,8 +1070,10 @@ static void ipip6_tunnel_bind_dev(struct net_device *dev)
                tdev = __dev_get_by_index(tunnel->net, tunnel->parms.link);
 
        if (tdev) {
+               int t_hlen = tunnel->hlen + sizeof(struct iphdr);
+
                dev->hard_header_len = tdev->hard_header_len + sizeof(struct iphdr);
-               dev->mtu = tdev->mtu - sizeof(struct iphdr);
+               dev->mtu = tdev->mtu - t_hlen;
                if (dev->mtu < IPV6_MIN_MTU)
                        dev->mtu = IPV6_MIN_MTU;
        }
@@ -1123,7 +1136,7 @@ static int ipip6_tunnel_update_6rd(struct ip_tunnel *t,
 #endif
 
 static int
-ipip6_tunnel_ioctl (struct net_device *dev, struct ifreq *ifr, int cmd)
+ipip6_tunnel_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
 {
        int err = 0;
        struct ip_tunnel_parm p;
@@ -1307,7 +1320,10 @@ done:
 
 static int ipip6_tunnel_change_mtu(struct net_device *dev, int new_mtu)
 {
-       if (new_mtu < IPV6_MIN_MTU || new_mtu > 0xFFF8 - sizeof(struct iphdr))
+       struct ip_tunnel *tunnel = netdev_priv(dev);
+       int t_hlen = tunnel->hlen + sizeof(struct iphdr);
+
+       if (new_mtu < IPV6_MIN_MTU || new_mtu > 0xFFF8 - t_hlen)
                return -EINVAL;
        dev->mtu = new_mtu;
        return 0;
@@ -1338,14 +1354,17 @@ static void ipip6_dev_free(struct net_device *dev)
 
 static void ipip6_tunnel_setup(struct net_device *dev)
 {
+       struct ip_tunnel *tunnel = netdev_priv(dev);
+       int t_hlen = tunnel->hlen + sizeof(struct iphdr);
+
        dev->netdev_ops         = &ipip6_netdev_ops;
-       dev->destructor         = ipip6_dev_free;
+       dev->destructor         = ipip6_dev_free;
 
        dev->type               = ARPHRD_SIT;
-       dev->hard_header_len    = LL_MAX_HEADER + sizeof(struct iphdr);
-       dev->mtu                = ETH_DATA_LEN - sizeof(struct iphdr);
+       dev->hard_header_len    = LL_MAX_HEADER + t_hlen;
+       dev->mtu                = ETH_DATA_LEN - t_hlen;
        dev->flags              = IFF_NOARP;
-       dev->priv_flags        &= ~IFF_XMIT_DST_RELEASE;
+       netif_keep_dst(dev);
        dev->iflink             = 0;
        dev->addr_len           = 4;
        dev->features           |= NETIF_F_LLTX;
@@ -1466,6 +1485,40 @@ static void ipip6_netlink_parms(struct nlattr *data[],
 
 }
 
+/* This function returns true when ENCAP attributes are present in the nl msg */
+static bool ipip6_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_u16(data[IFLA_IPTUN_ENCAP_SPORT]);
+       }
+
+       if (data[IFLA_IPTUN_ENCAP_DPORT]) {
+               ret = true;
+               ipencap->dport = nla_get_u16(data[IFLA_IPTUN_ENCAP_DPORT]);
+       }
+
+       return ret;
+}
+
 #ifdef CONFIG_IPV6_SIT_6RD
 /* This function returns true when 6RD attributes are present in the nl msg */
 static bool ipip6_netlink_6rd_parms(struct nlattr *data[],
@@ -1509,12 +1562,20 @@ static int ipip6_newlink(struct net *src_net, struct net_device *dev,
 {
        struct net *net = dev_net(dev);
        struct ip_tunnel *nt;
+       struct ip_tunnel_encap ipencap;
 #ifdef CONFIG_IPV6_SIT_6RD
        struct ip_tunnel_6rd ip6rd;
 #endif
        int err;
 
        nt = netdev_priv(dev);
+
+       if (ipip6_netlink_encap_parms(data, &ipencap)) {
+               err = ip_tunnel_encap_setup(nt, &ipencap);
+               if (err < 0)
+                       return err;
+       }
+
        ipip6_netlink_parms(data, &nt->parms);
 
        if (ipip6_tunnel_locate(net, &nt->parms, 0))
@@ -1537,15 +1598,23 @@ static int ipip6_changelink(struct net_device *dev, struct nlattr *tb[],
 {
        struct ip_tunnel *t = netdev_priv(dev);
        struct ip_tunnel_parm p;
+       struct ip_tunnel_encap ipencap;
        struct net *net = t->net;
        struct sit_net *sitn = net_generic(net, sit_net_id);
 #ifdef CONFIG_IPV6_SIT_6RD
        struct ip_tunnel_6rd ip6rd;
 #endif
+       int err;
 
        if (dev == sitn->fb_tunnel_dev)
                return -EINVAL;
 
+       if (ipip6_netlink_encap_parms(data, &ipencap)) {
+               err = ip_tunnel_encap_setup(t, &ipencap);
+               if (err < 0)
+                       return err;
+       }
+
        ipip6_netlink_parms(data, &p);
 
        if (((dev->flags & IFF_POINTOPOINT) && !p.iph.daddr) ||
@@ -1599,6 +1668,14 @@ static size_t ipip6_get_size(const struct net_device *dev)
                /* IFLA_IPTUN_6RD_RELAY_PREFIXLEN */
                nla_total_size(2) +
 #endif
+               /* 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;
 }
 
@@ -1630,6 +1707,16 @@ static int ipip6_fill_info(struct sk_buff *skb, const struct net_device *dev)
                goto nla_put_failure;
 #endif
 
+       if (nla_put_u16(skb, IFLA_IPTUN_ENCAP_TYPE,
+                       tunnel->encap.type) ||
+           nla_put_u16(skb, IFLA_IPTUN_ENCAP_SPORT,
+                       tunnel->encap.sport) ||
+           nla_put_u16(skb, IFLA_IPTUN_ENCAP_DPORT,
+                       tunnel->encap.dport) ||
+           nla_put_u16(skb, IFLA_IPTUN_ENCAP_FLAGS,
+                       tunnel->encap.dport))
+               goto nla_put_failure;
+
        return 0;
 
 nla_put_failure:
@@ -1651,6 +1738,10 @@ static const struct nla_policy ipip6_policy[IFLA_IPTUN_MAX + 1] = {
        [IFLA_IPTUN_6RD_PREFIXLEN]      = { .type = NLA_U16 },
        [IFLA_IPTUN_6RD_RELAY_PREFIXLEN] = { .type = NLA_U16 },
 #endif
+       [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 void ipip6_dellink(struct net_device *dev, struct list_head *head)