Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/freebsd-src
Path: blob/main/sys/netlink/netlink_module.c
39475 views
1
/*-
2
* SPDX-License-Identifier: BSD-2-Clause
3
*
4
* Copyright (c) 2021 Ng Peng Nam Sean
5
* Copyright (c) 2022 Alexander V. Chernikov <[email protected]>
6
*
7
* Redistribution and use in source and binary forms, with or without
8
* modification, are permitted provided that the following conditions
9
* are met:
10
* 1. Redistributions of source code must retain the above copyright
11
* notice, this list of conditions and the following disclaimer.
12
* 2. Redistributions in binary form must reproduce the above copyright
13
* notice, this list of conditions and the following disclaimer in the
14
* documentation and/or other materials provided with the distribution.
15
*
16
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
17
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
20
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26
* SUCH DAMAGE.
27
*/
28
29
#include <sys/param.h>
30
#include <sys/kernel.h>
31
#include <sys/malloc.h>
32
#include <sys/module.h>
33
34
#include <sys/lock.h>
35
#include <sys/rmlock.h>
36
#include <sys/ck.h>
37
#include <sys/syslog.h>
38
39
#include <netlink/netlink.h>
40
#include <netlink/netlink_ctl.h>
41
#include <netlink/netlink_var.h>
42
#include <netlink/route/route_var.h>
43
44
#include <machine/atomic.h>
45
46
FEATURE(netlink, "Netlink support");
47
48
#define DEBUG_MOD_NAME nl_mod
49
#define DEBUG_MAX_LEVEL LOG_DEBUG3
50
#include <netlink/netlink_debug.h>
51
_DECLARE_DEBUG(LOG_INFO);
52
53
54
#define NL_MAX_HANDLERS 20
55
struct nl_proto_handler _nl_handlers[NL_MAX_HANDLERS];
56
struct nl_proto_handler *nl_handlers = _nl_handlers;
57
58
CK_LIST_HEAD(nl_control_head, nl_control);
59
static struct nl_control_head vnets_head = CK_LIST_HEAD_INITIALIZER();
60
61
VNET_DEFINE(struct nl_control, nl_ctl) = {
62
.ctl_port_head = CK_LIST_HEAD_INITIALIZER(),
63
.ctl_pcb_head = CK_LIST_HEAD_INITIALIZER(),
64
};
65
66
struct mtx nl_global_mtx;
67
MTX_SYSINIT(nl_global_mtx, &nl_global_mtx, "global netlink lock", MTX_DEF);
68
69
#define NL_GLOBAL_LOCK() mtx_lock(&nl_global_mtx)
70
#define NL_GLOBAL_UNLOCK() mtx_unlock(&nl_global_mtx)
71
72
int netlink_unloading = 0;
73
74
static void
75
vnet_nl_init(const void *unused __unused)
76
{
77
rm_init(&V_nl_ctl.ctl_lock, "netlink lock");
78
79
NL_GLOBAL_LOCK();
80
CK_LIST_INSERT_HEAD(&vnets_head, &V_nl_ctl, ctl_next);
81
NL_LOG(LOG_DEBUG2, "VNET %p init done, inserted %p into global list",
82
curvnet, &V_nl_ctl);
83
NL_GLOBAL_UNLOCK();
84
}
85
VNET_SYSINIT(vnet_nl_init, SI_SUB_INIT_IF, SI_ORDER_FIRST, vnet_nl_init, NULL);
86
87
static void
88
vnet_nl_uninit(const void *unused __unused)
89
{
90
/* Assume at the time all of the processes / sockets are dead */
91
NL_GLOBAL_LOCK();
92
NL_LOG(LOG_DEBUG2, "Removing %p from global list", &V_nl_ctl);
93
CK_LIST_REMOVE(&V_nl_ctl, ctl_next);
94
NL_GLOBAL_UNLOCK();
95
96
rm_destroy(&V_nl_ctl.ctl_lock);
97
}
98
VNET_SYSUNINIT(vnet_nl_uninit, SI_SUB_INIT_IF, SI_ORDER_FIRST, vnet_nl_uninit,
99
NULL);
100
101
int
102
nl_verify_proto(int proto)
103
{
104
if (proto < 0 || proto >= NL_MAX_HANDLERS) {
105
return (EINVAL);
106
}
107
int handler_defined = nl_handlers[proto].cb != NULL;
108
return (handler_defined ? 0 : EPROTONOSUPPORT);
109
}
110
111
const char *
112
nl_get_proto_name(int proto)
113
{
114
return (nl_handlers[proto].proto_name);
115
}
116
117
bool
118
netlink_register_proto(int proto, const char *proto_name, nl_handler_f handler)
119
{
120
if ((proto < 0) || (proto >= NL_MAX_HANDLERS))
121
return (false);
122
NL_GLOBAL_LOCK();
123
KASSERT((nl_handlers[proto].cb == NULL), ("netlink handler %d is already set", proto));
124
nl_handlers[proto].cb = handler;
125
nl_handlers[proto].proto_name = proto_name;
126
NL_GLOBAL_UNLOCK();
127
NL_LOG(LOG_DEBUG2, "Registered netlink %s(%d) handler", proto_name, proto);
128
return (true);
129
}
130
131
bool
132
netlink_unregister_proto(int proto)
133
{
134
if ((proto < 0) || (proto >= NL_MAX_HANDLERS))
135
return (false);
136
NL_GLOBAL_LOCK();
137
KASSERT((nl_handlers[proto].cb != NULL), ("netlink handler %d is not set", proto));
138
nl_handlers[proto].cb = NULL;
139
nl_handlers[proto].proto_name = NULL;
140
NL_GLOBAL_UNLOCK();
141
NL_LOG(LOG_DEBUG2, "Unregistered netlink proto %d handler", proto);
142
return (true);
143
}
144
145
#if !defined(NETLINK) && defined(NETLINK_MODULE)
146
/* Non-stub function provider */
147
const static struct nl_function_wrapper nl_module = {
148
.nlmsg_add = _nlmsg_add,
149
.nlmsg_refill_buffer = _nlmsg_refill_buffer,
150
.nlmsg_flush = _nlmsg_flush,
151
.nlmsg_end = _nlmsg_end,
152
.nlmsg_abort = _nlmsg_abort,
153
.nl_writer_unicast = _nl_writer_unicast,
154
.nl_writer_group = _nl_writer_group,
155
.nlmsg_end_dump = _nlmsg_end_dump,
156
.nl_modify_ifp_generic = _nl_modify_ifp_generic,
157
.nl_store_ifp_cookie = _nl_store_ifp_cookie,
158
.nl_get_thread_nlp = _nl_get_thread_nlp,
159
};
160
#endif
161
162
static bool
163
can_unload(void)
164
{
165
struct nl_control *ctl;
166
bool result = true;
167
168
NL_GLOBAL_LOCK();
169
170
CK_LIST_FOREACH(ctl, &vnets_head, ctl_next) {
171
NL_LOG(LOG_DEBUG2, "Iterating VNET head %p", ctl);
172
if (!CK_LIST_EMPTY(&ctl->ctl_pcb_head)) {
173
NL_LOG(LOG_NOTICE, "non-empty socket list in ctl %p", ctl);
174
result = false;
175
break;
176
}
177
}
178
179
NL_GLOBAL_UNLOCK();
180
181
return (result);
182
}
183
184
static int
185
netlink_modevent(module_t mod __unused, int what, void *priv __unused)
186
{
187
int ret = 0;
188
189
switch (what) {
190
case MOD_LOAD:
191
NL_LOG(LOG_DEBUG2, "Loading");
192
nl_osd_register();
193
#if !defined(NETLINK) && defined(NETLINK_MODULE)
194
nl_set_functions(&nl_module);
195
#endif
196
break;
197
198
case MOD_UNLOAD:
199
NL_LOG(LOG_DEBUG2, "Unload called");
200
if (can_unload()) {
201
NL_LOG(LOG_WARNING, "unloading");
202
netlink_unloading = 1;
203
#if !defined(NETLINK) && defined(NETLINK_MODULE)
204
nl_set_functions(NULL);
205
#endif
206
nl_osd_unregister();
207
} else
208
ret = EBUSY;
209
break;
210
211
default:
212
ret = EOPNOTSUPP;
213
break;
214
}
215
216
return (ret);
217
}
218
static moduledata_t netlink_mod = { "netlink", netlink_modevent, NULL };
219
220
DECLARE_MODULE(netlink, netlink_mod, SI_SUB_PSEUDO, SI_ORDER_ANY);
221
MODULE_VERSION(netlink, 1);
222
223