Blame view

net/phonet/af_phonet.c 12.7 KB
4b07b3f69   Remi Denis-Courmont   Phonet: PF_PHONET...
1
2
3
4
5
6
7
  /*
   * File: af_phonet.c
   *
   * Phonet protocols family
   *
   * Copyright (C) 2008 Nokia Corporation.
   *
31fdc5553   Rémi Denis-Courmont   net: remove my fu...
8
9
   * Authors: Sakari Ailus <sakari.ailus@nokia.com>
   *          Rémi Denis-Courmont
4b07b3f69   Remi Denis-Courmont   Phonet: PF_PHONET...
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
   *
   * This program is free software; you can redistribute it and/or
   * modify it under the terms of the GNU General Public License
   * version 2 as published by the Free Software Foundation.
   *
   * This program is distributed in the hope that it will be useful, but
   * WITHOUT ANY WARRANTY; without even the implied warranty of
   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
   * General Public License for more details.
   *
   * You should have received a copy of the GNU General Public License
   * along with this program; if not, write to the Free Software
   * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
   * 02110-1301 USA
   */
  
  #include <linux/kernel.h>
  #include <linux/module.h>
5a0e3ad6a   Tejun Heo   include cleanup: ...
28
  #include <linux/slab.h>
4b07b3f69   Remi Denis-Courmont   Phonet: PF_PHONET...
29
30
31
32
33
34
  #include <asm/unaligned.h>
  #include <net/sock.h>
  
  #include <linux/if_phonet.h>
  #include <linux/phonet.h>
  #include <net/phonet/phonet.h>
f8ff60283   Remi Denis-Courmont   Phonet: network d...
35
  #include <net/phonet/pn_dev.h>
4b07b3f69   Remi Denis-Courmont   Phonet: PF_PHONET...
36

566521d63   Alexey Dobriyan   phonet: fix compi...
37
38
  /* Transport protocol registration */
  static struct phonet_protocol *proto_tab[PHONET_NPROTO] __read_mostly;
566521d63   Alexey Dobriyan   phonet: fix compi...
39

facb4edc1   Dan Carpenter   phonet: some sign...
40
  static struct phonet_protocol *phonet_proto_get(unsigned int protocol)
566521d63   Alexey Dobriyan   phonet: fix compi...
41
42
43
44
45
  {
  	struct phonet_protocol *pp;
  
  	if (protocol >= PHONET_NPROTO)
  		return NULL;
7ed0132f2   Rémi Denis-Courmont   Phonet: put proto...
46
  	rcu_read_lock();
b2a5decdd   Rémi Denis-Courmont   Phonet: missing r...
47
  	pp = rcu_dereference(proto_tab[protocol]);
566521d63   Alexey Dobriyan   phonet: fix compi...
48
49
  	if (pp && !try_module_get(pp->prot->owner))
  		pp = NULL;
7ed0132f2   Rémi Denis-Courmont   Phonet: put proto...
50
  	rcu_read_unlock();
566521d63   Alexey Dobriyan   phonet: fix compi...
51
52
53
54
55
56
57
58
  
  	return pp;
  }
  
  static inline void phonet_proto_put(struct phonet_protocol *pp)
  {
  	module_put(pp->prot->owner);
  }
4b07b3f69   Remi Denis-Courmont   Phonet: PF_PHONET...
59
60
  
  /* protocol family functions */
3f378b684   Eric Paris   net: pass kern to...
61
62
  static int pn_socket_create(struct net *net, struct socket *sock, int protocol,
  			    int kern)
4b07b3f69   Remi Denis-Courmont   Phonet: PF_PHONET...
63
  {
ba113a94b   Remi Denis-Courmont   Phonet: common so...
64
65
  	struct sock *sk;
  	struct pn_sock *pn;
4b07b3f69   Remi Denis-Courmont   Phonet: PF_PHONET...
66
67
  	struct phonet_protocol *pnp;
  	int err;
4b07b3f69   Remi Denis-Courmont   Phonet: PF_PHONET...
68
69
70
71
72
73
74
75
76
  	if (!capable(CAP_SYS_ADMIN))
  		return -EPERM;
  
  	if (protocol == 0) {
  		/* Default protocol selection */
  		switch (sock->type) {
  		case SOCK_DGRAM:
  			protocol = PN_PROTO_PHONET;
  			break;
9641458d3   Rémi Denis-Courmont   Phonet: Pipe End ...
77
78
79
  		case SOCK_SEQPACKET:
  			protocol = PN_PROTO_PIPE;
  			break;
4b07b3f69   Remi Denis-Courmont   Phonet: PF_PHONET...
80
81
82
83
84
85
  		default:
  			return -EPROTONOSUPPORT;
  		}
  	}
  
  	pnp = phonet_proto_get(protocol);
25532824f   Rémi Denis-Courmont   Phonet: modules a...
86
87
88
  	if (pnp == NULL &&
  	    request_module("net-pf-%d-proto-%d", PF_PHONET, protocol) == 0)
  		pnp = phonet_proto_get(protocol);
95a5afca4   Johannes Berg   net: Remove CONFI...
89

4b07b3f69   Remi Denis-Courmont   Phonet: PF_PHONET...
90
91
92
93
94
95
  	if (pnp == NULL)
  		return -EPROTONOSUPPORT;
  	if (sock->type != pnp->sock_type) {
  		err = -EPROTONOSUPPORT;
  		goto out;
  	}
ba113a94b   Remi Denis-Courmont   Phonet: common so...
96
97
98
99
100
101
102
103
104
105
106
107
108
  	sk = sk_alloc(net, PF_PHONET, GFP_KERNEL, pnp->prot);
  	if (sk == NULL) {
  		err = -ENOMEM;
  		goto out;
  	}
  
  	sock_init_data(sock, sk);
  	sock->state = SS_UNCONNECTED;
  	sock->ops = pnp->ops;
  	sk->sk_backlog_rcv = sk->sk_prot->backlog_rcv;
  	sk->sk_protocol = protocol;
  	pn = pn_sk(sk);
  	pn->sobject = 0;
a8059512b   Rémi Denis-Courmont   Phonet: implement...
109
  	pn->dobject = 0;
ba113a94b   Remi Denis-Courmont   Phonet: common so...
110
111
112
  	pn->resource = 0;
  	sk->sk_prot->init(sk);
  	err = 0;
4b07b3f69   Remi Denis-Courmont   Phonet: PF_PHONET...
113
114
115
116
117
  
  out:
  	phonet_proto_put(pnp);
  	return err;
  }
ec1b4cf74   Stephen Hemminger   net: mark net_pro...
118
  static const struct net_proto_family phonet_proto_family = {
25532824f   Rémi Denis-Courmont   Phonet: modules a...
119
  	.family = PF_PHONET,
4b07b3f69   Remi Denis-Courmont   Phonet: PF_PHONET...
120
121
122
  	.create = pn_socket_create,
  	.owner = THIS_MODULE,
  };
5f77076d7   Remi Denis-Courmont   Phonet: provide M...
123
124
125
  /* Phonet device header operations */
  static int pn_header_create(struct sk_buff *skb, struct net_device *dev,
  				unsigned short type, const void *daddr,
95c961747   Eric Dumazet   net: cleanup unsi...
126
  				const void *saddr, unsigned int len)
5f77076d7   Remi Denis-Courmont   Phonet: provide M...
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
  {
  	u8 *media = skb_push(skb, 1);
  
  	if (type != ETH_P_PHONET)
  		return -1;
  
  	if (!saddr)
  		saddr = dev->dev_addr;
  	*media = *(const u8 *)saddr;
  	return 1;
  }
  
  static int pn_header_parse(const struct sk_buff *skb, unsigned char *haddr)
  {
  	const u8 *media = skb_mac_header(skb);
  	*haddr = *media;
  	return 1;
  }
  
  struct header_ops phonet_header_ops = {
  	.create = pn_header_create,
  	.parse = pn_header_parse,
  };
  EXPORT_SYMBOL(phonet_header_ops);
