📄 isdn_net.c
字号:
printk(KERN_INFO "OPEN: %d.%d.%d.%d -> %d.%d.%d.%d%s\n", p[12], p[13], p[14], p[15], p[16], p[17], p[18], p[19], addinfo); break; case ETH_P_ARP: printk(KERN_INFO "OPEN: ARP %d.%d.%d.%d -> *.*.*.* ?%d.%d.%d.%d\n", p[14], p[15], p[16], p[17], p[24], p[25], p[26], p[27]); break; }}/* * this function is used to send supervisory data, i.e. data which was * not received from the network layer, but e.g. frames from ipppd, CCP * reset frames etc. */void isdn_net_write_super(isdn_net_local *lp, struct sk_buff *skb){ if (in_irq()) { // we can't grab the lock from irq context, // so we just queue the packet skb_queue_tail(&lp->super_tx_queue, skb); schedule_work(&lp->tqueue); return; } spin_lock_bh(&lp->xmit_lock); if (!isdn_net_lp_busy(lp)) { isdn_net_writebuf_skb(lp, skb); } else { skb_queue_tail(&lp->super_tx_queue, skb); } spin_unlock_bh(&lp->xmit_lock);}/* * called from tq_immediate */static void isdn_net_softint(void *private){ isdn_net_local *lp = private; struct sk_buff *skb; spin_lock_bh(&lp->xmit_lock); while (!isdn_net_lp_busy(lp)) { skb = skb_dequeue(&lp->super_tx_queue); if (!skb) break; isdn_net_writebuf_skb(lp, skb); } spin_unlock_bh(&lp->xmit_lock);}/* * all frames sent from the (net) LL to a HL driver should go via this function * it's serialized by the caller holding the lp->xmit_lock spinlock */void isdn_net_writebuf_skb(isdn_net_local *lp, struct sk_buff *skb){ int ret; int len = skb->len; /* save len */ /* before obtaining the lock the caller should have checked that the lp isn't busy */ if (isdn_net_lp_busy(lp)) { printk("isdn BUG at %s:%d!\n", __FILE__, __LINE__); goto error; } if (!(lp->flags & ISDN_NET_CONNECTED)) { printk("isdn BUG at %s:%d!\n", __FILE__, __LINE__); goto error; } ret = isdn_writebuf_skb_stub(lp->isdn_device, lp->isdn_channel, 1, skb); if (ret != len) { /* we should never get here */ printk(KERN_WARNING "%s: HL driver queue full\n", lp->name); goto error; } lp->transcount += len; isdn_net_inc_frame_cnt(lp); return; error: dev_kfree_skb(skb); lp->stats.tx_errors++;}/* * Helper function for isdn_net_start_xmit. * When called, the connection is already established. * Based on cps-calculation, check if device is overloaded. * If so, and if a slave exists, trigger dialing for it. * If any slave is online, deliver packets using a simple round robin * scheme. * * Return: 0 on success, !0 on failure. */static intisdn_net_xmit(struct net_device *ndev, struct sk_buff *skb){ isdn_net_dev *nd; isdn_net_local *slp; isdn_net_local *lp = (isdn_net_local *) ndev->priv; int retv = 0; if (((isdn_net_local *) (ndev->priv))->master) { printk("isdn BUG at %s:%d!\n", __FILE__, __LINE__); dev_kfree_skb(skb); return 0; } /* For the other encaps the header has already been built */#ifdef CONFIG_ISDN_PPP if (lp->p_encap == ISDN_NET_ENCAP_SYNCPPP) { return isdn_ppp_xmit(skb, ndev); }#endif nd = ((isdn_net_local *) ndev->priv)->netdev; lp = isdn_net_get_locked_lp(nd); if (!lp) { printk(KERN_WARNING "%s: all channels busy - requeuing!\n", ndev->name); return 1; } /* we have our lp locked from now on */ /* Reset hangup-timeout */ lp->huptimer = 0; // FIXME? isdn_net_writebuf_skb(lp, skb); spin_unlock_bh(&lp->xmit_lock); /* the following stuff is here for backwards compatibility. * in future, start-up and hangup of slaves (based on current load) * should move to userspace and get based on an overall cps * calculation */ if (lp->cps > lp->triggercps) { if (lp->slave) { if (!lp->sqfull) { /* First time overload: set timestamp only */ lp->sqfull = 1; lp->sqfull_stamp = jiffies; } else { /* subsequent overload: if slavedelay exceeded, start dialing */ if (time_after(jiffies, lp->sqfull_stamp + lp->slavedelay)) { slp = lp->slave->priv; if (!(slp->flags & ISDN_NET_CONNECTED)) { isdn_net_force_dial_lp((isdn_net_local *) lp->slave->priv); } } } } } else { if (lp->sqfull && time_after(jiffies, lp->sqfull_stamp + lp->slavedelay + (10 * HZ))) { lp->sqfull = 0; } /* this is a hack to allow auto-hangup for slaves on moderate loads */ nd->queue = nd->local; } return retv;}static voidisdn_net_adjust_hdr(struct sk_buff *skb, struct net_device *dev){ isdn_net_local *lp = (isdn_net_local *) dev->priv; if (!skb) return; if (lp->p_encap == ISDN_NET_ENCAP_ETHER) { int pullsize = (ulong)skb->nh.raw - (ulong)skb->data - ETH_HLEN; if (pullsize > 0) { printk(KERN_DEBUG "isdn_net: Pull junk %d\n", pullsize); skb_pull(skb, pullsize); } }}static void isdn_net_tx_timeout(struct net_device * ndev){ isdn_net_local *lp = (isdn_net_local *) ndev->priv; printk(KERN_WARNING "isdn_tx_timeout dev %s dialstate %d\n", ndev->name, lp->dialstate); if (!lp->dialstate){ lp->stats.tx_errors++; /* * There is a certain probability that this currently * works at all because if we always wake up the interface, * then upper layer will try to send the next packet * immediately. And then, the old clean_up logic in the * driver will hopefully continue to work as it used to do. * * This is rather primitive right know, we better should * clean internal queues here, in particular for multilink and * ppp, and reset HL driver's channel, too. --HE * * actually, this may not matter at all, because ISDN hardware * should not see transmitter hangs at all IMO * changed KERN_DEBUG to KERN_WARNING to find out if this is * ever called --KG */ } ndev->trans_start = jiffies; netif_wake_queue(ndev);}/* * Try sending a packet. * If this interface isn't connected to a ISDN-Channel, find a free channel, * and start dialing. */static intisdn_net_start_xmit(struct sk_buff *skb, struct net_device *ndev){ isdn_net_local *lp = (isdn_net_local *) ndev->priv;#ifdef CONFIG_ISDN_X25 struct concap_proto * cprot = lp -> netdev -> cprot;/* At this point hard_start_xmit() passes control to the encapsulation protocol (if present). For X.25 auto-dialing is completly bypassed because: - It does not conform with the semantics of a reliable datalink service as needed by X.25 PLP. - I don't want that the interface starts dialing when the network layer sends a message which requests to disconnect the lapb link (or if it sends any other message not resulting in data transmission). Instead, dialing will be initiated by the encapsulation protocol entity when a dl_establish request is received from the upper layer.*/ if (cprot && cprot -> pops) { int ret = cprot -> pops -> encap_and_xmit ( cprot , skb); if (ret) netif_stop_queue(ndev); return ret; } else#endif /* auto-dialing xmit function */ {#ifdef ISDN_DEBUG_NET_DUMP u_char *buf;#endif isdn_net_adjust_hdr(skb, ndev);#ifdef ISDN_DEBUG_NET_DUMP buf = skb->data; isdn_dumppkt("S:", buf, skb->len, 40);#endif if (!(lp->flags & ISDN_NET_CONNECTED)) { int chi; /* only do autodial if allowed by config */ if (!(ISDN_NET_DIALMODE(*lp) == ISDN_NET_DM_AUTO)) { isdn_net_unreachable(ndev, skb, "dial rejected: interface not in dialmode `auto'"); dev_kfree_skb(skb); return 0; } if (lp->phone[1]) { ulong flags; if(lp->dialwait_timer <= 0) if(lp->dialstarted > 0 && lp->dialtimeout > 0 && time_before(jiffies, lp->dialstarted + lp->dialtimeout + lp->dialwait)) lp->dialwait_timer = lp->dialstarted + lp->dialtimeout + lp->dialwait; if(lp->dialwait_timer > 0) { if(time_before(jiffies, lp->dialwait_timer)) { isdn_net_unreachable(ndev, skb, "dial rejected: retry-time not reached"); dev_kfree_skb(skb); return 0; } else lp->dialwait_timer = 0; } /* Grab a free ISDN-Channel */ spin_lock_irqsave(&dev->lock, flags); if (((chi = isdn_get_free_channel( ISDN_USAGE_NET, lp->l2_proto, lp->l3_proto, lp->pre_device, lp->pre_channel, lp->msn) ) < 0) && ((chi = isdn_get_free_channel( ISDN_USAGE_NET, lp->l2_proto, lp->l3_proto, lp->pre_device, lp->pre_channel^1, lp->msn) ) < 0)) { spin_unlock_irqrestore(&dev->lock, flags); isdn_net_unreachable(ndev, skb, "No channel"); dev_kfree_skb(skb); return 0; } /* Log packet, which triggered dialing */ if (dev->net_verbose) isdn_net_log_skb(skb, lp); lp->dialstate = 1; /* Connect interface with channel */ isdn_net_bind_channel(lp, chi);#ifdef CONFIG_ISDN_PPP if (lp->p_encap == ISDN_NET_ENCAP_SYNCPPP) { /* no 'first_skb' handling for syncPPP */ if (isdn_ppp_bind(lp) < 0) { dev_kfree_skb(skb); isdn_net_unbind_channel(lp); spin_unlock_irqrestore(&dev->lock, flags); return 0; /* STN (skb to nirvana) ;) */ }#ifdef CONFIG_IPPP_FILTER if (isdn_ppp_autodial_filter(skb, lp)) { isdn_ppp_free(lp); isdn_net_unbind_channel(lp); spin_unlock_irqrestore(&dev->lock, flags); isdn_net_unreachable(ndev, skb, "dial rejected: packet filtered"); dev_kfree_skb(skb); return 0; }#endif spin_unlock_irqrestore(&dev->lock, flags); isdn_net_dial(); /* Initiate dialing */ netif_stop_queue(ndev); return 1; /* let upper layer requeue skb packet */ }#endif /* Initiate dialing */ spin_unlock_irqrestore(&dev->lock, flags); isdn_net_dial(); isdn_net_device_stop_queue(lp); return 1; } else { isdn_net_unreachable(ndev, skb, "No phone number"); dev_kfree_skb(skb); return 0; } } else { /* Device is connected to an ISDN channel */ ndev->trans_start = jiffies; if (!lp->dialstate) { /* ISDN connection is established, try sending */ int ret; ret = (isdn_net_xmit(ndev, skb)); if(ret) netif_stop_queue(ndev); return ret; } else netif_stop_queue(ndev); } } return 1;}/* * Shutdown a net-interface. */static intisdn_net_close(struct net_device *dev){ struct net_device *p;#ifdef CONFIG_ISDN_X25 struct concap_proto * cprot = ( (isdn_net_local *) dev->priv ) -> netdev -> cprot; /* printk(KERN_DEBUG "isdn_net_close %s\n" , dev-> name ); */#endif#ifdef CONFIG_ISDN_X25 if( cprot && cprot -> pops ) cprot -> pops -> close( cprot );#endif netif_stop_queue(dev); if ((p = (((isdn_net_local *) dev->priv)->slave))) { /* If this interface has slaves, stop them also */ while (p) {#ifdef CONFIG_ISDN_X25 cprot = ( (isdn_net_local *) p->priv ) -> netdev -> cprot; if( cprot && cprot -> pops ) cprot -> pops -> close( cprot );#endif isdn_net_hangup(p); p = (((isdn_net_local *) p->priv)->slave); } } isdn_net_hangup(dev); isdn_unlock_drivers(); return 0;}/* * Get statistics */static struct net_device_stats *isdn_net_get_stats(struct net_device *dev){ isdn_net_local *lp = (isdn_net_local *) dev->priv; return &lp->stats;}/* This is simply a copy from std. eth.c EXCEPT we pull ETH_HLEN * instead of dev->hard_header_len off. This is done because the * lowlevel-driver has already pulled off its stuff when we get * here and this routine only gets called with p_encap == ETHER. * Determine the packet's protocol ID. The rule here is that we * assume 802.3 if the type field is short enough to be a length. * This is normal practice and works for any 'now in use' protocol. */static unsigned shortisdn_net_type_trans(struct sk_buff *skb, struct net_device *dev){ struct ethhdr *eth; unsigned char *rawp; skb->mac.raw = skb->data; skb_pull(skb, ETH_HLEN); eth = eth_hdr(skb); if (*eth->h_dest & 1) { if (memcmp(eth->h_dest, dev->broadcast, ETH_ALEN) == 0) skb->pkt_type = PACKET_BROADCAST; else skb->pkt_type = PACKET_MULTICAST; } /* * This ALLMULTI check should be redundant by 1.4 * so don't forget to remove it. */ else if (dev->flags & (IFF_PROMISC /*| IFF_ALLMULTI*/)) { if (memcmp(eth->h_dest, dev->dev_addr, ETH_ALEN)) skb->pkt_type = PACKET_OTHERHOST; } if (ntohs(eth->h_proto) >= 1536) return eth->h_proto; rawp = skb->data; /* * This is a magic hack to spot IPX packets. Older Novell breaks * the protocol design and runs IPX over 802.3 without an 802.2 LLC * layer. We look for FFFF which isn't a used 802.2 SSAP/DSAP. This * won't work for fault tolerant netware but does for the rest. */ if (*(unsigned short *) rawp == 0xFFFF) return htons(ETH_P_802_3); /* * Real 802.2 LLC */ return htons(ETH_P_802_2);}/* * CISCO HDLC keepalive specific stuff */static struct sk_buff*isdn_net_ciscohdlck_alloc_skb(isdn_net_local *lp, int len){ unsigned short hl = dev->drv[lp->isdn_device]->interface->hl_hdrlen; struct sk_buff *skb;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -