cleanup
[linux-2.4.21-pre4.git] / net / ipv4 / ip_sockglue.c
1 /*
2  * INET         An implementation of the TCP/IP protocol suite for the LINUX
3  *              operating system.  INET is implemented using the  BSD Socket
4  *              interface as the means of communication with the user level.
5  *
6  *              The IP to API glue.
7  *              
8  * Version:     $Id: ip_sockglue.c,v 1.1.1.1 2005/04/11 02:51:13 jack Exp $
9  *
10  * Authors:     see ip.c
11  *
12  * Fixes:
13  *              Many            :       Split from ip.c , see ip.c for history.
14  *              Martin Mares    :       TOS setting fixed.
15  *              Alan Cox        :       Fixed a couple of oopses in Martin's 
16  *                                      TOS tweaks.
17  *              Mike McLagan    :       Routing by source
18  */
19
20 #include <linux/config.h>
21 #include <linux/types.h>
22 #include <linux/mm.h>
23 #include <linux/sched.h>
24 #include <linux/skbuff.h>
25 #include <linux/ip.h>
26 #include <linux/icmp.h>
27 #include <linux/netdevice.h>
28 #include <net/sock.h>
29 #include <net/ip.h>
30 #include <net/icmp.h>
31 #include <net/tcp.h>
32 #include <linux/tcp.h>
33 #include <linux/udp.h>
34 #include <linux/igmp.h>
35 #include <linux/netfilter.h>
36 #include <linux/route.h>
37 #include <linux/mroute.h>
38 #include <net/route.h>
39 #if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
40 #include <net/transp_v6.h>
41 #endif
42
43 #include <linux/errqueue.h>
44 #include <asm/uaccess.h>
45
46 #define IP_CMSG_PKTINFO         1
47 #define IP_CMSG_TTL             2
48 #define IP_CMSG_TOS             4
49 #define IP_CMSG_RECVOPTS        8
50 #define IP_CMSG_RETOPTS         16
51
52 /*
53  *      SOL_IP control messages.
54  */
55
56 static void ip_cmsg_recv_pktinfo(struct msghdr *msg, struct sk_buff *skb)
57 {
58         struct in_pktinfo info;
59         struct rtable *rt = (struct rtable *)skb->dst;
60
61         info.ipi_addr.s_addr = skb->nh.iph->daddr;
62         if (rt) {
63                 info.ipi_ifindex = rt->rt_iif;
64                 info.ipi_spec_dst.s_addr = rt->rt_spec_dst;
65         } else {
66                 info.ipi_ifindex = 0;
67                 info.ipi_spec_dst.s_addr = 0;
68         }
69
70         put_cmsg(msg, SOL_IP, IP_PKTINFO, sizeof(info), &info);
71 }
72
73 static void ip_cmsg_recv_ttl(struct msghdr *msg, struct sk_buff *skb)
74 {
75         int ttl = skb->nh.iph->ttl;
76         put_cmsg(msg, SOL_IP, IP_TTL, sizeof(int), &ttl);
77 }
78
79 static void ip_cmsg_recv_tos(struct msghdr *msg, struct sk_buff *skb)
80 {
81         put_cmsg(msg, SOL_IP, IP_TOS, 1, &skb->nh.iph->tos);
82 }
83
84 static void ip_cmsg_recv_opts(struct msghdr *msg, struct sk_buff *skb)
85 {
86         if (IPCB(skb)->opt.optlen == 0)
87                 return;
88
89         put_cmsg(msg, SOL_IP, IP_RECVOPTS, IPCB(skb)->opt.optlen, skb->nh.iph+1);
90 }
91
92
93 void ip_cmsg_recv_retopts(struct msghdr *msg, struct sk_buff *skb)
94 {
95         unsigned char optbuf[sizeof(struct ip_options) + 40];
96         struct ip_options * opt = (struct ip_options*)optbuf;
97
98         if (IPCB(skb)->opt.optlen == 0)
99                 return;
100
101         if (ip_options_echo(opt, skb)) {
102                 msg->msg_flags |= MSG_CTRUNC;
103                 return;
104         }
105         ip_options_undo(opt);
106
107         put_cmsg(msg, SOL_IP, IP_RETOPTS, opt->optlen, opt->__data);
108 }
109
110
111 void ip_cmsg_recv(struct msghdr *msg, struct sk_buff *skb)
112 {
113         unsigned flags = skb->sk->protinfo.af_inet.cmsg_flags;
114
115         /* Ordered by supposed usage frequency */
116         if (flags & 1)
117                 ip_cmsg_recv_pktinfo(msg, skb);
118         if ((flags>>=1) == 0)
119                 return;
120
121         if (flags & 1)
122                 ip_cmsg_recv_ttl(msg, skb);
123         if ((flags>>=1) == 0)
124                 return;
125
126         if (flags & 1)
127                 ip_cmsg_recv_tos(msg, skb);
128         if ((flags>>=1) == 0)
129                 return;
130
131         if (flags & 1)
132                 ip_cmsg_recv_opts(msg, skb);
133         if ((flags>>=1) == 0)
134                 return;
135
136         if (flags & 1)
137                 ip_cmsg_recv_retopts(msg, skb);
138 }
139
140 int ip_cmsg_send(struct msghdr *msg, struct ipcm_cookie *ipc)
141 {
142         int err;
143         struct cmsghdr *cmsg;
144
145         for (cmsg = CMSG_FIRSTHDR(msg); cmsg; cmsg = CMSG_NXTHDR(msg, cmsg)) {
146                 if (cmsg->cmsg_len < sizeof(struct cmsghdr) ||
147                     (unsigned long)(((char*)cmsg - (char*)msg->msg_control)
148                                     + cmsg->cmsg_len) > msg->msg_controllen) {
149                         return -EINVAL;
150                 }
151                 if (cmsg->cmsg_level != SOL_IP)
152                         continue;
153                 switch (cmsg->cmsg_type) {
154                 case IP_RETOPTS:
155                         err = cmsg->cmsg_len - CMSG_ALIGN(sizeof(struct cmsghdr));
156                         err = ip_options_get(&ipc->opt, CMSG_DATA(cmsg), err < 40 ? err : 40, 0);
157                         if (err)
158                                 return err;
159                         break;
160                 case IP_PKTINFO:
161                 {
162                         struct in_pktinfo *info;
163                         if (cmsg->cmsg_len != CMSG_LEN(sizeof(struct in_pktinfo)))
164                                 return -EINVAL;
165                         info = (struct in_pktinfo *)CMSG_DATA(cmsg);
166                         ipc->oif = info->ipi_ifindex;
167                         ipc->addr = info->ipi_spec_dst.s_addr;
168                         break;
169                 }
170                 default:
171                         return -EINVAL;
172                 }
173         }
174         return 0;
175 }
176
177
178 /* Special input handler for packets catched by router alert option.
179    They are selected only by protocol field, and then processed likely
180    local ones; but only if someone wants them! Otherwise, router
181    not running rsvpd will kill RSVP.
182
183    It is user level problem, what it will make with them.
184    I have no idea, how it will masquearde or NAT them (it is joke, joke :-)),
185    but receiver should be enough clever f.e. to forward mtrace requests,
186    sent to multicast group to reach destination designated router.
187  */
188 struct ip_ra_chain *ip_ra_chain;
189 rwlock_t ip_ra_lock = RW_LOCK_UNLOCKED;
190
191 int ip_ra_control(struct sock *sk, unsigned char on, void (*destructor)(struct sock *))
192 {
193         struct ip_ra_chain *ra, *new_ra, **rap;
194
195         if (sk->type != SOCK_RAW || sk->num == IPPROTO_RAW)
196                 return -EINVAL;
197
198         new_ra = on ? kmalloc(sizeof(*new_ra), GFP_KERNEL) : NULL;
199
200         write_lock_bh(&ip_ra_lock);
201         for (rap = &ip_ra_chain; (ra=*rap) != NULL; rap = &ra->next) {
202                 if (ra->sk == sk) {
203                         if (on) {
204                                 write_unlock_bh(&ip_ra_lock);
205                                 if (new_ra)
206                                         kfree(new_ra);
207                                 return -EADDRINUSE;
208                         }
209                         *rap = ra->next;
210                         write_unlock_bh(&ip_ra_lock);
211
212                         if (ra->destructor)
213                                 ra->destructor(sk);
214                         sock_put(sk);
215                         kfree(ra);
216                         return 0;
217                 }
218         }
219         if (new_ra == NULL) {
220                 write_unlock_bh(&ip_ra_lock);
221                 return -ENOBUFS;
222         }
223         new_ra->sk = sk;
224         new_ra->destructor = destructor;
225
226         new_ra->next = ra;
227         *rap = new_ra;
228         sock_hold(sk);
229         write_unlock_bh(&ip_ra_lock);
230
231         return 0;
232 }
233
234 void ip_icmp_error(struct sock *sk, struct sk_buff *skb, int err, 
235                    u16 port, u32 info, u8 *payload)
236 {
237         struct sock_exterr_skb *serr;
238
239         if (!sk->protinfo.af_inet.recverr)
240                 return;
241
242         skb = skb_clone(skb, GFP_ATOMIC);
243         if (!skb)
244                 return;
245
246         serr = SKB_EXT_ERR(skb);  
247         serr->ee.ee_errno = err;
248         serr->ee.ee_origin = SO_EE_ORIGIN_ICMP;
249         serr->ee.ee_type = skb->h.icmph->type; 
250         serr->ee.ee_code = skb->h.icmph->code;
251         serr->ee.ee_pad = 0;
252         serr->ee.ee_info = info;
253         serr->ee.ee_data = 0;
254         serr->addr_offset = (u8*)&(((struct iphdr*)(skb->h.icmph+1))->daddr) - skb->nh.raw;
255         serr->port = port;
256
257         skb->h.raw = payload;
258         if (!skb_pull(skb, payload - skb->data) ||
259             sock_queue_err_skb(sk, skb))
260                 kfree_skb(skb);
261 }
262
263 void ip_local_error(struct sock *sk, int err, u32 daddr, u16 port, u32 info)
264 {
265         struct sock_exterr_skb *serr;
266         struct iphdr *iph;
267         struct sk_buff *skb;
268
269         if (!sk->protinfo.af_inet.recverr)
270                 return;
271
272         skb = alloc_skb(sizeof(struct iphdr), GFP_ATOMIC);
273         if (!skb)
274                 return;
275
276         iph = (struct iphdr*)skb_put(skb, sizeof(struct iphdr));
277         skb->nh.iph = iph;
278         iph->daddr = daddr;
279
280         serr = SKB_EXT_ERR(skb);  
281         serr->ee.ee_errno = err;
282         serr->ee.ee_origin = SO_EE_ORIGIN_LOCAL;
283         serr->ee.ee_type = 0; 
284         serr->ee.ee_code = 0;
285         serr->ee.ee_pad = 0;
286         serr->ee.ee_info = info;
287         serr->ee.ee_data = 0;
288         serr->addr_offset = (u8*)&iph->daddr - skb->nh.raw;
289         serr->port = port;
290
291         skb->h.raw = skb->tail;
292         __skb_pull(skb, skb->tail - skb->data);
293
294         if (sock_queue_err_skb(sk, skb))
295                 kfree_skb(skb);
296 }
297
298 /* 
299  *      Handle MSG_ERRQUEUE
300  */
301 int ip_recv_error(struct sock *sk, struct msghdr *msg, int len)
302 {
303         struct sock_exterr_skb *serr;
304         struct sk_buff *skb, *skb2;
305         struct sockaddr_in *sin;
306         struct {
307                 struct sock_extended_err ee;
308                 struct sockaddr_in       offender;
309         } errhdr;
310         int err;
311         int copied;
312
313         err = -EAGAIN;
314         skb = skb_dequeue(&sk->error_queue);
315         if (skb == NULL)
316                 goto out;
317
318         copied = skb->len;
319         if (copied > len) {
320                 msg->msg_flags |= MSG_TRUNC;
321                 copied = len;
322         }
323         err = skb_copy_datagram_iovec(skb, 0, msg->msg_iov, copied);
324         if (err)
325                 goto out_free_skb;
326
327         sock_recv_timestamp(msg, sk, skb);
328
329         serr = SKB_EXT_ERR(skb);
330
331         sin = (struct sockaddr_in *)msg->msg_name;
332         if (sin) {
333                 sin->sin_family = AF_INET;
334                 sin->sin_addr.s_addr = *(u32*)(skb->nh.raw + serr->addr_offset);
335                 sin->sin_port = serr->port;
336                 memset(&sin->sin_zero, 0, sizeof(sin->sin_zero));
337         }
338
339         memcpy(&errhdr.ee, &serr->ee, sizeof(struct sock_extended_err));
340         sin = &errhdr.offender;
341         sin->sin_family = AF_UNSPEC;
342         if (serr->ee.ee_origin == SO_EE_ORIGIN_ICMP) {
343                 sin->sin_family = AF_INET;
344                 sin->sin_addr.s_addr = skb->nh.iph->saddr;
345                 sin->sin_port = 0;
346                 memset(&sin->sin_zero, 0, sizeof(sin->sin_zero));
347                 if (sk->protinfo.af_inet.cmsg_flags)
348                         ip_cmsg_recv(msg, skb);
349         }
350
351         put_cmsg(msg, SOL_IP, IP_RECVERR, sizeof(errhdr), &errhdr);
352
353         /* Now we could try to dump offended packet options */
354
355         msg->msg_flags |= MSG_ERRQUEUE;
356         err = copied;
357
358         /* Reset and regenerate socket error */
359         spin_lock_irq(&sk->error_queue.lock);
360         sk->err = 0;
361         if ((skb2 = skb_peek(&sk->error_queue)) != NULL) {
362                 sk->err = SKB_EXT_ERR(skb2)->ee.ee_errno;
363                 spin_unlock_irq(&sk->error_queue.lock);
364                 sk->error_report(sk);
365         } else {
366                 spin_unlock_irq(&sk->error_queue.lock);
367         }
368
369 out_free_skb:   
370         kfree_skb(skb);
371 out:
372         return err;
373 }
374
375
376 /*
377  *      Socket option code for IP. This is the end of the line after any TCP,UDP etc options on
378  *      an IP socket.
379  */
380
381 int ip_setsockopt(struct sock *sk, int level, int optname, char *optval, int optlen)
382 {
383         int val=0,err;
384
385         if (level != SOL_IP)
386                 return -ENOPROTOOPT;
387
388         if (((1<<optname) & ((1<<IP_PKTINFO) | (1<<IP_RECVTTL) | 
389                             (1<<IP_RECVOPTS) | (1<<IP_RECVTOS) | 
390                             (1<<IP_RETOPTS) | (1<<IP_TOS) | 
391                             (1<<IP_TTL) | (1<<IP_HDRINCL) | 
392                             (1<<IP_MTU_DISCOVER) | (1<<IP_RECVERR) | 
393                             (1<<IP_ROUTER_ALERT) | (1<<IP_FREEBIND))) || 
394                                 optname == IP_MULTICAST_TTL || 
395                                 optname == IP_MULTICAST_LOOP) { 
396                 if (optlen >= sizeof(int)) {
397                         if (get_user(val, (int *) optval))
398                                 return -EFAULT;
399                 } else if (optlen >= sizeof(char)) {
400                         unsigned char ucval;
401
402                         if (get_user(ucval, (unsigned char *) optval))
403                                 return -EFAULT;
404                         val = (int) ucval;
405                 }
406         }
407
408         /* If optlen==0, it is equivalent to val == 0 */
409
410 #ifdef CONFIG_IP_MROUTE
411         if (optname >= MRT_BASE && optname <= (MRT_BASE + 10))
412                 return ip_mroute_setsockopt(sk,optname,optval,optlen);
413 #endif
414
415         err = 0;
416         lock_sock(sk);
417
418         switch (optname) {
419                 case IP_OPTIONS:
420                 {
421                         struct ip_options * opt = NULL;
422                         if (optlen > 40 || optlen < 0)
423                                 goto e_inval;
424                         err = ip_options_get(&opt, optval, optlen, 1);
425                         if (err)
426                                 break;
427                         if (sk->type == SOCK_STREAM) {
428                                 struct tcp_opt *tp = &sk->tp_pinfo.af_tcp;
429 #if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
430                                 if (sk->family == PF_INET ||
431                                     (!((1<<sk->state)&(TCPF_LISTEN|TCPF_CLOSE))
432                                      && sk->daddr != LOOPBACK4_IPV6)) {
433 #endif
434                                         if (opt)
435                                                 tp->ext_header_len = opt->optlen;
436                                         tcp_sync_mss(sk, tp->pmtu_cookie);
437 #if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
438                                 }
439 #endif
440                         }
441                         opt = xchg(&sk->protinfo.af_inet.opt, opt);
442                         if (opt)
443                                 kfree(opt);
444                         break;
445                 }
446                 case IP_PKTINFO:
447                         if (val)
448                                 sk->protinfo.af_inet.cmsg_flags |= IP_CMSG_PKTINFO;
449                         else
450                                 sk->protinfo.af_inet.cmsg_flags &= ~IP_CMSG_PKTINFO;
451                         break;
452                 case IP_RECVTTL:
453                         if (val)
454                                 sk->protinfo.af_inet.cmsg_flags |=  IP_CMSG_TTL;
455                         else
456                                 sk->protinfo.af_inet.cmsg_flags &= ~IP_CMSG_TTL;
457                         break;
458                 case IP_RECVTOS:
459                         if (val)
460                                 sk->protinfo.af_inet.cmsg_flags |=  IP_CMSG_TOS;
461                         else
462                                 sk->protinfo.af_inet.cmsg_flags &= ~IP_CMSG_TOS;
463                         break;
464                 case IP_RECVOPTS:
465                         if (val)
466                                 sk->protinfo.af_inet.cmsg_flags |=  IP_CMSG_RECVOPTS;
467                         else
468                                 sk->protinfo.af_inet.cmsg_flags &= ~IP_CMSG_RECVOPTS;
469                         break;
470                 case IP_RETOPTS:
471                         if (val)
472                                 sk->protinfo.af_inet.cmsg_flags |= IP_CMSG_RETOPTS;
473                         else
474                                 sk->protinfo.af_inet.cmsg_flags &= ~IP_CMSG_RETOPTS;
475                         break;
476                 case IP_TOS:    /* This sets both TOS and Precedence */
477                         if (sk->type == SOCK_STREAM) {
478                                 val &= ~3;
479                                 val |= sk->protinfo.af_inet.tos & 3;
480                         }
481                         if (IPTOS_PREC(val) >= IPTOS_PREC_CRITIC_ECP && 
482                             !capable(CAP_NET_ADMIN)) {
483                                 err = -EPERM;
484                                 break;
485                         }
486                         if (sk->protinfo.af_inet.tos != val) {
487                                 sk->protinfo.af_inet.tos=val;
488                                 sk->priority = rt_tos2priority(val);
489                                 sk_dst_reset(sk); 
490                         }
491                         break;
492                 case IP_TTL:
493                         if (optlen<1)
494                                 goto e_inval;
495                         if(val==-1)
496                                 val = sysctl_ip_default_ttl;
497                         if(val<1||val>255)
498                                 goto e_inval;
499                         sk->protinfo.af_inet.ttl=val;
500                         break;
501                 case IP_HDRINCL:
502                         if(sk->type!=SOCK_RAW) {
503                                 err = -ENOPROTOOPT;
504                                 break;
505                         }
506                         sk->protinfo.af_inet.hdrincl=val?1:0;
507                         break;
508                 case IP_MTU_DISCOVER:
509                         if (val<0 || val>2)
510                                 goto e_inval;
511                         sk->protinfo.af_inet.pmtudisc = val;
512                         break;
513                 case IP_RECVERR:
514                         sk->protinfo.af_inet.recverr = !!val;
515                         if (!val)
516                                 skb_queue_purge(&sk->error_queue);
517                         break;
518                 case IP_MULTICAST_TTL:
519                         if (sk->type == SOCK_STREAM)
520                                 goto e_inval;
521                         if (optlen<1)
522                                 goto e_inval;
523                         if (val==-1)
524                                 val = 1;
525                         if (val < 0 || val > 255)
526                                 goto e_inval;
527                         sk->protinfo.af_inet.mc_ttl=val;
528                         break;
529                 case IP_MULTICAST_LOOP: 
530                         if (optlen<1)
531                                 goto e_inval;
532                         sk->protinfo.af_inet.mc_loop = val ? 1 : 0;
533                         break;
534                 case IP_MULTICAST_IF: 
535                 {
536                         struct ip_mreqn mreq;
537                         struct net_device *dev = NULL;
538
539                         if (sk->type == SOCK_STREAM)
540                                 goto e_inval;
541                         /*
542                          *      Check the arguments are allowable
543                          */
544
545                         err = -EFAULT;
546                         if (optlen >= sizeof(struct ip_mreqn)) {
547                                 if (copy_from_user(&mreq,optval,sizeof(mreq)))
548                                         break;
549                         } else {
550                                 memset(&mreq, 0, sizeof(mreq));
551                                 if (optlen >= sizeof(struct in_addr) &&
552                                     copy_from_user(&mreq.imr_address,optval,sizeof(struct in_addr)))
553                                         break;
554                         }
555
556                         if (!mreq.imr_ifindex) {
557                                 if (mreq.imr_address.s_addr == INADDR_ANY) {
558                                         sk->protinfo.af_inet.mc_index = 0;
559                                         sk->protinfo.af_inet.mc_addr  = 0;
560                                         err = 0;
561                                         break;
562                                 }
563                                 dev = ip_dev_find(mreq.imr_address.s_addr);
564                                 if (dev) {
565                                         mreq.imr_ifindex = dev->ifindex;
566                                         dev_put(dev);
567                                 }
568                         } else
569                                 dev = __dev_get_by_index(mreq.imr_ifindex);
570
571
572                         err = -EADDRNOTAVAIL;
573                         if (!dev)
574                                 break;
575
576                         err = -EINVAL;
577                         if (sk->bound_dev_if && mreq.imr_ifindex != sk->bound_dev_if)
578                                 break;
579
580                         sk->protinfo.af_inet.mc_index = mreq.imr_ifindex;
581                         sk->protinfo.af_inet.mc_addr  = mreq.imr_address.s_addr;
582                         err = 0;
583                         break;
584                 }
585
586                 case IP_ADD_MEMBERSHIP:
587                 case IP_DROP_MEMBERSHIP: 
588                 {
589                         struct ip_mreqn mreq;
590
591                         if (optlen < sizeof(struct ip_mreq))
592                                 goto e_inval;
593                         err = -EFAULT;
594                         if (optlen >= sizeof(struct ip_mreqn)) {
595                                 if(copy_from_user(&mreq,optval,sizeof(mreq)))
596                                         break;
597                         } else {
598                                 memset(&mreq, 0, sizeof(mreq));
599                                 if (copy_from_user(&mreq,optval,sizeof(struct ip_mreq)))
600                                         break; 
601                         }
602
603                         if (optname == IP_ADD_MEMBERSHIP)
604                                 err = ip_mc_join_group(sk,&mreq);
605                         else
606                                 err = ip_mc_leave_group(sk,&mreq);
607                         break;
608                 }
609                 case IP_ROUTER_ALERT:   
610                         err = ip_ra_control(sk, val ? 1 : 0, NULL);
611                         break;
612
613                 case IP_FREEBIND:
614                         if (optlen<1)
615                                 goto e_inval;
616                         sk->protinfo.af_inet.freebind = !!val; 
617                         break;                  
618  
619                 default:
620 #ifdef CONFIG_NETFILTER
621                         err = nf_setsockopt(sk, PF_INET, optname, optval, 
622                                             optlen);
623 #else
624                         err = -ENOPROTOOPT;
625 #endif
626                         break;
627         }
628         release_sock(sk);
629         return err;
630
631 e_inval:
632         release_sock(sk);
633         return -EINVAL;
634 }
635
636 /*
637  *      Get the options. Note for future reference. The GET of IP options gets the
638  *      _received_ ones. The set sets the _sent_ ones.
639  */
640
641 int ip_getsockopt(struct sock *sk, int level, int optname, char *optval, int *optlen)
642 {
643         int val;
644         int len;
645         
646         if(level!=SOL_IP)
647                 return -EOPNOTSUPP;
648
649 #ifdef CONFIG_IP_MROUTE
650         if(optname>=MRT_BASE && optname <=MRT_BASE+10)
651         {
652                 return ip_mroute_getsockopt(sk,optname,optval,optlen);
653         }
654 #endif
655
656         if(get_user(len,optlen))
657                 return -EFAULT;
658         if(len < 0)
659                 return -EINVAL;
660                 
661         lock_sock(sk);
662
663         switch(optname) {
664                 case IP_OPTIONS:
665                         {
666                                 unsigned char optbuf[sizeof(struct ip_options)+40];
667                                 struct ip_options * opt = (struct ip_options*)optbuf;
668                                 opt->optlen = 0;
669                                 if (sk->protinfo.af_inet.opt)
670                                         memcpy(optbuf, sk->protinfo.af_inet.opt,
671                                                sizeof(struct ip_options)+
672                                                sk->protinfo.af_inet.opt->optlen);
673                                 release_sock(sk);
674
675                                 if (opt->optlen == 0) 
676                                         return put_user(0, optlen);
677
678                                 ip_options_undo(opt);
679
680                                 len = min_t(unsigned int, len, opt->optlen);
681                                 if(put_user(len, optlen))
682                                         return -EFAULT;
683                                 if(copy_to_user(optval, opt->__data, len))
684                                         return -EFAULT;
685                                 return 0;
686                         }
687                 case IP_PKTINFO:
688                         val = (sk->protinfo.af_inet.cmsg_flags & IP_CMSG_PKTINFO) != 0;
689                         break;
690                 case IP_RECVTTL:
691                         val = (sk->protinfo.af_inet.cmsg_flags & IP_CMSG_TTL) != 0;
692                         break;
693                 case IP_RECVTOS:
694                         val = (sk->protinfo.af_inet.cmsg_flags & IP_CMSG_TOS) != 0;
695                         break;
696                 case IP_RECVOPTS:
697                         val = (sk->protinfo.af_inet.cmsg_flags & IP_CMSG_RECVOPTS) != 0;
698                         break;
699                 case IP_RETOPTS:
700                         val = (sk->protinfo.af_inet.cmsg_flags & IP_CMSG_RETOPTS) != 0;
701                         break;
702                 case IP_TOS:
703                         val=sk->protinfo.af_inet.tos;
704                         break;
705                 case IP_TTL:
706                         val=sk->protinfo.af_inet.ttl;
707                         break;
708                 case IP_HDRINCL:
709                         val=sk->protinfo.af_inet.hdrincl;
710                         break;
711                 case IP_MTU_DISCOVER:
712                         val=sk->protinfo.af_inet.pmtudisc;
713                         break;
714                 case IP_MTU:
715                 {
716                         struct dst_entry *dst;
717                         val = 0;
718                         dst = sk_dst_get(sk);
719                         if (dst) {
720                                 val = dst->pmtu;
721                                 dst_release(dst);
722                         }
723                         if (!val) {
724                                 release_sock(sk);
725                                 return -ENOTCONN;
726                         }
727                         break;
728                 }
729                 case IP_RECVERR:
730                         val=sk->protinfo.af_inet.recverr;
731                         break;
732                 case IP_MULTICAST_TTL:
733                         val=sk->protinfo.af_inet.mc_ttl;
734                         break;
735                 case IP_MULTICAST_LOOP:
736                         val=sk->protinfo.af_inet.mc_loop;
737                         break;
738                 case IP_MULTICAST_IF:
739                 {
740                         struct in_addr addr;
741                         len = min_t(unsigned int, len, sizeof(struct in_addr));
742                         addr.s_addr = sk->protinfo.af_inet.mc_addr;
743                         release_sock(sk);
744
745                         if(put_user(len, optlen))
746                                 return -EFAULT;
747                         if(copy_to_user((void *)optval, &addr, len))
748                                 return -EFAULT;
749                         return 0;
750                 }
751                 case IP_PKTOPTIONS:             
752                 {
753                         struct msghdr msg;
754
755                         release_sock(sk);
756
757                         if (sk->type != SOCK_STREAM)
758                                 return -ENOPROTOOPT;
759
760                         msg.msg_control = optval;
761                         msg.msg_controllen = len;
762                         msg.msg_flags = 0;
763
764                         if (sk->protinfo.af_inet.cmsg_flags&IP_CMSG_PKTINFO) {
765                                 struct in_pktinfo info;
766
767                                 info.ipi_addr.s_addr = sk->rcv_saddr;
768                                 info.ipi_spec_dst.s_addr = sk->rcv_saddr;
769                                 info.ipi_ifindex = sk->protinfo.af_inet.mc_index;
770                                 put_cmsg(&msg, SOL_IP, IP_PKTINFO, sizeof(info), &info);
771                         }
772                         if (sk->protinfo.af_inet.cmsg_flags&IP_CMSG_TTL) {
773                                 int hlim = sk->protinfo.af_inet.mc_ttl;
774                                 put_cmsg(&msg, SOL_IP, IP_TTL, sizeof(hlim), &hlim);
775                         }
776                         len -= msg.msg_controllen;
777                         return put_user(len, optlen);
778                 }
779                 case IP_FREEBIND: 
780                         val = sk->protinfo.af_inet.freebind; 
781                         break; 
782                 default:
783 #ifdef CONFIG_NETFILTER
784                         val = nf_getsockopt(sk, PF_INET, optname, optval, 
785                                             &len);
786                         release_sock(sk);
787                         if (val >= 0)
788                                 val = put_user(len, optlen);
789                         return val;
790 #else
791                         release_sock(sk);
792                         return -ENOPROTOOPT;
793 #endif
794         }
795         release_sock(sk);
796         
797         if (len < sizeof(int) && len > 0 && val>=0 && val<255) {
798                 unsigned char ucval = (unsigned char)val;
799                 len = 1;
800                 if(put_user(len, optlen))
801                         return -EFAULT;
802                 if(copy_to_user(optval,&ucval,1))
803                         return -EFAULT;
804         } else {
805                 len = min_t(unsigned int, sizeof(int), len);
806                 if(put_user(len, optlen))
807                         return -EFAULT;
808                 if(copy_to_user(optval,&val,len))
809                         return -EFAULT;
810         }
811         return 0;
812 }