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 31df8bae1dSRodney W. Grimes */ 32df8bae1dSRodney W. Grimes 334b421e2dSMike Silbersack #include <sys/cdefs.h> 344b421e2dSMike Silbersack __FBSDID("$FreeBSD$"); 354b421e2dSMike Silbersack 3650bb1704SGleb Smirnoff #include "opt_carp.h" 3750bb1704SGleb Smirnoff 38df8bae1dSRodney W. Grimes #include <sys/param.h> 3926f9a767SRodney W. Grimes #include <sys/systm.h> 4051a53488SBruce Evans #include <sys/sockio.h> 41df8bae1dSRodney W. Grimes #include <sys/malloc.h> 42acd3428bSRobert Watson #include <sys/priv.h> 43df8bae1dSRodney W. Grimes #include <sys/socket.h> 44f6d24a78SPoul-Henning Kamp #include <sys/kernel.h> 45f6d24a78SPoul-Henning Kamp #include <sys/sysctl.h> 46df8bae1dSRodney W. Grimes 47df8bae1dSRodney W. Grimes #include <net/if.h> 486a800098SYoshinobu Inoue #include <net/if_types.h> 49df8bae1dSRodney W. Grimes #include <net/route.h> 50df8bae1dSRodney W. Grimes 51df8bae1dSRodney W. Grimes #include <netinet/in.h> 52df8bae1dSRodney W. Grimes #include <netinet/in_var.h> 53e43cc4aeSHajimu UMEMOTO #include <netinet/in_pcb.h> 5471498f30SBruce M Simpson #include <netinet/ip_var.h> 5555166637SPoul-Henning Kamp 564d77a549SAlfred Perlstein static int in_mask2len(struct in_addr *); 574d77a549SAlfred Perlstein static void in_len2mask(struct in_addr *, int); 584d77a549SAlfred Perlstein static int in_lifaddr_ioctl(struct socket *, u_long, caddr_t, 594d77a549SAlfred Perlstein struct ifnet *, struct thread *); 606a800098SYoshinobu Inoue 6148321abeSMax Laier static int in_addprefix(struct in_ifaddr *, int); 6248321abeSMax Laier static int in_scrubprefix(struct in_ifaddr *); 634d77a549SAlfred Perlstein static void in_socktrim(struct sockaddr_in *); 644d77a549SAlfred Perlstein static int in_ifinit(struct ifnet *, 654d77a549SAlfred Perlstein struct in_ifaddr *, struct sockaddr_in *, int); 66ec002feeSBruce M Simpson static void in_purgemaddrs(struct ifnet *); 67df8bae1dSRodney W. Grimes 68f8731310SGarrett Wollman static int subnetsarelocal = 0; 69f6d24a78SPoul-Henning Kamp SYSCTL_INT(_net_inet_ip, OID_AUTO, subnets_are_local, CTLFLAG_RW, 703b95e134SRuslan Ermilov &subnetsarelocal, 0, "Treat all subnets as directly connected"); 711ae95409SGleb Smirnoff static int sameprefixcarponly = 0; 721ae95409SGleb Smirnoff SYSCTL_INT(_net_inet_ip, OID_AUTO, same_prefix_carp_only, CTLFLAG_RW, 731ae95409SGleb Smirnoff &sameprefixcarponly, 0, 741ae95409SGleb Smirnoff "Refuse to create same prefixes on different interfaces"); 75477180fbSGarrett Wollman 76e43cc4aeSHajimu UMEMOTO extern struct inpcbinfo ripcbinfo; 77e43cc4aeSHajimu UMEMOTO extern struct inpcbinfo udbinfo; 78e43cc4aeSHajimu UMEMOTO 79df8bae1dSRodney W. Grimes /* 80df8bae1dSRodney W. Grimes * Return 1 if an internet address is for a ``local'' host 81df8bae1dSRodney W. Grimes * (one to which we have a connection). If subnetsarelocal 82df8bae1dSRodney W. Grimes * is true, this includes other subnets of the local net. 83df8bae1dSRodney W. Grimes * Otherwise, it includes only the directly-connected (sub)nets. 84df8bae1dSRodney W. Grimes */ 8526f9a767SRodney W. Grimes int 86f2565d68SRobert Watson in_localaddr(struct in_addr in) 87df8bae1dSRodney W. Grimes { 88df8bae1dSRodney W. Grimes register u_long i = ntohl(in.s_addr); 89df8bae1dSRodney W. Grimes register struct in_ifaddr *ia; 90df8bae1dSRodney W. Grimes 91df8bae1dSRodney W. Grimes if (subnetsarelocal) { 92462b86feSPoul-Henning Kamp TAILQ_FOREACH(ia, &in_ifaddrhead, ia_link) 93df8bae1dSRodney W. Grimes if ((i & ia->ia_netmask) == ia->ia_net) 94df8bae1dSRodney W. Grimes return (1); 95df8bae1dSRodney W. Grimes } else { 9637d40066SPoul-Henning Kamp TAILQ_FOREACH(ia, &in_ifaddrhead, ia_link) 97df8bae1dSRodney W. Grimes if ((i & ia->ia_subnetmask) == ia->ia_subnet) 98df8bae1dSRodney W. Grimes return (1); 99df8bae1dSRodney W. Grimes } 100df8bae1dSRodney W. Grimes return (0); 101df8bae1dSRodney W. Grimes } 102df8bae1dSRodney W. Grimes 103df8bae1dSRodney W. Grimes /* 1042eccc90bSAndre Oppermann * Return 1 if an internet address is for the local host and configured 1052eccc90bSAndre Oppermann * on one of its interfaces. 1062eccc90bSAndre Oppermann */ 1072eccc90bSAndre Oppermann int 108f2565d68SRobert Watson in_localip(struct in_addr in) 1092eccc90bSAndre Oppermann { 1102eccc90bSAndre Oppermann struct in_ifaddr *ia; 1112eccc90bSAndre Oppermann 1122eccc90bSAndre Oppermann LIST_FOREACH(ia, INADDR_HASH(in.s_addr), ia_hash) { 1132eccc90bSAndre Oppermann if (IA_SIN(ia)->sin_addr.s_addr == in.s_addr) 1142eccc90bSAndre Oppermann return 1; 1152eccc90bSAndre Oppermann } 1162eccc90bSAndre Oppermann return 0; 1172eccc90bSAndre Oppermann } 1182eccc90bSAndre Oppermann 1192eccc90bSAndre Oppermann /* 120df8bae1dSRodney W. Grimes * Determine whether an IP address is in a reserved set of addresses 121df8bae1dSRodney W. Grimes * that may not be forwarded, or whether datagrams to that destination 122df8bae1dSRodney W. Grimes * may be forwarded. 123df8bae1dSRodney W. Grimes */ 12426f9a767SRodney W. Grimes int 125f2565d68SRobert Watson in_canforward(struct in_addr in) 126df8bae1dSRodney W. Grimes { 127df8bae1dSRodney W. Grimes register u_long i = ntohl(in.s_addr); 128df8bae1dSRodney W. Grimes register u_long net; 129df8bae1dSRodney W. Grimes 130f8429ca2SBruce M Simpson if (IN_EXPERIMENTAL(i) || IN_MULTICAST(i) || IN_LINKLOCAL(i)) 131df8bae1dSRodney W. Grimes return (0); 132df8bae1dSRodney W. Grimes if (IN_CLASSA(i)) { 133df8bae1dSRodney W. Grimes net = i & IN_CLASSA_NET; 134df8bae1dSRodney W. Grimes if (net == 0 || net == (IN_LOOPBACKNET << IN_CLASSA_NSHIFT)) 135df8bae1dSRodney W. Grimes return (0); 136df8bae1dSRodney W. Grimes } 137df8bae1dSRodney W. Grimes return (1); 138df8bae1dSRodney W. Grimes } 139df8bae1dSRodney W. Grimes 140df8bae1dSRodney W. Grimes /* 141df8bae1dSRodney W. Grimes * Trim a mask in a sockaddr 142df8bae1dSRodney W. Grimes */ 1430312fbe9SPoul-Henning Kamp static void 144f2565d68SRobert Watson in_socktrim(struct sockaddr_in *ap) 145df8bae1dSRodney W. Grimes { 146df8bae1dSRodney W. Grimes register char *cplim = (char *) &ap->sin_addr; 147df8bae1dSRodney W. Grimes register char *cp = (char *) (&ap->sin_addr + 1); 148df8bae1dSRodney W. Grimes 149df8bae1dSRodney W. Grimes ap->sin_len = 0; 150df00058dSGarrett Wollman while (--cp >= cplim) 151df8bae1dSRodney W. Grimes if (*cp) { 152df8bae1dSRodney W. Grimes (ap)->sin_len = cp - (char *) (ap) + 1; 153df8bae1dSRodney W. Grimes break; 154df8bae1dSRodney W. Grimes } 155df8bae1dSRodney W. Grimes } 156df8bae1dSRodney W. Grimes 1576a800098SYoshinobu Inoue static int 1586a800098SYoshinobu Inoue in_mask2len(mask) 1596a800098SYoshinobu Inoue struct in_addr *mask; 1606a800098SYoshinobu Inoue { 1616a800098SYoshinobu Inoue int x, y; 1626a800098SYoshinobu Inoue u_char *p; 1636a800098SYoshinobu Inoue 1646a800098SYoshinobu Inoue p = (u_char *)mask; 1656a800098SYoshinobu Inoue for (x = 0; x < sizeof(*mask); x++) { 1666a800098SYoshinobu Inoue if (p[x] != 0xff) 1676a800098SYoshinobu Inoue break; 1686a800098SYoshinobu Inoue } 1696a800098SYoshinobu Inoue y = 0; 1706a800098SYoshinobu Inoue if (x < sizeof(*mask)) { 1716a800098SYoshinobu Inoue for (y = 0; y < 8; y++) { 1726a800098SYoshinobu Inoue if ((p[x] & (0x80 >> y)) == 0) 1736a800098SYoshinobu Inoue break; 1746a800098SYoshinobu Inoue } 1756a800098SYoshinobu Inoue } 1766a800098SYoshinobu Inoue return x * 8 + y; 1776a800098SYoshinobu Inoue } 1786a800098SYoshinobu Inoue 1796a800098SYoshinobu Inoue static void 180f2565d68SRobert Watson in_len2mask(struct in_addr *mask, int len) 1816a800098SYoshinobu Inoue { 1826a800098SYoshinobu Inoue int i; 1836a800098SYoshinobu Inoue u_char *p; 1846a800098SYoshinobu Inoue 1856a800098SYoshinobu Inoue p = (u_char *)mask; 1866a800098SYoshinobu Inoue bzero(mask, sizeof(*mask)); 1876a800098SYoshinobu Inoue for (i = 0; i < len / 8; i++) 1886a800098SYoshinobu Inoue p[i] = 0xff; 1896a800098SYoshinobu Inoue if (len % 8) 1906a800098SYoshinobu Inoue p[i] = (0xff00 >> (len % 8)) & 0xff; 1916a800098SYoshinobu Inoue } 1926a800098SYoshinobu Inoue 193df8bae1dSRodney W. Grimes /* 194df8bae1dSRodney W. Grimes * Generic internet control operations (ioctl's). 195df8bae1dSRodney W. Grimes * Ifp is 0 if not an interface-specific ioctl. 196df8bae1dSRodney W. Grimes */ 197df8bae1dSRodney W. Grimes /* ARGSUSED */ 19826f9a767SRodney W. Grimes int 199f2565d68SRobert Watson in_control(struct socket *so, u_long cmd, caddr_t data, struct ifnet *ifp, 200f2565d68SRobert Watson struct thread *td) 201df8bae1dSRodney W. Grimes { 202df8bae1dSRodney W. Grimes register struct ifreq *ifr = (struct ifreq *)data; 203ac0aa473SBill Fenner register struct in_ifaddr *ia = 0, *iap; 204df8bae1dSRodney W. Grimes register struct ifaddr *ifa; 205f7e083afSBruce M Simpson struct in_addr allhosts_addr; 206ca925d9cSJonathan Lemon struct in_addr dst; 207df8bae1dSRodney W. Grimes struct in_ifaddr *oia; 208df8bae1dSRodney W. Grimes struct in_aliasreq *ifra = (struct in_aliasreq *)data; 209df8bae1dSRodney W. Grimes struct sockaddr_in oldaddr; 2100f02fdacSBrian Somers int error, hostIsNew, iaIsNew, maskIsNew, s; 211f7e083afSBruce M Simpson int iaIsFirst; 2120f02fdacSBrian Somers 213f7e083afSBruce M Simpson iaIsFirst = 0; 2140f02fdacSBrian Somers iaIsNew = 0; 215f7e083afSBruce M Simpson allhosts_addr.s_addr = htonl(INADDR_ALLHOSTS_GROUP); 216df8bae1dSRodney W. Grimes 2176a800098SYoshinobu Inoue switch (cmd) { 2186a800098SYoshinobu Inoue case SIOCALIFADDR: 219acd3428bSRobert Watson if (td != NULL) { 220acd3428bSRobert Watson error = priv_check(td, PRIV_NET_ADDIFADDR); 221acd3428bSRobert Watson if (error) 222acd3428bSRobert Watson return (error); 223acd3428bSRobert Watson } 224acd3428bSRobert Watson if (!ifp) 225acd3428bSRobert Watson return EINVAL; 226acd3428bSRobert Watson return in_lifaddr_ioctl(so, cmd, data, ifp, td); 227acd3428bSRobert Watson 2286a800098SYoshinobu Inoue case SIOCDLIFADDR: 229acd3428bSRobert Watson if (td != NULL) { 230acd3428bSRobert Watson error = priv_check(td, PRIV_NET_DELIFADDR); 231acd3428bSRobert Watson if (error) 232acd3428bSRobert Watson return (error); 233acd3428bSRobert Watson } 234acd3428bSRobert Watson if (!ifp) 235acd3428bSRobert Watson return EINVAL; 236acd3428bSRobert Watson return in_lifaddr_ioctl(so, cmd, data, ifp, td); 237acd3428bSRobert Watson 2386a800098SYoshinobu Inoue case SIOCGLIFADDR: 2396a800098SYoshinobu Inoue if (!ifp) 2406a800098SYoshinobu Inoue return EINVAL; 241b40ce416SJulian Elischer return in_lifaddr_ioctl(so, cmd, data, ifp, td); 2426a800098SYoshinobu Inoue } 2436a800098SYoshinobu Inoue 244df8bae1dSRodney W. Grimes /* 245df8bae1dSRodney W. Grimes * Find address for this interface, if it exists. 246ac0aa473SBill Fenner * 247ac0aa473SBill Fenner * If an alias address was specified, find that one instead of 248ca925d9cSJonathan Lemon * the first one on the interface, if possible. 249df8bae1dSRodney W. Grimes */ 250ca925d9cSJonathan Lemon if (ifp) { 251ca925d9cSJonathan Lemon dst = ((struct sockaddr_in *)&ifr->ifr_addr)->sin_addr; 252ca925d9cSJonathan Lemon LIST_FOREACH(iap, INADDR_HASH(dst.s_addr), ia_hash) 253ca925d9cSJonathan Lemon if (iap->ia_ifp == ifp && 254ca925d9cSJonathan Lemon iap->ia_addr.sin_addr.s_addr == dst.s_addr) { 255ac0aa473SBill Fenner ia = iap; 256df8bae1dSRodney W. Grimes break; 257ca925d9cSJonathan Lemon } 258ca925d9cSJonathan Lemon if (ia == NULL) 259ca925d9cSJonathan Lemon TAILQ_FOREACH(ifa, &ifp->if_addrhead, ifa_link) { 260ca925d9cSJonathan Lemon iap = ifatoia(ifa); 261ca925d9cSJonathan Lemon if (iap->ia_addr.sin_family == AF_INET) { 262ac0aa473SBill Fenner ia = iap; 263ac0aa473SBill Fenner break; 264ac0aa473SBill Fenner } 265ac0aa473SBill Fenner } 266f7e083afSBruce M Simpson if (ia == NULL) 267f7e083afSBruce M Simpson iaIsFirst = 1; 268ca925d9cSJonathan Lemon } 269df8bae1dSRodney W. Grimes 270df8bae1dSRodney W. Grimes switch (cmd) { 271df8bae1dSRodney W. Grimes 272df8bae1dSRodney W. Grimes case SIOCAIFADDR: 273df8bae1dSRodney W. Grimes case SIOCDIFADDR: 2746572231dSEivind Eklund if (ifp == 0) 2756572231dSEivind Eklund return (EADDRNOTAVAIL); 2761067217dSGarrett Wollman if (ifra->ifra_addr.sin_family == AF_INET) { 277fc2ffbe6SPoul-Henning Kamp for (oia = ia; ia; ia = TAILQ_NEXT(ia, ia_link)) { 278df8bae1dSRodney W. Grimes if (ia->ia_ifp == ifp && 279df8bae1dSRodney W. Grimes ia->ia_addr.sin_addr.s_addr == 280df8bae1dSRodney W. Grimes ifra->ifra_addr.sin_addr.s_addr) 281df8bae1dSRodney W. Grimes break; 282df8bae1dSRodney W. Grimes } 2831067217dSGarrett Wollman if ((ifp->if_flags & IFF_POINTOPOINT) 2841067217dSGarrett Wollman && (cmd == SIOCAIFADDR) 2851067217dSGarrett Wollman && (ifra->ifra_dstaddr.sin_addr.s_addr 2861067217dSGarrett Wollman == INADDR_ANY)) { 287357b78a9SGarrett Wollman return EDESTADDRREQ; 2881067217dSGarrett Wollman } 2891067217dSGarrett Wollman } 290df8bae1dSRodney W. Grimes if (cmd == SIOCDIFADDR && ia == 0) 291df8bae1dSRodney W. Grimes return (EADDRNOTAVAIL); 292df8bae1dSRodney W. Grimes /* FALLTHROUGH */ 293df8bae1dSRodney W. Grimes case SIOCSIFADDR: 294df8bae1dSRodney W. Grimes case SIOCSIFNETMASK: 295df8bae1dSRodney W. Grimes case SIOCSIFDSTADDR: 296acd3428bSRobert Watson if (td != NULL) { 297acd3428bSRobert Watson error = priv_check(td, PRIV_NET_ADDIFADDR); 298acd3428bSRobert Watson if (error) 299acd3428bSRobert Watson return (error); 300acd3428bSRobert Watson } 301df8bae1dSRodney W. Grimes 302df8bae1dSRodney W. Grimes if (ifp == 0) 3036572231dSEivind Eklund return (EADDRNOTAVAIL); 304df8bae1dSRodney W. Grimes if (ia == (struct in_ifaddr *)0) { 30559562606SGarrett Wollman ia = (struct in_ifaddr *) 306a163d034SWarner Losh malloc(sizeof *ia, M_IFADDR, M_WAITOK | M_ZERO); 30759562606SGarrett Wollman if (ia == (struct in_ifaddr *)NULL) 308df8bae1dSRodney W. Grimes return (ENOBUFS); 309c655b7c4SDavid Greenman /* 310c655b7c4SDavid Greenman * Protect from ipintr() traversing address list 311c655b7c4SDavid Greenman * while we're modifying it. 312c655b7c4SDavid Greenman */ 313c655b7c4SDavid Greenman s = splnet(); 31419fc74fbSJeffrey Hsu ifa = &ia->ia_ifa; 31519fc74fbSJeffrey Hsu IFA_LOCK_INIT(ifa); 31659562606SGarrett Wollman ifa->ifa_addr = (struct sockaddr *)&ia->ia_addr; 31759562606SGarrett Wollman ifa->ifa_dstaddr = (struct sockaddr *)&ia->ia_dstaddr; 31859562606SGarrett Wollman ifa->ifa_netmask = (struct sockaddr *)&ia->ia_sockmask; 31919fc74fbSJeffrey Hsu ifa->ifa_refcnt = 1; 32019fc74fbSJeffrey Hsu TAILQ_INSERT_TAIL(&ifp->if_addrhead, ifa, ifa_link); 32119fc74fbSJeffrey Hsu 322df8bae1dSRodney W. Grimes ia->ia_sockmask.sin_len = 8; 323bc183b3fSDag-Erling Smørgrav ia->ia_sockmask.sin_family = AF_INET; 324df8bae1dSRodney W. Grimes if (ifp->if_flags & IFF_BROADCAST) { 325df8bae1dSRodney W. Grimes ia->ia_broadaddr.sin_len = sizeof(ia->ia_addr); 326df8bae1dSRodney W. Grimes ia->ia_broadaddr.sin_family = AF_INET; 327df8bae1dSRodney W. Grimes } 328df8bae1dSRodney W. Grimes ia->ia_ifp = ifp; 329f3d30eb2SGleb Smirnoff 330f3d30eb2SGleb Smirnoff TAILQ_INSERT_TAIL(&in_ifaddrhead, ia, ia_link); 331c655b7c4SDavid Greenman splx(s); 3320f02fdacSBrian Somers iaIsNew = 1; 333df8bae1dSRodney W. Grimes } 334df8bae1dSRodney W. Grimes break; 335df8bae1dSRodney W. Grimes 336df8bae1dSRodney W. Grimes case SIOCSIFBRDADDR: 337acd3428bSRobert Watson if (td != NULL) { 338acd3428bSRobert Watson error = priv_check(td, PRIV_NET_ADDIFADDR); 339acd3428bSRobert Watson if (error) 340acd3428bSRobert Watson return (error); 341acd3428bSRobert Watson } 342df8bae1dSRodney W. Grimes /* FALLTHROUGH */ 343df8bae1dSRodney W. Grimes 344df8bae1dSRodney W. Grimes case SIOCGIFADDR: 345df8bae1dSRodney W. Grimes case SIOCGIFNETMASK: 346df8bae1dSRodney W. Grimes case SIOCGIFDSTADDR: 347df8bae1dSRodney W. Grimes case SIOCGIFBRDADDR: 348df8bae1dSRodney W. Grimes if (ia == (struct in_ifaddr *)0) 349df8bae1dSRodney W. Grimes return (EADDRNOTAVAIL); 350df8bae1dSRodney W. Grimes break; 351df8bae1dSRodney W. Grimes } 352df8bae1dSRodney W. Grimes switch (cmd) { 353df8bae1dSRodney W. Grimes 354df8bae1dSRodney W. Grimes case SIOCGIFADDR: 355df8bae1dSRodney W. Grimes *((struct sockaddr_in *)&ifr->ifr_addr) = ia->ia_addr; 3560f02fdacSBrian Somers return (0); 357df8bae1dSRodney W. Grimes 358df8bae1dSRodney W. Grimes case SIOCGIFBRDADDR: 359df8bae1dSRodney W. Grimes if ((ifp->if_flags & IFF_BROADCAST) == 0) 360df8bae1dSRodney W. Grimes return (EINVAL); 361df8bae1dSRodney W. Grimes *((struct sockaddr_in *)&ifr->ifr_dstaddr) = ia->ia_broadaddr; 3620f02fdacSBrian Somers return (0); 363df8bae1dSRodney W. Grimes 364df8bae1dSRodney W. Grimes case SIOCGIFDSTADDR: 365df8bae1dSRodney W. Grimes if ((ifp->if_flags & IFF_POINTOPOINT) == 0) 366df8bae1dSRodney W. Grimes return (EINVAL); 367df8bae1dSRodney W. Grimes *((struct sockaddr_in *)&ifr->ifr_dstaddr) = ia->ia_dstaddr; 3680f02fdacSBrian Somers return (0); 369df8bae1dSRodney W. Grimes 370df8bae1dSRodney W. Grimes case SIOCGIFNETMASK: 371df8bae1dSRodney W. Grimes *((struct sockaddr_in *)&ifr->ifr_addr) = ia->ia_sockmask; 3720f02fdacSBrian Somers return (0); 373df8bae1dSRodney W. Grimes 374df8bae1dSRodney W. Grimes case SIOCSIFDSTADDR: 375df8bae1dSRodney W. Grimes if ((ifp->if_flags & IFF_POINTOPOINT) == 0) 376df8bae1dSRodney W. Grimes return (EINVAL); 377df8bae1dSRodney W. Grimes oldaddr = ia->ia_dstaddr; 378df8bae1dSRodney W. Grimes ia->ia_dstaddr = *(struct sockaddr_in *)&ifr->ifr_dstaddr; 379ba5da2a0SIan Dowse if (ifp->if_ioctl) { 380ba5da2a0SIan Dowse IFF_LOCKGIANT(ifp); 381ba5da2a0SIan Dowse error = (*ifp->if_ioctl)(ifp, SIOCSIFDSTADDR, 382ba5da2a0SIan Dowse (caddr_t)ia); 383ba5da2a0SIan Dowse IFF_UNLOCKGIANT(ifp); 384ba5da2a0SIan Dowse if (error) { 385df8bae1dSRodney W. Grimes ia->ia_dstaddr = oldaddr; 386df8bae1dSRodney W. Grimes return (error); 387df8bae1dSRodney W. Grimes } 388ba5da2a0SIan Dowse } 389df8bae1dSRodney W. Grimes if (ia->ia_flags & IFA_ROUTE) { 390df8bae1dSRodney W. Grimes ia->ia_ifa.ifa_dstaddr = (struct sockaddr *)&oldaddr; 391df8bae1dSRodney W. Grimes rtinit(&(ia->ia_ifa), (int)RTM_DELETE, RTF_HOST); 392df8bae1dSRodney W. Grimes ia->ia_ifa.ifa_dstaddr = 393df8bae1dSRodney W. Grimes (struct sockaddr *)&ia->ia_dstaddr; 394df8bae1dSRodney W. Grimes rtinit(&(ia->ia_ifa), (int)RTM_ADD, RTF_HOST|RTF_UP); 395df8bae1dSRodney W. Grimes } 3960f02fdacSBrian Somers return (0); 397df8bae1dSRodney W. Grimes 398df8bae1dSRodney W. Grimes case SIOCSIFBRDADDR: 399df8bae1dSRodney W. Grimes if ((ifp->if_flags & IFF_BROADCAST) == 0) 400df8bae1dSRodney W. Grimes return (EINVAL); 401df8bae1dSRodney W. Grimes ia->ia_broadaddr = *(struct sockaddr_in *)&ifr->ifr_broadaddr; 4020f02fdacSBrian Somers return (0); 403df8bae1dSRodney W. Grimes 404df8bae1dSRodney W. Grimes case SIOCSIFADDR: 4050f02fdacSBrian Somers error = in_ifinit(ifp, ia, 4060f02fdacSBrian Somers (struct sockaddr_in *) &ifr->ifr_addr, 1); 4070f02fdacSBrian Somers if (error != 0 && iaIsNew) 4080f02fdacSBrian Somers break; 409f7e083afSBruce M Simpson if (error == 0) { 410f7e083afSBruce M Simpson if (iaIsFirst && (ifp->if_flags & IFF_MULTICAST) != 0) 411f7e083afSBruce M Simpson in_addmulti(&allhosts_addr, ifp); 41225a4adceSMax Laier EVENTHANDLER_INVOKE(ifaddr_event, ifp); 413f7e083afSBruce M Simpson } 4140f02fdacSBrian Somers return (0); 415df8bae1dSRodney W. Grimes 416df8bae1dSRodney W. Grimes case SIOCSIFNETMASK: 417bc183b3fSDag-Erling Smørgrav ia->ia_sockmask.sin_addr = ifra->ifra_addr.sin_addr; 418bc183b3fSDag-Erling Smørgrav ia->ia_subnetmask = ntohl(ia->ia_sockmask.sin_addr.s_addr); 4190f02fdacSBrian Somers return (0); 420df8bae1dSRodney W. Grimes 421df8bae1dSRodney W. Grimes case SIOCAIFADDR: 422df8bae1dSRodney W. Grimes maskIsNew = 0; 423df8bae1dSRodney W. Grimes hostIsNew = 1; 424df8bae1dSRodney W. Grimes error = 0; 425df8bae1dSRodney W. Grimes if (ia->ia_addr.sin_family == AF_INET) { 426df8bae1dSRodney W. Grimes if (ifra->ifra_addr.sin_len == 0) { 427df8bae1dSRodney W. Grimes ifra->ifra_addr = ia->ia_addr; 428df8bae1dSRodney W. Grimes hostIsNew = 0; 429df8bae1dSRodney W. Grimes } else if (ifra->ifra_addr.sin_addr.s_addr == 430df8bae1dSRodney W. Grimes ia->ia_addr.sin_addr.s_addr) 431df8bae1dSRodney W. Grimes hostIsNew = 0; 432df8bae1dSRodney W. Grimes } 433df8bae1dSRodney W. Grimes if (ifra->ifra_mask.sin_len) { 434df8bae1dSRodney W. Grimes in_ifscrub(ifp, ia); 435df8bae1dSRodney W. Grimes ia->ia_sockmask = ifra->ifra_mask; 436bc183b3fSDag-Erling Smørgrav ia->ia_sockmask.sin_family = AF_INET; 437df8bae1dSRodney W. Grimes ia->ia_subnetmask = 438df8bae1dSRodney W. Grimes ntohl(ia->ia_sockmask.sin_addr.s_addr); 439df8bae1dSRodney W. Grimes maskIsNew = 1; 440df8bae1dSRodney W. Grimes } 441df8bae1dSRodney W. Grimes if ((ifp->if_flags & IFF_POINTOPOINT) && 442df8bae1dSRodney W. Grimes (ifra->ifra_dstaddr.sin_family == AF_INET)) { 443df8bae1dSRodney W. Grimes in_ifscrub(ifp, ia); 444df8bae1dSRodney W. Grimes ia->ia_dstaddr = ifra->ifra_dstaddr; 445df8bae1dSRodney W. Grimes maskIsNew = 1; /* We lie; but the effect's the same */ 446df8bae1dSRodney W. Grimes } 447df8bae1dSRodney W. Grimes if (ifra->ifra_addr.sin_family == AF_INET && 448df8bae1dSRodney W. Grimes (hostIsNew || maskIsNew)) 449df8bae1dSRodney W. Grimes error = in_ifinit(ifp, ia, &ifra->ifra_addr, 0); 4500f02fdacSBrian Somers if (error != 0 && iaIsNew) 4510f02fdacSBrian Somers break; 4520f02fdacSBrian Somers 453df8bae1dSRodney W. Grimes if ((ifp->if_flags & IFF_BROADCAST) && 454df8bae1dSRodney W. Grimes (ifra->ifra_broadaddr.sin_family == AF_INET)) 455df8bae1dSRodney W. Grimes ia->ia_broadaddr = ifra->ifra_broadaddr; 456f7e083afSBruce M Simpson if (error == 0) { 457f7e083afSBruce M Simpson if (iaIsFirst && (ifp->if_flags & IFF_MULTICAST) != 0) 458f7e083afSBruce M Simpson in_addmulti(&allhosts_addr, ifp); 45925a4adceSMax Laier EVENTHANDLER_INVOKE(ifaddr_event, ifp); 460f7e083afSBruce M Simpson } 461df8bae1dSRodney W. Grimes return (error); 462df8bae1dSRodney W. Grimes 463df8bae1dSRodney W. Grimes case SIOCDIFADDR: 464089cdfadSRuslan Ermilov /* 465089cdfadSRuslan Ermilov * in_ifscrub kills the interface route. 466089cdfadSRuslan Ermilov */ 467df8bae1dSRodney W. Grimes in_ifscrub(ifp, ia); 468c655b7c4SDavid Greenman /* 469089cdfadSRuslan Ermilov * in_ifadown gets rid of all the rest of 470089cdfadSRuslan Ermilov * the routes. This is not quite the right 471089cdfadSRuslan Ermilov * thing to do, but at least if we are running 472089cdfadSRuslan Ermilov * a routing process they will come back. 473089cdfadSRuslan Ermilov */ 47491854268SRuslan Ermilov in_ifadown(&ia->ia_ifa, 1); 47525a4adceSMax Laier EVENTHANDLER_INVOKE(ifaddr_event, ifp); 4760f02fdacSBrian Somers error = 0; 477df8bae1dSRodney W. Grimes break; 478df8bae1dSRodney W. Grimes 479df8bae1dSRodney W. Grimes default: 480df8bae1dSRodney W. Grimes if (ifp == 0 || ifp->if_ioctl == 0) 481df8bae1dSRodney W. Grimes return (EOPNOTSUPP); 482ba5da2a0SIan Dowse IFF_LOCKGIANT(ifp); 483ba5da2a0SIan Dowse error = (*ifp->if_ioctl)(ifp, cmd, data); 484ba5da2a0SIan Dowse IFF_UNLOCKGIANT(ifp); 485ba5da2a0SIan Dowse return (error); 486df8bae1dSRodney W. Grimes } 4870f02fdacSBrian Somers 4880f02fdacSBrian Somers /* 4890f02fdacSBrian Somers * Protect from ipintr() traversing address list while we're modifying 4900f02fdacSBrian Somers * it. 4910f02fdacSBrian Somers */ 4920f02fdacSBrian Somers s = splnet(); 4930f02fdacSBrian Somers TAILQ_REMOVE(&ifp->if_addrhead, &ia->ia_ifa, ifa_link); 4940f02fdacSBrian Somers TAILQ_REMOVE(&in_ifaddrhead, ia, ia_link); 495f7e083afSBruce M Simpson if (ia->ia_addr.sin_family == AF_INET) { 4960f02fdacSBrian Somers LIST_REMOVE(ia, ia_hash); 497f7e083afSBruce M Simpson /* 498f7e083afSBruce M Simpson * If this is the last IPv4 address configured on this 499f7e083afSBruce M Simpson * interface, leave the all-hosts group. 500f7e083afSBruce M Simpson * XXX: This is quite ugly because of locking and structure. 501f7e083afSBruce M Simpson */ 502f7e083afSBruce M Simpson oia = NULL; 503f7e083afSBruce M Simpson IFP_TO_IA(ifp, oia); 504f7e083afSBruce M Simpson if (oia == NULL) { 505f7e083afSBruce M Simpson struct in_multi *inm; 506f7e083afSBruce M Simpson 507f7e083afSBruce M Simpson IFF_LOCKGIANT(ifp); 508f7e083afSBruce M Simpson IN_MULTI_LOCK(); 509f7e083afSBruce M Simpson IN_LOOKUP_MULTI(allhosts_addr, ifp, inm); 510f7e083afSBruce M Simpson if (inm != NULL) 511f7e083afSBruce M Simpson in_delmulti_locked(inm); 512f7e083afSBruce M Simpson IN_MULTI_UNLOCK(); 513f7e083afSBruce M Simpson IFF_UNLOCKGIANT(ifp); 514f7e083afSBruce M Simpson } 515f7e083afSBruce M Simpson } 5160f02fdacSBrian Somers IFAFREE(&ia->ia_ifa); 5170f02fdacSBrian Somers splx(s); 5180f02fdacSBrian Somers 5190f02fdacSBrian Somers return (error); 520df8bae1dSRodney W. Grimes } 521df8bae1dSRodney W. Grimes 522df8bae1dSRodney W. Grimes /* 5236a800098SYoshinobu Inoue * SIOC[GAD]LIFADDR. 5246a800098SYoshinobu Inoue * SIOCGLIFADDR: get first address. (?!?) 5256a800098SYoshinobu Inoue * SIOCGLIFADDR with IFLR_PREFIX: 5266a800098SYoshinobu Inoue * get first address that matches the specified prefix. 5276a800098SYoshinobu Inoue * SIOCALIFADDR: add the specified address. 5286a800098SYoshinobu Inoue * SIOCALIFADDR with IFLR_PREFIX: 5296a800098SYoshinobu Inoue * EINVAL since we can't deduce hostid part of the address. 5306a800098SYoshinobu Inoue * SIOCDLIFADDR: delete the specified address. 5316a800098SYoshinobu Inoue * SIOCDLIFADDR with IFLR_PREFIX: 5326a800098SYoshinobu Inoue * delete the first address that matches the specified prefix. 5336a800098SYoshinobu Inoue * return values: 5346a800098SYoshinobu Inoue * EINVAL on invalid parameters 5356a800098SYoshinobu Inoue * EADDRNOTAVAIL on prefix match failed/specified address not found 5366a800098SYoshinobu Inoue * other values may be returned from in_ioctl() 5376a800098SYoshinobu Inoue */ 5386a800098SYoshinobu Inoue static int 539f2565d68SRobert Watson in_lifaddr_ioctl(struct socket *so, u_long cmd, caddr_t data, 540f2565d68SRobert Watson struct ifnet *ifp, struct thread *td) 5416a800098SYoshinobu Inoue { 5426a800098SYoshinobu Inoue struct if_laddrreq *iflr = (struct if_laddrreq *)data; 5436a800098SYoshinobu Inoue struct ifaddr *ifa; 5446a800098SYoshinobu Inoue 5456a800098SYoshinobu Inoue /* sanity checks */ 5466a800098SYoshinobu Inoue if (!data || !ifp) { 5476a800098SYoshinobu Inoue panic("invalid argument to in_lifaddr_ioctl"); 5486a800098SYoshinobu Inoue /*NOTRECHED*/ 5496a800098SYoshinobu Inoue } 5506a800098SYoshinobu Inoue 5516a800098SYoshinobu Inoue switch (cmd) { 5526a800098SYoshinobu Inoue case SIOCGLIFADDR: 5536a800098SYoshinobu Inoue /* address must be specified on GET with IFLR_PREFIX */ 5546a800098SYoshinobu Inoue if ((iflr->flags & IFLR_PREFIX) == 0) 5556a800098SYoshinobu Inoue break; 5566a800098SYoshinobu Inoue /*FALLTHROUGH*/ 5576a800098SYoshinobu Inoue case SIOCALIFADDR: 5586a800098SYoshinobu Inoue case SIOCDLIFADDR: 5596a800098SYoshinobu Inoue /* address must be specified on ADD and DELETE */ 5605d60ed0eSYoshinobu Inoue if (iflr->addr.ss_family != AF_INET) 5616a800098SYoshinobu Inoue return EINVAL; 5625d60ed0eSYoshinobu Inoue if (iflr->addr.ss_len != sizeof(struct sockaddr_in)) 5636a800098SYoshinobu Inoue return EINVAL; 5646a800098SYoshinobu Inoue /* XXX need improvement */ 5655d60ed0eSYoshinobu Inoue if (iflr->dstaddr.ss_family 5665d60ed0eSYoshinobu Inoue && iflr->dstaddr.ss_family != AF_INET) 5676a800098SYoshinobu Inoue return EINVAL; 5685d60ed0eSYoshinobu Inoue if (iflr->dstaddr.ss_family 5695d60ed0eSYoshinobu Inoue && iflr->dstaddr.ss_len != sizeof(struct sockaddr_in)) 5706a800098SYoshinobu Inoue return EINVAL; 5716a800098SYoshinobu Inoue break; 5726a800098SYoshinobu Inoue default: /*shouldn't happen*/ 5736a800098SYoshinobu Inoue return EOPNOTSUPP; 5746a800098SYoshinobu Inoue } 5756a800098SYoshinobu Inoue if (sizeof(struct in_addr) * 8 < iflr->prefixlen) 5766a800098SYoshinobu Inoue return EINVAL; 5776a800098SYoshinobu Inoue 5786a800098SYoshinobu Inoue switch (cmd) { 5796a800098SYoshinobu Inoue case SIOCALIFADDR: 5806a800098SYoshinobu Inoue { 5816a800098SYoshinobu Inoue struct in_aliasreq ifra; 5826a800098SYoshinobu Inoue 5836a800098SYoshinobu Inoue if (iflr->flags & IFLR_PREFIX) 5846a800098SYoshinobu Inoue return EINVAL; 5856a800098SYoshinobu Inoue 5866a800098SYoshinobu Inoue /* copy args to in_aliasreq, perform ioctl(SIOCAIFADDR_IN6). */ 5876a800098SYoshinobu Inoue bzero(&ifra, sizeof(ifra)); 5886a800098SYoshinobu Inoue bcopy(iflr->iflr_name, ifra.ifra_name, 5896a800098SYoshinobu Inoue sizeof(ifra.ifra_name)); 5906a800098SYoshinobu Inoue 5915d60ed0eSYoshinobu Inoue bcopy(&iflr->addr, &ifra.ifra_addr, iflr->addr.ss_len); 5926a800098SYoshinobu Inoue 5935d60ed0eSYoshinobu Inoue if (iflr->dstaddr.ss_family) { /*XXX*/ 5946a800098SYoshinobu Inoue bcopy(&iflr->dstaddr, &ifra.ifra_dstaddr, 5955d60ed0eSYoshinobu Inoue iflr->dstaddr.ss_len); 5966a800098SYoshinobu Inoue } 5976a800098SYoshinobu Inoue 5986a800098SYoshinobu Inoue ifra.ifra_mask.sin_family = AF_INET; 5996a800098SYoshinobu Inoue ifra.ifra_mask.sin_len = sizeof(struct sockaddr_in); 6006a800098SYoshinobu Inoue in_len2mask(&ifra.ifra_mask.sin_addr, iflr->prefixlen); 6016a800098SYoshinobu Inoue 602b40ce416SJulian Elischer return in_control(so, SIOCAIFADDR, (caddr_t)&ifra, ifp, td); 6036a800098SYoshinobu Inoue } 6046a800098SYoshinobu Inoue case SIOCGLIFADDR: 6056a800098SYoshinobu Inoue case SIOCDLIFADDR: 6066a800098SYoshinobu Inoue { 6076a800098SYoshinobu Inoue struct in_ifaddr *ia; 6086a800098SYoshinobu Inoue struct in_addr mask, candidate, match; 6096a800098SYoshinobu Inoue struct sockaddr_in *sin; 6106a800098SYoshinobu Inoue 6116a800098SYoshinobu Inoue bzero(&mask, sizeof(mask)); 612fbdd20a1SMatt Jacob bzero(&match, sizeof(match)); 6136a800098SYoshinobu Inoue if (iflr->flags & IFLR_PREFIX) { 6146a800098SYoshinobu Inoue /* lookup a prefix rather than address. */ 6156a800098SYoshinobu Inoue in_len2mask(&mask, iflr->prefixlen); 6166a800098SYoshinobu Inoue 6176a800098SYoshinobu Inoue sin = (struct sockaddr_in *)&iflr->addr; 6186a800098SYoshinobu Inoue match.s_addr = sin->sin_addr.s_addr; 6196a800098SYoshinobu Inoue match.s_addr &= mask.s_addr; 6206a800098SYoshinobu Inoue 6216a800098SYoshinobu Inoue /* if you set extra bits, that's wrong */ 6226a800098SYoshinobu Inoue if (match.s_addr != sin->sin_addr.s_addr) 6236a800098SYoshinobu Inoue return EINVAL; 6246a800098SYoshinobu Inoue 6256a800098SYoshinobu Inoue } else { 6266a800098SYoshinobu Inoue /* on getting an address, take the 1st match */ 6276a800098SYoshinobu Inoue /* on deleting an address, do exact match */ 628fbdd20a1SMatt Jacob if (cmd != SIOCGLIFADDR) { 6296a800098SYoshinobu Inoue in_len2mask(&mask, 32); 6306a800098SYoshinobu Inoue sin = (struct sockaddr_in *)&iflr->addr; 6316a800098SYoshinobu Inoue match.s_addr = sin->sin_addr.s_addr; 6326a800098SYoshinobu Inoue } 6336a800098SYoshinobu Inoue } 6346a800098SYoshinobu Inoue 6356a800098SYoshinobu Inoue TAILQ_FOREACH(ifa, &ifp->if_addrhead, ifa_link) { 6366a800098SYoshinobu Inoue if (ifa->ifa_addr->sa_family != AF_INET6) 6376a800098SYoshinobu Inoue continue; 638fbdd20a1SMatt Jacob if (match.s_addr == 0) 6396a800098SYoshinobu Inoue break; 6406a800098SYoshinobu Inoue candidate.s_addr = ((struct sockaddr_in *)&ifa->ifa_addr)->sin_addr.s_addr; 6416a800098SYoshinobu Inoue candidate.s_addr &= mask.s_addr; 6426a800098SYoshinobu Inoue if (candidate.s_addr == match.s_addr) 6436a800098SYoshinobu Inoue break; 6446a800098SYoshinobu Inoue } 6456a800098SYoshinobu Inoue if (!ifa) 6466a800098SYoshinobu Inoue return EADDRNOTAVAIL; 6476a800098SYoshinobu Inoue ia = (struct in_ifaddr *)ifa; 6486a800098SYoshinobu Inoue 6496a800098SYoshinobu Inoue if (cmd == SIOCGLIFADDR) { 6506a800098SYoshinobu Inoue /* fill in the if_laddrreq structure */ 6516a800098SYoshinobu Inoue bcopy(&ia->ia_addr, &iflr->addr, ia->ia_addr.sin_len); 6526a800098SYoshinobu Inoue 6536a800098SYoshinobu Inoue if ((ifp->if_flags & IFF_POINTOPOINT) != 0) { 6546a800098SYoshinobu Inoue bcopy(&ia->ia_dstaddr, &iflr->dstaddr, 6556a800098SYoshinobu Inoue ia->ia_dstaddr.sin_len); 6566a800098SYoshinobu Inoue } else 6576a800098SYoshinobu Inoue bzero(&iflr->dstaddr, sizeof(iflr->dstaddr)); 6586a800098SYoshinobu Inoue 6596a800098SYoshinobu Inoue iflr->prefixlen = 6606a800098SYoshinobu Inoue in_mask2len(&ia->ia_sockmask.sin_addr); 6616a800098SYoshinobu Inoue 6626a800098SYoshinobu Inoue iflr->flags = 0; /*XXX*/ 6636a800098SYoshinobu Inoue 6646a800098SYoshinobu Inoue return 0; 6656a800098SYoshinobu Inoue } else { 6666a800098SYoshinobu Inoue struct in_aliasreq ifra; 6676a800098SYoshinobu Inoue 6686a800098SYoshinobu Inoue /* fill in_aliasreq and do ioctl(SIOCDIFADDR_IN6) */ 6696a800098SYoshinobu Inoue bzero(&ifra, sizeof(ifra)); 6706a800098SYoshinobu Inoue bcopy(iflr->iflr_name, ifra.ifra_name, 6716a800098SYoshinobu Inoue sizeof(ifra.ifra_name)); 6726a800098SYoshinobu Inoue 6736a800098SYoshinobu Inoue bcopy(&ia->ia_addr, &ifra.ifra_addr, 6746a800098SYoshinobu Inoue ia->ia_addr.sin_len); 6756a800098SYoshinobu Inoue if ((ifp->if_flags & IFF_POINTOPOINT) != 0) { 6766a800098SYoshinobu Inoue bcopy(&ia->ia_dstaddr, &ifra.ifra_dstaddr, 6776a800098SYoshinobu Inoue ia->ia_dstaddr.sin_len); 6786a800098SYoshinobu Inoue } 6796a800098SYoshinobu Inoue bcopy(&ia->ia_sockmask, &ifra.ifra_dstaddr, 6806a800098SYoshinobu Inoue ia->ia_sockmask.sin_len); 6816a800098SYoshinobu Inoue 6826a800098SYoshinobu Inoue return in_control(so, SIOCDIFADDR, (caddr_t)&ifra, 683b40ce416SJulian Elischer ifp, td); 6846a800098SYoshinobu Inoue } 6856a800098SYoshinobu Inoue } 6866a800098SYoshinobu Inoue } 6876a800098SYoshinobu Inoue 6886a800098SYoshinobu Inoue return EOPNOTSUPP; /*just for safety*/ 6896a800098SYoshinobu Inoue } 6906a800098SYoshinobu Inoue 6916a800098SYoshinobu Inoue /* 692df8bae1dSRodney W. Grimes * Delete any existing route for an interface. 693df8bae1dSRodney W. Grimes */ 69439191c8eSGarrett Wollman void 695f2565d68SRobert Watson in_ifscrub(struct ifnet *ifp, struct in_ifaddr *ia) 696df8bae1dSRodney W. Grimes { 697f2565d68SRobert Watson 69848321abeSMax Laier in_scrubprefix(ia); 699df8bae1dSRodney W. Grimes } 700df8bae1dSRodney W. Grimes 701df8bae1dSRodney W. Grimes /* 702df8bae1dSRodney W. Grimes * Initialize an interface's internet address 703df8bae1dSRodney W. Grimes * and routing table entry. 704df8bae1dSRodney W. Grimes */ 7050312fbe9SPoul-Henning Kamp static int 706f2565d68SRobert Watson in_ifinit(struct ifnet *ifp, struct in_ifaddr *ia, struct sockaddr_in *sin, 707f2565d68SRobert Watson int scrub) 708df8bae1dSRodney W. Grimes { 709df8bae1dSRodney W. Grimes register u_long i = ntohl(sin->sin_addr.s_addr); 710df8bae1dSRodney W. Grimes struct sockaddr_in oldaddr; 7115a43847dSBrian Somers int s = splimp(), flags = RTF_UP, error = 0; 712df8bae1dSRodney W. Grimes 713df8bae1dSRodney W. Grimes oldaddr = ia->ia_addr; 7142754d95dSSUZUKI Shinsuke if (oldaddr.sin_family == AF_INET) 7152754d95dSSUZUKI Shinsuke LIST_REMOVE(ia, ia_hash); 716df8bae1dSRodney W. Grimes ia->ia_addr = *sin; 7172754d95dSSUZUKI Shinsuke if (ia->ia_addr.sin_family == AF_INET) 7182754d95dSSUZUKI Shinsuke LIST_INSERT_HEAD(INADDR_HASH(ia->ia_addr.sin_addr.s_addr), 7192754d95dSSUZUKI Shinsuke ia, ia_hash); 720df8bae1dSRodney W. Grimes /* 721df8bae1dSRodney W. Grimes * Give the interface a chance to initialize 722df8bae1dSRodney W. Grimes * if this is its first address, 723df8bae1dSRodney W. Grimes * and to validate the address if necessary. 724df8bae1dSRodney W. Grimes */ 725ba5da2a0SIan Dowse if (ifp->if_ioctl) { 726ba5da2a0SIan Dowse IFF_LOCKGIANT(ifp); 727ba5da2a0SIan Dowse error = (*ifp->if_ioctl)(ifp, SIOCSIFADDR, (caddr_t)ia); 728ba5da2a0SIan Dowse IFF_UNLOCKGIANT(ifp); 729ba5da2a0SIan Dowse if (error) { 730df8bae1dSRodney W. Grimes splx(s); 7312754d95dSSUZUKI Shinsuke /* LIST_REMOVE(ia, ia_hash) is done in in_control */ 732df8bae1dSRodney W. Grimes ia->ia_addr = oldaddr; 73322c819a7SJonathan Lemon if (ia->ia_addr.sin_family == AF_INET) 734ba5da2a0SIan Dowse LIST_INSERT_HEAD(INADDR_HASH( 735ba5da2a0SIan Dowse ia->ia_addr.sin_addr.s_addr), ia, ia_hash); 7362754d95dSSUZUKI Shinsuke return (error); 7372754d95dSSUZUKI Shinsuke } 738ba5da2a0SIan Dowse } 739df8bae1dSRodney W. Grimes splx(s); 740df8bae1dSRodney W. Grimes if (scrub) { 741df8bae1dSRodney W. Grimes ia->ia_ifa.ifa_addr = (struct sockaddr *)&oldaddr; 742df8bae1dSRodney W. Grimes in_ifscrub(ifp, ia); 743df8bae1dSRodney W. Grimes ia->ia_ifa.ifa_addr = (struct sockaddr *)&ia->ia_addr; 744df8bae1dSRodney W. Grimes } 745df8bae1dSRodney W. Grimes if (IN_CLASSA(i)) 746df8bae1dSRodney W. Grimes ia->ia_netmask = IN_CLASSA_NET; 747df8bae1dSRodney W. Grimes else if (IN_CLASSB(i)) 748df8bae1dSRodney W. Grimes ia->ia_netmask = IN_CLASSB_NET; 749df8bae1dSRodney W. Grimes else 750df8bae1dSRodney W. Grimes ia->ia_netmask = IN_CLASSC_NET; 751df8bae1dSRodney W. Grimes /* 752df8bae1dSRodney W. Grimes * The subnet mask usually includes at least the standard network part, 753df8bae1dSRodney W. Grimes * but may may be smaller in the case of supernetting. 754df8bae1dSRodney W. Grimes * If it is set, we believe it. 755df8bae1dSRodney W. Grimes */ 756df8bae1dSRodney W. Grimes if (ia->ia_subnetmask == 0) { 757df8bae1dSRodney W. Grimes ia->ia_subnetmask = ia->ia_netmask; 758df8bae1dSRodney W. Grimes ia->ia_sockmask.sin_addr.s_addr = htonl(ia->ia_subnetmask); 759df8bae1dSRodney W. Grimes } else 760df8bae1dSRodney W. Grimes ia->ia_netmask &= ia->ia_subnetmask; 761df8bae1dSRodney W. Grimes ia->ia_net = i & ia->ia_netmask; 762df8bae1dSRodney W. Grimes ia->ia_subnet = i & ia->ia_subnetmask; 763df8bae1dSRodney W. Grimes in_socktrim(&ia->ia_sockmask); 76450bb1704SGleb Smirnoff #ifdef DEV_CARP 76550bb1704SGleb Smirnoff /* 76650bb1704SGleb Smirnoff * XXX: carp(4) does not have interface route 76750bb1704SGleb Smirnoff */ 76850bb1704SGleb Smirnoff if (ifp->if_type == IFT_CARP) 76950bb1704SGleb Smirnoff return (0); 77050bb1704SGleb Smirnoff #endif 771df8bae1dSRodney W. Grimes /* 772df8bae1dSRodney W. Grimes * Add route for the network. 773df8bae1dSRodney W. Grimes */ 774df8bae1dSRodney W. Grimes ia->ia_ifa.ifa_metric = ifp->if_metric; 775df8bae1dSRodney W. Grimes if (ifp->if_flags & IFF_BROADCAST) { 776df8bae1dSRodney W. Grimes ia->ia_broadaddr.sin_addr.s_addr = 777df8bae1dSRodney W. Grimes htonl(ia->ia_subnet | ~ia->ia_subnetmask); 778df8bae1dSRodney W. Grimes ia->ia_netbroadcast.s_addr = 779df8bae1dSRodney W. Grimes htonl(ia->ia_net | ~ ia->ia_netmask); 780df8bae1dSRodney W. Grimes } else if (ifp->if_flags & IFF_LOOPBACK) { 7819a6a6eebSMax Laier ia->ia_dstaddr = ia->ia_addr; 782df8bae1dSRodney W. Grimes flags |= RTF_HOST; 783df8bae1dSRodney W. Grimes } else if (ifp->if_flags & IFF_POINTOPOINT) { 784df8bae1dSRodney W. Grimes if (ia->ia_dstaddr.sin_family != AF_INET) 785df8bae1dSRodney W. Grimes return (0); 786df8bae1dSRodney W. Grimes flags |= RTF_HOST; 787df8bae1dSRodney W. Grimes } 78848321abeSMax Laier if ((error = in_addprefix(ia, flags)) != 0) 7890f02fdacSBrian Somers return (error); 7900f02fdacSBrian Somers 791df8bae1dSRodney W. Grimes return (error); 792df8bae1dSRodney W. Grimes } 793df8bae1dSRodney W. Grimes 79448321abeSMax Laier #define rtinitflags(x) \ 79548321abeSMax Laier ((((x)->ia_ifp->if_flags & (IFF_LOOPBACK | IFF_POINTOPOINT)) != 0) \ 79648321abeSMax Laier ? RTF_HOST : 0) 79748321abeSMax Laier /* 798fbdd20a1SMatt Jacob * Check if we have a route for the given prefix already or add one accordingly. 79948321abeSMax Laier */ 80048321abeSMax Laier static int 801f2565d68SRobert Watson in_addprefix(struct in_ifaddr *target, int flags) 80248321abeSMax Laier { 80348321abeSMax Laier struct in_ifaddr *ia; 804bfb26eecSGleb Smirnoff struct in_addr prefix, mask, p, m; 80548321abeSMax Laier int error; 80648321abeSMax Laier 807fbdd20a1SMatt Jacob if ((flags & RTF_HOST) != 0) { 80848321abeSMax Laier prefix = target->ia_dstaddr.sin_addr; 809fbdd20a1SMatt Jacob mask.s_addr = 0; 810fbdd20a1SMatt Jacob } else { 81148321abeSMax Laier prefix = target->ia_addr.sin_addr; 81248321abeSMax Laier mask = target->ia_sockmask.sin_addr; 81348321abeSMax Laier prefix.s_addr &= mask.s_addr; 81448321abeSMax Laier } 81548321abeSMax Laier 81648321abeSMax Laier TAILQ_FOREACH(ia, &in_ifaddrhead, ia_link) { 817bfb26eecSGleb Smirnoff if (rtinitflags(ia)) { 81848321abeSMax Laier p = ia->ia_addr.sin_addr; 81948321abeSMax Laier 82048321abeSMax Laier if (prefix.s_addr != p.s_addr) 82148321abeSMax Laier continue; 822bfb26eecSGleb Smirnoff } else { 823bfb26eecSGleb Smirnoff p = ia->ia_addr.sin_addr; 824bfb26eecSGleb Smirnoff m = ia->ia_sockmask.sin_addr; 825bfb26eecSGleb Smirnoff p.s_addr &= m.s_addr; 826bfb26eecSGleb Smirnoff 827bfb26eecSGleb Smirnoff if (prefix.s_addr != p.s_addr || 828bfb26eecSGleb Smirnoff mask.s_addr != m.s_addr) 829bfb26eecSGleb Smirnoff continue; 830bfb26eecSGleb Smirnoff } 83148321abeSMax Laier 83248321abeSMax Laier /* 83348321abeSMax Laier * If we got a matching prefix route inserted by other 83448321abeSMax Laier * interface address, we are done here. 83548321abeSMax Laier */ 8361ae95409SGleb Smirnoff if (ia->ia_flags & IFA_ROUTE) { 8371ae95409SGleb Smirnoff if (sameprefixcarponly && 8381ae95409SGleb Smirnoff target->ia_ifp->if_type != IFT_CARP && 8391ae95409SGleb Smirnoff ia->ia_ifp->if_type != IFT_CARP) 8401ae95409SGleb Smirnoff return (EEXIST); 8411ae95409SGleb Smirnoff else 8421ae95409SGleb Smirnoff return (0); 8431ae95409SGleb Smirnoff } 84448321abeSMax Laier } 84548321abeSMax Laier 84648321abeSMax Laier /* 84748321abeSMax Laier * No-one seem to have this prefix route, so we try to insert it. 84848321abeSMax Laier */ 84948321abeSMax Laier error = rtinit(&target->ia_ifa, (int)RTM_ADD, flags); 85048321abeSMax Laier if (!error) 85148321abeSMax Laier target->ia_flags |= IFA_ROUTE; 85248321abeSMax Laier return error; 85348321abeSMax Laier } 85448321abeSMax Laier 85548321abeSMax Laier /* 85648321abeSMax Laier * If there is no other address in the system that can serve a route to the 85748321abeSMax Laier * same prefix, remove the route. Hand over the route to the new address 85848321abeSMax Laier * otherwise. 85948321abeSMax Laier */ 86048321abeSMax Laier static int 861f2565d68SRobert Watson in_scrubprefix(struct in_ifaddr *target) 86248321abeSMax Laier { 86348321abeSMax Laier struct in_ifaddr *ia; 86448321abeSMax Laier struct in_addr prefix, mask, p; 86548321abeSMax Laier int error; 86648321abeSMax Laier 86748321abeSMax Laier if ((target->ia_flags & IFA_ROUTE) == 0) 86848321abeSMax Laier return 0; 86948321abeSMax Laier 87048321abeSMax Laier if (rtinitflags(target)) 87148321abeSMax Laier prefix = target->ia_dstaddr.sin_addr; 87248321abeSMax Laier else { 87348321abeSMax Laier prefix = target->ia_addr.sin_addr; 87448321abeSMax Laier mask = target->ia_sockmask.sin_addr; 87548321abeSMax Laier prefix.s_addr &= mask.s_addr; 87648321abeSMax Laier } 87748321abeSMax Laier 87848321abeSMax Laier TAILQ_FOREACH(ia, &in_ifaddrhead, ia_link) { 87948321abeSMax Laier if (rtinitflags(ia)) 88048321abeSMax Laier p = ia->ia_dstaddr.sin_addr; 88148321abeSMax Laier else { 88248321abeSMax Laier p = ia->ia_addr.sin_addr; 88348321abeSMax Laier p.s_addr &= ia->ia_sockmask.sin_addr.s_addr; 88448321abeSMax Laier } 88548321abeSMax Laier 88648321abeSMax Laier if (prefix.s_addr != p.s_addr) 88748321abeSMax Laier continue; 88848321abeSMax Laier 88948321abeSMax Laier /* 89048321abeSMax Laier * If we got a matching prefix address, move IFA_ROUTE and 89148321abeSMax Laier * the route itself to it. Make sure that routing daemons 89248321abeSMax Laier * get a heads-up. 89350bb1704SGleb Smirnoff * 89450bb1704SGleb Smirnoff * XXX: a special case for carp(4) interface 89548321abeSMax Laier */ 89650bb1704SGleb Smirnoff if ((ia->ia_flags & IFA_ROUTE) == 0 89750bb1704SGleb Smirnoff #ifdef DEV_CARP 89850bb1704SGleb Smirnoff && (ia->ia_ifp->if_type != IFT_CARP) 89950bb1704SGleb Smirnoff #endif 90050bb1704SGleb Smirnoff ) { 90148321abeSMax Laier rtinit(&(target->ia_ifa), (int)RTM_DELETE, 90248321abeSMax Laier rtinitflags(target)); 90348321abeSMax Laier target->ia_flags &= ~IFA_ROUTE; 90448321abeSMax Laier 90548321abeSMax Laier error = rtinit(&ia->ia_ifa, (int)RTM_ADD, 90648321abeSMax Laier rtinitflags(ia) | RTF_UP); 90748321abeSMax Laier if (error == 0) 90848321abeSMax Laier ia->ia_flags |= IFA_ROUTE; 90948321abeSMax Laier return error; 91048321abeSMax Laier } 91148321abeSMax Laier } 91248321abeSMax Laier 91348321abeSMax Laier /* 91448321abeSMax Laier * As no-one seem to have this prefix, we can remove the route. 91548321abeSMax Laier */ 91648321abeSMax Laier rtinit(&(target->ia_ifa), (int)RTM_DELETE, rtinitflags(target)); 91748321abeSMax Laier target->ia_flags &= ~IFA_ROUTE; 91848321abeSMax Laier return 0; 91948321abeSMax Laier } 92048321abeSMax Laier 92148321abeSMax Laier #undef rtinitflags 922df8bae1dSRodney W. Grimes 923df8bae1dSRodney W. Grimes /* 924df8bae1dSRodney W. Grimes * Return 1 if the address might be a local broadcast address. 925df8bae1dSRodney W. Grimes */ 92626f9a767SRodney W. Grimes int 927f2565d68SRobert Watson in_broadcast(struct in_addr in, struct ifnet *ifp) 928df8bae1dSRodney W. Grimes { 929df8bae1dSRodney W. Grimes register struct ifaddr *ifa; 930df8bae1dSRodney W. Grimes u_long t; 931df8bae1dSRodney W. Grimes 932df8bae1dSRodney W. Grimes if (in.s_addr == INADDR_BROADCAST || 933df8bae1dSRodney W. Grimes in.s_addr == INADDR_ANY) 934df8bae1dSRodney W. Grimes return 1; 935df8bae1dSRodney W. Grimes if ((ifp->if_flags & IFF_BROADCAST) == 0) 936df8bae1dSRodney W. Grimes return 0; 937df8bae1dSRodney W. Grimes t = ntohl(in.s_addr); 938df8bae1dSRodney W. Grimes /* 939df8bae1dSRodney W. Grimes * Look through the list of addresses for a match 940df8bae1dSRodney W. Grimes * with a broadcast address. 941df8bae1dSRodney W. Grimes */ 942df8bae1dSRodney W. Grimes #define ia ((struct in_ifaddr *)ifa) 943462b86feSPoul-Henning Kamp TAILQ_FOREACH(ifa, &ifp->if_addrhead, ifa_link) 944df8bae1dSRodney W. Grimes if (ifa->ifa_addr->sa_family == AF_INET && 945df8bae1dSRodney W. Grimes (in.s_addr == ia->ia_broadaddr.sin_addr.s_addr || 946df8bae1dSRodney W. Grimes in.s_addr == ia->ia_netbroadcast.s_addr || 947df8bae1dSRodney W. Grimes /* 948df8bae1dSRodney W. Grimes * Check for old-style (host 0) broadcast. 949df8bae1dSRodney W. Grimes */ 9508dd27fd6SGuido van Rooij t == ia->ia_subnet || t == ia->ia_net) && 9518dd27fd6SGuido van Rooij /* 9528dd27fd6SGuido van Rooij * Check for an all one subnetmask. These 9538dd27fd6SGuido van Rooij * only exist when an interface gets a secondary 9548dd27fd6SGuido van Rooij * address. 9558dd27fd6SGuido van Rooij */ 9568dd27fd6SGuido van Rooij ia->ia_subnetmask != (u_long)0xffffffff) 957df8bae1dSRodney W. Grimes return 1; 958df8bae1dSRodney W. Grimes return (0); 959df8bae1dSRodney W. Grimes #undef ia 960df8bae1dSRodney W. Grimes } 961ec002feeSBruce M Simpson 962df8bae1dSRodney W. Grimes /* 963ec002feeSBruce M Simpson * Delete all IPv4 multicast address records, and associated link-layer 964ec002feeSBruce M Simpson * multicast address records, associated with ifp. 965d9668414SBruce M Simpson */ 966ec002feeSBruce M Simpson static void 967ec002feeSBruce M Simpson in_purgemaddrs(struct ifnet *ifp) 968d9668414SBruce M Simpson { 969d9668414SBruce M Simpson struct in_multi *inm; 970d9668414SBruce M Simpson struct in_multi *oinm; 971d9668414SBruce M Simpson 972f7e083afSBruce M Simpson #ifdef DIAGNOSTIC 973f7e083afSBruce M Simpson printf("%s: purging ifp %p\n", __func__, ifp); 974f7e083afSBruce M Simpson #endif 975d9668414SBruce M Simpson IFF_LOCKGIANT(ifp); 976d9668414SBruce M Simpson IN_MULTI_LOCK(); 977d9668414SBruce M Simpson LIST_FOREACH_SAFE(inm, &in_multihead, inm_link, oinm) { 978d9668414SBruce M Simpson if (inm->inm_ifp == ifp) 979d9668414SBruce M Simpson in_delmulti_locked(inm); 980d9668414SBruce M Simpson } 981dd5a318bSRobert Watson IN_MULTI_UNLOCK(); 982c48b03fbSRobert Watson IFF_UNLOCKGIANT(ifp); 983df8bae1dSRodney W. Grimes } 984b1c53bc9SRobert Watson 985b1c53bc9SRobert Watson /* 986b1c53bc9SRobert Watson * On interface removal, clean up IPv4 data structures hung off of the ifnet. 987b1c53bc9SRobert Watson */ 988b1c53bc9SRobert Watson void 989f2565d68SRobert Watson in_ifdetach(struct ifnet *ifp) 990b1c53bc9SRobert Watson { 991b1c53bc9SRobert Watson 992b1c53bc9SRobert Watson in_pcbpurgeif0(&ripcbinfo, ifp); 993b1c53bc9SRobert Watson in_pcbpurgeif0(&udbinfo, ifp); 994ec002feeSBruce M Simpson in_purgemaddrs(ifp); 995b1c53bc9SRobert Watson } 996