fedora core 6 1.2949 + vserver 2.2.0
[linux-2.6.git] / net / ipv4 / ip_options.c
index 461bbd0..9f02917 100644 (file)
@@ -11,6 +11,7 @@
  *             
  */
 
+#include <linux/capability.h>
 #include <linux/module.h>
 #include <linux/types.h>
 #include <asm/uaccess.h>
@@ -22,6 +23,8 @@
 #include <net/sock.h>
 #include <net/ip.h>
 #include <net/icmp.h>
+#include <net/route.h>
+#include <net/cipso_ipv4.h>
 
 /* 
  * Write options to IP header, record destination address to
@@ -35,7 +38,7 @@
  */
 
 void ip_options_build(struct sk_buff * skb, struct ip_options * opt,
-                           u32 daddr, struct rtable *rt, int is_frag) 
+                           __be32 daddr, struct rtable *rt, int is_frag)
 {
        unsigned char * iph = skb->nh.raw;
 
@@ -54,7 +57,7 @@ void ip_options_build(struct sk_buff * skb, struct ip_options * opt,
                        ip_rt_get_source(iph+opt->ts+iph[opt->ts+2]-9, rt);
                if (opt->ts_needtime) {
                        struct timeval tv;
-                       __u32 midtime;
+                       __be32 midtime;
                        do_gettimeofday(&tv);
                        midtime = htonl((tv.tv_sec % 86400) * 1000 + tv.tv_usec / 1000);
                        memcpy(iph+opt->ts+iph[opt->ts+2]-5, &midtime, 4);
@@ -88,7 +91,7 @@ int ip_options_echo(struct ip_options * dopt, struct sk_buff * skb)
        unsigned char *sptr, *dptr;
        int soffset, doffset;
        int     optlen;
-       u32     daddr;
+       __be32  daddr;
 
        memset(dopt, 0, sizeof(struct ip_options));
 
@@ -145,7 +148,7 @@ int ip_options_echo(struct ip_options * dopt, struct sk_buff * skb)
                                        dopt->ts_needtime = 0;
 
                                        if (soffset + 8 <= optlen) {
-                                               __u32 addr;
+                                               __be32 addr;
 
                                                memcpy(&addr, sptr+soffset-1, 4);
                                                if (inet_addr_type(addr) != RTN_LOCAL) {
@@ -162,7 +165,7 @@ int ip_options_echo(struct ip_options * dopt, struct sk_buff * skb)
        }
        if (sopt->srr) {
                unsigned char * start = sptr+sopt->srr;
-               u32 faddr;
+               __be32 faddr;
 
                optlen  = start[1];
                soffset = start[2];
@@ -192,6 +195,13 @@ int ip_options_echo(struct ip_options * dopt, struct sk_buff * skb)
                        dopt->is_strictroute = sopt->is_strictroute;
                }
        }
+       if (sopt->cipso) {
+               optlen  = sptr[sopt->cipso+1];
+               dopt->cipso = dopt->optlen+sizeof(struct iphdr);
+               memcpy(dptr, sptr+sopt->cipso, optlen);
+               dptr += optlen;
+               dopt->optlen += optlen;
+       }
        while (dopt->optlen & 3) {
                *dptr++ = IPOPT_END;
                dopt->optlen++;
@@ -207,7 +217,7 @@ int ip_options_echo(struct ip_options * dopt, struct sk_buff * skb)
 
 void ip_options_fragment(struct sk_buff * skb) 
 {
-       unsigned char * optptr = skb->nh.raw;
+       unsigned char * optptr = skb->nh.raw + sizeof(struct iphdr);
        struct ip_options * opt = &(IPCB(skb)->opt);
        int  l = opt->optlen;
        int  optlen;
@@ -254,7 +264,6 @@ int ip_options_compile(struct ip_options * opt, struct sk_buff * skb)
 
        if (!opt) {
                opt = &(IPCB(skb)->opt);
-               memset(opt, 0, sizeof(struct ip_options));
                iph = skb->nh.raw;
                opt->optlen = ((struct iphdr *)iph)->ihl*4 - sizeof(struct iphdr);
                optptr = iph + sizeof(struct iphdr);
@@ -353,7 +362,7 @@ int ip_options_compile(struct ip_options * opt, struct sk_buff * skb)
                                goto error;
                        }
                        if (optptr[2] <= optlen) {
-                               __u32 * timeptr = NULL;
+                               __be32 *timeptr = NULL;
                                if (optptr[2]+3 > optptr[1]) {
                                        pp_ptr = optptr + 2;
                                        goto error;
@@ -362,7 +371,7 @@ int ip_options_compile(struct ip_options * opt, struct sk_buff * skb)
                                      case IPOPT_TS_TSONLY:
                                        opt->ts = optptr - iph;
                                        if (skb) 
-                                               timeptr = (__u32*)&optptr[optptr[2]-1];
+                                               timeptr = (__be32*)&optptr[optptr[2]-1];
                                        opt->ts_needtime = 1;
                                        optptr[2] += 4;
                                        break;
@@ -374,7 +383,7 @@ int ip_options_compile(struct ip_options * opt, struct sk_buff * skb)
                                        opt->ts = optptr - iph;
                                        if (skb) {
                                                memcpy(&optptr[optptr[2]-1], &rt->rt_spec_dst, 4);
-                                               timeptr = (__u32*)&optptr[optptr[2]+3];
+                                               timeptr = (__be32*)&optptr[optptr[2]+3];
                                        }
                                        opt->ts_needaddr = 1;
                                        opt->ts_needtime = 1;
@@ -387,12 +396,12 @@ int ip_options_compile(struct ip_options * opt, struct sk_buff * skb)
                                        }
                                        opt->ts = optptr - iph;
                                        {
-                                               u32 addr;
+                                               __be32 addr;
                                                memcpy(&addr, &optptr[optptr[2]-1], 4);
                                                if (inet_addr_type(addr) == RTN_UNICAST)
                                                        break;
                                                if (skb)
-                                                       timeptr = (__u32*)&optptr[optptr[2]+3];
+                                                       timeptr = (__be32*)&optptr[optptr[2]+3];
                                        }
                                        opt->ts_needtime = 1;
                                        optptr[2] += 8;
@@ -406,10 +415,10 @@ int ip_options_compile(struct ip_options * opt, struct sk_buff * skb)
                                }
                                if (timeptr) {
                                        struct timeval tv;
-                                       __u32  midtime;
+                                       __be32  midtime;
                                        do_gettimeofday(&tv);
                                        midtime = htonl((tv.tv_sec % 86400) * 1000 + tv.tv_usec / 1000);
-                                       memcpy(timeptr, &midtime, sizeof(__u32));
+                                       memcpy(timeptr, &midtime, sizeof(__be32));
                                        opt->is_changed = 1;
                                }
                        } else {
@@ -433,6 +442,17 @@ int ip_options_compile(struct ip_options * opt, struct sk_buff * skb)
                        if (optptr[2] == 0 && optptr[3] == 0)
                                opt->router_alert = optptr - iph;
                        break;
+                     case IPOPT_CIPSO:
+                       if ((!skb && !capable(CAP_NET_RAW)) || opt->cipso) {
+                               pp_ptr = optptr;
+                               goto error;
+                       }
+                       opt->cipso = optptr - iph;
+                       if (cipso_v4_validate(&optptr)) {
+                               pp_ptr = optptr;
+                               goto error;
+                       }
+                       break;
                      case IPOPT_SEC:
                      case IPOPT_SID:
                      default:
@@ -489,36 +509,55 @@ void ip_options_undo(struct ip_options * opt)
        }
 }
 
-int ip_options_get(struct ip_options **optp, unsigned char *data, int optlen, int user)
+static struct ip_options *ip_options_get_alloc(const int optlen)
 {
-       struct ip_options *opt;
+       struct ip_options *opt = kmalloc(sizeof(*opt) + ((optlen + 3) & ~3),
+                                        GFP_KERNEL);
+       if (opt)
+               memset(opt, 0, sizeof(*opt));
+       return opt;
+}
 
-       opt = kmalloc(sizeof(struct ip_options)+((optlen+3)&~3), GFP_KERNEL);
-       if (!opt)
-               return -ENOMEM;
-       memset(opt, 0, sizeof(struct ip_options));
-       if (optlen) {
-               if (user) {
-                       if (copy_from_user(opt->__data, data, optlen)) {
-                               kfree(opt);
-                               return -EFAULT;
-                       }
-               } else
-                       memcpy(opt->__data, data, optlen);
-       }
+static int ip_options_get_finish(struct ip_options **optp,
+                                struct ip_options *opt, int optlen)
+{
        while (optlen & 3)
                opt->__data[optlen++] = IPOPT_END;
        opt->optlen = optlen;
        opt->is_data = 1;
-       opt->is_setbyuser = 1;
        if (optlen && ip_options_compile(opt, NULL)) {
                kfree(opt);
                return -EINVAL;
        }
+       kfree(*optp);
        *optp = opt;
        return 0;
 }
 
+int ip_options_get_from_user(struct ip_options **optp, unsigned char __user *data, int optlen)
+{
+       struct ip_options *opt = ip_options_get_alloc(optlen);
+
+       if (!opt)
+               return -ENOMEM;
+       if (optlen && copy_from_user(opt->__data, data, optlen)) {
+               kfree(opt);
+               return -EFAULT;
+       }
+       return ip_options_get_finish(optp, opt, optlen);
+}
+
+int ip_options_get(struct ip_options **optp, unsigned char *data, int optlen)
+{
+       struct ip_options *opt = ip_options_get_alloc(optlen);
+
+       if (!opt)
+               return -ENOMEM;
+       if (optlen)
+               memcpy(opt->__data, data, optlen);
+       return ip_options_get_finish(optp, opt, optlen);
+}
+
 void ip_forward_options(struct sk_buff *skb)
 {
        struct   ip_options * opt       = &(IPCB(skb)->opt);
@@ -568,7 +607,7 @@ int ip_options_rcv_srr(struct sk_buff *skb)
 {
        struct ip_options *opt = &(IPCB(skb)->opt);
        int srrspace, srrptr;
-       u32 nexthop;
+       __be32 nexthop;
        struct iphdr *iph = skb->nh.iph;
        unsigned char * optptr = skb->nh.raw + opt->srr;
        struct rtable *rt = (struct rtable*)skb->dst;
@@ -618,6 +657,3 @@ int ip_options_rcv_srr(struct sk_buff *skb)
        }
        return 0;
 }
-
-EXPORT_SYMBOL(ip_options_compile);
-EXPORT_SYMBOL(ip_options_undo);