📄 ddp.c
字号:
goto out; } /* if IP over DDP is not selected this code will be optimized out */ if (is_ip_over_ddp(skb)) return handle_ip_over_ddp(skb); /* * Which socket - atalk_search_socket() looks for a *full match* * of the <net, node, port> tuple. */ tosat.sat_addr.s_net = ddp->deh_dnet; tosat.sat_addr.s_node = ddp->deh_dnode; tosat.sat_port = ddp->deh_dport; sock = atalk_search_socket(&tosat, atif); if (!sock) /* But not one of our sockets */ goto freeit; /* Queue packet (standard) */ skb->sk = sock; if (sock_queue_rcv_skb(sock, skb) < 0) goto freeit;out: return 0;freeit: kfree_skb(skb); goto out;}/* * Receive a LocalTalk frame. We make some demands on the caller here. * Caller must provide enough headroom on the packet to pull the short * header and append a long one. */static int ltalk_rcv(struct sk_buff *skb, struct net_device *dev, struct packet_type *pt, struct net_device *orig_dev){ if (dev->nd_net != &init_net) goto freeit; /* Expand any short form frames */ if (skb_mac_header(skb)[2] == 1) { struct ddpehdr *ddp; /* Find our address */ struct atalk_addr *ap = atalk_find_dev_addr(dev); if (!ap || skb->len < sizeof(__be16) || skb->len > 1023) goto freeit; /* Don't mangle buffer if shared */ if (!(skb = skb_share_check(skb, GFP_ATOMIC))) return 0; /* * The push leaves us with a ddephdr not an shdr, and * handily the port bytes in the right place preset. */ ddp = (struct ddpehdr *) skb_push(skb, sizeof(*ddp) - 4); /* Now fill in the long header */ /* * These two first. The mac overlays the new source/dest * network information so we MUST copy these before * we write the network numbers ! */ ddp->deh_dnode = skb_mac_header(skb)[0]; /* From physical header */ ddp->deh_snode = skb_mac_header(skb)[1]; /* From physical header */ ddp->deh_dnet = ap->s_net; /* Network number */ ddp->deh_snet = ap->s_net; ddp->deh_sum = 0; /* No checksum */ /* * Not sure about this bit... */ /* Non routable, so force a drop if we slip up later */ ddp->deh_len_hops = htons(skb->len + (DDP_MAXHOPS << 10)); } skb_reset_transport_header(skb); return atalk_rcv(skb, dev, pt, orig_dev);freeit: kfree_skb(skb); return 0;}static int atalk_sendmsg(struct kiocb *iocb, struct socket *sock, struct msghdr *msg, size_t len){ struct sock *sk = sock->sk; struct atalk_sock *at = at_sk(sk); struct sockaddr_at *usat = (struct sockaddr_at *)msg->msg_name; int flags = msg->msg_flags; int loopback = 0; struct sockaddr_at local_satalk, gsat; struct sk_buff *skb; struct net_device *dev; struct ddpehdr *ddp; int size; struct atalk_route *rt; int err; if (flags & ~(MSG_DONTWAIT|MSG_CMSG_COMPAT)) return -EINVAL; if (len > DDP_MAXSZ) return -EMSGSIZE; if (usat) { if (sock_flag(sk, SOCK_ZAPPED)) if (atalk_autobind(sk) < 0) return -EBUSY; if (msg->msg_namelen < sizeof(*usat) || usat->sat_family != AF_APPLETALK) return -EINVAL; /* netatalk doesn't implement this check */ if (usat->sat_addr.s_node == ATADDR_BCAST && !sock_flag(sk, SOCK_BROADCAST)) { printk(KERN_INFO "SO_BROADCAST: Fix your netatalk as " "it will break before 2.2\n");#if 0 return -EPERM;#endif } } else { if (sk->sk_state != TCP_ESTABLISHED) return -ENOTCONN; usat = &local_satalk; usat->sat_family = AF_APPLETALK; usat->sat_port = at->dest_port; usat->sat_addr.s_node = at->dest_node; usat->sat_addr.s_net = at->dest_net; } /* Build a packet */ SOCK_DEBUG(sk, "SK %p: Got address.\n", sk); /* For headers */ size = sizeof(struct ddpehdr) + len + ddp_dl->header_length; if (usat->sat_addr.s_net || usat->sat_addr.s_node == ATADDR_ANYNODE) { rt = atrtr_find(&usat->sat_addr); } else { struct atalk_addr at_hint; at_hint.s_node = 0; at_hint.s_net = at->src_net; rt = atrtr_find(&at_hint); } if (!rt) return -ENETUNREACH; dev = rt->dev; SOCK_DEBUG(sk, "SK %p: Size needed %d, device %s\n", sk, size, dev->name); size += dev->hard_header_len; skb = sock_alloc_send_skb(sk, size, (flags & MSG_DONTWAIT), &err); if (!skb) return err; skb->sk = sk; skb_reserve(skb, ddp_dl->header_length); skb_reserve(skb, dev->hard_header_len); skb->dev = dev; SOCK_DEBUG(sk, "SK %p: Begin build.\n", sk); ddp = (struct ddpehdr *)skb_put(skb, sizeof(struct ddpehdr)); 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; ddp->deh_snode = at->src_node; ddp->deh_dport = usat->sat_port; ddp->deh_sport = at->src_port; SOCK_DEBUG(sk, "SK %p: Copy user data (%Zd bytes).\n", sk, len); err = memcpy_fromiovec(skb_put(skb, len), msg->msg_iov, len); if (err) { kfree_skb(skb); return -EFAULT; } if (sk->sk_no_check == 1) ddp->deh_sum = 0; else ddp->deh_sum = atalk_checksum(skb, len + sizeof(*ddp)); /* * Loopback broadcast packets to non gateway targets (ie routes * to group we are in) */ if (ddp->deh_dnode == ATADDR_BCAST && !(rt->flags & RTF_GATEWAY) && !(dev->flags & IFF_LOOPBACK)) { struct sk_buff *skb2 = skb_copy(skb, GFP_KERNEL); if (skb2) { loopback = 1; SOCK_DEBUG(sk, "SK %p: send out(copy).\n", sk); if (aarp_send_ddp(dev, skb2, &usat->sat_addr, NULL) == -1) kfree_skb(skb2); /* else queued/sent above in the aarp queue */ } } if (dev->flags & IFF_LOOPBACK || loopback) { 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); if (rt->flags & RTF_GATEWAY) { gsat.sat_addr = rt->gateway; usat = &gsat; } if (aarp_send_ddp(dev, skb, &usat->sat_addr, NULL) == -1) kfree_skb(skb); /* else queued/sent above in the aarp queue */ } SOCK_DEBUG(sk, "SK %p: Done write (%Zd).\n", sk, len); return len;}static int atalk_recvmsg(struct kiocb *iocb, struct socket *sock, struct msghdr *msg, size_t size, int flags){ struct sock *sk = sock->sk; struct sockaddr_at *sat = (struct sockaddr_at *)msg->msg_name; struct ddpehdr *ddp; int copied = 0; int offset = 0; int err = 0; struct sk_buff *skb = skb_recv_datagram(sk, flags & ~MSG_DONTWAIT, flags & MSG_DONTWAIT, &err); if (!skb) return err; /* FIXME: use skb->cb to be able to use shared skbs */ ddp = ddp_hdr(skb); copied = ntohs(ddp->deh_len_hops) & 1023; if (sk->sk_type != SOCK_RAW) { offset = sizeof(*ddp); copied -= offset; } 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) { sat->sat_family = AF_APPLETALK; sat->sat_port = ddp->deh_sport; sat->sat_addr.s_node = ddp->deh_snode; sat->sat_addr.s_net = ddp->deh_snet; } msg->msg_namelen = sizeof(*sat); } skb_free_datagram(sk, skb); /* Free the datagram. */ return err ? : copied;}/* * AppleTalk ioctl calls. */static int atalk_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg){ int rc = -ENOIOCTLCMD; struct sock *sk = sock->sk; void __user *argp = (void __user *)arg; switch (cmd) { /* Protocol layer */ case TIOCOUTQ: { long amount = sk->sk_sndbuf - atomic_read(&sk->sk_wmem_alloc); if (amount < 0) amount = 0; rc = put_user(amount, (int __user *)argp); break; } case TIOCINQ: { /* * These two are safe on a single CPU system as only * user tasks fiddle here */ struct sk_buff *skb = skb_peek(&sk->sk_receive_queue); long amount = 0; if (skb) amount = skb->len - sizeof(struct ddpehdr); rc = put_user(amount, (int __user *)argp); break; } case SIOCGSTAMP: rc = sock_get_timestamp(sk, argp); break; case SIOCGSTAMPNS: rc = sock_get_timestampns(sk, argp); break; /* Routing */ case SIOCADDRT: case SIOCDELRT: rc = -EPERM; if (capable(CAP_NET_ADMIN)) rc = atrtr_ioctl(cmd, argp); break; /* Interface */ case SIOCGIFADDR: case SIOCSIFADDR: case SIOCGIFBRDADDR: case SIOCATALKDIFADDR: case SIOCDIFADDR: case SIOCSARP: /* proxy AARP */ case SIOCDARP: /* proxy AARP */ rtnl_lock(); rc = atif_ioctl(cmd, argp); rtnl_unlock(); break; } return rc;}#ifdef CONFIG_COMPATstatic 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;}#endifstatic struct net_proto_family atalk_family_ops = { .family = PF_APPLETALK, .create = atalk_create, .owner = THIS_MODULE,};static const struct proto_ops SOCKOPS_WRAPPED(atalk_dgram_ops) = { .family = PF_APPLETALK, .owner = THIS_MODULE, .release = atalk_release, .bind = atalk_bind, .connect = atalk_connect, .socketpair = sock_no_socketpair, .accept = sock_no_accept, .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, .getsockopt = sock_no_getsockopt, .sendmsg = atalk_sendmsg, .recvmsg = atalk_recvmsg, .mmap = sock_no_mmap, .sendpage = sock_no_sendpage,};SOCKOPS_WRAP(atalk_dgram, PF_APPLETALK);static struct notifier_block ddp_notifier = { .notifier_call = ddp_device_event,};static struct packet_type ltalk_packet_type = { .type = __constant_htons(ETH_P_LOCALTALK), .func = ltalk_rcv,};static struct packet_type ppptalk_packet_type = { .type = __constant_htons(ETH_P_PPPTALK), .func = atalk_rcv,};static unsigned char ddp_snap_id[] = { 0x08, 0x00, 0x07, 0x80, 0x9B };/* Export symbols for use by drivers when AppleTalk is a module */EXPORT_SYMBOL(aarp_send_ddp);EXPORT_SYMBOL(atrtr_get_dev);EXPORT_SYMBOL(atalk_find_dev_addr);static char atalk_err_snap[] __initdata = KERN_CRIT "Unable to register DDP with SNAP.\n";/* 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) printk(atalk_err_snap); dev_add_pack(<alk_packet_type); dev_add_pack(&ppptalk_packet_type); register_netdevice_notifier(&ddp_notifier); aarp_proto_init(); atalk_proc_init(); atalk_register_sysctl();out: return rc;}module_init(atalk_init);/* * No explicit module reference count manipulation is needed in the * protocol. Socket layer sets module reference count for us * and interfaces reference counting is done * by the network device layer. * * Ergo, before the AppleTalk module can be removed, all AppleTalk * sockets be closed from user space. */static void __exit atalk_exit(void){#ifdef CONFIG_SYSCTL atalk_unregister_sysctl();#endif /* CONFIG_SYSCTL */ atalk_proc_exit(); aarp_cleanup_module(); /* General aarp clean-up. */ unregister_netdevice_notifier(&ddp_notifier); dev_remove_pack(<alk_packet_type); dev_remove_pack(&ppptalk_packet_type); unregister_snap_client(ddp_dl); sock_unregister(PF_APPLETALK); proto_unregister(&ddp_proto);}module_exit(atalk_exit);MODULE_LICENSE("GPL");MODULE_AUTHOR("Alan Cox <Alan.Cox@linux.org>");MODULE_DESCRIPTION("AppleTalk 0.20\n");MODULE_ALIAS_NETPROTO(PF_APPLETALK);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -