source: rtems/cpukit/libnetworking/netinet/ip_fw.c @ 65c6425

4.115
Last change on this file since 65c6425 was 65c6425, checked in by Joel Sherrill <joel.sherrill@…>, on 05/03/12 at 17:24:46

Remove CVS Id Strings (manual edits after script)

These modifications were required by hand after running the script.
In some cases, the file names did not match patterns. In others,
the format of the file did not match any common patterns.

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