~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~ [ freetext search ] ~ [ file search ] ~

Linux Cross Reference
Linux/net/ipv6/ipv6_sockglue.c

Version: ~ [ 2.4.0 ] ~
Architecture: ~ [ i386 ] ~ [ alpha ] ~ [ m68k ] ~ [ mips ] ~ [ ppc ] ~ [ sparc ] ~ [ sparc64 ] ~

  1 /*
  2  *      IPv6 BSD socket options interface
  3  *      Linux INET6 implementation 
  4  *
  5  *      Authors:
  6  *      Pedro Roque             <roque@di.fc.ul.pt>     
  7  *
  8  *      Based on linux/net/ipv4/ip_sockglue.c
  9  *
 10  *      $Id: ipv6_sockglue.c,v 1.34 2000/11/28 13:44:28 davem Exp $
 11  *
 12  *      This program is free software; you can redistribute it and/or
 13  *      modify it under the terms of the GNU General Public License
 14  *      as published by the Free Software Foundation; either version
 15  *      2 of the License, or (at your option) any later version.
 16  *
 17  *      FIXME: Make the setsockopt code POSIX compliant: That is
 18  *
 19  *      o       Return -EINVAL for setsockopt of short lengths
 20  *      o       Truncate getsockopt returns
 21  *      o       Return an optlen of the truncated length if need be
 22  */
 23 
 24 #define __NO_VERSION__
 25 #include <linux/module.h>
 26 #include <linux/config.h>
 27 #include <linux/errno.h>
 28 #include <linux/types.h>
 29 #include <linux/socket.h>
 30 #include <linux/sockios.h>
 31 #include <linux/sched.h>
 32 #include <linux/net.h>
 33 #include <linux/in6.h>
 34 #include <linux/netdevice.h>
 35 #include <linux/if_arp.h>
 36 #include <linux/init.h>
 37 #include <linux/sysctl.h>
 38 #include <linux/netfilter.h>
 39 
 40 #include <net/sock.h>
 41 #include <net/snmp.h>
 42 #include <net/ipv6.h>
 43 #include <net/ndisc.h>
 44 #include <net/protocol.h>
 45 #include <net/transp_v6.h>
 46 #include <net/ip6_route.h>
 47 #include <net/addrconf.h>
 48 #include <net/inet_common.h>
 49 #include <net/tcp.h>
 50 #include <net/udp.h>
 51 
 52 #include <asm/uaccess.h>
 53 
 54 struct ipv6_mib ipv6_statistics[NR_CPUS*2];
 55 
 56 struct packet_type ipv6_packet_type =
 57 {
 58         __constant_htons(ETH_P_IPV6), 
 59         NULL,                                   /* All devices */
 60         ipv6_rcv,
 61         (void*)1,
 62         NULL
 63 };
 64 
 65 /*
 66  *      addrconf module should be notifyed of a device going up
 67  */
 68 static struct notifier_block ipv6_dev_notf = {
 69         addrconf_notify,
 70         NULL,
 71         0
 72 };
 73 
 74 struct ip6_ra_chain *ip6_ra_chain;
 75 rwlock_t ip6_ra_lock = RW_LOCK_UNLOCKED;
 76 
 77 int ip6_ra_control(struct sock *sk, int sel, void (*destructor)(struct sock *))
 78 {
 79         struct ip6_ra_chain *ra, *new_ra, **rap;
 80 
 81         /* RA packet may be delivered ONLY to IPPROTO_RAW socket */
 82         if (sk->type != SOCK_RAW || sk->num != IPPROTO_RAW)
 83                 return -EINVAL;
 84 
 85         new_ra = (sel>=0) ? kmalloc(sizeof(*new_ra), GFP_KERNEL) : NULL;
 86 
 87         write_lock_bh(&ip6_ra_lock);
 88         for (rap = &ip6_ra_chain; (ra=*rap) != NULL; rap = &ra->next) {
 89                 if (ra->sk == sk) {
 90                         if (sel>=0) {
 91                                 write_unlock_bh(&ip6_ra_lock);
 92                                 if (new_ra)
 93                                         kfree(new_ra);
 94                                 return -EADDRINUSE;
 95                         }
 96 
 97                         *rap = ra->next;
 98                         write_unlock_bh(&ip6_ra_lock);
 99 
100                         if (ra->destructor)
101                                 ra->destructor(sk);
102                         sock_put(sk);
103                         kfree(ra);
104                         return 0;
105                 }
106         }
107         if (new_ra == NULL) {
108                 write_unlock_bh(&ip6_ra_lock);
109                 return -ENOBUFS;
110         }
111         new_ra->sk = sk;
112         new_ra->sel = sel;
113         new_ra->destructor = destructor;
114         new_ra->next = ra;
115         *rap = new_ra;
116         sock_hold(sk);
117         write_unlock_bh(&ip6_ra_lock);
118         return 0;
119 }
120 
121 
122 int ipv6_setsockopt(struct sock *sk, int level, int optname, char *optval, 
123                     int optlen)
124 {
125         struct ipv6_pinfo *np = &sk->net_pinfo.af_inet6;
126         int val, valbool;
127         int retv = -ENOPROTOOPT;
128 
129         if(level==SOL_IP && sk->type != SOCK_RAW)
130                 return udp_prot.setsockopt(sk, level, optname, optval, optlen);
131 
132         if(level!=SOL_IPV6)
133                 goto out;
134 
135         if (optval == NULL)
136                 val=0;
137         else if (get_user(val, (int *) optval))
138                 return -EFAULT;
139 
140         valbool = (val!=0);
141 
142         lock_sock(sk);
143 
144         switch (optname) {
145 
146         case IPV6_ADDRFORM:
147                 if (val == PF_INET) {
148                         struct ipv6_txoptions *opt;
149                         struct sk_buff *pktopt;
150 
151                         if (sk->protocol != IPPROTO_UDP &&
152                             sk->protocol != IPPROTO_TCP)
153                                 break;
154 
155                         if (sk->state != TCP_ESTABLISHED) {
156                                 retv = -ENOTCONN;
157                                 break;
158                         }
159 
160                         if (!(ipv6_addr_type(&np->daddr) & IPV6_ADDR_MAPPED)) {
161                                 retv = -EADDRNOTAVAIL;
162                                 break;
163                         }
164 
165                         fl6_free_socklist(sk);
166                         ipv6_sock_mc_close(sk);
167 
168                         if (sk->protocol == IPPROTO_TCP) {
169                                 struct tcp_opt *tp = &(sk->tp_pinfo.af_tcp);
170 
171                                 local_bh_disable();
172                                 sock_prot_dec_use(sk->prot);
173                                 sock_prot_inc_use(&tcp_prot);
174                                 local_bh_enable();
175                                 sk->prot = &tcp_prot;
176                                 tp->af_specific = &ipv4_specific;
177                                 sk->socket->ops = &inet_stream_ops;
178                                 sk->family = PF_INET;
179                                 tcp_sync_mss(sk, tp->pmtu_cookie);
180                         } else {
181                                 local_bh_disable();
182                                 sock_prot_dec_use(sk->prot);
183                                 sock_prot_inc_use(&udp_prot);
184                                 local_bh_enable();
185                                 sk->prot = &udp_prot;
186                                 sk->socket->ops = &inet_dgram_ops;
187                                 sk->family = PF_INET;
188                         }
189                         opt = xchg(&np->opt, NULL);
190                         if (opt)
191                                 sock_kfree_s(sk, opt, opt->tot_len);
192                         pktopt = xchg(&np->pktoptions, NULL);
193                         if (pktopt)
194                                 kfree_skb(pktopt);
195 
196                         sk->destruct = inet_sock_destruct;
197 #ifdef INET_REFCNT_DEBUG
198                         atomic_dec(&inet6_sock_nr);
199 #endif
200                         MOD_DEC_USE_COUNT;
201                         retv = 0;
202                         break;
203                 }
204                 goto e_inval;
205 
206         case IPV6_PKTINFO:
207                 np->rxopt.bits.rxinfo = valbool;
208                 retv = 0;
209                 break;
210 
211         case IPV6_HOPLIMIT:
212                 np->rxopt.bits.rxhlim = valbool;
213                 retv = 0;
214                 break;
215 
216         case IPV6_RTHDR:
217                 if (val < 0 || val > 2)
218                         goto e_inval;
219                 np->rxopt.bits.srcrt = val;
220                 retv = 0;
221                 break;
222 
223         case IPV6_HOPOPTS:
224                 np->rxopt.bits.hopopts = valbool;
225                 retv = 0;
226                 break;
227 
228         case IPV6_AUTHHDR:
229                 np->rxopt.bits.authhdr = valbool;
230                 retv = 0;
231                 break;
232 
233         case IPV6_DSTOPTS:
234                 np->rxopt.bits.dstopts = valbool;
235                 retv = 0;
236                 break;
237 
238         case IPV6_FLOWINFO:
239                 np->rxopt.bits.rxflow = valbool;
240                 retv = 0;
241                 break;
242 
243         case IPV6_PKTOPTIONS:
244         {
245                 struct ipv6_txoptions *opt = NULL;
246                 struct msghdr msg;
247                 struct flowi fl;
248                 int junk;
249 
250                 fl.fl6_flowlabel = 0;
251                 fl.oif = sk->bound_dev_if;
252 
253                 if (optlen == 0)
254                         goto update;
255 
256                 opt = sock_kmalloc(sk, sizeof(*opt) + optlen, GFP_KERNEL);
257                 retv = -ENOBUFS;
258                 if (opt == NULL)
259                         break;
260 
261                 memset(opt, 0, sizeof(*opt));
262                 opt->tot_len = sizeof(*opt) + optlen;
263                 retv = -EFAULT;
264                 if (copy_from_user(opt+1, optval, optlen))
265                         goto done;
266 
267                 msg.msg_controllen = optlen;
268                 msg.msg_control = (void*)(opt+1);
269 
270                 retv = datagram_send_ctl(&msg, &fl, opt, &junk);
271                 if (retv)
272                         goto done;
273 update:
274                 retv = 0;
275                 if (sk->type == SOCK_STREAM) {
276                         if (opt) {
277                                 struct tcp_opt *tp = &sk->tp_pinfo.af_tcp;
278                                 if (!((1<<sk->state)&(TCPF_LISTEN|TCPF_CLOSE))
279                                     && sk->daddr != LOOPBACK4_IPV6) {
280                                         tp->ext_header_len = opt->opt_flen + opt->opt_nflen;
281                                         tcp_sync_mss(sk, tp->pmtu_cookie);
282                                 }
283                         }
284                         opt = xchg(&np->opt, opt);
285                         sk_dst_reset(sk);
286                 } else {
287                         write_lock(&sk->dst_lock);
288                         opt = xchg(&np->opt, opt);
289                         write_unlock(&sk->dst_lock);
290                         sk_dst_reset(sk);
291                 }
292 
293 done:
294                 if (opt)
295                         sock_kfree_s(sk, opt, opt->tot_len);
296                 break;
297         }
298         case IPV6_UNICAST_HOPS:
299                 if (val > 255 || val < -1)
300                         goto e_inval;
301                 np->hop_limit = val;
302                 retv = 0;
303                 break;
304 
305         case IPV6_MULTICAST_HOPS:
306                 if (sk->type == SOCK_STREAM)
307                         goto e_inval;
308                 if (val > 255 || val < -1)
309                         goto e_inval;
310                 np->mcast_hops = val;
311                 retv = 0;
312                 break;
313 
314         case IPV6_MULTICAST_LOOP:
315                 np->mc_loop = valbool;
316                 retv = 0;
317                 break;
318 
319         case IPV6_MULTICAST_IF:
320                 if (sk->type == SOCK_STREAM)
321                         goto e_inval;
322                 if (sk->bound_dev_if && sk->bound_dev_if != val)
323                         goto e_inval;
324 
325                 if (__dev_get_by_index(val) == NULL) {
326                         retv = -ENODEV;
327                         break;
328                 }
329                 np->mcast_oif = val;
330                 retv = 0;
331                 break;
332         case IPV6_ADD_MEMBERSHIP:
333         case IPV6_DROP_MEMBERSHIP:
334         {
335                 struct ipv6_mreq mreq;
336 
337                 retv = -EFAULT;
338                 if (copy_from_user(&mreq, optval, sizeof(struct ipv6_mreq)))
339                         break;
340 
341                 if (optname == IPV6_ADD_MEMBERSHIP)
342                         retv = ipv6_sock_mc_join(sk, mreq.ipv6mr_ifindex, &mreq.ipv6mr_multiaddr);
343                 else
344                         retv = ipv6_sock_mc_drop(sk, mreq.ipv6mr_ifindex, &mreq.ipv6mr_multiaddr);
345                 break;
346         }
347         case IPV6_ROUTER_ALERT:
348                 retv = ip6_ra_control(sk, val, NULL);
349                 break;
350         case IPV6_MTU_DISCOVER:
351                 if (val<0 || val>2)
352                         goto e_inval;
353                 np->pmtudisc = val;
354                 retv = 0;
355                 break;
356         case IPV6_MTU:
357                 if (val && val < IPV6_MIN_MTU)
358                         goto e_inval;
359                 np->frag_size = val;
360                 retv = 0;
361                 break;
362         case IPV6_RECVERR:
363                 np->recverr = valbool;
364                 if (!val)
365                         skb_queue_purge(&sk->error_queue);
366                 retv = 0;
367                 break;
368         case IPV6_FLOWINFO_SEND:
369                 np->sndflow = valbool;
370                 retv = 0;
371                 break;
372         case IPV6_FLOWLABEL_MGR:
373                 retv = ipv6_flowlabel_opt(sk, optval, optlen);
374                 break;
375 
376 #ifdef CONFIG_NETFILTER
377         default:
378                 retv = nf_setsockopt(sk, PF_INET6, optname, optval, 
379                                             optlen);
380                 break;
381 #endif
382 
383         }
384         release_sock(sk);
385 
386 out:
387         return retv;
388 
389 e_inval:
390         release_sock(sk);
391         return -EINVAL;
392 }
393 
394 int ipv6_getsockopt(struct sock *sk, int level, int optname, char *optval, 
395                     int *optlen)
396 {
397         struct ipv6_pinfo *np = &sk->net_pinfo.af_inet6;
398         int len;
399         int val;
400 
401         if(level==SOL_IP && sk->type != SOCK_RAW)
402                 return udp_prot.getsockopt(sk, level, optname, optval, optlen);
403         if(level!=SOL_IPV6)
404                 return -ENOPROTOOPT;
405         if (get_user(len, optlen))
406                 return -EFAULT;
407         switch (optname) {
408         case IPV6_PKTOPTIONS:
409         {
410                 struct msghdr msg;
411                 struct sk_buff *skb;
412 
413                 if (sk->type != SOCK_STREAM)
414                         return -ENOPROTOOPT;
415 
416                 msg.msg_control = optval;
417                 msg.msg_controllen = len;
418                 msg.msg_flags = 0;
419 
420                 lock_sock(sk);
421                 skb = np->pktoptions;
422                 if (skb)
423                         atomic_inc(&skb->users);
424                 release_sock(sk);
425 
426                 if (skb) {
427                         int err = datagram_recv_ctl(sk, &msg, skb);
428                         kfree_skb(skb);
429                         if (err)
430                                 return err;
431                 } else {
432                         if (np->rxopt.bits.rxinfo) {
433                                 struct in6_pktinfo src_info;
434                                 src_info.ipi6_ifindex = np->mcast_oif;
435                                 ipv6_addr_copy(&src_info.ipi6_addr, &np->daddr);
436                                 put_cmsg(&msg, SOL_IPV6, IPV6_PKTINFO, sizeof(src_info), &src_info);
437                         }
438                         if (np->rxopt.bits.rxhlim) {
439                                 int hlim = np->mcast_hops;
440                                 put_cmsg(&msg, SOL_IPV6, IPV6_HOPLIMIT, sizeof(hlim), &hlim);
441                         }
442                 }
443                 len -= msg.msg_controllen;
444                 return put_user(len, optlen);
445         }
446         case IPV6_MTU:
447         {
448                 struct dst_entry *dst;
449                 val = 0;        
450                 lock_sock(sk);
451                 dst = sk_dst_get(sk);
452                 if (dst) {
453                         val = dst->pmtu;
454                         dst_release(dst);
455                 }
456                 release_sock(sk);
457                 if (!val)
458                         return -ENOTCONN;
459                 break;
460         }
461 
462         case IPV6_PKTINFO:
463                 val = np->rxopt.bits.rxinfo;
464                 break;
465 
466         case IPV6_HOPLIMIT:
467                 val = np->rxopt.bits.rxhlim;
468                 break;
469 
470         case IPV6_RTHDR:
471                 val = np->rxopt.bits.srcrt;
472                 break;
473 
474         case IPV6_HOPOPTS:
475                 val = np->rxopt.bits.hopopts;
476                 break;
477 
478         case IPV6_AUTHHDR:
479                 val = np->rxopt.bits.authhdr;
480                 break;
481 
482         case IPV6_DSTOPTS:
483                 val = np->rxopt.bits.dstopts;
484                 break;
485 
486         case IPV6_FLOWINFO:
487                 val = np->rxopt.bits.rxflow;
488                 break;
489 
490         case IPV6_UNICAST_HOPS:
491                 val = np->hop_limit;
492                 break;
493 
494         case IPV6_MULTICAST_HOPS:
495                 val = np->mcast_hops;
496                 break;
497 
498         case IPV6_MULTICAST_LOOP:
499                 val = np->mc_loop;
500                 break;
501 
502         case IPV6_MULTICAST_IF:
503                 val = np->mcast_oif;
504                 break;
505 
506         case IPV6_MTU_DISCOVER:
507                 val = np->pmtudisc;
508                 break;
509 
510         case IPV6_RECVERR:
511                 val = np->recverr;
512                 break;
513 
514         case IPV6_FLOWINFO_SEND:
515                 val = np->sndflow;
516                 break;
517 
518         default:
519 #ifdef CONFIG_NETFILTER
520                 lock_sock(sk);
521                 val = nf_getsockopt(sk, PF_INET6, optname, optval, 
522                                     &len);
523                 release_sock(sk);
524                 if (val >= 0)
525                         val = put_user(len, optlen);
526                 return val;
527 #else
528                 return -EINVAL;
529 #endif
530         }
531         len=min(sizeof(int),len);
532         if(put_user(len, optlen))
533                 return -EFAULT;
534         if(copy_to_user(optval,&val,len))
535                 return -EFAULT;
536         return 0;
537 }
538 
539 #if defined(MODULE) && defined(CONFIG_SYSCTL)
540 
541 /*
542  *      sysctl registration functions defined in sysctl_net_ipv6.c
543  */
544 
545 extern void ipv6_sysctl_register(void);
546 extern void ipv6_sysctl_unregister(void);
547 #endif
548 
549 void __init ipv6_packet_init(void)
550 {
551         dev_add_pack(&ipv6_packet_type);
552 }
553 
554 void __init ipv6_netdev_notif_init(void)
555 {
556         register_netdevice_notifier(&ipv6_dev_notf);
557 }
558 
559 #ifdef MODULE
560 void ipv6_packet_cleanup(void)
561 {
562         dev_remove_pack(&ipv6_packet_type);
563 }
564 
565 void ipv6_netdev_notif_cleanup(void)
566 {
567         unregister_netdevice_notifier(&ipv6_dev_notf);
568 }
569 #endif
570 

~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~ [ freetext search ] ~ [ file search ] ~

This page was automatically generated by the LXR engine.
Visit the LXR main site for more information.