107d0d9b8   Remi Denis-Courmont   Phonet: Phonet da...
151
152
153
154
  /*
   * Prepends an ISI header and sends a datagram.
   */
  static int pn_send(struct sk_buff *skb, struct net_device *dev,
be0c52bfe   Remi Denis-Courmont   Phonet: emit erro...
155
  			u16 dst, u16 src, u8 res, u8 irq)
107d0d9b8   Remi Denis-Courmont   Phonet: Phonet da...
156
157
158
  {
  	struct phonethdr *ph;
  	int err;
ebfe92ca6   Rémi Denis-Courmont   Phonet: refuse to...
159
160
  	if (skb->len + 2 > 0xffff /* Phonet length field limit */ ||
  	    skb->len + sizeof(struct phonethdr) > dev->mtu) {
107d0d9b8   Remi Denis-Courmont   Phonet: Phonet da...
161
162
163
  		err = -EMSGSIZE;
  		goto drop;
  	}
18a1166de   Rémi Denis-Courmont   Phonet: error on ...
164
165
166
167
168
  	/* Broadcast sending is not implemented */
  	if (pn_addr(dst) == PNADDR_BROADCAST) {
  		err = -EOPNOTSUPP;
  		goto drop;
  	}
107d0d9b8   Remi Denis-Courmont   Phonet: Phonet da...
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
  	skb_reset_transport_header(skb);
  	WARN_ON(skb_headroom(skb) & 1); /* HW assumes word alignment */
  	skb_push(skb, sizeof(struct phonethdr));
  	skb_reset_network_header(skb);
  	ph = pn_hdr(skb);
  	ph->pn_rdev = pn_dev(dst);
  	ph->pn_sdev = pn_dev(src);
  	ph->pn_res = res;
  	ph->pn_length = __cpu_to_be16(skb->len + 2 - sizeof(*ph));
  	ph->pn_robj = pn_obj(dst);
  	ph->pn_sobj = pn_obj(src);
  
  	skb->protocol = htons(ETH_P_PHONET);
  	skb->priority = 0;
  	skb->dev = dev;
aa6c45f32   Rémi Denis-Courmont   Phonet: route out...
184
  	if (skb->pkt_type == PACKET_LOOPBACK) {
107d0d9b8   Remi Denis-Courmont   Phonet: Phonet da...
185
  		skb_reset_mac_header(skb);
107d0d9b8   Remi Denis-Courmont   Phonet: Phonet da...
186
  		skb_orphan(skb);
b765e84f9   Rémi Denis-Courmont   Phonet: return an...
187
  		err = (irq ? netif_rx(skb) : netif_rx_ni(skb)) ? -ENOBUFS : 0;
107d0d9b8   Remi Denis-Courmont   Phonet: Phonet da...
188
189
190
191
192
193
194
195
  	} else {
  		err = dev_hard_header(skb, dev, ntohs(skb->protocol),
  					NULL, NULL, skb->len);
  		if (err < 0) {
  			err = -EHOSTUNREACH;
  			goto drop;
  		}
  		err = dev_queue_xmit(skb);
b765e84f9   Rémi Denis-Courmont   Phonet: return an...
196
197
  		if (unlikely(err > 0))
  			err = net_xmit_errno(err);
107d0d9b8   Remi Denis-Courmont   Phonet: Phonet da...
198
199
200
201
202
203
204
  	}
  
  	return err;
  drop:
  	kfree_skb(skb);
  	return err;
  }
