📄 ddp.c
字号:
iface->dev->name, ntohs(iface->address.s_net), iface->address.s_node, ntohs(iface->nets.nr_firstnet), ntohs(iface->nets.nr_lastnet), iface->status); pos = begin + len; if (pos < offset) { len = 0; begin = pos; } if (pos > offset + length) break; } spin_unlock_bh(&atalk_iface_lock); *start = buffer + (offset - begin); len -= (offset - begin); if (len > length) len = length; return len;}/* Called from proc fs - just make it print the routes neatly */static int atalk_rt_get_info(char *buffer, char **start, off_t offset, int length){ off_t pos = 0; off_t begin = 0; int len = sprintf(buffer, "Target Router Flags Dev\n"); struct atalk_route *rt; if (atrtr_default.dev) { rt = &atrtr_default; len += sprintf(buffer + len,"Default %04X:%02X %-4d %s\n", ntohs(rt->gateway.s_net), rt->gateway.s_node, rt->flags, rt->dev->name); } read_lock_bh(&atalk_router_lock); for (rt = atalk_router_list; rt; rt = rt->next) { len += sprintf(buffer + len, "%04X:%02X %04X:%02X %-4d %s\n", ntohs(rt->target.s_net), rt->target.s_node, ntohs(rt->gateway.s_net), rt->gateway.s_node, rt->flags, rt->dev->name); pos = begin + len; if (pos < offset) { len = 0; begin = pos; } if (pos > offset + length) break; } read_unlock_bh(&atalk_router_lock); *start = buffer + (offset - begin); len -= (offset - begin); if (len > length) len = length; return len;}/**************************************************************************\* ** Handling for system calls applied via the various interfaces to an ** AppleTalk socket object. ** *\**************************************************************************//* * Checksum: This is 'optional'. It's quite likely also a good * candidate for assembler hackery 8) */unsigned short atalk_checksum(struct ddpehdr *ddp, int len){ unsigned long sum = 0; /* Assume unsigned long is >16 bits */ unsigned char *data = (unsigned char *) ddp; len -= 4; /* skip header 4 bytes */ data += 4; /* This ought to be unwrapped neatly. I'll trust gcc for now */ while (len--) { sum += *data; sum <<= 1; if (sum & 0x10000) { sum++; sum &= 0xFFFF; } data++; } /* Use 0xFFFF for 0. 0 itself means none */ return sum ? htons((unsigned short) sum) : 0xFFFF;}/* * Create a socket. Initialise the socket, blank the addresses * set the state. */static int atalk_create(struct socket *sock, int protocol){ struct sock *sk = sk_alloc(PF_APPLETALK, GFP_KERNEL, 1); if (!sk) return -ENOMEM; switch (sock->type) { /* * 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) */ case SOCK_RAW: case SOCK_DGRAM: sock->ops = &atalk_dgram_ops; break; case SOCK_STREAM: /* * TODO: if you want to implement ADSP, here's the * place to start */ /* sock->ops = &atalk_stream_ops; break; */ default: sk_free(sk); return -ESOCKTNOSUPPORT; } MOD_INC_USE_COUNT; sock_init_data(sock, sk); sk->destruct = NULL; /* Checksums on by default */ sk->zapped = 1; return 0;}/* Free a socket. No work needed */static int atalk_release(struct socket *sock){ struct sock *sk = sock->sk; if (!sk) return 0; if (!sk->dead) sk->state_change(sk); sk->dead = 1; sock->sk = NULL; atalk_destroy_socket(sk); return 0;}/* * 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){ struct sock *s; int retval; spin_lock_bh(&atalk_sockets_lock); for (sat->sat_port = ATPORT_RESERVED; sat->sat_port < ATPORT_LAST; sat->sat_port++) { for (s = atalk_sockets; s; s = s->next) { if (s->protinfo.af_at.src_net == sat->sat_addr.s_net && s->protinfo.af_at.src_node == sat->sat_addr.s_node && s->protinfo.af_at.src_port == sat->sat_port) goto try_next_port; } /* Wheee, it's free, assign and insert. */ sk->next = atalk_sockets; if (sk->next) atalk_sockets->pprev = &sk->next; atalk_sockets = sk; sk->pprev = &atalk_sockets; sk->protinfo.af_at.src_port = sat->sat_port; retval = 0; goto out; try_next_port: ; } retval = -EBUSY;out: spin_unlock_bh(&atalk_sockets_lock); return retval;}static int atalk_autobind(struct sock *sk){ struct sockaddr_at sat; int n; struct at_addr *ap = atalk_find_primary(); if (!ap || ap->s_net == htons(ATADDR_ANYNET)) return -EADDRNOTAVAIL; sk->protinfo.af_at.src_net = sat.sat_addr.s_net = ap->s_net; sk->protinfo.af_at.src_node = sat.sat_addr.s_node = ap->s_node; n = atalk_pick_and_bind_port(sk, &sat); if (n < 0) return n; sk->zapped = 0; return 0;}/* 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; if (!sk->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 at_addr *ap = atalk_find_primary(); if (!ap) return -EADDRNOTAVAIL; sk->protinfo.af_at.src_net = addr->sat_addr.s_net = ap->s_net; sk->protinfo.af_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; sk->protinfo.af_at.src_net = addr->sat_addr.s_net; sk->protinfo.af_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 { sk->protinfo.af_at.src_port = addr->sat_port; if (atalk_find_or_insert_socket(sk, addr)) return -EADDRINUSE; } sk->zapped = 0; 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 sockaddr_at *addr; 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 && !sk->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 (sk->zapped) if (atalk_autobind(sk) < 0) return -EBUSY; if (!atrtr_get_dev(&addr->sat_addr)) return -ENETUNREACH; sk->protinfo.af_at.dest_port = addr->sat_port; sk->protinfo.af_at.dest_net = addr->sat_addr.s_net; sk->protinfo.af_at.dest_node = addr->sat_addr.s_node; sock->state = SS_CONNECTED; 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; if (sk->zapped) if (atalk_autobind(sk) < 0) return -ENOBUFS; *uaddr_len = sizeof(struct sockaddr_at); if (peer) { if (sk->state != TCP_ESTABLISHED) return -ENOTCONN; sat.sat_addr.s_net = sk->protinfo.af_at.dest_net; sat.sat_addr.s_node = sk->protinfo.af_at.dest_node; sat.sat_port = sk->protinfo.af_at.dest_port; } else { sat.sat_addr.s_net = sk->protinfo.af_at.src_net; sat.sat_addr.s_node = sk->protinfo.af_at.src_node; sat.sat_port = sk->protinfo.af_at.src_port; } sat.sat_family = AF_APPLETALK; memcpy(uaddr, &sat, sizeof(sat)); return 0;}/* * Receive a packet (in skb) from device dev. This has come from the SNAP * decoder, and on entry skb->h.raw 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 ddpehdr *ddp = (void *) skb->h.raw; struct sock *sock; struct atalk_iface *atif; struct sockaddr_at tosat; int origlen; struct ddpebits ddphv; /* Size check */ if (skb->len < sizeof(*ddp)) goto freeit; /* * 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..] * * FIXME: This is a write to a shared object. Granted it * happens to be safe BUT.. (Its safe as user space will not * run until we put it back) */ *((__u16 *)&ddphv) = ntohs(*((__u16 *)ddp)); /* Trim buffer in case of stray trailing data */ origlen = skb->len; skb_trim(skb, min_t(unsigned int, skb->len, ddphv.deh_len)); /* * Size check to see if ddp->deh_len was crap * (Otherwise we'll detonate most spectacularly * in the middle of recvmsg()). */ if (skb->len < sizeof(*ddp)) 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(ddp, ddphv.deh_len) != 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); /* Not ours, so we route the packet via the correct AppleTalk iface */ if (!atif) { struct atalk_route *rt; struct at_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 freeit; } ta.s_net = ddp->deh_dnet; ta.s_node = ddp->deh_dnode; /* Route the packet */ rt = atrtr_find(&ta); if (!rt || ddphv.deh_hops == DDP_MAXHOPS) goto freeit; ddphv.deh_hops++; /* * 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 + ddphv.deh_len)); /* Mend the byte order */ *((__u16 *)ddp) = ntohs(*((__u16 *)&ddphv)); /* * 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 freeit; goto out; }#if defined(CONFIG_IPDDP) || defined(CONFIG_IPDDP_MODULE) /* Check if IP-over-DDP */ if (skb->data[12] == 22) { struct net_device *dev = __dev_get_by_name("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->h.raw = skb->data; stats = dev->priv; stats->rx_packets++; stats->rx_bytes += skb->len + 13; netif_rx(skb); /* Send the SKB up to a higher place. */ goto out; }#endif /* * Which socket - atalk_search_socket() looks for a *full match* * of the <net,node,port> tuple. */ tosat.sat_addr.s_net = ddp->deh_dnet;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -