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

55-freebsd-126-freebsd-12
Last change on this file since 666a568 was 666a568, checked in by Sebastian Huber <sebastian.huber@…>, on 08/25/17 at 12:23:18

Include missing <string.h> and <limits.h>

Fix warnings.

Update #2132.
Update #2133.

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