Skip to content

Commit

Permalink
[IPV6]: Support several new sockopt / ancillary data in Advanced API …
Browse files Browse the repository at this point in the history
…(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>
  • Loading branch information
YOSHIFUJI Hideaki committed Sep 8, 2005
1 parent 4706df3 commit 333fad5
Show file tree
Hide file tree
Showing 10 changed files with 423 additions and 49 deletions.
38 changes: 32 additions & 6 deletions include/linux/in6.h
Original file line number Diff line number Diff line change
Expand Up @@ -148,13 +148,13 @@ struct in6_flowlabel_req
*/

#define IPV6_ADDRFORM 1
#define IPV6_PKTINFO 2
#define IPV6_HOPOPTS 3
#define IPV6_DSTOPTS 4
#define IPV6_RTHDR 5
#define IPV6_PKTOPTIONS 6
#define IPV6_2292PKTINFO 2
#define IPV6_2292HOPOPTS 3
#define IPV6_2292DSTOPTS 4
#define IPV6_2292RTHDR 5
#define IPV6_2292PKTOPTIONS 6
#define IPV6_CHECKSUM 7
#define IPV6_HOPLIMIT 8
#define IPV6_2292HOPLIMIT 8
#define IPV6_NEXTHOP 9
#define IPV6_AUTHHDR 10 /* obsolete */
#define IPV6_FLOWINFO 11
Expand Down Expand Up @@ -198,4 +198,30 @@ struct in6_flowlabel_req
* MCAST_MSFILTER 48
*/

/* RFC3542 advanced socket options (50-67) */
#define IPV6_RECVPKTINFO 50
#define IPV6_PKTINFO 51
#if 0
#define IPV6_RECVPATHMTU 52
#define IPV6_PATHMTU 53
#define IPV6_DONTFRAG 54
#define IPV6_USE_MIN_MTU 55
#endif
#define IPV6_RECVHOPOPTS 56
#define IPV6_HOPOPTS 57
#if 0
#define IPV6_RECVRTHDRDSTOPTS 58 /* Unused, see net/ipv6/datagram.c */
#endif
#define IPV6_RTHDRDSTOPTS 59
#define IPV6_RECVRTHDR 60
#define IPV6_RTHDR 61
#define IPV6_RECVDSTOPTS 62
#define IPV6_DSTOPTS 63
#define IPV6_RECVHOPLIMIT 64
#define IPV6_HOPLIMIT 65
#if 0
#define IPV6_RECVTCLASS 66
#define IPV6_TCLASS 67
#endif

#endif
10 changes: 8 additions & 2 deletions include/linux/ipv6.h
Original file line number Diff line number Diff line change
Expand Up @@ -189,6 +189,7 @@ struct inet6_skb_parm {
__u16 dst0;
__u16 srcrt;
__u16 dst1;
__u16 lastopt;
};

#define IP6CB(skb) ((struct inet6_skb_parm*)((skb)->cb))
Expand Down Expand Up @@ -234,14 +235,19 @@ struct ipv6_pinfo {
/* pktoption flags */
union {
struct {
__u8 srcrt:2,
__u16 srcrt:2,
osrcrt:2,
rxinfo:1,
rxoinfo:1,
rxhlim:1,
rxohlim:1,
hopopts:1,
ohopopts:1,
dstopts:1,
odstopts:1,
rxflow:1;
} bits;
__u8 all;
__u16 all;
} rxopt;

/* sockopt flags */
Expand Down
4 changes: 4 additions & 0 deletions include/net/ipv6.h
Original file line number Diff line number Diff line change
Expand Up @@ -233,6 +233,10 @@ extern int ip6_ra_control(struct sock *sk, int sel,
extern int ipv6_parse_hopopts(struct sk_buff *skb, int);

extern struct ipv6_txoptions * ipv6_dup_options(struct sock *sk, struct ipv6_txoptions *opt);
extern struct ipv6_txoptions * ipv6_renew_options(struct sock *sk, struct ipv6_txoptions *opt,
int newtype,
struct ipv6_opt_hdr __user *newopt,
int newoptlen);

extern int ip6_frag_nqueues;
extern atomic_t ip6_frag_mem;
Expand Down
110 changes: 102 additions & 8 deletions net/ipv6/datagram.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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;
Expand All @@ -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;
Expand All @@ -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;
Expand Down Expand Up @@ -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;
Expand All @@ -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;
Expand Down
107 changes: 104 additions & 3 deletions net/ipv6/exthdrs.c
Original file line number Diff line number Diff line change
Expand Up @@ -164,6 +164,7 @@ static int ipv6_destopt_rcv(struct sk_buff **skbp, unsigned int *nhoffp)
return -1;
}

opt->lastopt = skb->h.raw - skb->nh.raw;
opt->dst1 = skb->h.raw - skb->nh.raw;

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

looped_back:
if (hdr->segments_left == 0) {
opt->lastopt = skb->h.raw - skb->nh.raw;
opt->srcrt = skb->h.raw - skb->nh.raw;
skb->h.raw += (hdr->hdrlen + 1) << 3;
opt->dst0 = opt->dst1;
Expand Down Expand Up @@ -539,10 +541,15 @@ void ipv6_push_nfrag_opts(struct sk_buff *skb, struct ipv6_txoptions *opt,
u8 *proto,
struct in6_addr **daddr)
{
if (opt->srcrt)
if (opt->srcrt) {
ipv6_push_rthdr(skb, proto, opt->srcrt, daddr);
if (opt->dst0opt)
ipv6_push_exthdr(skb, proto, NEXTHDR_DEST, opt->dst0opt);
/*
* IPV6_RTHDRDSTOPTS is ignored
* unless IPV6_RTHDR is set (RFC3542).
*/
if (opt->dst0opt)
ipv6_push_exthdr(skb, proto, NEXTHDR_DEST, opt->dst0opt);
}
if (opt->hopopt)
ipv6_push_exthdr(skb, proto, NEXTHDR_HOP, opt->hopopt);
}
Expand Down Expand Up @@ -573,3 +580,97 @@ ipv6_dup_options(struct sock *sk, struct ipv6_txoptions *opt)
}
return opt2;
}

static int ipv6_renew_option(void *ohdr,
struct ipv6_opt_hdr __user *newopt, int newoptlen,
int inherit,
struct ipv6_opt_hdr **hdr,
char **p)
{
if (inherit) {
if (ohdr) {
memcpy(*p, ohdr, ipv6_optlen((struct ipv6_opt_hdr *)ohdr));
*hdr = (struct ipv6_opt_hdr *)*p;
*p += CMSG_ALIGN(ipv6_optlen(*(struct ipv6_opt_hdr **)hdr));
}
} else {
if (newopt) {
if (copy_from_user(*p, newopt, newoptlen))
return -EFAULT;
*hdr = (struct ipv6_opt_hdr *)*p;
if (ipv6_optlen(*(struct ipv6_opt_hdr **)hdr) > newoptlen)
return -EINVAL;
*p += CMSG_ALIGN(newoptlen);
}
}
return 0;
}

struct ipv6_txoptions *
ipv6_renew_options(struct sock *sk, struct ipv6_txoptions *opt,
int newtype,
struct ipv6_opt_hdr __user *newopt, int newoptlen)
{
int tot_len = 0;
char *p;
struct ipv6_txoptions *opt2;
int err;

if (newtype != IPV6_HOPOPTS && opt->hopopt)
tot_len += CMSG_ALIGN(ipv6_optlen(opt->hopopt));
if (newtype != IPV6_RTHDRDSTOPTS && opt->dst0opt)
tot_len += CMSG_ALIGN(ipv6_optlen(opt->dst0opt));
if (newtype != IPV6_RTHDR && opt->srcrt)
tot_len += CMSG_ALIGN(ipv6_optlen(opt->srcrt));
if (newtype != IPV6_DSTOPTS && opt->dst1opt)
tot_len += CMSG_ALIGN(ipv6_optlen(opt->dst1opt));
if (newopt && newoptlen)
tot_len += CMSG_ALIGN(newoptlen);

if (!tot_len)
return NULL;

opt2 = sock_kmalloc(sk, tot_len, GFP_ATOMIC);
if (!opt2)
return ERR_PTR(-ENOBUFS);

memset(opt2, 0, tot_len);

opt2->tot_len = tot_len;
p = (char *)(opt2 + 1);

err = ipv6_renew_option(opt->hopopt, newopt, newoptlen,
newtype != IPV6_HOPOPTS,
&opt2->hopopt, &p);
if (err)
goto out;

err = ipv6_renew_option(opt->dst0opt, newopt, newoptlen,
newtype != IPV6_RTHDRDSTOPTS,
&opt2->dst0opt, &p);
if (err)
goto out;

err = ipv6_renew_option(opt->srcrt, newopt, newoptlen,
newtype != IPV6_RTHDR,
(struct ipv6_opt_hdr **)opt2->srcrt, &p);
if (err)
goto out;

err = ipv6_renew_option(opt->dst1opt, newopt, newoptlen,
newtype != IPV6_DSTOPTS,
&opt2->dst1opt, &p);
if (err)
goto out;

opt2->opt_nflen = (opt2->hopopt ? ipv6_optlen(opt2->hopopt) : 0) +
(opt2->dst0opt ? ipv6_optlen(opt2->dst0opt) : 0) +
(opt2->srcrt ? ipv6_optlen(opt2->srcrt) : 0);
opt2->opt_flen = (opt2->dst1opt ? ipv6_optlen(opt2->dst1opt) : 0);

return opt2;
out:
sock_kfree_s(sk, p, tot_len);
return ERR_PTR(err);
}

Loading

0 comments on commit 333fad5

Please sign in to comment.