be0c52bfe   Remi Denis-Courmont   Phonet: emit erro...
205
206
207
208
209
210
  static int pn_raw_send(const void *data, int len, struct net_device *dev,
  			u16 dst, u16 src, u8 res)
  {
  	struct sk_buff *skb = alloc_skb(MAX_PHONET_HEADER + len, GFP_ATOMIC);
  	if (skb == NULL)
  		return -ENOMEM;
aa6c45f32   Rémi Denis-Courmont   Phonet: route out...
211
212
  	if (phonet_address_lookup(dev_net(dev), pn_addr(dst)) == 0)
  		skb->pkt_type = PACKET_LOOPBACK;
be0c52bfe   Remi Denis-Courmont   Phonet: emit erro...
213
214
215
216
217
  	skb_reserve(skb, MAX_PHONET_HEADER);
  	__skb_put(skb, len);
  	skb_copy_to_linear_data(skb, data, len);
  	return pn_send(skb, dev, dst, src, res, 1);
  }
107d0d9b8   Remi Denis-Courmont   Phonet: Phonet da...
218
219
220
221
222
223
224
  /*
   * Create a Phonet header for the skb and send it out. Returns
   * non-zero error code if failed. The skb is freed then.
   */
  int pn_skb_send(struct sock *sk, struct sk_buff *skb,
  		const struct sockaddr_pn *target)
  {
aa6c45f32   Rémi Denis-Courmont   Phonet: route out...
225
  	struct net *net = sock_net(sk);
107d0d9b8   Remi Denis-Courmont   Phonet: Phonet da...
226
227
228
  	struct net_device *dev;
  	struct pn_sock *pn = pn_sk(sk);
  	int err;
a8059512b   Rémi Denis-Courmont   Phonet: implement...
229
230
231
232
233
234
235
236
237
238
239
240
  	u16 src, dst;
  	u8 daddr, saddr, res;
  
  	src = pn->sobject;
  	if (target != NULL) {
  		dst = pn_sockaddr_get_object(target);
  		res = pn_sockaddr_get_resource(target);
  	} else {
  		dst = pn->dobject;
  		res = pn->resource;
  	}
  	daddr = pn_addr(dst);
107d0d9b8   Remi Denis-Courmont   Phonet: Phonet da...
241
242
243
  
  	err = -EHOSTUNREACH;
  	if (sk->sk_bound_dev_if)
aa6c45f32   Rémi Denis-Courmont   Phonet: route out...
244
245
246
247
  		dev = dev_get_by_index(net, sk->sk_bound_dev_if);
  	else if (phonet_address_lookup(net, daddr) == 0) {
  		dev = phonet_device_get(net);
  		skb->pkt_type = PACKET_LOOPBACK;
c69d4407d   Rémi Denis-Courmont   Phonet: fix NULL ...
248
  	} else if (dst == 0) {
b6a563b2a   Rémi Denis-Courmont   Phonet: look up t...
249
  		/* Resource routing (small race until phonet_rcv()) */
c69d4407d   Rémi Denis-Courmont   Phonet: fix NULL ...
250
  		struct sock *sk = pn_find_sock_by_res(net, res);
b6a563b2a   Rémi Denis-Courmont   Phonet: look up t...
251
252
253
254
255
256
  		if (sk)	{
  			sock_put(sk);
  			dev = phonet_device_get(net);
  			skb->pkt_type = PACKET_LOOPBACK;
  		} else
  			dev = phonet_route_output(net, daddr);
aa6c45f32   Rémi Denis-Courmont   Phonet: route out...
257
258
  	} else
  		dev = phonet_route_output(net, daddr);
107d0d9b8   Remi Denis-Courmont   Phonet: Phonet da...
259
260
261
262
263
264
  	if (!dev || !(dev->flags & IFF_UP))
  		goto drop;
  
  	saddr = phonet_address_get(dev, daddr);
  	if (saddr == PN_NO_ADDR)
  		goto drop;
107d0d9b8   Remi Denis-Courmont   Phonet: Phonet da...
265
266
  	if (!pn_addr(src))
  		src = pn_object(saddr, pn_obj(src));
a8059512b   Rémi Denis-Courmont   Phonet: implement...
267
  	err = pn_send(skb, dev, dst, src, res, 0);
107d0d9b8   Remi Denis-Courmont   Phonet: Phonet da...
268
269
270
271
272
273
274
275
276
277
  	dev_put(dev);
  	return err;
  
  drop:
  	kfree_skb(skb);
  	if (dev)
  		dev_put(dev);
  	return err;
  }
  EXPORT_SYMBOL(pn_skb_send);
be0c52bfe   Remi Denis-Courmont   Phonet: emit erro...
278
279
280
281
282
283
284
285
286
287
288
  /* Do not send an error message in response to an error message */
  static inline int can_respond(struct sk_buff *skb)
  {
  	const struct phonethdr *ph;
  	const struct phonetmsg *pm;
  	u8 submsg_id;
  
  	if (!pskb_may_pull(skb, 3))
  		return 0;
  
  	ph = pn_hdr(skb);
be0c52bfe   Remi Denis-Courmont   Phonet: emit erro...
289
290
  	if (ph->pn_res == PN_PREFIX && !pskb_may_pull(skb, 5))
  		return 0;
c3a90c788   Remi Denis-Courmont   Phonet: do not re...
291
292
  	if (ph->pn_res == PN_COMMGR) /* indications */
  		return 0;
be0c52bfe   Remi Denis-Courmont   Phonet: emit erro...
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
  
  	ph = pn_hdr(skb); /* re-acquires the pointer */
  	pm = pn_msg(skb);
  	if (pm->pn_msg_id != PN_COMMON_MESSAGE)
  		return 1;
  	submsg_id = (ph->pn_res == PN_PREFIX)
  		? pm->pn_e_submsg_id : pm->pn_submsg_id;
  	if (submsg_id != PN_COMM_ISA_ENTITY_NOT_REACHABLE_RESP &&
  		pm->pn_e_submsg_id != PN_COMM_SERVICE_NOT_IDENTIFIED_RESP)
  		return 1;
  	return 0;
  }
  
  static int send_obj_unreachable(struct sk_buff *rskb)
  {
  	const struct phonethdr *oph = pn_hdr(rskb);
  	const struct phonetmsg *opm = pn_msg(rskb);
  	struct phonetmsg resp;
  
  	memset(&resp, 0, sizeof(resp));
  	resp.pn_trans_id = opm->pn_trans_id;
  	resp.pn_msg_id = PN_COMMON_MESSAGE;
  	if (oph->pn_res == PN_PREFIX) {
  		resp.pn_e_res_id = opm->pn_e_res_id;
  		resp.pn_e_submsg_id = PN_COMM_ISA_ENTITY_NOT_REACHABLE_RESP;
  		resp.pn_e_orig_msg_id = opm->pn_msg_id;
  		resp.pn_e_status = 0;
  	} else {
  		resp.pn_submsg_id = PN_COMM_ISA_ENTITY_NOT_REACHABLE_RESP;
  		resp.pn_orig_msg_id = opm->pn_msg_id;
  		resp.pn_status = 0;
  	}
  	return pn_raw_send(&resp, sizeof(resp), rskb->dev,
  				pn_object(oph->pn_sdev, oph->pn_sobj),
  				pn_object(oph->pn_rdev, oph->pn_robj),
  				oph->pn_res);
  }
  
  static int send_reset_indications(struct sk_buff *rskb)
  {
  	struct phonethdr *oph = pn_hdr(rskb);
  	static const u8 data[4] = {
  		0x00 /* trans ID */, 0x10 /* subscribe msg */,
  		0x00 /* subscription count */, 0x00 /* dummy */
  	};
  
  	return pn_raw_send(data, sizeof(data), rskb->dev,
  				pn_object(oph->pn_sdev, 0x00),
c3a90c788   Remi Denis-Courmont   Phonet: do not re...
341
342
  				pn_object(oph->pn_rdev, oph->pn_robj),
  				PN_COMMGR);
be0c52bfe   Remi Denis-Courmont   Phonet: emit erro...
343
  }
