1c398230bSWarner Losh /*- 2df8bae1dSRodney W. Grimes * Copyright (c) 1982, 1986, 1991, 1993 3df8bae1dSRodney W. Grimes * The Regents of the University of California. All rights reserved. 481d96ce8SMax Laier * Copyright (C) 2001 WIDE Project. All rights reserved. 5df8bae1dSRodney W. Grimes * 6df8bae1dSRodney W. Grimes * Redistribution and use in source and binary forms, with or without 7df8bae1dSRodney W. Grimes * modification, are permitted provided that the following conditions 8df8bae1dSRodney W. Grimes * are met: 9df8bae1dSRodney W. Grimes * 1. Redistributions of source code must retain the above copyright 10df8bae1dSRodney W. Grimes * notice, this list of conditions and the following disclaimer. 11df8bae1dSRodney W. Grimes * 2. Redistributions in binary form must reproduce the above copyright 12df8bae1dSRodney W. Grimes * notice, this list of conditions and the following disclaimer in the 13df8bae1dSRodney W. Grimes * documentation and/or other materials provided with the distribution. 14df8bae1dSRodney W. Grimes * 4. Neither the name of the University nor the names of its contributors 15df8bae1dSRodney W. Grimes * may be used to endorse or promote products derived from this software 16df8bae1dSRodney W. Grimes * without specific prior written permission. 17df8bae1dSRodney W. Grimes * 18df8bae1dSRodney W. Grimes * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 19df8bae1dSRodney W. Grimes * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 20df8bae1dSRodney W. Grimes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 21df8bae1dSRodney W. Grimes * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 22df8bae1dSRodney W. Grimes * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 23df8bae1dSRodney W. Grimes * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 24df8bae1dSRodney W. Grimes * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 25df8bae1dSRodney W. Grimes * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 26df8bae1dSRodney W. Grimes * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 27df8bae1dSRodney W. Grimes * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 28df8bae1dSRodney W. Grimes * SUCH DAMAGE. 29df8bae1dSRodney W. Grimes * 302180b925SGarrett Wollman * @(#)in.c 8.4 (Berkeley) 1/9/95 31c3aac50fSPeter Wemm * $FreeBSD$ 32df8bae1dSRodney W. Grimes */ 33df8bae1dSRodney W. Grimes 3450bb1704SGleb Smirnoff #include "opt_carp.h" 3550bb1704SGleb Smirnoff 36df8bae1dSRodney W. Grimes #include <sys/param.h> 3726f9a767SRodney W. Grimes #include <sys/systm.h> 3851a53488SBruce Evans #include <sys/sockio.h> 39df8bae1dSRodney W. Grimes #include <sys/malloc.h> 40df8bae1dSRodney W. Grimes #include <sys/socket.h> 41f6d24a78SPoul-Henning Kamp #include <sys/kernel.h> 42f6d24a78SPoul-Henning Kamp #include <sys/sysctl.h> 43df8bae1dSRodney W. Grimes 44df8bae1dSRodney W. Grimes #include <net/if.h> 456a800098SYoshinobu Inoue #include <net/if_types.h> 46df8bae1dSRodney W. Grimes #include <net/route.h> 47df8bae1dSRodney W. Grimes 48df8bae1dSRodney W. Grimes #include <netinet/in.h> 49df8bae1dSRodney W. Grimes #include <netinet/in_var.h> 50e43cc4aeSHajimu UMEMOTO #include <netinet/in_pcb.h> 51df8bae1dSRodney W. Grimes 52c70f4510SPoul-Henning Kamp #include <netinet/igmp_var.h> 53c70f4510SPoul-Henning Kamp 54a1c995b6SPoul-Henning Kamp static MALLOC_DEFINE(M_IPMADDR, "in_multi", "internet multicast address"); 5555166637SPoul-Henning Kamp 564d77a549SAlfred Perlstein static int in_mask2len(struct in_addr *); 574d77a549SAlfred Perlstein static void in_len2mask(struct in_addr *, int); 584d77a549SAlfred Perlstein static int in_lifaddr_ioctl(struct socket *, u_long, caddr_t, 594d77a549SAlfred Perlstein struct ifnet *, struct thread *); 606a800098SYoshinobu Inoue 6148321abeSMax Laier static int in_addprefix(struct in_ifaddr *, int); 6248321abeSMax Laier static int in_scrubprefix(struct in_ifaddr *); 634d77a549SAlfred Perlstein static void in_socktrim(struct sockaddr_in *); 644d77a549SAlfred Perlstein static int in_ifinit(struct ifnet *, 654d77a549SAlfred Perlstein struct in_ifaddr *, struct sockaddr_in *, int); 66df8bae1dSRodney W. Grimes 67f8731310SGarrett Wollman static int subnetsarelocal = 0; 68f6d24a78SPoul-Henning Kamp SYSCTL_INT(_net_inet_ip, OID_AUTO, subnets_are_local, CTLFLAG_RW, 693b95e134SRuslan Ermilov &subnetsarelocal, 0, "Treat all subnets as directly connected"); 701ae95409SGleb Smirnoff static int sameprefixcarponly = 0; 711ae95409SGleb Smirnoff SYSCTL_INT(_net_inet_ip, OID_AUTO, same_prefix_carp_only, CTLFLAG_RW, 721ae95409SGleb Smirnoff &sameprefixcarponly, 0, 731ae95409SGleb Smirnoff "Refuse to create same prefixes on different interfaces"); 74477180fbSGarrett Wollman 75dd5a318bSRobert Watson /* 76dd5a318bSRobert Watson * The IPv4 multicast list (in_multihead and associated structures) are 77dd5a318bSRobert Watson * protected by the global in_multi_mtx. See in_var.h for more details. For 78dd5a318bSRobert Watson * now, in_multi_mtx is marked as recursible due to IGMP's calling back into 79dd5a318bSRobert Watson * ip_output() to send IGMP packets while holding the lock; this probably is 80dd5a318bSRobert Watson * not quite desirable. 81dd5a318bSRobert Watson */ 82477180fbSGarrett Wollman struct in_multihead in_multihead; /* XXX BSS initialization */ 83dd5a318bSRobert Watson struct mtx in_multi_mtx; 84dd5a318bSRobert Watson MTX_SYSINIT(in_multi_mtx, &in_multi_mtx, "in_multi_mtx", MTX_DEF | MTX_RECURSE); 85477180fbSGarrett Wollman 86e43cc4aeSHajimu UMEMOTO extern struct inpcbinfo ripcbinfo; 87e43cc4aeSHajimu UMEMOTO extern struct inpcbinfo udbinfo; 88e43cc4aeSHajimu UMEMOTO 89df8bae1dSRodney W. Grimes /* 90df8bae1dSRodney W. Grimes * Return 1 if an internet address is for a ``local'' host 91df8bae1dSRodney W. Grimes * (one to which we have a connection). If subnetsarelocal 92df8bae1dSRodney W. Grimes * is true, this includes other subnets of the local net. 93df8bae1dSRodney W. Grimes * Otherwise, it includes only the directly-connected (sub)nets. 94df8bae1dSRodney W. Grimes */ 9526f9a767SRodney W. Grimes int 96df8bae1dSRodney W. Grimes in_localaddr(in) 97df8bae1dSRodney W. Grimes struct in_addr in; 98df8bae1dSRodney W. Grimes { 99df8bae1dSRodney W. Grimes register u_long i = ntohl(in.s_addr); 100df8bae1dSRodney W. Grimes register struct in_ifaddr *ia; 101df8bae1dSRodney W. Grimes 102df8bae1dSRodney W. Grimes if (subnetsarelocal) { 103462b86feSPoul-Henning Kamp TAILQ_FOREACH(ia, &in_ifaddrhead, ia_link) 104df8bae1dSRodney W. Grimes if ((i & ia->ia_netmask) == ia->ia_net) 105df8bae1dSRodney W. Grimes return (1); 106df8bae1dSRodney W. Grimes } else { 10737d40066SPoul-Henning Kamp TAILQ_FOREACH(ia, &in_ifaddrhead, ia_link) 108df8bae1dSRodney W. Grimes if ((i & ia->ia_subnetmask) == ia->ia_subnet) 109df8bae1dSRodney W. Grimes return (1); 110df8bae1dSRodney W. Grimes } 111df8bae1dSRodney W. Grimes return (0); 112df8bae1dSRodney W. Grimes } 113df8bae1dSRodney W. Grimes 114df8bae1dSRodney W. Grimes /* 1152eccc90bSAndre Oppermann * Return 1 if an internet address is for the local host and configured 1162eccc90bSAndre Oppermann * on one of its interfaces. 1172eccc90bSAndre Oppermann */ 1182eccc90bSAndre Oppermann int 1192eccc90bSAndre Oppermann in_localip(in) 1202eccc90bSAndre Oppermann struct in_addr in; 1212eccc90bSAndre Oppermann { 1222eccc90bSAndre Oppermann struct in_ifaddr *ia; 1232eccc90bSAndre Oppermann 1242eccc90bSAndre Oppermann LIST_FOREACH(ia, INADDR_HASH(in.s_addr), ia_hash) { 1252eccc90bSAndre Oppermann if (IA_SIN(ia)->sin_addr.s_addr == in.s_addr) 1262eccc90bSAndre Oppermann return 1; 1272eccc90bSAndre Oppermann } 1282eccc90bSAndre Oppermann return 0; 1292eccc90bSAndre Oppermann } 1302eccc90bSAndre Oppermann 1312eccc90bSAndre Oppermann /* 132df8bae1dSRodney W. Grimes * Determine whether an IP address is in a reserved set of addresses 133df8bae1dSRodney W. Grimes * that may not be forwarded, or whether datagrams to that destination 134df8bae1dSRodney W. Grimes * may be forwarded. 135df8bae1dSRodney W. Grimes */ 13626f9a767SRodney W. Grimes int 137df8bae1dSRodney W. Grimes in_canforward(in) 138df8bae1dSRodney W. Grimes struct in_addr in; 139df8bae1dSRodney W. Grimes { 140df8bae1dSRodney W. Grimes register u_long i = ntohl(in.s_addr); 141df8bae1dSRodney W. Grimes register u_long net; 142df8bae1dSRodney W. Grimes 143df8bae1dSRodney W. Grimes if (IN_EXPERIMENTAL(i) || IN_MULTICAST(i)) 144df8bae1dSRodney W. Grimes return (0); 145df8bae1dSRodney W. Grimes if (IN_CLASSA(i)) { 146df8bae1dSRodney W. Grimes net = i & IN_CLASSA_NET; 147df8bae1dSRodney W. Grimes if (net == 0 || net == (IN_LOOPBACKNET << IN_CLASSA_NSHIFT)) 148df8bae1dSRodney W. Grimes return (0); 149df8bae1dSRodney W. Grimes } 150df8bae1dSRodney W. Grimes return (1); 151df8bae1dSRodney W. Grimes } 152df8bae1dSRodney W. Grimes 153df8bae1dSRodney W. Grimes /* 154df8bae1dSRodney W. Grimes * Trim a mask in a sockaddr 155df8bae1dSRodney W. Grimes */ 1560312fbe9SPoul-Henning Kamp static void 157df8bae1dSRodney W. Grimes in_socktrim(ap) 158df8bae1dSRodney W. Grimes struct sockaddr_in *ap; 159df8bae1dSRodney W. Grimes { 160df8bae1dSRodney W. Grimes register char *cplim = (char *) &ap->sin_addr; 161df8bae1dSRodney W. Grimes register char *cp = (char *) (&ap->sin_addr + 1); 162df8bae1dSRodney W. Grimes 163df8bae1dSRodney W. Grimes ap->sin_len = 0; 164df00058dSGarrett Wollman while (--cp >= cplim) 165df8bae1dSRodney W. Grimes if (*cp) { 166df8bae1dSRodney W. Grimes (ap)->sin_len = cp - (char *) (ap) + 1; 167df8bae1dSRodney W. Grimes break; 168df8bae1dSRodney W. Grimes } 169df8bae1dSRodney W. Grimes } 170df8bae1dSRodney W. Grimes 1716a800098SYoshinobu Inoue static int 1726a800098SYoshinobu Inoue in_mask2len(mask) 1736a800098SYoshinobu Inoue struct in_addr *mask; 1746a800098SYoshinobu Inoue { 1756a800098SYoshinobu Inoue int x, y; 1766a800098SYoshinobu Inoue u_char *p; 1776a800098SYoshinobu Inoue 1786a800098SYoshinobu Inoue p = (u_char *)mask; 1796a800098SYoshinobu Inoue for (x = 0; x < sizeof(*mask); x++) { 1806a800098SYoshinobu Inoue if (p[x] != 0xff) 1816a800098SYoshinobu Inoue break; 1826a800098SYoshinobu Inoue } 1836a800098SYoshinobu Inoue y = 0; 1846a800098SYoshinobu Inoue if (x < sizeof(*mask)) { 1856a800098SYoshinobu Inoue for (y = 0; y < 8; y++) { 1866a800098SYoshinobu Inoue if ((p[x] & (0x80 >> y)) == 0) 1876a800098SYoshinobu Inoue break; 1886a800098SYoshinobu Inoue } 1896a800098SYoshinobu Inoue } 1906a800098SYoshinobu Inoue return x * 8 + y; 1916a800098SYoshinobu Inoue } 1926a800098SYoshinobu Inoue 1936a800098SYoshinobu Inoue static void 1946a800098SYoshinobu Inoue in_len2mask(mask, len) 1956a800098SYoshinobu Inoue struct in_addr *mask; 1966a800098SYoshinobu Inoue int len; 1976a800098SYoshinobu Inoue { 1986a800098SYoshinobu Inoue int i; 1996a800098SYoshinobu Inoue u_char *p; 2006a800098SYoshinobu Inoue 2016a800098SYoshinobu Inoue p = (u_char *)mask; 2026a800098SYoshinobu Inoue bzero(mask, sizeof(*mask)); 2036a800098SYoshinobu Inoue for (i = 0; i < len / 8; i++) 2046a800098SYoshinobu Inoue p[i] = 0xff; 2056a800098SYoshinobu Inoue if (len % 8) 2066a800098SYoshinobu Inoue p[i] = (0xff00 >> (len % 8)) & 0xff; 2076a800098SYoshinobu Inoue } 2086a800098SYoshinobu Inoue 209df8bae1dSRodney W. Grimes /* 210df8bae1dSRodney W. Grimes * Generic internet control operations (ioctl's). 211df8bae1dSRodney W. Grimes * Ifp is 0 if not an interface-specific ioctl. 212df8bae1dSRodney W. Grimes */ 213df8bae1dSRodney W. Grimes /* ARGSUSED */ 21426f9a767SRodney W. Grimes int 215b40ce416SJulian Elischer in_control(so, cmd, data, ifp, td) 216df8bae1dSRodney W. Grimes struct socket *so; 217ecbb00a2SDoug Rabson u_long cmd; 218df8bae1dSRodney W. Grimes caddr_t data; 219df8bae1dSRodney W. Grimes register struct ifnet *ifp; 220b40ce416SJulian Elischer struct thread *td; 221df8bae1dSRodney W. Grimes { 222df8bae1dSRodney W. Grimes register struct ifreq *ifr = (struct ifreq *)data; 223ac0aa473SBill Fenner register struct in_ifaddr *ia = 0, *iap; 224df8bae1dSRodney W. Grimes register struct ifaddr *ifa; 225ca925d9cSJonathan Lemon struct in_addr dst; 226df8bae1dSRodney W. Grimes struct in_ifaddr *oia; 227df8bae1dSRodney W. Grimes struct in_aliasreq *ifra = (struct in_aliasreq *)data; 228df8bae1dSRodney W. Grimes struct sockaddr_in oldaddr; 2290f02fdacSBrian Somers int error, hostIsNew, iaIsNew, maskIsNew, s; 2300f02fdacSBrian Somers 2310f02fdacSBrian Somers iaIsNew = 0; 232df8bae1dSRodney W. Grimes 2336a800098SYoshinobu Inoue switch (cmd) { 2346a800098SYoshinobu Inoue case SIOCALIFADDR: 2356a800098SYoshinobu Inoue case SIOCDLIFADDR: 23644731cabSJohn Baldwin if (td && (error = suser(td)) != 0) 2376a800098SYoshinobu Inoue return error; 2386a800098SYoshinobu Inoue /*fall through*/ 2396a800098SYoshinobu Inoue case SIOCGLIFADDR: 2406a800098SYoshinobu Inoue if (!ifp) 2416a800098SYoshinobu Inoue return EINVAL; 242b40ce416SJulian Elischer return in_lifaddr_ioctl(so, cmd, data, ifp, td); 2436a800098SYoshinobu Inoue } 2446a800098SYoshinobu Inoue 245df8bae1dSRodney W. Grimes /* 246df8bae1dSRodney W. Grimes * Find address for this interface, if it exists. 247ac0aa473SBill Fenner * 248ac0aa473SBill Fenner * If an alias address was specified, find that one instead of 249ca925d9cSJonathan Lemon * the first one on the interface, if possible. 250df8bae1dSRodney W. Grimes */ 251ca925d9cSJonathan Lemon if (ifp) { 252ca925d9cSJonathan Lemon dst = ((struct sockaddr_in *)&ifr->ifr_addr)->sin_addr; 253ca925d9cSJonathan Lemon LIST_FOREACH(iap, INADDR_HASH(dst.s_addr), ia_hash) 254ca925d9cSJonathan Lemon if (iap->ia_ifp == ifp && 255ca925d9cSJonathan Lemon iap->ia_addr.sin_addr.s_addr == dst.s_addr) { 256ac0aa473SBill Fenner ia = iap; 257df8bae1dSRodney W. Grimes break; 258ca925d9cSJonathan Lemon } 259ca925d9cSJonathan Lemon if (ia == NULL) 260ca925d9cSJonathan Lemon TAILQ_FOREACH(ifa, &ifp->if_addrhead, ifa_link) { 261ca925d9cSJonathan Lemon iap = ifatoia(ifa); 262ca925d9cSJonathan Lemon if (iap->ia_addr.sin_family == AF_INET) { 263ac0aa473SBill Fenner ia = iap; 264ac0aa473SBill Fenner break; 265ac0aa473SBill Fenner } 266ac0aa473SBill Fenner } 267ca925d9cSJonathan Lemon } 268df8bae1dSRodney W. Grimes 269df8bae1dSRodney W. Grimes switch (cmd) { 270df8bae1dSRodney W. Grimes 271df8bae1dSRodney W. Grimes case SIOCAIFADDR: 272df8bae1dSRodney W. Grimes case SIOCDIFADDR: 2736572231dSEivind Eklund if (ifp == 0) 2746572231dSEivind Eklund return (EADDRNOTAVAIL); 2751067217dSGarrett Wollman if (ifra->ifra_addr.sin_family == AF_INET) { 276fc2ffbe6SPoul-Henning Kamp for (oia = ia; ia; ia = TAILQ_NEXT(ia, ia_link)) { 277df8bae1dSRodney W. Grimes if (ia->ia_ifp == ifp && 278df8bae1dSRodney W. Grimes ia->ia_addr.sin_addr.s_addr == 279df8bae1dSRodney W. Grimes ifra->ifra_addr.sin_addr.s_addr) 280df8bae1dSRodney W. Grimes break; 281df8bae1dSRodney W. Grimes } 2821067217dSGarrett Wollman if ((ifp->if_flags & IFF_POINTOPOINT) 2831067217dSGarrett Wollman && (cmd == SIOCAIFADDR) 2841067217dSGarrett Wollman && (ifra->ifra_dstaddr.sin_addr.s_addr 2851067217dSGarrett Wollman == INADDR_ANY)) { 286357b78a9SGarrett Wollman return EDESTADDRREQ; 2871067217dSGarrett Wollman } 2881067217dSGarrett Wollman } 289df8bae1dSRodney W. Grimes if (cmd == SIOCDIFADDR && ia == 0) 290df8bae1dSRodney W. Grimes return (EADDRNOTAVAIL); 291df8bae1dSRodney W. Grimes /* FALLTHROUGH */ 292df8bae1dSRodney W. Grimes case SIOCSIFADDR: 293df8bae1dSRodney W. Grimes case SIOCSIFNETMASK: 294df8bae1dSRodney W. Grimes case SIOCSIFDSTADDR: 29544731cabSJohn Baldwin if (td && (error = suser(td)) != 0) 296a29f300eSGarrett Wollman return error; 297df8bae1dSRodney W. Grimes 298df8bae1dSRodney W. Grimes if (ifp == 0) 2996572231dSEivind Eklund return (EADDRNOTAVAIL); 300df8bae1dSRodney W. Grimes if (ia == (struct in_ifaddr *)0) { 30159562606SGarrett Wollman ia = (struct in_ifaddr *) 302a163d034SWarner Losh malloc(sizeof *ia, M_IFADDR, M_WAITOK | M_ZERO); 30359562606SGarrett Wollman if (ia == (struct in_ifaddr *)NULL) 304df8bae1dSRodney W. Grimes return (ENOBUFS); 305c655b7c4SDavid Greenman /* 306c655b7c4SDavid Greenman * Protect from ipintr() traversing address list 307c655b7c4SDavid Greenman * while we're modifying it. 308c655b7c4SDavid Greenman */ 309c655b7c4SDavid Greenman s = splnet(); 31059562606SGarrett Wollman TAILQ_INSERT_TAIL(&in_ifaddrhead, ia, ia_link); 31159562606SGarrett Wollman 31219fc74fbSJeffrey Hsu ifa = &ia->ia_ifa; 31319fc74fbSJeffrey Hsu IFA_LOCK_INIT(ifa); 31459562606SGarrett Wollman ifa->ifa_addr = (struct sockaddr *)&ia->ia_addr; 31559562606SGarrett Wollman ifa->ifa_dstaddr = (struct sockaddr *)&ia->ia_dstaddr; 31659562606SGarrett Wollman ifa->ifa_netmask = (struct sockaddr *)&ia->ia_sockmask; 31719fc74fbSJeffrey Hsu ifa->ifa_refcnt = 1; 31819fc74fbSJeffrey Hsu TAILQ_INSERT_TAIL(&ifp->if_addrhead, ifa, ifa_link); 31919fc74fbSJeffrey Hsu 320df8bae1dSRodney W. Grimes ia->ia_sockmask.sin_len = 8; 321bc183b3fSDag-Erling Smørgrav ia->ia_sockmask.sin_family = AF_INET; 322df8bae1dSRodney W. Grimes if (ifp->if_flags & IFF_BROADCAST) { 323df8bae1dSRodney W. Grimes ia->ia_broadaddr.sin_len = sizeof(ia->ia_addr); 324df8bae1dSRodney W. Grimes ia->ia_broadaddr.sin_family = AF_INET; 325df8bae1dSRodney W. Grimes } 326df8bae1dSRodney W. Grimes ia->ia_ifp = ifp; 327c655b7c4SDavid Greenman splx(s); 3280f02fdacSBrian Somers iaIsNew = 1; 329df8bae1dSRodney W. Grimes } 330df8bae1dSRodney W. Grimes break; 331df8bae1dSRodney W. Grimes 332df8bae1dSRodney W. Grimes case SIOCSIFBRDADDR: 33344731cabSJohn Baldwin if (td && (error = suser(td)) != 0) 334a29f300eSGarrett Wollman return error; 335df8bae1dSRodney W. Grimes /* FALLTHROUGH */ 336df8bae1dSRodney W. Grimes 337df8bae1dSRodney W. Grimes case SIOCGIFADDR: 338df8bae1dSRodney W. Grimes case SIOCGIFNETMASK: 339df8bae1dSRodney W. Grimes case SIOCGIFDSTADDR: 340df8bae1dSRodney W. Grimes case SIOCGIFBRDADDR: 341df8bae1dSRodney W. Grimes if (ia == (struct in_ifaddr *)0) 342df8bae1dSRodney W. Grimes return (EADDRNOTAVAIL); 343df8bae1dSRodney W. Grimes break; 344df8bae1dSRodney W. Grimes } 345df8bae1dSRodney W. Grimes switch (cmd) { 346df8bae1dSRodney W. Grimes 347df8bae1dSRodney W. Grimes case SIOCGIFADDR: 348df8bae1dSRodney W. Grimes *((struct sockaddr_in *)&ifr->ifr_addr) = ia->ia_addr; 3490f02fdacSBrian Somers return (0); 350df8bae1dSRodney W. Grimes 351df8bae1dSRodney W. Grimes case SIOCGIFBRDADDR: 352df8bae1dSRodney W. Grimes if ((ifp->if_flags & IFF_BROADCAST) == 0) 353df8bae1dSRodney W. Grimes return (EINVAL); 354df8bae1dSRodney W. Grimes *((struct sockaddr_in *)&ifr->ifr_dstaddr) = ia->ia_broadaddr; 3550f02fdacSBrian Somers return (0); 356df8bae1dSRodney W. Grimes 357df8bae1dSRodney W. Grimes case SIOCGIFDSTADDR: 358df8bae1dSRodney W. Grimes if ((ifp->if_flags & IFF_POINTOPOINT) == 0) 359df8bae1dSRodney W. Grimes return (EINVAL); 360df8bae1dSRodney W. Grimes *((struct sockaddr_in *)&ifr->ifr_dstaddr) = ia->ia_dstaddr; 3610f02fdacSBrian Somers return (0); 362df8bae1dSRodney W. Grimes 363df8bae1dSRodney W. Grimes case SIOCGIFNETMASK: 364df8bae1dSRodney W. Grimes *((struct sockaddr_in *)&ifr->ifr_addr) = ia->ia_sockmask; 3650f02fdacSBrian Somers return (0); 366df8bae1dSRodney W. Grimes 367df8bae1dSRodney W. Grimes case SIOCSIFDSTADDR: 368df8bae1dSRodney W. Grimes if ((ifp->if_flags & IFF_POINTOPOINT) == 0) 369df8bae1dSRodney W. Grimes return (EINVAL); 370df8bae1dSRodney W. Grimes oldaddr = ia->ia_dstaddr; 371df8bae1dSRodney W. Grimes ia->ia_dstaddr = *(struct sockaddr_in *)&ifr->ifr_dstaddr; 372ba5da2a0SIan Dowse if (ifp->if_ioctl) { 373ba5da2a0SIan Dowse IFF_LOCKGIANT(ifp); 374ba5da2a0SIan Dowse error = (*ifp->if_ioctl)(ifp, SIOCSIFDSTADDR, 375ba5da2a0SIan Dowse (caddr_t)ia); 376ba5da2a0SIan Dowse IFF_UNLOCKGIANT(ifp); 377ba5da2a0SIan Dowse if (error) { 378df8bae1dSRodney W. Grimes ia->ia_dstaddr = oldaddr; 379df8bae1dSRodney W. Grimes return (error); 380df8bae1dSRodney W. Grimes } 381ba5da2a0SIan Dowse } 382df8bae1dSRodney W. Grimes if (ia->ia_flags & IFA_ROUTE) { 383df8bae1dSRodney W. Grimes ia->ia_ifa.ifa_dstaddr = (struct sockaddr *)&oldaddr; 384df8bae1dSRodney W. Grimes rtinit(&(ia->ia_ifa), (int)RTM_DELETE, RTF_HOST); 385df8bae1dSRodney W. Grimes ia->ia_ifa.ifa_dstaddr = 386df8bae1dSRodney W. Grimes (struct sockaddr *)&ia->ia_dstaddr; 387df8bae1dSRodney W. Grimes rtinit(&(ia->ia_ifa), (int)RTM_ADD, RTF_HOST|RTF_UP); 388df8bae1dSRodney W. Grimes } 3890f02fdacSBrian Somers return (0); 390df8bae1dSRodney W. Grimes 391df8bae1dSRodney W. Grimes case SIOCSIFBRDADDR: 392df8bae1dSRodney W. Grimes if ((ifp->if_flags & IFF_BROADCAST) == 0) 393df8bae1dSRodney W. Grimes return (EINVAL); 394df8bae1dSRodney W. Grimes ia->ia_broadaddr = *(struct sockaddr_in *)&ifr->ifr_broadaddr; 3950f02fdacSBrian Somers return (0); 396df8bae1dSRodney W. Grimes 397df8bae1dSRodney W. Grimes case SIOCSIFADDR: 3980f02fdacSBrian Somers error = in_ifinit(ifp, ia, 3990f02fdacSBrian Somers (struct sockaddr_in *) &ifr->ifr_addr, 1); 4000f02fdacSBrian Somers if (error != 0 && iaIsNew) 4010f02fdacSBrian Somers break; 40225a4adceSMax Laier if (error == 0) 40325a4adceSMax Laier EVENTHANDLER_INVOKE(ifaddr_event, ifp); 4040f02fdacSBrian Somers return (0); 405df8bae1dSRodney W. Grimes 406df8bae1dSRodney W. Grimes case SIOCSIFNETMASK: 407bc183b3fSDag-Erling Smørgrav ia->ia_sockmask.sin_addr = ifra->ifra_addr.sin_addr; 408bc183b3fSDag-Erling Smørgrav ia->ia_subnetmask = ntohl(ia->ia_sockmask.sin_addr.s_addr); 4090f02fdacSBrian Somers return (0); 410df8bae1dSRodney W. Grimes 411df8bae1dSRodney W. Grimes case SIOCAIFADDR: 412df8bae1dSRodney W. Grimes maskIsNew = 0; 413df8bae1dSRodney W. Grimes hostIsNew = 1; 414df8bae1dSRodney W. Grimes error = 0; 415df8bae1dSRodney W. Grimes if (ia->ia_addr.sin_family == AF_INET) { 416df8bae1dSRodney W. Grimes if (ifra->ifra_addr.sin_len == 0) { 417df8bae1dSRodney W. Grimes ifra->ifra_addr = ia->ia_addr; 418df8bae1dSRodney W. Grimes hostIsNew = 0; 419df8bae1dSRodney W. Grimes } else if (ifra->ifra_addr.sin_addr.s_addr == 420df8bae1dSRodney W. Grimes ia->ia_addr.sin_addr.s_addr) 421df8bae1dSRodney W. Grimes hostIsNew = 0; 422df8bae1dSRodney W. Grimes } 423df8bae1dSRodney W. Grimes if (ifra->ifra_mask.sin_len) { 424df8bae1dSRodney W. Grimes in_ifscrub(ifp, ia); 425df8bae1dSRodney W. Grimes ia->ia_sockmask = ifra->ifra_mask; 426bc183b3fSDag-Erling Smørgrav ia->ia_sockmask.sin_family = AF_INET; 427df8bae1dSRodney W. Grimes ia->ia_subnetmask = 428df8bae1dSRodney W. Grimes ntohl(ia->ia_sockmask.sin_addr.s_addr); 429df8bae1dSRodney W. Grimes maskIsNew = 1; 430df8bae1dSRodney W. Grimes } 431df8bae1dSRodney W. Grimes if ((ifp->if_flags & IFF_POINTOPOINT) && 432df8bae1dSRodney W. Grimes (ifra->ifra_dstaddr.sin_family == AF_INET)) { 433df8bae1dSRodney W. Grimes in_ifscrub(ifp, ia); 434df8bae1dSRodney W. Grimes ia->ia_dstaddr = ifra->ifra_dstaddr; 435df8bae1dSRodney W. Grimes maskIsNew = 1; /* We lie; but the effect's the same */ 436df8bae1dSRodney W. Grimes } 437df8bae1dSRodney W. Grimes if (ifra->ifra_addr.sin_family == AF_INET && 438df8bae1dSRodney W. Grimes (hostIsNew || maskIsNew)) 439df8bae1dSRodney W. Grimes error = in_ifinit(ifp, ia, &ifra->ifra_addr, 0); 4400f02fdacSBrian Somers if (error != 0 && iaIsNew) 4410f02fdacSBrian Somers break; 4420f02fdacSBrian Somers 443df8bae1dSRodney W. Grimes if ((ifp->if_flags & IFF_BROADCAST) && 444df8bae1dSRodney W. Grimes (ifra->ifra_broadaddr.sin_family == AF_INET)) 445df8bae1dSRodney W. Grimes ia->ia_broadaddr = ifra->ifra_broadaddr; 44625a4adceSMax Laier if (error == 0) 44725a4adceSMax Laier EVENTHANDLER_INVOKE(ifaddr_event, ifp); 448df8bae1dSRodney W. Grimes return (error); 449df8bae1dSRodney W. Grimes 450df8bae1dSRodney W. Grimes case SIOCDIFADDR: 451089cdfadSRuslan Ermilov /* 452089cdfadSRuslan Ermilov * in_ifscrub kills the interface route. 453089cdfadSRuslan Ermilov */ 454df8bae1dSRodney W. Grimes in_ifscrub(ifp, ia); 455c655b7c4SDavid Greenman /* 456089cdfadSRuslan Ermilov * in_ifadown gets rid of all the rest of 457089cdfadSRuslan Ermilov * the routes. This is not quite the right 458089cdfadSRuslan Ermilov * thing to do, but at least if we are running 459089cdfadSRuslan Ermilov * a routing process they will come back. 460089cdfadSRuslan Ermilov */ 46191854268SRuslan Ermilov in_ifadown(&ia->ia_ifa, 1); 462e43cc4aeSHajimu UMEMOTO /* 463e43cc4aeSHajimu UMEMOTO * XXX horrible hack to detect that we are being called 464e43cc4aeSHajimu UMEMOTO * from if_detach() 465e43cc4aeSHajimu UMEMOTO */ 4669ce78778SSam Leffler if (ifaddr_byindex(ifp->if_index) == NULL) { 467f76fcf6dSJeffrey Hsu in_pcbpurgeif0(&ripcbinfo, ifp); 468f76fcf6dSJeffrey Hsu in_pcbpurgeif0(&udbinfo, ifp); 469e43cc4aeSHajimu UMEMOTO } 47025a4adceSMax Laier EVENTHANDLER_INVOKE(ifaddr_event, ifp); 4710f02fdacSBrian Somers error = 0; 472df8bae1dSRodney W. Grimes break; 473df8bae1dSRodney W. Grimes 474df8bae1dSRodney W. Grimes default: 475df8bae1dSRodney W. Grimes if (ifp == 0 || ifp->if_ioctl == 0) 476df8bae1dSRodney W. Grimes return (EOPNOTSUPP); 477ba5da2a0SIan Dowse IFF_LOCKGIANT(ifp); 478ba5da2a0SIan Dowse error = (*ifp->if_ioctl)(ifp, cmd, data); 479ba5da2a0SIan Dowse IFF_UNLOCKGIANT(ifp); 480ba5da2a0SIan Dowse return (error); 481df8bae1dSRodney W. Grimes } 4820f02fdacSBrian Somers 4830f02fdacSBrian Somers /* 4840f02fdacSBrian Somers * Protect from ipintr() traversing address list while we're modifying 4850f02fdacSBrian Somers * it. 4860f02fdacSBrian Somers */ 4870f02fdacSBrian Somers s = splnet(); 4880f02fdacSBrian Somers TAILQ_REMOVE(&ifp->if_addrhead, &ia->ia_ifa, ifa_link); 4890f02fdacSBrian Somers TAILQ_REMOVE(&in_ifaddrhead, ia, ia_link); 4900f02fdacSBrian Somers LIST_REMOVE(ia, ia_hash); 4910f02fdacSBrian Somers IFAFREE(&ia->ia_ifa); 4920f02fdacSBrian Somers splx(s); 4930f02fdacSBrian Somers 4940f02fdacSBrian Somers return (error); 495df8bae1dSRodney W. Grimes } 496df8bae1dSRodney W. Grimes 497df8bae1dSRodney W. Grimes /* 4986a800098SYoshinobu Inoue * SIOC[GAD]LIFADDR. 4996a800098SYoshinobu Inoue * SIOCGLIFADDR: get first address. (?!?) 5006a800098SYoshinobu Inoue * SIOCGLIFADDR with IFLR_PREFIX: 5016a800098SYoshinobu Inoue * get first address that matches the specified prefix. 5026a800098SYoshinobu Inoue * SIOCALIFADDR: add the specified address. 5036a800098SYoshinobu Inoue * SIOCALIFADDR with IFLR_PREFIX: 5046a800098SYoshinobu Inoue * EINVAL since we can't deduce hostid part of the address. 5056a800098SYoshinobu Inoue * SIOCDLIFADDR: delete the specified address. 5066a800098SYoshinobu Inoue * SIOCDLIFADDR with IFLR_PREFIX: 5076a800098SYoshinobu Inoue * delete the first address that matches the specified prefix. 5086a800098SYoshinobu Inoue * return values: 5096a800098SYoshinobu Inoue * EINVAL on invalid parameters 5106a800098SYoshinobu Inoue * EADDRNOTAVAIL on prefix match failed/specified address not found 5116a800098SYoshinobu Inoue * other values may be returned from in_ioctl() 5126a800098SYoshinobu Inoue */ 5136a800098SYoshinobu Inoue static int 514b40ce416SJulian Elischer in_lifaddr_ioctl(so, cmd, data, ifp, td) 5156a800098SYoshinobu Inoue struct socket *so; 5166a800098SYoshinobu Inoue u_long cmd; 5176a800098SYoshinobu Inoue caddr_t data; 5186a800098SYoshinobu Inoue struct ifnet *ifp; 519b40ce416SJulian Elischer struct thread *td; 5206a800098SYoshinobu Inoue { 5216a800098SYoshinobu Inoue struct if_laddrreq *iflr = (struct if_laddrreq *)data; 5226a800098SYoshinobu Inoue struct ifaddr *ifa; 5236a800098SYoshinobu Inoue 5246a800098SYoshinobu Inoue /* sanity checks */ 5256a800098SYoshinobu Inoue if (!data || !ifp) { 5266a800098SYoshinobu Inoue panic("invalid argument to in_lifaddr_ioctl"); 5276a800098SYoshinobu Inoue /*NOTRECHED*/ 5286a800098SYoshinobu Inoue } 5296a800098SYoshinobu Inoue 5306a800098SYoshinobu Inoue switch (cmd) { 5316a800098SYoshinobu Inoue case SIOCGLIFADDR: 5326a800098SYoshinobu Inoue /* address must be specified on GET with IFLR_PREFIX */ 5336a800098SYoshinobu Inoue if ((iflr->flags & IFLR_PREFIX) == 0) 5346a800098SYoshinobu Inoue break; 5356a800098SYoshinobu Inoue /*FALLTHROUGH*/ 5366a800098SYoshinobu Inoue case SIOCALIFADDR: 5376a800098SYoshinobu Inoue case SIOCDLIFADDR: 5386a800098SYoshinobu Inoue /* address must be specified on ADD and DELETE */ 5395d60ed0eSYoshinobu Inoue if (iflr->addr.ss_family != AF_INET) 5406a800098SYoshinobu Inoue return EINVAL; 5415d60ed0eSYoshinobu Inoue if (iflr->addr.ss_len != sizeof(struct sockaddr_in)) 5426a800098SYoshinobu Inoue return EINVAL; 5436a800098SYoshinobu Inoue /* XXX need improvement */ 5445d60ed0eSYoshinobu Inoue if (iflr->dstaddr.ss_family 5455d60ed0eSYoshinobu Inoue && iflr->dstaddr.ss_family != AF_INET) 5466a800098SYoshinobu Inoue return EINVAL; 5475d60ed0eSYoshinobu Inoue if (iflr->dstaddr.ss_family 5485d60ed0eSYoshinobu Inoue && iflr->dstaddr.ss_len != sizeof(struct sockaddr_in)) 5496a800098SYoshinobu Inoue return EINVAL; 5506a800098SYoshinobu Inoue break; 5516a800098SYoshinobu Inoue default: /*shouldn't happen*/ 5526a800098SYoshinobu Inoue return EOPNOTSUPP; 5536a800098SYoshinobu Inoue } 5546a800098SYoshinobu Inoue if (sizeof(struct in_addr) * 8 < iflr->prefixlen) 5556a800098SYoshinobu Inoue return EINVAL; 5566a800098SYoshinobu Inoue 5576a800098SYoshinobu Inoue switch (cmd) { 5586a800098SYoshinobu Inoue case SIOCALIFADDR: 5596a800098SYoshinobu Inoue { 5606a800098SYoshinobu Inoue struct in_aliasreq ifra; 5616a800098SYoshinobu Inoue 5626a800098SYoshinobu Inoue if (iflr->flags & IFLR_PREFIX) 5636a800098SYoshinobu Inoue return EINVAL; 5646a800098SYoshinobu Inoue 5656a800098SYoshinobu Inoue /* copy args to in_aliasreq, perform ioctl(SIOCAIFADDR_IN6). */ 5666a800098SYoshinobu Inoue bzero(&ifra, sizeof(ifra)); 5676a800098SYoshinobu Inoue bcopy(iflr->iflr_name, ifra.ifra_name, 5686a800098SYoshinobu Inoue sizeof(ifra.ifra_name)); 5696a800098SYoshinobu Inoue 5705d60ed0eSYoshinobu Inoue bcopy(&iflr->addr, &ifra.ifra_addr, iflr->addr.ss_len); 5716a800098SYoshinobu Inoue 5725d60ed0eSYoshinobu Inoue if (iflr->dstaddr.ss_family) { /*XXX*/ 5736a800098SYoshinobu Inoue bcopy(&iflr->dstaddr, &ifra.ifra_dstaddr, 5745d60ed0eSYoshinobu Inoue iflr->dstaddr.ss_len); 5756a800098SYoshinobu Inoue } 5766a800098SYoshinobu Inoue 5776a800098SYoshinobu Inoue ifra.ifra_mask.sin_family = AF_INET; 5786a800098SYoshinobu Inoue ifra.ifra_mask.sin_len = sizeof(struct sockaddr_in); 5796a800098SYoshinobu Inoue in_len2mask(&ifra.ifra_mask.sin_addr, iflr->prefixlen); 5806a800098SYoshinobu Inoue 581b40ce416SJulian Elischer return in_control(so, SIOCAIFADDR, (caddr_t)&ifra, ifp, td); 5826a800098SYoshinobu Inoue } 5836a800098SYoshinobu Inoue case SIOCGLIFADDR: 5846a800098SYoshinobu Inoue case SIOCDLIFADDR: 5856a800098SYoshinobu Inoue { 5866a800098SYoshinobu Inoue struct in_ifaddr *ia; 5876a800098SYoshinobu Inoue struct in_addr mask, candidate, match; 5886a800098SYoshinobu Inoue struct sockaddr_in *sin; 5896a800098SYoshinobu Inoue int cmp; 5906a800098SYoshinobu Inoue 5916a800098SYoshinobu Inoue bzero(&mask, sizeof(mask)); 5926a800098SYoshinobu Inoue if (iflr->flags & IFLR_PREFIX) { 5936a800098SYoshinobu Inoue /* lookup a prefix rather than address. */ 5946a800098SYoshinobu Inoue in_len2mask(&mask, iflr->prefixlen); 5956a800098SYoshinobu Inoue 5966a800098SYoshinobu Inoue sin = (struct sockaddr_in *)&iflr->addr; 5976a800098SYoshinobu Inoue match.s_addr = sin->sin_addr.s_addr; 5986a800098SYoshinobu Inoue match.s_addr &= mask.s_addr; 5996a800098SYoshinobu Inoue 6006a800098SYoshinobu Inoue /* if you set extra bits, that's wrong */ 6016a800098SYoshinobu Inoue if (match.s_addr != sin->sin_addr.s_addr) 6026a800098SYoshinobu Inoue return EINVAL; 6036a800098SYoshinobu Inoue 6046a800098SYoshinobu Inoue cmp = 1; 6056a800098SYoshinobu Inoue } else { 6066a800098SYoshinobu Inoue if (cmd == SIOCGLIFADDR) { 6076a800098SYoshinobu Inoue /* on getting an address, take the 1st match */ 6086a800098SYoshinobu Inoue cmp = 0; /*XXX*/ 6096a800098SYoshinobu Inoue } else { 6106a800098SYoshinobu Inoue /* on deleting an address, do exact match */ 6116a800098SYoshinobu Inoue in_len2mask(&mask, 32); 6126a800098SYoshinobu Inoue sin = (struct sockaddr_in *)&iflr->addr; 6136a800098SYoshinobu Inoue match.s_addr = sin->sin_addr.s_addr; 6146a800098SYoshinobu Inoue 6156a800098SYoshinobu Inoue cmp = 1; 6166a800098SYoshinobu Inoue } 6176a800098SYoshinobu Inoue } 6186a800098SYoshinobu Inoue 6196a800098SYoshinobu Inoue TAILQ_FOREACH(ifa, &ifp->if_addrhead, ifa_link) { 6206a800098SYoshinobu Inoue if (ifa->ifa_addr->sa_family != AF_INET6) 6216a800098SYoshinobu Inoue continue; 6226a800098SYoshinobu Inoue if (!cmp) 6236a800098SYoshinobu Inoue break; 6246a800098SYoshinobu Inoue candidate.s_addr = ((struct sockaddr_in *)&ifa->ifa_addr)->sin_addr.s_addr; 6256a800098SYoshinobu Inoue candidate.s_addr &= mask.s_addr; 6266a800098SYoshinobu Inoue if (candidate.s_addr == match.s_addr) 6276a800098SYoshinobu Inoue break; 6286a800098SYoshinobu Inoue } 6296a800098SYoshinobu Inoue if (!ifa) 6306a800098SYoshinobu Inoue return EADDRNOTAVAIL; 6316a800098SYoshinobu Inoue ia = (struct in_ifaddr *)ifa; 6326a800098SYoshinobu Inoue 6336a800098SYoshinobu Inoue if (cmd == SIOCGLIFADDR) { 6346a800098SYoshinobu Inoue /* fill in the if_laddrreq structure */ 6356a800098SYoshinobu Inoue bcopy(&ia->ia_addr, &iflr->addr, ia->ia_addr.sin_len); 6366a800098SYoshinobu Inoue 6376a800098SYoshinobu Inoue if ((ifp->if_flags & IFF_POINTOPOINT) != 0) { 6386a800098SYoshinobu Inoue bcopy(&ia->ia_dstaddr, &iflr->dstaddr, 6396a800098SYoshinobu Inoue ia->ia_dstaddr.sin_len); 6406a800098SYoshinobu Inoue } else 6416a800098SYoshinobu Inoue bzero(&iflr->dstaddr, sizeof(iflr->dstaddr)); 6426a800098SYoshinobu Inoue 6436a800098SYoshinobu Inoue iflr->prefixlen = 6446a800098SYoshinobu Inoue in_mask2len(&ia->ia_sockmask.sin_addr); 6456a800098SYoshinobu Inoue 6466a800098SYoshinobu Inoue iflr->flags = 0; /*XXX*/ 6476a800098SYoshinobu Inoue 6486a800098SYoshinobu Inoue return 0; 6496a800098SYoshinobu Inoue } else { 6506a800098SYoshinobu Inoue struct in_aliasreq ifra; 6516a800098SYoshinobu Inoue 6526a800098SYoshinobu Inoue /* fill in_aliasreq and do ioctl(SIOCDIFADDR_IN6) */ 6536a800098SYoshinobu Inoue bzero(&ifra, sizeof(ifra)); 6546a800098SYoshinobu Inoue bcopy(iflr->iflr_name, ifra.ifra_name, 6556a800098SYoshinobu Inoue sizeof(ifra.ifra_name)); 6566a800098SYoshinobu Inoue 6576a800098SYoshinobu Inoue bcopy(&ia->ia_addr, &ifra.ifra_addr, 6586a800098SYoshinobu Inoue ia->ia_addr.sin_len); 6596a800098SYoshinobu Inoue if ((ifp->if_flags & IFF_POINTOPOINT) != 0) { 6606a800098SYoshinobu Inoue bcopy(&ia->ia_dstaddr, &ifra.ifra_dstaddr, 6616a800098SYoshinobu Inoue ia->ia_dstaddr.sin_len); 6626a800098SYoshinobu Inoue } 6636a800098SYoshinobu Inoue bcopy(&ia->ia_sockmask, &ifra.ifra_dstaddr, 6646a800098SYoshinobu Inoue ia->ia_sockmask.sin_len); 6656a800098SYoshinobu Inoue 6666a800098SYoshinobu Inoue return in_control(so, SIOCDIFADDR, (caddr_t)&ifra, 667b40ce416SJulian Elischer ifp, td); 6686a800098SYoshinobu Inoue } 6696a800098SYoshinobu Inoue } 6706a800098SYoshinobu Inoue } 6716a800098SYoshinobu Inoue 6726a800098SYoshinobu Inoue return EOPNOTSUPP; /*just for safety*/ 6736a800098SYoshinobu Inoue } 6746a800098SYoshinobu Inoue 6756a800098SYoshinobu Inoue /* 676df8bae1dSRodney W. Grimes * Delete any existing route for an interface. 677df8bae1dSRodney W. Grimes */ 67839191c8eSGarrett Wollman void 679df8bae1dSRodney W. Grimes in_ifscrub(ifp, ia) 680df8bae1dSRodney W. Grimes register struct ifnet *ifp; 681df8bae1dSRodney W. Grimes register struct in_ifaddr *ia; 682df8bae1dSRodney W. Grimes { 68348321abeSMax Laier in_scrubprefix(ia); 684df8bae1dSRodney W. Grimes } 685df8bae1dSRodney W. Grimes 686df8bae1dSRodney W. Grimes /* 687df8bae1dSRodney W. Grimes * Initialize an interface's internet address 688df8bae1dSRodney W. Grimes * and routing table entry. 689df8bae1dSRodney W. Grimes */ 6900312fbe9SPoul-Henning Kamp static int 691df8bae1dSRodney W. Grimes in_ifinit(ifp, ia, sin, scrub) 692df8bae1dSRodney W. Grimes register struct ifnet *ifp; 693df8bae1dSRodney W. Grimes register struct in_ifaddr *ia; 694df8bae1dSRodney W. Grimes struct sockaddr_in *sin; 695df8bae1dSRodney W. Grimes int scrub; 696df8bae1dSRodney W. Grimes { 697df8bae1dSRodney W. Grimes register u_long i = ntohl(sin->sin_addr.s_addr); 698df8bae1dSRodney W. Grimes struct sockaddr_in oldaddr; 6995a43847dSBrian Somers int s = splimp(), flags = RTF_UP, error = 0; 700df8bae1dSRodney W. Grimes 701df8bae1dSRodney W. Grimes oldaddr = ia->ia_addr; 7022754d95dSSUZUKI Shinsuke if (oldaddr.sin_family == AF_INET) 7032754d95dSSUZUKI Shinsuke LIST_REMOVE(ia, ia_hash); 704df8bae1dSRodney W. Grimes ia->ia_addr = *sin; 7052754d95dSSUZUKI Shinsuke if (ia->ia_addr.sin_family == AF_INET) 7062754d95dSSUZUKI Shinsuke LIST_INSERT_HEAD(INADDR_HASH(ia->ia_addr.sin_addr.s_addr), 7072754d95dSSUZUKI Shinsuke ia, ia_hash); 708df8bae1dSRodney W. Grimes /* 709df8bae1dSRodney W. Grimes * Give the interface a chance to initialize 710df8bae1dSRodney W. Grimes * if this is its first address, 711df8bae1dSRodney W. Grimes * and to validate the address if necessary. 712df8bae1dSRodney W. Grimes */ 713ba5da2a0SIan Dowse if (ifp->if_ioctl) { 714ba5da2a0SIan Dowse IFF_LOCKGIANT(ifp); 715ba5da2a0SIan Dowse error = (*ifp->if_ioctl)(ifp, SIOCSIFADDR, (caddr_t)ia); 716ba5da2a0SIan Dowse IFF_UNLOCKGIANT(ifp); 717ba5da2a0SIan Dowse if (error) { 718df8bae1dSRodney W. Grimes splx(s); 7192754d95dSSUZUKI Shinsuke /* LIST_REMOVE(ia, ia_hash) is done in in_control */ 720df8bae1dSRodney W. Grimes ia->ia_addr = oldaddr; 72122c819a7SJonathan Lemon if (ia->ia_addr.sin_family == AF_INET) 722ba5da2a0SIan Dowse LIST_INSERT_HEAD(INADDR_HASH( 723ba5da2a0SIan Dowse ia->ia_addr.sin_addr.s_addr), ia, ia_hash); 7242754d95dSSUZUKI Shinsuke return (error); 7252754d95dSSUZUKI Shinsuke } 726ba5da2a0SIan Dowse } 727df8bae1dSRodney W. Grimes splx(s); 728df8bae1dSRodney W. Grimes if (scrub) { 729df8bae1dSRodney W. Grimes ia->ia_ifa.ifa_addr = (struct sockaddr *)&oldaddr; 730df8bae1dSRodney W. Grimes in_ifscrub(ifp, ia); 731df8bae1dSRodney W. Grimes ia->ia_ifa.ifa_addr = (struct sockaddr *)&ia->ia_addr; 732df8bae1dSRodney W. Grimes } 733df8bae1dSRodney W. Grimes if (IN_CLASSA(i)) 734df8bae1dSRodney W. Grimes ia->ia_netmask = IN_CLASSA_NET; 735df8bae1dSRodney W. Grimes else if (IN_CLASSB(i)) 736df8bae1dSRodney W. Grimes ia->ia_netmask = IN_CLASSB_NET; 737df8bae1dSRodney W. Grimes else 738df8bae1dSRodney W. Grimes ia->ia_netmask = IN_CLASSC_NET; 739df8bae1dSRodney W. Grimes /* 740df8bae1dSRodney W. Grimes * The subnet mask usually includes at least the standard network part, 741df8bae1dSRodney W. Grimes * but may may be smaller in the case of supernetting. 742df8bae1dSRodney W. Grimes * If it is set, we believe it. 743df8bae1dSRodney W. Grimes */ 744df8bae1dSRodney W. Grimes if (ia->ia_subnetmask == 0) { 745df8bae1dSRodney W. Grimes ia->ia_subnetmask = ia->ia_netmask; 746df8bae1dSRodney W. Grimes ia->ia_sockmask.sin_addr.s_addr = htonl(ia->ia_subnetmask); 747df8bae1dSRodney W. Grimes } else 748df8bae1dSRodney W. Grimes ia->ia_netmask &= ia->ia_subnetmask; 749df8bae1dSRodney W. Grimes ia->ia_net = i & ia->ia_netmask; 750df8bae1dSRodney W. Grimes ia->ia_subnet = i & ia->ia_subnetmask; 751df8bae1dSRodney W. Grimes in_socktrim(&ia->ia_sockmask); 75250bb1704SGleb Smirnoff #ifdef DEV_CARP 75350bb1704SGleb Smirnoff /* 75450bb1704SGleb Smirnoff * XXX: carp(4) does not have interface route 75550bb1704SGleb Smirnoff */ 75650bb1704SGleb Smirnoff if (ifp->if_type == IFT_CARP) 75750bb1704SGleb Smirnoff return (0); 75850bb1704SGleb Smirnoff #endif 759df8bae1dSRodney W. Grimes /* 760df8bae1dSRodney W. Grimes * Add route for the network. 761df8bae1dSRodney W. Grimes */ 762df8bae1dSRodney W. Grimes ia->ia_ifa.ifa_metric = ifp->if_metric; 763df8bae1dSRodney W. Grimes if (ifp->if_flags & IFF_BROADCAST) { 764df8bae1dSRodney W. Grimes ia->ia_broadaddr.sin_addr.s_addr = 765df8bae1dSRodney W. Grimes htonl(ia->ia_subnet | ~ia->ia_subnetmask); 766df8bae1dSRodney W. Grimes ia->ia_netbroadcast.s_addr = 767df8bae1dSRodney W. Grimes htonl(ia->ia_net | ~ ia->ia_netmask); 768df8bae1dSRodney W. Grimes } else if (ifp->if_flags & IFF_LOOPBACK) { 7699a6a6eebSMax Laier ia->ia_dstaddr = ia->ia_addr; 770df8bae1dSRodney W. Grimes flags |= RTF_HOST; 771df8bae1dSRodney W. Grimes } else if (ifp->if_flags & IFF_POINTOPOINT) { 772df8bae1dSRodney W. Grimes if (ia->ia_dstaddr.sin_family != AF_INET) 773df8bae1dSRodney W. Grimes return (0); 774df8bae1dSRodney W. Grimes flags |= RTF_HOST; 775df8bae1dSRodney W. Grimes } 77648321abeSMax Laier if ((error = in_addprefix(ia, flags)) != 0) 7770f02fdacSBrian Somers return (error); 7780f02fdacSBrian Somers 779df8bae1dSRodney W. Grimes /* 780df8bae1dSRodney W. Grimes * If the interface supports multicast, join the "all hosts" 781df8bae1dSRodney W. Grimes * multicast group on that interface. 782df8bae1dSRodney W. Grimes */ 783df8bae1dSRodney W. Grimes if (ifp->if_flags & IFF_MULTICAST) { 784df8bae1dSRodney W. Grimes struct in_addr addr; 785df8bae1dSRodney W. Grimes 786df8bae1dSRodney W. Grimes addr.s_addr = htonl(INADDR_ALLHOSTS_GROUP); 787df8bae1dSRodney W. Grimes in_addmulti(&addr, ifp); 788df8bae1dSRodney W. Grimes } 789df8bae1dSRodney W. Grimes return (error); 790df8bae1dSRodney W. Grimes } 791df8bae1dSRodney W. Grimes 79248321abeSMax Laier #define rtinitflags(x) \ 79348321abeSMax Laier ((((x)->ia_ifp->if_flags & (IFF_LOOPBACK | IFF_POINTOPOINT)) != 0) \ 79448321abeSMax Laier ? RTF_HOST : 0) 79548321abeSMax Laier /* 79648321abeSMax Laier * Check if we have a route for the given prefix already or add a one 79748321abeSMax Laier * accordingly. 79848321abeSMax Laier */ 79948321abeSMax Laier static int 80048321abeSMax Laier in_addprefix(target, flags) 80148321abeSMax Laier struct in_ifaddr *target; 80248321abeSMax Laier int flags; 80348321abeSMax Laier { 80448321abeSMax Laier struct in_ifaddr *ia; 80548321abeSMax Laier struct in_addr prefix, mask, p; 80648321abeSMax Laier int error; 80748321abeSMax Laier 80848321abeSMax Laier if ((flags & RTF_HOST) != 0) 80948321abeSMax Laier prefix = target->ia_dstaddr.sin_addr; 81048321abeSMax Laier else { 81148321abeSMax Laier prefix = target->ia_addr.sin_addr; 81248321abeSMax Laier mask = target->ia_sockmask.sin_addr; 81348321abeSMax Laier prefix.s_addr &= mask.s_addr; 81448321abeSMax Laier } 81548321abeSMax Laier 81648321abeSMax Laier TAILQ_FOREACH(ia, &in_ifaddrhead, ia_link) { 81748321abeSMax Laier if (rtinitflags(ia)) 81848321abeSMax Laier p = ia->ia_dstaddr.sin_addr; 81948321abeSMax Laier else { 82048321abeSMax Laier p = ia->ia_addr.sin_addr; 82148321abeSMax Laier p.s_addr &= ia->ia_sockmask.sin_addr.s_addr; 82248321abeSMax Laier } 82348321abeSMax Laier 82448321abeSMax Laier if (prefix.s_addr != p.s_addr) 82548321abeSMax Laier continue; 82648321abeSMax Laier 82748321abeSMax Laier /* 82848321abeSMax Laier * If we got a matching prefix route inserted by other 82948321abeSMax Laier * interface address, we are done here. 83048321abeSMax Laier */ 8311ae95409SGleb Smirnoff if (ia->ia_flags & IFA_ROUTE) { 8321ae95409SGleb Smirnoff if (sameprefixcarponly && 8331ae95409SGleb Smirnoff target->ia_ifp->if_type != IFT_CARP && 8341ae95409SGleb Smirnoff ia->ia_ifp->if_type != IFT_CARP) 8351ae95409SGleb Smirnoff return (EEXIST); 8361ae95409SGleb Smirnoff else 8371ae95409SGleb Smirnoff return (0); 8381ae95409SGleb Smirnoff } 83948321abeSMax Laier } 84048321abeSMax Laier 84148321abeSMax Laier /* 84248321abeSMax Laier * No-one seem to have this prefix route, so we try to insert it. 84348321abeSMax Laier */ 84448321abeSMax Laier error = rtinit(&target->ia_ifa, (int)RTM_ADD, flags); 84548321abeSMax Laier if (!error) 84648321abeSMax Laier target->ia_flags |= IFA_ROUTE; 84748321abeSMax Laier return error; 84848321abeSMax Laier } 84948321abeSMax Laier 85048321abeSMax Laier /* 85148321abeSMax Laier * If there is no other address in the system that can serve a route to the 85248321abeSMax Laier * same prefix, remove the route. Hand over the route to the new address 85348321abeSMax Laier * otherwise. 85448321abeSMax Laier */ 85548321abeSMax Laier static int 85648321abeSMax Laier in_scrubprefix(target) 85748321abeSMax Laier struct in_ifaddr *target; 85848321abeSMax Laier { 85948321abeSMax Laier struct in_ifaddr *ia; 86048321abeSMax Laier struct in_addr prefix, mask, p; 86148321abeSMax Laier int error; 86248321abeSMax Laier 86348321abeSMax Laier if ((target->ia_flags & IFA_ROUTE) == 0) 86448321abeSMax Laier return 0; 86548321abeSMax Laier 86648321abeSMax Laier if (rtinitflags(target)) 86748321abeSMax Laier prefix = target->ia_dstaddr.sin_addr; 86848321abeSMax Laier else { 86948321abeSMax Laier prefix = target->ia_addr.sin_addr; 87048321abeSMax Laier mask = target->ia_sockmask.sin_addr; 87148321abeSMax Laier prefix.s_addr &= mask.s_addr; 87248321abeSMax Laier } 87348321abeSMax Laier 87448321abeSMax Laier TAILQ_FOREACH(ia, &in_ifaddrhead, ia_link) { 87548321abeSMax Laier if (rtinitflags(ia)) 87648321abeSMax Laier p = ia->ia_dstaddr.sin_addr; 87748321abeSMax Laier else { 87848321abeSMax Laier p = ia->ia_addr.sin_addr; 87948321abeSMax Laier p.s_addr &= ia->ia_sockmask.sin_addr.s_addr; 88048321abeSMax Laier } 88148321abeSMax Laier 88248321abeSMax Laier if (prefix.s_addr != p.s_addr) 88348321abeSMax Laier continue; 88448321abeSMax Laier 88548321abeSMax Laier /* 88648321abeSMax Laier * If we got a matching prefix address, move IFA_ROUTE and 88748321abeSMax Laier * the route itself to it. Make sure that routing daemons 88848321abeSMax Laier * get a heads-up. 88950bb1704SGleb Smirnoff * 89050bb1704SGleb Smirnoff * XXX: a special case for carp(4) interface 89148321abeSMax Laier */ 89250bb1704SGleb Smirnoff if ((ia->ia_flags & IFA_ROUTE) == 0 89350bb1704SGleb Smirnoff #ifdef DEV_CARP 89450bb1704SGleb Smirnoff && (ia->ia_ifp->if_type != IFT_CARP) 89550bb1704SGleb Smirnoff #endif 89650bb1704SGleb Smirnoff ) { 89748321abeSMax Laier rtinit(&(target->ia_ifa), (int)RTM_DELETE, 89848321abeSMax Laier rtinitflags(target)); 89948321abeSMax Laier target->ia_flags &= ~IFA_ROUTE; 90048321abeSMax Laier 90148321abeSMax Laier error = rtinit(&ia->ia_ifa, (int)RTM_ADD, 90248321abeSMax Laier rtinitflags(ia) | RTF_UP); 90348321abeSMax Laier if (error == 0) 90448321abeSMax Laier ia->ia_flags |= IFA_ROUTE; 90548321abeSMax Laier return error; 90648321abeSMax Laier } 90748321abeSMax Laier } 90848321abeSMax Laier 90948321abeSMax Laier /* 91048321abeSMax Laier * As no-one seem to have this prefix, we can remove the route. 91148321abeSMax Laier */ 91248321abeSMax Laier rtinit(&(target->ia_ifa), (int)RTM_DELETE, rtinitflags(target)); 91348321abeSMax Laier target->ia_flags &= ~IFA_ROUTE; 91448321abeSMax Laier return 0; 91548321abeSMax Laier } 91648321abeSMax Laier 91748321abeSMax Laier #undef rtinitflags 918df8bae1dSRodney W. Grimes 919df8bae1dSRodney W. Grimes /* 920df8bae1dSRodney W. Grimes * Return 1 if the address might be a local broadcast address. 921df8bae1dSRodney W. Grimes */ 92226f9a767SRodney W. Grimes int 923df8bae1dSRodney W. Grimes in_broadcast(in, ifp) 924df8bae1dSRodney W. Grimes struct in_addr in; 925df8bae1dSRodney W. Grimes struct ifnet *ifp; 926df8bae1dSRodney W. Grimes { 927df8bae1dSRodney W. Grimes register struct ifaddr *ifa; 928df8bae1dSRodney W. Grimes u_long t; 929df8bae1dSRodney W. Grimes 930df8bae1dSRodney W. Grimes if (in.s_addr == INADDR_BROADCAST || 931df8bae1dSRodney W. Grimes in.s_addr == INADDR_ANY) 932df8bae1dSRodney W. Grimes return 1; 933df8bae1dSRodney W. Grimes if ((ifp->if_flags & IFF_BROADCAST) == 0) 934df8bae1dSRodney W. Grimes return 0; 935df8bae1dSRodney W. Grimes t = ntohl(in.s_addr); 936df8bae1dSRodney W. Grimes /* 937df8bae1dSRodney W. Grimes * Look through the list of addresses for a match 938df8bae1dSRodney W. Grimes * with a broadcast address. 939df8bae1dSRodney W. Grimes */ 940df8bae1dSRodney W. Grimes #define ia ((struct in_ifaddr *)ifa) 941462b86feSPoul-Henning Kamp TAILQ_FOREACH(ifa, &ifp->if_addrhead, ifa_link) 942df8bae1dSRodney W. Grimes if (ifa->ifa_addr->sa_family == AF_INET && 943df8bae1dSRodney W. Grimes (in.s_addr == ia->ia_broadaddr.sin_addr.s_addr || 944df8bae1dSRodney W. Grimes in.s_addr == ia->ia_netbroadcast.s_addr || 945df8bae1dSRodney W. Grimes /* 946df8bae1dSRodney W. Grimes * Check for old-style (host 0) broadcast. 947df8bae1dSRodney W. Grimes */ 9488dd27fd6SGuido van Rooij t == ia->ia_subnet || t == ia->ia_net) && 9498dd27fd6SGuido van Rooij /* 9508dd27fd6SGuido van Rooij * Check for an all one subnetmask. These 9518dd27fd6SGuido van Rooij * only exist when an interface gets a secondary 9528dd27fd6SGuido van Rooij * address. 9538dd27fd6SGuido van Rooij */ 9548dd27fd6SGuido van Rooij ia->ia_subnetmask != (u_long)0xffffffff) 955df8bae1dSRodney W. Grimes return 1; 956df8bae1dSRodney W. Grimes return (0); 957df8bae1dSRodney W. Grimes #undef ia 958df8bae1dSRodney W. Grimes } 959df8bae1dSRodney W. Grimes /* 960df8bae1dSRodney W. Grimes * Add an address to the list of IP multicast addresses for a given interface. 961df8bae1dSRodney W. Grimes */ 962df8bae1dSRodney W. Grimes struct in_multi * 963df8bae1dSRodney W. Grimes in_addmulti(ap, ifp) 964df8bae1dSRodney W. Grimes register struct in_addr *ap; 965df8bae1dSRodney W. Grimes register struct ifnet *ifp; 966df8bae1dSRodney W. Grimes { 967df8bae1dSRodney W. Grimes register struct in_multi *inm; 968477180fbSGarrett Wollman int error; 969477180fbSGarrett Wollman struct sockaddr_in sin; 970477180fbSGarrett Wollman struct ifmultiaddr *ifma; 971df8bae1dSRodney W. Grimes 972dd5a318bSRobert Watson IN_MULTI_LOCK(); 973df8bae1dSRodney W. Grimes /* 974477180fbSGarrett Wollman * Call generic routine to add membership or increment 975477180fbSGarrett Wollman * refcount. It wants addresses in the form of a sockaddr, 976477180fbSGarrett Wollman * so we build one here (being careful to zero the unused bytes). 977df8bae1dSRodney W. Grimes */ 978477180fbSGarrett Wollman bzero(&sin, sizeof sin); 979477180fbSGarrett Wollman sin.sin_family = AF_INET; 980477180fbSGarrett Wollman sin.sin_len = sizeof sin; 981477180fbSGarrett Wollman sin.sin_addr = *ap; 982477180fbSGarrett Wollman error = if_addmulti(ifp, (struct sockaddr *)&sin, &ifma); 983477180fbSGarrett Wollman if (error) { 984dd5a318bSRobert Watson IN_MULTI_UNLOCK(); 985477180fbSGarrett Wollman return 0; 986df8bae1dSRodney W. Grimes } 987477180fbSGarrett Wollman 988df8bae1dSRodney W. Grimes /* 989477180fbSGarrett Wollman * If ifma->ifma_protospec is null, then if_addmulti() created 990477180fbSGarrett Wollman * a new record. Otherwise, we are done. 991df8bae1dSRodney W. Grimes */ 992d4d22970SGleb Smirnoff if (ifma->ifma_protospec != NULL) { 993dd5a318bSRobert Watson IN_MULTI_UNLOCK(); 994477180fbSGarrett Wollman return ifma->ifma_protospec; 9954153a3a3SBruce Evans } 996477180fbSGarrett Wollman 9977cc0979fSDavid Malone inm = (struct in_multi *)malloc(sizeof(*inm), M_IPMADDR, 9987cc0979fSDavid Malone M_NOWAIT | M_ZERO); 999df8bae1dSRodney W. Grimes if (inm == NULL) { 1000dd5a318bSRobert Watson IN_MULTI_UNLOCK(); 1001df8bae1dSRodney W. Grimes return (NULL); 1002df8bae1dSRodney W. Grimes } 1003477180fbSGarrett Wollman 1004df8bae1dSRodney W. Grimes inm->inm_addr = *ap; 1005df8bae1dSRodney W. Grimes inm->inm_ifp = ifp; 1006477180fbSGarrett Wollman inm->inm_ifma = ifma; 1007477180fbSGarrett Wollman ifma->ifma_protospec = inm; 1008477180fbSGarrett Wollman LIST_INSERT_HEAD(&in_multihead, inm, inm_link); 1009ffa5b11aSGarrett Wollman 1010df8bae1dSRodney W. Grimes /* 1011df8bae1dSRodney W. Grimes * Let IGMP know that we have joined a new IP multicast group. 1012df8bae1dSRodney W. Grimes */ 1013df8bae1dSRodney W. Grimes igmp_joingroup(inm); 1014dd5a318bSRobert Watson IN_MULTI_UNLOCK(); 1015df8bae1dSRodney W. Grimes return (inm); 1016df8bae1dSRodney W. Grimes } 1017df8bae1dSRodney W. Grimes 1018df8bae1dSRodney W. Grimes /* 1019df8bae1dSRodney W. Grimes * Delete a multicast address record. 1020df8bae1dSRodney W. Grimes */ 102126f9a767SRodney W. Grimes void 1022df8bae1dSRodney W. Grimes in_delmulti(inm) 1023df8bae1dSRodney W. Grimes register struct in_multi *inm; 1024df8bae1dSRodney W. Grimes { 1025dd5a318bSRobert Watson struct ifmultiaddr *ifma; 102688a5354eSLuigi Rizzo struct in_multi my_inm; 1027df8bae1dSRodney W. Grimes 1028dd5a318bSRobert Watson IN_MULTI_LOCK(); 1029dd5a318bSRobert Watson ifma = inm->inm_ifma; 103088a5354eSLuigi Rizzo my_inm.inm_ifp = NULL ; /* don't send the leave msg */ 1031477180fbSGarrett Wollman if (ifma->ifma_refcount == 1) { 1032df8bae1dSRodney W. Grimes /* 1033df8bae1dSRodney W. Grimes * No remaining claims to this record; let IGMP know that 1034df8bae1dSRodney W. Grimes * we are leaving the multicast group. 103588a5354eSLuigi Rizzo * But do it after the if_delmulti() which might reset 103688a5354eSLuigi Rizzo * the interface and nuke the packet. 1037df8bae1dSRodney W. Grimes */ 103888a5354eSLuigi Rizzo my_inm = *inm ; 1039d4d22970SGleb Smirnoff ifma->ifma_protospec = NULL; 1040477180fbSGarrett Wollman LIST_REMOVE(inm, inm_link); 1041df8bae1dSRodney W. Grimes free(inm, M_IPMADDR); 1042df8bae1dSRodney W. Grimes } 1043477180fbSGarrett Wollman /* XXX - should be separate API for when we have an ifma? */ 1044477180fbSGarrett Wollman if_delmulti(ifma->ifma_ifp, ifma->ifma_addr); 104588a5354eSLuigi Rizzo if (my_inm.inm_ifp != NULL) 104688a5354eSLuigi Rizzo igmp_leavegroup(&my_inm); 1047dd5a318bSRobert Watson IN_MULTI_UNLOCK(); 1048df8bae1dSRodney W. Grimes } 1049