[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:
YOSHIFUJI Hideaki
2005-09-08 09:59:17 +09:00
parent 4706df3d3c
commit 333fad5364
10 changed files with 423 additions and 49 deletions

View File

@@ -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;