4b07b3f69   Remi Denis-Courmont   Phonet: PF_PHONET...
344
345
346
347
348
349
350
351
352
353
  /* packet type functions */
  
  /*
   * Stuff received packets to associated sockets.
   * On error, returns non-zero and releases the skb.
   */
  static int phonet_rcv(struct sk_buff *skb, struct net_device *dev,
  			struct packet_type *pkttype,
  			struct net_device *orig_dev)
  {
4b8f704be   remi.denis-courmont@nokia   Phonet: check des...
354
  	struct net *net = dev_net(dev);
4b07b3f69   Remi Denis-Courmont   Phonet: PF_PHONET...
355
356
357
  	struct phonethdr *ph;
  	struct sockaddr_pn sa;
  	u16 len;
4b07b3f69   Remi Denis-Courmont   Phonet: PF_PHONET...
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
  	/* check we have at least a full Phonet header */
  	if (!pskb_pull(skb, sizeof(struct phonethdr)))
  		goto out;
  
  	/* check that the advertised length is correct */
  	ph = pn_hdr(skb);
  	len = get_unaligned_be16(&ph->pn_length);
  	if (len < 2)
  		goto out;
  	len -= 2;
  	if ((len > skb->len) || pskb_trim(skb, len))
  		goto out;
  	skb_reset_transport_header(skb);
  
  	pn_skb_get_dst_sockaddr(skb, &sa);
4b07b3f69   Remi Denis-Courmont   Phonet: PF_PHONET...
373

f14001fcd   Rémi Denis-Courmont   Phonet: deliver b...
374
375
376
377
378
  	/* check if this is broadcasted */
  	if (pn_sockaddr_get_addr(&sa) == PNADDR_BROADCAST) {
  		pn_deliver_sock_broadcast(net, skb);
  		goto out;
  	}
b6a563b2a   Rémi Denis-Courmont   Phonet: look up t...
379
380
381
382
383
384
  	/* resource routing */
  	if (pn_sockaddr_get_object(&sa) == 0) {
  		struct sock *sk = pn_find_sock_by_res(net, sa.spn_resource);
  		if (sk)
  			return sk_receive_skb(sk, skb, 0);
  	}
4b8f704be   remi.denis-courmont@nokia   Phonet: check des...
385
386
387
388
389
390
391
  	/* check if we are the destination */
  	if (phonet_address_lookup(net, pn_sockaddr_get_addr(&sa)) == 0) {
  		/* Phonet packet input */
  		struct sock *sk = pn_find_sock_by_sa(net, &sa);
  
  		if (sk)
  			return sk_receive_skb(sk, skb, 0);
be0c52bfe   Remi Denis-Courmont   Phonet: emit erro...
392
393
394
395
  		if (can_respond(skb)) {
  			send_obj_unreachable(skb);
  			send_reset_indications(skb);
  		}
86a0a1e52   Rémi Denis-Courmont   Phonet: forward i...
396
397
398
399
400
401
402
403
  	} else if (unlikely(skb->pkt_type == PACKET_LOOPBACK))
  		goto out; /* Race between address deletion and loopback */
  	else {
  		/* Phonet packet routing */
  		struct net_device *out_dev;
  
  		out_dev = phonet_route_output(net, pn_sockaddr_get_addr(&sa));
  		if (!out_dev) {
ba7a46f16   Joe Perches   net: Convert LIMI...
404
405
406
  			net_dbg_ratelimited("No Phonet route to %02X
  ",
  					    pn_sockaddr_get_addr(&sa));
86a0a1e52   Rémi Denis-Courmont   Phonet: forward i...
407
408
409
410
411
412
  			goto out;
  		}
  
  		__skb_push(skb, sizeof(struct phonethdr));
  		skb->dev = out_dev;
  		if (out_dev == dev) {
ba7a46f16   Joe Perches   net: Convert LIMI...
413
414
415
416
  			net_dbg_ratelimited("Phonet loop to %02X on %s
  ",
  					    pn_sockaddr_get_addr(&sa),
  					    dev->name);
86a0a1e52   Rémi Denis-Courmont   Phonet: forward i...
417
418
419
420
421
422
423
424
425
426
427
428
429
430
  			goto out_dev;
  		}
  		/* Some drivers (e.g. TUN) do not allocate HW header space */
  		if (skb_cow_head(skb, out_dev->hard_header_len))
  			goto out_dev;
  
  		if (dev_hard_header(skb, out_dev, ETH_P_PHONET, NULL, NULL,
  					skb->len) < 0)
  			goto out_dev;
  		dev_queue_xmit(skb);
  		dev_put(out_dev);
  		return NET_RX_SUCCESS;
  out_dev:
  		dev_put(out_dev);
be0c52bfe   Remi Denis-Courmont   Phonet: emit erro...
431
  	}
ba113a94b   Remi Denis-Courmont   Phonet: common so...
432

4b07b3f69   Remi Denis-Courmont   Phonet: PF_PHONET...
433
434
435
436
  out:
  	kfree_skb(skb);
  	return NET_RX_DROP;
  }
7546dd97d   Stephen Hemminger   net: convert usag...
437
  static struct packet_type phonet_packet_type __read_mostly = {
09640e636   Harvey Harrison   net: replace uses...
438
  	.type = cpu_to_be16(ETH_P_PHONET),
4b07b3f69   Remi Denis-Courmont   Phonet: PF_PHONET...
439
440
  	.func = phonet_rcv,
  };
7ed0132f2   Rémi Denis-Courmont   Phonet: put proto...
441
  static DEFINE_MUTEX(proto_tab_lock);
facb4edc1   Dan Carpenter   phonet: some sign...
442
  int __init_or_module phonet_proto_register(unsigned int protocol,
4b07b3f69   Remi Denis-Courmont   Phonet: PF_PHONET...
443
444
445
446
447
448
449
450
451
452
  						struct phonet_protocol *pp)
  {
  	int err = 0;
  
  	if (protocol >= PHONET_NPROTO)
  		return -EINVAL;
  
  	err = proto_register(pp->prot, 1);
  	if (err)
  		return err;
7ed0132f2   Rémi Denis-Courmont   Phonet: put proto...
453
  	mutex_lock(&proto_tab_lock);
4b07b3f69   Remi Denis-Courmont   Phonet: PF_PHONET...
454
455
456
  	if (proto_tab[protocol])
  		err = -EBUSY;
  	else
cf778b00e   Eric Dumazet   net: reintroduce ...
457
  		rcu_assign_pointer(proto_tab[protocol], pp);
7ed0132f2   Rémi Denis-Courmont   Phonet: put proto...
458
  	mutex_unlock(&proto_tab_lock);
4b07b3f69   Remi Denis-Courmont   Phonet: PF_PHONET...
459
460
461
462
  
  	return err;
  }
  EXPORT_SYMBOL(phonet_proto_register);
facb4edc1   Dan Carpenter   phonet: some sign...
463
  void phonet_proto_unregister(unsigned int protocol, struct phonet_protocol *pp)
