patch-2.4.4 linux/drivers/isdn/isdn_net.c

Next file: linux/drivers/isdn/isdn_net.h
Previous file: linux/drivers/isdn/isdn_common.c
Back to the patch index
Back to the overall index

diff -u --recursive --new-file v2.4.3/linux/drivers/isdn/isdn_net.c linux/drivers/isdn/isdn_net.c
@@ -22,6 +22,11 @@
  *
  */
 
+/* Jan 2001: fix CISCO HDLC      Bjoern A. Zeeb <i4l@zabbadoz.net>
+ *           for info on the protocol, see 
+ *           http://i4l.zabbadoz.net/i4l/cisco-hdlc.txt
+ */
+
 #include <linux/config.h>
 #define __NO_VERSION__
 #include <linux/module.h>
@@ -182,7 +187,10 @@
 int isdn_net_force_dial_lp(isdn_net_local *);
 static int isdn_net_start_xmit(struct sk_buff *, struct net_device *);
 
-char *isdn_net_revision = "$Revision: 1.140.6.3 $";
+static void isdn_net_ciscohdlck_connected(isdn_net_local *lp);
+static void isdn_net_ciscohdlck_disconnected(isdn_net_local *lp);
+
+char *isdn_net_revision = "$Revision: 1.144 $";
 
  /*
   * Code for raw-networking over ISDN
@@ -454,6 +462,8 @@
 					pops -> disconn_ind(cprot);
 #endif /* CONFIG_ISDN_X25 */
 				if ((!lp->dialstate) && (lp->flags & ISDN_NET_CONNECTED)) {
+					if (lp->p_encap == ISDN_NET_ENCAP_CISCOHDLCK)
+						isdn_net_ciscohdlck_disconnected(lp);
 #ifdef CONFIG_ISDN_PPP
 					if (lp->p_encap == ISDN_NET_ENCAP_SYNCPPP)
 						isdn_ppp_free(lp);
@@ -498,7 +508,7 @@
 						lp->dialstate = 0;
 						isdn_timer_ctrl(ISDN_TIMER_NETHANGUP, 1);
 						if (lp->p_encap == ISDN_NET_ENCAP_CISCOHDLCK)
-							isdn_timer_ctrl(ISDN_TIMER_KEEPALIVE, 1);
+							isdn_net_ciscohdlck_connected(lp);
 						if (lp->p_encap != ISDN_NET_ENCAP_SYNCPPP) {
 							if (lp->master) { /* is lp a slave? */
 								isdn_net_dev *nd = ((isdn_net_local *)lp->master->priv)->netdev;
@@ -660,7 +670,7 @@
 					isdn_net_hangup(&p->dev);
 					break;
 				}
-				if (!strcmp(lp->dial->num, "LEASED")) {
+				if (!strncmp(lp->dial->num, "LEASED", strlen("LEASED"))) {
 					restore_flags(flags);
 					lp->dialstate = 4;
 					printk(KERN_INFO "%s: Open leased line ...\n", lp->name);
@@ -1415,92 +1425,347 @@
 	return htons(ETH_P_802_2);
 }
 
-static void
-isdn_net_slarp_send(isdn_net_local *lp, int is_reply)
+
+/* 
+ * 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 = alloc_skb(hl + sizeof(cisco_hdr) + sizeof(cisco_slarp), GFP_ATOMIC);
-	unsigned long t = (jiffies / HZ * 1000000);
-	cisco_hdr *ch;
-	cisco_slarp *s;
+	struct sk_buff *skb;
 
+	skb = alloc_skb(hl + len, GFP_ATOMIC);
 	if (!skb) {
-		printk(KERN_WARNING
-		       "%s: Could not allocate SLARP reply\n", lp->name);
-		return;
+		printk("isdn out of mem at %s:%d!\n", __FILE__, __LINE__);
+		return 0;
 	}
 	skb_reserve(skb, hl);
-	ch = (cisco_hdr *)skb_put(skb, sizeof(cisco_hdr));
-	ch->addr = CISCO_ADDR_UNICAST;
-	ch->ctrl = 0;
-	ch->type = htons(CISCO_TYPE_SLARP);
-	s = (cisco_slarp *)skb_put(skb, sizeof(cisco_slarp));
-	if (is_reply) {
-		s->code = htonl(CISCO_SLARP_REPLY);
-		memset(&s->slarp.reply.ifaddr, 0, sizeof(__u32));
-		memset(&s->slarp.reply.netmask, 0, sizeof(__u32));
-	} else {
-		lp->cisco_myseq++;
-		s->code = htonl(CISCO_SLARP_KEEPALIVE);
-		s->slarp.keepalive.my_seq = htonl(lp->cisco_myseq);
-		s->slarp.keepalive.your_seq = htonl(lp->cisco_yourseq);
-	}
-	s->rel = 0xffff;
-	s->t1 = t >> 16;
-	s->t0 = t & 0xffff;
-	isdn_net_write_super(lp, skb);
+	return skb;
 }
 
-static void
-isdn_net_slarp_in(isdn_net_local *lp, struct sk_buff *skb)
+/* cisco hdlck device private ioctls */
+int
+isdn_ciscohdlck_dev_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
 {
-	cisco_slarp *s = (cisco_slarp *)skb->data;
+	isdn_net_local *lp = (isdn_net_local *) dev->priv;
+	unsigned long len = 0;
+	unsigned long expires = 0;
+	int tmp = 0;
+	int period = lp->cisco_keepalive_period;
+	char debserint = lp->cisco_debserint;
+	int rc = 0;
+
+	if (lp->p_encap != ISDN_NET_ENCAP_CISCOHDLCK)
+		return -EINVAL;
+
+	switch (cmd) {
+		/* get/set keepalive period */
+		case SIOCGKEEPPERIOD:
+			len = (unsigned long)sizeof(lp->cisco_keepalive_period);
+			if (copy_to_user((char *)ifr->ifr_ifru.ifru_data,
+				(int *)&lp->cisco_keepalive_period, len))
+				rc = -EFAULT;
+			break;
+		case SIOCSKEEPPERIOD:
+			tmp = lp->cisco_keepalive_period;
+			len = (unsigned long)sizeof(lp->cisco_keepalive_period);
+			if (copy_from_user((int *)&period,
+				(char *)ifr->ifr_ifru.ifru_data, len))
+				rc = -EFAULT;
+			if ((period > 0) && (period <= 32767))
+				lp->cisco_keepalive_period = period;
+			else
+				rc = -EINVAL;
+			if (!rc && (tmp != lp->cisco_keepalive_period)) {
+				expires = (unsigned long)(jiffies +
+					lp->cisco_keepalive_period * HZ);
+				mod_timer(&lp->cisco_timer, expires);
+				printk(KERN_INFO "%s: Keepalive period set "
+					"to %d seconds.\n",
+					lp->name, lp->cisco_keepalive_period);
+			}
+			break;
 
-	switch (ntohl(s->code)) {
-		case CISCO_SLARP_REQUEST:
-			isdn_net_slarp_send(lp, 1);
+		/* get/set debugging */
+		case SIOCGDEBSERINT:
+			len = (unsigned long)sizeof(lp->cisco_debserint);
+			if (copy_to_user((char *)ifr->ifr_ifru.ifru_data,
+				(char *)&lp->cisco_debserint, len))
+				rc = -EFAULT;
 			break;
-		case CISCO_SLARP_REPLY:
-			/* Ignore replies */
+		case SIOCSDEBSERINT:
+			len = (unsigned long)sizeof(lp->cisco_debserint);
+			if (copy_from_user((char *)&debserint,
+				(char *)ifr->ifr_ifru.ifru_data, len))
+				rc = -EFAULT;
+			if ((debserint >= 0) && (debserint <= 64))
+				lp->cisco_debserint = debserint;
+			else
+				rc = -EINVAL;
 			break;
-		case CISCO_SLARP_KEEPALIVE:
-			lp->cisco_yourseq = s->slarp.keepalive.my_seq;
-			if (ntohl(s->slarp.keepalive.my_seq == lp->cisco_myseq)) {
-				if (lp->cisco_loop++ == 2) {
-					printk(KERN_WARNING "%s: Keepalive Loop\n",
-					       lp->name);
-					lp->cisco_myseq ^= jiffies;
-				}
-			} else
-				lp->cisco_loop = 0;
+
+		default:
+			rc = -EINVAL;
 			break;
 	}
-	kfree_skb(skb);
+	return (rc);
 }
 
-/*
- * Called every 10 sec. via timer-interrupt if
- * any network-interface has Cisco-Keepalive-Encapsulation
- * and is online.
- * Send Keepalive-Packet and re-schedule.
- */
-void
-isdn_net_slarp_out(void)
+/* called via cisco_timer.function */
+static void
+isdn_net_ciscohdlck_slarp_send_keepalive(unsigned long data)
 {
-	isdn_net_dev *p = dev->netdev;
-	int anymore = 0;
+	isdn_net_local *lp = (isdn_net_local *) data;
+	struct sk_buff *skb;
+	unsigned char *p;
+	unsigned long last_cisco_myseq = lp->cisco_myseq;
+	int myseq_diff = 0;
 
-	while (p) {
-		isdn_net_local *l = p->local;
-		if ((l->p_encap == ISDN_NET_ENCAP_CISCOHDLCK) &&
-		    (l->flags & ISDN_NET_CONNECTED) &&
-		    (!l->dialstate)                           ) {
-			anymore = 1;
-			isdn_net_slarp_send(l, 0);
+	if (!(lp->flags & ISDN_NET_CONNECTED) || lp->dialstate) {
+		printk("isdn BUG at %s:%d!\n", __FILE__, __LINE__);
+		return;
+	}
+	lp->cisco_myseq++;
+
+	myseq_diff = (lp->cisco_myseq - lp->cisco_mineseen);
+	if ((lp->cisco_line_state) && ((myseq_diff >= 3)||(myseq_diff <= -3))) {
+		/* line up -> down */
+		lp->cisco_line_state = 0;
+		printk (KERN_WARNING
+				"UPDOWN: Line protocol on Interface %s,"
+				" changed state to down\n", lp->name);
+		/* should stop routing higher-level data accross */
+	} else if ((!lp->cisco_line_state) &&
+		(myseq_diff >= 0) && (myseq_diff <= 2)) {
+		/* line down -> up */
+		lp->cisco_line_state = 1;
+		printk (KERN_WARNING
+				"UPDOWN: Line protocol on Interface %s,"
+				" changed state to up\n", lp->name);
+		/* restart routing higher-level data accross */
+	}
+
+	if (lp->cisco_debserint)
+		printk (KERN_DEBUG "%s: HDLC "
+			"myseq %lu, mineseen %lu%c, yourseen %lu, %s\n",
+			lp->name, last_cisco_myseq, lp->cisco_mineseen,
+			((last_cisco_myseq == lp->cisco_mineseen) ? '*' : 040),
+			lp->cisco_yourseq,
+			((lp->cisco_line_state) ? "line up" : "line down"));
+
+	skb = isdn_net_ciscohdlck_alloc_skb(lp, 4 + 14);
+	if (!skb)
+		return;
+
+	p = skb_put(skb, 4 + 14);
+
+	/* cisco header */
+	p += put_u8 (p, CISCO_ADDR_UNICAST);
+	p += put_u8 (p, CISCO_CTRL);
+	p += put_u16(p, CISCO_TYPE_SLARP);
+
+	/* slarp keepalive */
+	p += put_u32(p, CISCO_SLARP_KEEPALIVE);
+	p += put_u32(p, lp->cisco_myseq);
+	p += put_u32(p, lp->cisco_yourseq);
+	p += put_u16(p, 0xffff); // reliablity, always 0xffff
+
+	isdn_net_write_super(lp, skb);
+
+	lp->cisco_timer.expires = jiffies + lp->cisco_keepalive_period * HZ;
+	
+	add_timer(&lp->cisco_timer);
+}
+
+static void
+isdn_net_ciscohdlck_slarp_send_request(isdn_net_local *lp)
+{
+	struct sk_buff *skb;
+	unsigned char *p;
+
+	skb = isdn_net_ciscohdlck_alloc_skb(lp, 4 + 14);
+	if (!skb)
+		return;
+
+	p = skb_put(skb, 4 + 14);
+
+	/* cisco header */
+	p += put_u8 (p, CISCO_ADDR_UNICAST);
+	p += put_u8 (p, CISCO_CTRL);
+	p += put_u16(p, CISCO_TYPE_SLARP);
+
+	/* slarp request */
+	p += put_u32(p, CISCO_SLARP_REQUEST);
+	p += put_u32(p, 0); // address
+	p += put_u32(p, 0); // netmask
+	p += put_u16(p, 0); // unused
+
+	isdn_net_write_super(lp, skb);
+}
+
+static void 
+isdn_net_ciscohdlck_connected(isdn_net_local *lp)
+{
+	lp->cisco_myseq = 0;
+	lp->cisco_mineseen = 0;
+	lp->cisco_yourseq = 0;
+	lp->cisco_keepalive_period = ISDN_TIMER_KEEPINT;
+	lp->cisco_last_slarp_in = 0;
+	lp->cisco_line_state = 0;
+	lp->cisco_debserint = 0;
+
+	/* send slarp request because interface/seq.no.s reset */
+	isdn_net_ciscohdlck_slarp_send_request(lp);
+
+	init_timer(&lp->cisco_timer);
+	lp->cisco_timer.data = (unsigned long) lp;
+	lp->cisco_timer.function = isdn_net_ciscohdlck_slarp_send_keepalive;
+	lp->cisco_timer.expires = jiffies + lp->cisco_keepalive_period * HZ;
+	add_timer(&lp->cisco_timer);
+}
+
+static void 
+isdn_net_ciscohdlck_disconnected(isdn_net_local *lp)
+{
+	del_timer(&lp->cisco_timer);
+}
+
+static void
+isdn_net_ciscohdlck_slarp_send_reply(isdn_net_local *lp)
+{
+	struct sk_buff *skb;
+	unsigned char *p;
+	struct in_device *in_dev = NULL;
+	u32 addr = 0;		/* local ipv4 address */
+	u32 mask = 0;		/* local netmask */
+
+	if ((in_dev = lp->netdev->dev.ip_ptr) != NULL) {
+		/* take primary(first) address of interface */
+		struct in_ifaddr *ifa = in_dev->ifa_list;
+		if (ifa != NULL) {
+			addr = ifa->ifa_local;
+			mask = ifa->ifa_mask;
 		}
-		p = (isdn_net_dev *) p->next;
 	}
-	isdn_timer_ctrl(ISDN_TIMER_KEEPALIVE, anymore);
+
+	skb = isdn_net_ciscohdlck_alloc_skb(lp, 4 + 14);
+	if (!skb)
+		return;
+
+	p = skb_put(skb, 4 + 14);
+
+	/* cisco header */
+	p += put_u8 (p, CISCO_ADDR_UNICAST);
+	p += put_u8 (p, CISCO_CTRL);
+	p += put_u16(p, CISCO_TYPE_SLARP);
+
+	/* slarp reply, send own ip/netmask; if values are nonsense remote
+	 * should think we are unable to provide it with an address via SLARP */
+	p += put_u32(p, CISCO_SLARP_REPLY);
+	p += put_u32(p, addr);	// address
+	p += put_u32(p, mask);	// netmask
+	p += put_u16(p, 0);	// unused
+
+	isdn_net_write_super(lp, skb);
+}
+
+static void
+isdn_net_ciscohdlck_slarp_in(isdn_net_local *lp, struct sk_buff *skb)
+{
+	unsigned char *p;
+	int period;
+	__u32 code;
+	__u32 my_seq, addr;
+	__u32 your_seq, mask;
+	__u16 unused;
+
+	if (skb->len < 14)
+		return;
+
+	p = skb->data;
+	p += get_u32(p, &code);
+	
+	switch (code) {
+	case CISCO_SLARP_REQUEST:
+		lp->cisco_yourseq = 0;
+		isdn_net_ciscohdlck_slarp_send_reply(lp);
+		break;
+	case CISCO_SLARP_REPLY:
+		/* Ignore replies - at least for now */
+		if (lp->cisco_debserint) {
+			p += get_u32(p, &addr);
+			p += get_u32(p, &mask);
+			p += get_u16(p, &unused);
+			printk(KERN_DEBUG "%s: got slarp reply (%ul/%ul) - "
+				"ignored\n", lp->name, addr, mask);
+		}
+		break;
+	case CISCO_SLARP_KEEPALIVE:
+		period = (int)((jiffies - lp->cisco_last_slarp_in
+				+ HZ/2 - 1) / HZ);
+		if (lp->cisco_debserint &&
+				(period != lp->cisco_keepalive_period) &&
+				lp->cisco_last_slarp_in) {
+			printk(KERN_DEBUG "%s: Keepalive period mismatch - "
+				"is %d but should be %d.\n",
+				lp->name, period, lp->cisco_keepalive_period);
+		}
+		lp->cisco_last_slarp_in = jiffies;
+		p += get_u32(p, &my_seq);
+		p += get_u32(p, &your_seq);
+		p += get_u16(p, &unused);
+		lp->cisco_yourseq = my_seq;
+		lp->cisco_mineseen = your_seq;
+		break;
+	}
+}
+
+static void
+isdn_net_ciscohdlck_receive(isdn_net_local *lp, struct sk_buff *skb)
+{
+	unsigned char *p;
+	__u8 addr;
+	__u8 ctrl;
+	__u16 type;
+	
+	if (skb->len < 4)
+		goto out_free;
+
+	p = skb->data;
+	p += get_u8 (p, &addr);
+	p += get_u8 (p, &ctrl);
+	p += get_u16(p, &type);
+	skb_pull(skb, 4);
+	
+	if (addr != CISCO_ADDR_UNICAST && addr != CISCO_ADDR_BROADCAST) {
+		printk(KERN_WARNING "%s: Unknown Cisco addr 0x%02x\n",
+		       lp->name, addr);
+		goto out_free;
+	}
+	if (ctrl != CISCO_CTRL) {
+		printk(KERN_WARNING "%s: Unknown Cisco ctrl 0x%02x\n",
+		       lp->name, ctrl);
+		goto out_free;
+	}
+
+	switch (type) {
+	case CISCO_TYPE_INET:
+		skb->protocol = htons(ETH_P_IP);
+		netif_rx(skb);
+		break;
+	case CISCO_TYPE_SLARP:
+		isdn_net_ciscohdlck_slarp_in(lp, skb);
+		goto out_free;
+	default:
+		printk(KERN_WARNING "%s: Unknown Cisco type 0x%04x\n",
+		       lp->name, type);
+		goto out_free;
+	}
+	return;
+
+ out_free:
+	kfree_skb(skb);
 }
 
 /*
@@ -1517,8 +1782,6 @@
 #ifdef CONFIG_ISDN_X25
 	struct concap_proto *cprot = lp -> netdev -> cprot;
 #endif
-	cisco_hdr *ch;
-
 	lp->transcount += skb->len;
 
 	lp->stats.rx_packets++;
@@ -1558,36 +1821,8 @@
 			skb->protocol = htons(ETH_P_IP);
 			break;
 		case ISDN_NET_ENCAP_CISCOHDLCK:
-			ch = (cisco_hdr *)skb->data;
-			if ((ch->addr != CISCO_ADDR_UNICAST) &&
-			    (ch->addr != CISCO_ADDR_BROADCAST)  ) {
-				printk(KERN_WARNING "%s: Unknown Cisco addr 0x%02x\n",
-				       lp->name, ch->addr);
-				kfree_skb(skb);
-				return;
-			}
-			if (ch->ctrl != 0) {
-				printk(KERN_WARNING "%s: Unknown Cisco ctrl 0x%02x\n",
-				       lp->name, ch->ctrl);
-				kfree_skb(skb);
-				return;
-			}
-			switch (ntohs(ch->type)) {
-				case CISCO_TYPE_INET:
-					skb_pull(skb, 4);
-					skb->protocol = htons(ETH_P_IP);
-					break;
-				case CISCO_TYPE_SLARP:
-					skb_pull(skb, 4);
-					isdn_net_slarp_in(olp, skb);
-					return;
-				default:
-					printk(KERN_WARNING "%s: Unknown Cisco type 0x%04x\n",
-					       lp->name, ch->type);
-					kfree_skb(skb);
-					return;
-			}
-			break;
+			isdn_net_ciscohdlck_receive(lp, skb);
+			return;
 		case ISDN_NET_ENCAP_CISCOHDLC:
 			/* CISCO-HDLC IP with type field and  fake I-frame-header */
 			skb_pull(skb, 2);
@@ -1705,6 +1940,7 @@
 		void *daddr, void *saddr, unsigned plen)
 {
 	isdn_net_local *lp = dev->priv;
+	unsigned char *p;
 	ushort len = 0;
 
 	switch (lp->p_encap) {
@@ -1733,10 +1969,11 @@
 			len = 2;
 			break;
 		case ISDN_NET_ENCAP_CISCOHDLC:
-			skb_push(skb, 4);
-			skb->data[0] = 0x0f;
-			skb->data[1] = 0x00;
-			*((ushort *) & skb->data[2]) = htons(type);
+		case ISDN_NET_ENCAP_CISCOHDLCK:
+			p = skb_push(skb, 4);
+			p += put_u8 (p, CISCO_ADDR_UNICAST);
+			p += put_u8 (p, CISCO_CTRL);
+			p += put_u16(p, type);
 			len = 4;
 			break;
 #ifdef CONFIG_ISDN_X25
@@ -1842,9 +2079,7 @@
 	ndev->stop = &isdn_net_close;
 	ndev->get_stats = &isdn_net_get_stats;
 	ndev->rebuild_header = &isdn_net_rebuild_header;
-#ifdef CONFIG_ISDN_PPP
-	ndev->do_ioctl = isdn_ppp_dev_ioctl;
-#endif
+	ndev->do_ioctl = NULL;
 	return 0;
 }
 
@@ -2508,6 +2743,7 @@
 #else
 			p->dev.type = ARPHRD_PPP;	/* change ARP type */
 			p->dev.addr_len = 0;
+			p->dev.do_ioctl = isdn_ppp_dev_ioctl;
 #endif
 			break;
 		case ISDN_NET_ENCAP_X25IFACE:
@@ -2519,6 +2755,9 @@
 			p->dev.type = ARPHRD_X25;	/* change ARP type */
 			p->dev.addr_len = 0;
 #endif
+			break;
+		case ISDN_NET_ENCAP_CISCOHDLCK:
+			p->dev.do_ioctl = isdn_ciscohdlck_dev_ioctl;
 			break;
 		default:
 			if( cfg->p_encap >= 0 &&

FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen (who was at: slshen@lbl.gov)