]> Sergey Matveev's repositories - ndproxy.git/blob - ndproxy.c
Compatibility with FreeBSD 14
[ndproxy.git] / ndproxy.c
1 /*-
2  * Copyright (c) 2015-2019 Alexandre Fenyo <alex@fenyo.net> - http://www.fenyo.net
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 AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24  * SUCH DAMAGE.
25  */
26
27 #include <sys/param.h>
28 #include <sys/systm.h>
29 #include <sys/kernel.h>
30 #include <sys/malloc.h>
31 #include <sys/types.h>
32 #include <sys/socket.h>
33 #include <sys/module.h>
34 #include <sys/sysctl.h>
35 #include <net/if.h>
36 #include <net/pfil.h>
37 #include <net/if_var.h>
38 #include <net/ethernet.h>
39 #include <netinet/in.h>
40
41 #ifdef PFIL_VERSION
42 #include <net/vnet.h>
43 #include <netinet6/ip6_var.h>
44 #endif
45
46 #include "ndproxy.h"
47 #include "ndconf.h"
48 #include "ndparse.h"
49 #include "ndpacket.h"
50
51 static int hook_added = false;
52
53 #ifdef PFIL_VERSION
54
55 static pfil_hook_t pfh_hook;
56
57 static void register_hook(void) {
58   struct pfil_hook_args pha;
59   struct pfil_link_args pla;
60
61   if (hook_added) return;
62
63   pha.pa_version = PFIL_VERSION;
64   pha.pa_type = PFIL_TYPE_IP6;
65   pha.pa_flags = PFIL_IN;
66   pha.pa_modname = "ndproxy";
67   pha.pa_ruleset = NULL;
68   pha.pa_rulname = "default-in6";
69   pha.pa_mbuf_chk = packet;
70   pha.pa_mem_chk = NULL;
71   pfh_hook = pfil_add_hook(&pha);
72
73   pla.pa_version = PFIL_VERSION;
74   pla.pa_flags = PFIL_IN | PFIL_HEADPTR | PFIL_HOOKPTR;
75   pla.pa_hook = pfh_hook;
76   pla.pa_head = V_inet6_pfil_head;
77   pfil_link(&pla);
78
79   hook_added = true;
80 }
81
82 static void unregister_hook(void) {
83   if (!hook_added) return;
84   pfil_remove_hook(pfh_hook);
85 }
86
87 #else
88
89 static struct pfil_head *pfh_inet6 = NULL;
90
91 // when module is loaded from /boot/loader.conf, pfh_inet6 is not already initialized,
92 // so postpone registration
93 static void register_hook() {
94   if (hook_added) return;
95   
96   if (pfh_inet6 == NULL) {
97     if ((pfh_inet6 = pfil_head_get(PFIL_TYPE_AF, AF_INET6)) == NULL) {
98 #ifdef DEBUG_NDPROXY
99       uprintf("NDPROXY WARNING: pfil_head_get returned null\n");
100       printf("NDPROXY WARNING: pfil_head_get returned null\n");
101 #endif
102       return;
103     }
104   }
105
106   const int ret = pfil_add_hook(packet, NULL, PFIL_IN | PFIL_WAITOK, pfh_inet6);
107   if (ret) {
108 #ifdef DEBUG_NDPROXY
109     uprintf("NDPROXY WARNING: can not add hook (err=%d)\n", ret);
110     printf("NDPROXY WARNING: can not add hook (err=%d)\n", ret);
111 #endif
112     return;
113   }
114   hook_added = true;
115 }
116
117 static void unregister_hook() {
118   int ret;
119
120   if (hook_added && (ret = pfil_remove_hook(packet, NULL, PFIL_IN | PFIL_WAITOK, pfh_inet6))) {
121 #ifdef DEBUG_NDPROXY
122     uprintf("NDPROXY WARNING: can not remove hook (err=%d)\n", ret);
123     printf("NDPROXY WARNING: can not remove hook (err=%d)\n", ret);
124 #endif
125   }
126 }
127
128 #endif
129
130 // called when the module is loaded or unloaded
131 static int event_handler(struct module *module, const int event, void *arg) {
132   switch (event) {
133   case MOD_LOAD:
134     register_hook();
135 #ifdef DEBUG_NDPROXY
136     uprintf("NDPROXY loaded\n");
137     printf("NDPROXY loaded\n");
138 #endif
139     return 0;
140     // NOTREACHED
141     break;
142
143   case MOD_UNLOAD:
144     unregister_hook();
145 #ifdef DEBUG_NDPROXY
146     uprintf("NDPROXY unloaded\n");
147     printf("NDPROXY unloaded\n");
148 #endif
149     return 0;
150     // NOTREACHED
151     break;
152
153   default:
154     return EOPNOTSUPP;
155     // NOTREACHED
156     break;
157   }
158 }
159
160 // declare module data
161
162 static moduledata_t ndproxy_conf = {
163   "ndproxy",     // module name
164   event_handler, // event handler
165   NULL           // extra data
166 };
167 DECLARE_MODULE(ndproxy, ndproxy_conf, SI_SUB_DRIVERS, SI_ORDER_MIDDLE);
168
169 // declare sysctl interface used to configure the behaviour of the module
170
171 SYSCTL_DECL(_net_inet6);
172
173 #define GENERIC_CB_STRING                                            \
174   if (arg1 == NULL) {                                                \
175     printf("NDPROXY ERROR: conf arg is null\n");                     \
176     return EFAULT;                                                   \
177   }                                                                  \
178                                                                      \
179   if (strlen((char *) arg1) > sizeof conf_str - 1) return E2BIG;     \
180                                                                      \
181   strncpy(conf_str, (char *) arg1, sizeof conf_str);                 \
182   conf_str[(sizeof conf_str) - 1] = '\0';                            \
183                                                                      \
184   ret = SYSCTL_OUT(req, conf_str, sizeof conf_str);                  \
185   if (ret || !req->newptr) return ret;                               \
186                                                                      \
187   /* the caller asks to set a new value */                           \
188                                                                      \
189   if ((req->newlen - req->newidx) >= arg2) return EINVAL;            \
190   arg2 = (req->newlen - req->newidx);                                \
191   ret = SYSCTL_IN(req, arg1, arg2);                                  \
192   ((char *)arg1)[arg2] = '\0';                                       \
193   if (ret) return ret;
194
195 ////////////////////////////////////////////////////////////////////////////////
196 // net.inet6.ndproxyconf_uplink_interface
197
198 // declare the sysctl node named net.inet6.ndproxyconf_uplink_interface
199 SYSCTL_STRING(_net_inet6, OID_AUTO, ndproxyconf_uplink_interface, CTLFLAG_RW, ndproxy_conf_str_uplink_interface, sizeof ndproxy_conf_str_uplink_interface, "uplink interface name");
200
201 ////////////////////////////////////////////////////////////////////////////////
202 // net.inet6.ndproxyconf_{up,down}link_mac_address
203
204 // storage string for the sysctl node named net.inet6.ndproxyconf_{up,down}link_mac_address
205 #if 0
206 // reserved for a future use
207 static char ndproxy_conf_str_uplink_mac_address[MACMAXSIZE + 1] = "";
208 #endif
209 static char ndproxy_conf_str_downlink_mac_address[MACMAXSIZE + 1] = "";
210
211 // get or update the value of the sysctl node named net.inet6.ndproxyconf_{up,down}link_mac_address
212 static int cb_string_mac_addr(SYSCTL_HANDLER_ARGS, char xconf_str[MACMAXSIZE + 1], struct ether_addr *xconf_val, bool *xconf_isset) {
213   char conf_str[MACMAXSIZE + 1];
214   struct ether_addr _ndproxy_conf_link_mac_address;
215   int ret;
216
217   register_hook();
218
219   GENERIC_CB_STRING;
220
221   if (!strlen(xconf_str)) {
222     *xconf_isset = false;
223     return 0;
224   }
225
226   char *curp = xconf_str;
227   char tmpstr[18];
228   strcpy(tmpstr, curp);
229   ret = parse_mac(tmpstr, &_ndproxy_conf_link_mac_address);
230   if (!ret) {
231     *xconf_isset = true;
232 #ifdef DEBUG_NDPROXY
233     printf("NDPROXY INFO: parsed address: [");
234     printf_macaddr_network_format(&_ndproxy_conf_link_mac_address);
235     printf("]\n");
236 #endif
237   } else {
238     strncpy(xconf_str, conf_str, sizeof conf_str);
239     xconf_str[sizeof conf_str - 1] = 0;
240     return EINVAL;
241   }
242
243   *xconf_val = _ndproxy_conf_link_mac_address;
244   *xconf_isset = true;
245   return 0;
246 }
247
248 // get or update the value of the sysctl node named net.inet6.ndproxyconf_downlink_mac_address
249 static int cb_string_downlink_mac_addr(SYSCTL_HANDLER_ARGS) {
250   return cb_string_mac_addr(oidp, arg1, arg2, req, ndproxy_conf_str_downlink_mac_address, &ndproxy_conf_downlink_mac_address, &ndproxy_conf_downlink_mac_address_isset);
251 }
252
253 #if 0
254 // reserved for a future use
255 // get or update the value of the sysctl node named net.inet6.ndproxyconf_uplink_mac_address
256 static int cb_string_uplink_mac_addr(SYSCTL_HANDLER_ARGS) {
257   return cb_string_mac_addr(oidp, arg1, arg2, req, ndproxy_conf_str_uplink_mac_address, &ndproxy_conf_uplink_mac_address, &ndproxy_conf_uplink_mac_address_isset);
258 }
259 #endif
260
261 // declare the sysctl node named net.inet6.ndproxyconf_{up,down}link_mac_address
262 // format: NN:NN:NN:NN:NN:NN
263 SYSCTL_OID(_net_inet6, OID_AUTO, ndproxyconf_downlink_mac_address, CTLTYPE_STRING | CTLFLAG_MPSAFE | CTLFLAG_RW, ndproxy_conf_str_downlink_mac_address, sizeof ndproxy_conf_str_downlink_mac_address, cb_string_downlink_mac_addr, "S", "downlink mac adress");
264 // the uplink mac address is reserved for a future use when it could be used to filter uplink packets instead of using the uplink ipv6 addresses
265 // SYSCTL_OID(_net_inet6, OID_AUTO, ndproxyconf_uplink_mac_address, CTLTYPE_STRING | CTLFLAG_MPSAFE | CTLFLAG_RW, ndproxy_conf_str_uplink_mac_address, sizeof ndproxy_conf_str_uplink_mac_address, cb_string_uplink_mac_addr, "S", "uplink mac adress");
266
267 ////////////////////////////////////////////////////////////////////////////////
268 // net.inet6.ndproxyconf_exception_ipv6_addresses && net.inet6.ndproxyconf_uplink_ipv6_addresses
269
270 // storage string for the sysctl node named net.inet6.ndproxyconf_exception_ipv6_addresses
271 static char ndproxy_conf_str_exception_ipv6_addresses[CONF_NEXCEPTIONS_SIZE] = "";
272
273 // storage string for the sysctl node named net.inet6.ndproxyconf_uplink_ipv6_addresses
274 static char ndproxy_conf_str_uplink_ipv6_addresses[CONF_NUPLINK_SIZE] = "";
275
276 // get or update the value of the sysctl node named net.inet6.ndproxyconf_{uplink,exception}_ipv6_addresses
277 static int cb_string_list(SYSCTL_HANDLER_ARGS, int nentries_size, int nentries_max, char *ndproxy_conf_str_ipv6_addresses, struct in6_addr *ndproxy_conf_ipv6_addresses, int *ndproxy_conf_ipv6_naddresses) {
278   char conf_str[nentries_size];
279   struct in6_addr _ndproxy_conf_ipv6_addresses[nentries_max];
280   int _ndproxy_conf_ipv6_naddresses = 0;
281   int ret;
282
283   register_hook();
284   
285   GENERIC_CB_STRING;
286
287   if (!strlen(ndproxy_conf_str_ipv6_addresses)) {
288     *ndproxy_conf_ipv6_naddresses = 0;
289     return 0;
290   }
291
292   char *curp = ndproxy_conf_str_ipv6_addresses;
293   char *delim;
294   do {
295     char tmpstr[nentries_size];
296     delim = strchr(curp, ';');
297     if (delim != NULL) {
298       strncpy(tmpstr, curp, delim - curp);
299       tmpstr[delim - curp] = 0;
300     } else strcpy(tmpstr, curp);
301     ret = parse_ipv6(tmpstr, _ndproxy_conf_ipv6_addresses + _ndproxy_conf_ipv6_naddresses);
302     if (!ret) {
303 #ifdef DEBUG_NDPROXY
304       printf("NDPROXY INFO: parsed address: [");
305       printf_ip6addr_network_format(_ndproxy_conf_ipv6_addresses + _ndproxy_conf_ipv6_naddresses);
306       printf("]\n");
307 #endif
308     } else {
309       strncpy(ndproxy_conf_str_ipv6_addresses, conf_str, nentries_size);
310       ndproxy_conf_str_ipv6_addresses[nentries_size - 1] = 0;
311       return EINVAL;
312     }
313     _ndproxy_conf_ipv6_naddresses++;
314   } while (delim != NULL && (curp = ++delim) < (char *) (ndproxy_conf_str_ipv6_addresses + nentries_size) && _ndproxy_conf_ipv6_naddresses < nentries_max);
315
316   if (delim) {
317       strncpy(ndproxy_conf_str_ipv6_addresses, conf_str, nentries_size);
318       ndproxy_conf_str_ipv6_addresses[nentries_size - 1] = 0;
319       return EINVAL;
320   }
321
322   bcopy(_ndproxy_conf_ipv6_addresses, ndproxy_conf_ipv6_addresses, _ndproxy_conf_ipv6_naddresses * sizeof(struct in6_addr));
323   *ndproxy_conf_ipv6_naddresses = _ndproxy_conf_ipv6_naddresses;
324   
325   return 0;
326 }
327
328 static int cb_string_list_exception(SYSCTL_HANDLER_ARGS) {
329   return cb_string_list(oidp, arg1, arg2, req, CONF_NEXCEPTIONS_SIZE, CONF_NEXCEPTIONS_MAX, ndproxy_conf_str_exception_ipv6_addresses, ndproxy_conf_exception_ipv6_addresses, &ndproxy_conf_exception_ipv6_naddresses);
330 }
331
332 static int cb_string_list_uplink(SYSCTL_HANDLER_ARGS) {
333   return cb_string_list(oidp, arg1, arg2, req, CONF_NUPLINK_SIZE, CONF_NUPLINK_MAX, ndproxy_conf_str_uplink_ipv6_addresses, ndproxy_conf_uplink_ipv6_addresses, &ndproxy_conf_uplink_ipv6_naddresses);
334 }
335
336 // declare the sysctl node named net.inet6.ndproxyconf_exception_ipv6_addresses
337 // format: NNNN:NNNN:NNNN:NNNN:NNNN:NNNN:{NNNN:NNNN,XXX.XXX.XXX.XXX};...;...
338 SYSCTL_OID(_net_inet6, OID_AUTO, ndproxyconf_exception_ipv6_addresses, CTLTYPE_STRING | CTLFLAG_MPSAFE | CTLFLAG_RW, ndproxy_conf_str_exception_ipv6_addresses, sizeof ndproxy_conf_str_exception_ipv6_addresses, cb_string_list_exception, "S", "do not proxy this list of IPv6 adresses");
339
340 // declare the sysctl node named net.inet6.ndproxyconf_uplink_ipv6_addresses
341 // format: NNNN:NNNN:NNNN:NNNN:NNNN:NNNN:{NNNN:NNNN,XXX.XXX.XXX.XXX};...;...
342 SYSCTL_OID(_net_inet6, OID_AUTO, ndproxyconf_uplink_ipv6_addresses, CTLTYPE_STRING | CTLFLAG_MPSAFE | CTLFLAG_RW, ndproxy_conf_str_uplink_ipv6_addresses, sizeof ndproxy_conf_str_uplink_ipv6_addresses, cb_string_list_uplink, "S", "uplink router IPv6 adresses");
343
344 ////////////////////////////////////////////////////////////////////////////////
345 // net.inet6.ndproxycount
346
347 static int cb_count(SYSCTL_HANDLER_ARGS) {
348   register_hook();
349   
350 #ifdef DEBUG_NDPROXY
351     printf("NDPROXY INFO: count\n");
352 #endif
353
354     return sysctl_handle_int(oidp, arg1, arg2, req);
355 }
356
357 SYSCTL_OID(_net_inet6, OID_AUTO, ndproxycount, CTLTYPE_INT | CTLFLAG_MPSAFE | CTLFLAG_RW, &ndproxy_conf_count, 0, cb_count, "I", "fire an event");