*/
#include <linux/errno.h>
-#include <linux/types.h>
+#include <linux/kernel.h>
#include <linux/string.h>
#include <linux/socket.h>
#include <linux/net.h>
spin_unlock_bh(&ip6_id_lock);
}
+int __ip6_local_out(struct sk_buff *skb)
+{
+ int len;
+
+ len = skb->len - sizeof(struct ipv6hdr);
+ if (len > IPV6_MAXPLEN)
+ len = 0;
+ ipv6_hdr(skb)->payload_len = htons(len);
+
+ return nf_hook(PF_INET6, NF_IP6_LOCAL_OUT, skb, NULL, skb->dst->dev,
+ dst_output);
+}
+
+int ip6_local_out(struct sk_buff *skb)
+{
+ int err;
+
+ err = __ip6_local_out(skb);
+ if (likely(err == 1))
+ err = dst_output(skb);
+
+ return err;
+}
+EXPORT_SYMBOL_GPL(ip6_local_out);
+
static int ip6_output_finish(struct sk_buff *skb)
{
struct dst_entry *dst = skb->dst;
u32 mtu;
if (opt) {
- int head_room;
+ unsigned int head_room;
/* First: exthdrs may take lots of space (~8K for now)
MAX_HEADER is not enough.
return 0;
out_err_release:
+ if (err == -ENETUNREACH)
+ IP6_INC_STATS_BH(NULL, IPSTATS_MIB_OUTNOROUTES);
dst_release(*dst);
*dst = NULL;
return err;
inet->cork.length = 0;
sk->sk_sndmsg_page = NULL;
sk->sk_sndmsg_off = 0;
- exthdrlen = rt->u.dst.header_len + (opt ? opt->opt_flen : 0);
+ exthdrlen = rt->u.dst.header_len + (opt ? opt->opt_flen : 0) -
+ rt->nfheader_len;
length += exthdrlen;
transhdrlen += exthdrlen;
} else {
hh_len = LL_RESERVED_SPACE(rt->u.dst.dev);
- fragheaderlen = sizeof(struct ipv6hdr) + rt->u.dst.nfheader_len + (opt ? opt->opt_nflen : 0);
+ fragheaderlen = sizeof(struct ipv6hdr) + rt->nfheader_len +
+ (opt ? opt->opt_nflen : 0);
maxfraglen = ((mtu - fragheaderlen) & ~7) + fragheaderlen - sizeof(struct frag_hdr);
if (mtu <= sizeof(struct ipv6hdr) + IPV6_MAXPLEN) {
skb_fill_page_desc(skb, i, page, 0, 0);
frag = &skb_shinfo(skb)->frags[i];
- skb->truesize += PAGE_SIZE;
- atomic_add(PAGE_SIZE, &sk->sk_wmem_alloc);
} else {
err = -EMSGSIZE;
goto error;
frag->size += copy;
skb->len += copy;
skb->data_len += copy;
+ skb->truesize += copy;
+ atomic_add(copy, &sk->sk_wmem_alloc);
}
offset += copy;
length -= copy;
return err;
}
+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.rt) {
+ dst_release(&np->cork.rt->u.dst);
+ np->cork.rt = NULL;
+ inet->cork.flags &= ~IPCORK_ALLFRAG;
+ }
+ memset(&inet->cork.fl, 0, sizeof(inet->cork.fl));
+}
+
int ip6_push_pending_frames(struct sock *sk)
{
struct sk_buff *skb, *tmp_skb;
*(__be32*)hdr = fl->fl6_flowlabel |
htonl(0x60000000 | ((int)np->cork.tclass << 20));
- if (skb->len <= sizeof(struct ipv6hdr) + IPV6_MAXPLEN)
- hdr->payload_len = htons(skb->len - sizeof(struct ipv6hdr));
- else
- hdr->payload_len = 0;
hdr->hop_limit = np->cork.hop_limit;
hdr->nexthdr = proto;
ipv6_addr_copy(&hdr->saddr, &fl->fl6_src);
ICMP6_INC_STATS_BH(idev, ICMP6_MIB_OUTMSGS);
}
- err = NF_HOOK(PF_INET6, NF_IP6_LOCAL_OUT, skb, NULL, skb->dst->dev, dst_output);
+ err = ip6_local_out(skb);
if (err) {
if (err > 0)
err = np->recverr ? net_xmit_errno(err) : 0;
}
out:
- inet->cork.flags &= ~IPCORK_OPT;
- kfree(np->cork.opt);
- np->cork.opt = NULL;
- if (np->cork.rt) {
- dst_release(&np->cork.rt->u.dst);
- np->cork.rt = NULL;
- inet->cork.flags &= ~IPCORK_ALLFRAG;
- }
- memset(&inet->cork.fl, 0, sizeof(inet->cork.fl));
+ ip6_cork_release(inet, np);
return err;
error:
goto out;
void ip6_flush_pending_frames(struct sock *sk)
{
- struct inet_sock *inet = inet_sk(sk);
- struct ipv6_pinfo *np = inet6_sk(sk);
struct sk_buff *skb;
while ((skb = __skb_dequeue_tail(&sk->sk_write_queue)) != NULL) {
kfree_skb(skb);
}
- inet->cork.flags &= ~IPCORK_OPT;
-
- kfree(np->cork.opt);
- np->cork.opt = NULL;
- if (np->cork.rt) {
- dst_release(&np->cork.rt->u.dst);
- np->cork.rt = NULL;
- inet->cork.flags &= ~IPCORK_ALLFRAG;
- }
- memset(&inet->cork.fl, 0, sizeof(inet->cork.fl));
+ ip6_cork_release(inet_sk(sk), inet6_sk(sk));
}