]> Sergey Matveev's repositories - ndproxy.git/blob - ndproxy.c
Different behaviour of ndproxyconf_exception_ipv6_addresses
[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_func = packet;
70   pfh_hook = pfil_add_hook(&pha);
71
72   pla.pa_version = PFIL_VERSION;
73   pla.pa_flags = PFIL_IN | PFIL_HEADPTR | PFIL_HOOKPTR;
74   pla.pa_hook = pfh_hook;
75   pla.pa_head = V_inet6_pfil_head;
76   pfil_link(&pla);
77
78   hook_added = true;
79 }
80
81 static void unregister_hook(void) {
82   if (!hook_added) return;
83   pfil_remove_hook(pfh_hook);
84 }
85
86 #else
87
88 static struct pfil_head *pfh_inet6 = NULL;
89
90 // when module is loaded from /boot/loader.conf, pfh_inet6 is not already initialized,
91 // so postpone registration
92 static void register_hook() {
93   if (hook_added) return;
94   
95   if (pfh_inet6 == NULL) {
96     if ((pfh_inet6 = pfil_head_get(PFIL_TYPE_AF, AF_INET6)) == NULL) {
97 #ifdef DEBUG_NDPROXY
98       uprintf("NDPROXY WARNING: pfil_head_get returned null\n");
99       printf("NDPROXY WARNING: pfil_head_get returned null\n");
100 #endif
101       return;
102     }
103   }
104
105   const int ret = pfil_add_hook(packet, NULL, PFIL_IN | PFIL_WAITOK, pfh_inet6);
106   if (ret) {
107 #ifdef DEBUG_NDPROXY
108     uprintf("NDPROXY WARNING: can not add hook (err=%d)\n", ret);
109     printf("NDPROXY WARNING: can not add hook (err=%d)\n", ret);
110 #endif
111     return;
112   }
113   hook_added = true;
114 }
115
116 static void unregister_hook() {
117   int ret;
118
119   if (hook_added && (ret = pfil_remove_hook(packet, NULL, PFIL_IN | PFIL_WAITOK, pfh_inet6))) {
120 #ifdef DEBUG_NDPROXY
121     uprintf("NDPROXY WARNING: can not remove hook (err=%d)\n", ret);
122     printf("NDPROXY WARNING: can not remove hook (err=%d)\n", ret);
123 #endif
124   }
125 }
126
127 #endif
128
129 // called when the module is loaded or unloaded
130 static int event_handler(struct module *module, const int event, void *arg) {
131   switch (event) {
132   case MOD_LOAD:
133     register_hook();
134 #ifdef DEBUG_NDPROXY
135     uprintf("NDPROXY loaded\n");
136     printf("NDPROXY loaded\n");
137 #endif
138     return 0;
139     // NOTREACHED
140     break;
141
142   case MOD_UNLOAD:
143     unregister_hook();
144 #ifdef DEBUG_NDPROXY
145     uprintf("NDPROXY unloaded\n");
146     printf("NDPROXY unloaded\n");
147 #endif
148     return 0;
149     // NOTREACHED
150     break;
151
152   default:
153     return EOPNOTSUPP;
154     // NOTREACHED
155     break;
156   }
157 }
158
159 // declare module data
160
161 static moduledata_t ndproxy_conf = {
162   "ndproxy",     // module name
163   event_handler, // event handler
164   NULL           // extra data
165 };
166 DECLARE_MODULE(ndproxy, ndproxy_conf, SI_SUB_DRIVERS, SI_ORDER_MIDDLE);
167
168 // declare sysctl interface used to configure the behaviour of the module
169
170 SYSCTL_DECL(_net_inet6);
171
172 #define GENERIC_CB_STRING                                            \
173   if (arg1 == NULL) {                                                \
174     printf("NDPROXY ERROR: conf arg is null\n");                     \
175     return EFAULT;                                                   \
176   }                                                                  \
177                                                                      \
178   if (strlen((char *) arg1) > sizeof conf_str - 1) return E2BIG;     \
179                                                                      \
180   strncpy(conf_str, (char *) arg1, sizeof conf_str);                 \
181   conf_str[(sizeof conf_str) - 1] = '\0';                            \
182                                                                      \
183   ret = SYSCTL_OUT(req, conf_str, sizeof conf_str);                  \
184   if (ret || !req->newptr) return ret;                               \
185                                                                      \
186   /* the caller asks to set a new value */                           \
187                                                                      \
188   if ((req->newlen - req->newidx) >= arg2) return EINVAL;            \
189   arg2 = (req->newlen - req->newidx);                                \
190   ret = SYSCTL_IN(req, arg1, arg2);                                  \
191   ((char *)arg1)[arg2] = '\0';                                       \
192   if (ret) return ret;
193
194 ////////////////////////////////////////////////////////////////////////////////
195 // net.inet6.ndproxyconf_uplink_interface
196
197 // declare the sysctl node named net.inet6.ndproxyconf_uplink_interface
198 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");
199
200 ////////////////////////////////////////////////////////////////////////////////
201 // net.inet6.ndproxyconf_{up,down}link_mac_address
202
203 // storage string for the sysctl node named net.inet6.ndproxyconf_{up,down}link_mac_address
204 #if 0
205 // reserved for a future use
206 static char ndproxy_conf_str_uplink_mac_address[MACMAXSIZE + 1] = "";
207 #endif
208 static char ndproxy_conf_str_downlink_mac_address[MACMAXSIZE + 1] = "";
209
210 // get or update the value of the sysctl node named net.inet6.ndproxyconf_{up,down}link_mac_address
211 static int cb_string_mac_addr(SYSCTL_HANDLER_ARGS, char xconf_str[MACMAXSIZE + 1], struct ether_addr *xconf_val, bool *xconf_isset) {
212   char conf_str[MACMAXSIZE + 1];
213   struct ether_addr _ndproxy_conf_link_mac_address;
214   int ret;
215
216   register_hook();
217
218   GENERIC_CB_STRING;
219
220   if (!strlen(xconf_str)) {
221     *xconf_isset = false;
222     return 0;
223   }
224
225   char *curp = xconf_str;
226   char tmpstr[18];
227   strcpy(tmpstr, curp);
228   ret = parse_mac(tmpstr, &_ndproxy_conf_link_mac_address);
229   if (!ret) {
230     *xconf_isset = true;
231 #ifdef DEBUG_NDPROXY
232     printf("NDPROXY INFO: parsed address: [");
233     printf_macaddr_network_format(&_ndproxy_conf_link_mac_address);
234     printf("]\n");
235 #endif
236   } else {
237     strncpy(xconf_str, conf_str, sizeof conf_str);
238     xconf_str[sizeof conf_str - 1] = 0;
239     return EINVAL;
240   }
241
242   *xconf_val = _ndproxy_conf_link_mac_address;
243   *xconf_isset = true;
244   return 0;
245 }
246
247 // get or update the value of the sysctl node named net.inet6.ndproxyconf_downlink_mac_address
248 static int cb_string_downlink_mac_addr(SYSCTL_HANDLER_ARGS) {
249   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);
250 }
251
252 #if 0
253 // reserved for a future use
254 // get or update the value of the sysctl node named net.inet6.ndproxyconf_uplink_mac_address
255 static int cb_string_uplink_mac_addr(SYSCTL_HANDLER_ARGS) {
256   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);
257 }
258 #endif
259
260 // declare the sysctl node named net.inet6.ndproxyconf_{up,down}link_mac_address
261 // format: NN:NN:NN:NN:NN:NN
262 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");
263 // 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
264 // 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");
265
266 ////////////////////////////////////////////////////////////////////////////////
267 // net.inet6.ndproxyconf_exception_ipv6_addresses && net.inet6.ndproxyconf_uplink_ipv6_addresses
268
269 // storage string for the sysctl node named net.inet6.ndproxyconf_exception_ipv6_addresses
270 static char ndproxy_conf_str_exception_ipv6_addresses[CONF_NEXCEPTIONS_SIZE] = "";
271
272 // storage string for the sysctl node named net.inet6.ndproxyconf_uplink_ipv6_addresses
273 static char ndproxy_conf_str_uplink_ipv6_addresses[CONF_NUPLINK_SIZE] = "";
274
275 // get or update the value of the sysctl node named net.inet6.ndproxyconf_{uplink,exception}_ipv6_addresses
276 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) {
277   char conf_str[nentries_size];
278   struct in6_addr _ndproxy_conf_ipv6_addresses[nentries_max];
279   int _ndproxy_conf_ipv6_naddresses = 0;
280   int ret;
281
282   register_hook();
283   
284   GENERIC_CB_STRING;
285
286   if (!strlen(ndproxy_conf_str_ipv6_addresses)) {
287     *ndproxy_conf_ipv6_naddresses = 0;
288     return 0;
289   }
290
291   char *curp = ndproxy_conf_str_ipv6_addresses;
292   char *delim;
293   do {
294     char tmpstr[nentries_size];
295     delim = strchr(curp, ';');
296     if (delim != NULL) {
297       strncpy(tmpstr, curp, delim - curp);
298       tmpstr[delim - curp] = 0;
299     } else strcpy(tmpstr, curp);
300     ret = parse_ipv6(tmpstr, _ndproxy_conf_ipv6_addresses + _ndproxy_conf_ipv6_naddresses);
301     if (!ret) {
302 #ifdef DEBUG_NDPROXY
303       printf("NDPROXY INFO: parsed address: [");
304       printf_ip6addr_network_format(_ndproxy_conf_ipv6_addresses + _ndproxy_conf_ipv6_naddresses);
305       printf("]\n");
306 #endif
307     } else {
308       strncpy(ndproxy_conf_str_ipv6_addresses, conf_str, nentries_size);
309       ndproxy_conf_str_ipv6_addresses[nentries_size - 1] = 0;
310       return EINVAL;
311     }
312     _ndproxy_conf_ipv6_naddresses++;
313   } while (delim != NULL && (curp = ++delim) < (char *) (ndproxy_conf_str_ipv6_addresses + nentries_size) && _ndproxy_conf_ipv6_naddresses < nentries_max);
314
315   if (delim) {
316       strncpy(ndproxy_conf_str_ipv6_addresses, conf_str, nentries_size);
317       ndproxy_conf_str_ipv6_addresses[nentries_size - 1] = 0;
318       return EINVAL;
319   }
320
321   bcopy(_ndproxy_conf_ipv6_addresses, ndproxy_conf_ipv6_addresses, _ndproxy_conf_ipv6_naddresses * sizeof(struct in6_addr));
322   *ndproxy_conf_ipv6_naddresses = _ndproxy_conf_ipv6_naddresses;
323   
324   return 0;
325 }
326
327 static int cb_string_list_exception(SYSCTL_HANDLER_ARGS) {
328   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);
329 }
330
331 static int cb_string_list_uplink(SYSCTL_HANDLER_ARGS) {
332   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);
333 }
334
335 // declare the sysctl node named net.inet6.ndproxyconf_exception_ipv6_addresses
336 // format: NNNN:NNNN:NNNN:NNNN:NNNN:NNNN:{NNNN:NNNN,XXX.XXX.XXX.XXX};...;...
337 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");
338
339 // declare the sysctl node named net.inet6.ndproxyconf_uplink_ipv6_addresses
340 // format: NNNN:NNNN:NNNN:NNNN:NNNN:NNNN:{NNNN:NNNN,XXX.XXX.XXX.XXX};...;...
341 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");
342
343 ////////////////////////////////////////////////////////////////////////////////
344 // net.inet6.ndproxycount
345
346 static int cb_count(SYSCTL_HANDLER_ARGS) {
347   register_hook();
348   
349 #ifdef DEBUG_NDPROXY
350     printf("NDPROXY INFO: count\n");
351 #endif
352
353     return sysctl_handle_int(oidp, arg1, arg2, req);
354 }
355
356 SYSCTL_OID(_net_inet6, OID_AUTO, ndproxycount, CTLTYPE_INT | CTLFLAG_MPSAFE | CTLFLAG_RW, &ndproxy_conf_count, 0, cb_count, "I", "fire an event");