#include <linux/tcp.h>
#include <linux/route.h>
#include <linux/module.h>
+#include <linux/slab.h>
#include <linux/netfilter.h>
#include <linux/netfilter_ipv6.h>
static int ip6_fragment(struct sk_buff *skb, int (*output)(struct sk_buff *));
-static __inline__ void ipv6_select_ident(struct sk_buff *skb, struct frag_hdr *fhdr)
-{
- static u32 ipv6_fragmentation_id = 1;
- static DEFINE_SPINLOCK(ip6_id_lock);
-
- spin_lock_bh(&ip6_id_lock);
- fhdr->identification = htonl(ipv6_fragmentation_id);
- if (++ipv6_fragmentation_id == 0)
- ipv6_fragmentation_id = 1;
- spin_unlock_bh(&ip6_id_lock);
-}
-
int __ip6_local_out(struct sk_buff *skb)
{
int len;
len = 0;
ipv6_hdr(skb)->payload_len = htons(len);
- return nf_hook(PF_INET6, NF_INET_LOCAL_OUT, skb, NULL, skb->dst->dev,
- dst_output);
+ return nf_hook(NFPROTO_IPV6, NF_INET_LOCAL_OUT, skb, NULL,
+ skb_dst(skb)->dev, dst_output);
}
int ip6_local_out(struct sk_buff *skb)
}
EXPORT_SYMBOL_GPL(ip6_local_out);
-static int ip6_output_finish(struct sk_buff *skb)
-{
- struct dst_entry *dst = skb->dst;
-
- if (dst->hh)
- return neigh_hh_output(dst->hh, skb);
- else if (dst->neighbour)
- return dst->neighbour->output(skb);
-
- IP6_INC_STATS_BH(dev_net(dst->dev),
- ip6_dst_idev(dst), IPSTATS_MIB_OUTNOROUTES);
- kfree_skb(skb);
- return -EINVAL;
-
-}
-
/* dev_loopback_xmit for use with netfilter. */
static int ip6_dev_loopback_xmit(struct sk_buff *newskb)
{
__skb_pull(newskb, skb_network_offset(newskb));
newskb->pkt_type = PACKET_LOOPBACK;
newskb->ip_summed = CHECKSUM_UNNECESSARY;
- WARN_ON(!newskb->dst);
+ WARN_ON(!skb_dst(newskb));
- netif_rx(newskb);
+ netif_rx_ni(newskb);
return 0;
}
-
-static int ip6_output2(struct sk_buff *skb)
+static int ip6_finish_output2(struct sk_buff *skb)
{
- struct dst_entry *dst = skb->dst;
+ struct dst_entry *dst = skb_dst(skb);
struct net_device *dev = dst->dev;
skb->protocol = htons(ETH_P_IPV6);
skb->dev = dev;
if (ipv6_addr_is_multicast(&ipv6_hdr(skb)->daddr)) {
- struct ipv6_pinfo* np = skb->sk ? inet6_sk(skb->sk) : NULL;
- struct inet6_dev *idev = ip6_dst_idev(skb->dst);
+ struct inet6_dev *idev = ip6_dst_idev(skb_dst(skb));
- if (!(dev->flags & IFF_LOOPBACK) && (!np || np->mc_loop) &&
- ((mroute6_socket && !(IP6CB(skb)->flags & IP6SKB_FORWARDED)) ||
+ if (!(dev->flags & IFF_LOOPBACK) && sk_mc_loop(skb->sk) &&
+ ((mroute6_socket(dev_net(dev), skb) &&
+ !(IP6CB(skb)->flags & IP6SKB_FORWARDED)) ||
ipv6_chk_mcast_addr(dev, &ipv6_hdr(skb)->daddr,
&ipv6_hdr(skb)->saddr))) {
struct sk_buff *newskb = skb_clone(skb, GFP_ATOMIC);
is not supported in any case.
*/
if (newskb)
- NF_HOOK(PF_INET6, NF_INET_POST_ROUTING, newskb,
- NULL, newskb->dev,
+ NF_HOOK(NFPROTO_IPV6, NF_INET_POST_ROUTING,
+ newskb, NULL, newskb->dev,
ip6_dev_loopback_xmit);
if (ipv6_hdr(skb)->hop_limit == 0) {
}
}
- IP6_INC_STATS(dev_net(dev), idev, IPSTATS_MIB_OUTMCASTPKTS);
+ IP6_UPD_PO_STATS(dev_net(dev), idev, IPSTATS_MIB_OUTMCAST,
+ skb->len);
}
- return NF_HOOK(PF_INET6, NF_INET_POST_ROUTING, skb, NULL, skb->dev,
- ip6_output_finish);
+ if (dst->hh)
+ return neigh_hh_output(dst->hh, skb);
+ else if (dst->neighbour)
+ return dst->neighbour->output(skb);
+
+ IP6_INC_STATS_BH(dev_net(dst->dev),
+ ip6_dst_idev(dst), IPSTATS_MIB_OUTNOROUTES);
+ kfree_skb(skb);
+ return -EINVAL;
}
static inline int ip6_skb_dst_mtu(struct sk_buff *skb)
struct ipv6_pinfo *np = skb->sk ? inet6_sk(skb->sk) : NULL;
return (np && np->pmtudisc == IPV6_PMTUDISC_PROBE) ?
- skb->dst->dev->mtu : dst_mtu(skb->dst);
+ skb_dst(skb)->dev->mtu : dst_mtu(skb_dst(skb));
+}
+
+static int ip6_finish_output(struct sk_buff *skb)
+{
+ if ((skb->len > ip6_skb_dst_mtu(skb) && !skb_is_gso(skb)) ||
+ dst_allfrag(skb_dst(skb)))
+ return ip6_fragment(skb, ip6_finish_output2);
+ else
+ return ip6_finish_output2(skb);
}
int ip6_output(struct sk_buff *skb)
{
- struct inet6_dev *idev = ip6_dst_idev(skb->dst);
+ struct net_device *dev = skb_dst(skb)->dev;
+ struct inet6_dev *idev = ip6_dst_idev(skb_dst(skb));
if (unlikely(idev->cnf.disable_ipv6)) {
- IP6_INC_STATS(dev_net(skb->dst->dev), idev,
+ IP6_INC_STATS(dev_net(dev), idev,
IPSTATS_MIB_OUTDISCARDS);
kfree_skb(skb);
return 0;
}
- if ((skb->len > ip6_skb_dst_mtu(skb) && !skb_is_gso(skb)) ||
- dst_allfrag(skb->dst))
- return ip6_fragment(skb, ip6_output2);
- else
- return ip6_output2(skb);
+ return NF_HOOK_COND(NFPROTO_IPV6, NF_INET_POST_ROUTING, skb, NULL, dev,
+ ip6_finish_output,
+ !(IP6CB(skb)->flags & IP6SKB_REROUTED));
}
/*
- * xmit an sk_buff (used by TCP)
+ * xmit an sk_buff (used by TCP, SCTP and DCCP)
*/
int ip6_xmit(struct sock *sk, struct sk_buff *skb, struct flowi *fl,
- struct ipv6_txoptions *opt, int ipfragok)
+ struct ipv6_txoptions *opt)
{
struct net *net = sock_net(sk);
struct ipv6_pinfo *np = inet6_sk(sk);
struct in6_addr *first_hop = &fl->fl6_dst;
- struct dst_entry *dst = skb->dst;
+ struct dst_entry *dst = skb_dst(skb);
struct ipv6hdr *hdr;
u8 proto = fl->proto;
int seg_len = skb->len;
- int hlimit, tclass;
+ int hlimit = -1;
+ int tclass = 0;
u32 mtu;
if (opt) {
if (skb_headroom(skb) < head_room) {
struct sk_buff *skb2 = skb_realloc_headroom(skb, head_room);
if (skb2 == NULL) {
- IP6_INC_STATS(net, ip6_dst_idev(skb->dst),
+ IP6_INC_STATS(net, ip6_dst_idev(skb_dst(skb)),
IPSTATS_MIB_OUTDISCARDS);
kfree_skb(skb);
return -ENOBUFS;
}
kfree_skb(skb);
skb = skb2;
- if (sk)
- skb_set_owner_w(skb, sk);
+ skb_set_owner_w(skb, sk);
}
if (opt->opt_flen)
ipv6_push_frag_opts(skb, opt, &proto);
skb_reset_network_header(skb);
hdr = ipv6_hdr(skb);
- /* Allow local fragmentation. */
- if (ipfragok)
- skb->local_df = 1;
-
/*
* Fill in the IPv6 header
*/
-
- hlimit = -1;
- if (np)
+ if (np) {
+ tclass = np->tclass;
hlimit = np->hop_limit;
+ }
if (hlimit < 0)
hlimit = ip6_dst_hoplimit(dst);
- tclass = -1;
- if (np)
- tclass = np->tclass;
- if (tclass < 0)
- tclass = 0;
-
*(__be32 *)hdr = htonl(0x60000000 | (tclass << 20)) | fl->fl6_flowlabel;
hdr->payload_len = htons(seg_len);
mtu = dst_mtu(dst);
if ((skb->len <= mtu) || skb->local_df || skb_is_gso(skb)) {
- IP6_INC_STATS(net, ip6_dst_idev(skb->dst),
- IPSTATS_MIB_OUTREQUESTS);
- return NF_HOOK(PF_INET6, NF_INET_LOCAL_OUT, skb, NULL, dst->dev,
- dst_output);
+ IP6_UPD_PO_STATS(net, ip6_dst_idev(skb_dst(skb)),
+ IPSTATS_MIB_OUT, skb->len);
+ return NF_HOOK(NFPROTO_IPV6, NF_INET_LOCAL_OUT, skb, NULL,
+ dst->dev, dst_output);
}
if (net_ratelimit())
printk(KERN_DEBUG "IPv6: sending pkt_too_big to self\n");
skb->dev = dst->dev;
- icmpv6_send(skb, ICMPV6_PKT_TOOBIG, 0, mtu, skb->dev);
- IP6_INC_STATS(net, ip6_dst_idev(skb->dst), IPSTATS_MIB_FRAGFAILS);
+ icmpv6_send(skb, ICMPV6_PKT_TOOBIG, 0, mtu);
+ IP6_INC_STATS(net, ip6_dst_idev(skb_dst(skb)), IPSTATS_MIB_FRAGFAILS);
kfree_skb(skb);
return -EMSGSIZE;
}
int ip6_forward(struct sk_buff *skb)
{
- struct dst_entry *dst = skb->dst;
+ struct dst_entry *dst = skb_dst(skb);
struct ipv6hdr *hdr = ipv6_hdr(skb);
struct inet6_skb_parm *opt = IP6CB(skb);
struct net *net = dev_net(dst->dev);
+ u32 mtu;
if (net->ipv6.devconf_all->forwarding == 0)
goto error;
if (hdr->hop_limit <= 1) {
/* Force OUTPUT device used as source address */
skb->dev = dst->dev;
- icmpv6_send(skb, ICMPV6_TIME_EXCEED, ICMPV6_EXC_HOPLIMIT,
- 0, skb->dev);
+ icmpv6_send(skb, ICMPV6_TIME_EXCEED, ICMPV6_EXC_HOPLIMIT, 0);
IP6_INC_STATS_BH(net,
ip6_dst_idev(dst), IPSTATS_MIB_INHDRERRORS);
IP6_INC_STATS(net, ip6_dst_idev(dst), IPSTATS_MIB_INDISCARDS);
goto drop;
}
- dst = skb->dst;
+ dst = skb_dst(skb);
/* IPv6 specs say nothing about it, but it is clear that we cannot
send redirects to source routed frames.
We don't send redirects to frames decapsulated from IPsec.
*/
if (skb->dev == dst->dev && dst->neighbour && opt->srcrt == 0 &&
- !skb->sp) {
+ !skb_sec_path(skb)) {
struct in6_addr *target = NULL;
struct rt6_info *rt;
struct neighbour *n = dst->neighbour;
goto error;
if (addrtype & IPV6_ADDR_LINKLOCAL) {
icmpv6_send(skb, ICMPV6_DEST_UNREACH,
- ICMPV6_NOT_NEIGHBOUR, 0, skb->dev);
+ ICMPV6_NOT_NEIGHBOUR, 0);
goto error;
}
}
- if (skb->len > dst_mtu(dst)) {
+ mtu = dst_mtu(dst);
+ if (mtu < IPV6_MIN_MTU)
+ mtu = IPV6_MIN_MTU;
+
+ if (skb->len > mtu && !skb_is_gso(skb)) {
/* Again, force OUTPUT device used as source address */
skb->dev = dst->dev;
- icmpv6_send(skb, ICMPV6_PKT_TOOBIG, 0, dst_mtu(dst), skb->dev);
+ icmpv6_send(skb, ICMPV6_PKT_TOOBIG, 0, mtu);
IP6_INC_STATS_BH(net,
ip6_dst_idev(dst), IPSTATS_MIB_INTOOBIGERRORS);
IP6_INC_STATS_BH(net,
hdr->hop_limit--;
IP6_INC_STATS_BH(net, ip6_dst_idev(dst), IPSTATS_MIB_OUTFORWDATAGRAMS);
- return NF_HOOK(PF_INET6, NF_INET_FORWARD, skb, skb->dev, dst->dev,
+ return NF_HOOK(NFPROTO_IPV6, NF_INET_FORWARD, skb, skb->dev, dst->dev,
ip6_forward_finish);
error:
to->pkt_type = from->pkt_type;
to->priority = from->priority;
to->protocol = from->protocol;
- dst_release(to->dst);
- to->dst = dst_clone(from->dst);
+ skb_dst_drop(to);
+ skb_dst_set(to, dst_clone(skb_dst(from)));
to->dev = from->dev;
to->mark = from->mark;
static int ip6_fragment(struct sk_buff *skb, int (*output)(struct sk_buff *))
{
struct sk_buff *frag;
- struct rt6_info *rt = (struct rt6_info*)skb->dst;
+ struct rt6_info *rt = (struct rt6_info*)skb_dst(skb);
struct ipv6_pinfo *np = skb->sk ? inet6_sk(skb->sk) : NULL;
struct ipv6hdr *tmp_hdr;
struct frag_hdr *fh;
__be32 frag_id = 0;
int ptr, offset = 0, err=0;
u8 *prevhdr, nexthdr = 0;
- struct net *net = dev_net(skb->dst->dev);
+ struct net *net = dev_net(skb_dst(skb)->dev);
hlen = ip6_find_1stfragopt(skb, &prevhdr);
nexthdr = *prevhdr;
mtu = ip6_skb_dst_mtu(skb);
/* We must not fragment if the socket is set to force MTU discovery
- * or if the skb it not generated by a local socket. (This last
- * check should be redundant, but it's free.)
+ * or if the skb it not generated by a local socket.
*/
- if (!skb->local_df) {
- skb->dev = skb->dst->dev;
- icmpv6_send(skb, ICMPV6_PKT_TOOBIG, 0, mtu, skb->dev);
- IP6_INC_STATS(net, ip6_dst_idev(skb->dst),
+ if (!skb->local_df && skb->len > mtu) {
+ skb->dev = skb_dst(skb)->dev;
+ icmpv6_send(skb, ICMPV6_PKT_TOOBIG, 0, mtu);
+ IP6_INC_STATS(net, ip6_dst_idev(skb_dst(skb)),
IPSTATS_MIB_FRAGFAILS);
kfree_skb(skb);
return -EMSGSIZE;
}
mtu -= hlen + sizeof(struct frag_hdr);
- if (skb_shinfo(skb)->frag_list) {
+ if (skb_has_frags(skb)) {
int first_len = skb_pagelen(skb);
int truesizes = 0;
skb_cloned(skb))
goto slow_path;
- for (frag = skb_shinfo(skb)->frag_list; frag; frag = frag->next) {
+ skb_walk_frags(skb, frag) {
/* Correct geometry. */
if (frag->len > mtu ||
((frag->len & 7) && frag->next) ||
BUG_ON(frag->sk);
if (skb->sk) {
- sock_hold(skb->sk);
frag->sk = skb->sk;
frag->destructor = sock_wfree;
truesizes += frag->truesize;
err = 0;
offset = 0;
frag = skb_shinfo(skb)->frag_list;
- skb_shinfo(skb)->frag_list = NULL;
+ skb_frag_list_init(skb);
/* BUILD HEADER */
*prevhdr = NEXTHDR_FRAGMENT;
tmp_hdr = kmemdup(skb_network_header(skb), hlen, GFP_ATOMIC);
if (!tmp_hdr) {
- IP6_INC_STATS(net, ip6_dst_idev(skb->dst),
+ IP6_INC_STATS(net, ip6_dst_idev(skb_dst(skb)),
IPSTATS_MIB_FRAGFAILS);
return -ENOMEM;
}
skb_reset_network_header(skb);
memcpy(skb_network_header(skb), tmp_hdr, hlen);
- ipv6_select_ident(skb, fh);
+ ipv6_select_ident(fh);
fh->nexthdr = nexthdr;
fh->reserved = 0;
fh->frag_off = htons(IP6_MF);
if ((frag = alloc_skb(len+hlen+sizeof(struct frag_hdr)+LL_ALLOCATED_SPACE(rt->u.dst.dev), GFP_ATOMIC)) == NULL) {
NETDEBUG(KERN_INFO "IPv6: frag: no memory for new fragment!\n");
- IP6_INC_STATS(net, ip6_dst_idev(skb->dst),
+ IP6_INC_STATS(net, ip6_dst_idev(skb_dst(skb)),
IPSTATS_MIB_FRAGFAILS);
err = -ENOMEM;
goto fail;
fh->nexthdr = nexthdr;
fh->reserved = 0;
if (!frag_id) {
- ipv6_select_ident(skb, fh);
+ ipv6_select_ident(fh);
frag_id = fh->identification;
} else
fh->identification = frag_id;
if (err)
goto fail;
- IP6_INC_STATS(net, ip6_dst_idev(skb->dst),
+ IP6_INC_STATS(net, ip6_dst_idev(skb_dst(skb)),
IPSTATS_MIB_FRAGCREATES);
}
- IP6_INC_STATS(net, ip6_dst_idev(skb->dst),
+ IP6_INC_STATS(net, ip6_dst_idev(skb_dst(skb)),
IPSTATS_MIB_FRAGOKS);
kfree_skb(skb);
return err;
fail:
- IP6_INC_STATS(net, ip6_dst_idev(skb->dst),
+ IP6_INC_STATS(net, ip6_dst_idev(skb_dst(skb)),
IPSTATS_MIB_FRAGFAILS);
kfree_skb(skb);
return err;
if (!err) {
struct frag_hdr fhdr;
- /* specify the length of each IP datagram fragment*/
- skb_shinfo(skb)->gso_size = mtu - fragheaderlen -
- sizeof(struct frag_hdr);
+ /* Specify the length of each IPv6 datagram fragment.
+ * It has to be a multiple of 8.
+ */
+ skb_shinfo(skb)->gso_size = (mtu - fragheaderlen -
+ sizeof(struct frag_hdr)) & ~7;
skb_shinfo(skb)->gso_type = SKB_GSO_UDP;
- ipv6_select_ident(skb, &fhdr);
+ ipv6_select_ident(&fhdr);
skb_shinfo(skb)->ip6_frag_id = fhdr.identification;
__skb_queue_tail(&sk->sk_write_queue, skb);
return err;
}
+static inline struct ipv6_opt_hdr *ip6_opt_dup(struct ipv6_opt_hdr *src,
+ gfp_t gfp)
+{
+ return src ? kmemdup(src, (src->hdrlen + 1) * 8, gfp) : NULL;
+}
+
+static inline struct ipv6_rt_hdr *ip6_rthdr_dup(struct ipv6_rt_hdr *src,
+ gfp_t gfp)
+{
+ return src ? kmemdup(src, (src->hdrlen + 1) * 8, gfp) : NULL;
+}
+
int ip6_append_data(struct sock *sk, int getfrag(void *from, char *to,
int offset, int len, int odd, struct sk_buff *skb),
void *from, int length, int transhdrlen,
int hlimit, int tclass, struct ipv6_txoptions *opt, struct flowi *fl,
- struct rt6_info *rt, unsigned int flags)
+ struct rt6_info *rt, unsigned int flags, int dontfrag)
{
struct inet_sock *inet = inet_sk(sk);
struct ipv6_pinfo *np = inet6_sk(sk);
* setup for corking
*/
if (opt) {
- if (np->cork.opt == NULL) {
- np->cork.opt = kmalloc(opt->tot_len,
- sk->sk_allocation);
- if (unlikely(np->cork.opt == NULL))
- return -ENOBUFS;
- } else if (np->cork.opt->tot_len < opt->tot_len) {
- printk(KERN_DEBUG "ip6_append_data: invalid option length\n");
+ if (WARN_ON(np->cork.opt))
return -EINVAL;
- }
- memcpy(np->cork.opt, opt, opt->tot_len);
- inet->cork.flags |= IPCORK_OPT;
+
+ np->cork.opt = kmalloc(opt->tot_len, sk->sk_allocation);
+ if (unlikely(np->cork.opt == NULL))
+ return -ENOBUFS;
+
+ np->cork.opt->tot_len = opt->tot_len;
+ np->cork.opt->opt_flen = opt->opt_flen;
+ np->cork.opt->opt_nflen = opt->opt_nflen;
+
+ np->cork.opt->dst0opt = ip6_opt_dup(opt->dst0opt,
+ sk->sk_allocation);
+ if (opt->dst0opt && !np->cork.opt->dst0opt)
+ return -ENOBUFS;
+
+ np->cork.opt->dst1opt = ip6_opt_dup(opt->dst1opt,
+ sk->sk_allocation);
+ if (opt->dst1opt && !np->cork.opt->dst1opt)
+ return -ENOBUFS;
+
+ np->cork.opt->hopopt = ip6_opt_dup(opt->hopopt,
+ sk->sk_allocation);
+ if (opt->hopopt && !np->cork.opt->hopopt)
+ return -ENOBUFS;
+
+ np->cork.opt->srcrt = ip6_rthdr_dup(opt->srcrt,
+ sk->sk_allocation);
+ if (opt->srcrt && !np->cork.opt->srcrt)
+ return -ENOBUFS;
+
/* need source address above miyazawa*/
}
dst_hold(&rt->u.dst);
} else {
rt = (struct rt6_info *)inet->cork.dst;
fl = &inet->cork.fl;
- if (inet->cork.flags & IPCORK_OPT)
- opt = np->cork.opt;
+ opt = np->cork.opt;
transhdrlen = 0;
exthdrlen = 0;
mtu = inet->cork.fragsize;
*/
inet->cork.length += length;
- if (((length > mtu) && (sk->sk_protocol == IPPROTO_UDP)) &&
- (rt->u.dst.dev->features & NETIF_F_UFO)) {
+ if (length > mtu) {
+ int proto = sk->sk_protocol;
+ if (dontfrag && (proto == IPPROTO_UDP || proto == IPPROTO_RAW)){
+ ipv6_local_rxpmtu(sk, fl, mtu-exthdrlen);
+ return -EMSGSIZE;
+ }
- err = ip6_ufo_append_data(sk, getfrag, from, length, hh_len,
- fragheaderlen, transhdrlen, mtu,
- flags);
- if (err)
- goto error;
- return 0;
+ if (proto == IPPROTO_UDP &&
+ (rt->u.dst.dev->features & NETIF_F_UFO)) {
+
+ err = ip6_ufo_append_data(sk, getfrag, from, length,
+ hh_len, fragheaderlen,
+ transhdrlen, mtu, flags);
+ if (err)
+ goto error;
+ return 0;
+ }
}
if ((skb = skb_peek_tail(&sk->sk_write_queue)) == NULL)
static void ip6_cork_release(struct inet_sock *inet, struct ipv6_pinfo *np)
{
- inet->cork.flags &= ~IPCORK_OPT;
- kfree(np->cork.opt);
- np->cork.opt = NULL;
+ if (np->cork.opt) {
+ kfree(np->cork.opt->dst0opt);
+ kfree(np->cork.opt->dst1opt);
+ kfree(np->cork.opt->hopopt);
+ kfree(np->cork.opt->srcrt);
+ kfree(np->cork.opt);
+ np->cork.opt = NULL;
+ }
+
if (inet->cork.dst) {
dst_release(inet->cork.dst);
inet->cork.dst = NULL;
skb->len += tmp_skb->len;
skb->data_len += tmp_skb->len;
skb->truesize += tmp_skb->truesize;
- __sock_put(tmp_skb->sk);
tmp_skb->destructor = NULL;
tmp_skb->sk = NULL;
}
skb->priority = sk->sk_priority;
skb->mark = sk->sk_mark;
- skb->dst = dst_clone(&rt->u.dst);
- IP6_INC_STATS(net, rt->rt6i_idev, IPSTATS_MIB_OUTREQUESTS);
+ skb_dst_set(skb, dst_clone(&rt->u.dst));
+ IP6_UPD_PO_STATS(net, rt->rt6i_idev, IPSTATS_MIB_OUT, skb->len);
if (proto == IPPROTO_ICMPV6) {
- struct inet6_dev *idev = ip6_dst_idev(skb->dst);
+ struct inet6_dev *idev = ip6_dst_idev(skb_dst(skb));
- ICMP6MSGOUT_INC_STATS_BH(idev, icmp6_hdr(skb)->icmp6_type);
- ICMP6_INC_STATS_BH(idev, ICMP6_MIB_OUTMSGS);
+ ICMP6MSGOUT_INC_STATS_BH(net, idev, icmp6_hdr(skb)->icmp6_type);
+ ICMP6_INC_STATS_BH(net, idev, ICMP6_MIB_OUTMSGS);
}
err = ip6_local_out(skb);
if (err) {
if (err > 0)
- err = np->recverr ? net_xmit_errno(err) : 0;
+ err = net_xmit_errno(err);
if (err)
goto error;
}
ip6_cork_release(inet, np);
return err;
error:
+ IP6_INC_STATS(net, rt->rt6i_idev, IPSTATS_MIB_OUTDISCARDS);
goto out;
}
struct sk_buff *skb;
while ((skb = __skb_dequeue_tail(&sk->sk_write_queue)) != NULL) {
- if (skb->dst)
- IP6_INC_STATS(sock_net(sk), ip6_dst_idev(skb->dst),
+ if (skb_dst(skb))
+ IP6_INC_STATS(sock_net(sk), ip6_dst_idev(skb_dst(skb)),
IPSTATS_MIB_OUTDISCARDS);
kfree_skb(skb);
}