| 1 | /* $NetBSD: at_control.c,v 1.39 2016/08/01 03:15:30 ozaki-r Exp $ */ |
| 2 | |
| 3 | /* |
| 4 | * Copyright (c) 1990,1994 Regents of The University of Michigan. |
| 5 | * All Rights Reserved. |
| 6 | * |
| 7 | * Permission to use, copy, modify, and distribute this software and |
| 8 | * its documentation for any purpose and without fee is hereby granted, |
| 9 | * provided that the above copyright notice appears in all copies and |
| 10 | * that both that copyright notice and this permission notice appear |
| 11 | * in supporting documentation, and that the name of The University |
| 12 | * of Michigan not be used in advertising or publicity pertaining to |
| 13 | * distribution of the software without specific, written prior |
| 14 | * permission. This software is supplied as is without expressed or |
| 15 | * implied warranties of any kind. |
| 16 | * |
| 17 | * This product includes software developed by the University of |
| 18 | * California, Berkeley and its contributors. |
| 19 | * |
| 20 | * Research Systems Unix Group |
| 21 | * The University of Michigan |
| 22 | * c/o Wesley Craig |
| 23 | * 535 W. William Street |
| 24 | * Ann Arbor, Michigan |
| 25 | * +1-313-764-2278 |
| 26 | * netatalk@umich.edu |
| 27 | */ |
| 28 | |
| 29 | #include <sys/cdefs.h> |
| 30 | __KERNEL_RCSID(0, "$NetBSD: at_control.c,v 1.39 2016/08/01 03:15:30 ozaki-r Exp $" ); |
| 31 | |
| 32 | #include <sys/param.h> |
| 33 | #include <sys/systm.h> |
| 34 | #include <sys/proc.h> |
| 35 | #include <sys/errno.h> |
| 36 | #include <sys/ioctl.h> |
| 37 | #include <sys/mbuf.h> |
| 38 | #include <sys/kernel.h> |
| 39 | #include <sys/socket.h> |
| 40 | #include <sys/socketvar.h> |
| 41 | #include <sys/kauth.h> |
| 42 | #include <net/if.h> |
| 43 | #include <net/route.h> |
| 44 | #include <net/if_ether.h> |
| 45 | #include <netinet/in.h> |
| 46 | #undef s_net |
| 47 | |
| 48 | #include <netatalk/at.h> |
| 49 | #include <netatalk/at_var.h> |
| 50 | #include <netatalk/aarp.h> |
| 51 | #include <netatalk/phase2.h> |
| 52 | #include <netatalk/at_extern.h> |
| 53 | |
| 54 | static int aa_dorangeroute(struct ifaddr * ifa, |
| 55 | u_int first, u_int last, int cmd); |
| 56 | static int aa_addsingleroute(struct ifaddr * ifa, |
| 57 | struct at_addr * addr, struct at_addr * mask); |
| 58 | static int aa_delsingleroute(struct ifaddr * ifa, |
| 59 | struct at_addr * addr, struct at_addr * mask); |
| 60 | static int aa_dosingleroute(struct ifaddr * ifa, struct at_addr * addr, |
| 61 | struct at_addr * mask, int cmd, int flags); |
| 62 | static int at_scrub(struct ifnet * ifp, struct at_ifaddr * aa); |
| 63 | static int at_ifinit(struct ifnet *, struct at_ifaddr *, |
| 64 | const struct sockaddr_at *); |
| 65 | #if 0 |
| 66 | static void aa_clean(void); |
| 67 | #endif |
| 68 | |
| 69 | #define sateqaddr(a,b) ((a)->sat_len == (b)->sat_len && \ |
| 70 | (a)->sat_family == (b)->sat_family && \ |
| 71 | (a)->sat_addr.s_net == (b)->sat_addr.s_net && \ |
| 72 | (a)->sat_addr.s_node == (b)->sat_addr.s_node ) |
| 73 | |
| 74 | int |
| 75 | at_control(u_long cmd, void *data, struct ifnet *ifp) |
| 76 | { |
| 77 | struct ifreq *ifr = (struct ifreq *) data; |
| 78 | const struct sockaddr_at *csat; |
| 79 | struct netrange *nr; |
| 80 | const struct netrange *cnr; |
| 81 | struct at_aliasreq *ifra = (struct at_aliasreq *) data; |
| 82 | struct at_ifaddr *aa0; |
| 83 | struct at_ifaddr *aa = 0; |
| 84 | |
| 85 | /* |
| 86 | * If we have an ifp, then find the matching at_ifaddr if it exists |
| 87 | */ |
| 88 | if (ifp) |
| 89 | for (aa = at_ifaddr.tqh_first; aa; aa = aa->aa_list.tqe_next) |
| 90 | if (aa->aa_ifp == ifp) |
| 91 | break; |
| 92 | |
| 93 | /* |
| 94 | * In this first switch table we are basically getting ready for |
| 95 | * the second one, by getting the atalk-specific things set up |
| 96 | * so that they start to look more similar to other protocols etc. |
| 97 | */ |
| 98 | |
| 99 | switch (cmd) { |
| 100 | case SIOCAIFADDR: |
| 101 | case SIOCDIFADDR: |
| 102 | /* |
| 103 | * If we have an appletalk sockaddr, scan forward of where |
| 104 | * we are now on the at_ifaddr list to find one with a matching |
| 105 | * address on this interface. |
| 106 | * This may leave aa pointing to the first address on the |
| 107 | * NEXT interface! |
| 108 | */ |
| 109 | if (ifra->ifra_addr.sat_family == AF_APPLETALK) { |
| 110 | for (; aa; aa = aa->aa_list.tqe_next) |
| 111 | if (aa->aa_ifp == ifp && |
| 112 | sateqaddr(&aa->aa_addr, &ifra->ifra_addr)) |
| 113 | break; |
| 114 | } |
| 115 | /* |
| 116 | * If we a retrying to delete an addres but didn't find such, |
| 117 | * then return with an error |
| 118 | */ |
| 119 | if (cmd == SIOCDIFADDR && aa == 0) |
| 120 | return (EADDRNOTAVAIL); |
| 121 | /* FALLTHROUGH */ |
| 122 | |
| 123 | case SIOCSIFADDR: |
| 124 | /* |
| 125 | * If we are not superuser, then we don't get to do these |
| 126 | * ops. |
| 127 | */ |
| 128 | if (kauth_authorize_network(curlwp->l_cred, |
| 129 | KAUTH_NETWORK_INTERFACE, |
| 130 | KAUTH_REQ_NETWORK_INTERFACE_SETPRIV, ifp, (void *)cmd, |
| 131 | NULL) != 0) |
| 132 | return (EPERM); |
| 133 | |
| 134 | csat = satocsat(ifreq_getaddr(cmd, ifr)); |
| 135 | cnr = (const struct netrange *)csat->sat_zero; |
| 136 | if (cnr->nr_phase == 1) { |
| 137 | /* |
| 138 | * Look for a phase 1 address on this interface. |
| 139 | * This may leave aa pointing to the first address on |
| 140 | * the NEXT interface! |
| 141 | */ |
| 142 | for (; aa; aa = aa->aa_list.tqe_next) { |
| 143 | if (aa->aa_ifp == ifp && |
| 144 | (aa->aa_flags & AFA_PHASE2) == 0) |
| 145 | break; |
| 146 | } |
| 147 | } else { /* default to phase 2 */ |
| 148 | /* |
| 149 | * Look for a phase 2 address on this interface. |
| 150 | * This may leave aa pointing to the first address on |
| 151 | * the NEXT interface! |
| 152 | */ |
| 153 | for (; aa; aa = aa->aa_list.tqe_next) { |
| 154 | if (aa->aa_ifp == ifp && |
| 155 | (aa->aa_flags & AFA_PHASE2)) |
| 156 | break; |
| 157 | } |
| 158 | } |
| 159 | |
| 160 | if (ifp == 0) |
| 161 | panic("at_control" ); |
| 162 | |
| 163 | /* |
| 164 | * If we failed to find an existing at_ifaddr entry, then we |
| 165 | * allocate a fresh one. |
| 166 | * XXX change this to use malloc |
| 167 | */ |
| 168 | if (aa == (struct at_ifaddr *) 0) { |
| 169 | aa = (struct at_ifaddr *) |
| 170 | malloc(sizeof(struct at_ifaddr), M_IFADDR, |
| 171 | M_WAITOK|M_ZERO); |
| 172 | |
| 173 | if (aa == NULL) |
| 174 | return (ENOBUFS); |
| 175 | |
| 176 | callout_init(&aa->aa_probe_ch, 0); |
| 177 | |
| 178 | if ((aa0 = at_ifaddr.tqh_first) != NULL) { |
| 179 | /* |
| 180 | * Don't let the loopback be first, since the |
| 181 | * first address is the machine's default |
| 182 | * address for binding. |
| 183 | * If it is, stick ourself in front, otherwise |
| 184 | * go to the back of the list. |
| 185 | */ |
| 186 | if (aa0->aa_ifp->if_flags & IFF_LOOPBACK) { |
| 187 | TAILQ_INSERT_HEAD(&at_ifaddr, aa, |
| 188 | aa_list); |
| 189 | } else { |
| 190 | TAILQ_INSERT_TAIL(&at_ifaddr, aa, |
| 191 | aa_list); |
| 192 | } |
| 193 | } else { |
| 194 | TAILQ_INSERT_TAIL(&at_ifaddr, aa, aa_list); |
| 195 | } |
| 196 | ifaref(&aa->aa_ifa); |
| 197 | ifa_psref_init(&aa->aa_ifa); |
| 198 | |
| 199 | /* |
| 200 | * Find the end of the interface's addresses |
| 201 | * and link our new one on the end |
| 202 | */ |
| 203 | ifa_insert(ifp, &aa->aa_ifa); |
| 204 | |
| 205 | /* |
| 206 | * As the at_ifaddr contains the actual sockaddrs, |
| 207 | * and the ifaddr itself, link them al together |
| 208 | * correctly. |
| 209 | */ |
| 210 | aa->aa_ifa.ifa_addr = |
| 211 | (struct sockaddr *) &aa->aa_addr; |
| 212 | aa->aa_ifa.ifa_dstaddr = |
| 213 | (struct sockaddr *) &aa->aa_addr; |
| 214 | aa->aa_ifa.ifa_netmask = |
| 215 | (struct sockaddr *) &aa->aa_netmask; |
| 216 | |
| 217 | /* |
| 218 | * Set/clear the phase 2 bit. |
| 219 | */ |
| 220 | if (cnr->nr_phase == 1) |
| 221 | aa->aa_flags &= ~AFA_PHASE2; |
| 222 | else |
| 223 | aa->aa_flags |= AFA_PHASE2; |
| 224 | |
| 225 | /* |
| 226 | * and link it all together |
| 227 | */ |
| 228 | aa->aa_ifp = ifp; |
| 229 | } else { |
| 230 | /* |
| 231 | * If we DID find one then we clobber any routes |
| 232 | * dependent on it.. |
| 233 | */ |
| 234 | at_scrub(ifp, aa); |
| 235 | } |
| 236 | break; |
| 237 | |
| 238 | case SIOCGIFADDR: |
| 239 | csat = satocsat(ifreq_getaddr(cmd, ifr)); |
| 240 | cnr = (const struct netrange *)csat->sat_zero; |
| 241 | if (cnr->nr_phase == 1) { |
| 242 | /* |
| 243 | * If the request is specifying phase 1, then |
| 244 | * only look at a phase one address |
| 245 | */ |
| 246 | for (; aa; aa = aa->aa_list.tqe_next) { |
| 247 | if (aa->aa_ifp == ifp && |
| 248 | (aa->aa_flags & AFA_PHASE2) == 0) |
| 249 | break; |
| 250 | } |
| 251 | } else if (cnr->nr_phase == 2) { |
| 252 | /* |
| 253 | * If the request is specifying phase 2, then |
| 254 | * only look at a phase two address |
| 255 | */ |
| 256 | for (; aa; aa = aa->aa_list.tqe_next) { |
| 257 | if (aa->aa_ifp == ifp && |
| 258 | (aa->aa_flags & AFA_PHASE2)) |
| 259 | break; |
| 260 | } |
| 261 | } else { |
| 262 | /* |
| 263 | * default to everything |
| 264 | */ |
| 265 | for (; aa; aa = aa->aa_list.tqe_next) { |
| 266 | if (aa->aa_ifp == ifp) |
| 267 | break; |
| 268 | } |
| 269 | } |
| 270 | |
| 271 | if (aa == (struct at_ifaddr *) 0) |
| 272 | return (EADDRNOTAVAIL); |
| 273 | break; |
| 274 | } |
| 275 | |
| 276 | /* |
| 277 | * By the time this switch is run we should be able to assume that |
| 278 | * the "aa" pointer is valid when needed. |
| 279 | */ |
| 280 | switch (cmd) { |
| 281 | case SIOCGIFADDR: { |
| 282 | union { |
| 283 | struct sockaddr sa; |
| 284 | struct sockaddr_at sat; |
| 285 | } u; |
| 286 | |
| 287 | /* |
| 288 | * copy the contents of the sockaddr blindly. |
| 289 | */ |
| 290 | sockaddr_copy(&u.sa, sizeof(u), |
| 291 | (const struct sockaddr *)&aa->aa_addr); |
| 292 | /* |
| 293 | * and do some cleanups |
| 294 | */ |
| 295 | nr = (struct netrange *)&u.sat.sat_zero; |
| 296 | nr->nr_phase = (aa->aa_flags & AFA_PHASE2) ? 2 : 1; |
| 297 | nr->nr_firstnet = aa->aa_firstnet; |
| 298 | nr->nr_lastnet = aa->aa_lastnet; |
| 299 | ifreq_setaddr(cmd, ifr, &u.sa); |
| 300 | break; |
| 301 | } |
| 302 | |
| 303 | case SIOCSIFADDR: |
| 304 | return at_ifinit(ifp, aa, |
| 305 | (const struct sockaddr_at *)ifreq_getaddr(cmd, ifr)); |
| 306 | |
| 307 | case SIOCAIFADDR: |
| 308 | if (sateqaddr(&ifra->ifra_addr, &aa->aa_addr)) |
| 309 | return 0; |
| 310 | return at_ifinit(ifp, aa, |
| 311 | (const struct sockaddr_at *)ifreq_getaddr(cmd, ifr)); |
| 312 | |
| 313 | case SIOCDIFADDR: |
| 314 | at_purgeaddr(&aa->aa_ifa); |
| 315 | break; |
| 316 | |
| 317 | default: |
| 318 | return ENOTTY; |
| 319 | } |
| 320 | return (0); |
| 321 | } |
| 322 | |
| 323 | void |
| 324 | at_purgeaddr(struct ifaddr *ifa) |
| 325 | { |
| 326 | struct ifnet *ifp = ifa->ifa_ifp; |
| 327 | struct at_ifaddr *aa = (void *) ifa; |
| 328 | |
| 329 | /* |
| 330 | * scrub all routes.. didn't we just DO this? XXX yes, del it |
| 331 | * XXX above XXX not necessarily true anymore |
| 332 | */ |
| 333 | at_scrub(ifp, aa); |
| 334 | |
| 335 | /* |
| 336 | * remove the ifaddr from the interface |
| 337 | */ |
| 338 | ifa_remove(ifp, &aa->aa_ifa); |
| 339 | TAILQ_REMOVE(&at_ifaddr, aa, aa_list); |
| 340 | ifafree(&aa->aa_ifa); |
| 341 | } |
| 342 | |
| 343 | void |
| 344 | at_purgeif(struct ifnet *ifp) |
| 345 | { |
| 346 | if_purgeaddrs(ifp, AF_APPLETALK, at_purgeaddr); |
| 347 | } |
| 348 | |
| 349 | /* |
| 350 | * Given an interface and an at_ifaddr (supposedly on that interface) remove |
| 351 | * any routes that depend on this. Why ifp is needed I'm not sure, as |
| 352 | * aa->at_ifaddr.ifa_ifp should be the same. |
| 353 | */ |
| 354 | static int |
| 355 | at_scrub(struct ifnet *ifp, struct at_ifaddr *aa) |
| 356 | { |
| 357 | int error = 0; |
| 358 | |
| 359 | if (aa->aa_flags & AFA_ROUTE) { |
| 360 | if (ifp->if_flags & IFF_LOOPBACK) |
| 361 | error = aa_delsingleroute(&aa->aa_ifa, |
| 362 | &aa->aa_addr.sat_addr, &aa->aa_netmask.sat_addr); |
| 363 | else if (ifp->if_flags & IFF_POINTOPOINT) |
| 364 | error = rtinit(&aa->aa_ifa, RTM_DELETE, RTF_HOST); |
| 365 | else if (ifp->if_flags & IFF_BROADCAST) |
| 366 | error = aa_dorangeroute(&aa->aa_ifa, |
| 367 | ntohs(aa->aa_firstnet), ntohs(aa->aa_lastnet), |
| 368 | RTM_DELETE); |
| 369 | |
| 370 | aa->aa_ifa.ifa_flags &= ~IFA_ROUTE; |
| 371 | aa->aa_flags &= ~AFA_ROUTE; |
| 372 | } |
| 373 | return error; |
| 374 | } |
| 375 | |
| 376 | /* |
| 377 | * given an at_ifaddr,a sockaddr_at and an ifp, |
| 378 | * bang them all together at high speed and see what happens |
| 379 | */ |
| 380 | static int |
| 381 | at_ifinit(struct ifnet *ifp, struct at_ifaddr *aa, const struct sockaddr_at *sat) |
| 382 | { |
| 383 | struct netrange nr, onr; |
| 384 | struct sockaddr_at oldaddr; |
| 385 | int s = splnet(), error = 0, i, j; |
| 386 | int netinc, nodeinc, nnets; |
| 387 | u_short net; |
| 388 | |
| 389 | /* |
| 390 | * save the old addresses in the at_ifaddr just in case we need them. |
| 391 | */ |
| 392 | oldaddr = aa->aa_addr; |
| 393 | onr.nr_firstnet = aa->aa_firstnet; |
| 394 | onr.nr_lastnet = aa->aa_lastnet; |
| 395 | |
| 396 | /* |
| 397 | * take the address supplied as an argument, and add it to the |
| 398 | * at_ifnet (also given). Remember ing to update |
| 399 | * those parts of the at_ifaddr that need special processing |
| 400 | */ |
| 401 | memset(AA_SAT(aa), 0, sizeof(struct sockaddr_at)); |
| 402 | memcpy(&nr, sat->sat_zero, sizeof(struct netrange)); |
| 403 | memcpy(AA_SAT(aa)->sat_zero, sat->sat_zero, sizeof(struct netrange)); |
| 404 | nnets = ntohs(nr.nr_lastnet) - ntohs(nr.nr_firstnet) + 1; |
| 405 | aa->aa_firstnet = nr.nr_firstnet; |
| 406 | aa->aa_lastnet = nr.nr_lastnet; |
| 407 | |
| 408 | #ifdef NETATALKDEBUG |
| 409 | printf("at_ifinit: %s: %u.%u range %u-%u phase %d\n" , |
| 410 | ifp->if_xname, |
| 411 | ntohs(sat->sat_addr.s_net), sat->sat_addr.s_node, |
| 412 | ntohs(aa->aa_firstnet), ntohs(aa->aa_lastnet), |
| 413 | (aa->aa_flags & AFA_PHASE2) ? 2 : 1); |
| 414 | #endif |
| 415 | |
| 416 | /* |
| 417 | * We could eliminate the need for a second phase 1 probe (post |
| 418 | * autoconf) if we check whether we're resetting the node. Note |
| 419 | * that phase 1 probes use only nodes, not net.node pairs. Under |
| 420 | * phase 2, both the net and node must be the same. |
| 421 | */ |
| 422 | AA_SAT(aa)->sat_len = sat->sat_len; |
| 423 | AA_SAT(aa)->sat_family = AF_APPLETALK; |
| 424 | if (ifp->if_flags & IFF_LOOPBACK) { |
| 425 | AA_SAT(aa)->sat_addr.s_net = sat->sat_addr.s_net; |
| 426 | AA_SAT(aa)->sat_addr.s_node = sat->sat_addr.s_node; |
| 427 | #if 0 |
| 428 | } else if (fp->if_flags & IFF_POINTOPOINT) { |
| 429 | /* unimplemented */ |
| 430 | /* |
| 431 | * we'd have to copy the dstaddr field over from the sat |
| 432 | * but it's not clear that it would contain the right info.. |
| 433 | */ |
| 434 | #endif |
| 435 | } else { |
| 436 | /* |
| 437 | * We are a normal (probably ethernet) interface. |
| 438 | * apply the new address to the interface structures etc. |
| 439 | * We will probe this address on the net first, before |
| 440 | * applying it to ensure that it is free.. If it is not, then |
| 441 | * we will try a number of other randomly generated addresses |
| 442 | * in this net and then increment the net. etc.etc. until |
| 443 | * we find an unused address. |
| 444 | */ |
| 445 | aa->aa_flags |= AFA_PROBING; /* if not loopback we Must |
| 446 | * probe? */ |
| 447 | if (aa->aa_flags & AFA_PHASE2) { |
| 448 | if (sat->sat_addr.s_net == ATADDR_ANYNET) { |
| 449 | /* |
| 450 | * If we are phase 2, and the net was not |
| 451 | * specified * then we select a random net |
| 452 | * within the supplied netrange. |
| 453 | * XXX use /dev/random? |
| 454 | */ |
| 455 | if (nnets != 1) { |
| 456 | net = ntohs(nr.nr_firstnet) + |
| 457 | time_second % (nnets - 1); |
| 458 | } else { |
| 459 | net = ntohs(nr.nr_firstnet); |
| 460 | } |
| 461 | } else { |
| 462 | /* |
| 463 | * if a net was supplied, then check that it |
| 464 | * is within the netrange. If it is not then |
| 465 | * replace the old values and return an error |
| 466 | */ |
| 467 | if (ntohs(sat->sat_addr.s_net) < |
| 468 | ntohs(nr.nr_firstnet) || |
| 469 | ntohs(sat->sat_addr.s_net) > |
| 470 | ntohs(nr.nr_lastnet)) { |
| 471 | aa->aa_addr = oldaddr; |
| 472 | aa->aa_firstnet = onr.nr_firstnet; |
| 473 | aa->aa_lastnet = onr.nr_lastnet; |
| 474 | splx(s); |
| 475 | return (EINVAL); |
| 476 | } |
| 477 | /* |
| 478 | * otherwise just use the new net number.. |
| 479 | */ |
| 480 | net = ntohs(sat->sat_addr.s_net); |
| 481 | } |
| 482 | } else { |
| 483 | /* |
| 484 | * we must be phase one, so just use whatever we were |
| 485 | * given. I guess it really isn't going to be used... |
| 486 | * RIGHT? |
| 487 | */ |
| 488 | net = ntohs(sat->sat_addr.s_net); |
| 489 | } |
| 490 | |
| 491 | /* |
| 492 | * set the node part of the address into the ifaddr. If it's |
| 493 | * not specified, be random about it... XXX use /dev/random? |
| 494 | */ |
| 495 | if (sat->sat_addr.s_node == ATADDR_ANYNODE) { |
| 496 | AA_SAT(aa)->sat_addr.s_node = time_second; |
| 497 | } else { |
| 498 | AA_SAT(aa)->sat_addr.s_node = sat->sat_addr.s_node; |
| 499 | } |
| 500 | |
| 501 | /* |
| 502 | * step through the nets in the range starting at the |
| 503 | * (possibly random) start point. |
| 504 | */ |
| 505 | for (i = nnets, netinc = 1; i > 0; net = ntohs(nr.nr_firstnet) + |
| 506 | ((net - ntohs(nr.nr_firstnet) + netinc) % nnets), i--) { |
| 507 | AA_SAT(aa)->sat_addr.s_net = htons(net); |
| 508 | |
| 509 | /* |
| 510 | * using a rather strange stepping method, |
| 511 | * stagger through the possible node addresses |
| 512 | * Once again, starting at the (possibly random) |
| 513 | * initial node address. |
| 514 | */ |
| 515 | for (j = 0, nodeinc = time_second | 1; j < 256; |
| 516 | j++, AA_SAT(aa)->sat_addr.s_node += nodeinc) { |
| 517 | if (AA_SAT(aa)->sat_addr.s_node > 253 || |
| 518 | AA_SAT(aa)->sat_addr.s_node < 1) { |
| 519 | continue; |
| 520 | } |
| 521 | aa->aa_probcnt = 10; |
| 522 | |
| 523 | /* |
| 524 | * start off the probes as an asynchronous |
| 525 | * activity. though why wait 200mSec? |
| 526 | */ |
| 527 | callout_reset(&aa->aa_probe_ch, hz / 5, |
| 528 | aarpprobe, ifp); |
| 529 | if (tsleep(aa, PPAUSE | PCATCH, "at_ifinit" , |
| 530 | 0)) { |
| 531 | /* |
| 532 | * theoretically we shouldn't time out |
| 533 | * here so if we returned with an error. |
| 534 | */ |
| 535 | printf("at_ifinit: timeout?!\n" ); |
| 536 | aa->aa_addr = oldaddr; |
| 537 | aa->aa_firstnet = onr.nr_firstnet; |
| 538 | aa->aa_lastnet = onr.nr_lastnet; |
| 539 | splx(s); |
| 540 | return (EINTR); |
| 541 | } |
| 542 | /* |
| 543 | * The async activity should have woken us |
| 544 | * up. We need to see if it was successful in |
| 545 | * finding a free spot, or if we need to |
| 546 | * iterate to the next address to try. |
| 547 | */ |
| 548 | if ((aa->aa_flags & AFA_PROBING) == 0) |
| 549 | break; |
| 550 | } |
| 551 | |
| 552 | /* |
| 553 | * of course we need to break out through two loops... |
| 554 | */ |
| 555 | if ((aa->aa_flags & AFA_PROBING) == 0) |
| 556 | break; |
| 557 | |
| 558 | /* reset node for next network */ |
| 559 | AA_SAT(aa)->sat_addr.s_node = time_second; |
| 560 | } |
| 561 | |
| 562 | /* |
| 563 | * if we are still trying to probe, then we have finished all |
| 564 | * the possible addresses, so we need to give up |
| 565 | */ |
| 566 | if (aa->aa_flags & AFA_PROBING) { |
| 567 | aa->aa_addr = oldaddr; |
| 568 | aa->aa_firstnet = onr.nr_firstnet; |
| 569 | aa->aa_lastnet = onr.nr_lastnet; |
| 570 | splx(s); |
| 571 | return (EADDRINUSE); |
| 572 | } |
| 573 | } |
| 574 | |
| 575 | /* |
| 576 | * Now that we have selected an address, we need to tell the |
| 577 | * interface about it, just in case it needs to adjust something. |
| 578 | */ |
| 579 | if ((error = if_addr_init(ifp, &aa->aa_ifa, true)) != 0) { |
| 580 | /* |
| 581 | * of course this could mean that it objects violently |
| 582 | * so if it does, we back out again.. |
| 583 | */ |
| 584 | aa->aa_addr = oldaddr; |
| 585 | aa->aa_firstnet = onr.nr_firstnet; |
| 586 | aa->aa_lastnet = onr.nr_lastnet; |
| 587 | splx(s); |
| 588 | return (error); |
| 589 | } |
| 590 | /* |
| 591 | * set up the netmask part of the at_ifaddr and point the appropriate |
| 592 | * pointer in the ifaddr to it. probably pointless, but what the |
| 593 | * heck.. XXX |
| 594 | */ |
| 595 | memset(&aa->aa_netmask, 0, sizeof(aa->aa_netmask)); |
| 596 | aa->aa_netmask.sat_len = sizeof(struct sockaddr_at); |
| 597 | aa->aa_netmask.sat_family = AF_APPLETALK; |
| 598 | aa->aa_netmask.sat_addr.s_net = 0xffff; |
| 599 | aa->aa_netmask.sat_addr.s_node = 0; |
| 600 | #if 0 |
| 601 | aa->aa_ifa.ifa_netmask = (struct sockaddr *) &(aa->aa_netmask);/* XXX */ |
| 602 | #endif |
| 603 | |
| 604 | /* |
| 605 | * Initialize broadcast (or remote p2p) address |
| 606 | */ |
| 607 | memset(&aa->aa_broadaddr, 0, sizeof(aa->aa_broadaddr)); |
| 608 | aa->aa_broadaddr.sat_len = sizeof(struct sockaddr_at); |
| 609 | aa->aa_broadaddr.sat_family = AF_APPLETALK; |
| 610 | |
| 611 | aa->aa_ifa.ifa_metric = ifp->if_metric; |
| 612 | if (ifp->if_flags & IFF_BROADCAST) { |
| 613 | aa->aa_broadaddr.sat_addr.s_net = htons(ATADDR_ANYNET); |
| 614 | aa->aa_broadaddr.sat_addr.s_node = ATADDR_BCAST; |
| 615 | aa->aa_ifa.ifa_broadaddr = |
| 616 | (struct sockaddr *) &aa->aa_broadaddr; |
| 617 | /* add the range of routes needed */ |
| 618 | error = aa_dorangeroute(&aa->aa_ifa, |
| 619 | ntohs(aa->aa_firstnet), ntohs(aa->aa_lastnet), RTM_ADD); |
| 620 | } else if (ifp->if_flags & IFF_POINTOPOINT) { |
| 621 | struct at_addr rtaddr, rtmask; |
| 622 | |
| 623 | memset(&rtaddr, 0, sizeof(rtaddr)); |
| 624 | memset(&rtmask, 0, sizeof(rtmask)); |
| 625 | /* fill in the far end if we know it here XXX */ |
| 626 | aa->aa_ifa.ifa_dstaddr = (struct sockaddr *) & aa->aa_dstaddr; |
| 627 | error = aa_addsingleroute(&aa->aa_ifa, &rtaddr, &rtmask); |
| 628 | } else if (ifp->if_flags & IFF_LOOPBACK) { |
| 629 | struct at_addr rtaddr, rtmask; |
| 630 | |
| 631 | memset(&rtaddr, 0, sizeof(rtaddr)); |
| 632 | memset(&rtmask, 0, sizeof(rtmask)); |
| 633 | rtaddr.s_net = AA_SAT(aa)->sat_addr.s_net; |
| 634 | rtaddr.s_node = AA_SAT(aa)->sat_addr.s_node; |
| 635 | rtmask.s_net = 0xffff; |
| 636 | rtmask.s_node = 0x0; |
| 637 | error = aa_addsingleroute(&aa->aa_ifa, &rtaddr, &rtmask); |
| 638 | } |
| 639 | /* |
| 640 | * of course if we can't add these routes we back out, but it's getting |
| 641 | * risky by now XXX |
| 642 | */ |
| 643 | if (error) { |
| 644 | at_scrub(ifp, aa); |
| 645 | aa->aa_addr = oldaddr; |
| 646 | aa->aa_firstnet = onr.nr_firstnet; |
| 647 | aa->aa_lastnet = onr.nr_lastnet; |
| 648 | splx(s); |
| 649 | return (error); |
| 650 | } |
| 651 | /* |
| 652 | * note that the address has a route associated with it.... |
| 653 | */ |
| 654 | aa->aa_ifa.ifa_flags |= IFA_ROUTE; |
| 655 | aa->aa_flags |= AFA_ROUTE; |
| 656 | splx(s); |
| 657 | return (0); |
| 658 | } |
| 659 | |
| 660 | /* |
| 661 | * check whether a given address is a broadcast address for us.. |
| 662 | */ |
| 663 | int |
| 664 | at_broadcast(const struct sockaddr_at *sat) |
| 665 | { |
| 666 | struct at_ifaddr *aa; |
| 667 | |
| 668 | /* |
| 669 | * If the node is not right, it can't be a broadcast |
| 670 | */ |
| 671 | if (sat->sat_addr.s_node != ATADDR_BCAST) |
| 672 | return 0; |
| 673 | |
| 674 | /* |
| 675 | * If the node was right then if the net is right, it's a broadcast |
| 676 | */ |
| 677 | if (sat->sat_addr.s_net == ATADDR_ANYNET) |
| 678 | return 1; |
| 679 | |
| 680 | /* |
| 681 | * failing that, if the net is one we have, it's a broadcast as well. |
| 682 | */ |
| 683 | for (aa = at_ifaddr.tqh_first; aa; aa = aa->aa_list.tqe_next) { |
| 684 | if ((aa->aa_ifp->if_flags & IFF_BROADCAST) |
| 685 | && (ntohs(sat->sat_addr.s_net) >= ntohs(aa->aa_firstnet) |
| 686 | && ntohs(sat->sat_addr.s_net) <= ntohs(aa->aa_lastnet))) |
| 687 | return 1; |
| 688 | } |
| 689 | return 0; |
| 690 | } |
| 691 | |
| 692 | |
| 693 | /* |
| 694 | * aa_dorangeroute() |
| 695 | * |
| 696 | * Add a route for a range of networks from bot to top - 1. |
| 697 | * Algorithm: |
| 698 | * |
| 699 | * Split the range into two subranges such that the middle |
| 700 | * of the two ranges is the point where the highest bit of difference |
| 701 | * between the two addresses, makes its transition |
| 702 | * Each of the upper and lower ranges might not exist, or might be |
| 703 | * representable by 1 or more netmasks. In addition, if both |
| 704 | * ranges can be represented by the same netmask, then teh can be merged |
| 705 | * by using the next higher netmask.. |
| 706 | */ |
| 707 | |
| 708 | static int |
| 709 | aa_dorangeroute(struct ifaddr *ifa, u_int bot, u_int top, int cmd) |
| 710 | { |
| 711 | u_int mask1; |
| 712 | struct at_addr addr; |
| 713 | struct at_addr mask; |
| 714 | int error; |
| 715 | |
| 716 | /* |
| 717 | * slight sanity check |
| 718 | */ |
| 719 | if (bot > top) |
| 720 | return (EINVAL); |
| 721 | |
| 722 | addr.s_node = 0; |
| 723 | mask.s_node = 0; |
| 724 | /* |
| 725 | * just start out with the lowest boundary |
| 726 | * and keep extending the mask till it's too big. |
| 727 | */ |
| 728 | |
| 729 | while (bot <= top) { |
| 730 | mask1 = 1; |
| 731 | while (((bot & ~mask1) >= bot) |
| 732 | && ((bot | mask1) <= top)) { |
| 733 | mask1 <<= 1; |
| 734 | mask1 |= 1; |
| 735 | } |
| 736 | mask1 >>= 1; |
| 737 | mask.s_net = htons(~mask1); |
| 738 | addr.s_net = htons(bot); |
| 739 | if (cmd == RTM_ADD) { |
| 740 | error = aa_addsingleroute(ifa, &addr, &mask); |
| 741 | if (error) { |
| 742 | /* XXX clean up? */ |
| 743 | return (error); |
| 744 | } |
| 745 | } else { |
| 746 | error = aa_delsingleroute(ifa, &addr, &mask); |
| 747 | } |
| 748 | bot = (bot | mask1) + 1; |
| 749 | } |
| 750 | return 0; |
| 751 | } |
| 752 | |
| 753 | static int |
| 754 | aa_addsingleroute(struct ifaddr *ifa, struct at_addr *addr, struct at_addr *mask) |
| 755 | { |
| 756 | int error; |
| 757 | |
| 758 | #ifdef NETATALKDEBUG |
| 759 | printf("aa_addsingleroute: %x.%x mask %x.%x ..." , |
| 760 | ntohs(addr->s_net), addr->s_node, |
| 761 | ntohs(mask->s_net), mask->s_node); |
| 762 | #endif |
| 763 | |
| 764 | error = aa_dosingleroute(ifa, addr, mask, RTM_ADD, RTF_UP); |
| 765 | #ifdef NETATALKDEBUG |
| 766 | if (error) |
| 767 | printf("aa_addsingleroute: error %d\n" , error); |
| 768 | #endif |
| 769 | return (error); |
| 770 | } |
| 771 | |
| 772 | static int |
| 773 | aa_delsingleroute(struct ifaddr *ifa, struct at_addr *addr, struct at_addr *mask) |
| 774 | { |
| 775 | int error; |
| 776 | |
| 777 | #ifdef NETATALKDEBUG |
| 778 | printf("aa_delsingleroute: %x.%x mask %x.%x ..." , |
| 779 | ntohs(addr->s_net), addr->s_node, |
| 780 | ntohs(mask->s_net), mask->s_node); |
| 781 | #endif |
| 782 | |
| 783 | error = aa_dosingleroute(ifa, addr, mask, RTM_DELETE, 0); |
| 784 | #ifdef NETATALKDEBUG |
| 785 | if (error) |
| 786 | printf("aa_delsingleroute: error %d\n" , error); |
| 787 | #endif |
| 788 | return (error); |
| 789 | } |
| 790 | |
| 791 | static int |
| 792 | aa_dosingleroute(struct ifaddr *ifa, struct at_addr *at_addr, struct at_addr *at_mask, int cmd, int flags) |
| 793 | { |
| 794 | struct sockaddr_at addr, mask, *gate; |
| 795 | |
| 796 | memset(&addr, 0, sizeof(addr)); |
| 797 | memset(&mask, 0, sizeof(mask)); |
| 798 | addr.sat_family = AF_APPLETALK; |
| 799 | addr.sat_len = sizeof(struct sockaddr_at); |
| 800 | addr.sat_addr.s_net = at_addr->s_net; |
| 801 | addr.sat_addr.s_node = at_addr->s_node; |
| 802 | mask.sat_family = AF_APPLETALK; |
| 803 | mask.sat_len = sizeof(struct sockaddr_at); |
| 804 | mask.sat_addr.s_net = at_mask->s_net; |
| 805 | mask.sat_addr.s_node = at_mask->s_node; |
| 806 | |
| 807 | if (at_mask->s_node) { |
| 808 | gate = satosat(ifa->ifa_dstaddr); |
| 809 | flags |= RTF_HOST; |
| 810 | } else { |
| 811 | gate = satosat(ifa->ifa_addr); |
| 812 | } |
| 813 | |
| 814 | #ifdef NETATALKDEBUG |
| 815 | printf("on %s %x.%x\n" , (flags & RTF_HOST) ? "host" : "net" , |
| 816 | ntohs(gate->sat_addr.s_net), gate->sat_addr.s_node); |
| 817 | #endif |
| 818 | return (rtrequest(cmd, (struct sockaddr *) &addr, |
| 819 | (struct sockaddr *) gate, (struct sockaddr *) &mask, flags, NULL)); |
| 820 | } |
| 821 | |
| 822 | #if 0 |
| 823 | static void |
| 824 | aa_clean(void) |
| 825 | { |
| 826 | struct at_ifaddr *aa; |
| 827 | struct ifaddr *ifa; |
| 828 | struct ifnet *ifp; |
| 829 | |
| 830 | while ((aa = TAILQ_FIRST(&at_ifaddr)) != NULL) { |
| 831 | TAILQ_REMOVE(&at_ifaddr, aa, aa_list); |
| 832 | ifp = aa->aa_ifp; |
| 833 | at_scrub(ifp, aa); |
| 834 | IFADDR_READER_FOREACH(ifa, ifp) { |
| 835 | if (ifa == &aa->aa_ifa) |
| 836 | break; |
| 837 | } |
| 838 | if (ifa == NULL) |
| 839 | panic("aa not present" ); |
| 840 | ifa_remove(ifp, ifa); |
| 841 | } |
| 842 | } |
| 843 | #endif |
| 844 | |