Commit 6bac9098 authored by Kazunori Miyazawa's avatar Kazunori Miyazawa Committed by David S. Miller

[IPV4]: Introduce ip6_append_data.

parent d983ec93
...@@ -121,6 +121,7 @@ struct ipv6hdr { ...@@ -121,6 +121,7 @@ struct ipv6hdr {
#include <linux/icmpv6.h> #include <linux/icmpv6.h>
#include <net/if_inet6.h> /* struct ipv6_mc_socklist */ #include <net/if_inet6.h> /* struct ipv6_mc_socklist */
#include <linux/tcp.h> #include <linux/tcp.h>
#include <linux/udp.h>
/* /*
This structure contains results of exthdrs parsing This structure contains results of exthdrs parsing
...@@ -178,6 +179,11 @@ struct ipv6_pinfo { ...@@ -178,6 +179,11 @@ struct ipv6_pinfo {
struct ipv6_txoptions *opt; struct ipv6_txoptions *opt;
struct sk_buff *pktoptions; struct sk_buff *pktoptions;
struct {
struct ipv6_txoptions *opt;
struct rt6_info *rt;
struct flowi *fl;
} cork;
}; };
struct raw6_opt { struct raw6_opt {
...@@ -200,6 +206,7 @@ struct udp6_sock { ...@@ -200,6 +206,7 @@ struct udp6_sock {
struct sock sk; struct sock sk;
struct ipv6_pinfo *pinet6; struct ipv6_pinfo *pinet6;
struct inet_opt inet; struct inet_opt inet;
struct udp_opt udp;
struct ipv6_pinfo inet6; struct ipv6_pinfo inet6;
}; };
......
...@@ -792,6 +792,15 @@ static inline int skb_pagelen(const struct sk_buff *skb) ...@@ -792,6 +792,15 @@ static inline int skb_pagelen(const struct sk_buff *skb)
return len + skb_headlen(skb); return len + skb_headlen(skb);
} }
static inline void skb_fill_page_desc(struct sk_buff *skb, int i, struct page *page, int off, int size)
{
skb_frag_t *frag = &skb_shinfo(skb)->frags[i];
frag->page = page;
frag->page_offset = off;
frag->size = size;
skb_shinfo(skb)->nr_frags = i+1;
}
#define SKB_PAGE_ASSERT(skb) do { if (skb_shinfo(skb)->nr_frags) \ #define SKB_PAGE_ASSERT(skb) do { if (skb_shinfo(skb)->nr_frags) \
BUG(); } while (0) BUG(); } while (0)
#define SKB_FRAG_ASSERT(skb) do { if (skb_shinfo(skb)->frag_list) \ #define SKB_FRAG_ASSERT(skb) do { if (skb_shinfo(skb)->frag_list) \
......
...@@ -316,6 +316,26 @@ extern int ip6_build_xmit(struct sock *sk, ...@@ -316,6 +316,26 @@ extern int ip6_build_xmit(struct sock *sk,
struct ipv6_txoptions *opt, struct ipv6_txoptions *opt,
int hlimit, int flags); int hlimit, int flags);
extern 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,
struct ipv6_txoptions *opt,
struct flowi *fl,
struct rt6_info *rt,
unsigned int flags);
extern int ip6_push_pending_frames(struct sock *sk);
extern void ip6_flush_pending_frames(struct sock *sk);
extern int ip6_dst_lookup(struct sock *sk,
struct dst_entry **dst,
struct flowi *fl,
struct in6_addr **saddr);
/* /*
* skb processing functions * skb processing functions
*/ */
......
...@@ -685,16 +685,6 @@ skb_can_coalesce(struct sk_buff *skb, int i, struct page *page, int off) ...@@ -685,16 +685,6 @@ skb_can_coalesce(struct sk_buff *skb, int i, struct page *page, int off)
return 0; return 0;
} }
static void
skb_fill_page_desc(struct sk_buff *skb, int i, struct page *page, int off, int size)
{
skb_frag_t *frag = &skb_shinfo(skb)->frags[i];
frag->page = page;
frag->page_offset = off;
frag->size = size;
skb_shinfo(skb)->nr_frags = i+1;
}
static inline unsigned int static inline unsigned int
csum_page(struct page *page, int offset, int copy) csum_page(struct page *page, int offset, int copy)
{ {
......
...@@ -28,6 +28,7 @@ ...@@ -28,6 +28,7 @@
* YOSHIFUJI Hideaki @USAGI: added sysctl for icmp rate limit. * YOSHIFUJI Hideaki @USAGI: added sysctl for icmp rate limit.
* Randy Dunlap and * Randy Dunlap and
* YOSHIFUJI Hideaki @USAGI: Per-interface statistics support * YOSHIFUJI Hideaki @USAGI: Per-interface statistics support
* Kazunori MIYAZAWA @USAGI: change output process to use ip6_append_data
*/ */
#include <linux/module.h> #include <linux/module.h>
...@@ -104,42 +105,6 @@ static __inline__ void icmpv6_xmit_unlock(void) ...@@ -104,42 +105,6 @@ static __inline__ void icmpv6_xmit_unlock(void)
spin_unlock_bh(&icmpv6_socket->sk->lock.slock); spin_unlock_bh(&icmpv6_socket->sk->lock.slock);
} }
/*
* getfrag callback
*/
static int icmpv6_getfrag(const void *data, struct in6_addr *saddr,
char *buff, unsigned int offset, unsigned int len)
{
struct icmpv6_msg *msg = (struct icmpv6_msg *) data;
struct icmp6hdr *icmph;
__u32 csum;
if (offset) {
csum = skb_copy_and_csum_bits(msg->skb, msg->offset +
(offset - sizeof(struct icmp6hdr)),
buff, len, msg->csum);
msg->csum = csum;
return 0;
}
csum = csum_partial_copy_nocheck((void *) &msg->icmph, buff,
sizeof(struct icmp6hdr), msg->csum);
csum = skb_copy_and_csum_bits(msg->skb, msg->offset,
buff + sizeof(struct icmp6hdr),
len - sizeof(struct icmp6hdr), csum);
icmph = (struct icmp6hdr *) buff;
icmph->icmp6_cksum = csum_ipv6_magic(saddr, msg->daddr, msg->len,
IPPROTO_ICMPV6, csum);
return 0;
}
/* /*
* Slightly more convenient version of icmpv6_send. * Slightly more convenient version of icmpv6_send.
*/ */
...@@ -242,22 +207,74 @@ static __inline__ int opt_unrec(struct sk_buff *skb, __u32 offset) ...@@ -242,22 +207,74 @@ static __inline__ int opt_unrec(struct sk_buff *skb, __u32 offset)
return (optval&0xC0) == 0x80; return (optval&0xC0) == 0x80;
} }
int icmpv6_push_pending_frames(struct sock *sk, struct flowi *fl, struct icmp6hdr *thdr, int len)
{
struct sk_buff *skb;
struct icmp6hdr *icmp6h;
int err = 0;
if ((skb = skb_peek(&sk->write_queue)) == NULL)
goto out;
icmp6h = (struct icmp6hdr*) skb->h.raw;
memcpy(icmp6h, thdr, sizeof(struct icmp6hdr));
icmp6h->icmp6_cksum = 0;
if (skb_queue_len(&sk->write_queue) == 1) {
skb->csum = csum_partial((char *)icmp6h,
sizeof(struct icmp6hdr), skb->csum);
icmp6h->icmp6_cksum = csum_ipv6_magic(fl->fl6_src,
fl->fl6_dst,
len, fl->proto, skb->csum);
} else {
u32 tmp_csum = 0;
skb_queue_walk(&sk->write_queue, skb) {
tmp_csum = csum_add(tmp_csum, skb->csum);
}
tmp_csum = csum_partial((char *)icmp6h,
sizeof(struct icmp6hdr), tmp_csum);
tmp_csum = csum_ipv6_magic(fl->fl6_src,
fl->fl6_dst,
len, fl->proto, tmp_csum);
icmp6h->icmp6_cksum = tmp_csum;
}
if (icmp6h->icmp6_cksum == 0)
icmp6h->icmp6_cksum = -1;
ip6_push_pending_frames(sk);
out:
return err;
}
static int icmpv6_getfrag(void *from, char *to, int offset, int len, int odd, struct sk_buff *skb)
{
struct sk_buff *org_skb = (struct sk_buff *)from;
__u32 csum = 0;
csum = skb_copy_and_csum_bits(org_skb, offset, to, len, csum);
skb->csum = csum_block_add(skb->csum, csum, odd);
return 0;
}
/* /*
* Send an ICMP message in response to a packet in error * Send an ICMP message in response to a packet in error
*/ */
void icmpv6_send(struct sk_buff *skb, int type, int code, __u32 info, void icmpv6_send(struct sk_buff *skb, int type, int code, __u32 info,
struct net_device *dev) struct net_device *dev)
{ {
struct inet6_dev *idev; struct inet6_dev *idev;
struct ipv6hdr *hdr = skb->nh.ipv6h; struct ipv6hdr *hdr = skb->nh.ipv6h;
struct sock *sk = icmpv6_socket->sk; struct sock *sk = icmpv6_socket->sk;
struct in6_addr *saddr = NULL; struct ipv6_pinfo *np = inet6_sk(sk);
int iif = 0; struct in6_addr *saddr = NULL, *tmp_saddr = NULL;
struct icmpv6_msg msg; struct dst_entry *dst;
struct icmp6hdr tmp_hdr;
struct flowi fl; struct flowi fl;
int iif = 0;
int addr_type = 0; int addr_type = 0;
int len; int len, plen;
int hlimit = -1;
int err = 0;
if ((u8*)hdr < skb->head || (u8*)(hdr+1) > skb->tail) if ((u8*)hdr < skb->head || (u8*)(hdr+1) > skb->tail)
return; return;
...@@ -328,36 +345,48 @@ void icmpv6_send(struct sk_buff *skb, int type, int code, __u32 info, ...@@ -328,36 +345,48 @@ void icmpv6_send(struct sk_buff *skb, int type, int code, __u32 info,
if (!icmpv6_xrlim_allow(sk, type, &fl)) if (!icmpv6_xrlim_allow(sk, type, &fl))
goto out; goto out;
/* tmp_hdr.icmp6_type = type;
* ok. kick it. checksum will be provided by the tmp_hdr.icmp6_code = code;
* getfrag_t callback. tmp_hdr.icmp6_cksum = 0;
*/ tmp_hdr.icmp6_pointer = htonl(info);
msg.icmph.icmp6_type = type; if (!fl.oif && ipv6_addr_is_multicast(fl.fl6_dst))
msg.icmph.icmp6_code = code; fl.oif = np->mcast_oif;
msg.icmph.icmp6_cksum = 0;
msg.icmph.icmp6_pointer = htonl(info);
msg.skb = skb; err = ip6_dst_lookup(sk, &dst, &fl, &tmp_saddr);
msg.offset = skb->nh.raw - skb->data; if (err) goto out;
msg.csum = 0;
msg.daddr = &hdr->saddr;
len = skb->len - msg.offset + sizeof(struct icmp6hdr); if (hlimit < 0) {
len = min_t(unsigned int, len, IPV6_MIN_MTU - sizeof(struct ipv6hdr)); if (ipv6_addr_is_multicast(fl.fl6_dst))
hlimit = np->mcast_hops;
else
hlimit = np->hop_limit;
if (hlimit < 0)
hlimit = ((struct rt6_info*)dst)->rt6i_hoplimit;
}
plen = skb->nh.raw - skb->data;
__skb_pull(skb, plen);
len = skb->len;
len = min_t(unsigned int, len, IPV6_MIN_MTU - sizeof(struct ipv6hdr) -sizeof(struct icmp6hdr));
if (len < 0) { if (len < 0) {
if (net_ratelimit()) if (net_ratelimit())
printk(KERN_DEBUG "icmp: len problem\n"); printk(KERN_DEBUG "icmp: len problem\n");
__skb_push(skb, plen);
goto out; goto out;
} }
msg.len = len;
idev = in6_dev_get(skb->dev); idev = in6_dev_get(skb->dev);
ip6_build_xmit(sk, icmpv6_getfrag, &msg, &fl, len, NULL, -1, err = ip6_append_data(sk, icmpv6_getfrag, skb, len + sizeof(struct icmp6hdr), sizeof(struct icmp6hdr),
MSG_DONTWAIT); hlimit, NULL, &fl, (struct rt6_info*)dst, MSG_DONTWAIT);
if (err) {
ip6_flush_pending_frames(sk);
goto out;
}
err = icmpv6_push_pending_frames(sk, &fl, &tmp_hdr, len + sizeof(struct icmp6hdr));
__skb_push(skb, plen);
if (type >= ICMPV6_DEST_UNREACH && type <= ICMPV6_PARAMPROB) if (type >= ICMPV6_DEST_UNREACH && type <= ICMPV6_PARAMPROB)
ICMP6_INC_STATS_OFFSET_BH(idev, Icmp6OutDestUnreachs, type - ICMPV6_DEST_UNREACH); ICMP6_INC_STATS_OFFSET_BH(idev, Icmp6OutDestUnreachs, type - ICMPV6_DEST_UNREACH);
ICMP6_INC_STATS_BH(idev, Icmp6OutMsgs); ICMP6_INC_STATS_BH(idev, Icmp6OutMsgs);
...@@ -365,6 +394,7 @@ void icmpv6_send(struct sk_buff *skb, int type, int code, __u32 info, ...@@ -365,6 +394,7 @@ void icmpv6_send(struct sk_buff *skb, int type, int code, __u32 info,
if (likely(idev != NULL)) if (likely(idev != NULL))
in6_dev_put(idev); in6_dev_put(idev);
out: out:
if (tmp_saddr) kfree(tmp_saddr);
icmpv6_xmit_unlock(); icmpv6_xmit_unlock();
} }
...@@ -372,10 +402,14 @@ static void icmpv6_echo_reply(struct sk_buff *skb) ...@@ -372,10 +402,14 @@ static void icmpv6_echo_reply(struct sk_buff *skb)
{ {
struct sock *sk = icmpv6_socket->sk; struct sock *sk = icmpv6_socket->sk;
struct inet6_dev *idev; struct inet6_dev *idev;
struct ipv6_pinfo *np = inet6_sk(sk);
struct in6_addr *saddr = NULL, *tmp_saddr = NULL;
struct icmp6hdr *icmph = (struct icmp6hdr *) skb->h.raw; struct icmp6hdr *icmph = (struct icmp6hdr *) skb->h.raw;
struct in6_addr *saddr; struct icmp6hdr tmp_hdr;
struct icmpv6_msg msg;
struct flowi fl; struct flowi fl;
struct dst_entry *dst;
int err = 0;
int hlimit = -1;
saddr = &skb->nh.ipv6h->daddr; saddr = &skb->nh.ipv6h->daddr;
...@@ -383,39 +417,55 @@ static void icmpv6_echo_reply(struct sk_buff *skb) ...@@ -383,39 +417,55 @@ static void icmpv6_echo_reply(struct sk_buff *skb)
ipv6_chk_acast_addr(0, saddr)) ipv6_chk_acast_addr(0, saddr))
saddr = NULL; saddr = NULL;
msg.icmph.icmp6_type = ICMPV6_ECHO_REPLY; memcpy(&tmp_hdr, icmph, sizeof(tmp_hdr));
msg.icmph.icmp6_code = 0; tmp_hdr.icmp6_type = ICMPV6_ECHO_REPLY;
msg.icmph.icmp6_cksum = 0;
msg.icmph.icmp6_identifier = icmph->icmp6_identifier;
msg.icmph.icmp6_sequence = icmph->icmp6_sequence;
msg.skb = skb;
msg.offset = 0;
msg.csum = 0;
msg.len = skb->len + sizeof(struct icmp6hdr);
msg.daddr = &skb->nh.ipv6h->saddr;
fl.proto = IPPROTO_ICMPV6; fl.proto = IPPROTO_ICMPV6;
fl.fl6_dst = msg.daddr; fl.fl6_dst = &skb->nh.ipv6h->saddr;
fl.fl6_src = saddr; fl.fl6_src = saddr;
fl.oif = skb->dev->ifindex; fl.oif = skb->dev->ifindex;
fl.fl6_flowlabel = 0; fl.fl6_flowlabel = 0;
fl.fl_icmp_type = ICMPV6_ECHO_REPLY; fl.fl_icmp_type = ICMPV6_ECHO_REPLY;
fl.fl_icmp_code = 0; fl.fl_icmp_code = 0;
icmpv6_xmit_lock();
if (!fl.oif && ipv6_addr_is_multicast(fl.nl_u.ip6_u.daddr))
fl.oif = np->mcast_oif;
err = ip6_dst_lookup(sk, &dst, &fl, &tmp_saddr);
if (err) goto out;
if (hlimit < 0) {
if (ipv6_addr_is_multicast(fl.fl6_dst))
hlimit = np->mcast_hops;
else
hlimit = np->hop_limit;
if (hlimit < 0)
hlimit = ((struct rt6_info*)dst)->rt6i_hoplimit;
}
idev = in6_dev_get(skb->dev); idev = in6_dev_get(skb->dev);
icmpv6_xmit_lock(); err = ip6_append_data(sk, icmpv6_getfrag, skb, skb->len + sizeof(struct icmp6hdr),
sizeof(struct icmp6hdr), hlimit, NULL, &fl,
(struct rt6_info*)dst, MSG_DONTWAIT);
ip6_build_xmit(sk, icmpv6_getfrag, &msg, &fl, msg.len, NULL, -1, if (err) {
MSG_DONTWAIT); ip6_flush_pending_frames(sk);
ICMP6_INC_STATS_BH(idev, Icmp6OutEchoReplies); goto out;
ICMP6_INC_STATS_BH(idev, Icmp6OutMsgs); }
err = icmpv6_push_pending_frames(sk, &fl, &tmp_hdr, skb->len + sizeof(struct icmp6hdr));
icmpv6_xmit_unlock(); ICMP6_INC_STATS_BH(idev, Icmp6OutEchoReplies);
ICMP6_INC_STATS_BH(idev, Icmp6OutMsgs);
if (likely(idev != NULL)) if (likely(idev != NULL))
in6_dev_put(idev); in6_dev_put(idev);
out:
if (tmp_saddr) kfree(tmp_saddr);
icmpv6_xmit_unlock();
} }
static void icmpv6_notify(struct sk_buff *skb, int type, int code, u32 info) static void icmpv6_notify(struct sk_buff *skb, int type, int code, u32 info)
......
This diff is collapsed.
...@@ -12,6 +12,7 @@ ...@@ -12,6 +12,7 @@
* Fixes: * Fixes:
* Hideaki YOSHIFUJI : sin6_scope_id support * Hideaki YOSHIFUJI : sin6_scope_id support
* YOSHIFUJI,H.@USAGI : raw checksum (RFC2292(bis) compliance) * YOSHIFUJI,H.@USAGI : raw checksum (RFC2292(bis) compliance)
* Kazunori MIYAZAWA @USAGI: change process style to use ip6_append_data
* *
* This program is free software; you can redistribute it and/or * This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License * modify it under the terms of the GNU General Public License
...@@ -29,6 +30,8 @@ ...@@ -29,6 +30,8 @@
#include <linux/netdevice.h> #include <linux/netdevice.h>
#include <linux/if_arp.h> #include <linux/if_arp.h>
#include <linux/icmpv6.h> #include <linux/icmpv6.h>
#include <linux/netfilter.h>
#include <linux/netfilter_ipv6.h>
#include <asm/uaccess.h> #include <asm/uaccess.h>
#include <asm/ioctls.h> #include <asm/ioctls.h>
...@@ -438,87 +441,115 @@ static int rawv6_recvmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg ...@@ -438,87 +441,115 @@ static int rawv6_recvmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg
goto out_free; goto out_free;
} }
/* static int rawv6_push_pending_frames(struct sock *sk, struct flowi *fl, struct raw6_opt *opt, int len)
* Sending... {
*/ struct sk_buff *skb;
int err = 0;
u16 *csum;
struct rawv6_fakehdr { if ((skb = skb_peek(&sk->write_queue)) == NULL)
struct iovec *iov; goto out;
struct sock *sk;
__u32 len;
__u32 cksum;
__u32 proto;
struct in6_addr *daddr;
};
static int rawv6_getfrag(const void *data, struct in6_addr *saddr, if (opt->offset + 1 < len)
char *buff, unsigned int offset, unsigned int len) csum = (u16 *)(skb->h.raw + opt->offset);
{ else {
struct iovec *iov = (struct iovec *) data; err = -EINVAL;
goto out;
}
if (skb_queue_len(&sk->write_queue) == 1) {
/*
* Only one fragment on the socket.
*/
/* should be check HW csum miyazawa */
*csum = csum_ipv6_magic(fl->fl6_src,
fl->fl6_dst,
len, fl->proto, skb->csum);
} else {
u32 tmp_csum = 0;
skb_queue_walk(&sk->write_queue, skb) {
tmp_csum = csum_add(tmp_csum, skb->csum);
}
return memcpy_fromiovecend(buff, iov, offset, len); tmp_csum = csum_ipv6_magic(fl->fl6_src,
fl->fl6_dst,
len, fl->proto, tmp_csum);
*csum = tmp_csum;
}
if (*csum == 0)
*csum = -1;
ip6_push_pending_frames(sk);
out:
return err;
} }
static int rawv6_frag_cksum(const void *data, struct in6_addr *addr, static int rawv6_send_hdrinc(struct sock *sk, void *from, int length,
char *buff, unsigned int offset, struct flowi *fl, struct rt6_info *rt,
unsigned int len) unsigned int flags)
{ {
struct rawv6_fakehdr *hdr = (struct rawv6_fakehdr *) data; struct inet_opt *inet = inet_sk(sk);
struct ipv6hdr *iph;
if (csum_partial_copy_fromiovecend(buff, hdr->iov, offset, struct sk_buff *skb;
len, &hdr->cksum)) unsigned int hh_len;
return -EFAULT; int err;
if (offset == 0) {
struct sock *sk;
struct raw6_opt *opt;
struct in6_addr *daddr;
sk = hdr->sk;
opt = raw6_sk(sk);
if (hdr->daddr) if (length > rt->u.dst.dev->mtu) {
daddr = hdr->daddr; ipv6_local_error(sk, EMSGSIZE, fl, rt->u.dst.dev->mtu);
else return -EMSGSIZE;
daddr = addr + 1; }
if (flags&MSG_PROBE)
hdr->cksum = csum_ipv6_magic(addr, daddr, hdr->len, goto out;
hdr->proto, hdr->cksum);
hh_len = LL_RESERVED_SPACE(rt->u.dst.dev);
if (opt->offset + 1 < len) {
__u16 *csum; skb = sock_alloc_send_skb(sk, length+hh_len+15,
flags&MSG_DONTWAIT, &err);
csum = (__u16 *) (buff + opt->offset); if (skb == NULL)
if (*csum) { goto error;
/* in case cksum was not initialized */ skb_reserve(skb, hh_len);
__u32 sum = hdr->cksum;
sum += *csum; skb->priority = sk->priority;
*csum = hdr->cksum = (sum + (sum>>16)); skb->dst = dst_clone(&rt->u.dst);
} else {
*csum = hdr->cksum; skb->nh.ipv6h = iph = (struct ipv6hdr *)skb_put(skb, length);
}
} else { skb->ip_summed = CHECKSUM_NONE;
if (net_ratelimit())
printk(KERN_DEBUG "icmp: cksum offset too big\n"); skb->h.raw = skb->nh.raw;
return -EINVAL; err = memcpy_fromiovecend((void *)iph, from, 0, length);
} if (err)
} goto error_fault;
return 0;
}
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;
if (err)
goto error;
out:
return 0;
error_fault:
err = -EFAULT;
kfree_skb(skb);
error:
IP6_INC_STATS(Ip6OutDiscards);
return err;
}
static int rawv6_sendmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg, int len) static int rawv6_sendmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg, int len)
{ {
struct ipv6_txoptions opt_space; struct ipv6_txoptions opt_space;
struct sockaddr_in6 * sin6 = (struct sockaddr_in6 *) msg->msg_name; struct sockaddr_in6 * sin6 = (struct sockaddr_in6 *) msg->msg_name;
struct in6_addr *daddr, *saddr = NULL;
struct inet_opt *inet = inet_sk(sk); struct inet_opt *inet = inet_sk(sk);
struct ipv6_pinfo *np = inet6_sk(sk); struct ipv6_pinfo *np = inet6_sk(sk);
struct raw6_opt *raw_opt = raw6_sk(sk);
struct ipv6_txoptions *opt = NULL; struct ipv6_txoptions *opt = NULL;
struct ip6_flowlabel *flowlabel = NULL; struct ip6_flowlabel *flowlabel = NULL;
struct dst_entry *dst = NULL;
struct flowi fl; struct flowi fl;
int addr_len = msg->msg_namelen; int addr_len = msg->msg_namelen;
struct in6_addr *daddr;
struct raw6_opt *raw_opt;
int hlimit = -1; int hlimit = -1;
u16 proto; u16 proto;
int err; int err;
...@@ -552,6 +583,8 @@ static int rawv6_sendmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg ...@@ -552,6 +583,8 @@ static int rawv6_sendmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg
if (!proto) if (!proto)
proto = inet->num; proto = inet->num;
else if (proto != inet->num)
return(-EINVAL);
if (proto > 255) if (proto > 255)
return(-EINVAL); return(-EINVAL);
...@@ -590,6 +623,7 @@ static int rawv6_sendmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg ...@@ -590,6 +623,7 @@ static int rawv6_sendmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg
* unspecfied destination address * unspecfied destination address
* treated as error... is this correct ? * treated as error... is this correct ?
*/ */
fl6_sock_release(flowlabel);
return(-EINVAL); return(-EINVAL);
} }
...@@ -619,39 +653,71 @@ static int rawv6_sendmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg ...@@ -619,39 +653,71 @@ static int rawv6_sendmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg
if (flowlabel) if (flowlabel)
opt = fl6_merge_options(&opt_space, flowlabel, opt); opt = fl6_merge_options(&opt_space, flowlabel, opt);
raw_opt = raw6_sk(sk);
fl.proto = proto; fl.proto = proto;
fl.fl6_dst = daddr; fl.fl6_dst = daddr;
if (fl.fl6_src == NULL && !ipv6_addr_any(&np->saddr)) if (fl.fl6_src == NULL && !ipv6_addr_any(&np->saddr))
fl.fl6_src = &np->saddr; fl.fl6_src = &np->saddr;
fl.fl_icmp_type = 0; fl.fl_icmp_type = 0;
fl.fl_icmp_code = 0; fl.fl_icmp_code = 0;
if (raw_opt->checksum) { /* merge ip6_build_xmit from ip6_output */
struct rawv6_fakehdr hdr; if (opt && opt->srcrt) {
struct rt0_hdr *rt0 = (struct rt0_hdr *) opt->srcrt;
hdr.iov = msg->msg_iov; fl.fl6_dst = rt0->addr;
hdr.sk = sk; }
hdr.len = len;
hdr.cksum = 0; if (!fl.oif && ipv6_addr_is_multicast(fl.nl_u.ip6_u.daddr))
hdr.proto = proto; fl.oif = np->mcast_oif;
if (opt && opt->srcrt) err = ip6_dst_lookup(sk, &dst, &fl, &saddr);
hdr.daddr = daddr; if (err) goto out;
if (hlimit < 0) {
if (ipv6_addr_is_multicast(fl.fl6_dst))
hlimit = np->mcast_hops;
else else
hdr.daddr = NULL; hlimit = np->hop_limit;
if (hlimit < 0)
hlimit = ((struct rt6_info*)dst)->rt6i_hoplimit;
}
err = ip6_build_xmit(sk, rawv6_frag_cksum, &hdr, &fl, len, if (msg->msg_flags&MSG_CONFIRM)
opt, hlimit, msg->msg_flags); goto do_confirm;
back_from_confirm:
if (inet->hdrincl) {
err = rawv6_send_hdrinc(sk, msg->msg_iov, len, &fl, (struct rt6_info*)dst, msg->msg_flags);
} else { } else {
err = ip6_build_xmit(sk, rawv6_getfrag, msg->msg_iov, &fl, len, lock_sock(sk);
opt, hlimit, msg->msg_flags); err = ip6_append_data(sk, ip_generic_getfrag, msg->msg_iov, len, 0,
hlimit, opt, &fl, (struct rt6_info*)dst, msg->msg_flags);
if (err)
ip6_flush_pending_frames(sk);
else if (!(msg->msg_flags & MSG_MORE)) {
if (raw_opt->checksum) {
err = rawv6_push_pending_frames(sk, &fl, raw_opt, len);
} else {
err = ip6_push_pending_frames(sk);
}
}
} }
done:
ip6_dst_store(sk, dst, fl.nl_u.ip6_u.daddr == &np->daddr ? &np->daddr : NULL);
if (err > 0)
err = np->recverr ? net_xmit_errno(err) : 0;
release_sock(sk);
out:
fl6_sock_release(flowlabel); fl6_sock_release(flowlabel);
if (saddr) kfree(saddr);
return err<0?err:len; return err<0?err:len;
do_confirm:
dst_confirm(dst);
if (!(msg->msg_flags & MSG_PROBE) || len)
goto back_from_confirm;
err = 0;
goto done;
} }
static int rawv6_seticmpfilter(struct sock *sk, int level, int optname, static int rawv6_seticmpfilter(struct sock *sk, int level, int optname,
......
This diff is collapsed.
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment