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

6-freebsd-12
Last change on this file was 8e33f3b, checked in by Moyano, Gabriel <gabriel.moyano@…>, on 04/03/20 at 07:35:23

testsuite: A description for each test added

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