| 1 | /* |
| 2 | * Copyright (c) 2000 Apple Computer, Inc. All rights reserved. |
| 3 | * |
| 4 | * @APPLE_LICENSE_HEADER_START@ |
| 5 | * |
| 6 | * This file contains Original Code and/or Modifications of Original Code |
| 7 | * as defined in and that are subject to the Apple Public Source License |
| 8 | * Version 2.0 (the 'License'). You may not use this file except in |
| 9 | * compliance with the License. Please obtain a copy of the License at |
| 10 | * http://www.opensource.apple.com/apsl/ and read it before using this |
| 11 | * file. |
| 12 | * |
| 13 | * The Original Code and all software distributed under the License are |
| 14 | * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER |
| 15 | * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, |
| 16 | * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, |
| 17 | * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. |
| 18 | * Please see the License for the specific language governing rights and |
| 19 | * limitations under the License. |
| 20 | * |
| 21 | * @APPLE_LICENSE_HEADER_END@ |
| 22 | */ |
| 23 | /* |
| 24 | * Copyright (c) 1993-1998 Apple Computer, Inc. |
| 25 | * All Rights Reserved. |
| 26 | */ |
| 27 | |
| 28 | /* |
| 29 | * Modified, March 17, 1997 by Tuyen Nguyen for MacOSX. |
| 30 | */ |
| 31 | |
| 32 | #include <sys/errno.h> |
| 33 | #include <sys/types.h> |
| 34 | #include <sys/param.h> |
| 35 | #include <machine/spl.h> |
| 36 | #include <sys/systm.h> |
| 37 | #include <sys/kernel.h> |
| 38 | #include <sys/proc.h> |
| 39 | #include <sys/filedesc.h> |
| 40 | #include <sys/fcntl.h> |
| 41 | #include <sys/mbuf.h> |
| 42 | #include <sys/ioctl.h> |
| 43 | #include <sys/malloc.h> |
| 44 | #include <sys/socket.h> |
| 45 | #include <sys/socketvar.h> |
| 46 | |
| 47 | #include <net/if.h> |
| 48 | |
| 49 | #include <netat/sysglue.h> |
| 50 | #include <netat/appletalk.h> |
| 51 | #include <netat/at_var.h> |
| 52 | #include <netat/ddp.h> |
| 53 | #include <netat/rtmp.h> |
| 54 | #include <netat/zip.h> |
| 55 | #include <netat/routing_tables.h> |
| 56 | #include <netat/debug.h> |
| 57 | #include <netat/at_pcb.h> |
| 58 | |
| 59 | #include <sys/kern_event.h> |
| 60 | |
| 61 | extern void rtmp_router_input(); |
| 62 | |
| 63 | /****************************************************************/ |
| 64 | /* */ |
| 65 | /* */ |
| 66 | /* RTMP Protocol */ |
| 67 | /* */ |
| 68 | /* */ |
| 69 | /****************************************************************/ |
| 70 | |
| 71 | |
| 72 | /* rtmp.c: , 1.6; 2/26/93; Apple Computer, Inc." */ |
| 73 | |
| 74 | |
| 75 | #define NROUTERS2TRAK 8 |
| 76 | #define FIFTYSECS 10 |
| 77 | #define NODE(r) ((r)->ifARouter.s_node) |
| 78 | #define NET(r) ((r)->ifARouter.s_net) |
| 79 | #define INUSE(r) (NODE(r)) |
| 80 | |
| 81 | void ddp_age_router(); |
| 82 | |
| 83 | static struct routerinfo { |
| 84 | struct at_addr ifARouter; |
| 85 | at_ifaddr_t *ifID; |
| 86 | void *tmo; |
| 87 | } trackedrouters[NROUTERS2TRAK]; |
| 88 | |
| 89 | void trackrouter_rem_if(ifID) |
| 90 | register at_ifaddr_t *ifID; |
| 91 | { |
| 92 | register i; |
| 93 | register struct routerinfo *router; |
| 94 | |
| 95 | for (i = NROUTERS2TRAK; --i >= 0;) { |
| 96 | router = &trackedrouters[i]; |
| 97 | if (trackedrouters[i].ifID == ifID) { |
| 98 | untimeout(ddp_age_router, (caddr_t)router); |
| 99 | break; |
| 100 | } |
| 101 | } |
| 102 | } |
| 103 | |
| 104 | |
| 105 | void routershutdown() |
| 106 | { |
| 107 | register i; |
| 108 | |
| 109 | for (i = NROUTERS2TRAK; --i >= 0;) { |
| 110 | register struct routerinfo *router; |
| 111 | |
| 112 | router = &trackedrouters[i]; |
| 113 | if (INUSE(router)) { |
| 114 | untimeout(ddp_age_router, (caddr_t) router); |
| 115 | bzero((caddr_t) router, sizeof(struct routerinfo)); |
| 116 | } |
| 117 | } |
| 118 | } |
| 119 | |
| 120 | int router_added = 0; |
| 121 | int router_killed = 0; |
| 122 | |
| 123 | |
| 124 | |
| 125 | void trackrouter(ifID, net, node) |
| 126 | register at_ifaddr_t *ifID; |
| 127 | register unsigned short net; |
| 128 | register unsigned char node; |
| 129 | { |
| 130 | register struct routerinfo *unused = NULL; |
| 131 | register i; |
| 132 | |
| 133 | for (i = NROUTERS2TRAK; --i >= 0;) { |
| 134 | register struct routerinfo *router; |
| 135 | |
| 136 | router = &trackedrouters[(i + node) & (NROUTERS2TRAK-1)]; |
| 137 | if ((NODE(router) == node) && (NET(router) == net)) { |
| 138 | untimeout(ddp_age_router, (caddr_t) router); |
| 139 | timeout(ddp_age_router, (caddr_t) router, 50*SYS_HZ); |
| 140 | unused = NULL; |
| 141 | break; |
| 142 | } |
| 143 | else if (!INUSE(router) && !unused) |
| 144 | unused = router; |
| 145 | } |
| 146 | if (unused) { |
| 147 | router_added++; |
| 148 | |
| 149 | if (ifID->ifARouter.s_net == 0) { |
| 150 | /* Send event that this interface just got a router. This does not |
| 151 | discriminate on whether this router is valid or not. If it is not |
| 152 | valid rtmp_input will send a KEV_ATALK_ROUTERUP_INVALID event. */ |
| 153 | atalk_post_msg(ifID->aa_ifp, KEV_ATALK_ROUTERUP, 0, 0); |
| 154 | } |
| 155 | |
| 156 | unused->ifID = ifID; |
| 157 | NET(unused) = net; |
| 158 | NODE(unused) = node; |
| 159 | ifID->ifRouterState = ROUTER_AROUND; |
| 160 | timeout(ddp_age_router, (caddr_t) unused, 50*SYS_HZ); |
| 161 | |
| 162 | if (NET(ifID) == 0 && NODE(ifID) == 0) { |
| 163 | NET(ifID) = net; |
| 164 | NODE(ifID) = node; |
| 165 | } |
| 166 | } |
| 167 | } |
| 168 | |
| 169 | /* |
| 170 | * This is the timeout function that is called after 50 seconds, |
| 171 | * if no router packets come in. That way we won't send extended |
| 172 | * frames to something that is not there. Untimeout is called if |
| 173 | * an RTMP packet comes in so this routine will not be called. |
| 174 | */ |
| 175 | void ddp_age_router(deadrouter) |
| 176 | register struct routerinfo *deadrouter; |
| 177 | { |
| 178 | register at_ifaddr_t *ourrouter; |
| 179 | |
| 180 | atalk_lock(); |
| 181 | |
| 182 | ourrouter = deadrouter->ifID; |
| 183 | if (ourrouter == NULL) { |
| 184 | atalk_unlock(); |
| 185 | return; |
| 186 | } |
| 187 | |
| 188 | dPrintf(D_M_RTMP, D_L_INFO, |
| 189 | ("ddp_age_router called deadrouter=%d:%d\n", NODE(deadrouter), NET(deadrouter))); |
| 190 | |
| 191 | router_killed++; |
| 192 | |
| 193 | if (NODE(ourrouter) == NODE(deadrouter) && |
| 194 | NET(ourrouter) == NET(deadrouter)) { |
| 195 | register unsigned long atrandom = random(); |
| 196 | register struct routerinfo *newrouter; |
| 197 | register i; |
| 198 | |
| 199 | bzero((caddr_t) deadrouter, sizeof(struct routerinfo)); |
| 200 | for (i = NROUTERS2TRAK; --i >= 0;) { |
| 201 | newrouter = &trackedrouters[(i + atrandom) & (NROUTERS2TRAK-1)]; |
| 202 | if (INUSE(newrouter)) |
| 203 | break; |
| 204 | else |
| 205 | newrouter = NULL; |
| 206 | } |
| 207 | if (newrouter) { |
| 208 | /* Set our router to another on the list and go on with life */ |
| 209 | NET(ourrouter) = NET(newrouter); |
| 210 | NODE(ourrouter) = NODE(newrouter); |
| 211 | } |
| 212 | else { |
| 213 | /* from gorouterless() */ |
| 214 | /* We have no other routers. */ |
| 215 | ATTRACE(AT_MID_DDP, AT_SID_TIMERS, AT_LV_WARNING, FALSE, |
| 216 | "ddp_age_router entry : ARouter = 0x%x, RouterState = 0x%x", |
| 217 | ATALK_VALUE(ourrouter->ifARouter), ourrouter->ifRouterState, 0); |
| 218 | |
| 219 | switch (ourrouter->ifRouterState) { |
| 220 | case ROUTER_AROUND : |
| 221 | /* This is where we lose our cable. |
| 222 | Reset router fields and state accordingly. */ |
| 223 | ourrouter->ifARouter.s_net = 0; |
| 224 | ourrouter->ifARouter.s_node = 0; |
| 225 | ourrouter->ifThisCableStart = DDP_MIN_NETWORK; |
| 226 | ourrouter->ifThisCableEnd = DDP_MAX_NETWORK; |
| 227 | ourrouter->ifRouterState = NO_ROUTER; |
| 228 | |
| 229 | /* Send event to indicate that we've lost our seed router. */ |
| 230 | atalk_post_msg(ourrouter->aa_ifp, KEV_ATALK_ROUTERDOWN, 0, 0); |
| 231 | |
| 232 | zip_control(ourrouter, ZIP_NO_ROUTER); |
| 233 | break; |
| 234 | case ROUTER_WARNING : |
| 235 | /* there was a router that we were ignoring... |
| 236 | * now, even that's gone. But we want to tackle the |
| 237 | * case where another router may come up after all |
| 238 | * of them have died... |
| 239 | */ |
| 240 | ourrouter->ifRouterState = NO_ROUTER; |
| 241 | break; |
| 242 | } |
| 243 | } |
| 244 | } else |
| 245 | bzero((caddr_t) deadrouter, sizeof(struct routerinfo)); |
| 246 | |
| 247 | atalk_unlock(); |
| 248 | |
| 249 | } /* ddp_age_router */ |
| 250 | |
| 251 | void rtmp_input (mp, ifID) |
| 252 | register gbuf_t *mp; |
| 253 | register at_ifaddr_t *ifID; |
| 254 | { |
| 255 | register at_net_al this_net; |
| 256 | register at_net_al range_start, range_end; |
| 257 | register at_ddp_t *ddp = (at_ddp_t *)gbuf_rptr(mp); |
| 258 | /* NOTE: there is an assumption here that the |
| 259 | * DATA follows the header. */ |
| 260 | register at_rtmp *rtmp = (at_rtmp *)ddp->data; |
| 261 | |
| 262 | if (gbuf_type(mp) != MSG_DATA) { |
| 263 | /* If this is a M_ERROR message, DDP is shutting down, |
| 264 | * nothing to do here...If it's something else, we don't |
| 265 | * understand what it is |
| 266 | */ |
| 267 | gbuf_freem(mp); |
| 268 | return; |
| 269 | } |
| 270 | |
| 271 | if (!ifID) { |
| 272 | gbuf_freem(mp); |
| 273 | return; |
| 274 | } |
| 275 | if (gbuf_len(mp) < (DDP_X_HDR_SIZE + sizeof(at_rtmp))) { |
| 276 | gbuf_freem(mp); |
| 277 | return; |
| 278 | } |
| 279 | this_net = ifID->ifThisNode.s_net; |
| 280 | if (rtmp->at_rtmp_id_length != 8) { |
| 281 | gbuf_freem(mp); |
| 282 | return; |
| 283 | } |
| 284 | |
| 285 | { |
| 286 | at_rtmp_tuple *tp; |
| 287 | tp = ((at_rtmp_tuple *)&rtmp->at_rtmp_id[1]); |
| 288 | range_start = NET_VALUE(tp->at_rtmp_net); |
| 289 | tp = ((at_rtmp_tuple *)&rtmp->at_rtmp_id[4]); |
| 290 | range_end = NET_VALUE(tp->at_rtmp_net); |
| 291 | |
| 292 | if (ifID->ifRouterState == ROUTER_AROUND) { |
| 293 | if ((ifID->ifThisCableStart == range_start) && |
| 294 | (ifID->ifThisCableEnd == range_end)) { |
| 295 | trackrouter(ifID, |
| 296 | NET_VALUE(rtmp->at_rtmp_this_net), |
| 297 | rtmp->at_rtmp_id[0] |
| 298 | ); |
| 299 | } |
| 300 | } else { |
| 301 | /* There was no router around earlier, one |
| 302 | * probably just came up. |
| 303 | */ |
| 304 | if ((this_net >= DDP_STARTUP_LOW) && |
| 305 | (this_net <= DDP_STARTUP_HIGH)) { |
| 306 | /* we're operating in the startup range, |
| 307 | * ignore the presence of router |
| 308 | */ |
| 309 | if (ifID->ifRouterState == NO_ROUTER) { |
| 310 | dPrintf(D_M_RTMP, D_L_INFO, ("rtmp_input: new router came up, INVALID: net \ |
| 311 | in startup range.\n")); |
| 312 | /* trackrouter sends a KEV_ATALK_ROUTERUP event to note that |
| 313 | a new router has come up when we had none before. */ |
| 314 | trackrouter(ifID, |
| 315 | NET_VALUE(rtmp->at_rtmp_this_net), |
| 316 | rtmp->at_rtmp_id[0] |
| 317 | ); |
| 318 | ifID->ifRouterState = ROUTER_WARNING; |
| 319 | |
| 320 | /* This router is invalid. Send event. */ |
| 321 | atalk_post_msg(ifID->aa_ifp, KEV_ATALK_ROUTERUP_INVALID, 0, 0); |
| 322 | } |
| 323 | } else { |
| 324 | /* our address |
| 325 | * is not in startup range; Is our |
| 326 | * address good for the cable?? |
| 327 | */ |
| 328 | if ((this_net >= range_start) && |
| 329 | (this_net <= range_end)) { |
| 330 | /* Our address is in the range |
| 331 | * valid for this cable... Note |
| 332 | * the router address and then |
| 333 | * get ZIP rolling to get the |
| 334 | * zone info. |
| 335 | */ |
| 336 | ifID->ifThisCableStart = range_start; |
| 337 | ifID->ifThisCableEnd = range_end; |
| 338 | |
| 339 | /* A seed router that gives us back our cable range came up. |
| 340 | It's a valid router and gives us our network back. */ |
| 341 | atalk_post_msg(ifID->aa_ifp, KEV_ATALK_ROUTERUP, 0, 0); |
| 342 | |
| 343 | trackrouter(ifID, |
| 344 | NET_VALUE(rtmp->at_rtmp_this_net), |
| 345 | rtmp->at_rtmp_id[0] |
| 346 | ); |
| 347 | zip_control(ifID, ZIP_LATE_ROUTER); |
| 348 | } else { |
| 349 | /* Our address is not in the |
| 350 | * range valid for this cable.. |
| 351 | * ignore presence of the |
| 352 | * router |
| 353 | */ |
| 354 | if (ifID->ifRouterState == NO_ROUTER) { |
| 355 | /* trackrouter sends a KEV_ATALK_ROUTERUP event to note that |
| 356 | a new router has come up when we had none before. */ |
| 357 | trackrouter(ifID, |
| 358 | NET_VALUE(rtmp->at_rtmp_this_net), |
| 359 | rtmp->at_rtmp_id[0] |
| 360 | ); |
| 361 | ifID->ifRouterState = ROUTER_WARNING; |
| 362 | |
| 363 | /* A new seed router came up, but the cable range is different |
| 364 | than what we had before. */ |
| 365 | atalk_post_msg(ifID->aa_ifp, KEV_ATALK_ROUTERUP_INVALID, 0, 0); |
| 366 | } |
| 367 | } |
| 368 | } |
| 369 | } |
| 370 | } |
| 371 | |
| 372 | gbuf_freem(mp); |
| 373 | return; |
| 374 | } |
| 375 | |
| 376 | |
| 377 | void rtmp_init() |
| 378 | { |
| 379 | bzero((caddr_t)trackedrouters, sizeof(struct routerinfo)*NROUTERS2TRAK); |
| 380 | } |
| 381 | |
| 382 | |