Merge from vendor branch CVS:
[dragonfly.git] / contrib / ipfilter / mli_ipl.c
1 /*
2  * Copyright (C) 1993-2001 by Darren Reed.
3  * (C)opyright 1997 by Marc Boucher.
4  *
5  * See the IPFILTER.LICENCE file for details on licencing.
6  */
7
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
13 */
14
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"
42
43 /*#define IPFDEBUG 1*/
44
45 unsigned IPL_EXTERN(devflag) = D_MP;
46 #ifdef IPFILTER_LKM
47 char *IPL_EXTERN(mversion) = M_VERSION;
48 #endif
49
50 kmutex_t ipl_mutex, ipf_mutex, ipfi_mutex, ipf_rw;
51 kmutex_t ipf_frag, ipf_state, ipf_nat, ipf_natfrag, ipf_auth;
52
53 int     (*fr_checkp) __P((struct ip *, int, void *, int, mb_t **));
54
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
61
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;
74
75 static nif_t *nif_head = 0;
76 static int nif_interfaces = 0;
77 extern int in_interfaces;
78
79 extern ipnat_t *nat_list;
80
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
88 {
89         nif_t *nif;
90
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;
95
96         MUTEX_EXIT(&ipfi_mutex);
97         if (!nif) {
98                 printf("IP Filter: ipl_if_output intf %x NOT FOUND\n", ifp);
99                 return ENETDOWN;
100         }
101
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;
111
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;
119                         }
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;
126                         }
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;
131                         }
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;
138                         }
139
140                         hlen = ip->ip_hl << 2;
141                         if ((*fr_checkp)(ip, hlen, ifp, 1, &m1))
142                                 return EHOSTUNREACH;
143
144                         if (!m1)
145                                 return 0;
146
147                         m = m1;
148                         break;
149
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;
153                 }
154         }
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
161 }
162
163 int
164 IPL_EXTERN(_kernel)(struct ifnet *rcvif, struct mbuf *m)
165 {
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
171
172         /*
173          * Check if we want to allow this packet to be processed.
174          * Consider it to be bad if not.
175          */
176         if (fr_checkp) {
177                 struct mbuf *m1 = m;
178                 struct ip *ip;
179                 int hlen;
180
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;
184                 }
185
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;
190                 }
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;
195                 }
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;
201                 }
202
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");
208         }
209
210         return IPF_ACCEPTIT;
211 }
212
213 static int
214 ipfilterattach(void)
215 {
216 #ifdef IPFILTER_LKM
217         __psunsigned_t *addr_ff, *addr_fk;
218
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;
225
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;
232
233         MUTEX_ENTER(&ipfi_mutex); /* sets interrupt priority level to splhi */
234
235         ipff_addr = (int *)addr_ff;
236         
237         ipff_value = *ipff_addr;
238         *ipff_addr = 0;
239
240
241         ipfk_addr = addr_fk;
242
243         bcopy(ipfk_addr, ipfk_code,
244                 sizeof(ipfk_code));
245
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;
253
254         /* write a "nop" instruction */
255         ipfk_addr[3] = 0;
256
257         icache_inval(ipfk_addr, sizeof(ipfk_code));
258
259         *ipff_addr = 1; /* enable ipfilter_kernel */
260
261         MUTEX_EXIT(&ipfi_mutex);
262 #else
263         extern int ipfilterflag;
264
265         ipfilterflag = 1;
266 #endif
267
268         return 0;
269 }
270
271 /*
272  * attach the packet filter to each non-loopback interface that is running
273  */
274 static void
275 nifattach()
276 {
277         struct ifnet *ifp;
278         struct frentry *f;
279         ipnat_t *np;
280         nif_t *nif;
281
282         MUTEX_ENTER(&ipfi_mutex); /* sets interrupt priority level to splhi */
283
284         for (ifp = ifnet; ifp; ifp = ifp->if_next) {
285                 if ((!(ifp->if_flags & IFF_RUNNING)) ||
286                         (ifp->if_flags & IFF_LOOPBACK))
287                         continue;
288
289                 /*
290                  * Look for entry already setup for this device
291                  */
292                 for (nif = nif_head; nif; nif = nif->nf_next)
293                         if (nif->nf_ifp == ifp)
294                                 break;
295                 if (nif)
296                         continue;
297
298                 if (ifp->if_output == ipl_if_output) {
299                         printf("IP Filter: ERROR INTF 0x%lx STILL ATTACHED\n",
300                                 ifp);
301                         continue;
302                 }
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;
312                 }
313
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;
318
319                 nif->nf_next = nif_head;
320                 nif_head = nif;
321
322                 /*
323                  * Activate any rules directly associated with this interface
324                  */
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;
331                         }
332                 }
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;
338                         }
339                 }
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;
347                         }
348                 }
349                 MUTEX_EXIT(&ipf_nat);
350
351                 nif->nf_output = ifp->if_output;
352                 ifp->if_output = ipl_if_output;
353
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
358
359                 printf("IP Filter: attach to [%s,%d]\n",
360                         nif->nf_name, ifp->if_unit);
361         }
362         if (!nif_head)
363                 printf("IP Filter: not attached to any interfaces\n");
364
365         nif_interfaces = in_interfaces;
366
367         MUTEX_EXIT(&ipfi_mutex);
368
369         return;
370 }
371
372 /*
373  * look for bad consistancies between the list of interfaces the filter knows
374  * about and those which are currently configured.
375  */
376 int
377 ipfsync(void)
378 {
379         register struct frentry *f;
380         register ipnat_t *np;
381         register nif_t *nif, **qp;
382         register struct ifnet *ifp;
383
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;
391                         }
392                 if (ifp) {
393                         qp = &nif->nf_next;
394                         continue;
395                 }
396                 printf("IP Filter: detaching [%s]\n", nif->nf_name);
397                 *qp = nif->nf_next;
398
399                 /*
400                  * Disable any rules directly associated with this interface
401                  */
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);
415
416                 KFREE(nif);
417                 nif = *qp;
418         }
419         MUTEX_EXIT(&ipfi_mutex);
420
421         nifattach();
422
423         return 0;
424 }
425
426
427 /*
428  * unhook the IP filter from all defined interfaces with IP addresses
429  */
430 static void
431 nifdetach()
432 {
433         struct ifnet *ifp;
434         nif_t *nif, **qp;
435
436         MUTEX_ENTER(&ipfi_mutex); /* sets interrupt priority level to splhi */
437         /*
438          * Make two passes, first get rid of all the unknown devices, next
439          * unlink known devices.
440          */
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;
448                 }
449                 printf("IP Filter: removing [%s]\n", nif->nf_name);
450                 *qp = nif->nf_next;
451                 KFREE(nif);
452         }
453
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);
462
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;
468                 }
469                 KFREE(nif);
470         }
471         MUTEX_EXIT(&ipfi_mutex);
472
473         return;
474 }
475
476
477 static void
478 ipfilterdetach(void)
479 {
480 #ifdef IPFILTER_LKM
481         MUTEX_ENTER(&ipfi_mutex); /* sets interrupt priority level to splhi */
482
483         if (ipff_addr) {
484                 *ipff_addr = 0;
485
486                 if (ipfk_addr)
487                         bcopy(ipfk_code, ipfk_addr, sizeof(ipfk_code));
488
489                 *ipff_addr = ipff_value;
490         }
491
492         MUTEX_EXIT(&ipfi_mutex);
493 #else
494         extern int ipfilterflag;
495
496         ipfilterflag = 0;
497 #endif
498 }
499
500 /* called by ipldetach() */
501 void
502 ipfilter_sgi_detach(void)
503 {
504         nifdetach();
505
506         ipfilterdetach();
507 }
508
509 /* called by iplattach() */
510 int
511 ipfilter_sgi_attach(void)
512 {
513         int error;
514
515         nif_interfaces = 0;
516
517         error = ipfilterattach();
518
519         if (!error)
520                 nifattach();
521
522         return error;
523 }
524
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)
529 {
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);
537 }
538
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().
543 */
544 int
545 IPL_EXTERN(unload)(void)
546 {
547         int error = 0;
548
549         error = ipldetach();
550
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);
560
561         return error;
562 }
563 #endif
564
565 void
566 IPL_EXTERN(init)(void)
567 {
568 #ifdef IPFILTER_LKM
569         int error;
570 #endif
571
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);
581
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");
586
587 #ifdef IPFILTER_LKM
588         error = iplattach();
589         if (error) {
590                 IPL_EXTERN(unload)();
591         }
592 #endif
593
594         return;
595 }
596