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

4.104.114.84.95
Last change on this file since f6161c72 was 36799d4, checked in by Joel Sherrill <joel.sherrill@…>, on 01/03/03 at 18:09:57

2002-11-26 Chris Johns <cjohns@…>

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