static void fr_timer(unsigned long arg) { hdlc_device *hdlc = (hdlc_device*)arg; int i, cnt = 0, reliable; u32 list; if (mode_is(hdlc, MODE_DCE)) reliable = (jiffies - hdlc->lmi.last_poll < hdlc->lmi.T392*HZ); else { hdlc->lmi.last_errors <<= 1; /* Shift the list */ if (hdlc->lmi.state & LINK_STATE_REQUEST) { printk(KERN_INFO "%s: No LMI status reply received\n", hdlc_to_name(hdlc)); hdlc->lmi.last_errors |= 1; } for (i = 0, list = hdlc->lmi.last_errors; i < hdlc->lmi.N393; i++, list >>= 1) cnt += (list & 1); /* errors count */ reliable = (cnt < hdlc->lmi.N392); } if ((hdlc->lmi.state & LINK_STATE_RELIABLE) != (reliable ? LINK_STATE_RELIABLE : 0)) { pvc_device *pvc = hdlc->first_pvc; while (pvc) {/* Deactivate all PVCs */ pvc->state &= ~(PVC_STATE_NEW | PVC_STATE_ACTIVE); pvc = pvc->next; } hdlc->lmi.state ^= LINK_STATE_RELIABLE; printk(KERN_INFO "%s: Link %sreliable\n", hdlc_to_name(hdlc), reliable ? "" : "un"); if (reliable) { hdlc->lmi.N391cnt = 0; /* Request full status */ hdlc->lmi.state |= LINK_STATE_CHANGED; } } if (mode_is(hdlc, MODE_DCE)) hdlc->timer.expires = jiffies + hdlc->lmi.T392*HZ; else { if (hdlc->lmi.N391cnt) hdlc->lmi.N391cnt--; fr_lmi_send(hdlc, hdlc->lmi.N391cnt == 0); hdlc->lmi.state |= LINK_STATE_REQUEST; hdlc->timer.expires = jiffies + hdlc->lmi.T391*HZ; } hdlc->timer.function = fr_timer; hdlc->timer.data = arg; add_timer(&hdlc->timer); }
static int hdlc_xmit(struct sk_buff *skb, struct net_device *dev) { hdlc_device *hdlc = dev_to_hdlc(dev); #ifdef CONFIG_HDLC_X25 if (mode_is(hdlc, MODE_X25 | MODE_SOFT)) { int result; /* X.25 to LAPB */ switch (skb->data[0]) { case 0: /* Data to be transmitted */ skb_pull(skb, 1); if ((result = lapb_data_request(hdlc, skb)) != LAPB_OK) dev_kfree_skb(skb); return 0; case 1: if ((result = lapb_connect_request(hdlc))!= LAPB_OK) { if (result == LAPB_CONNECTED) { /* Send connect confirm. msg to level 3 */ x25_connected(hdlc, 0); } else { printk(KERN_ERR "%s: LAPB connect " "request failed, error code = " "%i\n", hdlc_to_name(hdlc), result); } } break; case 2: if ((result=lapb_disconnect_request(hdlc))!=LAPB_OK) { if (result == LAPB_NOTCONNECTED) { /* Send disconnect confirm. msg to level 3 */ x25_disconnected(hdlc, 0); } else { printk(KERN_ERR "%s: LAPB disconnect " "request failed, error code = " "%i\n", hdlc_to_name(hdlc), result); } } break; default: /* to be defined */ break; } dev_kfree_skb(skb); return 0; } /* MODE_X25 */ #endif /* CONFIG_HDLC_X25 */ return hdlc->xmit(hdlc, skb); }
static void cisco_keepalive_send(hdlc_device *hdlc, u32 type, u32 par1, u32 par2) { struct sk_buff *skb; cisco_packet *data; skb = dev_alloc_skb(sizeof(hdlc_header) + sizeof(cisco_packet)); if (!skb) { printk(KERN_WARNING "%s: Memory squeeze on cisco_keepalive_send()\n", hdlc_to_name(hdlc)); return; } skb_reserve(skb, 4); cisco_hard_header(skb, hdlc_to_dev(hdlc), CISCO_KEEPALIVE, NULL, NULL, 0); data = (cisco_packet*)skb->tail; data->type = htonl(type); data->par1 = htonl(par1); data->par2 = htonl(par2); data->rel = 0xFFFF; /* we will need do_div here if 1000 % HZ != 0 */ data->time = htonl(jiffies * (1000 / HZ)); skb_put(skb, sizeof(cisco_packet)); skb->priority = TC_PRIO_CONTROL; skb->dev = hdlc_to_dev(hdlc); skb->nh.raw = skb->data; dev_queue_xmit(skb); }
static void cisco_timer(unsigned long arg) { hdlc_device *hdlc = (hdlc_device*)arg; if ((hdlc->lmi.state & LINK_STATE_RELIABLE) && (jiffies - hdlc->lmi.last_poll >= hdlc->lmi.T392 * HZ)) { hdlc->lmi.state &= ~LINK_STATE_RELIABLE; printk(KERN_INFO "%s: Link down\n", hdlc_to_name(hdlc)); } cisco_keepalive_send(hdlc, CISCO_KEEPALIVE_REQ, ++hdlc->lmi.txseq, hdlc->lmi.rxseq); hdlc->timer.expires = jiffies + hdlc->lmi.T391*HZ; hdlc->timer.function = cisco_timer; hdlc->timer.data = arg; add_timer(&hdlc->timer); }
static void cisco_timer(unsigned long arg) { hdlc_device *hdlc = (hdlc_device*)arg; if (hdlc->state.cisco.up && jiffies - hdlc->state.cisco.last_poll >= hdlc->state.cisco.settings.timeout * HZ) { hdlc->state.cisco.up = 0; printk(KERN_INFO "%s: Link down\n", hdlc_to_name(hdlc)); } cisco_keepalive_send(hdlc, CISCO_KEEPALIVE_REQ, ++hdlc->state.cisco.txseq, hdlc->state.cisco.rxseq); hdlc->state.cisco.timer.expires = jiffies + hdlc->state.cisco.settings.interval * HZ; hdlc->state.cisco.timer.function = cisco_timer; hdlc->state.cisco.timer.data = arg; add_timer(&hdlc->state.cisco.timer); }
void x25_connect_disconnect(void *token, int reason, int code) { hdlc_device *hdlc = token; struct sk_buff *skb; unsigned char *ptr; if ((skb = dev_alloc_skb(1)) == NULL) { printk(KERN_ERR "%s: out of memory\n", hdlc_to_name(hdlc)); return; } ptr = skb_put(skb, 1); *ptr = code; skb->dev = hdlc_to_dev(hdlc); skb->protocol = htons(ETH_P_X25); skb->mac.raw = skb->data; skb->pkt_type = PACKET_HOST; netif_rx(skb); }
static void cisco_rx(struct sk_buff *skb) { hdlc_device *hdlc = dev_to_hdlc(skb->dev); hdlc_header *data = (hdlc_header*)skb->data; cisco_packet *cisco_data; struct in_device *in_dev; u32 addr, mask; if (skb->len < sizeof(hdlc_header)) goto rx_error; if (data->address != CISCO_MULTICAST && data->address != CISCO_UNICAST) goto rx_error; skb_pull(skb, sizeof(hdlc_header)); switch(ntohs(data->protocol)) { case CISCO_SYS_INFO: /* Packet is not needed, drop it. */ dev_kfree_skb_any(skb); return; case CISCO_KEEPALIVE: if (skb->len != CISCO_PACKET_LEN && skb->len != CISCO_BIG_PACKET_LEN) { printk(KERN_INFO "%s: Invalid length of Cisco " "control packet (%d bytes)\n", hdlc_to_name(hdlc), skb->len); goto rx_error; } cisco_data = (cisco_packet*)skb->data; switch(ntohl (cisco_data->type)) { case CISCO_ADDR_REQ: /* Stolen from syncppp.c :-) */ in_dev = hdlc_to_dev(hdlc)->ip_ptr; addr = 0; mask = ~0; /* is the mask correct? */ if (in_dev != NULL) { struct in_ifaddr **ifap = &in_dev->ifa_list; while (*ifap != NULL) { if (strcmp(hdlc_to_name(hdlc), (*ifap)->ifa_label) == 0) { addr = (*ifap)->ifa_local; mask = (*ifap)->ifa_mask; break; } ifap = &(*ifap)->ifa_next; } cisco_keepalive_send(hdlc, CISCO_ADDR_REPLY, addr, mask); } dev_kfree_skb_any(skb); return; case CISCO_ADDR_REPLY: printk(KERN_INFO "%s: Unexpected Cisco IP address " "reply\n", hdlc_to_name(hdlc)); goto rx_error; case CISCO_KEEPALIVE_REQ: hdlc->state.cisco.rxseq = ntohl(cisco_data->par1); if (ntohl(cisco_data->par2)==hdlc->state.cisco.txseq) { hdlc->state.cisco.last_poll = jiffies; if (!hdlc->state.cisco.up) { u32 sec, min, hrs, days; sec = ntohl(cisco_data->time) / 1000; min = sec / 60; sec -= min * 60; hrs = min / 60; min -= hrs * 60; days = hrs / 24; hrs -= days * 24; printk(KERN_INFO "%s: Link up (peer " "uptime %ud%uh%um%us)\n", hdlc_to_name(hdlc), days, hrs, min, sec); } hdlc->state.cisco.up = 1; } dev_kfree_skb_any(skb); return; } /* switch(keepalive type) */ } /* switch(protocol) */ printk(KERN_INFO "%s: Unsupported protocol %x\n", hdlc_to_name(hdlc), data->protocol); dev_kfree_skb_any(skb); return; rx_error: hdlc->stats.rx_errors++; /* Mark error */ dev_kfree_skb_any(skb); }
static void fr_netif(hdlc_device *hdlc, struct sk_buff *skb) { fr_hdr *fh = (fr_hdr*)skb->data; u8 *data = skb->data; u16 dlci; pvc_device *pvc; if (skb->len<4 || fh->ea1 || data[2] != FR_UI) goto rx_error; dlci = q922_to_dlci(skb->data); if (dlci == LMI_DLCI) { if (data[3] == LMI_PROTO) { if (fr_lmi_recv(hdlc, skb)) goto rx_error; else { /* No request pending */ hdlc->lmi.state &= ~LINK_STATE_REQUEST; hdlc->lmi.last_poll = jiffies; dev_kfree_skb_any(skb); return; } } printk(KERN_INFO "%s: Received non-LMI frame with LMI DLCI\n", hdlc_to_name(hdlc)); goto rx_error; } pvc = find_pvc(hdlc, dlci); if (!pvc) { #ifdef DEBUG_PKT printk(KERN_INFO "%s: No PVC for received frame's DLCI %d\n", hdlc_to_name(hdlc), dlci); #endif goto rx_error; } if ((pvc->netdev.flags & IFF_UP) == 0) { #ifdef DEBUG_PKT printk(KERN_INFO "%s: PVC for received frame's DLCI %d is down\n", hdlc_to_name(hdlc), dlci); #endif goto rx_error; } pvc->stats.rx_packets++; /* PVC traffic */ pvc->stats.rx_bytes += skb->len; if ((pvc->state & PVC_STATE_FECN) != (fh->fecn ? PVC_STATE_FECN : 0)) { #ifdef DEBUG_FECN printk(KERN_DEBUG "%s: FECN O%s\n", pvc_to_name(pvc), fh->fecn ? "N" : "FF"); #endif pvc->state ^= PVC_STATE_FECN; } if ((pvc->state & PVC_STATE_BECN) != (fh->becn ? PVC_STATE_BECN : 0)) { #ifdef DEBUG_FECN printk(KERN_DEBUG "%s: BECN O%s\n", pvc_to_name(pvc), fh->becn ? "N" : "FF"); #endif pvc->state ^= PVC_STATE_BECN; } if (pvc->state & PVC_STATE_BECN) pvc->stats.rx_compressed++; if (data[3] == NLPID_IP) { skb_pull(skb, 4); /* Remove 4-byte header (hdr, UI, NLPID) */ skb->protocol = htons(ETH_P_IP); skb->dev = &pvc->netdev; netif_rx(skb); return; } if (data[3] == NLPID_IPV6) { skb_pull(skb, 4); /* Remove 4-byte header (hdr, UI, NLPID) */ skb->protocol = htons(ETH_P_IPV6); skb->dev = &pvc->netdev; netif_rx(skb); return; } if (data[3] == FR_PAD && data[4] == NLPID_SNAP && data[5] == FR_PAD && data[6] == FR_PAD && data[7] == FR_PAD && ((data[8]<<8) | data[9]) == ETH_P_ARP) { skb_pull(skb, 10); skb->protocol = htons(ETH_P_ARP); skb->dev = &pvc->netdev; netif_rx(skb); return; } printk(KERN_INFO "%s: Unusupported protocol %x\n", hdlc_to_name(hdlc), data[3]); dev_kfree_skb_any(skb); return; rx_error: hdlc->stats.rx_errors++; /* Mark error */ dev_kfree_skb_any(skb); }
static int fr_lmi_recv(hdlc_device *hdlc, struct sk_buff *skb) { int stat_len; pvc_device *pvc; int reptype = -1, error; u8 rxseq, txseq; int i; if (skb->len < (mode_is(hdlc, MODE_FR_ANSI) ? LMI_ANSI_LENGTH : LMI_LENGTH)) { printk(KERN_INFO "%s: Short LMI frame\n", hdlc_to_name(hdlc)); return 1; } if (skb->data[5] != (!mode_is(hdlc, MODE_DCE) ? LMI_STATUS : LMI_STATUS_ENQUIRY)) { printk(KERN_INFO "%s: LMI msgtype=%x, Not LMI status %s\n", hdlc_to_name(hdlc), skb->data[2], mode_is(hdlc, MODE_DCE) ? "enquiry" : "reply"); return 1; } i = mode_is(hdlc, MODE_FR_ANSI) ? 7 : 6; if (skb->data[i] != (mode_is(hdlc, MODE_FR_CCITT) ? LMI_CCITT_REPTYPE : LMI_REPTYPE)) { printk(KERN_INFO "%s: Not a report type=%x\n", hdlc_to_name(hdlc), skb->data[i]); return 1; } i++; i++; /* Skip length field */ reptype = skb->data[i++]; if (skb->data[i]!= (mode_is(hdlc, MODE_FR_CCITT) ? LMI_CCITT_ALIVE : LMI_ALIVE)) { printk(KERN_INFO "%s: Unsupported status element=%x\n", hdlc_to_name(hdlc), skb->data[i]); return 1; } i++; i++; /* Skip length field */ hdlc->lmi.rxseq = skb->data[i++]; /* TX sequence from peer */ rxseq = skb->data[i++]; /* Should confirm our sequence */ txseq = hdlc->lmi.txseq; if (mode_is(hdlc, MODE_DCE)) { if (reptype != LMI_FULLREP && reptype != LMI_INTEGRITY) { printk(KERN_INFO "%s: Unsupported report type=%x\n", hdlc_to_name(hdlc), reptype); return 1; } } error = 0; if (!(hdlc->lmi.state & LINK_STATE_RELIABLE)) error = 1; if (rxseq == 0 || rxseq != txseq) { hdlc->lmi.N391cnt = 0; /* Ask for full report next time */ error = 1; } if (mode_is(hdlc, MODE_DCE)) { if ((hdlc->lmi.state & LINK_STATE_FULLREP_SENT) && !error) { /* Stop sending full report - the last one has been confirmed by DTE */ hdlc->lmi.state &= ~LINK_STATE_FULLREP_SENT; pvc = hdlc->first_pvc; while (pvc) { if (pvc->state & PVC_STATE_NEW) { pvc->state &= ~PVC_STATE_NEW; pvc->state |= PVC_STATE_ACTIVE; fr_log_dlci_active(pvc); /* Tell DTE that new PVC is now active */ hdlc->lmi.state |= LINK_STATE_CHANGED; } pvc = pvc->next; } } if (hdlc->lmi.state & LINK_STATE_CHANGED) { reptype = LMI_FULLREP; hdlc->lmi.state |= LINK_STATE_FULLREP_SENT; hdlc->lmi.state &= ~LINK_STATE_CHANGED; } fr_lmi_send(hdlc, reptype == LMI_FULLREP ? 1 : 0); return 0; } /* DTE */ if (reptype != LMI_FULLREP || error) return 0; stat_len = 3; pvc = hdlc->first_pvc; while (pvc) { pvc->newstate = 0; pvc = pvc->next; } while (skb->len >= i + 2 + stat_len) { u16 dlci; u8 state = 0; if (skb->data[i] != (mode_is(hdlc, MODE_FR_CCITT) ? LMI_CCITT_PVCSTAT : LMI_PVCSTAT)) { printk(KERN_WARNING "%s: Invalid PVCSTAT ID: %x\n", hdlc_to_name(hdlc), skb->data[i]); return 1; } i++; if (skb->data[i] != stat_len) { printk(KERN_WARNING "%s: Invalid PVCSTAT length: %x\n", hdlc_to_name(hdlc), skb->data[i]); return 1; } i++; dlci = status_to_dlci(hdlc, skb->data+i, &state); pvc = find_pvc(hdlc, dlci); if (pvc) pvc->newstate = state; else if (state == PVC_STATE_NEW) printk(KERN_INFO "%s: new PVC available, DLCI=%u\n", hdlc_to_name(hdlc), dlci); i += stat_len; } pvc = hdlc->first_pvc; while (pvc) { if (pvc->newstate == PVC_STATE_NEW) pvc->newstate = PVC_STATE_ACTIVE; pvc->newstate |= (pvc->state & ~(PVC_STATE_NEW|PVC_STATE_ACTIVE)); if (pvc->state != pvc->newstate) { pvc->state = pvc->newstate; fr_log_dlci_active(pvc); } pvc = pvc->next; } /* Next full report after N391 polls */ hdlc->lmi.N391cnt = hdlc->lmi.N391; return 0; }
static void fr_lmi_send(hdlc_device *hdlc, int fullrep) { struct sk_buff *skb; pvc_device *pvc = hdlc->first_pvc; int len = mode_is(hdlc, MODE_FR_ANSI) ? LMI_ANSI_LENGTH : LMI_LENGTH; int stat_len = 3; u8 *data; int i = 0; if (mode_is(hdlc, MODE_DCE) && fullrep) { len += hdlc->pvc_count * (2 + stat_len); if (len > HDLC_MAX_MTU) { printk(KERN_WARNING "%s: Too many PVCs while sending " "LMI full report\n", hdlc_to_name(hdlc)); return; } } skb = dev_alloc_skb(len); if (!skb) { printk(KERN_WARNING "%s: Memory squeeze on fr_lmi_send()\n", hdlc_to_name(hdlc)); return; } memset(skb->data, 0, len); skb_reserve(skb, 4); fr_hard_header(skb, hdlc_to_dev(hdlc), LMI_PROTO, NULL, NULL, 0); data = skb->tail; data[i++] = LMI_CALLREF; data[i++] = mode_is(hdlc, MODE_DCE) ? LMI_STATUS : LMI_STATUS_ENQUIRY; if (mode_is(hdlc, MODE_FR_ANSI)) data[i++] = LMI_ANSI_LOCKSHIFT; data[i++] = mode_is(hdlc, MODE_FR_CCITT) ? LMI_CCITT_REPTYPE : LMI_REPTYPE; data[i++] = LMI_REPT_LEN; data[i++] = fullrep ? LMI_FULLREP : LMI_INTEGRITY; data[i++] = mode_is(hdlc, MODE_FR_CCITT) ? LMI_CCITT_ALIVE : LMI_ALIVE; data[i++] = LMI_INTEG_LEN; data[i++] = hdlc->lmi.txseq = fr_lmi_nextseq(hdlc->lmi.txseq); data[i++] = hdlc->lmi.rxseq; if (mode_is(hdlc, MODE_DCE) && fullrep) { while (pvc) { data[i++] = mode_is(hdlc, MODE_FR_CCITT) ? LMI_CCITT_PVCSTAT:LMI_PVCSTAT; data[i++] = stat_len; if ((hdlc->lmi.state & LINK_STATE_RELIABLE) && (pvc->netdev.flags & IFF_UP) && !(pvc->state & (PVC_STATE_ACTIVE|PVC_STATE_NEW))) { pvc->state |= PVC_STATE_NEW; fr_log_dlci_active(pvc); } dlci_to_status(hdlc, netdev_dlci(&pvc->netdev), data+i, pvc->state); i += stat_len; pvc = pvc->next; } } skb_put(skb, i); skb->priority = TC_PRIO_CONTROL; skb->dev = hdlc_to_dev(hdlc); dev_queue_xmit(skb); }
static int hdlc_fr_pvc(hdlc_device *hdlc, int dlci) { pvc_device **pvc_p = &hdlc->first_pvc; pvc_device *pvc; int result, create = 1; /* Create or delete PVC */ if(!capable(CAP_NET_ADMIN)) return -EPERM; if(dlci<0) { dlci = -dlci; create = 0; } if(dlci <= 0 || dlci >= 1024) return -EINVAL; /* Only 10 bits for DLCI, DLCI=0 is reserved */ if(!mode_is(hdlc, MODE_FR)) return -EINVAL; /* Only meaningfull on FR */ while(*pvc_p) { if (netdev_dlci(&(*pvc_p)->netdev) == dlci) break; pvc_p = &(*pvc_p)->next; } if (create) { /* Create PVC */ if (*pvc_p != NULL) return -EEXIST; pvc = *pvc_p = kmalloc(sizeof(pvc_device), GFP_KERNEL); if (!pvc) { printk(KERN_WARNING "%s: Memory squeeze on " "hdlc_fr_pvc()\n", hdlc_to_name(hdlc)); return -ENOBUFS; } memset(pvc, 0, sizeof(pvc_device)); pvc->netdev.hard_start_xmit = pvc_xmit; pvc->netdev.get_stats = pvc_get_stats; pvc->netdev.open = pvc_open; pvc->netdev.stop = pvc_close; pvc->netdev.change_mtu = pvc_change_mtu; pvc->netdev.mtu = HDLC_MAX_MTU; pvc->netdev.type = ARPHRD_DLCI; pvc->netdev.hard_header_len = 16; pvc->netdev.hard_header = fr_hard_header; pvc->netdev.tx_queue_len = 0; pvc->netdev.flags = IFF_POINTOPOINT; pvc->master = hdlc; *(u16*)pvc->netdev.dev_addr = htons(dlci); dlci_to_q922(pvc->netdev.broadcast, dlci); pvc->netdev.addr_len = 2; pvc->netdev.irq = hdlc_to_dev(hdlc)->irq; result = dev_alloc_name(&pvc->netdev, "pvc%d"); if (result < 0) { kfree(pvc); *pvc_p = NULL; return result; } if (register_netdevice(&pvc->netdev) != 0) { kfree(pvc); *pvc_p = NULL; return -EIO; } if (!mode_is(hdlc, MODE_SOFT) && hdlc->create_pvc) { result = hdlc->create_pvc(pvc); if (result) { unregister_netdevice(&pvc->netdev); kfree(pvc); *pvc_p = NULL; return result; } } hdlc->lmi.state |= LINK_STATE_CHANGED; hdlc->pvc_count++; return 0; } if (*pvc_p == NULL) /* Delete PVC */ return -ENOENT; pvc = *pvc_p; if (pvc->netdev.flags & IFF_UP) return -EBUSY; /* PVC in use */ if (!mode_is(hdlc, MODE_SOFT) && hdlc->destroy_pvc) hdlc->destroy_pvc(pvc); hdlc->lmi.state |= LINK_STATE_CHANGED; hdlc->pvc_count--; *pvc_p = pvc->next; unregister_netdevice(&pvc->netdev); kfree(pvc); return 0; }