#include <linux/netfilter_ipv6.h>
#include <asm/uaccess.h>
#include <asm/ioctls.h>
+#include <asm/bug.h>
#include <net/ip.h>
#include <net/sock.h>
#include <net/transp_v6.h>
#include <net/udp.h>
#include <net/inet_common.h>
+#include <net/tcp_states.h>
#include <net/rawv6.h>
#include <net/xfrm.h>
/* Grumble... icmp and ip_input want to get at this... */
struct sock *__raw_v6_lookup(struct sock *sk, unsigned short num,
- struct in6_addr *loc_addr, struct in6_addr *rmt_addr)
+ struct in6_addr *loc_addr, struct in6_addr *rmt_addr,
+ int dif)
{
struct hlist_node *node;
int is_multicast = ipv6_addr_is_multicast(loc_addr);
!ipv6_addr_equal(&np->daddr, rmt_addr))
continue;
+ if (sk->sk_bound_dev_if && sk->sk_bound_dev_if != dif)
+ continue;
+
if (!ipv6_addr_any(&np->rcv_saddr)) {
if (ipv6_addr_equal(&np->rcv_saddr, loc_addr))
goto found;
*
* Caller owns SKB so we must make clones.
*/
-void ipv6_raw_deliver(struct sk_buff *skb, int nexthdr)
+int ipv6_raw_deliver(struct sk_buff *skb, int nexthdr)
{
struct in6_addr *saddr;
struct in6_addr *daddr;
struct sock *sk;
+ int delivered = 0;
__u8 hash;
saddr = &skb->nh.ipv6h->saddr;
if (sk == NULL)
goto out;
- sk = __raw_v6_lookup(sk, nexthdr, daddr, saddr);
+ sk = __raw_v6_lookup(sk, nexthdr, daddr, saddr, IP6CB(skb)->iif);
while (sk) {
+ delivered = 1;
if (nexthdr != IPPROTO_ICMPV6 || !icmpv6_filter(sk, skb)) {
struct sk_buff *clone = skb_clone(skb, GFP_ATOMIC);
if (clone)
rawv6_rcv(sk, clone);
}
- sk = __raw_v6_lookup(sk_next(sk), nexthdr, daddr, saddr);
+ sk = __raw_v6_lookup(sk_next(sk), nexthdr, daddr, saddr,
+ IP6CB(skb)->iif);
}
out:
read_unlock(&raw_v6_lock);
+ return delivered;
}
/* This cleans up af_inet6 a bit. -DaveM */
if (skb->ip_summed != CHECKSUM_UNNECESSARY) {
if (skb->ip_summed == CHECKSUM_HW) {
+ skb_postpull_rcsum(skb, skb->nh.raw,
+ skb->h.raw - skb->nh.raw);
skb->ip_summed = CHECKSUM_UNNECESSARY;
if (csum_ipv6_magic(&skb->nh.ipv6h->saddr,
&skb->nh.ipv6h->daddr,
skb->len, inet->num, skb->csum)) {
- LIMIT_NETDEBUG(
- printk(KERN_DEBUG "raw v6 hw csum failure.\n"));
+ LIMIT_NETDEBUG(KERN_DEBUG "raw v6 hw csum failure.\n");
skb->ip_summed = CHECKSUM_NONE;
}
}
/* Clear queue. */
if (flags&MSG_PEEK) {
int clear = 0;
- spin_lock_irq(&sk->sk_receive_queue.lock);
+ spin_lock_bh(&sk->sk_receive_queue.lock);
if (skb == skb_peek(&sk->sk_receive_queue)) {
__skb_unlink(skb, &sk->sk_receive_queue);
clear = 1;
}
- spin_unlock_irq(&sk->sk_receive_queue.lock);
+ spin_unlock_bh(&sk->sk_receive_queue.lock);
if (clear)
kfree_skb(skb);
}
}
static int rawv6_push_pending_frames(struct sock *sk, struct flowi *fl,
- struct raw6_sock *rp, int len)
+ struct raw6_sock *rp)
{
struct sk_buff *skb;
int err = 0;
- u16 *csum;
+ int offset;
+ int len;
+ int total_len;
u32 tmp_csum;
+ u16 csum;
if (!rp->checksum)
goto send;
if ((skb = skb_peek(&sk->sk_write_queue)) == NULL)
goto out;
- if (rp->offset + 1 < len)
- csum = (u16 *)(skb->h.raw + rp->offset);
- else {
+ offset = rp->offset;
+ total_len = inet_sk(sk)->cork.length - (skb->nh.raw - skb->data);
+ if (offset >= total_len - 1) {
err = -EINVAL;
+ ip6_flush_pending_frames(sk);
goto out;
}
*/
tmp_csum = skb->csum;
} else {
+ struct sk_buff *csum_skb = NULL;
tmp_csum = 0;
skb_queue_walk(&sk->sk_write_queue, skb) {
tmp_csum = csum_add(tmp_csum, skb->csum);
+
+ if (csum_skb)
+ continue;
+
+ len = skb->len - (skb->h.raw - skb->data);
+ if (offset >= len) {
+ offset -= len;
+ continue;
+ }
+
+ csum_skb = skb;
}
+
+ skb = csum_skb;
}
+ offset += skb->h.raw - skb->data;
+ if (skb_copy_bits(skb, offset, &csum, 2))
+ BUG();
+
/* in case cksum was not initialized */
- if (unlikely(*csum))
- tmp_csum = csum_sub(tmp_csum, *csum);
+ if (unlikely(csum))
+ tmp_csum = csum_sub(tmp_csum, csum);
+
+ tmp_csum = csum_ipv6_magic(&fl->fl6_src,
+ &fl->fl6_dst,
+ total_len, fl->proto, tmp_csum);
- *csum = csum_ipv6_magic(&fl->fl6_src,
- &fl->fl6_dst,
- len, fl->proto, tmp_csum);
+ if (tmp_csum == 0)
+ tmp_csum = -1;
+
+ csum = tmp_csum;
+ if (skb_store_bits(skb, offset, &csum, 2))
+ BUG();
- if (*csum == 0)
- *csum = -1;
send:
err = ip6_push_pending_frames(sk);
out:
struct flowi *fl, struct rt6_info *rt,
unsigned int flags)
{
- struct inet_sock *inet = inet_sk(sk);
+ struct ipv6_pinfo *np = inet6_sk(sk);
struct ipv6hdr *iph;
struct sk_buff *skb;
unsigned int hh_len;
err = NF_HOOK(PF_INET6, NF_IP6_LOCAL_OUT, skb, NULL, rt->u.dst.dev,
dst_output);
if (err > 0)
- err = inet->recverr ? net_xmit_errno(err) : 0;
+ err = np->recverr ? net_xmit_errno(err) : 0;
if (err)
goto error;
out:
if (err)
ip6_flush_pending_frames(sk);
else if (!(msg->msg_flags & MSG_MORE))
- err = rawv6_push_pending_frames(sk, &fl, rp, len);
+ err = rawv6_push_pending_frames(sk, &fl, rp);
}
done:
ip6_dst_store(sk, dst,
ipv6_addr_equal(&fl.fl6_dst, &np->daddr) ?
&np->daddr : NULL);
- if (err > 0)
- err = np->recverr ? net_xmit_errno(err) : 0;
release_sock(sk);
out:
struct sk_buff *skb;
int amount = 0;
- spin_lock_irq(&sk->sk_receive_queue.lock);
+ spin_lock_bh(&sk->sk_receive_queue.lock);
skb = skb_peek(&sk->sk_receive_queue);
if (skb != NULL)
amount = skb->tail - skb->h.raw;
- spin_unlock_irq(&sk->sk_receive_queue.lock);
+ spin_unlock_bh(&sk->sk_receive_queue.lock);
return put_user(amount, (int __user *)arg);
}