📄 ddp.c
字号:
/* * Find a match for a specific network:node pair */static struct atalk_iface *atalk_find_interface(int net, int node){ struct atalk_iface *iface; spin_lock_bh(&atalk_iface_lock); for (iface = atalk_iface_list; iface != NULL; iface = iface->next) { if ((node == ATADDR_BCAST || node == ATADDR_ANYNODE || iface->address.s_node == node) && iface->address.s_net == net && !(iface->status & ATIF_PROBE)) break; /* XXXX.0 -- net.0 returns the iface associated with net */ if ((node == ATADDR_ANYNODE) && (net != ATADDR_ANYNET) && (ntohs(iface->nets.nr_firstnet) <= ntohs(net)) && (ntohs(net) <= ntohs(iface->nets.nr_lastnet))) break; } spin_unlock_bh(&atalk_iface_lock); return iface;}/* * Find a route for an AppleTalk packet. This ought to get cached in * the socket (later on...). We know about host routes and the fact * that a route must be direct to broadcast. */static struct atalk_route *atrtr_find(struct at_addr *target){ /* * we must search through all routes unless we find a * host route, because some host routes might overlap * network routes */ struct atalk_route *r; struct atalk_route *net_route = NULL; read_lock_bh(&atalk_router_lock); for (r = atalk_router_list; r != NULL; r = r->next) { if (!(r->flags & RTF_UP)) continue; if (r->target.s_net == target->s_net) { if (r->flags & RTF_HOST) { /* * if this host route is for the target, * the we're done */ if (r->target.s_node == target->s_node) goto out; } else { /* * this route will work if there isn't a * direct host route, so cache it */ net_route = r; } } } /* * if we found a network route but not a direct host * route, then return it */ if (net_route != NULL) { r = net_route; } else if (atrtr_default.dev) { r = &atrtr_default; } else { /* * No route can be found. */ r = NULL; }out: read_unlock_bh(&atalk_router_lock); return r;}/* * Given an AppleTalk network, find the device to use. This can be * a simple lookup. */struct net_device *atrtr_get_dev(struct at_addr *sa){ struct atalk_route *atr = atrtr_find(sa); if (atr == NULL) return NULL; else return atr->dev;}/* * Set up a default router. */static void atrtr_set_default(struct net_device *dev){ atrtr_default.dev = dev; atrtr_default.flags = RTF_UP; atrtr_default.gateway.s_net = htons(0); atrtr_default.gateway.s_node = 0;}/* * Add a router. Basically make sure it looks valid and stuff the * entry in the list. While it uses netranges we always set them to one * entry to work like netatalk. */static int atrtr_create(struct rtentry *r, struct net_device *devhint){ struct sockaddr_at *ta = (struct sockaddr_at *)&r->rt_dst; struct sockaddr_at *ga = (struct sockaddr_at *)&r->rt_gateway; struct atalk_route *rt; struct atalk_iface *iface, *riface; int retval; /* * Fixme: Raise/Lower a routing change semaphore for these * operations. */ /* * Validate the request */ if (ta->sat_family != AF_APPLETALK) return -EINVAL; if (devhint == NULL && ga->sat_family != AF_APPLETALK) return -EINVAL; /* * Now walk the routing table and make our decisions. */ write_lock_bh(&atalk_router_lock); for (rt = atalk_router_list; rt != NULL; rt = rt->next) { if (r->rt_flags != rt->flags) continue; if (ta->sat_addr.s_net == rt->target.s_net) { if (!(rt->flags & RTF_HOST)) break; if (ta->sat_addr.s_node == rt->target.s_node) break; } } if(devhint == NULL) { riface = NULL; spin_lock_bh(&atalk_iface_lock); for (iface = atalk_iface_list; iface; iface = iface->next) { if (riface == NULL && ntohs(ga->sat_addr.s_net) >= ntohs(iface->nets.nr_firstnet) && ntohs(ga->sat_addr.s_net) <= ntohs(iface->nets.nr_lastnet)) riface = iface; if (ga->sat_addr.s_net == iface->address.s_net && ga->sat_addr.s_node == iface->address.s_node) riface = iface; } spin_unlock_bh(&atalk_iface_lock); retval = -ENETUNREACH; if (riface == NULL) goto out; devhint = riface->dev; } if (rt == NULL) { rt = (struct atalk_route *) kmalloc(sizeof(struct atalk_route), GFP_ATOMIC); retval = -ENOBUFS; if (rt == NULL) goto out; rt->next = atalk_router_list; atalk_router_list = rt; } /* * Fill in the routing entry. */ rt->target = ta->sat_addr; rt->dev = devhint; rt->flags = r->rt_flags; rt->gateway = ga->sat_addr; retval = 0;out: write_unlock_bh(&atalk_router_lock); return retval;}/* * Delete a route. Find it and discard it. */static int atrtr_delete(struct at_addr * addr){ struct atalk_route **r = &atalk_router_list; struct atalk_route *tmp; int retval = 0; write_lock_bh(&atalk_router_lock); while ((tmp = *r) != NULL) { if (tmp->target.s_net == addr->s_net && (!(tmp->flags&RTF_GATEWAY) || tmp->target.s_node == addr->s_node)) { *r = tmp->next; kfree(tmp); goto out; } r = &tmp->next; } retval = -ENOENT;out: write_unlock_bh(&atalk_router_lock); return retval;}/* * Called when a device is downed. Just throw away any routes * via it. */void atrtr_device_down(struct net_device *dev){ struct atalk_route **r = &atalk_router_list; struct atalk_route *tmp; write_lock_bh(&atalk_router_lock); while ((tmp = *r) != NULL) { if (tmp->dev == dev) { *r = tmp->next; kfree(tmp); } else { r = &tmp->next; } } write_unlock_bh(&atalk_router_lock); if (atrtr_default.dev == dev) atrtr_set_default(NULL);}/* * Actually down the interface. */static inline void atalk_dev_down(struct net_device *dev){ atrtr_device_down(dev); /* Remove all routes for the device */ aarp_device_down(dev); /* Remove AARP entries for the device */ atif_drop_device(dev); /* Remove the device */}/* * A device event has occurred. Watch for devices going down and * delete our use of them (iface and route). */static int ddp_device_event(struct notifier_block *this, unsigned long event, void *ptr){ if (event == NETDEV_DOWN) { /* Discard any use of this */ atalk_dev_down((struct net_device *) ptr); } return NOTIFY_DONE;}/* * ioctl calls. Shouldn't even need touching. *//* * Device configuration ioctl calls. */int atif_ioctl(int cmd, void *arg){ struct ifreq atreq; static char aarp_mcast[6] = {0x09, 0x00, 0x00, 0xFF, 0xFF, 0xFF}; struct netrange *nr; struct sockaddr_at *sa; struct net_device *dev; struct atalk_iface *atif; int ct; int limit; struct rtentry rtdef; int add_route; if (copy_from_user(&atreq, arg, sizeof(atreq))) return -EFAULT; if ((dev = __dev_get_by_name(atreq.ifr_name)) == NULL) return -ENODEV; sa = (struct sockaddr_at*) &atreq.ifr_addr; atif = atalk_find_dev(dev); switch (cmd) { case SIOCSIFADDR: if (!capable(CAP_NET_ADMIN)) return -EPERM; if (sa->sat_family != AF_APPLETALK) return -EINVAL; if (dev->type != ARPHRD_ETHER && dev->type != ARPHRD_LOOPBACK && dev->type != ARPHRD_LOCALTLK && dev->type != ARPHRD_PPP) return -EPROTONOSUPPORT; nr = (struct netrange *) &sa->sat_zero[0]; add_route = 1; /* * if this is a point-to-point iface, and we already have an * iface for this AppleTalk address, then we should not add a route */ if ((dev->flags & IFF_POINTOPOINT) && atalk_find_interface(sa->sat_addr.s_net, sa->sat_addr.s_node)) { printk(KERN_DEBUG "AppleTalk: point-to-point interface added with existing address\n"); add_route = 0; } /* * Phase 1 is fine on LocalTalk but we don't do * EtherTalk phase 1. Anyone wanting to add it go ahead. */ if (dev->type == ARPHRD_ETHER && nr->nr_phase != 2) return -EPROTONOSUPPORT; if (sa->sat_addr.s_node == ATADDR_BCAST || sa->sat_addr.s_node == 254) return -EINVAL; if (atif) { /* * Already setting address. */ if (atif->status & ATIF_PROBE) return -EBUSY; atif->address.s_net = sa->sat_addr.s_net; atif->address.s_node = sa->sat_addr.s_node; atrtr_device_down(dev); /* Flush old routes */ } else { atif = atif_add_device(dev, &sa->sat_addr); if (atif == NULL) return -ENOMEM; } atif->nets = *nr; /* * Check if the chosen address is used. If so we * error and atalkd will try another. */ if (!(dev->flags & IFF_LOOPBACK) && !(dev->flags & IFF_POINTOPOINT) && atif_probe_device(atif) < 0) { atif_drop_device(dev); return -EADDRINUSE; } /* * Hey it worked - add the direct routes. */ sa = (struct sockaddr_at *) &rtdef.rt_gateway; sa->sat_family = AF_APPLETALK; sa->sat_addr.s_net = atif->address.s_net; sa->sat_addr.s_node = atif->address.s_node; sa = (struct sockaddr_at *) &rtdef.rt_dst; rtdef.rt_flags = RTF_UP; sa->sat_family = AF_APPLETALK; sa->sat_addr.s_node = ATADDR_ANYNODE; if ((dev->flags & IFF_LOOPBACK) || (dev->flags & IFF_POINTOPOINT)) rtdef.rt_flags |= RTF_HOST; /* * Routerless initial state. */ if (nr->nr_firstnet == htons(0) && nr->nr_lastnet == htons(0xFFFE)) { sa->sat_addr.s_net = atif->address.s_net; atrtr_create(&rtdef, dev); atrtr_set_default(dev); } else { limit = ntohs(nr->nr_lastnet); if (limit - ntohs(nr->nr_firstnet) > 4096) { printk(KERN_WARNING "Too many routes/iface.\n"); return -EINVAL; } if (add_route) { for(ct = ntohs(nr->nr_firstnet);ct <= limit; ct++) { sa->sat_addr.s_net = htons(ct); atrtr_create(&rtdef, dev); } } } dev_mc_add(dev, aarp_mcast, 6, 1); return 0; case SIOCGIFADDR: if (atif == NULL) return -EADDRNOTAVAIL; ((struct sockaddr_at *)(&atreq.ifr_addr))->sat_family = AF_APPLETALK; ((struct sockaddr_at *)(&atreq.ifr_addr))->sat_addr = atif->address; break; case SIOCGIFBRDADDR: if (atif == NULL) return -EADDRNOTAVAIL; ((struct sockaddr_at *)(&atreq.ifr_addr))->sat_family = AF_APPLETALK; ((struct sockaddr_at *)(&atreq.ifr_addr))->sat_addr.s_net = atif->address.s_net; ((struct sockaddr_at *)(&atreq.ifr_addr))->sat_addr.s_node = ATADDR_BCAST; break; case SIOCATALKDIFADDR: case SIOCDIFADDR: if (!capable(CAP_NET_ADMIN)) return -EPERM; if (sa->sat_family != AF_APPLETALK) return -EINVAL; atalk_dev_down(dev); break; case SIOCSARP: if (!capable(CAP_NET_ADMIN)) return -EPERM; if (sa->sat_family != AF_APPLETALK) return -EINVAL; if (atif == NULL) return -EADDRNOTAVAIL; /* * for now, we only support proxy AARP on ELAP; * we should be able to do it for LocalTalk, too. */ if (dev->type != ARPHRD_ETHER) return -EPROTONOSUPPORT; /* * atif points to the current interface on this network; * we aren't concerned about its current status (at least for now), * but it has all the settings about the network we're going * to probe. consequently, it must exist. */ if (!atif) return -EADDRNOTAVAIL; nr = (struct netrange *) &(atif->nets); /* * Phase 1 is fine on Localtalk but we don't do * Ethertalk phase 1. Anyone wanting to add it go ahead. */ if (dev->type == ARPHRD_ETHER && nr->nr_phase != 2) return -EPROTONOSUPPORT; if (sa->sat_addr.s_node == ATADDR_BCAST || sa->sat_addr.s_node == 254) return -EINVAL; /* * Check if the chosen address is used. If so we * error and ATCP will try another. */ if (atif_proxy_probe_device(atif, &(sa->sat_addr)) < 0) return -EADDRINUSE; /* * We now have an address on the local network, and the AARP * code will defend it for us until we take it down. * We don't set up any routes right now, because ATCP will * install them manually via SIOCADDRT. */ break; case SIOCDARP: if (!capable(CAP_NET_ADMIN)) return -EPERM; if (sa->sat_family != AF_APPLETALK) return -EINVAL; if (atif == NULL) return -EADDRNOTAVAIL; /* * give to aarp module to remove proxy entry */ aarp_proxy_remove(atif->dev, &(sa->sat_addr)); return 0; }; if (copy_to_user(arg, &atreq, sizeof(atreq))) return -EFAULT; return 0;}/* * Routing ioctl() calls */static int atrtr_ioctl(unsigned int cmd, void *arg){ struct rtentry rt; struct net_device *dev = NULL; if (copy_from_user(&rt, arg, sizeof(rt))) return -EFAULT; switch (cmd) { case SIOCDELRT: if (rt.rt_dst.sa_family != AF_APPLETALK) return -EINVAL; return atrtr_delete(&((struct sockaddr_at *)&rt.rt_dst)->sat_addr); case SIOCADDRT: /* FIX ME: the name of the device is still in user space, isn't it? */ if (rt.rt_dev != NULL) { if ((dev = __dev_get_by_name(rt.rt_dev)) == NULL) return -ENODEV; } return atrtr_create(&rt, dev); default: return -EINVAL; };}/* Called from proc fs - just make it print the ifaces neatly */static int atalk_if_get_info(char *buffer, char **start, off_t offset, int length){ struct atalk_iface *iface;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -