X-Git-Url: http://git.onelab.eu/?a=blobdiff_plain;f=net%2Fappletalk%2Fddp.c;h=7c69506a17bcf8497bab91606b3a0886e70582b6;hb=refs%2Fheads%2Fvserver;hp=4185d7b8ed0244d6f7df5ad5357bd92e3e628ffa;hpb=9bf4aaab3e101692164d49b7ca357651eb691cb6;p=linux-2.6.git diff --git a/net/appletalk/ddp.c b/net/appletalk/ddp.c index 4185d7b8e..7c69506a1 100644 --- a/net/appletalk/ddp.c +++ b/net/appletalk/ddp.c @@ -9,6 +9,7 @@ * Wesley Craig * * Fixes: + * Neil Horman : Added missing device ioctls * Michael Callahan : Made routing work * Wesley Craig : Fix probing to listen to a * passed node id. @@ -50,29 +51,20 @@ * */ -#include +#include #include -#include #include #include /* For TIOCOUTQ/INQ */ #include #include #include +#include #include #include - -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); +#include "../core/kmap_skb.h" struct datalink_proto *ddp_dl, *aarp_dl; -static struct proto_ops atalk_dgram_ops; +static const struct proto_ops atalk_dgram_ops; /**************************************************************************\ * * @@ -81,20 +73,13 @@ static 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); @@ -116,8 +101,7 @@ static struct sock *atalk_search_socket(struct sockaddr_at *to, 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 && @@ -210,10 +194,10 @@ static inline void atalk_destroy_socket(struct sock *sk) /* 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; @@ -244,12 +228,11 @@ static void atif_drop_device(struct net_device *dev) static struct atalk_iface *atif_add_device(struct net_device *dev, struct atalk_addr *sa) { - struct atalk_iface *iface = kmalloc(sizeof(*iface), GFP_KERNEL); + struct atalk_iface *iface = kzalloc(sizeof(*iface), GFP_KERNEL); if (!iface) goto out; - memset(iface, 0, sizeof(*iface)); dev_hold(dev); iface->dev = dev; dev->atalk_ptr = iface; @@ -417,7 +400,7 @@ out_err: } /* 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; @@ -576,12 +559,11 @@ static int atrtr_create(struct rtentry *r, struct net_device *devhint) } if (!rt) { - rt = kmalloc(sizeof(*rt), GFP_ATOMIC); + rt = kzalloc(sizeof(*rt), GFP_ATOMIC); retval = -ENOBUFS; if (!rt) - goto out; - memset(rt, 0, sizeof(*rt)); + goto out_unlock; rt->next = atalk_routes; atalk_routes = rt; @@ -589,6 +571,7 @@ static int atrtr_create(struct rtentry *r, struct net_device *devhint) /* 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; @@ -629,7 +612,7 @@ out: * 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; @@ -1020,7 +1003,7 @@ static unsigned long atalk_sum_skb(const struct sk_buff *skb, int offset, return sum; } -static unsigned short atalk_checksum(const struct sk_buff *skb, int len) +static __be16 atalk_checksum(const struct sk_buff *skb, int len) { unsigned long sum; @@ -1028,9 +1011,15 @@ static unsigned short atalk_checksum(const struct sk_buff *skb, int len) sum = atalk_sum_skb(skb, 4, len-4, 0); /* Use 0xFFFF for 0. 0 itself means none */ - return sum ? htons((unsigned short)sum) : 0xFFFF; + return sum ? htons((unsigned short)sum) : htons(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. @@ -1038,7 +1027,6 @@ static unsigned short atalk_checksum(const struct sk_buff *skb, int len) static int atalk_create(struct socket *sock, int protocol) { struct sock *sk; - struct atalk_sock *at; int rc = -ESOCKTNOSUPPORT; /* @@ -1048,25 +1036,17 @@ static int atalk_create(struct socket *sock, int protocol) 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 */ @@ -1143,7 +1123,7 @@ static int atalk_autobind(struct sock *sk) n = atalk_pick_and_bind_port(sk, &sat); if (!n) - sk->sk_zapped = 0; + sock_reset_flag(sk, SOCK_ZAPPED); out: return n; } @@ -1155,7 +1135,8 @@ static int atalk_bind(struct socket *sock, struct sockaddr *uaddr, int addr_len) 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) @@ -1190,7 +1171,7 @@ static int atalk_bind(struct socket *sock, struct sockaddr *uaddr, int addr_len) return -EADDRINUSE; } - sk->sk_zapped = 0; + sock_reset_flag(sk, SOCK_ZAPPED); return 0; } @@ -1225,7 +1206,7 @@ static int atalk_connect(struct socket *sock, struct sockaddr *uaddr, #endif } - if (sk->sk_zapped) + if (sock_flag(sk, SOCK_ZAPPED)) if (atalk_autobind(sk) < 0) return -EBUSY; @@ -1252,7 +1233,7 @@ static int atalk_getname(struct socket *sock, struct sockaddr *uaddr, 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; @@ -1309,7 +1290,7 @@ static int handle_ip_over_ddp(struct sk_buff *skb) #endif static void atalk_route_packet(struct sk_buff *skb, struct net_device *dev, - struct ddpehdr *ddp, struct ddpebits *ddphv, + struct ddpehdr *ddp, __u16 len_hops, int origlen) { struct atalk_route *rt; @@ -1337,10 +1318,12 @@ static void atalk_route_packet(struct sk_buff *skb, struct net_device *dev, /* Route the packet */ rt = atrtr_find(&ta); - if (!rt || ddphv->deh_hops == DDP_MAXHOPS) + /* increment hops count */ + len_hops += 1 << 10; + if (!rt || !(len_hops & (15 << 10))) goto free_it; + /* FIXME: use skb->cb to be able to use shared skbs */ - ddphv->deh_hops++; /* * Route goes through another gateway, so set the target to the @@ -1355,11 +1338,10 @@ static void atalk_route_packet(struct sk_buff *skb, struct net_device *dev, /* Fix up skb->len field */ skb_trim(skb, min_t(unsigned int, origlen, (rt->dev->hard_header_len + - ddp_dl->header_length + ddphv->deh_len))); + ddp_dl->header_length + (len_hops & 1023)))); - /* Mend the byte order */ /* FIXME: use skb->cb to be able to use shared skbs */ - *((__u16 *)ddp) = ntohs(*((__u16 *)ddphv)); + ddp->deh_len_hops = htons(len_hops); /* * Send the buffer onwards @@ -1407,14 +1389,14 @@ free_it: * [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; struct atalk_iface *atif; struct sockaddr_at tosat; int origlen; - struct ddpebits ddphv; + __u16 len_hops; /* Don't mangle buffer if shared */ if (!(skb = skb_share_check(skb, GFP_ATOMIC))) @@ -1426,31 +1408,29 @@ static int atalk_rcv(struct sk_buff *skb, struct net_device *dev, ddp = ddp_hdr(skb); - /* - * Fix up the length field [Ok this is horrible but otherwise - * I end up with unions of bit fields and messy bit field order - * compiler/endian dependencies..] - */ - *((__u16 *)&ddphv) = ntohs(*((__u16 *)ddp)); + len_hops = ntohs(ddp->deh_len_hops); /* Trim buffer in case of stray trailing data */ origlen = skb->len; - skb_trim(skb, min_t(unsigned int, skb->len, ddphv.deh_len)); + skb_trim(skb, min_t(unsigned int, skb->len, len_hops & 1023)); /* * Size check to see if ddp->deh_len was crap * (Otherwise we'll detonate most spectacularly - * in the middle of recvmsg()). + * in the middle of atalk_checksum() or recvmsg()). */ - if (skb->len < sizeof(*ddp)) + if (skb->len < sizeof(*ddp) || skb->len < (len_hops & 1023)) { + pr_debug("AppleTalk: dropping corrupted frame (deh_len=%u, " + "skb->len=%u)\n", len_hops & 1023, skb->len); goto freeit; + } /* * Any checksums. Note we don't do htons() on this == is assumed to be * valid for net byte orders all over the networking code... */ if (ddp->deh_sum && - atalk_checksum(skb, ddphv.deh_len) != ddp->deh_sum) + atalk_checksum(skb, len_hops & 1023) != ddp->deh_sum) /* Not a valid AppleTalk frame - dustbin time */ goto freeit; @@ -1460,9 +1440,11 @@ static int atalk_rcv(struct sk_buff *skb, struct net_device *dev, 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) { - atalk_route_packet(skb, dev, ddp, &ddphv, origlen); + /* Not ours, so we route the packet via the correct + * AppleTalk iface + */ + atalk_route_packet(skb, dev, ddp, len_hops, origlen); goto out; } @@ -1499,7 +1481,7 @@ freeit: * 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) { @@ -1507,7 +1489,7 @@ static int ltalk_rcv(struct sk_buff *skb, struct net_device *dev, /* Find our address */ struct atalk_addr *ap = atalk_find_dev_addr(dev); - if (!ap || skb->len < sizeof(struct ddpshdr)) + if (!ap || skb->len < sizeof(__be16) || skb->len > 1023) goto freeit; /* Don't mangle buffer if shared */ @@ -1537,15 +1519,12 @@ static int ltalk_rcv(struct sk_buff *skb, struct net_device *dev, /* * Not sure about this bit... */ - ddp->deh_len = skb->len; - ddp->deh_hops = DDP_MAXHOPS; /* Non routable, so force a drop - if we slip up later */ - /* Mend the byte order */ - *((__u16 *)ddp) = htons(*((__u16 *)ddp)); + /* Non routable, so force a drop if we slip up later */ + ddp->deh_len_hops = htons(skb->len + (DDP_MAXHOPS << 10)); } 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; @@ -1574,7 +1553,7 @@ static int atalk_sendmsg(struct kiocb *iocb, struct socket *sock, struct msghdr return -EMSGSIZE; if (usat) { - if (sk->sk_zapped) + if (sock_flag(sk, SOCK_ZAPPED)) if (atalk_autobind(sk) < 0) return -EBUSY; @@ -1609,10 +1588,6 @@ static int atalk_sendmsg(struct kiocb *iocb, struct socket *sock, struct msghdr 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; @@ -1620,11 +1595,11 @@ static int atalk_sendmsg(struct kiocb *iocb, struct socket *sock, struct msghdr 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); @@ -1642,16 +1617,7 @@ static int atalk_sendmsg(struct kiocb *iocb, struct socket *sock, struct msghdr SOCK_DEBUG(sk, "SK %p: Begin build.\n", sk); ddp = (struct ddpehdr *)skb_put(skb, sizeof(struct ddpehdr)); - ddp->deh_pad = 0; - ddp->deh_hops = 0; - ddp->deh_len = len + sizeof(*ddp); - /* - * Fix up the length field [Ok this is horrible but otherwise - * I end up with unions of bit fields and messy bit field order - * compiler/endian dependencies.. - */ - *((__u16 *)ddp) = ntohs(*((__u16 *)ddp)); - + ddp->deh_len_hops = htons(len + sizeof(*ddp)); ddp->deh_dnet = usat->sat_addr.s_net; ddp->deh_snet = at->src_net; ddp->deh_dnode = usat->sat_addr.s_node; @@ -1694,6 +1660,20 @@ static int atalk_sendmsg(struct kiocb *iocb, struct socket *sock, struct msghdr 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); @@ -1718,8 +1698,8 @@ static int atalk_recvmsg(struct kiocb *iocb, struct socket *sock, struct msghdr struct sockaddr_at *sat = (struct sockaddr_at *)msg->msg_name; struct ddpehdr *ddp; int copied = 0; + int offset = 0; int err = 0; - struct ddpebits ddphv; struct sk_buff *skb = skb_recv_datagram(sk, flags & ~MSG_DONTWAIT, flags & MSG_DONTWAIT, &err); if (!skb) @@ -1727,25 +1707,18 @@ static int atalk_recvmsg(struct kiocb *iocb, struct socket *sock, struct msghdr /* FIXME: use skb->cb to be able to use shared skbs */ ddp = ddp_hdr(skb); - *((__u16 *)&ddphv) = ntohs(*((__u16 *)ddp)); + copied = ntohs(ddp->deh_len_hops) & 1023; - if (sk->sk_type == SOCK_RAW) { - copied = ddphv.deh_len; - if (copied > size) { - copied = size; - msg->msg_flags |= MSG_TRUNC; - } + if (sk->sk_type != SOCK_RAW) { + offset = sizeof(*ddp); + copied -= offset; + } - err = skb_copy_datagram_iovec(skb, 0, msg->msg_iov, copied); - } else { - copied = ddphv.deh_len - sizeof(*ddp); - if (copied > size) { - copied = size; - msg->msg_flags |= MSG_TRUNC; - } - err = skb_copy_datagram_iovec(skb, sizeof(*ddp), - msg->msg_iov, copied); + if (copied > size) { + copied = size; + msg->msg_flags |= MSG_TRUNC; } + err = skb_copy_datagram_iovec(skb, offset, msg->msg_iov, copied); if (!err) { if (sat) { @@ -1767,7 +1740,7 @@ static int atalk_recvmsg(struct kiocb *iocb, struct socket *sock, struct msghdr */ 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; @@ -1817,33 +1790,34 @@ static int atalk_ioctl(struct socket *sock, unsigned int cmd, unsigned long 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, argp); - 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, @@ -1854,6 +1828,9 @@ static struct proto_ops SOCKOPS_WRAPPED(atalk_dgram_ops) = { .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, @@ -1871,12 +1848,12 @@ static struct notifier_block ddp_notifier = { .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, }; @@ -1894,6 +1871,11 @@ static char atalk_err_snap[] __initdata = /* 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) @@ -1906,7 +1888,8 @@ static int __init atalk_init(void) aarp_proto_init(); atalk_proc_init(); atalk_register_sysctl(); - return 0; +out: + return rc; } module_init(atalk_init); @@ -1931,6 +1914,7 @@ static void __exit atalk_exit(void) dev_remove_pack(&ppptalk_packet_type); unregister_snap_client(ddp_dl); sock_unregister(PF_APPLETALK); + proto_unregister(&ddp_proto); } module_exit(atalk_exit);