source: rtems-libbsd/testsuite/pf01/test_main.c @ df222eb

5-freebsd-12freebsd-9.3
Last change on this file since df222eb was df222eb, checked in by Christian Mauderer <Christian.Mauderer@…>, on Jul 22, 2016 at 12:50:09 PM

testsuite/pf01: Test pfctl and pf.

  • Property mode set to 100644
File size: 12.0 KB
Line 
1/*
2 * Copyright (c) 2016 embedded brains GmbH.  All rights reserved.
3 *
4 *  embedded brains GmbH
5 *  Dornierstr. 4
6 *  82178 Puchheim
7 *  Germany
8 *  <rtems@embedded-brains.de>
9 *
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
12 * are met:
13 * 1. Redistributions of source code must retain the above copyright
14 *    notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 *    notice, this list of conditions and the following disclaimer in the
17 *    documentation and/or other materials provided with the distribution.
18 *
19 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
20 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
23 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
25 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
28 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29 * SUCH DAMAGE.
30 */
31
32#include <assert.h>
33#include <fcntl.h>
34#include <stdio.h>
35#include <stdlib.h>
36#include <sys/stat.h>
37#include <sys/socket.h>
38#include <netinet/in.h>
39#include <netinet/tcp.h>
40#include <netdb.h>
41#include <unistd.h>
42#include <errno.h>
43
44#include <machine/rtems-bsd-commands.h>
45
46#include <rtems/libcsupport.h>
47#include <rtems/shell.h>
48#include <rtems/telnetd.h>
49#include <rtems/ftpd.h>
50
51#define TEST_NAME "LIBBSD PF 1"
52
53#define ARGC(x) RTEMS_BSD_ARGC(x)
54
55#define LO1_IP "172.21.1.1"
56#define LO2_IP "172.22.2.2"
57#define TELNET_PORT 23
58#define FTP_PORT 21
59
60#define SERVER_TASK_PRIO 110
61
62/*
63 * WARNING: The following rules are not made to be an good example for using PF.
64 * Check the online manuals for PF to find out how to write good rules.
65 */
66
67/* Config with invalid syntax */
68#define TEST_CFG_INVALID "/etc/pf_invalid.conf"
69#define TEST_CFG_INVALID_CONTENT \
70        "some\n" \
71        "garbage\n"
72
73/* Allow only some services on output. */
74#define TEST_CFG_BLOCK_MOST "/etc/pf_block_most.conf"
75#define TEST_CFG_BLOCK_MOST_CONTENT \
76        "tcp_services = \"{ ssh, telnet, www }\"\n" \
77        "udp_services = \"{ telnet }\"\n" \
78        "block all\n" \
79        "pass out proto tcp to any port $tcp_services keep state\n" \
80        "pass proto udp to any port $udp_services keep state\n"
81
82/* Allow all on one and only some on another interface. */
83#define TEST_CFG_IF_DEPEND "/etc/pf_if_depend.conf"
84#define TEST_CFG_IF_DEPEND_CONTENT \
85        "ext_if = \"cgem0\"\n" \
86        "int_if = \"lo0\"\n" \
87        "block in all\n" \
88        "pass in on $int_if all\n" \
89        "pass in on $ext_if inet proto { tcp, udp } from any port { telnet }\n"
90
91/* Block all input except on lo1. */
92#define TEST_CFG_ALLOW_LO1 "/etc/pf_allow_lo1.conf"
93#define TEST_CFG_ALLOW_LO1_CONTENT \
94        "block all\n" \
95        "pass in on lo1 all keep state\n" \
96        "pass out all\n"
97
98/* Block all input except telnet on lo1. */
99#define TEST_CFG_TELNET_LO1 "/etc/pf_allow_telnet_lo1.conf"
100#define TEST_CFG_TELNET_LO1_CONTENT \
101        "block all\n" \
102        "pass in on lo1 inet proto { tcp } from any to any port { telnet } keep state\n" \
103        "pass out all\n"
104
105/* pf.os */
106#define ETC_PF_OS "/etc/pf.os"
107#define ETC_PF_OS_CONTENT "# empty"
108
109/* protocols */
110#define ETC_PROTOCOLS "/etc/protocols"
111#define ETC_PROTOCOLS_CONTENT \
112        "ip     0       IP              # internet protocol, pseudo protocol number\n" \
113        "tcp    6       TCP             # transmission control protocol\n" \
114        "udp    17      UDP             # user datagram protocol\n"
115
116/* services */
117#define ETC_SERVICES "/etc/services"
118#define ETC_SERVICES_CONTENT \
119        "ftp-data        20/sctp   #File Transfer [Default Data]\n" \
120        "ftp-data        20/tcp    #File Transfer [Default Data]\n" \
121        "ftp-data        20/udp    #File Transfer [Default Data]\n" \
122        "ftp             21/sctp   #File Transfer [Control]\n" \
123        "ftp             21/tcp    #File Transfer [Control]\n" \
124        "ftp             21/udp    #File Transfer [Control]\n" \
125        "ssh             22/tcp    #Secure Shell Login\n" \
126        "telnet          23/tcp\n" \
127        "telnet          23/udp\n" \
128        "http            80/tcp    www www-http #World Wide Web HTTP\n"
129
130static const struct {
131        const char *name;
132        const char *content;
133} init_files[] = {
134        {.name = ETC_PF_OS, .content = ETC_PF_OS_CONTENT},
135        {.name = ETC_PROTOCOLS, .content = ETC_PROTOCOLS_CONTENT},
136        {.name = ETC_SERVICES, .content = ETC_SERVICES_CONTENT},
137        {.name = TEST_CFG_INVALID, .content = TEST_CFG_INVALID_CONTENT},
138        {.name = TEST_CFG_BLOCK_MOST, .content = TEST_CFG_BLOCK_MOST_CONTENT},
139        {.name = TEST_CFG_IF_DEPEND, .content = TEST_CFG_IF_DEPEND_CONTENT},
140        {.name = TEST_CFG_ALLOW_LO1, .content = TEST_CFG_ALLOW_LO1_CONTENT},
141        {.name = TEST_CFG_TELNET_LO1, .content = TEST_CFG_TELNET_LO1_CONTENT},
142};
143
144/* Create all necessary files */
145static void
146prepare_files()
147{
148        size_t i;
149        struct stat sb;
150        int rv;
151        int fd;
152        size_t written;
153
154        /* Create /etc if necessary */
155        rv = mkdir("/etc", S_IRWXU | S_IRWXG | S_IRWXO);
156        /* ignore errors, check the dir after. */
157        assert(stat("/etc", &sb) == 0);
158        assert(S_ISDIR(sb.st_mode));
159
160        /* Create files */
161        for(i = 0; i < (sizeof(init_files)/sizeof(init_files[0])); ++i) {
162                const char *content;
163                size_t len;
164
165                content = init_files[i].content;
166                len = strlen(content);
167
168                fd = open(init_files[i].name, O_WRONLY | O_CREAT,
169                    S_IRWXU | S_IRWXG | S_IRWXO);
170                assert(fd != -1);
171
172                written = write(fd, content, len);
173                assert(written == len);
174
175                rv = close(fd);
176                assert(rv == 0);
177        }
178}
179
180/* Generic server task */
181static rtems_task
182tcp_server_task(rtems_task_argument arg)
183{
184        int sd, client;
185        socklen_t addrlen;
186        struct sockaddr_in addr, client_addr;
187        uint16_t port = arg;
188        int rv;
189        const char answer[] = "I am a dummy\n";
190
191        sd = socket(AF_INET, SOCK_STREAM, 0);
192        assert(sd > 0);
193
194        memset(&addr, 0, sizeof addr);
195        addr.sin_family = AF_INET;
196        addr.sin_addr.s_addr = INADDR_ANY;
197        addr.sin_port = htons(port);
198
199        rv = bind(sd, (struct sockaddr *)&addr, sizeof(addr));
200        assert(rv == 0);
201
202        rv = listen(sd, 10);
203        assert(rv == 0);
204
205        for(;;) {
206                addrlen = sizeof(client_addr);
207                client = accept(sd, (struct sockaddr *)&client_addr, &addrlen);
208                if(client > 0) {
209                        write(client, answer, strlen(answer));
210                        close(client);
211                }
212        }
213}
214
215static void
216spawn_server(uint16_t port)
217{
218        rtems_status_code sc;
219        rtems_id tid;
220
221        sc = rtems_task_create(rtems_build_name('s','r','v',' '),
222            SERVER_TASK_PRIO,
223            RTEMS_MINIMUM_STACK_SIZE,
224            RTEMS_DEFAULT_MODES,
225            RTEMS_DEFAULT_ATTRIBUTES,
226            &tid);
227        assert(sc == RTEMS_SUCCESSFUL);
228        sc = rtems_task_start(tid, tcp_server_task, port);
229        assert(sc == RTEMS_SUCCESSFUL);
230}
231
232/* Start a few services to test */
233static void
234prepare_services()
235{
236        spawn_server(TELNET_PORT);
237        spawn_server(FTP_PORT);
238}
239
240/* Create some additional loopback interfaces for the test. */
241static void
242prepare_interfaces()
243{
244        int exit_code;
245        char *lo1[] = {
246                "ifconfig", "lo1", "create", "inet", LO1_IP,
247                "netmask", "255.255.0.0", NULL
248        };
249        char *lo2[] = {
250                "ifconfig", "lo2", "create", "inet", LO2_IP,
251                "netmask", "255.255.0.0", NULL
252        };
253
254        puts("--- create lo1 and lo2");
255        exit_code = rtems_bsd_command_ifconfig(ARGC(lo1), lo1);
256        assert(exit_code == EXIT_SUCCESS);
257        exit_code = rtems_bsd_command_ifconfig(ARGC(lo2), lo2);
258        assert(exit_code == EXIT_SUCCESS);
259}
260
261static void
262check_tcp_port(char *host, bool expect_open, uint16_t port)
263{
264        int sd;
265        struct sockaddr_in addr;
266        struct hostent *server;
267        int rv;
268        struct timeval tv;
269        fd_set select_set;
270
271        sd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
272        assert(sd > 0);
273
274        /* make socket nonblocking */
275        rv = fcntl(sd, F_GETFL, NULL);
276        assert(rv >= 0);
277        rv |= O_NONBLOCK;
278        rv = fcntl(sd, F_SETFL, rv);
279        assert(rv == 0);
280
281        memset(&addr, 0, sizeof(addr));
282        addr.sin_family = AF_INET;
283        addr.sin_port = htons(port);
284
285        server = gethostbyname(host);
286        assert(server != NULL);
287        memcpy(&addr.sin_addr.s_addr, server->h_addr, server->h_length);
288
289        rv = connect(sd, (struct sockaddr *)&addr, sizeof(addr));
290        if(rv == 0) {
291                /* successfully connected */
292                assert(expect_open == true);
293        } else {
294                if(errno != EINPROGRESS) {
295                        assert(expect_open == false);
296                } else {
297                        /* wait for connection or timeout */
298                        memset(&tv, 0, sizeof(tv));
299                        tv.tv_usec = 100000;
300
301                        FD_ZERO(&select_set);
302                        FD_SET(sd, &select_set);
303                        rv = select(sd+1, NULL, &select_set, NULL, &tv);
304
305                        /* rv < 0 would be an error
306                         * rv == 0 would be an timeout
307                         * rv > 0 would be success */
308                        if(expect_open == false) {
309                                assert(rv == 0);
310                        } else {
311                                assert(rv > 0);
312                        }
313                }
314        }
315
316        rv = close(sd);
317        assert(rv == 0);
318}
319
320static void
321check_telnet(char *host, bool expect_open)
322{
323        check_tcp_port(host, expect_open, TELNET_PORT);
324}
325
326static void
327check_ftp(char *host, bool expect_open)
328{
329        check_tcp_port(host, expect_open, FTP_PORT);
330}
331
332/* Check if services work like expected */
333static void
334check_services(char *host, bool exp_telnet, bool exp_ftp)
335{
336        check_telnet(host, exp_telnet);
337        check_ftp(host, exp_ftp);
338}
339
340static void
341disable_pf(bool ignore_not_enabled)
342{
343        int exit_code;
344        char *pfctl[] = {"pfctl", "-d", "-q", NULL};
345
346        exit_code = rtems_bsd_command_pfctl(ARGC(pfctl), pfctl);
347        assert(ignore_not_enabled || (exit_code == EXIT_SUCCESS));
348}
349
350static void
351enable_pf()
352{
353        int exit_code;
354        char *pfctl[] = {"pfctl", "-e", "-q", NULL};
355
356        exit_code = rtems_bsd_command_pfctl(ARGC(pfctl), pfctl);
357        assert(exit_code == EXIT_SUCCESS);
358}
359
360/* Execute pfctl two times with the given arguments. Check the resources. */
361static void
362run_pfctl(int argc, char *argv[], int expected_result)
363{
364        int exit_code;
365        rtems_resource_snapshot snapshot, snapshot2;
366
367        exit_code = rtems_bsd_command_pfctl(argc, argv);
368        assert(exit_code == expected_result);
369
370        rtems_resource_snapshot_take(&snapshot);
371        exit_code = rtems_bsd_command_pfctl(argc, argv);
372        assert(exit_code == expected_result);
373        rtems_resource_snapshot_take(&snapshot2);
374
375        /* heap fragmentation might change so remove it from compare */
376        snapshot.workspace_info.Free.largest = 0;
377        snapshot2.workspace_info.Free.largest = 0;
378        snapshot.heap_info.Free.largest = 0;
379        snapshot2.heap_info.Free.largest = 0;
380        assert(rtems_resource_snapshot_equal(&snapshot, &snapshot2));
381}
382
383static void
384test_pfctl(void)
385{
386        char *no_params[] = {
387                "pfctl",
388                NULL
389        };
390        char *invalid[] = {
391                "pfctl",
392                "-f",
393                TEST_CFG_INVALID,
394                "-q",
395                NULL
396        };
397        char *block_most[] = {
398                "pfctl",
399                "-f",
400                TEST_CFG_BLOCK_MOST,
401                "-q",
402                NULL
403        };
404        char *if_depend[] = {
405                "pfctl",
406                "-f",
407                TEST_CFG_IF_DEPEND,
408                "-q",
409                NULL
410        };
411
412        puts("--- run pfctl with no paramters");
413        run_pfctl(ARGC(no_params), no_params, EXIT_FAILURE);
414
415        puts("--- load invalid rule");
416        run_pfctl(ARGC(invalid), invalid, EXIT_FAILURE);
417
418        puts("--- load rule to block input and most output");
419        run_pfctl(ARGC(block_most), block_most, EXIT_SUCCESS);
420
421        puts("--- load rule to block if dependent");
422        run_pfctl(ARGC(if_depend), if_depend, EXIT_SUCCESS);
423}
424
425static void
426test_allow_lo1()
427{
428        char *pfctl[] = {
429                "pfctl",
430                "-f",
431                TEST_CFG_ALLOW_LO1,
432                "-q",
433                NULL
434        };
435
436        disable_pf(true);
437
438        puts("--- check services");
439        check_services(LO1_IP, true, true);
440        check_services(LO2_IP, true, true);
441
442        puts("--- load and enable rule to allow only lo1");
443        run_pfctl(ARGC(pfctl), pfctl, EXIT_SUCCESS);
444        enable_pf();
445
446        puts("--- check services");
447        check_services(LO1_IP, true, true);
448        check_services(LO2_IP, false, false);
449}
450
451static void
452test_telnet_lo1()
453{
454        char *pfctl[] = {
455                "pfctl",
456                "-f",
457                TEST_CFG_TELNET_LO1,
458                "-q",
459                NULL
460        };
461
462        disable_pf(true);
463
464        puts("--- check services");
465        check_services(LO1_IP, true, true);
466        check_services(LO2_IP, true, true);
467
468        puts("--- load and enable rule to allow telnet on lo1");
469        run_pfctl(ARGC(pfctl), pfctl, EXIT_SUCCESS);
470        enable_pf();
471
472        puts("--- check services");
473        check_services(LO1_IP, true, false);
474        check_services(LO2_IP, false, false);
475}
476
477static void
478test_main(void)
479{
480        prepare_files();
481        prepare_interfaces();
482        prepare_services();
483
484        test_pfctl();
485
486        test_allow_lo1();
487        test_telnet_lo1();
488
489        exit(0);
490}
491
492#include <machine/rtems-bsd-sysinit.h>
493
494SYSINIT_NEED_FIREWALL_PF;
495SYSINIT_NEED_FIREWALL_PFLOG;
496
497#include <rtems/bsd/test/default-network-init.h>
Note: See TracBrowser for help on using the repository browser.