[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:
+102
-8
@@ -394,21 +394,85 @@ int datagram_recv_ctl(struct sock *sk, struct msghdr *msg, struct sk_buff *skb)
|
||||
u32 flowinfo = *(u32*)skb->nh.raw & IPV6_FLOWINFO_MASK;
|
||||
put_cmsg(msg, SOL_IPV6, IPV6_FLOWINFO, sizeof(flowinfo), &flowinfo);
|
||||
}
|
||||
|
||||
/* HbH is allowed only once */
|
||||
if (np->rxopt.bits.hopopts && opt->hop) {
|
||||
u8 *ptr = skb->nh.raw + opt->hop;
|
||||
put_cmsg(msg, SOL_IPV6, IPV6_HOPOPTS, (ptr[1]+1)<<3, ptr);
|
||||
}
|
||||
if (np->rxopt.bits.dstopts && opt->dst0) {
|
||||
|
||||
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.
|
||||
*
|
||||
* Also note that IPV6_RECVRTHDRDSTOPTS is NOT
|
||||
* (and WILL NOT be) defined because
|
||||
* IPV6_RECVDSTOPTS is more generic. --yoshfuji
|
||||
*/
|
||||
unsigned int off = sizeof(struct ipv6hdr);
|
||||
u8 nexthdr = skb->nh.ipv6h->nexthdr;
|
||||
|
||||
while (off <= opt->lastopt) {
|
||||
unsigned len;
|
||||
u8 *ptr = skb->nh.raw + off;
|
||||
|
||||
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];
|
||||
len = (ptr[1] + 1) << 2;
|
||||
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;
|
||||
ipv6_addr_copy(&src_info.ipi6_addr, &skb->nh.ipv6h->daddr);
|
||||
put_cmsg(msg, SOL_IPV6, IPV6_2292PKTINFO, sizeof(src_info), &src_info);
|
||||
}
|
||||
if (np->rxopt.bits.rxohlim) {
|
||||
int hlim = skb->nh.ipv6h->hop_limit;
|
||||
put_cmsg(msg, SOL_IPV6, IPV6_2292HOPLIMIT, sizeof(hlim), &hlim);
|
||||
}
|
||||
if (np->rxopt.bits.ohopopts && opt->hop) {
|
||||
u8 *ptr = skb->nh.raw + opt->hop;
|
||||
put_cmsg(msg, SOL_IPV6, IPV6_2292HOPOPTS, (ptr[1]+1)<<3, ptr);
|
||||
}
|
||||
if (np->rxopt.bits.odstopts && opt->dst0) {
|
||||
u8 *ptr = skb->nh.raw + opt->dst0;
|
||||
put_cmsg(msg, SOL_IPV6, IPV6_DSTOPTS, (ptr[1]+1)<<3, ptr);
|
||||
put_cmsg(msg, SOL_IPV6, IPV6_2292DSTOPTS, (ptr[1]+1)<<3, ptr);
|
||||
}
|
||||
if (np->rxopt.bits.srcrt && opt->srcrt) {
|
||||
if (np->rxopt.bits.osrcrt && opt->srcrt) {
|
||||
struct ipv6_rt_hdr *rthdr = (struct ipv6_rt_hdr *)(skb->nh.raw + opt->srcrt);
|
||||
put_cmsg(msg, SOL_IPV6, IPV6_RTHDR, (rthdr->hdrlen+1) << 3, rthdr);
|
||||
put_cmsg(msg, SOL_IPV6, IPV6_2292RTHDR, (rthdr->hdrlen+1) << 3, rthdr);
|
||||
}
|
||||
if (np->rxopt.bits.dstopts && opt->dst1) {
|
||||
if (np->rxopt.bits.odstopts && opt->dst1) {
|
||||
u8 *ptr = skb->nh.raw + opt->dst1;
|
||||
put_cmsg(msg, SOL_IPV6, IPV6_DSTOPTS, (ptr[1]+1)<<3, ptr);
|
||||
put_cmsg(msg, SOL_IPV6, IPV6_2292DSTOPTS, (ptr[1]+1)<<3, ptr);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
@@ -438,6 +502,7 @@ int datagram_send_ctl(struct msghdr *msg, struct flowi *fl,
|
||||
|
||||
switch (cmsg->cmsg_type) {
|
||||
case IPV6_PKTINFO:
|
||||
case IPV6_2292PKTINFO:
|
||||
if (cmsg->cmsg_len < CMSG_LEN(sizeof(struct in6_pktinfo))) {
|
||||
err = -EINVAL;
|
||||
goto exit_f;
|
||||
@@ -492,6 +557,7 @@ int datagram_send_ctl(struct msghdr *msg, struct flowi *fl,
|
||||
fl->fl6_flowlabel = IPV6_FLOWINFO_MASK & *(u32 *)CMSG_DATA(cmsg);
|
||||
break;
|
||||
|
||||
case IPV6_2292HOPOPTS:
|
||||
case IPV6_HOPOPTS:
|
||||
if (opt->hopopt || cmsg->cmsg_len < CMSG_LEN(sizeof(struct ipv6_opt_hdr))) {
|
||||
err = -EINVAL;
|
||||
@@ -512,7 +578,7 @@ int datagram_send_ctl(struct msghdr *msg, struct flowi *fl,
|
||||
opt->hopopt = hdr;
|
||||
break;
|
||||
|
||||
case IPV6_DSTOPTS:
|
||||
case IPV6_2292DSTOPTS:
|
||||
if (cmsg->cmsg_len < CMSG_LEN(sizeof(struct ipv6_opt_hdr))) {
|
||||
err = -EINVAL;
|
||||
goto exit_f;
|
||||
@@ -536,6 +602,33 @@ int datagram_send_ctl(struct msghdr *msg, struct flowi *fl,
|
||||
opt->dst1opt = hdr;
|
||||
break;
|
||||
|
||||
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:
|
||||
case IPV6_RTHDR:
|
||||
if (cmsg->cmsg_len < CMSG_LEN(sizeof(struct ipv6_rt_hdr))) {
|
||||
err = -EINVAL;
|
||||
@@ -568,7 +661,7 @@ int datagram_send_ctl(struct msghdr *msg, struct flowi *fl,
|
||||
opt->opt_nflen += len;
|
||||
opt->srcrt = rthdr;
|
||||
|
||||
if (opt->dst1opt) {
|
||||
if (cmsg->cmsg_type == IPV6_2292RTHDR && opt->dst1opt) {
|
||||
int dsthdrlen = ((opt->dst1opt->hdrlen+1)<<3);
|
||||
|
||||
opt->opt_nflen += dsthdrlen;
|
||||
@@ -579,6 +672,7 @@ int datagram_send_ctl(struct msghdr *msg, struct flowi *fl,
|
||||
|
||||
break;
|
||||
|
||||
case IPV6_2292HOPLIMIT:
|
||||
case IPV6_HOPLIMIT:
|
||||
if (cmsg->cmsg_len != CMSG_LEN(sizeof(int))) {
|
||||
err = -EINVAL;
|
||||
|
||||
Reference in New Issue
Block a user