1/*-
2 * Copyright (c) 2010-2012 The NetBSD Foundation, Inc.
3 * All rights reserved.
4 *
5 * This material is based upon work partially supported by The
6 * NetBSD Foundation under a contract with Mindaugas Rasiukevicius.
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 *
17 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
18 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
19 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
20 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
21 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
22 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
23 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
24 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
25 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
26 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
27 * POSSIBILITY OF SUCH DAMAGE.
28 */
29
30/*
31 * NPF logging extension.
32 */
33
34#ifdef _KERNEL
35#include <sys/cdefs.h>
36__KERNEL_RCSID(0, "$NetBSD: npf_ext_log.c,v 1.17 2020/05/30 14:16:56 rmind Exp $");
37
38#include <sys/types.h>
39#include <sys/module.h>
40
41#include <sys/conf.h>
42#include <sys/kmem.h>
43#include <sys/mbuf.h>
44#include <sys/mutex.h>
45#include <sys/queue.h>
46
47#include <net/if.h>
48#include <net/if_types.h>
49#include <net/bpf.h>
50#endif
51
52#include "npf_impl.h"
53#include "if_npflog.h"
54
55NPF_EXT_MODULE(npf_ext_log, "");
56
57#define	NPFEXT_LOG_VER		1
58
59static void *		npf_ext_log_id;
60
61typedef struct {
62	unsigned int	if_idx;
63} npf_ext_log_t;
64
65static int
66npf_log_ctor(npf_rproc_t *rp, const nvlist_t *params)
67{
68	npf_ext_log_t *meta;
69
70	meta = kmem_zalloc(sizeof(npf_ext_log_t), KM_SLEEP);
71	meta->if_idx = dnvlist_get_number(params, "log-interface", 0);
72	npf_rproc_assign(rp, meta);
73	return 0;
74}
75
76static void
77npf_log_dtor(npf_rproc_t *rp, void *meta)
78{
79	kmem_free(meta, sizeof(npf_ext_log_t));
80}
81
82static bool
83npf_log(npf_cache_t *npc, void *meta, const npf_match_info_t *mi, int *decision)
84{
85	struct mbuf *m = nbuf_head_mbuf(npc->npc_nbuf);
86	const npf_ext_log_t *log = meta;
87	struct psref psref;
88	ifnet_t *ifp;
89	struct npfloghdr hdr;
90
91	memset(&hdr, 0, sizeof(hdr));
92	/* Set the address family. */
93	if (npf_iscached(npc, NPC_IP4)) {
94		hdr.af = AF_INET;
95	} else if (npf_iscached(npc, NPC_IP6)) {
96		hdr.af = AF_INET6;
97	} else {
98		hdr.af = AF_UNSPEC;
99	}
100
101	hdr.length = NPFLOG_REAL_HDRLEN;
102	hdr.action = *decision == NPF_DECISION_PASS ?
103	    0 /* pass */ : 1 /* block */;
104	hdr.reason = 0;	/* match */
105
106	struct nbuf *nb = npc->npc_nbuf;
107	npf_ifmap_copyname(npc->npc_ctx, nb ? nb->nb_ifid : 0,
108	    hdr.ifname, sizeof(hdr.ifname));
109
110	hdr.rulenr = htonl((uint32_t)mi->mi_rid);
111	hdr.subrulenr = htonl((uint32_t)(mi->mi_rid >> 32));
112	strlcpy(hdr.ruleset, "rules", sizeof(hdr.ruleset));
113
114	hdr.uid = UID_MAX;
115	hdr.pid = (pid_t)-1;
116	hdr.rule_uid = UID_MAX;
117	hdr.rule_pid = (pid_t)-1;
118
119	switch (mi->mi_di) {
120	default:
121	case PFIL_IN|PFIL_OUT:
122		hdr.dir = 0;
123		break;
124	case PFIL_IN:
125		hdr.dir = 1;
126		break;
127	case PFIL_OUT:
128		hdr.dir = 2;
129		break;
130	}
131
132	KERNEL_LOCK(1, NULL);
133
134	/* Find a pseudo-interface to log. */
135	ifp = if_get_byindex(log->if_idx, &psref);
136	if (ifp == NULL) {
137		/* No interface. */
138		KERNEL_UNLOCK_ONE(NULL);
139		return true;
140	}
141
142	if_statadd2(ifp, if_opackets, 1, if_obytes, m->m_pkthdr.len);
143	if (ifp->if_bpf) {
144		/* Pass through BPF. */
145		bpf_mtap2(ifp->if_bpf, &hdr, NPFLOG_HDRLEN, m, BPF_D_OUT);
146	}
147	if_put(ifp, &psref);
148
149	KERNEL_UNLOCK_ONE(NULL);
150
151	return true;
152}
153
154__dso_public int
155npf_ext_log_init(npf_t *npf)
156{
157	static const npf_ext_ops_t npf_log_ops = {
158		.version	= NPFEXT_LOG_VER,
159		.ctx		= NULL,
160		.ctor		= npf_log_ctor,
161		.dtor		= npf_log_dtor,
162		.proc		= npf_log
163	};
164	npf_ext_log_id = npf_ext_register(npf, "log", &npf_log_ops);
165	return npf_ext_log_id ? 0 : EEXIST;
166}
167
168__dso_public int
169npf_ext_log_fini(npf_t *npf)
170{
171	return npf_ext_unregister(npf, npf_ext_log_id);
172}
173
174#ifdef _KERNEL
175static int
176npf_ext_log_modcmd(modcmd_t cmd, void *arg)
177{
178	npf_t *npf = npf_getkernctx();
179
180	switch (cmd) {
181	case MODULE_CMD_INIT:
182		return npf_ext_log_init(npf);
183	case MODULE_CMD_FINI:
184		return npf_ext_log_fini(npf);
185		break;
186	case MODULE_CMD_AUTOUNLOAD:
187		return npf_autounload_p() ? 0 : EBUSY;
188	default:
189		return ENOTTY;
190	}
191	return 0;
192}
193#endif
194