📄 ip_to_dlpi.c
字号:
{ printk("ip2xopen: failed: allocb failed"); return -EAGAIN; /* Other drivers seem to return this on error */ } ip2xinet_status.ip2x_dlstate = DL_BIND_PENDING; mp->b_datap->db_type = M_PROTO; mp->b_wptr += DL_BIND_REQ_SIZE; bindmp = (dl_bind_req_t *) mp->b_rptr; bindmp->dl_primitive = DL_BIND_REQ; bindmp->dl_sap = IP_SAP; putq(q, mp); return 0;}/* * Open and close */int ip2xinet_open(struct net_device *dev){ int i; int err; struct ip2xinet_priv *privp = (struct ip2xinet_priv *)dev->priv; lis_flags_t oldpl; lis_spin_lock_irqsave(ip2xinet_lock, &oldpl); /* BEFORE ANYTHING CHECK THAT the streams I_LINK SUCCEEDED */ if (!(ip2xinet_status.ip2x_dlstate == DL_UNBOUND || (ip2xinet_num_ip_opened != 0 && ip2xinet_status.ip2x_dlstate == DL_IDLE))) { /* Normally we'd do the I_LINK, this would set us up into the * UNBOUND state but something went wrong. Either the I_LINK has * not completed yet, or it failed. In any case we're not in * the shape to succeed so return a failure code and exit. * */ lis_spin_unlock_irqrestore(ip2xinet_lock, &oldpl); return -EAGAIN; /* Other drivers seem to return this on error */ } /* Send a DL_BIND DOWN */ if (ip2xinet_num_ip_opened == 0) { if ((err = ip2xinet_send_down_bind(ip2xinet_status.lowerq)) != 0) { lis_spin_unlock_irqrestore(ip2xinet_lock, &oldpl); return err; } } ip2xinet_num_ip_opened++; /* * Assign the hardware address of the board: use "\0IP2Xx", where * x is 0 to 7. The first byte is '\0': a safe choice with regard * to multicast */ for (i=0; i < ETH_ALEN; i++) dev->dev_addr[i] = "\0IP2X0"[i]; dev->dev_addr[ETH_ALEN-1] += (dev - ip2xinet_devs); /* the number */ privp->state = 1; if (ip2xinet_status.ip2x_dlstate == DL_IDLE) netif_start_queue(dev); /* kernel can transmit */ else netif_stop_queue(dev); /* wait until DL_IDLE, then kernel can tx */ MOD_INC_USE_COUNT; lis_spin_unlock_irqrestore(ip2xinet_lock, &oldpl); return 0;}int ip2xinet_release(struct net_device *dev){ queue_t *q; mblk_t *mp; lis_flags_t oldpl; struct ip2xinet_priv *privp = (struct ip2xinet_priv *)dev->priv; lis_spin_lock_irqsave(ip2xinet_lock, &oldpl); privp->state = 0; netif_stop_queue(dev); /* can't transmit any more */ MOD_DEC_USE_COUNT; ip2xinet_num_ip_opened--; /* BEFORE ANYTHING CHECK THAT we're in IDLE */ if (ip2xinet_status.ip2x_dlstate != DL_IDLE) { /* Normally we'd do the I_UNBIND, from DL_IDLE * In all other cases we ignore the dlpi state as we'll unlink soon * */ lis_spin_unlock_irqrestore(ip2xinet_lock, &oldpl); return 0; } /* Send a DL_UNBIND DOWN */ if (ip2xinet_num_ip_opened == 0) { q=ip2xinet_status.lowerq; if ((mp = allocb(sizeof(union DL_primitives), BPRI_LO)) == NULL) { printk("ip2xopen: failed: allocb failed"); lis_spin_unlock_irqrestore(ip2xinet_lock, &oldpl); return 0; /* Other drivers seem to return this on error */ } ip2xinet_status.ip2x_dlstate = DL_UNBIND_PENDING; if (mp) { dl_unbind_req_t *unbindmp; mp->b_datap->db_type = M_PROTO; mp->b_wptr += DL_UNBIND_REQ_SIZE; unbindmp = (dl_unbind_req_t *) mp->b_rptr; unbindmp->dl_primitive = DL_UNBIND_REQ; putq(q, mp); } } lis_spin_unlock_irqrestore(ip2xinet_lock, &oldpl); return 0;}/* * Configuration changes (passed on by ifconfig) * Not that we actually do anything with them. */int ip2xinet_config(struct net_device *dev, struct ifmap *map){ lis_flags_t oldpl; lis_spin_lock_irqsave(ip2xinet_lock, &oldpl); if (dev->flags & IFF_UP) /* can't act on a running interface */ { lis_spin_unlock_irqrestore(ip2xinet_lock, &oldpl); return -EBUSY; } /* Don't allow changing the I/O address */ if (map->base_addr != dev->base_addr) { lis_spin_unlock_irqrestore(ip2xinet_lock, &oldpl); printk(KERN_WARNING "ip2xinet: Can't change I/O address\n"); return -EOPNOTSUPP; } /* Don't allow changing the IRQ */ if (map->irq != dev->irq) { lis_spin_unlock_irqrestore(ip2xinet_lock, &oldpl); printk(KERN_WARNING "ip2xinet: Can't change IRQ\n"); return -EOPNOTSUPP; } /* ignore other fields */ lis_spin_unlock_irqrestore(ip2xinet_lock, &oldpl); return 0;}/* * Receive a packet: retrieve, encapsulate and pass over to upper levels * * This routine is called from the lrput routine. We should already hold * the driver lock when we are called from there. */void ip2xinet_rx(struct net_device *dev, struct sk_buff *skb){ struct ip2xinet_priv *privp = (struct ip2xinet_priv *)dev->priv; /* * The packet has been retrieved from the transmission * medium. Build an skb around it, so upper layers can handle it */ /* Write metadata, and then pass to the receive level */ skb->dev = dev; skb->protocol = eth_type_trans(skb, dev); skb->ip_summed = CHECKSUM_UNNECESSARY; /* don't check it */ privp->stats.rx_packets++; netif_rx(skb); return;} /* * Transmit a packet (low level interface) * * This routine is called from ip2xinet_tx. That function * grabbed the driver lock when it was called. */void ip2xinet_hw_tx(char *buf, int len, struct net_device *dev){ /* * This function deals with hw details, * while all other procedures are rather device-independent */ struct iphdr *ih, *iph; struct ethhdr *eth; struct ip2xinet_priv *privp; queue_t *q; mblk_t *mp, *nmp; dl_unitdata_req_t * req; int mylen; /* sanity check */ if (len < sizeof(struct ethhdr) + sizeof(struct iphdr)) { printk("ip2xinet: Hmm... packet too short (%i octets)\n", len); return; }#if 0 if (0) { /* enable this conditional to look at all the data */ int i; PDEBUG("ip2xinet: len is %i\n" KERN_DEBUG "data:",len); for (i=14 ; i<len; i++) printk(" %02x",buf[i]&0xff); printk("\n"); }#endif /* * Ethhdr is 14 bytes, but the kernel arranges for iphdr * to be aligned (i.e., ethhdr is unaligned) */ ih = (struct iphdr *)(buf+sizeof(struct ethhdr)); PDEBUGG("%08lx:%05i --> %08lx:%05i\n", ntohl(ih->saddr),ntohs(((struct tcphdr *)(ih+1))->source), ntohl(ih->daddr),ntohs(((struct tcphdr *)(ih+1))->dest)); /* * Ok, now the packet is ready for transmission: */ /* * Here we do a putq to the bottom q. */ privp = &ip2xinet_private[dev - ip2xinet_devs]; q=ip2xinet_status.lowerq; /* THIS IS WHERE WE ALLOCATE UNITDATA_REQ and send data down */ if ((mp = allocb(sizeof(struct iphdr)+DL_UNITDATA_REQ_SIZE, BPRI_LO)) == NULL) { printk("ip2xhwtx: failed: allocb failed"); return ; } mp->b_datap->db_type = M_PROTO; mp->b_wptr += (sizeof(struct iphdr ) + DL_UNITDATA_REQ_SIZE); /* * xinet expects a DLPI header ahead of the datagram, as in Unix. * The destination address in this header needs to be the next hop * address. We're going to get this from the destination address in * the Ethernet header and rely upon froute/x25route having added * a static ARP entry with: * IP address of machine at other end of circuit * MAC address equal to IP address of that same machine * Though the IP address of IP datagrams passed down to us may be many * hops away, the destination Ethernet address will always be the next * hop IP address. */ eth = (struct ethhdr *)(buf); iph = (struct iphdr *)(mp->b_rptr+ DL_UNITDATA_REQ_SIZE); iph->saddr = ih->saddr; /* likely unused by xinet */ iph->daddr = (eth->h_dest[3] << 24) /* next hop address */ + (eth->h_dest[2] << 16) + (eth->h_dest[1] << 8) + (eth->h_dest[0]); iph->check = 0; req = (dl_unitdata_req_t *) mp->b_rptr; req->dl_primitive = DL_UNITDATA_REQ; req->dl_dest_addr_length = 4; req->dl_dest_addr_offset = DL_UNITDATA_REQ_SIZE + (int ) &((struct iphdr *) 0)->daddr; /* Copy from buf to mp, make everything right, then send the stuff to * xinet IF WE CAN. * Could we use esballoc here? */ mylen = len - sizeof(struct ethhdr); if ((nmp = allocb(mylen, BPRI_LO)) == NULL) { printk("ip2xhwtx: failed: allocb failed"); freemsg(mp); return; } linkb(mp, nmp); bcopy( buf+sizeof(struct ethhdr), nmp->b_rptr, mylen); nmp->b_wptr += mylen; putq(q, mp); privp->stats.tx_packets++;}/* * Transmit a packet (called by the kernel) */int ip2xinet_tx(struct sk_buff *skb, struct net_device *dev){ lis_flags_t oldpl; if (skb == NULL) { return 0; } if (skb->len < (sizeof(struct ethhdr) + sizeof(struct iphdr))) { dev_kfree_skb(skb); return 0; } lis_spin_lock_irqsave(ip2xinet_lock, &oldpl); if (netif_queue_stopped(dev)) { lis_spin_unlock_irqrestore(ip2xinet_lock, &oldpl); return -EBUSY; } dev->trans_start = jiffies; /* save the timestamp */ ip2xinet_hw_tx(skb->data, skb->len, dev); lis_spin_unlock_irqrestore(ip2xinet_lock, &oldpl); dev_kfree_skb(skb); /* release it */ return 0; /* zero == done */}/* * Ioctl commands */int ip2xinet_ioctl(struct net_device *dev, struct ifreq *rq, int cmd){ PDEBUG("ioctl\n"); return 0;}/* * Return statistics to the caller */struct net_device_stats *ip2xinet_stats(struct net_device *dev){ struct ip2xinet_priv *priv = (struct ip2xinet_priv *)dev->priv; /* The only stats we keep are transmitted and received packets. * * Note: xinet stats are kind of useless since the structure * ifstats doesn't exist under linux. So rather than * keep track of stats in xinet we do it in ip2xinet. */ return &priv->stats;}int ip2xinet_rebuild_header(struct sk_buff *skb){ struct ethhdr *eth = (struct ethhdr *)(skb->data); // return arp_find(&(eth->h_dest),skb); return arp_find(eth->h_dest, skb);}/* * This function builds the hardware header from the source and destination * hardware addresses that were previousely retrieved, its job is to organise * the information passed to it as arguments */int ip2xinet_hard_header(struct sk_buff *skb, struct net_device *dev, unsigned short type, void *daddr, void *saddr, unsigned len){ struct ethhdr *eth = (struct ethhdr *)skb_push(skb,ETH_HLEN); /* * Set the protocol type. For a packet of type ETH_P_802_3 * we put the length in here instead. It is up to the * 802.2 layer to carry protocol information. */ if(type!=ETH_P_802_3) eth->h_proto = htons(type); else eth->h_proto = htons(len); /* * Set the source hardware address. */ if(saddr) memcpy(eth->h_source,saddr,dev->addr_len); else memcpy(eth->h_source,dev->dev_addr,dev->addr_len); /* * Anyway, the loopback-device should never use this function... */ if (dev->flags & IFF_LOOPBACK) { memset(eth->h_dest, 0, dev->addr_len); return(dev->hard_header_len); } if(daddr) { memcpy(eth->h_dest,daddr,dev->addr_len); return dev->hard_header_len; } return -dev->hard_header_len;}/* * The "change_mtu" method is usually not needed. * If you need it, it must be like this. * * The change in MTU (Maximum Transfer Unit) * must be communicated to xinet */int ip2xinet_change_mtu(struct net_device *dev, int new_mtu){ lis_flags_t oldpl; /* check ranges */ if ((new_mtu < 68) || (new_mtu > 1500)) return -EINVAL; /* Do anything you need, and accept the value */ lis_spin_lock_irqsave(ip2xinet_lock, &oldpl); dev->mtu = new_mtu; /* accept the new value */ lis_spin_unlock_irqrestore(ip2xinet_lock, &oldpl); return 0;}/* * The init function (sometimes called probe). * It is invoked by register_netdev() * * NOTE: This is different from the init() function that can be called from * streams */int ip2xinet_init(struct net_device *dev){ /* * Assign other fields in dev, using ether_setup() and some * hand assignments */ ether_setup(dev); dev->open = ip2xinet_open; dev->stop = ip2xinet_release; dev->set_config = ip2xinet_config; dev->hard_start_xmit = ip2xinet_tx; dev->do_ioctl = ip2xinet_ioctl; dev->get_stats = ip2xinet_stats; dev->change_mtu = ip2xinet_change_mtu; dev->rebuild_header = ip2xinet_rebuild_header; dev->hard_header = ip2xinet_hard_header; dev->hard_header_len = ETH_HLEN; dev->flags |= IFF_NOARP; dev->type = ARPHRD_ETHER; dev->addr_len = 6; /* fake ethernet address */ dev->tx_queue_len = 10; /* dev->dev_addr is handled in the open() */ dev->flags &= ~IFF_BROADCAST; /* X25 doesn't broadcast */ dev->flags &= ~IFF_MULTICAST; /* X25 doesn't multicast */ /* * Then, allocate the priv field. This encloses the statistics * and a few private fields. */ dev->priv = &ip2xinet_private[dev - ip2xinet_devs]; if (dev->priv == NULL) return -ENOMEM; memset(dev->priv, 0, sizeof(struct ip2xinet_priv)); dev_init_buffers(dev); ip2xinet_status.ip2x_dlstate = UNLINKED; return 0;}struct net_device ip2xinet_devs[NUMIP2XINET];/* * Finally, the module stuff */int init_linuxip(void){ int result, i, device_present = 0; struct net_device *dev = ip2xinet_devs; ip2xinet_eth = eth; /* copy the cfg datum in the non-static place */ /* call them "ip2x0"... "ip2x7" */ for (i = 0; i<NUMIP2XINET; i++,dev++) { memset(dev, 0, sizeof (struct net_device)); memcpy(dev->name, "ip2x0", 6); dev->name[4] = (char) ('0' + i); dev->init = ip2xinet_init; /* the rest of the fields are filled in by ip2xinet_init */ } dev = ip2xinet_devs; for (i=0; i < NUMIP2XINET; i++, dev++) if ((result = register_netdev(dev))) printk("ip2xinet: error %i registering device \"%s\"\n", result, dev->name); else device_present++; return device_present ? 0 : -ENODEV;}void cleanup_linuxip(void){ int i; for (i = 0; i<NUMIP2XINET; i++) { unregister_netdev(&ip2xinet_devs[i]); }}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -