source: rtems/cpukit/libnetworking/netinet/in.c @ b3f8c9e1

4.104.11
Last change on this file since b3f8c9e1 was b3f8c9e1, checked in by Ralf Corsepius <ralf.corsepius@…>, on Dec 22, 2008 at 7:47:28 AM

Include <errno.h> (POSIX,C99) instead of <sys/errno.h> (BSD'ism).

  • Property mode set to 100644
File size: 18.3 KB
Line 
1/*
2 * Copyright (c) 1982, 1986, 1991, 1993
3 *      The Regents of the University of California.  All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 *    notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 *    notice, this list of conditions and the following disclaimer in the
12 *    documentation and/or other materials provided with the distribution.
13 * 4. Neither the name of the University nor the names of its contributors
14 *    may be used to endorse or promote products derived from this software
15 *    without specific prior written permission.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
18 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
21 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27 * SUCH DAMAGE.
28 *
29 *      @(#)in.c        8.4 (Berkeley) 1/9/95
30 * $FreeBSD: src/sys/netinet/in.c,v 1.75 2004/04/07 20:46:13 imp Exp $
31 */
32
33#include <sys/param.h>
34#include <rtems/bsd/sys/queue.h>
35#include <sys/systm.h>
36#include <sys/ioctl.h>
37#include <errno.h>
38#include <sys/malloc.h>
39#include <sys/socket.h>
40#include <sys/socketvar.h>
41#include <sys/kernel.h>
42#include <sys/sysctl.h>
43
44#include <net/if.h>
45#include <net/route.h>
46
47#include <netinet/in_systm.h>
48#include <netinet/in.h>
49#include <netinet/in_var.h>
50#include <netinet/if_ether.h>
51
52#include <netinet/igmp_var.h>
53
54/*
55 * This structure is used to keep track of in_multi chains which belong to
56 * deleted interface addresses.
57 */
58static LIST_HEAD(, multi_kludge) in_mk; /* XXX BSS initialization */
59
60struct multi_kludge {
61        LIST_ENTRY(multi_kludge) mk_entry;
62        struct ifnet *mk_ifp;
63        struct in_multihead mk_head;
64};
65
66static void     in_socktrim(struct sockaddr_in *);
67static int      in_ifinit(struct ifnet *,
68            struct in_ifaddr *, struct sockaddr_in *, int);
69static void     in_ifscrub(struct ifnet *, struct in_ifaddr *);
70
71static int subnetsarelocal = 0;
72SYSCTL_INT(_net_inet_ip, OID_AUTO, subnets_are_local, CTLFLAG_RW, 
73        &subnetsarelocal, 0, "");
74/*
75 * Return 1 if an internet address is for a ``local'' host
76 * (one to which we have a connection).  If subnetsarelocal
77 * is true, this includes other subnets of the local net.
78 * Otherwise, it includes only the directly-connected (sub)nets.
79 */
80int
81in_localaddr(struct in_addr in)
82{
83        register u_long i = ntohl(in.s_addr);
84        register struct in_ifaddr *ia;
85
86        if (subnetsarelocal) {
87                for (ia = in_ifaddr; ia; ia = ia->ia_next)
88                        if ((i & ia->ia_netmask) == ia->ia_net)
89                                return (1);
90        } else {
91                for (ia = in_ifaddr; ia; ia = ia->ia_next)
92                        if ((i & ia->ia_subnetmask) == ia->ia_subnet)
93                                return (1);
94        }
95        return (0);
96}
97
98/*
99 * Determine whether an IP address is in a reserved set of addresses
100 * that may not be forwarded, or whether datagrams to that destination
101 * may be forwarded.
102 */
103int
104in_canforward(struct in_addr in)
105{
106        register u_long i = ntohl(in.s_addr);
107        register u_long net;
108
109        if (IN_EXPERIMENTAL(i) || IN_MULTICAST(i))
110                return (0);
111        if (IN_CLASSA(i)) {
112                net = i & IN_CLASSA_NET;
113                if (net == 0 || net == (IN_LOOPBACKNET << IN_CLASSA_NSHIFT))
114                        return (0);
115        }
116        return (1);
117}
118
119/*
120 * Trim a mask in a sockaddr
121 */
122static void
123in_socktrim(struct sockaddr_in *ap)
124{
125    register char *cplim = (char *) &ap->sin_addr;
126    register char *cp = (char *) (&ap->sin_addr + 1);
127
128    ap->sin_len = 0;
129    while (--cp >= cplim)
130        if (*cp) {
131            (ap)->sin_len = cp - (char *) (ap) + 1;
132            break;
133        }
134}
135
136static int in_interfaces;       /* number of external internet interfaces */
137
138/*
139 * Generic internet control operations (ioctl's).
140 * Ifp is 0 if not an interface-specific ioctl.
141 */
142int
143in_control(struct socket *so, u_long cmd, caddr_t data, struct ifnet *ifp)
144{
145        register struct ifreq *ifr = (struct ifreq *)data;
146        register struct in_ifaddr *ia = 0, *iap;
147        register struct ifaddr *ifa;
148        struct in_ifaddr *oia;
149        struct in_aliasreq *ifra = (struct in_aliasreq *)data;
150        struct sockaddr_in oldaddr;
151        int error, hostIsNew, maskIsNew, s;
152        struct multi_kludge *mk;
153
154        /*
155         * Find address for this interface, if it exists.
156         *
157         * If an alias address was specified, find that one instead of
158         * the first one on the interface.
159         */
160        if (ifp)
161                for (iap = in_ifaddr; iap; iap = iap->ia_next)
162                        if (iap->ia_ifp == ifp) {
163                                if (((struct sockaddr_in *)&ifr->ifr_addr)->sin_addr.s_addr ==
164                                    iap->ia_addr.sin_addr.s_addr) {
165                                        ia = iap;
166                                        break;
167                                } else if (ia == NULL) {
168                                        ia = iap;
169                                        if (ifr->ifr_addr.sa_family != AF_INET)
170                                                break;
171                                }
172                        }
173
174        switch (cmd) {
175
176        case SIOCAIFADDR:
177        case SIOCDIFADDR:
178                if (ifra->ifra_addr.sin_family == AF_INET) {
179                        for (oia = ia; ia; ia = ia->ia_next) {
180                                if (ia->ia_ifp == ifp  &&
181                                    ia->ia_addr.sin_addr.s_addr ==
182                                    ifra->ifra_addr.sin_addr.s_addr)
183                                        break;
184                        }
185                        if ((ifp->if_flags & IFF_POINTOPOINT)
186                            && (cmd == SIOCAIFADDR)
187                            && (ifra->ifra_dstaddr.sin_addr.s_addr
188                                == INADDR_ANY)) {
189                                return EDESTADDRREQ;
190                        }
191                }
192                if (cmd == SIOCDIFADDR && ia == 0)
193                        return (EADDRNOTAVAIL);
194                /* FALLTHROUGH */
195        case SIOCSIFADDR:
196        case SIOCSIFNETMASK:
197        case SIOCSIFDSTADDR:
198                if ((so->so_state & SS_PRIV) == 0)
199                        return (EPERM);
200
201                if (ifp == 0)
202                        panic("in_control");
203                if (ia == (struct in_ifaddr *)0) {
204                        oia = (struct in_ifaddr *)
205                                malloc(sizeof *oia, M_IFADDR, M_WAITOK);
206                        if (oia == (struct in_ifaddr *)NULL)
207                                return (ENOBUFS);
208                        bzero((caddr_t)oia, sizeof *oia);
209                        ia = in_ifaddr;
210                        /*
211                         * Protect from ipintr() traversing address list
212                         * while we're modifying it.
213                         */
214                        s = splnet();
215
216                        if (ia) {
217                                for ( ; ia->ia_next; ia = ia->ia_next)
218                                        continue;
219                                ia->ia_next = oia;
220                        } else
221                                in_ifaddr = oia;
222                        ia = oia;
223                        ifa = ifp->if_addrlist;
224                        if (ifa) {
225                                for ( ; ifa->ifa_next; ifa = ifa->ifa_next)
226                                        continue;
227                                ifa->ifa_next = (struct ifaddr *) ia;
228                        } else
229                                ifp->if_addrlist = (struct ifaddr *) ia;
230                        ia->ia_ifa.ifa_addr = (struct sockaddr *)&ia->ia_addr;
231                        ia->ia_ifa.ifa_dstaddr
232                                        = (struct sockaddr *)&ia->ia_dstaddr;
233                        ia->ia_ifa.ifa_netmask
234                                        = (struct sockaddr *)&ia->ia_sockmask;
235                        ia->ia_sockmask.sin_len = 8;
236                        if (ifp->if_flags & IFF_BROADCAST) {
237                                ia->ia_broadaddr.sin_len = sizeof(ia->ia_addr);
238                                ia->ia_broadaddr.sin_family = AF_INET;
239                        }
240                        ia->ia_ifp = ifp;
241                        if (!(ifp->if_flags & IFF_LOOPBACK))
242                                in_interfaces++;
243                        splx(s);
244                }
245                break;
246
247        case SIOCSIFBRDADDR:
248                if ((so->so_state & SS_PRIV) == 0)
249                        return (EPERM);
250                /* FALLTHROUGH */
251
252        case SIOCGIFADDR:
253        case SIOCGIFNETMASK:
254        case SIOCGIFDSTADDR:
255        case SIOCGIFBRDADDR:
256                if (ia == (struct in_ifaddr *)0)
257                        return (EADDRNOTAVAIL);
258                break;
259        }
260        switch (cmd) {
261
262        case SIOCGIFADDR:
263                *((struct sockaddr_in *)&ifr->ifr_addr) = ia->ia_addr;
264                break;
265
266        case SIOCGIFBRDADDR:
267                if ((ifp->if_flags & IFF_BROADCAST) == 0)
268                        return (EINVAL);
269                *((struct sockaddr_in *)&ifr->ifr_dstaddr) = ia->ia_broadaddr;
270                break;
271
272        case SIOCGIFDSTADDR:
273                if ((ifp->if_flags & IFF_POINTOPOINT) == 0)
274                        return (EINVAL);
275                *((struct sockaddr_in *)&ifr->ifr_dstaddr) = ia->ia_dstaddr;
276                break;
277
278        case SIOCGIFNETMASK:
279                *((struct sockaddr_in *)&ifr->ifr_addr) = ia->ia_sockmask;
280                break;
281
282        case SIOCSIFDSTADDR:
283                if ((ifp->if_flags & IFF_POINTOPOINT) == 0)
284                        return (EINVAL);
285                oldaddr = ia->ia_dstaddr;
286                ia->ia_dstaddr = *(struct sockaddr_in *)&ifr->ifr_dstaddr;
287                if (ifp->if_ioctl && (error = (*ifp->if_ioctl)
288                                        (ifp, SIOCSIFDSTADDR, (caddr_t)ia))) {
289                        ia->ia_dstaddr = oldaddr;
290                        return (error);
291                }
292                if (ia->ia_flags & IFA_ROUTE) {
293                        ia->ia_ifa.ifa_dstaddr = (struct sockaddr *)&oldaddr;
294                        rtinit(&(ia->ia_ifa), (int)RTM_DELETE, RTF_HOST);
295                        ia->ia_ifa.ifa_dstaddr =
296                                        (struct sockaddr *)&ia->ia_dstaddr;
297                        rtinit(&(ia->ia_ifa), (int)RTM_ADD, RTF_HOST|RTF_UP);
298                }
299                break;
300
301        case SIOCSIFBRDADDR:
302                if ((ifp->if_flags & IFF_BROADCAST) == 0)
303                        return (EINVAL);
304                ia->ia_broadaddr = *(struct sockaddr_in *)&ifr->ifr_broadaddr;
305                break;
306
307        case SIOCSIFADDR:
308                return (in_ifinit(ifp, ia,
309                    (struct sockaddr_in *) &ifr->ifr_addr, 1));
310
311        case SIOCSIFNETMASK:
312                ia->ia_sockmask.sin_addr = ifra->ifra_addr.sin_addr;
313                ia->ia_subnetmask = ntohl(ia->ia_sockmask.sin_addr.s_addr);
314                break;
315
316        case SIOCAIFADDR:
317                maskIsNew = 0;
318                hostIsNew = 1;
319                error = 0;
320                if (ia->ia_addr.sin_family == AF_INET) {
321                        if (ifra->ifra_addr.sin_len == 0) {
322                                ifra->ifra_addr = ia->ia_addr;
323                                hostIsNew = 0;
324                        } else if (ifra->ifra_addr.sin_addr.s_addr ==
325                                               ia->ia_addr.sin_addr.s_addr)
326                                hostIsNew = 0;
327                }
328                if (ifra->ifra_mask.sin_len) {
329                        in_ifscrub(ifp, ia);
330                        ia->ia_sockmask = ifra->ifra_mask;
331                        ia->ia_subnetmask =
332                             ntohl(ia->ia_sockmask.sin_addr.s_addr);
333                        maskIsNew = 1;
334                }
335                if ((ifp->if_flags & IFF_POINTOPOINT) &&
336                    (ifra->ifra_dstaddr.sin_family == AF_INET)) {
337                        in_ifscrub(ifp, ia);
338                        ia->ia_dstaddr = ifra->ifra_dstaddr;
339                        maskIsNew  = 1; /* We lie; but the effect's the same */
340                }
341                if (ifra->ifra_addr.sin_family == AF_INET &&
342                    (hostIsNew || maskIsNew))
343                        error = in_ifinit(ifp, ia, &ifra->ifra_addr, 0);
344                if ((ifp->if_flags & IFF_BROADCAST) &&
345                    (ifra->ifra_broadaddr.sin_family == AF_INET))
346                        ia->ia_broadaddr = ifra->ifra_broadaddr;
347                return (error);
348
349        case SIOCDIFADDR:
350                mk = malloc(sizeof *mk, M_IPMADDR, M_WAITOK);
351                if (!mk)
352                        return ENOBUFS;
353
354                in_ifscrub(ifp, ia);
355                /*
356                 * Protect from ipintr() traversing address list
357                 * while we're modifying it.
358                 */
359                s = splnet();
360
361                if ((ifa = ifp->if_addrlist) == (struct ifaddr *)ia)
362                        ifp->if_addrlist = ifa->ifa_next;
363                else {
364                        while (ifa->ifa_next &&
365                               (ifa->ifa_next != (struct ifaddr *)ia))
366                                    ifa = ifa->ifa_next;
367                        if (ifa->ifa_next)
368                                ifa->ifa_next = ((struct ifaddr *)ia)->ifa_next;
369                        else
370                                printf("Couldn't unlink inifaddr from ifp\n");
371                }
372                oia = ia;
373                if (oia == (ia = in_ifaddr))
374                        in_ifaddr = ia->ia_next;
375                else {
376                        while (ia->ia_next && (ia->ia_next != oia))
377                                ia = ia->ia_next;
378                        if (ia->ia_next)
379                                ia->ia_next = oia->ia_next;
380                        else
381                                printf("Didn't unlink inifadr from list\n");
382                }
383
384                if (!oia->ia_multiaddrs.lh_first) {
385                        IFAFREE(&oia->ia_ifa);
386                        FREE(mk, M_IPMADDR);
387                        splx(s);
388                        break;
389                }
390
391                /*
392                 * Multicast address kludge:
393                 * If there were any multicast addresses attached to this
394                 * interface address, either move them to another address
395                 * on this interface, or save them until such time as this
396                 * interface is reconfigured for IP.
397                 */
398                IFP_TO_IA(oia->ia_ifp, ia);
399                if (ia) {       /* there is another address */
400                        struct in_multi *inm;
401                        for(inm = oia->ia_multiaddrs.lh_first; inm;
402                            inm = inm->inm_entry.le_next) {
403                                IFAFREE(&inm->inm_ia->ia_ifa);
404                                ia->ia_ifa.ifa_refcnt++;
405                                inm->inm_ia = ia;
406                                LIST_INSERT_HEAD(&ia->ia_multiaddrs, inm,
407                                                 inm_entry);
408                        }
409                        FREE(mk, M_IPMADDR);
410                } else {        /* last address on this if deleted, save */
411                        struct in_multi *inm;
412
413                        LIST_INIT(&mk->mk_head);
414                        mk->mk_ifp = ifp;
415
416                        for(inm = oia->ia_multiaddrs.lh_first; inm;
417                            inm = inm->inm_entry.le_next) {
418                                LIST_INSERT_HEAD(&mk->mk_head, inm, inm_entry);
419                        }
420
421                        if (mk->mk_head.lh_first) {
422                                LIST_INSERT_HEAD(&in_mk, mk, mk_entry);
423                        } else {
424                                FREE(mk, M_IPMADDR);
425                        }
426                }
427
428                IFAFREE((&oia->ia_ifa));
429                splx(s);
430                break;
431
432        default:
433                if (ifp == 0 || ifp->if_ioctl == 0)
434                        return (EOPNOTSUPP);
435                return ((*ifp->if_ioctl)(ifp, cmd, data));
436        }
437        return (0);
438}
439
440/*
441 * Delete any existing route for an interface.
442 */
443static void
444in_ifscrub(struct ifnet *ifp, struct in_ifaddr *ia)
445{
446
447        if ((ia->ia_flags & IFA_ROUTE) == 0)
448                return;
449        if (ifp->if_flags & (IFF_LOOPBACK|IFF_POINTOPOINT))
450                rtinit(&(ia->ia_ifa), (int)RTM_DELETE, RTF_HOST);
451        else
452                rtinit(&(ia->ia_ifa), (int)RTM_DELETE, 0);
453        ia->ia_flags &= ~IFA_ROUTE;
454}
455
456/*
457 * Initialize an interface's internet address
458 * and routing table entry.
459 */
460static int
461in_ifinit(struct ifnet *ifp, struct in_ifaddr *ia, struct sockaddr_in *sin,
462        int scrub)
463{
464        register u_long i = ntohl(sin->sin_addr.s_addr);
465        struct sockaddr_in oldaddr;
466        int s = splimp(), flags = RTF_UP, error;
467        struct multi_kludge *mk;
468
469        oldaddr = ia->ia_addr;
470        ia->ia_addr = *sin;
471        /*
472         * Give the interface a chance to initialize
473         * if this is its first address,
474         * and to validate the address if necessary.
475         */
476        if (ifp->if_ioctl &&
477            (error = (*ifp->if_ioctl)(ifp, SIOCSIFADDR, (caddr_t)ia))) {
478                splx(s);
479                ia->ia_addr = oldaddr;
480                return (error);
481        }
482        splx(s);
483        if (scrub) {
484                ia->ia_ifa.ifa_addr = (struct sockaddr *)&oldaddr;
485                in_ifscrub(ifp, ia);
486                ia->ia_ifa.ifa_addr = (struct sockaddr *)&ia->ia_addr;
487        }
488        if (IN_CLASSA(i))
489                ia->ia_netmask = IN_CLASSA_NET;
490        else if (IN_CLASSB(i))
491                ia->ia_netmask = IN_CLASSB_NET;
492        else
493                ia->ia_netmask = IN_CLASSC_NET;
494        /*
495         * The subnet mask usually includes at least the standard network part,
496         * but may may be smaller in the case of supernetting.
497         * If it is set, we believe it.
498         */
499        if (ia->ia_subnetmask == 0) {
500                ia->ia_subnetmask = ia->ia_netmask;
501                ia->ia_sockmask.sin_addr.s_addr = htonl(ia->ia_subnetmask);
502        } else
503                ia->ia_netmask &= ia->ia_subnetmask;
504        ia->ia_net = i & ia->ia_netmask;
505        ia->ia_subnet = i & ia->ia_subnetmask;
506        in_socktrim(&ia->ia_sockmask);
507        /*
508         * Add route for the network.
509         */
510        ia->ia_ifa.ifa_metric = ifp->if_metric;
511        if (ifp->if_flags & IFF_BROADCAST) {
512                ia->ia_broadaddr.sin_addr.s_addr =
513                        htonl(ia->ia_subnet | ~ia->ia_subnetmask);
514                ia->ia_netbroadcast.s_addr =
515                        htonl(ia->ia_net | ~ ia->ia_netmask);
516        } else if (ifp->if_flags & IFF_LOOPBACK) {
517                ia->ia_ifa.ifa_dstaddr = ia->ia_ifa.ifa_addr;
518                flags |= RTF_HOST;
519        } else if (ifp->if_flags & IFF_POINTOPOINT) {
520                if (ia->ia_dstaddr.sin_family != AF_INET)
521                        return (0);
522                flags |= RTF_HOST;
523        }
524        if ((error = rtinit(&(ia->ia_ifa), (int)RTM_ADD, flags)) == 0)
525                ia->ia_flags |= IFA_ROUTE;
526
527        LIST_INIT(&ia->ia_multiaddrs);
528        /*
529         * If the interface supports multicast, join the "all hosts"
530         * multicast group on that interface.
531         */
532        if (ifp->if_flags & IFF_MULTICAST) {
533                struct in_addr addr;
534
535                /*
536                 * Continuation of multicast address hack:
537                 * If there was a multicast group list previously saved
538                 * for this interface, then we re-attach it to the first
539                 * address configured on the i/f.
540                 */
541                for(mk = in_mk.lh_first; mk; mk = mk->mk_entry.le_next) {
542                        if(mk->mk_ifp == ifp) {
543                                struct in_multi *inm;
544
545                                for(inm = mk->mk_head.lh_first; inm;
546                                    inm = inm->inm_entry.le_next) {
547                                        IFAFREE(&inm->inm_ia->ia_ifa);
548                                        ia->ia_ifa.ifa_refcnt++;
549                                        inm->inm_ia = ia;
550                                        LIST_INSERT_HEAD(&ia->ia_multiaddrs,
551                                                         inm, inm_entry);
552                                }
553                                LIST_REMOVE(mk, mk_entry);
554                                free(mk, M_IPMADDR);
555                                break;
556                        }
557                }
558
559                addr.s_addr = htonl(INADDR_ALLHOSTS_GROUP);
560                in_addmulti(&addr, ifp);
561        }
562        return (error);
563}
564
565
566/*
567 * Return 1 if the address might be a local broadcast address.
568 */
569int
570in_broadcast(struct in_addr in, struct ifnet *ifp)
571{
572        register struct ifaddr *ifa;
573        u_long t;
574
575        if (in.s_addr == INADDR_BROADCAST ||
576            in.s_addr == INADDR_ANY)
577                return 1;
578        if ((ifp->if_flags & IFF_BROADCAST) == 0)
579                return 0;
580        t = ntohl(in.s_addr);
581        /*
582         * Look through the list of addresses for a match
583         * with a broadcast address.
584         */
585#define ia ((struct in_ifaddr *)ifa)
586        for (ifa = ifp->if_addrlist; ifa; ifa = ifa->ifa_next)
587                if (ifa->ifa_addr->sa_family == AF_INET &&
588                    (in.s_addr == ia->ia_broadaddr.sin_addr.s_addr ||
589                     in.s_addr == ia->ia_netbroadcast.s_addr ||
590                     /*
591                      * Check for old-style (host 0) broadcast.
592                      */
593                     t == ia->ia_subnet || t == ia->ia_net) &&
594                     /*
595                      * Check for an all one subnetmask. These
596                      * only exist when an interface gets a secondary
597                      * address.
598                      */
599                     ia->ia_subnetmask != (u_long)0xffffffff)
600                            return 1;
601        return (0);
602#undef ia
603}
604/*
605 * Add an address to the list of IP multicast addresses for a given interface.
606 */
607struct in_multi *
608in_addmulti(struct in_addr *ap, struct ifnet *ifp)
609{
610        register struct in_multi *inm;
611        struct ifreq ifr;
612        struct in_ifaddr *ia;
613        int s = splnet();
614
615        /*
616         * See if address already in list.
617         */
618        IN_LOOKUP_MULTI(*ap, ifp, inm);
619        if (inm != NULL) {
620                /*
621                 * Found it; just increment the reference count.
622                 */
623                ++inm->inm_refcount;
624        }
625        else {
626                /*
627                 * New address; allocate a new multicast record
628                 * and link it into the interface's multicast list.
629                 */
630                inm = (struct in_multi *)malloc(sizeof(*inm),
631                    M_IPMADDR, M_NOWAIT);
632                if (inm == NULL) {
633                        splx(s);
634                        return (NULL);
635                }
636                inm->inm_addr = *ap;
637                inm->inm_ifp = ifp;
638                inm->inm_refcount = 1;
639                IFP_TO_IA(ifp, ia);
640                if (ia == NULL) {
641                        free(inm, M_IPMADDR);
642                        splx(s);
643                        return (NULL);
644                }
645                inm->inm_ia = ia;
646                ia->ia_ifa.ifa_refcnt++; /* gain a reference */
647                LIST_INSERT_HEAD(&ia->ia_multiaddrs, inm, inm_entry);
648
649                /*
650                 * Ask the network driver to update its multicast reception
651                 * filter appropriately for the new address.
652                 */
653                ((struct sockaddr_in *)&ifr.ifr_addr)->sin_family = AF_INET;
654                ((struct sockaddr_in *)&ifr.ifr_addr)->sin_addr = *ap;
655                if ((ifp->if_ioctl == NULL) ||
656                    (*ifp->if_ioctl)(ifp, SIOCADDMULTI,(caddr_t)&ifr) != 0) {
657                        LIST_REMOVE(inm, inm_entry);
658                        IFAFREE(&ia->ia_ifa); /* release reference */
659                        free(inm, M_IPMADDR);
660                        splx(s);
661                        return (NULL);
662                }
663                /*
664                 * Let IGMP know that we have joined a new IP multicast group.
665                 */
666                igmp_joingroup(inm);
667        }
668        splx(s);
669        return (inm);
670}
671
672/*
673 * Delete a multicast address record.
674 */
675void
676in_delmulti(struct in_multi *inm)
677{
678        struct ifreq ifr;
679        int s = splnet();
680
681        if (--inm->inm_refcount == 0) {
682                /*
683                 * No remaining claims to this record; let IGMP know that
684                 * we are leaving the multicast group.
685                 */
686                igmp_leavegroup(inm);
687                /*
688                 * Unlink from list.
689                 */
690                LIST_REMOVE(inm, inm_entry);
691                IFAFREE(&inm->inm_ia->ia_ifa); /* release reference */
692
693                /*
694                 * Notify the network driver to update its multicast reception
695                 * filter.
696                 */
697                ((struct sockaddr_in *)&(ifr.ifr_addr))->sin_family = AF_INET;
698                ((struct sockaddr_in *)&(ifr.ifr_addr))->sin_addr =
699                                                                inm->inm_addr;
700                (*inm->inm_ifp->if_ioctl)(inm->inm_ifp, SIOCDELMULTI,
701                                                             (caddr_t)&ifr);
702                free(inm, M_IPMADDR);
703        }
704        splx(s);
705}
Note: See TracBrowser for help on using the repository browser.