source: rtems/cpukit/libnetworking/netinet/ip_fw.c @ 6e401331

4.104.115
Last change on this file since 6e401331 was b25b88e7, checked in by Ralf Corsepius <ralf.corsepius@…>, on 03/28/10 at 05:50:29

Add HAVE_CONFIG_H support to let files receive configure defines.

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