HAMMER Utilities: Sync with recent work.
[dragonfly.git] / contrib / ipfilter / mli_ipl.c
blob235a5af21f1739ab6c26e2e2d5220c9dfbf50541
1 /*
2 * Copyright (C) 1993-2001 by Darren Reed.
3 * (C)opyright 1997 by Marc Boucher.
5 * See the IPFILTER.LICENCE file for details on licencing.
6 */
8 /* TODO: (MARCXXX)
9 - ipl_init failure -> open ENODEV or whatever
10 - prevent multiple LKM loads
11 - surround access to ifnet structures by IFNET_LOCK()/IFNET_UNLOCK() ?
12 - m != m1 problem
15 #include <sys/types.h>
16 #include <sys/conf.h>
17 #ifdef IPFILTER_LKM
18 #include <sys/mload.h>
19 #endif
20 #include <sys/systm.h>
21 #include <sys/errno.h>
22 #include <net/if.h>
23 #include <net/route.h>
24 #include <netinet/in.h>
25 #ifdef IFF_DRVRLOCK /* IRIX6 */
26 #include <sys/hashing.h>
27 #include <netinet/in_var.h>
28 #endif
29 #include <sys/mbuf.h>
30 #include <netinet/in_systm.h>
31 #include <netinet/ip.h>
32 #include <netinet/ip_var.h>
33 #include <netinet/tcp.h>
34 #include <netinet/udp.h>
35 #include <netinet/tcpip.h>
36 #include <netinet/ip_icmp.h>
37 #include <netinet/ipfilter.h>
38 #include "ipl.h"
39 #include "ip_compat.h"
40 #include "ip_fil.h"
41 #include "ip_nat.h"
43 /*#define IPFDEBUG 1*/
45 unsigned IPL_EXTERN(devflag) = D_MP;
46 #ifdef IPFILTER_LKM
47 char *IPL_EXTERN(mversion) = M_VERSION;
48 #endif
50 kmutex_t ipl_mutex, ipf_mutex, ipfi_mutex, ipf_rw;
51 kmutex_t ipf_frag, ipf_state, ipf_nat, ipf_natfrag, ipf_auth;
53 int (*fr_checkp) __P((struct ip *, int, void *, int, mb_t **));
55 #ifdef IPFILTER_LKM
56 static int *ipff_addr = 0;
57 static int ipff_value;
58 static __psunsigned_t *ipfk_addr = 0;
59 static __psunsigned_t ipfk_code[4];
60 #endif
62 typedef struct nif {
63 struct nif *nf_next;
64 struct ifnet *nf_ifp;
65 #if IRIX < 605
66 int (*nf_output)(struct ifnet *, struct mbuf *, struct sockaddr *);
67 #else
68 int (*nf_output)(struct ifnet *, struct mbuf *, struct sockaddr *,
69 struct rtentry *);
70 #endif
71 char nf_name[IFNAMSIZ];
72 int nf_unit;
73 } nif_t;
75 static nif_t *nif_head = 0;
76 static int nif_interfaces = 0;
77 extern int in_interfaces;
79 extern ipnat_t *nat_list;
81 static int
82 #if IRIX < 605
83 ipl_if_output(struct ifnet *ifp, struct mbuf *m, struct sockaddr *dst)
84 #else
85 ipl_if_output(struct ifnet *ifp, struct mbuf *m, struct sockaddr *dst,
86 struct rtentry *rt)
87 #endif
89 nif_t *nif;
91 MUTEX_ENTER(&ipfi_mutex); /* sets interrupt priority level to splhi */
92 for (nif = nif_head; nif; nif = nif->nf_next)
93 if (nif->nf_ifp == ifp)
94 break;
96 MUTEX_EXIT(&ipfi_mutex);
97 if (!nif) {
98 printf("IP Filter: ipl_if_output intf %x NOT FOUND\n", ifp);
99 return ENETDOWN;
102 #if IPFDEBUG >= 4
103 static unsigned int cnt = 0;
104 if ((++cnt % 200) == 0)
105 printf("IP Filter: ipl_if_output(ifp=0x%lx, m=0x%lx, dst=0x%lx), m_type=%d m_flags=0x%lx m_off=0x%lx\n", ifp, m, dst, m->m_type, (unsigned long)(m->m_flags), m->m_off);
106 #endif
107 if (fr_checkp) {
108 struct mbuf *m1 = m;
109 struct ip *ip;
110 int hlen;
112 switch(m->m_type) {
113 case MT_DATA:
114 if (m->m_flags & M_BCAST) {
115 #if IPFDEBUG >= 2
116 printf("IP Filter: ipl_if_output: passing M_BCAST\n");
117 #endif
118 break;
120 /* FALLTHROUGH */
121 case MT_HEADER:
122 #if IPFDEBUG >= 4
123 if (!MBUF_IS_CLUSTER(m) && ((m->m_off < MMINOFF) || (m->m_off > MMAXOFF))) {
124 printf("IP Filter: ipl_if_output: bad m_off m_type=%d m_flags=0x%lx m_off=0x%lx\n", m->m_type, (unsigned long)(m->m_flags), m->m_off);
125 goto done;
127 #endif
128 if (m->m_len < sizeof(char)) {
129 printf("IP Filter: ipl_if_output: mbuf block too small (m_len=%d) for IP vers+hlen, m_type=%d m_flags=0x%lx\n", m->m_len, m->m_type, (unsigned long)(m->m_flags));
130 goto done;
132 ip = mtod(m, struct ip *);
133 if (ip->ip_v != IPVERSION) {
134 #if IPFDEBUG >= 4
135 printf("IP Filter: ipl_if_output: bad ip_v m_type=%d m_flags=0x%lx m_off=0x%lx\n", m->m_type, (unsigned long)(m->m_flags), m->m_off);
136 #endif
137 goto done;
140 hlen = ip->ip_hl << 2;
141 if ((*fr_checkp)(ip, hlen, ifp, 1, &m1))
142 return EHOSTUNREACH;
144 if (!m1)
145 return 0;
147 m = m1;
148 break;
150 default:
151 printf("IP Filter: ipl_if_output: bad m_type=%d m_flags=0x%lxm_off=0x%lx\n", m->m_type, (unsigned long)(m->m_flags), m->m_off);
152 break;
155 done:
156 #if IRIX < 605
157 return (*nif->nf_output)(ifp, m, dst);
158 #else
159 return (*nif->nf_output)(ifp, m, dst, rt);
160 #endif
164 IPL_EXTERN(_kernel)(struct ifnet *rcvif, struct mbuf *m)
166 #if IPFDEBUG >= 4
167 static unsigned int cnt = 0;
168 if ((++cnt % 200) == 0)
169 printf("IP Filter: ipl_ipfilter_kernel(rcvif=0x%lx, m=0x%lx\n", rcvif, m);
170 #endif
173 * Check if we want to allow this packet to be processed.
174 * Consider it to be bad if not.
176 if (fr_checkp) {
177 struct mbuf *m1 = m;
178 struct ip *ip;
179 int hlen;
181 if ((m->m_type != MT_DATA) && (m->m_type != MT_HEADER)) {
182 printf("IP Filter: ipl_ipfilter_kernel: bad m_type=%d m_flags=0x%lx m_off=0x%lx\n", m->m_type, (unsigned long)(m->m_flags), m->m_off);
183 return IPF_ACCEPTIT;
186 #if IPFDEBUG >= 4
187 if (!MBUF_IS_CLUSTER(m) && ((m->m_off < MMINOFF) || (m->m_off > MMAXOFF))) {
188 printf("IP Filter: ipl_ipfilter_kernel: bad m_off m_type=%d m_flags=0x%lx m_off=0x%lx\n", m->m_type, (unsigned long)(m->m_flags), m->m_off);
189 return IPF_ACCEPTIT;
191 #endif
192 if (m->m_len < sizeof(char)) {
193 printf("IP Filter: ipl_ipfilter_kernel: mbuf block too small (m_len=%d) for IP vers+hlen, m_type=%d m_flags=0x%lx\n", m->m_len, m->m_type, (unsigned long)(m->m_flags));
194 return IPF_ACCEPTIT;
196 ip = mtod(m, struct ip *);
197 if (ip->ip_v != IPVERSION) {
198 printf("IP Filter: ipl_ipfilter_kernel: bad ip_v\n");
199 m_freem(m);
200 return IPF_DROPIT;
203 hlen = ip->ip_hl << 2;
204 if ((*fr_checkp)(ip, hlen, rcvif, 0, &m1) || !m1)
205 return IPF_DROPIT;
206 if (m != m1)
207 printf("IP Filter: ipl_ipfilter_kernel: m != m1\n");
210 return IPF_ACCEPTIT;
213 static int
214 ipfilterattach(void)
216 #ifdef IPFILTER_LKM
217 __psunsigned_t *addr_ff, *addr_fk;
219 st_findaddr("ipfilterflag", &addr_ff);
220 #if IPFDEBUG >= 4
221 printf("IP Filter: st_findaddr ipfilterflag=0x%lx\n", addr_ff);
222 #endif
223 if (!addr_ff)
224 return ESRCH;
226 st_findaddr("ipfilter_kernel", &addr_fk);
227 #if IPFDEBUG >= 4
228 printf("IP Filter: st_findaddr ipfilter_kernel=0x%lx\n", addr_fk);
229 #endif
230 if (!addr_fk)
231 return ESRCH;
233 MUTEX_ENTER(&ipfi_mutex); /* sets interrupt priority level to splhi */
235 ipff_addr = (int *)addr_ff;
237 ipff_value = *ipff_addr;
238 *ipff_addr = 0;
241 ipfk_addr = addr_fk;
243 bcopy(ipfk_addr, ipfk_code,
244 sizeof(ipfk_code));
246 /* write a "li t4, ipl_ipfilter_kernel" instruction */
247 ipfk_addr[0] = 0x3c0c0000 |
248 (((__psunsigned_t)IPL_EXTERN(_kernel) >> 16) & 0xffff);
249 ipfk_addr[1] = 0x358c0000 |
250 ((__psunsigned_t)IPL_EXTERN(_kernel) & 0xffff);
251 /* write a "jr t4" instruction" */
252 ipfk_addr[2] = 0x01800008;
254 /* write a "nop" instruction */
255 ipfk_addr[3] = 0;
257 icache_inval(ipfk_addr, sizeof(ipfk_code));
259 *ipff_addr = 1; /* enable ipfilter_kernel */
261 MUTEX_EXIT(&ipfi_mutex);
262 #else
263 extern int ipfilterflag;
265 ipfilterflag = 1;
266 #endif
268 return 0;
272 * attach the packet filter to each non-loopback interface that is running
274 static void
275 nifattach()
277 struct ifnet *ifp;
278 struct frentry *f;
279 ipnat_t *np;
280 nif_t *nif;
282 MUTEX_ENTER(&ipfi_mutex); /* sets interrupt priority level to splhi */
284 for (ifp = ifnet; ifp; ifp = ifp->if_next) {
285 if ((!(ifp->if_flags & IFF_RUNNING)) ||
286 (ifp->if_flags & IFF_LOOPBACK))
287 continue;
290 * Look for entry already setup for this device
292 for (nif = nif_head; nif; nif = nif->nf_next)
293 if (nif->nf_ifp == ifp)
294 break;
295 if (nif)
296 continue;
298 if (ifp->if_output == ipl_if_output) {
299 printf("IP Filter: ERROR INTF 0x%lx STILL ATTACHED\n",
300 ifp);
301 continue;
303 #if IPFDEBUG >= 4
304 printf("IP Filter: nifattach nif %x opt %x\n",
305 ifp, ifp->if_output);
306 #endif
307 KMALLOC(nif, nif_t *);
308 if (!nif) {
309 printf("IP Filter: malloc(%d) for nif_t failed\n",
310 sizeof(nif_t));
311 continue;
314 nif->nf_ifp = ifp;
315 strncpy(nif->nf_name, ifp->if_name, sizeof(nif->nf_name));
316 nif->nf_name[sizeof(nif->nf_name) - 1] = '\0';
317 nif->nf_unit = ifp->if_unit;
319 nif->nf_next = nif_head;
320 nif_head = nif;
323 * Activate any rules directly associated with this interface
325 MUTEX_ENTER(&ipf_mutex);
326 for (f = ipfilter[0][0]; f; f = f->fr_next) {
327 if ((f->fr_ifa == (struct ifnet *)-1)) {
328 if (f->fr_ifname[0] &&
329 (GETUNIT(f->fr_ifname, 4) == ifp))
330 f->fr_ifa = ifp;
333 for (f = ipfilter[1][0]; f; f = f->fr_next) {
334 if ((f->fr_ifa == (struct ifnet *)-1)) {
335 if (f->fr_ifname[0] &&
336 (GETUNIT(f->fr_ifname, 4) == ifp))
337 f->fr_ifa = ifp;
340 MUTEX_EXIT(&ipf_mutex);
341 MUTEX_ENTER(&ipf_nat);
342 for (np = nat_list; np; np = np->in_next) {
343 if ((np->in_ifp == (void *)-1)) {
344 if (np->in_ifname[0] &&
345 (GETUNIT(np->in_ifname, 4) == ifp))
346 np->in_ifp = (void *)ifp;
349 MUTEX_EXIT(&ipf_nat);
351 nif->nf_output = ifp->if_output;
352 ifp->if_output = ipl_if_output;
354 #if IPFDEBUG >= 4
355 printf("IP Filter: nifattach: ifp(%lx)->if_output FROM %lx TO %lx\n",
356 ifp, nif->nf_output, ifp->if_output);
357 #endif
359 printf("IP Filter: attach to [%s,%d]\n",
360 nif->nf_name, ifp->if_unit);
362 if (!nif_head)
363 printf("IP Filter: not attached to any interfaces\n");
365 nif_interfaces = in_interfaces;
367 MUTEX_EXIT(&ipfi_mutex);
369 return;
373 * look for bad consistancies between the list of interfaces the filter knows
374 * about and those which are currently configured.
377 ipfsync(void)
379 register struct frentry *f;
380 register ipnat_t *np;
381 register nif_t *nif, **qp;
382 register struct ifnet *ifp;
384 MUTEX_ENTER(&ipfi_mutex); /* sets interrupt priority level to splhi */
385 for (qp = &nif_head; (nif = *qp); ) {
386 for (ifp = ifnet; ifp; ifp = ifp->if_next)
387 if ((nif->nf_ifp == ifp) &&
388 (nif->nf_unit == ifp->if_unit) &&
389 !strcmp(nif->nf_name, ifp->if_name)) {
390 break;
392 if (ifp) {
393 qp = &nif->nf_next;
394 continue;
396 printf("IP Filter: detaching [%s]\n", nif->nf_name);
397 *qp = nif->nf_next;
400 * Disable any rules directly associated with this interface
402 MUTEX_ENTER(&ipf_mutex);
403 for (f = ipfilter[0][0]; f; f = f->fr_next)
404 if (f->fr_ifa == (void *)nif->nf_ifp)
405 f->fr_ifa = (struct ifnet *)-1;
406 for (f = ipfilter[1][0]; f; f = f->fr_next)
407 if (f->fr_ifa == (void *)nif->nf_ifp)
408 f->fr_ifa = (struct ifnet *)-1;
409 MUTEX_EXIT(&ipf_mutex);
410 MUTEX_ENTER(&ipf_nat);
411 for (np = nat_list; np; np = np->in_next)
412 if (np->in_ifp == (void *)nif->nf_ifp)
413 np->in_ifp =(struct ifnet *)-1;
414 MUTEX_EXIT(&ipf_nat);
416 KFREE(nif);
417 nif = *qp;
419 MUTEX_EXIT(&ipfi_mutex);
421 nifattach();
423 return 0;
428 * unhook the IP filter from all defined interfaces with IP addresses
430 static void
431 nifdetach()
433 struct ifnet *ifp;
434 nif_t *nif, **qp;
436 MUTEX_ENTER(&ipfi_mutex); /* sets interrupt priority level to splhi */
438 * Make two passes, first get rid of all the unknown devices, next
439 * unlink known devices.
441 for (qp = &nif_head; (nif = *qp); ) {
442 for (ifp = ifnet; ifp; ifp = ifp->if_next)
443 if (nif->nf_ifp == ifp)
444 break;
445 if (ifp) {
446 qp = &nif->nf_next;
447 continue;
449 printf("IP Filter: removing [%s]\n", nif->nf_name);
450 *qp = nif->nf_next;
451 KFREE(nif);
454 while ((nif = nif_head)) {
455 nif_head = nif->nf_next;
456 for (ifp = ifnet; ifp; ifp = ifp->if_next)
457 if (nif->nf_ifp == ifp)
458 break;
459 if (ifp) {
460 printf("IP Filter: detaching [%s,%d]\n",
461 nif->nf_name, ifp->if_unit);
463 #if IPFDEBUG >= 4
464 printf("IP Filter: nifdetach: ifp(%lx)->if_output FROM %lx TO %lx\n",
465 ifp, ifp->if_output, nif->nf_output);
466 #endif
467 ifp->if_output = nif->nf_output;
469 KFREE(nif);
471 MUTEX_EXIT(&ipfi_mutex);
473 return;
477 static void
478 ipfilterdetach(void)
480 #ifdef IPFILTER_LKM
481 MUTEX_ENTER(&ipfi_mutex); /* sets interrupt priority level to splhi */
483 if (ipff_addr) {
484 *ipff_addr = 0;
486 if (ipfk_addr)
487 bcopy(ipfk_code, ipfk_addr, sizeof(ipfk_code));
489 *ipff_addr = ipff_value;
492 MUTEX_EXIT(&ipfi_mutex);
493 #else
494 extern int ipfilterflag;
496 ipfilterflag = 0;
497 #endif
500 /* called by ipldetach() */
501 void
502 ipfilter_sgi_detach(void)
504 nifdetach();
506 ipfilterdetach();
509 /* called by iplattach() */
511 ipfilter_sgi_attach(void)
513 int error;
515 nif_interfaces = 0;
517 error = ipfilterattach();
519 if (!error)
520 nifattach();
522 return error;
525 /* this function is called from ipfr_slowtimer at 500ms intervals to
526 keep our interface list in sync */
527 void
528 ipfilter_sgi_intfsync(void)
530 MUTEX_ENTER(&ipfi_mutex);
531 if (nif_interfaces != in_interfaces) {
532 /* if the number of interfaces has changed, resync */
533 MUTEX_EXIT(&ipfi_mutex);
534 ipfsync();
535 } else
536 MUTEX_EXIT(&ipfi_mutex);
539 #ifdef IPFILTER_LKM
540 /* this routine should be treated as an interrupt routine and should
541 not call any routines that would cause it to sleep, such as: biowait(),
542 sleep(), psema() or delay().
545 IPL_EXTERN(unload)(void)
547 int error = 0;
549 error = ipldetach();
551 LOCK_DEALLOC(ipl_mutex.l);
552 LOCK_DEALLOC(ipf_rw.l);
553 LOCK_DEALLOC(ipf_auth.l);
554 LOCK_DEALLOC(ipf_natfrag.l);
555 LOCK_DEALLOC(ipf_nat.l);
556 LOCK_DEALLOC(ipf_state.l);
557 LOCK_DEALLOC(ipf_frag.l);
558 LOCK_DEALLOC(ipf_mutex.l);
559 LOCK_DEALLOC(ipfi_mutex.l);
561 return error;
563 #endif
565 void
566 IPL_EXTERN(init)(void)
568 #ifdef IPFILTER_LKM
569 int error;
570 #endif
572 ipfi_mutex.l = LOCK_ALLOC((uchar_t)-1, IPF_LOCK_PL, (lkinfo_t *)-1, KM_NOSLEEP);
573 ipf_mutex.l = LOCK_ALLOC((uchar_t)-1, IPF_LOCK_PL, (lkinfo_t *)-1, KM_NOSLEEP);
574 ipf_frag.l = LOCK_ALLOC((uchar_t)-1, IPF_LOCK_PL, (lkinfo_t *)-1, KM_NOSLEEP);
575 ipf_state.l = LOCK_ALLOC((uchar_t)-1, IPF_LOCK_PL, (lkinfo_t *)-1, KM_NOSLEEP);
576 ipf_nat.l = LOCK_ALLOC((uchar_t)-1, IPF_LOCK_PL, (lkinfo_t *)-1, KM_NOSLEEP);
577 ipf_natfrag.l = LOCK_ALLOC((uchar_t)-1, IPF_LOCK_PL, (lkinfo_t *)-1, KM_NOSLEEP);
578 ipf_auth.l = LOCK_ALLOC((uchar_t)-1, IPF_LOCK_PL, (lkinfo_t *)-1, KM_NOSLEEP);
579 ipf_rw.l = LOCK_ALLOC((uchar_t)-1, IPF_LOCK_PL, (lkinfo_t *)-1, KM_NOSLEEP);
580 ipl_mutex.l = LOCK_ALLOC((uchar_t)-1, IPF_LOCK_PL, (lkinfo_t *)-1, KM_NOSLEEP);
582 if (!ipfi_mutex.l || !ipf_mutex.l || !ipf_frag.l || !ipf_state.l ||
583 !ipf_nat.l || !ipf_natfrag.l || !ipf_auth.l || !ipf_rw.l ||
584 !ipl_mutex.l)
585 panic("IP Filter: LOCK_ALLOC failed");
587 #ifdef IPFILTER_LKM
588 error = iplattach();
589 if (error) {
590 IPL_EXTERN(unload)();
592 #endif
594 return;