📄 syncppp.c
字号:
/* * NET3: A (fairly minimal) implementation of synchronous PPP for Linux * as well as a CISCO HDLC implementation. See the copyright * message below for the original source. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version * 2 of the license, or (at your option) any later version. * * Note however. This code is also used in a different form by FreeBSD. * Therefore when making any non OS specific change please consider * contributing it back to the original author under the terms * below in addition. * -- Alan * * Port for Linux-2.1 by Jan "Yenya" Kasprzak <kas@fi.muni.cz> *//* * Synchronous PPP/Cisco link level subroutines. * Keepalive protocol implemented in both Cisco and PPP modes. * * Copyright (C) 1994 Cronyx Ltd. * Author: Serge Vakulenko, <vak@zebub.msk.su> * * This software is distributed with NO WARRANTIES, not even the implied * warranties for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * Authors grant any other persons or organisations permission to use * or modify this software as long as this message is kept with the software, * all derivative works or modified versions. * * Version 1.9, Wed Oct 4 18:58:15 MSK 1995 * * $Id: syncppp.c,v 1.18 2000/04/11 05:25:31 asj Exp $ */#undef DEBUG#include <linux/config.h>#include <linux/module.h>#include <linux/kernel.h>#include <linux/errno.h>#include <linux/init.h>#include <linux/if_arp.h>#include <linux/skbuff.h>#include <linux/route.h>#include <linux/netdevice.h>#include <linux/inetdevice.h>#include <linux/random.h>#include <linux/pkt_sched.h>#include <linux/spinlock.h>#include <linux/rcupdate.h>#include <net/syncppp.h>#include <asm/byteorder.h>#include <asm/uaccess.h>#define MAXALIVECNT 6 /* max. alive packets */#define PPP_ALLSTATIONS 0xff /* All-Stations broadcast address */#define PPP_UI 0x03 /* Unnumbered Information */#define PPP_IP 0x0021 /* Internet Protocol */#define PPP_ISO 0x0023 /* ISO OSI Protocol */#define PPP_XNS 0x0025 /* Xerox NS Protocol */#define PPP_IPX 0x002b /* Novell IPX Protocol */#define PPP_LCP 0xc021 /* Link Control Protocol */#define PPP_IPCP 0x8021 /* Internet Protocol Control Protocol */#define LCP_CONF_REQ 1 /* PPP LCP configure request */#define LCP_CONF_ACK 2 /* PPP LCP configure acknowledge */#define LCP_CONF_NAK 3 /* PPP LCP configure negative ack */#define LCP_CONF_REJ 4 /* PPP LCP configure reject */#define LCP_TERM_REQ 5 /* PPP LCP terminate request */#define LCP_TERM_ACK 6 /* PPP LCP terminate acknowledge */#define LCP_CODE_REJ 7 /* PPP LCP code reject */#define LCP_PROTO_REJ 8 /* PPP LCP protocol reject */#define LCP_ECHO_REQ 9 /* PPP LCP echo request */#define LCP_ECHO_REPLY 10 /* PPP LCP echo reply */#define LCP_DISC_REQ 11 /* PPP LCP discard request */#define LCP_OPT_MRU 1 /* maximum receive unit */#define LCP_OPT_ASYNC_MAP 2 /* async control character map */#define LCP_OPT_AUTH_PROTO 3 /* authentication protocol */#define LCP_OPT_QUAL_PROTO 4 /* quality protocol */#define LCP_OPT_MAGIC 5 /* magic number */#define LCP_OPT_RESERVED 6 /* reserved */#define LCP_OPT_PROTO_COMP 7 /* protocol field compression */#define LCP_OPT_ADDR_COMP 8 /* address/control field compression */#define IPCP_CONF_REQ LCP_CONF_REQ /* PPP IPCP configure request */#define IPCP_CONF_ACK LCP_CONF_ACK /* PPP IPCP configure acknowledge */#define IPCP_CONF_NAK LCP_CONF_NAK /* PPP IPCP configure negative ack */#define IPCP_CONF_REJ LCP_CONF_REJ /* PPP IPCP configure reject */#define IPCP_TERM_REQ LCP_TERM_REQ /* PPP IPCP terminate request */#define IPCP_TERM_ACK LCP_TERM_ACK /* PPP IPCP terminate acknowledge */#define IPCP_CODE_REJ LCP_CODE_REJ /* PPP IPCP code reject */#define CISCO_MULTICAST 0x8f /* Cisco multicast address */#define CISCO_UNICAST 0x0f /* Cisco unicast address */#define CISCO_KEEPALIVE 0x8035 /* Cisco keepalive protocol */#define CISCO_ADDR_REQ 0 /* Cisco address request */#define CISCO_ADDR_REPLY 1 /* Cisco address reply */#define CISCO_KEEPALIVE_REQ 2 /* Cisco keepalive request */struct ppp_header { u8 address; u8 control; u16 protocol;};#define PPP_HEADER_LEN sizeof (struct ppp_header)struct lcp_header { u8 type; u8 ident; u16 len;};#define LCP_HEADER_LEN sizeof (struct lcp_header)struct cisco_packet { u32 type; u32 par1; u32 par2; u16 rel; u16 time0; u16 time1;};#define CISCO_PACKET_LEN 18#define CISCO_BIG_PACKET_LEN 20static struct sppp *spppq;static struct timer_list sppp_keepalive_timer;static DEFINE_SPINLOCK(spppq_lock);/* global xmit queue for sending packets while spinlock is held */static struct sk_buff_head tx_queue;static void sppp_keepalive (unsigned long dummy);static void sppp_cp_send (struct sppp *sp, u16 proto, u8 type, u8 ident, u16 len, void *data);static void sppp_cisco_send (struct sppp *sp, int type, long par1, long par2);static void sppp_lcp_input (struct sppp *sp, struct sk_buff *m);static void sppp_cisco_input (struct sppp *sp, struct sk_buff *m);static void sppp_ipcp_input (struct sppp *sp, struct sk_buff *m);static void sppp_lcp_open (struct sppp *sp);static void sppp_ipcp_open (struct sppp *sp);static int sppp_lcp_conf_parse_options (struct sppp *sp, struct lcp_header *h, int len, u32 *magic);static void sppp_cp_timeout (unsigned long arg);static char *sppp_lcp_type_name (u8 type);static char *sppp_ipcp_type_name (u8 type);static void sppp_print_bytes (u8 *p, u16 len);static int debug;/* Flush global outgoing packet queue to dev_queue_xmit(). * * dev_queue_xmit() must be called with interrupts enabled * which means it can't be called with spinlocks held. * If a packet needs to be sent while a spinlock is held, * then put the packet into tx_queue, and call sppp_flush_xmit() * after spinlock is released. */static void sppp_flush_xmit(void){ struct sk_buff *skb; while ((skb = skb_dequeue(&tx_queue)) != NULL) dev_queue_xmit(skb);}/* * Interface down stub */ static void if_down(struct net_device *dev){ struct sppp *sp = (struct sppp *)sppp_of(dev); sp->pp_link_state=SPPP_LINK_DOWN;}/* * Timeout routine activations. */static void sppp_set_timeout(struct sppp *p,int s) { if (! (p->pp_flags & PP_TIMO)) { init_timer(&p->pp_timer); p->pp_timer.function=sppp_cp_timeout; p->pp_timer.expires=jiffies+s*HZ; p->pp_timer.data=(unsigned long)p; p->pp_flags |= PP_TIMO; add_timer(&p->pp_timer); }}static void sppp_clear_timeout(struct sppp *p){ if (p->pp_flags & PP_TIMO) { del_timer(&p->pp_timer); p->pp_flags &= ~PP_TIMO; }}/** * sppp_input - receive and process a WAN PPP frame * @skb: The buffer to process * @dev: The device it arrived on * * This can be called directly by cards that do not have * timing constraints but is normally called from the network layer * after interrupt servicing to process frames queued via netif_rx(). * * We process the options in the card. If the frame is destined for * the protocol stacks then it requeues the frame for the upper level * protocol. If it is a control from it is processed and discarded * here. */ static void sppp_input (struct net_device *dev, struct sk_buff *skb){ struct ppp_header *h; struct sppp *sp = (struct sppp *)sppp_of(dev); unsigned long flags; skb->dev=dev; skb->mac.raw=skb->data; if (dev->flags & IFF_RUNNING) { /* Count received bytes, add FCS and one flag */ sp->ibytes+= skb->len + 3; sp->ipkts++; } if (!pskb_may_pull(skb, PPP_HEADER_LEN)) { /* Too small packet, drop it. */ if (sp->pp_flags & PP_DEBUG) printk (KERN_DEBUG "%s: input packet is too small, %d bytes\n", dev->name, skb->len); kfree_skb(skb); return; } /* Get PPP header. */ h = (struct ppp_header *)skb->data; skb_pull(skb,sizeof(struct ppp_header)); spin_lock_irqsave(&sp->lock, flags); switch (h->address) { default: /* Invalid PPP packet. */ goto invalid; case PPP_ALLSTATIONS: if (h->control != PPP_UI) goto invalid; if (sp->pp_flags & PP_CISCO) { if (sp->pp_flags & PP_DEBUG) printk (KERN_WARNING "%s: PPP packet in Cisco mode <0x%x 0x%x 0x%x>\n", dev->name, h->address, h->control, ntohs (h->protocol)); goto drop; } switch (ntohs (h->protocol)) { default: if (sp->lcp.state == LCP_STATE_OPENED) sppp_cp_send (sp, PPP_LCP, LCP_PROTO_REJ, ++sp->pp_seq, skb->len + 2, &h->protocol); if (sp->pp_flags & PP_DEBUG) printk (KERN_WARNING "%s: invalid input protocol <0x%x 0x%x 0x%x>\n", dev->name, h->address, h->control, ntohs (h->protocol)); goto drop; case PPP_LCP: sppp_lcp_input (sp, skb); goto drop; case PPP_IPCP: if (sp->lcp.state == LCP_STATE_OPENED) sppp_ipcp_input (sp, skb); else printk(KERN_DEBUG "IPCP when still waiting LCP finish.\n"); goto drop; case PPP_IP: if (sp->ipcp.state == IPCP_STATE_OPENED) { if(sp->pp_flags&PP_DEBUG) printk(KERN_DEBUG "Yow an IP frame.\n"); skb->protocol=htons(ETH_P_IP); netif_rx(skb); dev->last_rx = jiffies; goto done; } break;#ifdef IPX case PPP_IPX: /* IPX IPXCP not implemented yet */ if (sp->lcp.state == LCP_STATE_OPENED) { skb->protocol=htons(ETH_P_IPX); netif_rx(skb); dev->last_rx = jiffies; goto done; } break;#endif } break; case CISCO_MULTICAST: case CISCO_UNICAST: /* Don't check the control field here (RFC 1547). */ if (! (sp->pp_flags & PP_CISCO)) { if (sp->pp_flags & PP_DEBUG) printk (KERN_WARNING "%s: Cisco packet in PPP mode <0x%x 0x%x 0x%x>\n", dev->name, h->address, h->control, ntohs (h->protocol)); goto drop; } switch (ntohs (h->protocol)) { default: goto invalid; case CISCO_KEEPALIVE: sppp_cisco_input (sp, skb); goto drop;#ifdef CONFIG_INET case ETH_P_IP: skb->protocol=htons(ETH_P_IP); netif_rx(skb); dev->last_rx = jiffies; goto done;#endif#ifdef CONFIG_IPX case ETH_P_IPX: skb->protocol=htons(ETH_P_IPX); netif_rx(skb); dev->last_rx = jiffies; goto done;#endif } break; } goto drop;invalid: if (sp->pp_flags & PP_DEBUG) printk (KERN_WARNING "%s: invalid input packet <0x%x 0x%x 0x%x>\n", dev->name, h->address, h->control, ntohs (h->protocol));drop: kfree_skb(skb);done: spin_unlock_irqrestore(&sp->lock, flags); sppp_flush_xmit(); return;}/* * Handle transmit packets. */ static int sppp_hard_header(struct sk_buff *skb, struct net_device *dev, __u16 type, void *daddr, void *saddr, unsigned int len){ struct sppp *sp = (struct sppp *)sppp_of(dev); struct ppp_header *h; skb_push(skb,sizeof(struct ppp_header)); h=(struct ppp_header *)skb->data; if(sp->pp_flags&PP_CISCO) { h->address = CISCO_UNICAST; h->control = 0; } else { h->address = PPP_ALLSTATIONS; h->control = PPP_UI; } if(sp->pp_flags & PP_CISCO) { h->protocol = htons(type); } else switch(type) { case ETH_P_IP: h->protocol = htons(PPP_IP); break; case ETH_P_IPX: h->protocol = htons(PPP_IPX); break; } return sizeof(struct ppp_header);}static int sppp_rebuild_header(struct sk_buff *skb){ return 0;}/* * Send keepalive packets, every 10 seconds. */static void sppp_keepalive (unsigned long dummy){ struct sppp *sp; unsigned long flags; spin_lock_irqsave(&spppq_lock, flags); for (sp=spppq; sp; sp=sp->pp_next) { struct net_device *dev = sp->pp_if; /* Keepalive mode disabled or channel down? */ if (! (sp->pp_flags & PP_KEEPALIVE) || ! (dev->flags & IFF_UP)) continue; spin_lock(&sp->lock); /* No keepalive in PPP mode if LCP not opened yet. */ if (! (sp->pp_flags & PP_CISCO) && sp->lcp.state != LCP_STATE_OPENED) { spin_unlock(&sp->lock); continue; } if (sp->pp_alivecnt == MAXALIVECNT) { /* No keepalive packets got. Stop the interface. */ printk (KERN_WARNING "%s: protocol down\n", dev->name); if_down (dev); if (! (sp->pp_flags & PP_CISCO)) { /* Shut down the PPP link. */ sp->lcp.magic = jiffies; sp->lcp.state = LCP_STATE_CLOSED; sp->ipcp.state = IPCP_STATE_CLOSED; sppp_clear_timeout (sp); /* Initiate negotiation. */ sppp_lcp_open (sp); } } if (sp->pp_alivecnt <= MAXALIVECNT) ++sp->pp_alivecnt; if (sp->pp_flags & PP_CISCO) sppp_cisco_send (sp, CISCO_KEEPALIVE_REQ, ++sp->pp_seq, sp->pp_rseq); else if (sp->lcp.state == LCP_STATE_OPENED) { long nmagic = htonl (sp->lcp.magic); sp->lcp.echoid = ++sp->pp_seq; sppp_cp_send (sp, PPP_LCP, LCP_ECHO_REQ, sp->lcp.echoid, 4, &nmagic); } spin_unlock(&sp->lock); } spin_unlock_irqrestore(&spppq_lock, flags); sppp_flush_xmit(); sppp_keepalive_timer.expires=jiffies+10*HZ; add_timer(&sppp_keepalive_timer);}/* * Handle incoming PPP Link Control Protocol packets. */ static void sppp_lcp_input (struct sppp *sp, struct sk_buff *skb){ struct lcp_header *h; struct net_device *dev = sp->pp_if; int len = skb->len; u8 *p, opt[6]; u32 rmagic; if (!pskb_may_pull(skb, sizeof(struct lcp_header))) { if (sp->pp_flags & PP_DEBUG) printk (KERN_WARNING "%s: invalid lcp packet length: %d bytes\n", dev->name, len); return; } h = (struct lcp_header *)skb->data; skb_pull(skb,sizeof(struct lcp_header *)); if (sp->pp_flags & PP_DEBUG) { char state = '?'; switch (sp->lcp.state) { case LCP_STATE_CLOSED: state = 'C'; break; case LCP_STATE_ACK_RCVD: state = 'R'; break; case LCP_STATE_ACK_SENT: state = 'S'; break; case LCP_STATE_OPENED: state = 'O'; break; } printk (KERN_WARNING "%s: lcp input(%c): %d bytes <%s id=%xh len=%xh", dev->name, state, len, sppp_lcp_type_name (h->type), h->ident, ntohs (h->len)); if (len > 4)
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -