igmp.c
上传用户:lgb322
上传日期:2013-02-24
资源大小:30529k
文件大小:19k
源码类别:

嵌入式Linux

开发平台:

Unix_Linux

  1. /*
  2.  * Linux NET3: Internet Group Management Protocol  [IGMP]
  3.  *
  4.  * This code implements the IGMP protocol as defined in RFC1112. There has
  5.  * been a further revision of this protocol since which is now supported.
  6.  *
  7.  * If you have trouble with this module be careful what gcc you have used,
  8.  * the older version didn't come out right using gcc 2.5.8, the newer one
  9.  * seems to fall out with gcc 2.6.2.
  10.  *
  11.  * Version: $Id: igmp.c,v 1.46 2001/07/27 09:27:29 davem Exp $
  12.  *
  13.  * Authors:
  14.  * Alan Cox <Alan.Cox@linux.org>
  15.  *
  16.  * This program is free software; you can redistribute it and/or
  17.  * modify it under the terms of the GNU General Public License
  18.  * as published by the Free Software Foundation; either version
  19.  * 2 of the License, or (at your option) any later version.
  20.  *
  21.  * Fixes:
  22.  *
  23.  * Alan Cox : Added lots of __inline__ to optimise
  24.  * the memory usage of all the tiny little
  25.  * functions.
  26.  * Alan Cox : Dumped the header building experiment.
  27.  * Alan Cox : Minor tweaks ready for multicast routing
  28.  * and extended IGMP protocol.
  29.  * Alan Cox : Removed a load of inline directives. Gcc 2.5.8
  30.  * writes utterly bogus code otherwise (sigh)
  31.  * fixed IGMP loopback to behave in the manner
  32.  * desired by mrouted, fixed the fact it has been
  33.  * broken since 1.3.6 and cleaned up a few minor
  34.  * points.
  35.  *
  36.  * Chih-Jen Chang : Tried to revise IGMP to Version 2
  37.  * Tsu-Sheng Tsao E-mail: chihjenc@scf.usc.edu and tsusheng@scf.usc.edu
  38.  * The enhancements are mainly based on Steve Deering's 
  39.  *  ipmulti-3.5 source code.
  40.  * Chih-Jen Chang : Added the igmp_get_mrouter_info and
  41.  * Tsu-Sheng Tsao igmp_set_mrouter_info to keep track of
  42.  * the mrouted version on that device.
  43.  * Chih-Jen Chang : Added the max_resp_time parameter to
  44.  * Tsu-Sheng Tsao igmp_heard_query(). Using this parameter
  45.  * to identify the multicast router version
  46.  * and do what the IGMP version 2 specified.
  47.  * Chih-Jen Chang : Added a timer to revert to IGMP V2 router
  48.  * Tsu-Sheng Tsao if the specified time expired.
  49.  * Alan Cox : Stop IGMP from 0.0.0.0 being accepted.
  50.  * Alan Cox : Use GFP_ATOMIC in the right places.
  51.  * Christian Daudt : igmp timer wasn't set for local group
  52.  * memberships but was being deleted, 
  53.  * which caused a "del_timer() called 
  54.  * from %p with timer not initializedn"
  55.  * message (960131).
  56.  * Christian Daudt : removed del_timer from 
  57.  * igmp_timer_expire function (960205).
  58.  *             Christian Daudt :       igmp_heard_report now only calls
  59.  *                                     igmp_timer_expire if tm->running is
  60.  *                                     true (960216).
  61.  * Malcolm Beattie : ttl comparison wrong in igmp_rcv made
  62.  * igmp_heard_query never trigger. Expiry
  63.  * miscalculation fixed in igmp_heard_query
  64.  * and random() made to return unsigned to
  65.  * prevent negative expiry times.
  66.  * Alexey Kuznetsov: Wrong group leaving behaviour, backport
  67.  * fix from pending 2.1.x patches.
  68.  * Alan Cox: Forget to enable FDDI support earlier.
  69.  * Alexey Kuznetsov: Fixed leaving groups on device down.
  70.  * Alexey Kuznetsov: Accordance to igmp-v2-06 draft.
  71.  */
  72. #include <linux/config.h>
  73. #include <asm/uaccess.h>
  74. #include <asm/system.h>
  75. #include <linux/types.h>
  76. #include <linux/kernel.h>
  77. #include <linux/sched.h>
  78. #include <linux/string.h>
  79. #include <linux/socket.h>
  80. #include <linux/sockios.h>
  81. #include <linux/in.h>
  82. #include <linux/inet.h>
  83. #include <linux/netdevice.h>
  84. #include <linux/skbuff.h>
  85. #include <linux/inetdevice.h>
  86. #include <linux/igmp.h>
  87. #include <linux/if_arp.h>
  88. #include <linux/rtnetlink.h>
  89. #include <net/ip.h>
  90. #include <net/protocol.h>
  91. #include <net/route.h>
  92. #include <net/sock.h>
  93. #include <net/checksum.h>
  94. #include <linux/netfilter_ipv4.h>
  95. #ifdef CONFIG_IP_MROUTE
  96. #include <linux/mroute.h>
  97. #endif
  98. #define IP_MAX_MEMBERSHIPS 20
  99. #ifdef CONFIG_IP_MULTICAST
  100. /* Parameter names and values are taken from igmp-v2-06 draft */
  101. #define IGMP_V1_Router_Present_Timeout (400*HZ)
  102. #define IGMP_Unsolicited_Report_Interval (10*HZ)
  103. #define IGMP_Query_Response_Interval (10*HZ)
  104. #define IGMP_Unsolicited_Report_Count 2
  105. #define IGMP_Initial_Report_Delay (1)
  106. /* IGMP_Initial_Report_Delay is not from IGMP specs!
  107.  * IGMP specs require to report membership immediately after
  108.  * joining a group, but we delay the first report by a
  109.  * small interval. It seems more natural and still does not
  110.  * contradict to specs provided this delay is small enough.
  111.  */
  112. #define IGMP_V1_SEEN(in_dev) ((in_dev)->mr_v1_seen && (long)(jiffies - (in_dev)->mr_v1_seen) < 0)
  113. #endif
  114. static void ip_ma_put(struct ip_mc_list *im)
  115. {
  116. if (atomic_dec_and_test(&im->refcnt)) {
  117. in_dev_put(im->interface);
  118. kfree(im);
  119. }
  120. }
  121. #ifdef CONFIG_IP_MULTICAST
  122. /*
  123.  * Timer management
  124.  */
  125. static __inline__ void igmp_stop_timer(struct ip_mc_list *im)
  126. {
  127. spin_lock_bh(&im->lock);
  128. if (del_timer(&im->timer))
  129. atomic_dec(&im->refcnt);
  130. im->tm_running=0;
  131. im->reporter = 0;
  132. im->unsolicit_count = 0;
  133. spin_unlock_bh(&im->lock);
  134. }
  135. /* It must be called with locked im->lock */
  136. static void igmp_start_timer(struct ip_mc_list *im, int max_delay)
  137. {
  138. int tv=net_random() % max_delay;
  139. im->tm_running=1;
  140. if (!mod_timer(&im->timer, jiffies+tv+2))
  141. atomic_inc(&im->refcnt);
  142. }
  143. static void igmp_mod_timer(struct ip_mc_list *im, int max_delay)
  144. {
  145. spin_lock_bh(&im->lock);
  146. im->unsolicit_count = 0;
  147. if (del_timer(&im->timer)) {
  148. if ((long)(im->timer.expires-jiffies) < max_delay) {
  149. add_timer(&im->timer);
  150. im->tm_running=1;
  151. spin_unlock_bh(&im->lock);
  152. return;
  153. }
  154. atomic_dec(&im->refcnt);
  155. }
  156. igmp_start_timer(im, max_delay);
  157. spin_unlock_bh(&im->lock);
  158. }
  159. /*
  160.  * Send an IGMP report.
  161.  */
  162. #define IGMP_SIZE (sizeof(struct igmphdr)+sizeof(struct iphdr)+4)
  163. /* Don't just hand NF_HOOK skb->dst->output, in case netfilter hook
  164.    changes route */
  165. static inline int
  166. output_maybe_reroute(struct sk_buff *skb)
  167. {
  168. return skb->dst->output(skb);
  169. }
  170. static int igmp_send_report(struct net_device *dev, u32 group, int type)
  171. {
  172. struct sk_buff *skb;
  173. struct iphdr *iph;
  174. struct igmphdr *ih;
  175. struct rtable *rt;
  176. u32 dst;
  177. /* According to IGMPv2 specs, LEAVE messages are
  178.  * sent to all-routers group.
  179.  */
  180. dst = group;
  181. if (type == IGMP_HOST_LEAVE_MESSAGE)
  182. dst = IGMP_ALL_ROUTER;
  183. if (ip_route_output(&rt, dst, 0, 0, dev->ifindex))
  184. return -1;
  185. if (rt->rt_src == 0) {
  186. ip_rt_put(rt);
  187. return -1;
  188. }
  189. skb=alloc_skb(IGMP_SIZE+dev->hard_header_len+15, GFP_ATOMIC);
  190. if (skb == NULL) {
  191. ip_rt_put(rt);
  192. return -1;
  193. }
  194. skb->dst = &rt->u.dst;
  195. skb_reserve(skb, (dev->hard_header_len+15)&~15);
  196. skb->nh.iph = iph = (struct iphdr *)skb_put(skb, sizeof(struct iphdr)+4);
  197. iph->version  = 4;
  198. iph->ihl      = (sizeof(struct iphdr)+4)>>2;
  199. iph->tos      = 0;
  200. iph->frag_off = __constant_htons(IP_DF);
  201. iph->ttl      = 1;
  202. iph->daddr    = dst;
  203. iph->saddr    = rt->rt_src;
  204. iph->protocol = IPPROTO_IGMP;
  205. iph->tot_len  = htons(IGMP_SIZE);
  206. ip_select_ident(iph, &rt->u.dst, NULL);
  207. ((u8*)&iph[1])[0] = IPOPT_RA;
  208. ((u8*)&iph[1])[1] = 4;
  209. ((u8*)&iph[1])[2] = 0;
  210. ((u8*)&iph[1])[3] = 0;
  211. ip_send_check(iph);
  212. ih = (struct igmphdr *)skb_put(skb, sizeof(struct igmphdr));
  213. ih->type=type;
  214. ih->code=0;
  215. ih->csum=0;
  216. ih->group=group;
  217. ih->csum=ip_compute_csum((void *)ih, sizeof(struct igmphdr));
  218. return NF_HOOK(PF_INET, NF_IP_LOCAL_OUT, skb, NULL, rt->u.dst.dev,
  219.        output_maybe_reroute);
  220. }
  221. static void igmp_timer_expire(unsigned long data)
  222. {
  223. struct ip_mc_list *im=(struct ip_mc_list *)data;
  224. struct in_device *in_dev = im->interface;
  225. int err;
  226. spin_lock(&im->lock);
  227. im->tm_running=0;
  228. if (IGMP_V1_SEEN(in_dev))
  229. err = igmp_send_report(in_dev->dev, im->multiaddr, IGMP_HOST_MEMBERSHIP_REPORT);
  230. else
  231. err = igmp_send_report(in_dev->dev, im->multiaddr, IGMP_HOST_NEW_MEMBERSHIP_REPORT);
  232. /* Failed. Retry later. */
  233. if (err) {
  234. if (!in_dev->dead)
  235. igmp_start_timer(im, IGMP_Unsolicited_Report_Interval);
  236. goto out;
  237. }
  238. if (im->unsolicit_count) {
  239. im->unsolicit_count--;
  240. igmp_start_timer(im, IGMP_Unsolicited_Report_Interval);
  241. }
  242. im->reporter = 1;
  243. out:
  244. spin_unlock(&im->lock);
  245. ip_ma_put(im);
  246. }
  247. static void igmp_heard_report(struct in_device *in_dev, u32 group)
  248. {
  249. struct ip_mc_list *im;
  250. /* Timers are only set for non-local groups */
  251. if (group == IGMP_ALL_HOSTS)
  252. return;
  253. read_lock(&in_dev->lock);
  254. for (im=in_dev->mc_list; im!=NULL; im=im->next) {
  255. if (im->multiaddr == group) {
  256. igmp_stop_timer(im);
  257. break;
  258. }
  259. }
  260. read_unlock(&in_dev->lock);
  261. }
  262. static void igmp_heard_query(struct in_device *in_dev, unsigned char max_resp_time,
  263.      u32 group)
  264. {
  265. struct ip_mc_list *im;
  266. int max_delay;
  267. max_delay = max_resp_time*(HZ/IGMP_TIMER_SCALE);
  268. if (max_resp_time == 0) {
  269. /* Alas, old v1 router presents here. */
  270. max_delay = IGMP_Query_Response_Interval;
  271. in_dev->mr_v1_seen = jiffies + IGMP_V1_Router_Present_Timeout;
  272. group = 0;
  273. }
  274. /*
  275.  * - Start the timers in all of our membership records
  276.  *   that the query applies to for the interface on
  277.  *   which the query arrived excl. those that belong
  278.  *   to a "local" group (224.0.0.X)
  279.  * - For timers already running check if they need to
  280.  *   be reset.
  281.  * - Use the igmp->igmp_code field as the maximum
  282.  *   delay possible
  283.  */
  284. read_lock(&in_dev->lock);
  285. for (im=in_dev->mc_list; im!=NULL; im=im->next) {
  286. if (group && group != im->multiaddr)
  287. continue;
  288. if (im->multiaddr == IGMP_ALL_HOSTS)
  289. continue;
  290. igmp_mod_timer(im, max_delay);
  291. }
  292. read_unlock(&in_dev->lock);
  293. }
  294. int igmp_rcv(struct sk_buff *skb)
  295. {
  296. /* This basically follows the spec line by line -- see RFC1112 */
  297. struct igmphdr *ih = skb->h.igmph;
  298. struct in_device *in_dev = in_dev_get(skb->dev);
  299. int len = skb->len;
  300. if (in_dev==NULL) {
  301. kfree_skb(skb);
  302. return 0;
  303. }
  304. if (skb_is_nonlinear(skb)) {
  305. if (skb_linearize(skb, GFP_ATOMIC) != 0) {
  306. kfree_skb(skb);
  307. return -ENOMEM;
  308. }
  309. ih = skb->h.igmph;
  310. }
  311. if (len < sizeof(struct igmphdr) || ip_compute_csum((void *)ih, len)) {
  312. in_dev_put(in_dev);
  313. kfree_skb(skb);
  314. return 0;
  315. }
  316. switch (ih->type) {
  317. case IGMP_HOST_MEMBERSHIP_QUERY:
  318. igmp_heard_query(in_dev, ih->code, ih->group);
  319. break;
  320. case IGMP_HOST_MEMBERSHIP_REPORT:
  321. case IGMP_HOST_NEW_MEMBERSHIP_REPORT:
  322. /* Is it our report looped back? */
  323. if (((struct rtable*)skb->dst)->key.iif == 0)
  324. break;
  325. igmp_heard_report(in_dev, ih->group);
  326. break;
  327. case IGMP_PIM:
  328. #ifdef CONFIG_IP_PIMSM_V1
  329. in_dev_put(in_dev);
  330. return pim_rcv_v1(skb);
  331. #endif
  332. case IGMP_DVMRP:
  333. case IGMP_TRACE:
  334. case IGMP_HOST_LEAVE_MESSAGE:
  335. case IGMP_MTRACE:
  336. case IGMP_MTRACE_RESP:
  337. break;
  338. default:
  339. NETDEBUG(printk(KERN_DEBUG "New IGMP type=%d, why we do not know about it?n", ih->type));
  340. }
  341. in_dev_put(in_dev);
  342. kfree_skb(skb);
  343. return 0;
  344. }
  345. #endif
  346. /*
  347.  * Add a filter to a device
  348.  */
  349. static void ip_mc_filter_add(struct in_device *in_dev, u32 addr)
  350. {
  351. char buf[MAX_ADDR_LEN];
  352. struct net_device *dev = in_dev->dev;
  353. /* Checking for IFF_MULTICAST here is WRONG-WRONG-WRONG.
  354.    We will get multicast token leakage, when IFF_MULTICAST
  355.    is changed. This check should be done in dev->set_multicast_list
  356.    routine. Something sort of:
  357.    if (dev->mc_list && dev->flags&IFF_MULTICAST) { do it; }
  358.    --ANK
  359.    */
  360. if (arp_mc_map(addr, buf, dev, 0) == 0)
  361. dev_mc_add(dev,buf,dev->addr_len,0);
  362. }
  363. /*
  364.  * Remove a filter from a device
  365.  */
  366. static void ip_mc_filter_del(struct in_device *in_dev, u32 addr)
  367. {
  368. char buf[MAX_ADDR_LEN];
  369. struct net_device *dev = in_dev->dev;
  370. if (arp_mc_map(addr, buf, dev, 0) == 0)
  371. dev_mc_delete(dev,buf,dev->addr_len,0);
  372. }
  373. static void igmp_group_dropped(struct ip_mc_list *im)
  374. {
  375. #ifdef CONFIG_IP_MULTICAST
  376. int reporter;
  377. #endif
  378. if (im->loaded) {
  379. im->loaded = 0;
  380. ip_mc_filter_del(im->interface, im->multiaddr);
  381. }
  382. #ifdef CONFIG_IP_MULTICAST
  383. if (im->multiaddr == IGMP_ALL_HOSTS)
  384. return;
  385. reporter = im->reporter;
  386. igmp_stop_timer(im);
  387. if (reporter && !IGMP_V1_SEEN(im->interface))
  388. igmp_send_report(im->interface->dev, im->multiaddr, IGMP_HOST_LEAVE_MESSAGE);
  389. #endif
  390. }
  391. static void igmp_group_added(struct ip_mc_list *im)
  392. {
  393. if (im->loaded == 0) {
  394. im->loaded = 1;
  395. ip_mc_filter_add(im->interface, im->multiaddr);
  396. }
  397. #ifdef CONFIG_IP_MULTICAST
  398. if (im->multiaddr == IGMP_ALL_HOSTS)
  399. return;
  400. spin_lock_bh(&im->lock);
  401. igmp_start_timer(im, IGMP_Initial_Report_Delay);
  402. spin_unlock_bh(&im->lock);
  403. #endif
  404. }
  405. /*
  406.  * Multicast list managers
  407.  */
  408. /*
  409.  * A socket has joined a multicast group on device dev.
  410.  */
  411. void ip_mc_inc_group(struct in_device *in_dev, u32 addr)
  412. {
  413. struct ip_mc_list *im;
  414. ASSERT_RTNL();
  415. for (im=in_dev->mc_list; im; im=im->next) {
  416. if (im->multiaddr == addr) {
  417. im->users++;
  418. goto out;
  419. }
  420. }
  421. im = (struct ip_mc_list *)kmalloc(sizeof(*im), GFP_KERNEL);
  422. if (!im)
  423. goto out;
  424. im->users=1;
  425. im->interface=in_dev;
  426. in_dev_hold(in_dev);
  427. im->multiaddr=addr;
  428. atomic_set(&im->refcnt, 1);
  429. spin_lock_init(&im->lock);
  430. #ifdef  CONFIG_IP_MULTICAST
  431. im->tm_running=0;
  432. init_timer(&im->timer);
  433. im->timer.data=(unsigned long)im;
  434. im->timer.function=&igmp_timer_expire;
  435. im->unsolicit_count = IGMP_Unsolicited_Report_Count;
  436. im->reporter = 0;
  437. #endif
  438. im->loaded = 0;
  439. write_lock_bh(&in_dev->lock);
  440. im->next=in_dev->mc_list;
  441. in_dev->mc_list=im;
  442. write_unlock_bh(&in_dev->lock);
  443. igmp_group_added(im);
  444. if (in_dev->dev->flags & IFF_UP)
  445. ip_rt_multicast_event(in_dev);
  446. out:
  447. return;
  448. }
  449. /*
  450.  * A socket has left a multicast group on device dev
  451.  */
  452. int ip_mc_dec_group(struct in_device *in_dev, u32 addr)
  453. {
  454. int err = -ESRCH;
  455. struct ip_mc_list *i, **ip;
  456. ASSERT_RTNL();
  457. for (ip=&in_dev->mc_list; (i=*ip)!=NULL; ip=&i->next) {
  458. if (i->multiaddr==addr) {
  459. if (--i->users == 0) {
  460. write_lock_bh(&in_dev->lock);
  461. *ip = i->next;
  462. write_unlock_bh(&in_dev->lock);
  463. igmp_group_dropped(i);
  464. if (in_dev->dev->flags & IFF_UP)
  465. ip_rt_multicast_event(in_dev);
  466. ip_ma_put(i);
  467. return 0;
  468. }
  469. err = 0;
  470. break;
  471. }
  472. }
  473. return -ESRCH;
  474. }
  475. /* Device going down */
  476. void ip_mc_down(struct in_device *in_dev)
  477. {
  478. struct ip_mc_list *i;
  479. ASSERT_RTNL();
  480. for (i=in_dev->mc_list; i; i=i->next)
  481. igmp_group_dropped(i);
  482. ip_mc_dec_group(in_dev, IGMP_ALL_HOSTS);
  483. }
  484. /* Device going up */
  485. void ip_mc_up(struct in_device *in_dev)
  486. {
  487. struct ip_mc_list *i;
  488. ASSERT_RTNL();
  489. ip_mc_inc_group(in_dev, IGMP_ALL_HOSTS);
  490. for (i=in_dev->mc_list; i; i=i->next)
  491. igmp_group_added(i);
  492. }
  493. /*
  494.  * Device is about to be destroyed: clean up.
  495.  */
  496. void ip_mc_destroy_dev(struct in_device *in_dev)
  497. {
  498. struct ip_mc_list *i;
  499. ASSERT_RTNL();
  500. write_lock_bh(&in_dev->lock);
  501. while ((i = in_dev->mc_list) != NULL) {
  502. in_dev->mc_list = i->next;
  503. write_unlock_bh(&in_dev->lock);
  504. igmp_group_dropped(i);
  505. ip_ma_put(i);
  506. write_lock_bh(&in_dev->lock);
  507. }
  508. write_unlock_bh(&in_dev->lock);
  509. }
  510. static struct in_device * ip_mc_find_dev(struct ip_mreqn *imr)
  511. {
  512. struct rtable *rt;
  513. struct net_device *dev = NULL;
  514. struct in_device *idev = NULL;
  515. if (imr->imr_address.s_addr) {
  516. dev = ip_dev_find(imr->imr_address.s_addr);
  517. if (!dev)
  518. return NULL;
  519. __dev_put(dev);
  520. }
  521. if (!dev && !ip_route_output(&rt, imr->imr_multiaddr.s_addr, 0, 0, 0)) {
  522. dev = rt->u.dst.dev;
  523. ip_rt_put(rt);
  524. }
  525. if (dev) {
  526. imr->imr_ifindex = dev->ifindex;
  527. idev = __in_dev_get(dev);
  528. }
  529. return idev;
  530. }
  531. /*
  532.  * Join a socket to a group
  533.  */
  534. int sysctl_igmp_max_memberships = IP_MAX_MEMBERSHIPS;
  535. int ip_mc_join_group(struct sock *sk , struct ip_mreqn *imr)
  536. {
  537. int err;
  538. u32 addr = imr->imr_multiaddr.s_addr;
  539. struct ip_mc_socklist *iml, *i;
  540. struct in_device *in_dev;
  541. int count = 0;
  542. if (!MULTICAST(addr))
  543. return -EINVAL;
  544. rtnl_shlock();
  545. if (!imr->imr_ifindex)
  546. in_dev = ip_mc_find_dev(imr);
  547. else {
  548. in_dev = inetdev_by_index(imr->imr_ifindex);
  549. if (in_dev)
  550. __in_dev_put(in_dev);
  551. }
  552. if (!in_dev) {
  553. iml = NULL;
  554. err = -ENODEV;
  555. goto done;
  556. }
  557. iml = (struct ip_mc_socklist *)sock_kmalloc(sk, sizeof(*iml), GFP_KERNEL);
  558. err = -EADDRINUSE;
  559. for (i=sk->protinfo.af_inet.mc_list; i; i=i->next) {
  560. if (memcmp(&i->multi, imr, sizeof(*imr)) == 0) {
  561. /* New style additions are reference counted */
  562. if (imr->imr_address.s_addr == 0) {
  563. i->count++;
  564. err = 0;
  565. }
  566. goto done;
  567. }
  568. count++;
  569. }
  570. err = -ENOBUFS;
  571. if (iml == NULL || count >= sysctl_igmp_max_memberships)
  572. goto done;
  573. memcpy(&iml->multi, imr, sizeof(*imr));
  574. iml->next = sk->protinfo.af_inet.mc_list;
  575. iml->count = 1;
  576. sk->protinfo.af_inet.mc_list = iml;
  577. ip_mc_inc_group(in_dev, addr);
  578. iml = NULL;
  579. err = 0;
  580. done:
  581. rtnl_shunlock();
  582. if (iml)
  583. sock_kfree_s(sk, iml, sizeof(*iml));
  584. return err;
  585. }
  586. /*
  587.  * Ask a socket to leave a group.
  588.  */
  589. int ip_mc_leave_group(struct sock *sk, struct ip_mreqn *imr)
  590. {
  591. struct ip_mc_socklist *iml, **imlp;
  592. rtnl_lock();
  593. for (imlp=&sk->protinfo.af_inet.mc_list; (iml=*imlp)!=NULL; imlp=&iml->next) {
  594. if (iml->multi.imr_multiaddr.s_addr==imr->imr_multiaddr.s_addr &&
  595.     iml->multi.imr_address.s_addr==imr->imr_address.s_addr &&
  596.     (!imr->imr_ifindex || iml->multi.imr_ifindex==imr->imr_ifindex)) {
  597. struct in_device *in_dev;
  598. if (--iml->count) {
  599. rtnl_unlock();
  600. return 0;
  601. }
  602. *imlp = iml->next;
  603. in_dev = inetdev_by_index(iml->multi.imr_ifindex);
  604. if (in_dev) {
  605. ip_mc_dec_group(in_dev, imr->imr_multiaddr.s_addr);
  606. in_dev_put(in_dev);
  607. }
  608. rtnl_unlock();
  609. sock_kfree_s(sk, iml, sizeof(*iml));
  610. return 0;
  611. }
  612. }
  613. rtnl_unlock();
  614. return -EADDRNOTAVAIL;
  615. }
  616. /*
  617.  * A socket is closing.
  618.  */
  619. void ip_mc_drop_socket(struct sock *sk)
  620. {
  621. struct ip_mc_socklist *iml;
  622. if (sk->protinfo.af_inet.mc_list == NULL)
  623. return;
  624. rtnl_lock();
  625. while ((iml=sk->protinfo.af_inet.mc_list) != NULL) {
  626. struct in_device *in_dev;
  627. sk->protinfo.af_inet.mc_list = iml->next;
  628. if ((in_dev = inetdev_by_index(iml->multi.imr_ifindex)) != NULL) {
  629. ip_mc_dec_group(in_dev, iml->multi.imr_multiaddr.s_addr);
  630. in_dev_put(in_dev);
  631. }
  632. sock_kfree_s(sk, iml, sizeof(*iml));
  633. }
  634. rtnl_unlock();
  635. }
  636. int ip_check_mc(struct in_device *in_dev, u32 mc_addr)
  637. {
  638. struct ip_mc_list *im;
  639. read_lock(&in_dev->lock);
  640. for (im=in_dev->mc_list; im; im=im->next) {
  641. if (im->multiaddr == mc_addr) {
  642. read_unlock(&in_dev->lock);
  643. return 1;
  644. }
  645. }
  646. read_unlock(&in_dev->lock);
  647. return 0;
  648. }
  649. #ifdef CONFIG_IP_MULTICAST
  650.  
  651. int ip_mc_procinfo(char *buffer, char **start, off_t offset, int length)
  652. {
  653. off_t pos=0, begin=0;
  654. struct ip_mc_list *im;
  655. int len=0;
  656. struct net_device *dev;
  657. len=sprintf(buffer,"IdxtDevice    : Count QueriertGroup    Users TimertReportern");  
  658. read_lock(&dev_base_lock);
  659. for(dev = dev_base; dev; dev = dev->next) {
  660. struct in_device *in_dev = in_dev_get(dev);
  661. char   *querier = "NONE";
  662. if (in_dev == NULL)
  663. continue;
  664. querier = IGMP_V1_SEEN(in_dev) ? "V1" : "V2";
  665. len+=sprintf(buffer+len,"%dt%-10s: %5d %7sn",
  666.      dev->ifindex, dev->name, dev->mc_count, querier);
  667. read_lock(&in_dev->lock);
  668. for (im = in_dev->mc_list; im; im = im->next) {
  669. len+=sprintf(buffer+len,
  670.      "tttt%08lX %5d %d:%08lXtt%dn",
  671.      im->multiaddr, im->users,
  672.      im->tm_running, im->timer.expires-jiffies, im->reporter);
  673. pos=begin+len;
  674. if(pos<offset)
  675. {
  676. len=0;
  677. begin=pos;
  678. }
  679. if(pos>offset+length) {
  680. read_unlock(&in_dev->lock);
  681. in_dev_put(in_dev);
  682. goto done;
  683. }
  684. }
  685. read_unlock(&in_dev->lock);
  686. in_dev_put(in_dev);
  687. }
  688. done:
  689. read_unlock(&dev_base_lock);
  690. *start=buffer+(offset-begin);
  691. len-=(offset-begin);
  692. if(len>length)
  693. len=length;
  694. if(len<0)
  695. len=0;
  696. return len;
  697. }
  698. #endif