1/*- 2 * Copyright (c) 1984, 1985, 1986, 1987, 1993 3 * The Regents of the University of California. 4 * Copyright (c) 2009 Robert N. M. Watson 5 * All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 4. Neither the name of the University nor the names of its contributors 16 * may be used to endorse or promote products derived from this software 17 * without specific prior written permission. 18 * 19 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 20 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 21 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 22 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 23 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 24 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 25 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 26 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 27 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 28 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 29 * SUCH DAMAGE. 30 * 31 * Copyright (c) 1995, Mike Mitchell 32 * 33 * Redistribution and use in source and binary forms, with or without 34 * modification, are permitted provided that the following conditions 35 * are met: 36 * 1. Redistributions of source code must retain the above copyright 37 * notice, this list of conditions and the following disclaimer. 38 * 2. Redistributions in binary form must reproduce the above copyright 39 * notice, this list of conditions and the following disclaimer in the 40 * documentation and/or other materials provided with the distribution. 41 * 3. All advertising materials mentioning features or use of this software 42 * must display the following acknowledgement: 43 * This product includes software developed by the University of 44 * California, Berkeley and its contributors. 45 * 4. Neither the name of the University nor the names of its contributors 46 * may be used to endorse or promote products derived from this software 47 * without specific prior written permission. 48 * 49 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 50 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 51 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 52 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 53 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 54 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 55 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 56 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 57 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 58 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 59 * SUCH DAMAGE. 60 * 61 * @(#)ipx.c 62 */ 63 64#include <sys/cdefs.h> 65__FBSDID("$FreeBSD$"); 66 67#include <sys/param.h> 68#include <sys/kernel.h> 69#include <sys/systm.h> 70#include <sys/lock.h> 71#include <sys/malloc.h> 72#include <sys/priv.h> 73#include <sys/rwlock.h> 74#include <sys/sockio.h> 75#include <sys/socket.h> 76 77#include <net/if.h> 78#include <net/route.h> 79 80#include <netipx/ipx.h> 81#include <netipx/ipx_if.h> 82#include <netipx/ipx_var.h> 83 84/* 85 * The IPX-layer address list is protected by ipx_ifaddr_rw. 86 */ 87struct rwlock ipx_ifaddr_rw; 88struct ipx_ifaddrhead ipx_ifaddrhead; 89 90static void ipx_ifscrub(struct ifnet *ifp, struct ipx_ifaddr *ia); 91static int ipx_ifinit(struct ifnet *ifp, struct ipx_ifaddr *ia, 92 struct sockaddr_ipx *sipx, int scrub); 93 94/* 95 * Generic internet control operations (ioctl's). 96 */ 97int 98ipx_control(struct socket *so, u_long cmd, caddr_t data, struct ifnet *ifp, 99 struct thread *td) 100{ 101 struct ifreq *ifr = (struct ifreq *)data; 102 struct ipx_aliasreq *ifra = (struct ipx_aliasreq *)data; 103 struct ipx_ifaddr *ia; 104 struct ifaddr *ifa; 105 int dstIsNew, hostIsNew; 106 int error, priv; 107 108 /* 109 * Find address for this interface, if it exists. 110 */ 111 if (ifp == NULL) 112 return (EADDRNOTAVAIL); 113 114 IPX_IFADDR_RLOCK(); 115 TAILQ_FOREACH(ia, &ipx_ifaddrhead, ia_link) { 116 if (ia->ia_ifp == ifp) 117 break; 118 } 119 if (ia != NULL) 120 ifa_ref(&ia->ia_ifa); 121 IPX_IFADDR_RUNLOCK(); 122 123 error = 0; 124 switch (cmd) { 125 case SIOCGIFADDR: 126 if (ia == NULL) { 127 error = EADDRNOTAVAIL; 128 goto out; 129 } 130 *(struct sockaddr_ipx *)&ifr->ifr_addr = ia->ia_addr; 131 goto out; 132 133 case SIOCGIFBRDADDR: 134 if (ia == NULL) { 135 error = EADDRNOTAVAIL; 136 goto out; 137 } 138 if ((ifp->if_flags & IFF_BROADCAST) == 0) { 139 error = EINVAL; 140 goto out; 141 } 142 *(struct sockaddr_ipx *)&ifr->ifr_dstaddr = ia->ia_broadaddr; 143 goto out; 144 145 case SIOCGIFDSTADDR: 146 if (ia == NULL) { 147 error = EADDRNOTAVAIL; 148 goto out; 149 } 150 if ((ifp->if_flags & IFF_POINTOPOINT) == 0) { 151 error = EINVAL; 152 goto out; 153 } 154 *(struct sockaddr_ipx *)&ifr->ifr_dstaddr = ia->ia_dstaddr; 155 goto out; 156 } 157 158 switch (cmd) { 159 case SIOCAIFADDR: 160 case SIOCDIFADDR: 161 priv = (cmd == SIOCAIFADDR) ? PRIV_NET_ADDIFADDR : 162 PRIV_NET_DELIFADDR; 163 if (td && (error = priv_check(td, priv)) != 0) 164 goto out; 165 166 IPX_IFADDR_RLOCK(); 167 if (ifra->ifra_addr.sipx_family == AF_IPX) { 168 struct ipx_ifaddr *oia; 169 170 for (oia = ia; ia; ia = TAILQ_NEXT(ia, ia_link)) { 171 if (ia->ia_ifp == ifp && 172 ipx_neteq(ia->ia_addr.sipx_addr, 173 ifra->ifra_addr.sipx_addr)) 174 break; 175 } 176 if (oia != NULL && oia != ia) 177 ifa_free(&oia->ia_ifa); 178 if (ia != NULL && oia != ia) 179 ifa_ref(&ia->ia_ifa); 180 } 181 IPX_IFADDR_RUNLOCK(); 182 if (cmd == SIOCDIFADDR && ia == NULL) { 183 error = EADDRNOTAVAIL; 184 goto out; 185 } 186 /* FALLTHROUGH */ 187 188 case SIOCSIFADDR: 189 case SIOCSIFDSTADDR: 190 if (td && (error = priv_check(td, PRIV_NET_SETLLADDR)) != 0) 191 goto out; 192 if (ia == NULL) { 193 ia = malloc(sizeof(*ia), M_IFADDR, M_NOWAIT | M_ZERO); 194 if (ia == NULL) { 195 error = ENOBUFS; 196 goto out; 197 } 198 ifa = (struct ifaddr *)ia; 199 ifa_init(ifa); 200 ia->ia_ifp = ifp; 201 ifa->ifa_addr = (struct sockaddr *)&ia->ia_addr; 202 ifa->ifa_netmask = (struct sockaddr *)&ipx_netmask; 203 ifa->ifa_dstaddr = (struct sockaddr *)&ia->ia_dstaddr; 204 if (ifp->if_flags & IFF_BROADCAST) { 205 ia->ia_broadaddr.sipx_family = AF_IPX; 206 ia->ia_broadaddr.sipx_len = 207 sizeof(ia->ia_addr); 208 ia->ia_broadaddr.sipx_addr.x_host = 209 ipx_broadhost; 210 } 211 ifa_ref(&ia->ia_ifa); /* ipx_ifaddrhead */ 212 IPX_IFADDR_WLOCK(); 213 TAILQ_INSERT_TAIL(&ipx_ifaddrhead, ia, ia_link); 214 IPX_IFADDR_WUNLOCK(); 215 216 ifa_ref(&ia->ia_ifa); /* if_addrhead */ 217 IF_ADDR_WLOCK(ifp); 218 TAILQ_INSERT_TAIL(&ifp->if_addrhead, ifa, ifa_link); 219 IF_ADDR_WUNLOCK(ifp); 220 } 221 break; 222 223 default: 224 if (td && (error = priv_check(td, PRIV_NET_HWIOCTL)) != 0) 225 goto out; 226 } 227 228 switch (cmd) { 229 case SIOCSIFDSTADDR: 230 if ((ifp->if_flags & IFF_POINTOPOINT) == 0) { 231 error = EINVAL; 232 goto out; 233 } 234 if (ia->ia_flags & IFA_ROUTE) { 235 rtinit(&(ia->ia_ifa), (int)RTM_DELETE, RTF_HOST); 236 ia->ia_flags &= ~IFA_ROUTE; 237 } 238 if (ifp->if_ioctl) { 239 error = (*ifp->if_ioctl)(ifp, SIOCSIFDSTADDR, 240 (void *)ia); 241 if (error) 242 goto out; 243 } 244 *(struct sockaddr *)&ia->ia_dstaddr = ifr->ifr_dstaddr; 245 goto out; 246 247 case SIOCSIFADDR: 248 error = ipx_ifinit(ifp, ia, 249 (struct sockaddr_ipx *)&ifr->ifr_addr, 1); 250 goto out; 251 252 case SIOCDIFADDR: 253 ipx_ifscrub(ifp, ia); 254 ifa = (struct ifaddr *)ia; 255 256 IF_ADDR_WLOCK(ifp); 257 TAILQ_REMOVE(&ifp->if_addrhead, ifa, ifa_link); 258 IF_ADDR_WUNLOCK(ifp); 259 ifa_free(ifa); /* if_addrhead */ 260 261 IPX_IFADDR_WLOCK(); 262 TAILQ_REMOVE(&ipx_ifaddrhead, ia, ia_link); 263 IPX_IFADDR_WUNLOCK(); 264 ifa_free(&ia->ia_ifa); /* ipx_ifaddrhead */ 265 goto out; 266 267 case SIOCAIFADDR: 268 dstIsNew = 0; 269 hostIsNew = 1; 270 if (ia->ia_addr.sipx_family == AF_IPX) { 271 if (ifra->ifra_addr.sipx_len == 0) { 272 ifra->ifra_addr = ia->ia_addr; 273 hostIsNew = 0; 274 } else if (ipx_neteq(ifra->ifra_addr.sipx_addr, 275 ia->ia_addr.sipx_addr)) 276 hostIsNew = 0; 277 } 278 if ((ifp->if_flags & IFF_POINTOPOINT) && 279 (ifra->ifra_dstaddr.sipx_family == AF_IPX)) { 280 if (hostIsNew == 0) 281 ipx_ifscrub(ifp, ia); 282 ia->ia_dstaddr = ifra->ifra_dstaddr; 283 dstIsNew = 1; 284 } 285 if (ifra->ifra_addr.sipx_family == AF_IPX && 286 (hostIsNew || dstIsNew)) 287 error = ipx_ifinit(ifp, ia, &ifra->ifra_addr, 0); 288 goto out; 289 290 default: 291 if (ifp->if_ioctl == NULL) { 292 error = EOPNOTSUPP; 293 goto out; 294 } 295 error = ((*ifp->if_ioctl)(ifp, cmd, data)); 296 } 297 298out: 299 if (ia != NULL) 300 ifa_free(&ia->ia_ifa); 301 return (error); 302} 303 304/* 305 * Delete any previous route for an old address. 306 */ 307static void 308ipx_ifscrub(struct ifnet *ifp, struct ipx_ifaddr *ia) 309{ 310 311 if (ia->ia_flags & IFA_ROUTE) { 312 if (ifp->if_flags & IFF_POINTOPOINT) { 313 rtinit(&(ia->ia_ifa), (int)RTM_DELETE, RTF_HOST); 314 } else 315 rtinit(&(ia->ia_ifa), (int)RTM_DELETE, 0); 316 ia->ia_flags &= ~IFA_ROUTE; 317 } 318} 319 320/* 321 * Initialize an interface's internet address and routing table entry. 322 */ 323static int 324ipx_ifinit(struct ifnet *ifp, struct ipx_ifaddr *ia, 325 struct sockaddr_ipx *sipx, int scrub) 326{ 327 struct sockaddr_ipx oldaddr; 328 int s = splimp(), error; 329 330 /* 331 * Set up new addresses. 332 */ 333 oldaddr = ia->ia_addr; 334 ia->ia_addr = *sipx; 335 336 /* 337 * The convention we shall adopt for naming is that a supplied 338 * address of zero means that "we don't care". Use the MAC address 339 * of the interface. If it is an interface without a MAC address, 340 * like a serial line, the address must be supplied. 341 * 342 * Give the interface a chance to initialize if this is its first 343 * address, and to validate the address if necessary. 344 */ 345 if (ifp->if_ioctl != NULL && 346 (error = (*ifp->if_ioctl)(ifp, SIOCSIFADDR, (void *)ia))) { 347 ia->ia_addr = oldaddr; 348 splx(s); 349 return (error); 350 } 351 splx(s); 352 ia->ia_ifa.ifa_metric = ifp->if_metric; 353 354 /* 355 * Add route for the network. 356 */ 357 if (scrub) { 358 ia->ia_ifa.ifa_addr = (struct sockaddr *)&oldaddr; 359 ipx_ifscrub(ifp, ia); 360 ia->ia_ifa.ifa_addr = (struct sockaddr *)&ia->ia_addr; 361 } 362 if (ifp->if_flags & IFF_POINTOPOINT) 363 rtinit(&(ia->ia_ifa), (int)RTM_ADD, RTF_HOST|RTF_UP); 364 else { 365 ia->ia_broadaddr.sipx_addr.x_net = ia->ia_addr.sipx_addr.x_net; 366 rtinit(&(ia->ia_ifa), (int)RTM_ADD, RTF_UP); 367 } 368 ia->ia_flags |= IFA_ROUTE; 369 return (0); 370} 371 372/* 373 * Return address info for specified internet network. 374 */ 375struct ipx_ifaddr * 376ipx_iaonnetof(struct ipx_addr *dst) 377{ 378 struct ipx_ifaddr *ia; 379 struct ipx_addr *compare; 380 struct ifnet *ifp; 381 struct ipx_ifaddr *ia_maybe = NULL; 382 union ipx_net net = dst->x_net; 383 384 IPX_IFADDR_LOCK_ASSERT(); 385 386 TAILQ_FOREACH(ia, &ipx_ifaddrhead, ia_link) { 387 if ((ifp = ia->ia_ifp) != NULL) { 388 if (ifp->if_flags & IFF_POINTOPOINT) { 389 compare = &satoipx_addr(ia->ia_dstaddr); 390 if (ipx_hosteq(*dst, *compare)) 391 return (ia); 392 if (ipx_neteqnn(net, 393 ia->ia_addr.sipx_addr.x_net)) 394 ia_maybe = ia; 395 } else { 396 if (ipx_neteqnn(net, 397 ia->ia_addr.sipx_addr.x_net)) 398 return (ia); 399 } 400 } 401 } 402 return (ia_maybe); 403} 404 405void 406ipx_printhost(struct ipx_addr *addr) 407{ 408 u_short port; 409 struct ipx_addr work = *addr; 410 char *p; u_char *q; 411 char *net = "", *host = ""; 412 char cport[10], chost[15], cnet[15]; 413 414 port = ntohs(work.x_port); 415 416 if (ipx_nullnet(work) && ipx_nullhost(work)) { 417 if (port) 418 printf("*.%x", port); 419 else 420 printf("*.*"); 421 422 return; 423 } 424 425 if (ipx_wildnet(work)) 426 net = "any"; 427 else if (ipx_nullnet(work)) 428 net = "*"; 429 else { 430 q = work.x_net.c_net; 431 snprintf(cnet, sizeof(cnet), "%x%x%x%x", 432 q[0], q[1], q[2], q[3]); 433 for (p = cnet; *p == '0' && p < cnet + 8; p++) 434 continue; 435 net = p; 436 } 437 438 if (ipx_wildhost(work)) 439 host = "any"; 440 else if (ipx_nullhost(work)) 441 host = "*"; 442 else { 443 q = work.x_host.c_host; 444 snprintf(chost, sizeof(chost), "%x%x%x%x%x%x", 445 q[0], q[1], q[2], q[3], q[4], q[5]); 446 for (p = chost; *p == '0' && p < chost + 12; p++) 447 continue; 448 host = p; 449 } 450 451 if (port) { 452 if (strcmp(host, "*") == 0) { 453 host = ""; 454 snprintf(cport, sizeof(cport), "%x", port); 455 } else 456 snprintf(cport, sizeof(cport), ".%x", port); 457 } else 458 *cport = 0; 459 460 printf("%s.%s%s", net, host, cport); 461} 462