X-Git-Url: http://git.onelab.eu/?a=blobdiff_plain;f=net%2Fipv4%2Fip_options.c;h=9f02917d6f45319de4680eade1dda3cf3440b050;hb=refs%2Fheads%2Fvserver;hp=461bbd032f34675d7b339ba79a8804279caaac09;hpb=5273a3df6485dc2ad6aa7ddd441b9a21970f003b;p=linux-2.6.git diff --git a/net/ipv4/ip_options.c b/net/ipv4/ip_options.c index 461bbd032..9f02917d6 100644 --- a/net/ipv4/ip_options.c +++ b/net/ipv4/ip_options.c @@ -11,6 +11,7 @@ * */ +#include #include #include #include @@ -22,6 +23,8 @@ #include #include #include +#include +#include /* * 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);