1df8bae1dSRodney W. Grimes /* 2df8bae1dSRodney W. Grimes * Copyright (c) 1982, 1986, 1991, 1993 3df8bae1dSRodney W. Grimes * The Regents of the University of California. All rights reserved. 4df8bae1dSRodney W. Grimes * 5df8bae1dSRodney W. Grimes * Redistribution and use in source and binary forms, with or without 6df8bae1dSRodney W. Grimes * modification, are permitted provided that the following conditions 7df8bae1dSRodney W. Grimes * are met: 8df8bae1dSRodney W. Grimes * 1. Redistributions of source code must retain the above copyright 9df8bae1dSRodney W. Grimes * notice, this list of conditions and the following disclaimer. 10df8bae1dSRodney W. Grimes * 2. Redistributions in binary form must reproduce the above copyright 11df8bae1dSRodney W. Grimes * notice, this list of conditions and the following disclaimer in the 12df8bae1dSRodney W. Grimes * documentation and/or other materials provided with the distribution. 13df8bae1dSRodney W. Grimes * 3. All advertising materials mentioning features or use of this software 14df8bae1dSRodney W. Grimes * must display the following acknowledgement: 15df8bae1dSRodney W. Grimes * This product includes software developed by the University of 16df8bae1dSRodney W. Grimes * California, Berkeley and its contributors. 17df8bae1dSRodney W. Grimes * 4. Neither the name of the University nor the names of its contributors 18df8bae1dSRodney W. Grimes * may be used to endorse or promote products derived from this software 19df8bae1dSRodney W. Grimes * without specific prior written permission. 20df8bae1dSRodney W. Grimes * 21df8bae1dSRodney W. Grimes * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 22df8bae1dSRodney W. Grimes * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 23df8bae1dSRodney W. Grimes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 24df8bae1dSRodney W. Grimes * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 25df8bae1dSRodney W. Grimes * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 26df8bae1dSRodney W. Grimes * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 27df8bae1dSRodney W. Grimes * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 28df8bae1dSRodney W. Grimes * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 29df8bae1dSRodney W. Grimes * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 30df8bae1dSRodney W. Grimes * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 31df8bae1dSRodney W. Grimes * SUCH DAMAGE. 32df8bae1dSRodney W. Grimes * 332180b925SGarrett Wollman * @(#)in.c 8.4 (Berkeley) 1/9/95 34c3aac50fSPeter Wemm * $FreeBSD$ 35df8bae1dSRodney W. Grimes */ 36df8bae1dSRodney W. Grimes 37df8bae1dSRodney W. Grimes #include <sys/param.h> 3826f9a767SRodney W. Grimes #include <sys/systm.h> 3951a53488SBruce Evans #include <sys/sockio.h> 40df8bae1dSRodney W. Grimes #include <sys/malloc.h> 41df8bae1dSRodney W. Grimes #include <sys/socket.h> 42f6d24a78SPoul-Henning Kamp #include <sys/kernel.h> 43f6d24a78SPoul-Henning Kamp #include <sys/sysctl.h> 44df8bae1dSRodney W. Grimes 45df8bae1dSRodney W. Grimes #include <net/if.h> 466a800098SYoshinobu Inoue #include <net/if_types.h> 47df8bae1dSRodney W. Grimes #include <net/route.h> 48df8bae1dSRodney W. Grimes 49df8bae1dSRodney W. Grimes #include <netinet/in.h> 50df8bae1dSRodney W. Grimes #include <netinet/in_var.h> 51e43cc4aeSHajimu UMEMOTO #include <netinet/in_pcb.h> 52df8bae1dSRodney W. Grimes 53c70f4510SPoul-Henning Kamp #include <netinet/igmp_var.h> 54c70f4510SPoul-Henning Kamp 55a1c995b6SPoul-Henning Kamp static MALLOC_DEFINE(M_IPMADDR, "in_multi", "internet multicast address"); 5655166637SPoul-Henning Kamp 576a800098SYoshinobu Inoue static int in_mask2len __P((struct in_addr *)); 586a800098SYoshinobu Inoue static void in_len2mask __P((struct in_addr *, int)); 596a800098SYoshinobu Inoue static int in_lifaddr_ioctl __P((struct socket *, u_long, caddr_t, 60b40ce416SJulian Elischer struct ifnet *, struct thread *)); 616a800098SYoshinobu Inoue 620312fbe9SPoul-Henning Kamp static void in_socktrim __P((struct sockaddr_in *)); 630312fbe9SPoul-Henning Kamp static int in_ifinit __P((struct ifnet *, 640312fbe9SPoul-Henning Kamp struct in_ifaddr *, struct sockaddr_in *, int)); 65df8bae1dSRodney W. Grimes 66f8731310SGarrett Wollman static int subnetsarelocal = 0; 67f6d24a78SPoul-Henning Kamp SYSCTL_INT(_net_inet_ip, OID_AUTO, subnets_are_local, CTLFLAG_RW, 68f6d24a78SPoul-Henning Kamp &subnetsarelocal, 0, ""); 69477180fbSGarrett Wollman 70477180fbSGarrett Wollman struct in_multihead in_multihead; /* XXX BSS initialization */ 71477180fbSGarrett Wollman 72e43cc4aeSHajimu UMEMOTO extern struct inpcbinfo ripcbinfo; 73e43cc4aeSHajimu UMEMOTO extern struct inpcbinfo udbinfo; 74e43cc4aeSHajimu UMEMOTO 75df8bae1dSRodney W. Grimes /* 76df8bae1dSRodney W. Grimes * Return 1 if an internet address is for a ``local'' host 77df8bae1dSRodney W. Grimes * (one to which we have a connection). If subnetsarelocal 78df8bae1dSRodney W. Grimes * is true, this includes other subnets of the local net. 79df8bae1dSRodney W. Grimes * Otherwise, it includes only the directly-connected (sub)nets. 80df8bae1dSRodney W. Grimes */ 8126f9a767SRodney W. Grimes int 82df8bae1dSRodney W. Grimes in_localaddr(in) 83df8bae1dSRodney W. Grimes struct in_addr in; 84df8bae1dSRodney W. Grimes { 85df8bae1dSRodney W. Grimes register u_long i = ntohl(in.s_addr); 86df8bae1dSRodney W. Grimes register struct in_ifaddr *ia; 87df8bae1dSRodney W. Grimes 88df8bae1dSRodney W. Grimes if (subnetsarelocal) { 89462b86feSPoul-Henning Kamp TAILQ_FOREACH(ia, &in_ifaddrhead, ia_link) 90df8bae1dSRodney W. Grimes if ((i & ia->ia_netmask) == ia->ia_net) 91df8bae1dSRodney W. Grimes return (1); 92df8bae1dSRodney W. Grimes } else { 9337d40066SPoul-Henning Kamp TAILQ_FOREACH(ia, &in_ifaddrhead, ia_link) 94df8bae1dSRodney W. Grimes if ((i & ia->ia_subnetmask) == ia->ia_subnet) 95df8bae1dSRodney W. Grimes return (1); 96df8bae1dSRodney W. Grimes } 97df8bae1dSRodney W. Grimes return (0); 98df8bae1dSRodney W. Grimes } 99df8bae1dSRodney W. Grimes 100df8bae1dSRodney W. Grimes /* 101df8bae1dSRodney W. Grimes * Determine whether an IP address is in a reserved set of addresses 102df8bae1dSRodney W. Grimes * that may not be forwarded, or whether datagrams to that destination 103df8bae1dSRodney W. Grimes * may be forwarded. 104df8bae1dSRodney W. Grimes */ 10526f9a767SRodney W. Grimes int 106df8bae1dSRodney W. Grimes in_canforward(in) 107df8bae1dSRodney W. Grimes struct in_addr in; 108df8bae1dSRodney W. Grimes { 109df8bae1dSRodney W. Grimes register u_long i = ntohl(in.s_addr); 110df8bae1dSRodney W. Grimes register u_long net; 111df8bae1dSRodney W. Grimes 112df8bae1dSRodney W. Grimes if (IN_EXPERIMENTAL(i) || IN_MULTICAST(i)) 113df8bae1dSRodney W. Grimes return (0); 114df8bae1dSRodney W. Grimes if (IN_CLASSA(i)) { 115df8bae1dSRodney W. Grimes net = i & IN_CLASSA_NET; 116df8bae1dSRodney W. Grimes if (net == 0 || net == (IN_LOOPBACKNET << IN_CLASSA_NSHIFT)) 117df8bae1dSRodney W. Grimes return (0); 118df8bae1dSRodney W. Grimes } 119df8bae1dSRodney W. Grimes return (1); 120df8bae1dSRodney W. Grimes } 121df8bae1dSRodney W. Grimes 122df8bae1dSRodney W. Grimes /* 123df8bae1dSRodney W. Grimes * Trim a mask in a sockaddr 124df8bae1dSRodney W. Grimes */ 1250312fbe9SPoul-Henning Kamp static void 126df8bae1dSRodney W. Grimes in_socktrim(ap) 127df8bae1dSRodney W. Grimes struct sockaddr_in *ap; 128df8bae1dSRodney W. Grimes { 129df8bae1dSRodney W. Grimes register char *cplim = (char *) &ap->sin_addr; 130df8bae1dSRodney W. Grimes register char *cp = (char *) (&ap->sin_addr + 1); 131df8bae1dSRodney W. Grimes 132df8bae1dSRodney W. Grimes ap->sin_len = 0; 133df00058dSGarrett Wollman while (--cp >= cplim) 134df8bae1dSRodney W. Grimes if (*cp) { 135df8bae1dSRodney W. Grimes (ap)->sin_len = cp - (char *) (ap) + 1; 136df8bae1dSRodney W. Grimes break; 137df8bae1dSRodney W. Grimes } 138df8bae1dSRodney W. Grimes } 139df8bae1dSRodney W. Grimes 1406a800098SYoshinobu Inoue static int 1416a800098SYoshinobu Inoue in_mask2len(mask) 1426a800098SYoshinobu Inoue struct in_addr *mask; 1436a800098SYoshinobu Inoue { 1446a800098SYoshinobu Inoue int x, y; 1456a800098SYoshinobu Inoue u_char *p; 1466a800098SYoshinobu Inoue 1476a800098SYoshinobu Inoue p = (u_char *)mask; 1486a800098SYoshinobu Inoue for (x = 0; x < sizeof(*mask); x++) { 1496a800098SYoshinobu Inoue if (p[x] != 0xff) 1506a800098SYoshinobu Inoue break; 1516a800098SYoshinobu Inoue } 1526a800098SYoshinobu Inoue y = 0; 1536a800098SYoshinobu Inoue if (x < sizeof(*mask)) { 1546a800098SYoshinobu Inoue for (y = 0; y < 8; y++) { 1556a800098SYoshinobu Inoue if ((p[x] & (0x80 >> y)) == 0) 1566a800098SYoshinobu Inoue break; 1576a800098SYoshinobu Inoue } 1586a800098SYoshinobu Inoue } 1596a800098SYoshinobu Inoue return x * 8 + y; 1606a800098SYoshinobu Inoue } 1616a800098SYoshinobu Inoue 1626a800098SYoshinobu Inoue static void 1636a800098SYoshinobu Inoue in_len2mask(mask, len) 1646a800098SYoshinobu Inoue struct in_addr *mask; 1656a800098SYoshinobu Inoue int len; 1666a800098SYoshinobu Inoue { 1676a800098SYoshinobu Inoue int i; 1686a800098SYoshinobu Inoue u_char *p; 1696a800098SYoshinobu Inoue 1706a800098SYoshinobu Inoue p = (u_char *)mask; 1716a800098SYoshinobu Inoue bzero(mask, sizeof(*mask)); 1726a800098SYoshinobu Inoue for (i = 0; i < len / 8; i++) 1736a800098SYoshinobu Inoue p[i] = 0xff; 1746a800098SYoshinobu Inoue if (len % 8) 1756a800098SYoshinobu Inoue p[i] = (0xff00 >> (len % 8)) & 0xff; 1766a800098SYoshinobu Inoue } 1776a800098SYoshinobu Inoue 178f6d24a78SPoul-Henning Kamp static int in_interfaces; /* number of external internet interfaces */ 179df8bae1dSRodney W. Grimes 180df8bae1dSRodney W. Grimes /* 181df8bae1dSRodney W. Grimes * Generic internet control operations (ioctl's). 182df8bae1dSRodney W. Grimes * Ifp is 0 if not an interface-specific ioctl. 183df8bae1dSRodney W. Grimes */ 184df8bae1dSRodney W. Grimes /* ARGSUSED */ 18526f9a767SRodney W. Grimes int 186b40ce416SJulian Elischer in_control(so, cmd, data, ifp, td) 187df8bae1dSRodney W. Grimes struct socket *so; 188ecbb00a2SDoug Rabson u_long cmd; 189df8bae1dSRodney W. Grimes caddr_t data; 190df8bae1dSRodney W. Grimes register struct ifnet *ifp; 191b40ce416SJulian Elischer struct thread *td; 192df8bae1dSRodney W. Grimes { 193df8bae1dSRodney W. Grimes register struct ifreq *ifr = (struct ifreq *)data; 194ac0aa473SBill Fenner register struct in_ifaddr *ia = 0, *iap; 195df8bae1dSRodney W. Grimes register struct ifaddr *ifa; 196ca925d9cSJonathan Lemon struct in_addr dst; 197df8bae1dSRodney W. Grimes struct in_ifaddr *oia; 198df8bae1dSRodney W. Grimes struct in_aliasreq *ifra = (struct in_aliasreq *)data; 199df8bae1dSRodney W. Grimes struct sockaddr_in oldaddr; 2000f02fdacSBrian Somers int error, hostIsNew, iaIsNew, maskIsNew, s; 2010f02fdacSBrian Somers 2020f02fdacSBrian Somers iaIsNew = 0; 203df8bae1dSRodney W. Grimes 2046a800098SYoshinobu Inoue switch (cmd) { 2056a800098SYoshinobu Inoue case SIOCALIFADDR: 2066a800098SYoshinobu Inoue case SIOCDLIFADDR: 207b40ce416SJulian Elischer if (td && (error = suser_td(td)) != 0) 2086a800098SYoshinobu Inoue return error; 2096a800098SYoshinobu Inoue /*fall through*/ 2106a800098SYoshinobu Inoue case SIOCGLIFADDR: 2116a800098SYoshinobu Inoue if (!ifp) 2126a800098SYoshinobu Inoue return EINVAL; 213b40ce416SJulian Elischer return in_lifaddr_ioctl(so, cmd, data, ifp, td); 2146a800098SYoshinobu Inoue } 2156a800098SYoshinobu Inoue 216df8bae1dSRodney W. Grimes /* 217df8bae1dSRodney W. Grimes * Find address for this interface, if it exists. 218ac0aa473SBill Fenner * 219ac0aa473SBill Fenner * If an alias address was specified, find that one instead of 220ca925d9cSJonathan Lemon * the first one on the interface, if possible. 221df8bae1dSRodney W. Grimes */ 222ca925d9cSJonathan Lemon if (ifp) { 223ca925d9cSJonathan Lemon dst = ((struct sockaddr_in *)&ifr->ifr_addr)->sin_addr; 224ca925d9cSJonathan Lemon LIST_FOREACH(iap, INADDR_HASH(dst.s_addr), ia_hash) 225ca925d9cSJonathan Lemon if (iap->ia_ifp == ifp && 226ca925d9cSJonathan Lemon iap->ia_addr.sin_addr.s_addr == dst.s_addr) { 227ac0aa473SBill Fenner ia = iap; 228df8bae1dSRodney W. Grimes break; 229ca925d9cSJonathan Lemon } 230ca925d9cSJonathan Lemon if (ia == NULL) 231ca925d9cSJonathan Lemon TAILQ_FOREACH(ifa, &ifp->if_addrhead, ifa_link) { 232ca925d9cSJonathan Lemon iap = ifatoia(ifa); 233ca925d9cSJonathan Lemon if (iap->ia_addr.sin_family == AF_INET) { 234ac0aa473SBill Fenner ia = iap; 235ac0aa473SBill Fenner break; 236ac0aa473SBill Fenner } 237ac0aa473SBill Fenner } 238ca925d9cSJonathan Lemon } 239df8bae1dSRodney W. Grimes 240df8bae1dSRodney W. Grimes switch (cmd) { 241df8bae1dSRodney W. Grimes 242df8bae1dSRodney W. Grimes case SIOCAIFADDR: 243df8bae1dSRodney W. Grimes case SIOCDIFADDR: 2446572231dSEivind Eklund if (ifp == 0) 2456572231dSEivind Eklund return (EADDRNOTAVAIL); 2461067217dSGarrett Wollman if (ifra->ifra_addr.sin_family == AF_INET) { 247fc2ffbe6SPoul-Henning Kamp for (oia = ia; ia; ia = TAILQ_NEXT(ia, ia_link)) { 248df8bae1dSRodney W. Grimes if (ia->ia_ifp == ifp && 249df8bae1dSRodney W. Grimes ia->ia_addr.sin_addr.s_addr == 250df8bae1dSRodney W. Grimes ifra->ifra_addr.sin_addr.s_addr) 251df8bae1dSRodney W. Grimes break; 252df8bae1dSRodney W. Grimes } 2531067217dSGarrett Wollman if ((ifp->if_flags & IFF_POINTOPOINT) 2541067217dSGarrett Wollman && (cmd == SIOCAIFADDR) 2551067217dSGarrett Wollman && (ifra->ifra_dstaddr.sin_addr.s_addr 2561067217dSGarrett Wollman == INADDR_ANY)) { 257357b78a9SGarrett Wollman return EDESTADDRREQ; 2581067217dSGarrett Wollman } 2591067217dSGarrett Wollman } 260df8bae1dSRodney W. Grimes if (cmd == SIOCDIFADDR && ia == 0) 261df8bae1dSRodney W. Grimes return (EADDRNOTAVAIL); 262df8bae1dSRodney W. Grimes /* FALLTHROUGH */ 263df8bae1dSRodney W. Grimes case SIOCSIFADDR: 264df8bae1dSRodney W. Grimes case SIOCSIFNETMASK: 265df8bae1dSRodney W. Grimes case SIOCSIFDSTADDR: 266b40ce416SJulian Elischer if (td && (error = suser_td(td)) != 0) 267a29f300eSGarrett Wollman return error; 268df8bae1dSRodney W. Grimes 269df8bae1dSRodney W. Grimes if (ifp == 0) 2706572231dSEivind Eklund return (EADDRNOTAVAIL); 271df8bae1dSRodney W. Grimes if (ia == (struct in_ifaddr *)0) { 27259562606SGarrett Wollman ia = (struct in_ifaddr *) 2737cc0979fSDavid Malone malloc(sizeof *ia, M_IFADDR, M_WAITOK | M_ZERO); 27459562606SGarrett Wollman if (ia == (struct in_ifaddr *)NULL) 275df8bae1dSRodney W. Grimes return (ENOBUFS); 276c655b7c4SDavid Greenman /* 277c655b7c4SDavid Greenman * Protect from ipintr() traversing address list 278c655b7c4SDavid Greenman * while we're modifying it. 279c655b7c4SDavid Greenman */ 280c655b7c4SDavid Greenman s = splnet(); 281c655b7c4SDavid Greenman 28259562606SGarrett Wollman TAILQ_INSERT_TAIL(&in_ifaddrhead, ia, ia_link); 28359562606SGarrett Wollman ifa = &ia->ia_ifa; 28459562606SGarrett Wollman TAILQ_INSERT_TAIL(&ifp->if_addrhead, ifa, ifa_link); 28559562606SGarrett Wollman 28659562606SGarrett Wollman ifa->ifa_addr = (struct sockaddr *)&ia->ia_addr; 28759562606SGarrett Wollman ifa->ifa_dstaddr = (struct sockaddr *)&ia->ia_dstaddr; 28859562606SGarrett Wollman ifa->ifa_netmask = (struct sockaddr *)&ia->ia_sockmask; 289df8bae1dSRodney W. Grimes ia->ia_sockmask.sin_len = 8; 290bc183b3fSDag-Erling Smørgrav ia->ia_sockmask.sin_family = AF_INET; 291df8bae1dSRodney W. Grimes if (ifp->if_flags & IFF_BROADCAST) { 292df8bae1dSRodney W. Grimes ia->ia_broadaddr.sin_len = sizeof(ia->ia_addr); 293df8bae1dSRodney W. Grimes ia->ia_broadaddr.sin_family = AF_INET; 294df8bae1dSRodney W. Grimes } 295df8bae1dSRodney W. Grimes ia->ia_ifp = ifp; 296f5fea3ddSPaul Traina if (!(ifp->if_flags & IFF_LOOPBACK)) 297df8bae1dSRodney W. Grimes in_interfaces++; 298c655b7c4SDavid Greenman splx(s); 2990f02fdacSBrian Somers iaIsNew = 1; 300df8bae1dSRodney W. Grimes } 301df8bae1dSRodney W. Grimes break; 302df8bae1dSRodney W. Grimes 303df8bae1dSRodney W. Grimes case SIOCSIFBRDADDR: 304b40ce416SJulian Elischer if (td && (error = suser_td(td)) != 0) 305a29f300eSGarrett Wollman return error; 306df8bae1dSRodney W. Grimes /* FALLTHROUGH */ 307df8bae1dSRodney W. Grimes 308df8bae1dSRodney W. Grimes case SIOCGIFADDR: 309df8bae1dSRodney W. Grimes case SIOCGIFNETMASK: 310df8bae1dSRodney W. Grimes case SIOCGIFDSTADDR: 311df8bae1dSRodney W. Grimes case SIOCGIFBRDADDR: 312df8bae1dSRodney W. Grimes if (ia == (struct in_ifaddr *)0) 313df8bae1dSRodney W. Grimes return (EADDRNOTAVAIL); 314df8bae1dSRodney W. Grimes break; 315df8bae1dSRodney W. Grimes } 316df8bae1dSRodney W. Grimes switch (cmd) { 317df8bae1dSRodney W. Grimes 318df8bae1dSRodney W. Grimes case SIOCGIFADDR: 319df8bae1dSRodney W. Grimes *((struct sockaddr_in *)&ifr->ifr_addr) = ia->ia_addr; 3200f02fdacSBrian Somers return (0); 321df8bae1dSRodney W. Grimes 322df8bae1dSRodney W. Grimes case SIOCGIFBRDADDR: 323df8bae1dSRodney W. Grimes if ((ifp->if_flags & IFF_BROADCAST) == 0) 324df8bae1dSRodney W. Grimes return (EINVAL); 325df8bae1dSRodney W. Grimes *((struct sockaddr_in *)&ifr->ifr_dstaddr) = ia->ia_broadaddr; 3260f02fdacSBrian Somers return (0); 327df8bae1dSRodney W. Grimes 328df8bae1dSRodney W. Grimes case SIOCGIFDSTADDR: 329df8bae1dSRodney W. Grimes if ((ifp->if_flags & IFF_POINTOPOINT) == 0) 330df8bae1dSRodney W. Grimes return (EINVAL); 331df8bae1dSRodney W. Grimes *((struct sockaddr_in *)&ifr->ifr_dstaddr) = ia->ia_dstaddr; 3320f02fdacSBrian Somers return (0); 333df8bae1dSRodney W. Grimes 334df8bae1dSRodney W. Grimes case SIOCGIFNETMASK: 335df8bae1dSRodney W. Grimes *((struct sockaddr_in *)&ifr->ifr_addr) = ia->ia_sockmask; 3360f02fdacSBrian Somers return (0); 337df8bae1dSRodney W. Grimes 338df8bae1dSRodney W. Grimes case SIOCSIFDSTADDR: 339df8bae1dSRodney W. Grimes if ((ifp->if_flags & IFF_POINTOPOINT) == 0) 340df8bae1dSRodney W. Grimes return (EINVAL); 341df8bae1dSRodney W. Grimes oldaddr = ia->ia_dstaddr; 342df8bae1dSRodney W. Grimes ia->ia_dstaddr = *(struct sockaddr_in *)&ifr->ifr_dstaddr; 343df8bae1dSRodney W. Grimes if (ifp->if_ioctl && (error = (*ifp->if_ioctl) 344df8bae1dSRodney W. Grimes (ifp, SIOCSIFDSTADDR, (caddr_t)ia))) { 345df8bae1dSRodney W. Grimes ia->ia_dstaddr = oldaddr; 346df8bae1dSRodney W. Grimes return (error); 347df8bae1dSRodney W. Grimes } 348df8bae1dSRodney W. Grimes if (ia->ia_flags & IFA_ROUTE) { 349df8bae1dSRodney W. Grimes ia->ia_ifa.ifa_dstaddr = (struct sockaddr *)&oldaddr; 350df8bae1dSRodney W. Grimes rtinit(&(ia->ia_ifa), (int)RTM_DELETE, RTF_HOST); 351df8bae1dSRodney W. Grimes ia->ia_ifa.ifa_dstaddr = 352df8bae1dSRodney W. Grimes (struct sockaddr *)&ia->ia_dstaddr; 353df8bae1dSRodney W. Grimes rtinit(&(ia->ia_ifa), (int)RTM_ADD, RTF_HOST|RTF_UP); 354df8bae1dSRodney W. Grimes } 3550f02fdacSBrian Somers return (0); 356df8bae1dSRodney W. Grimes 357df8bae1dSRodney W. Grimes case SIOCSIFBRDADDR: 358df8bae1dSRodney W. Grimes if ((ifp->if_flags & IFF_BROADCAST) == 0) 359df8bae1dSRodney W. Grimes return (EINVAL); 360df8bae1dSRodney W. Grimes ia->ia_broadaddr = *(struct sockaddr_in *)&ifr->ifr_broadaddr; 3610f02fdacSBrian Somers return (0); 362df8bae1dSRodney W. Grimes 363df8bae1dSRodney W. Grimes case SIOCSIFADDR: 3640f02fdacSBrian Somers error = in_ifinit(ifp, ia, 3650f02fdacSBrian Somers (struct sockaddr_in *) &ifr->ifr_addr, 1); 3660f02fdacSBrian Somers if (error != 0 && iaIsNew) 3670f02fdacSBrian Somers break; 3680f02fdacSBrian Somers return (0); 369df8bae1dSRodney W. Grimes 370df8bae1dSRodney W. Grimes case SIOCSIFNETMASK: 371bc183b3fSDag-Erling Smørgrav ia->ia_sockmask.sin_addr = ifra->ifra_addr.sin_addr; 372bc183b3fSDag-Erling Smørgrav ia->ia_subnetmask = ntohl(ia->ia_sockmask.sin_addr.s_addr); 3730f02fdacSBrian Somers return (0); 374df8bae1dSRodney W. Grimes 375df8bae1dSRodney W. Grimes case SIOCAIFADDR: 376df8bae1dSRodney W. Grimes maskIsNew = 0; 377df8bae1dSRodney W. Grimes hostIsNew = 1; 378df8bae1dSRodney W. Grimes error = 0; 379df8bae1dSRodney W. Grimes if (ia->ia_addr.sin_family == AF_INET) { 380df8bae1dSRodney W. Grimes if (ifra->ifra_addr.sin_len == 0) { 381df8bae1dSRodney W. Grimes ifra->ifra_addr = ia->ia_addr; 382df8bae1dSRodney W. Grimes hostIsNew = 0; 383df8bae1dSRodney W. Grimes } else if (ifra->ifra_addr.sin_addr.s_addr == 384df8bae1dSRodney W. Grimes ia->ia_addr.sin_addr.s_addr) 385df8bae1dSRodney W. Grimes hostIsNew = 0; 386df8bae1dSRodney W. Grimes } 387df8bae1dSRodney W. Grimes if (ifra->ifra_mask.sin_len) { 388df8bae1dSRodney W. Grimes in_ifscrub(ifp, ia); 389df8bae1dSRodney W. Grimes ia->ia_sockmask = ifra->ifra_mask; 390bc183b3fSDag-Erling Smørgrav ia->ia_sockmask.sin_family = AF_INET; 391df8bae1dSRodney W. Grimes ia->ia_subnetmask = 392df8bae1dSRodney W. Grimes ntohl(ia->ia_sockmask.sin_addr.s_addr); 393df8bae1dSRodney W. Grimes maskIsNew = 1; 394df8bae1dSRodney W. Grimes } 395df8bae1dSRodney W. Grimes if ((ifp->if_flags & IFF_POINTOPOINT) && 396df8bae1dSRodney W. Grimes (ifra->ifra_dstaddr.sin_family == AF_INET)) { 397df8bae1dSRodney W. Grimes in_ifscrub(ifp, ia); 398df8bae1dSRodney W. Grimes ia->ia_dstaddr = ifra->ifra_dstaddr; 399df8bae1dSRodney W. Grimes maskIsNew = 1; /* We lie; but the effect's the same */ 400df8bae1dSRodney W. Grimes } 401df8bae1dSRodney W. Grimes if (ifra->ifra_addr.sin_family == AF_INET && 402df8bae1dSRodney W. Grimes (hostIsNew || maskIsNew)) 403df8bae1dSRodney W. Grimes error = in_ifinit(ifp, ia, &ifra->ifra_addr, 0); 4040f02fdacSBrian Somers if (error != 0 && iaIsNew) 4050f02fdacSBrian Somers break; 4060f02fdacSBrian Somers 407df8bae1dSRodney W. Grimes if ((ifp->if_flags & IFF_BROADCAST) && 408df8bae1dSRodney W. Grimes (ifra->ifra_broadaddr.sin_family == AF_INET)) 409df8bae1dSRodney W. Grimes ia->ia_broadaddr = ifra->ifra_broadaddr; 410df8bae1dSRodney W. Grimes return (error); 411df8bae1dSRodney W. Grimes 412df8bae1dSRodney W. Grimes case SIOCDIFADDR: 413089cdfadSRuslan Ermilov /* 414089cdfadSRuslan Ermilov * in_ifscrub kills the interface route. 415089cdfadSRuslan Ermilov */ 416df8bae1dSRodney W. Grimes in_ifscrub(ifp, ia); 417c655b7c4SDavid Greenman /* 418089cdfadSRuslan Ermilov * in_ifadown gets rid of all the rest of 419089cdfadSRuslan Ermilov * the routes. This is not quite the right 420089cdfadSRuslan Ermilov * thing to do, but at least if we are running 421089cdfadSRuslan Ermilov * a routing process they will come back. 422089cdfadSRuslan Ermilov */ 42391854268SRuslan Ermilov in_ifadown(&ia->ia_ifa, 1); 424e43cc4aeSHajimu UMEMOTO /* 425e43cc4aeSHajimu UMEMOTO * XXX horrible hack to detect that we are being called 426e43cc4aeSHajimu UMEMOTO * from if_detach() 427e43cc4aeSHajimu UMEMOTO */ 428f9132cebSJonathan Lemon if (ifaddr_byindex(ifp->if_index) != NULL) { 429e43cc4aeSHajimu UMEMOTO in_pcbpurgeif0(LIST_FIRST(ripcbinfo.listhead), ifp); 430e43cc4aeSHajimu UMEMOTO in_pcbpurgeif0(LIST_FIRST(udbinfo.listhead), ifp); 431e43cc4aeSHajimu UMEMOTO } 4320f02fdacSBrian Somers error = 0; 433df8bae1dSRodney W. Grimes break; 434df8bae1dSRodney W. Grimes 435df8bae1dSRodney W. Grimes default: 436df8bae1dSRodney W. Grimes if (ifp == 0 || ifp->if_ioctl == 0) 437df8bae1dSRodney W. Grimes return (EOPNOTSUPP); 438df8bae1dSRodney W. Grimes return ((*ifp->if_ioctl)(ifp, cmd, data)); 439df8bae1dSRodney W. Grimes } 4400f02fdacSBrian Somers 4410f02fdacSBrian Somers /* 4420f02fdacSBrian Somers * Protect from ipintr() traversing address list while we're modifying 4430f02fdacSBrian Somers * it. 4440f02fdacSBrian Somers */ 4450f02fdacSBrian Somers s = splnet(); 4460f02fdacSBrian Somers TAILQ_REMOVE(&ifp->if_addrhead, &ia->ia_ifa, ifa_link); 4470f02fdacSBrian Somers TAILQ_REMOVE(&in_ifaddrhead, ia, ia_link); 4480f02fdacSBrian Somers LIST_REMOVE(ia, ia_hash); 4490f02fdacSBrian Somers IFAFREE(&ia->ia_ifa); 4500f02fdacSBrian Somers splx(s); 4510f02fdacSBrian Somers 4520f02fdacSBrian Somers return (error); 453df8bae1dSRodney W. Grimes } 454df8bae1dSRodney W. Grimes 455df8bae1dSRodney W. Grimes /* 4566a800098SYoshinobu Inoue * SIOC[GAD]LIFADDR. 4576a800098SYoshinobu Inoue * SIOCGLIFADDR: get first address. (?!?) 4586a800098SYoshinobu Inoue * SIOCGLIFADDR with IFLR_PREFIX: 4596a800098SYoshinobu Inoue * get first address that matches the specified prefix. 4606a800098SYoshinobu Inoue * SIOCALIFADDR: add the specified address. 4616a800098SYoshinobu Inoue * SIOCALIFADDR with IFLR_PREFIX: 4626a800098SYoshinobu Inoue * EINVAL since we can't deduce hostid part of the address. 4636a800098SYoshinobu Inoue * SIOCDLIFADDR: delete the specified address. 4646a800098SYoshinobu Inoue * SIOCDLIFADDR with IFLR_PREFIX: 4656a800098SYoshinobu Inoue * delete the first address that matches the specified prefix. 4666a800098SYoshinobu Inoue * return values: 4676a800098SYoshinobu Inoue * EINVAL on invalid parameters 4686a800098SYoshinobu Inoue * EADDRNOTAVAIL on prefix match failed/specified address not found 4696a800098SYoshinobu Inoue * other values may be returned from in_ioctl() 4706a800098SYoshinobu Inoue */ 4716a800098SYoshinobu Inoue static int 472b40ce416SJulian Elischer in_lifaddr_ioctl(so, cmd, data, ifp, td) 4736a800098SYoshinobu Inoue struct socket *so; 4746a800098SYoshinobu Inoue u_long cmd; 4756a800098SYoshinobu Inoue caddr_t data; 4766a800098SYoshinobu Inoue struct ifnet *ifp; 477b40ce416SJulian Elischer struct thread *td; 4786a800098SYoshinobu Inoue { 4796a800098SYoshinobu Inoue struct if_laddrreq *iflr = (struct if_laddrreq *)data; 4806a800098SYoshinobu Inoue struct ifaddr *ifa; 4816a800098SYoshinobu Inoue 4826a800098SYoshinobu Inoue /* sanity checks */ 4836a800098SYoshinobu Inoue if (!data || !ifp) { 4846a800098SYoshinobu Inoue panic("invalid argument to in_lifaddr_ioctl"); 4856a800098SYoshinobu Inoue /*NOTRECHED*/ 4866a800098SYoshinobu Inoue } 4876a800098SYoshinobu Inoue 4886a800098SYoshinobu Inoue switch (cmd) { 4896a800098SYoshinobu Inoue case SIOCGLIFADDR: 4906a800098SYoshinobu Inoue /* address must be specified on GET with IFLR_PREFIX */ 4916a800098SYoshinobu Inoue if ((iflr->flags & IFLR_PREFIX) == 0) 4926a800098SYoshinobu Inoue break; 4936a800098SYoshinobu Inoue /*FALLTHROUGH*/ 4946a800098SYoshinobu Inoue case SIOCALIFADDR: 4956a800098SYoshinobu Inoue case SIOCDLIFADDR: 4966a800098SYoshinobu Inoue /* address must be specified on ADD and DELETE */ 4975d60ed0eSYoshinobu Inoue if (iflr->addr.ss_family != AF_INET) 4986a800098SYoshinobu Inoue return EINVAL; 4995d60ed0eSYoshinobu Inoue if (iflr->addr.ss_len != sizeof(struct sockaddr_in)) 5006a800098SYoshinobu Inoue return EINVAL; 5016a800098SYoshinobu Inoue /* XXX need improvement */ 5025d60ed0eSYoshinobu Inoue if (iflr->dstaddr.ss_family 5035d60ed0eSYoshinobu Inoue && iflr->dstaddr.ss_family != AF_INET) 5046a800098SYoshinobu Inoue return EINVAL; 5055d60ed0eSYoshinobu Inoue if (iflr->dstaddr.ss_family 5065d60ed0eSYoshinobu Inoue && iflr->dstaddr.ss_len != sizeof(struct sockaddr_in)) 5076a800098SYoshinobu Inoue return EINVAL; 5086a800098SYoshinobu Inoue break; 5096a800098SYoshinobu Inoue default: /*shouldn't happen*/ 5106a800098SYoshinobu Inoue return EOPNOTSUPP; 5116a800098SYoshinobu Inoue } 5126a800098SYoshinobu Inoue if (sizeof(struct in_addr) * 8 < iflr->prefixlen) 5136a800098SYoshinobu Inoue return EINVAL; 5146a800098SYoshinobu Inoue 5156a800098SYoshinobu Inoue switch (cmd) { 5166a800098SYoshinobu Inoue case SIOCALIFADDR: 5176a800098SYoshinobu Inoue { 5186a800098SYoshinobu Inoue struct in_aliasreq ifra; 5196a800098SYoshinobu Inoue 5206a800098SYoshinobu Inoue if (iflr->flags & IFLR_PREFIX) 5216a800098SYoshinobu Inoue return EINVAL; 5226a800098SYoshinobu Inoue 5236a800098SYoshinobu Inoue /* copy args to in_aliasreq, perform ioctl(SIOCAIFADDR_IN6). */ 5246a800098SYoshinobu Inoue bzero(&ifra, sizeof(ifra)); 5256a800098SYoshinobu Inoue bcopy(iflr->iflr_name, ifra.ifra_name, 5266a800098SYoshinobu Inoue sizeof(ifra.ifra_name)); 5276a800098SYoshinobu Inoue 5285d60ed0eSYoshinobu Inoue bcopy(&iflr->addr, &ifra.ifra_addr, iflr->addr.ss_len); 5296a800098SYoshinobu Inoue 5305d60ed0eSYoshinobu Inoue if (iflr->dstaddr.ss_family) { /*XXX*/ 5316a800098SYoshinobu Inoue bcopy(&iflr->dstaddr, &ifra.ifra_dstaddr, 5325d60ed0eSYoshinobu Inoue iflr->dstaddr.ss_len); 5336a800098SYoshinobu Inoue } 5346a800098SYoshinobu Inoue 5356a800098SYoshinobu Inoue ifra.ifra_mask.sin_family = AF_INET; 5366a800098SYoshinobu Inoue ifra.ifra_mask.sin_len = sizeof(struct sockaddr_in); 5376a800098SYoshinobu Inoue in_len2mask(&ifra.ifra_mask.sin_addr, iflr->prefixlen); 5386a800098SYoshinobu Inoue 539b40ce416SJulian Elischer return in_control(so, SIOCAIFADDR, (caddr_t)&ifra, ifp, td); 5406a800098SYoshinobu Inoue } 5416a800098SYoshinobu Inoue case SIOCGLIFADDR: 5426a800098SYoshinobu Inoue case SIOCDLIFADDR: 5436a800098SYoshinobu Inoue { 5446a800098SYoshinobu Inoue struct in_ifaddr *ia; 5456a800098SYoshinobu Inoue struct in_addr mask, candidate, match; 5466a800098SYoshinobu Inoue struct sockaddr_in *sin; 5476a800098SYoshinobu Inoue int cmp; 5486a800098SYoshinobu Inoue 5496a800098SYoshinobu Inoue bzero(&mask, sizeof(mask)); 5506a800098SYoshinobu Inoue if (iflr->flags & IFLR_PREFIX) { 5516a800098SYoshinobu Inoue /* lookup a prefix rather than address. */ 5526a800098SYoshinobu Inoue in_len2mask(&mask, iflr->prefixlen); 5536a800098SYoshinobu Inoue 5546a800098SYoshinobu Inoue sin = (struct sockaddr_in *)&iflr->addr; 5556a800098SYoshinobu Inoue match.s_addr = sin->sin_addr.s_addr; 5566a800098SYoshinobu Inoue match.s_addr &= mask.s_addr; 5576a800098SYoshinobu Inoue 5586a800098SYoshinobu Inoue /* if you set extra bits, that's wrong */ 5596a800098SYoshinobu Inoue if (match.s_addr != sin->sin_addr.s_addr) 5606a800098SYoshinobu Inoue return EINVAL; 5616a800098SYoshinobu Inoue 5626a800098SYoshinobu Inoue cmp = 1; 5636a800098SYoshinobu Inoue } else { 5646a800098SYoshinobu Inoue if (cmd == SIOCGLIFADDR) { 5656a800098SYoshinobu Inoue /* on getting an address, take the 1st match */ 5666a800098SYoshinobu Inoue cmp = 0; /*XXX*/ 5676a800098SYoshinobu Inoue } else { 5686a800098SYoshinobu Inoue /* on deleting an address, do exact match */ 5696a800098SYoshinobu Inoue in_len2mask(&mask, 32); 5706a800098SYoshinobu Inoue sin = (struct sockaddr_in *)&iflr->addr; 5716a800098SYoshinobu Inoue match.s_addr = sin->sin_addr.s_addr; 5726a800098SYoshinobu Inoue 5736a800098SYoshinobu Inoue cmp = 1; 5746a800098SYoshinobu Inoue } 5756a800098SYoshinobu Inoue } 5766a800098SYoshinobu Inoue 5776a800098SYoshinobu Inoue TAILQ_FOREACH(ifa, &ifp->if_addrhead, ifa_link) { 5786a800098SYoshinobu Inoue if (ifa->ifa_addr->sa_family != AF_INET6) 5796a800098SYoshinobu Inoue continue; 5806a800098SYoshinobu Inoue if (!cmp) 5816a800098SYoshinobu Inoue break; 5826a800098SYoshinobu Inoue candidate.s_addr = ((struct sockaddr_in *)&ifa->ifa_addr)->sin_addr.s_addr; 5836a800098SYoshinobu Inoue candidate.s_addr &= mask.s_addr; 5846a800098SYoshinobu Inoue if (candidate.s_addr == match.s_addr) 5856a800098SYoshinobu Inoue break; 5866a800098SYoshinobu Inoue } 5876a800098SYoshinobu Inoue if (!ifa) 5886a800098SYoshinobu Inoue return EADDRNOTAVAIL; 5896a800098SYoshinobu Inoue ia = (struct in_ifaddr *)ifa; 5906a800098SYoshinobu Inoue 5916a800098SYoshinobu Inoue if (cmd == SIOCGLIFADDR) { 5926a800098SYoshinobu Inoue /* fill in the if_laddrreq structure */ 5936a800098SYoshinobu Inoue bcopy(&ia->ia_addr, &iflr->addr, ia->ia_addr.sin_len); 5946a800098SYoshinobu Inoue 5956a800098SYoshinobu Inoue if ((ifp->if_flags & IFF_POINTOPOINT) != 0) { 5966a800098SYoshinobu Inoue bcopy(&ia->ia_dstaddr, &iflr->dstaddr, 5976a800098SYoshinobu Inoue ia->ia_dstaddr.sin_len); 5986a800098SYoshinobu Inoue } else 5996a800098SYoshinobu Inoue bzero(&iflr->dstaddr, sizeof(iflr->dstaddr)); 6006a800098SYoshinobu Inoue 6016a800098SYoshinobu Inoue iflr->prefixlen = 6026a800098SYoshinobu Inoue in_mask2len(&ia->ia_sockmask.sin_addr); 6036a800098SYoshinobu Inoue 6046a800098SYoshinobu Inoue iflr->flags = 0; /*XXX*/ 6056a800098SYoshinobu Inoue 6066a800098SYoshinobu Inoue return 0; 6076a800098SYoshinobu Inoue } else { 6086a800098SYoshinobu Inoue struct in_aliasreq ifra; 6096a800098SYoshinobu Inoue 6106a800098SYoshinobu Inoue /* fill in_aliasreq and do ioctl(SIOCDIFADDR_IN6) */ 6116a800098SYoshinobu Inoue bzero(&ifra, sizeof(ifra)); 6126a800098SYoshinobu Inoue bcopy(iflr->iflr_name, ifra.ifra_name, 6136a800098SYoshinobu Inoue sizeof(ifra.ifra_name)); 6146a800098SYoshinobu Inoue 6156a800098SYoshinobu Inoue bcopy(&ia->ia_addr, &ifra.ifra_addr, 6166a800098SYoshinobu Inoue ia->ia_addr.sin_len); 6176a800098SYoshinobu Inoue if ((ifp->if_flags & IFF_POINTOPOINT) != 0) { 6186a800098SYoshinobu Inoue bcopy(&ia->ia_dstaddr, &ifra.ifra_dstaddr, 6196a800098SYoshinobu Inoue ia->ia_dstaddr.sin_len); 6206a800098SYoshinobu Inoue } 6216a800098SYoshinobu Inoue bcopy(&ia->ia_sockmask, &ifra.ifra_dstaddr, 6226a800098SYoshinobu Inoue ia->ia_sockmask.sin_len); 6236a800098SYoshinobu Inoue 6246a800098SYoshinobu Inoue return in_control(so, SIOCDIFADDR, (caddr_t)&ifra, 625b40ce416SJulian Elischer ifp, td); 6266a800098SYoshinobu Inoue } 6276a800098SYoshinobu Inoue } 6286a800098SYoshinobu Inoue } 6296a800098SYoshinobu Inoue 6306a800098SYoshinobu Inoue return EOPNOTSUPP; /*just for safety*/ 6316a800098SYoshinobu Inoue } 6326a800098SYoshinobu Inoue 6336a800098SYoshinobu Inoue /* 634df8bae1dSRodney W. Grimes * Delete any existing route for an interface. 635df8bae1dSRodney W. Grimes */ 63639191c8eSGarrett Wollman void 637df8bae1dSRodney W. Grimes in_ifscrub(ifp, ia) 638df8bae1dSRodney W. Grimes register struct ifnet *ifp; 639df8bae1dSRodney W. Grimes register struct in_ifaddr *ia; 640df8bae1dSRodney W. Grimes { 641df8bae1dSRodney W. Grimes 642df8bae1dSRodney W. Grimes if ((ia->ia_flags & IFA_ROUTE) == 0) 643df8bae1dSRodney W. Grimes return; 644df8bae1dSRodney W. Grimes if (ifp->if_flags & (IFF_LOOPBACK|IFF_POINTOPOINT)) 645df8bae1dSRodney W. Grimes rtinit(&(ia->ia_ifa), (int)RTM_DELETE, RTF_HOST); 646df8bae1dSRodney W. Grimes else 647df8bae1dSRodney W. Grimes rtinit(&(ia->ia_ifa), (int)RTM_DELETE, 0); 648df8bae1dSRodney W. Grimes ia->ia_flags &= ~IFA_ROUTE; 649df8bae1dSRodney W. Grimes } 650df8bae1dSRodney W. Grimes 651df8bae1dSRodney W. Grimes /* 652df8bae1dSRodney W. Grimes * Initialize an interface's internet address 653df8bae1dSRodney W. Grimes * and routing table entry. 654df8bae1dSRodney W. Grimes */ 6550312fbe9SPoul-Henning Kamp static int 656df8bae1dSRodney W. Grimes in_ifinit(ifp, ia, sin, scrub) 657df8bae1dSRodney W. Grimes register struct ifnet *ifp; 658df8bae1dSRodney W. Grimes register struct in_ifaddr *ia; 659df8bae1dSRodney W. Grimes struct sockaddr_in *sin; 660df8bae1dSRodney W. Grimes int scrub; 661df8bae1dSRodney W. Grimes { 662df8bae1dSRodney W. Grimes register u_long i = ntohl(sin->sin_addr.s_addr); 663df8bae1dSRodney W. Grimes struct sockaddr_in oldaddr; 664f23b4c91SGarrett Wollman int s = splimp(), flags = RTF_UP, error; 665df8bae1dSRodney W. Grimes 666df8bae1dSRodney W. Grimes oldaddr = ia->ia_addr; 667df8bae1dSRodney W. Grimes ia->ia_addr = *sin; 668df8bae1dSRodney W. Grimes /* 669df8bae1dSRodney W. Grimes * Give the interface a chance to initialize 670df8bae1dSRodney W. Grimes * if this is its first address, 671df8bae1dSRodney W. Grimes * and to validate the address if necessary. 672df8bae1dSRodney W. Grimes */ 673df8bae1dSRodney W. Grimes if (ifp->if_ioctl && 674df8bae1dSRodney W. Grimes (error = (*ifp->if_ioctl)(ifp, SIOCSIFADDR, (caddr_t)ia))) { 675df8bae1dSRodney W. Grimes splx(s); 676df8bae1dSRodney W. Grimes ia->ia_addr = oldaddr; 677df8bae1dSRodney W. Grimes return (error); 678df8bae1dSRodney W. Grimes } 67922c819a7SJonathan Lemon if (oldaddr.sin_family == AF_INET) 68022c819a7SJonathan Lemon LIST_REMOVE(ia, ia_hash); 68122c819a7SJonathan Lemon if (ia->ia_addr.sin_family == AF_INET) 68222c819a7SJonathan Lemon LIST_INSERT_HEAD(INADDR_HASH(ia->ia_addr.sin_addr.s_addr), 68322c819a7SJonathan Lemon ia, ia_hash); 684df8bae1dSRodney W. Grimes splx(s); 685df8bae1dSRodney W. Grimes if (scrub) { 686df8bae1dSRodney W. Grimes ia->ia_ifa.ifa_addr = (struct sockaddr *)&oldaddr; 687df8bae1dSRodney W. Grimes in_ifscrub(ifp, ia); 688df8bae1dSRodney W. Grimes ia->ia_ifa.ifa_addr = (struct sockaddr *)&ia->ia_addr; 689df8bae1dSRodney W. Grimes } 690df8bae1dSRodney W. Grimes if (IN_CLASSA(i)) 691df8bae1dSRodney W. Grimes ia->ia_netmask = IN_CLASSA_NET; 692df8bae1dSRodney W. Grimes else if (IN_CLASSB(i)) 693df8bae1dSRodney W. Grimes ia->ia_netmask = IN_CLASSB_NET; 694df8bae1dSRodney W. Grimes else 695df8bae1dSRodney W. Grimes ia->ia_netmask = IN_CLASSC_NET; 696df8bae1dSRodney W. Grimes /* 697df8bae1dSRodney W. Grimes * The subnet mask usually includes at least the standard network part, 698df8bae1dSRodney W. Grimes * but may may be smaller in the case of supernetting. 699df8bae1dSRodney W. Grimes * If it is set, we believe it. 700df8bae1dSRodney W. Grimes */ 701df8bae1dSRodney W. Grimes if (ia->ia_subnetmask == 0) { 702df8bae1dSRodney W. Grimes ia->ia_subnetmask = ia->ia_netmask; 703df8bae1dSRodney W. Grimes ia->ia_sockmask.sin_addr.s_addr = htonl(ia->ia_subnetmask); 704df8bae1dSRodney W. Grimes } else 705df8bae1dSRodney W. Grimes ia->ia_netmask &= ia->ia_subnetmask; 706df8bae1dSRodney W. Grimes ia->ia_net = i & ia->ia_netmask; 707df8bae1dSRodney W. Grimes ia->ia_subnet = i & ia->ia_subnetmask; 708df8bae1dSRodney W. Grimes in_socktrim(&ia->ia_sockmask); 709df8bae1dSRodney W. Grimes /* 710df8bae1dSRodney W. Grimes * Add route for the network. 711df8bae1dSRodney W. Grimes */ 712df8bae1dSRodney W. Grimes ia->ia_ifa.ifa_metric = ifp->if_metric; 713df8bae1dSRodney W. Grimes if (ifp->if_flags & IFF_BROADCAST) { 714df8bae1dSRodney W. Grimes ia->ia_broadaddr.sin_addr.s_addr = 715df8bae1dSRodney W. Grimes htonl(ia->ia_subnet | ~ia->ia_subnetmask); 716df8bae1dSRodney W. Grimes ia->ia_netbroadcast.s_addr = 717df8bae1dSRodney W. Grimes htonl(ia->ia_net | ~ ia->ia_netmask); 718df8bae1dSRodney W. Grimes } else if (ifp->if_flags & IFF_LOOPBACK) { 719df8bae1dSRodney W. Grimes ia->ia_ifa.ifa_dstaddr = ia->ia_ifa.ifa_addr; 720df8bae1dSRodney W. Grimes flags |= RTF_HOST; 721df8bae1dSRodney W. Grimes } else if (ifp->if_flags & IFF_POINTOPOINT) { 722df8bae1dSRodney W. Grimes if (ia->ia_dstaddr.sin_family != AF_INET) 723df8bae1dSRodney W. Grimes return (0); 724df8bae1dSRodney W. Grimes flags |= RTF_HOST; 725df8bae1dSRodney W. Grimes } 726df8bae1dSRodney W. Grimes if ((error = rtinit(&(ia->ia_ifa), (int)RTM_ADD, flags)) == 0) 727df8bae1dSRodney W. Grimes ia->ia_flags |= IFA_ROUTE; 7280f02fdacSBrian Somers 7290f02fdacSBrian Somers if (error != 0 && ia->ia_dstaddr.sin_family == AF_INET) { 7300f02fdacSBrian Somers ia->ia_addr = oldaddr; 7310f02fdacSBrian Somers return (error); 7320f02fdacSBrian Somers } 7330f02fdacSBrian Somers 73433841545SHajimu UMEMOTO /* XXX check if the subnet route points to the same interface */ 73533841545SHajimu UMEMOTO if (error == EEXIST) 73633841545SHajimu UMEMOTO error = 0; 737ffa5b11aSGarrett Wollman 738df8bae1dSRodney W. Grimes /* 739df8bae1dSRodney W. Grimes * If the interface supports multicast, join the "all hosts" 740df8bae1dSRodney W. Grimes * multicast group on that interface. 741df8bae1dSRodney W. Grimes */ 742df8bae1dSRodney W. Grimes if (ifp->if_flags & IFF_MULTICAST) { 743df8bae1dSRodney W. Grimes struct in_addr addr; 744df8bae1dSRodney W. Grimes 745df8bae1dSRodney W. Grimes addr.s_addr = htonl(INADDR_ALLHOSTS_GROUP); 746df8bae1dSRodney W. Grimes in_addmulti(&addr, ifp); 747df8bae1dSRodney W. Grimes } 748df8bae1dSRodney W. Grimes return (error); 749df8bae1dSRodney W. Grimes } 750df8bae1dSRodney W. Grimes 751df8bae1dSRodney W. Grimes 752df8bae1dSRodney W. Grimes /* 753df8bae1dSRodney W. Grimes * Return 1 if the address might be a local broadcast address. 754df8bae1dSRodney W. Grimes */ 75526f9a767SRodney W. Grimes int 756df8bae1dSRodney W. Grimes in_broadcast(in, ifp) 757df8bae1dSRodney W. Grimes struct in_addr in; 758df8bae1dSRodney W. Grimes struct ifnet *ifp; 759df8bae1dSRodney W. Grimes { 760df8bae1dSRodney W. Grimes register struct ifaddr *ifa; 761df8bae1dSRodney W. Grimes u_long t; 762df8bae1dSRodney W. Grimes 763df8bae1dSRodney W. Grimes if (in.s_addr == INADDR_BROADCAST || 764df8bae1dSRodney W. Grimes in.s_addr == INADDR_ANY) 765df8bae1dSRodney W. Grimes return 1; 766df8bae1dSRodney W. Grimes if ((ifp->if_flags & IFF_BROADCAST) == 0) 767df8bae1dSRodney W. Grimes return 0; 768df8bae1dSRodney W. Grimes t = ntohl(in.s_addr); 769df8bae1dSRodney W. Grimes /* 770df8bae1dSRodney W. Grimes * Look through the list of addresses for a match 771df8bae1dSRodney W. Grimes * with a broadcast address. 772df8bae1dSRodney W. Grimes */ 773df8bae1dSRodney W. Grimes #define ia ((struct in_ifaddr *)ifa) 774462b86feSPoul-Henning Kamp TAILQ_FOREACH(ifa, &ifp->if_addrhead, ifa_link) 775df8bae1dSRodney W. Grimes if (ifa->ifa_addr->sa_family == AF_INET && 776df8bae1dSRodney W. Grimes (in.s_addr == ia->ia_broadaddr.sin_addr.s_addr || 777df8bae1dSRodney W. Grimes in.s_addr == ia->ia_netbroadcast.s_addr || 778df8bae1dSRodney W. Grimes /* 779df8bae1dSRodney W. Grimes * Check for old-style (host 0) broadcast. 780df8bae1dSRodney W. Grimes */ 7818dd27fd6SGuido van Rooij t == ia->ia_subnet || t == ia->ia_net) && 7828dd27fd6SGuido van Rooij /* 7838dd27fd6SGuido van Rooij * Check for an all one subnetmask. These 7848dd27fd6SGuido van Rooij * only exist when an interface gets a secondary 7858dd27fd6SGuido van Rooij * address. 7868dd27fd6SGuido van Rooij */ 7878dd27fd6SGuido van Rooij ia->ia_subnetmask != (u_long)0xffffffff) 788df8bae1dSRodney W. Grimes return 1; 789df8bae1dSRodney W. Grimes return (0); 790df8bae1dSRodney W. Grimes #undef ia 791df8bae1dSRodney W. Grimes } 792df8bae1dSRodney W. Grimes /* 793df8bae1dSRodney W. Grimes * Add an address to the list of IP multicast addresses for a given interface. 794df8bae1dSRodney W. Grimes */ 795df8bae1dSRodney W. Grimes struct in_multi * 796df8bae1dSRodney W. Grimes in_addmulti(ap, ifp) 797df8bae1dSRodney W. Grimes register struct in_addr *ap; 798df8bae1dSRodney W. Grimes register struct ifnet *ifp; 799df8bae1dSRodney W. Grimes { 800df8bae1dSRodney W. Grimes register struct in_multi *inm; 801477180fbSGarrett Wollman int error; 802477180fbSGarrett Wollman struct sockaddr_in sin; 803477180fbSGarrett Wollman struct ifmultiaddr *ifma; 804df8bae1dSRodney W. Grimes int s = splnet(); 805df8bae1dSRodney W. Grimes 806df8bae1dSRodney W. Grimes /* 807477180fbSGarrett Wollman * Call generic routine to add membership or increment 808477180fbSGarrett Wollman * refcount. It wants addresses in the form of a sockaddr, 809477180fbSGarrett Wollman * so we build one here (being careful to zero the unused bytes). 810df8bae1dSRodney W. Grimes */ 811477180fbSGarrett Wollman bzero(&sin, sizeof sin); 812477180fbSGarrett Wollman sin.sin_family = AF_INET; 813477180fbSGarrett Wollman sin.sin_len = sizeof sin; 814477180fbSGarrett Wollman sin.sin_addr = *ap; 815477180fbSGarrett Wollman error = if_addmulti(ifp, (struct sockaddr *)&sin, &ifma); 816477180fbSGarrett Wollman if (error) { 817477180fbSGarrett Wollman splx(s); 818477180fbSGarrett Wollman return 0; 819df8bae1dSRodney W. Grimes } 820477180fbSGarrett Wollman 821df8bae1dSRodney W. Grimes /* 822477180fbSGarrett Wollman * If ifma->ifma_protospec is null, then if_addmulti() created 823477180fbSGarrett Wollman * a new record. Otherwise, we are done. 824df8bae1dSRodney W. Grimes */ 8254153a3a3SBruce Evans if (ifma->ifma_protospec != 0) { 8264153a3a3SBruce Evans splx(s); 827477180fbSGarrett Wollman return ifma->ifma_protospec; 8284153a3a3SBruce Evans } 829477180fbSGarrett Wollman 830477180fbSGarrett Wollman /* XXX - if_addmulti uses M_WAITOK. Can this really be called 831477180fbSGarrett Wollman at interrupt time? If so, need to fix if_addmulti. XXX */ 8327cc0979fSDavid Malone inm = (struct in_multi *)malloc(sizeof(*inm), M_IPMADDR, 8337cc0979fSDavid Malone M_NOWAIT | M_ZERO); 834df8bae1dSRodney W. Grimes if (inm == NULL) { 835df8bae1dSRodney W. Grimes splx(s); 836df8bae1dSRodney W. Grimes return (NULL); 837df8bae1dSRodney W. Grimes } 838477180fbSGarrett Wollman 839df8bae1dSRodney W. Grimes inm->inm_addr = *ap; 840df8bae1dSRodney W. Grimes inm->inm_ifp = ifp; 841477180fbSGarrett Wollman inm->inm_ifma = ifma; 842477180fbSGarrett Wollman ifma->ifma_protospec = inm; 843477180fbSGarrett Wollman LIST_INSERT_HEAD(&in_multihead, inm, inm_link); 844ffa5b11aSGarrett Wollman 845df8bae1dSRodney W. Grimes /* 846df8bae1dSRodney W. Grimes * Let IGMP know that we have joined a new IP multicast group. 847df8bae1dSRodney W. Grimes */ 848df8bae1dSRodney W. Grimes igmp_joingroup(inm); 849df8bae1dSRodney W. Grimes splx(s); 850df8bae1dSRodney W. Grimes return (inm); 851df8bae1dSRodney W. Grimes } 852df8bae1dSRodney W. Grimes 853df8bae1dSRodney W. Grimes /* 854df8bae1dSRodney W. Grimes * Delete a multicast address record. 855df8bae1dSRodney W. Grimes */ 85626f9a767SRodney W. Grimes void 857df8bae1dSRodney W. Grimes in_delmulti(inm) 858df8bae1dSRodney W. Grimes register struct in_multi *inm; 859df8bae1dSRodney W. Grimes { 860477180fbSGarrett Wollman struct ifmultiaddr *ifma = inm->inm_ifma; 86188a5354eSLuigi Rizzo struct in_multi my_inm; 862df8bae1dSRodney W. Grimes int s = splnet(); 863df8bae1dSRodney W. Grimes 86488a5354eSLuigi Rizzo my_inm.inm_ifp = NULL ; /* don't send the leave msg */ 865477180fbSGarrett Wollman if (ifma->ifma_refcount == 1) { 866df8bae1dSRodney W. Grimes /* 867df8bae1dSRodney W. Grimes * No remaining claims to this record; let IGMP know that 868df8bae1dSRodney W. Grimes * we are leaving the multicast group. 86988a5354eSLuigi Rizzo * But do it after the if_delmulti() which might reset 87088a5354eSLuigi Rizzo * the interface and nuke the packet. 871df8bae1dSRodney W. Grimes */ 87288a5354eSLuigi Rizzo my_inm = *inm ; 873477180fbSGarrett Wollman ifma->ifma_protospec = 0; 874477180fbSGarrett Wollman LIST_REMOVE(inm, inm_link); 875df8bae1dSRodney W. Grimes free(inm, M_IPMADDR); 876df8bae1dSRodney W. Grimes } 877477180fbSGarrett Wollman /* XXX - should be separate API for when we have an ifma? */ 878477180fbSGarrett Wollman if_delmulti(ifma->ifma_ifp, ifma->ifma_addr); 87988a5354eSLuigi Rizzo if (my_inm.inm_ifp != NULL) 88088a5354eSLuigi Rizzo igmp_leavegroup(&my_inm); 881df8bae1dSRodney W. Grimes splx(s); 882df8bae1dSRodney W. Grimes } 883