📄 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: * 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) * * 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/config.h>#if defined(CONFIG_ATALK) || defined(CONFIG_ATALK_MODULE)#include <linux/module.h>#include <asm/uaccess.h>#include <asm/system.h>#include <asm/bitops.h>#include <linux/types.h>#include <linux/kernel.h>#include <linux/sched.h>#include <linux/string.h>#include <linux/mm.h>#include <linux/socket.h>#include <linux/sockios.h>#include <linux/in.h>#include <linux/errno.h>#include <linux/interrupt.h>#include <linux/if_ether.h>#include <linux/notifier.h>#include <linux/netdevice.h>#include <linux/inetdevice.h>#include <linux/route.h>#include <linux/inet.h>#include <linux/etherdevice.h>#include <linux/if_arp.h>#include <linux/skbuff.h>#include <linux/spinlock.h>#include <linux/termios.h> /* For TIOCOUTQ/INQ */#include <net/datalink.h>#include <net/p8022.h>#include <net/psnap.h>#include <net/sock.h>#include <linux/ip.h>#include <net/route.h>#include <linux/atalk.h>#include <linux/proc_fs.h>#include <linux/stat.h>#include <linux/init.h>#ifdef CONFIG_PROC_FSextern void aarp_register_proc_fs(void);extern void aarp_unregister_proc_fs(void);#endifextern void aarp_cleanup_module(void);extern void aarp_probe_network(struct atalk_iface *atif);extern int aarp_proxy_probe_network(struct atalk_iface *atif, struct at_addr *sa);extern void aarp_proxy_remove(struct net_device *dev, struct at_addr *sa);#undef APPLETALK_DEBUG#ifdef APPLETALK_DEBUG#define DPRINT(x) print(x)#else#define DPRINT(x)#endif /* APPLETALK_DEBUG */#ifdef CONFIG_SYSCTLextern inline void atalk_register_sysctl(void);extern inline void atalk_unregister_sysctl(void);#endif /* CONFIG_SYSCTL */struct datalink_proto *ddp_dl, *aarp_dl;static struct proto_ops atalk_dgram_ops;/**************************************************************************\* ** Handlers for the socket list. ** *\**************************************************************************/static struct sock *atalk_sockets;static spinlock_t atalk_sockets_lock = SPIN_LOCK_UNLOCKED;extern inline void atalk_insert_socket(struct sock *sk){ spin_lock_bh(&atalk_sockets_lock); sk->next = atalk_sockets; if (sk->next) atalk_sockets->pprev = &sk->next; atalk_sockets = sk; sk->pprev = &atalk_sockets; spin_unlock_bh(&atalk_sockets_lock);}extern inline void atalk_remove_socket(struct sock *sk){ spin_lock_bh(&atalk_sockets_lock); if (sk->pprev) { if (sk->next) sk->next->pprev = sk->pprev; *sk->pprev = sk->next; sk->pprev = NULL; } spin_unlock_bh(&atalk_sockets_lock);}static struct sock *atalk_search_socket(struct sockaddr_at *to, struct atalk_iface *atif){ struct sock *s; spin_lock_bh(&atalk_sockets_lock); for (s = atalk_sockets; s; s = s->next) { if (to->sat_port != s->protinfo.af_at.src_port) continue; if (to->sat_addr.s_net == ATADDR_ANYNET && to->sat_addr.s_node == ATADDR_BCAST && s->protinfo.af_at.src_net == atif->address.s_net) break; if (to->sat_addr.s_net == s->protinfo.af_at.src_net && (to->sat_addr.s_node == s->protinfo.af_at.src_node || to->sat_addr.s_node == ATADDR_BCAST || to->sat_addr.s_node == ATADDR_ANYNODE)) break; /* 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 == s->protinfo.af_at.src_node) { to->sat_addr.s_node = atif->address.s_node; break; } } spin_unlock_bh(&atalk_sockets_lock); return s;}/* * 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; spin_lock_bh(&atalk_sockets_lock); for (s = atalk_sockets; s; s = s->next) if (s->protinfo.af_at.src_net == sat->sat_addr.s_net && s->protinfo.af_at.src_node == sat->sat_addr.s_node && s->protinfo.af_at.src_port == sat->sat_port) break; if (!s) { /* Wheee, it's free, assign and insert. */ sk->next = atalk_sockets; if (sk->next) atalk_sockets->pprev = &sk->next; atalk_sockets = sk; sk->pprev = &atalk_sockets; } spin_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->wmem_alloc) && !atomic_read(&sk->rmem_alloc) && sk->dead) { sock_put(sk); MOD_DEC_USE_COUNT; } else { sk->timer.expires = jiffies + SOCK_DESTROY_TIME; add_timer(&sk->timer); }}extern inline void atalk_destroy_socket(struct sock *sk){ atalk_remove_socket(sk); skb_queue_purge(&sk->receive_queue); if (!atomic_read(&sk->wmem_alloc) && !atomic_read(&sk->rmem_alloc) && sk->dead) { sock_put(sk); MOD_DEC_USE_COUNT; } else { init_timer(&sk->timer); sk->timer.expires = jiffies + SOCK_DESTROY_TIME; sk->timer.function = atalk_destroy_timer; sk->timer.data = (unsigned long) sk; add_timer(&sk->timer); }}/* Called from proc fs */static int atalk_get_info(char *buffer, char **start, off_t offset, int length){ off_t pos = 0; off_t begin = 0; int len = sprintf(buffer, "Type local_addr remote_addr tx_queue " "rx_queue st uid\n"); struct sock *s; /* Output the AppleTalk data for the /proc filesystem */ spin_lock_bh(&atalk_sockets_lock); for (s = atalk_sockets; s; s = s->next) { len += sprintf(buffer + len,"%02X ", s->type); len += sprintf(buffer + len,"%04X:%02X:%02X ", ntohs(s->protinfo.af_at.src_net), s->protinfo.af_at.src_node, s->protinfo.af_at.src_port); len += sprintf(buffer + len,"%04X:%02X:%02X ", ntohs(s->protinfo.af_at.dest_net), s->protinfo.af_at.dest_node, s->protinfo.af_at.dest_port); len += sprintf(buffer + len,"%08X:%08X ", atomic_read(&s->wmem_alloc), atomic_read(&s->rmem_alloc)); len += sprintf(buffer + len,"%02X %d\n", s->state, SOCK_INODE(s->socket)->i_uid); /* Are we still dumping unwanted data then discard the record */ pos = begin + len; if (pos < offset) { len = 0; /* Keep dumping into the buffer start */ begin = pos; } if (pos > offset + length) /* We have dumped enough */ break; } spin_unlock_bh(&atalk_sockets_lock); /* The data in question runs from begin to begin+len */ *start = buffer + offset - begin; /* Start of wanted data */ len -= offset - begin; /* Remove unwanted header data from length */ if (len > length) len = length; /* Remove unwanted tail data from length */ return len;}/**************************************************************************\* ** Routing tables for the AppleTalk socket layer. ** *\**************************************************************************//* Anti-deadlock ordering is router_lock --> iface_lock -DaveM */static struct atalk_route *atalk_router_list;static rwlock_t atalk_router_lock = RW_LOCK_UNLOCKED;static struct atalk_iface *atalk_iface_list;static spinlock_t atalk_iface_lock = SPIN_LOCK_UNLOCKED;/* For probing devices or in a routerless network */static 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_iface_list; struct atalk_iface *tmp; spin_lock_bh(&atalk_iface_lock); while ((tmp = *iface) != NULL) { if (tmp->dev == dev) { *iface = tmp->next; kfree(tmp); dev->atalk_ptr = NULL; MOD_DEC_USE_COUNT; } else iface = &tmp->next; } spin_unlock_bh(&atalk_iface_lock);}static struct atalk_iface *atif_add_device(struct net_device *dev, struct at_addr *sa){ struct atalk_iface *iface = kmalloc(sizeof(*iface), GFP_KERNEL); if (!iface) return NULL; iface->dev = dev; dev->atalk_ptr = iface; iface->address = *sa; iface->status = 0; spin_lock_bh(&atalk_iface_lock); iface->next = atalk_iface_list; atalk_iface_list = iface; spin_unlock_bh(&atalk_iface_lock); MOD_INC_USE_COUNT; 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 at_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 at_addr *atalk_find_dev_addr(struct net_device *dev){ struct atalk_iface *iface = dev->atalk_ptr; return iface ? &iface->address : NULL;}static struct at_addr *atalk_find_primary(void){ struct atalk_iface *fiface = NULL; struct at_addr *retval; struct atalk_iface *iface; /* * Return a point-to-point interface only if * there is no non-ptp interface available. */ spin_lock_bh(&atalk_iface_lock); for (iface = atalk_iface_list; 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_iface_list) retval = &atalk_iface_list->address; else retval = NULL;out: spin_unlock_bh(&atalk_iface_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) return NULL; if (node == ATADDR_BCAST || iface->address.s_node == node || node == ATADDR_ANYNODE) return iface; return NULL;}/* Find a match for a specific network:node pair */static struct atalk_iface *atalk_find_interface(int net, int node){ struct atalk_iface *iface;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -