home *** CD-ROM | disk | FTP | other *** search
/ Il CD di internet / CD.iso / SOURCE / KERNEL-S / V1.2 / LINUX-1.2 / LINUX-1 / linux / net / inet / igmp.c < prev    next >
Encoding:
C/C++ Source or Header  |  1995-02-12  |  7.9 KB  |  391 lines

  1. /*
  2.  *    Linux NET3:    Internet Gateway Management Protocol  [IGMP]
  3.  *
  4.  *    Authors:
  5.  *        Alan Cox <Alan.Cox@linux.org>    
  6.  *
  7.  *    WARNING:
  8.  *        This is a 'preliminary' implementation... on your own head
  9.  *    be it.
  10.  *
  11.  *    This program is free software; you can redistribute it and/or
  12.  *    modify it under the terms of the GNU General Public License
  13.  *    as published by the Free Software Foundation; either version
  14.  *    2 of the License, or (at your option) any later version.
  15.  */
  16.  
  17.  
  18. #include <asm/segment.h>
  19. #include <asm/system.h>
  20. #include <linux/types.h>
  21. #include <linux/kernel.h>
  22. #include <linux/sched.h>
  23. #include <linux/string.h>
  24. #include <linux/config.h>
  25. #include <linux/socket.h>
  26. #include <linux/sockios.h>
  27. #include <linux/in.h>
  28. #include <linux/inet.h>
  29. #include <linux/netdevice.h>
  30. #include "ip.h"
  31. #include "protocol.h"
  32. #include "route.h"
  33. #include <linux/skbuff.h>
  34. #include "sock.h"
  35. #include <linux/igmp.h>
  36.  
  37. #ifdef CONFIG_IP_MULTICAST
  38.  
  39.  
  40. /*
  41.  *    Timer management
  42.  */
  43.  
  44.  
  45. static void igmp_stop_timer(struct ip_mc_list *im)
  46. {
  47.     del_timer(&im->timer);
  48.     im->tm_running=0;
  49. }
  50.  
  51. static int random(void)
  52. {
  53.     static unsigned long seed=152L;
  54.     seed=seed*69069L+1;
  55.     return seed^jiffies;
  56. }
  57.  
  58.  
  59. static void igmp_start_timer(struct ip_mc_list *im)
  60. {
  61.     int tv;
  62.     if(im->tm_running)
  63.         return;
  64.     tv=random()%(10*HZ);        /* Pick a number any number 8) */
  65.     im->timer.expires=tv;
  66.     im->tm_running=1;
  67.     add_timer(&im->timer);
  68. }
  69.  
  70. /*
  71.  *    Send an IGMP report.
  72.  */
  73.  
  74. #define MAX_IGMP_SIZE (sizeof(struct igmphdr)+sizeof(struct iphdr)+64)
  75.  
  76. static void igmp_send_report(struct device *dev, unsigned long address, int type)
  77. {
  78.     struct sk_buff *skb=alloc_skb(MAX_IGMP_SIZE, GFP_ATOMIC);
  79.     int tmp;
  80.     struct igmphdr *igh;
  81.     
  82.     if(skb==NULL)
  83.         return;
  84.     tmp=ip_build_header(skb, INADDR_ANY, address, &dev, IPPROTO_IGMP, NULL,
  85.                 skb->mem_len, 0, 1);
  86.     if(tmp<0)
  87.     {
  88.         kfree_skb(skb, FREE_WRITE);
  89.         return;
  90.     }
  91.     igh=(struct igmphdr *)(skb->data+tmp);
  92.     skb->len=tmp+sizeof(*igh);
  93.     igh->csum=0;
  94.     igh->unused=0;
  95.     igh->type=type;
  96.     igh->group=address;
  97.     igh->csum=ip_compute_csum((void *)igh,sizeof(*igh));
  98.     ip_queue_xmit(NULL,dev,skb,1);
  99. }
  100.  
  101.  
  102. static void igmp_timer_expire(unsigned long data)
  103. {
  104.     struct ip_mc_list *im=(struct ip_mc_list *)data;
  105.     igmp_stop_timer(im);
  106.     igmp_send_report(im->interface, im->multiaddr, IGMP_HOST_MEMBERSHIP_REPORT);
  107. }
  108.  
  109. static void igmp_init_timer(struct ip_mc_list *im)
  110. {
  111.     im->tm_running=0;
  112.     init_timer(&im->timer);
  113.     im->timer.data=(unsigned long)im;
  114.     im->timer.function=&igmp_timer_expire;
  115. }
  116.     
  117.  
  118. static void igmp_heard_report(struct device *dev, unsigned long address)
  119. {
  120.     struct ip_mc_list *im;
  121.     for(im=dev->ip_mc_list;im!=NULL;im=im->next)
  122.         if(im->multiaddr==address)
  123.             igmp_stop_timer(im);
  124. }
  125.  
  126. static void igmp_heard_query(struct device *dev)
  127. {
  128.     struct ip_mc_list *im;
  129.     for(im=dev->ip_mc_list;im!=NULL;im=im->next)
  130.         if(!im->tm_running && im->multiaddr!=IGMP_ALL_HOSTS)
  131.             igmp_start_timer(im);
  132. }
  133.  
  134. /*
  135.  *    Map a multicast IP onto multicast MAC for type ethernet.
  136.  */
  137.  
  138. static void ip_mc_map(unsigned long addr, char *buf)
  139. {
  140.     addr=ntohl(addr);
  141.     buf[0]=0x01;
  142.     buf[1]=0x00;
  143.     buf[2]=0x5e;
  144.     buf[5]=addr&0xFF;
  145.     addr>>=8;
  146.     buf[4]=addr&0xFF;
  147.     addr>>=8;
  148.     buf[3]=addr&0x7F;
  149. }
  150.  
  151. /*
  152.  *    Add a filter to a device
  153.  */
  154.  
  155. void ip_mc_filter_add(struct device *dev, unsigned long addr)
  156. {
  157.     char buf[6];
  158.     if(dev->type!=ARPHRD_ETHER)
  159.         return;    /* Only do ethernet now */
  160.     ip_mc_map(addr,buf);    
  161.     dev_mc_add(dev,buf,ETH_ALEN,0);
  162. }
  163.  
  164. /*
  165.  *    Remove a filter from a device
  166.  */
  167.  
  168. void ip_mc_filter_del(struct device *dev, unsigned long addr)
  169. {
  170.     char buf[6];
  171.     if(dev->type!=ARPHRD_ETHER)
  172.         return;    /* Only do ethernet now */
  173.     ip_mc_map(addr,buf);    
  174.     dev_mc_delete(dev,buf,ETH_ALEN,0);
  175. }
  176.  
  177. static void igmp_group_dropped(struct ip_mc_list *im)
  178. {
  179.     del_timer(&im->timer);
  180.     igmp_send_report(im->interface, im->multiaddr, IGMP_HOST_LEAVE_MESSAGE);
  181.     ip_mc_filter_del(im->interface, im->multiaddr);
  182. /*    printk("Left group %lX\n",im->multiaddr);*/
  183. }
  184.  
  185. static void igmp_group_added(struct ip_mc_list *im)
  186. {
  187.     igmp_init_timer(im);
  188.     igmp_send_report(im->interface, im->multiaddr, IGMP_HOST_MEMBERSHIP_REPORT);
  189.     ip_mc_filter_add(im->interface, im->multiaddr);
  190. /*    printk("Joined group %lX\n",im->multiaddr);*/
  191. }
  192.  
  193. int igmp_rcv(struct sk_buff *skb, struct device *dev, struct options *opt,
  194.     unsigned long daddr, unsigned short len, unsigned long saddr, int redo,
  195.     struct inet_protocol *protocol)
  196. {
  197.     /* This basically follows the spec line by line -- see RFC1112 */
  198.     struct igmphdr *igh=(struct igmphdr *)skb->h.raw;
  199.     
  200.     if(skb->ip_hdr->ttl!=1 || ip_compute_csum((void *)igh,sizeof(*igh)))
  201.     {
  202.         kfree_skb(skb, FREE_READ);
  203.         return 0;
  204.     }
  205.     
  206.     if(igh->type==IGMP_HOST_MEMBERSHIP_QUERY && daddr==IGMP_ALL_HOSTS)
  207.         igmp_heard_query(dev);
  208.     if(igh->type==IGMP_HOST_MEMBERSHIP_REPORT && daddr==igh->group)
  209.         igmp_heard_report(dev,igh->group);
  210.     kfree_skb(skb, FREE_READ);
  211.     return 0;
  212. }
  213.  
  214. /*
  215.  *    Multicast list managers
  216.  */
  217.  
  218.  
  219. /*
  220.  *    A socket has joined a multicast group on device dev.
  221.  */
  222.   
  223. static void ip_mc_inc_group(struct device *dev, unsigned long addr)
  224. {
  225.     struct ip_mc_list *i;
  226.     for(i=dev->ip_mc_list;i!=NULL;i=i->next)
  227.     {
  228.         if(i->multiaddr==addr)
  229.         {
  230.             i->users++;
  231.             return;
  232.         }
  233.     }
  234.     i=(struct ip_mc_list *)kmalloc(sizeof(*i), GFP_KERNEL);
  235.     if(!i)
  236.         return;
  237.     i->users=1;
  238.     i->interface=dev;
  239.     i->multiaddr=addr;
  240.     i->next=dev->ip_mc_list;
  241.     igmp_group_added(i);
  242.     dev->ip_mc_list=i;
  243. }
  244.  
  245. /*
  246.  *    A socket has left a multicast group on device dev
  247.  */
  248.     
  249. static void ip_mc_dec_group(struct device *dev, unsigned long addr)
  250. {
  251.     struct ip_mc_list **i;
  252.     for(i=&(dev->ip_mc_list);(*i)!=NULL;i=&(*i)->next)
  253.     {
  254.         if((*i)->multiaddr==addr)
  255.         {
  256.             if(--((*i)->users))
  257.                 return;
  258.             else
  259.             {
  260.                 struct ip_mc_list *tmp= *i;
  261.                 igmp_group_dropped(tmp);
  262.                 *i=(*i)->next;
  263.                 kfree_s(tmp,sizeof(*tmp));
  264.             }
  265.         }
  266.     }
  267. }
  268.  
  269. /*
  270.  *    Device going down: Clean up.
  271.  */
  272.  
  273. void ip_mc_drop_device(struct device *dev)
  274. {
  275.     struct ip_mc_list *i;
  276.     struct ip_mc_list *j;
  277.     for(i=dev->ip_mc_list;i!=NULL;i=j)
  278.     {
  279.         j=i->next;
  280.         kfree_s(i,sizeof(*i));
  281.     }
  282.     dev->ip_mc_list=NULL;
  283. }
  284.  
  285. /*
  286.  *    Device going up. Make sure it is in all hosts
  287.  */
  288.  
  289. void ip_mc_allhost(struct device *dev)
  290. {
  291.     struct ip_mc_list *i;
  292.     for(i=dev->ip_mc_list;i!=NULL;i=i->next)
  293.         if(i->multiaddr==IGMP_ALL_HOSTS)
  294.             return;
  295.     i=(struct ip_mc_list *)kmalloc(sizeof(*i), GFP_KERNEL);
  296.     if(!i)
  297.         return;
  298.     i->users=1;
  299.     i->interface=dev;
  300.     i->multiaddr=IGMP_ALL_HOSTS;
  301.     i->next=dev->ip_mc_list;
  302.     dev->ip_mc_list=i;
  303.     ip_mc_filter_add(i->interface, i->multiaddr);
  304.  
  305. }    
  306.  
  307. /*
  308.  *    Join a socket to a group
  309.  */
  310.  
  311. int ip_mc_join_group(struct sock *sk , struct device *dev, unsigned long addr)
  312. {
  313.     int unused= -1;
  314.     int i;
  315.     if(!MULTICAST(addr))
  316.         return -EINVAL;
  317.     if(!(dev->flags&IFF_MULTICAST))
  318.         return -EADDRNOTAVAIL;
  319.     if(sk->ip_mc_list==NULL)
  320.     {
  321.         if((sk->ip_mc_list=(struct ip_mc_socklist *)kmalloc(sizeof(*sk->ip_mc_list), GFP_KERNEL))==NULL)
  322.             return -ENOMEM;
  323.         memset(sk->ip_mc_list,'\0',sizeof(*sk->ip_mc_list));
  324.     }
  325.     for(i=0;i<IP_MAX_MEMBERSHIPS;i++)
  326.     {
  327.         if(sk->ip_mc_list->multiaddr[i]==addr && sk->ip_mc_list->multidev[i]==dev)
  328.             return -EADDRINUSE;
  329.         if(sk->ip_mc_list->multidev[i]==NULL)
  330.             unused=i;
  331.     }
  332.     
  333.     if(unused==-1)
  334.         return -ENOBUFS;
  335.     sk->ip_mc_list->multiaddr[unused]=addr;
  336.     sk->ip_mc_list->multidev[unused]=dev;
  337.     ip_mc_inc_group(dev,addr);
  338.     return 0;
  339. }
  340.  
  341. /*
  342.  *    Ask a socket to leave a group.
  343.  */
  344.  
  345. int ip_mc_leave_group(struct sock *sk, struct device *dev, unsigned long addr)
  346. {
  347.     int i;
  348.     if(!MULTICAST(addr))
  349.         return -EINVAL;
  350.     if(!(dev->flags&IFF_MULTICAST))
  351.         return -EADDRNOTAVAIL;
  352.     if(sk->ip_mc_list==NULL)
  353.         return -EADDRNOTAVAIL;
  354.         
  355.     for(i=0;i<IP_MAX_MEMBERSHIPS;i++)
  356.     {
  357.         if(sk->ip_mc_list->multiaddr[i]==addr && sk->ip_mc_list->multidev[i]==dev)
  358.         {
  359.             sk->ip_mc_list->multidev[i]=NULL;
  360.             ip_mc_dec_group(dev,addr);
  361.             return 0;
  362.         }
  363.     }
  364.     return -EADDRNOTAVAIL;
  365. }
  366.  
  367. /*
  368.  *    A socket is closing.
  369.  */
  370.  
  371. void ip_mc_drop_socket(struct sock *sk)
  372. {
  373.     int i;
  374.     
  375.     if(sk->ip_mc_list==NULL)
  376.         return;
  377.         
  378.     for(i=0;i<IP_MAX_MEMBERSHIPS;i++)
  379.     {
  380.         if(sk->ip_mc_list->multidev[i])
  381.         {
  382.             ip_mc_dec_group(sk->ip_mc_list->multidev[i], sk->ip_mc_list->multiaddr[i]);
  383.             sk->ip_mc_list->multidev[i]=NULL;
  384.         }
  385.     }
  386.     kfree_s(sk->ip_mc_list,sizeof(*sk->ip_mc_list));
  387.     sk->ip_mc_list=NULL;
  388. }
  389.  
  390. #endif
  391.