cregit-Linux how code gets into the kernel

Release 4.18 net/appletalk/ddp.c

Directory: net/appletalk
/*
 *      DDP:    An implementation of the AppleTalk DDP protocol for
 *              Ethernet 'ELAP'.
 *
 *              Alan Cox  <alan@lxorguk.ukuu.org.uk>
 *
 *              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 <linux/compat.h>
#include <linux/slab.h>
#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 <linux/highmem.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); }

Contributors

PersonTokensPropCommitsCommitProp
Arnaldo Carvalho de Melo20100.00%1100.00%
Total20100.00%1100.00%


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); }

Contributors

PersonTokensPropCommitsCommitProp
Arnaldo Carvalho de Melo1965.52%250.00%
Linus Torvalds (pre-git)1034.48%250.00%
Total29100.00%4100.00%


static struct sock *atalk_search_socket(struct sockaddr_at *to, struct atalk_iface *atif) { struct sock *s; read_lock_bh(&atalk_sockets_lock); sk_for_each(s, &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; }

Contributors

PersonTokensPropCommitsCommitProp
Linus Torvalds (pre-git)14679.78%872.73%
Arnaldo Carvalho de Melo2312.57%218.18%
David S. Miller147.65%19.09%
Total183100.00%11100.00%

/** * 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 atalk_sock *at; write_lock_bh(&atalk_sockets_lock); sk_for_each(s, &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; }

Contributors

PersonTokensPropCommitsCommitProp
Linus Torvalds (pre-git)6866.02%562.50%
Arnaldo Carvalho de Melo2524.27%225.00%
David S. Miller109.71%112.50%
Total103100.00%8100.00%


static void atalk_destroy_timer(struct timer_list *t) { struct sock *sk = from_timer(sk, t, sk_timer); if (sk_has_allocations(sk)) { sk->sk_timer.expires = jiffies + SOCK_DESTROY_TIME; add_timer(&sk->sk_timer); } else sock_put(sk); }

Contributors

PersonTokensPropCommitsCommitProp
Linus Torvalds (pre-git)3763.79%120.00%
Kees Cook1220.69%120.00%
Stephen Hemminger610.34%120.00%
Arnaldo Carvalho de Melo23.45%120.00%
Eric Dumazet11.72%120.00%
Total58100.00%5100.00%


static inline void atalk_destroy_socket(struct sock *sk) { atalk_remove_socket(sk); skb_queue_purge(&sk->sk_receive_queue); if (sk_has_allocations(sk)) { timer_setup(&sk->sk_timer, atalk_destroy_timer, 0); sk->sk_timer.expires = jiffies + SOCK_DESTROY_TIME; add_timer(&sk->sk_timer); } else sock_put(sk); }

Contributors

PersonTokensPropCommitsCommitProp
Linus Torvalds (pre-git)5274.29%541.67%
Stephen Hemminger68.57%18.33%
Arnaldo Carvalho de Melo45.71%18.33%
Pavel Emelyanov34.29%18.33%
Kees Cook22.86%18.33%
David S. Miller11.43%18.33%
Eric Dumazet11.43%18.33%
Linus Torvalds11.43%18.33%
Total70100.00%12100.00%

/**************************************************************************\ * * * 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); }

Contributors

PersonTokensPropCommitsCommitProp
Arnaldo Carvalho de Melo5762.64%218.18%
Linus Torvalds (pre-git)2426.37%763.64%
Stephen Hemminger55.49%19.09%
Linus Torvalds55.49%19.09%
Total91100.00%11100.00%


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; }

Contributors

PersonTokensPropCommitsCommitProp
Arnaldo Carvalho de Melo7171.72%333.33%
Linus Torvalds (pre-git)2222.22%444.44%
Stephen Hemminger55.05%111.11%
Panagiotis Issaris11.01%111.11%
Total99100.00%9100.00%

/* 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... */ }

Contributors

PersonTokensPropCommitsCommitProp
Linus Torvalds (pre-git)24795.00%675.00%
Linus Torvalds93.46%112.50%
Arnaldo Carvalho de Melo41.54%112.50%
Total260100.00%8100.00%

/* 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... */ }

Contributors

PersonTokensPropCommitsCommitProp
Linus Torvalds (pre-git)21393.83%981.82%
Linus Torvalds135.73%19.09%
Arnaldo Carvalho de Melo10.44%19.09%
Total227100.00%11100.00%


struct atalk_addr *atalk_find_dev_addr(struct net_device *dev) { struct atalk_iface *iface = dev->atalk_ptr; return iface ? &iface->address : NULL; }

Contributors

PersonTokensPropCommitsCommitProp
Linus Torvalds (pre-git)2787.10%571.43%
Linus Torvalds39.68%114.29%
Arnaldo Carvalho de Melo13.23%114.29%
Total31100.00%7100.00%


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; }

Contributors

PersonTokensPropCommitsCommitProp
Linus Torvalds (pre-git)12389.13%660.00%
Arnaldo Carvalho de Melo96.52%330.00%
Linus Torvalds64.35%110.00%
Total138100.00%10100.00%

