source: rtems/c/src/libnetworking/netinet/ip_fw.c @ 39e6e65a

4.104.114.84.95
Last change on this file since 39e6e65a was 39e6e65a, checked in by Joel Sherrill <joel.sherrill@…>, on 08/19/98 at 21:32:28

Base files

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