Changeset View
Changeset View
Standalone View
Standalone View
tests/sys/netgraph/bridge.c
- This file was added.
/* | |||||
* SPDX-License-Identifier: BSD-3-Clause | |||||
* | |||||
* Copyright 2021 Lutz Donnerhacke | |||||
* | |||||
* Redistribution and use in source and binary forms, with or without | |||||
* modification, are permitted provided that the following conditions | |||||
* are met: | |||||
* | |||||
* 1. Redistributions of source code must retain the above copyright | |||||
* notice, this list of conditions and the following disclaimer. | |||||
* 2. Redistributions in binary form must reproduce the above | |||||
* copyright notice, this list of conditions and the following | |||||
* disclaimer in the documentation and/or other materials provided | |||||
* with the distribution. | |||||
* 3. Neither the name of the copyright holder nor the names of its | |||||
* contributors may be used to endorse or promote products derived | |||||
* from this software without specific prior written permission. | |||||
* | |||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND | |||||
* CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, | |||||
* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF | |||||
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE | |||||
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS | |||||
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, | |||||
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED | |||||
* TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | |||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON | |||||
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR | |||||
* TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF | |||||
* THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF | |||||
* SUCH DAMAGE. | |||||
*/ | |||||
#include <atf-c.h> | |||||
#include <errno.h> | |||||
#include <stdio.h> | |||||
#include <net/ethernet.h> | |||||
#include <netinet/in.h> | |||||
#include <netinet/ip.h> | |||||
#include <netinet/ip6.h> | |||||
#include "util.h" | |||||
static void get_data(void *data, size_t len, void *ctx); | |||||
struct frame4 { | |||||
struct ether_header eh; | |||||
struct ip ip; | |||||
char data[64]; | |||||
}; | |||||
struct frame6 { | |||||
struct ether_header eh; | |||||
struct ip6_hdr ip; | |||||
char data[64]; | |||||
}; | |||||
static struct frame4 msg4 = { | |||||
.ip.ip_v = 4, | |||||
.ip.ip_hl = 5, | |||||
.ip.ip_ttl = 1, | |||||
.ip.ip_p = 254, | |||||
.ip.ip_src = { htonl(0x0a00dead) }, | |||||
.ip.ip_dst = { htonl(0x0a00beef) }, | |||||
.ip.ip_len = 32, | |||||
.eh.ether_type = ETHERTYPE_IP, | |||||
.eh.ether_shost = { 2,4,6 }, | |||||
.eh.ether_dhost = { 2,4,6 }, | |||||
}; | |||||
ATF_TC_WITH_CLEANUP(basic); | |||||
ATF_TC_HEAD(basic, conf) | |||||
{ | |||||
atf_tc_set_md_var(conf, "require.user", "root"); | |||||
} | |||||
ATF_TC_BODY(basic, dummy) | |||||
{ | |||||
int received; | |||||
ng_init(); | |||||
ng_errors(PASS); | |||||
ng_shutdown("bridge:"); | |||||
ng_errors(FAIL); | |||||
ng_mkpeer(".", "a", "bridge", "link0"); | |||||
ng_name("a", "bridge"); | |||||
ng_connect(".", "b", "bridge:", "link1"); | |||||
ng_connect(".", "c", "bridge:", "link2"); | |||||
/* do not bounce back */ | |||||
ng_register_data("a", get_data); | |||||
received = 0; | |||||
msg4.eh.ether_shost[5] = 1; | |||||
ng_send_data("a", &msg4, sizeof(msg4)); | |||||
ng_handle_events(50, &received); | |||||
ATF_CHECK(received == 0); | |||||
/* send to others */ | |||||
ng_register_data("b", get_data); | |||||
ng_register_data("c", get_data); | |||||
received = 0; | |||||
msg4.eh.ether_shost[5] = 1; | |||||
ng_send_data("a", &msg4, sizeof(msg4)); | |||||
ng_handle_events(50, &received); | |||||
ATF_CHECK(received == 2); | |||||
received = 0; | |||||
msg4.eh.ether_shost[5] = 2; | |||||
ng_send_data("b", &msg4, sizeof(msg4)); | |||||
ng_handle_events(50, &received); | |||||
ATF_CHECK(received == 2); | |||||
received = 0; | |||||
msg4.eh.ether_shost[5] = 3; | |||||
ng_send_data("c", &msg4, sizeof(msg4)); | |||||
ng_handle_events(50, &received); | |||||
ATF_CHECK(received == 2); | |||||
/* remove a link */ | |||||
ng_rmhook(".", "b"); | |||||
received = 0; | |||||
msg4.eh.ether_shost[5] = 1; | |||||
ng_send_data("a", &msg4, sizeof(msg4)); | |||||
ng_handle_events(50, &received); | |||||
ATF_CHECK(received == 1); | |||||
} | |||||
ATF_TC_CLEANUP(basic, dummy) | |||||
{ | |||||
ng_errors(PASS); | |||||
ng_shutdown("bridge:"); | |||||
} | |||||
ATF_TC_WITH_CLEANUP(persistence); | |||||
ATF_TC_HEAD(persistence, conf) | |||||
{ | |||||
atf_tc_set_md_var(conf, "require.user", "root"); | |||||
} | |||||
ATF_TC_BODY(persistence, dummy) | |||||
{ | |||||
ng_init(); | |||||
ng_errors(PASS); | |||||
ng_shutdown("bridge:"); | |||||
ng_errors(FAIL); | |||||
ng_mkpeer(".", "a", "bridge", "link0"); | |||||
ng_name("a", "bridge"); | |||||
ng_send_msg("bridge:", "setpersistent"); | |||||
ng_rmhook(".", "a"); | |||||
ng_shutdown("bridge:"); | |||||
} | |||||
ATF_TC_CLEANUP(persistence, dummy) | |||||
{ | |||||
ng_errors(PASS); | |||||
ng_shutdown("bridge:"); | |||||
} | |||||
ATF_TC_WITH_CLEANUP(loop); | |||||
ATF_TC_HEAD(loop, conf) | |||||
{ | |||||
atf_tc_set_md_var(conf, "require.user", "root"); | |||||
} | |||||
ATF_TC_BODY(loop, dummy) | |||||
{ | |||||
int received, i; | |||||
ng_init(); | |||||
ng_errors(PASS); | |||||
ng_shutdown("bridge1:"); | |||||
ng_shutdown("bridge2:"); | |||||
ng_errors(FAIL); | |||||
ng_mkpeer(".", "a", "bridge", "link0"); | |||||
ng_name("a", "bridge1"); | |||||
ng_mkpeer(".", "b", "bridge", "link1"); | |||||
ng_name("b", "bridge2"); | |||||
ng_register_data("a", get_data); | |||||
ng_register_data("b", get_data); | |||||
/* | |||||
* Open loop | |||||
* | |||||
* /-- bridge1 | |||||
* . < | | |||||
* \-- bridge2 | |||||
*/ | |||||
ng_connect("bridge1:", "link11", "bridge2:", "link11"); | |||||
received = 0; | |||||
msg4.eh.ether_shost[5] = 1; | |||||
ng_send_data("a", &msg4, sizeof(msg4)); | |||||
ng_handle_events(50, &received); | |||||
ATF_CHECK(received == 1); | |||||
/* | |||||
* Closed loop, DANGEROUS! | |||||
* | |||||
* /-- bridge1 -\ | |||||
* . < | | | |||||
* \-- bridge2 -/ | |||||
*/ | |||||
ng_connect("bridge1:", "link12", "bridge2:", "link12"); | |||||
received = 0; | |||||
msg4.eh.ether_shost[5] = 1; | |||||
ng_errors(PASS); | |||||
ng_send_data("a", &msg4, sizeof(msg4)); | |||||
ATF_CHECK_ERRNO(ELOOP, errno != 0); /* loop might be detected */ | |||||
ng_errors(FAIL); | |||||
for(i = 0; i < 10; i++) /* don't run forever */ | |||||
if (!ng_handle_event(50, &received)) | |||||
break; | |||||
ATF_CHECK(received == 1); | |||||
} | |||||
ATF_TC_CLEANUP(loop, dummy) | |||||
{ | |||||
printf("CALLED: %s\n", __FUNCTION__); | |||||
ng_errors(PASS); | |||||
ng_shutdown("bridge1:"); | |||||
ng_shutdown("bridge2:"); | |||||
} | |||||
ATF_TC_WITH_CLEANUP(many_unicasts); | |||||
ATF_TC_HEAD(many_unicasts, conf) | |||||
{ | |||||
atf_tc_set_md_var(conf, "require.user", "root"); | |||||
} | |||||
ATF_TC_BODY(many_unicasts, dummy) | |||||
{ | |||||
int received, i; | |||||
const int HOOKS = 1000; | |||||
ng_init(); | |||||
ng_errors(PASS); | |||||
ng_shutdown("bridge:"); | |||||
ng_errors(FAIL); | |||||
ng_mkpeer(".", "a", "bridge", "link0"); | |||||
ng_name("a", "bridge"); | |||||
/* learn MAC */ | |||||
msg4.eh.ether_shost[3] = 0xff; | |||||
ng_send_data("a", &msg4, sizeof(msg4)); | |||||
/* use learned MAC */ | |||||
msg4.eh.ether_shost[3] = 0; | |||||
msg4.eh.ether_dhost[3] = 0xff; | |||||
ng_register_data("a", get_data); | |||||
received = 0; | |||||
for (i = 1; i <= HOOKS; i++) { | |||||
char hook[20]; | |||||
snprintf(hook, sizeof(hook), "link%d", i); | |||||
ng_connect(".", hook, "bridge:", hook); | |||||
msg4.eh.ether_shost[4] = i >> 8; | |||||
msg4.eh.ether_shost[5] = i & 0xff; | |||||
ng_errors(PASS); | |||||
ng_send_data(hook, &msg4, sizeof(msg4)); | |||||
ng_errors(FAIL); | |||||
if (errno != 0) | |||||
break; | |||||
ng_handle_events(50, &received); | |||||
} | |||||
ATF_CHECK(received == HOOKS); | |||||
} | |||||
ATF_TC_CLEANUP(many_unicasts, dummy) | |||||
{ | |||||
ng_errors(PASS); | |||||
ng_shutdown("bridge:"); | |||||
} | |||||
ATF_TC_WITH_CLEANUP(many_broadcasts); | |||||
ATF_TC_HEAD(many_broadcasts, conf) | |||||
{ | |||||
atf_tc_set_md_var(conf, "require.user", "root"); | |||||
} | |||||
ATF_TC_BODY(many_broadcasts, dummy) | |||||
{ | |||||
int received, i; | |||||
const int HOOKS = 1000; | |||||
ng_init(); | |||||
ng_errors(PASS); | |||||
ng_shutdown("bridge:"); | |||||
ng_errors(FAIL); | |||||
ng_mkpeer(".", "a", "bridge", "link0"); | |||||
ng_name("a", "bridge"); | |||||
/* learn MAC */ | |||||
msg4.eh.ether_shost[3] = 0xff; | |||||
ng_send_data("a", &msg4, sizeof(msg4)); | |||||
/* use learned MAC */ | |||||
msg4.eh.ether_shost[3] = 0; | |||||
memset(msg4.eh.ether_dhost, 0xff, sizeof(msg4.eh.ether_dhost)); | |||||
ng_register_data("a", get_data); | |||||
received = 0; | |||||
for (i = 1; i <= HOOKS; i++) { | |||||
char hook[20]; | |||||
snprintf(hook, sizeof(hook), "link%d", i); | |||||
ng_connect(".", hook, "bridge:", hook); | |||||
msg4.eh.ether_shost[4] = i >> 8; | |||||
msg4.eh.ether_shost[5] = i & 0xff; | |||||
ng_errors(PASS); | |||||
ng_send_data(hook, &msg4, sizeof(msg4)); | |||||
ng_errors(FAIL); | |||||
if (errno != 0) | |||||
break; | |||||
ng_handle_events(50, &received); | |||||
} | |||||
ATF_CHECK(received > 100); | |||||
atf_tc_expect_fail("Implementation limitation (%d)", i); | |||||
ATF_CHECK(received == HOOKS); | |||||
atf_tc_expect_pass(); | |||||
} | |||||
ATF_TC_CLEANUP(many_broadcasts, dummy) | |||||
{ | |||||
ng_errors(PASS); | |||||
ng_shutdown("bridge:"); | |||||
} | |||||
ATF_TP_ADD_TCS(bridge) | |||||
{ | |||||
ATF_TP_ADD_TC(bridge, basic); | |||||
ATF_TP_ADD_TC(bridge, loop); | |||||
ATF_TP_ADD_TC(bridge, persistence); | |||||
ATF_TP_ADD_TC(bridge, many_unicasts); | |||||
ATF_TP_ADD_TC(bridge, many_broadcasts); | |||||
return atf_no_error(); | |||||
} | |||||
static void | |||||
get_data(void *data, size_t len, void *ctx) | |||||
{ | |||||
int *cnt = ctx; | |||||
(void)data; | |||||
printf("Got %zu bytes of data.\n", len); | |||||
(*cnt)++; | |||||
} |