]>
Commit | Line | Data |
---|---|---|
1a032709 JR |
1 | diff -urN linux.orig/Documentation/Configure.help linux/Documentation/Configure.help |
2 | --- linux.orig/Documentation/Configure.help Sat Apr 21 18:05:00 2001 | |
3 | +++ linux/Documentation/Configure.help Sat Apr 21 18:06:09 2001 | |
4 | @@ -5328,6 +5328,19 @@ | |
5 | briefly removed during revalidation. If you say Y here, packets to | |
6 | such neighbours are silently discarded instead. | |
7 | ||
8 | +RFC1483/2684 Bridged protocols | |
9 | +CONFIG_ATM_BR2684 | |
10 | + ATM PVCs can carry ethernet PDUs according to rfc2684 (formerly 1483) | |
11 | + This device will act like an ethernet from the kernels point of view, | |
12 | + with the traffic being carried by ATM PVCs (currently 1 PVC/device). | |
13 | + This is sometimes used over DSL lines. If in doubt, say N. | |
14 | + | |
15 | +Per-VC IP filter kludge | |
16 | +CONFIG_ATM_BR2684_IPFILTER | |
17 | + This is an experimental mechanism for users who need to terminating a | |
18 | + large number of IP-only vcc's. Do not enable this unless you are sure | |
19 | + you know what you are doing. | |
20 | + | |
21 | LAN Emulation (LANE) support | |
22 | CONFIG_ATM_LANE | |
23 | LAN Emulation emulates services of existing LANs across an ATM | |
24 | @@ -7558,6 +7571,13 @@ | |
25 | This driver requires a specially patched pppd daemon. The patch to | |
26 | pppd, along with binaries of a patched pppd package can be found at: | |
27 | http://www.shoshin.uwaterloo.ca/~mostrows | |
28 | + | |
29 | +PPP over ATM (EXPERIMENTAL) | |
30 | +CONFIG_PPPOATM | |
31 | + PPP over ATM is one of several protocols used over DSL lines. | |
32 | + If you are connecting to a DSL line using an internal ATM or | |
33 | + DSL adaptor (as opposed to an ethernet card) then there is | |
34 | + a good possibility you will need this protocol. | |
35 | ||
36 | Wireless LAN (non-hamradio) | |
37 | CONFIG_NET_RADIO | |
38 | diff -urN linux.orig/MAINTAINERS linux/MAINTAINERS | |
39 | --- linux.orig/MAINTAINERS Sat Apr 21 18:05:00 2001 | |
40 | +++ linux/MAINTAINERS Sat Apr 21 18:07:12 2001 | |
41 | @@ -1060,6 +1060,11 @@ | |
42 | L: linux-ppp@vger.kernel.org | |
43 | S: Maintained | |
44 | ||
45 | +PPP OVER ATM (RFC 2364) | |
46 | +P: Mitchell Blank Jr | |
47 | +M: mitch@sfgoth.com | |
48 | +S: Maintained | |
49 | + | |
50 | PPP OVER ETHERNET | |
51 | P: Michal Ostrowski | |
52 | M: mostrows@styx.uwaterloo.ca | |
53 | diff -urN linux.orig/arch/sparc/config.in linux/arch/sparc/config.in | |
54 | --- linux.orig/arch/sparc/config.in Sat Apr 21 18:04:59 2001 | |
55 | +++ linux/arch/sparc/config.in Sat Apr 21 18:06:09 2001 | |
56 | @@ -215,6 +215,12 @@ | |
57 | dep_tristate ' PPP support for sync tty ports' CONFIG_PPP_SYNC_TTY $CONFIG_PPP | |
58 | dep_tristate ' PPP Deflate compression' CONFIG_PPP_DEFLATE $CONFIG_PPP | |
59 | dep_tristate ' PPP BSD-Compress compression' CONFIG_PPP_BSDCOMP m | |
60 | + if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then | |
61 | + dep_tristate ' PPP over Ethernet (EXPERIMENTAL)' CONFIG_PPPOE $CONFIG_PPP | |
62 | + if [ "$CONFIG_ATM" = "y" ]; then | |
63 | + dep_tristate ' PPP over ATM (EXPERIMENTAL)' CONFIG_PPPOATM $CONFIG_PPP | |
64 | + fi | |
65 | + fi | |
66 | fi | |
67 | tristate ' SLIP (serial line) support' CONFIG_SLIP | |
68 | if [ "$CONFIG_SLIP" != "n" ]; then | |
69 | diff -urN linux.orig/drivers/net/Config.in linux/drivers/net/Config.in | |
70 | --- linux.orig/drivers/net/Config.in Sat Apr 21 18:04:55 2001 | |
71 | +++ linux/drivers/net/Config.in Sat Apr 21 18:06:09 2001 | |
72 | @@ -245,6 +245,9 @@ | |
73 | dep_tristate ' PPP BSD-Compress compression' CONFIG_PPP_BSDCOMP $CONFIG_PPP | |
74 | if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then | |
75 | dep_tristate ' PPP over Ethernet (EXPERIMENTAL)' CONFIG_PPPOE $CONFIG_PPP | |
76 | + if [ "$CONFIG_ATM" = "y" ]; then | |
77 | + dep_tristate ' PPP over ATM (EXPERIMENTAL)' CONFIG_PPPOATM $CONFIG_PPP $CONFIG_EXPERIMENTAL | |
78 | + fi | |
79 | fi | |
80 | fi | |
81 | ||
82 | diff -urN linux.orig/include/linux/atm.h linux/include/linux/atm.h | |
83 | --- linux.orig/include/linux/atm.h Tue Mar 27 01:49:24 2001 | |
84 | +++ linux/include/linux/atm.h Sat Apr 21 18:06:09 2001 | |
85 | @@ -235,6 +235,7 @@ | |
86 | void *arg; | |
87 | }; | |
88 | ||
89 | +typedef unsigned short atm_backend_t; | |
90 | ||
91 | #ifdef __KERNEL__ | |
92 | ||
93 | diff -urN linux.orig/include/linux/atmbr2684.h linux/include/linux/atmbr2684.h | |
94 | --- linux.orig/include/linux/atmbr2684.h Thu Jan 1 01:00:00 1970 | |
95 | +++ linux/include/linux/atmbr2684.h Sat Apr 21 18:06:09 2001 | |
96 | @@ -0,0 +1,101 @@ | |
97 | +#ifndef _LINUX_ATMBR2684_H | |
98 | +#define _LINUX_ATMBR2684_H | |
99 | + | |
100 | +#include <linux/atm.h> | |
101 | +#include <linux/if.h> /* For IFNAMSIZ */ | |
102 | + | |
103 | +/* | |
104 | + * Type of media we're bridging (ethernet, token ring, etc) Currently only | |
105 | + * ethernet is supported | |
106 | + */ | |
107 | +#define BR2684_MEDIA_ETHERNET (0) /* 802.3 */ | |
108 | +#define BR2684_MEDIA_802_4 (1) /* 802.4 */ | |
109 | +#define BR2684_MEDIA_TR (2) /* 802.5 - token ring */ | |
110 | +#define BR2684_MEDIA_FDDI (3) | |
111 | +#define BR2684_MEDIA_802_6 (4) /* 802.6 */ | |
112 | + | |
113 | +/* | |
114 | + * Is there FCS inbound on this VC? This currently isn't supported. | |
115 | + */ | |
116 | +#define BR2684_FCSIN_NO (0) | |
117 | +#define BR2684_FCSIN_IGNORE (1) | |
118 | +#define BR2684_FCSIN_VERIFY (2) | |
119 | + | |
120 | +/* | |
121 | + * Is there FCS outbound on this VC? This currently isn't supported. | |
122 | + */ | |
123 | +#define BR2684_FCSOUT_NO (0) | |
124 | +#define BR2684_FCSOUT_SENDZERO (1) | |
125 | +#define BR2684_FCSOUT_GENERATE (2) | |
126 | + | |
127 | +/* | |
128 | + * Does this VC include LLC encapsulation? | |
129 | + */ | |
130 | +#define BR2684_ENCAPS_VC (0) /* VC-mux */ | |
131 | +#define BR2684_ENCAPS_LLC (1) | |
132 | +#define BR2684_ENCAPS_AUTODETECT (2) /* Unsuported */ | |
133 | + | |
134 | +/* | |
135 | + * This is for the ATM_NEWBACKENDIF call - these are like socket families: | |
136 | + * the first element of the structure is the backend number and the rest | |
137 | + * is per-backend specific | |
138 | + */ | |
139 | +struct atm_newif_br2684 { | |
140 | + atm_backend_t backend_num; /* ATM_BACKEND_BR2684 */ | |
141 | + int media; /* BR2684_MEDIA_* */ | |
142 | + char ifname[IFNAMSIZ]; | |
143 | + int mtu; | |
144 | +}; | |
145 | + | |
146 | +/* | |
147 | + * This structure is used to specify a br2684 interface - either by a | |
148 | + * positive integer (returned by ATM_NEWBACKENDIF) or the interfaces name | |
149 | + */ | |
150 | +#define BR2684_FIND_BYNOTHING (0) | |
151 | +#define BR2684_FIND_BYNUM (1) | |
152 | +#define BR2684_FIND_BYIFNAME (2) | |
153 | +struct br2684_if_spec { | |
154 | + int method; /* BR2684_FIND_* */ | |
155 | + union { | |
156 | + char ifname[IFNAMSIZ]; | |
157 | + int devnum; | |
158 | + } spec; | |
159 | +}; | |
160 | + | |
161 | +/* | |
162 | + * This is for the ATM_SETBACKEND call - these are like socket families: | |
163 | + * the first element of the structure is the backend number and the rest | |
164 | + * is per-backend specific | |
165 | + */ | |
166 | +struct atm_backend_br2684 { | |
167 | + atm_backend_t backend_num; /* ATM_BACKEND_BR2684 */ | |
168 | + struct br2684_if_spec ifspec; | |
169 | + int fcs_in; /* BR2684_FCSIN_* */ | |
170 | + int fcs_out; /* BR2684_FCSOUT_* */ | |
171 | + int fcs_auto; /* 1: fcs_{in,out} disabled if no FCS rx'ed */ | |
172 | + int encaps; /* BR2684_ENCAPS_* */ | |
173 | + int has_vpiid; /* 1: use vpn_id - Unsupported */ | |
174 | + __u8 vpn_id[7]; | |
175 | + int send_padding; /* unsupported */ | |
176 | + int min_size; /* we will pad smaller packets than this */ | |
177 | +}; | |
178 | + | |
179 | +/* | |
180 | + * The BR2684_SETFILT ioctl is an experimental mechanism for folks | |
181 | + * terminating a large number of IP-only vcc's. When netfilter allows | |
182 | + * efficient per-if in/out filters, this support will be removed | |
183 | + */ | |
184 | +struct br2684_filter { | |
185 | + __u32 prefix; /* network byte order */ | |
186 | + __u32 netmask; /* 0 = disable filter */ | |
187 | +}; | |
188 | + | |
189 | +struct br2684_filter_set { | |
190 | + struct br2684_if_spec ifspec; | |
191 | + struct br2684_filter filter; | |
192 | +}; | |
193 | + | |
194 | +#define BR2684_SETFILT _IOW( 'a', ATMIOC_BACKEND + 0, \ | |
195 | + struct br2684_filter_set) | |
196 | + | |
197 | +#endif /* _LINUX_ATMBR2684_H */ | |
198 | diff -urN linux.orig/include/linux/atmdev.h linux/include/linux/atmdev.h | |
199 | --- linux.orig/include/linux/atmdev.h Tue Mar 27 01:50:03 2001 | |
200 | +++ linux/include/linux/atmdev.h Sat Apr 21 18:06:09 2001 | |
201 | @@ -87,6 +87,20 @@ | |
202 | #define ATM_SETSC _IOW('a',ATMIOC_SPECIAL+1,int) | |
203 | /* enable or disable single-copy */ | |
204 | ||
205 | +#define ATM_SETBACKEND _IOW('a',ATMIOC_SPECIAL+2,atm_backend_t) | |
206 | + /* set backend handler */ | |
207 | +#define ATM_NEWBACKENDIF _IOW('a',ATMIOC_SPECIAL+3,atm_backend_t) | |
208 | + /* use backend to make new if */ | |
209 | + | |
210 | +/* | |
211 | + * These are backend handkers that can be set via the ATM_SETBACKEND call | |
212 | + * above. In the future we may support dynamic loading of these - for now, | |
213 | + * they're just being used to share the ATMIOC_BACKEND ioctls | |
214 | + */ | |
215 | +#define ATM_BACKEND_RAW 0 | |
216 | +#define ATM_BACKEND_PPP 1 /* PPPoATM - RFC2364 */ | |
217 | +#define ATM_BACKEND_BR2684 2 /* Bridged RFC1483/2684 */ | |
218 | + | |
219 | /* for ATM_GETTYPE */ | |
220 | #define ATM_ITFTYP_LEN 8 /* maximum length of interface type name */ | |
221 | ||
222 | diff -urN linux.orig/include/linux/atmioc.h linux/include/linux/atmioc.h | |
223 | --- linux.orig/include/linux/atmioc.h Wed Feb 9 03:23:13 2000 | |
224 | +++ linux/include/linux/atmioc.h Sat Apr 21 18:06:09 2001 | |
225 | @@ -27,7 +27,9 @@ | |
226 | #define ATMIOC_SARPRV_END 0x7f | |
227 | #define ATMIOC_ITF 0x80 /* Interface ioctls, globally unique */ | |
228 | #define ATMIOC_ITF_END 0x8f | |
229 | -/* 0x90-0xbf: Reserved for future use */ | |
230 | +#define ATMIOC_BACKEND 0x90 /* ATM generic backend ioctls, u. per backend */ | |
231 | +#define ATMIOC_BACKEND_END 0xaf | |
232 | +/* 0xb0-0xbf: Reserved for future use */ | |
233 | #define ATMIOC_AREQUIPA 0xc0 /* Application requested IP over ATM, glob. u. */ | |
234 | #define ATMIOC_LANE 0xd0 /* LAN Emulation, globally unique */ | |
235 | #define ATMIOC_MPOA 0xd8 /* MPOA, globally unique */ | |
236 | diff -urN linux.orig/include/linux/atmppp.h linux/include/linux/atmppp.h | |
237 | --- linux.orig/include/linux/atmppp.h Thu Jan 1 01:00:00 1970 | |
238 | +++ linux/include/linux/atmppp.h Sat Apr 21 18:06:09 2001 | |
239 | @@ -0,0 +1,24 @@ | |
240 | +/* atmppp.h - RFC2364 PPPoATM */ | |
241 | + | |
242 | +/* Written 2000 by Mitchell Blank Jr */ | |
243 | + | |
244 | +#ifndef _LINUX_ATMPPP_H | |
245 | +#define _LINUX_ATMPPP_H | |
246 | + | |
247 | +#include <linux/atm.h> | |
248 | + | |
249 | +#define PPPOATM_ENCAPS_AUTODETECT (0) | |
250 | +#define PPPOATM_ENCAPS_VC (1) | |
251 | +#define PPPOATM_ENCAPS_LLC (2) | |
252 | + | |
253 | +/* | |
254 | + * This is for the ATM_SETBACKEND call - these are like socket families: | |
255 | + * the first element of the structure is the backend number and the rest | |
256 | + * is per-backend specific | |
257 | + */ | |
258 | +struct atm_backend_ppp { | |
259 | + atm_backend_t backend_num; /* ATM_BACKEND_PPP */ | |
260 | + int encaps; /* PPPOATM_ENCAPS_* */ | |
261 | +}; | |
262 | + | |
263 | +#endif /* _LINUX_ATMPPP_H */ | |
264 | diff -urN linux.orig/net/Config.in linux/net/Config.in | |
265 | --- linux.orig/net/Config.in Sat Apr 21 18:05:00 2001 | |
266 | +++ linux/net/Config.in Sat Apr 21 18:06:09 2001 | |
267 | @@ -45,6 +45,10 @@ | |
268 | if [ "$CONFIG_INET" = "y" -a "$CONFIG_ATM_LANE" != "n" ]; then | |
269 | tristate ' Multi-Protocol Over ATM (MPOA) support' CONFIG_ATM_MPOA | |
270 | fi | |
271 | + tristate ' RFC1483/2684 Bridged protocols' CONFIG_ATM_BR2684 | |
272 | + if [ "$CONFIG_ATM_BR2684" != "n" ]; then | |
273 | + bool ' Per-VC IP filter kludge' CONFIG_ATM_BR2684_IPFILTER | |
274 | + fi | |
275 | fi | |
276 | fi | |
277 | ||
278 | diff -urN linux.orig/net/atm/Makefile linux/net/atm/Makefile | |
279 | --- linux.orig/net/atm/Makefile Tue Mar 27 01:36:30 2001 | |
280 | +++ linux/net/atm/Makefile Sat Apr 21 18:06:09 2001 | |
281 | @@ -21,6 +21,15 @@ | |
282 | NEED_IPCOM = ipcommon.o | |
283 | endif | |
284 | ||
285 | +ifeq ($(CONFIG_ATM_BR2684),y) | |
286 | + NEED_IPCOM = ipcommon.o | |
287 | +else | |
288 | + ifeq ($(CONFIG_ATM_BR2684),m) | |
289 | + NEED_IPCOM = ipcommon.o | |
290 | + endif | |
291 | +endif | |
292 | +obj-$(CONFIG_ATM_BR2684) += br2684.o | |
293 | + | |
294 | ifeq ($(CONFIG_NET_SCH_ATM),y) | |
295 | NEED_IPCOM = ipcommon.o | |
296 | endif | |
297 | @@ -33,6 +42,7 @@ | |
298 | ||
299 | obj-$(CONFIG_ATM_LANE) += lec.o | |
300 | obj-$(CONFIG_ATM_MPOA) += mpoa.o | |
301 | +obj-$(CONFIG_PPPOATM) += pppoatm.o | |
302 | ||
303 | include $(TOPDIR)/Rules.make | |
304 | ||
305 | diff -urN linux.orig/net/atm/br2684.c linux/net/atm/br2684.c | |
306 | --- linux.orig/net/atm/br2684.c Thu Jan 1 01:00:00 1970 | |
307 | +++ linux/net/atm/br2684.c Sat Apr 21 18:06:09 2001 | |
308 | @@ -0,0 +1,794 @@ | |
309 | +/* | |
310 | +Experimental ethernet netdevice using ATM AAL5 as underlying carrier | |
311 | +(RFC1483 obsoleted by RFC2684) for Linux 2.4 | |
312 | +Author: Marcell GAL, 2000, XDSL Ltd, Hungary | |
313 | +*/ | |
314 | + | |
315 | +#include <linux/module.h> | |
316 | +#include <linux/config.h> | |
317 | +#include <linux/init.h> | |
318 | +#include <linux/kernel.h> | |
319 | +#include <linux/list.h> | |
320 | +#include <asm/uaccess.h> | |
321 | +#include <linux/netdevice.h> | |
322 | +#include <linux/skbuff.h> | |
323 | +#include <linux/etherdevice.h> | |
324 | +#include <net/arp.h> | |
325 | +#include <linux/rtnetlink.h> | |
326 | +#include <linux/atmbr2684.h> | |
327 | + | |
328 | +#include "ipcommon.h" | |
329 | + | |
330 | +/* | |
331 | + * Define this to use a version of the code which interacts with the higher | |
332 | + * layers in a more intellegent way, by always reserving enough space for | |
333 | + * our header at the begining of the packet. However, there may still be | |
334 | + * some problems with programs like tcpdump. In 2.5 we'll sort out what | |
335 | + * we need to do to get this perfect. For now we just will copy the packet | |
336 | + * if we need space for the header | |
337 | + */ | |
338 | +/* #define FASTER_VERSION */ | |
339 | + | |
340 | +#ifdef DEBUG | |
341 | +#define DPRINTK(format, args...) printk(KERN_DEBUG "br2684: " format, ##args) | |
342 | +#else | |
343 | +#define DPRINTK(format, args...) | |
344 | +#endif | |
345 | + | |
346 | +#ifdef SKB_DEBUG | |
347 | +static void skb_debug(const struct sk_buff *skb) | |
348 | +{ | |
349 | +#define NUM2PRINT 50 | |
350 | + char buf[NUM2PRINT * 3 + 1]; /* 3 chars per byte */ | |
351 | + int i = 0; | |
352 | + for (i = 0; i < skb->len && i < NUM2PRINT; i++) { | |
353 | + sprintf(buf + i * 3, "%2.2x ", 0xff & skb->data[i]); | |
354 | + } | |
355 | + printk(KERN_DEBUG "br2684: skb: %s\n", buf); | |
356 | +} | |
357 | +#else | |
358 | +#define skb_debug(skb) do {} while (0) | |
359 | +#endif | |
360 | + | |
361 | +static unsigned char llc_oui_pid_pad[] = | |
362 | + { 0xAA, 0xAA, 0x03, 0x00, 0x80, 0xC2, 0x00, 0x07, 0x00, 0x00 }; | |
363 | +#define PADLEN (2) | |
364 | + | |
365 | +enum br2684_encaps { | |
366 | + e_vc = BR2684_ENCAPS_VC, | |
367 | + e_llc = BR2684_ENCAPS_LLC, | |
368 | +}; | |
369 | + | |
370 | +struct br2684_vcc { | |
371 | + struct atm_vcc *atmvcc; | |
372 | + struct br2684_dev *brdev; | |
373 | + /* keep old push,pop functions for chaining */ | |
374 | + void (*old_push)(struct atm_vcc *vcc,struct sk_buff *skb); | |
375 | + /* void (*old_pop)(struct atm_vcc *vcc,struct sk_buff *skb); */ | |
376 | + enum br2684_encaps encaps; | |
377 | + struct list_head brvccs; | |
378 | +#ifdef CONFIG_ATM_BR2684_IPFILTER | |
379 | + struct br2684_filter filter; | |
380 | +#endif /* CONFIG_ATM_BR2684_IPFILTER */ | |
381 | +#ifndef FASTER_VERSION | |
382 | + unsigned copies_needed, copies_failed; | |
383 | +#endif /* FASTER_VERSION */ | |
384 | +}; | |
385 | + | |
386 | +struct br2684_dev { | |
387 | + struct net_device net_dev; | |
388 | + struct list_head br2684_devs; | |
389 | + int number; | |
390 | + struct list_head brvccs; /* one device <=> one vcc (before xmas) */ | |
391 | + struct net_device_stats stats; | |
392 | + int mac_was_set; | |
393 | +}; | |
394 | + | |
395 | +/* | |
396 | + * This lock should be held for writing any time the list of devices or | |
397 | + * their attached vcc's could be altered. It should be held for reading | |
398 | + * any time these are being queried. Note that we sometimes need to | |
399 | + * do read-locking under interrupt context, so write locking must block | |
400 | + * the current CPU's interrupts | |
401 | + */ | |
402 | +static rwlock_t devs_lock = RW_LOCK_UNLOCKED; | |
403 | + | |
404 | +static LIST_HEAD(br2684_devs); | |
405 | + | |
406 | +static inline struct br2684_dev *BRPRIV(const struct net_device *net_dev) | |
407 | +{ | |
408 | + return (struct br2684_dev *) ((char *) (net_dev) - | |
409 | + (unsigned long) (&((struct br2684_dev *) 0)->net_dev)); | |
410 | +} | |
411 | + | |
412 | +static inline struct br2684_dev *list_entry_brdev(const struct list_head *le) | |
413 | +{ | |
414 | + return list_entry(le, struct br2684_dev, br2684_devs); | |
415 | +} | |
416 | + | |
417 | +static inline struct br2684_vcc *BR2684_VCC(const struct atm_vcc *atmvcc) | |
418 | +{ | |
419 | + return (struct br2684_vcc *) (atmvcc->user_back); | |
420 | +} | |
421 | + | |
422 | +static inline struct br2684_vcc *list_entry_brvcc(const struct list_head *le) | |
423 | +{ | |
424 | + return list_entry(le, struct br2684_vcc, brvccs); | |
425 | +} | |
426 | + | |
427 | +/* Caller should hold read_lock(&devs_lock) */ | |
428 | +static struct br2684_dev *br2684_find_dev(const struct br2684_if_spec *s) | |
429 | +{ | |
430 | + struct list_head *lh; | |
431 | + struct br2684_dev *brdev; | |
432 | + switch (s->method) { | |
433 | + case BR2684_FIND_BYNUM: | |
434 | + list_for_each(lh, &br2684_devs) { | |
435 | + brdev = list_entry_brdev(lh); | |
436 | + if (brdev->number == s->spec.devnum) | |
437 | + return brdev; | |
438 | + } | |
439 | + break; | |
440 | + case BR2684_FIND_BYIFNAME: | |
441 | + list_for_each(lh, &br2684_devs) { | |
442 | + brdev = list_entry_brdev(lh); | |
443 | + if (!strncmp(brdev->net_dev.name, s->spec.ifname, | |
444 | + sizeof brdev->net_dev.name)) | |
445 | + return brdev; | |
446 | + } | |
447 | + break; | |
448 | + } | |
449 | + return NULL; | |
450 | +} | |
451 | + | |
452 | +/* | |
453 | + * Send a packet out a particular vcc. Not to useful right now, but paves | |
454 | + * the way for multiple vcc's per itf. Returns true if we can send, | |
455 | + * otherwise false | |
456 | + */ | |
457 | +static int br2684_xmit_vcc(struct sk_buff *skb, struct br2684_dev *brdev, | |
458 | + struct br2684_vcc *brvcc) | |
459 | +{ | |
460 | + struct atm_vcc *atmvcc; | |
461 | +#ifdef FASTER_VERSION | |
462 | + if (brvcc->encaps == e_llc) | |
463 | + memcpy(skb_push(skb, 8), llc_oui_pid_pad, 8); | |
464 | + /* last 2 bytes of llc_oui_pid_pad are managed by header routines; | |
465 | + yes, you got it: 8 + 2 = sizeof(llc_oui_pid_pad) | |
466 | + */ | |
467 | +#else | |
468 | + int minheadroom = (brvcc->encaps == e_llc) ? 10 : 2; | |
469 | + if (skb_headroom(skb) < minheadroom) { | |
470 | + struct sk_buff *skb2 = skb_realloc_headroom(skb, minheadroom); | |
471 | + brvcc->copies_needed++; | |
472 | + dev_kfree_skb(skb); | |
473 | + if (skb2 == NULL) { | |
474 | + brvcc->copies_failed++; | |
475 | + return 0; | |
476 | + } | |
477 | + skb = skb2; | |
478 | + } | |
479 | + skb_push(skb, minheadroom); | |
480 | + if (brvcc->encaps == e_llc) | |
481 | + memcpy(skb->data, llc_oui_pid_pad, 10); | |
482 | + else | |
483 | + memset(skb->data, 0, 2); | |
484 | +#endif /* FASTER_VERSION */ | |
485 | + skb_debug(skb); | |
486 | + | |
487 | + ATM_SKB(skb)->vcc = atmvcc = brvcc->atmvcc; | |
488 | + DPRINTK("atm_skb(%p)->vcc(%p)->dev(%p)\n", skb, atmvcc, atmvcc->dev); | |
489 | + if (!atm_may_send(atmvcc, skb->truesize)) { | |
490 | + /* we free this here for now, because we cannot know in a higher | |
491 | + layer whether the skb point it supplied wasn't freed yet. | |
492 | + now, it always is. | |
493 | + */ | |
494 | + dev_kfree_skb(skb); | |
495 | + return 0; | |
496 | + } | |
497 | + atomic_add(skb->truesize, &atmvcc->tx_inuse); | |
498 | + ATM_SKB(skb)->iovcnt = 0; | |
499 | + ATM_SKB(skb)->atm_options = atmvcc->atm_options; | |
500 | + brdev->stats.tx_packets++; | |
501 | + brdev->stats.tx_bytes += skb->len; | |
502 | + atmvcc->send(atmvcc, skb); | |
503 | + return 1; | |
504 | +} | |
505 | + | |
506 | +static inline struct br2684_vcc *pick_outgoing_vcc(struct sk_buff *skb, | |
507 | + struct br2684_dev *brdev) | |
508 | +{ | |
509 | + return list_empty(&brdev->brvccs) ? NULL : | |
510 | + list_entry_brvcc(brdev->brvccs.next); /* 1 vcc/dev right now */ | |
511 | +} | |
512 | + | |
513 | +static int br2684_start_xmit(struct sk_buff *skb, struct net_device *dev) | |
514 | +{ | |
515 | + struct br2684_dev *brdev = BRPRIV(dev); | |
516 | + struct br2684_vcc *brvcc; | |
517 | + | |
518 | + DPRINTK("br2684_start_xmit, skb->dst=%p\n", skb->dst); | |
519 | + read_lock(&devs_lock); | |
520 | + brvcc = pick_outgoing_vcc(skb, brdev); | |
521 | + if (brvcc == NULL) { | |
522 | + DPRINTK("no vcc attached to dev %s\n", dev->name); | |
523 | + brdev->stats.tx_errors++; | |
524 | + brdev->stats.tx_carrier_errors++; | |
525 | + /* netif_stop_queue(dev); */ | |
526 | + dev_kfree_skb(skb); | |
527 | + read_unlock(&devs_lock); | |
528 | + return -EUNATCH; | |
529 | + } | |
530 | + if (!br2684_xmit_vcc(skb, brdev, brvcc)) { | |
531 | + /* | |
532 | + * We should probably use netif_*_queue() here, but that | |
533 | + * involves added complication. We need to walk before | |
534 | + * we can run | |
535 | + */ | |
536 | + /* don't free here! this pointer might be no longer valid! | |
537 | + dev_kfree_skb(skb); | |
538 | + */ | |
539 | + brdev->stats.tx_errors++; | |
540 | + brdev->stats.tx_fifo_errors++; | |
541 | + } | |
542 | + read_unlock(&devs_lock); | |
543 | + return 0; | |
544 | +} | |
545 | + | |
546 | +static struct net_device_stats *br2684_get_stats(struct net_device *dev) | |
547 | +{ | |
548 | + DPRINTK("br2684_get_stats\n"); | |
549 | + return &BRPRIV(dev)->stats; | |
550 | +} | |
551 | + | |
552 | +#ifdef FASTER_VERSION | |
553 | +/* | |
554 | + * These mirror eth_header and eth_header_cache. They are not usually | |
555 | + * exported for use in modules, so we grab them from net_device | |
556 | + * after ether_setup() is done with it. Bit of a hack. | |
557 | + */ | |
558 | +static int (*my_eth_header)(struct sk_buff *, struct net_device *, | |
559 | + unsigned short, void *, void *, unsigned); | |
560 | +static int (*my_eth_header_cache)(struct neighbour *, struct hh_cache *); | |
561 | + | |
562 | +static int | |
563 | +br2684_header(struct sk_buff *skb, struct net_device *dev, | |
564 | + unsigned short type, void *daddr, void *saddr, unsigned len) | |
565 | +{ | |
566 | + u16 *pad_before_eth; | |
567 | + int t = my_eth_header(skb, dev, type, daddr, saddr, len); | |
568 | + if (t > 0) { | |
569 | + pad_before_eth = (u16 *) skb_push(skb, 2); | |
570 | + *pad_before_eth = 0; | |
571 | + return dev->hard_header_len; /* or return 16; ? */ | |
572 | + } else | |
573 | + return t; | |
574 | +} | |
575 | + | |
576 | +static int | |
577 | +br2684_header_cache(struct neighbour *neigh, struct hh_cache *hh) | |
578 | +{ | |
579 | +/* hh_data is 16 bytes long. if encaps is ether-llc we need 24, so | |
580 | +xmit will add the additional header part in that case */ | |
581 | + u16 *pad_before_eth = (u16 *)(hh->hh_data); | |
582 | + int t = my_eth_header_cache(neigh, hh); | |
583 | + DPRINTK("br2684_header_cache, neigh=%p, hh_cache=%p\n", neigh, hh); | |
584 | + if (t < 0) | |
585 | + return t; | |
586 | + else { | |
587 | + *pad_before_eth = 0; | |
588 | + hh->hh_len = PADLEN + ETH_HLEN; | |
589 | + } | |
590 | + return 0; | |
591 | +} | |
592 | + | |
593 | +/* | |
594 | + * This is similar to eth_type_trans, which cannot be used because of | |
595 | + * our dev->hard_header_len | |
596 | + */ | |
597 | +static inline unsigned short br_type_trans(struct sk_buff *skb, | |
598 | + struct net_device *dev) | |
599 | +{ | |
600 | + struct ethhdr *eth; | |
601 | + unsigned char *rawp; | |
602 | + eth = skb->mac.ethernet; | |
603 | + | |
604 | + if (*eth->h_dest & 1) { | |
605 | + if (memcmp(eth->h_dest, dev->broadcast, ETH_ALEN) == 0) | |
606 | + skb->pkt_type = PACKET_BROADCAST; | |
607 | + else | |
608 | + skb->pkt_type = PACKET_MULTICAST; | |
609 | + } | |
610 | + | |
611 | + else if (memcmp(eth->h_dest, dev->dev_addr, ETH_ALEN)) | |
612 | + skb->pkt_type = PACKET_OTHERHOST; | |
613 | + | |
614 | + if (ntohs(eth->h_proto) >= 1536) | |
615 | + return eth->h_proto; | |
616 | + | |
617 | + rawp = skb->data; | |
618 | + | |
619 | + /* | |
620 | + * This is a magic hack to spot IPX packets. Older Novell breaks | |
621 | + * the protocol design and runs IPX over 802.3 without an 802.2 LLC | |
622 | + * layer. We look for FFFF which isn't a used 802.2 SSAP/DSAP. This | |
623 | + * won't work for fault tolerant netware but does for the rest. | |
624 | + */ | |
625 | + if (*(unsigned short *) rawp == 0xFFFF) | |
626 | + return htons(ETH_P_802_3); | |
627 | + | |
628 | + /* | |
629 | + * Real 802.2 LLC | |
630 | + */ | |
631 | + return htons(ETH_P_802_2); | |
632 | +} | |
633 | +#endif /* FASTER_VERSION */ | |
634 | + | |
635 | +/* | |
636 | + * We remember when the MAC gets set, so we don't override it later with | |
637 | + * the ESI of the ATM card of the first VC | |
638 | + */ | |
639 | +static int (*my_eth_mac_addr)(struct net_device *, void *); | |
640 | +static int br2684_mac_addr(struct net_device *dev, void *p) | |
641 | +{ | |
642 | + int err = my_eth_mac_addr(dev, p); | |
643 | + if (!err) | |
644 | + BRPRIV(dev)->mac_was_set = 1; | |
645 | + return err; | |
646 | +} | |
647 | + | |
648 | +#ifdef CONFIG_ATM_BR2684_IPFILTER | |
649 | +/* this IOCTL is experimental. */ | |
650 | +static int br2684_setfilt(struct atm_vcc *atmvcc, unsigned long arg) | |
651 | +{ | |
652 | + struct br2684_vcc *brvcc; | |
653 | + struct br2684_filter_set fs; | |
654 | + | |
655 | + if (copy_from_user(&fs, (void *) arg, sizeof fs)) | |
656 | + return -EFAULT; | |
657 | + if (fs.ifspec.method != BR2684_FIND_BYNOTHING) { | |
658 | + /* | |
659 | + * This is really a per-vcc thing, but we can also search | |
660 | + * by device | |
661 | + */ | |
662 | + struct br2684_dev *brdev; | |
663 | + read_lock(&devs_lock); | |
664 | + brdev = br2684_find_dev(&fs.ifspec); | |
665 | + if (brdev == NULL || list_empty(&brdev->brvccs) || | |
666 | + brdev->brvccs.next != brdev->brvccs.prev) /* >1 VCC */ | |
667 | + brvcc = NULL; | |
668 | + else | |
669 | + brvcc = list_entry_brvcc(brdev->brvccs.next); | |
670 | + read_unlock(&devs_lock); | |
671 | + if (brvcc == NULL) | |
672 | + return -ESRCH; | |
673 | + } else | |
674 | + brvcc = BR2684_VCC(atmvcc); | |
675 | + memcpy(&brvcc->filter, &fs.filter, sizeof(brvcc->filter)); | |
676 | + return 0; | |
677 | +} | |
678 | + | |
679 | +/* Returns 1 if packet should be dropped */ | |
680 | +static inline int | |
681 | +packet_fails_filter(u16 type, struct br2684_vcc *brvcc, struct sk_buff *skb) | |
682 | +{ | |
683 | + if (brvcc->filter.netmask == 0) | |
684 | + return 0; /* no filter in place */ | |
685 | + if (type == __constant_htons(ETH_P_IP) && | |
686 | + (((struct iphdr *) (skb->data))->daddr & brvcc->filter. | |
687 | + netmask) == brvcc->filter.prefix) | |
688 | + return 0; | |
689 | + if (type == __constant_htons(ETH_P_ARP)) | |
690 | + return 0; | |
691 | + /* TODO: we should probably filter ARPs too.. don't want to have | |
692 | + * them returning values that don't make sense, or is that ok? | |
693 | + */ | |
694 | + return 1; /* drop */ | |
695 | +} | |
696 | +#endif /* CONFIG_ATM_BR2684_IPFILTER */ | |
697 | + | |
698 | +static void br2684_close_vcc(struct br2684_vcc *brvcc) | |
699 | +{ | |
700 | + DPRINTK("removing VCC %p from dev %p\n", brvcc, brvcc->brdev); | |
701 | + write_lock_irq(&devs_lock); | |
702 | + list_del(&brvcc->brvccs); | |
703 | + write_unlock_irq(&devs_lock); | |
704 | + brvcc->atmvcc->user_back = NULL; /* what about vcc->recvq ??? */ | |
705 | + brvcc->old_push(brvcc->atmvcc, NULL); /* pass on the bad news */ | |
706 | + kfree(brvcc); | |
707 | + MOD_DEC_USE_COUNT; | |
708 | +} | |
709 | + | |
710 | +/* when AAL5 PDU comes in: */ | |
711 | +static void br2684_push(struct atm_vcc *atmvcc, struct sk_buff *skb) | |
712 | +{ | |
713 | + struct br2684_vcc *brvcc = BR2684_VCC(atmvcc); | |
714 | + struct br2684_dev *brdev = brvcc->brdev; | |
715 | + int plen = sizeof(llc_oui_pid_pad) + ETH_HLEN; | |
716 | + | |
717 | + DPRINTK("br2684_push\n"); | |
718 | + | |
719 | + if (skb == NULL) { /* skb==NULL means VCC is being destroyed */ | |
720 | + br2684_close_vcc(brvcc); | |
721 | + return; | |
722 | + } | |
723 | + | |
724 | + skb_debug(skb); | |
725 | + atm_return(atmvcc, skb->truesize); | |
726 | + DPRINTK("skb from brdev %p\n", brdev); | |
727 | + if (brvcc->encaps == e_llc) { | |
728 | + /* let us waste some time for checking the encapsulation. | |
729 | + Note, that only 7 char is checked so frames with a valid FCS | |
730 | + are also accepted (but FCS is not checked of course) */ | |
731 | + if (memcmp(skb->data, llc_oui_pid_pad, 7)) { | |
732 | + brdev->stats.rx_errors++; | |
733 | + dev_kfree_skb(skb); | |
734 | + return; | |
735 | + } | |
736 | + } else { | |
737 | + plen = PADLEN + ETH_HLEN; /* pad, dstmac,srcmac, ethtype */ | |
738 | + /* first 2 chars should be 0 */ | |
739 | + if (*((u16 *) (skb->data)) != 0) { | |
740 | + brdev->stats.rx_errors++; | |
741 | + dev_kfree_skb(skb); | |
742 | + return; | |
743 | + } | |
744 | + } | |
745 | + if (skb->len < plen) { | |
746 | + brdev->stats.rx_errors++; | |
747 | + dev_kfree_skb(skb); /* dev_ not needed? */ | |
748 | + return; | |
749 | + } | |
750 | + | |
751 | +#ifdef FASTER_VERSION | |
752 | + /* FIXME: tcpdump shows that pointer to mac header is 2 bytes earlier, | |
753 | + than should be. What else should I set? */ | |
754 | + skb_pull(skb, plen); | |
755 | + skb->mac.raw = ((char *) (skb->data)) - ETH_HLEN; | |
756 | + skb->pkt_type = PACKET_HOST; | |
757 | +#ifdef CONFIG_BR2684_FAST_TRANS | |
758 | + skb->protocol = ((u16 *) skb->data)[-1]; | |
759 | +#else /* some protocols might require this: */ | |
760 | + skb->protocol = br_type_trans(skb, &brdev->net_dev); | |
761 | +#endif /* CONFIG_BR2684_FAST_TRANS */ | |
762 | +#else | |
763 | + skb_pull(skb, plen - ETH_HLEN); | |
764 | + skb->protocol = eth_type_trans(skb, &brdev->net_dev); | |
765 | +#endif /* FASTER_VERSION */ | |
766 | +#ifdef CONFIG_ATM_BR2684_IPFILTER | |
767 | + if (packet_fails_filter(skb->protocol, brvcc, skb)) { | |
768 | + brdev->stats.rx_dropped++; | |
769 | + dev_kfree_skb(skb); | |
770 | + return; | |
771 | + } | |
772 | +#endif /* CONFIG_ATM_BR2684_IPFILTER */ | |
773 | + skb->dev = &brdev->net_dev; | |
774 | + ATM_SKB(skb)->vcc = atmvcc; /* needed ? */ | |
775 | + DPRINTK("received packet's protocol: %x\n", ntohs(skb->protocol)); | |
776 | + skb_debug(skb); | |
777 | + if (!(brdev->net_dev.flags & IFF_UP)) { /* sigh, interface is down */ | |
778 | + brdev->stats.rx_dropped++; | |
779 | + dev_kfree_skb(skb); | |
780 | + return; | |
781 | + } | |
782 | + brdev->stats.rx_packets++; | |
783 | + brdev->stats.rx_bytes += skb->len; | |
784 | + netif_rx(skb); | |
785 | +} | |
786 | + | |
787 | +static int br2684_regvcc(struct atm_vcc *atmvcc, unsigned long arg) | |
788 | +{ | |
789 | +/* assign a vcc to a dev | |
790 | +Note: we do not have explicit unassign, but look at _push() | |
791 | +*/ | |
792 | + int err; | |
793 | + struct br2684_vcc *brvcc; | |
794 | + struct sk_buff_head copy; | |
795 | + struct sk_buff *skb; | |
796 | + struct br2684_dev *brdev; | |
797 | + struct atm_backend_br2684 be; | |
798 | + | |
799 | + MOD_INC_USE_COUNT; | |
800 | + if (copy_from_user(&be, (void *) arg, sizeof be)) { | |
801 | + MOD_DEC_USE_COUNT; | |
802 | + return -EFAULT; | |
803 | + } | |
804 | + write_lock_irq(&devs_lock); | |
805 | + brdev = br2684_find_dev(&be.ifspec); | |
806 | + if (brdev == NULL) { | |
807 | + printk(KERN_ERR | |
808 | + "br2684: tried to attach to non-existant device\n"); | |
809 | + err = -ENXIO; | |
810 | + goto error; | |
811 | + } | |
812 | + if (atmvcc->push == NULL) { | |
813 | + err = -EBADFD; | |
814 | + goto error; | |
815 | + } | |
816 | + if (!list_empty(&brdev->brvccs)) { /* Only 1 VCC/dev right now */ | |
817 | + err = -EEXIST; | |
818 | + goto error; | |
819 | + } | |
820 | + if (be.fcs_in != BR2684_FCSIN_NO || be.fcs_out != BR2684_FCSOUT_NO || | |
821 | + be.fcs_auto || be.has_vpiid || be.send_padding || (be.encaps != | |
822 | + BR2684_ENCAPS_VC && be.encaps != BR2684_ENCAPS_LLC) || | |
823 | + be.min_size != 0) { | |
824 | + err = -EINVAL; | |
825 | + goto error; | |
826 | + } | |
827 | + brvcc = kmalloc(sizeof(struct br2684_vcc), GFP_KERNEL); | |
828 | + if (!brvcc) { | |
829 | + err = -ENOMEM; | |
830 | + goto error; | |
831 | + } | |
832 | + memset(brvcc, 0, sizeof(struct br2684_vcc)); | |
833 | + DPRINTK("br2684_regvcc vcc=%p, encaps=%d, brvcc=%p\n", atmvcc, be.encaps, | |
834 | + brvcc); | |
835 | + if (list_empty(&brdev->brvccs) && !brdev->mac_was_set) { | |
836 | + unsigned char *esi = atmvcc->dev->esi; | |
837 | + if (esi[0] | esi[1] | esi[2] | esi[3] | esi[4] | esi[5]) | |
838 | + memcpy(brdev->net_dev.dev_addr, esi, | |
839 | + brdev->net_dev.addr_len); | |
840 | + else | |
841 | + brdev->net_dev.dev_addr[2] = 1; | |
842 | + } | |
843 | + list_add(&brvcc->brvccs, &brdev->brvccs); | |
844 | + write_unlock_irq(&devs_lock); | |
845 | + brvcc->brdev = brdev; | |
846 | + brvcc->atmvcc = atmvcc; | |
847 | + atmvcc->user_back = brvcc; | |
848 | + brvcc->encaps = (enum br2684_encaps) be.encaps; | |
849 | + brvcc->old_push = atmvcc->push; | |
850 | + barrier(); | |
851 | + atmvcc->push = br2684_push; | |
852 | + skb_queue_head_init(©); | |
853 | + skb_migrate(&atmvcc->recvq, ©); | |
854 | + while ((skb = skb_dequeue(©))) { | |
855 | + BRPRIV(skb->dev)->stats.rx_bytes -= skb->len; | |
856 | + BRPRIV(skb->dev)->stats.rx_packets--; | |
857 | + br2684_push(atmvcc, skb); | |
858 | + } | |
859 | + return 0; | |
860 | + error: | |
861 | + write_unlock_irq(&devs_lock); | |
862 | + MOD_DEC_USE_COUNT; | |
863 | + return err; | |
864 | +} | |
865 | + | |
866 | +static int br2684_create(unsigned long arg) | |
867 | +{ | |
868 | + int err; | |
869 | + struct br2684_dev *brdev; | |
870 | + struct atm_newif_br2684 ni; | |
871 | + | |
872 | + DPRINTK("br2684_create\n"); | |
873 | + /* | |
874 | + * We track module use by vcc's NOT the devices they're on. We're | |
875 | + * protected here against module death by the kernel_lock, but if | |
876 | + * we need to sleep we should make sure that the module doesn't | |
877 | + * disappear under us. | |
878 | + */ | |
879 | + MOD_INC_USE_COUNT; | |
880 | + if (copy_from_user(&ni, (void *) arg, sizeof ni)) { | |
881 | + MOD_DEC_USE_COUNT; | |
882 | + return -EFAULT; | |
883 | + } | |
884 | + if (ni.media != BR2684_MEDIA_ETHERNET || ni.mtu != 1500) { | |
885 | + MOD_DEC_USE_COUNT; | |
886 | + return -EINVAL; | |
887 | + } | |
888 | + if ((brdev = kmalloc(sizeof(struct br2684_dev), GFP_KERNEL)) == NULL) { | |
889 | + MOD_DEC_USE_COUNT; | |
890 | + return -ENOMEM; | |
891 | + } | |
892 | + memset(brdev, 0, sizeof(struct br2684_dev)); | |
893 | + INIT_LIST_HEAD(&brdev->brvccs); | |
894 | + | |
895 | + write_lock_irq(&devs_lock); | |
896 | + brdev->number = list_empty(&br2684_devs) ? 1 : | |
897 | + list_entry_brdev(br2684_devs.prev)->number + 1; | |
898 | + list_add_tail(&brdev->br2684_devs, &br2684_devs); | |
899 | + write_unlock_irq(&devs_lock); | |
900 | + | |
901 | + if (ni.ifname[0] != '\0') { | |
902 | + memcpy(brdev->net_dev.name, ni.ifname, | |
903 | + sizeof(brdev->net_dev.name)); | |
904 | + brdev->net_dev.name[sizeof(brdev->net_dev.name) - 1] = '\0'; | |
905 | + } else | |
906 | + sprintf(brdev->net_dev.name, "nas%d", brdev->number); | |
907 | + DPRINTK("registered netdev %s\n", brdev->net_dev.name); | |
908 | + ether_setup(&brdev->net_dev); | |
909 | + brdev->mac_was_set = 0; | |
910 | +#ifdef FASTER_VERSION | |
911 | + my_eth_header = brdev->net_dev.hard_header; | |
912 | + brdev->net_dev.hard_header = br2684_header; | |
913 | + my_eth_header_cache = brdev->net_dev.hard_header_cache; | |
914 | + brdev->net_dev.hard_header_cache = br2684_header_cache; | |
915 | + brdev->net_dev.hard_header_len = sizeof(llc_oui_pid_pad) + ETH_HLEN; /* 10 + 14 */ | |
916 | +#endif | |
917 | + my_eth_mac_addr = brdev->net_dev.set_mac_address; | |
918 | + brdev->net_dev.set_mac_address = br2684_mac_addr; | |
919 | + brdev->net_dev.hard_start_xmit = br2684_start_xmit; | |
920 | + brdev->net_dev.get_stats = br2684_get_stats; | |
921 | + | |
922 | + /* open, stop, do_ioctl ? */ | |
923 | + err = register_netdev(&brdev->net_dev); | |
924 | + MOD_DEC_USE_COUNT; | |
925 | + if (err < 0) { | |
926 | + printk(KERN_ERR "br2684_create: register_netdev failed\n"); | |
927 | + write_lock_irq(&devs_lock); | |
928 | + list_del(&brdev->br2684_devs); | |
929 | + write_unlock_irq(&devs_lock); | |
930 | + kfree(brdev); | |
931 | + return err; | |
932 | + } | |
933 | + return 0; | |
934 | +} | |
935 | + | |
936 | +/* | |
937 | + * This handles ioctls actually performed on our vcc - we must return | |
938 | + * -ENOIOCTLCMD for any unrecognized ioctl | |
939 | + */ | |
940 | +static int br2684_ioctl(struct atm_vcc *atmvcc, unsigned int cmd, | |
941 | + unsigned long arg) | |
942 | +{ | |
943 | + int err; | |
944 | + switch(cmd) { | |
945 | + case ATM_SETBACKEND: | |
946 | + case ATM_NEWBACKENDIF: { | |
947 | + atm_backend_t b; | |
948 | + MOD_INC_USE_COUNT; | |
949 | + err = get_user(b, (atm_backend_t *) arg); | |
950 | + MOD_DEC_USE_COUNT; | |
951 | + if (err) | |
952 | + return -EFAULT; | |
953 | + if (b != ATM_BACKEND_BR2684) | |
954 | + return -ENOIOCTLCMD; | |
955 | + if (!capable(CAP_NET_ADMIN)) | |
956 | + return -EPERM; | |
957 | + if (cmd == ATM_SETBACKEND) | |
958 | + return br2684_regvcc(atmvcc, arg); | |
959 | + else | |
960 | + return br2684_create(arg); | |
961 | + } | |
962 | +#ifdef CONFIG_ATM_BR2684_IPFILTER | |
963 | + case BR2684_SETFILT: | |
964 | + if (atmvcc->push != br2684_push) | |
965 | + return -ENOIOCTLCMD; | |
966 | + if (!capable(CAP_NET_ADMIN)) | |
967 | + return -EPERM; | |
968 | + MOD_INC_USE_COUNT; | |
969 | + err = br2684_setfilt(atmvcc, arg); | |
970 | + MOD_DEC_USE_COUNT; | |
971 | + return err; | |
972 | +#endif /* CONFIG_ATM_BR2684_IPFILTER */ | |
973 | + } | |
974 | + return -ENOIOCTLCMD; | |
975 | +} | |
976 | + | |
977 | +/* Never put more than 256 bytes in at once */ | |
978 | +static int br2684_proc_engine(loff_t pos, char *buf) | |
979 | +{ | |
980 | + struct list_head *lhd, *lhc; | |
981 | + struct br2684_dev *brdev; | |
982 | + struct br2684_vcc *brvcc; | |
983 | + list_for_each(lhd, &br2684_devs) { | |
984 | + brdev = list_entry_brdev(lhd); | |
985 | + if (pos-- == 0) | |
986 | + return sprintf(buf, "dev %.16s: num=%d, mac=%02X:%02X:" | |
987 | + "%02X:%02X:%02X:%02X (%s)\n", brdev->net_dev.name, | |
988 | + brdev->number, | |
989 | + brdev->net_dev.dev_addr[0], | |
990 | + brdev->net_dev.dev_addr[1], | |
991 | + brdev->net_dev.dev_addr[2], | |
992 | + brdev->net_dev.dev_addr[3], | |
993 | + brdev->net_dev.dev_addr[4], | |
994 | + brdev->net_dev.dev_addr[5], | |
995 | + brdev->mac_was_set ? "set" : "auto"); | |
996 | + list_for_each(lhc, &brdev->brvccs) { | |
997 | + brvcc = list_entry_brvcc(lhc); | |
998 | + if (pos-- == 0) | |
999 | + return sprintf(buf, " vcc %d.%d.%d: encaps=%s" | |
1000 | +#ifndef FASTER_VERSION | |
1001 | + ", failed copies %u/%u" | |
1002 | +#endif /* FASTER_VERSION */ | |
1003 | + "\n", brvcc->atmvcc->dev->number, | |
1004 | + brvcc->atmvcc->vpi, brvcc->atmvcc->vci, | |
1005 | + (brvcc->encaps == e_llc) ? "LLC" : "VC" | |
1006 | +#ifndef FASTER_VERSION | |
1007 | + , brvcc->copies_failed | |
1008 | + , brvcc->copies_needed | |
1009 | +#endif /* FASTER_VERSION */ | |
1010 | + ); | |
1011 | +#ifdef CONFIG_ATM_BR2684_IPFILTER | |
1012 | +#define b1(var, byte) ((u8 *) &brvcc->filter.var)[byte] | |
1013 | +#define bs(var) b1(var, 0), b1(var, 1), b1(var, 2), b1(var, 3) | |
1014 | + if (brvcc->filter.netmask != 0 && pos-- == 0) | |
1015 | + return sprintf(buf, " filter=%d.%d.%d.%d/" | |
1016 | + "%d.%d.%d.%d\n", bs(prefix), bs(netmask)); | |
1017 | +#undef bs | |
1018 | +#undef b1 | |
1019 | +#endif /* CONFIG_ATM_BR2684_IPFILTER */ | |
1020 | + } | |
1021 | + } | |
1022 | + return 0; | |
1023 | +} | |
1024 | + | |
1025 | +static ssize_t br2684_proc_read(struct file *file, char *buf, size_t count, | |
1026 | + loff_t *pos) | |
1027 | +{ | |
1028 | + unsigned long page; | |
1029 | + int len = 0, x, left; | |
1030 | + page = get_free_page(GFP_KERNEL); | |
1031 | + if (!page) | |
1032 | + return -ENOMEM; | |
1033 | + left = PAGE_SIZE - 256; | |
1034 | + if (count < left) | |
1035 | + left = count; | |
1036 | + read_lock(&devs_lock); | |
1037 | + for (;;) { | |
1038 | + x = br2684_proc_engine(*pos, &((char *) page)[len]); | |
1039 | + if (x == 0) | |
1040 | + break; | |
1041 | + if (x > left) | |
1042 | + /* | |
1043 | + * This should only happen if the user passed in | |
1044 | + * a "count" too small for even one line | |
1045 | + */ | |
1046 | + x = -EINVAL; | |
1047 | + if (x < 0) { | |
1048 | + len = x; | |
1049 | + break; | |
1050 | + } | |
1051 | + len += x; | |
1052 | + left -= x; | |
1053 | + (*pos)++; | |
1054 | + if (left < 256) | |
1055 | + break; | |
1056 | + } | |
1057 | + read_unlock(&devs_lock); | |
1058 | + if (len > 0 && copy_to_user(buf, (char *) page, len)) | |
1059 | + len = -EFAULT; | |
1060 | + free_page(page); | |
1061 | + return len; | |
1062 | +} | |
1063 | + | |
1064 | +static struct file_operations br2684_proc_operations = { | |
1065 | + read: br2684_proc_read, | |
1066 | +}; | |
1067 | + | |
1068 | +extern struct proc_dir_entry *atm_proc_root; /* from proc.c */ | |
1069 | + | |
1070 | +extern int (*br2684_ioctl_hook)(struct atm_vcc *, unsigned int, unsigned long); | |
1071 | + | |
1072 | +/* the following avoids some spurious warnings from the compiler */ | |
1073 | +#define UNUSED __attribute__((unused)) | |
1074 | + | |
1075 | +static int __init UNUSED br2684_init(void) | |
1076 | +{ | |
1077 | + struct proc_dir_entry *p; | |
1078 | + if ((p = create_proc_entry("br2684", 0, atm_proc_root)) == NULL) | |
1079 | + return -ENOMEM; | |
1080 | + p->proc_fops = &br2684_proc_operations; | |
1081 | + br2684_ioctl_hook = br2684_ioctl; | |
1082 | + return 0; | |
1083 | +} | |
1084 | + | |
1085 | +static void __exit UNUSED br2684_exit(void) | |
1086 | +{ | |
1087 | + struct br2684_dev *brdev; | |
1088 | + br2684_ioctl_hook = NULL; | |
1089 | + remove_proc_entry("br2684", atm_proc_root); | |
1090 | + while (!list_empty(&br2684_devs)) { | |
1091 | + brdev = list_entry_brdev(br2684_devs.next); | |
1092 | + unregister_netdev(&brdev->net_dev); | |
1093 | + list_del(&brdev->br2684_devs); | |
1094 | + kfree(brdev); | |
1095 | + } | |
1096 | +} | |
1097 | + | |
1098 | +module_init(br2684_init); | |
1099 | +module_exit(br2684_exit); | |
1100 | + | |
1101 | +MODULE_AUTHOR("Marcell GAL"); | |
1102 | +MODULE_DESCRIPTION("RFC2684 bridged protocols over ATM/AAL5"); | |
1103 | diff -urN linux.orig/net/atm/common.c linux/net/atm/common.c | |
1104 | --- linux.orig/net/atm/common.c Sat Apr 21 18:04:58 2001 | |
1105 | +++ linux/net/atm/common.c Sat Apr 21 18:06:09 2001 | |
1106 | @@ -58,6 +58,18 @@ | |
1107 | #endif | |
1108 | #endif | |
1109 | ||
1110 | +#if defined(CONFIG_PPPOATM) || defined(CONFIG_PPPOATM_MODULE) | |
1111 | +int (*pppoatm_ioctl_hook)(struct atm_vcc *, unsigned int, unsigned long); | |
1112 | +EXPORT_SYMBOL(pppoatm_ioctl_hook); | |
1113 | +#endif | |
1114 | + | |
1115 | +#if defined(CONFIG_ATM_BR2684) || defined(CONFIG_ATM_BR2684_MODULE) | |
1116 | +int (*br2684_ioctl_hook)(struct atm_vcc *, unsigned int, unsigned long); | |
1117 | +#endif | |
1118 | +#ifdef CONFIG_ATM_BR2684_MODULE | |
1119 | +EXPORT_SYMBOL(br2684_ioctl_hook); | |
1120 | +#endif | |
1121 | + | |
1122 | #include "resources.h" /* atm_find_dev */ | |
1123 | #include "common.h" /* prototypes */ | |
1124 | #include "protocols.h" /* atm_init_<transport> */ | |
1125 | @@ -774,6 +786,20 @@ | |
1126 | default: | |
1127 | break; | |
1128 | } | |
1129 | +#if defined(CONFIG_PPPOATM) || defined(CONFIG_PPPOATM_MODULE) | |
1130 | + if (pppoatm_ioctl_hook && | |
1131 | + (error = pppoatm_ioctl_hook(vcc, cmd, arg)) != -ENOIOCTLCMD) { | |
1132 | + spin_unlock (&atm_dev_lock); | |
1133 | + return error; | |
1134 | + } | |
1135 | +#endif | |
1136 | +#if defined(CONFIG_ATM_BR2684) || defined(CONFIG_ATM_BR2684_MODULE) | |
1137 | + if (br2684_ioctl_hook && | |
1138 | + (error = br2684_ioctl_hook(vcc, cmd, arg)) != -ENOIOCTLCMD) { | |
1139 | + spin_unlock (&atm_dev_lock); | |
1140 | + return error; | |
1141 | + } | |
1142 | +#endif | |
1143 | if (get_user(buf,&((struct atmif_sioc *) arg)->arg)) { | |
1144 | ret_val = -EFAULT; | |
1145 | goto done; | |
1146 | diff -urN linux.orig/net/atm/pppoatm.c linux/net/atm/pppoatm.c | |
1147 | --- linux.orig/net/atm/pppoatm.c Thu Jan 1 01:00:00 1970 | |
1148 | +++ linux/net/atm/pppoatm.c Sat Apr 21 18:06:09 2001 | |
1149 | @@ -0,0 +1,373 @@ | |
1150 | +/* net/atm/pppoatm.c - RFC2364 PPP over ATM/AAL5 */ | |
1151 | + | |
1152 | +/* Copyright 1999-2000 by Mitchell Blank Jr */ | |
1153 | +/* Based on clip.c; 1995-1999 by Werner Almesberger, EPFL LRC/ICA */ | |
1154 | +/* And on ppp_async.c; Copyright 1999 Paul Mackerras */ | |
1155 | +/* And help from Jens Axboe */ | |
1156 | + | |
1157 | +/* | |
1158 | + * This program is free software; you can redistribute it and/or | |
1159 | + * modify it under the terms of the GNU General Public License | |
1160 | + * as published by the Free Software Foundation; either version | |
1161 | + * 2 of the License, or (at your option) any later version. | |
1162 | + * | |
1163 | + * This driver provides the encapsulation and framing for sending | |
1164 | + * and receiving PPP frames in ATM AAL5 PDUs. | |
1165 | + */ | |
1166 | + | |
1167 | +/* | |
1168 | + * One shortcoming of this driver is that it does not comply with | |
1169 | + * section 8 of RFC2364 - we are supposed to detect a change | |
1170 | + * in encapsulation and immediately abort the connection (in order | |
1171 | + * to avoid a black-hole being created if our peer loses state | |
1172 | + * and changes encapsulation unilaterally. However, since the | |
1173 | + * ppp_generic layer actually does the decapsulation, we need | |
1174 | + * a way of notifying it when we _think_ there might be a problem) | |
1175 | + * There's two cases: | |
1176 | + * 1. LLC-encapsulation was missing when it was enabled. In | |
1177 | + * this case, we should tell the upper layer "tear down | |
1178 | + * this session if this skb looks ok to you" | |
1179 | + * 2. LLC-encapsulation was present when it was disabled. Then | |
1180 | + * we need to tell the upper layer "this packet may be | |
1181 | + * ok, but if its in error tear down the session" | |
1182 | + * These hooks are not yet available in ppp_generic | |
1183 | + */ | |
1184 | + | |
1185 | +#include <linux/module.h> | |
1186 | +#include <linux/config.h> | |
1187 | +#include <linux/init.h> | |
1188 | +#include <linux/skbuff.h> | |
1189 | +#include <linux/atm.h> | |
1190 | +#include <linux/atmdev.h> | |
1191 | +#include <linux/ppp_defs.h> | |
1192 | +#include <linux/if_ppp.h> | |
1193 | +#include <linux/ppp_channel.h> | |
1194 | +#include <linux/atmppp.h> | |
1195 | + | |
1196 | +#if 0 | |
1197 | +#define DPRINTK(format, args...) \ | |
1198 | + printk(KERN_DEBUG "pppoatm: " format, ##args) | |
1199 | +#else | |
1200 | +#define DPRINTK(format, args...) | |
1201 | +#endif | |
1202 | + | |
1203 | +enum pppoatm_encaps { | |
1204 | + e_autodetect = PPPOATM_ENCAPS_AUTODETECT, | |
1205 | + e_vc = PPPOATM_ENCAPS_VC, | |
1206 | + e_llc = PPPOATM_ENCAPS_LLC, | |
1207 | +}; | |
1208 | + | |
1209 | +struct pppoatm_vcc { | |
1210 | + struct atm_vcc *atmvcc; /* VCC descriptor */ | |
1211 | + void (*old_push)(struct atm_vcc *, struct sk_buff *); | |
1212 | + void (*old_pop)(struct atm_vcc *, struct sk_buff *); | |
1213 | + /* keep old push/pop for detaching */ | |
1214 | + enum pppoatm_encaps encaps; | |
1215 | + int flags; /* SC_COMP_PROT - compress protocol */ | |
1216 | + struct ppp_channel chan; /* interface to generic ppp layer */ | |
1217 | + struct tasklet_struct wakeup_tasklet; | |
1218 | +}; | |
1219 | + | |
1220 | +/* | |
1221 | + * Header used for LLC Encapsulated PPP (4 bytes) followed by the LCP protocol | |
1222 | + * ID (0xC021) used in autodetection | |
1223 | + */ | |
1224 | +static const unsigned char pppllc[6] = { 0xFE, 0xFE, 0x03, 0xCF, 0xC0, 0x21 }; | |
1225 | +#define LLC_LEN (4) | |
1226 | + | |
1227 | +static inline struct pppoatm_vcc *atmvcc_to_pvcc(const struct atm_vcc *atmvcc) | |
1228 | +{ | |
1229 | + return (struct pppoatm_vcc *) (atmvcc->user_back); | |
1230 | +} | |
1231 | + | |
1232 | +static inline struct pppoatm_vcc *chan_to_pvcc(const struct ppp_channel *chan) | |
1233 | +{ | |
1234 | + return (struct pppoatm_vcc *) (chan->private); | |
1235 | +} | |
1236 | + | |
1237 | +/* | |
1238 | + * We can't do this directly from our _pop handler, since the ppp code | |
1239 | + * doesn't want to be called in interrupt context, so we do it from | |
1240 | + * a tasklet | |
1241 | + */ | |
1242 | +static void pppoatm_wakeup_sender(unsigned long arg) | |
1243 | +{ | |
1244 | + ppp_output_wakeup((struct ppp_channel *) arg); | |
1245 | +} | |
1246 | + | |
1247 | +/* | |
1248 | + * This gets called every time the ATM card has finished sending our | |
1249 | + * skb. The ->old_pop will take care up normal atm flow control, | |
1250 | + * but we also need to wake up the device if we blocked it | |
1251 | + */ | |
1252 | +static void pppoatm_pop(struct atm_vcc *atmvcc, struct sk_buff *skb) | |
1253 | +{ | |
1254 | + struct pppoatm_vcc *pvcc = atmvcc_to_pvcc(atmvcc); | |
1255 | + pvcc->old_pop(atmvcc, skb); | |
1256 | + /* | |
1257 | + * We don't really always want to do this since it's | |
1258 | + * really inefficient - it would be much better if we could | |
1259 | + * test if we had actually throttled the generic layer. | |
1260 | + * Unfortunately then there would be a nasty SMP race where | |
1261 | + * we could clear that flag just as we refuse another packet. | |
1262 | + * For now we do the safe thing. | |
1263 | + */ | |
1264 | + tasklet_schedule(&pvcc->wakeup_tasklet); | |
1265 | +} | |
1266 | + | |
1267 | +/* | |
1268 | + * Unbind from PPP - currently we only do this when closing the socket, | |
1269 | + * but we could put this into an ioctl if need be | |
1270 | + */ | |
1271 | +static void pppoatm_unassign_vcc(struct atm_vcc *atmvcc) | |
1272 | +{ | |
1273 | + struct pppoatm_vcc *pvcc; | |
1274 | + pvcc = atmvcc_to_pvcc(atmvcc); | |
1275 | + atmvcc->push = pvcc->old_push; | |
1276 | + atmvcc->pop = pvcc->old_pop; | |
1277 | + tasklet_disable(&pvcc->wakeup_tasklet); | |
1278 | + ppp_unregister_channel(&pvcc->chan); | |
1279 | + atmvcc->user_back = NULL; | |
1280 | + kfree(pvcc); | |
1281 | + /* Gee, I hope we have the big kernel lock here... */ | |
1282 | + MOD_DEC_USE_COUNT; | |
1283 | +} | |
1284 | + | |
1285 | +/* Called when an AAL5 PDU comes in */ | |
1286 | +static void pppoatm_push(struct atm_vcc *atmvcc, struct sk_buff *skb) | |
1287 | +{ | |
1288 | + struct pppoatm_vcc *pvcc = atmvcc_to_pvcc(atmvcc); | |
1289 | + DPRINTK("pppoatm push\n"); | |
1290 | + if (skb == NULL) { /* VCC was closed */ | |
1291 | + DPRINTK("removing ATMPPP VCC %p\n", pvcc); | |
1292 | + pppoatm_unassign_vcc(atmvcc); | |
1293 | + atmvcc->push(atmvcc, NULL); /* Pass along bad news */ | |
1294 | + return; | |
1295 | + } | |
1296 | + atm_return(atmvcc, skb->truesize); | |
1297 | + switch (pvcc->encaps) { | |
1298 | + case e_llc: | |
1299 | + if (skb->len < LLC_LEN || | |
1300 | + memcmp(skb->data, pppllc, LLC_LEN)) | |
1301 | + goto error; | |
1302 | + skb_pull(skb, LLC_LEN); | |
1303 | + break; | |
1304 | + case e_autodetect: | |
1305 | + if (pvcc->chan.ppp == NULL) { /* Not bound yet! */ | |
1306 | + kfree_skb(skb); | |
1307 | + return; | |
1308 | + } | |
1309 | + if (skb->len >= sizeof(pppllc) && | |
1310 | + !memcmp(skb->data, pppllc, sizeof(pppllc))) { | |
1311 | + pvcc->encaps = e_llc; | |
1312 | + skb_pull(skb, LLC_LEN); | |
1313 | + break; | |
1314 | + } | |
1315 | + if (skb->len >= (sizeof(pppllc) - LLC_LEN) && | |
1316 | + !memcmp(skb->data, &pppllc[LLC_LEN], | |
1317 | + sizeof(pppllc) - LLC_LEN)) { | |
1318 | + pvcc->encaps = e_vc; | |
1319 | + pvcc->chan.mtu += LLC_LEN; | |
1320 | + break; | |
1321 | + } | |
1322 | + DPRINTK("(unit %d): Couldn't autodetect yet " | |
1323 | + "(skb: %02X %02X %02X %02X %02X %02X)\n", | |
1324 | + pvcc->chan.unit, | |
1325 | + skb->data[0], skb->data[1], skb->data[2], | |
1326 | + skb->data[3], skb->data[4], skb->data[5]); | |
1327 | + goto error; | |
1328 | + case e_vc: | |
1329 | + break; | |
1330 | + } | |
1331 | + ppp_input(&pvcc->chan, skb); | |
1332 | + return; | |
1333 | + error: | |
1334 | + kfree_skb(skb); | |
1335 | + ppp_input_error(&pvcc->chan, 0); | |
1336 | +} | |
1337 | + | |
1338 | +/* | |
1339 | + * Called by the ppp_generic.c to send a packet - returns true if packet | |
1340 | + * was accepted. If we return false, then it's our job to call | |
1341 | + * ppp_output_wakeup(chan) when we're feeling more up to it. | |
1342 | + * Note that in the ENOMEM case (as opposed to the !atm_may_send case) | |
1343 | + * we should really drop the packet, but the generic layer doesn't | |
1344 | + * support this yet. We just return 'DROP_PACKET' which we actually define | |
1345 | + * as success, just to be clear what we're really doing. | |
1346 | + */ | |
1347 | +#define DROP_PACKET 1 | |
1348 | +static int pppoatm_send(struct ppp_channel *chan, struct sk_buff *skb) | |
1349 | +{ | |
1350 | + struct pppoatm_vcc *pvcc = chan_to_pvcc(chan); | |
1351 | + ATM_SKB(skb)->vcc = pvcc->atmvcc; | |
1352 | + DPRINTK("(unit %d): pppoatm_send (skb=0x%p, vcc=0x%p)\n", | |
1353 | + pvcc->chan.unit, skb, pvcc->atmvcc); | |
1354 | + if (skb->data[0] == '\0' && (pvcc->flags & SC_COMP_PROT)) | |
1355 | + (void) skb_pull(skb, 1); | |
1356 | + switch (pvcc->encaps) { /* LLC encapsulation needed */ | |
1357 | + case e_llc: | |
1358 | + if (skb_headroom(skb) < LLC_LEN) { | |
1359 | + struct sk_buff *n; | |
1360 | + n = skb_realloc_headroom(skb, LLC_LEN); | |
1361 | + if (n != NULL && | |
1362 | + !atm_may_send(pvcc->atmvcc, n->truesize)) { | |
1363 | + kfree_skb(n); | |
1364 | + goto nospace; | |
1365 | + } | |
1366 | + kfree_skb(skb); | |
1367 | + if ((skb = n) == NULL) | |
1368 | + return DROP_PACKET; | |
1369 | + } else if (!atm_may_send(pvcc->atmvcc, skb->truesize)) | |
1370 | + goto nospace; | |
1371 | + memcpy(skb_push(skb, LLC_LEN), pppllc, LLC_LEN); | |
1372 | + break; | |
1373 | + case e_vc: | |
1374 | + if (!atm_may_send(pvcc->atmvcc, skb->truesize)) | |
1375 | + goto nospace; | |
1376 | + break; | |
1377 | + case e_autodetect: | |
1378 | + DPRINTK("(unit %d): Trying to send without setting encaps!\n", | |
1379 | + pvcc->chan.unit); | |
1380 | + kfree_skb(skb); | |
1381 | + return 1; | |
1382 | + } | |
1383 | + atomic_add(skb->truesize, &ATM_SKB(skb)->vcc->tx_inuse); | |
1384 | + ATM_SKB(skb)->iovcnt = 0; | |
1385 | + ATM_SKB(skb)->atm_options = ATM_SKB(skb)->vcc->atm_options; | |
1386 | + DPRINTK("(unit %d): atm_skb(%p)->vcc(%p)->dev(%p)\n", | |
1387 | + pvcc->chan.unit, skb, ATM_SKB(skb)->vcc, | |
1388 | + ATM_SKB(skb)->vcc->dev); | |
1389 | + return ATM_SKB(skb)->vcc->send(ATM_SKB(skb)->vcc, skb) | |
1390 | + ? DROP_PACKET : 1; | |
1391 | + nospace: | |
1392 | + /* | |
1393 | + * We don't have space to send this SKB now, but we might have | |
1394 | + * already applied SC_COMP_PROT compression, so may need to undo | |
1395 | + */ | |
1396 | + if ((pvcc->flags & SC_COMP_PROT) && skb_headroom(skb) > 0 && | |
1397 | + skb->data[-1] == '\0') | |
1398 | + (void) skb_push(skb, 1); | |
1399 | + return 0; | |
1400 | +} | |
1401 | + | |
1402 | +/* This handles ioctls sent to the /dev/ppp interface */ | |
1403 | +static int pppoatm_devppp_ioctl(struct ppp_channel *chan, unsigned int cmd, | |
1404 | + unsigned long arg) | |
1405 | +{ | |
1406 | + switch (cmd) { | |
1407 | + case PPPIOCGFLAGS: | |
1408 | + return put_user(chan_to_pvcc(chan)->flags, (int *) arg) | |
1409 | + ? -EFAULT : 0; | |
1410 | + case PPPIOCSFLAGS: | |
1411 | + return get_user(chan_to_pvcc(chan)->flags, (int *) arg) | |
1412 | + ? -EFAULT : 0; | |
1413 | + } | |
1414 | + return -ENOTTY; | |
1415 | +} | |
1416 | + | |
1417 | +static /*const*/ struct ppp_channel_ops pppoatm_ops = { | |
1418 | + start_xmit: pppoatm_send, | |
1419 | + ioctl: pppoatm_devppp_ioctl, | |
1420 | +}; | |
1421 | + | |
1422 | +static int pppoatm_assign_vcc(struct atm_vcc *atmvcc, unsigned long arg) | |
1423 | +{ | |
1424 | + struct atm_backend_ppp be; | |
1425 | + struct pppoatm_vcc *pvcc; | |
1426 | + int err; | |
1427 | + /* | |
1428 | + * Each PPPoATM instance has its own tasklet - this is just a | |
1429 | + * prototypical one used to initialize them | |
1430 | + */ | |
1431 | + static const DECLARE_TASKLET(tasklet_proto, pppoatm_wakeup_sender, 0); | |
1432 | + if (copy_from_user(&be, (void *) arg, sizeof be)) | |
1433 | + return -EFAULT; | |
1434 | + if (be.encaps != PPPOATM_ENCAPS_AUTODETECT && | |
1435 | + be.encaps != PPPOATM_ENCAPS_VC && be.encaps != PPPOATM_ENCAPS_LLC) | |
1436 | + return -EINVAL; | |
1437 | + MOD_INC_USE_COUNT; | |
1438 | + pvcc = kmalloc(sizeof(*pvcc), GFP_KERNEL); | |
1439 | + if (pvcc == NULL) { | |
1440 | + MOD_DEC_USE_COUNT; | |
1441 | + return -ENOMEM; | |
1442 | + } | |
1443 | + memset(pvcc, 0, sizeof(*pvcc)); | |
1444 | + pvcc->atmvcc = atmvcc; | |
1445 | + pvcc->old_push = atmvcc->push; | |
1446 | + pvcc->old_pop = atmvcc->pop; | |
1447 | + pvcc->encaps = (enum pppoatm_encaps) be.encaps; | |
1448 | + pvcc->chan.private = pvcc; | |
1449 | + pvcc->chan.ops = &pppoatm_ops; | |
1450 | + pvcc->chan.mtu = atmvcc->qos.txtp.max_sdu - PPP_HDRLEN - | |
1451 | + (be.encaps == e_vc ? 0 : LLC_LEN); | |
1452 | + pvcc->wakeup_tasklet = tasklet_proto; | |
1453 | + pvcc->wakeup_tasklet.data = (unsigned long) &pvcc->chan; | |
1454 | + if ((err = ppp_register_channel(&pvcc->chan)) != 0) { | |
1455 | + kfree(pvcc); | |
1456 | + return err; | |
1457 | + } | |
1458 | + atmvcc->user_back = pvcc; | |
1459 | + atmvcc->push = pppoatm_push; | |
1460 | + atmvcc->pop = pppoatm_pop; | |
1461 | + return 0; | |
1462 | +} | |
1463 | + | |
1464 | +/* | |
1465 | + * This handles ioctls actually performed on our vcc - we must return | |
1466 | + * -ENOIOCTLCMD for any unrecognized ioctl | |
1467 | + */ | |
1468 | +static int pppoatm_ioctl(struct atm_vcc *atmvcc, unsigned int cmd, | |
1469 | + unsigned long arg) | |
1470 | +{ | |
1471 | + int err; | |
1472 | + if (cmd != ATM_SETBACKEND && atmvcc->push != pppoatm_push) | |
1473 | + return -ENOIOCTLCMD; | |
1474 | + switch (cmd) { | |
1475 | + case ATM_SETBACKEND: { | |
1476 | + atm_backend_t b; | |
1477 | + MOD_INC_USE_COUNT; | |
1478 | + err = get_user(b, (atm_backend_t *) arg); | |
1479 | + MOD_DEC_USE_COUNT; | |
1480 | + if (err) | |
1481 | + return -EFAULT; | |
1482 | + if (b != ATM_BACKEND_PPP) | |
1483 | + return -ENOIOCTLCMD; | |
1484 | + if (!capable(CAP_NET_ADMIN)) | |
1485 | + return -EPERM; | |
1486 | + return pppoatm_assign_vcc(atmvcc, arg); | |
1487 | + } | |
1488 | + case PPPIOCGCHAN: | |
1489 | + case PPPIOCGUNIT: | |
1490 | + MOD_INC_USE_COUNT; | |
1491 | + err = put_user((cmd == PPPIOCGCHAN) ? | |
1492 | + ppp_channel_index(&atmvcc_to_pvcc(atmvcc)->chan) : | |
1493 | + ppp_unit_number(&atmvcc_to_pvcc(atmvcc)->chan), | |
1494 | + (int *) arg); | |
1495 | + MOD_DEC_USE_COUNT; | |
1496 | + return err ? -EFAULT : 0; | |
1497 | + } | |
1498 | + return -ENOIOCTLCMD; | |
1499 | +} | |
1500 | + | |
1501 | +/* the following avoids some spurious warnings from the compiler */ | |
1502 | +#define UNUSED __attribute__((unused)) | |
1503 | + | |
1504 | +extern int (*pppoatm_ioctl_hook)(struct atm_vcc *, | |
1505 | + unsigned int, unsigned long); | |
1506 | + | |
1507 | +static int __init UNUSED pppoatm_init(void) | |
1508 | +{ | |
1509 | + pppoatm_ioctl_hook = pppoatm_ioctl; | |
1510 | + return 0; | |
1511 | +} | |
1512 | + | |
1513 | +static void __exit UNUSED pppoatm_exit(void) | |
1514 | +{ | |
1515 | + pppoatm_ioctl_hook = NULL; | |
1516 | +} | |
1517 | + | |
1518 | +module_init(pppoatm_init); | |
1519 | +module_exit(pppoatm_exit); | |
1520 | + | |
1521 | +MODULE_AUTHOR("Mitchell Blank Jr <mitch@sfgoth.com>"); | |
1522 | +MODULE_DESCRIPTION("RFC2364 PPP over ATM/AAL5"); |