Merge master.kernel.org:/pub/scm/linux/kernel/git/davem/net-2.6
authorLinus Torvalds <torvalds@g5.osdl.org>
Thu, 8 Sep 2005 22:52:11 +0000 (15:52 -0700)
committerLinus Torvalds <torvalds@g5.osdl.org>
Thu, 8 Sep 2005 22:52:11 +0000 (15:52 -0700)
30 files changed:
drivers/net/bnx2.c
drivers/net/bnx2.h
include/linux/in6.h
include/linux/ipv6.h
include/linux/skbuff.h
include/net/ax25.h
include/net/compat.h
include/net/ipv6.h
include/net/transp_v6.h
net/ax25/ax25_addr.c
net/ipv4/netfilter/ip_conntrack_netbios_ns.c
net/ipv4/netfilter/ipt_REJECT.c
net/ipv4/route.c
net/ipv4/tcp_output.c
net/ipv4/udp.c
net/ipv6/datagram.c
net/ipv6/exthdrs.c
net/ipv6/icmp.c
net/ipv6/ip6_flowlabel.c
net/ipv6/ip6_output.c
net/ipv6/ip6_tunnel.c
net/ipv6/ipv6_sockglue.c
net/ipv6/ndisc.c
net/ipv6/netfilter/ip6t_REJECT.c
net/ipv6/raw.c
net/ipv6/reassembly.c
net/ipv6/tcp_ipv6.c
net/ipv6/udp.c
net/rose/rose_subr.c
net/xfrm/xfrm_policy.c

index 55a72c7..83598e3 100644 (file)
@@ -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;
index 9ad3f57..62857b6 100644 (file)
@@ -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.
index dcf5720..bd32b79 100644 (file)
@@ -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
index 3c7dbc6..6c5f7b3 100644 (file)
@@ -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;
-               __u           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;
 };
 
index da7da9c..2741c0c 100644 (file)
@@ -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;
index 364b046..227d337 100644 (file)
@@ -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 *);
index 482eb82..290bab4 100644 (file)
@@ -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 */
index 3203eaf..65ec866 100644 (file)
@@ -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,
index 8b075ab..4e86f2d 100644 (file)
@@ -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)
 
index dca179d..0164a15 100644 (file)
@@ -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;
 }
 
 /*
index 2b5cf9c..bb72466 100644 (file)
@@ -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,
index f115a84..f057025 100644 (file)
@@ -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;
 }
index 8c0b14e..8549f26 100644 (file)
@@ -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,
index 6094db5..15e1134 100644 (file)
@@ -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);
 
index e5beca7..e0bd101 100644 (file)
@@ -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)
index 01468fa..cc51840 100644 (file)
@@ -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);
index 5be6da2..4712272 100644 (file)
@@ -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);
+}
+
index fa8f1bb..b7185fb 100644 (file)
@@ -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();
index b6c73da..a7db762 100644 (file)
@@ -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;
index 01ef94f..2f589f2 100644 (file)
@@ -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));
index 0961372..cf94372 100644 (file)
@@ -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;
 
index 76466af..8567873 100644 (file)
@@ -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;
index a7eae30..555a313 100644 (file)
@@ -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;
 
index 14316c3..b03e87a 100644 (file)
@@ -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)
index ed3a76b..5aa3691 100644 (file)
@@ -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);
index 9d9e043..e4fe9ee 100644 (file)
@@ -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
index 794734f..80643e6 100644 (file)
@@ -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;
                }
 
index 390d750..69b1468 100644 (file)
@@ -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)
index 02891ce..36a7794 100644 (file)
@@ -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;
index 83c8135..fda737d 100644 (file)
@@ -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)