[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>
This commit is contained in:
@@ -210,39 +210,127 @@ int ipv6_setsockopt(struct sock *sk, int level, int optname,
|
||||
retv = 0;
|
||||
break;
|
||||
|
||||
case IPV6_PKTINFO:
|
||||
case IPV6_RECVPKTINFO:
|
||||
np->rxopt.bits.rxinfo = valbool;
|
||||
retv = 0;
|
||||
break;
|
||||
|
||||
case IPV6_2292PKTINFO:
|
||||
np->rxopt.bits.rxoinfo = valbool;
|
||||
retv = 0;
|
||||
break;
|
||||
|
||||
case IPV6_HOPLIMIT:
|
||||
case IPV6_RECVHOPLIMIT:
|
||||
np->rxopt.bits.rxhlim = valbool;
|
||||
retv = 0;
|
||||
break;
|
||||
|
||||
case IPV6_RTHDR:
|
||||
case IPV6_2292HOPLIMIT:
|
||||
np->rxopt.bits.rxohlim = valbool;
|
||||
retv = 0;
|
||||
break;
|
||||
|
||||
case IPV6_RECVRTHDR:
|
||||
if (val < 0 || val > 2)
|
||||
goto e_inval;
|
||||
np->rxopt.bits.srcrt = val;
|
||||
retv = 0;
|
||||
break;
|
||||
|
||||
case IPV6_HOPOPTS:
|
||||
case IPV6_2292RTHDR:
|
||||
if (val < 0 || val > 2)
|
||||
goto e_inval;
|
||||
np->rxopt.bits.osrcrt = val;
|
||||
retv = 0;
|
||||
break;
|
||||
|
||||
case IPV6_RECVHOPOPTS:
|
||||
np->rxopt.bits.hopopts = valbool;
|
||||
retv = 0;
|
||||
break;
|
||||
|
||||
case IPV6_DSTOPTS:
|
||||
case IPV6_2292HOPOPTS:
|
||||
np->rxopt.bits.ohopopts = valbool;
|
||||
retv = 0;
|
||||
break;
|
||||
|
||||
case IPV6_RECVDSTOPTS:
|
||||
np->rxopt.bits.dstopts = valbool;
|
||||
retv = 0;
|
||||
break;
|
||||
|
||||
case IPV6_2292DSTOPTS:
|
||||
np->rxopt.bits.odstopts = valbool;
|
||||
retv = 0;
|
||||
break;
|
||||
|
||||
case IPV6_FLOWINFO:
|
||||
np->rxopt.bits.rxflow = valbool;
|
||||
retv = 0;
|
||||
break;
|
||||
|
||||
case IPV6_PKTOPTIONS:
|
||||
case IPV6_HOPOPTS:
|
||||
case IPV6_RTHDRDSTOPTS:
|
||||
case IPV6_RTHDR:
|
||||
case IPV6_DSTOPTS:
|
||||
{
|
||||
struct ipv6_txoptions *opt;
|
||||
if (optlen == 0)
|
||||
optval = 0;
|
||||
|
||||
/* hop-by-hop / destination options are privileged option */
|
||||
retv = -EPERM;
|
||||
if (optname != IPV6_RTHDR && !capable(CAP_NET_RAW))
|
||||
break;
|
||||
|
||||
retv = -EINVAL;
|
||||
if (optlen & 0x7 || optlen > 8 * 255)
|
||||
break;
|
||||
|
||||
opt = ipv6_renew_options(sk, np->opt, optname,
|
||||
(struct ipv6_opt_hdr __user *)optval,
|
||||
optlen);
|
||||
if (IS_ERR(opt)) {
|
||||
retv = PTR_ERR(opt);
|
||||
break;
|
||||
}
|
||||
|
||||
/* routing header option needs extra check */
|
||||
if (optname == IPV6_RTHDR && opt->srcrt) {
|
||||
struct ipv6_rt_hdr *rthdr = opt->srcrt;
|
||||
if (rthdr->type)
|
||||
goto sticky_done;
|
||||
if ((rthdr->hdrlen & 1) ||
|
||||
(rthdr->hdrlen >> 1) != rthdr->segments_left)
|
||||
goto sticky_done;
|
||||
}
|
||||
|
||||
retv = 0;
|
||||
if (sk->sk_type == SOCK_STREAM) {
|
||||
if (opt) {
|
||||
struct tcp_sock *tp = tcp_sk(sk);
|
||||
if (!((1 << sk->sk_state) &
|
||||
(TCPF_LISTEN | TCPF_CLOSE))
|
||||
&& inet_sk(sk)->daddr != LOOPBACK4_IPV6) {
|
||||
tp->ext_header_len = opt->opt_flen + opt->opt_nflen;
|
||||
tcp_sync_mss(sk, tp->pmtu_cookie);
|
||||
}
|
||||
}
|
||||
opt = xchg(&np->opt, opt);
|
||||
sk_dst_reset(sk);
|
||||
} else {
|
||||
write_lock(&sk->sk_dst_lock);
|
||||
opt = xchg(&np->opt, opt);
|
||||
write_unlock(&sk->sk_dst_lock);
|
||||
sk_dst_reset(sk);
|
||||
}
|
||||
sticky_done:
|
||||
if (opt)
|
||||
sock_kfree_s(sk, opt, opt->tot_len);
|
||||
break;
|
||||
}
|
||||
|
||||
case IPV6_2292PKTOPTIONS:
|
||||
{
|
||||
struct ipv6_txoptions *opt = NULL;
|
||||
struct msghdr msg;
|
||||
@@ -529,6 +617,17 @@ e_inval:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
int ipv6_getsockopt_sticky(struct sock *sk, struct ipv6_opt_hdr *hdr,
|
||||
char __user *optval, int len)
|
||||
{
|
||||
if (!hdr)
|
||||
return 0;
|
||||
len = min_t(int, len, ipv6_optlen(hdr));
|
||||
if (copy_to_user(optval, hdr, ipv6_optlen(hdr)))
|
||||
return -EFAULT;
|
||||
return len;
|
||||
}
|
||||
|
||||
int ipv6_getsockopt(struct sock *sk, int level, int optname,
|
||||
char __user *optval, int __user *optlen)
|
||||
{
|
||||
@@ -567,7 +666,7 @@ int ipv6_getsockopt(struct sock *sk, int level, int optname,
|
||||
return err;
|
||||
}
|
||||
|
||||
case IPV6_PKTOPTIONS:
|
||||
case IPV6_2292PKTOPTIONS:
|
||||
{
|
||||
struct msghdr msg;
|
||||
struct sk_buff *skb;
|
||||
@@ -601,6 +700,16 @@ int ipv6_getsockopt(struct sock *sk, int level, int optname,
|
||||
int hlim = np->mcast_hops;
|
||||
put_cmsg(&msg, SOL_IPV6, IPV6_HOPLIMIT, sizeof(hlim), &hlim);
|
||||
}
|
||||
if (np->rxopt.bits.rxoinfo) {
|
||||
struct in6_pktinfo src_info;
|
||||
src_info.ipi6_ifindex = np->mcast_oif;
|
||||
ipv6_addr_copy(&src_info.ipi6_addr, &np->daddr);
|
||||
put_cmsg(&msg, SOL_IPV6, IPV6_2292PKTINFO, sizeof(src_info), &src_info);
|
||||
}
|
||||
if (np->rxopt.bits.rxohlim) {
|
||||
int hlim = np->mcast_hops;
|
||||
put_cmsg(&msg, SOL_IPV6, IPV6_2292HOPLIMIT, sizeof(hlim), &hlim);
|
||||
}
|
||||
}
|
||||
len -= msg.msg_controllen;
|
||||
return put_user(len, optlen);
|
||||
@@ -625,26 +734,59 @@ int ipv6_getsockopt(struct sock *sk, int level, int optname,
|
||||
val = np->ipv6only;
|
||||
break;
|
||||
|
||||
case IPV6_PKTINFO:
|
||||
case IPV6_RECVPKTINFO:
|
||||
val = np->rxopt.bits.rxinfo;
|
||||
break;
|
||||
|
||||
case IPV6_HOPLIMIT:
|
||||
case IPV6_2292PKTINFO:
|
||||
val = np->rxopt.bits.rxoinfo;
|
||||
break;
|
||||
|
||||
case IPV6_RECVHOPLIMIT:
|
||||
val = np->rxopt.bits.rxhlim;
|
||||
break;
|
||||
|
||||
case IPV6_RTHDR:
|
||||
case IPV6_2292HOPLIMIT:
|
||||
val = np->rxopt.bits.rxohlim;
|
||||
break;
|
||||
|
||||
case IPV6_RECVRTHDR:
|
||||
val = np->rxopt.bits.srcrt;
|
||||
break;
|
||||
|
||||
case IPV6_2292RTHDR:
|
||||
val = np->rxopt.bits.osrcrt;
|
||||
break;
|
||||
|
||||
case IPV6_HOPOPTS:
|
||||
case IPV6_RTHDRDSTOPTS:
|
||||
case IPV6_RTHDR:
|
||||
case IPV6_DSTOPTS:
|
||||
{
|
||||
|
||||
lock_sock(sk);
|
||||
len = ipv6_getsockopt_sticky(sk, np->opt->hopopt,
|
||||
optval, len);
|
||||
release_sock(sk);
|
||||
return put_user(len, optlen);
|
||||
}
|
||||
|
||||
case IPV6_RECVHOPOPTS:
|
||||
val = np->rxopt.bits.hopopts;
|
||||
break;
|
||||
|
||||
case IPV6_DSTOPTS:
|
||||
case IPV6_2292HOPOPTS:
|
||||
val = np->rxopt.bits.ohopopts;
|
||||
break;
|
||||
|
||||
case IPV6_RECVDSTOPTS:
|
||||
val = np->rxopt.bits.dstopts;
|
||||
break;
|
||||
|
||||
case IPV6_2292DSTOPTS:
|
||||
val = np->rxopt.bits.odstopts;
|
||||
break;
|
||||
|
||||
case IPV6_FLOWINFO:
|
||||
val = np->rxopt.bits.rxflow;
|
||||
break;
|
||||
|
Reference in New Issue
Block a user