Changeset df222eb in rtems-libbsd


Ignore:
Timestamp:
Jul 22, 2016, 12:50:09 PM (3 years ago)
Author:
Christian Mauderer <Christian.Mauderer@…>
Branches:
afaeccc05a556f6aa25ba044a7e49d6aa634a59e, freebsd-9.3, master
Children:
f71cbd0
Parents:
7f86f6a
git-author:
Christian Mauderer <Christian.Mauderer@…> (07/22/16 12:50:09)
git-committer:
Christian Mauderer <Christian.Mauderer@…> (08/02/16 08:21:52)
Message:

testsuite/pf01: Test pfctl and pf.

File:
1 edited

Legend:

Unmodified
Added
Removed
  • testsuite/pf01/test_main.c

    r7f86f6a rdf222eb  
    3030 */
    3131
     32#include <assert.h>
     33#include <fcntl.h>
     34#include <stdio.h>
    3235#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>
    3350
    3451#define TEST_NAME "LIBBSD PF 1"
    3552
     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
    36477static void
    37478test_main(void)
    38479{
     480        prepare_files();
     481        prepare_interfaces();
     482        prepare_services();
     483
     484        test_pfctl();
     485
     486        test_allow_lo1();
     487        test_telnet_lo1();
     488
    39489        exit(0);
    40490}
Note: See TracChangeset for help on using the changeset viewer.