/* * 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; }

Contributors

PersonTokensPropCommitsCommitProp
Linus Torvalds (pre-git)5270.27%777.78%
Arnaldo Carvalho de Melo2128.38%111.11%
Linus Torvalds11.35%111.11%
Total74100.00%9100.00%

/* 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; }

Contributors

PersonTokensPropCommitsCommitProp
Linus Torvalds (pre-git)12295.31%770.00%
Arnaldo Carvalho de Melo53.91%220.00%
Alexey Dobriyan10.78%110.00%
Total128100.00%10100.00%

/* * 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; }

Contributors

PersonTokensPropCommitsCommitProp
Linus Torvalds (pre-git)13392.36%866.67%
Linus Torvalds53.47%18.33%
Arnaldo Carvalho de Melo42.78%216.67%
Hideaki Yoshifuji / 吉藤英明21.39%18.33%
Total144100.00%12100.00%

/* * 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); return atr ? atr->dev : NULL; }

Contributors

PersonTokensPropCommitsCommitProp
Linus Torvalds (pre-git)2683.87%360.00%
Linus Torvalds412.90%120.00%
Arnaldo Carvalho de Melo13.23%120.00%
Total31100.00%5100.00%

/* 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; }

Contributors

PersonTokensPropCommitsCommitProp
Linus Torvalds (pre-git)42100.00%3100.00%
Total42100.00%3100.00%

/* * 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 = -EINVAL; /* * Fixme: Raise/Lower a routing change semaphore for these * operations. */ /* Validate the request */ if (ta->sat_family != AF_APPLETALK || (!devhint && ga->sat_family != AF_APPLETALK)) goto out; /* Now walk the routing table and make our decisions */ write_lock_bh(&atalk_routes_lock); for (rt = atalk_routes; rt; 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) { riface = NULL; read_lock_bh(&atalk_interfaces_lock); for (iface = atalk_interfaces; iface; iface = iface->next) { if (!riface && 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; } read_unlock_bh(&atalk_interfaces_lock); retval = -ENETUNREACH; if (!riface) goto out_unlock; devhint = riface->dev; } if (!rt) { rt = kzalloc(sizeof(*rt), GFP_ATOMIC); retval = -ENOBUFS; if (!rt) goto out_unlock; rt->next = atalk_routes; atalk_routes = rt; } /* Fill in the routing entry */ rt->target = ta->sat_addr; dev_hold(devhint); rt->dev = devhint; rt->flags = r->rt_flags; rt->gateway = ga->sat_addr; retval = 0; out_unlock: write_unlock_bh(&atalk_routes_lock); out: return retval; }

Contributors

PersonTokensPropCommitsCommitProp
Linus Torvalds (pre-git)35489.62%853.33%
Arnaldo Carvalho de Melo256.33%320.00%
Linus Torvalds92.28%16.67%
Herbert Xu51.27%16.67%
Panagiotis Issaris10.25%16.67%
Andries E. Brouwer10.25%16.67%
Total395100.00%15100.00%

/* Delete a route. Find it and discard it */
static int atrtr_delete(struct atalk_addr *addr) { struct atalk_route **r = &atalk_routes; int retval = 0; struct atalk_route *tmp; write_lock_bh(&atalk_routes_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; dev_put(tmp->dev); kfree(tmp); goto out; } r = &tmp->next; } retval = -ENOENT; out: write_unlock_bh(&atalk_routes_lock); return retval; }

Contributors

PersonTokensPropCommitsCommitProp
Linus Torvalds (pre-git)11387.60%660.00%
Stephen Hemminger75.43%110.00%
Linus Torvalds53.88%110.00%
Arnaldo Carvalho de Melo43.10%220.00%
Total129100.00%10100.00%

/* * Called when a device is downed. Just throw away any routes * via it. */
static void atrtr_device_down(struct net_device *dev) { struct atalk_route **r = &atalk_routes; struct atalk_route *tmp; write_lock_bh(&atalk_routes_lock); while ((tmp = *r) != NULL) { if (tmp->dev == dev) { *r = tmp->next; dev_put(dev); kfree(tmp); } else r = &tmp->next; } write_unlock_bh(&atalk_routes_lock); if (atrtr_default.dev == dev) atrtr_set_default(NULL); }

Contributors

PersonTokensPropCommitsCommitProp
Linus Torvalds (pre-git)8990.82%872.73%
Stephen Hemminger55.10%19.09%
Arnaldo Carvalho de Melo33.06%19.09%
Adrian Bunk11.02%19.09%
Total98100.00%11100.00%

/* 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 */ }

Contributors

PersonTokensPropCommitsCommitProp
Linus Torvalds (pre-git)30100.00%2100.00%
Total30100.00%2100.00%

/* * 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) { struct net_device *dev = netdev_notifier_info_to_dev(ptr); if (!net_eq(dev_net(dev), &init_net)) return NOTIFY_DONE; if (event == NETDEV_DOWN) /* Discard any use of this */ atalk_dev_down(dev); return NOTIFY_DONE; }

Contributors

PersonTokensPropCommitsCommitProp
Linus Torvalds (pre-git)3354.10%444.44%
Eric W. Biedermann1727.87%222.22%
Hideaki Yoshifuji / 吉藤英明813.11%222.22%
Jiri Pirko34.92%111.11%
Total61100.00%9100.00%

/* ioctl calls. Shouldn't even need touching */ /* Device configuration ioctl calls */
static int atif_ioctl(int cmd, void __user *arg) { static char aarp_mcast[6] = { 0x09, 0x00, 0x00, 0xFF, 0xFF, 0xFF }; struct ifreq atreq; struct atalk_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; dev = __dev_get_by_name(&init_net, atreq.ifr_name); if (!dev) 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 atalk_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)