1*4882a593Smuzhiyun /*
2*4882a593Smuzhiyun BNEP implementation for Linux Bluetooth stack (BlueZ).
3*4882a593Smuzhiyun Copyright (C) 2001-2002 Inventel Systemes
4*4882a593Smuzhiyun Written 2001-2002 by
5*4882a593Smuzhiyun Clément Moreau <clement.moreau@inventel.fr>
6*4882a593Smuzhiyun David Libault <david.libault@inventel.fr>
7*4882a593Smuzhiyun
8*4882a593Smuzhiyun Copyright (C) 2002 Maxim Krasnyansky <maxk@qualcomm.com>
9*4882a593Smuzhiyun
10*4882a593Smuzhiyun This program is free software; you can redistribute it and/or modify
11*4882a593Smuzhiyun it under the terms of the GNU General Public License version 2 as
12*4882a593Smuzhiyun published by the Free Software Foundation;
13*4882a593Smuzhiyun
14*4882a593Smuzhiyun THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
15*4882a593Smuzhiyun OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16*4882a593Smuzhiyun FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS.
17*4882a593Smuzhiyun IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) AND AUTHOR(S) BE LIABLE FOR ANY
18*4882a593Smuzhiyun CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES
19*4882a593Smuzhiyun WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
20*4882a593Smuzhiyun ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
21*4882a593Smuzhiyun OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
22*4882a593Smuzhiyun
23*4882a593Smuzhiyun ALL LIABILITY, INCLUDING LIABILITY FOR INFRINGEMENT OF ANY PATENTS,
24*4882a593Smuzhiyun COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS, RELATING TO USE OF THIS
25*4882a593Smuzhiyun SOFTWARE IS DISCLAIMED.
26*4882a593Smuzhiyun */
27*4882a593Smuzhiyun
28*4882a593Smuzhiyun #include <linux/module.h>
29*4882a593Smuzhiyun #include <linux/kthread.h>
30*4882a593Smuzhiyun #include <linux/file.h>
31*4882a593Smuzhiyun #include <linux/etherdevice.h>
32*4882a593Smuzhiyun #include <asm/unaligned.h>
33*4882a593Smuzhiyun
34*4882a593Smuzhiyun #include <net/bluetooth/bluetooth.h>
35*4882a593Smuzhiyun #include <net/bluetooth/l2cap.h>
36*4882a593Smuzhiyun #include <net/bluetooth/hci_core.h>
37*4882a593Smuzhiyun
38*4882a593Smuzhiyun #include "bnep.h"
39*4882a593Smuzhiyun
40*4882a593Smuzhiyun #define VERSION "1.3"
41*4882a593Smuzhiyun
42*4882a593Smuzhiyun static bool compress_src = true;
43*4882a593Smuzhiyun static bool compress_dst = true;
44*4882a593Smuzhiyun
45*4882a593Smuzhiyun static LIST_HEAD(bnep_session_list);
46*4882a593Smuzhiyun static DECLARE_RWSEM(bnep_session_sem);
47*4882a593Smuzhiyun
__bnep_get_session(u8 * dst)48*4882a593Smuzhiyun static struct bnep_session *__bnep_get_session(u8 *dst)
49*4882a593Smuzhiyun {
50*4882a593Smuzhiyun struct bnep_session *s;
51*4882a593Smuzhiyun
52*4882a593Smuzhiyun BT_DBG("");
53*4882a593Smuzhiyun
54*4882a593Smuzhiyun list_for_each_entry(s, &bnep_session_list, list)
55*4882a593Smuzhiyun if (ether_addr_equal(dst, s->eh.h_source))
56*4882a593Smuzhiyun return s;
57*4882a593Smuzhiyun
58*4882a593Smuzhiyun return NULL;
59*4882a593Smuzhiyun }
60*4882a593Smuzhiyun
__bnep_link_session(struct bnep_session * s)61*4882a593Smuzhiyun static void __bnep_link_session(struct bnep_session *s)
62*4882a593Smuzhiyun {
63*4882a593Smuzhiyun list_add(&s->list, &bnep_session_list);
64*4882a593Smuzhiyun }
65*4882a593Smuzhiyun
__bnep_unlink_session(struct bnep_session * s)66*4882a593Smuzhiyun static void __bnep_unlink_session(struct bnep_session *s)
67*4882a593Smuzhiyun {
68*4882a593Smuzhiyun list_del(&s->list);
69*4882a593Smuzhiyun }
70*4882a593Smuzhiyun
bnep_send(struct bnep_session * s,void * data,size_t len)71*4882a593Smuzhiyun static int bnep_send(struct bnep_session *s, void *data, size_t len)
72*4882a593Smuzhiyun {
73*4882a593Smuzhiyun struct socket *sock = s->sock;
74*4882a593Smuzhiyun struct kvec iv = { data, len };
75*4882a593Smuzhiyun
76*4882a593Smuzhiyun return kernel_sendmsg(sock, &s->msg, &iv, 1, len);
77*4882a593Smuzhiyun }
78*4882a593Smuzhiyun
bnep_send_rsp(struct bnep_session * s,u8 ctrl,u16 resp)79*4882a593Smuzhiyun static int bnep_send_rsp(struct bnep_session *s, u8 ctrl, u16 resp)
80*4882a593Smuzhiyun {
81*4882a593Smuzhiyun struct bnep_control_rsp rsp;
82*4882a593Smuzhiyun rsp.type = BNEP_CONTROL;
83*4882a593Smuzhiyun rsp.ctrl = ctrl;
84*4882a593Smuzhiyun rsp.resp = htons(resp);
85*4882a593Smuzhiyun return bnep_send(s, &rsp, sizeof(rsp));
86*4882a593Smuzhiyun }
87*4882a593Smuzhiyun
88*4882a593Smuzhiyun #ifdef CONFIG_BT_BNEP_PROTO_FILTER
bnep_set_default_proto_filter(struct bnep_session * s)89*4882a593Smuzhiyun static inline void bnep_set_default_proto_filter(struct bnep_session *s)
90*4882a593Smuzhiyun {
91*4882a593Smuzhiyun /* (IPv4, ARP) */
92*4882a593Smuzhiyun s->proto_filter[0].start = ETH_P_IP;
93*4882a593Smuzhiyun s->proto_filter[0].end = ETH_P_ARP;
94*4882a593Smuzhiyun /* (RARP, AppleTalk) */
95*4882a593Smuzhiyun s->proto_filter[1].start = ETH_P_RARP;
96*4882a593Smuzhiyun s->proto_filter[1].end = ETH_P_AARP;
97*4882a593Smuzhiyun /* (IPX, IPv6) */
98*4882a593Smuzhiyun s->proto_filter[2].start = ETH_P_IPX;
99*4882a593Smuzhiyun s->proto_filter[2].end = ETH_P_IPV6;
100*4882a593Smuzhiyun }
101*4882a593Smuzhiyun #endif
102*4882a593Smuzhiyun
bnep_ctrl_set_netfilter(struct bnep_session * s,__be16 * data,int len)103*4882a593Smuzhiyun static int bnep_ctrl_set_netfilter(struct bnep_session *s, __be16 *data, int len)
104*4882a593Smuzhiyun {
105*4882a593Smuzhiyun int n;
106*4882a593Smuzhiyun
107*4882a593Smuzhiyun if (len < 2)
108*4882a593Smuzhiyun return -EILSEQ;
109*4882a593Smuzhiyun
110*4882a593Smuzhiyun n = get_unaligned_be16(data);
111*4882a593Smuzhiyun data++;
112*4882a593Smuzhiyun len -= 2;
113*4882a593Smuzhiyun
114*4882a593Smuzhiyun if (len < n)
115*4882a593Smuzhiyun return -EILSEQ;
116*4882a593Smuzhiyun
117*4882a593Smuzhiyun BT_DBG("filter len %d", n);
118*4882a593Smuzhiyun
119*4882a593Smuzhiyun #ifdef CONFIG_BT_BNEP_PROTO_FILTER
120*4882a593Smuzhiyun n /= 4;
121*4882a593Smuzhiyun if (n <= BNEP_MAX_PROTO_FILTERS) {
122*4882a593Smuzhiyun struct bnep_proto_filter *f = s->proto_filter;
123*4882a593Smuzhiyun int i;
124*4882a593Smuzhiyun
125*4882a593Smuzhiyun for (i = 0; i < n; i++) {
126*4882a593Smuzhiyun f[i].start = get_unaligned_be16(data++);
127*4882a593Smuzhiyun f[i].end = get_unaligned_be16(data++);
128*4882a593Smuzhiyun
129*4882a593Smuzhiyun BT_DBG("proto filter start %d end %d",
130*4882a593Smuzhiyun f[i].start, f[i].end);
131*4882a593Smuzhiyun }
132*4882a593Smuzhiyun
133*4882a593Smuzhiyun if (i < BNEP_MAX_PROTO_FILTERS)
134*4882a593Smuzhiyun memset(f + i, 0, sizeof(*f));
135*4882a593Smuzhiyun
136*4882a593Smuzhiyun if (n == 0)
137*4882a593Smuzhiyun bnep_set_default_proto_filter(s);
138*4882a593Smuzhiyun
139*4882a593Smuzhiyun bnep_send_rsp(s, BNEP_FILTER_NET_TYPE_RSP, BNEP_SUCCESS);
140*4882a593Smuzhiyun } else {
141*4882a593Smuzhiyun bnep_send_rsp(s, BNEP_FILTER_NET_TYPE_RSP, BNEP_FILTER_LIMIT_REACHED);
142*4882a593Smuzhiyun }
143*4882a593Smuzhiyun #else
144*4882a593Smuzhiyun bnep_send_rsp(s, BNEP_FILTER_NET_TYPE_RSP, BNEP_FILTER_UNSUPPORTED_REQ);
145*4882a593Smuzhiyun #endif
146*4882a593Smuzhiyun return 0;
147*4882a593Smuzhiyun }
148*4882a593Smuzhiyun
bnep_ctrl_set_mcfilter(struct bnep_session * s,u8 * data,int len)149*4882a593Smuzhiyun static int bnep_ctrl_set_mcfilter(struct bnep_session *s, u8 *data, int len)
150*4882a593Smuzhiyun {
151*4882a593Smuzhiyun int n;
152*4882a593Smuzhiyun
153*4882a593Smuzhiyun if (len < 2)
154*4882a593Smuzhiyun return -EILSEQ;
155*4882a593Smuzhiyun
156*4882a593Smuzhiyun n = get_unaligned_be16(data);
157*4882a593Smuzhiyun data += 2;
158*4882a593Smuzhiyun len -= 2;
159*4882a593Smuzhiyun
160*4882a593Smuzhiyun if (len < n)
161*4882a593Smuzhiyun return -EILSEQ;
162*4882a593Smuzhiyun
163*4882a593Smuzhiyun BT_DBG("filter len %d", n);
164*4882a593Smuzhiyun
165*4882a593Smuzhiyun #ifdef CONFIG_BT_BNEP_MC_FILTER
166*4882a593Smuzhiyun n /= (ETH_ALEN * 2);
167*4882a593Smuzhiyun
168*4882a593Smuzhiyun if (n > 0) {
169*4882a593Smuzhiyun int i;
170*4882a593Smuzhiyun
171*4882a593Smuzhiyun s->mc_filter = 0;
172*4882a593Smuzhiyun
173*4882a593Smuzhiyun /* Always send broadcast */
174*4882a593Smuzhiyun set_bit(bnep_mc_hash(s->dev->broadcast), (ulong *) &s->mc_filter);
175*4882a593Smuzhiyun
176*4882a593Smuzhiyun /* Add address ranges to the multicast hash */
177*4882a593Smuzhiyun for (; n > 0; n--) {
178*4882a593Smuzhiyun u8 a1[6], *a2;
179*4882a593Smuzhiyun
180*4882a593Smuzhiyun memcpy(a1, data, ETH_ALEN);
181*4882a593Smuzhiyun data += ETH_ALEN;
182*4882a593Smuzhiyun a2 = data;
183*4882a593Smuzhiyun data += ETH_ALEN;
184*4882a593Smuzhiyun
185*4882a593Smuzhiyun BT_DBG("mc filter %pMR -> %pMR", a1, a2);
186*4882a593Smuzhiyun
187*4882a593Smuzhiyun /* Iterate from a1 to a2 */
188*4882a593Smuzhiyun set_bit(bnep_mc_hash(a1), (ulong *) &s->mc_filter);
189*4882a593Smuzhiyun while (memcmp(a1, a2, 6) < 0 && s->mc_filter != ~0LL) {
190*4882a593Smuzhiyun /* Increment a1 */
191*4882a593Smuzhiyun i = 5;
192*4882a593Smuzhiyun while (i >= 0 && ++a1[i--] == 0)
193*4882a593Smuzhiyun ;
194*4882a593Smuzhiyun
195*4882a593Smuzhiyun set_bit(bnep_mc_hash(a1), (ulong *) &s->mc_filter);
196*4882a593Smuzhiyun }
197*4882a593Smuzhiyun }
198*4882a593Smuzhiyun }
199*4882a593Smuzhiyun
200*4882a593Smuzhiyun BT_DBG("mc filter hash 0x%llx", s->mc_filter);
201*4882a593Smuzhiyun
202*4882a593Smuzhiyun bnep_send_rsp(s, BNEP_FILTER_MULTI_ADDR_RSP, BNEP_SUCCESS);
203*4882a593Smuzhiyun #else
204*4882a593Smuzhiyun bnep_send_rsp(s, BNEP_FILTER_MULTI_ADDR_RSP, BNEP_FILTER_UNSUPPORTED_REQ);
205*4882a593Smuzhiyun #endif
206*4882a593Smuzhiyun return 0;
207*4882a593Smuzhiyun }
208*4882a593Smuzhiyun
bnep_rx_control(struct bnep_session * s,void * data,int len)209*4882a593Smuzhiyun static int bnep_rx_control(struct bnep_session *s, void *data, int len)
210*4882a593Smuzhiyun {
211*4882a593Smuzhiyun u8 cmd = *(u8 *)data;
212*4882a593Smuzhiyun int err = 0;
213*4882a593Smuzhiyun
214*4882a593Smuzhiyun data++;
215*4882a593Smuzhiyun len--;
216*4882a593Smuzhiyun
217*4882a593Smuzhiyun switch (cmd) {
218*4882a593Smuzhiyun case BNEP_CMD_NOT_UNDERSTOOD:
219*4882a593Smuzhiyun case BNEP_SETUP_CONN_RSP:
220*4882a593Smuzhiyun case BNEP_FILTER_NET_TYPE_RSP:
221*4882a593Smuzhiyun case BNEP_FILTER_MULTI_ADDR_RSP:
222*4882a593Smuzhiyun /* Ignore these for now */
223*4882a593Smuzhiyun break;
224*4882a593Smuzhiyun
225*4882a593Smuzhiyun case BNEP_FILTER_NET_TYPE_SET:
226*4882a593Smuzhiyun err = bnep_ctrl_set_netfilter(s, data, len);
227*4882a593Smuzhiyun break;
228*4882a593Smuzhiyun
229*4882a593Smuzhiyun case BNEP_FILTER_MULTI_ADDR_SET:
230*4882a593Smuzhiyun err = bnep_ctrl_set_mcfilter(s, data, len);
231*4882a593Smuzhiyun break;
232*4882a593Smuzhiyun
233*4882a593Smuzhiyun case BNEP_SETUP_CONN_REQ:
234*4882a593Smuzhiyun /* Successful response should be sent only once */
235*4882a593Smuzhiyun if (test_bit(BNEP_SETUP_RESPONSE, &s->flags) &&
236*4882a593Smuzhiyun !test_and_set_bit(BNEP_SETUP_RSP_SENT, &s->flags))
237*4882a593Smuzhiyun err = bnep_send_rsp(s, BNEP_SETUP_CONN_RSP,
238*4882a593Smuzhiyun BNEP_SUCCESS);
239*4882a593Smuzhiyun else
240*4882a593Smuzhiyun err = bnep_send_rsp(s, BNEP_SETUP_CONN_RSP,
241*4882a593Smuzhiyun BNEP_CONN_NOT_ALLOWED);
242*4882a593Smuzhiyun break;
243*4882a593Smuzhiyun
244*4882a593Smuzhiyun default: {
245*4882a593Smuzhiyun u8 pkt[3];
246*4882a593Smuzhiyun pkt[0] = BNEP_CONTROL;
247*4882a593Smuzhiyun pkt[1] = BNEP_CMD_NOT_UNDERSTOOD;
248*4882a593Smuzhiyun pkt[2] = cmd;
249*4882a593Smuzhiyun err = bnep_send(s, pkt, sizeof(pkt));
250*4882a593Smuzhiyun }
251*4882a593Smuzhiyun break;
252*4882a593Smuzhiyun }
253*4882a593Smuzhiyun
254*4882a593Smuzhiyun return err;
255*4882a593Smuzhiyun }
256*4882a593Smuzhiyun
bnep_rx_extension(struct bnep_session * s,struct sk_buff * skb)257*4882a593Smuzhiyun static int bnep_rx_extension(struct bnep_session *s, struct sk_buff *skb)
258*4882a593Smuzhiyun {
259*4882a593Smuzhiyun struct bnep_ext_hdr *h;
260*4882a593Smuzhiyun int err = 0;
261*4882a593Smuzhiyun
262*4882a593Smuzhiyun do {
263*4882a593Smuzhiyun h = (void *) skb->data;
264*4882a593Smuzhiyun if (!skb_pull(skb, sizeof(*h))) {
265*4882a593Smuzhiyun err = -EILSEQ;
266*4882a593Smuzhiyun break;
267*4882a593Smuzhiyun }
268*4882a593Smuzhiyun
269*4882a593Smuzhiyun BT_DBG("type 0x%x len %d", h->type, h->len);
270*4882a593Smuzhiyun
271*4882a593Smuzhiyun switch (h->type & BNEP_TYPE_MASK) {
272*4882a593Smuzhiyun case BNEP_EXT_CONTROL:
273*4882a593Smuzhiyun bnep_rx_control(s, skb->data, skb->len);
274*4882a593Smuzhiyun break;
275*4882a593Smuzhiyun
276*4882a593Smuzhiyun default:
277*4882a593Smuzhiyun /* Unknown extension, skip it. */
278*4882a593Smuzhiyun break;
279*4882a593Smuzhiyun }
280*4882a593Smuzhiyun
281*4882a593Smuzhiyun if (!skb_pull(skb, h->len)) {
282*4882a593Smuzhiyun err = -EILSEQ;
283*4882a593Smuzhiyun break;
284*4882a593Smuzhiyun }
285*4882a593Smuzhiyun } while (!err && (h->type & BNEP_EXT_HEADER));
286*4882a593Smuzhiyun
287*4882a593Smuzhiyun return err;
288*4882a593Smuzhiyun }
289*4882a593Smuzhiyun
290*4882a593Smuzhiyun static u8 __bnep_rx_hlen[] = {
291*4882a593Smuzhiyun ETH_HLEN, /* BNEP_GENERAL */
292*4882a593Smuzhiyun 0, /* BNEP_CONTROL */
293*4882a593Smuzhiyun 2, /* BNEP_COMPRESSED */
294*4882a593Smuzhiyun ETH_ALEN + 2, /* BNEP_COMPRESSED_SRC_ONLY */
295*4882a593Smuzhiyun ETH_ALEN + 2 /* BNEP_COMPRESSED_DST_ONLY */
296*4882a593Smuzhiyun };
297*4882a593Smuzhiyun
bnep_rx_frame(struct bnep_session * s,struct sk_buff * skb)298*4882a593Smuzhiyun static int bnep_rx_frame(struct bnep_session *s, struct sk_buff *skb)
299*4882a593Smuzhiyun {
300*4882a593Smuzhiyun struct net_device *dev = s->dev;
301*4882a593Smuzhiyun struct sk_buff *nskb;
302*4882a593Smuzhiyun u8 type, ctrl_type;
303*4882a593Smuzhiyun
304*4882a593Smuzhiyun dev->stats.rx_bytes += skb->len;
305*4882a593Smuzhiyun
306*4882a593Smuzhiyun type = *(u8 *) skb->data;
307*4882a593Smuzhiyun skb_pull(skb, 1);
308*4882a593Smuzhiyun ctrl_type = *(u8 *)skb->data;
309*4882a593Smuzhiyun
310*4882a593Smuzhiyun if ((type & BNEP_TYPE_MASK) >= sizeof(__bnep_rx_hlen))
311*4882a593Smuzhiyun goto badframe;
312*4882a593Smuzhiyun
313*4882a593Smuzhiyun if ((type & BNEP_TYPE_MASK) == BNEP_CONTROL) {
314*4882a593Smuzhiyun if (bnep_rx_control(s, skb->data, skb->len) < 0) {
315*4882a593Smuzhiyun dev->stats.tx_errors++;
316*4882a593Smuzhiyun kfree_skb(skb);
317*4882a593Smuzhiyun return 0;
318*4882a593Smuzhiyun }
319*4882a593Smuzhiyun
320*4882a593Smuzhiyun if (!(type & BNEP_EXT_HEADER)) {
321*4882a593Smuzhiyun kfree_skb(skb);
322*4882a593Smuzhiyun return 0;
323*4882a593Smuzhiyun }
324*4882a593Smuzhiyun
325*4882a593Smuzhiyun /* Verify and pull ctrl message since it's already processed */
326*4882a593Smuzhiyun switch (ctrl_type) {
327*4882a593Smuzhiyun case BNEP_SETUP_CONN_REQ:
328*4882a593Smuzhiyun /* Pull: ctrl type (1 b), len (1 b), data (len bytes) */
329*4882a593Smuzhiyun if (!skb_pull(skb, 2 + *(u8 *)(skb->data + 1) * 2))
330*4882a593Smuzhiyun goto badframe;
331*4882a593Smuzhiyun break;
332*4882a593Smuzhiyun case BNEP_FILTER_MULTI_ADDR_SET:
333*4882a593Smuzhiyun case BNEP_FILTER_NET_TYPE_SET:
334*4882a593Smuzhiyun /* Pull: ctrl type (1 b), len (2 b), data (len bytes) */
335*4882a593Smuzhiyun if (!skb_pull(skb, 3 + *(u16 *)(skb->data + 1) * 2))
336*4882a593Smuzhiyun goto badframe;
337*4882a593Smuzhiyun break;
338*4882a593Smuzhiyun default:
339*4882a593Smuzhiyun kfree_skb(skb);
340*4882a593Smuzhiyun return 0;
341*4882a593Smuzhiyun }
342*4882a593Smuzhiyun } else {
343*4882a593Smuzhiyun skb_reset_mac_header(skb);
344*4882a593Smuzhiyun
345*4882a593Smuzhiyun /* Verify and pull out header */
346*4882a593Smuzhiyun if (!skb_pull(skb, __bnep_rx_hlen[type & BNEP_TYPE_MASK]))
347*4882a593Smuzhiyun goto badframe;
348*4882a593Smuzhiyun
349*4882a593Smuzhiyun s->eh.h_proto = get_unaligned((__be16 *) (skb->data - 2));
350*4882a593Smuzhiyun }
351*4882a593Smuzhiyun
352*4882a593Smuzhiyun if (type & BNEP_EXT_HEADER) {
353*4882a593Smuzhiyun if (bnep_rx_extension(s, skb) < 0)
354*4882a593Smuzhiyun goto badframe;
355*4882a593Smuzhiyun }
356*4882a593Smuzhiyun
357*4882a593Smuzhiyun /* Strip 802.1p header */
358*4882a593Smuzhiyun if (ntohs(s->eh.h_proto) == ETH_P_8021Q) {
359*4882a593Smuzhiyun if (!skb_pull(skb, 4))
360*4882a593Smuzhiyun goto badframe;
361*4882a593Smuzhiyun s->eh.h_proto = get_unaligned((__be16 *) (skb->data - 2));
362*4882a593Smuzhiyun }
363*4882a593Smuzhiyun
364*4882a593Smuzhiyun /* We have to alloc new skb and copy data here :(. Because original skb
365*4882a593Smuzhiyun * may not be modified and because of the alignment requirements. */
366*4882a593Smuzhiyun nskb = alloc_skb(2 + ETH_HLEN + skb->len, GFP_KERNEL);
367*4882a593Smuzhiyun if (!nskb) {
368*4882a593Smuzhiyun dev->stats.rx_dropped++;
369*4882a593Smuzhiyun kfree_skb(skb);
370*4882a593Smuzhiyun return -ENOMEM;
371*4882a593Smuzhiyun }
372*4882a593Smuzhiyun skb_reserve(nskb, 2);
373*4882a593Smuzhiyun
374*4882a593Smuzhiyun /* Decompress header and construct ether frame */
375*4882a593Smuzhiyun switch (type & BNEP_TYPE_MASK) {
376*4882a593Smuzhiyun case BNEP_COMPRESSED:
377*4882a593Smuzhiyun __skb_put_data(nskb, &s->eh, ETH_HLEN);
378*4882a593Smuzhiyun break;
379*4882a593Smuzhiyun
380*4882a593Smuzhiyun case BNEP_COMPRESSED_SRC_ONLY:
381*4882a593Smuzhiyun __skb_put_data(nskb, s->eh.h_dest, ETH_ALEN);
382*4882a593Smuzhiyun __skb_put_data(nskb, skb_mac_header(skb), ETH_ALEN);
383*4882a593Smuzhiyun put_unaligned(s->eh.h_proto, (__be16 *) __skb_put(nskb, 2));
384*4882a593Smuzhiyun break;
385*4882a593Smuzhiyun
386*4882a593Smuzhiyun case BNEP_COMPRESSED_DST_ONLY:
387*4882a593Smuzhiyun __skb_put_data(nskb, skb_mac_header(skb), ETH_ALEN);
388*4882a593Smuzhiyun __skb_put_data(nskb, s->eh.h_source, ETH_ALEN + 2);
389*4882a593Smuzhiyun break;
390*4882a593Smuzhiyun
391*4882a593Smuzhiyun case BNEP_GENERAL:
392*4882a593Smuzhiyun __skb_put_data(nskb, skb_mac_header(skb), ETH_ALEN * 2);
393*4882a593Smuzhiyun put_unaligned(s->eh.h_proto, (__be16 *) __skb_put(nskb, 2));
394*4882a593Smuzhiyun break;
395*4882a593Smuzhiyun }
396*4882a593Smuzhiyun
397*4882a593Smuzhiyun skb_copy_from_linear_data(skb, __skb_put(nskb, skb->len), skb->len);
398*4882a593Smuzhiyun kfree_skb(skb);
399*4882a593Smuzhiyun
400*4882a593Smuzhiyun dev->stats.rx_packets++;
401*4882a593Smuzhiyun nskb->ip_summed = CHECKSUM_NONE;
402*4882a593Smuzhiyun nskb->protocol = eth_type_trans(nskb, dev);
403*4882a593Smuzhiyun netif_rx_ni(nskb);
404*4882a593Smuzhiyun return 0;
405*4882a593Smuzhiyun
406*4882a593Smuzhiyun badframe:
407*4882a593Smuzhiyun dev->stats.rx_errors++;
408*4882a593Smuzhiyun kfree_skb(skb);
409*4882a593Smuzhiyun return 0;
410*4882a593Smuzhiyun }
411*4882a593Smuzhiyun
412*4882a593Smuzhiyun static u8 __bnep_tx_types[] = {
413*4882a593Smuzhiyun BNEP_GENERAL,
414*4882a593Smuzhiyun BNEP_COMPRESSED_SRC_ONLY,
415*4882a593Smuzhiyun BNEP_COMPRESSED_DST_ONLY,
416*4882a593Smuzhiyun BNEP_COMPRESSED
417*4882a593Smuzhiyun };
418*4882a593Smuzhiyun
bnep_tx_frame(struct bnep_session * s,struct sk_buff * skb)419*4882a593Smuzhiyun static int bnep_tx_frame(struct bnep_session *s, struct sk_buff *skb)
420*4882a593Smuzhiyun {
421*4882a593Smuzhiyun struct ethhdr *eh = (void *) skb->data;
422*4882a593Smuzhiyun struct socket *sock = s->sock;
423*4882a593Smuzhiyun struct kvec iv[3];
424*4882a593Smuzhiyun int len = 0, il = 0;
425*4882a593Smuzhiyun u8 type = 0;
426*4882a593Smuzhiyun
427*4882a593Smuzhiyun BT_DBG("skb %p dev %p type %d", skb, skb->dev, skb->pkt_type);
428*4882a593Smuzhiyun
429*4882a593Smuzhiyun if (!skb->dev) {
430*4882a593Smuzhiyun /* Control frame sent by us */
431*4882a593Smuzhiyun goto send;
432*4882a593Smuzhiyun }
433*4882a593Smuzhiyun
434*4882a593Smuzhiyun iv[il++] = (struct kvec) { &type, 1 };
435*4882a593Smuzhiyun len++;
436*4882a593Smuzhiyun
437*4882a593Smuzhiyun if (compress_src && ether_addr_equal(eh->h_dest, s->eh.h_source))
438*4882a593Smuzhiyun type |= 0x01;
439*4882a593Smuzhiyun
440*4882a593Smuzhiyun if (compress_dst && ether_addr_equal(eh->h_source, s->eh.h_dest))
441*4882a593Smuzhiyun type |= 0x02;
442*4882a593Smuzhiyun
443*4882a593Smuzhiyun if (type)
444*4882a593Smuzhiyun skb_pull(skb, ETH_ALEN * 2);
445*4882a593Smuzhiyun
446*4882a593Smuzhiyun type = __bnep_tx_types[type];
447*4882a593Smuzhiyun switch (type) {
448*4882a593Smuzhiyun case BNEP_COMPRESSED_SRC_ONLY:
449*4882a593Smuzhiyun iv[il++] = (struct kvec) { eh->h_source, ETH_ALEN };
450*4882a593Smuzhiyun len += ETH_ALEN;
451*4882a593Smuzhiyun break;
452*4882a593Smuzhiyun
453*4882a593Smuzhiyun case BNEP_COMPRESSED_DST_ONLY:
454*4882a593Smuzhiyun iv[il++] = (struct kvec) { eh->h_dest, ETH_ALEN };
455*4882a593Smuzhiyun len += ETH_ALEN;
456*4882a593Smuzhiyun break;
457*4882a593Smuzhiyun }
458*4882a593Smuzhiyun
459*4882a593Smuzhiyun send:
460*4882a593Smuzhiyun iv[il++] = (struct kvec) { skb->data, skb->len };
461*4882a593Smuzhiyun len += skb->len;
462*4882a593Smuzhiyun
463*4882a593Smuzhiyun /* FIXME: linearize skb */
464*4882a593Smuzhiyun {
465*4882a593Smuzhiyun len = kernel_sendmsg(sock, &s->msg, iv, il, len);
466*4882a593Smuzhiyun }
467*4882a593Smuzhiyun kfree_skb(skb);
468*4882a593Smuzhiyun
469*4882a593Smuzhiyun if (len > 0) {
470*4882a593Smuzhiyun s->dev->stats.tx_bytes += len;
471*4882a593Smuzhiyun s->dev->stats.tx_packets++;
472*4882a593Smuzhiyun return 0;
473*4882a593Smuzhiyun }
474*4882a593Smuzhiyun
475*4882a593Smuzhiyun return len;
476*4882a593Smuzhiyun }
477*4882a593Smuzhiyun
bnep_session(void * arg)478*4882a593Smuzhiyun static int bnep_session(void *arg)
479*4882a593Smuzhiyun {
480*4882a593Smuzhiyun struct bnep_session *s = arg;
481*4882a593Smuzhiyun struct net_device *dev = s->dev;
482*4882a593Smuzhiyun struct sock *sk = s->sock->sk;
483*4882a593Smuzhiyun struct sk_buff *skb;
484*4882a593Smuzhiyun DEFINE_WAIT_FUNC(wait, woken_wake_function);
485*4882a593Smuzhiyun
486*4882a593Smuzhiyun BT_DBG("");
487*4882a593Smuzhiyun
488*4882a593Smuzhiyun set_user_nice(current, -15);
489*4882a593Smuzhiyun
490*4882a593Smuzhiyun add_wait_queue(sk_sleep(sk), &wait);
491*4882a593Smuzhiyun while (1) {
492*4882a593Smuzhiyun if (atomic_read(&s->terminate))
493*4882a593Smuzhiyun break;
494*4882a593Smuzhiyun /* RX */
495*4882a593Smuzhiyun while ((skb = skb_dequeue(&sk->sk_receive_queue))) {
496*4882a593Smuzhiyun skb_orphan(skb);
497*4882a593Smuzhiyun if (!skb_linearize(skb))
498*4882a593Smuzhiyun bnep_rx_frame(s, skb);
499*4882a593Smuzhiyun else
500*4882a593Smuzhiyun kfree_skb(skb);
501*4882a593Smuzhiyun }
502*4882a593Smuzhiyun
503*4882a593Smuzhiyun if (sk->sk_state != BT_CONNECTED)
504*4882a593Smuzhiyun break;
505*4882a593Smuzhiyun
506*4882a593Smuzhiyun /* TX */
507*4882a593Smuzhiyun while ((skb = skb_dequeue(&sk->sk_write_queue)))
508*4882a593Smuzhiyun if (bnep_tx_frame(s, skb))
509*4882a593Smuzhiyun break;
510*4882a593Smuzhiyun netif_wake_queue(dev);
511*4882a593Smuzhiyun
512*4882a593Smuzhiyun /*
513*4882a593Smuzhiyun * wait_woken() performs the necessary memory barriers
514*4882a593Smuzhiyun * for us; see the header comment for this primitive.
515*4882a593Smuzhiyun */
516*4882a593Smuzhiyun wait_woken(&wait, TASK_INTERRUPTIBLE, MAX_SCHEDULE_TIMEOUT);
517*4882a593Smuzhiyun }
518*4882a593Smuzhiyun remove_wait_queue(sk_sleep(sk), &wait);
519*4882a593Smuzhiyun
520*4882a593Smuzhiyun /* Cleanup session */
521*4882a593Smuzhiyun down_write(&bnep_session_sem);
522*4882a593Smuzhiyun
523*4882a593Smuzhiyun /* Delete network device */
524*4882a593Smuzhiyun unregister_netdev(dev);
525*4882a593Smuzhiyun
526*4882a593Smuzhiyun /* Wakeup user-space polling for socket errors */
527*4882a593Smuzhiyun s->sock->sk->sk_err = EUNATCH;
528*4882a593Smuzhiyun
529*4882a593Smuzhiyun wake_up_interruptible(sk_sleep(s->sock->sk));
530*4882a593Smuzhiyun
531*4882a593Smuzhiyun /* Release the socket */
532*4882a593Smuzhiyun fput(s->sock->file);
533*4882a593Smuzhiyun
534*4882a593Smuzhiyun __bnep_unlink_session(s);
535*4882a593Smuzhiyun
536*4882a593Smuzhiyun up_write(&bnep_session_sem);
537*4882a593Smuzhiyun free_netdev(dev);
538*4882a593Smuzhiyun module_put_and_exit(0);
539*4882a593Smuzhiyun return 0;
540*4882a593Smuzhiyun }
541*4882a593Smuzhiyun
bnep_get_device(struct bnep_session * session)542*4882a593Smuzhiyun static struct device *bnep_get_device(struct bnep_session *session)
543*4882a593Smuzhiyun {
544*4882a593Smuzhiyun struct l2cap_conn *conn = l2cap_pi(session->sock->sk)->chan->conn;
545*4882a593Smuzhiyun
546*4882a593Smuzhiyun if (!conn || !conn->hcon)
547*4882a593Smuzhiyun return NULL;
548*4882a593Smuzhiyun
549*4882a593Smuzhiyun return &conn->hcon->dev;
550*4882a593Smuzhiyun }
551*4882a593Smuzhiyun
552*4882a593Smuzhiyun static struct device_type bnep_type = {
553*4882a593Smuzhiyun .name = "bluetooth",
554*4882a593Smuzhiyun };
555*4882a593Smuzhiyun
bnep_add_connection(struct bnep_connadd_req * req,struct socket * sock)556*4882a593Smuzhiyun int bnep_add_connection(struct bnep_connadd_req *req, struct socket *sock)
557*4882a593Smuzhiyun {
558*4882a593Smuzhiyun u32 valid_flags = BIT(BNEP_SETUP_RESPONSE);
559*4882a593Smuzhiyun struct net_device *dev;
560*4882a593Smuzhiyun struct bnep_session *s, *ss;
561*4882a593Smuzhiyun u8 dst[ETH_ALEN], src[ETH_ALEN];
562*4882a593Smuzhiyun int err;
563*4882a593Smuzhiyun
564*4882a593Smuzhiyun BT_DBG("");
565*4882a593Smuzhiyun
566*4882a593Smuzhiyun if (!l2cap_is_socket(sock))
567*4882a593Smuzhiyun return -EBADFD;
568*4882a593Smuzhiyun
569*4882a593Smuzhiyun if (req->flags & ~valid_flags)
570*4882a593Smuzhiyun return -EINVAL;
571*4882a593Smuzhiyun
572*4882a593Smuzhiyun baswap((void *) dst, &l2cap_pi(sock->sk)->chan->dst);
573*4882a593Smuzhiyun baswap((void *) src, &l2cap_pi(sock->sk)->chan->src);
574*4882a593Smuzhiyun
575*4882a593Smuzhiyun /* session struct allocated as private part of net_device */
576*4882a593Smuzhiyun dev = alloc_netdev(sizeof(struct bnep_session),
577*4882a593Smuzhiyun (*req->device) ? req->device : "bnep%d",
578*4882a593Smuzhiyun NET_NAME_UNKNOWN,
579*4882a593Smuzhiyun bnep_net_setup);
580*4882a593Smuzhiyun if (!dev)
581*4882a593Smuzhiyun return -ENOMEM;
582*4882a593Smuzhiyun
583*4882a593Smuzhiyun down_write(&bnep_session_sem);
584*4882a593Smuzhiyun
585*4882a593Smuzhiyun ss = __bnep_get_session(dst);
586*4882a593Smuzhiyun if (ss && ss->state == BT_CONNECTED) {
587*4882a593Smuzhiyun err = -EEXIST;
588*4882a593Smuzhiyun goto failed;
589*4882a593Smuzhiyun }
590*4882a593Smuzhiyun
591*4882a593Smuzhiyun s = netdev_priv(dev);
592*4882a593Smuzhiyun
593*4882a593Smuzhiyun /* This is rx header therefore addresses are swapped.
594*4882a593Smuzhiyun * ie. eh.h_dest is our local address. */
595*4882a593Smuzhiyun memcpy(s->eh.h_dest, &src, ETH_ALEN);
596*4882a593Smuzhiyun memcpy(s->eh.h_source, &dst, ETH_ALEN);
597*4882a593Smuzhiyun memcpy(dev->dev_addr, s->eh.h_dest, ETH_ALEN);
598*4882a593Smuzhiyun
599*4882a593Smuzhiyun s->dev = dev;
600*4882a593Smuzhiyun s->sock = sock;
601*4882a593Smuzhiyun s->role = req->role;
602*4882a593Smuzhiyun s->state = BT_CONNECTED;
603*4882a593Smuzhiyun s->flags = req->flags;
604*4882a593Smuzhiyun
605*4882a593Smuzhiyun s->msg.msg_flags = MSG_NOSIGNAL;
606*4882a593Smuzhiyun
607*4882a593Smuzhiyun #ifdef CONFIG_BT_BNEP_MC_FILTER
608*4882a593Smuzhiyun /* Set default mc filter to not filter out any mc addresses
609*4882a593Smuzhiyun * as defined in the BNEP specification (revision 0.95a)
610*4882a593Smuzhiyun * http://grouper.ieee.org/groups/802/15/Bluetooth/BNEP.pdf
611*4882a593Smuzhiyun */
612*4882a593Smuzhiyun s->mc_filter = ~0LL;
613*4882a593Smuzhiyun #endif
614*4882a593Smuzhiyun
615*4882a593Smuzhiyun #ifdef CONFIG_BT_BNEP_PROTO_FILTER
616*4882a593Smuzhiyun /* Set default protocol filter */
617*4882a593Smuzhiyun bnep_set_default_proto_filter(s);
618*4882a593Smuzhiyun #endif
619*4882a593Smuzhiyun
620*4882a593Smuzhiyun SET_NETDEV_DEV(dev, bnep_get_device(s));
621*4882a593Smuzhiyun SET_NETDEV_DEVTYPE(dev, &bnep_type);
622*4882a593Smuzhiyun
623*4882a593Smuzhiyun err = register_netdev(dev);
624*4882a593Smuzhiyun if (err)
625*4882a593Smuzhiyun goto failed;
626*4882a593Smuzhiyun
627*4882a593Smuzhiyun __bnep_link_session(s);
628*4882a593Smuzhiyun
629*4882a593Smuzhiyun __module_get(THIS_MODULE);
630*4882a593Smuzhiyun s->task = kthread_run(bnep_session, s, "kbnepd %s", dev->name);
631*4882a593Smuzhiyun if (IS_ERR(s->task)) {
632*4882a593Smuzhiyun /* Session thread start failed, gotta cleanup. */
633*4882a593Smuzhiyun module_put(THIS_MODULE);
634*4882a593Smuzhiyun unregister_netdev(dev);
635*4882a593Smuzhiyun __bnep_unlink_session(s);
636*4882a593Smuzhiyun err = PTR_ERR(s->task);
637*4882a593Smuzhiyun goto failed;
638*4882a593Smuzhiyun }
639*4882a593Smuzhiyun
640*4882a593Smuzhiyun up_write(&bnep_session_sem);
641*4882a593Smuzhiyun strcpy(req->device, dev->name);
642*4882a593Smuzhiyun return 0;
643*4882a593Smuzhiyun
644*4882a593Smuzhiyun failed:
645*4882a593Smuzhiyun up_write(&bnep_session_sem);
646*4882a593Smuzhiyun free_netdev(dev);
647*4882a593Smuzhiyun return err;
648*4882a593Smuzhiyun }
649*4882a593Smuzhiyun
bnep_del_connection(struct bnep_conndel_req * req)650*4882a593Smuzhiyun int bnep_del_connection(struct bnep_conndel_req *req)
651*4882a593Smuzhiyun {
652*4882a593Smuzhiyun u32 valid_flags = 0;
653*4882a593Smuzhiyun struct bnep_session *s;
654*4882a593Smuzhiyun int err = 0;
655*4882a593Smuzhiyun
656*4882a593Smuzhiyun BT_DBG("");
657*4882a593Smuzhiyun
658*4882a593Smuzhiyun if (req->flags & ~valid_flags)
659*4882a593Smuzhiyun return -EINVAL;
660*4882a593Smuzhiyun
661*4882a593Smuzhiyun down_read(&bnep_session_sem);
662*4882a593Smuzhiyun
663*4882a593Smuzhiyun s = __bnep_get_session(req->dst);
664*4882a593Smuzhiyun if (s) {
665*4882a593Smuzhiyun atomic_inc(&s->terminate);
666*4882a593Smuzhiyun wake_up_interruptible(sk_sleep(s->sock->sk));
667*4882a593Smuzhiyun } else
668*4882a593Smuzhiyun err = -ENOENT;
669*4882a593Smuzhiyun
670*4882a593Smuzhiyun up_read(&bnep_session_sem);
671*4882a593Smuzhiyun return err;
672*4882a593Smuzhiyun }
673*4882a593Smuzhiyun
__bnep_copy_ci(struct bnep_conninfo * ci,struct bnep_session * s)674*4882a593Smuzhiyun static void __bnep_copy_ci(struct bnep_conninfo *ci, struct bnep_session *s)
675*4882a593Smuzhiyun {
676*4882a593Smuzhiyun u32 valid_flags = BIT(BNEP_SETUP_RESPONSE);
677*4882a593Smuzhiyun
678*4882a593Smuzhiyun memset(ci, 0, sizeof(*ci));
679*4882a593Smuzhiyun memcpy(ci->dst, s->eh.h_source, ETH_ALEN);
680*4882a593Smuzhiyun strcpy(ci->device, s->dev->name);
681*4882a593Smuzhiyun ci->flags = s->flags & valid_flags;
682*4882a593Smuzhiyun ci->state = s->state;
683*4882a593Smuzhiyun ci->role = s->role;
684*4882a593Smuzhiyun }
685*4882a593Smuzhiyun
bnep_get_connlist(struct bnep_connlist_req * req)686*4882a593Smuzhiyun int bnep_get_connlist(struct bnep_connlist_req *req)
687*4882a593Smuzhiyun {
688*4882a593Smuzhiyun struct bnep_session *s;
689*4882a593Smuzhiyun int err = 0, n = 0;
690*4882a593Smuzhiyun
691*4882a593Smuzhiyun down_read(&bnep_session_sem);
692*4882a593Smuzhiyun
693*4882a593Smuzhiyun list_for_each_entry(s, &bnep_session_list, list) {
694*4882a593Smuzhiyun struct bnep_conninfo ci;
695*4882a593Smuzhiyun
696*4882a593Smuzhiyun __bnep_copy_ci(&ci, s);
697*4882a593Smuzhiyun
698*4882a593Smuzhiyun if (copy_to_user(req->ci, &ci, sizeof(ci))) {
699*4882a593Smuzhiyun err = -EFAULT;
700*4882a593Smuzhiyun break;
701*4882a593Smuzhiyun }
702*4882a593Smuzhiyun
703*4882a593Smuzhiyun if (++n >= req->cnum)
704*4882a593Smuzhiyun break;
705*4882a593Smuzhiyun
706*4882a593Smuzhiyun req->ci++;
707*4882a593Smuzhiyun }
708*4882a593Smuzhiyun req->cnum = n;
709*4882a593Smuzhiyun
710*4882a593Smuzhiyun up_read(&bnep_session_sem);
711*4882a593Smuzhiyun return err;
712*4882a593Smuzhiyun }
713*4882a593Smuzhiyun
bnep_get_conninfo(struct bnep_conninfo * ci)714*4882a593Smuzhiyun int bnep_get_conninfo(struct bnep_conninfo *ci)
715*4882a593Smuzhiyun {
716*4882a593Smuzhiyun struct bnep_session *s;
717*4882a593Smuzhiyun int err = 0;
718*4882a593Smuzhiyun
719*4882a593Smuzhiyun down_read(&bnep_session_sem);
720*4882a593Smuzhiyun
721*4882a593Smuzhiyun s = __bnep_get_session(ci->dst);
722*4882a593Smuzhiyun if (s)
723*4882a593Smuzhiyun __bnep_copy_ci(ci, s);
724*4882a593Smuzhiyun else
725*4882a593Smuzhiyun err = -ENOENT;
726*4882a593Smuzhiyun
727*4882a593Smuzhiyun up_read(&bnep_session_sem);
728*4882a593Smuzhiyun return err;
729*4882a593Smuzhiyun }
730*4882a593Smuzhiyun
bnep_init(void)731*4882a593Smuzhiyun static int __init bnep_init(void)
732*4882a593Smuzhiyun {
733*4882a593Smuzhiyun char flt[50] = "";
734*4882a593Smuzhiyun
735*4882a593Smuzhiyun #ifdef CONFIG_BT_BNEP_PROTO_FILTER
736*4882a593Smuzhiyun strcat(flt, "protocol ");
737*4882a593Smuzhiyun #endif
738*4882a593Smuzhiyun
739*4882a593Smuzhiyun #ifdef CONFIG_BT_BNEP_MC_FILTER
740*4882a593Smuzhiyun strcat(flt, "multicast");
741*4882a593Smuzhiyun #endif
742*4882a593Smuzhiyun
743*4882a593Smuzhiyun BT_INFO("BNEP (Ethernet Emulation) ver %s", VERSION);
744*4882a593Smuzhiyun if (flt[0])
745*4882a593Smuzhiyun BT_INFO("BNEP filters: %s", flt);
746*4882a593Smuzhiyun
747*4882a593Smuzhiyun bnep_sock_init();
748*4882a593Smuzhiyun return 0;
749*4882a593Smuzhiyun }
750*4882a593Smuzhiyun
bnep_exit(void)751*4882a593Smuzhiyun static void __exit bnep_exit(void)
752*4882a593Smuzhiyun {
753*4882a593Smuzhiyun bnep_sock_cleanup();
754*4882a593Smuzhiyun }
755*4882a593Smuzhiyun
756*4882a593Smuzhiyun module_init(bnep_init);
757*4882a593Smuzhiyun module_exit(bnep_exit);
758*4882a593Smuzhiyun
759*4882a593Smuzhiyun module_param(compress_src, bool, 0644);
760*4882a593Smuzhiyun MODULE_PARM_DESC(compress_src, "Compress sources headers");
761*4882a593Smuzhiyun
762*4882a593Smuzhiyun module_param(compress_dst, bool, 0644);
763*4882a593Smuzhiyun MODULE_PARM_DESC(compress_dst, "Compress destination headers");
764*4882a593Smuzhiyun
765*4882a593Smuzhiyun MODULE_AUTHOR("Marcel Holtmann <marcel@holtmann.org>");
766*4882a593Smuzhiyun MODULE_DESCRIPTION("Bluetooth BNEP ver " VERSION);
767*4882a593Smuzhiyun MODULE_VERSION(VERSION);
768*4882a593Smuzhiyun MODULE_LICENSE("GPL");
769*4882a593Smuzhiyun MODULE_ALIAS("bt-proto-4");
770