Browse Source

Merge master.kernel.org:/pub/scm/linux/kernel/git/davem/net-2.6

Linus Torvalds 20 years ago
parent
commit
27e2df2228

+ 4 - 3
drivers/net/bnx2.c

@@ -14,8 +14,8 @@
 
 #define DRV_MODULE_NAME		"bnx2"
 #define PFX DRV_MODULE_NAME	": "
-#define DRV_MODULE_VERSION	"1.2.20"
-#define DRV_MODULE_RELDATE	"August 22, 2005"
+#define DRV_MODULE_VERSION	"1.2.21"
+#define DRV_MODULE_RELDATE	"September 7, 2005"
 
 #define RUN_AT(x) (jiffies + (x))
 
@@ -1533,6 +1533,7 @@ bnx2_msi(int irq, void *dev_instance, struct pt_regs *regs)
 	struct net_device *dev = dev_instance;
 	struct bnx2 *bp = dev->priv;
 
+	prefetch(bp->status_blk);
 	REG_WR(bp, BNX2_PCICFG_INT_ACK_CMD,
 		BNX2_PCICFG_INT_ACK_CMD_USE_INT_HC_PARAM |
 		BNX2_PCICFG_INT_ACK_CMD_MASK_INT);
@@ -1558,7 +1559,7 @@ bnx2_interrupt(int irq, void *dev_instance, struct pt_regs *regs)
 	 * When using MSI, the MSI message will always complete after
 	 * the status block write.
 	 */