4b07b3f69   Remi Denis-Courmont   Phonet: PF_PHONET...
464
  {
7ed0132f2   Rémi Denis-Courmont   Phonet: put proto...
465
  	mutex_lock(&proto_tab_lock);
4b07b3f69   Remi Denis-Courmont   Phonet: PF_PHONET...
466
  	BUG_ON(proto_tab[protocol] != pp);
a9b3cd7f3   Stephen Hemminger   rcu: convert uses...
467
  	RCU_INIT_POINTER(proto_tab[protocol], NULL);
7ed0132f2   Rémi Denis-Courmont   Phonet: put proto...
468
469
  	mutex_unlock(&proto_tab_lock);
  	synchronize_rcu();
4b07b3f69   Remi Denis-Courmont   Phonet: PF_PHONET...
470
471
472
  	proto_unregister(pp->prot);
  }
  EXPORT_SYMBOL(phonet_proto_unregister);
4b07b3f69   Remi Denis-Courmont   Phonet: PF_PHONET...
473
474
475
476
  /* Module registration */
  static int __init phonet_init(void)
  {
  	int err;
76e02cf69   remi.denis-courmont@nokia   Phonet: allow pho...
477
478
479
  	err = phonet_device_init();
  	if (err)
  		return err;
6b0d07ba1   Rémi Denis-Courmont   Phonet: put socke...
480
  	pn_sock_init();
4b07b3f69   Remi Denis-Courmont   Phonet: PF_PHONET...
481
482
483
484
485
  	err = sock_register(&phonet_proto_family);
  	if (err) {
  		printk(KERN_ALERT
  			"phonet protocol family initialization failed
  ");
76e02cf69   remi.denis-courmont@nokia   Phonet: allow pho...
486
  		goto err_sock;
4b07b3f69   Remi Denis-Courmont   Phonet: PF_PHONET...
487
488
489
  	}
  
  	dev_add_pack(&phonet_packet_type);
87ab4e20b   Remi Denis-Courmont   Phonet: proc inte...
490
  	phonet_sysctl_init();
107d0d9b8   Remi Denis-Courmont   Phonet: Phonet da...
491
492
493
494
  
  	err = isi_register();
  	if (err)
  		goto err;
4b07b3f69   Remi Denis-Courmont   Phonet: PF_PHONET...
495
  	return 0;
107d0d9b8   Remi Denis-Courmont   Phonet: Phonet da...
496
497
  
  err:
87ab4e20b   Remi Denis-Courmont   Phonet: proc inte...
498
  	phonet_sysctl_exit();
25532824f   Rémi Denis-Courmont   Phonet: modules a...
499
  	sock_unregister(PF_PHONET);
107d0d9b8   Remi Denis-Courmont   Phonet: Phonet da...
500
  	dev_remove_pack(&phonet_packet_type);
76e02cf69   remi.denis-courmont@nokia   Phonet: allow pho...
501
  err_sock:
107d0d9b8   Remi Denis-Courmont   Phonet: Phonet da...
502
503
  	phonet_device_exit();
  	return err;
4b07b3f69   Remi Denis-Courmont   Phonet: PF_PHONET...
504
505
506
507
  }
  
  static void __exit phonet_exit(void)
  {
107d0d9b8   Remi Denis-Courmont   Phonet: Phonet da...
508
  	isi_unregister();
87ab4e20b   Remi Denis-Courmont   Phonet: proc inte...
509
  	phonet_sysctl_exit();
25532824f   Rémi Denis-Courmont   Phonet: modules a...
510
  	sock_unregister(PF_PHONET);
4b07b3f69   Remi Denis-Courmont   Phonet: PF_PHONET...
511
  	dev_remove_pack(&phonet_packet_type);
f8ff60283   Remi Denis-Courmont   Phonet: network d...
512
  	phonet_device_exit();
4b07b3f69   Remi Denis-Courmont   Phonet: PF_PHONET...
513
514
515
516
517
518
  }
  
  module_init(phonet_init);
  module_exit(phonet_exit);
  MODULE_DESCRIPTION("Phonet protocol stack for Linux");
  MODULE_LICENSE("GPL");
25532824f   Rémi Denis-Courmont   Phonet: modules a...
519
  MODULE_ALIAS_NETPROTO(PF_PHONET);