source: rtems/cpukit/libnetworking/netinet/igmp.c @ 657e6c93

Last change on this file since 657e6c93 was 657e6c93, checked in by Christian Mauderer <Christian.Mauderer@…>, on Jun 24, 2016 at 5:57:17 AM

libnetworking: Import current <netinet/in.h>

Import the <netinet/in.h> from current FreeBSD. This allows to build
some current software (e.g. libressl).

Add legacy support like

  • prototype for in_cksum(),
  • IPPORT_USERRESERVED,
  • deprecated IPCTL_RT* defines,
  • ip_fw_chk_t and ip_fw_ctl_t,
  • ip_nat_... (IP NAT hooks), and
  • IP_NAT option for get/setsockopt()

to new <rtems/rtems_netinet_in.h>.

  • Property mode set to 100644
File size: 12.4 KB
Line 
1/*
2 * Copyright (c) 1988 Stephen Deering.
3 * Copyright (c) 1992, 1993
4 *      The Regents of the University of California.  All rights reserved.
5 *
6 * This code is derived from software contributed to Berkeley by
7 * Stephen Deering of Stanford University.
8 *
9 * Redistribution and use in source and binary forms, with or without
10 * modification, are permitted provided that the following conditions
11 * are met:
12 * 1. Redistributions of source code must retain the above copyright
13 *    notice, this list of conditions and the following disclaimer.
14 * 2. Redistributions in binary form must reproduce the above copyright
15 *    notice, this list of conditions and the following disclaimer in the
16 *    documentation and/or other materials provided with the distribution.
17 * 4. Neither the name of the University nor the names of its contributors
18 *    may be used to endorse or promote products derived from this software
19 *    without specific prior written permission.
20 *
21 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
22 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
25 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31 * SUCH DAMAGE.
32 *
33 *      @(#)igmp.c      8.1 (Berkeley) 7/19/93
34 */
35
36/*
37 * Internet Group Management Protocol (IGMP) routines.
38 *
39 * Written by Steve Deering, Stanford, May 1988.
40 * Modified by Rosen Sharma, Stanford, Aug 1994.
41 * Modified by Bill Fenner, Xerox PARC, Feb 1995.
42 * Modified to fully comply to IGMPv2 by Bill Fenner, Oct 1995.
43 *
44 * MULTICAST Revision: 3.5.1.4
45 */
46
47#ifdef HAVE_CONFIG_H
48#include "config.h"
49#endif
50
51#include <sys/param.h>
52#include <sys/systm.h>
53#include <sys/mbuf.h>
54#include <sys/socket.h>
55#include <sys/protosw.h>
56#include <sys/kernel.h>
57#include <sys/sysctl.h>
58
59#include <net/if.h>
60#include <net/route.h>
61
62#include <netinet/in.h>
63#include <rtems/rtems_netinet_in.h>
64#include <netinet/in_var.h>
65#include <netinet/in_systm.h>
66#include <netinet/ip.h>
67#include <netinet/ip_var.h>
68#include <netinet/igmp.h>
69#include <netinet/igmp_var.h>
70
71static struct router_info *
72                find_rti (struct ifnet *ifp);
73
74static struct igmpstat igmpstat;
75
76SYSCTL_STRUCT(_net_inet_igmp, IGMPCTL_STATS, stats, CTLFLAG_RD,
77        &igmpstat, igmpstat, "");
78
79static int igmp_timers_are_running;
80static u_long igmp_all_hosts_group;
81static u_long igmp_all_rtrs_group;
82static struct mbuf *router_alert;
83static struct router_info *Head;
84
85static void igmp_sendpkt(struct in_multi *, int, unsigned long);
86
87void
88igmp_init(void)
89{
90        struct ipoption *ra;
91
92        /*
93         * To avoid byte-swapping the same value over and over again.
94         */
95        igmp_all_hosts_group = htonl(INADDR_ALLHOSTS_GROUP);
96        igmp_all_rtrs_group = htonl(INADDR_ALLRTRS_GROUP);
97
98        igmp_timers_are_running = 0;
99
100        /*
101         * Construct a Router Alert option to use in outgoing packets
102         */
103        MGET(router_alert, M_DONTWAIT, MT_DATA);
104        ra = mtod(router_alert, struct ipoption *);
105        ra->ipopt_dst.s_addr = 0;
106        ra->ipopt_list[0] = IPOPT_RA;   /* Router Alert Option */
107        ra->ipopt_list[1] = 0x04;       /* 4 bytes long */
108        ra->ipopt_list[2] = 0x00;
109        ra->ipopt_list[3] = 0x00;
110        router_alert->m_len = sizeof(ra->ipopt_dst) + ra->ipopt_list[1];
111
112        Head = (struct router_info *) 0;
113}
114
115static struct router_info *
116find_rti(struct ifnet *ifp)
117{
118        register struct router_info *rti = Head;
119
120#ifdef IGMP_DEBUG
121        printf("[igmp.c, _find_rti] --> entering \n");
122#endif
123        while (rti) {
124                if (rti->rti_ifp == ifp) {
125#ifdef IGMP_DEBUG
126                        printf("[igmp.c, _find_rti] --> found old entry \n");
127#endif
128                        return rti;
129                }
130                rti = rti->rti_next;
131        }
132        MALLOC(rti, struct router_info *, sizeof *rti, M_MRTABLE, M_NOWAIT);
133        rti->rti_ifp = ifp;
134        rti->rti_type = IGMP_V2_ROUTER;
135        rti->rti_time = 0;
136        rti->rti_next = Head;
137        Head = rti;
138#ifdef IGMP_DEBUG
139        printf("[igmp.c, _find_rti] --> created an entry \n");
140#endif
141        return rti;
142}
143
144void
145igmp_input(struct mbuf *m, int iphlen)
146{
147        register struct igmp *igmp;
148        register struct ip *ip;
149        register int igmplen;
150        register struct ifnet *ifp = m->m_pkthdr.rcvif;
151        register int minlen;
152        register struct in_multi *inm;
153        register struct in_ifaddr *ia;
154        struct in_multistep step;
155        struct router_info *rti;
156       
157        int timer; /** timer value in the igmp query header **/
158
159        ++igmpstat.igps_rcv_total;
160
161        ip = mtod(m, struct ip *);
162        igmplen = ip->ip_len;
163
164        /*
165         * Validate lengths
166         */
167        if (igmplen < IGMP_MINLEN) {
168                ++igmpstat.igps_rcv_tooshort;
169                m_freem(m);
170                return;
171        }
172        minlen = iphlen + IGMP_MINLEN;
173        if ((m->m_flags & M_EXT || m->m_len < minlen) &&
174            (m = m_pullup(m, minlen)) == 0) {
175                ++igmpstat.igps_rcv_tooshort;
176                return;
177        }
178
179        /*
180         * Validate checksum
181         */
182        m->m_data += iphlen;
183        m->m_len -= iphlen;
184        igmp = mtod(m, struct igmp *);
185        if (in_cksum(m, igmplen)) {
186                ++igmpstat.igps_rcv_badsum;
187                m_freem(m);
188                return;
189        }
190        m->m_data -= iphlen;
191        m->m_len += iphlen;
192
193        ip = mtod(m, struct ip *);
194        timer = igmp->igmp_code * PR_FASTHZ / IGMP_TIMER_SCALE;
195        rti = find_rti(ifp);
196
197        /*
198         * In the IGMPv2 specification, there are 3 states and a flag.
199         *
200         * In Non-Member state, we simply don't have a membership record.
201         * In Delaying Member state, our timer is running (inm->inm_timer)
202         * In Idle Member state, our timer is not running (inm->inm_timer==0)
203         *
204         * The flag is inm->inm_state, it is set to IGMP_OTHERMEMBER if
205         * we have heard a report from another member, or IGMP_IREPORTEDLAST
206         * if I sent the last report.
207         */
208        switch (igmp->igmp_type) {
209
210        case IGMP_MEMBERSHIP_QUERY:
211                ++igmpstat.igps_rcv_queries;
212
213                if (ifp->if_flags & IFF_LOOPBACK)
214                        break;
215
216                if (igmp->igmp_code == 0) {
217                        /*
218                         * Old router.  Remember that the querier on this
219                         * interface is old, and set the timer to the
220                         * value in RFC 1112.
221                         */
222
223                        rti->rti_type = IGMP_V1_ROUTER;
224                        rti->rti_time = 0;
225
226                        timer = IGMP_MAX_HOST_REPORT_DELAY * PR_FASTHZ;
227
228                        if (ip->ip_dst.s_addr != igmp_all_hosts_group ||
229                            igmp->igmp_group.s_addr != 0) {
230                                ++igmpstat.igps_rcv_badqueries;
231                                m_freem(m);
232                                return;
233                        }
234                } else {
235                        /*
236                         * New router.  Simply do the new validity check.
237                         */
238                       
239                        if (igmp->igmp_group.s_addr != 0 &&
240                            !IN_MULTICAST(ntohl(igmp->igmp_group.s_addr))) {
241                                ++igmpstat.igps_rcv_badqueries;
242                                m_freem(m);
243                                return;
244                        }
245                }
246
247                /*
248                 * - Start the timers in all of our membership records
249                 *   that the query applies to for the interface on
250                 *   which the query arrived excl. those that belong
251                 *   to the "all-hosts" group (224.0.0.1).
252                 * - Restart any timer that is already running but has
253                 *   a value longer than the requested timeout.
254                 * - Use the value specified in the query message as
255                 *   the maximum timeout.
256                 */
257                IN_FIRST_MULTI(step, inm);
258                while (inm != NULL) {
259                        if (inm->inm_ifp == ifp &&
260                            inm->inm_addr.s_addr != igmp_all_hosts_group &&
261                            (igmp->igmp_group.s_addr == 0 ||
262                             igmp->igmp_group.s_addr == inm->inm_addr.s_addr)) {
263                                if (inm->inm_timer == 0 ||
264                                    inm->inm_timer > timer) {
265                                        inm->inm_timer =
266                                                IGMP_RANDOM_DELAY(timer);
267                                        igmp_timers_are_running = 1;
268                                }
269                        }
270                        IN_NEXT_MULTI(step, inm);
271                }
272
273                break;
274
275        case IGMP_V1_MEMBERSHIP_REPORT:
276        case IGMP_V2_MEMBERSHIP_REPORT:
277                /*
278                 * For fast leave to work, we have to know that we are the
279                 * last person to send a report for this group.  Reports
280                 * can potentially get looped back if we are a multicast
281                 * router, so discard reports sourced by me.
282                 */
283                IFP_TO_IA(ifp, ia);
284                if (ia && ip->ip_src.s_addr == IA_SIN(ia)->sin_addr.s_addr)
285                        break;
286
287                ++igmpstat.igps_rcv_reports;
288
289                if (ifp->if_flags & IFF_LOOPBACK)
290                        break;
291
292                if (!IN_MULTICAST(ntohl(igmp->igmp_group.s_addr))) {
293                        ++igmpstat.igps_rcv_badreports;
294                        m_freem(m);
295                        return;
296                }
297
298                /*
299                 * KLUDGE: if the IP source address of the report has an
300                 * unspecified (i.e., zero) subnet number, as is allowed for
301                 * a booting host, replace it with the correct subnet number
302                 * so that a process-level multicast routing demon can
303                 * determine which subnet it arrived from.  This is necessary
304                 * to compensate for the lack of any way for a process to
305                 * determine the arrival interface of an incoming packet.
306                 */
307                if ((ntohl(ip->ip_src.s_addr) & IN_CLASSA_NET) == 0)
308                        if (ia) ip->ip_src.s_addr = htonl(ia->ia_subnet);
309
310                /*
311                 * If we belong to the group being reported, stop
312                 * our timer for that group.
313                 */
314                IN_LOOKUP_MULTI(igmp->igmp_group, ifp, inm);
315
316                if (inm != NULL) {
317                        inm->inm_timer = 0;
318                        ++igmpstat.igps_rcv_ourreports;
319
320                        inm->inm_state = IGMP_OTHERMEMBER;
321                }
322
323                break;
324        }
325
326        /*
327         * Pass all valid IGMP packets up to any process(es) listening
328         * on a raw IGMP socket.
329         */
330        rip_input(m, iphlen);
331}
332
333void
334igmp_joingroup(struct in_multi *inm)
335{
336        int s = splnet();
337
338        if (inm->inm_addr.s_addr == igmp_all_hosts_group
339            || inm->inm_ifp->if_flags & IFF_LOOPBACK) {
340                inm->inm_timer = 0;
341                inm->inm_state = IGMP_OTHERMEMBER;
342        } else {
343                inm->inm_rti = find_rti(inm->inm_ifp);
344                igmp_sendpkt(inm, inm->inm_rti->rti_type, 0);
345                inm->inm_timer = IGMP_RANDOM_DELAY(
346                                        IGMP_MAX_HOST_REPORT_DELAY*PR_FASTHZ);
347                inm->inm_state = IGMP_IREPORTEDLAST;
348                igmp_timers_are_running = 1;
349        }
350        splx(s);
351}
352
353void
354igmp_leavegroup(struct in_multi *inm)
355{
356        if (inm->inm_state == IGMP_IREPORTEDLAST &&
357            inm->inm_addr.s_addr != igmp_all_hosts_group &&
358            !(inm->inm_ifp->if_flags & IFF_LOOPBACK) &&
359            inm->inm_rti->rti_type != IGMP_V1_ROUTER)
360                igmp_sendpkt(inm, IGMP_V2_LEAVE_GROUP, igmp_all_rtrs_group);
361}
362
363void
364igmp_fasttimo(void)
365{
366        register struct in_multi *inm;
367        struct in_multistep step;
368        int s;
369
370        /*
371         * Quick check to see if any work needs to be done, in order
372         * to minimize the overhead of fasttimo processing.
373         */
374
375        if (!igmp_timers_are_running)
376                return;
377
378        s = splnet();
379        igmp_timers_are_running = 0;
380        IN_FIRST_MULTI(step, inm);
381        while (inm != NULL) {
382                if (inm->inm_timer == 0) {
383                        /* do nothing */
384                } else if (--inm->inm_timer == 0) {
385                        igmp_sendpkt(inm, inm->inm_rti->rti_type, 0);
386                        inm->inm_state = IGMP_IREPORTEDLAST;
387                } else {
388                        igmp_timers_are_running = 1;
389                }
390                IN_NEXT_MULTI(step, inm);
391        }
392        splx(s);
393}
394
395void
396igmp_slowtimo(void)
397{
398        int s = splnet();
399        register struct router_info *rti =  Head;
400
401#ifdef IGMP_DEBUG
402        printf("[igmp.c,_slowtimo] -- > entering \n");
403#endif
404        while (rti) {
405            if (rti->rti_type == IGMP_V1_ROUTER) {
406                rti->rti_time++;
407                if (rti->rti_time >= IGMP_AGE_THRESHOLD) {
408                        rti->rti_type = IGMP_V2_ROUTER;
409                }
410            }
411            rti = rti->rti_next;
412        }
413#ifdef IGMP_DEBUG       
414        printf("[igmp.c,_slowtimo] -- > exiting \n");
415#endif
416        splx(s);
417}
418
419static struct route igmprt;
420
421static void
422igmp_sendpkt(struct in_multi *inm, int type, unsigned long addr)
423{
424        struct mbuf *m;
425        struct igmp *igmp;
426        struct ip *ip;
427        struct ip_moptions imo;
428
429        MGETHDR(m, M_DONTWAIT, MT_HEADER);
430        if (m == NULL)
431                return;
432
433        m->m_pkthdr.rcvif = loif;
434        m->m_pkthdr.len = sizeof(struct ip) + IGMP_MINLEN;
435        MH_ALIGN(m, IGMP_MINLEN + sizeof(struct ip));
436        m->m_data += sizeof(struct ip);
437        m->m_len = IGMP_MINLEN;
438        igmp = mtod(m, struct igmp *);
439        igmp->igmp_type   = type;
440        igmp->igmp_code   = 0;
441        igmp->igmp_group  = inm->inm_addr;
442        igmp->igmp_cksum  = 0;
443        igmp->igmp_cksum  = in_cksum(m, IGMP_MINLEN);
444
445        m->m_data -= sizeof(struct ip);
446        m->m_len += sizeof(struct ip);
447        ip = mtod(m, struct ip *);
448        ip->ip_tos        = 0;
449        ip->ip_len        = sizeof(struct ip) + IGMP_MINLEN;
450        ip->ip_off        = 0;
451        ip->ip_p          = IPPROTO_IGMP;
452        ip->ip_src.s_addr = INADDR_ANY;
453        ip->ip_dst.s_addr = addr ? addr : igmp->igmp_group.s_addr;
454
455        imo.imo_multicast_ifp  = inm->inm_ifp;
456        imo.imo_multicast_ttl  = 1;
457        imo.imo_multicast_vif  = -1;
458        /*
459         * Request loopback of the report if we are acting as a multicast
460         * router, so that the process-level routing demon can hear it.
461         */
462        imo.imo_multicast_loop = (ip_mrouter != NULL);
463
464        /*
465         * XXX
466         * Do we have to worry about reentrancy here?  Don't think so.
467         */
468        ip_output(m, router_alert, &igmprt, 0, &imo);
469
470        ++igmpstat.igps_snd_reports;
471}
Note: See TracBrowser for help on using the repository browser.