1/*	$NetBSD: npf_ext_log.c,v 1.1.4.2 2012/11/18 22:38:26 riz Exp $	*/
2
3/*-
4 * Copyright (c) 2010-2012 The NetBSD Foundation, Inc.
5 * All rights reserved.
6 *
7 * This material is based upon work partially supported by The
8 * NetBSD Foundation under a contract with Mindaugas Rasiukevicius.
9 *
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
12 * are met:
13 * 1. Redistributions of source code must retain the above copyright
14 *    notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 *    notice, this list of conditions and the following disclaimer in the
17 *    documentation and/or other materials provided with the distribution.
18 *
19 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
20 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
21 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
23 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29 * POSSIBILITY OF SUCH DAMAGE.
30 */
31
32/*
33 * NPF logging extension.
34 */
35
36#include <sys/cdefs.h>
37__KERNEL_RCSID(0, "$NetBSD: npf_ext_log.c,v 1.1.4.2 2012/11/18 22:38:26 riz Exp $");
38
39#include <sys/types.h>
40#include <sys/module.h>
41
42#include <sys/conf.h>
43#include <sys/kmem.h>
44#include <sys/mbuf.h>
45#include <sys/mutex.h>
46#include <sys/queue.h>
47
48#include <net/if.h>
49#include <net/if_types.h>
50#include <net/bpf.h>
51
52#include "npf_impl.h"
53
54NPF_EXT_MODULE(npf_ext_log, "");
55
56#define	NPFEXT_LOG_VER		1
57
58static void *		npf_ext_log_id;
59
60typedef struct {
61	unsigned int	if_idx;
62} npf_ext_log_t;
63
64typedef struct npflog_softc {
65	LIST_ENTRY(npflog_softc)	sc_entry;
66	kmutex_t			sc_lock;
67	ifnet_t				sc_if;
68	int				sc_unit;
69} npflog_softc_t;
70
71static int	npflog_clone_create(struct if_clone *, int);
72static int	npflog_clone_destroy(ifnet_t *);
73
74static LIST_HEAD(, npflog_softc)	npflog_if_list	__cacheline_aligned;
75static struct if_clone			npflog_cloner =
76    IF_CLONE_INITIALIZER("npflog", npflog_clone_create, npflog_clone_destroy);
77
78void
79npflogattach(int nunits)
80{
81
82	LIST_INIT(&npflog_if_list);
83	if_clone_attach(&npflog_cloner);
84}
85
86void
87npflogdetach(void)
88{
89	npflog_softc_t *sc;
90
91	while ((sc = LIST_FIRST(&npflog_if_list)) != NULL) {
92		npflog_clone_destroy(&sc->sc_if);
93	}
94	if_clone_detach(&npflog_cloner);
95}
96
97static int
98npflog_ioctl(ifnet_t *ifp, u_long cmd, void *data)
99{
100	npflog_softc_t *sc = ifp->if_softc;
101	int error = 0;
102
103	mutex_enter(&sc->sc_lock);
104	switch (cmd) {
105	case SIOCINITIFADDR:
106		ifp->if_flags |= (IFF_UP | IFF_RUNNING);
107		break;
108	default:
109		error = ifioctl_common(ifp, cmd, data);
110		break;
111	}
112	mutex_exit(&sc->sc_lock);
113	return error;
114}
115
116static int
117npflog_clone_create(struct if_clone *ifc, int unit)
118{
119	npflog_softc_t *sc;
120	ifnet_t *ifp;
121
122	sc = kmem_zalloc(sizeof(npflog_softc_t), KM_SLEEP);
123	mutex_init(&sc->sc_lock, MUTEX_DEFAULT, IPL_SOFTNET);
124
125	ifp = &sc->sc_if;
126	ifp->if_softc = sc;
127
128	if_initname(ifp, "npflog", unit);
129	ifp->if_type = IFT_OTHER;
130	ifp->if_dlt = DLT_NULL;
131	ifp->if_ioctl = npflog_ioctl;
132
133	KERNEL_LOCK(1, NULL);
134	if_attach(ifp);
135	if_alloc_sadl(ifp);
136	bpf_attach(ifp, DLT_NULL, 0);
137	LIST_INSERT_HEAD(&npflog_if_list, sc, sc_entry);
138	KERNEL_UNLOCK_ONE(NULL);
139
140	return 0;
141}
142
143static int
144npflog_clone_destroy(ifnet_t *ifp)
145{
146	npflog_softc_t *sc = ifp->if_softc;
147
148	KERNEL_LOCK(1, NULL);
149	LIST_REMOVE(sc, sc_entry);
150	bpf_detach(ifp);
151	if_detach(ifp);
152	KERNEL_UNLOCK_ONE(NULL);
153
154	mutex_destroy(&sc->sc_lock);
155	kmem_free(sc, sizeof(npflog_softc_t));
156	return 0;
157}
158
159static int
160npf_log_ctor(npf_rproc_t *rp, prop_dictionary_t params)
161{
162	npf_ext_log_t *meta;
163
164	meta = kmem_zalloc(sizeof(npf_ext_log_t), KM_SLEEP);
165	prop_dictionary_get_uint32(params, "log-interface", &meta->if_idx);
166	npf_rproc_assign(rp, meta);
167	return 0;
168}
169
170static void
171npf_log_dtor(npf_rproc_t *rp, void *meta)
172{
173	kmem_free(meta, sizeof(npf_ext_log_t));
174}
175
176static void
177npf_log(npf_cache_t *npc, nbuf_t *nbuf, void *meta, int *decision)
178{
179	struct mbuf *m = nbuf_head_mbuf(nbuf);
180	const npf_ext_log_t *log = meta;
181	ifnet_t *ifp;
182	int family;
183
184	/* Set the address family. */
185	if (npf_iscached(npc, NPC_IP4)) {
186		family = AF_INET;
187	} else if (npf_iscached(npc, NPC_IP6)) {
188		family = AF_INET6;
189	} else {
190		family = AF_UNSPEC;
191	}
192
193	KERNEL_LOCK(1, NULL);
194
195	/* Find a pseudo-interface to log. */
196	ifp = if_byindex(log->if_idx);
197	if (ifp == NULL) {
198		/* No interface. */
199		KERNEL_UNLOCK_ONE(NULL);
200		return;
201	}
202
203	/* Pass through BPF. */
204	ifp->if_opackets++;
205	ifp->if_obytes += m->m_pkthdr.len;
206	bpf_mtap_af(ifp, family, m);
207	KERNEL_UNLOCK_ONE(NULL);
208}
209
210/*
211 * Module interface.
212 */
213static int
214npf_ext_log_modcmd(modcmd_t cmd, void *arg)
215{
216	static const npf_ext_ops_t npf_log_ops = {
217		.version	= NPFEXT_LOG_VER,
218		.ctx		= NULL,
219		.ctor		= npf_log_ctor,
220		.dtor		= npf_log_dtor,
221		.proc		= npf_log
222	};
223	int error;
224
225	switch (cmd) {
226	case MODULE_CMD_INIT:
227		/*
228		 * Initialise the NPF logging extension.
229		 */
230		npflogattach(1);
231		npf_ext_log_id = npf_ext_register("log", &npf_log_ops);
232		if (!npf_ext_log_id) {
233			npflogdetach();
234			return EEXIST;
235		}
236		break;
237
238	case MODULE_CMD_FINI:
239		error = npf_ext_unregister(npf_ext_log_id);
240		if (error) {
241			return error;
242		}
243		npflogdetach();
244		break;
245
246	case MODULE_CMD_AUTOUNLOAD:
247		/* Allow auto-unload only if NPF permits it. */
248		return npf_autounload_p() ? 0 : EBUSY;
249
250	default:
251		return ENOTTY;
252	}
253	return 0;
254}
255