source: rtems/cpukit/libnetworking/netinet/ip_fw.c @ e67b6ea1

4.104.114.84.9
Last change on this file since e67b6ea1 was e67b6ea1, checked in by Ralf Corsepius <ralf.corsepius@…>, on Mar 29, 2007 at 5:16:42 AM

Eliminate P().

  • Property mode set to 100644
File size: 25.5 KB
Line 
1/*
2 * Copyright (c) 1996 Alex Nash
3 * Copyright (c) 1993 Daniel Boulet
4 * Copyright (c) 1994 Ugen J.S.Antsilevich
5 *
6 * Redistribution and use in source forms, with and without modification,
7 * are permitted provided that this entire comment appears intact.
8 *
9 * Redistribution in binary form may occur without any restrictions.
10 * Obviously, it would be nice if you gave credit where credit is due
11 * but requiring it would be too onerous.
12 *
13 * This software is provided ``AS IS'' without any warranties of any kind.
14 *
15 *      $Id$
16 */
17
18/*
19 * Implement IP packet firewall
20 */
21
22#ifndef IPFIREWALL_MODULE
23#include "opt_ipfw.h"
24#endif
25
26#include <sys/param.h>
27#include <sys/systm.h>
28#include <sys/malloc.h>
29#include <sys/mbuf.h>
30#include <sys/queue.h>
31#include <sys/kernel.h>
32#include <sys/socket.h>
33#include <sys/time.h>
34#include <sys/sysctl.h>
35#include <net/if.h>
36#include <net/route.h>
37#include <netinet/in.h>
38#include <netinet/in_systm.h>
39#include <netinet/ip.h>
40#include <netinet/ip_var.h>
41#include <netinet/ip_icmp.h>
42#include <netinet/ip_fw.h>
43#include <netinet/tcp.h>
44#include <netinet/tcp_timer.h>
45#include <netinet/tcp_var.h>
46#include <netinet/tcpip.h>
47#include <netinet/udp.h>
48
49static int fw_debug = 1;
50#ifdef IPFIREWALL_VERBOSE
51static int fw_verbose = 1;
52#else
53static int fw_verbose = 0;
54#endif
55#ifdef IPFIREWALL_VERBOSE_LIMIT
56static int fw_verbose_limit = IPFIREWALL_VERBOSE_LIMIT;
57#else
58static int fw_verbose_limit = 0;
59#endif
60
61LIST_HEAD (ip_fw_head, ip_fw_chain) ip_fw_chain;
62
63/*
64 * ccj - No current need for firewall so have provided the MIB.
65 */
66#if 0
67#ifdef SYSCTL_NODE
68SYSCTL_NODE(_net_inet_ip, OID_AUTO, fw, CTLFLAG_RW, 0, "Firewall");
69SYSCTL_INT(_net_inet_ip_fw, OID_AUTO, debug, CTLFLAG_RW, &fw_debug, 0, "");
70SYSCTL_INT(_net_inet_ip_fw, OID_AUTO, verbose, CTLFLAG_RW, &fw_verbose, 0, "");
71SYSCTL_INT(_net_inet_ip_fw, OID_AUTO, verbose_limit, CTLFLAG_RW, &fw_verbose_limit, 0, "");
72#endif
73#endif
74
75#define dprintf(a)      if (!fw_debug); else printf a
76
77#define print_ip(a)      printf("%ld.%ld.%ld.%ld",(ntohl(a.s_addr)>>24)&0xFF,\
78                                                  (ntohl(a.s_addr)>>16)&0xFF,\
79                                                  (ntohl(a.s_addr)>>8)&0xFF,\
80                                                  (ntohl(a.s_addr))&0xFF);
81
82#define dprint_ip(a)    if (!fw_debug); else print_ip(a)
83
84static int      add_entry(struct ip_fw_head *chainptr, struct ip_fw *frwl);
85static int      del_entry(struct ip_fw_head *chainptr, u_short number);
86static int      zero_entry(struct mbuf *m);
87static struct ip_fw *check_ipfw_struct(struct ip_fw *m);
88static struct ip_fw *check_ipfw_mbuf(struct mbuf *fw);
89static int      ipopts_match(struct ip *ip, struct ip_fw *f);
90static int      port_match(u_short *portptr, int nports, u_short port,
91                                int range_flag);
92static int      tcpflg_match(struct tcphdr *tcp, struct ip_fw *f);
93static int      icmptype_match(struct icmp *  icmp, struct ip_fw * f);
94static void     ipfw_report(struct ip_fw *f, struct ip *ip,
95                                struct ifnet *rif, struct ifnet *oif);
96
97#ifdef IPFIREWALL_MODULE
98static ip_fw_chk_t *old_chk_ptr;
99static ip_fw_ctl_t *old_ctl_ptr;
100#endif
101
102static int      ip_fw_chk(struct ip **pip, int hlen,
103                        struct ifnet *oif, int ignport, struct mbuf **m);
104static int      ip_fw_ctl(int stage, struct mbuf **mm);
105
106static char err_prefix[] = "ip_fw_ctl:";
107
108/*
109 * Returns 1 if the port is matched by the vector, 0 otherwise
110 */
111static inline int 
112port_match(u_short *portptr, int nports, u_short port, int range_flag)
113{
114        if (!nports)
115                return 1;
116        if (range_flag) {
117                if (portptr[0] <= port && port <= portptr[1]) {
118                        return 1;
119                }
120                nports -= 2;
121                portptr += 2;
122        }
123        while (nports-- > 0) {
124                if (*portptr++ == port) {
125                        return 1;
126                }
127        }
128        return 0;
129}
130
131static int
132tcpflg_match(struct tcphdr *tcp, struct ip_fw *f)
133{
134        u_char          flg_set, flg_clr;
135       
136        if ((f->fw_tcpf & IP_FW_TCPF_ESTAB) &&
137            (tcp->th_flags & (IP_FW_TCPF_RST | IP_FW_TCPF_ACK)))
138                return 1;
139
140        flg_set = tcp->th_flags & f->fw_tcpf;
141        flg_clr = tcp->th_flags & f->fw_tcpnf;
142
143        if (flg_set != f->fw_tcpf)
144                return 0;
145        if (flg_clr)
146                return 0;
147
148        return 1;
149}
150
151static int
152icmptype_match(struct icmp *icmp, struct ip_fw *f)
153{
154        int type;
155
156        if (!(f->fw_flg & IP_FW_F_ICMPBIT))
157                return(1);
158
159        type = icmp->icmp_type;
160
161        /* check for matching type in the bitmap */
162        if (type < IP_FW_ICMPTYPES_DIM * sizeof(unsigned) * 8 &&
163                (f->fw_icmptypes[type / (sizeof(unsigned) * 8)] & 
164                (1U << (type % (8 * sizeof(unsigned))))))
165                return(1);
166
167        return(0); /* no match */
168}
169
170static int
171ipopts_match(struct ip *ip, struct ip_fw *f)
172{
173        register u_char *cp;
174        int opt, optlen, cnt;
175        u_char  opts, nopts, nopts_sve;
176
177        cp = (u_char *)(ip + 1);
178        cnt = (ip->ip_hl << 2) - sizeof (struct ip);
179        opts = f->fw_ipopt;
180        nopts = nopts_sve = f->fw_ipnopt;
181
182        for (; cnt > 0; cnt -= optlen, cp += optlen) {
183                opt = cp[IPOPT_OPTVAL];
184                if (opt == IPOPT_EOL)
185                        break;
186                if (opt == IPOPT_NOP)
187                        optlen = 1;
188                else {
189                        optlen = cp[IPOPT_OLEN];
190                        if (optlen <= 0 || optlen > cnt) {
191                                return 0; /*XXX*/
192                        }
193                }
194                switch (opt) {
195
196                default:
197                        break;
198
199                case IPOPT_LSRR:
200                        opts &= ~IP_FW_IPOPT_LSRR;
201                        nopts &= ~IP_FW_IPOPT_LSRR;
202                        break;
203
204                case IPOPT_SSRR:
205                        opts &= ~IP_FW_IPOPT_SSRR;
206                        nopts &= ~IP_FW_IPOPT_SSRR;
207                        break;
208
209                case IPOPT_RR:
210                        opts &= ~IP_FW_IPOPT_RR;
211                        nopts &= ~IP_FW_IPOPT_RR;
212                        break;
213                case IPOPT_TS:
214                        opts &= ~IP_FW_IPOPT_TS;
215                        nopts &= ~IP_FW_IPOPT_TS;
216                        break;
217                }
218                if (opts == nopts)
219                        break;
220        }
221        if (opts == 0 && nopts == nopts_sve)
222                return 1;
223        else
224                return 0;
225}
226
227static inline int
228iface_match(struct ifnet *ifp, union ip_fw_if *ifu, int byname)
229{
230        /* Check by name or by IP address */
231        if (byname) {
232                /* Check unit number (-1 is wildcard) */
233                if (ifu->fu_via_if.unit != -1
234                    && ifp->if_unit != ifu->fu_via_if.unit)
235                        return(0);
236                /* Check name */
237                if (strncmp(ifp->if_name, ifu->fu_via_if.name, FW_IFNLEN))
238                        return(0);
239                return(1);
240        } else if (ifu->fu_via_ip.s_addr != 0) {        /* Zero == wildcard */
241                struct ifaddr *ia;
242
243                for (ia = ifp->if_addrlist; ia; ia = ia->ifa_next) {
244                        if (ia->ifa_addr == NULL)
245                                continue;
246                        if (ia->ifa_addr->sa_family != AF_INET)
247                                continue;
248                        if (ifu->fu_via_ip.s_addr != ((struct sockaddr_in *)
249                            (ia->ifa_addr))->sin_addr.s_addr)
250                                continue;
251                        return(1);
252                }
253                return(0);
254        }
255        return(1);
256}
257
258static void
259ipfw_report(struct ip_fw *f, struct ip *ip,
260        struct ifnet *rif, struct ifnet *oif)
261{
262        static int counter;
263        struct tcphdr *const tcp = (struct tcphdr *) ((u_long *) ip+ ip->ip_hl);
264        struct udphdr *const udp = (struct udphdr *) ((u_long *) ip+ ip->ip_hl);
265        struct icmp *const icmp = (struct icmp *) ((u_long *) ip + ip->ip_hl);
266        int count;
267
268        count = f ? f->fw_pcnt : ++counter;
269        if (fw_verbose_limit != 0 && count > fw_verbose_limit)
270                return;
271
272        /* Print command name */
273        printf("ipfw: %d ", f ? f->fw_number : -1);
274        if (!f)
275                printf("Refuse");
276        else
277                switch (f->fw_flg & IP_FW_F_COMMAND) {
278                case IP_FW_F_DENY:
279                        printf("Deny");
280                        break;
281                case IP_FW_F_REJECT:
282                        if (f->fw_reject_code == IP_FW_REJECT_RST)
283                                printf("Reset");
284                        else
285                                printf("Unreach");
286                        break;
287                case IP_FW_F_ACCEPT:
288                        printf("Accept");
289                        break;
290                case IP_FW_F_COUNT:
291                        printf("Count");
292                        break;
293                case IP_FW_F_DIVERT:
294                        printf("Divert %d", f->fw_divert_port);
295                        break;
296                case IP_FW_F_TEE:
297                        printf("Tee %d", f->fw_divert_port);
298                        break;
299                case IP_FW_F_SKIPTO:
300                        printf("SkipTo %d", f->fw_skipto_rule);
301                        break;
302                default:       
303                        printf("UNKNOWN");
304                        break;
305                }
306        printf(" ");
307
308        switch (ip->ip_p) {
309        case IPPROTO_TCP:
310                printf("TCP ");
311                print_ip(ip->ip_src);
312                if ((ip->ip_off & IP_OFFMASK) == 0)
313                        printf(":%d ", ntohs(tcp->th_sport));
314                else
315                        printf(" ");
316                print_ip(ip->ip_dst);
317                if ((ip->ip_off & IP_OFFMASK) == 0)
318                        printf(":%d", ntohs(tcp->th_dport));
319                break;
320        case IPPROTO_UDP:
321                printf("UDP ");
322                print_ip(ip->ip_src);
323                if ((ip->ip_off & IP_OFFMASK) == 0)
324                        printf(":%d ", ntohs(udp->uh_sport));
325                else
326                        printf(" ");
327                print_ip(ip->ip_dst);
328                if ((ip->ip_off & IP_OFFMASK) == 0)
329                        printf(":%d", ntohs(udp->uh_dport));
330                break;
331        case IPPROTO_ICMP:
332                printf("ICMP:%u.%u ", icmp->icmp_type, icmp->icmp_code);
333                print_ip(ip->ip_src);
334                printf(" ");
335                print_ip(ip->ip_dst);
336                break;
337        default:
338                printf("P:%d ", ip->ip_p);
339                print_ip(ip->ip_src);
340                printf(" ");
341                print_ip(ip->ip_dst);
342                break;
343        }
344        if (oif)
345                printf(" out via %s%d", oif->if_name, oif->if_unit);
346        else if (rif)
347                printf(" in via %s%d", rif->if_name, rif->if_unit);
348        if ((ip->ip_off & IP_OFFMASK)) 
349                printf(" Fragment = %d",ip->ip_off & IP_OFFMASK);
350        printf("\n");
351        if (fw_verbose_limit != 0 && count == fw_verbose_limit)
352                printf("ipfw: limit reached on rule #%d\n",
353                f ? f->fw_number : -1);
354}
355
356/*
357 * Parameters:
358 *
359 *      ip      Pointer to packet header (struct ip *)
360 *      hlen    Packet header length
361 *      oif     Outgoing interface, or NULL if packet is incoming
362 *      ignport Ignore all divert/tee rules to this port (if non-zero)
363 *      *m      The packet; we set to NULL when/if we nuke it.
364 *
365 * Return value:
366 *
367 *      0       The packet is to be accepted and routed normally OR
368 *              the packet was denied/rejected and has been dropped;
369 *              in the latter case, *m is equal to NULL upon return.
370 *      port    Divert the packet to port.
371 */
372
373static int 
374ip_fw_chk(struct ip **pip, int hlen,
375        struct ifnet *oif, int ignport, struct mbuf **m)
376{
377        struct ip_fw_chain *chain;
378        struct ip_fw *rule = NULL;
379        struct ip *ip = *pip;
380        struct ifnet *const rif = (*m)->m_pkthdr.rcvif;
381        u_short offset = (ip->ip_off & IP_OFFMASK);
382        u_short src_port, dst_port;
383
384        /*
385         * Go down the chain, looking for enlightment
386         */
387        for (chain=ip_fw_chain.lh_first; chain; chain = chain->chain.le_next) {
388                register struct ip_fw *const f = chain->rule;
389
390                /* Check direction inbound */
391                if (!oif && !(f->fw_flg & IP_FW_F_IN))
392                        continue;
393
394                /* Check direction outbound */
395                if (oif && !(f->fw_flg & IP_FW_F_OUT))
396                        continue;
397
398                /* Fragments */
399                if ((f->fw_flg & IP_FW_F_FRAG) && !(ip->ip_off & IP_OFFMASK))
400                        continue;
401
402                /* If src-addr doesn't match, not this rule. */
403                if (((f->fw_flg & IP_FW_F_INVSRC) != 0) ^ ((ip->ip_src.s_addr
404                    & f->fw_smsk.s_addr) != f->fw_src.s_addr))
405                        continue;
406
407                /* If dest-addr doesn't match, not this rule. */
408                if (((f->fw_flg & IP_FW_F_INVDST) != 0) ^ ((ip->ip_dst.s_addr
409                    & f->fw_dmsk.s_addr) != f->fw_dst.s_addr))
410                        continue;
411
412                /* Interface check */
413                if ((f->fw_flg & IF_FW_F_VIAHACK) == IF_FW_F_VIAHACK) {
414                        struct ifnet *const iface = oif ? oif : rif;
415
416                        /* Backwards compatibility hack for "via" */
417                        if (!iface || !iface_match(iface,
418                            &f->fw_in_if, f->fw_flg & IP_FW_F_OIFNAME))
419                                continue;
420                } else {
421                        /* Check receive interface */
422                        if ((f->fw_flg & IP_FW_F_IIFACE)
423                            && (!rif || !iface_match(rif,
424                              &f->fw_in_if, f->fw_flg & IP_FW_F_IIFNAME)))
425                                continue;
426                        /* Check outgoing interface */
427                        if ((f->fw_flg & IP_FW_F_OIFACE)
428                            && (!oif || !iface_match(oif,
429                              &f->fw_out_if, f->fw_flg & IP_FW_F_OIFNAME)))
430                                continue;
431                }
432
433                /* Check IP options */
434                if (f->fw_ipopt != f->fw_ipnopt && !ipopts_match(ip, f))
435                        continue;
436
437                /* Check protocol; if wildcard, match */
438                if (f->fw_prot == IPPROTO_IP)
439                        goto got_match;
440
441                /* If different, don't match */
442                if (ip->ip_p != f->fw_prot) 
443                        continue;
444
445#define PULLUP_TO(len)  do {                                            \
446                            if ((*m)->m_len < (len)                     \
447                                && (*m = m_pullup(*m, (len))) == 0) {   \
448                                    goto bogusfrag;                     \
449                            }                                           \
450                            *pip = ip = mtod(*m, struct ip *);          \
451                            offset = (ip->ip_off & IP_OFFMASK);         \
452                        } while (0)
453
454                /* Protocol specific checks */
455                switch (ip->ip_p) {
456                case IPPROTO_TCP:
457                    {
458                        struct tcphdr *tcp;
459
460                        if (offset == 1)        /* cf. RFC 1858 */
461                                goto bogusfrag;
462                        if (offset != 0) {
463                                /*
464                                 * TCP flags and ports aren't available in this
465                                 * packet -- if this rule specified either one,
466                                 * we consider the rule a non-match.
467                                 */
468                                if (f->fw_nports != 0 ||
469                                    f->fw_tcpf != f->fw_tcpnf)
470                                        continue;
471
472                                break;
473                        }
474                        PULLUP_TO(hlen + 14);
475                        tcp = (struct tcphdr *) ((u_long *)ip + ip->ip_hl);
476                        if (f->fw_tcpf != f->fw_tcpnf && !tcpflg_match(tcp, f))
477                                continue;
478                        src_port = ntohs(tcp->th_sport);
479                        dst_port = ntohs(tcp->th_dport);
480                        goto check_ports;
481                    }
482
483                case IPPROTO_UDP:
484                    {
485                        struct udphdr *udp;
486
487                        if (offset != 0) {
488                                /*
489                                 * Port specification is unavailable -- if this
490                                 * rule specifies a port, we consider the rule
491                                 * a non-match.
492                                 */
493                                if (f->fw_nports != 0)
494                                        continue;
495
496                                break;
497                        }
498                        PULLUP_TO(hlen + 4);
499                        udp = (struct udphdr *) ((u_long *)ip + ip->ip_hl);
500                        src_port = ntohs(udp->uh_sport);
501                        dst_port = ntohs(udp->uh_dport);
502check_ports:
503                        if (!port_match(&f->fw_pts[0],
504                            IP_FW_GETNSRCP(f), src_port,
505                            f->fw_flg & IP_FW_F_SRNG))
506                                continue;
507                        if (!port_match(&f->fw_pts[IP_FW_GETNSRCP(f)],
508                            IP_FW_GETNDSTP(f), dst_port,
509                            f->fw_flg & IP_FW_F_DRNG)) 
510                                continue;
511                        break;
512                    }
513
514                case IPPROTO_ICMP:
515                    {
516                        struct icmp *icmp;
517
518                        if (offset != 0)        /* Type isn't valid */
519                                break;
520                        PULLUP_TO(hlen + 2);
521                        icmp = (struct icmp *) ((u_long *)ip + ip->ip_hl);
522                        if (!icmptype_match(icmp, f))
523                                continue;
524                        break;
525                    }
526#undef PULLUP_TO
527
528bogusfrag:
529                        if (fw_verbose)
530                                ipfw_report(NULL, ip, rif, oif);
531                        goto dropit;
532                }
533
534got_match:
535                /* Ignore divert/tee rule if socket port is "ignport" */
536                switch (f->fw_flg & IP_FW_F_COMMAND) {
537                case IP_FW_F_DIVERT:
538                case IP_FW_F_TEE:
539                        if (f->fw_divert_port == ignport)
540                                continue;       /* ignore this rule */
541                        break;
542                }
543
544                /* Update statistics */
545                f->fw_pcnt += 1;
546                f->fw_bcnt += ip->ip_len;
547                f->timestamp = rtems_bsdnet_seconds_since_boot();
548
549                /* Log to console if desired */
550                if ((f->fw_flg & IP_FW_F_PRN) && fw_verbose)
551                        ipfw_report(f, ip, rif, oif);
552
553                /* Take appropriate action */
554                switch (f->fw_flg & IP_FW_F_COMMAND) {
555                case IP_FW_F_ACCEPT:
556                        return(0);
557                case IP_FW_F_COUNT:
558                        continue;
559                case IP_FW_F_DIVERT:
560                        return(f->fw_divert_port);
561                case IP_FW_F_TEE:
562                        /*
563                         * XXX someday tee packet here, but beware that you
564                         * can't use m_copym() or m_copypacket() because
565                         * the divert input routine modifies the mbuf
566                         * (and these routines only increment reference
567                         * counts in the case of mbuf clusters), so need
568                         * to write custom routine.
569                         */
570                        continue;
571                case IP_FW_F_SKIPTO:
572#ifdef DIAGNOSTIC
573                        while (chain->chain.le_next
574                            && chain->chain.le_next->rule->fw_number
575                                < f->fw_skipto_rule)
576#else
577                        while (chain->chain.le_next->rule->fw_number
578                            < f->fw_skipto_rule)
579#endif
580                                chain = chain->chain.le_next;
581                        continue;
582                }
583
584                /* Deny/reject this packet using this rule */
585                rule = f;
586                break;
587        }
588
589#ifdef DIAGNOSTIC
590        /* Rule 65535 should always be there and should always match */
591        if (!chain)
592                panic("ip_fw: chain");
593#endif
594
595        /*
596         * At this point, we're going to drop the packet.
597         * Send a reject notice if all of the following are true:
598         *
599         * - The packet matched a reject rule
600         * - The packet is not an ICMP packet
601         * - The packet is not a multicast or broadcast packet
602         */
603        if ((rule->fw_flg & IP_FW_F_COMMAND) == IP_FW_F_REJECT
604            && ip->ip_p != IPPROTO_ICMP
605            && !((*m)->m_flags & (M_BCAST|M_MCAST))
606            && !IN_MULTICAST(ntohl(ip->ip_dst.s_addr))) {
607                switch (rule->fw_reject_code) {
608                case IP_FW_REJECT_RST:
609                  {
610                        struct tcphdr *const tcp =
611                                (struct tcphdr *) ((u_long *)ip + ip->ip_hl);
612                        struct tcpiphdr ti, *const tip = (struct tcpiphdr *) ip;
613
614                        if (offset != 0 || (tcp->th_flags & TH_RST))
615                                break;
616                        ti.ti_i = *((struct ipovly *) ip);
617                        ti.ti_t = *tcp;
618                        bcopy(&ti, ip, sizeof(ti));
619                        NTOHL(tip->ti_seq);
620                        NTOHL(tip->ti_ack);
621                        tip->ti_len = ip->ip_len - hlen - (tip->ti_off << 2);
622                        if (tcp->th_flags & TH_ACK) {
623                                tcp_respond(NULL, tip, *m,
624                                    (tcp_seq)0, ntohl(tcp->th_ack), TH_RST);
625                        } else {
626                                if (tcp->th_flags & TH_SYN)
627                                        tip->ti_len++;
628                                tcp_respond(NULL, tip, *m, tip->ti_seq
629                                    + tip->ti_len, (tcp_seq)0, TH_RST|TH_ACK);
630                        }
631                        *m = NULL;
632                        break;
633                  }
634                default:        /* Send an ICMP unreachable using code */
635                        icmp_error(*m, ICMP_UNREACH,
636                            rule->fw_reject_code, 0L, 0);
637                        *m = NULL;
638                        break;
639                }
640        }
641
642dropit:
643        /*
644         * Finally, drop the packet.
645         */
646        if (*m) {
647                m_freem(*m);
648                *m = NULL;
649        }
650        return(0);
651}
652
653static int
654add_entry(struct ip_fw_head *chainptr, struct ip_fw *frwl)
655{
656        struct ip_fw *ftmp = 0;
657        struct ip_fw_chain *fwc = 0, *fcp, *fcpl = 0;
658        u_short nbr = 0;
659        int s;
660
661        fwc = malloc(sizeof *fwc, M_IPFW, M_DONTWAIT);
662        ftmp = malloc(sizeof *ftmp, M_IPFW, M_DONTWAIT);
663        if (!fwc || !ftmp) {
664                dprintf(("%s malloc said no\n", err_prefix));
665                if (fwc)  free(fwc, M_IPFW);
666                if (ftmp) free(ftmp, M_IPFW);
667                return (ENOSPC);
668        }
669
670        bcopy(frwl, ftmp, sizeof(struct ip_fw));
671        ftmp->fw_in_if.fu_via_if.name[FW_IFNLEN - 1] = '\0';
672        ftmp->fw_pcnt = 0L;
673        ftmp->fw_bcnt = 0L;
674        fwc->rule = ftmp;
675       
676        s = splnet();
677
678        if (!chainptr->lh_first) {
679                LIST_INSERT_HEAD(chainptr, fwc, chain);
680                splx(s);
681                return(0);
682        } else if (ftmp->fw_number == (u_short)-1) {
683                if (fwc)  free(fwc, M_IPFW);
684                if (ftmp) free(ftmp, M_IPFW);
685                splx(s);
686                dprintf(("%s bad rule number\n", err_prefix));
687                return (EINVAL);
688        }
689
690        /* If entry number is 0, find highest numbered rule and add 100 */
691        if (ftmp->fw_number == 0) {
692                for (fcp = chainptr->lh_first; fcp; fcp = fcp->chain.le_next) {
693                        if (fcp->rule->fw_number != (u_short)-1)
694                                nbr = fcp->rule->fw_number;
695                        else
696                                break;
697                }
698                if (nbr < (u_short)-1 - 100)
699                        nbr += 100;
700                ftmp->fw_number = nbr;
701        }
702
703        /* Got a valid number; now insert it, keeping the list ordered */
704        for (fcp = chainptr->lh_first; fcp; fcp = fcp->chain.le_next) {
705                if (fcp->rule->fw_number > ftmp->fw_number) {
706                        if (fcpl) {
707                                LIST_INSERT_AFTER(fcpl, fwc, chain);
708                        } else {
709                                LIST_INSERT_HEAD(chainptr, fwc, chain);
710                        }
711                        break;
712                } else {
713                        fcpl = fcp;
714                }
715        }
716
717        splx(s);
718        return (0);
719}
720
721static int
722del_entry(struct ip_fw_head *chainptr, u_short number)
723{
724        struct ip_fw_chain *fcp;
725        int s;
726
727        s = splnet();
728
729        fcp = chainptr->lh_first; 
730        if (number != (u_short)-1) {
731                for (; fcp; fcp = fcp->chain.le_next) {
732                        if (fcp->rule->fw_number == number) {
733                                LIST_REMOVE(fcp, chain);
734                                splx(s);
735                                free(fcp->rule, M_IPFW);
736                                free(fcp, M_IPFW);
737                                return 0;
738                        }
739                }
740        }
741
742        splx(s);
743        return (EINVAL);
744}
745
746static int
747zero_entry(struct mbuf *m)
748{
749        struct ip_fw *frwl;
750        struct ip_fw_chain *fcp;
751        int s;
752
753        if (m) {
754                if (m->m_len != sizeof(struct ip_fw))
755                        return(EINVAL);
756                frwl = mtod(m, struct ip_fw *);
757        }
758        else
759                frwl = NULL;
760
761        /*
762         *      It's possible to insert multiple chain entries with the
763         *      same number, so we don't stop after finding the first
764         *      match if zeroing a specific entry.
765         */
766        s = splnet();
767        for (fcp = ip_fw_chain.lh_first; fcp; fcp = fcp->chain.le_next)
768                if (!frwl || frwl->fw_number == fcp->rule->fw_number) {
769                        fcp->rule->fw_bcnt = fcp->rule->fw_pcnt = 0;
770                        fcp->rule->timestamp = 0;
771                }
772        splx(s);
773
774        if (fw_verbose) {
775                if (frwl)
776                        printf("ipfw: Entry %d cleared.\n", frwl->fw_number);
777                else
778                        printf("ipfw: Accounting cleared.\n");
779        }
780
781        return(0);
782}
783
784static struct ip_fw *
785check_ipfw_mbuf(struct mbuf *m)
786{
787        /* Check length */
788        if (m->m_len != sizeof(struct ip_fw)) {
789                dprintf(("%s len=%d, want %d\n", err_prefix, m->m_len,
790                    (int)sizeof(struct ip_fw)));
791                return (NULL);
792        }
793        return(check_ipfw_struct(mtod(m, struct ip_fw *)));
794}
795
796static struct ip_fw *
797check_ipfw_struct(struct ip_fw *frwl)
798{
799        /* Check for invalid flag bits */
800        if ((frwl->fw_flg & ~IP_FW_F_MASK) != 0) {
801                dprintf(("%s undefined flag bits set (flags=%x)\n",
802                    err_prefix, frwl->fw_flg));
803                return (NULL);
804        }
805        /* Must apply to incoming or outgoing (or both) */
806        if (!(frwl->fw_flg & (IP_FW_F_IN | IP_FW_F_OUT))) {
807                dprintf(("%s neither in nor out\n", err_prefix));
808                return (NULL);
809        }
810        /* Empty interface name is no good */
811        if (((frwl->fw_flg & IP_FW_F_IIFNAME)
812              && !*frwl->fw_in_if.fu_via_if.name)
813            || ((frwl->fw_flg & IP_FW_F_OIFNAME)
814              && !*frwl->fw_out_if.fu_via_if.name)) {
815                dprintf(("%s empty interface name\n", err_prefix));
816                return (NULL);
817        }
818        /* Sanity check interface matching */
819        if ((frwl->fw_flg & IF_FW_F_VIAHACK) == IF_FW_F_VIAHACK) {
820                ;               /* allow "via" backwards compatibility */
821        } else if ((frwl->fw_flg & IP_FW_F_IN)
822            && (frwl->fw_flg & IP_FW_F_OIFACE)) {
823                dprintf(("%s outgoing interface check on incoming\n",
824                    err_prefix));
825                return (NULL);
826        }
827        /* Sanity check port ranges */
828        if ((frwl->fw_flg & IP_FW_F_SRNG) && IP_FW_GETNSRCP(frwl) < 2) {
829                dprintf(("%s src range set but n_src_p=%d\n",
830                    err_prefix, IP_FW_GETNSRCP(frwl)));
831                return (NULL);
832        }
833        if ((frwl->fw_flg & IP_FW_F_DRNG) && IP_FW_GETNDSTP(frwl) < 2) {
834                dprintf(("%s dst range set but n_dst_p=%d\n",
835                    err_prefix, IP_FW_GETNDSTP(frwl)));
836                return (NULL);
837        }
838        if (IP_FW_GETNSRCP(frwl) + IP_FW_GETNDSTP(frwl) > IP_FW_MAX_PORTS) {
839                dprintf(("%s too many ports (%d+%d)\n",
840                    err_prefix, IP_FW_GETNSRCP(frwl), IP_FW_GETNDSTP(frwl)));
841                return (NULL);
842        }
843        /*
844         *      Protocols other than TCP/UDP don't use port range
845         */
846        if ((frwl->fw_prot != IPPROTO_TCP) &&
847            (frwl->fw_prot != IPPROTO_UDP) &&
848            (IP_FW_GETNSRCP(frwl) || IP_FW_GETNDSTP(frwl))) {
849                dprintf(("%s port(s) specified for non TCP/UDP rule\n",
850                    err_prefix));
851                return(NULL);
852        }
853
854        /*
855         *      Rather than modify the entry to make such entries work,
856         *      we reject this rule and require user level utilities
857         *      to enforce whatever policy they deem appropriate.
858         */
859        if ((frwl->fw_src.s_addr & (~frwl->fw_smsk.s_addr)) || 
860                (frwl->fw_dst.s_addr & (~frwl->fw_dmsk.s_addr))) {
861                dprintf(("%s rule never matches\n", err_prefix));
862                return(NULL);
863        }
864
865        if ((frwl->fw_flg & IP_FW_F_FRAG) &&
866                (frwl->fw_prot == IPPROTO_UDP || frwl->fw_prot == IPPROTO_TCP)) {
867                if (frwl->fw_nports) {
868                        dprintf(("%s cannot mix 'frag' and ports\n", err_prefix));
869                        return(NULL);
870                }
871                if (frwl->fw_prot == IPPROTO_TCP &&
872                        frwl->fw_tcpf != frwl->fw_tcpnf) {
873                        dprintf(("%s cannot mix 'frag' with TCP flags\n", err_prefix));
874                        return(NULL);
875                }
876        }
877
878        /* Check command specific stuff */
879        switch (frwl->fw_flg & IP_FW_F_COMMAND)
880        {
881        case IP_FW_F_REJECT:
882                if (frwl->fw_reject_code >= 0x100
883                    && !(frwl->fw_prot == IPPROTO_TCP
884                      && frwl->fw_reject_code == IP_FW_REJECT_RST)) {
885                        dprintf(("%s unknown reject code\n", err_prefix));
886                        return(NULL);
887                }
888                break;
889        case IP_FW_F_DIVERT:            /* Diverting to port zero is invalid */
890        case IP_FW_F_TEE:
891                if (frwl->fw_divert_port == 0) {
892                        dprintf(("%s can't divert to port 0\n", err_prefix));
893                        return (NULL);
894                }
895                break;
896        case IP_FW_F_DENY:
897        case IP_FW_F_ACCEPT:
898        case IP_FW_F_COUNT:
899        case IP_FW_F_SKIPTO:
900                break;
901        default:
902                dprintf(("%s invalid command\n", err_prefix));
903                return(NULL);
904        }
905
906        return frwl;
907}
908
909static int
910ip_fw_ctl(int stage, struct mbuf **mm)
911{
912        int error;
913        struct mbuf *m;
914
915        if (stage == IP_FW_GET) {
916                struct ip_fw_chain *fcp = ip_fw_chain.lh_first;
917                *mm = m = m_get(M_WAIT, MT_SOOPTS);
918                for (; fcp; fcp = fcp->chain.le_next) {
919                        memcpy(m->m_data, fcp->rule, sizeof *(fcp->rule));
920                        m->m_len = sizeof *(fcp->rule);
921                        m->m_next = m_get(M_WAIT, MT_SOOPTS);
922                        m = m->m_next;
923                        m->m_len = 0;
924                }
925                return (0);
926        }
927        m = *mm;
928        /* only allow get calls if secure mode > 2 */
929        if (securelevel > 2) {
930                if (m) (void)m_free(m);
931                return(EPERM);
932        }
933        if (stage == IP_FW_FLUSH) {
934                while (ip_fw_chain.lh_first != NULL && 
935                    ip_fw_chain.lh_first->rule->fw_number != (u_short)-1) {
936                        struct ip_fw_chain *fcp = ip_fw_chain.lh_first;
937                        int s = splnet();
938                        LIST_REMOVE(ip_fw_chain.lh_first, chain);
939                        splx(s);
940                        free(fcp->rule, M_IPFW);
941                        free(fcp, M_IPFW);
942                }
943                if (m) (void)m_free(m);
944                return (0);
945        }
946        if (stage == IP_FW_ZERO) {
947                error = zero_entry(m);
948                if (m) (void)m_free(m);
949                return (error);
950        }
951        if (m == NULL) {
952                printf("%s NULL mbuf ptr\n", err_prefix);
953                return (EINVAL);
954        }
955
956        if (stage == IP_FW_ADD) {
957                struct ip_fw *frwl = check_ipfw_mbuf(m);
958
959                if (!frwl)
960                        error = EINVAL;
961                else
962                        error = add_entry(&ip_fw_chain, frwl);
963                if (m) (void)m_free(m);
964                return error;
965        }
966        if (stage == IP_FW_DEL) {
967                if (m->m_len != sizeof(struct ip_fw)) {
968                        dprintf(("%s len=%d, want %d\n", err_prefix, m->m_len,
969                            (int)sizeof(struct ip_fw)));
970                        error = EINVAL;
971                } else if (mtod(m, struct ip_fw *)->fw_number == (u_short)-1) {
972                        dprintf(("%s can't delete rule 65535\n", err_prefix));
973                        error = EINVAL;
974                } else
975                        error = del_entry(&ip_fw_chain,
976                            mtod(m, struct ip_fw *)->fw_number);
977                if (m) (void)m_free(m);
978                return error;
979        }
980
981        dprintf(("%s unknown request %d\n", err_prefix, stage));
982        if (m) (void)m_free(m);
983        return (EINVAL);
984}
985
986void
987ip_fw_init(void)
988{
989        struct ip_fw default_rule;
990
991        ip_fw_chk_ptr = ip_fw_chk;
992        ip_fw_ctl_ptr = ip_fw_ctl;
993        LIST_INIT(&ip_fw_chain);
994
995        bzero(&default_rule, sizeof default_rule);
996        default_rule.fw_prot = IPPROTO_IP;
997        default_rule.fw_number = (u_short)-1;
998#ifdef IPFIREWALL_DEFAULT_TO_ACCEPT
999        default_rule.fw_flg |= IP_FW_F_ACCEPT;
1000#else
1001        default_rule.fw_flg |= IP_FW_F_DENY;
1002#endif
1003        default_rule.fw_flg |= IP_FW_F_IN | IP_FW_F_OUT;
1004        if (check_ipfw_struct(&default_rule) == NULL ||
1005                add_entry(&ip_fw_chain, &default_rule))
1006                panic(__FUNCTION__);
1007
1008        printf("IP packet filtering initialized, "
1009#ifdef IPDIVERT
1010                "divert enabled, ");
1011#else
1012                "divert disabled, ");
1013#endif
1014#ifdef IPFIREWALL_DEFAULT_TO_ACCEPT
1015        printf("default to accept, ");
1016#endif
1017#ifndef IPFIREWALL_VERBOSE
1018        printf("logging disabled\n");
1019#else
1020        if (fw_verbose_limit == 0)
1021                printf("unlimited logging\n");
1022        else
1023                printf("logging limited to %d packets/entry\n",
1024                    fw_verbose_limit);
1025#endif
1026}
1027
1028#ifdef IPFIREWALL_MODULE
1029
1030#include <sys/exec.h>
1031#include <sys/sysent.h>
1032#include <sys/lkm.h>
1033
1034MOD_MISC(ipfw);
1035
1036static int
1037ipfw_load(struct lkm_table *lkmtp, int cmd)
1038{
1039        int s=splnet();
1040
1041        old_chk_ptr = ip_fw_chk_ptr;
1042        old_ctl_ptr = ip_fw_ctl_ptr;
1043
1044        ip_fw_init();
1045        splx(s);
1046        return 0;
1047}
1048
1049static int
1050ipfw_unload(struct lkm_table *lkmtp, int cmd)
1051{
1052        int s=splnet();
1053
1054        ip_fw_chk_ptr =  old_chk_ptr;
1055        ip_fw_ctl_ptr =  old_ctl_ptr;
1056
1057        while (ip_fw_chain.lh_first != NULL) {
1058                struct ip_fw_chain *fcp = ip_fw_chain.lh_first;
1059                LIST_REMOVE(ip_fw_chain.lh_first, chain);
1060                free(fcp->rule, M_IPFW);
1061                free(fcp, M_IPFW);
1062        }
1063       
1064        splx(s);
1065        printf("IP firewall unloaded\n");
1066        return 0;
1067}
1068
1069int
1070ipfw_mod(struct lkm_table *lkmtp, int cmd, int ver)
1071{
1072        DISPATCH(lkmtp, cmd, ver, ipfw_load, ipfw_unload, lkm_nullcmd);
1073}
1074#endif
Note: See TracBrowser for help on using the repository browser.