/*
* IPv6 BSD socket options interface
- * Linux INET6 implementation
+ * Linux INET6 implementation
*
* Authors:
- * Pedro Roque <roque@di.fc.ul.pt>
+ * Pedro Roque <roque@di.fc.ul.pt>
*
* Based on linux/net/ipv4/ip_sockglue.c
*
#include <linux/types.h>
#include <linux/socket.h>
#include <linux/sockios.h>
-#include <linux/sched.h>
#include <linux/net.h>
#include <linux/in6.h>
#include <linux/netdevice.h>
#include <net/inet_common.h>
#include <net/tcp.h>
#include <net/udp.h>
+#include <net/udplite.h>
#include <net/xfrm.h>
#include <asm/uaccess.h>
struct ipv6hdr *ipv6h;
struct inet6_protocol *ops;
+ if (!(features & NETIF_F_HW_CSUM))
+ features &= ~NETIF_F_SG;
+
if (unlikely(skb_shinfo(skb)->gso_type &
~(SKB_GSO_UDP |
SKB_GSO_DODGY |
}
static struct packet_type ipv6_packet_type = {
- .type = __constant_htons(ETH_P_IPV6),
+ .type = __constant_htons(ETH_P_IPV6),
.func = ipv6_rcv,
.gso_send_check = ipv6_gso_send_check,
.gso_segment = ipv6_gso_segment,
struct sk_buff *pktopt;
if (sk->sk_protocol != IPPROTO_UDP &&
+ sk->sk_protocol != IPPROTO_UDPLITE &&
sk->sk_protocol != IPPROTO_TCP)
break;
sk->sk_family = PF_INET;
tcp_sync_mss(sk, icsk->icsk_pmtu_cookie);
} else {
+ struct proto *prot = &udp_prot;
+
+ if (sk->sk_protocol == IPPROTO_UDPLITE)
+ prot = &udplite_prot;
local_bh_disable();
sock_prot_dec_use(sk->sk_prot);
- sock_prot_inc_use(&udp_prot);
+ sock_prot_inc_use(prot);
local_bh_enable();
- sk->sk_prot = &udp_prot;
+ sk->sk_prot = prot;
sk->sk_socket->ops = &inet_dgram_ops;
sk->sk_family = PF_INET;
}
np->rxopt.bits.rxinfo = valbool;
retv = 0;
break;
-
+
case IPV6_2292PKTINFO:
np->rxopt.bits.rxoinfo = valbool;
retv = 0;
break;
case IPV6_TCLASS:
- if (val < 0 || val > 0xff)
+ if (val < -1 || val > 0xff)
goto e_inval;
np->tclass = val;
retv = 0;
break;
-
+
case IPV6_RECVTCLASS:
np->rxopt.bits.rxtclass = valbool;
retv = 0;
}
/* routing header option needs extra check */
- if (optname == IPV6_RTHDR && opt->srcrt) {
+ if (optname == IPV6_RTHDR && opt && opt->srcrt) {
struct ipv6_rt_hdr *rthdr = opt->srcrt;
- if (rthdr->type)
+ switch (rthdr->type) {
+ case IPV6_SRCRT_TYPE_0:
+#ifdef CONFIG_IPV6_MIP6
+ case IPV6_SRCRT_TYPE_2:
+#endif
+ break;
+ default:
goto sticky_done;
+ }
+
if ((rthdr->hdrlen & 1) ||
(rthdr->hdrlen >> 1) != rthdr->segments_left)
goto sticky_done;
EXPORT_SYMBOL(compat_ipv6_setsockopt);
#endif
-static int ipv6_getsockopt_sticky(struct sock *sk, struct ipv6_opt_hdr *hdr,
+static int ipv6_getsockopt_sticky(struct sock *sk, struct ipv6_txoptions *opt,
char __user *optval, int len)
{
- if (!hdr)
+ struct ipv6_opt_hdr *hdr;
+
+ if (!opt || !opt->hopopt)
return 0;
- len = min_t(int, len, ipv6_optlen(hdr));
+ hdr = opt->hopopt;
+
+ len = min_t(unsigned int, len, ipv6_optlen(hdr));
if (copy_to_user(optval, hdr, ipv6_optlen(hdr)))
return -EFAULT;
return len;
switch (optname) {
case IPV6_ADDRFORM:
if (sk->sk_protocol != IPPROTO_UDP &&
+ sk->sk_protocol != IPPROTO_UDPLITE &&
sk->sk_protocol != IPPROTO_TCP)
return -EINVAL;
if (sk->sk_state != TCP_ESTABLISHED)
case IPV6_MTU:
{
struct dst_entry *dst;
- val = 0;
+ val = 0;
lock_sock(sk);
dst = sk_dst_get(sk);
if (dst) {
{
lock_sock(sk);
- len = ipv6_getsockopt_sticky(sk, np->opt->hopopt,
+ len = ipv6_getsockopt_sticky(sk, np->opt,
optval, len);
release_sock(sk);
return put_user(len, optlen);
case IPV6_TCLASS:
val = np->tclass;
+ if (val < 0)
+ val = 0;
break;
case IPV6_RECVTCLASS:
break;
case IPV6_UNICAST_HOPS:
- val = np->hop_limit;
- break;
-
case IPV6_MULTICAST_HOPS:
- val = np->mcast_hops;
+ {
+ struct dst_entry *dst;
+
+ if (optname == IPV6_UNICAST_HOPS)
+ val = np->hop_limit;
+ else
+ val = np->mcast_hops;
+
+ dst = sk_dst_get(sk);
+ if (dst) {
+ if (val < 0)
+ val = dst_metric(dst, RTAX_HOPLIMIT);
+ if (val < 0)
+ val = ipv6_get_hoplimit(dst->dev);
+ dst_release(dst);
+ }
+ if (val < 0)
+ val = ipv6_devconf.hop_limit;
break;
+ }
case IPV6_MULTICAST_LOOP:
val = np->mc_loop;