send.c revision 254523
1189251Ssam/*-
2189251Ssam * Copyright (c) 2009-2010 Ana Kukec <anchie@FreeBSD.org>
3281806Srpaulo * All rights reserved.
4189251Ssam *
5252726Srpaulo * Redistribution and use in source and binary forms, with or without
6252726Srpaulo * modification, are permitted provided that the following conditions
7189251Ssam * are met:
8189251Ssam * 1. Redistributions of source code must retain the above copyright
9189251Ssam *    notice, this list of conditions and the following disclaimer.
10189251Ssam * 2. Redistributions in binary form must reproduce the above copyright
11189251Ssam *    notice, this list of conditions and the following disclaimer in the
12189251Ssam *    documentation and/or other materials provided with the distribution.
13189251Ssam *
14189251Ssam * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15189251Ssam * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16189251Ssam * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17214734Srpaulo * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18252726Srpaulo * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19252726Srpaulo * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20252726Srpaulo * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21252726Srpaulo * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22214734Srpaulo * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23252726Srpaulo * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24252726Srpaulo * SUCH DAMAGE.
25252726Srpaulo */
26252726Srpaulo
27189251Ssam#include <sys/cdefs.h>
28189251Ssam__FBSDID("$FreeBSD: head/sys/netinet6/send.c 254523 2013-08-19 13:27:32Z andre $");
29289549Srpaulo
30189251Ssam#include <sys/param.h>
31281806Srpaulo#include <sys/kernel.h>
32189251Ssam#include <sys/mbuf.h>
33189251Ssam#include <sys/module.h>
34289549Srpaulo#include <sys/priv.h>
35252726Srpaulo#include <sys/protosw.h>
36252726Srpaulo#include <sys/systm.h>
37189251Ssam#include <sys/socket.h>
38289549Srpaulo#include <sys/sockstate.h>
39252726Srpaulo#include <sys/sockbuf.h>
40189251Ssam#include <sys/socketvar.h>
41189251Ssam#include <sys/types.h>
42189251Ssam
43189251Ssam#include <net/route.h>
44189251Ssam#include <net/if.h>
45189251Ssam#include <net/if_var.h>
46189251Ssam#include <net/vnet.h>
47189251Ssam
48189251Ssam#include <netinet/in.h>
49189251Ssam#include <netinet/ip_var.h>
50189251Ssam#include <netinet/ip6.h>
51189251Ssam#include <netinet/icmp6.h>
52189251Ssam
53189251Ssam#include <netinet6/in6_var.h>
54189251Ssam#include <netinet6/nd6.h>
55189251Ssam#include <netinet6/scope6_var.h>
56189251Ssam#include <netinet6/send.h>
57189251Ssam
58189251Ssamstatic MALLOC_DEFINE(M_SEND, "send", "Secure Neighbour Discovery");
59189251Ssam
60189251Ssam/*
61189251Ssam * The socket used to communicate with the SeND daemon.
62189251Ssam */
63189251Ssamstatic VNET_DEFINE(struct socket *, send_so);
64189251Ssam#define	V_send_so	VNET(send_so)
65189251Ssam
66189251Ssamu_long	send_sendspace	= 8 * (1024 + sizeof(struct sockaddr_send));
67189251Ssamu_long	send_recvspace	= 9216;
68189251Ssam
69189251Ssamstruct mtx	send_mtx;
70214734Srpaulo#define SEND_LOCK_INIT()	mtx_init(&send_mtx, "send_mtx", NULL, MTX_DEF)
71189251Ssam#define SEND_LOCK()		mtx_lock(&send_mtx)
72189251Ssam#define SEND_UNLOCK()		mtx_unlock(&send_mtx)
73281806Srpaulo#define SEND_LOCK_DESTROY()     mtx_destroy(&send_mtx)
74189251Ssam
75252726Srpaulostatic int
76252726Srpaulosend_attach(struct socket *so, int proto, struct thread *td)
77252726Srpaulo{
78252726Srpaulo	int error;
79289549Srpaulo
80189251Ssam	SEND_LOCK();
81189251Ssam	if (V_send_so != NULL) {
82189251Ssam		SEND_UNLOCK();
83189251Ssam		return (EEXIST);
84214734Srpaulo	}
85281806Srpaulo
86189251Ssam	error = priv_check(td, PRIV_NETINET_RAW);
87252726Srpaulo	if (error) {
88252726Srpaulo		SEND_UNLOCK();
89252726Srpaulo		return(error);
90252726Srpaulo	}
91189251Ssam
92252726Srpaulo	if (proto != IPPROTO_SEND) {
93252726Srpaulo		SEND_UNLOCK();
94252726Srpaulo		return (EPROTONOSUPPORT);
95281806Srpaulo	}
96289549Srpaulo	error = soreserve(so, send_sendspace, send_recvspace);
97189251Ssam	if (error) {
98189251Ssam		SEND_UNLOCK();
99252726Srpaulo		return(error);
100252726Srpaulo	}
101252726Srpaulo
102252726Srpaulo	V_send_so = so;
103252726Srpaulo	SEND_UNLOCK();
104289549Srpaulo
105252726Srpaulo	return (0);
106252726Srpaulo}
107189251Ssam
108189251Ssamstatic int
109189251Ssamsend_output(struct mbuf *m, struct ifnet *ifp, int direction)
110189251Ssam{
111289549Srpaulo	struct ip6_hdr *ip6;
112289549Srpaulo	struct sockaddr_in6 dst;
113289549Srpaulo	struct icmp6_hdr *icmp6;
114189251Ssam	int icmp6len;
115189251Ssam
116189251Ssam	/*
117189251Ssam	 * Receive incoming (SeND-protected) or outgoing traffic
118189251Ssam	 * (SeND-validated) from the SeND user space application.
119189251Ssam	 */
120189251Ssam
121252726Srpaulo	switch (direction) {
122189251Ssam	case SND_IN:
123252726Srpaulo		if (m->m_len < (sizeof(struct ip6_hdr) +
124189251Ssam		    sizeof(struct icmp6_hdr))) {
125189251Ssam			m = m_pullup(m, sizeof(struct ip6_hdr) +
126189251Ssam			    sizeof(struct icmp6_hdr));
127252726Srpaulo			if (!m)
128252726Srpaulo				return (ENOBUFS);
129252726Srpaulo		}
130252726Srpaulo
131252726Srpaulo		/* Before passing off the mbuf record the proper interface. */
132252726Srpaulo		m->m_pkthdr.rcvif = ifp;
133214734Srpaulo
134252726Srpaulo		if (m->m_flags & M_PKTHDR)
135252726Srpaulo			icmp6len = m->m_pkthdr.len - sizeof(struct ip6_hdr);
136189251Ssam		else
137252726Srpaulo			panic("Doh! not the first mbuf.");
138252726Srpaulo
139252726Srpaulo		ip6 = mtod(m, struct ip6_hdr *);
140214734Srpaulo		icmp6 = (struct icmp6_hdr *)(ip6 + 1);
141214734Srpaulo
142252726Srpaulo		/*
143252726Srpaulo		 * Output the packet as icmp6.c:icpm6_input() would do.
144252726Srpaulo		 * The mbuf is always consumed, so we do not have to
145214734Srpaulo		 * care about that.
146252726Srpaulo		 */
147252726Srpaulo		switch (icmp6->icmp6_type) {
148252726Srpaulo		case ND_NEIGHBOR_SOLICIT:
149252726Srpaulo			nd6_ns_input(m, sizeof(struct ip6_hdr), icmp6len);
150252726Srpaulo			break;
151252726Srpaulo		case ND_NEIGHBOR_ADVERT:
152252726Srpaulo			nd6_na_input(m, sizeof(struct ip6_hdr), icmp6len);
153214734Srpaulo			break;
154214734Srpaulo		case ND_REDIRECT:
155252726Srpaulo			icmp6_redirect_input(m, sizeof(struct ip6_hdr));
156252726Srpaulo			break;
157252726Srpaulo		case ND_ROUTER_SOLICIT:
158252726Srpaulo			nd6_rs_input(m, sizeof(struct ip6_hdr), icmp6len);
159252726Srpaulo			break;
160252726Srpaulo		case ND_ROUTER_ADVERT:
161252726Srpaulo			nd6_ra_input(m, sizeof(struct ip6_hdr), icmp6len);
162252726Srpaulo			break;
163252726Srpaulo		default:
164252726Srpaulo			return (ENOSYS);
165252726Srpaulo		}
166252726Srpaulo		return (0);
167252726Srpaulo
168252726Srpaulo	case SND_OUT:
169252726Srpaulo		if (m->m_len < sizeof(struct ip6_hdr)) {
170252726Srpaulo			m = m_pullup(m, sizeof(struct ip6_hdr));
171252726Srpaulo			if (!m)
172252726Srpaulo				return (ENOBUFS);
173252726Srpaulo		}
174252726Srpaulo		ip6 = mtod(m, struct ip6_hdr *);
175252726Srpaulo		if (IN6_IS_ADDR_MULTICAST(&ip6->ip6_dst))
176289549Srpaulo			m->m_flags |= M_MCAST;
177289549Srpaulo
178252726Srpaulo		bzero(&dst, sizeof(dst));
179252726Srpaulo		dst.sin6_family = AF_INET6;
180252726Srpaulo		dst.sin6_len = sizeof(dst);
181289549Srpaulo		dst.sin6_addr = ip6->ip6_dst;
182252726Srpaulo
183252726Srpaulo		m_clrprotoflags(m);	/* Avoid confusing lower layers. */
184281806Srpaulo
185252726Srpaulo		/*
186252726Srpaulo		 * Output the packet as nd6.c:nd6_output_lle() would do.
187252726Srpaulo		 * The mbuf is always consumed, so we do not have to care
188252726Srpaulo		 * about that.
189252726Srpaulo		 * XXX-BZ as we added data, what about fragmenting,
190252726Srpaulo		 * if now needed?
191252726Srpaulo		 */
192252726Srpaulo		int error;
193252726Srpaulo		error = ((*ifp->if_output)(ifp, m, (struct sockaddr *)&dst,
194252726Srpaulo		    NULL));
195252726Srpaulo		if (error)
196252726Srpaulo			error = ENOENT;
197252726Srpaulo		return (error);
198252726Srpaulo
199252726Srpaulo	default:
200252726Srpaulo		panic("%s: direction %d neither SND_IN nor SND_OUT.",
201252726Srpaulo		     __func__, direction);
202252726Srpaulo	}
203252726Srpaulo}
204252726Srpaulo
205252726Srpaulo/*
206252726Srpaulo * Receive a SeND message from user space to be either send out by the kernel
207252726Srpaulo * or, with SeND ICMPv6 options removed, to be further processed by the icmp6
208252726Srpaulo * input path.
209252726Srpaulo */
210252726Srpaulostatic int
211252726Srpaulosend_send(struct socket *so, int flags, struct mbuf *m, struct sockaddr *nam,
212252726Srpaulo    struct mbuf *control, struct thread *td)
213252726Srpaulo{
214252726Srpaulo	struct sockaddr_send *sendsrc;
215252726Srpaulo	struct ifnet *ifp;
216252726Srpaulo	int error;
217252726Srpaulo
218252726Srpaulo	KASSERT(V_send_so == so, ("%s: socket %p not send socket %p",
219252726Srpaulo		__func__, so, V_send_so));
220252726Srpaulo
221252726Srpaulo	sendsrc = (struct sockaddr_send *)nam;
222289549Srpaulo	ifp = ifnet_byindex_ref(sendsrc->send_ifidx);
223252726Srpaulo	if (ifp == NULL) {
224252726Srpaulo		error = ENETUNREACH;
225289549Srpaulo		goto err;
226289549Srpaulo	}
227252726Srpaulo
228252726Srpaulo	error = send_output(m, ifp, sendsrc->send_direction);
229252726Srpaulo	if_rele(ifp);
230252726Srpaulo	m = NULL;
231289549Srpaulo
232252726Srpauloerr:
233252726Srpaulo	if (m != NULL)
234281806Srpaulo		m_freem(m);
235252726Srpaulo	return (error);
236252726Srpaulo}
237252726Srpaulo
238252726Srpaulostatic void
239252726Srpaulosend_close(struct socket *so)
240252726Srpaulo{
241252726Srpaulo
242252726Srpaulo	SEND_LOCK();
243252726Srpaulo	if (V_send_so)
244252726Srpaulo		V_send_so = NULL;
245252726Srpaulo	SEND_UNLOCK();
246252726Srpaulo}
247252726Srpaulo
248252726Srpaulo/*
249252726Srpaulo * Send a SeND message to user space, that was either received and has to be
250252726Srpaulo * validated or was about to be send out and has to be handled by the SEND
251252726Srpaulo * daemon adding SeND ICMPv6 options.
252252726Srpaulo */
253252726Srpaulostatic int
254252726Srpaulosend_input(struct mbuf *m, struct ifnet *ifp, int direction, int msglen __unused)
255252726Srpaulo{
256252726Srpaulo	struct ip6_hdr *ip6;
257214734Srpaulo	struct sockaddr_send sendsrc;
258252726Srpaulo
259252726Srpaulo	SEND_LOCK();
260252726Srpaulo	if (V_send_so == NULL) {
261252726Srpaulo		SEND_UNLOCK();
262252726Srpaulo		return (-1);
263252726Srpaulo	}
264252726Srpaulo
265252726Srpaulo	/*
266252726Srpaulo	 * Make sure to clear any possible internally embedded scope before
267252726Srpaulo	 * passing the packet to user space for SeND cryptographic signature
268252726Srpaulo	 * validation to succeed.
269252726Srpaulo	 */
270252726Srpaulo	ip6 = mtod(m, struct ip6_hdr *);
271252726Srpaulo	in6_clearscope(&ip6->ip6_src);
272252726Srpaulo	in6_clearscope(&ip6->ip6_dst);
273252726Srpaulo
274214734Srpaulo	bzero(&sendsrc, sizeof(sendsrc));
275214734Srpaulo	sendsrc.send_len = sizeof(sendsrc);
276252726Srpaulo	sendsrc.send_family = AF_INET6;
277252726Srpaulo	sendsrc.send_direction = direction;
278252726Srpaulo	sendsrc.send_ifidx = ifp->if_index;
279252726Srpaulo
280214734Srpaulo	/*
281214734Srpaulo	 * Send incoming or outgoing traffic to user space either to be
282214734Srpaulo	 * protected (outgoing) or validated (incoming) according to rfc3971.
283252726Srpaulo	 */
284252726Srpaulo	SOCKBUF_LOCK(&V_send_so->so_rcv);
285252726Srpaulo	if (sbappendaddr_locked(&V_send_so->so_rcv,
286252726Srpaulo	    (struct sockaddr *)&sendsrc, m, NULL) == 0) {
287252726Srpaulo		SOCKBUF_UNLOCK(&V_send_so->so_rcv);
288252726Srpaulo		/* XXX stats. */
289252726Srpaulo		m_freem(m);
290252726Srpaulo	} else {
291252726Srpaulo		sorwakeup_locked(V_send_so);
292252726Srpaulo	}
293252726Srpaulo
294252726Srpaulo	SEND_UNLOCK();
295252726Srpaulo	return (0);
296252726Srpaulo}
297252726Srpaulo
298252726Srpaulostruct pr_usrreqs send_usrreqs = {
299252726Srpaulo	.pru_attach =		send_attach,
300252726Srpaulo	.pru_send =		send_send,
301252726Srpaulo	.pru_detach =		send_close
302252726Srpaulo};
303252726Srpaulostruct protosw send_protosw = {
304252726Srpaulo	.pr_type =		SOCK_RAW,
305252726Srpaulo	.pr_flags =		PR_ATOMIC|PR_ADDR,
306252726Srpaulo	.pr_protocol =		IPPROTO_SEND,
307252726Srpaulo	.pr_usrreqs =		&send_usrreqs
308252726Srpaulo};
309252726Srpaulo
310214734Srpaulostatic int
311214734Srpaulosend_modevent(module_t mod, int type, void *unused)
312189251Ssam{
313189251Ssam#ifdef __notyet__
314214734Srpaulo	VNET_ITERATOR_DECL(vnet_iter);
315214734Srpaulo#endif
316214734Srpaulo	int error;
317214734Srpaulo
318214734Srpaulo	switch (type) {
319214734Srpaulo	case MOD_LOAD:
320214734Srpaulo		SEND_LOCK_INIT();
321189251Ssam
322252726Srpaulo		error = pf_proto_register(PF_INET6, &send_protosw);
323189251Ssam		if (error != 0) {
324189251Ssam			printf("%s:%d: MOD_LOAD pf_proto_register(): %d\n",
325189251Ssam			   __func__, __LINE__, error);
326214734Srpaulo			SEND_LOCK_DESTROY();
327189251Ssam			break;
328252726Srpaulo		}
329252726Srpaulo		send_sendso_input_hook = send_input;
330252726Srpaulo		break;
331252726Srpaulo	case MOD_UNLOAD:
332252726Srpaulo		/* Do not allow unloading w/o locking. */
333189251Ssam		return (EBUSY);
334252726Srpaulo#ifdef __notyet__
335189251Ssam		VNET_LIST_RLOCK_NOSLEEP();
336289549Srpaulo		SEND_LOCK();
337289549Srpaulo		VNET_FOREACH(vnet_iter) {
338289549Srpaulo			CURVNET_SET(vnet_iter);
339289549Srpaulo			if (V_send_so != NULL) {
340289549Srpaulo				CURVNET_RESTORE();
341289549Srpaulo				SEND_UNLOCK();
342289549Srpaulo				VNET_LIST_RUNLOCK_NOSLEEP();
343252726Srpaulo				return (EBUSY);
344252726Srpaulo			}
345252726Srpaulo			CURVNET_RESTORE();
346252726Srpaulo		}
347252726Srpaulo		SEND_UNLOCK();
348252726Srpaulo		VNET_LIST_RUNLOCK_NOSLEEP();
349252726Srpaulo		error = pf_proto_unregister(PF_INET6, IPPROTO_SEND, SOCK_RAW);
350281806Srpaulo		if (error == 0)
351252726Srpaulo			SEND_LOCK_DESTROY();
352252726Srpaulo		send_sendso_input_hook = NULL;
353252726Srpaulo		break;
354252726Srpaulo#endif
355252726Srpaulo	default:
356289549Srpaulo		error = 0;
357214734Srpaulo		break;
358214734Srpaulo	}
359214734Srpaulo
360214734Srpaulo	return (error);
361214734Srpaulo}
362214734Srpaulo
363289549Srpaulostatic moduledata_t sendmod = {
364214734Srpaulo	"send",
365214734Srpaulo	send_modevent,
366189251Ssam	0
367189251Ssam};
368214734Srpaulo
369214734SrpauloDECLARE_MODULE(send, sendmod, SI_SUB_PROTO_DOMAIN, SI_ORDER_ANY);
370214734Srpaulo