* 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.
*/
#include <linux/config.h>
+#include <linux/capability.h>
#include <linux/module.h>
-#include <linux/tcp.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>
-extern 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 atalk_addr *sa);
-extern void aarp_proxy_remove(struct net_device *dev, struct atalk_addr *sa);
-
-extern void atalk_register_sysctl(void);
-extern void atalk_unregister_sysctl(void);
-
struct datalink_proto *ddp_dl, *aarp_dl;
-static struct proto_ops atalk_dgram_ops;
+static const struct proto_ops atalk_dgram_ops;
/**************************************************************************\
* *
\**************************************************************************/
HLIST_HEAD(atalk_sockets);
-rwlock_t atalk_sockets_lock = RW_LOCK_UNLOCKED;
+DEFINE_RWLOCK(atalk_sockets_lock);
static inline void __atalk_insert_socket(struct sock *sk)
{
sk_add_node(sk, &atalk_sockets);
}
-static inline void atalk_insert_socket(struct sock *sk)
-{
- write_lock_bh(&atalk_sockets_lock);
- __atalk_insert_socket(sk);
- write_unlock_bh(&atalk_sockets_lock);
-}
-
static inline void atalk_remove_socket(struct sock *sk)
{
write_lock_bh(&atalk_sockets_lock);
continue;
if (to->sat_addr.s_net == ATADDR_ANYNET &&
- to->sat_addr.s_node == ATADDR_BCAST &&
- at->src_net == atif->address.s_net)
+ to->sat_addr.s_node == ATADDR_BCAST)
goto found;
if (to->sat_addr.s_net == at->src_net &&
/* Anti-deadlock ordering is atalk_routes_lock --> iface_lock -DaveM */
struct atalk_route *atalk_routes;
-rwlock_t atalk_routes_lock = RW_LOCK_UNLOCKED;
+DEFINE_RWLOCK(atalk_routes_lock);
struct atalk_iface *atalk_interfaces;
-rwlock_t atalk_interfaces_lock = RW_LOCK_UNLOCKED;
+DEFINE_RWLOCK(atalk_interfaces_lock);
/* For probing devices or in a routerless network */
struct atalk_route atrtr_default;
}
/* Find a match for a specific network:node pair */
-static struct atalk_iface *atalk_find_interface(int net, int node)
+static struct atalk_iface *atalk_find_interface(__be16 net, int node)
{
struct atalk_iface *iface;
retval = -ENOBUFS;
if (!rt)
- goto out;
+ goto out_unlock;
memset(rt, 0, sizeof(*rt));
rt->next = atalk_routes;
/* 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;
* Called when a device is downed. Just throw away any routes
* via it.
*/
-void atrtr_device_down(struct net_device *dev)
+static void atrtr_device_down(struct net_device *dev)
{
struct atalk_route **r = &atalk_routes;
struct atalk_route *tmp;
/* ioctl calls. Shouldn't even need touching */
/* Device configuration ioctl calls */
-static int atif_ioctl(int cmd, void *arg)
+static int atif_ioctl(int cmd, void __user *arg)
{
static char aarp_mcast[6] = { 0x09, 0x00, 0x00, 0xFF, 0xFF, 0xFF };
struct ifreq atreq;
}
/* Routing ioctl() calls */
-static int atrtr_ioctl(unsigned int cmd, void *arg)
+static int atrtr_ioctl(unsigned int cmd, void __user *arg)
{
struct rtentry rt;
case SIOCADDRT: {
struct net_device *dev = NULL;
- /*
- * FIXME: the name of the device is still in user
- * space, isn't it?
- */
if (rt.rt_dev) {
- dev = __dev_get_by_name(rt.rt_dev);
+ char name[IFNAMSIZ];
+ if (copy_from_user(name, rt.rt_dev, IFNAMSIZ-1))
+ return -EFAULT;
+ name[IFNAMSIZ-1] = '\0';
+ dev = __dev_get_by_name(name);
if (!dev)
return -ENODEV;
}
return sum ? htons((unsigned short)sum) : 0xFFFF;
}
+static struct proto ddp_proto = {
+ .name = "DDP",
+ .owner = THIS_MODULE,
+ .obj_size = sizeof(struct atalk_sock),
+};
+
/*
* Create a socket. Initialise the socket, blank the addresses
* set the state.
static int atalk_create(struct socket *sock, int protocol)
{
struct sock *sk;
- struct atalk_sock *at;
int rc = -ESOCKTNOSUPPORT;
/*
if (sock->type != SOCK_RAW && sock->type != SOCK_DGRAM)
goto out;
rc = -ENOMEM;
- sk = sk_alloc(PF_APPLETALK, GFP_KERNEL, 1, NULL);
+ sk = sk_alloc(PF_APPLETALK, GFP_KERNEL, &ddp_proto, 1);
if (!sk)
goto out;
- at = sk->sk_protinfo = kmalloc(sizeof(*at), GFP_KERNEL);
- if (!at)
- goto outsk;
- memset(at, 0, sizeof(*at));
rc = 0;
sock->ops = &atalk_dgram_ops;
sock_init_data(sock, sk);
- sk_set_owner(sk, THIS_MODULE);
/* Checksums on by default */
- sk->sk_zapped = 1;
+ sock_set_flag(sk, SOCK_ZAPPED);
out:
return rc;
-outsk:
- sk_free(sk);
- goto out;
}
/* Free a socket. No work needed */
n = atalk_pick_and_bind_port(sk, &sat);
if (!n)
- sk->sk_zapped = 0;
+ sock_reset_flag(sk, SOCK_ZAPPED);
out:
return n;
}
struct sock *sk = sock->sk;
struct atalk_sock *at = at_sk(sk);
- if (!sk->sk_zapped || addr_len != sizeof(struct sockaddr_at))
+ if (!sock_flag(sk, SOCK_ZAPPED) ||
+ addr_len != sizeof(struct sockaddr_at))
return -EINVAL;
if (addr->sat_family != AF_APPLETALK)
return -EADDRINUSE;
}
- sk->sk_zapped = 0;
+ sock_reset_flag(sk, SOCK_ZAPPED);
return 0;
}
#endif
}
- if (sk->sk_zapped)
+ if (sock_flag(sk, SOCK_ZAPPED))
if (atalk_autobind(sk) < 0)
return -EBUSY;
struct sock *sk = sock->sk;
struct atalk_sock *at = at_sk(sk);
- if (sk->sk_zapped)
+ if (sock_flag(sk, SOCK_ZAPPED))
if (atalk_autobind(sk) < 0)
return -ENOBUFS;
* [ie ARPHRD_ETHERTALK]
*/
static int atalk_rcv(struct sk_buff *skb, struct net_device *dev,
- struct packet_type *pt)
+ struct packet_type *pt, struct net_device *orig_dev)
{
struct ddpehdr *ddp;
struct sock *sock;
else
atif = atalk_find_interface(ddp->deh_dnet, ddp->deh_dnode);
- /* Not ours, so we route the packet via the correct AppleTalk iface */
if (!atif) {
+ /* Not ours, so we route the packet via the correct
+ * AppleTalk iface
+ */
atalk_route_packet(skb, dev, ddp, &ddphv, origlen);
goto out;
}
* header and append a long one.
*/
static int ltalk_rcv(struct sk_buff *skb, struct net_device *dev,
- struct packet_type *pt)
+ struct packet_type *pt, struct net_device *orig_dev)
{
/* Expand any short form frames */
if (skb->mac.raw[2] == 1) {
}
skb->h.raw = skb->data;
- return atalk_rcv(skb, dev, pt);
+ return atalk_rcv(skb, dev, pt, orig_dev);
freeit:
kfree_skb(skb);
return 0;
struct atalk_route *rt;
int err;
- if (flags & ~MSG_DONTWAIT)
+ if (flags & ~(MSG_DONTWAIT|MSG_CMSG_COMPAT))
return -EINVAL;
if (len > DDP_MAXSZ)
return -EMSGSIZE;
if (usat) {
- if (sk->sk_zapped)
+ if (sock_flag(sk, SOCK_ZAPPED))
if (atalk_autobind(sk) < 0)
return -EBUSY;
if (usat->sat_addr.s_net || usat->sat_addr.s_node == ATADDR_ANYNODE) {
rt = atrtr_find(&usat->sat_addr);
- if (!rt)
- return -ENETUNREACH;
-
dev = rt->dev;
} else {
struct atalk_addr at_hint;
at_hint.s_net = at->src_net;
rt = atrtr_find(&at_hint);
- if (!rt)
- return -ENETUNREACH;
-
dev = rt->dev;
}
+ if (!rt)
+ return -ENETUNREACH;
+
+ dev = rt->dev;
SOCK_DEBUG(sk, "SK %p: Size needed %d, device %s\n",
sk, size, dev->name);
SOCK_DEBUG(sk, "SK %p: Loop back.\n", sk);
/* loop back */
skb_orphan(skb);
+ if (ddp->deh_dnode == ATADDR_BCAST) {
+ struct atalk_addr at_lo;
+
+ at_lo.s_node = 0;
+ at_lo.s_net = 0;
+
+ rt = atrtr_find(&at_lo);
+ if (!rt) {
+ kfree_skb(skb);
+ return -ENETUNREACH;
+ }
+ dev = rt->dev;
+ skb->dev = dev;
+ }
ddp_dl->request(ddp_dl, skb, dev->dev_addr);
} else {
SOCK_DEBUG(sk, "SK %p: send out.\n", sk);
*/
static int atalk_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg)
{
- int rc = -EINVAL;
+ int rc = -ENOIOCTLCMD;
struct sock *sk = sock->sk;
+ void __user *argp = (void __user *)arg;
switch (cmd) {
/* Protocol layer */
if (amount < 0)
amount = 0;
- rc = put_user(amount, (int *)arg);
+ rc = put_user(amount, (int __user *)argp);
break;
}
case TIOCINQ: {
if (skb)
amount = skb->len - sizeof(struct ddpehdr);
- rc = put_user(amount, (int *)arg);
+ rc = put_user(amount, (int __user *)argp);
break;
}
case SIOCGSTAMP:
- rc = sock_get_timestamp(sk, (struct timeval *)arg);
+ rc = sock_get_timestamp(sk, argp);
break;
/* Routing */
case SIOCADDRT:
case SIOCDELRT:
rc = -EPERM;
if (capable(CAP_NET_ADMIN))
- rc = atrtr_ioctl(cmd, (void *)arg);
+ rc = atrtr_ioctl(cmd, argp);
break;
/* Interface */
case SIOCGIFADDR:
case SIOCSARP: /* proxy AARP */
case SIOCDARP: /* proxy AARP */
rtnl_lock();
- rc = atif_ioctl(cmd, (void *)arg);
+ rc = atif_ioctl(cmd, argp);
rtnl_unlock();
break;
- /* Physical layer ioctl calls */
- case SIOCSIFLINK:
- case SIOCGIFHWADDR:
- case SIOCSIFHWADDR:
- case SIOCGIFFLAGS:
- case SIOCSIFFLAGS:
- case SIOCGIFMTU:
- case SIOCGIFCONF:
- case SIOCADDMULTI:
- case SIOCDELMULTI:
- case SIOCGIFCOUNT:
- case SIOCGIFINDEX:
- case SIOCGIFNAME:
- rc = dev_ioctl(cmd, (void *)arg);
- break;
}
return rc;
}
+
+#ifdef CONFIG_COMPAT
+static int atalk_compat_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg)
+{
+ /*
+ * All Appletalk ioctls except SIOCATALKDIFADDR are standard. And
+ * SIOCATALKDIFADDR is handled by upper layer as well, so there is
+ * nothing to do. Eventually SIOCATALKDIFADDR should be moved
+ * here so there is no generic SIOCPROTOPRIVATE translation in the
+ * system.
+ */
+ return -ENOIOCTLCMD;
+}
+#endif
+
+
static struct net_proto_family atalk_family_ops = {
.family = PF_APPLETALK,
.create = atalk_create,
.owner = THIS_MODULE,
};
-static struct proto_ops SOCKOPS_WRAPPED(atalk_dgram_ops) = {
+static const struct proto_ops SOCKOPS_WRAPPED(atalk_dgram_ops) = {
.family = PF_APPLETALK,
.owner = THIS_MODULE,
.release = atalk_release,
.getname = atalk_getname,
.poll = datagram_poll,
.ioctl = atalk_ioctl,
+#ifdef CONFIG_COMPAT
+ .compat_ioctl = atalk_compat_ioctl,
+#endif
.listen = sock_no_listen,
.shutdown = sock_no_shutdown,
.setsockopt = sock_no_setsockopt,
.notifier_call = ddp_device_event,
};
-struct packet_type ltalk_packet_type = {
+static struct packet_type ltalk_packet_type = {
.type = __constant_htons(ETH_P_LOCALTALK),
.func = ltalk_rcv,
};
-struct packet_type ppptalk_packet_type = {
+static struct packet_type ppptalk_packet_type = {
.type = __constant_htons(ETH_P_PPPTALK),
.func = atalk_rcv,
};
/* Called by proto.c on kernel start up */
static int __init atalk_init(void)
{
+ int rc = proto_register(&ddp_proto, 0);
+
+ if (rc != 0)
+ goto out;
+
(void)sock_register(&atalk_family_ops);
ddp_dl = register_snap_client(ddp_snap_id, atalk_rcv);
if (!ddp_dl)
aarp_proto_init();
atalk_proc_init();
atalk_register_sysctl();
- return 0;
+out:
+ return rc;
}
module_init(atalk_init);
dev_remove_pack(&ppptalk_packet_type);
unregister_snap_client(ddp_dl);
sock_unregister(PF_APPLETALK);
+ proto_unregister(&ddp_proto);
}
module_exit(atalk_exit);