1/*-
2 * Copyright (c) 2012 The NetBSD Foundation, Inc.
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 *    notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 *    notice, this list of conditions and the following disclaimer in the
12 *    documentation and/or other materials provided with the distribution.
13 *
14 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
15 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
16 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
17 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
18 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
19 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
20 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
21 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
22 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
23 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
24 * POSSIBILITY OF SUCH DAMAGE.
25 */
26
27/*
28 * NPF random blocking extension - kernel module.
29 * This is also a demo extension.
30 */
31
32#ifdef _KERNEL
33#include <sys/cdefs.h>
34__KERNEL_RCSID(0, "$NetBSD: npf_ext_rndblock.c,v 1.9 2020/05/30 14:16:56 rmind Exp $");
35
36#include <sys/types.h>
37#include <sys/cprng.h>
38#include <sys/atomic.h>
39#include <sys/module.h>
40#include <sys/kmem.h>
41#endif
42
43#include "npf_impl.h"
44
45/*
46 * NPF extension module definition and the identifier.
47 */
48NPF_EXT_MODULE(npf_ext_rndblock, "");
49
50#define	NPFEXT_RNDBLOCK_VER		1
51
52static void *		npf_ext_rndblock_id;
53
54#define	PERCENTAGE_BASE	10000
55
56/*
57 * Meta-data structure, containing parameters.
58 */
59typedef struct {
60	unsigned int	mod;
61	unsigned long	counter;
62	unsigned int	percentage;
63} npf_ext_rndblock_t;
64
65/*
66 * npf_ext_rndblock_ctor: a constructor to parse and store any parameters
67 * associated with a rule procedure, which is being newly created.
68 */
69static int
70npf_ext_rndblock_ctor(npf_rproc_t *rp, const nvlist_t *params)
71{
72	npf_ext_rndblock_t *meta;
73
74	/*
75	 * Allocate and a associate a structure for the parameter
76	 * and our meta-data.
77	 */
78	meta = kmem_zalloc(sizeof(npf_ext_rndblock_t), KM_SLEEP);
79	meta->mod = dnvlist_get_number(params, "mod", 0);
80	meta->percentage = dnvlist_get_number(params, "percentage", 0);
81	npf_rproc_assign(rp, meta);
82
83	return 0;
84}
85
86/*
87 * npf_ext_rndblock_dtor: a destructor for our rule procedure.
88 */
89static void
90npf_ext_rndblock_dtor(npf_rproc_t *rp, void *meta)
91{
92	/* Free our meta-data, associated with the procedure. */
93	kmem_free(meta, sizeof(npf_ext_rndblock_t));
94}
95
96/*
97 * npf_ext_rndblock: main routine implementing the extension functionality.
98 */
99static bool
100npf_ext_rndblock(npf_cache_t *npc, void *meta, const npf_match_info_t *mi,
101    int *decision)
102{
103	npf_ext_rndblock_t *rndblock = meta;
104	unsigned long c;
105
106	/* Skip, if already blocking. */
107	if (*decision == NPF_DECISION_BLOCK) {
108		return true;
109	}
110
111	/*
112	 * Sample demo:
113	 *
114	 * Drop the packets according to the given module or percentage.
115	 *
116	 * Rule procedures may be executed concurrently in an SMP system.
117	 * Use atomic operation to increment the counter.
118	 */
119	c = atomic_inc_ulong_nv(&rndblock->counter);
120
121	if (rndblock->mod) {
122		if ((c % rndblock->mod) == 0) {
123			*decision = NPF_DECISION_BLOCK;
124		}
125	}
126
127	if (rndblock->percentage) {
128		uint32_t w = cprng_fast32() % PERCENTAGE_BASE;
129		if (w <= rndblock->percentage) {
130			*decision = NPF_DECISION_BLOCK;
131		}
132	}
133
134	return true;
135}
136
137__dso_public int
138npf_ext_rndblock_init(npf_t *npf)
139{
140	static const npf_ext_ops_t npf_rndblock_ops = {
141		.version	= NPFEXT_RNDBLOCK_VER,
142		.ctx		= NULL,
143		.ctor		= npf_ext_rndblock_ctor,
144		.dtor		= npf_ext_rndblock_dtor,
145		.proc		= npf_ext_rndblock
146	};
147
148	/*
149	 * Initialize the NPF extension.  Register the "rndblock" extension
150	 * calls (constructor, destructor, the processing routine, etc).
151	 */
152	npf_ext_rndblock_id = npf_ext_register(npf, "rndblock",
153	    &npf_rndblock_ops);
154	return npf_ext_rndblock_id ? 0 : EEXIST;
155}
156
157__dso_public int
158npf_ext_rndblock_fini(npf_t *npf)
159{
160	/*
161	 * Remove the rndblock extension.  NPF may return an if there
162	 * are active references and it cannot drain them.
163	 */
164	return npf_ext_unregister(npf, npf_ext_rndblock_id);
165}
166
167#ifdef _KERNEL
168/*
169 * Kernel module interface.
170 */
171static int
172npf_ext_rndblock_modcmd(modcmd_t cmd, void *arg)
173{
174	npf_t *npf = npf_getkernctx();
175
176	switch (cmd) {
177	case MODULE_CMD_INIT:
178		return npf_ext_rndblock_init(npf);
179	case MODULE_CMD_FINI:
180		return npf_ext_rndblock_fini(npf);
181	case MODULE_CMD_AUTOUNLOAD:
182		/* Allow auto-unload only if NPF permits it. */
183		return npf_autounload_p() ? 0 : EBUSY;
184	default:
185		return ENOTTY;
186	}
187	return 0;
188}
189#endif
190