source: rtems/cpukit/libnetworking/netinet/ip_fw.c @ 26ccd139

5
Last change on this file since 26ccd139 was 26ccd139, checked in by Sebastian Huber <sebastian.huber@…>, on 06/07/17 at 05:24:00

network: Header file compatiblity

Move legacy network stack implementation specifics to
<rtems/rtems_bsdnet_internal.h>. Include missing header files. Add
interface flags compatibility.

Update #2833.

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