natm.c revision 160549
1/*	$NetBSD: natm.c,v 1.5 1996/11/09 03:26:26 chuck Exp $	*/
2/*-
3 *
4 * Copyright (c) 1996 Charles D. Cranor and Washington University.
5 * Copyright (c) 2005-2006 Robert N. M. Watson
6 * All rights reserved.
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
11 * 1. Redistributions of source code must retain the above copyright
12 *    notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 *    notice, this list of conditions and the following disclaimer in the
15 *    documentation and/or other materials provided with the distribution.
16 * 3. All advertising materials mentioning features or use of this software
17 *    must display the following acknowledgement:
18 *      This product includes software developed by Charles D. Cranor and
19 *      Washington University.
20 * 4. The name of the author may not be used to endorse or promote products
21 *    derived from this software without specific prior written permission.
22 *
23 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
24 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
25 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
26 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
27 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
28 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
29 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
30 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
31 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
32 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
33 */
34
35/*
36 * natm.c: Native mode ATM access (both aal0 and aal5).
37 */
38
39#include <sys/cdefs.h>
40__FBSDID("$FreeBSD: head/sys/netnatm/natm.c 160549 2006-07-21 17:11:15Z rwatson $");
41
42#include <sys/param.h>
43#include <sys/conf.h>
44#include <sys/kernel.h>
45#include <sys/lock.h>
46#include <sys/malloc.h>
47#include <sys/mbuf.h>
48#include <sys/protosw.h>
49#include <sys/signalvar.h>
50#include <sys/socket.h>
51#include <sys/socketvar.h>
52#include <sys/sockio.h>
53#include <sys/sx.h>
54#include <sys/systm.h>
55#include <sys/sysctl.h>
56
57#include <net/if.h>
58#include <net/if_atm.h>
59#include <net/netisr.h>
60
61#include <netinet/in.h>
62
63#include <netnatm/natm.h>
64
65static const u_long	natm5_sendspace = 16*1024;
66static const u_long	natm5_recvspace = 16*1024;
67
68static const u_long	natm0_sendspace = 16*1024;
69static const u_long	natm0_recvspace = 16*1024;
70
71/*
72 * netnatm global subsystem lock, protects all global data structures in
73 * netnatm.
74 */
75struct mtx	natm_mtx;
76
77/*
78 * User socket requests.
79 */
80static int	natm_usr_attach(struct socket *, int, d_thread_t *);
81static void	natm_usr_detach(struct socket *);
82static int	natm_usr_connect(struct socket *, struct sockaddr *,
83		    d_thread_t *);
84static int	natm_usr_disconnect(struct socket *);
85static int	natm_usr_shutdown(struct socket *);
86static int	natm_usr_send(struct socket *, int, struct mbuf *,
87		    struct sockaddr *, struct mbuf *, d_thread_t *);
88static int	natm_usr_peeraddr(struct socket *, struct sockaddr **);
89static int	natm_usr_control(struct socket *, u_long, caddr_t,
90		    struct ifnet *, d_thread_t *);
91static void	natm_usr_abort(struct socket *);
92static int	natm_usr_bind(struct socket *, struct sockaddr *,
93		    d_thread_t *);
94static int	natm_usr_sockaddr(struct socket *, struct sockaddr **);
95
96static int
97natm_usr_attach(struct socket *so, int proto, d_thread_t *p)
98{
99	struct natmpcb *npcb;
100	int error = 0;
101
102	npcb = (struct natmpcb *)so->so_pcb;
103	KASSERT(npcb == NULL, ("natm_usr_attach: so_pcb != NULL"));
104
105	if (so->so_snd.sb_hiwat == 0 || so->so_rcv.sb_hiwat == 0) {
106		if (proto == PROTO_NATMAAL5)
107			error = soreserve(so, natm5_sendspace,
108			    natm5_recvspace);
109		else
110			error = soreserve(so, natm0_sendspace,
111			    natm0_recvspace);
112		if (error)
113			return (error);
114	}
115	so->so_pcb = npcb = npcb_alloc(M_WAITOK);
116	npcb->npcb_socket = so;
117	return (error);
118}
119
120static void
121natm_usr_detach(struct socket *so)
122{
123	struct natmpcb *npcb;
124
125	npcb = (struct natmpcb *)so->so_pcb;
126	KASSERT(npcb != NULL, ("natm_usr_detach: npcb == NULL"));
127
128	NATM_LOCK();
129	npcb_free(npcb, NPCB_DESTROY);	/* drain */
130	so->so_pcb = NULL;
131	NATM_UNLOCK();
132}
133
134static int
135natm_usr_connect(struct socket *so, struct sockaddr *nam, d_thread_t *p)
136{
137	struct natmpcb *npcb;
138	struct sockaddr_natm *snatm;
139	struct atmio_openvcc op;
140	struct ifnet *ifp;
141	int error = 0;
142	int proto = so->so_proto->pr_protocol;
143
144	npcb = (struct natmpcb *)so->so_pcb;
145	KASSERT(npcb != NULL, ("natm_usr_connect: npcb == NULL"));
146
147	/*
148	 * Validate nam and npcb.
149	 */
150	NATM_LOCK();
151	snatm = (struct sockaddr_natm *)nam;
152	if (snatm->snatm_len != sizeof(*snatm) ||
153		(npcb->npcb_flags & NPCB_FREE) == 0) {
154		NATM_UNLOCK();
155		return (EINVAL);
156	}
157	if (snatm->snatm_family != AF_NATM) {
158		NATM_UNLOCK();
159		return (EAFNOSUPPORT);
160	}
161
162	snatm->snatm_if[IFNAMSIZ - 1] = '\0';	/* XXX ensure null termination
163						   since ifunit() uses strcmp */
164
165	/*
166	 * Convert interface string to ifp, validate.
167	 */
168	ifp = ifunit(snatm->snatm_if);
169	if (ifp == NULL || (ifp->if_drv_flags & IFF_DRV_RUNNING) == 0) {
170		NATM_UNLOCK();
171		return (ENXIO);
172	}
173	if (ifp->if_output != atm_output) {
174		NATM_UNLOCK();
175		return (EAFNOSUPPORT);
176	}
177
178	/*
179	 * Register us with the NATM PCB layer.
180	 */
181	if (npcb_add(npcb, ifp, snatm->snatm_vci, snatm->snatm_vpi) != npcb) {
182		NATM_UNLOCK();
183		return (EADDRINUSE);
184	}
185
186	/*
187	 * Open the channel.
188	 *
189	 * XXXRW: Eventually desirable to hold mutex over ioctl?
190	 */
191	bzero(&op, sizeof(op));
192	op.rxhand = npcb;
193	op.param.flags = ATMIO_FLAG_PVC;
194	op.param.vpi = npcb->npcb_vpi;
195	op.param.vci = npcb->npcb_vci;
196	op.param.rmtu = op.param.tmtu = ifp->if_mtu;
197	op.param.aal = (proto == PROTO_NATMAAL5) ? ATMIO_AAL_5 : ATMIO_AAL_0;
198	op.param.traffic = ATMIO_TRAFFIC_UBR;
199	NATM_UNLOCK();
200
201	IFF_LOCKGIANT(ifp);
202	if (ifp->if_ioctl == NULL ||
203	    ifp->if_ioctl(ifp, SIOCATMOPENVCC, (caddr_t)&op) != 0) {
204		IFF_UNLOCKGIANT(ifp);
205		return (EIO);
206	}
207	IFF_UNLOCKGIANT(ifp);
208	soisconnected(so);
209	return (error);
210}
211
212static int
213natm_usr_disconnect(struct socket *so)
214{
215	struct natmpcb *npcb;
216	struct atmio_closevcc cl;
217	struct ifnet *ifp;
218	int error = 0;
219
220	npcb = (struct natmpcb *)so->so_pcb;
221	KASSERT(npcb != NULL, ("natm_usr_disconnect: npcb == NULL"));
222
223	NATM_LOCK();
224	if ((npcb->npcb_flags & NPCB_CONNECTED) == 0) {
225		NATM_UNLOCK();
226		printf("natm: disconnected check\n");
227		return (EIO);
228	}
229	ifp = npcb->npcb_ifp;
230
231	/*
232	 * Disable rx.
233	 *
234	 * XXXRW: Eventually desirable to hold mutex over ioctl?
235	 */
236	cl.vpi = npcb->npcb_vpi;
237	cl.vci = npcb->npcb_vci;
238	NATM_UNLOCK();
239	if (ifp->if_ioctl != NULL) {
240		IFF_LOCKGIANT(ifp);
241		ifp->if_ioctl(ifp, SIOCATMCLOSEVCC, (caddr_t)&cl);
242		IFF_UNLOCKGIANT(ifp);
243	}
244	soisdisconnected(so);
245	return (error);
246}
247
248static int
249natm_usr_shutdown(struct socket *so)
250{
251
252	socantsendmore(so);
253	return (0);
254}
255
256static int
257natm_usr_send(struct socket *so, int flags, struct mbuf *m,
258	struct sockaddr *nam, struct mbuf *control, d_thread_t *p)
259{
260	struct natmpcb *npcb;
261	struct atm_pseudohdr *aph;
262	int error = 0;
263	int proto = so->so_proto->pr_protocol;
264
265	npcb = (struct natmpcb *)so->so_pcb;
266	KASSERT(npcb != NULL, ("natm_usr_send: npcb == NULL"));
267
268	NATM_LOCK();
269	if (control && control->m_len) {
270		NATM_UNLOCK();
271		m_freem(control);
272		m_freem(m);
273		return (EINVAL);
274	}
275
276	/*
277	 * Send the data.  We must put an atm_pseudohdr on first.
278	 */
279	M_PREPEND(m, sizeof(*aph), M_DONTWAIT);
280	if (m == NULL) {
281		NATM_UNLOCK();
282		m_freem(control);
283		return (ENOBUFS);
284	}
285	aph = mtod(m, struct atm_pseudohdr *);
286	ATM_PH_VPI(aph) = npcb->npcb_vpi;
287	ATM_PH_SETVCI(aph, npcb->npcb_vci);
288	ATM_PH_FLAGS(aph) = (proto == PROTO_NATMAAL5) ? ATM_PH_AAL5 : 0;
289	error = atm_output(npcb->npcb_ifp, m, NULL, NULL);
290	NATM_UNLOCK();
291	return (error);
292}
293
294static int
295natm_usr_peeraddr(struct socket *so, struct sockaddr **nam)
296{
297	struct natmpcb *npcb;
298	struct sockaddr_natm *snatm, ssnatm;
299
300	npcb = (struct natmpcb *)so->so_pcb;
301	KASSERT(npcb != NULL, ("natm_usr_peeraddr: npcb == NULL"));
302
303	NATM_LOCK();
304	snatm = &ssnatm;
305	bzero(snatm, sizeof(*snatm));
306	snatm->snatm_len = sizeof(*snatm);
307	snatm->snatm_family = AF_NATM;
308	strlcpy(snatm->snatm_if, npcb->npcb_ifp->if_xname,
309	    sizeof(snatm->snatm_if));
310	snatm->snatm_vci = npcb->npcb_vci;
311	snatm->snatm_vpi = npcb->npcb_vpi;
312	NATM_UNLOCK();
313	*nam = sodupsockaddr((struct sockaddr *)snatm, M_WAITOK);
314	return (0);
315}
316
317static int
318natm_usr_control(struct socket *so, u_long cmd, caddr_t arg,
319	struct ifnet *ifp, d_thread_t *p)
320{
321	struct natmpcb *npcb;
322	int error;
323
324	npcb = (struct natmpcb *)so->so_pcb;
325	KASSERT(npcb != NULL, ("natm_usr_control: npcb == NULL"));
326
327	if (ifp == NULL || ifp->if_ioctl == NULL)
328		return (EOPNOTSUPP);
329	IFF_LOCKGIANT(ifp);
330	error = ((*ifp->if_ioctl)(ifp, cmd, arg));
331	IFF_UNLOCKGIANT(ifp);
332	return (error);
333}
334
335static void
336natm_usr_abort(struct socket *so)
337{
338
339}
340
341static void
342natm_usr_close(struct socket *so)
343{
344
345}
346
347static int
348natm_usr_bind(struct socket *so, struct sockaddr *nam, d_thread_t *p)
349{
350
351	return (EOPNOTSUPP);
352}
353
354static int
355natm_usr_sockaddr(struct socket *so, struct sockaddr **nam)
356{
357
358	return (EOPNOTSUPP);
359}
360
361/* xxx - should be const */
362struct pr_usrreqs natm_usrreqs = {
363	.pru_abort =		natm_usr_abort,
364	.pru_attach =		natm_usr_attach,
365	.pru_bind =		natm_usr_bind,
366	.pru_connect =		natm_usr_connect,
367	.pru_control =		natm_usr_control,
368	.pru_detach =		natm_usr_detach,
369	.pru_disconnect =	natm_usr_disconnect,
370	.pru_peeraddr =		natm_usr_peeraddr,
371	.pru_send =		natm_usr_send,
372	.pru_shutdown =		natm_usr_shutdown,
373	.pru_sockaddr =		natm_usr_sockaddr,
374	.pru_close =		natm_usr_close,
375};
376
377/*
378 * natmintr: interrupt
379 *
380 * Note: we expect a socket pointer in rcvif rather than an interface
381 * pointer.  We can get the interface pointer from the so's PCB if we really
382 * need it.
383 */
384void
385natmintr(struct mbuf *m)
386{
387	struct socket *so;
388	struct natmpcb *npcb;
389
390#ifdef DIAGNOSTIC
391	M_ASSERTPKTHDR(m);
392#endif
393
394	NATM_LOCK();
395	npcb = (struct natmpcb *)m->m_pkthdr.rcvif;	/* XXX: overloaded */
396	so = npcb->npcb_socket;
397
398	npcb->npcb_inq--;
399
400	if (npcb->npcb_flags & NPCB_DRAIN) {
401		if (npcb->npcb_inq == 0)
402			FREE(npcb, M_PCB);			/* done! */
403		NATM_UNLOCK();
404		m_freem(m);
405		return;
406	}
407
408	if (npcb->npcb_flags & NPCB_FREE) {
409		NATM_UNLOCK();
410		m_freem(m);					/* drop */
411		return;
412	}
413
414#ifdef NEED_TO_RESTORE_IFP
415	m->m_pkthdr.rcvif = npcb->npcb_ifp;
416#else
417#ifdef DIAGNOSTIC
418	m->m_pkthdr.rcvif = NULL;	/* null it out to be safe */
419#endif
420#endif
421
422	if (sbspace(&so->so_rcv) > m->m_pkthdr.len) {
423#ifdef NATM_STAT
424		natm_sookcnt++;
425		natm_sookbytes += m->m_pkthdr.len;
426#endif
427		sbappendrecord(&so->so_rcv, m);
428		sorwakeup(so);
429		NATM_UNLOCK();
430	} else {
431#ifdef NATM_STAT
432		natm_sodropcnt++;
433		natm_sodropbytes += m->m_pkthdr.len;
434#endif
435		NATM_UNLOCK();
436		m_freem(m);
437	}
438}
439
440/*
441 * natm0_sysctl: not used, but here in case we want to add something
442 * later...
443 */
444int
445natm0_sysctl(SYSCTL_HANDLER_ARGS)
446{
447
448	/* All sysctl names at this level are terminal. */
449	return (ENOENT);
450}
451
452/*
453 * natm5_sysctl: not used, but here in case we want to add something
454 * later...
455 */
456int
457natm5_sysctl(SYSCTL_HANDLER_ARGS)
458{
459
460	/* All sysctl names at this level are terminal. */
461	return (ENOENT);
462}
463