📄 ddp.c
字号:
/* * DDP: An implementation of the AppleTalk DDP protocol for * Ethernet 'ELAP'. * * Alan Cox <Alan.Cox@linux.org> * * With more than a little assistance from * * Wesley Craig <netatalk@umich.edu> * * Fixes: * Neil Horman : Added missing device ioctls * Michael Callahan : Made routing work * Wesley Craig : Fix probing to listen to a * passed node id. * Alan Cox : Added send/recvmsg support * Alan Cox : Moved at. to protinfo in * socket. * Alan Cox : Added firewall hooks. * Alan Cox : Supports new ARPHRD_LOOPBACK * Christer Weinigel : Routing and /proc fixes. * Bradford Johnson : LocalTalk. * Tom Dyas : Module support. * Alan Cox : Hooks for PPP (based on the * LocalTalk hook). * Alan Cox : Posix bits * Alan Cox/Mike Freeman : Possible fix to NBP problems * Bradford Johnson : IP-over-DDP (experimental) * Jay Schulist : Moved IP-over-DDP to its own * driver file. (ipddp.c & ipddp.h) * Jay Schulist : Made work as module with * AppleTalk drivers, cleaned it. * Rob Newberry : Added proxy AARP and AARP * procfs, moved probing to AARP * module. * Adrian Sun/ * Michael Zuelsdorff : fix for net.0 packets. don't * allow illegal ether/tokentalk * port assignment. we lose a * valid localtalk port as a * result. * Arnaldo C. de Melo : Cleanup, in preparation for * shared skb support 8) * Arnaldo C. de Melo : Move proc stuff to atalk_proc.c, * use seq_file * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version * 2 of the License, or (at your option) any later version. * */#include <linux/capability.h>#include <linux/module.h>#include <linux/if_arp.h>#include <linux/termios.h> /* For TIOCOUTQ/INQ */#include <net/datalink.h>#include <net/psnap.h>#include <net/sock.h>#include <net/tcp_states.h>#include <net/route.h>#include <linux/atalk.h>#include "../core/kmap_skb.h"struct datalink_proto *ddp_dl, *aarp_dl;static const struct proto_ops atalk_dgram_ops;/**************************************************************************\* ** Handlers for the socket list. ** *\**************************************************************************/HLIST_HEAD(atalk_sockets);DEFINE_RWLOCK(atalk_sockets_lock);static inline void __atalk_insert_socket(struct sock *sk){ sk_add_node(sk, &atalk_sockets);}static inline void atalk_remove_socket(struct sock *sk){ write_lock_bh(&atalk_sockets_lock); sk_del_node_init(sk); write_unlock_bh(&atalk_sockets_lock);}static struct sock *atalk_search_socket(struct sockaddr_at *to, struct atalk_iface *atif){ struct sock *s; struct hlist_node *node; read_lock_bh(&atalk_sockets_lock); sk_for_each(s, node, &atalk_sockets) { struct atalk_sock *at = at_sk(s); if (to->sat_port != at->src_port) continue; if (to->sat_addr.s_net == ATADDR_ANYNET && to->sat_addr.s_node == ATADDR_BCAST) goto found; if (to->sat_addr.s_net == at->src_net && (to->sat_addr.s_node == at->src_node || to->sat_addr.s_node == ATADDR_BCAST || to->sat_addr.s_node == ATADDR_ANYNODE)) goto found; /* XXXX.0 -- we got a request for this router. make sure * that the node is appropriately set. */ if (to->sat_addr.s_node == ATADDR_ANYNODE && to->sat_addr.s_net != ATADDR_ANYNET && atif->address.s_node == at->src_node) { to->sat_addr.s_node = atif->address.s_node; goto found; } } s = NULL;found: read_unlock_bh(&atalk_sockets_lock); return s;}/** * atalk_find_or_insert_socket - Try to find a socket matching ADDR * @sk - socket to insert in the list if it is not there already * @sat - address to search for * * Try to find a socket matching ADDR in the socket list, if found then return * it. If not, insert SK into the socket list. * * This entire operation must execute atomically. */static struct sock *atalk_find_or_insert_socket(struct sock *sk, struct sockaddr_at *sat){ struct sock *s; struct hlist_node *node; struct atalk_sock *at; write_lock_bh(&atalk_sockets_lock); sk_for_each(s, node, &atalk_sockets) { at = at_sk(s); if (at->src_net == sat->sat_addr.s_net && at->src_node == sat->sat_addr.s_node && at->src_port == sat->sat_port) goto found; } s = NULL; __atalk_insert_socket(sk); /* Wheee, it's free, assign and insert. */found: write_unlock_bh(&atalk_sockets_lock); return s;}static void atalk_destroy_timer(unsigned long data){ struct sock *sk = (struct sock *)data; if (atomic_read(&sk->sk_wmem_alloc) || atomic_read(&sk->sk_rmem_alloc)) { sk->sk_timer.expires = jiffies + SOCK_DESTROY_TIME; add_timer(&sk->sk_timer); } else sock_put(sk);}static inline void atalk_destroy_socket(struct sock *sk){ atalk_remove_socket(sk); skb_queue_purge(&sk->sk_receive_queue); if (atomic_read(&sk->sk_wmem_alloc) || atomic_read(&sk->sk_rmem_alloc)) { init_timer(&sk->sk_timer); sk->sk_timer.expires = jiffies + SOCK_DESTROY_TIME; sk->sk_timer.function = atalk_destroy_timer; sk->sk_timer.data = (unsigned long)sk; add_timer(&sk->sk_timer); } else sock_put(sk);}/**************************************************************************\* ** Routing tables for the AppleTalk socket layer. ** *\**************************************************************************//* Anti-deadlock ordering is atalk_routes_lock --> iface_lock -DaveM */struct atalk_route *atalk_routes;DEFINE_RWLOCK(atalk_routes_lock);struct atalk_iface *atalk_interfaces;DEFINE_RWLOCK(atalk_interfaces_lock);/* For probing devices or in a routerless network */struct atalk_route atrtr_default;/* AppleTalk interface control *//* * Drop a device. Doesn't drop any of its routes - that is the caller's * problem. Called when we down the interface or delete the address. */static void atif_drop_device(struct net_device *dev){ struct atalk_iface **iface = &atalk_interfaces; struct atalk_iface *tmp; write_lock_bh(&atalk_interfaces_lock); while ((tmp = *iface) != NULL) { if (tmp->dev == dev) { *iface = tmp->next; dev_put(dev); kfree(tmp); dev->atalk_ptr = NULL; } else iface = &tmp->next; } write_unlock_bh(&atalk_interfaces_lock);}static struct atalk_iface *atif_add_device(struct net_device *dev, struct atalk_addr *sa){ struct atalk_iface *iface = kzalloc(sizeof(*iface), GFP_KERNEL); if (!iface) goto out; dev_hold(dev); iface->dev = dev; dev->atalk_ptr = iface; iface->address = *sa; iface->status = 0; write_lock_bh(&atalk_interfaces_lock); iface->next = atalk_interfaces; atalk_interfaces = iface; write_unlock_bh(&atalk_interfaces_lock);out: return iface;}/* Perform phase 2 AARP probing on our tentative address */static int atif_probe_device(struct atalk_iface *atif){ int netrange = ntohs(atif->nets.nr_lastnet) - ntohs(atif->nets.nr_firstnet) + 1; int probe_net = ntohs(atif->address.s_net); int probe_node = atif->address.s_node; int netct, nodect; /* Offset the network we start probing with */ if (probe_net == ATADDR_ANYNET) { probe_net = ntohs(atif->nets.nr_firstnet); if (netrange) probe_net += jiffies % netrange; } if (probe_node == ATADDR_ANYNODE) probe_node = jiffies & 0xFF; /* Scan the networks */ atif->status |= ATIF_PROBE; for (netct = 0; netct <= netrange; netct++) { /* Sweep the available nodes from a given start */ atif->address.s_net = htons(probe_net); for (nodect = 0; nodect < 256; nodect++) { atif->address.s_node = (nodect + probe_node) & 0xFF; if (atif->address.s_node > 0 && atif->address.s_node < 254) { /* Probe a proposed address */ aarp_probe_network(atif); if (!(atif->status & ATIF_PROBE_FAIL)) { atif->status &= ~ATIF_PROBE; return 0; } } atif->status &= ~ATIF_PROBE_FAIL; } probe_net++; if (probe_net > ntohs(atif->nets.nr_lastnet)) probe_net = ntohs(atif->nets.nr_firstnet); } atif->status &= ~ATIF_PROBE; return -EADDRINUSE; /* Network is full... */}/* Perform AARP probing for a proxy address */static int atif_proxy_probe_device(struct atalk_iface *atif, struct atalk_addr* proxy_addr){ int netrange = ntohs(atif->nets.nr_lastnet) - ntohs(atif->nets.nr_firstnet) + 1; /* we probe the interface's network */ int probe_net = ntohs(atif->address.s_net); int probe_node = ATADDR_ANYNODE; /* we'll take anything */ int netct, nodect; /* Offset the network we start probing with */ if (probe_net == ATADDR_ANYNET) { probe_net = ntohs(atif->nets.nr_firstnet); if (netrange) probe_net += jiffies % netrange; } if (probe_node == ATADDR_ANYNODE) probe_node = jiffies & 0xFF; /* Scan the networks */ for (netct = 0; netct <= netrange; netct++) { /* Sweep the available nodes from a given start */ proxy_addr->s_net = htons(probe_net); for (nodect = 0; nodect < 256; nodect++) { proxy_addr->s_node = (nodect + probe_node) & 0xFF; if (proxy_addr->s_node > 0 && proxy_addr->s_node < 254) { /* Tell AARP to probe a proposed address */ int ret = aarp_proxy_probe_network(atif, proxy_addr); if (ret != -EADDRINUSE) return ret; } } probe_net++; if (probe_net > ntohs(atif->nets.nr_lastnet)) probe_net = ntohs(atif->nets.nr_firstnet); } return -EADDRINUSE; /* Network is full... */}struct atalk_addr *atalk_find_dev_addr(struct net_device *dev){ struct atalk_iface *iface = dev->atalk_ptr; return iface ? &iface->address : NULL;}static struct atalk_addr *atalk_find_primary(void){ struct atalk_iface *fiface = NULL; struct atalk_addr *retval; struct atalk_iface *iface; /* * Return a point-to-point interface only if * there is no non-ptp interface available. */ read_lock_bh(&atalk_interfaces_lock); for (iface = atalk_interfaces; iface; iface = iface->next) { if (!fiface && !(iface->dev->flags & IFF_LOOPBACK)) fiface = iface; if (!(iface->dev->flags & (IFF_LOOPBACK | IFF_POINTOPOINT))) { retval = &iface->address; goto out; } } if (fiface) retval = &fiface->address; else if (atalk_interfaces) retval = &atalk_interfaces->address; else retval = NULL;out: read_unlock_bh(&atalk_interfaces_lock); return retval;}/* * Find a match for 'any network' - ie any of our interfaces with that * node number will do just nicely. */static struct atalk_iface *atalk_find_anynet(int node, struct net_device *dev){ struct atalk_iface *iface = dev->atalk_ptr; if (!iface || iface->status & ATIF_PROBE) goto out_err; if (node != ATADDR_BCAST && iface->address.s_node != node && node != ATADDR_ANYNODE) goto out_err;out: return iface;out_err: iface = NULL; goto out;}/* Find a match for a specific network:node pair */static struct atalk_iface *atalk_find_interface(__be16 net, int node){ struct atalk_iface *iface; read_lock_bh(&atalk_interfaces_lock); for (iface = atalk_interfaces; iface; 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; } read_unlock_bh(&atalk_interfaces_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 atalk_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 *net_route = NULL; struct atalk_route *r; read_lock_bh(&atalk_routes_lock); for (r = atalk_routes; r; 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) r = net_route; else if (atrtr_default.dev) r = &atrtr_default; else /* No route can be found */ r = NULL;out: read_unlock_bh(&atalk_routes_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 atalk_addr *sa){ struct atalk_route *atr = atrtr_find(sa);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -