xref: /dflybsd-src/sys/net/ipfw3_basic/ip_fw3_table.c (revision 6823c302c37b3feda6c2c8b524a99daa1bcff11f)
1 /*
2  * Copyright (c) 2015 -2018 The DragonFly Project.  All rights reserved.
3  *
4  * This code is derived from software contributed to The DragonFly Project
5  * by Bill Yuan <bycn82@dragonflybsd.org>
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  *
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
15  *    the documentation and/or other materials provided with the
16  *    distribution.
17  * 3. Neither the name of The DragonFly Project nor the names of its
18  *    contributors may be used to endorse or promote products derived
19  *    from this software without specific, prior written permission.
20  *
21  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
22  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
23  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
24  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE
25  * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
26  * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING,
27  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
28  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
29  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
30  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
31  * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
32  * SUCH DAMAGE.
33  *
34  */
35 
36 #include <sys/types.h>
37 #include <sys/param.h>
38 #include <sys/systm.h>
39 #include <sys/malloc.h>
40 #include <sys/mbuf.h>
41 #include <sys/kernel.h>
42 #include <sys/proc.h>
43 #include <sys/socket.h>
44 #include <sys/socketvar.h>
45 #include <sys/sysctl.h>
46 #include <sys/syslog.h>
47 #include <sys/ucred.h>
48 #include <sys/in_cksum.h>
49 #include <sys/lock.h>
50 
51 #include <net/if.h>
52 #include <net/route.h>
53 #include <net/pfil.h>
54 #include <net/netmsg2.h>
55 #include <net/ethernet.h>
56 
57 #include <netinet/in.h>
58 #include <netinet/in_systm.h>
59 #include <netinet/in_var.h>
60 #include <netinet/in_pcb.h>
61 #include <netinet/ip.h>
62 #include <netinet/ip_var.h>
63 #include <netinet/ip_icmp.h>
64 #include <netinet/tcp.h>
65 #include <netinet/tcp_timer.h>
66 #include <netinet/tcp_var.h>
67 #include <netinet/tcpip.h>
68 #include <netinet/udp.h>
69 #include <netinet/udp_var.h>
70 #include <netinet/ip_divert.h>
71 #include <netinet/if_ether.h>
72 
73 #include <net/ipfw3/ip_fw.h>
74 #include <net/ipfw3_basic/ip_fw3_table.h>
75 
76 MALLOC_DEFINE(M_IPFW3_TABLE, "IPFW3_TABLE", "mem for ip_fw3 table");
77 
78 extern struct ipfw3_context	*fw3_ctx[MAXCPU];
79 extern ip_fw_ctl_t 		*ip_fw3_ctl_table_ptr;
80 
81 /*
82  * activate/create the table by setup the type and reset counts.
83  */
84 void
85 table_create_dispatch(netmsg_t nmsg)
86 {
87 	struct netmsg_table *tbmsg = (struct netmsg_table *)nmsg;
88 	struct ipfw_ioc_table *ioc_table;
89 	struct ipfw3_context *ctx = fw3_ctx[mycpuid];
90 	struct ipfw3_table_context *table_ctx;
91 	ioc_table = tbmsg->ioc_table;
92 	int id = ioc_table->id;
93 
94 	table_ctx = ctx->table_ctx;
95 	table_ctx += id;
96 	table_ctx->type = ioc_table->type;
97 	table_ctx->count = 0;
98 	strlcpy(table_ctx->name , ioc_table->name, IPFW_TABLE_NAME_LEN);
99 	if (table_ctx->type == 1) {
100 		rn_inithead(&table_ctx->mask, NULL, 0);
101 		rn_inithead(&table_ctx->node, table_ctx->mask,
102 			    offsetof(struct sockaddr_in, sin_addr));
103 	} else if (table_ctx->type == 2) {
104 		rn_inithead(&table_ctx->mask, NULL, 0);
105 		rn_inithead(&table_ctx->node, table_ctx->mask,
106 			    offsetof(struct sockaddr, sa_data));
107 	} else {
108 		goto done;
109 	}
110 done:
111 	netisr_forwardmsg_all(&nmsg->base, mycpuid + 1);
112 }
113 
114 /*
115  * clean the table, especially the node
116  */
117 void
118 table_delete_dispatch(netmsg_t nmsg)
119 {
120 	struct netmsg_table *tbmsg = (struct netmsg_table *)nmsg;
121 	struct ipfw_ioc_table *ioc_tbl;
122 	struct ipfw3_context *ctx = fw3_ctx[mycpuid];
123 	struct ipfw3_table_context *table_ctx;
124 
125 	ioc_tbl = tbmsg->ioc_table;
126 	table_ctx = ctx->table_ctx;
127 	table_ctx += ioc_tbl->id;
128 	table_ctx->count = 0;
129 
130 	rn_flush(table_ctx->node, flush_table_entry);
131 	/* XXX: should free the tree: rn_freehead(table_ctx->node) */
132 	table_ctx->type = 0;
133 	netisr_forwardmsg_all(&nmsg->base, mycpuid + 1);
134 }
135 
136 void
137 table_append_dispatch(netmsg_t nmsg)
138 {
139 	struct netmsg_table *tbmsg = (struct netmsg_table *)nmsg;
140 	struct ipfw_ioc_table *ioc_tbl;
141 	struct ipfw3_context *ctx = fw3_ctx[mycpuid];
142 	struct ipfw3_table_context *table_ctx;
143 	struct radix_node_head *rnh;
144 
145 	uint8_t mlen;
146 
147 	ioc_tbl = tbmsg->ioc_table;
148 	table_ctx = ctx->table_ctx;
149 	table_ctx += ioc_tbl->id;
150 	if (table_ctx->type != ioc_tbl->type)
151 		goto done;
152 
153         if (table_ctx->type == 1 ) {
154                 struct table_ip_entry *ent;
155 
156                 rnh = table_ctx->node;
157                 ent = kmalloc(sizeof(struct table_ip_entry),
158                                 M_IPFW3_TABLE, M_NOWAIT | M_ZERO);
159 
160                 if (ent == NULL)
161                         return;
162                 mlen = ioc_tbl->ip_ent->masklen;
163                 ent->addr.sin_len = sizeof(ent->addr);
164                 ent->mask.sin_len = sizeof(ent->mask);
165                 ent->mask.sin_addr.s_addr = htonl(~((1 << (32 - mlen)) - 1));
166                 ent->addr.sin_addr.s_addr = ioc_tbl->ip_ent->addr &
167                                                 ent->mask.sin_addr.s_addr;
168 
169                 if (rnh->rnh_addaddr((char *)&ent->addr,
170                                 (char *)&ent->mask, rnh,
171                                 (void *)ent->rn) != NULL) {
172                         table_ctx->count++;
173                 }
174         } else if (table_ctx->type == 2 ) {
175                 struct table_mac_entry *ent;
176 
177                 rnh = table_ctx->node;
178                 ent = kmalloc(sizeof(struct table_mac_entry),
179                                 M_IPFW3_TABLE, M_NOWAIT | M_ZERO);
180                 if (ent == NULL)
181                         return;
182                 ent->addr.sa_len = offsetof(struct sockaddr, sa_data[6]);
183                 strncpy(ent->addr.sa_data, ioc_tbl->mac_ent->addr.octet, 6);
184 
185                 if (rnh->rnh_addaddr((char *)&ent->addr,
186                                 NULL, rnh, (void *)ent->rn) != NULL) {
187                        table_ctx->count++;
188                 }
189         }
190 
191 done:
192 	netisr_forwardmsg_all(&nmsg->base, mycpuid + 1);
193 }
194 
195 void
196 table_remove_dispatch(netmsg_t nmsg)
197 {
198 	struct netmsg_table *tbmsg = (struct netmsg_table *)nmsg;
199 	struct ipfw_ioc_table *ioc_tbl;
200 	struct ipfw3_context *ctx = fw3_ctx[mycpuid];
201 	struct ipfw3_table_context *table_ctx;
202 	struct radix_node_head *rnh;
203 	struct table_entry *ent;
204 	struct sockaddr_in sa, mask;
205 	in_addr_t addr;
206 	uint8_t mlen;
207 
208 	ioc_tbl = tbmsg->ioc_table;
209 	table_ctx = ctx->table_ctx;
210 	table_ctx += ioc_tbl->id;
211 	if (table_ctx->type != ioc_tbl->type)
212 		goto done;
213 
214 	rnh = table_ctx->node;
215 
216 	mlen = ioc_tbl->ip_ent->masklen;
217 	addr = ioc_tbl->ip_ent->addr;
218 
219 	sa.sin_len = mask.sin_len = 8;
220 	mask.sin_addr.s_addr = htonl(mlen ? ~((1 << (32 - mlen)) - 1) : 0);
221 	sa.sin_addr.s_addr = addr & mask.sin_addr.s_addr;
222 
223 	ent = (struct table_entry *)rnh->rnh_deladdr((char *)&sa, (char *)&mask, rnh);
224 	if (ent != NULL) {
225 		table_ctx->count--;
226 		kfree(ent, M_IPFW3_TABLE);
227 	}
228 done:
229 	netisr_forwardmsg_all(&nmsg->base, mycpuid + 1);
230 }
231 
232 void
233 flush_table_entry(struct radix_node *rn)
234 {
235 	kfree(rn, M_IPFW3_TABLE);
236 }
237 
238 void
239 table_flush_dispatch(netmsg_t nmsg)
240 {
241 	struct netmsg_table *tbmsg = (struct netmsg_table *)nmsg;
242 	struct ipfw_ioc_table *ioc_tbl;
243 	struct ipfw3_context *ctx = fw3_ctx[mycpuid];
244 	struct ipfw3_table_context *table_ctx;
245 	struct radix_node_head *rnh;
246 
247 	ioc_tbl = tbmsg->ioc_table;
248 	table_ctx = ctx->table_ctx;
249 	table_ctx += ioc_tbl->id;
250 	rnh = table_ctx->node;
251 	table_ctx->count = 0;
252 
253 	rn_flush(rnh, flush_table_entry);
254 	netisr_forwardmsg_all(&nmsg->base, mycpuid + 1);
255 }
256 
257 /*
258  * rename the table
259  */
260 void
261 table_rename_dispatch(netmsg_t nmsg)
262 {
263 	struct netmsg_table *tbmsg = (struct netmsg_table *)nmsg;
264 	struct ipfw_ioc_table *ioc_tbl;
265 	struct ipfw3_context *ctx = fw3_ctx[mycpuid];
266 	struct ipfw3_table_context *table_ctx;
267 
268 	ioc_tbl = tbmsg->ioc_table;
269 	table_ctx = ctx->table_ctx;
270 	table_ctx += ioc_tbl->id;
271 	strlcpy(table_ctx->name, ioc_tbl->name, IPFW_TABLE_NAME_LEN);
272 	netisr_forwardmsg_all(&nmsg->base, mycpuid + 1);
273 }
274 
275 /*
276  * list all the overview information about each table
277  */
278 int
279 ip_fw3_ctl_table_list(struct sockopt *sopt)
280 {
281 	struct ipfw3_context *ctx = fw3_ctx[mycpuid];
282 	struct ipfw3_table_context *table_ctx = ctx->table_ctx;
283 	struct ipfw_ioc_table *ioc_table;
284 	int i, error = 0, size;
285 
286 	size = IPFW_TABLES_MAX * sizeof(struct ipfw_ioc_table);
287 	if (sopt->sopt_valsize < size) {
288 		/* sopt_val is not big enough */
289 		bzero(sopt->sopt_val, sopt->sopt_valsize);
290 		return 0;
291 	}
292 	ioc_table = (struct ipfw_ioc_table *)sopt->sopt_val;
293 	for (i = 0; i < IPFW_TABLES_MAX; i++, ioc_table++, table_ctx++) {
294 		ioc_table->id = i;
295 		ioc_table->type = table_ctx->type;
296 		ioc_table->count = table_ctx->count;
297 		strlcpy(ioc_table->name, table_ctx->name, IPFW_TABLE_NAME_LEN);
298 	}
299 	sopt->sopt_valsize = size;
300 	return error;
301 }
302 
303 /*
304  * remove an item from the table
305  */
306 int
307 ip_fw3_ctl_table_remove(struct sockopt *sopt)
308 {
309 	struct netmsg_table tbmsg;
310 	bzero(&tbmsg,sizeof(tbmsg));
311 	tbmsg.ioc_table = sopt->sopt_val;
312 	netmsg_init(&tbmsg.base, NULL, &curthread->td_msgport,
313 			0, table_remove_dispatch);
314 	netisr_domsg(&tbmsg.base, 0);
315 	return tbmsg.retval;
316 }
317 
318 /*
319  * flush everything inside the table
320  */
321 int
322 ip_fw3_ctl_table_flush(struct sockopt *sopt)
323 {
324 	struct netmsg_table tbmsg;
325 	bzero(&tbmsg,sizeof(tbmsg));
326 	tbmsg.ioc_table = sopt->sopt_val;
327 	netmsg_init(&tbmsg.base, NULL, &curthread->td_msgport,
328 			0, table_flush_dispatch);
329 	netisr_domsg(&tbmsg.base, 0);
330 	return tbmsg.retval;
331 }
332 
333 /*
334  * dump the entries into the ioc_table
335  */
336 int
337 dump_table_ip_entry(struct radix_node *rn, void *arg)
338 {
339 	struct table_ip_entry *ent = (struct table_ip_entry *)rn;
340 	struct ipfw_ioc_table_ip_entry *ioc_ent;
341 	struct ipfw_ioc_table *tbl = arg;
342         struct sockaddr_in *addr, *mask;
343 
344         addr = &ent->addr;
345         mask = &ent->mask;
346 
347 	ioc_ent = &tbl->ip_ent[tbl->count];
348 	if (in_nullhost(mask->sin_addr))
349 		ioc_ent->masklen = 0;
350 	else
351 		ioc_ent->masklen = 33 - ffs(ntohl(mask->sin_addr.s_addr));
352 	ioc_ent->addr = addr->sin_addr.s_addr;
353 	tbl->count++;
354 	return (0);
355 }
356 
357 int
358 dump_table_mac_entry(struct radix_node *rn, void *arg)
359 {
360 	struct table_mac_entry *ent = (struct table_mac_entry *)rn;
361 	struct ipfw_ioc_table_mac_entry *ioc_ent;
362 	struct ipfw_ioc_table *tbl = arg;
363 	ioc_ent = &tbl->mac_ent[tbl->count];
364         strncpy(ioc_ent->addr.octet, ent->addr.sa_data, 6);
365 	tbl->count++;
366 	return (0);
367 }
368 
369 /*
370  * get and display all items in the table
371  */
372 int
373 ip_fw3_ctl_table_show(struct sockopt *sopt)
374 {
375 	struct ipfw3_context *ctx = fw3_ctx[mycpuid];
376 	struct ipfw3_table_context *table_ctx;
377 	struct radix_node_head *rnh;
378 	struct ipfw_ioc_table *tbl;
379 	void *data;
380 	int size;
381 
382 	int *id = (int *)sopt->sopt_val;
383 	table_ctx = ctx->table_ctx;
384 	table_ctx += *id;
385         if (table_ctx->type == 1) {
386                 size = table_ctx->count * sizeof(struct ipfw_ioc_table_ip_entry) +
387                                 sizeof(struct ipfw_ioc_table);
388                 if (sopt->sopt_valsize < size) {
389                         /* sopt_val is not big enough */
390                         bzero(sopt->sopt_val, sopt->sopt_valsize);
391                         return 0;
392                 }
393                 data = kmalloc(size, M_IPFW3_TABLE, M_NOWAIT | M_ZERO);
394                 tbl = (struct ipfw_ioc_table *)data;
395                 tbl->id = *id;
396                 tbl->type = table_ctx->type;
397 		strlcpy(tbl->name, table_ctx->name, IPFW_TABLE_NAME_LEN);
398                 rnh = table_ctx->node;
399                 rnh->rnh_walktree(rnh, dump_table_ip_entry, tbl);
400                 bcopy(tbl, sopt->sopt_val, size);
401                 sopt->sopt_valsize = size;
402                 kfree(data, M_IPFW3_TABLE);
403         } else if (table_ctx->type == 2) {
404                 size = table_ctx->count * sizeof(struct ipfw_ioc_table_mac_entry) +
405                                 sizeof(struct ipfw_ioc_table);
406                 if (sopt->sopt_valsize < size) {
407                         /* sopt_val is not big enough */
408                         bzero(sopt->sopt_val, sopt->sopt_valsize);
409                         return 0;
410                 }
411                 data = kmalloc(size, M_IPFW3_TABLE, M_NOWAIT | M_ZERO);
412                 tbl = (struct ipfw_ioc_table *)data;
413                 tbl->id = *id;
414                 tbl->type = table_ctx->type;
415 		strlcpy(tbl->name, table_ctx->name, IPFW_TABLE_NAME_LEN);
416                 rnh = table_ctx->node;
417                 rnh->rnh_walktree(rnh, dump_table_mac_entry, tbl);
418                 bcopy(tbl, sopt->sopt_val, size);
419                 sopt->sopt_valsize = size;
420                 kfree(data, M_IPFW3_TABLE);
421         }
422 	return 0;
423 }
424 
425 /*
426  * test whether the ip is in the table
427  */
428 int
429 ip_fw3_ctl_table_test(struct sockopt *sopt)
430 {
431 	struct ipfw3_context *ctx = fw3_ctx[mycpuid];
432 	struct ipfw3_table_context *table_ctx;
433 	struct radix_node_head *rnh;
434 	struct ipfw_ioc_table *tbl;
435 
436 	tbl = (struct ipfw_ioc_table *)sopt->sopt_val;
437 	table_ctx = ctx->table_ctx;
438 	table_ctx += tbl->id;
439 
440         if (table_ctx->type != tbl->type)
441                 goto done;
442 
443         rnh = table_ctx->node;
444         if (tbl->type == 1) {
445                 struct sockaddr_in sa;
446                 sa.sin_len = 8;
447                 sa.sin_addr.s_addr = tbl->ip_ent->addr;
448 
449                 if(rnh->rnh_lookup((char *)&sa, NULL, rnh) != NULL)
450                         return 0;
451         } else if (tbl->type == 2) {
452                 struct sockaddr sa;
453                 sa.sa_len = 8;
454                 strncpy(sa.sa_data, tbl->mac_ent->addr.octet, 6);
455 
456                 if(rnh->rnh_lookup((char *)&sa, NULL, rnh) != NULL)
457                         return 0;
458         } else {
459                 /* XXX TODO */
460         }
461 done:
462 	return 1;
463 }
464 
465 /*
466  * activate the table
467  */
468 int
469 ip_fw3_ctl_table_create(struct sockopt *sopt)
470 {
471 	struct netmsg_table tbmsg;
472 	bzero(&tbmsg,sizeof(tbmsg));
473 	tbmsg.ioc_table = sopt->sopt_val;
474 	netmsg_init(&tbmsg.base, NULL, &curthread->td_msgport,
475 			0, table_create_dispatch);
476 	netisr_domsg(&tbmsg.base, 0);
477 	return tbmsg.retval;
478 }
479 
480 /*
481  * deactivate the table
482  */
483 int
484 ip_fw3_ctl_table_delete(struct sockopt *sopt)
485 {
486 	struct netmsg_table tbmsg;
487 	bzero(&tbmsg,sizeof(tbmsg));
488 	tbmsg.ioc_table = sopt->sopt_val;
489 	netmsg_init(&tbmsg.base, NULL, &curthread->td_msgport,
490 			0, table_delete_dispatch);
491 	netisr_domsg(&tbmsg.base, 0);
492 	return tbmsg.retval;
493 }
494 
495 /*
496  * append an item into the table
497  */
498 int
499 ip_fw3_ctl_table_append(struct sockopt *sopt)
500 {
501 	struct netmsg_table tbmsg;
502 	bzero(&tbmsg,sizeof(tbmsg));
503 	tbmsg.ioc_table = sopt->sopt_val;
504 	netmsg_init(&tbmsg.base, NULL, &curthread->td_msgport,
505 			0, table_append_dispatch);
506 	netisr_domsg(&tbmsg.base, 0);
507 	return tbmsg.retval;
508 }
509 
510 /*
511  * rename an table
512  */
513 int
514 ip_fw3_ctl_table_rename(struct sockopt *sopt)
515 {
516 	struct netmsg_table tbmsg;
517 	bzero(&tbmsg,sizeof(tbmsg));
518 	tbmsg.ioc_table = sopt->sopt_val;
519 	netmsg_init(&tbmsg.base, NULL, &curthread->td_msgport,
520 			0, table_rename_dispatch);
521 	netisr_domsg(&tbmsg.base, 0);
522 	return tbmsg.retval;
523 }
524 
525 /*
526  * sockopt handler
527  */
528 int
529 ip_fw3_ctl_table_sockopt(struct sockopt *sopt)
530 {
531 	int error = 0;
532 	switch (sopt->sopt_name) {
533 		case IP_FW_TABLE_CREATE:
534 			error = ip_fw3_ctl_table_create(sopt);
535 			break;
536 		case IP_FW_TABLE_DELETE:
537 			error = ip_fw3_ctl_table_delete(sopt);
538 			break;
539 		case IP_FW_TABLE_APPEND:
540 			error = ip_fw3_ctl_table_append(sopt);
541 			break;
542 		case IP_FW_TABLE_REMOVE:
543 			error = ip_fw3_ctl_table_remove(sopt);
544 			break;
545 		case IP_FW_TABLE_LIST:
546 			error = ip_fw3_ctl_table_list(sopt);
547 			break;
548 		case IP_FW_TABLE_FLUSH:
549 			error = ip_fw3_ctl_table_flush(sopt);
550 			break;
551 		case IP_FW_TABLE_SHOW:
552 			error = ip_fw3_ctl_table_show(sopt);
553 			break;
554 		case IP_FW_TABLE_TEST:
555 			error = ip_fw3_ctl_table_test(sopt);
556 			break;
557 		case IP_FW_TABLE_RENAME:
558 			error = ip_fw3_ctl_table_rename(sopt);
559 			break;
560 		default:
561 			kprintf("ipfw table invalid socket option %d\n",
562 				sopt->sopt_name);
563 	}
564 	return error;
565 }
566 
567 /*
568  * it will be invoked during init of ipfw3
569  * this function will prepare the tables
570  */
571 void
572 ip_fw3_table_init_dispatch(netmsg_t nmsg)
573 {
574 	struct ipfw3_context *ctx = fw3_ctx[mycpuid];
575 	ctx->table_ctx = kmalloc(sizeof(struct ipfw3_table_context) * IPFW_TABLES_MAX,
576 			M_IPFW3_TABLE, M_WAITOK | M_ZERO);
577 	netisr_forwardmsg_all(&nmsg->base, mycpuid + 1);
578 }
579 
580 void
581 ip_fw3_table_fini_dispatch(netmsg_t nmsg)
582 {
583 	struct ipfw3_table_context *table_ctx, *tmp_table;
584 	int id;
585 	table_ctx = fw3_ctx[mycpuid]->table_ctx;
586 	tmp_table = table_ctx;
587 	for (id = 0; id < IPFW_TABLES_MAX; id++, table_ctx++) {
588 		rn_flush(table_ctx->node, flush_table_entry);
589 		/* XXX: should free the tree: rn_freehead(table_ctx->node) */
590 	}
591 	kfree(tmp_table, M_IPFW3_TABLE);
592 
593 	netisr_forwardmsg_all(&nmsg->base, mycpuid + 1);
594 }
595 
596 
597 void
598 ip_fw3_table_fini(void)
599 {
600 	struct netmsg_base msg;
601 
602 	netmsg_init(&msg, NULL, &curthread->td_msgport,
603 		0, ip_fw3_table_fini_dispatch);
604 
605 	netisr_domsg(&msg, 0);
606 }
607 
608 void
609 ip_fw3_table_init(void)
610 {
611 	struct netmsg_base msg;
612 
613 	ip_fw3_ctl_table_ptr = ip_fw3_ctl_table_sockopt;
614 	netmsg_init(&msg, NULL, &curthread->td_msgport,
615 		0, ip_fw3_table_init_dispatch);
616 	netisr_domsg(&msg, 0);
617 }
618 
619 
620 void
621 ip_fw3_table_modevent(int type)
622 {
623 	switch (type) {
624 		case MOD_LOAD:
625 			ip_fw3_table_init();
626 			break;
627 		case MOD_UNLOAD:
628 			ip_fw3_table_fini();
629 			break;
630 	}
631 }
632