📄 ddp.c
字号:
sum = atalk_sum_partial(vaddr + frag->page_offset + offset - start, copy, sum); kunmap_skb_frag(vaddr); if (!(len -= copy)) return sum; offset += copy; } start = end; } if (skb_shinfo(skb)->frag_list) { struct sk_buff *list = skb_shinfo(skb)->frag_list; for (; list; list = list->next) { int end; BUG_TRAP(start <= offset + len); end = start + list->len; if ((copy = end - offset) > 0) { if (copy > len) copy = len; sum = atalk_sum_skb(list, offset - start, copy, sum); if ((len -= copy) == 0) return sum; offset += copy; } start = end; } } BUG_ON(len > 0); return sum;}static __be16 atalk_checksum(const struct sk_buff *skb, int len){ unsigned long sum; /* skip header 4 bytes */ sum = atalk_sum_skb(skb, 4, len-4, 0); /* Use 0xFFFF for 0. 0 itself means none */ 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. */static int atalk_create(struct net *net, struct socket *sock, int protocol){ struct sock *sk; int rc = -ESOCKTNOSUPPORT; if (net != &init_net) return -EAFNOSUPPORT; /* * We permit SOCK_DGRAM and RAW is an extension. It is trivial to do * and gives you the full ELAP frame. Should be handy for CAP 8) */ if (sock->type != SOCK_RAW && sock->type != SOCK_DGRAM) goto out; rc = -ENOMEM; sk = sk_alloc(net, PF_APPLETALK, GFP_KERNEL, &ddp_proto); if (!sk) goto out; rc = 0; sock->ops = &atalk_dgram_ops; sock_init_data(sock, sk); /* Checksums on by default */ sock_set_flag(sk, SOCK_ZAPPED);out: return rc;}/* Free a socket. No work needed */static int atalk_release(struct socket *sock){ struct sock *sk = sock->sk; if (sk) { sock_orphan(sk); sock->sk = NULL; atalk_destroy_socket(sk); } return 0;}/** * atalk_pick_and_bind_port - Pick a source port when one is not given * @sk - socket to insert into the tables * @sat - address to search for * * Pick a source port when one is not given. If we can find a suitable free * one, we insert the socket into the tables using it. * * This whole operation must be atomic. */static int atalk_pick_and_bind_port(struct sock *sk, struct sockaddr_at *sat){ int retval; write_lock_bh(&atalk_sockets_lock); for (sat->sat_port = ATPORT_RESERVED; sat->sat_port < ATPORT_LAST; sat->sat_port++) { struct sock *s; struct hlist_node *node; sk_for_each(s, node, &atalk_sockets) { struct atalk_sock *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 try_next_port; } /* Wheee, it's free, assign and insert. */ __atalk_insert_socket(sk); at_sk(sk)->src_port = sat->sat_port; retval = 0; goto out;try_next_port:; } retval = -EBUSY;out: write_unlock_bh(&atalk_sockets_lock); return retval;}static int atalk_autobind(struct sock *sk){ struct atalk_sock *at = at_sk(sk); struct sockaddr_at sat; struct atalk_addr *ap = atalk_find_primary(); int n = -EADDRNOTAVAIL; if (!ap || ap->s_net == htons(ATADDR_ANYNET)) goto out; at->src_net = sat.sat_addr.s_net = ap->s_net; at->src_node = sat.sat_addr.s_node = ap->s_node; n = atalk_pick_and_bind_port(sk, &sat); if (!n) sock_reset_flag(sk, SOCK_ZAPPED);out: return n;}/* Set the address 'our end' of the connection */static int atalk_bind(struct socket *sock, struct sockaddr *uaddr, int addr_len){ struct sockaddr_at *addr = (struct sockaddr_at *)uaddr; struct sock *sk = sock->sk; struct atalk_sock *at = at_sk(sk); if (!sock_flag(sk, SOCK_ZAPPED) || addr_len != sizeof(struct sockaddr_at)) return -EINVAL; if (addr->sat_family != AF_APPLETALK) return -EAFNOSUPPORT; if (addr->sat_addr.s_net == htons(ATADDR_ANYNET)) { struct atalk_addr *ap = atalk_find_primary(); if (!ap) return -EADDRNOTAVAIL; at->src_net = addr->sat_addr.s_net = ap->s_net; at->src_node = addr->sat_addr.s_node= ap->s_node; } else { if (!atalk_find_interface(addr->sat_addr.s_net, addr->sat_addr.s_node)) return -EADDRNOTAVAIL; at->src_net = addr->sat_addr.s_net; at->src_node = addr->sat_addr.s_node; } if (addr->sat_port == ATADDR_ANYPORT) { int n = atalk_pick_and_bind_port(sk, addr); if (n < 0) return n; } else { at->src_port = addr->sat_port; if (atalk_find_or_insert_socket(sk, addr)) return -EADDRINUSE; } sock_reset_flag(sk, SOCK_ZAPPED); return 0;}/* Set the address we talk to */static int atalk_connect(struct socket *sock, struct sockaddr *uaddr, int addr_len, int flags){ struct sock *sk = sock->sk; struct atalk_sock *at = at_sk(sk); struct sockaddr_at *addr; sk->sk_state = TCP_CLOSE; sock->state = SS_UNCONNECTED; if (addr_len != sizeof(*addr)) return -EINVAL; addr = (struct sockaddr_at *)uaddr; if (addr->sat_family != AF_APPLETALK) return -EAFNOSUPPORT; if (addr->sat_addr.s_node == ATADDR_BCAST && !sock_flag(sk, SOCK_BROADCAST)) {#if 1 printk(KERN_WARNING "%s is broken and did not set " "SO_BROADCAST. It will break when 2.2 is " "released.\n", current->comm);#else return -EACCES;#endif } if (sock_flag(sk, SOCK_ZAPPED)) if (atalk_autobind(sk) < 0) return -EBUSY; if (!atrtr_get_dev(&addr->sat_addr)) return -ENETUNREACH; at->dest_port = addr->sat_port; at->dest_net = addr->sat_addr.s_net; at->dest_node = addr->sat_addr.s_node; sock->state = SS_CONNECTED; sk->sk_state = TCP_ESTABLISHED; return 0;}/* * Find the name of an AppleTalk socket. Just copy the right * fields into the sockaddr. */static int atalk_getname(struct socket *sock, struct sockaddr *uaddr, int *uaddr_len, int peer){ struct sockaddr_at sat; struct sock *sk = sock->sk; struct atalk_sock *at = at_sk(sk); if (sock_flag(sk, SOCK_ZAPPED)) if (atalk_autobind(sk) < 0) return -ENOBUFS; *uaddr_len = sizeof(struct sockaddr_at); if (peer) { if (sk->sk_state != TCP_ESTABLISHED) return -ENOTCONN; sat.sat_addr.s_net = at->dest_net; sat.sat_addr.s_node = at->dest_node; sat.sat_port = at->dest_port; } else { sat.sat_addr.s_net = at->src_net; sat.sat_addr.s_node = at->src_node; sat.sat_port = at->src_port; } sat.sat_family = AF_APPLETALK; memcpy(uaddr, &sat, sizeof(sat)); return 0;}#if defined(CONFIG_IPDDP) || defined(CONFIG_IPDDP_MODULE)static __inline__ int is_ip_over_ddp(struct sk_buff *skb){ return skb->data[12] == 22;}static int handle_ip_over_ddp(struct sk_buff *skb){ struct net_device *dev = __dev_get_by_name(&init_net, "ipddp0"); struct net_device_stats *stats; /* This needs to be able to handle ipddp"N" devices */ if (!dev) return -ENODEV; skb->protocol = htons(ETH_P_IP); skb_pull(skb, 13); skb->dev = dev; skb_reset_transport_header(skb); stats = dev->priv; stats->rx_packets++; stats->rx_bytes += skb->len + 13; netif_rx(skb); /* Send the SKB up to a higher place. */ return 0;}#else/* make it easy for gcc to optimize this test out, i.e. kill the code */#define is_ip_over_ddp(skb) 0#define handle_ip_over_ddp(skb) 0#endifstatic void atalk_route_packet(struct sk_buff *skb, struct net_device *dev, struct ddpehdr *ddp, __u16 len_hops, int origlen){ struct atalk_route *rt; struct atalk_addr ta; /* * Don't route multicast, etc., packets, or packets sent to "this * network" */ if (skb->pkt_type != PACKET_HOST || !ddp->deh_dnet) { /* * FIXME: * * Can it ever happen that a packet is from a PPP iface and * needs to be broadcast onto the default network? */ if (dev->type == ARPHRD_PPP) printk(KERN_DEBUG "AppleTalk: didn't forward broadcast " "packet received from PPP iface\n"); goto free_it; } ta.s_net = ddp->deh_dnet; ta.s_node = ddp->deh_dnode; /* Route the packet */ rt = atrtr_find(&ta); /* 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 */ /* * Route goes through another gateway, so set the target to the * gateway instead. */ if (rt->flags & RTF_GATEWAY) { ta.s_net = rt->gateway.s_net; ta.s_node = rt->gateway.s_node; } /* Fix up skb->len field */ skb_trim(skb, min_t(unsigned int, origlen, (rt->dev->hard_header_len + ddp_dl->header_length + (len_hops & 1023)))); /* FIXME: use skb->cb to be able to use shared skbs */ ddp->deh_len_hops = htons(len_hops); /* * Send the buffer onwards * * Now we must always be careful. If it's come from LocalTalk to * EtherTalk it might not fit * * Order matters here: If a packet has to be copied to make a new * headroom (rare hopefully) then it won't need unsharing. * * Note. ddp-> becomes invalid at the realloc. */ if (skb_headroom(skb) < 22) { /* 22 bytes - 12 ether, 2 len, 3 802.2 5 snap */ struct sk_buff *nskb = skb_realloc_headroom(skb, 32); kfree_skb(skb); if (!nskb) goto out; skb = nskb; } else skb = skb_unshare(skb, GFP_ATOMIC); /* * If the buffer didn't vanish into the lack of space bitbucket we can * send it. */ if (skb && aarp_send_ddp(rt->dev, skb, &ta, NULL) == -1) goto free_it;out: return;free_it: kfree_skb(skb);}/** * atalk_rcv - Receive a packet (in skb) from device dev * @skb - packet received * @dev - network device where the packet comes from * @pt - packet type * * Receive a packet (in skb) from device dev. This has come from the SNAP * decoder, and on entry skb->transport_header is the DDP header, skb->len * is the DDP header, skb->len is the DDP length. The physical headers * have been extracted. PPP should probably pass frames marked as for this * layer. [ie ARPHRD_ETHERTALK] */static int atalk_rcv(struct sk_buff *skb, struct net_device *dev, 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; __u16 len_hops; if (dev->nd_net != &init_net) goto freeit; /* Don't mangle buffer if shared */ if (!(skb = skb_share_check(skb, GFP_ATOMIC))) goto out; /* Size check and make sure header is contiguous */ if (!pskb_may_pull(skb, sizeof(*ddp))) goto freeit; ddp = ddp_hdr(skb); 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, len_hops & 1023)); /* * Size check to see if ddp->deh_len was crap * (Otherwise we'll detonate most spectacularly * in the middle of atalk_checksum() or recvmsg()). */ 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, len_hops & 1023) != ddp->deh_sum) /* Not a valid AppleTalk frame - dustbin time */ goto freeit; /* Check the packet is aimed at us */ if (!ddp->deh_dnet) /* Net 0 is 'this network' */ atif = atalk_find_anynet(ddp->deh_dnode, dev); else atif = atalk_find_interface(ddp->deh_dnet, ddp->deh_dnode); if (!atif) { /* Not ours, so we route the packet via the correct * AppleTalk iface */ atalk_route_packet(skb, dev, ddp, len_hops, origlen);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -