2005-04-17 06:20:36 +08:00
|
|
|
/*
|
|
|
|
* common UDP/RAW code
|
2007-02-09 22:24:49 +08:00
|
|
|
* Linux INET6 implementation
|
2005-04-17 06:20:36 +08:00
|
|
|
*
|
|
|
|
* Authors:
|
2007-02-09 22:24:49 +08:00
|
|
|
* Pedro Roque <roque@di.fc.ul.pt>
|
2005-04-17 06:20:36 +08:00
|
|
|
*
|
|
|
|
* This program is free software; you can redistribute it and/or
|
|
|
|
* modify it under the terms of the GNU General Public License
|
|
|
|
* as published by the Free Software Foundation; either version
|
|
|
|
* 2 of the License, or (at your option) any later version.
|
|
|
|
*/
|
|
|
|
|
2006-01-12 04:17:47 +08:00
|
|
|
#include <linux/capability.h>
|
2005-04-17 06:20:36 +08:00
|
|
|
#include <linux/errno.h>
|
|
|
|
#include <linux/types.h>
|
|
|
|
#include <linux/kernel.h>
|
|
|
|
#include <linux/interrupt.h>
|
|
|
|
#include <linux/socket.h>
|
|
|
|
#include <linux/sockios.h>
|
|
|
|
#include <linux/in6.h>
|
|
|
|
#include <linux/ipv6.h>
|
|
|
|
#include <linux/route.h>
|
include cleanup: Update gfp.h and slab.h includes to prepare for breaking implicit slab.h inclusion from percpu.h
percpu.h is included by sched.h and module.h and thus ends up being
included when building most .c files. percpu.h includes slab.h which
in turn includes gfp.h making everything defined by the two files
universally available and complicating inclusion dependencies.
percpu.h -> slab.h dependency is about to be removed. Prepare for
this change by updating users of gfp and slab facilities include those
headers directly instead of assuming availability. As this conversion
needs to touch large number of source files, the following script is
used as the basis of conversion.
http://userweb.kernel.org/~tj/misc/slabh-sweep.py
The script does the followings.
* Scan files for gfp and slab usages and update includes such that
only the necessary includes are there. ie. if only gfp is used,
gfp.h, if slab is used, slab.h.
* When the script inserts a new include, it looks at the include
blocks and try to put the new include such that its order conforms
to its surrounding. It's put in the include block which contains
core kernel includes, in the same order that the rest are ordered -
alphabetical, Christmas tree, rev-Xmas-tree or at the end if there
doesn't seem to be any matching order.
* If the script can't find a place to put a new include (mostly
because the file doesn't have fitting include block), it prints out
an error message indicating which .h file needs to be added to the
file.
The conversion was done in the following steps.
1. The initial automatic conversion of all .c files updated slightly
over 4000 files, deleting around 700 includes and adding ~480 gfp.h
and ~3000 slab.h inclusions. The script emitted errors for ~400
files.
2. Each error was manually checked. Some didn't need the inclusion,
some needed manual addition while adding it to implementation .h or
embedding .c file was more appropriate for others. This step added
inclusions to around 150 files.
3. The script was run again and the output was compared to the edits
from #2 to make sure no file was left behind.
4. Several build tests were done and a couple of problems were fixed.
e.g. lib/decompress_*.c used malloc/free() wrappers around slab
APIs requiring slab.h to be added manually.
5. The script was run on all .h files but without automatically
editing them as sprinkling gfp.h and slab.h inclusions around .h
files could easily lead to inclusion dependency hell. Most gfp.h
inclusion directives were ignored as stuff from gfp.h was usually
wildly available and often used in preprocessor macros. Each
slab.h inclusion directive was examined and added manually as
necessary.
6. percpu.h was updated not to include slab.h.
7. Build test were done on the following configurations and failures
were fixed. CONFIG_GCOV_KERNEL was turned off for all tests (as my
distributed build env didn't work with gcov compiles) and a few
more options had to be turned off depending on archs to make things
build (like ipr on powerpc/64 which failed due to missing writeq).
* x86 and x86_64 UP and SMP allmodconfig and a custom test config.
* powerpc and powerpc64 SMP allmodconfig
* sparc and sparc64 SMP allmodconfig
* ia64 SMP allmodconfig
* s390 SMP allmodconfig
* alpha SMP allmodconfig
* um on x86_64 SMP allmodconfig
8. percpu.h modifications were reverted so that it could be applied as
a separate patch and serve as bisection point.
Given the fact that I had only a couple of failures from tests on step
6, I'm fairly confident about the coverage of this conversion patch.
If there is a breakage, it's likely to be something in one of the arch
headers which should be easily discoverable easily on most builds of
the specific arch.
Signed-off-by: Tejun Heo <tj@kernel.org>
Guess-its-ok-by: Christoph Lameter <cl@linux-foundation.org>
Cc: Ingo Molnar <mingo@redhat.com>
Cc: Lee Schermerhorn <Lee.Schermerhorn@hp.com>
2010-03-24 16:04:11 +08:00
|
|
|
#include <linux/slab.h>
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
#include <net/ipv6.h>
|
|
|
|
#include <net/ndisc.h>
|
|
|
|
#include <net/addrconf.h>
|
|
|
|
#include <net/transp_v6.h>
|
|
|
|
#include <net/ip6_route.h>
|
2005-08-10 11:08:28 +08:00
|
|
|
#include <net/tcp_states.h>
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
#include <linux/errqueue.h>
|
|
|
|
#include <asm/uaccess.h>
|
|
|
|
|
|
|
|
int ip6_datagram_connect(struct sock *sk, struct sockaddr *uaddr, int addr_len)
|
|
|
|
{
|
|
|
|
struct sockaddr_in6 *usin = (struct sockaddr_in6 *) uaddr;
|
|
|
|
struct inet_sock *inet = inet_sk(sk);
|
|
|
|
struct ipv6_pinfo *np = inet6_sk(sk);
|
2010-06-02 05:35:01 +08:00
|
|
|
struct in6_addr *daddr, *final_p, final;
|
2005-04-17 06:20:36 +08:00
|
|
|
struct dst_entry *dst;
|
2011-03-13 05:22:43 +08:00
|
|
|
struct flowi6 fl6;
|
2005-04-17 06:20:36 +08:00
|
|
|
struct ip6_flowlabel *flowlabel = NULL;
|
2010-06-02 05:35:01 +08:00
|
|
|
struct ipv6_txoptions *opt;
|
2005-04-17 06:20:36 +08:00
|
|
|
int addr_type;
|
|
|
|
int err;
|
|
|
|
|
|
|
|
if (usin->sin6_family == AF_INET) {
|
|
|
|
if (__ipv6_only_sock(sk))
|
|
|
|
return -EAFNOSUPPORT;
|
|
|
|
err = ip4_datagram_connect(sk, uaddr, addr_len);
|
|
|
|
goto ipv4_connected;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (addr_len < SIN6_LEN_RFC2133)
|
2007-02-09 22:24:49 +08:00
|
|
|
return -EINVAL;
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2007-02-09 22:24:49 +08:00
|
|
|
if (usin->sin6_family != AF_INET6)
|
|
|
|
return -EAFNOSUPPORT;
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2011-03-13 05:22:43 +08:00
|
|
|
memset(&fl6, 0, sizeof(fl6));
|
2005-04-17 06:20:36 +08:00
|
|
|
if (np->sndflow) {
|
2011-03-13 05:22:43 +08:00
|
|
|
fl6.flowlabel = usin->sin6_flowinfo&IPV6_FLOWINFO_MASK;
|
|
|
|
if (fl6.flowlabel&IPV6_FLOWLABEL_MASK) {
|
|
|
|
flowlabel = fl6_sock_lookup(sk, fl6.flowlabel);
|
2005-04-17 06:20:36 +08:00
|
|
|
if (flowlabel == NULL)
|
|
|
|
return -EINVAL;
|
|
|
|
ipv6_addr_copy(&usin->sin6_addr, &flowlabel->dst);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
addr_type = ipv6_addr_type(&usin->sin6_addr);
|
|
|
|
|
|
|
|
if (addr_type == IPV6_ADDR_ANY) {
|
|
|
|
/*
|
|
|
|
* connect to self
|
|
|
|
*/
|
|
|
|
usin->sin6_addr.s6_addr[15] = 0x01;
|
|
|
|
}
|
|
|
|
|
|
|
|
daddr = &usin->sin6_addr;
|
|
|
|
|
|
|
|
if (addr_type == IPV6_ADDR_MAPPED) {
|
|
|
|
struct sockaddr_in sin;
|
|
|
|
|
|
|
|
if (__ipv6_only_sock(sk)) {
|
|
|
|
err = -ENETUNREACH;
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
sin.sin_family = AF_INET;
|
|
|
|
sin.sin_addr.s_addr = daddr->s6_addr32[3];
|
|
|
|
sin.sin_port = usin->sin6_port;
|
|
|
|
|
2007-02-09 22:24:49 +08:00
|
|
|
err = ip4_datagram_connect(sk,
|
|
|
|
(struct sockaddr*) &sin,
|
2005-04-17 06:20:36 +08:00
|
|
|
sizeof(sin));
|
|
|
|
|
|
|
|
ipv4_connected:
|
|
|
|
if (err)
|
|
|
|
goto out;
|
2007-02-09 22:24:49 +08:00
|
|
|
|
2009-10-15 14:30:45 +08:00
|
|
|
ipv6_addr_set_v4mapped(inet->inet_daddr, &np->daddr);
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2009-10-08 04:58:25 +08:00
|
|
|
if (ipv6_addr_any(&np->saddr))
|
2009-10-15 14:30:45 +08:00
|
|
|
ipv6_addr_set_v4mapped(inet->inet_saddr, &np->saddr);
|
2009-10-08 04:58:25 +08:00
|
|
|
|
udp: add rehash on connect()
commit 30fff923 introduced in linux-2.6.33 (udp: bind() optimisation)
added a secondary hash on UDP, hashed on (local addr, local port).
Problem is that following sequence :
fd = socket(...)
connect(fd, &remote, ...)
not only selects remote end point (address and port), but also sets
local address, while UDP stack stored in secondary hash table the socket
while its local address was INADDR_ANY (or ipv6 equivalent)
Sequence is :
- autobind() : choose a random local port, insert socket in hash tables
[while local address is INADDR_ANY]
- connect() : set remote address and port, change local address to IP
given by a route lookup.
When an incoming UDP frame comes, if more than 10 sockets are found in
primary hash table, we switch to secondary table, and fail to find
socket because its local address changed.
One solution to this problem is to rehash datagram socket if needed.
We add a new rehash(struct socket *) method in "struct proto", and
implement this method for UDP v4 & v6, using a common helper.
This rehashing only takes care of secondary hash table, since primary
hash (based on local port only) is not changed.
Reported-by: Krzysztof Piotr Oledzki <ole@ans.pl>
Signed-off-by: Eric Dumazet <eric.dumazet@gmail.com>
Tested-by: Krzysztof Piotr Oledzki <ole@ans.pl>
Signed-off-by: David S. Miller <davem@davemloft.net>
2010-09-08 13:08:44 +08:00
|
|
|
if (ipv6_addr_any(&np->rcv_saddr)) {
|
2009-10-15 14:30:45 +08:00
|
|
|
ipv6_addr_set_v4mapped(inet->inet_rcv_saddr,
|
|
|
|
&np->rcv_saddr);
|
udp: add rehash on connect()
commit 30fff923 introduced in linux-2.6.33 (udp: bind() optimisation)
added a secondary hash on UDP, hashed on (local addr, local port).
Problem is that following sequence :
fd = socket(...)
connect(fd, &remote, ...)
not only selects remote end point (address and port), but also sets
local address, while UDP stack stored in secondary hash table the socket
while its local address was INADDR_ANY (or ipv6 equivalent)
Sequence is :
- autobind() : choose a random local port, insert socket in hash tables
[while local address is INADDR_ANY]
- connect() : set remote address and port, change local address to IP
given by a route lookup.
When an incoming UDP frame comes, if more than 10 sockets are found in
primary hash table, we switch to secondary table, and fail to find
socket because its local address changed.
One solution to this problem is to rehash datagram socket if needed.
We add a new rehash(struct socket *) method in "struct proto", and
implement this method for UDP v4 & v6, using a common helper.
This rehashing only takes care of secondary hash table, since primary
hash (based on local port only) is not changed.
Reported-by: Krzysztof Piotr Oledzki <ole@ans.pl>
Signed-off-by: Eric Dumazet <eric.dumazet@gmail.com>
Tested-by: Krzysztof Piotr Oledzki <ole@ans.pl>
Signed-off-by: David S. Miller <davem@davemloft.net>
2010-09-08 13:08:44 +08:00
|
|
|
if (sk->sk_prot->rehash)
|
|
|
|
sk->sk_prot->rehash(sk);
|
|
|
|
}
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (addr_type&IPV6_ADDR_LINKLOCAL) {
|
|
|
|
if (addr_len >= sizeof(struct sockaddr_in6) &&
|
|
|
|
usin->sin6_scope_id) {
|
|
|
|
if (sk->sk_bound_dev_if &&
|
|
|
|
sk->sk_bound_dev_if != usin->sin6_scope_id) {
|
|
|
|
err = -EINVAL;
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
sk->sk_bound_dev_if = usin->sin6_scope_id;
|
|
|
|
}
|
|
|
|
|
2008-01-09 15:52:21 +08:00
|
|
|
if (!sk->sk_bound_dev_if && (addr_type & IPV6_ADDR_MULTICAST))
|
|
|
|
sk->sk_bound_dev_if = np->mcast_oif;
|
|
|
|
|
2005-04-17 06:20:36 +08:00
|
|
|
/* Connect to link-local address requires an interface */
|
|
|
|
if (!sk->sk_bound_dev_if) {
|
|
|
|
err = -EINVAL;
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
ipv6_addr_copy(&np->daddr, daddr);
|
2011-03-13 05:22:43 +08:00
|
|
|
np->flow_label = fl6.flowlabel;
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2009-10-15 14:30:45 +08:00
|
|
|
inet->inet_dport = usin->sin6_port;
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Check for a route to destination an obtain the
|
|
|
|
* destination cache for it.
|
|
|
|
*/
|
|
|
|
|
2011-03-13 05:22:43 +08:00
|
|
|
fl6.flowi6_proto = sk->sk_protocol;
|
|
|
|
ipv6_addr_copy(&fl6.daddr, &np->daddr);
|
|
|
|
ipv6_addr_copy(&fl6.saddr, &np->saddr);
|
|
|
|
fl6.flowi6_oif = sk->sk_bound_dev_if;
|
|
|
|
fl6.flowi6_mark = sk->sk_mark;
|
2011-03-13 05:36:19 +08:00
|
|
|
fl6.fl6_dport = inet->inet_dport;
|
|
|
|
fl6.fl6_sport = inet->inet_sport;
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2011-03-13 05:22:43 +08:00
|
|
|
if (!fl6.flowi6_oif && (addr_type&IPV6_ADDR_MULTICAST))
|
|
|
|
fl6.flowi6_oif = np->mcast_oif;
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2011-03-13 05:22:43 +08:00
|
|
|
security_sk_classify_flow(sk, flowi6_to_flowi(&fl6));
|
2006-08-05 14:12:42 +08:00
|
|
|
|
2010-06-02 05:35:01 +08:00
|
|
|
opt = flowlabel ? flowlabel->opt : np->opt;
|
2011-03-13 05:22:43 +08:00
|
|
|
final_p = fl6_update_dst(&fl6, opt, &final);
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2011-03-13 05:22:43 +08:00
|
|
|
dst = ip6_dst_lookup_flow(sk, &fl6, final_p, true);
|
2011-03-02 05:19:07 +08:00
|
|
|
err = 0;
|
|
|
|
if (IS_ERR(dst)) {
|
|
|
|
err = PTR_ERR(dst);
|
2005-04-17 06:20:36 +08:00
|
|
|
goto out;
|
2007-05-25 09:17:54 +08:00
|
|
|
}
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
/* source address lookup done in ip6_dst_lookup */
|
|
|
|
|
|
|
|
if (ipv6_addr_any(&np->saddr))
|
2011-03-13 05:22:43 +08:00
|
|
|
ipv6_addr_copy(&np->saddr, &fl6.saddr);
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
if (ipv6_addr_any(&np->rcv_saddr)) {
|
2011-03-13 05:22:43 +08:00
|
|
|
ipv6_addr_copy(&np->rcv_saddr, &fl6.saddr);
|
2009-10-15 14:30:45 +08:00
|
|
|
inet->inet_rcv_saddr = LOOPBACK4_IPV6;
|
udp: add rehash on connect()
commit 30fff923 introduced in linux-2.6.33 (udp: bind() optimisation)
added a secondary hash on UDP, hashed on (local addr, local port).
Problem is that following sequence :
fd = socket(...)
connect(fd, &remote, ...)
not only selects remote end point (address and port), but also sets
local address, while UDP stack stored in secondary hash table the socket
while its local address was INADDR_ANY (or ipv6 equivalent)
Sequence is :
- autobind() : choose a random local port, insert socket in hash tables
[while local address is INADDR_ANY]
- connect() : set remote address and port, change local address to IP
given by a route lookup.
When an incoming UDP frame comes, if more than 10 sockets are found in
primary hash table, we switch to secondary table, and fail to find
socket because its local address changed.
One solution to this problem is to rehash datagram socket if needed.
We add a new rehash(struct socket *) method in "struct proto", and
implement this method for UDP v4 & v6, using a common helper.
This rehashing only takes care of secondary hash table, since primary
hash (based on local port only) is not changed.
Reported-by: Krzysztof Piotr Oledzki <ole@ans.pl>
Signed-off-by: Eric Dumazet <eric.dumazet@gmail.com>
Tested-by: Krzysztof Piotr Oledzki <ole@ans.pl>
Signed-off-by: David S. Miller <davem@davemloft.net>
2010-09-08 13:08:44 +08:00
|
|
|
if (sk->sk_prot->rehash)
|
|
|
|
sk->sk_prot->rehash(sk);
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
ip6_dst_store(sk, dst,
|
2011-03-13 05:22:43 +08:00
|
|
|
ipv6_addr_equal(&fl6.daddr, &np->daddr) ?
|
2006-08-30 08:15:09 +08:00
|
|
|
&np->daddr : NULL,
|
|
|
|
#ifdef CONFIG_IPV6_SUBTREES
|
2011-03-13 05:22:43 +08:00
|
|
|
ipv6_addr_equal(&fl6.saddr, &np->saddr) ?
|
2006-08-30 08:15:09 +08:00
|
|
|
&np->saddr :
|
|
|
|
#endif
|
|
|
|
NULL);
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
sk->sk_state = TCP_ESTABLISHED;
|
|
|
|
out:
|
|
|
|
fl6_sock_release(flowlabel);
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
2007-02-09 22:24:49 +08:00
|
|
|
void ipv6_icmp_error(struct sock *sk, struct sk_buff *skb, int err,
|
2006-11-15 12:56:00 +08:00
|
|
|
__be16 port, u32 info, u8 *payload)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
|
|
|
struct ipv6_pinfo *np = inet6_sk(sk);
|
2007-03-14 01:03:22 +08:00
|
|
|
struct icmp6hdr *icmph = icmp6_hdr(skb);
|
2005-04-17 06:20:36 +08:00
|
|
|
struct sock_exterr_skb *serr;
|
|
|
|
|
|
|
|
if (!np->recverr)
|
|
|
|
return;
|
|
|
|
|
|
|
|
skb = skb_clone(skb, GFP_ATOMIC);
|
|
|
|
if (!skb)
|
|
|
|
return;
|
|
|
|
|
2010-05-03 23:44:27 +08:00
|
|
|
skb->protocol = htons(ETH_P_IPV6);
|
|
|
|
|
2005-04-17 06:20:36 +08:00
|
|
|
serr = SKB_EXT_ERR(skb);
|
|
|
|
serr->ee.ee_errno = err;
|
|
|
|
serr->ee.ee_origin = SO_EE_ORIGIN_ICMP6;
|
2007-02-09 22:24:49 +08:00
|
|
|
serr->ee.ee_type = icmph->icmp6_type;
|
2005-04-17 06:20:36 +08:00
|
|
|
serr->ee.ee_code = icmph->icmp6_code;
|
|
|
|
serr->ee.ee_pad = 0;
|
|
|
|
serr->ee.ee_info = info;
|
|
|
|
serr->ee.ee_data = 0;
|
2007-04-11 11:50:43 +08:00
|
|
|
serr->addr_offset = (u8 *)&(((struct ipv6hdr *)(icmph + 1))->daddr) -
|
|
|
|
skb_network_header(skb);
|
2005-04-17 06:20:36 +08:00
|
|
|
serr->port = port;
|
|
|
|
|
|
|
|
__skb_pull(skb, payload - skb->data);
|
2007-03-14 04:10:43 +08:00
|
|
|
skb_reset_transport_header(skb);
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
if (sock_queue_err_skb(sk, skb))
|
|
|
|
kfree_skb(skb);
|
|
|
|
}
|
|
|
|
|
2011-03-13 05:22:43 +08:00
|
|
|
void ipv6_local_error(struct sock *sk, int err, struct flowi6 *fl6, u32 info)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
|
|
|
struct ipv6_pinfo *np = inet6_sk(sk);
|
|
|
|
struct sock_exterr_skb *serr;
|
|
|
|
struct ipv6hdr *iph;
|
|
|
|
struct sk_buff *skb;
|
|
|
|
|
|
|
|
if (!np->recverr)
|
|
|
|
return;
|
|
|
|
|
|
|
|
skb = alloc_skb(sizeof(struct ipv6hdr), GFP_ATOMIC);
|
|
|
|
if (!skb)
|
|
|
|
return;
|
|
|
|
|
2010-05-03 23:44:27 +08:00
|
|
|
skb->protocol = htons(ETH_P_IPV6);
|
|
|
|
|
2007-03-11 06:57:15 +08:00
|
|
|
skb_put(skb, sizeof(struct ipv6hdr));
|
|
|
|
skb_reset_network_header(skb);
|
2007-04-26 08:54:47 +08:00
|
|
|
iph = ipv6_hdr(skb);
|
2011-03-13 05:22:43 +08:00
|
|
|
ipv6_addr_copy(&iph->daddr, &fl6->daddr);
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
serr = SKB_EXT_ERR(skb);
|
|
|
|
serr->ee.ee_errno = err;
|
|
|
|
serr->ee.ee_origin = SO_EE_ORIGIN_LOCAL;
|
2007-02-09 22:24:49 +08:00
|
|
|
serr->ee.ee_type = 0;
|
2005-04-17 06:20:36 +08:00
|
|
|
serr->ee.ee_code = 0;
|
|
|
|
serr->ee.ee_pad = 0;
|
|
|
|
serr->ee.ee_info = info;
|
|
|
|
serr->ee.ee_data = 0;
|
2007-04-11 11:50:43 +08:00
|
|
|
serr->addr_offset = (u8 *)&iph->daddr - skb_network_header(skb);
|
2011-03-13 05:36:19 +08:00
|
|
|
serr->port = fl6->fl6_dport;
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2007-04-20 11:29:13 +08:00
|
|
|
__skb_pull(skb, skb_tail_pointer(skb) - skb->data);
|
2007-03-14 04:10:43 +08:00
|
|
|
skb_reset_transport_header(skb);
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
if (sock_queue_err_skb(sk, skb))
|
|
|
|
kfree_skb(skb);
|
|
|
|
}
|
|
|
|
|
2011-03-13 05:22:43 +08:00
|
|
|
void ipv6_local_rxpmtu(struct sock *sk, struct flowi6 *fl6, u32 mtu)
|
2010-04-23 19:26:09 +08:00
|
|
|
{
|
|
|
|
struct ipv6_pinfo *np = inet6_sk(sk);
|
|
|
|
struct ipv6hdr *iph;
|
|
|
|
struct sk_buff *skb;
|
|
|
|
struct ip6_mtuinfo *mtu_info;
|
|
|
|
|
|
|
|
if (!np->rxopt.bits.rxpmtu)
|
|
|
|
return;
|
|
|
|
|
|
|
|
skb = alloc_skb(sizeof(struct ipv6hdr), GFP_ATOMIC);
|
|
|
|
if (!skb)
|
|
|
|
return;
|
|
|
|
|
|
|
|
skb_put(skb, sizeof(struct ipv6hdr));
|
|
|
|
skb_reset_network_header(skb);
|
|
|
|
iph = ipv6_hdr(skb);
|
2011-03-13 05:22:43 +08:00
|
|
|
ipv6_addr_copy(&iph->daddr, &fl6->daddr);
|
2010-04-23 19:26:09 +08:00
|
|
|
|
|
|
|
mtu_info = IP6CBMTU(skb);
|
|
|
|
if (!mtu_info) {
|
|
|
|
kfree_skb(skb);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
mtu_info->ip6m_mtu = mtu;
|
|
|
|
mtu_info->ip6m_addr.sin6_family = AF_INET6;
|
|
|
|
mtu_info->ip6m_addr.sin6_port = 0;
|
|
|
|
mtu_info->ip6m_addr.sin6_flowinfo = 0;
|
2011-03-13 05:22:43 +08:00
|
|
|
mtu_info->ip6m_addr.sin6_scope_id = fl6->flowi6_oif;
|
2010-04-23 19:26:09 +08:00
|
|
|
ipv6_addr_copy(&mtu_info->ip6m_addr.sin6_addr, &ipv6_hdr(skb)->daddr);
|
|
|
|
|
|
|
|
__skb_pull(skb, skb_tail_pointer(skb) - skb->data);
|
|
|
|
skb_reset_transport_header(skb);
|
|
|
|
|
|
|
|
skb = xchg(&np->rxpmtu, skb);
|
|
|
|
kfree_skb(skb);
|
|
|
|
}
|
|
|
|
|
2007-02-09 22:24:49 +08:00
|
|
|
/*
|
2005-04-17 06:20:36 +08:00
|
|
|
* Handle MSG_ERRQUEUE
|
|
|
|
*/
|
|
|
|
int ipv6_recv_error(struct sock *sk, struct msghdr *msg, int len)
|
|
|
|
{
|
|
|
|
struct ipv6_pinfo *np = inet6_sk(sk);
|
|
|
|
struct sock_exterr_skb *serr;
|
|
|
|
struct sk_buff *skb, *skb2;
|
|
|
|
struct sockaddr_in6 *sin;
|
|
|
|
struct {
|
|
|
|
struct sock_extended_err ee;
|
|
|
|
struct sockaddr_in6 offender;
|
|
|
|
} errhdr;
|
|
|
|
int err;
|
|
|
|
int copied;
|
|
|
|
|
|
|
|
err = -EAGAIN;
|
|
|
|
skb = skb_dequeue(&sk->sk_error_queue);
|
|
|
|
if (skb == NULL)
|
|
|
|
goto out;
|
|
|
|
|
|
|
|
copied = skb->len;
|
|
|
|
if (copied > len) {
|
|
|
|
msg->msg_flags |= MSG_TRUNC;
|
|
|
|
copied = len;
|
|
|
|
}
|
|
|
|
err = skb_copy_datagram_iovec(skb, 0, msg->msg_iov, copied);
|
|
|
|
if (err)
|
|
|
|
goto out_free_skb;
|
|
|
|
|
|
|
|
sock_recv_timestamp(msg, sk, skb);
|
|
|
|
|
|
|
|
serr = SKB_EXT_ERR(skb);
|
|
|
|
|
|
|
|
sin = (struct sockaddr_in6 *)msg->msg_name;
|
|
|
|
if (sin) {
|
2007-04-11 11:50:43 +08:00
|
|
|
const unsigned char *nh = skb_network_header(skb);
|
2005-04-17 06:20:36 +08:00
|
|
|
sin->sin6_family = AF_INET6;
|
|
|
|
sin->sin6_flowinfo = 0;
|
2007-02-09 22:24:49 +08:00
|
|
|
sin->sin6_port = serr->port;
|
2005-04-17 06:20:36 +08:00
|
|
|
sin->sin6_scope_id = 0;
|
2010-05-03 23:44:27 +08:00
|
|
|
if (skb->protocol == htons(ETH_P_IPV6)) {
|
2005-04-17 06:20:36 +08:00
|
|
|
ipv6_addr_copy(&sin->sin6_addr,
|
2007-04-11 11:50:43 +08:00
|
|
|
(struct in6_addr *)(nh + serr->addr_offset));
|
2005-04-17 06:20:36 +08:00
|
|
|
if (np->sndflow)
|
2007-04-11 11:50:43 +08:00
|
|
|
sin->sin6_flowinfo =
|
|
|
|
(*(__be32 *)(nh + serr->addr_offset - 24) &
|
|
|
|
IPV6_FLOWINFO_MASK);
|
2005-04-17 06:20:36 +08:00
|
|
|
if (ipv6_addr_type(&sin->sin6_addr) & IPV6_ADDR_LINKLOCAL)
|
|
|
|
sin->sin6_scope_id = IP6CB(skb)->iif;
|
|
|
|
} else {
|
2009-10-08 04:58:25 +08:00
|
|
|
ipv6_addr_set_v4mapped(*(__be32 *)(nh + serr->addr_offset),
|
|
|
|
&sin->sin6_addr);
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
memcpy(&errhdr.ee, &serr->ee, sizeof(struct sock_extended_err));
|
|
|
|
sin = &errhdr.offender;
|
|
|
|
sin->sin6_family = AF_UNSPEC;
|
|
|
|
if (serr->ee.ee_origin != SO_EE_ORIGIN_LOCAL) {
|
|
|
|
sin->sin6_family = AF_INET6;
|
|
|
|
sin->sin6_flowinfo = 0;
|
|
|
|
sin->sin6_scope_id = 0;
|
2010-05-03 23:44:27 +08:00
|
|
|
if (skb->protocol == htons(ETH_P_IPV6)) {
|
2007-04-26 08:54:47 +08:00
|
|
|
ipv6_addr_copy(&sin->sin6_addr, &ipv6_hdr(skb)->saddr);
|
2005-04-17 06:20:36 +08:00
|
|
|
if (np->rxopt.all)
|
|
|
|
datagram_recv_ctl(sk, msg, skb);
|
|
|
|
if (ipv6_addr_type(&sin->sin6_addr) & IPV6_ADDR_LINKLOCAL)
|
|
|
|
sin->sin6_scope_id = IP6CB(skb)->iif;
|
|
|
|
} else {
|
|
|
|
struct inet_sock *inet = inet_sk(sk);
|
|
|
|
|
2009-10-08 04:58:25 +08:00
|
|
|
ipv6_addr_set_v4mapped(ip_hdr(skb)->saddr,
|
|
|
|
&sin->sin6_addr);
|
2005-04-17 06:20:36 +08:00
|
|
|
if (inet->cmsg_flags)
|
|
|
|
ip_cmsg_recv(msg, skb);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
put_cmsg(msg, SOL_IPV6, IPV6_RECVERR, sizeof(errhdr), &errhdr);
|
|
|
|
|
|
|
|
/* Now we could try to dump offended packet options */
|
|
|
|
|
|
|
|
msg->msg_flags |= MSG_ERRQUEUE;
|
|
|
|
err = copied;
|
|
|
|
|
|
|
|
/* Reset and regenerate socket error */
|
2005-06-19 13:56:18 +08:00
|
|
|
spin_lock_bh(&sk->sk_error_queue.lock);
|
2005-04-17 06:20:36 +08:00
|
|
|
sk->sk_err = 0;
|
|
|
|
if ((skb2 = skb_peek(&sk->sk_error_queue)) != NULL) {
|
|
|
|
sk->sk_err = SKB_EXT_ERR(skb2)->ee.ee_errno;
|
2005-06-19 13:56:18 +08:00
|
|
|
spin_unlock_bh(&sk->sk_error_queue.lock);
|
2005-04-17 06:20:36 +08:00
|
|
|
sk->sk_error_report(sk);
|
|
|
|
} else {
|
2005-06-19 13:56:18 +08:00
|
|
|
spin_unlock_bh(&sk->sk_error_queue.lock);
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
|
2007-02-09 22:24:49 +08:00
|
|
|
out_free_skb:
|
2005-04-17 06:20:36 +08:00
|
|
|
kfree_skb(skb);
|
|
|
|
out:
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
2010-04-23 19:26:09 +08:00
|
|
|
/*
|
|
|
|
* Handle IPV6_RECVPATHMTU
|
|
|
|
*/
|
|
|
|
int ipv6_recv_rxpmtu(struct sock *sk, struct msghdr *msg, int len)
|
|
|
|
{
|
|
|
|
struct ipv6_pinfo *np = inet6_sk(sk);
|
|
|
|
struct sk_buff *skb;
|
|
|
|
struct sockaddr_in6 *sin;
|
|
|
|
struct ip6_mtuinfo mtu_info;
|
|
|
|
int err;
|
|
|
|
int copied;
|
|
|
|
|
|
|
|
err = -EAGAIN;
|
|
|
|
skb = xchg(&np->rxpmtu, NULL);
|
|
|
|
if (skb == NULL)
|
|
|
|
goto out;
|
|
|
|
|
|
|
|
copied = skb->len;
|
|
|
|
if (copied > len) {
|
|
|
|
msg->msg_flags |= MSG_TRUNC;
|
|
|
|
copied = len;
|
|
|
|
}
|
|
|
|
err = skb_copy_datagram_iovec(skb, 0, msg->msg_iov, copied);
|
|
|
|
if (err)
|
|
|
|
goto out_free_skb;
|
|
|
|
|
|
|
|
sock_recv_timestamp(msg, sk, skb);
|
|
|
|
|
|
|
|
memcpy(&mtu_info, IP6CBMTU(skb), sizeof(mtu_info));
|
|
|
|
|
|
|
|
sin = (struct sockaddr_in6 *)msg->msg_name;
|
|
|
|
if (sin) {
|
|
|
|
sin->sin6_family = AF_INET6;
|
|
|
|
sin->sin6_flowinfo = 0;
|
|
|
|
sin->sin6_port = 0;
|
|
|
|
sin->sin6_scope_id = mtu_info.ip6m_addr.sin6_scope_id;
|
|
|
|
ipv6_addr_copy(&sin->sin6_addr, &mtu_info.ip6m_addr.sin6_addr);
|
|
|
|
}
|
|
|
|
|
|
|
|
put_cmsg(msg, SOL_IPV6, IPV6_PATHMTU, sizeof(mtu_info), &mtu_info);
|
|
|
|
|
|
|
|
err = copied;
|
|
|
|
|
|
|
|
out_free_skb:
|
|
|
|
kfree_skb(skb);
|
|
|
|
out:
|
|
|
|
return err;
|
|
|
|
}
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
|
|
|
|
int datagram_recv_ctl(struct sock *sk, struct msghdr *msg, struct sk_buff *skb)
|
|
|
|
{
|
|
|
|
struct ipv6_pinfo *np = inet6_sk(sk);
|
|
|
|
struct inet6_skb_parm *opt = IP6CB(skb);
|
2007-04-11 11:50:43 +08:00
|
|
|
unsigned char *nh = skb_network_header(skb);
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
if (np->rxopt.bits.rxinfo) {
|
|
|
|
struct in6_pktinfo src_info;
|
|
|
|
|
|
|
|
src_info.ipi6_ifindex = opt->iif;
|
2007-04-26 08:54:47 +08:00
|
|
|
ipv6_addr_copy(&src_info.ipi6_addr, &ipv6_hdr(skb)->daddr);
|
2005-04-17 06:20:36 +08:00
|
|
|
put_cmsg(msg, SOL_IPV6, IPV6_PKTINFO, sizeof(src_info), &src_info);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (np->rxopt.bits.rxhlim) {
|
2007-04-26 08:54:47 +08:00
|
|
|
int hlim = ipv6_hdr(skb)->hop_limit;
|
2005-04-17 06:20:36 +08:00
|
|
|
put_cmsg(msg, SOL_IPV6, IPV6_HOPLIMIT, sizeof(hlim), &hlim);
|
|
|
|
}
|
|
|
|
|
2005-09-08 09:19:03 +08:00
|
|
|
if (np->rxopt.bits.rxtclass) {
|
2007-04-26 08:54:47 +08:00
|
|
|
int tclass = (ntohl(*(__be32 *)ipv6_hdr(skb)) >> 20) & 0xff;
|
2005-09-08 09:19:03 +08:00
|
|
|
put_cmsg(msg, SOL_IPV6, IPV6_TCLASS, sizeof(tclass), &tclass);
|
|
|
|
}
|
|
|
|
|
2007-04-11 11:50:43 +08:00
|
|
|
if (np->rxopt.bits.rxflow && (*(__be32 *)nh & IPV6_FLOWINFO_MASK)) {
|
|
|
|
__be32 flowinfo = *(__be32 *)nh & IPV6_FLOWINFO_MASK;
|
2005-04-17 06:20:36 +08:00
|
|
|
put_cmsg(msg, SOL_IPV6, IPV6_FLOWINFO, sizeof(flowinfo), &flowinfo);
|
|
|
|
}
|
[IPV6]: Support several new sockopt / ancillary data in Advanced API (RFC3542).
Support several new socket options / ancillary data:
IPV6_RECVPKTINFO, IPV6_PKTINFO,
IPV6_RECVHOPOPTS, IPV6_HOPOPTS,
IPV6_RECVDSTOPTS, IPV6_DSTOPTS, IPV6_RTHDRDSTOPTS,
IPV6_RECVRTHDR, IPV6_RTHDR,
IPV6_RECVHOPOPTS, IPV6_HOPOPTS
Old semantics are preserved as IPV6_2292xxxx so that
we can maintain backward compatibility.
Signed-off-by: YOSHIFUJI Hideaki <yoshfuji@linux-ipv6.org>
2005-09-08 08:59:17 +08:00
|
|
|
|
|
|
|
/* HbH is allowed only once */
|
2005-04-17 06:20:36 +08:00
|
|
|
if (np->rxopt.bits.hopopts && opt->hop) {
|
2007-04-11 11:50:43 +08:00
|
|
|
u8 *ptr = nh + opt->hop;
|
2005-04-17 06:20:36 +08:00
|
|
|
put_cmsg(msg, SOL_IPV6, IPV6_HOPOPTS, (ptr[1]+1)<<3, ptr);
|
|
|
|
}
|
[IPV6]: Support several new sockopt / ancillary data in Advanced API (RFC3542).
Support several new socket options / ancillary data:
IPV6_RECVPKTINFO, IPV6_PKTINFO,
IPV6_RECVHOPOPTS, IPV6_HOPOPTS,
IPV6_RECVDSTOPTS, IPV6_DSTOPTS, IPV6_RTHDRDSTOPTS,
IPV6_RECVRTHDR, IPV6_RTHDR,
IPV6_RECVHOPOPTS, IPV6_HOPOPTS
Old semantics are preserved as IPV6_2292xxxx so that
we can maintain backward compatibility.
Signed-off-by: YOSHIFUJI Hideaki <yoshfuji@linux-ipv6.org>
2005-09-08 08:59:17 +08:00
|
|
|
|
|
|
|
if (opt->lastopt &&
|
|
|
|
(np->rxopt.bits.dstopts || np->rxopt.bits.srcrt)) {
|
|
|
|
/*
|
|
|
|
* Silly enough, but we need to reparse in order to
|
|
|
|
* report extension headers (except for HbH)
|
|
|
|
* in order.
|
|
|
|
*
|
2007-02-09 22:24:49 +08:00
|
|
|
* Also note that IPV6_RECVRTHDRDSTOPTS is NOT
|
[IPV6]: Support several new sockopt / ancillary data in Advanced API (RFC3542).
Support several new socket options / ancillary data:
IPV6_RECVPKTINFO, IPV6_PKTINFO,
IPV6_RECVHOPOPTS, IPV6_HOPOPTS,
IPV6_RECVDSTOPTS, IPV6_DSTOPTS, IPV6_RTHDRDSTOPTS,
IPV6_RECVRTHDR, IPV6_RTHDR,
IPV6_RECVHOPOPTS, IPV6_HOPOPTS
Old semantics are preserved as IPV6_2292xxxx so that
we can maintain backward compatibility.
Signed-off-by: YOSHIFUJI Hideaki <yoshfuji@linux-ipv6.org>
2005-09-08 08:59:17 +08:00
|
|
|
* (and WILL NOT be) defined because
|
|
|
|
* IPV6_RECVDSTOPTS is more generic. --yoshfuji
|
|
|
|
*/
|
|
|
|
unsigned int off = sizeof(struct ipv6hdr);
|
2007-04-26 08:54:47 +08:00
|
|
|
u8 nexthdr = ipv6_hdr(skb)->nexthdr;
|
[IPV6]: Support several new sockopt / ancillary data in Advanced API (RFC3542).
Support several new socket options / ancillary data:
IPV6_RECVPKTINFO, IPV6_PKTINFO,
IPV6_RECVHOPOPTS, IPV6_HOPOPTS,
IPV6_RECVDSTOPTS, IPV6_DSTOPTS, IPV6_RTHDRDSTOPTS,
IPV6_RECVRTHDR, IPV6_RTHDR,
IPV6_RECVHOPOPTS, IPV6_HOPOPTS
Old semantics are preserved as IPV6_2292xxxx so that
we can maintain backward compatibility.
Signed-off-by: YOSHIFUJI Hideaki <yoshfuji@linux-ipv6.org>
2005-09-08 08:59:17 +08:00
|
|
|
|
|
|
|
while (off <= opt->lastopt) {
|
|
|
|
unsigned len;
|
2007-04-11 11:50:43 +08:00
|
|
|
u8 *ptr = nh + off;
|
[IPV6]: Support several new sockopt / ancillary data in Advanced API (RFC3542).
Support several new socket options / ancillary data:
IPV6_RECVPKTINFO, IPV6_PKTINFO,
IPV6_RECVHOPOPTS, IPV6_HOPOPTS,
IPV6_RECVDSTOPTS, IPV6_DSTOPTS, IPV6_RTHDRDSTOPTS,
IPV6_RECVRTHDR, IPV6_RTHDR,
IPV6_RECVHOPOPTS, IPV6_HOPOPTS
Old semantics are preserved as IPV6_2292xxxx so that
we can maintain backward compatibility.
Signed-off-by: YOSHIFUJI Hideaki <yoshfuji@linux-ipv6.org>
2005-09-08 08:59:17 +08:00
|
|
|
|
|
|
|
switch(nexthdr) {
|
|
|
|
case IPPROTO_DSTOPTS:
|
|
|
|
nexthdr = ptr[0];
|
|
|
|
len = (ptr[1] + 1) << 3;
|
|
|
|
if (np->rxopt.bits.dstopts)
|
|
|
|
put_cmsg(msg, SOL_IPV6, IPV6_DSTOPTS, len, ptr);
|
|
|
|
break;
|
|
|
|
case IPPROTO_ROUTING:
|
|
|
|
nexthdr = ptr[0];
|
|
|
|
len = (ptr[1] + 1) << 3;
|
|
|
|
if (np->rxopt.bits.srcrt)
|
|
|
|
put_cmsg(msg, SOL_IPV6, IPV6_RTHDR, len, ptr);
|
|
|
|
break;
|
|
|
|
case IPPROTO_AH:
|
|
|
|
nexthdr = ptr[0];
|
2005-11-20 11:21:59 +08:00
|
|
|
len = (ptr[1] + 2) << 2;
|
[IPV6]: Support several new sockopt / ancillary data in Advanced API (RFC3542).
Support several new socket options / ancillary data:
IPV6_RECVPKTINFO, IPV6_PKTINFO,
IPV6_RECVHOPOPTS, IPV6_HOPOPTS,
IPV6_RECVDSTOPTS, IPV6_DSTOPTS, IPV6_RTHDRDSTOPTS,
IPV6_RECVRTHDR, IPV6_RTHDR,
IPV6_RECVHOPOPTS, IPV6_HOPOPTS
Old semantics are preserved as IPV6_2292xxxx so that
we can maintain backward compatibility.
Signed-off-by: YOSHIFUJI Hideaki <yoshfuji@linux-ipv6.org>
2005-09-08 08:59:17 +08:00
|
|
|
break;
|
|
|
|
default:
|
|
|
|
nexthdr = ptr[0];
|
|
|
|
len = (ptr[1] + 1) << 3;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
off += len;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* socket options in old style */
|
|
|
|
if (np->rxopt.bits.rxoinfo) {
|
|
|
|
struct in6_pktinfo src_info;
|
|
|
|
|
|
|
|
src_info.ipi6_ifindex = opt->iif;
|
2007-04-26 08:54:47 +08:00
|
|
|
ipv6_addr_copy(&src_info.ipi6_addr, &ipv6_hdr(skb)->daddr);
|
[IPV6]: Support several new sockopt / ancillary data in Advanced API (RFC3542).
Support several new socket options / ancillary data:
IPV6_RECVPKTINFO, IPV6_PKTINFO,
IPV6_RECVHOPOPTS, IPV6_HOPOPTS,
IPV6_RECVDSTOPTS, IPV6_DSTOPTS, IPV6_RTHDRDSTOPTS,
IPV6_RECVRTHDR, IPV6_RTHDR,
IPV6_RECVHOPOPTS, IPV6_HOPOPTS
Old semantics are preserved as IPV6_2292xxxx so that
we can maintain backward compatibility.
Signed-off-by: YOSHIFUJI Hideaki <yoshfuji@linux-ipv6.org>
2005-09-08 08:59:17 +08:00
|
|
|
put_cmsg(msg, SOL_IPV6, IPV6_2292PKTINFO, sizeof(src_info), &src_info);
|
|
|
|
}
|
|
|
|
if (np->rxopt.bits.rxohlim) {
|
2007-04-26 08:54:47 +08:00
|
|
|
int hlim = ipv6_hdr(skb)->hop_limit;
|
[IPV6]: Support several new sockopt / ancillary data in Advanced API (RFC3542).
Support several new socket options / ancillary data:
IPV6_RECVPKTINFO, IPV6_PKTINFO,
IPV6_RECVHOPOPTS, IPV6_HOPOPTS,
IPV6_RECVDSTOPTS, IPV6_DSTOPTS, IPV6_RTHDRDSTOPTS,
IPV6_RECVRTHDR, IPV6_RTHDR,
IPV6_RECVHOPOPTS, IPV6_HOPOPTS
Old semantics are preserved as IPV6_2292xxxx so that
we can maintain backward compatibility.
Signed-off-by: YOSHIFUJI Hideaki <yoshfuji@linux-ipv6.org>
2005-09-08 08:59:17 +08:00
|
|
|
put_cmsg(msg, SOL_IPV6, IPV6_2292HOPLIMIT, sizeof(hlim), &hlim);
|
|
|
|
}
|
|
|
|
if (np->rxopt.bits.ohopopts && opt->hop) {
|
2007-04-11 11:50:43 +08:00
|
|
|
u8 *ptr = nh + opt->hop;
|
[IPV6]: Support several new sockopt / ancillary data in Advanced API (RFC3542).
Support several new socket options / ancillary data:
IPV6_RECVPKTINFO, IPV6_PKTINFO,
IPV6_RECVHOPOPTS, IPV6_HOPOPTS,
IPV6_RECVDSTOPTS, IPV6_DSTOPTS, IPV6_RTHDRDSTOPTS,
IPV6_RECVRTHDR, IPV6_RTHDR,
IPV6_RECVHOPOPTS, IPV6_HOPOPTS
Old semantics are preserved as IPV6_2292xxxx so that
we can maintain backward compatibility.
Signed-off-by: YOSHIFUJI Hideaki <yoshfuji@linux-ipv6.org>
2005-09-08 08:59:17 +08:00
|
|
|
put_cmsg(msg, SOL_IPV6, IPV6_2292HOPOPTS, (ptr[1]+1)<<3, ptr);
|
|
|
|
}
|
|
|
|
if (np->rxopt.bits.odstopts && opt->dst0) {
|
2007-04-11 11:50:43 +08:00
|
|
|
u8 *ptr = nh + opt->dst0;
|
[IPV6]: Support several new sockopt / ancillary data in Advanced API (RFC3542).
Support several new socket options / ancillary data:
IPV6_RECVPKTINFO, IPV6_PKTINFO,
IPV6_RECVHOPOPTS, IPV6_HOPOPTS,
IPV6_RECVDSTOPTS, IPV6_DSTOPTS, IPV6_RTHDRDSTOPTS,
IPV6_RECVRTHDR, IPV6_RTHDR,
IPV6_RECVHOPOPTS, IPV6_HOPOPTS
Old semantics are preserved as IPV6_2292xxxx so that
we can maintain backward compatibility.
Signed-off-by: YOSHIFUJI Hideaki <yoshfuji@linux-ipv6.org>
2005-09-08 08:59:17 +08:00
|
|
|
put_cmsg(msg, SOL_IPV6, IPV6_2292DSTOPTS, (ptr[1]+1)<<3, ptr);
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
[IPV6]: Support several new sockopt / ancillary data in Advanced API (RFC3542).
Support several new socket options / ancillary data:
IPV6_RECVPKTINFO, IPV6_PKTINFO,
IPV6_RECVHOPOPTS, IPV6_HOPOPTS,
IPV6_RECVDSTOPTS, IPV6_DSTOPTS, IPV6_RTHDRDSTOPTS,
IPV6_RECVRTHDR, IPV6_RTHDR,
IPV6_RECVHOPOPTS, IPV6_HOPOPTS
Old semantics are preserved as IPV6_2292xxxx so that
we can maintain backward compatibility.
Signed-off-by: YOSHIFUJI Hideaki <yoshfuji@linux-ipv6.org>
2005-09-08 08:59:17 +08:00
|
|
|
if (np->rxopt.bits.osrcrt && opt->srcrt) {
|
2007-04-11 11:50:43 +08:00
|
|
|
struct ipv6_rt_hdr *rthdr = (struct ipv6_rt_hdr *)(nh + opt->srcrt);
|
[IPV6]: Support several new sockopt / ancillary data in Advanced API (RFC3542).
Support several new socket options / ancillary data:
IPV6_RECVPKTINFO, IPV6_PKTINFO,
IPV6_RECVHOPOPTS, IPV6_HOPOPTS,
IPV6_RECVDSTOPTS, IPV6_DSTOPTS, IPV6_RTHDRDSTOPTS,
IPV6_RECVRTHDR, IPV6_RTHDR,
IPV6_RECVHOPOPTS, IPV6_HOPOPTS
Old semantics are preserved as IPV6_2292xxxx so that
we can maintain backward compatibility.
Signed-off-by: YOSHIFUJI Hideaki <yoshfuji@linux-ipv6.org>
2005-09-08 08:59:17 +08:00
|
|
|
put_cmsg(msg, SOL_IPV6, IPV6_2292RTHDR, (rthdr->hdrlen+1) << 3, rthdr);
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
[IPV6]: Support several new sockopt / ancillary data in Advanced API (RFC3542).
Support several new socket options / ancillary data:
IPV6_RECVPKTINFO, IPV6_PKTINFO,
IPV6_RECVHOPOPTS, IPV6_HOPOPTS,
IPV6_RECVDSTOPTS, IPV6_DSTOPTS, IPV6_RTHDRDSTOPTS,
IPV6_RECVRTHDR, IPV6_RTHDR,
IPV6_RECVHOPOPTS, IPV6_HOPOPTS
Old semantics are preserved as IPV6_2292xxxx so that
we can maintain backward compatibility.
Signed-off-by: YOSHIFUJI Hideaki <yoshfuji@linux-ipv6.org>
2005-09-08 08:59:17 +08:00
|
|
|
if (np->rxopt.bits.odstopts && opt->dst1) {
|
2007-04-11 11:50:43 +08:00
|
|
|
u8 *ptr = nh + opt->dst1;
|
[IPV6]: Support several new sockopt / ancillary data in Advanced API (RFC3542).
Support several new socket options / ancillary data:
IPV6_RECVPKTINFO, IPV6_PKTINFO,
IPV6_RECVHOPOPTS, IPV6_HOPOPTS,
IPV6_RECVDSTOPTS, IPV6_DSTOPTS, IPV6_RTHDRDSTOPTS,
IPV6_RECVRTHDR, IPV6_RTHDR,
IPV6_RECVHOPOPTS, IPV6_HOPOPTS
Old semantics are preserved as IPV6_2292xxxx so that
we can maintain backward compatibility.
Signed-off-by: YOSHIFUJI Hideaki <yoshfuji@linux-ipv6.org>
2005-09-08 08:59:17 +08:00
|
|
|
put_cmsg(msg, SOL_IPV6, IPV6_2292DSTOPTS, (ptr[1]+1)<<3, ptr);
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
2010-10-21 22:08:28 +08:00
|
|
|
if (np->rxopt.bits.rxorigdstaddr) {
|
|
|
|
struct sockaddr_in6 sin6;
|
|
|
|
u16 *ports = (u16 *) skb_transport_header(skb);
|
|
|
|
|
|
|
|
if (skb_transport_offset(skb) + 4 <= skb->len) {
|
|
|
|
/* All current transport protocols have the port numbers in the
|
|
|
|
* first four bytes of the transport header and this function is
|
|
|
|
* written with this assumption in mind.
|
|
|
|
*/
|
|
|
|
|
|
|
|
sin6.sin6_family = AF_INET6;
|
|
|
|
ipv6_addr_copy(&sin6.sin6_addr, &ipv6_hdr(skb)->daddr);
|
|
|
|
sin6.sin6_port = ports[1];
|
|
|
|
sin6.sin6_flowinfo = 0;
|
|
|
|
sin6.sin6_scope_id = 0;
|
|
|
|
|
|
|
|
put_cmsg(msg, SOL_IPV6, IPV6_ORIGDSTADDR, sizeof(sin6), &sin6);
|
|
|
|
}
|
|
|
|
}
|
2005-04-17 06:20:36 +08:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2008-06-04 12:02:49 +08:00
|
|
|
int datagram_send_ctl(struct net *net,
|
2011-03-13 05:22:43 +08:00
|
|
|
struct msghdr *msg, struct flowi6 *fl6,
|
2005-04-17 06:20:36 +08:00
|
|
|
struct ipv6_txoptions *opt,
|
2010-04-23 19:26:08 +08:00
|
|
|
int *hlimit, int *tclass, int *dontfrag)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
|
|
|
struct in6_pktinfo *src_info;
|
|
|
|
struct cmsghdr *cmsg;
|
|
|
|
struct ipv6_rt_hdr *rthdr;
|
|
|
|
struct ipv6_opt_hdr *hdr;
|
|
|
|
int len;
|
|
|
|
int err = 0;
|
|
|
|
|
|
|
|
for (cmsg = CMSG_FIRSTHDR(msg); cmsg; cmsg = CMSG_NXTHDR(msg, cmsg)) {
|
|
|
|
int addr_type;
|
|
|
|
|
|
|
|
if (!CMSG_OK(msg, cmsg)) {
|
|
|
|
err = -EINVAL;
|
|
|
|
goto exit_f;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (cmsg->cmsg_level != SOL_IPV6)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
switch (cmsg->cmsg_type) {
|
2007-02-09 22:24:49 +08:00
|
|
|
case IPV6_PKTINFO:
|
|
|
|
case IPV6_2292PKTINFO:
|
2008-06-04 12:01:37 +08:00
|
|
|
{
|
|
|
|
struct net_device *dev = NULL;
|
|
|
|
|
2007-02-09 22:24:49 +08:00
|
|
|
if (cmsg->cmsg_len < CMSG_LEN(sizeof(struct in6_pktinfo))) {
|
2005-04-17 06:20:36 +08:00
|
|
|
err = -EINVAL;
|
|
|
|
goto exit_f;
|
|
|
|
}
|
|
|
|
|
|
|
|
src_info = (struct in6_pktinfo *)CMSG_DATA(cmsg);
|
2007-02-09 22:24:49 +08:00
|
|
|
|
2005-04-17 06:20:36 +08:00
|
|
|
if (src_info->ipi6_ifindex) {
|
2011-03-13 05:22:43 +08:00
|
|
|
if (fl6->flowi6_oif &&
|
|
|
|
src_info->ipi6_ifindex != fl6->flowi6_oif)
|
2005-04-17 06:20:36 +08:00
|
|
|
return -EINVAL;
|
2011-03-13 05:22:43 +08:00
|
|
|
fl6->flowi6_oif = src_info->ipi6_ifindex;
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
|
2008-06-04 12:01:37 +08:00
|
|
|
addr_type = __ipv6_addr_type(&src_info->ipi6_addr);
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2009-11-02 19:21:06 +08:00
|
|
|
rcu_read_lock();
|
2011-03-13 05:22:43 +08:00
|
|
|
if (fl6->flowi6_oif) {
|
|
|
|
dev = dev_get_by_index_rcu(net, fl6->flowi6_oif);
|
2009-11-02 19:21:06 +08:00
|
|
|
if (!dev) {
|
|
|
|
rcu_read_unlock();
|
2008-06-04 12:01:37 +08:00
|
|
|
return -ENODEV;
|
2009-11-02 19:21:06 +08:00
|
|
|
}
|
|
|
|
} else if (addr_type & IPV6_ADDR_LINKLOCAL) {
|
|
|
|
rcu_read_unlock();
|
2008-06-04 12:01:37 +08:00
|
|
|
return -EINVAL;
|
2009-11-02 19:21:06 +08:00
|
|
|
}
|
2007-02-09 22:24:49 +08:00
|
|
|
|
2008-06-04 12:01:37 +08:00
|
|
|
if (addr_type != IPV6_ADDR_ANY) {
|
|
|
|
int strict = __ipv6_addr_src_scope(addr_type) <= IPV6_ADDR_SCOPE_LINKLOCAL;
|
2008-06-04 12:02:49 +08:00
|
|
|
if (!ipv6_chk_addr(net, &src_info->ipi6_addr,
|
2008-06-04 12:01:37 +08:00
|
|
|
strict ? dev : NULL, 0))
|
|
|
|
err = -EINVAL;
|
|
|
|
else
|
2011-03-13 05:22:43 +08:00
|
|
|
ipv6_addr_copy(&fl6->saddr, &src_info->ipi6_addr);
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
2008-06-04 12:01:37 +08:00
|
|
|
|
2009-11-02 19:21:06 +08:00
|
|
|
rcu_read_unlock();
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2008-06-04 12:01:37 +08:00
|
|
|
if (err)
|
|
|
|
goto exit_f;
|
|
|
|
|
2005-04-17 06:20:36 +08:00
|
|
|
break;
|
2008-06-04 12:01:37 +08:00
|
|
|
}
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
case IPV6_FLOWINFO:
|
2007-02-09 22:24:49 +08:00
|
|
|
if (cmsg->cmsg_len < CMSG_LEN(4)) {
|
2005-04-17 06:20:36 +08:00
|
|
|
err = -EINVAL;
|
|
|
|
goto exit_f;
|
|
|
|
}
|
|
|
|
|
2011-03-13 05:22:43 +08:00
|
|
|
if (fl6->flowlabel&IPV6_FLOWINFO_MASK) {
|
|
|
|
if ((fl6->flowlabel^*(__be32 *)CMSG_DATA(cmsg))&~IPV6_FLOWINFO_MASK) {
|
2005-04-17 06:20:36 +08:00
|
|
|
err = -EINVAL;
|
|
|
|
goto exit_f;
|
|
|
|
}
|
|
|
|
}
|
2011-03-13 05:22:43 +08:00
|
|
|
fl6->flowlabel = IPV6_FLOWINFO_MASK & *(__be32 *)CMSG_DATA(cmsg);
|
2005-04-17 06:20:36 +08:00
|
|
|
break;
|
|
|
|
|
[IPV6]: Support several new sockopt / ancillary data in Advanced API (RFC3542).
Support several new socket options / ancillary data:
IPV6_RECVPKTINFO, IPV6_PKTINFO,
IPV6_RECVHOPOPTS, IPV6_HOPOPTS,
IPV6_RECVDSTOPTS, IPV6_DSTOPTS, IPV6_RTHDRDSTOPTS,
IPV6_RECVRTHDR, IPV6_RTHDR,
IPV6_RECVHOPOPTS, IPV6_HOPOPTS
Old semantics are preserved as IPV6_2292xxxx so that
we can maintain backward compatibility.
Signed-off-by: YOSHIFUJI Hideaki <yoshfuji@linux-ipv6.org>
2005-09-08 08:59:17 +08:00
|
|
|
case IPV6_2292HOPOPTS:
|
2005-04-17 06:20:36 +08:00
|
|
|
case IPV6_HOPOPTS:
|
2007-02-09 22:24:49 +08:00
|
|
|
if (opt->hopopt || cmsg->cmsg_len < CMSG_LEN(sizeof(struct ipv6_opt_hdr))) {
|
2005-04-17 06:20:36 +08:00
|
|
|
err = -EINVAL;
|
|
|
|
goto exit_f;
|
|
|
|
}
|
|
|
|
|
|
|
|
hdr = (struct ipv6_opt_hdr *)CMSG_DATA(cmsg);
|
|
|
|
len = ((hdr->hdrlen + 1) << 3);
|
|
|
|
if (cmsg->cmsg_len < CMSG_LEN(len)) {
|
|
|
|
err = -EINVAL;
|
|
|
|
goto exit_f;
|
|
|
|
}
|
|
|
|
if (!capable(CAP_NET_RAW)) {
|
|
|
|
err = -EPERM;
|
|
|
|
goto exit_f;
|
|
|
|
}
|
|
|
|
opt->opt_nflen += len;
|
|
|
|
opt->hopopt = hdr;
|
|
|
|
break;
|
|
|
|
|
[IPV6]: Support several new sockopt / ancillary data in Advanced API (RFC3542).
Support several new socket options / ancillary data:
IPV6_RECVPKTINFO, IPV6_PKTINFO,
IPV6_RECVHOPOPTS, IPV6_HOPOPTS,
IPV6_RECVDSTOPTS, IPV6_DSTOPTS, IPV6_RTHDRDSTOPTS,
IPV6_RECVRTHDR, IPV6_RTHDR,
IPV6_RECVHOPOPTS, IPV6_HOPOPTS
Old semantics are preserved as IPV6_2292xxxx so that
we can maintain backward compatibility.
Signed-off-by: YOSHIFUJI Hideaki <yoshfuji@linux-ipv6.org>
2005-09-08 08:59:17 +08:00
|
|
|
case IPV6_2292DSTOPTS:
|
2007-02-09 22:24:49 +08:00
|
|
|
if (cmsg->cmsg_len < CMSG_LEN(sizeof(struct ipv6_opt_hdr))) {
|
2005-04-17 06:20:36 +08:00
|
|
|
err = -EINVAL;
|
|
|
|
goto exit_f;
|
|
|
|
}
|
|
|
|
|
|
|
|
hdr = (struct ipv6_opt_hdr *)CMSG_DATA(cmsg);
|
|
|
|
len = ((hdr->hdrlen + 1) << 3);
|
|
|
|
if (cmsg->cmsg_len < CMSG_LEN(len)) {
|
|
|
|
err = -EINVAL;
|
|
|
|
goto exit_f;
|
|
|
|
}
|
|
|
|
if (!capable(CAP_NET_RAW)) {
|
|
|
|
err = -EPERM;
|
|
|
|
goto exit_f;
|
|
|
|
}
|
|
|
|
if (opt->dst1opt) {
|
|
|
|
err = -EINVAL;
|
|
|
|
goto exit_f;
|
|
|
|
}
|
|
|
|
opt->opt_flen += len;
|
|
|
|
opt->dst1opt = hdr;
|
|
|
|
break;
|
|
|
|
|
[IPV6]: Support several new sockopt / ancillary data in Advanced API (RFC3542).
Support several new socket options / ancillary data:
IPV6_RECVPKTINFO, IPV6_PKTINFO,
IPV6_RECVHOPOPTS, IPV6_HOPOPTS,
IPV6_RECVDSTOPTS, IPV6_DSTOPTS, IPV6_RTHDRDSTOPTS,
IPV6_RECVRTHDR, IPV6_RTHDR,
IPV6_RECVHOPOPTS, IPV6_HOPOPTS
Old semantics are preserved as IPV6_2292xxxx so that
we can maintain backward compatibility.
Signed-off-by: YOSHIFUJI Hideaki <yoshfuji@linux-ipv6.org>
2005-09-08 08:59:17 +08:00
|
|
|
case IPV6_DSTOPTS:
|
|
|
|
case IPV6_RTHDRDSTOPTS:
|
|
|
|
if (cmsg->cmsg_len < CMSG_LEN(sizeof(struct ipv6_opt_hdr))) {
|
|
|
|
err = -EINVAL;
|
|
|
|
goto exit_f;
|
|
|
|
}
|
|
|
|
|
|
|
|
hdr = (struct ipv6_opt_hdr *)CMSG_DATA(cmsg);
|
|
|
|
len = ((hdr->hdrlen + 1) << 3);
|
|
|
|
if (cmsg->cmsg_len < CMSG_LEN(len)) {
|
|
|
|
err = -EINVAL;
|
|
|
|
goto exit_f;
|
|
|
|
}
|
|
|
|
if (!capable(CAP_NET_RAW)) {
|
|
|
|
err = -EPERM;
|
|
|
|
goto exit_f;
|
|
|
|
}
|
|
|
|
if (cmsg->cmsg_type == IPV6_DSTOPTS) {
|
|
|
|
opt->opt_flen += len;
|
|
|
|
opt->dst1opt = hdr;
|
|
|
|
} else {
|
|
|
|
opt->opt_nflen += len;
|
|
|
|
opt->dst0opt = hdr;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case IPV6_2292RTHDR:
|
2005-04-17 06:20:36 +08:00
|
|
|
case IPV6_RTHDR:
|
2007-02-09 22:24:49 +08:00
|
|
|
if (cmsg->cmsg_len < CMSG_LEN(sizeof(struct ipv6_rt_hdr))) {
|
2005-04-17 06:20:36 +08:00
|
|
|
err = -EINVAL;
|
|
|
|
goto exit_f;
|
|
|
|
}
|
|
|
|
|
|
|
|
rthdr = (struct ipv6_rt_hdr *)CMSG_DATA(cmsg);
|
|
|
|
|
2006-08-24 10:17:12 +08:00
|
|
|
switch (rthdr->type) {
|
2007-06-27 14:56:32 +08:00
|
|
|
#if defined(CONFIG_IPV6_MIP6) || defined(CONFIG_IPV6_MIP6_MODULE)
|
2006-08-24 10:17:12 +08:00
|
|
|
case IPV6_SRCRT_TYPE_2:
|
2008-11-13 14:59:21 +08:00
|
|
|
if (rthdr->hdrlen != 2 ||
|
|
|
|
rthdr->segments_left != 1) {
|
|
|
|
err = -EINVAL;
|
|
|
|
goto exit_f;
|
|
|
|
}
|
2006-08-24 10:17:12 +08:00
|
|
|
break;
|
2007-07-11 13:55:49 +08:00
|
|
|
#endif
|
2006-08-24 10:17:12 +08:00
|
|
|
default:
|
2005-04-17 06:20:36 +08:00
|
|
|
err = -EINVAL;
|
|
|
|
goto exit_f;
|
|
|
|
}
|
|
|
|
|
|
|
|
len = ((rthdr->hdrlen + 1) << 3);
|
|
|
|
|
2007-02-09 22:24:49 +08:00
|
|
|
if (cmsg->cmsg_len < CMSG_LEN(len)) {
|
2005-04-17 06:20:36 +08:00
|
|
|
err = -EINVAL;
|
|
|
|
goto exit_f;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* segments left must also match */
|
|
|
|
if ((rthdr->hdrlen >> 1) != rthdr->segments_left) {
|
|
|
|
err = -EINVAL;
|
|
|
|
goto exit_f;
|
|
|
|
}
|
|
|
|
|
|
|
|
opt->opt_nflen += len;
|
|
|
|
opt->srcrt = rthdr;
|
|
|
|
|
[IPV6]: Support several new sockopt / ancillary data in Advanced API (RFC3542).
Support several new socket options / ancillary data:
IPV6_RECVPKTINFO, IPV6_PKTINFO,
IPV6_RECVHOPOPTS, IPV6_HOPOPTS,
IPV6_RECVDSTOPTS, IPV6_DSTOPTS, IPV6_RTHDRDSTOPTS,
IPV6_RECVRTHDR, IPV6_RTHDR,
IPV6_RECVHOPOPTS, IPV6_HOPOPTS
Old semantics are preserved as IPV6_2292xxxx so that
we can maintain backward compatibility.
Signed-off-by: YOSHIFUJI Hideaki <yoshfuji@linux-ipv6.org>
2005-09-08 08:59:17 +08:00
|
|
|
if (cmsg->cmsg_type == IPV6_2292RTHDR && opt->dst1opt) {
|
2005-04-17 06:20:36 +08:00
|
|
|
int dsthdrlen = ((opt->dst1opt->hdrlen+1)<<3);
|
|
|
|
|
|
|
|
opt->opt_nflen += dsthdrlen;
|
|
|
|
opt->dst0opt = opt->dst1opt;
|
|
|
|
opt->dst1opt = NULL;
|
|
|
|
opt->opt_flen -= dsthdrlen;
|
|
|
|
}
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
[IPV6]: Support several new sockopt / ancillary data in Advanced API (RFC3542).
Support several new socket options / ancillary data:
IPV6_RECVPKTINFO, IPV6_PKTINFO,
IPV6_RECVHOPOPTS, IPV6_HOPOPTS,
IPV6_RECVDSTOPTS, IPV6_DSTOPTS, IPV6_RTHDRDSTOPTS,
IPV6_RECVRTHDR, IPV6_RTHDR,
IPV6_RECVHOPOPTS, IPV6_HOPOPTS
Old semantics are preserved as IPV6_2292xxxx so that
we can maintain backward compatibility.
Signed-off-by: YOSHIFUJI Hideaki <yoshfuji@linux-ipv6.org>
2005-09-08 08:59:17 +08:00
|
|
|
case IPV6_2292HOPLIMIT:
|
2005-04-17 06:20:36 +08:00
|
|
|
case IPV6_HOPLIMIT:
|
|
|
|
if (cmsg->cmsg_len != CMSG_LEN(sizeof(int))) {
|
|
|
|
err = -EINVAL;
|
|
|
|
goto exit_f;
|
|
|
|
}
|
|
|
|
|
|
|
|
*hlimit = *(int *)CMSG_DATA(cmsg);
|
2008-06-10 15:50:55 +08:00
|
|
|
if (*hlimit < -1 || *hlimit > 0xff) {
|
|
|
|
err = -EINVAL;
|
|
|
|
goto exit_f;
|
|
|
|
}
|
|
|
|
|
2005-04-17 06:20:36 +08:00
|
|
|
break;
|
|
|
|
|
2005-09-08 09:19:03 +08:00
|
|
|
case IPV6_TCLASS:
|
|
|
|
{
|
|
|
|
int tc;
|
|
|
|
|
|
|
|
err = -EINVAL;
|
|
|
|
if (cmsg->cmsg_len != CMSG_LEN(sizeof(int))) {
|
|
|
|
goto exit_f;
|
|
|
|
}
|
|
|
|
|
|
|
|
tc = *(int *)CMSG_DATA(cmsg);
|
2006-09-14 11:08:07 +08:00
|
|
|
if (tc < -1 || tc > 0xff)
|
2005-09-08 09:19:03 +08:00
|
|
|
goto exit_f;
|
|
|
|
|
|
|
|
err = 0;
|
|
|
|
*tclass = tc;
|
|
|
|
|
2010-04-23 19:26:08 +08:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case IPV6_DONTFRAG:
|
|
|
|
{
|
|
|
|
int df;
|
|
|
|
|
|
|
|
err = -EINVAL;
|
|
|
|
if (cmsg->cmsg_len != CMSG_LEN(sizeof(int))) {
|
|
|
|
goto exit_f;
|
|
|
|
}
|
|
|
|
|
|
|
|
df = *(int *)CMSG_DATA(cmsg);
|
|
|
|
if (df < 0 || df > 1)
|
|
|
|
goto exit_f;
|
|
|
|
|
|
|
|
err = 0;
|
|
|
|
*dontfrag = df;
|
|
|
|
|
2005-09-08 09:19:03 +08:00
|
|
|
break;
|
|
|
|
}
|
2005-04-17 06:20:36 +08:00
|
|
|
default:
|
2005-08-10 11:50:53 +08:00
|
|
|
LIMIT_NETDEBUG(KERN_DEBUG "invalid cmsg type: %d\n",
|
2007-02-09 22:24:49 +08:00
|
|
|
cmsg->cmsg_type);
|
2005-04-17 06:20:36 +08:00
|
|
|
err = -EINVAL;
|
2008-07-30 14:57:58 +08:00
|
|
|
goto exit_f;
|
2007-04-21 08:09:22 +08:00
|
|
|
}
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
exit_f:
|
|
|
|
return err;
|
|
|
|
}
|