]> git.proxmox.com Git - mirror_ubuntu-bionic-kernel.git/blobdiff - net/ipv6/ip6_output.c
ipv6: constify rt6_nexthop()
[mirror_ubuntu-bionic-kernel.git] / net / ipv6 / ip6_output.c
index 75b8c8a4374c367b97ebbb450f2669ebdd638ffd..5fceb667843a146255fe7beaa4100fc41fc3faab 100644 (file)
@@ -63,8 +63,8 @@ static int ip6_finish_output2(struct net *net, struct sock *sk, struct sk_buff *
 {
        struct dst_entry *dst = skb_dst(skb);
        struct net_device *dev = dst->dev;
+       const struct in6_addr *nexthop;
        struct neighbour *neigh;
-       struct in6_addr *nexthop;
        int ret;
 
        if (ipv6_addr_is_multicast(&ipv6_hdr(skb)->daddr)) {
@@ -195,39 +195,37 @@ int ip6_xmit(const struct sock *sk, struct sk_buff *skb, struct flowi6 *fl6,
        const struct ipv6_pinfo *np = inet6_sk(sk);
        struct in6_addr *first_hop = &fl6->daddr;
        struct dst_entry *dst = skb_dst(skb);
+       unsigned int head_room;
        struct ipv6hdr *hdr;
        u8  proto = fl6->flowi6_proto;
        int seg_len = skb->len;
        int hlimit = -1;
        u32 mtu;
 
-       if (opt) {
-               unsigned int head_room;
+       head_room = sizeof(struct ipv6hdr) + LL_RESERVED_SPACE(dst->dev);
+       if (opt)
+               head_room += opt->opt_nflen + opt->opt_flen;
 
-               /* First: exthdrs may take lots of space (~8K for now)
-                  MAX_HEADER is not enough.
-                */
-               head_room = opt->opt_nflen + opt->opt_flen;
-               seg_len += head_room;
-               head_room += sizeof(struct ipv6hdr) + LL_RESERVED_SPACE(dst->dev);
-
-               if (skb_headroom(skb) < head_room) {
-                       struct sk_buff *skb2 = skb_realloc_headroom(skb, head_room);
-                       if (!skb2) {
-                               IP6_INC_STATS(net, ip6_dst_idev(skb_dst(skb)),
-                                             IPSTATS_MIB_OUTDISCARDS);
-                               kfree_skb(skb);
-                               return -ENOBUFS;
-                       }
-                       consume_skb(skb);
-                       skb = skb2;
-                       /* skb_set_owner_w() changes sk->sk_wmem_alloc atomically,
-                        * it is safe to call in our context (socket lock not held)
-                        */
-                       skb_set_owner_w(skb, (struct sock *)sk);
+       if (unlikely(skb_headroom(skb) < head_room)) {
+               struct sk_buff *skb2 = skb_realloc_headroom(skb, head_room);
+               if (!skb2) {
+                       IP6_INC_STATS(net, ip6_dst_idev(skb_dst(skb)),
+                                     IPSTATS_MIB_OUTDISCARDS);
+                       kfree_skb(skb);
+                       return -ENOBUFS;
                }
+               if (skb->sk)
+                       skb_set_owner_w(skb2, skb->sk);
+               consume_skb(skb);
+               skb = skb2;
+       }
+
+       if (opt) {
+               seg_len += opt->opt_nflen + opt->opt_flen;
+
                if (opt->opt_flen)
                        ipv6_push_frag_opts(skb, opt, &proto);
+
                if (opt->opt_nflen)
                        ipv6_push_nfrag_opts(skb, opt, &proto, &first_hop,
                                             &fl6->saddr);
@@ -375,6 +373,12 @@ static int ip6_forward_proxy_check(struct sk_buff *skb)
 static inline int ip6_forward_finish(struct net *net, struct sock *sk,
                                     struct sk_buff *skb)
 {
+       struct dst_entry *dst = skb_dst(skb);
+
+       __IP6_INC_STATS(net, ip6_dst_idev(dst), IPSTATS_MIB_OUTFORWDATAGRAMS);
+       __IP6_ADD_STATS(net, ip6_dst_idev(dst), IPSTATS_MIB_OUTOCTETS, skb->len);
+
+       skb->tstamp = 0;
        return dst_output(net, sk, skb);
 }
 
@@ -501,7 +505,8 @@ int ip6_forward(struct sk_buff *skb)
           send redirects to source routed frames.
           We don't send redirects to frames decapsulated from IPsec.
         */
-       if (skb->dev == dst->dev && opt->srcrt == 0 && !skb_sec_path(skb)) {
+       if (IP6CB(skb)->iif == dst->dev->ifindex &&
+           opt->srcrt == 0 && !skb_sec_path(skb)) {
                struct in6_addr *target = NULL;
                struct inet_peer *peer;
                struct rt6_info *rt;
@@ -568,8 +573,6 @@ int ip6_forward(struct sk_buff *skb)
 
        hdr->hop_limit--;
 
-       __IP6_INC_STATS(net, ip6_dst_idev(dst), IPSTATS_MIB_OUTFORWDATAGRAMS);
-       __IP6_ADD_STATS(net, ip6_dst_idev(dst), IPSTATS_MIB_OUTOCTETS, skb->len);
        return NF_HOOK(NFPROTO_IPV6, NF_INET_FORWARD,
                       net, NULL, skb, skb->dev, dst->dev,
                       ip6_forward_finish);
@@ -591,6 +594,8 @@ static void ip6_copy_metadata(struct sk_buff *to, struct sk_buff *from)
        to->dev = from->dev;
        to->mark = from->mark;
 
+       skb_copy_hash(to, from);
+
 #ifdef CONFIG_NET_SCHED
        to->tc_index = from->tc_index;
 #endif
@@ -607,7 +612,7 @@ int ip6_fragment(struct net *net, struct sock *sk, struct sk_buff *skb,
                                inet6_sk(skb->sk) : NULL;
        struct ipv6hdr *tmp_hdr;
        struct frag_hdr *fh;
-       unsigned int mtu, hlen, left, len;
+       unsigned int mtu, hlen, left, len, nexthdr_offset;
        int hroom, troom;
        __be32 frag_id;
        int ptr, offset = 0, err = 0;
@@ -618,6 +623,7 @@ int ip6_fragment(struct net *net, struct sock *sk, struct sk_buff *skb,
                goto fail;
        hlen = err;
        nexthdr = *prevhdr;
+       nexthdr_offset = prevhdr - skb_network_header(skb);
 
        mtu = ip6_skb_dst_mtu(skb);
 
@@ -652,6 +658,7 @@ int ip6_fragment(struct net *net, struct sock *sk, struct sk_buff *skb,
            (err = skb_checksum_help(skb)))
                goto fail;
 
+       prevhdr = skb_network_header(skb) + nexthdr_offset;
        hroom = LL_RESERVED_SPACE(rt->dst.dev);
        if (skb_has_frag_list(skb)) {
                unsigned int first_len = skb_pagelen(skb);
@@ -1485,7 +1492,8 @@ alloc_new_skb:
                if (copy > length)
                        copy = length;
 
-               if (!(rt->dst.dev->features&NETIF_F_SG)) {
+               if (!(rt->dst.dev->features&NETIF_F_SG) &&
+                   skb_tailroom(skb) >= copy) {
                        unsigned int off;
 
                        off = skb->len;