Commit 2e66fc41 authored by David S. Miller's avatar David S. Miller
parents 42ca89c1 41a1f8ea
...@@ -148,13 +148,13 @@ struct in6_flowlabel_req ...@@ -148,13 +148,13 @@ struct in6_flowlabel_req
*/ */
#define IPV6_ADDRFORM 1 #define IPV6_ADDRFORM 1
#define IPV6_PKTINFO 2 #define IPV6_2292PKTINFO 2
#define IPV6_HOPOPTS 3 #define IPV6_2292HOPOPTS 3
#define IPV6_DSTOPTS 4 #define IPV6_2292DSTOPTS 4
#define IPV6_RTHDR 5 #define IPV6_2292RTHDR 5
#define IPV6_PKTOPTIONS 6 #define IPV6_2292PKTOPTIONS 6
#define IPV6_CHECKSUM 7 #define IPV6_CHECKSUM 7
#define IPV6_HOPLIMIT 8 #define IPV6_2292HOPLIMIT 8
#define IPV6_NEXTHOP 9 #define IPV6_NEXTHOP 9
#define IPV6_AUTHHDR 10 /* obsolete */ #define IPV6_AUTHHDR 10 /* obsolete */
#define IPV6_FLOWINFO 11 #define IPV6_FLOWINFO 11
...@@ -198,4 +198,28 @@ struct in6_flowlabel_req ...@@ -198,4 +198,28 @@ struct in6_flowlabel_req
* MCAST_MSFILTER 48 * 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
#define IPV6_RECVTCLASS 66
#define IPV6_TCLASS 67
#endif #endif
...@@ -189,6 +189,7 @@ struct inet6_skb_parm { ...@@ -189,6 +189,7 @@ struct inet6_skb_parm {
__u16 dst0; __u16 dst0;
__u16 srcrt; __u16 srcrt;
__u16 dst1; __u16 dst1;
__u16 lastopt;
}; };
#define IP6CB(skb) ((struct inet6_skb_parm*)((skb)->cb)) #define IP6CB(skb) ((struct inet6_skb_parm*)((skb)->cb))
...@@ -234,14 +235,20 @@ struct ipv6_pinfo { ...@@ -234,14 +235,20 @@ struct ipv6_pinfo {
/* pktoption flags */ /* pktoption flags */
union { union {
struct { struct {
__u8 srcrt:2, __u16 srcrt:2,
osrcrt:2,
rxinfo:1, rxinfo:1,
rxoinfo:1,
rxhlim:1, rxhlim:1,
rxohlim:1,
hopopts:1, hopopts:1,
ohopopts:1,
dstopts:1, dstopts:1,
rxflow:1; odstopts:1,
rxflow:1,
rxtclass:1;
} bits; } bits;
__u8 all; __u16 all;
} rxopt; } rxopt;
/* sockopt flags */ /* sockopt flags */
...@@ -250,6 +257,7 @@ struct ipv6_pinfo { ...@@ -250,6 +257,7 @@ struct ipv6_pinfo {
sndflow:1, sndflow:1,
pmtudisc:2, pmtudisc:2,
ipv6only:1; ipv6only:1;
__u8 tclass;
__u32 dst_cookie; __u32 dst_cookie;
...@@ -263,6 +271,7 @@ struct ipv6_pinfo { ...@@ -263,6 +271,7 @@ struct ipv6_pinfo {
struct ipv6_txoptions *opt; struct ipv6_txoptions *opt;
struct rt6_info *rt; struct rt6_info *rt;
int hop_limit; int hop_limit;
int tclass;
} cork; } cork;
}; };
......
...@@ -233,6 +233,10 @@ extern int ip6_ra_control(struct sock *sk, int sel, ...@@ -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 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_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 int ip6_frag_nqueues;
extern atomic_t ip6_frag_mem; extern atomic_t ip6_frag_mem;
...@@ -373,6 +377,7 @@ extern int ip6_append_data(struct sock *sk, ...@@ -373,6 +377,7 @@ extern int ip6_append_data(struct sock *sk,
int length, int length,
int transhdrlen, int transhdrlen,
int hlimit, int hlimit,
int tclass,
struct ipv6_txoptions *opt, struct ipv6_txoptions *opt,
struct flowi *fl, struct flowi *fl,
struct rt6_info *rt, struct rt6_info *rt,
......
...@@ -37,7 +37,7 @@ extern int datagram_recv_ctl(struct sock *sk, ...@@ -37,7 +37,7 @@ extern int datagram_recv_ctl(struct sock *sk,
extern int datagram_send_ctl(struct msghdr *msg, extern int datagram_send_ctl(struct msghdr *msg,
struct flowi *fl, struct flowi *fl,
struct ipv6_txoptions *opt, struct ipv6_txoptions *opt,
int *hlimit); int *hlimit, int *tclass);
#define LOOPBACK4_IPV6 __constant_htonl(0x7f000006) #define LOOPBACK4_IPV6 __constant_htonl(0x7f000006)
......
...@@ -390,32 +390,101 @@ int datagram_recv_ctl(struct sock *sk, struct msghdr *msg, struct sk_buff *skb) ...@@ -390,32 +390,101 @@ int datagram_recv_ctl(struct sock *sk, struct msghdr *msg, struct sk_buff *skb)
put_cmsg(msg, SOL_IPV6, IPV6_HOPLIMIT, sizeof(hlim), &hlim); put_cmsg(msg, SOL_IPV6, IPV6_HOPLIMIT, sizeof(hlim), &hlim);
} }
if (np->rxopt.bits.rxtclass) {
int tclass = (ntohl(*(u32 *)skb->nh.ipv6h) >> 20) & 0xff;
put_cmsg(msg, SOL_IPV6, IPV6_TCLASS, sizeof(tclass), &tclass);
}
if (np->rxopt.bits.rxflow && (*(u32*)skb->nh.raw & IPV6_FLOWINFO_MASK)) { if (np->rxopt.bits.rxflow && (*(u32*)skb->nh.raw & IPV6_FLOWINFO_MASK)) {
u32 flowinfo = *(u32*)skb->nh.raw & IPV6_FLOWINFO_MASK; u32 flowinfo = *(u32*)skb->nh.raw & IPV6_FLOWINFO_MASK;
put_cmsg(msg, SOL_IPV6, IPV6_FLOWINFO, sizeof(flowinfo), &flowinfo); put_cmsg(msg, SOL_IPV6, IPV6_FLOWINFO, sizeof(flowinfo), &flowinfo);
} }
/* HbH is allowed only once */
if (np->rxopt.bits.hopopts && opt->hop) { if (np->rxopt.bits.hopopts && opt->hop) {
u8 *ptr = skb->nh.raw + opt->hop; u8 *ptr = skb->nh.raw + opt->hop;
put_cmsg(msg, SOL_IPV6, IPV6_HOPOPTS, (ptr[1]+1)<<3, ptr); 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; 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); 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; 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; return 0;
} }
int datagram_send_ctl(struct msghdr *msg, struct flowi *fl, int datagram_send_ctl(struct msghdr *msg, struct flowi *fl,
struct ipv6_txoptions *opt, struct ipv6_txoptions *opt,
int *hlimit) int *hlimit, int *tclass)
{ {
struct in6_pktinfo *src_info; struct in6_pktinfo *src_info;
struct cmsghdr *cmsg; struct cmsghdr *cmsg;
...@@ -438,6 +507,7 @@ int datagram_send_ctl(struct msghdr *msg, struct flowi *fl, ...@@ -438,6 +507,7 @@ int datagram_send_ctl(struct msghdr *msg, struct flowi *fl,
switch (cmsg->cmsg_type) { switch (cmsg->cmsg_type) {
case IPV6_PKTINFO: case IPV6_PKTINFO:
case IPV6_2292PKTINFO:
if (cmsg->cmsg_len < CMSG_LEN(sizeof(struct in6_pktinfo))) { if (cmsg->cmsg_len < CMSG_LEN(sizeof(struct in6_pktinfo))) {
err = -EINVAL; err = -EINVAL;
goto exit_f; goto exit_f;
...@@ -492,6 +562,7 @@ int datagram_send_ctl(struct msghdr *msg, struct flowi *fl, ...@@ -492,6 +562,7 @@ int datagram_send_ctl(struct msghdr *msg, struct flowi *fl,
fl->fl6_flowlabel = IPV6_FLOWINFO_MASK & *(u32 *)CMSG_DATA(cmsg); fl->fl6_flowlabel = IPV6_FLOWINFO_MASK & *(u32 *)CMSG_DATA(cmsg);
break; break;
case IPV6_2292HOPOPTS:
case IPV6_HOPOPTS: case IPV6_HOPOPTS:
if (opt->hopopt || cmsg->cmsg_len < CMSG_LEN(sizeof(struct ipv6_opt_hdr))) { if (opt->hopopt || cmsg->cmsg_len < CMSG_LEN(sizeof(struct ipv6_opt_hdr))) {
err = -EINVAL; err = -EINVAL;
...@@ -512,7 +583,7 @@ int datagram_send_ctl(struct msghdr *msg, struct flowi *fl, ...@@ -512,7 +583,7 @@ int datagram_send_ctl(struct msghdr *msg, struct flowi *fl,
opt->hopopt = hdr; opt->hopopt = hdr;
break; break;
case IPV6_DSTOPTS: case IPV6_2292DSTOPTS:
if (cmsg->cmsg_len < CMSG_LEN(sizeof(struct ipv6_opt_hdr))) { if (cmsg->cmsg_len < CMSG_LEN(sizeof(struct ipv6_opt_hdr))) {
err = -EINVAL; err = -EINVAL;
goto exit_f; goto exit_f;
...@@ -536,6 +607,33 @@ int datagram_send_ctl(struct msghdr *msg, struct flowi *fl, ...@@ -536,6 +607,33 @@ int datagram_send_ctl(struct msghdr *msg, struct flowi *fl,
opt->dst1opt = hdr; opt->dst1opt = hdr;
break; 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: case IPV6_RTHDR:
if (cmsg->cmsg_len < CMSG_LEN(sizeof(struct ipv6_rt_hdr))) { if (cmsg->cmsg_len < CMSG_LEN(sizeof(struct ipv6_rt_hdr))) {
err = -EINVAL; err = -EINVAL;
...@@ -568,7 +666,7 @@ int datagram_send_ctl(struct msghdr *msg, struct flowi *fl, ...@@ -568,7 +666,7 @@ int datagram_send_ctl(struct msghdr *msg, struct flowi *fl,
opt->opt_nflen += len; opt->opt_nflen += len;
opt->srcrt = rthdr; opt->srcrt = rthdr;
if (opt->dst1opt) { if (cmsg->cmsg_type == IPV6_2292RTHDR && opt->dst1opt) {
int dsthdrlen = ((opt->dst1opt->hdrlen+1)<<3); int dsthdrlen = ((opt->dst1opt->hdrlen+1)<<3);
opt->opt_nflen += dsthdrlen; opt->opt_nflen += dsthdrlen;
...@@ -579,6 +677,7 @@ int datagram_send_ctl(struct msghdr *msg, struct flowi *fl, ...@@ -579,6 +677,7 @@ int datagram_send_ctl(struct msghdr *msg, struct flowi *fl,
break; break;
case IPV6_2292HOPLIMIT:
case IPV6_HOPLIMIT: case IPV6_HOPLIMIT:
if (cmsg->cmsg_len != CMSG_LEN(sizeof(int))) { if (cmsg->cmsg_len != CMSG_LEN(sizeof(int))) {
err = -EINVAL; err = -EINVAL;
...@@ -588,6 +687,24 @@ int datagram_send_ctl(struct msghdr *msg, struct flowi *fl, ...@@ -588,6 +687,24 @@ int datagram_send_ctl(struct msghdr *msg, struct flowi *fl,
*hlimit = *(int *)CMSG_DATA(cmsg); *hlimit = *(int *)CMSG_DATA(cmsg);
break; break;
case IPV6_TCLASS:
{
int tc;
err = -EINVAL;
if (cmsg->cmsg_len != CMSG_LEN(sizeof(int))) {
goto exit_f;
}
tc = *(int *)CMSG_DATA(cmsg);
if (tc < 0 || tc > 0xff)
goto exit_f;
err = 0;
*tclass = tc;
break;
}
default: default:
LIMIT_NETDEBUG(KERN_DEBUG "invalid cmsg type: %d\n", LIMIT_NETDEBUG(KERN_DEBUG "invalid cmsg type: %d\n",
cmsg->cmsg_type); cmsg->cmsg_type);
......
...@@ -164,6 +164,7 @@ static int ipv6_destopt_rcv(struct sk_buff **skbp, unsigned int *nhoffp) ...@@ -164,6 +164,7 @@ static int ipv6_destopt_rcv(struct sk_buff **skbp, unsigned int *nhoffp)
return -1; return -1;
} }
opt->lastopt = skb->h.raw - skb->nh.raw;
opt->dst1 = skb->h.raw - skb->nh.raw; opt->dst1 = skb->h.raw - skb->nh.raw;
if (ip6_parse_tlv(tlvprocdestopt_lst, skb)) { if (ip6_parse_tlv(tlvprocdestopt_lst, skb)) {
...@@ -243,6 +244,7 @@ static int ipv6_rthdr_rcv(struct sk_buff **skbp, unsigned int *nhoffp) ...@@ -243,6 +244,7 @@ static int ipv6_rthdr_rcv(struct sk_buff **skbp, unsigned int *nhoffp)
looped_back: looped_back:
if (hdr->segments_left == 0) { if (hdr->segments_left == 0) {
opt->lastopt = skb->h.raw - skb->nh.raw;
opt->srcrt = skb->h.raw - skb->nh.raw; opt->srcrt = skb->h.raw - skb->nh.raw;
skb->h.raw += (hdr->hdrlen + 1) << 3; skb->h.raw += (hdr->hdrlen + 1) << 3;
opt->dst0 = opt->dst1; opt->dst0 = opt->dst1;
...@@ -538,10 +540,15 @@ void ipv6_push_nfrag_opts(struct sk_buff *skb, struct ipv6_txoptions *opt, ...@@ -538,10 +540,15 @@ void ipv6_push_nfrag_opts(struct sk_buff *skb, struct ipv6_txoptions *opt,
u8 *proto, u8 *proto,
struct in6_addr **daddr) struct in6_addr **daddr)
{ {
if (opt->srcrt) if (opt->srcrt) {
ipv6_push_rthdr(skb, proto, opt->srcrt, daddr); 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) if (opt->hopopt)
ipv6_push_exthdr(skb, proto, NEXTHDR_HOP, opt->hopopt); ipv6_push_exthdr(skb, proto, NEXTHDR_HOP, opt->hopopt);
} }
...@@ -572,3 +579,97 @@ ipv6_dup_options(struct sock *sk, struct ipv6_txoptions *opt) ...@@ -572,3 +579,97 @@ ipv6_dup_options(struct sock *sk, struct ipv6_txoptions *opt)
} }
return opt2; 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);
}
...@@ -287,7 +287,7 @@ void icmpv6_send(struct sk_buff *skb, int type, int code, __u32 info, ...@@ -287,7 +287,7 @@ void icmpv6_send(struct sk_buff *skb, int type, int code, __u32 info,
int iif = 0; int iif = 0;
int addr_type = 0; int addr_type = 0;
int len; int len;
int hlimit; int hlimit, tclass;
int err = 0; int err = 0;
if ((u8*)hdr < skb->head || (u8*)(hdr+1) > skb->tail) if ((u8*)hdr < skb->head || (u8*)(hdr+1) > skb->tail)
...@@ -385,6 +385,10 @@ void icmpv6_send(struct sk_buff *skb, int type, int code, __u32 info, ...@@ -385,6 +385,10 @@ void icmpv6_send(struct sk_buff *skb, int type, int code, __u32 info,
if (hlimit < 0) if (hlimit < 0)
hlimit = ipv6_get_hoplimit(dst->dev); hlimit = ipv6_get_hoplimit(dst->dev);
tclass = np->cork.tclass;
if (tclass < 0)
tclass = 0;
msg.skb = skb; msg.skb = skb;
msg.offset = skb->nh.raw - skb->data; msg.offset = skb->nh.raw - skb->data;
...@@ -400,7 +404,7 @@ void icmpv6_send(struct sk_buff *skb, int type, int code, __u32 info, ...@@ -400,7 +404,7 @@ void icmpv6_send(struct sk_buff *skb, int type, int code, __u32 info,
err = ip6_append_data(sk, icmpv6_getfrag, &msg, err = ip6_append_data(sk, icmpv6_getfrag, &msg,
len + sizeof(struct icmp6hdr), len + sizeof(struct icmp6hdr),
sizeof(struct icmp6hdr), sizeof(struct icmp6hdr),
hlimit, NULL, &fl, (struct rt6_info*)dst, hlimit, tclass, NULL, &fl, (struct rt6_info*)dst,
MSG_DONTWAIT); MSG_DONTWAIT);
if (err) { if (err) {
ip6_flush_pending_frames(sk); ip6_flush_pending_frames(sk);
...@@ -434,6 +438,7 @@ static void icmpv6_echo_reply(struct sk_buff *skb) ...@@ -434,6 +438,7 @@ static void icmpv6_echo_reply(struct sk_buff *skb)
struct dst_entry *dst; struct dst_entry *dst;
int err = 0; int err = 0;
int hlimit; int hlimit;
int tclass;
saddr = &skb->nh.ipv6h->daddr; saddr = &skb->nh.ipv6h->daddr;
...@@ -475,13 +480,17 @@ static void icmpv6_echo_reply(struct sk_buff *skb) ...@@ -475,13 +480,17 @@ static void icmpv6_echo_reply(struct sk_buff *skb)
if (hlimit < 0) if (hlimit < 0)
hlimit = ipv6_get_hoplimit(dst->dev); hlimit = ipv6_get_hoplimit(dst->dev);
tclass = np->cork.tclass;
if (tclass < 0)
tclass = 0;
idev = in6_dev_get(skb->dev); idev = in6_dev_get(skb->dev);
msg.skb = skb; msg.skb = skb;
msg.offset = 0; msg.offset = 0;
err = ip6_append_data(sk, icmpv6_getfrag, &msg, skb->len + sizeof(struct icmp6hdr), err = ip6_append_data(sk, icmpv6_getfrag, &msg, skb->len + sizeof(struct icmp6hdr),
sizeof(struct icmp6hdr), hlimit, NULL, &fl, sizeof(struct icmp6hdr), hlimit, tclass, NULL, &fl,
(struct rt6_info*)dst, MSG_DONTWAIT); (struct rt6_info*)dst, MSG_DONTWAIT);
if (err) { if (err) {
......
...@@ -225,16 +225,20 @@ struct ipv6_txoptions *fl6_merge_options(struct ipv6_txoptions * opt_space, ...@@ -225,16 +225,20 @@ struct ipv6_txoptions *fl6_merge_options(struct ipv6_txoptions * opt_space,
struct ip6_flowlabel * fl, struct ip6_flowlabel * fl,
struct ipv6_txoptions * fopt) struct ipv6_txoptions * fopt)
{ {
struct ipv6_txoptions * fl_opt = fl->opt; struct ipv6_txoptions * fl_opt = fl ? fl->opt : NULL;
if (fopt == NULL || fopt->opt_flen == 0) if (fopt == NULL || fopt->opt_flen == 0) {
return fl_opt; if (!fl_opt || !fl_opt->dst0opt || fl_opt->srcrt)
return fl_opt;
}
if (fl_opt != NULL) { if (fl_opt != NULL) {
opt_space->hopopt = fl_opt->hopopt; opt_space->hopopt = fl_opt->hopopt;
opt_space->dst0opt = fl_opt->dst0opt; opt_space->dst0opt = fl_opt->srcrt ? fl_opt->dst0opt : NULL;
opt_space->srcrt = fl_opt->srcrt; opt_space->srcrt = fl_opt->srcrt;
opt_space->opt_nflen = fl_opt->opt_nflen; opt_space->opt_nflen = fl_opt->opt_nflen;
if (fl_opt->dst0opt && !fl_opt->srcrt)
opt_space->opt_nflen -= ipv6_optlen(fl_opt->dst0opt);
} else { } else {
if (fopt->opt_nflen == 0) if (fopt->opt_nflen == 0)
return fopt; return fopt;
...@@ -310,7 +314,7 @@ fl_create(struct in6_flowlabel_req *freq, char __user *optval, int optlen, int * ...@@ -310,7 +314,7 @@ fl_create(struct in6_flowlabel_req *freq, char __user *optval, int optlen, int *
msg.msg_control = (void*)(fl->opt+1); msg.msg_control = (void*)(fl->opt+1);
flowi.oif = 0; flowi.oif = 0;
err = datagram_send_ctl(&msg, &flowi, fl->opt, &junk); err = datagram_send_ctl(&msg, &flowi, fl->opt, &junk, &junk);
if (err) if (err)
goto done; goto done;
err = -EINVAL; err = -EINVAL;
......
...@@ -166,7 +166,7 @@ int ip6_xmit(struct sock *sk, struct sk_buff *skb, struct flowi *fl, ...@@ -166,7 +166,7 @@ int ip6_xmit(struct sock *sk, struct sk_buff *skb, struct flowi *fl,
struct ipv6hdr *hdr; struct ipv6hdr *hdr;
u8 proto = fl->proto; u8 proto = fl->proto;
int seg_len = skb->len; int seg_len = skb->len;
int hlimit; int hlimit, tclass;
u32 mtu; u32 mtu;
if (opt) { if (opt) {
...@@ -202,7 +202,6 @@ int ip6_xmit(struct sock *sk, struct sk_buff *skb, struct flowi *fl, ...@@ -202,7 +202,6 @@ int ip6_xmit(struct sock *sk, struct sk_buff *skb, struct flowi *fl,
* Fill in the IPv6 header * Fill in the IPv6 header
*/ */
*(u32*)hdr = htonl(0x60000000) | fl->fl6_flowlabel;
hlimit = -1; hlimit = -1;
if (np) if (np)
hlimit = np->hop_limit; hlimit = np->hop_limit;
...@@ -211,6 +210,14 @@ int ip6_xmit(struct sock *sk, struct sk_buff *skb, struct flowi *fl, ...@@ -211,6 +210,14 @@ int ip6_xmit(struct sock *sk, struct sk_buff *skb, struct flowi *fl,
if (hlimit < 0) if (hlimit < 0)
hlimit = ipv6_get_hoplimit(dst->dev); hlimit = ipv6_get_hoplimit(dst->dev);
tclass = -1;
if (np)
tclass = np->tclass;
if (tclass < 0)
tclass = 0;
*(u32 *)hdr = htonl(0x60000000 | (tclass << 20)) | fl->fl6_flowlabel;
hdr->payload_len = htons(seg_len); hdr->payload_len = htons(seg_len);
hdr->nexthdr = proto; hdr->nexthdr = proto;
hdr->hop_limit = hlimit; hdr->hop_limit = hlimit;
...@@ -762,10 +769,11 @@ int ip6_dst_lookup(struct sock *sk, struct dst_entry **dst, struct flowi *fl) ...@@ -762,10 +769,11 @@ int ip6_dst_lookup(struct sock *sk, struct dst_entry **dst, struct flowi *fl)
return err; return err;
} }
int ip6_append_data(struct sock *sk, int getfrag(void *from, char *to, int offset, int len, int odd, struct sk_buff *skb), int ip6_append_data(struct sock *sk, int getfrag(void *from, char *to,
void *from, int length, int transhdrlen, int offset, int len, int odd, struct sk_buff *skb),
int hlimit, struct ipv6_txoptions *opt, struct flowi *fl, struct rt6_info *rt, void *from, int length, int transhdrlen,
unsigned int flags) int hlimit, int tclass, struct ipv6_txoptions *opt, struct flowi *fl,
struct rt6_info *rt, unsigned int flags)
{ {
struct inet_sock *inet = inet_sk(sk); struct inet_sock *inet = inet_sk(sk);
struct ipv6_pinfo *np = inet6_sk(sk); struct ipv6_pinfo *np = inet6_sk(sk);
...@@ -803,6 +811,7 @@ int ip6_append_data(struct sock *sk, int getfrag(void *from, char *to, int offse ...@@ -803,6 +811,7 @@ int ip6_append_data(struct sock *sk, int getfrag(void *from, char *to, int offse
np->cork.rt = rt; np->cork.rt = rt;
inet->cork.fl = *fl; inet->cork.fl = *fl;
np->cork.hop_limit = hlimit; np->cork.hop_limit = hlimit;
np->cork.tclass = tclass;
inet->cork.fragsize = mtu = dst_mtu(rt->u.dst.path); inet->cork.fragsize = mtu = dst_mtu(rt->u.dst.path);
if (dst_allfrag(rt->u.dst.path)) if (dst_allfrag(rt->u.dst.path))
inet->cork.flags |= IPCORK_ALLFRAG; inet->cork.flags |= IPCORK_ALLFRAG;
...@@ -1084,7 +1093,8 @@ int ip6_push_pending_frames(struct sock *sk) ...@@ -1084,7 +1093,8 @@ int ip6_push_pending_frames(struct sock *sk)
skb->nh.ipv6h = hdr = (struct ipv6hdr*) skb_push(skb, sizeof(struct ipv6hdr)); skb->nh.ipv6h = hdr = (struct ipv6hdr*) skb_push(skb, sizeof(struct ipv6hdr));
*(u32*)hdr = fl->fl6_flowlabel | htonl(0x60000000); *(u32*)hdr = fl->fl6_flowlabel |
htonl(0x60000000 | ((int)np->cork.tclass << 20));
if (skb->len <= sizeof(struct ipv6hdr) + IPV6_MAXPLEN) if (skb->len <= sizeof(struct ipv6hdr) + IPV6_MAXPLEN)
hdr->payload_len = htons(skb->len - sizeof(struct ipv6hdr)); hdr->payload_len = htons(skb->len - sizeof(struct ipv6hdr));
......
...@@ -210,39 +210,139 @@ int ipv6_setsockopt(struct sock *sk, int level, int optname, ...@@ -210,39 +210,139 @@ int ipv6_setsockopt(struct sock *sk, int level, int optname,
retv = 0; retv = 0;
break; break;
case IPV6_PKTINFO: case IPV6_RECVPKTINFO:
np->rxopt.bits.rxinfo = valbool; np->rxopt.bits.rxinfo = valbool;
retv = 0; retv = 0;
break; break;
case IPV6_2292PKTINFO:
np->rxopt.bits.rxoinfo = valbool;
retv = 0;
break;
case IPV6_HOPLIMIT: case IPV6_RECVHOPLIMIT:
np->rxopt.bits.rxhlim = valbool; np->rxopt.bits.rxhlim = valbool;
retv = 0; retv = 0;
break; break;
case IPV6_RTHDR: case IPV6_2292HOPLIMIT:
np->rxopt.bits.rxohlim = valbool;
retv = 0;
break;
case IPV6_RECVRTHDR:
if (val < 0 || val > 2) if (val < 0 || val > 2)
goto e_inval; goto e_inval;
np->rxopt.bits.srcrt = val; np->rxopt.bits.srcrt = val;
retv = 0; retv = 0;
break; 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; np->rxopt.bits.hopopts = valbool;
retv = 0; retv = 0;
break; break;
case IPV6_DSTOPTS: case IPV6_2292HOPOPTS:
np->rxopt.bits.ohopopts = valbool;
retv = 0;
break;
case IPV6_RECVDSTOPTS:
np->rxopt.bits.dstopts = valbool; np->rxopt.bits.dstopts = valbool;
retv = 0; retv = 0;
break; break;
case IPV6_2292DSTOPTS:
np->rxopt.bits.odstopts = valbool;
retv = 0;
break;
case IPV6_TCLASS:
if (val < 0 || val > 0xff)
goto e_inval;
np->tclass = val;
retv = 0;
break;
case IPV6_RECVTCLASS:
np->rxopt.bits.rxtclass = valbool;
retv = 0;
break;
case IPV6_FLOWINFO: case IPV6_FLOWINFO:
np->rxopt.bits.rxflow = valbool; np->rxopt.bits.rxflow = valbool;
retv = 0; retv = 0;
break; 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 ipv6_txoptions *opt = NULL;
struct msghdr msg; struct msghdr msg;
...@@ -276,7 +376,7 @@ int ipv6_setsockopt(struct sock *sk, int level, int optname, ...@@ -276,7 +376,7 @@ int ipv6_setsockopt(struct sock *sk, int level, int optname,
msg.msg_controllen = optlen; msg.msg_controllen = optlen;
msg.msg_control = (void*)(opt+1); msg.msg_control = (void*)(opt+1);
retv = datagram_send_ctl(&msg, &fl, opt, &junk); retv = datagram_send_ctl(&msg, &fl, opt, &junk, &junk);
if (retv) if (retv)
goto done; goto done;
update: update:
...@@ -529,6 +629,17 @@ int ipv6_setsockopt(struct sock *sk, int level, int optname, ...@@ -529,6 +629,17 @@ int ipv6_setsockopt(struct sock *sk, int level, int optname,
return -EINVAL; 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, int ipv6_getsockopt(struct sock *sk, int level, int optname,
char __user *optval, int __user *optlen) char __user *optval, int __user *optlen)
{ {
...@@ -567,7 +678,7 @@ int ipv6_getsockopt(struct sock *sk, int level, int optname, ...@@ -567,7 +678,7 @@ int ipv6_getsockopt(struct sock *sk, int level, int optname,
return err; return err;
} }
case IPV6_PKTOPTIONS: case IPV6_2292PKTOPTIONS:
{ {
struct msghdr msg; struct msghdr msg;
struct sk_buff *skb; struct sk_buff *skb;
...@@ -601,6 +712,16 @@ int ipv6_getsockopt(struct sock *sk, int level, int optname, ...@@ -601,6 +712,16 @@ int ipv6_getsockopt(struct sock *sk, int level, int optname,
int hlim = np->mcast_hops; int hlim = np->mcast_hops;
put_cmsg(&msg, SOL_IPV6, IPV6_HOPLIMIT, sizeof(hlim), &hlim); 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; len -= msg.msg_controllen;
return put_user(len, optlen); return put_user(len, optlen);
...@@ -625,26 +746,67 @@ int ipv6_getsockopt(struct sock *sk, int level, int optname, ...@@ -625,26 +746,67 @@ int ipv6_getsockopt(struct sock *sk, int level, int optname,
val = np->ipv6only; val = np->ipv6only;
break; break;
case IPV6_PKTINFO: case IPV6_RECVPKTINFO:
val = np->rxopt.bits.rxinfo; val = np->rxopt.bits.rxinfo;
break; break;
case IPV6_HOPLIMIT: case IPV6_2292PKTINFO:
val = np->rxopt.bits.rxoinfo;
break;
case IPV6_RECVHOPLIMIT:
val = np->rxopt.bits.rxhlim; val = np->rxopt.bits.rxhlim;
break; break;
case IPV6_RTHDR: case IPV6_2292HOPLIMIT:
val = np->rxopt.bits.rxohlim;
break;
case IPV6_RECVRTHDR:
val = np->rxopt.bits.srcrt; val = np->rxopt.bits.srcrt;
break; break;
case IPV6_2292RTHDR:
val = np->rxopt.bits.osrcrt;
break;
case IPV6_HOPOPTS: 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; val = np->rxopt.bits.hopopts;
break; break;
case IPV6_DSTOPTS: case IPV6_2292HOPOPTS:
val = np->rxopt.bits.ohopopts;
break;
case IPV6_RECVDSTOPTS:
val = np->rxopt.bits.dstopts; val = np->rxopt.bits.dstopts;
break; break;
case IPV6_2292DSTOPTS:
val = np->rxopt.bits.odstopts;
break;
case IPV6_TCLASS:
val = np->tclass;
break;
case IPV6_RECVTCLASS:
val = np->rxopt.bits.rxtclass;
break;
case IPV6_FLOWINFO: case IPV6_FLOWINFO:
val = np->rxopt.bits.rxflow; val = np->rxopt.bits.rxflow;
break; break;
......
...@@ -655,6 +655,7 @@ static int rawv6_sendmsg(struct kiocb *iocb, struct sock *sk, ...@@ -655,6 +655,7 @@ static int rawv6_sendmsg(struct kiocb *iocb, struct sock *sk,
struct flowi fl; struct flowi fl;
int addr_len = msg->msg_namelen; int addr_len = msg->msg_namelen;
int hlimit = -1; int hlimit = -1;
int tclass = -1;
u16 proto; u16 proto;
int err; int err;
...@@ -740,7 +741,7 @@ static int rawv6_sendmsg(struct kiocb *iocb, struct sock *sk, ...@@ -740,7 +741,7 @@ static int rawv6_sendmsg(struct kiocb *iocb, struct sock *sk,
memset(opt, 0, sizeof(struct ipv6_txoptions)); memset(opt, 0, sizeof(struct ipv6_txoptions));
opt->tot_len = sizeof(struct ipv6_txoptions); opt->tot_len = sizeof(struct ipv6_txoptions);
err = datagram_send_ctl(msg, &fl, opt, &hlimit); err = datagram_send_ctl(msg, &fl, opt, &hlimit, &tclass);
if (err < 0) { if (err < 0) {
fl6_sock_release(flowlabel); fl6_sock_release(flowlabel);
return err; return err;
...@@ -755,8 +756,7 @@ static int rawv6_sendmsg(struct kiocb *iocb, struct sock *sk, ...@@ -755,8 +756,7 @@ static int rawv6_sendmsg(struct kiocb *iocb, struct sock *sk,
} }
if (opt == NULL) if (opt == NULL)
opt = np->opt; opt = np->opt;
if (flowlabel) opt = fl6_merge_options(&opt_space, flowlabel, opt);
opt = fl6_merge_options(&opt_space, flowlabel, opt);
fl.proto = proto; fl.proto = proto;
rawv6_probe_proto_opt(&fl, msg); rawv6_probe_proto_opt(&fl, msg);
...@@ -798,6 +798,12 @@ static int rawv6_sendmsg(struct kiocb *iocb, struct sock *sk, ...@@ -798,6 +798,12 @@ static int rawv6_sendmsg(struct kiocb *iocb, struct sock *sk,
hlimit = ipv6_get_hoplimit(dst->dev); hlimit = ipv6_get_hoplimit(dst->dev);
} }
if (tclass < 0) {
tclass = np->cork.tclass;
if (tclass < 0)
tclass = 0;
}
if (msg->msg_flags&MSG_CONFIRM) if (msg->msg_flags&MSG_CONFIRM)
goto do_confirm; goto do_confirm;
...@@ -806,8 +812,9 @@ static int rawv6_sendmsg(struct kiocb *iocb, struct sock *sk, ...@@ -806,8 +812,9 @@ static int rawv6_sendmsg(struct kiocb *iocb, struct sock *sk,
err = rawv6_send_hdrinc(sk, msg->msg_iov, len, &fl, (struct rt6_info*)dst, msg->msg_flags); err = rawv6_send_hdrinc(sk, msg->msg_iov, len, &fl, (struct rt6_info*)dst, msg->msg_flags);
} else { } else {
lock_sock(sk); lock_sock(sk);
err = ip6_append_data(sk, ip_generic_getfrag, msg->msg_iov, len, 0, err = ip6_append_data(sk, ip_generic_getfrag, msg->msg_iov,
hlimit, opt, &fl, (struct rt6_info*)dst, msg->msg_flags); len, 0, hlimit, tclass, opt, &fl, (struct rt6_info*)dst,
msg->msg_flags);
if (err) if (err)
ip6_flush_pending_frames(sk); ip6_flush_pending_frames(sk);
......
...@@ -849,7 +849,7 @@ static int tcp_v6_send_synack(struct sock *sk, struct request_sock *req, ...@@ -849,7 +849,7 @@ static int tcp_v6_send_synack(struct sock *sk, struct request_sock *req,
if (dst == NULL) { if (dst == NULL) {
opt = np->opt; opt = np->opt;
if (opt == NULL && if (opt == NULL &&
np->rxopt.bits.srcrt == 2 && np->rxopt.bits.osrcrt == 2 &&
treq->pktopts) { treq->pktopts) {
struct sk_buff *pktopts = treq->pktopts; struct sk_buff *pktopts = treq->pktopts;
struct inet6_skb_parm *rxopt = IP6CB(pktopts); struct inet6_skb_parm *rxopt = IP6CB(pktopts);
...@@ -915,11 +915,10 @@ static int ipv6_opt_accepted(struct sock *sk, struct sk_buff *skb) ...@@ -915,11 +915,10 @@ static int ipv6_opt_accepted(struct sock *sk, struct sk_buff *skb)
struct inet6_skb_parm *opt = IP6CB(skb); struct inet6_skb_parm *opt = IP6CB(skb);
if (np->rxopt.all) { if (np->rxopt.all) {
if ((opt->hop && np->rxopt.bits.hopopts) || if ((opt->hop && (np->rxopt.bits.hopopts || np->rxopt.bits.ohopopts)) ||
((IPV6_FLOWINFO_MASK&*(u32*)skb->nh.raw) && ((IPV6_FLOWINFO_MASK & *(u32*)skb->nh.raw) && np->rxopt.bits.rxflow) ||
np->rxopt.bits.rxflow) || (opt->srcrt && (np->rxopt.bits.srcrt || np->rxopt.bits.osrcrt)) ||
(opt->srcrt && np->rxopt.bits.srcrt) || ((opt->dst1 || opt->dst0) && (np->rxopt.bits.dstopts || np->rxopt.bits.odstopts)))
((opt->dst1 || opt->dst0) && np->rxopt.bits.dstopts))
return 1; return 1;
} }
return 0; return 0;
...@@ -1190,8 +1189,8 @@ static int tcp_v6_conn_request(struct sock *sk, struct sk_buff *skb) ...@@ -1190,8 +1189,8 @@ static int tcp_v6_conn_request(struct sock *sk, struct sk_buff *skb)
TCP_ECN_create_request(req, skb->h.th); TCP_ECN_create_request(req, skb->h.th);
treq->pktopts = NULL; treq->pktopts = NULL;
if (ipv6_opt_accepted(sk, skb) || if (ipv6_opt_accepted(sk, skb) ||
np->rxopt.bits.rxinfo || np->rxopt.bits.rxinfo || np->rxopt.bits.rxoinfo ||
np->rxopt.bits.rxhlim) { np->rxopt.bits.rxhlim || np->rxopt.bits.rxohlim) {
atomic_inc(&skb->users); atomic_inc(&skb->users);
treq->pktopts = skb; treq->pktopts = skb;
} }
...@@ -1288,7 +1287,7 @@ static struct sock * tcp_v6_syn_recv_sock(struct sock *sk, struct sk_buff *skb, ...@@ -1288,7 +1287,7 @@ static struct sock * tcp_v6_syn_recv_sock(struct sock *sk, struct sk_buff *skb,
if (sk_acceptq_is_full(sk)) if (sk_acceptq_is_full(sk))
goto out_overflow; goto out_overflow;
if (np->rxopt.bits.srcrt == 2 && if (np->rxopt.bits.osrcrt == 2 &&
opt == NULL && treq->pktopts) { opt == NULL && treq->pktopts) {
struct inet6_skb_parm *rxopt = IP6CB(treq->pktopts); struct inet6_skb_parm *rxopt = IP6CB(treq->pktopts);
if (rxopt->srcrt) if (rxopt->srcrt)
...@@ -1544,9 +1543,9 @@ static int tcp_v6_do_rcv(struct sock *sk, struct sk_buff *skb) ...@@ -1544,9 +1543,9 @@ static int tcp_v6_do_rcv(struct sock *sk, struct sk_buff *skb)
tp = tcp_sk(sk); tp = tcp_sk(sk);
if (TCP_SKB_CB(opt_skb)->end_seq == tp->rcv_nxt && if (TCP_SKB_CB(opt_skb)->end_seq == tp->rcv_nxt &&
!((1 << sk->sk_state) & (TCPF_CLOSE | TCPF_LISTEN))) { !((1 << sk->sk_state) & (TCPF_CLOSE | TCPF_LISTEN))) {
if (np->rxopt.bits.rxinfo) if (np->rxopt.bits.rxinfo || np->rxopt.bits.rxoinfo)
np->mcast_oif = inet6_iif(opt_skb); np->mcast_oif = inet6_iif(opt_skb);
if (np->rxopt.bits.rxhlim) if (np->rxopt.bits.rxhlim || np->rxopt.bits.rxohlim)
np->mcast_hops = opt_skb->nh.ipv6h->hop_limit; np->mcast_hops = opt_skb->nh.ipv6h->hop_limit;
if (ipv6_opt_accepted(sk, opt_skb)) { if (ipv6_opt_accepted(sk, opt_skb)) {
skb_set_owner_r(opt_skb, sk); skb_set_owner_r(opt_skb, sk);
......
...@@ -637,6 +637,7 @@ static int udpv6_sendmsg(struct kiocb *iocb, struct sock *sk, ...@@ -637,6 +637,7 @@ static int udpv6_sendmsg(struct kiocb *iocb, struct sock *sk,
int addr_len = msg->msg_namelen; int addr_len = msg->msg_namelen;
int ulen = len; int ulen = len;
int hlimit = -1; int hlimit = -1;
int tclass = -1;
int corkreq = up->corkflag || msg->msg_flags&MSG_MORE; int corkreq = up->corkflag || msg->msg_flags&MSG_MORE;
int err; int err;
...@@ -758,7 +759,7 @@ static int udpv6_sendmsg(struct kiocb *iocb, struct sock *sk, ...@@ -758,7 +759,7 @@ static int udpv6_sendmsg(struct kiocb *iocb, struct sock *sk,
memset(opt, 0, sizeof(struct ipv6_txoptions)); memset(opt, 0, sizeof(struct ipv6_txoptions));
opt->tot_len = sizeof(*opt); opt->tot_len = sizeof(*opt);
err = datagram_send_ctl(msg, fl, opt, &hlimit); err = datagram_send_ctl(msg, fl, opt, &hlimit, &tclass);
if (err < 0) { if (err < 0) {
fl6_sock_release(flowlabel); fl6_sock_release(flowlabel);
return err; return err;
...@@ -773,8 +774,7 @@ static int udpv6_sendmsg(struct kiocb *iocb, struct sock *sk, ...@@ -773,8 +774,7 @@ static int udpv6_sendmsg(struct kiocb *iocb, struct sock *sk,
} }
if (opt == NULL) if (opt == NULL)
opt = np->opt; opt = np->opt;
if (flowlabel) opt = fl6_merge_options(&opt_space, flowlabel, opt);
opt = fl6_merge_options(&opt_space, flowlabel, opt);
fl->proto = IPPROTO_UDP; fl->proto = IPPROTO_UDP;
ipv6_addr_copy(&fl->fl6_dst, daddr); ipv6_addr_copy(&fl->fl6_dst, daddr);
...@@ -815,6 +815,12 @@ static int udpv6_sendmsg(struct kiocb *iocb, struct sock *sk, ...@@ -815,6 +815,12 @@ static int udpv6_sendmsg(struct kiocb *iocb, struct sock *sk,
hlimit = ipv6_get_hoplimit(dst->dev); hlimit = ipv6_get_hoplimit(dst->dev);
} }
if (tclass < 0) {
tclass = np->tclass;
if (tclass < 0)
tclass = 0;
}
if (msg->msg_flags&MSG_CONFIRM) if (msg->msg_flags&MSG_CONFIRM)
goto do_confirm; goto do_confirm;
back_from_confirm: back_from_confirm:
...@@ -834,9 +840,10 @@ static int udpv6_sendmsg(struct kiocb *iocb, struct sock *sk, ...@@ -834,9 +840,10 @@ static int udpv6_sendmsg(struct kiocb *iocb, struct sock *sk,
do_append_data: do_append_data:
up->len += ulen; up->len += ulen;
err = ip6_append_data(sk, ip_generic_getfrag, msg->msg_iov, ulen, sizeof(struct udphdr), err = ip6_append_data(sk, ip_generic_getfrag, msg->msg_iov, ulen,
hlimit, opt, fl, (struct rt6_info*)dst, sizeof(struct udphdr), hlimit, tclass, opt, fl,
corkreq ? msg->msg_flags|MSG_MORE : msg->msg_flags); (struct rt6_info*)dst,
corkreq ? msg->msg_flags|MSG_MORE : msg->msg_flags);
if (err) if (err)
udp_v6_flush_pending_frames(sk); udp_v6_flush_pending_frames(sk);
else if (!corkreq) else if (!corkreq)
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment