Blame view

drivers/net/wan/hdlc_ppp.c 18.7 KB
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1
2
3
4
  /*
   * Generic HDLC support routines for Linux
   * Point-to-point protocol support
   *
e022c2f07   Krzysztof Hałasa   WAN: new synchron...
5
   * Copyright (C) 1999 - 2008 Krzysztof Halasa <khc@pm.waw.pl>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
6
7
8
9
10
   *
   * This program is free software; you can redistribute it and/or modify it
   * under the terms of version 2 of the GNU General Public License
   * as published by the Free Software Foundation.
   */
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
11
  #include <linux/errno.h>
4dfce4075   Krzysztof Hałasa   WAN: cosmetic cha...
12
  #include <linux/hdlc.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
13
  #include <linux/if_arp.h>
4dfce4075   Krzysztof Hałasa   WAN: cosmetic cha...
14
  #include <linux/inetdevice.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
15
  #include <linux/init.h>
4dfce4075   Krzysztof Hałasa   WAN: cosmetic cha...
16
17
  #include <linux/kernel.h>
  #include <linux/module.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
18
  #include <linux/pkt_sched.h>
4dfce4075   Krzysztof Hałasa   WAN: cosmetic cha...
19
  #include <linux/poll.h>
4dfce4075   Krzysztof Hałasa   WAN: cosmetic cha...
20
21
  #include <linux/skbuff.h>
  #include <linux/slab.h>
e022c2f07   Krzysztof Hałasa   WAN: new synchron...
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
  #include <linux/spinlock.h>
  
  #define DEBUG_CP		0 /* also bytes# to dump */
  #define DEBUG_STATE		0
  #define DEBUG_HARD_HEADER	0
  
  #define HDLC_ADDR_ALLSTATIONS	0xFF
  #define HDLC_CTRL_UI		0x03
  
  #define PID_LCP			0xC021
  #define PID_IP			0x0021
  #define PID_IPCP		0x8021
  #define PID_IPV6		0x0057
  #define PID_IPV6CP		0x8057
  
  enum {IDX_LCP = 0, IDX_IPCP, IDX_IPV6CP, IDX_COUNT};
  enum {CP_CONF_REQ = 1, CP_CONF_ACK, CP_CONF_NAK, CP_CONF_REJ, CP_TERM_REQ,
        CP_TERM_ACK, CP_CODE_REJ, LCP_PROTO_REJ, LCP_ECHO_REQ, LCP_ECHO_REPLY,
        LCP_DISC_REQ, CP_CODES};
  #if DEBUG_CP
  static const char *const code_names[CP_CODES] = {
  	"0", "ConfReq", "ConfAck", "ConfNak", "ConfRej", "TermReq",
  	"TermAck", "CodeRej", "ProtoRej", "EchoReq", "EchoReply", "Discard"
  };
  static char debug_buffer[64 + 3 * DEBUG_CP];
  #endif
  
  enum {LCP_OPTION_MRU = 1, LCP_OPTION_ACCM, LCP_OPTION_MAGIC = 5};
  
  struct hdlc_header {
  	u8 address;
  	u8 control;
  	__be16 protocol;
  };
  
  struct cp_header {
  	u8 code;
  	u8 id;
  	__be16 len;
  };
eb2a2fd91   Krzysztof Halasa   [PATCH] Modulariz...
62

e022c2f07   Krzysztof Hałasa   WAN: new synchron...
63
64
65
66
67
68
69
70
  struct proto {
  	struct net_device *dev;
  	struct timer_list timer;
  	unsigned long timeout;
  	u16 pid;		/* protocol ID */
  	u8 state;
  	u8 cr_id;		/* ID of last Configuration-Request */
  	u8 restart_counter;
eb2a2fd91   Krzysztof Halasa   [PATCH] Modulariz...
71
  };
e022c2f07   Krzysztof Hałasa   WAN: new synchron...
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
  struct ppp {
  	struct proto protos[IDX_COUNT];
  	spinlock_t lock;
  	unsigned long last_pong;
  	unsigned int req_timeout, cr_retries, term_retries;
  	unsigned int keepalive_interval, keepalive_timeout;
  	u8 seq;			/* local sequence number for requests */
  	u8 echo_id;		/* ID of last Echo-Request (LCP) */
  };
  
  enum {CLOSED = 0, STOPPED, STOPPING, REQ_SENT, ACK_RECV, ACK_SENT, OPENED,
        STATES, STATE_MASK = 0xF};
  enum {START = 0, STOP, TO_GOOD, TO_BAD, RCR_GOOD, RCR_BAD, RCA, RCN, RTR, RTA,
        RUC, RXJ_GOOD, RXJ_BAD, EVENTS};
  enum {INV = 0x10, IRC = 0x20, ZRC = 0x40, SCR = 0x80, SCA = 0x100,
        SCN = 0x200, STR = 0x400, STA = 0x800, SCJ = 0x1000};
  
  #if DEBUG_STATE
  static const char *const state_names[STATES] = {
  	"Closed", "Stopped", "Stopping", "ReqSent", "AckRecv", "AckSent",
  	"Opened"
  };
  static const char *const event_names[EVENTS] = {
  	"Start", "Stop", "TO+", "TO-", "RCR+", "RCR-", "RCA", "RCN",
  	"RTR", "RTA", "RUC", "RXJ+", "RXJ-"
  };
  #endif
  
  static struct sk_buff_head tx_queue; /* used when holding the spin lock */
eb2a2fd91   Krzysztof Halasa   [PATCH] Modulariz...
101
  static int ppp_ioctl(struct net_device *dev, struct ifreq *ifr);
e022c2f07   Krzysztof Hałasa   WAN: new synchron...
102
103
104
105
  static inline struct ppp* get_ppp(struct net_device *dev)
  {
  	return (struct ppp *)dev_to_hdlc(dev)->state;
  }
eb2a2fd91   Krzysztof Halasa   [PATCH] Modulariz...
106

e022c2f07   Krzysztof Hałasa   WAN: new synchron...
107
  static inline struct proto* get_proto(struct net_device *dev, u16 pid)
