Skip to content

Commit 333fad5

Browse files
committed
[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>
1 parent 4706df3 commit 333fad5

File tree

10 files changed

+423
-49
lines changed

10 files changed

+423
-49
lines changed

include/linux/in6.h

Lines changed: 32 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -148,13 +148,13 @@ struct in6_flowlabel_req
148148
*/
149149

150150
#define IPV6_ADDRFORM 1
151-
#define IPV6_PKTINFO 2
152-
#define IPV6_HOPOPTS 3
153-
#define IPV6_DSTOPTS 4
154-
#define IPV6_RTHDR 5
155-
#define IPV6_PKTOPTIONS 6
151+
#define IPV6_2292PKTINFO 2
152+
#define IPV6_2292HOPOPTS 3
153+
#define IPV6_2292DSTOPTS 4
154+
#define IPV6_2292RTHDR 5
155+
#define IPV6_2292PKTOPTIONS 6
156156
#define IPV6_CHECKSUM 7
157-
#define IPV6_HOPLIMIT 8
157+
#define IPV6_2292HOPLIMIT 8
158158
#define IPV6_NEXTHOP 9
159159
#define IPV6_AUTHHDR 10 /* obsolete */
160160
#define IPV6_FLOWINFO 11
@@ -198,4 +198,30 @@ struct in6_flowlabel_req
198198
* MCAST_MSFILTER 48
199199
*/
200200

201+
/* RFC3542 advanced socket options (50-67) */
202+
#define IPV6_RECVPKTINFO 50
203+
#define IPV6_PKTINFO 51
204+
#if 0
205+
#define IPV6_RECVPATHMTU 52
206+
#define IPV6_PATHMTU 53
207+
#define IPV6_DONTFRAG 54
208+
#define IPV6_USE_MIN_MTU 55
209+
#endif
210+
#define IPV6_RECVHOPOPTS 56
211+
#define IPV6_HOPOPTS 57
212+
#if 0
213+
#define IPV6_RECVRTHDRDSTOPTS 58 /* Unused, see net/ipv6/datagram.c */
214+
#endif
215+
#define IPV6_RTHDRDSTOPTS 59
216+
#define IPV6_RECVRTHDR 60
217+
#define IPV6_RTHDR 61
218+
#define IPV6_RECVDSTOPTS 62
219+
#define IPV6_DSTOPTS 63
220+
#define IPV6_RECVHOPLIMIT 64
221+
#define IPV6_HOPLIMIT 65
222+
#if 0
223+
#define IPV6_RECVTCLASS 66
224+
#define IPV6_TCLASS 67
225+
#endif
226+
201227
#endif

include/linux/ipv6.h

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -189,6 +189,7 @@ struct inet6_skb_parm {
189189
__u16 dst0;
190190
__u16 srcrt;
191191
__u16 dst1;
192+
__u16 lastopt;
192193
};
193194

194195
#define IP6CB(skb) ((struct inet6_skb_parm*)((skb)->cb))
@@ -234,14 +235,19 @@ struct ipv6_pinfo {
234235
/* pktoption flags */
235236
union {
236237
struct {
237-
__u8 srcrt:2,
238+
__u16 srcrt:2,
239+
osrcrt:2,
238240
rxinfo:1,
241+
rxoinfo:1,
239242
rxhlim:1,
243+
rxohlim:1,
240244
hopopts:1,
245+
ohopopts:1,
241246
dstopts:1,
247+
odstopts:1,
242248
rxflow:1;
243249
} bits;
244-
__u8 all;
250+
__u16 all;
245251
} rxopt;
246252

247253
/* sockopt flags */

include/net/ipv6.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -233,6 +233,10 @@ extern int ip6_ra_control(struct sock *sk, int sel,
233233
extern int ipv6_parse_hopopts(struct sk_buff *skb, int);
234234

235235
extern struct ipv6_txoptions * ipv6_dup_options(struct sock *sk, struct ipv6_txoptions *opt);
236+
extern struct ipv6_txoptions * ipv6_renew_options(struct sock *sk, struct ipv6_txoptions *opt,
237+
int newtype,
238+
struct ipv6_opt_hdr __user *newopt,
239+
int newoptlen);
236240

237241
extern int ip6_frag_nqueues;
238242
extern atomic_t ip6_frag_mem;

net/ipv6/datagram.c

Lines changed: 102 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -394,21 +394,85 @@ int datagram_recv_ctl(struct sock *sk, struct msghdr *msg, struct sk_buff *skb)
394394
u32 flowinfo = *(u32*)skb->nh.raw & IPV6_FLOWINFO_MASK;
395395
put_cmsg(msg, SOL_IPV6, IPV6_FLOWINFO, sizeof(flowinfo), &flowinfo);
396396
}
397+
398+
/* HbH is allowed only once */
397399
if (np->rxopt.bits.hopopts && opt->hop) {
398400
u8 *ptr = skb->nh.raw + opt->hop;
399401
put_cmsg(msg, SOL_IPV6, IPV6_HOPOPTS, (ptr[1]+1)<<3, ptr);
400402
}
401-
if (np->rxopt.bits.dstopts && opt->dst0) {
403+
404+
if (opt->lastopt &&
405+
(np->rxopt.bits.dstopts || np->rxopt.bits.srcrt)) {
406+
/*
407+
* Silly enough, but we need to reparse in order to
408+
* report extension headers (except for HbH)
409+
* in order.
410+
*
411+
* Also note that IPV6_RECVRTHDRDSTOPTS is NOT
412+
* (and WILL NOT be) defined because
413+
* IPV6_RECVDSTOPTS is more generic. --yoshfuji
414+
*/
415+
unsigned int off = sizeof(struct ipv6hdr);
416+
u8 nexthdr = skb->nh.ipv6h->nexthdr;
417+
418+
while (off <= opt->lastopt) {
419+
unsigned len;
420+
u8 *ptr = skb->nh.raw + off;
421+
422+
switch(nexthdr) {
423+
case IPPROTO_DSTOPTS:
424+
nexthdr = ptr[0];
425+
len = (ptr[1] + 1) << 3;
426+
if (np->rxopt.bits.dstopts)
427+
put_cmsg(msg, SOL_IPV6, IPV6_DSTOPTS, len, ptr);
428+
break;
429+
case IPPROTO_ROUTING:
430+
nexthdr = ptr[0];
431+
len = (ptr[1] + 1) << 3;
432+
if (np->rxopt.bits.srcrt)
433+
put_cmsg(msg, SOL_IPV6, IPV6_RTHDR, len, ptr);
434+
break;
435+
case IPPROTO_AH:
436+
nexthdr = ptr[0];
437+
len = (ptr[1] + 1) << 2;
438+
break;
439+
default:
440+
nexthdr = ptr[0];
441+
len = (ptr[1] + 1) << 3;
442+
break;
443+
}
444+
445+
off += len;
446+
}
447+
}
448+
449+
/* socket options in old style */
450+
if (np->rxopt.bits.rxoinfo) {
451+
struct in6_pktinfo src_info;
452+
453+
src_info.ipi6_ifindex = opt->iif;
454+
ipv6_addr_copy(&src_info.ipi6_addr, &skb->nh.ipv6h->daddr);
455+
put_cmsg(msg, SOL_IPV6, IPV6_2292PKTINFO, sizeof(src_info), &src_info);
456+
}
457+
if (np->rxopt.bits.rxohlim) {
458+
int hlim = skb->nh.ipv6h->hop_limit;
459+
put_cmsg(msg, SOL_IPV6, IPV6_2292HOPLIMIT, sizeof(hlim), &hlim);
460+
}
461+
if (np->rxopt.bits.ohopopts && opt->hop) {
462+
u8 *ptr = skb->nh.raw + opt->hop;
463+
put_cmsg(msg, SOL_IPV6, IPV6_2292HOPOPTS, (ptr[1]+1)<<3, ptr);
464+
}
465+
if (np->rxopt.bits.odstopts && opt->dst0) {
402466
u8 *ptr = skb->nh.raw + opt->dst0;
403-
put_cmsg(msg, SOL_IPV6, IPV6_DSTOPTS, (ptr[1]+1)<<3, ptr);
467+
put_cmsg(msg, SOL_IPV6, IPV6_2292DSTOPTS, (ptr[1]+1)<<3, ptr);
404468
}
405-
if (np->rxopt.bits.srcrt && opt->srcrt) {
469+
if (np->rxopt.bits.osrcrt && opt->srcrt) {
406470
struct ipv6_rt_hdr *rthdr = (struct ipv6_rt_hdr *)(skb->nh.raw + opt->srcrt);
407-
put_cmsg(msg, SOL_IPV6, IPV6_RTHDR, (rthdr->hdrlen+1) << 3, rthdr);
471+
put_cmsg(msg, SOL_IPV6, IPV6_2292RTHDR, (rthdr->hdrlen+1) << 3, rthdr);
408472
}
409-
if (np->rxopt.bits.dstopts && opt->dst1) {
473+
if (np->rxopt.bits.odstopts && opt->dst1) {
410474
u8 *ptr = skb->nh.raw + opt->dst1;
411-
put_cmsg(msg, SOL_IPV6, IPV6_DSTOPTS, (ptr[1]+1)<<3, ptr);
475+
put_cmsg(msg, SOL_IPV6, IPV6_2292DSTOPTS, (ptr[1]+1)<<3, ptr);
412476
}
413477
return 0;
414478
}
@@ -438,6 +502,7 @@ int datagram_send_ctl(struct msghdr *msg, struct flowi *fl,
438502

439503
switch (cmsg->cmsg_type) {
440504
case IPV6_PKTINFO:
505+
case IPV6_2292PKTINFO:
441506
if (cmsg->cmsg_len < CMSG_LEN(sizeof(struct in6_pktinfo))) {
442507
err = -EINVAL;
443508
goto exit_f;
@@ -492,6 +557,7 @@ int datagram_send_ctl(struct msghdr *msg, struct flowi *fl,
492557
fl->fl6_flowlabel = IPV6_FLOWINFO_MASK & *(u32 *)CMSG_DATA(cmsg);
493558
break;
494559

560+
case IPV6_2292HOPOPTS:
495561
case IPV6_HOPOPTS:
496562
if (opt->hopopt || cmsg->cmsg_len < CMSG_LEN(sizeof(struct ipv6_opt_hdr))) {
497563
err = -EINVAL;
@@ -512,7 +578,7 @@ int datagram_send_ctl(struct msghdr *msg, struct flowi *fl,
512578
opt->hopopt = hdr;
513579
break;
514580

515-
case IPV6_DSTOPTS:
581+
case IPV6_2292DSTOPTS:
516582
if (cmsg->cmsg_len < CMSG_LEN(sizeof(struct ipv6_opt_hdr))) {
517583
err = -EINVAL;
518584
goto exit_f;
@@ -536,6 +602,33 @@ int datagram_send_ctl(struct msghdr *msg, struct flowi *fl,
536602
opt->dst1opt = hdr;
537603
break;
538604

605+
case IPV6_DSTOPTS:
606+
case IPV6_RTHDRDSTOPTS:
607+
if (cmsg->cmsg_len < CMSG_LEN(sizeof(struct ipv6_opt_hdr))) {
608+
err = -EINVAL;
609+
goto exit_f;
610+
}
611+
612+
hdr = (struct ipv6_opt_hdr *)CMSG_DATA(cmsg);
613+
len = ((hdr->hdrlen + 1) << 3);
614+
if (cmsg->cmsg_len < CMSG_LEN(len)) {
615+
err = -EINVAL;
616+
goto exit_f;
617+
}
618+
if (!capable(CAP_NET_RAW)) {
619+
err = -EPERM;
620+
goto exit_f;
621+
}
622+
if (cmsg->cmsg_type == IPV6_DSTOPTS) {
623+
opt->opt_flen += len;
624+
opt->dst1opt = hdr;
625+
} else {
626+
opt->opt_nflen += len;
627+
opt->dst0opt = hdr;
628+
}
629+
break;
630+
631+
case IPV6_2292RTHDR:
539632
case IPV6_RTHDR:
540633
if (cmsg->cmsg_len < CMSG_LEN(sizeof(struct ipv6_rt_hdr))) {
541634
err = -EINVAL;
@@ -568,7 +661,7 @@ int datagram_send_ctl(struct msghdr *msg, struct flowi *fl,
568661
opt->opt_nflen += len;
569662
opt->srcrt = rthdr;
570663

571-
if (opt->dst1opt) {
664+
if (cmsg->cmsg_type == IPV6_2292RTHDR && opt->dst1opt) {
572665
int dsthdrlen = ((opt->dst1opt->hdrlen+1)<<3);
573666

574667
opt->opt_nflen += dsthdrlen;
@@ -579,6 +672,7 @@ int datagram_send_ctl(struct msghdr *msg, struct flowi *fl,
579672

580673
break;
581674

675+
case IPV6_2292HOPLIMIT:
582676
case IPV6_HOPLIMIT:
583677
if (cmsg->cmsg_len != CMSG_LEN(sizeof(int))) {
584678
err = -EINVAL;

net/ipv6/exthdrs.c

Lines changed: 104 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -164,6 +164,7 @@ static int ipv6_destopt_rcv(struct sk_buff **skbp, unsigned int *nhoffp)
164164
return -1;
165165
}
166166

167+
opt->lastopt = skb->h.raw - skb->nh.raw;
167168
opt->dst1 = skb->h.raw - skb->nh.raw;
168169

169170
if (ip6_parse_tlv(tlvprocdestopt_lst, skb)) {
@@ -243,6 +244,7 @@ static int ipv6_rthdr_rcv(struct sk_buff **skbp, unsigned int *nhoffp)
243244

244245
looped_back:
245246
if (hdr->segments_left == 0) {
247+
opt->lastopt = skb->h.raw - skb->nh.raw;
246248
opt->srcrt = skb->h.raw - skb->nh.raw;
247249
skb->h.raw += (hdr->hdrlen + 1) << 3;
248250
opt->dst0 = opt->dst1;
@@ -539,10 +541,15 @@ void ipv6_push_nfrag_opts(struct sk_buff *skb, struct ipv6_txoptions *opt,
539541
u8 *proto,
540542
struct in6_addr **daddr)
541543
{
542-
if (opt->srcrt)
544+
if (opt->srcrt) {
543545
ipv6_push_rthdr(skb, proto, opt->srcrt, daddr);
544-
if (opt->dst0opt)
545-
ipv6_push_exthdr(skb, proto, NEXTHDR_DEST, opt->dst0opt);
546+
/*
547+
* IPV6_RTHDRDSTOPTS is ignored
548+
* unless IPV6_RTHDR is set (RFC3542).
549+
*/
550+
if (opt->dst0opt)
551+
ipv6_push_exthdr(skb, proto, NEXTHDR_DEST, opt->dst0opt);
552+
}
546553
if (opt->hopopt)
547554
ipv6_push_exthdr(skb, proto, NEXTHDR_HOP, opt->hopopt);
548555
}
@@ -573,3 +580,97 @@ ipv6_dup_options(struct sock *sk, struct ipv6_txoptions *opt)
573580
}
574581
return opt2;
575582
}
583+
584+
static int ipv6_renew_option(void *ohdr,
585+
struct ipv6_opt_hdr __user *newopt, int newoptlen,
586+
int inherit,
587+
struct ipv6_opt_hdr **hdr,
588+
char **p)
589+
{
590+
if (inherit) {
591+
if (ohdr) {
592+
memcpy(*p, ohdr, ipv6_optlen((struct ipv6_opt_hdr *)ohdr));
593+
*hdr = (struct ipv6_opt_hdr *)*p;
594+
*p += CMSG_ALIGN(ipv6_optlen(*(struct ipv6_opt_hdr **)hdr));
595+
}
596+
} else {
597+
if (newopt) {
598+
if (copy_from_user(*p, newopt, newoptlen))
599+
return -EFAULT;
600+
*hdr = (struct ipv6_opt_hdr *)*p;
601+
if (ipv6_optlen(*(struct ipv6_opt_hdr **)hdr) > newoptlen)
602+
return -EINVAL;
603+
*p += CMSG_ALIGN(newoptlen);
604+
}
605+
}
606+
return 0;
607+
}
608+
609+
struct ipv6_txoptions *
610+
ipv6_renew_options(struct sock *sk, struct ipv6_txoptions *opt,
611+
int newtype,
612+
struct ipv6_opt_hdr __user *newopt, int newoptlen)
613+
{
614+
int tot_len = 0;
615+
char *p;
616+
struct ipv6_txoptions *opt2;
617+
int err;
618+
619+
if (newtype != IPV6_HOPOPTS && opt->hopopt)
620+
tot_len += CMSG_ALIGN(ipv6_optlen(opt->hopopt));
621+
if (newtype != IPV6_RTHDRDSTOPTS && opt->dst0opt)
622+
tot_len += CMSG_ALIGN(ipv6_optlen(opt->dst0opt));
623+
if (newtype != IPV6_RTHDR && opt->srcrt)
624+
tot_len += CMSG_ALIGN(ipv6_optlen(opt->srcrt));
625+
if (newtype != IPV6_DSTOPTS && opt->dst1opt)
626+
tot_len += CMSG_ALIGN(ipv6_optlen(opt->dst1opt));
627+
if (newopt && newoptlen)
628+
tot_len += CMSG_ALIGN(newoptlen);
629+
630+
if (!tot_len)
631+
return NULL;
632+
633+
opt2 = sock_kmalloc(sk, tot_len, GFP_ATOMIC);
634+
if (!opt2)
635+
return ERR_PTR(-ENOBUFS);
636+
637+
memset(opt2, 0, tot_len);
638+
639+
opt2->tot_len = tot_len;
640+
p = (char *)(opt2 + 1);
641+
642+
err = ipv6_renew_option(opt->hopopt, newopt, newoptlen,
643+
newtype != IPV6_HOPOPTS,
644+
&opt2->hopopt, &p);
645+
if (err)
646+
goto out;
647+
648+
err = ipv6_renew_option(opt->dst0opt, newopt, newoptlen,
649+
newtype != IPV6_RTHDRDSTOPTS,
650+
&opt2->dst0opt, &p);
651+
if (err)
652+
goto out;
653+
654+
err = ipv6_renew_option(opt->srcrt, newopt, newoptlen,
655+
newtype != IPV6_RTHDR,
656+
(struct ipv6_opt_hdr **)opt2->srcrt, &p);
657+
if (err)
658+
goto out;
659+
660+
err = ipv6_renew_option(opt->dst1opt, newopt, newoptlen,
661+
newtype != IPV6_DSTOPTS,
662+
&opt2->dst1opt, &p);
663+
if (err)
664+
goto out;
665+
666+
opt2->opt_nflen = (opt2->hopopt ? ipv6_optlen(opt2->hopopt) : 0) +
667+
(opt2->dst0opt ? ipv6_optlen(opt2->dst0opt) : 0) +
668+
(opt2->srcrt ? ipv6_optlen(opt2->srcrt) : 0);
669+
opt2->opt_flen = (opt2->dst1opt ? ipv6_optlen(opt2->dst1opt) : 0);
670+
671+
return opt2;
672+
out:
673+
sock_kfree_s(sk, p, tot_len);
674+
return ERR_PTR(err);
675+
}
676+

0 commit comments

Comments
 (0)