-	if ((bp->status_blk->status_idx == bp->last_status_idx) ||
+	if ((bp->status_blk->status_idx == bp->last_status_idx) &&
 	    (REG_RD(bp, BNX2_PCICFG_MISC_STATUS) &
 	     BNX2_PCICFG_MISC_STATUS_INTA_VALUE))
 		return IRQ_NONE;

+ 1 - 0
drivers/net/bnx2.h

@@ -50,6 +50,7 @@
 #endif
 #include <linux/workqueue.h>
 #include <linux/crc32.h>
+#include <linux/prefetch.h>
 
 /* Hardware data structures and register definitions automatically
  * generated from RTL code. Do not modify.

+ 30 - 6
include/linux/in6.h

@@ -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
@@ -198,4 +198,28 @@ 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
+#define IPV6_RECVTCLASS		66
+#define IPV6_TCLASS		67
+
 #endif

+ 12 - 3
include/linux/ipv6.h

@@ -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))
@@ -234,14 +235,20 @@ 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,
-                                rxflow:1;
+				odstopts:1,
+                                rxflow:1,
+				rxtclass:1;
 		} bits;
-		__u8		all;
+		__u16		all;
 	} rxopt;
 
 	/* sockopt flags */
@@ -250,6 +257,7 @@ struct ipv6_pinfo {
 	                        sndflow:1,
 				pmtudisc:2,
 				ipv6only:1;
+	__u8			tclass;
 
 	__u32			dst_cookie;
 
@@ -263,6 +271,7 @@ struct ipv6_pinfo {
 		struct ipv6_txoptions *opt;
 		struct rt6_info	*rt;
 		int hop_limit;
+		int tclass;
 	} cork;
 };
 

+ 1 - 1
include/linux/skbuff.h

@@ -1167,7 +1167,7 @@ static inline void skb_postpull_rcsum(struct sk_buff *skb,
 
 static inline int pskb_trim_rcsum(struct sk_buff *skb, unsigned int len)
 {
-	if (len >= skb->len)
+	if (likely(len >= skb->len))
 		return 0;
 	if (skb->ip_summed == CHECKSUM_HW)
 		skb->ip_summed = CHECKSUM_NONE;

+ 1 - 1
include/net/ax25.h

@@ -258,7 +258,7 @@ extern struct sock *ax25_make_new(struct sock *, struct ax25_dev *);
 /* ax25_addr.c */
 extern ax25_address null_ax25_address;
 extern char *ax2asc(char *buf, ax25_address *);
-extern ax25_address *asc2ax(char *);
+extern void asc2ax(ax25_address *addr, char *callsign);
 extern int  ax25cmp(ax25_address *, ax25_address *);
 extern int  ax25digicmp(ax25_digi *, ax25_digi *);
 extern unsigned char *ax25_addr_parse(unsigned char *, int, ax25_address *, ax25_address *, ax25_digi *, int *, int *);

+ 3 - 2
include/net/compat.h

@@ -33,7 +33,8 @@ extern asmlinkage long compat_sys_sendmsg(int,struct compat_msghdr __user *,unsi
 extern asmlinkage long compat_sys_recvmsg(int,struct compat_msghdr __user *,unsigned);
 extern asmlinkage long compat_sys_getsockopt(int, int, int, char __user *, int __user *);
 extern int put_cmsg_compat(struct msghdr*, int, int, int, void *);
-extern int cmsghdr_from_user_compat_to_kern(struct msghdr *, struct sock *, unsigned char *,
-		int);
+
+struct sock;
+extern int cmsghdr_from_user_compat_to_kern(struct msghdr *, struct sock *, unsigned char *, int);
 
 #endif /* NET_COMPAT_H */

+ 5 - 0
include/net/ipv6.h

@@ -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;
@@ -373,6 +377,7 @@ extern int			ip6_append_data(struct sock *sk,
 						int length,
 						int transhdrlen,
 		      				int hlimit,
+		      				int tclass,
 						struct ipv6_txoptions *opt,
 						struct flowi *fl,
 						struct rt6_info *rt,

+ 1 - 1
include/net/transp_v6.h

@@ -37,7 +37,7 @@ extern int			datagram_recv_ctl(struct sock *sk,
 extern int			datagram_send_ctl(struct msghdr *msg,
 						  struct flowi *fl,
 						  struct ipv6_txoptions *opt,
-						  int *hlimit);
+						  int *hlimit, int *tclass);
 
 #define		LOOPBACK4_IPV6		__constant_htonl(0x7f000006)
 

+ 12 - 15
net/ax25/ax25_addr.c

@@ -67,37 +67,34 @@ char *ax2asc(char *buf, ax25_address *a)
 /*
  *	ascii -> ax25 conversion
  */
-ax25_address *asc2ax(char *callsign)
+void asc2ax(ax25_address *addr, char *callsign)
 {
-	static ax25_address addr;
 	char *s;
 	int n;
 
 	for (s = callsign, n = 0; n < 6; n++) {
 		if (*s != '\0' && *s != '-')
-			addr.ax25_call[n] = *s++;
+			addr->ax25_call[n] = *s++;
 		else
-			addr.ax25_call[n] = ' ';
-		addr.ax25_call[n] <<= 1;
-		addr.ax25_call[n] &= 0xFE;
+			addr->ax25_call[n] = ' ';
+		addr->ax25_call[n] <<= 1;
+		addr->ax25_call[n] &= 0xFE;
 	}
 
 	if (*s++ == '\0') {
-		addr.ax25_call[6] = 0x00;
-		return &addr;
+		addr->ax25_call[6] = 0x00;
+		return;
 	}
 
-	addr.ax25_call[6] = *s++ - '0';
+	addr->ax25_call[6] = *s++ - '0';
 
 	if (*s != '\0') {
-		addr.ax25_call[6] *= 10;
-		addr.ax25_call[6] += *s++ - '0';
+		addr->ax25_call[6] *= 10;
+		addr->ax25_call[6] += *s++ - '0';
 	}
 
-	addr.ax25_call[6] <<= 1;
-	addr.ax25_call[6] &= 0x1E;
-
-	return &addr;
+	addr->ax25_call[6] <<= 1;
+	addr->ax25_call[6] &= 0x1E;
 }
 
 /*

+ 20 - 4
net/ipv4/netfilter/ip_conntrack_netbios_ns.c

@@ -104,12 +104,28 @@ out:
 static struct ip_conntrack_helper helper = {
 	.name			= "netbios-ns",
 	.tuple = {
-		.src.u.udp.port	= __constant_htons(137),
-		.dst.protonum	= IPPROTO_UDP,
+		.src = {
+			.u = {
+				.udp = {
+					.port	= __constant_htons(137),
+				}
+			}
+		},
+		.dst = {
+			.protonum	= IPPROTO_UDP,
+		},
 	},
 	.mask = {
-		.src.u.udp.port	= 0xFFFF,
-		.dst.protonum	= 0xFF,
+		.src = {
+			.u = {
+				.udp = {
+					.port	= 0xFFFF,
+				}
+			}
+		},
+		.dst = {
+			.protonum	= 0xFF,
+		},
 	},
 	.max_expected		= 1,
 	.me			= THIS_MODULE,

+ 1 - 4
net/ipv4/netfilter/ipt_REJECT.c

@@ -92,10 +92,7 @@ static inline struct rtable *route_reverse(struct sk_buff *skb,
 	fl.fl_ip_sport = tcph->dest;
 	fl.fl_ip_dport = tcph->source;
 
-	if (xfrm_lookup((struct dst_entry **)&rt, &fl, NULL, 0)) {
-		dst_release(&rt->u.dst);
-		rt = NULL;
-	}
+	xfrm_lookup((struct dst_entry **)&rt, &fl, NULL, 0);
 
 	return rt;
 }

+ 14 - 15
net/ipv4/route.c

@@ -1760,6 +1760,7 @@ static inline int __mkroute_input(struct sk_buff *skb,
 		goto cleanup;
 	}
 
+	atomic_set(&rth->u.dst.__refcnt, 1);
 	rth->u.dst.flags= DST_HOST;
 #ifdef CONFIG_IP_ROUTE_MULTIPATH_CACHED
 	if (res->fi->fib_nhs > 1)
@@ -1820,7 +1821,6 @@ static inline int ip_mkroute_input_def(struct sk_buff *skb,
 	err = __mkroute_input(skb, res, in_dev, daddr, saddr, tos, &rth);
 	if (err)
 		return err;
-	atomic_set(&rth->u.dst.__refcnt, 1);
 
 	/* put it into the cache */
 	hash = rt_hash_code(daddr, saddr ^ (fl->iif << 5), tos);
@@ -1834,8 +1834,8 @@ static inline int ip_mkroute_input(struct sk_buff *skb,
 				   u32 daddr, u32 saddr, u32 tos)
 {
 #ifdef CONFIG_IP_ROUTE_MULTIPATH_CACHED
-	struct rtable* rth = NULL;
-	unsigned char hop, hopcount, lasthop;
+	struct rtable* rth = NULL, *rtres;
+	unsigned char hop, hopcount;
 	int err = -EINVAL;
 	unsigned int hash;
 
@@ -1844,8 +1844,6 @@ static inline int ip_mkroute_input(struct sk_buff *skb,
 	else
 		hopcount = 1;
 
-	lasthop = hopcount - 1;
-
 	/* distinguish between multipath and singlepath */
 	if (hopcount < 2)
 		return ip_mkroute_input_def(skb, res, fl, in_dev, daddr,
@@ -1855,6 +1853,10 @@ static inline int ip_mkroute_input(struct sk_buff *skb,
 	for (hop = 0; hop < hopcount; hop++) {
 		res->nh_sel = hop;
 
+		/* put reference to previous result */
+		if (hop)
+			ip_rt_put(rtres);
+
 		/* create a routing cache entry */
 		err = __mkroute_input(skb, res, in_dev, daddr, saddr, tos,
 				      &rth);
@@ -1863,7 +1865,7 @@ static inline int ip_mkroute_input(struct sk_buff *skb,
 
 		/* put it into the cache */
 		hash = rt_hash_code(daddr, saddr ^ (fl->iif << 5), tos);
-		err = rt_intern_hash(hash, rth, (struct rtable**)&skb->dst);
+		err = rt_intern_hash(hash, rth, &rtres);
 		if (err)
 			return err;
 
@@ -1873,13 +1875,8 @@ static inline int ip_mkroute_input(struct sk_buff *skb,
 				     FIB_RES_NETMASK(*res),
 				     res->prefixlen,
 				     &FIB_RES_NH(*res));
-
-		/* only for the last hop the reference count is handled
-		 * outside
-		 */
-		if (hop == lasthop)
-			atomic_set(&(skb->dst->__refcnt), 1);
 	}
+	skb->dst = &rtres->u.dst;
 	return err;
 #else /* CONFIG_IP_ROUTE_MULTIPATH_CACHED  */
 	return ip_mkroute_input_def(skb, res, fl, in_dev, daddr, saddr, tos);
@@ -2208,6 +2205,7 @@ static inline int __mkroute_output(struct rtable **result,
 		goto cleanup;
 	}		
 
+	atomic_set(&rth->u.dst.__refcnt, 1);
 	rth->u.dst.flags= DST_HOST;
 #ifdef CONFIG_IP_ROUTE_MULTIPATH_CACHED
 	if (res->fi) {
@@ -2290,8 +2288,6 @@ static inline int ip_mkroute_output_def(struct rtable **rp,
 	if (err == 0) {
 		u32 tos = RT_FL_TOS(oldflp);
 
-		atomic_set(&rth->u.dst.__refcnt, 1);
-		
 		hash = rt_hash_code(oldflp->fl4_dst, 
 				    oldflp->fl4_src ^ (oldflp->oif << 5), tos);
 		err = rt_intern_hash(hash, rth, rp);
@@ -2326,6 +2322,10 @@ static inline int ip_mkroute_output(struct rtable** rp,
 			dev2nexthop = FIB_RES_DEV(*res);
 			dev_hold(dev2nexthop);
 
+			/* put reference to previous result */
+			if (hop)
+				ip_rt_put(*rp);
+
 			err = __mkroute_output(&rth, res, fl, oldflp,
 					       dev2nexthop, flags);
 
@@ -2350,7 +2350,6 @@ static inline int ip_mkroute_output(struct rtable** rp,
 			if (err != 0)
 				return err;
 		}
-		atomic_set(&(*rp)->u.dst.__refcnt, 1);
 		return err;
 	} else {
 		return ip_mkroute_output_def(rp, res, fl, oldflp, dev_out,

+ 1 - 1
net/ipv4/tcp_output.c

@@ -499,7 +499,7 @@ int tcp_fragment(struct sock *sk, struct sk_buff *skb, u32 len, unsigned int mss
 	/* If this packet has been sent out already, we must
 	 * adjust the various packet counters.
 	 */
-	if (after(tp->snd_nxt, TCP_SKB_CB(buff)->end_seq)) {
+	if (!before(tp->snd_nxt, TCP_SKB_CB(buff)->end_seq)) {
 		int diff = old_factor - tcp_skb_pcount(skb) -
 			tcp_skb_pcount(buff);
 

+ 1 - 1
net/ipv4/udp.c

@@ -1141,7 +1141,7 @@ int udp_rcv(struct sk_buff *skb)
 	if (ulen > len || ulen < sizeof(*uh))
 		goto short_packet;
 
-	if (pskb_trim(skb, ulen))
+	if (pskb_trim_rcsum(skb, ulen))
 		goto short_packet;
 
 	if (udp_checksum_init(skb, uh, ulen, saddr, daddr) < 0)

+ 127 - 12
net/ipv6/datagram.c

@@ -175,10 +175,8 @@ ipv4_connected:
 	if (final_p)
 		ipv6_addr_copy(&fl.fl6_dst, final_p);
 
-	if ((err = xfrm_lookup(&dst, &fl, sk, 0)) < 0) {
-		dst_release(dst);
+	if ((err = xfrm_lookup(&dst, &fl, sk, 0)) < 0)
 		goto out;
-	}
 
 	/* source address lookup done in ip6_dst_lookup */
 
@@ -390,32 +388,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);
 	}
 
+	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)) {
 		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;
 }
 
 int datagram_send_ctl(struct msghdr *msg, struct flowi *fl,
 		      struct ipv6_txoptions *opt,
-		      int *hlimit)
+		      int *hlimit, int *tclass)
 {
 	struct in6_pktinfo *src_info;
 	struct cmsghdr *cmsg;
@@ -438,6 +505,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 +560,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 +581,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 +605,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 +664,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 +675,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;
@@ -588,6 +685,24 @@ int datagram_send_ctl(struct msghdr *msg, struct flowi *fl,
 			*hlimit = *(int *)CMSG_DATA(cmsg);
 			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:
 			LIMIT_NETDEBUG(KERN_DEBUG "invalid cmsg type: %d\n",
 			               cmsg->cmsg_type);

+ 108 - 8
net/ipv6/exthdrs.c

@@ -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)) {
@@ -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;
@@ -459,11 +461,10 @@ static int ipv6_hop_jumbo(struct sk_buff *skb, int optoff)
 		IP6_INC_STATS_BH(IPSTATS_MIB_INTRUNCATEDPKTS);
 		goto drop;
 	}
-	if (pkt_len + sizeof(struct ipv6hdr) < skb->len) {
-		__pskb_trim(skb, pkt_len + sizeof(struct ipv6hdr));
-		if (skb->ip_summed == CHECKSUM_HW)
-			skb->ip_summed = CHECKSUM_NONE;
-	}
+
+	if (pskb_trim_rcsum(skb, pkt_len + sizeof(struct ipv6hdr)))
+		goto drop;
+
 	return 1;
 
 drop:
@@ -539,10 +540,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);
 }
@@ -573,3 +579,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);
+}
+

+ 14 - 6
net/ipv6/icmp.c

@@ -287,7 +287,7 @@ void icmpv6_send(struct sk_buff *skb, int type, int code, __u32 info,
 	int iif = 0;
 	int addr_type = 0;
 	int len;
-	int hlimit;
+	int hlimit, tclass;
 	int err = 0;
 
 	if ((u8*)hdr < skb->head || (u8*)(hdr+1) > skb->tail)
@@ -374,7 +374,7 @@ void icmpv6_send(struct sk_buff *skb, int type, int code, __u32 info,
 	if (err)
 		goto out;
 	if ((err = xfrm_lookup(&dst, &fl, sk, 0)) < 0)
-		goto out_dst_release;
+		goto out;
 
 	if (ipv6_addr_is_multicast(&fl.fl6_dst))
 		hlimit = np->mcast_hops;
@@ -385,6 +385,10 @@ void icmpv6_send(struct sk_buff *skb, int type, int code, __u32 info,
 	if (hlimit < 0)
 		hlimit = ipv6_get_hoplimit(dst->dev);
 
+	tclass = np->cork.tclass;
+	if (tclass < 0)
+		tclass = 0;
+
 	msg.skb = skb;
 	msg.offset = skb->nh.raw - skb->data;
 
@@ -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,
 			      len + sizeof(struct icmp6hdr),
 			      sizeof(struct icmp6hdr),
-			      hlimit, NULL, &fl, (struct rt6_info*)dst,
+			      hlimit, tclass, NULL, &fl, (struct rt6_info*)dst,
 			      MSG_DONTWAIT);
 	if (err) {
 		ip6_flush_pending_frames(sk);
@@ -434,6 +438,7 @@ static void icmpv6_echo_reply(struct sk_buff *skb)
 	struct dst_entry *dst;
 	int err = 0;
 	int hlimit;
+	int tclass;
 
 	saddr = &skb->nh.ipv6h->daddr;
 
@@ -464,7 +469,7 @@ static void icmpv6_echo_reply(struct sk_buff *skb)
 	if (err)
 		goto out;
 	if ((err = xfrm_lookup(&dst, &fl, sk, 0)) < 0)
-		goto out_dst_release;
+		goto out;
 
 	if (ipv6_addr_is_multicast(&fl.fl6_dst))
 		hlimit = np->mcast_hops;
@@ -475,13 +480,17 @@ static void icmpv6_echo_reply(struct sk_buff *skb)
 	if (hlimit < 0)
 		hlimit = ipv6_get_hoplimit(dst->dev);
 
+	tclass = np->cork.tclass;
+	if (tclass < 0)
+		tclass = 0;
+
 	idev = in6_dev_get(skb->dev);
 
 	msg.skb = skb;
 	msg.offset = 0;
 
 	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);
 
 	if (err) {
@@ -496,7 +505,6 @@ static void icmpv6_echo_reply(struct sk_buff *skb)
 out_put: 
 	if (likely(idev != NULL))
 		in6_dev_put(idev);
-out_dst_release:
 	dst_release(dst);
 out: 
 	icmpv6_xmit_unlock();

+ 9 - 5
net/ipv6/ip6_flowlabel.c

@@ -225,16 +225,20 @@ struct ipv6_txoptions *fl6_merge_options(struct ipv6_txoptions * opt_space,
 					 struct ip6_flowlabel * fl,
 					 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)
-		return fl_opt;
+	if (fopt == NULL || fopt->opt_flen == 0) {
+		if (!fl_opt || !fl_opt->dst0opt || fl_opt->srcrt)
+			return fl_opt;
+	}
 
 	if (fl_opt != NULL) {
 		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->opt_nflen = fl_opt->opt_nflen;
+		if (fl_opt->dst0opt && !fl_opt->srcrt)
+			opt_space->opt_nflen -= ipv6_optlen(fl_opt->dst0opt);
 	} else {
 		if (fopt->opt_nflen == 0)
 			return fopt;
@@ -310,7 +314,7 @@ fl_create(struct in6_flowlabel_req *freq, char __user *optval, int optlen, int *
 		msg.msg_control = (void*)(fl->opt+1);
 		flowi.oif = 0;
 
-		err = datagram_send_ctl(&msg, &flowi, fl->opt, &junk);
+		err = datagram_send_ctl(&msg, &flowi, fl->opt, &junk, &junk);
 		if (err)
 			goto done;
 		err = -EINVAL;

+ 17 - 7
net/ipv6/ip6_output.c

@@ -166,7 +166,7 @@ int ip6_xmit(struct sock *sk, struct sk_buff *skb, struct flowi *fl,
 	struct ipv6hdr *hdr;
 	u8  proto = fl->proto;
 	int seg_len = skb->len;
-	int hlimit;
+	int hlimit, tclass;
 	u32 mtu;
 
 	if (opt) {
@@ -202,7 +202,6 @@ int ip6_xmit(struct sock *sk, struct sk_buff *skb, struct flowi *fl,
 	 *	Fill in the IPv6 header
 	 */
 
-	*(u32*)hdr = htonl(0x60000000) | fl->fl6_flowlabel;
 	hlimit = -1;
 	if (np)
 		hlimit = np->hop_limit;
@@ -211,6 +210,14 @@ int ip6_xmit(struct sock *sk, struct sk_buff *skb, struct flowi *fl,
 	if (hlimit < 0)
 		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->nexthdr = proto;
 	hdr->hop_limit = hlimit;
@@ -762,10 +769,11 @@ out_err_release:
 	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),
-		    void *from, int length, int transhdrlen,
-		    int hlimit, struct ipv6_txoptions *opt, struct flowi *fl, struct rt6_info *rt,
-		    unsigned int flags)
+int ip6_append_data(struct sock *sk, int getfrag(void *from, char *to,
+	int offset, int len, int odd, struct sk_buff *skb),
+	void *from, int length, int transhdrlen,
+	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 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
 		np->cork.rt = rt;
 		inet->cork.fl = *fl;
 		np->cork.hop_limit = hlimit;
+		np->cork.tclass = tclass;
 		inet->cork.fragsize = mtu = dst_mtu(rt->u.dst.path);
 		if (dst_allfrag(rt->u.dst.path))
 			inet->cork.flags |= IPCORK_ALLFRAG;
@@ -1084,7 +1093,8 @@ int ip6_push_pending_frames(struct sock *sk)
 
 	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)
 		hdr->payload_len = htons(skb->len - sizeof(struct ipv6hdr));

+ 4 - 3
net/ipv6/ip6_tunnel.c

@@ -673,11 +673,12 @@ ip6ip6_tnl_xmit(struct sk_buff *skb, struct net_device *dev)
 
 	if ((dst = ip6_tnl_dst_check(t)) != NULL)
 		dst_hold(dst);
-	else
+	else {
 		dst = ip6_route_output(NULL, &fl);
 
-	if (dst->error || xfrm_lookup(&dst, &fl, NULL, 0) < 0)
-		goto tx_err_link_failure;
+		if (dst->error || xfrm_lookup(&dst, &fl, NULL, 0) < 0)
+			goto tx_err_link_failure;
+	}
 
 	tdev = dst->dev;
 

+ 174 - 12
net/ipv6/ipv6_sockglue.c

@@ -210,39 +210,139 @@ 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_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:
 		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;
@@ -276,7 +376,7 @@ int ipv6_setsockopt(struct sock *sk, int level, int optname,
 		msg.msg_controllen = optlen;
 		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)
 			goto done;
 update:
@@ -529,6 +629,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 +678,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 +712,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 +746,67 @@ 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_TCLASS:
+		val = np->tclass;
+		break;
+
+	case IPV6_RECVTCLASS:
+		val = np->rxopt.bits.rxtclass;
+		break;
+
 	case IPV6_FLOWINFO:
 		val = np->rxopt.bits.rxflow;
 		break;

+ 4 - 12
net/ipv6/ndisc.c

@@ -447,10 +447,8 @@ static void ndisc_send_na(struct net_device *dev, struct neighbour *neigh,
 		return;
 
 	err = xfrm_lookup(&dst, &fl, NULL, 0);
-	if (err < 0) {
-		dst_release(dst);
+	if (err < 0)
 		return;
-	}
 
 	if (inc_opt) {
 		if (dev->addr_len)
@@ -539,10 +537,8 @@ void ndisc_send_ns(struct net_device *dev, struct neighbour *neigh,
 		return;
 
 	err = xfrm_lookup(&dst, &fl, NULL, 0);
-	if (err < 0) {
-		dst_release(dst);
+	if (err < 0)
 		return;
-	}
 
 	len = sizeof(struct icmp6hdr) + sizeof(struct in6_addr);
 	send_llinfo = dev->addr_len && !ipv6_addr_any(saddr);
@@ -616,10 +612,8 @@ void ndisc_send_rs(struct net_device *dev, struct in6_addr *saddr,
 		return;
 
 	err = xfrm_lookup(&dst, &fl, NULL, 0);
-	if (err < 0) {
-		dst_release(dst);
+	if (err < 0)
 		return;
-	}
 
 	len = sizeof(struct icmp6hdr);
 	if (dev->addr_len)
@@ -1353,10 +1347,8 @@ void ndisc_send_redirect(struct sk_buff *skb, struct neighbour *neigh,
 		return;
 
 	err = xfrm_lookup(&dst, &fl, NULL, 0);
-	if (err) {
-		dst_release(dst);
+	if (err)
 		return;
-	}
 
 	rt = (struct rt6_info *) dst;
 

+ 1 - 4
net/ipv6/netfilter/ip6t_REJECT.c

@@ -100,11 +100,8 @@ static void send_reset(struct sk_buff *oldskb)
 	dst = ip6_route_output(NULL, &fl);
 	if (dst == NULL)
 		return;
-	if (dst->error ||
-	    xfrm_lookup(&dst, &fl, NULL, 0)) {
-		dst_release(dst);
+	if (dst->error || xfrm_lookup(&dst, &fl, NULL, 0))
 		return;
-	}
 
 	hh_len = (dst->dev->hard_header_len + 15)&~15;
 	nskb = alloc_skb(hh_len + 15 + dst->header_len + sizeof(struct ipv6hdr)

+ 13 - 8
net/ipv6/raw.c

@@ -655,6 +655,7 @@ static int rawv6_sendmsg(struct kiocb *iocb, struct sock *sk,
 	struct flowi fl;
 	int addr_len = msg->msg_namelen;
 	int hlimit = -1;
+	int tclass = -1;
 	u16 proto;
 	int err;
 
@@ -740,7 +741,7 @@ static int rawv6_sendmsg(struct kiocb *iocb, struct sock *sk,
 		memset(opt, 0, 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) {
 			fl6_sock_release(flowlabel);
 			return err;
@@ -755,8 +756,7 @@ static int rawv6_sendmsg(struct kiocb *iocb, struct sock *sk,
 	}
 	if (opt == NULL)
 		opt = np->opt;
-	if (flowlabel)
-		opt = fl6_merge_options(&opt_space, flowlabel, opt);
+	opt = fl6_merge_options(&opt_space, flowlabel, opt);
 
 	fl.proto = proto;
 	rawv6_probe_proto_opt(&fl, msg);
@@ -782,10 +782,8 @@ static int rawv6_sendmsg(struct kiocb *iocb, struct sock *sk,
 	if (final_p)
 		ipv6_addr_copy(&fl.fl6_dst, final_p);
 
-	if ((err = xfrm_lookup(&dst, &fl, sk, 0)) < 0) {
-		dst_release(dst);
+	if ((err = xfrm_lookup(&dst, &fl, sk, 0)) < 0)
 		goto out;
-	}
 
 	if (hlimit < 0) {
 		if (ipv6_addr_is_multicast(&fl.fl6_dst))
@@ -798,6 +796,12 @@ static int rawv6_sendmsg(struct kiocb *iocb, struct sock *sk,
 			hlimit = ipv6_get_hoplimit(dst->dev);
 	}
 
+	if (tclass < 0) {
+		tclass = np->cork.tclass;
+		if (tclass < 0)
+			tclass = 0;
+	}
+
 	if (msg->msg_flags&MSG_CONFIRM)
 		goto do_confirm;
 
@@ -806,8 +810,9 @@ back_from_confirm:
 		err = rawv6_send_hdrinc(sk, msg->msg_iov, len, &fl, (struct rt6_info*)dst, msg->msg_flags);
 	} else {
 		lock_sock(sk);
-		err = ip6_append_data(sk, ip_generic_getfrag, msg->msg_iov, len, 0,
-					hlimit, opt, &fl, (struct rt6_info*)dst, msg->msg_flags);
+		err = ip6_append_data(sk, ip_generic_getfrag, msg->msg_iov,
+			len, 0, hlimit, tclass, opt, &fl, (struct rt6_info*)dst,
+			msg->msg_flags);
 
 		if (err)
 			ip6_flush_pending_frames(sk);

+ 3 - 6
net/ipv6/reassembly.c

@@ -479,12 +479,9 @@ static void ip6_frag_queue(struct frag_queue *fq, struct sk_buff *skb,
 	/* Point into the IP datagram 'data' part. */
 	if (!pskb_pull(skb, (u8 *) (fhdr + 1) - skb->data))
 		goto err;
-	if (end-offset < skb->len) {
-		if (pskb_trim(skb, end - offset))
-			goto err;
-		if (skb->ip_summed != CHECKSUM_UNNECESSARY)
-			skb->ip_summed = CHECKSUM_NONE;
-	}
+	
+	if (pskb_trim_rcsum(skb, end - offset))
+		goto err;
 
 	/* Find out which fragments are in front and at the back of us
 	 * in the chain of fragments so far.  We must know where to put

+ 13 - 23
net/ipv6/tcp_ipv6.c

@@ -632,10 +632,8 @@ static int tcp_v6_connect(struct sock *sk, struct sockaddr *uaddr,
 	if (final_p)
 		ipv6_addr_copy(&fl.fl6_dst, final_p);
 
-	if ((err = xfrm_lookup(&dst, &fl, sk, 0)) < 0) {
-		dst_release(dst);
+	if ((err = xfrm_lookup(&dst, &fl, sk, 0)) < 0)
 		goto failure;
-	}
 
 	if (saddr == NULL) {
 		saddr = &fl.fl6_src;
@@ -849,7 +847,7 @@ static int tcp_v6_send_synack(struct sock *sk, struct request_sock *req,
 	if (dst == NULL) {
 		opt = np->opt;
 		if (opt == NULL &&
-		    np->rxopt.bits.srcrt == 2 &&
+		    np->rxopt.bits.osrcrt == 2 &&
 		    treq->pktopts) {
 			struct sk_buff *pktopts = treq->pktopts;
 			struct inet6_skb_parm *rxopt = IP6CB(pktopts);
@@ -888,7 +886,6 @@ static int tcp_v6_send_synack(struct sock *sk, struct request_sock *req,
 	}
 
 done:
-	dst_release(dst);
         if (opt && opt != np->opt)
 		sock_kfree_s(sk, opt, opt->tot_len);
 	return err;
@@ -915,11 +912,10 @@ static int ipv6_opt_accepted(struct sock *sk, struct sk_buff *skb)
 	struct inet6_skb_parm *opt = IP6CB(skb);
 
 	if (np->rxopt.all) {
-		if ((opt->hop && np->rxopt.bits.hopopts) ||
-		    ((IPV6_FLOWINFO_MASK&*(u32*)skb->nh.raw) &&
-		     np->rxopt.bits.rxflow) ||
-		    (opt->srcrt && np->rxopt.bits.srcrt) ||
-		    ((opt->dst1 || opt->dst0) && np->rxopt.bits.dstopts))
+		if ((opt->hop && (np->rxopt.bits.hopopts || np->rxopt.bits.ohopopts)) ||
+		    ((IPV6_FLOWINFO_MASK & *(u32*)skb->nh.raw) && np->rxopt.bits.rxflow) ||
+		    (opt->srcrt && (np->rxopt.bits.srcrt || np->rxopt.bits.osrcrt)) ||
+		    ((opt->dst1 || opt->dst0) && (np->rxopt.bits.dstopts || np->rxopt.bits.odstopts)))
 			return 1;
 	}
 	return 0;
@@ -1001,10 +997,8 @@ static void tcp_v6_send_reset(struct sk_buff *skb)
 	/* sk = NULL, but it is safe for now. RST socket required. */
 	if (!ip6_dst_lookup(NULL, &buff->dst, &fl)) {
 
-		if ((xfrm_lookup(&buff->dst, &fl, NULL, 0)) < 0) {
-			dst_release(buff->dst);
+		if ((xfrm_lookup(&buff->dst, &fl, NULL, 0)) < 0)
 			return;
-		}
 
 		ip6_xmit(NULL, buff, &fl, NULL, 0);
 		TCP_INC_STATS_BH(TCP_MIB_OUTSEGS);
@@ -1068,10 +1062,8 @@ static void tcp_v6_send_ack(struct sk_buff *skb, u32 seq, u32 ack, u32 win, u32
 	fl.fl_ip_sport = t1->source;
 
 	if (!ip6_dst_lookup(NULL, &buff->dst, &fl)) {
-		if ((xfrm_lookup(&buff->dst, &fl, NULL, 0)) < 0) {
-			dst_release(buff->dst);
+		if ((xfrm_lookup(&buff->dst, &fl, NULL, 0)) < 0)
 			return;
-		}
 		ip6_xmit(NULL, buff, &fl, NULL, 0);
 		TCP_INC_STATS_BH(TCP_MIB_OUTSEGS);
 		return;
@@ -1190,8 +1182,8 @@ static int tcp_v6_conn_request(struct sock *sk, struct sk_buff *skb)
 	TCP_ECN_create_request(req, skb->h.th);
 	treq->pktopts = NULL;
 	if (ipv6_opt_accepted(sk, skb) ||
-	    np->rxopt.bits.rxinfo ||
-	    np->rxopt.bits.rxhlim) {
+	    np->rxopt.bits.rxinfo || np->rxopt.bits.rxoinfo ||
+	    np->rxopt.bits.rxhlim || np->rxopt.bits.rxohlim) {
 		atomic_inc(&skb->users);
 		treq->pktopts = skb;
 	}
@@ -1288,7 +1280,7 @@ static struct sock * tcp_v6_syn_recv_sock(struct sock *sk, struct sk_buff *skb,
 	if (sk_acceptq_is_full(sk))
 		goto out_overflow;
 
-	if (np->rxopt.bits.srcrt == 2 &&
+	if (np->rxopt.bits.osrcrt == 2 &&
 	    opt == NULL && treq->pktopts) {
 		struct inet6_skb_parm *rxopt = IP6CB(treq->pktopts);
 		if (rxopt->srcrt)
@@ -1544,9 +1536,9 @@ ipv6_pktoptions:
 	tp = tcp_sk(sk);
 	if (TCP_SKB_CB(opt_skb)->end_seq == tp->rcv_nxt &&
 	    !((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);
-		if (np->rxopt.bits.rxhlim)
+		if (np->rxopt.bits.rxhlim || np->rxopt.bits.rxohlim)
 			np->mcast_hops = opt_skb->nh.ipv6h->hop_limit;
 		if (ipv6_opt_accepted(sk, opt_skb)) {
 			skb_set_owner_r(opt_skb, sk);
@@ -1734,7 +1726,6 @@ static int tcp_v6_rebuild_header(struct sock *sk)
 
 		if ((err = xfrm_lookup(&dst, &fl, sk, 0)) < 0) {
 			sk->sk_err_soft = -err;
-			dst_release(dst);
 			return err;
 		}
 
@@ -1787,7 +1778,6 @@ static int tcp_v6_xmit(struct sk_buff *skb, int ipfragok)
 
 		if ((err = xfrm_lookup(&dst, &fl, sk, 0)) < 0) {
 			sk->sk_route_caps = 0;
-			dst_release(dst);
 			return err;
 		}
 

+ 15 - 10
net/ipv6/udp.c

@@ -483,7 +483,7 @@ static int udpv6_rcv(struct sk_buff **pskb, unsigned int *nhoffp)
 	}
 
 	if (ulen < skb->len) {
-		if (__pskb_trim(skb, ulen))
+		if (pskb_trim_rcsum(skb, ulen))
 			goto discard;
 		saddr = &skb->nh.ipv6h->saddr;
 		daddr = &skb->nh.ipv6h->daddr;
@@ -637,6 +637,7 @@ static int udpv6_sendmsg(struct kiocb *iocb, struct sock *sk,
 	int addr_len = msg->msg_namelen;
 	int ulen = len;
 	int hlimit = -1;
+	int tclass = -1;
 	int corkreq = up->corkflag || msg->msg_flags&MSG_MORE;
 	int err;
 
@@ -758,7 +759,7 @@ do_udp_sendmsg:
 		memset(opt, 0, sizeof(struct ipv6_txoptions));
 		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) {
 			fl6_sock_release(flowlabel);
 			return err;
@@ -773,8 +774,7 @@ do_udp_sendmsg:
 	}
 	if (opt == NULL)
 		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;
 	ipv6_addr_copy(&fl->fl6_dst, daddr);
@@ -799,10 +799,8 @@ do_udp_sendmsg:
 	if (final_p)
 		ipv6_addr_copy(&fl->fl6_dst, final_p);
 
-	if ((err = xfrm_lookup(&dst, fl, sk, 0)) < 0) {
-		dst_release(dst);
+	if ((err = xfrm_lookup(&dst, fl, sk, 0)) < 0)
 		goto out;
-	}
 
 	if (hlimit < 0) {
 		if (ipv6_addr_is_multicast(&fl->fl6_dst))
@@ -815,6 +813,12 @@ do_udp_sendmsg:
 			hlimit = ipv6_get_hoplimit(dst->dev);
 	}
 
+	if (tclass < 0) {
+		tclass = np->tclass;
+		if (tclass < 0)
+			tclass = 0;
+	}
+
 	if (msg->msg_flags&MSG_CONFIRM)
 		goto do_confirm;
 back_from_confirm:
@@ -834,9 +838,10 @@ back_from_confirm:
 
 do_append_data:
 	up->len += ulen;
-	err = ip6_append_data(sk, ip_generic_getfrag, msg->msg_iov, ulen, sizeof(struct udphdr),
-			      hlimit, opt, fl, (struct rt6_info*)dst,
-			      corkreq ? msg->msg_flags|MSG_MORE : msg->msg_flags);
+	err = ip6_append_data(sk, ip_generic_getfrag, msg->msg_iov, ulen,
+		sizeof(struct udphdr), hlimit, tclass, opt, fl,
+		(struct rt6_info*)dst,
+		corkreq ? msg->msg_flags|MSG_MORE : msg->msg_flags);
 	if (err)
 		udp_v6_flush_pending_frames(sk);
 	else if (!corkreq)

+ 2 - 2
net/rose/rose_subr.c

@@ -337,13 +337,13 @@ static int rose_parse_ccitt(unsigned char *p, struct rose_facilities_struct *fac
 				memcpy(&facilities->source_addr, p + 7, ROSE_ADDR_LEN);
 				memcpy(callsign, p + 12,   l - 10);
 				callsign[l - 10] = '\0';
-				facilities->source_call = *asc2ax(callsign);
+				asc2ax(&facilities->source_call, callsign);
 			}
 			if (*p == FAC_CCITT_SRC_NSAP) {
 				memcpy(&facilities->dest_addr, p + 7, ROSE_ADDR_LEN);
 				memcpy(callsign, p + 12, l - 10);
 				callsign[l - 10] = '\0';
-				facilities->dest_call = *asc2ax(callsign);
+				asc2ax(&facilities->dest_call, callsign);
 			}
 			p   += l + 2;
 			n   += l + 2;

+ 4 - 4
net/xfrm/xfrm_policy.c

@@ -765,8 +765,8 @@ restart:
 	switch (policy->action) {
 	case XFRM_POLICY_BLOCK:
 		/* Prohibit the flow */
-		xfrm_pol_put(policy);
-		return -EPERM;
+		err = -EPERM;
+		goto error;
 
 	case XFRM_POLICY_ALLOW:
 		if (policy->xfrm_nr == 0) {
@@ -782,8 +782,8 @@ restart:
 		 */
 		dst = xfrm_find_bundle(fl, policy, family);
 		if (IS_ERR(dst)) {
-			xfrm_pol_put(policy);
-			return PTR_ERR(dst);
+			err = PTR_ERR(dst);
+			goto error;
 		}
 
 		if (dst)