eb2a2fd91   Krzysztof Halasa   [PATCH] Modulariz...
108
  {
e022c2f07   Krzysztof Hałasa   WAN: new synchron...
109
110
111
112
113
114
115
116
117
118
119
120
  	struct ppp *ppp = get_ppp(dev);
  
  	switch (pid) {
  	case PID_LCP:
  		return &ppp->protos[IDX_LCP];
  	case PID_IPCP:
  		return &ppp->protos[IDX_IPCP];
  	case PID_IPV6CP:
  		return &ppp->protos[IDX_IPV6CP];
  	default:
  		return NULL;
  	}
eb2a2fd91   Krzysztof Halasa   [PATCH] Modulariz...
121
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
122

e022c2f07   Krzysztof Hałasa   WAN: new synchron...
123
124
125
126
127
128
129
130
131
132
133
134
135
  static inline const char* proto_name(u16 pid)
  {
  	switch (pid) {
  	case PID_LCP:
  		return "LCP";
  	case PID_IPCP:
  		return "IPCP";
  	case PID_IPV6CP:
  		return "IPV6CP";
  	default:
  		return NULL;
  	}
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
136

e022c2f07   Krzysztof Hałasa   WAN: new synchron...
137
  static __be16 ppp_type_trans(struct sk_buff *skb, struct net_device *dev)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
138
  {
e022c2f07   Krzysztof Hałasa   WAN: new synchron...
139
140
141
142
143
144
145
146
147
  	struct hdlc_header *data = (struct hdlc_header*)skb->data;
  
  	if (skb->len < sizeof(struct hdlc_header))
  		return htons(ETH_P_HDLC);
  	if (data->address != HDLC_ADDR_ALLSTATIONS ||
  	    data->control != HDLC_CTRL_UI)
  		return htons(ETH_P_HDLC);
  
  	switch (data->protocol) {
09640e636   Harvey Harrison   net: replace uses...
148
  	case cpu_to_be16(PID_IP):
e022c2f07   Krzysztof Hałasa   WAN: new synchron...
149
150
  		skb_pull(skb, sizeof(struct hdlc_header));
  		return htons(ETH_P_IP);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
151

09640e636   Harvey Harrison   net: replace uses...
152
  	case cpu_to_be16(PID_IPV6):
e022c2f07   Krzysztof Hałasa   WAN: new synchron...
153
154
  		skb_pull(skb, sizeof(struct hdlc_header));
  		return htons(ETH_P_IPV6);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
155

e022c2f07   Krzysztof Hałasa   WAN: new synchron...
156
157
  	default:
  		return htons(ETH_P_HDLC);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
158
  	}
e022c2f07   Krzysztof Hałasa   WAN: new synchron...
159
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
160

e022c2f07   Krzysztof Hałasa   WAN: new synchron...
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
  
  static int ppp_hard_header(struct sk_buff *skb, struct net_device *dev,
  			   u16 type, const void *daddr, const void *saddr,
  			   unsigned int len)
  {
  	struct hdlc_header *data;
  #if DEBUG_HARD_HEADER
  	printk(KERN_DEBUG "%s: ppp_hard_header() called
  ", dev->name);
  #endif
  
  	skb_push(skb, sizeof(struct hdlc_header));
  	data = (struct hdlc_header*)skb->data;
  
  	data->address = HDLC_ADDR_ALLSTATIONS;
  	data->control = HDLC_CTRL_UI;
  	switch (type) {
  	case ETH_P_IP:
  		data->protocol = htons(PID_IP);
  		break;
  	case ETH_P_IPV6:
  		data->protocol = htons(PID_IPV6);
  		break;
  	case PID_LCP:
  	case PID_IPCP:
  	case PID_IPV6CP:
  		data->protocol = htons(type);
  		break;
  	default:		/* unknown protocol */
  		data->protocol = 0;
  	}
  	return sizeof(struct hdlc_header);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
193
  }
e022c2f07   Krzysztof Hałasa   WAN: new synchron...
194
195
196
197
198
199
  static void ppp_tx_flush(void)
  {
  	struct sk_buff *skb;
  	while ((skb = skb_dequeue(&tx_queue)) != NULL)
  		dev_queue_xmit(skb);
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
200

e022c2f07   Krzysztof Hałasa   WAN: new synchron...
201
202
  static void ppp_tx_cp(struct net_device *dev, u16 pid, u8 code,
  		      u8 id, unsigned int len, const void *data)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
203
  {
e022c2f07   Krzysztof Hałasa   WAN: new synchron...
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
  	struct sk_buff *skb;
  	struct cp_header *cp;
  	unsigned int magic_len = 0;
  	static u32 magic;
  
  #if DEBUG_CP
  	int i;
  	char *ptr;
  #endif
  
  	if (pid == PID_LCP && (code == LCP_ECHO_REQ || code == LCP_ECHO_REPLY))
  		magic_len = sizeof(magic);
  
  	skb = dev_alloc_skb(sizeof(struct hdlc_header) +
  			    sizeof(struct cp_header) + magic_len + len);
  	if (!skb) {
  		printk(KERN_WARNING "%s: out of memory in ppp_tx_cp()
  ",
  		       dev->name);
  		return;
  	}
  	skb_reserve(skb, sizeof(struct hdlc_header));
  
  	cp = (struct cp_header *)skb_put(skb, sizeof(struct cp_header));
  	cp->code = code;
  	cp->id = id;
  	cp->len = htons(sizeof(struct cp_header) + magic_len + len);
  
  	if (magic_len)
  		memcpy(skb_put(skb, magic_len), &magic, magic_len);
  	if (len)
  		memcpy(skb_put(skb, len), data, len);
  
  #if DEBUG_CP
  	BUG_ON(code >= CP_CODES);
  	ptr = debug_buffer;
  	*ptr = '\x0';
  	for (i = 0; i < min_t(unsigned int, magic_len + len, DEBUG_CP); i++) {
  		sprintf(ptr, " %02X", skb->data[sizeof(struct cp_header) + i]);
  		ptr += strlen(ptr);
  	}
  	printk(KERN_DEBUG "%s: TX %s [%s id 0x%X]%s
  ", dev->name,
  	       proto_name(pid), code_names[code], id, debug_buffer);
  #endif
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
249

e022c2f07   Krzysztof Hałasa   WAN: new synchron...
250
  	ppp_hard_header(skb, dev, pid, NULL, NULL, 0);
3b04ddde0   Stephen Hemminger   [NET]: Move hardw...
251

e022c2f07   Krzysztof Hałasa   WAN: new synchron...
252
253
254
255
  	skb->priority = TC_PRIO_CONTROL;
  	skb->dev = dev;
  	skb_reset_network_header(skb);
  	skb_queue_tail(&tx_queue, skb);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
256
  }
e022c2f07   Krzysztof Hałasa   WAN: new synchron...
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
  /* State transition table (compare STD-51)
     Events                                   Actions
     TO+  = Timeout with counter > 0          irc = Initialize-Restart-Count
     TO-  = Timeout with counter expired      zrc = Zero-Restart-Count
  
     RCR+ = Receive-Configure-Request (Good)  scr = Send-Configure-Request
     RCR- = Receive-Configure-Request (Bad)
     RCA  = Receive-Configure-Ack             sca = Send-Configure-Ack
     RCN  = Receive-Configure-Nak/Rej         scn = Send-Configure-Nak/Rej
  
     RTR  = Receive-Terminate-Request         str = Send-Terminate-Request
     RTA  = Receive-Terminate-Ack             sta = Send-Terminate-Ack
  
     RUC  = Receive-Unknown-Code              scj = Send-Code-Reject
     RXJ+ = Receive-Code-Reject (permitted)
         or Receive-Protocol-Reject
     RXJ- = Receive-Code-Reject (catastrophic)
         or Receive-Protocol-Reject
  */
  static int cp_table[EVENTS][STATES] = {
  	/* CLOSED     STOPPED STOPPING REQ_SENT ACK_RECV ACK_SENT OPENED
  	     0           1         2       3       4      5          6    */
  	{IRC|SCR|3,     INV     , INV ,   INV   , INV ,  INV    ,   INV   }, /* START */
  	{   INV   ,      0      ,  0  ,    0    ,  0  ,   0     ,    0    }, /* STOP */
  	{   INV   ,     INV     ,STR|2,  SCR|3  ,SCR|3,  SCR|5  ,   INV   }, /* TO+ */
  	{   INV   ,     INV     ,  1  ,    1    ,  1  ,    1    ,   INV   }, /* TO- */
  	{  STA|0  ,IRC|SCR|SCA|5,  2  ,  SCA|5  ,SCA|6,  SCA|5  ,SCR|SCA|5}, /* RCR+ */
  	{  STA|0  ,IRC|SCR|SCN|3,  2  ,  SCN|3  ,SCN|4,  SCN|3  ,SCR|SCN|3}, /* RCR- */
  	{  STA|0  ,    STA|1    ,  2  ,  IRC|4  ,SCR|3,    6    , SCR|3   }, /* RCA */
  	{  STA|0  ,    STA|1    ,  2  ,IRC|SCR|3,SCR|3,IRC|SCR|5, SCR|3   }, /* RCN */
  	{  STA|0  ,    STA|1    ,STA|2,  STA|3  ,STA|3,  STA|3  ,ZRC|STA|2}, /* RTR */
  	{    0    ,      1      ,  1  ,    3    ,  3  ,    5    ,  SCR|3  }, /* RTA */
  	{  SCJ|0  ,    SCJ|1    ,SCJ|2,  SCJ|3  ,SCJ|4,  SCJ|5  ,  SCJ|6  }, /* RUC */
  	{    0    ,      1      ,  2  ,    3    ,  3  ,    5    ,    6    }, /* RXJ+ */
  	{    0    ,      1      ,  1  ,    1    ,  1  ,    1    ,IRC|STR|2}, /* RXJ- */
  };
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
293

e022c2f07   Krzysztof Hałasa   WAN: new synchron...
294
295
296
297
298
  /* SCA: RCR+ must supply id, len and data
     SCN: RCR- must supply code, id, len and data
     STA: RTR must supply id
     SCJ: RUC must supply CP packet len and data */
  static void ppp_cp_event(struct net_device *dev, u16 pid, u16 event, u8 code,
93bc93352   Krzysztof Hałasa   HDLC_PPP: Fix Con...
299
  			 u8 id, unsigned int len, const void *data)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
300
  {
e022c2f07   Krzysztof Hałasa   WAN: new synchron...
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
  	int old_state, action;
  	struct ppp *ppp = get_ppp(dev);
  	struct proto *proto = get_proto(dev, pid);
  
  	old_state = proto->state;
  	BUG_ON(old_state >= STATES);
  	BUG_ON(event >= EVENTS);
  
  #if DEBUG_STATE
  	printk(KERN_DEBUG "%s: %s ppp_cp_event(%s) %s ...
  ", dev->name,
  	       proto_name(pid), event_names[event], state_names[proto->state]);
  #endif
  
  	action = cp_table[event][old_state];
  
  	proto->state = action & STATE_MASK;
  	if (action & (SCR | STR)) /* set Configure-Req/Terminate-Req timer */
  		mod_timer(&proto->timer, proto->timeout =
  			  jiffies + ppp->req_timeout * HZ);
  	if (action & ZRC)
  		proto->restart_counter = 0;
  	if (action & IRC)
  		proto->restart_counter = (proto->state == STOPPING) ?
  			ppp->term_retries : ppp->cr_retries;
  
  	if (action & SCR)	/* send Configure-Request */
  		ppp_tx_cp(dev, pid, CP_CONF_REQ, proto->cr_id = ++ppp->seq,
  			  0, NULL);
  	if (action & SCA)	/* send Configure-Ack */
  		ppp_tx_cp(dev, pid, CP_CONF_ACK, id, len, data);
  	if (action & SCN)	/* send Configure-Nak/Reject */
  		ppp_tx_cp(dev, pid, code, id, len, data);
  	if (action & STR)	/* send Terminate-Request */
  		ppp_tx_cp(dev, pid, CP_TERM_REQ, ++ppp->seq, 0, NULL);
  	if (action & STA)	/* send Terminate-Ack */
  		ppp_tx_cp(dev, pid, CP_TERM_ACK, id, 0, NULL);
  	if (action & SCJ)	/* send Code-Reject */
  		ppp_tx_cp(dev, pid, CP_CODE_REJ, ++ppp->seq, len, data);
  
  	if (old_state != OPENED && proto->state == OPENED) {
  		printk(KERN_INFO "%s: %s up
  ", dev->name, proto_name(pid));
  		if (pid == PID_LCP) {
  			netif_dormant_off(dev);
  			ppp_cp_event(dev, PID_IPCP, START, 0, 0, 0, NULL);
  			ppp_cp_event(dev, PID_IPV6CP, START, 0, 0, 0, NULL);
  			ppp->last_pong = jiffies;
  			mod_timer(&proto->timer, proto->timeout =
  				  jiffies + ppp->keepalive_interval * HZ);
  		}
  	}
  	if (old_state == OPENED && proto->state != OPENED) {
  		printk(KERN_INFO "%s: %s down
  ", dev->name, proto_name(pid));
  		if (pid == PID_LCP) {
  			netif_dormant_on(dev);
  			ppp_cp_event(dev, PID_IPCP, STOP, 0, 0, 0, NULL);
  			ppp_cp_event(dev, PID_IPV6CP, STOP, 0, 0, 0, NULL);
  		}
  	}
  	if (old_state != CLOSED && proto->state == CLOSED)
  		del_timer(&proto->timer);
  
  #if DEBUG_STATE
  	printk(KERN_DEBUG "%s: %s ppp_cp_event(%s) ... %s
  ", dev->name,
  	       proto_name(pid), event_names[event], state_names[proto->state]);
  #endif
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
370
  }
e022c2f07   Krzysztof Hałasa   WAN: new synchron...
371
  static void ppp_cp_parse_cr(struct net_device *dev, u16 pid, u8 id,
93bc93352   Krzysztof Hałasa   HDLC_PPP: Fix Con...
372
  			    unsigned int req_len, const u8 *data)
e022c2f07   Krzysztof Hałasa   WAN: new synchron...
373
374
  {
  	static u8 const valid_accm[6] = { LCP_OPTION_ACCM, 6, 0, 0, 0, 0 };
93bc93352   Krzysztof Hałasa   HDLC_PPP: Fix Con...
375
376
377
  	const u8 *opt;
  	u8 *out;
  	unsigned int len = req_len, nak_len = 0, rej_len = 0;
e022c2f07   Krzysztof Hałasa   WAN: new synchron...
378
379
380
381
382
383
384
385
386
  
  	if (!(out = kmalloc(len, GFP_ATOMIC))) {
  		dev->stats.rx_dropped++;
  		return;	/* out of memory, ignore CR packet */
  	}
  
  	for (opt = data; len; len -= opt[1], opt += opt[1]) {
  		if (len < 2 || len < opt[1]) {
  			dev->stats.rx_errors++;
966a5d1b8   Julia Lawall   drivers/net/wan: ...
387
  			kfree(out);
e022c2f07   Krzysztof Hałasa   WAN: new synchron...
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
  			return; /* bad packet, drop silently */
  		}
  
  		if (pid == PID_LCP)
  			switch (opt[0]) {
  			case LCP_OPTION_MRU:
  				continue; /* MRU always OK and > 1500 bytes? */
  
  			case LCP_OPTION_ACCM: /* async control character map */
  				if (!memcmp(opt, valid_accm,
  					    sizeof(valid_accm)))
  					continue;
  				if (!rej_len) { /* NAK it */
  					memcpy(out + nak_len, valid_accm,
  					       sizeof(valid_accm));
  					nak_len += sizeof(valid_accm);
  					continue;
  				}
  				break;
  			case LCP_OPTION_MAGIC:
  				if (opt[1] != 6 || (!opt[2] && !opt[3] &&
  						    !opt[4] && !opt[5]))
  					break; /* reject invalid magic number */
  				continue;
  			}
  		/* reject this option */
  		memcpy(out + rej_len, opt, opt[1]);
  		rej_len += opt[1];
  	}
  
  	if (rej_len)
  		ppp_cp_event(dev, pid, RCR_BAD, CP_CONF_REJ, id, rej_len, out);
  	else if (nak_len)
  		ppp_cp_event(dev, pid, RCR_BAD, CP_CONF_NAK, id, nak_len, out);
  	else
93bc93352   Krzysztof Hałasa   HDLC_PPP: Fix Con...
423
  		ppp_cp_event(dev, pid, RCR_GOOD, CP_CONF_ACK, id, req_len, data);
e022c2f07   Krzysztof Hałasa   WAN: new synchron...
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
  
  	kfree(out);
  }
  
  static int ppp_rx(struct sk_buff *skb)
  {
  	struct hdlc_header *hdr = (struct hdlc_header*)skb->data;
  	struct net_device *dev = skb->dev;
  	struct ppp *ppp = get_ppp(dev);
  	struct proto *proto;
  	struct cp_header *cp;
  	unsigned long flags;
  	unsigned int len;
  	u16 pid;
  #if DEBUG_CP
  	int i;
  	char *ptr;
  #endif
  
  	spin_lock_irqsave(&ppp->lock, flags);
  	/* Check HDLC header */
  	if (skb->len < sizeof(struct hdlc_header))
  		goto rx_error;
  	cp = (struct cp_header*)skb_pull(skb, sizeof(struct hdlc_header));
  	if (hdr->address != HDLC_ADDR_ALLSTATIONS ||
  	    hdr->control != HDLC_CTRL_UI)
  		goto rx_error;
  
  	pid = ntohs(hdr->protocol);
  	proto = get_proto(dev, pid);
  	if (!proto) {
  		if (ppp->protos[IDX_LCP].state == OPENED)
  			ppp_tx_cp(dev, PID_LCP, LCP_PROTO_REJ,
  				  ++ppp->seq, skb->len + 2, &hdr->protocol);
  		goto rx_error;
  	}
  
  	len = ntohs(cp->len);
  	if (len < sizeof(struct cp_header) /* no complete CP header? */ ||
  	    skb->len < len /* truncated packet? */)
  		goto rx_error;
  	skb_pull(skb, sizeof(struct cp_header));
  	len -= sizeof(struct cp_header);
  
  	/* HDLC and CP headers stripped from skb */
  #if DEBUG_CP
  	if (cp->code < CP_CODES)
  		sprintf(debug_buffer, "[%s id 0x%X]", code_names[cp->code],
  			cp->id);
  	else
  		sprintf(debug_buffer, "[code %u id 0x%X]", cp->code, cp->id);
  	ptr = debug_buffer + strlen(debug_buffer);
  	for (i = 0; i < min_t(unsigned int, len, DEBUG_CP); i++) {
  		sprintf(ptr, " %02X", skb->data[i]);
  		ptr += strlen(ptr);
  	}
  	printk(KERN_DEBUG "%s: RX %s %s
  ", dev->name, proto_name(pid),
  	       debug_buffer);
  #endif
  
  	/* LCP only */
  	if (pid == PID_LCP)
  		switch (cp->code) {
  		case LCP_PROTO_REJ:
  			pid = ntohs(*(__be16*)skb->data);
  			if (pid == PID_LCP || pid == PID_IPCP ||
  			    pid == PID_IPV6CP)
  				ppp_cp_event(dev, pid, RXJ_BAD, 0, 0,
  					     0, NULL);
  			goto out;
  
  		case LCP_ECHO_REQ: /* send Echo-Reply */
  			if (len >= 4 && proto->state == OPENED)
  				ppp_tx_cp(dev, PID_LCP, LCP_ECHO_REPLY,
  					  cp->id, len - 4, skb->data + 4);
  			goto out;
  
  		case LCP_ECHO_REPLY:
  			if (cp->id == ppp->echo_id)
  				ppp->last_pong = jiffies;
  			goto out;
  
  		case LCP_DISC_REQ: /* discard */
  			goto out;
  		}
  
  	/* LCP, IPCP and IPV6CP */
  	switch (cp->code) {
  	case CP_CONF_REQ:
  		ppp_cp_parse_cr(dev, pid, cp->id, len, skb->data);
  		goto out;
  
  	case CP_CONF_ACK:
  		if (cp->id == proto->cr_id)
  			ppp_cp_event(dev, pid, RCA, 0, 0, 0, NULL);
  		goto out;
  
  	case CP_CONF_REJ:
  	case CP_CONF_NAK:
  		if (cp->id == proto->cr_id)
  			ppp_cp_event(dev, pid, RCN, 0, 0, 0, NULL);
  		goto out;
  
  	case CP_TERM_REQ:
  		ppp_cp_event(dev, pid, RTR, 0, cp->id, 0, NULL);
  		goto out;
  
  	case CP_TERM_ACK:
  		ppp_cp_event(dev, pid, RTA, 0, 0, 0, NULL);
  		goto out;
  
  	case CP_CODE_REJ:
  		ppp_cp_event(dev, pid, RXJ_BAD, 0, 0, 0, NULL);
  		goto out;
  
  	default:
  		len += sizeof(struct cp_header);
  		if (len > dev->mtu)
  			len = dev->mtu;
  		ppp_cp_event(dev, pid, RUC, 0, 0, len, cp);
  		goto out;
  	}
  	goto out;
  
  rx_error:
  	dev->stats.rx_errors++;
  out:
  	spin_unlock_irqrestore(&ppp->lock, flags);
  	dev_kfree_skb_any(skb);
  	ppp_tx_flush();
  	return NET_RX_DROP;
  }
e022c2f07   Krzysztof Hałasa   WAN: new synchron...
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
  static void ppp_timer(unsigned long arg)
  {
  	struct proto *proto = (struct proto *)arg;
  	struct ppp *ppp = get_ppp(proto->dev);
  	unsigned long flags;
  
  	spin_lock_irqsave(&ppp->lock, flags);
  	switch (proto->state) {
  	case STOPPING:
  	case REQ_SENT:
  	case ACK_RECV:
  	case ACK_SENT:
  		if (proto->restart_counter) {
  			ppp_cp_event(proto->dev, proto->pid, TO_GOOD, 0, 0,
  				     0, NULL);
  			proto->restart_counter--;
  		} else
  			ppp_cp_event(proto->dev, proto->pid, TO_BAD, 0, 0,
  				     0, NULL);
  		break;
  
  	case OPENED:
  		if (proto->pid != PID_LCP)
  			break;
  		if (time_after(jiffies, ppp->last_pong +
  			       ppp->keepalive_timeout * HZ)) {
  			printk(KERN_INFO "%s: Link down
  ", proto->dev->name);
  			ppp_cp_event(proto->dev, PID_LCP, STOP, 0, 0, 0, NULL);
  			ppp_cp_event(proto->dev, PID_LCP, START, 0, 0, 0, NULL);
  		} else {	/* send keep-alive packet */
  			ppp->echo_id = ++ppp->seq;
  			ppp_tx_cp(proto->dev, PID_LCP, LCP_ECHO_REQ,
  				  ppp->echo_id, 0, NULL);
  			proto->timer.expires = jiffies +
  				ppp->keepalive_interval * HZ;
  			add_timer(&proto->timer);
  		}
  		break;
  	}
  	spin_unlock_irqrestore(&ppp->lock, flags);
  	ppp_tx_flush();
  }
  
  
  static void ppp_start(struct net_device *dev)
  {
  	struct ppp *ppp = get_ppp(dev);
  	int i;
  
  	for (i = 0; i < IDX_COUNT; i++) {
  		struct proto *proto = &ppp->protos[i];
  		proto->dev = dev;
  		init_timer(&proto->timer);
  		proto->timer.function = ppp_timer;
  		proto->timer.data = (unsigned long)proto;
  		proto->state = CLOSED;
  	}
  	ppp->protos[IDX_LCP].pid = PID_LCP;
  	ppp->protos[IDX_IPCP].pid = PID_IPCP;
  	ppp->protos[IDX_IPV6CP].pid = PID_IPV6CP;
  
  	ppp_cp_event(dev, PID_LCP, START, 0, 0, 0, NULL);
  }
  
  static void ppp_stop(struct net_device *dev)
  {
  	ppp_cp_event(dev, PID_LCP, STOP, 0, 0, 0, NULL);
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
626

eb2a2fd91   Krzysztof Halasa   [PATCH] Modulariz...
627
  static struct hdlc_proto proto = {
e022c2f07   Krzysztof Hałasa   WAN: new synchron...
628
629
  	.start		= ppp_start,
  	.stop		= ppp_stop,
eb2a2fd91   Krzysztof Halasa   [PATCH] Modulariz...
630
631
  	.type_trans	= ppp_type_trans,
  	.ioctl		= ppp_ioctl,
e022c2f07   Krzysztof Hałasa   WAN: new synchron...
632
  	.netif_rx	= ppp_rx,
eb2a2fd91   Krzysztof Halasa   [PATCH] Modulariz...
633
634
  	.module		= THIS_MODULE,
  };
e022c2f07   Krzysztof Hałasa   WAN: new synchron...
635
636
637
  static const struct header_ops ppp_header_ops = {
  	.create = ppp_hard_header,
  };
eb2a2fd91   Krzysztof Halasa   [PATCH] Modulariz...
638
639
  
  static int ppp_ioctl(struct net_device *dev, struct ifreq *ifr)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
640
641
  {
  	hdlc_device *hdlc = dev_to_hdlc(dev);
e022c2f07   Krzysztof Hałasa   WAN: new synchron...
642
  	struct ppp *ppp;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
643
644
645
646
  	int result;
  
  	switch (ifr->ifr_settings.type) {
  	case IF_GET_PROTO:
eb2a2fd91   Krzysztof Halasa   [PATCH] Modulariz...
647
648
  		if (dev_to_hdlc(dev)->proto != &proto)
  			return -EINVAL;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
649
650
651
652
  		ifr->ifr_settings.type = IF_PROTO_PPP;
  		return 0; /* return protocol only, no settable parameters */
  
  	case IF_PROTO_PPP:
e022c2f07   Krzysztof Hałasa   WAN: new synchron...
653
  		if (!capable(CAP_NET_ADMIN))
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
654
  			return -EPERM;
e022c2f07   Krzysztof Hałasa   WAN: new synchron...
655
  		if (dev->flags & IFF_UP)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
656
657
658
  			return -EBUSY;
  
  		/* no settable parameters */
e022c2f07   Krzysztof Hałasa   WAN: new synchron...
659
  		result = hdlc->attach(dev, ENCODING_NRZ,PARITY_CRC16_PR1_CCITT);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
660
661
  		if (result)
  			return result;
e022c2f07   Krzysztof Hałasa   WAN: new synchron...
662
  		result = attach_hdlc_protocol(dev, &proto, sizeof(struct ppp));
eb2a2fd91   Krzysztof Halasa   [PATCH] Modulariz...
663
664
  		if (result)
  			return result;
e022c2f07   Krzysztof Hałasa   WAN: new synchron...
665
666
667
668
669
670
671
672
  
  		ppp = get_ppp(dev);
  		spin_lock_init(&ppp->lock);
  		ppp->req_timeout = 2;
  		ppp->cr_retries = 10;
  		ppp->term_retries = 2;
  		ppp->keepalive_interval = 10;
  		ppp->keepalive_timeout = 60;
e022c2f07   Krzysztof Hałasa   WAN: new synchron...
673
674
  		dev->hard_header_len = sizeof(struct hdlc_header);
  		dev->header_ops = &ppp_header_ops;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
675
  		dev->type = ARPHRD_PPP;
e022c2f07   Krzysztof Hałasa   WAN: new synchron...
676
  		netif_dormant_on(dev);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
677
678
679
680
681
  		return 0;
  	}
  
  	return -EINVAL;
  }
eb2a2fd91   Krzysztof Halasa   [PATCH] Modulariz...
682
683
684
685
  
  
  static int __init mod_init(void)
  {
e022c2f07   Krzysztof Hałasa   WAN: new synchron...
686
  	skb_queue_head_init(&tx_queue);
eb2a2fd91   Krzysztof Halasa   [PATCH] Modulariz...
687
688
689
  	register_hdlc_protocol(&proto);
  	return 0;
  }
eb2a2fd91   Krzysztof Halasa   [PATCH] Modulariz...
690
691
692
693
694
695
696
697
698
699
700
701
  static void __exit mod_exit(void)
  {
  	unregister_hdlc_protocol(&proto);
  }
  
  
  module_init(mod_init);
  module_exit(mod_exit);
  
  MODULE_AUTHOR("Krzysztof Halasa <khc@pm.waw.pl>");
  MODULE_DESCRIPTION("PPP protocol support for generic HDLC");
  MODULE_LICENSE("GPL v2");