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> 40acd3428bSRobert Watson #include <sys/priv.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> 5271498f30SBruce M Simpson #include <netinet/ip_var.h> 5355166637SPoul-Henning Kamp 544d77a549SAlfred Perlstein static int in_mask2len(struct in_addr *); 554d77a549SAlfred Perlstein static void in_len2mask(struct in_addr *, int); 564d77a549SAlfred Perlstein static int in_lifaddr_ioctl(struct socket *, u_long, caddr_t, 574d77a549SAlfred Perlstein struct ifnet *, struct thread *); 586a800098SYoshinobu Inoue 5948321abeSMax Laier static int in_addprefix(struct in_ifaddr *, int); 6048321abeSMax Laier static int in_scrubprefix(struct in_ifaddr *); 614d77a549SAlfred Perlstein static void in_socktrim(struct sockaddr_in *); 624d77a549SAlfred Perlstein static int in_ifinit(struct ifnet *, 634d77a549SAlfred Perlstein struct in_ifaddr *, struct sockaddr_in *, int); 64ec002feeSBruce M Simpson static void in_purgemaddrs(struct ifnet *); 65df8bae1dSRodney W. Grimes 66f8731310SGarrett Wollman static int subnetsarelocal = 0; 67f6d24a78SPoul-Henning Kamp SYSCTL_INT(_net_inet_ip, OID_AUTO, subnets_are_local, CTLFLAG_RW, 683b95e134SRuslan Ermilov &subnetsarelocal, 0, "Treat all subnets as directly connected"); 691ae95409SGleb Smirnoff static int sameprefixcarponly = 0; 701ae95409SGleb Smirnoff SYSCTL_INT(_net_inet_ip, OID_AUTO, same_prefix_carp_only, CTLFLAG_RW, 711ae95409SGleb Smirnoff &sameprefixcarponly, 0, 721ae95409SGleb Smirnoff "Refuse to create same prefixes on different interfaces"); 73477180fbSGarrett Wollman 74e43cc4aeSHajimu UMEMOTO extern struct inpcbinfo ripcbinfo; 75e43cc4aeSHajimu UMEMOTO extern struct inpcbinfo udbinfo; 76e43cc4aeSHajimu UMEMOTO 77df8bae1dSRodney W. Grimes /* 78df8bae1dSRodney W. Grimes * Return 1 if an internet address is for a ``local'' host 79df8bae1dSRodney W. Grimes * (one to which we have a connection). If subnetsarelocal 80df8bae1dSRodney W. Grimes * is true, this includes other subnets of the local net. 81df8bae1dSRodney W. Grimes * Otherwise, it includes only the directly-connected (sub)nets. 82df8bae1dSRodney W. Grimes */ 8326f9a767SRodney W. Grimes int 84f2565d68SRobert Watson in_localaddr(struct in_addr in) 85df8bae1dSRodney W. Grimes { 86df8bae1dSRodney W. Grimes register u_long i = ntohl(in.s_addr); 87df8bae1dSRodney W. Grimes register struct in_ifaddr *ia; 88df8bae1dSRodney W. Grimes 89df8bae1dSRodney W. Grimes if (subnetsarelocal) { 90462b86feSPoul-Henning Kamp TAILQ_FOREACH(ia, &in_ifaddrhead, ia_link) 91df8bae1dSRodney W. Grimes if ((i & ia->ia_netmask) == ia->ia_net) 92df8bae1dSRodney W. Grimes return (1); 93df8bae1dSRodney W. Grimes } else { 9437d40066SPoul-Henning Kamp TAILQ_FOREACH(ia, &in_ifaddrhead, ia_link) 95df8bae1dSRodney W. Grimes if ((i & ia->ia_subnetmask) == ia->ia_subnet) 96df8bae1dSRodney W. Grimes return (1); 97df8bae1dSRodney W. Grimes } 98df8bae1dSRodney W. Grimes return (0); 99df8bae1dSRodney W. Grimes } 100df8bae1dSRodney W. Grimes 101df8bae1dSRodney W. Grimes /* 1022eccc90bSAndre Oppermann * Return 1 if an internet address is for the local host and configured 1032eccc90bSAndre Oppermann * on one of its interfaces. 1042eccc90bSAndre Oppermann */ 1052eccc90bSAndre Oppermann int 106f2565d68SRobert Watson in_localip(struct in_addr in) 1072eccc90bSAndre Oppermann { 1082eccc90bSAndre Oppermann struct in_ifaddr *ia; 1092eccc90bSAndre Oppermann 1102eccc90bSAndre Oppermann LIST_FOREACH(ia, INADDR_HASH(in.s_addr), ia_hash) { 1112eccc90bSAndre Oppermann if (IA_SIN(ia)->sin_addr.s_addr == in.s_addr) 1122eccc90bSAndre Oppermann return 1; 1132eccc90bSAndre Oppermann } 1142eccc90bSAndre Oppermann return 0; 1152eccc90bSAndre Oppermann } 1162eccc90bSAndre Oppermann 1172eccc90bSAndre Oppermann /* 118df8bae1dSRodney W. Grimes * Determine whether an IP address is in a reserved set of addresses 119df8bae1dSRodney W. Grimes * that may not be forwarded, or whether datagrams to that destination 120df8bae1dSRodney W. Grimes * may be forwarded. 121df8bae1dSRodney W. Grimes */ 12226f9a767SRodney W. Grimes int 123f2565d68SRobert Watson in_canforward(struct in_addr in) 124df8bae1dSRodney W. Grimes { 125df8bae1dSRodney W. Grimes register u_long i = ntohl(in.s_addr); 126df8bae1dSRodney W. Grimes register u_long net; 127df8bae1dSRodney W. Grimes 128f8429ca2SBruce M Simpson if (IN_EXPERIMENTAL(i) || IN_MULTICAST(i) || IN_LINKLOCAL(i)) 129df8bae1dSRodney W. Grimes return (0); 130df8bae1dSRodney W. Grimes if (IN_CLASSA(i)) { 131df8bae1dSRodney W. Grimes net = i & IN_CLASSA_NET; 132df8bae1dSRodney W. Grimes if (net == 0 || net == (IN_LOOPBACKNET << IN_CLASSA_NSHIFT)) 133df8bae1dSRodney W. Grimes return (0); 134df8bae1dSRodney W. Grimes } 135df8bae1dSRodney W. Grimes return (1); 136df8bae1dSRodney W. Grimes } 137df8bae1dSRodney W. Grimes 138df8bae1dSRodney W. Grimes /* 139df8bae1dSRodney W. Grimes * Trim a mask in a sockaddr 140df8bae1dSRodney W. Grimes */ 1410312fbe9SPoul-Henning Kamp static void 142f2565d68SRobert Watson in_socktrim(struct sockaddr_in *ap) 143df8bae1dSRodney W. Grimes { 144df8bae1dSRodney W. Grimes register char *cplim = (char *) &ap->sin_addr; 145df8bae1dSRodney W. Grimes register char *cp = (char *) (&ap->sin_addr + 1); 146df8bae1dSRodney W. Grimes 147df8bae1dSRodney W. Grimes ap->sin_len = 0; 148df00058dSGarrett Wollman while (--cp >= cplim) 149df8bae1dSRodney W. Grimes if (*cp) { 150df8bae1dSRodney W. Grimes (ap)->sin_len = cp - (char *) (ap) + 1; 151df8bae1dSRodney W. Grimes break; 152df8bae1dSRodney W. Grimes } 153df8bae1dSRodney W. Grimes } 154df8bae1dSRodney W. Grimes 1556a800098SYoshinobu Inoue static int 1566a800098SYoshinobu Inoue in_mask2len(mask) 1576a800098SYoshinobu Inoue struct in_addr *mask; 1586a800098SYoshinobu Inoue { 1596a800098SYoshinobu Inoue int x, y; 1606a800098SYoshinobu Inoue u_char *p; 1616a800098SYoshinobu Inoue 1626a800098SYoshinobu Inoue p = (u_char *)mask; 1636a800098SYoshinobu Inoue for (x = 0; x < sizeof(*mask); x++) { 1646a800098SYoshinobu Inoue if (p[x] != 0xff) 1656a800098SYoshinobu Inoue break; 1666a800098SYoshinobu Inoue } 1676a800098SYoshinobu Inoue y = 0; 1686a800098SYoshinobu Inoue if (x < sizeof(*mask)) { 1696a800098SYoshinobu Inoue for (y = 0; y < 8; y++) { 1706a800098SYoshinobu Inoue if ((p[x] & (0x80 >> y)) == 0) 1716a800098SYoshinobu Inoue break; 1726a800098SYoshinobu Inoue } 1736a800098SYoshinobu Inoue } 1746a800098SYoshinobu Inoue return x * 8 + y; 1756a800098SYoshinobu Inoue } 1766a800098SYoshinobu Inoue 1776a800098SYoshinobu Inoue static void 178f2565d68SRobert Watson in_len2mask(struct in_addr *mask, int len) 1796a800098SYoshinobu Inoue { 1806a800098SYoshinobu Inoue int i; 1816a800098SYoshinobu Inoue u_char *p; 1826a800098SYoshinobu Inoue 1836a800098SYoshinobu Inoue p = (u_char *)mask; 1846a800098SYoshinobu Inoue bzero(mask, sizeof(*mask)); 1856a800098SYoshinobu Inoue for (i = 0; i < len / 8; i++) 1866a800098SYoshinobu Inoue p[i] = 0xff; 1876a800098SYoshinobu Inoue if (len % 8) 1886a800098SYoshinobu Inoue p[i] = (0xff00 >> (len % 8)) & 0xff; 1896a800098SYoshinobu Inoue } 1906a800098SYoshinobu Inoue 191df8bae1dSRodney W. Grimes /* 192df8bae1dSRodney W. Grimes * Generic internet control operations (ioctl's). 193df8bae1dSRodney W. Grimes * Ifp is 0 if not an interface-specific ioctl. 194df8bae1dSRodney W. Grimes */ 195df8bae1dSRodney W. Grimes /* ARGSUSED */ 19626f9a767SRodney W. Grimes int 197f2565d68SRobert Watson in_control(struct socket *so, u_long cmd, caddr_t data, struct ifnet *ifp, 198f2565d68SRobert Watson struct thread *td) 199df8bae1dSRodney W. Grimes { 200df8bae1dSRodney W. Grimes register struct ifreq *ifr = (struct ifreq *)data; 201ac0aa473SBill Fenner register struct in_ifaddr *ia = 0, *iap; 202df8bae1dSRodney W. Grimes register struct ifaddr *ifa; 203f7e083afSBruce M Simpson struct in_addr allhosts_addr; 204ca925d9cSJonathan Lemon struct in_addr dst; 205df8bae1dSRodney W. Grimes struct in_ifaddr *oia; 206df8bae1dSRodney W. Grimes struct in_aliasreq *ifra = (struct in_aliasreq *)data; 207df8bae1dSRodney W. Grimes struct sockaddr_in oldaddr; 2080f02fdacSBrian Somers int error, hostIsNew, iaIsNew, maskIsNew, s; 209f7e083afSBruce M Simpson int iaIsFirst; 2100f02fdacSBrian Somers 211f7e083afSBruce M Simpson iaIsFirst = 0; 2120f02fdacSBrian Somers iaIsNew = 0; 213f7e083afSBruce M Simpson allhosts_addr.s_addr = htonl(INADDR_ALLHOSTS_GROUP); 214df8bae1dSRodney W. Grimes 2156a800098SYoshinobu Inoue switch (cmd) { 2166a800098SYoshinobu Inoue case SIOCALIFADDR: 217acd3428bSRobert Watson if (td != NULL) { 218acd3428bSRobert Watson error = priv_check(td, PRIV_NET_ADDIFADDR); 219acd3428bSRobert Watson if (error) 220acd3428bSRobert Watson return (error); 221acd3428bSRobert Watson } 222acd3428bSRobert Watson if (!ifp) 223acd3428bSRobert Watson return EINVAL; 224acd3428bSRobert Watson return in_lifaddr_ioctl(so, cmd, data, ifp, td); 225acd3428bSRobert Watson 2266a800098SYoshinobu Inoue case SIOCDLIFADDR: 227acd3428bSRobert Watson if (td != NULL) { 228acd3428bSRobert Watson error = priv_check(td, PRIV_NET_DELIFADDR); 229acd3428bSRobert Watson if (error) 230acd3428bSRobert Watson return (error); 231acd3428bSRobert Watson } 232acd3428bSRobert Watson if (!ifp) 233acd3428bSRobert Watson return EINVAL; 234acd3428bSRobert Watson return in_lifaddr_ioctl(so, cmd, data, ifp, td); 235acd3428bSRobert Watson 2366a800098SYoshinobu Inoue case SIOCGLIFADDR: 2376a800098SYoshinobu Inoue if (!ifp) 2386a800098SYoshinobu Inoue return EINVAL; 239b40ce416SJulian Elischer return in_lifaddr_ioctl(so, cmd, data, ifp, td); 2406a800098SYoshinobu Inoue } 2416a800098SYoshinobu Inoue 242df8bae1dSRodney W. Grimes /* 243df8bae1dSRodney W. Grimes * Find address for this interface, if it exists. 244ac0aa473SBill Fenner * 245ac0aa473SBill Fenner * If an alias address was specified, find that one instead of 246ca925d9cSJonathan Lemon * the first one on the interface, if possible. 247df8bae1dSRodney W. Grimes */ 248ca925d9cSJonathan Lemon if (ifp) { 249ca925d9cSJonathan Lemon dst = ((struct sockaddr_in *)&ifr->ifr_addr)->sin_addr; 250ca925d9cSJonathan Lemon LIST_FOREACH(iap, INADDR_HASH(dst.s_addr), ia_hash) 251ca925d9cSJonathan Lemon if (iap->ia_ifp == ifp && 252ca925d9cSJonathan Lemon iap->ia_addr.sin_addr.s_addr == dst.s_addr) { 253ac0aa473SBill Fenner ia = iap; 254df8bae1dSRodney W. Grimes break; 255ca925d9cSJonathan Lemon } 256ca925d9cSJonathan Lemon if (ia == NULL) 257ca925d9cSJonathan Lemon TAILQ_FOREACH(ifa, &ifp->if_addrhead, ifa_link) { 258ca925d9cSJonathan Lemon iap = ifatoia(ifa); 259ca925d9cSJonathan Lemon if (iap->ia_addr.sin_family == AF_INET) { 260ac0aa473SBill Fenner ia = iap; 261ac0aa473SBill Fenner break; 262ac0aa473SBill Fenner } 263ac0aa473SBill Fenner } 264f7e083afSBruce M Simpson if (ia == NULL) 265f7e083afSBruce M Simpson iaIsFirst = 1; 266ca925d9cSJonathan Lemon } 267df8bae1dSRodney W. Grimes 268df8bae1dSRodney W. Grimes switch (cmd) { 269df8bae1dSRodney W. Grimes 270df8bae1dSRodney W. Grimes case SIOCAIFADDR: 271df8bae1dSRodney W. Grimes case SIOCDIFADDR: 2726572231dSEivind Eklund if (ifp == 0) 2736572231dSEivind Eklund return (EADDRNOTAVAIL); 2741067217dSGarrett Wollman if (ifra->ifra_addr.sin_family == AF_INET) { 275fc2ffbe6SPoul-Henning Kamp for (oia = ia; ia; ia = TAILQ_NEXT(ia, ia_link)) { 276df8bae1dSRodney W. Grimes if (ia->ia_ifp == ifp && 277df8bae1dSRodney W. Grimes ia->ia_addr.sin_addr.s_addr == 278df8bae1dSRodney W. Grimes ifra->ifra_addr.sin_addr.s_addr) 279df8bae1dSRodney W. Grimes break; 280df8bae1dSRodney W. Grimes } 2811067217dSGarrett Wollman if ((ifp->if_flags & IFF_POINTOPOINT) 2821067217dSGarrett Wollman && (cmd == SIOCAIFADDR) 2831067217dSGarrett Wollman && (ifra->ifra_dstaddr.sin_addr.s_addr 2841067217dSGarrett Wollman == INADDR_ANY)) { 285357b78a9SGarrett Wollman return EDESTADDRREQ; 2861067217dSGarrett Wollman } 2871067217dSGarrett Wollman } 288df8bae1dSRodney W. Grimes if (cmd == SIOCDIFADDR && ia == 0) 289df8bae1dSRodney W. Grimes return (EADDRNOTAVAIL); 290df8bae1dSRodney W. Grimes /* FALLTHROUGH */ 291df8bae1dSRodney W. Grimes case SIOCSIFADDR: 292df8bae1dSRodney W. Grimes case SIOCSIFNETMASK: 293df8bae1dSRodney W. Grimes case SIOCSIFDSTADDR: 294acd3428bSRobert Watson if (td != NULL) { 295acd3428bSRobert Watson error = priv_check(td, PRIV_NET_ADDIFADDR); 296acd3428bSRobert Watson if (error) 297acd3428bSRobert Watson return (error); 298acd3428bSRobert Watson } 299df8bae1dSRodney W. Grimes 300df8bae1dSRodney W. Grimes if (ifp == 0) 3016572231dSEivind Eklund return (EADDRNOTAVAIL); 302df8bae1dSRodney W. Grimes if (ia == (struct in_ifaddr *)0) { 30359562606SGarrett Wollman ia = (struct in_ifaddr *) 304a163d034SWarner Losh malloc(sizeof *ia, M_IFADDR, M_WAITOK | M_ZERO); 30559562606SGarrett Wollman if (ia == (struct in_ifaddr *)NULL) 306df8bae1dSRodney W. Grimes return (ENOBUFS); 307c655b7c4SDavid Greenman /* 308c655b7c4SDavid Greenman * Protect from ipintr() traversing address list 309c655b7c4SDavid Greenman * while we're modifying it. 310c655b7c4SDavid Greenman */ 311c655b7c4SDavid Greenman s = splnet(); 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; 327f3d30eb2SGleb Smirnoff 328f3d30eb2SGleb Smirnoff TAILQ_INSERT_TAIL(&in_ifaddrhead, ia, ia_link); 329c655b7c4SDavid Greenman splx(s); 3300f02fdacSBrian Somers iaIsNew = 1; 331df8bae1dSRodney W. Grimes } 332df8bae1dSRodney W. Grimes break; 333df8bae1dSRodney W. Grimes 334df8bae1dSRodney W. Grimes case SIOCSIFBRDADDR: 335acd3428bSRobert Watson if (td != NULL) { 336acd3428bSRobert Watson error = priv_check(td, PRIV_NET_ADDIFADDR); 337acd3428bSRobert Watson if (error) 338acd3428bSRobert Watson return (error); 339acd3428bSRobert Watson } 340df8bae1dSRodney W. Grimes /* FALLTHROUGH */ 341df8bae1dSRodney W. Grimes 342df8bae1dSRodney W. Grimes case SIOCGIFADDR: 343df8bae1dSRodney W. Grimes case SIOCGIFNETMASK: 344df8bae1dSRodney W. Grimes case SIOCGIFDSTADDR: 345df8bae1dSRodney W. Grimes case SIOCGIFBRDADDR: 346df8bae1dSRodney W. Grimes if (ia == (struct in_ifaddr *)0) 347df8bae1dSRodney W. Grimes return (EADDRNOTAVAIL); 348df8bae1dSRodney W. Grimes break; 349df8bae1dSRodney W. Grimes } 350df8bae1dSRodney W. Grimes switch (cmd) { 351df8bae1dSRodney W. Grimes 352df8bae1dSRodney W. Grimes case SIOCGIFADDR: 353df8bae1dSRodney W. Grimes *((struct sockaddr_in *)&ifr->ifr_addr) = ia->ia_addr; 3540f02fdacSBrian Somers return (0); 355df8bae1dSRodney W. Grimes 356df8bae1dSRodney W. Grimes case SIOCGIFBRDADDR: 357df8bae1dSRodney W. Grimes if ((ifp->if_flags & IFF_BROADCAST) == 0) 358df8bae1dSRodney W. Grimes return (EINVAL); 359df8bae1dSRodney W. Grimes *((struct sockaddr_in *)&ifr->ifr_dstaddr) = ia->ia_broadaddr; 3600f02fdacSBrian Somers return (0); 361df8bae1dSRodney W. Grimes 362df8bae1dSRodney W. Grimes case SIOCGIFDSTADDR: 363df8bae1dSRodney W. Grimes if ((ifp->if_flags & IFF_POINTOPOINT) == 0) 364df8bae1dSRodney W. Grimes return (EINVAL); 365df8bae1dSRodney W. Grimes *((struct sockaddr_in *)&ifr->ifr_dstaddr) = ia->ia_dstaddr; 3660f02fdacSBrian Somers return (0); 367df8bae1dSRodney W. Grimes 368df8bae1dSRodney W. Grimes case SIOCGIFNETMASK: 369df8bae1dSRodney W. Grimes *((struct sockaddr_in *)&ifr->ifr_addr) = ia->ia_sockmask; 3700f02fdacSBrian Somers return (0); 371df8bae1dSRodney W. Grimes 372df8bae1dSRodney W. Grimes case SIOCSIFDSTADDR: 373df8bae1dSRodney W. Grimes if ((ifp->if_flags & IFF_POINTOPOINT) == 0) 374df8bae1dSRodney W. Grimes return (EINVAL); 375df8bae1dSRodney W. Grimes oldaddr = ia->ia_dstaddr; 376df8bae1dSRodney W. Grimes ia->ia_dstaddr = *(struct sockaddr_in *)&ifr->ifr_dstaddr; 377ba5da2a0SIan Dowse if (ifp->if_ioctl) { 378ba5da2a0SIan Dowse IFF_LOCKGIANT(ifp); 379ba5da2a0SIan Dowse error = (*ifp->if_ioctl)(ifp, SIOCSIFDSTADDR, 380ba5da2a0SIan Dowse (caddr_t)ia); 381ba5da2a0SIan Dowse IFF_UNLOCKGIANT(ifp); 382ba5da2a0SIan Dowse if (error) { 383df8bae1dSRodney W. Grimes ia->ia_dstaddr = oldaddr; 384df8bae1dSRodney W. Grimes return (error); 385df8bae1dSRodney W. Grimes } 386ba5da2a0SIan Dowse } 387df8bae1dSRodney W. Grimes if (ia->ia_flags & IFA_ROUTE) { 388df8bae1dSRodney W. Grimes ia->ia_ifa.ifa_dstaddr = (struct sockaddr *)&oldaddr; 389df8bae1dSRodney W. Grimes rtinit(&(ia->ia_ifa), (int)RTM_DELETE, RTF_HOST); 390df8bae1dSRodney W. Grimes ia->ia_ifa.ifa_dstaddr = 391df8bae1dSRodney W. Grimes (struct sockaddr *)&ia->ia_dstaddr; 392df8bae1dSRodney W. Grimes rtinit(&(ia->ia_ifa), (int)RTM_ADD, RTF_HOST|RTF_UP); 393df8bae1dSRodney W. Grimes } 3940f02fdacSBrian Somers return (0); 395df8bae1dSRodney W. Grimes 396df8bae1dSRodney W. Grimes case SIOCSIFBRDADDR: 397df8bae1dSRodney W. Grimes if ((ifp->if_flags & IFF_BROADCAST) == 0) 398df8bae1dSRodney W. Grimes return (EINVAL); 399df8bae1dSRodney W. Grimes ia->ia_broadaddr = *(struct sockaddr_in *)&ifr->ifr_broadaddr; 4000f02fdacSBrian Somers return (0); 401df8bae1dSRodney W. Grimes 402df8bae1dSRodney W. Grimes case SIOCSIFADDR: 4030f02fdacSBrian Somers error = in_ifinit(ifp, ia, 4040f02fdacSBrian Somers (struct sockaddr_in *) &ifr->ifr_addr, 1); 4050f02fdacSBrian Somers if (error != 0 && iaIsNew) 4060f02fdacSBrian Somers break; 407f7e083afSBruce M Simpson if (error == 0) { 408f7e083afSBruce M Simpson if (iaIsFirst && (ifp->if_flags & IFF_MULTICAST) != 0) 409f7e083afSBruce M Simpson in_addmulti(&allhosts_addr, ifp); 41025a4adceSMax Laier EVENTHANDLER_INVOKE(ifaddr_event, ifp); 411f7e083afSBruce M Simpson } 4120f02fdacSBrian Somers return (0); 413df8bae1dSRodney W. Grimes 414df8bae1dSRodney W. Grimes case SIOCSIFNETMASK: 415bc183b3fSDag-Erling Smørgrav ia->ia_sockmask.sin_addr = ifra->ifra_addr.sin_addr; 416bc183b3fSDag-Erling Smørgrav ia->ia_subnetmask = ntohl(ia->ia_sockmask.sin_addr.s_addr); 4170f02fdacSBrian Somers return (0); 418df8bae1dSRodney W. Grimes 419df8bae1dSRodney W. Grimes case SIOCAIFADDR: 420df8bae1dSRodney W. Grimes maskIsNew = 0; 421df8bae1dSRodney W. Grimes hostIsNew = 1; 422df8bae1dSRodney W. Grimes error = 0; 423df8bae1dSRodney W. Grimes if (ia->ia_addr.sin_family == AF_INET) { 424df8bae1dSRodney W. Grimes if (ifra->ifra_addr.sin_len == 0) { 425df8bae1dSRodney W. Grimes ifra->ifra_addr = ia->ia_addr; 426df8bae1dSRodney W. Grimes hostIsNew = 0; 427df8bae1dSRodney W. Grimes } else if (ifra->ifra_addr.sin_addr.s_addr == 428df8bae1dSRodney W. Grimes ia->ia_addr.sin_addr.s_addr) 429df8bae1dSRodney W. Grimes hostIsNew = 0; 430df8bae1dSRodney W. Grimes } 431df8bae1dSRodney W. Grimes if (ifra->ifra_mask.sin_len) { 432df8bae1dSRodney W. Grimes in_ifscrub(ifp, ia); 433df8bae1dSRodney W. Grimes ia->ia_sockmask = ifra->ifra_mask; 434bc183b3fSDag-Erling Smørgrav ia->ia_sockmask.sin_family = AF_INET; 435df8bae1dSRodney W. Grimes ia->ia_subnetmask = 436df8bae1dSRodney W. Grimes ntohl(ia->ia_sockmask.sin_addr.s_addr); 437df8bae1dSRodney W. Grimes maskIsNew = 1; 438df8bae1dSRodney W. Grimes } 439df8bae1dSRodney W. Grimes if ((ifp->if_flags & IFF_POINTOPOINT) && 440df8bae1dSRodney W. Grimes (ifra->ifra_dstaddr.sin_family == AF_INET)) { 441df8bae1dSRodney W. Grimes in_ifscrub(ifp, ia); 442df8bae1dSRodney W. Grimes ia->ia_dstaddr = ifra->ifra_dstaddr; 443df8bae1dSRodney W. Grimes maskIsNew = 1; /* We lie; but the effect's the same */ 444df8bae1dSRodney W. Grimes } 445df8bae1dSRodney W. Grimes if (ifra->ifra_addr.sin_family == AF_INET && 446df8bae1dSRodney W. Grimes (hostIsNew || maskIsNew)) 447df8bae1dSRodney W. Grimes error = in_ifinit(ifp, ia, &ifra->ifra_addr, 0); 4480f02fdacSBrian Somers if (error != 0 && iaIsNew) 4490f02fdacSBrian Somers break; 4500f02fdacSBrian Somers 451df8bae1dSRodney W. Grimes if ((ifp->if_flags & IFF_BROADCAST) && 452df8bae1dSRodney W. Grimes (ifra->ifra_broadaddr.sin_family == AF_INET)) 453df8bae1dSRodney W. Grimes ia->ia_broadaddr = ifra->ifra_broadaddr; 454f7e083afSBruce M Simpson if (error == 0) { 455f7e083afSBruce M Simpson if (iaIsFirst && (ifp->if_flags & IFF_MULTICAST) != 0) 456f7e083afSBruce M Simpson in_addmulti(&allhosts_addr, ifp); 45725a4adceSMax Laier EVENTHANDLER_INVOKE(ifaddr_event, ifp); 458f7e083afSBruce M Simpson } 459df8bae1dSRodney W. Grimes return (error); 460df8bae1dSRodney W. Grimes 461df8bae1dSRodney W. Grimes case SIOCDIFADDR: 462089cdfadSRuslan Ermilov /* 463089cdfadSRuslan Ermilov * in_ifscrub kills the interface route. 464089cdfadSRuslan Ermilov */ 465df8bae1dSRodney W. Grimes in_ifscrub(ifp, ia); 466c655b7c4SDavid Greenman /* 467089cdfadSRuslan Ermilov * in_ifadown gets rid of all the rest of 468089cdfadSRuslan Ermilov * the routes. This is not quite the right 469089cdfadSRuslan Ermilov * thing to do, but at least if we are running 470089cdfadSRuslan Ermilov * a routing process they will come back. 471089cdfadSRuslan Ermilov */ 47291854268SRuslan Ermilov in_ifadown(&ia->ia_ifa, 1); 47325a4adceSMax Laier EVENTHANDLER_INVOKE(ifaddr_event, ifp); 4740f02fdacSBrian Somers error = 0; 475df8bae1dSRodney W. Grimes break; 476df8bae1dSRodney W. Grimes 477df8bae1dSRodney W. Grimes default: 478df8bae1dSRodney W. Grimes if (ifp == 0 || ifp->if_ioctl == 0) 479df8bae1dSRodney W. Grimes return (EOPNOTSUPP); 480ba5da2a0SIan Dowse IFF_LOCKGIANT(ifp); 481ba5da2a0SIan Dowse error = (*ifp->if_ioctl)(ifp, cmd, data); 482ba5da2a0SIan Dowse IFF_UNLOCKGIANT(ifp); 483ba5da2a0SIan Dowse return (error); 484df8bae1dSRodney W. Grimes } 4850f02fdacSBrian Somers 4860f02fdacSBrian Somers /* 4870f02fdacSBrian Somers * Protect from ipintr() traversing address list while we're modifying 4880f02fdacSBrian Somers * it. 4890f02fdacSBrian Somers */ 4900f02fdacSBrian Somers s = splnet(); 4910f02fdacSBrian Somers TAILQ_REMOVE(&ifp->if_addrhead, &ia->ia_ifa, ifa_link); 4920f02fdacSBrian Somers TAILQ_REMOVE(&in_ifaddrhead, ia, ia_link); 493f7e083afSBruce M Simpson if (ia->ia_addr.sin_family == AF_INET) { 4940f02fdacSBrian Somers LIST_REMOVE(ia, ia_hash); 495f7e083afSBruce M Simpson /* 496f7e083afSBruce M Simpson * If this is the last IPv4 address configured on this 497f7e083afSBruce M Simpson * interface, leave the all-hosts group. 498f7e083afSBruce M Simpson * XXX: This is quite ugly because of locking and structure. 499f7e083afSBruce M Simpson */ 500f7e083afSBruce M Simpson oia = NULL; 501f7e083afSBruce M Simpson IFP_TO_IA(ifp, oia); 502f7e083afSBruce M Simpson if (oia == NULL) { 503f7e083afSBruce M Simpson struct in_multi *inm; 504f7e083afSBruce M Simpson 505f7e083afSBruce M Simpson IFF_LOCKGIANT(ifp); 506f7e083afSBruce M Simpson IN_MULTI_LOCK(); 507f7e083afSBruce M Simpson IN_LOOKUP_MULTI(allhosts_addr, ifp, inm); 508f7e083afSBruce M Simpson if (inm != NULL) 509f7e083afSBruce M Simpson in_delmulti_locked(inm); 510f7e083afSBruce M Simpson IN_MULTI_UNLOCK(); 511f7e083afSBruce M Simpson IFF_UNLOCKGIANT(ifp); 512f7e083afSBruce M Simpson } 513f7e083afSBruce M Simpson } 5140f02fdacSBrian Somers IFAFREE(&ia->ia_ifa); 5150f02fdacSBrian Somers splx(s); 5160f02fdacSBrian Somers 5170f02fdacSBrian Somers return (error); 518df8bae1dSRodney W. Grimes } 519df8bae1dSRodney W. Grimes 520df8bae1dSRodney W. Grimes /* 5216a800098SYoshinobu Inoue * SIOC[GAD]LIFADDR. 5226a800098SYoshinobu Inoue * SIOCGLIFADDR: get first address. (?!?) 5236a800098SYoshinobu Inoue * SIOCGLIFADDR with IFLR_PREFIX: 5246a800098SYoshinobu Inoue * get first address that matches the specified prefix. 5256a800098SYoshinobu Inoue * SIOCALIFADDR: add the specified address. 5266a800098SYoshinobu Inoue * SIOCALIFADDR with IFLR_PREFIX: 5276a800098SYoshinobu Inoue * EINVAL since we can't deduce hostid part of the address. 5286a800098SYoshinobu Inoue * SIOCDLIFADDR: delete the specified address. 5296a800098SYoshinobu Inoue * SIOCDLIFADDR with IFLR_PREFIX: 5306a800098SYoshinobu Inoue * delete the first address that matches the specified prefix. 5316a800098SYoshinobu Inoue * return values: 5326a800098SYoshinobu Inoue * EINVAL on invalid parameters 5336a800098SYoshinobu Inoue * EADDRNOTAVAIL on prefix match failed/specified address not found 5346a800098SYoshinobu Inoue * other values may be returned from in_ioctl() 5356a800098SYoshinobu Inoue */ 5366a800098SYoshinobu Inoue static int 537f2565d68SRobert Watson in_lifaddr_ioctl(struct socket *so, u_long cmd, caddr_t data, 538f2565d68SRobert Watson struct ifnet *ifp, struct thread *td) 5396a800098SYoshinobu Inoue { 5406a800098SYoshinobu Inoue struct if_laddrreq *iflr = (struct if_laddrreq *)data; 5416a800098SYoshinobu Inoue struct ifaddr *ifa; 5426a800098SYoshinobu Inoue 5436a800098SYoshinobu Inoue /* sanity checks */ 5446a800098SYoshinobu Inoue if (!data || !ifp) { 5456a800098SYoshinobu Inoue panic("invalid argument to in_lifaddr_ioctl"); 5466a800098SYoshinobu Inoue /*NOTRECHED*/ 5476a800098SYoshinobu Inoue } 5486a800098SYoshinobu Inoue 5496a800098SYoshinobu Inoue switch (cmd) { 5506a800098SYoshinobu Inoue case SIOCGLIFADDR: 5516a800098SYoshinobu Inoue /* address must be specified on GET with IFLR_PREFIX */ 5526a800098SYoshinobu Inoue if ((iflr->flags & IFLR_PREFIX) == 0) 5536a800098SYoshinobu Inoue break; 5546a800098SYoshinobu Inoue /*FALLTHROUGH*/ 5556a800098SYoshinobu Inoue case SIOCALIFADDR: 5566a800098SYoshinobu Inoue case SIOCDLIFADDR: 5576a800098SYoshinobu Inoue /* address must be specified on ADD and DELETE */ 5585d60ed0eSYoshinobu Inoue if (iflr->addr.ss_family != AF_INET) 5596a800098SYoshinobu Inoue return EINVAL; 5605d60ed0eSYoshinobu Inoue if (iflr->addr.ss_len != sizeof(struct sockaddr_in)) 5616a800098SYoshinobu Inoue return EINVAL; 5626a800098SYoshinobu Inoue /* XXX need improvement */ 5635d60ed0eSYoshinobu Inoue if (iflr->dstaddr.ss_family 5645d60ed0eSYoshinobu Inoue && iflr->dstaddr.ss_family != AF_INET) 5656a800098SYoshinobu Inoue return EINVAL; 5665d60ed0eSYoshinobu Inoue if (iflr->dstaddr.ss_family 5675d60ed0eSYoshinobu Inoue && iflr->dstaddr.ss_len != sizeof(struct sockaddr_in)) 5686a800098SYoshinobu Inoue return EINVAL; 5696a800098SYoshinobu Inoue break; 5706a800098SYoshinobu Inoue default: /*shouldn't happen*/ 5716a800098SYoshinobu Inoue return EOPNOTSUPP; 5726a800098SYoshinobu Inoue } 5736a800098SYoshinobu Inoue if (sizeof(struct in_addr) * 8 < iflr->prefixlen) 5746a800098SYoshinobu Inoue return EINVAL; 5756a800098SYoshinobu Inoue 5766a800098SYoshinobu Inoue switch (cmd) { 5776a800098SYoshinobu Inoue case SIOCALIFADDR: 5786a800098SYoshinobu Inoue { 5796a800098SYoshinobu Inoue struct in_aliasreq ifra; 5806a800098SYoshinobu Inoue 5816a800098SYoshinobu Inoue if (iflr->flags & IFLR_PREFIX) 5826a800098SYoshinobu Inoue return EINVAL; 5836a800098SYoshinobu Inoue 5846a800098SYoshinobu Inoue /* copy args to in_aliasreq, perform ioctl(SIOCAIFADDR_IN6). */ 5856a800098SYoshinobu Inoue bzero(&ifra, sizeof(ifra)); 5866a800098SYoshinobu Inoue bcopy(iflr->iflr_name, ifra.ifra_name, 5876a800098SYoshinobu Inoue sizeof(ifra.ifra_name)); 5886a800098SYoshinobu Inoue 5895d60ed0eSYoshinobu Inoue bcopy(&iflr->addr, &ifra.ifra_addr, iflr->addr.ss_len); 5906a800098SYoshinobu Inoue 5915d60ed0eSYoshinobu Inoue if (iflr->dstaddr.ss_family) { /*XXX*/ 5926a800098SYoshinobu Inoue bcopy(&iflr->dstaddr, &ifra.ifra_dstaddr, 5935d60ed0eSYoshinobu Inoue iflr->dstaddr.ss_len); 5946a800098SYoshinobu Inoue } 5956a800098SYoshinobu Inoue 5966a800098SYoshinobu Inoue ifra.ifra_mask.sin_family = AF_INET; 5976a800098SYoshinobu Inoue ifra.ifra_mask.sin_len = sizeof(struct sockaddr_in); 5986a800098SYoshinobu Inoue in_len2mask(&ifra.ifra_mask.sin_addr, iflr->prefixlen); 5996a800098SYoshinobu Inoue 600b40ce416SJulian Elischer return in_control(so, SIOCAIFADDR, (caddr_t)&ifra, ifp, td); 6016a800098SYoshinobu Inoue } 6026a800098SYoshinobu Inoue case SIOCGLIFADDR: 6036a800098SYoshinobu Inoue case SIOCDLIFADDR: 6046a800098SYoshinobu Inoue { 6056a800098SYoshinobu Inoue struct in_ifaddr *ia; 6066a800098SYoshinobu Inoue struct in_addr mask, candidate, match; 6076a800098SYoshinobu Inoue struct sockaddr_in *sin; 6086a800098SYoshinobu Inoue int cmp; 6096a800098SYoshinobu Inoue 6106a800098SYoshinobu Inoue bzero(&mask, sizeof(mask)); 6116a800098SYoshinobu Inoue if (iflr->flags & IFLR_PREFIX) { 6126a800098SYoshinobu Inoue /* lookup a prefix rather than address. */ 6136a800098SYoshinobu Inoue in_len2mask(&mask, iflr->prefixlen); 6146a800098SYoshinobu Inoue 6156a800098SYoshinobu Inoue sin = (struct sockaddr_in *)&iflr->addr; 6166a800098SYoshinobu Inoue match.s_addr = sin->sin_addr.s_addr; 6176a800098SYoshinobu Inoue match.s_addr &= mask.s_addr; 6186a800098SYoshinobu Inoue 6196a800098SYoshinobu Inoue /* if you set extra bits, that's wrong */ 6206a800098SYoshinobu Inoue if (match.s_addr != sin->sin_addr.s_addr) 6216a800098SYoshinobu Inoue return EINVAL; 6226a800098SYoshinobu Inoue 6236a800098SYoshinobu Inoue cmp = 1; 6246a800098SYoshinobu Inoue } else { 6256a800098SYoshinobu Inoue if (cmd == SIOCGLIFADDR) { 6266a800098SYoshinobu Inoue /* on getting an address, take the 1st match */ 6276a800098SYoshinobu Inoue cmp = 0; /*XXX*/ 6286a800098SYoshinobu Inoue } else { 6296a800098SYoshinobu Inoue /* on deleting an address, do exact match */ 6306a800098SYoshinobu Inoue in_len2mask(&mask, 32); 6316a800098SYoshinobu Inoue sin = (struct sockaddr_in *)&iflr->addr; 6326a800098SYoshinobu Inoue match.s_addr = sin->sin_addr.s_addr; 6336a800098SYoshinobu Inoue 6346a800098SYoshinobu Inoue cmp = 1; 6356a800098SYoshinobu Inoue } 6366a800098SYoshinobu Inoue } 6376a800098SYoshinobu Inoue 6386a800098SYoshinobu Inoue TAILQ_FOREACH(ifa, &ifp->if_addrhead, ifa_link) { 6396a800098SYoshinobu Inoue if (ifa->ifa_addr->sa_family != AF_INET6) 6406a800098SYoshinobu Inoue continue; 6416a800098SYoshinobu Inoue if (!cmp) 6426a800098SYoshinobu Inoue break; 6436a800098SYoshinobu Inoue candidate.s_addr = ((struct sockaddr_in *)&ifa->ifa_addr)->sin_addr.s_addr; 6446a800098SYoshinobu Inoue candidate.s_addr &= mask.s_addr; 6456a800098SYoshinobu Inoue if (candidate.s_addr == match.s_addr) 6466a800098SYoshinobu Inoue break; 6476a800098SYoshinobu Inoue } 6486a800098SYoshinobu Inoue if (!ifa) 6496a800098SYoshinobu Inoue return EADDRNOTAVAIL; 6506a800098SYoshinobu Inoue ia = (struct in_ifaddr *)ifa; 6516a800098SYoshinobu Inoue 6526a800098SYoshinobu Inoue if (cmd == SIOCGLIFADDR) { 6536a800098SYoshinobu Inoue /* fill in the if_laddrreq structure */ 6546a800098SYoshinobu Inoue bcopy(&ia->ia_addr, &iflr->addr, ia->ia_addr.sin_len); 6556a800098SYoshinobu Inoue 6566a800098SYoshinobu Inoue if ((ifp->if_flags & IFF_POINTOPOINT) != 0) { 6576a800098SYoshinobu Inoue bcopy(&ia->ia_dstaddr, &iflr->dstaddr, 6586a800098SYoshinobu Inoue ia->ia_dstaddr.sin_len); 6596a800098SYoshinobu Inoue } else 6606a800098SYoshinobu Inoue bzero(&iflr->dstaddr, sizeof(iflr->dstaddr)); 6616a800098SYoshinobu Inoue 6626a800098SYoshinobu Inoue iflr->prefixlen = 6636a800098SYoshinobu Inoue in_mask2len(&ia->ia_sockmask.sin_addr); 6646a800098SYoshinobu Inoue 6656a800098SYoshinobu Inoue iflr->flags = 0; /*XXX*/ 6666a800098SYoshinobu Inoue 6676a800098SYoshinobu Inoue return 0; 6686a800098SYoshinobu Inoue } else { 6696a800098SYoshinobu Inoue struct in_aliasreq ifra; 6706a800098SYoshinobu Inoue 6716a800098SYoshinobu Inoue /* fill in_aliasreq and do ioctl(SIOCDIFADDR_IN6) */ 6726a800098SYoshinobu Inoue bzero(&ifra, sizeof(ifra)); 6736a800098SYoshinobu Inoue bcopy(iflr->iflr_name, ifra.ifra_name, 6746a800098SYoshinobu Inoue sizeof(ifra.ifra_name)); 6756a800098SYoshinobu Inoue 6766a800098SYoshinobu Inoue bcopy(&ia->ia_addr, &ifra.ifra_addr, 6776a800098SYoshinobu Inoue ia->ia_addr.sin_len); 6786a800098SYoshinobu Inoue if ((ifp->if_flags & IFF_POINTOPOINT) != 0) { 6796a800098SYoshinobu Inoue bcopy(&ia->ia_dstaddr, &ifra.ifra_dstaddr, 6806a800098SYoshinobu Inoue ia->ia_dstaddr.sin_len); 6816a800098SYoshinobu Inoue } 6826a800098SYoshinobu Inoue bcopy(&ia->ia_sockmask, &ifra.ifra_dstaddr, 6836a800098SYoshinobu Inoue ia->ia_sockmask.sin_len); 6846a800098SYoshinobu Inoue 6856a800098SYoshinobu Inoue return in_control(so, SIOCDIFADDR, (caddr_t)&ifra, 686b40ce416SJulian Elischer ifp, td); 6876a800098SYoshinobu Inoue } 6886a800098SYoshinobu Inoue } 6896a800098SYoshinobu Inoue } 6906a800098SYoshinobu Inoue 6916a800098SYoshinobu Inoue return EOPNOTSUPP; /*just for safety*/ 6926a800098SYoshinobu Inoue } 6936a800098SYoshinobu Inoue 6946a800098SYoshinobu Inoue /* 695df8bae1dSRodney W. Grimes * Delete any existing route for an interface. 696df8bae1dSRodney W. Grimes */ 69739191c8eSGarrett Wollman void 698f2565d68SRobert Watson in_ifscrub(struct ifnet *ifp, struct in_ifaddr *ia) 699df8bae1dSRodney W. Grimes { 700f2565d68SRobert Watson 70148321abeSMax Laier in_scrubprefix(ia); 702df8bae1dSRodney W. Grimes } 703df8bae1dSRodney W. Grimes 704df8bae1dSRodney W. Grimes /* 705df8bae1dSRodney W. Grimes * Initialize an interface's internet address 706df8bae1dSRodney W. Grimes * and routing table entry. 707df8bae1dSRodney W. Grimes */ 7080312fbe9SPoul-Henning Kamp static int 709f2565d68SRobert Watson in_ifinit(struct ifnet *ifp, struct in_ifaddr *ia, struct sockaddr_in *sin, 710f2565d68SRobert Watson int scrub) 711df8bae1dSRodney W. Grimes { 712df8bae1dSRodney W. Grimes register u_long i = ntohl(sin->sin_addr.s_addr); 713df8bae1dSRodney W. Grimes struct sockaddr_in oldaddr; 7145a43847dSBrian Somers int s = splimp(), flags = RTF_UP, error = 0; 715df8bae1dSRodney W. Grimes 716df8bae1dSRodney W. Grimes oldaddr = ia->ia_addr; 7172754d95dSSUZUKI Shinsuke if (oldaddr.sin_family == AF_INET) 7182754d95dSSUZUKI Shinsuke LIST_REMOVE(ia, ia_hash); 719df8bae1dSRodney W. Grimes ia->ia_addr = *sin; 7202754d95dSSUZUKI Shinsuke if (ia->ia_addr.sin_family == AF_INET) 7212754d95dSSUZUKI Shinsuke LIST_INSERT_HEAD(INADDR_HASH(ia->ia_addr.sin_addr.s_addr), 7222754d95dSSUZUKI Shinsuke ia, ia_hash); 723df8bae1dSRodney W. Grimes /* 724df8bae1dSRodney W. Grimes * Give the interface a chance to initialize 725df8bae1dSRodney W. Grimes * if this is its first address, 726df8bae1dSRodney W. Grimes * and to validate the address if necessary. 727df8bae1dSRodney W. Grimes */ 728ba5da2a0SIan Dowse if (ifp->if_ioctl) { 729ba5da2a0SIan Dowse IFF_LOCKGIANT(ifp); 730ba5da2a0SIan Dowse error = (*ifp->if_ioctl)(ifp, SIOCSIFADDR, (caddr_t)ia); 731ba5da2a0SIan Dowse IFF_UNLOCKGIANT(ifp); 732ba5da2a0SIan Dowse if (error) { 733df8bae1dSRodney W. Grimes splx(s); 7342754d95dSSUZUKI Shinsuke /* LIST_REMOVE(ia, ia_hash) is done in in_control */ 735df8bae1dSRodney W. Grimes ia->ia_addr = oldaddr; 73622c819a7SJonathan Lemon if (ia->ia_addr.sin_family == AF_INET) 737ba5da2a0SIan Dowse LIST_INSERT_HEAD(INADDR_HASH( 738ba5da2a0SIan Dowse ia->ia_addr.sin_addr.s_addr), ia, ia_hash); 7392754d95dSSUZUKI Shinsuke return (error); 7402754d95dSSUZUKI Shinsuke } 741ba5da2a0SIan Dowse } 742df8bae1dSRodney W. Grimes splx(s); 743df8bae1dSRodney W. Grimes if (scrub) { 744df8bae1dSRodney W. Grimes ia->ia_ifa.ifa_addr = (struct sockaddr *)&oldaddr; 745df8bae1dSRodney W. Grimes in_ifscrub(ifp, ia); 746df8bae1dSRodney W. Grimes ia->ia_ifa.ifa_addr = (struct sockaddr *)&ia->ia_addr; 747df8bae1dSRodney W. Grimes } 748df8bae1dSRodney W. Grimes if (IN_CLASSA(i)) 749df8bae1dSRodney W. Grimes ia->ia_netmask = IN_CLASSA_NET; 750df8bae1dSRodney W. Grimes else if (IN_CLASSB(i)) 751df8bae1dSRodney W. Grimes ia->ia_netmask = IN_CLASSB_NET; 752df8bae1dSRodney W. Grimes else 753df8bae1dSRodney W. Grimes ia->ia_netmask = IN_CLASSC_NET; 754df8bae1dSRodney W. Grimes /* 755df8bae1dSRodney W. Grimes * The subnet mask usually includes at least the standard network part, 756df8bae1dSRodney W. Grimes * but may may be smaller in the case of supernetting. 757df8bae1dSRodney W. Grimes * If it is set, we believe it. 758df8bae1dSRodney W. Grimes */ 759df8bae1dSRodney W. Grimes if (ia->ia_subnetmask == 0) { 760df8bae1dSRodney W. Grimes ia->ia_subnetmask = ia->ia_netmask; 761df8bae1dSRodney W. Grimes ia->ia_sockmask.sin_addr.s_addr = htonl(ia->ia_subnetmask); 762df8bae1dSRodney W. Grimes } else 763df8bae1dSRodney W. Grimes ia->ia_netmask &= ia->ia_subnetmask; 764df8bae1dSRodney W. Grimes ia->ia_net = i & ia->ia_netmask; 765df8bae1dSRodney W. Grimes ia->ia_subnet = i & ia->ia_subnetmask; 766df8bae1dSRodney W. Grimes in_socktrim(&ia->ia_sockmask); 76750bb1704SGleb Smirnoff #ifdef DEV_CARP 76850bb1704SGleb Smirnoff /* 76950bb1704SGleb Smirnoff * XXX: carp(4) does not have interface route 77050bb1704SGleb Smirnoff */ 77150bb1704SGleb Smirnoff if (ifp->if_type == IFT_CARP) 77250bb1704SGleb Smirnoff return (0); 77350bb1704SGleb Smirnoff #endif 774df8bae1dSRodney W. Grimes /* 775df8bae1dSRodney W. Grimes * Add route for the network. 776df8bae1dSRodney W. Grimes */ 777df8bae1dSRodney W. Grimes ia->ia_ifa.ifa_metric = ifp->if_metric; 778df8bae1dSRodney W. Grimes if (ifp->if_flags & IFF_BROADCAST) { 779df8bae1dSRodney W. Grimes ia->ia_broadaddr.sin_addr.s_addr = 780df8bae1dSRodney W. Grimes htonl(ia->ia_subnet | ~ia->ia_subnetmask); 781df8bae1dSRodney W. Grimes ia->ia_netbroadcast.s_addr = 782df8bae1dSRodney W. Grimes htonl(ia->ia_net | ~ ia->ia_netmask); 783df8bae1dSRodney W. Grimes } else if (ifp->if_flags & IFF_LOOPBACK) { 7849a6a6eebSMax Laier ia->ia_dstaddr = ia->ia_addr; 785df8bae1dSRodney W. Grimes flags |= RTF_HOST; 786df8bae1dSRodney W. Grimes } else if (ifp->if_flags & IFF_POINTOPOINT) { 787df8bae1dSRodney W. Grimes if (ia->ia_dstaddr.sin_family != AF_INET) 788df8bae1dSRodney W. Grimes return (0); 789df8bae1dSRodney W. Grimes flags |= RTF_HOST; 790df8bae1dSRodney W. Grimes } 79148321abeSMax Laier if ((error = in_addprefix(ia, flags)) != 0) 7920f02fdacSBrian Somers return (error); 7930f02fdacSBrian Somers 794df8bae1dSRodney W. Grimes return (error); 795df8bae1dSRodney W. Grimes } 796df8bae1dSRodney W. Grimes 79748321abeSMax Laier #define rtinitflags(x) \ 79848321abeSMax Laier ((((x)->ia_ifp->if_flags & (IFF_LOOPBACK | IFF_POINTOPOINT)) != 0) \ 79948321abeSMax Laier ? RTF_HOST : 0) 80048321abeSMax Laier /* 80148321abeSMax Laier * Check if we have a route for the given prefix already or add a one 80248321abeSMax Laier * accordingly. 80348321abeSMax Laier */ 80448321abeSMax Laier static int 805f2565d68SRobert Watson in_addprefix(struct in_ifaddr *target, int flags) 80648321abeSMax Laier { 80748321abeSMax Laier struct in_ifaddr *ia; 808bfb26eecSGleb Smirnoff struct in_addr prefix, mask, p, m; 80948321abeSMax Laier int error; 81048321abeSMax Laier 81148321abeSMax Laier if ((flags & RTF_HOST) != 0) 81248321abeSMax Laier prefix = target->ia_dstaddr.sin_addr; 81348321abeSMax Laier else { 81448321abeSMax Laier prefix = target->ia_addr.sin_addr; 81548321abeSMax Laier mask = target->ia_sockmask.sin_addr; 81648321abeSMax Laier prefix.s_addr &= mask.s_addr; 81748321abeSMax Laier } 81848321abeSMax Laier 81948321abeSMax Laier TAILQ_FOREACH(ia, &in_ifaddrhead, ia_link) { 820bfb26eecSGleb Smirnoff if (rtinitflags(ia)) { 82148321abeSMax Laier p = ia->ia_addr.sin_addr; 82248321abeSMax Laier 82348321abeSMax Laier if (prefix.s_addr != p.s_addr) 82448321abeSMax Laier continue; 825bfb26eecSGleb Smirnoff } else { 826bfb26eecSGleb Smirnoff p = ia->ia_addr.sin_addr; 827bfb26eecSGleb Smirnoff m = ia->ia_sockmask.sin_addr; 828bfb26eecSGleb Smirnoff p.s_addr &= m.s_addr; 829bfb26eecSGleb Smirnoff 830bfb26eecSGleb Smirnoff if (prefix.s_addr != p.s_addr || 831bfb26eecSGleb Smirnoff mask.s_addr != m.s_addr) 832bfb26eecSGleb Smirnoff continue; 833bfb26eecSGleb Smirnoff } 83448321abeSMax Laier 83548321abeSMax Laier /* 83648321abeSMax Laier * If we got a matching prefix route inserted by other 83748321abeSMax Laier * interface address, we are done here. 83848321abeSMax Laier */ 8391ae95409SGleb Smirnoff if (ia->ia_flags & IFA_ROUTE) { 8401ae95409SGleb Smirnoff if (sameprefixcarponly && 8411ae95409SGleb Smirnoff target->ia_ifp->if_type != IFT_CARP && 8421ae95409SGleb Smirnoff ia->ia_ifp->if_type != IFT_CARP) 8431ae95409SGleb Smirnoff return (EEXIST); 8441ae95409SGleb Smirnoff else 8451ae95409SGleb Smirnoff return (0); 8461ae95409SGleb Smirnoff } 84748321abeSMax Laier } 84848321abeSMax Laier 84948321abeSMax Laier /* 85048321abeSMax Laier * No-one seem to have this prefix route, so we try to insert it. 85148321abeSMax Laier */ 85248321abeSMax Laier error = rtinit(&target->ia_ifa, (int)RTM_ADD, flags); 85348321abeSMax Laier if (!error) 85448321abeSMax Laier target->ia_flags |= IFA_ROUTE; 85548321abeSMax Laier return error; 85648321abeSMax Laier } 85748321abeSMax Laier 85848321abeSMax Laier /* 85948321abeSMax Laier * If there is no other address in the system that can serve a route to the 86048321abeSMax Laier * same prefix, remove the route. Hand over the route to the new address 86148321abeSMax Laier * otherwise. 86248321abeSMax Laier */ 86348321abeSMax Laier static int 864f2565d68SRobert Watson in_scrubprefix(struct in_ifaddr *target) 86548321abeSMax Laier { 86648321abeSMax Laier struct in_ifaddr *ia; 86748321abeSMax Laier struct in_addr prefix, mask, p; 86848321abeSMax Laier int error; 86948321abeSMax Laier 87048321abeSMax Laier if ((target->ia_flags & IFA_ROUTE) == 0) 87148321abeSMax Laier return 0; 87248321abeSMax Laier 87348321abeSMax Laier if (rtinitflags(target)) 87448321abeSMax Laier prefix = target->ia_dstaddr.sin_addr; 87548321abeSMax Laier else { 87648321abeSMax Laier prefix = target->ia_addr.sin_addr; 87748321abeSMax Laier mask = target->ia_sockmask.sin_addr; 87848321abeSMax Laier prefix.s_addr &= mask.s_addr; 87948321abeSMax Laier } 88048321abeSMax Laier 88148321abeSMax Laier TAILQ_FOREACH(ia, &in_ifaddrhead, ia_link) { 88248321abeSMax Laier if (rtinitflags(ia)) 88348321abeSMax Laier p = ia->ia_dstaddr.sin_addr; 88448321abeSMax Laier else { 88548321abeSMax Laier p = ia->ia_addr.sin_addr; 88648321abeSMax Laier p.s_addr &= ia->ia_sockmask.sin_addr.s_addr; 88748321abeSMax Laier } 88848321abeSMax Laier 88948321abeSMax Laier if (prefix.s_addr != p.s_addr) 89048321abeSMax Laier continue; 89148321abeSMax Laier 89248321abeSMax Laier /* 89348321abeSMax Laier * If we got a matching prefix address, move IFA_ROUTE and 89448321abeSMax Laier * the route itself to it. Make sure that routing daemons 89548321abeSMax Laier * get a heads-up. 89650bb1704SGleb Smirnoff * 89750bb1704SGleb Smirnoff * XXX: a special case for carp(4) interface 89848321abeSMax Laier */ 89950bb1704SGleb Smirnoff if ((ia->ia_flags & IFA_ROUTE) == 0 90050bb1704SGleb Smirnoff #ifdef DEV_CARP 90150bb1704SGleb Smirnoff && (ia->ia_ifp->if_type != IFT_CARP) 90250bb1704SGleb Smirnoff #endif 90350bb1704SGleb Smirnoff ) { 90448321abeSMax Laier rtinit(&(target->ia_ifa), (int)RTM_DELETE, 90548321abeSMax Laier rtinitflags(target)); 90648321abeSMax Laier target->ia_flags &= ~IFA_ROUTE; 90748321abeSMax Laier 90848321abeSMax Laier error = rtinit(&ia->ia_ifa, (int)RTM_ADD, 90948321abeSMax Laier rtinitflags(ia) | RTF_UP); 91048321abeSMax Laier if (error == 0) 91148321abeSMax Laier ia->ia_flags |= IFA_ROUTE; 91248321abeSMax Laier return error; 91348321abeSMax Laier } 91448321abeSMax Laier } 91548321abeSMax Laier 91648321abeSMax Laier /* 91748321abeSMax Laier * As no-one seem to have this prefix, we can remove the route. 91848321abeSMax Laier */ 91948321abeSMax Laier rtinit(&(target->ia_ifa), (int)RTM_DELETE, rtinitflags(target)); 92048321abeSMax Laier target->ia_flags &= ~IFA_ROUTE; 92148321abeSMax Laier return 0; 92248321abeSMax Laier } 92348321abeSMax Laier 92448321abeSMax Laier #undef rtinitflags 925df8bae1dSRodney W. Grimes 926df8bae1dSRodney W. Grimes /* 927df8bae1dSRodney W. Grimes * Return 1 if the address might be a local broadcast address. 928df8bae1dSRodney W. Grimes */ 92926f9a767SRodney W. Grimes int 930f2565d68SRobert Watson in_broadcast(struct in_addr in, struct ifnet *ifp) 931df8bae1dSRodney W. Grimes { 932df8bae1dSRodney W. Grimes register struct ifaddr *ifa; 933df8bae1dSRodney W. Grimes u_long t; 934df8bae1dSRodney W. Grimes 935df8bae1dSRodney W. Grimes if (in.s_addr == INADDR_BROADCAST || 936df8bae1dSRodney W. Grimes in.s_addr == INADDR_ANY) 937df8bae1dSRodney W. Grimes return 1; 938df8bae1dSRodney W. Grimes if ((ifp->if_flags & IFF_BROADCAST) == 0) 939df8bae1dSRodney W. Grimes return 0; 940df8bae1dSRodney W. Grimes t = ntohl(in.s_addr); 941df8bae1dSRodney W. Grimes /* 942df8bae1dSRodney W. Grimes * Look through the list of addresses for a match 943df8bae1dSRodney W. Grimes * with a broadcast address. 944df8bae1dSRodney W. Grimes */ 945df8bae1dSRodney W. Grimes #define ia ((struct in_ifaddr *)ifa) 946462b86feSPoul-Henning Kamp TAILQ_FOREACH(ifa, &ifp->if_addrhead, ifa_link) 947df8bae1dSRodney W. Grimes if (ifa->ifa_addr->sa_family == AF_INET && 948df8bae1dSRodney W. Grimes (in.s_addr == ia->ia_broadaddr.sin_addr.s_addr || 949df8bae1dSRodney W. Grimes in.s_addr == ia->ia_netbroadcast.s_addr || 950df8bae1dSRodney W. Grimes /* 951df8bae1dSRodney W. Grimes * Check for old-style (host 0) broadcast. 952df8bae1dSRodney W. Grimes */ 9538dd27fd6SGuido van Rooij t == ia->ia_subnet || t == ia->ia_net) && 9548dd27fd6SGuido van Rooij /* 9558dd27fd6SGuido van Rooij * Check for an all one subnetmask. These 9568dd27fd6SGuido van Rooij * only exist when an interface gets a secondary 9578dd27fd6SGuido van Rooij * address. 9588dd27fd6SGuido van Rooij */ 9598dd27fd6SGuido van Rooij ia->ia_subnetmask != (u_long)0xffffffff) 960df8bae1dSRodney W. Grimes return 1; 961df8bae1dSRodney W. Grimes return (0); 962df8bae1dSRodney W. Grimes #undef ia 963df8bae1dSRodney W. Grimes } 964ec002feeSBruce M Simpson 965df8bae1dSRodney W. Grimes /* 966ec002feeSBruce M Simpson * Delete all IPv4 multicast address records, and associated link-layer 967ec002feeSBruce M Simpson * multicast address records, associated with ifp. 968d9668414SBruce M Simpson */ 969ec002feeSBruce M Simpson static void 970ec002feeSBruce M Simpson in_purgemaddrs(struct ifnet *ifp) 971d9668414SBruce M Simpson { 972d9668414SBruce M Simpson struct in_multi *inm; 973d9668414SBruce M Simpson struct in_multi *oinm; 974d9668414SBruce M Simpson 975f7e083afSBruce M Simpson #ifdef DIAGNOSTIC 976f7e083afSBruce M Simpson printf("%s: purging ifp %p\n", __func__, ifp); 977f7e083afSBruce M Simpson #endif 978d9668414SBruce M Simpson IFF_LOCKGIANT(ifp); 979d9668414SBruce M Simpson IN_MULTI_LOCK(); 980d9668414SBruce M Simpson LIST_FOREACH_SAFE(inm, &in_multihead, inm_link, oinm) { 981d9668414SBruce M Simpson if (inm->inm_ifp == ifp) 982d9668414SBruce M Simpson in_delmulti_locked(inm); 983d9668414SBruce M Simpson } 984dd5a318bSRobert Watson IN_MULTI_UNLOCK(); 985c48b03fbSRobert Watson IFF_UNLOCKGIANT(ifp); 986df8bae1dSRodney W. Grimes } 987b1c53bc9SRobert Watson 988b1c53bc9SRobert Watson /* 989b1c53bc9SRobert Watson * On interface removal, clean up IPv4 data structures hung off of the ifnet. 990b1c53bc9SRobert Watson */ 991b1c53bc9SRobert Watson void 992f2565d68SRobert Watson in_ifdetach(struct ifnet *ifp) 993b1c53bc9SRobert Watson { 994b1c53bc9SRobert Watson 995b1c53bc9SRobert Watson in_pcbpurgeif0(&ripcbinfo, ifp); 996b1c53bc9SRobert Watson in_pcbpurgeif0(&udbinfo, ifp); 997ec002feeSBruce M Simpson in_purgemaddrs(ifp); 998b1c53bc9SRobert Watson } 999