1
0
mirror of https://github.com/systemd/systemd synced 2026-03-31 12:14:57 +02:00

Compare commits

..

7 Commits

Author SHA1 Message Date
Yu Watanabe
231c7645ca
Merge pull request #21041 from yuwata/network-bpf-neighbor
network: introduce BPF to reject netlink messages about non-static neighbor
2021-10-20 13:30:32 +09:00
Yu Watanabe
952d873f56
Merge pull request #21056 from yuwata/test-network-cleanups
test-network: several cleanups related to dnsmasq
2021-10-20 13:29:45 +09:00
Yu Watanabe
a4a1385e07 test-network: cleanup dnsmasq related file on setup
Just for safety.
2021-10-20 03:49:05 +09:00
Yu Watanabe
888f57c11c test-network: drop pid_file argument from stop_dnsmasq() 2021-10-20 03:49:05 +09:00
Yu Watanabe
ed08ed4a45 test-network: use constant variables in dnsmasq command 2021-10-20 03:49:05 +09:00
Yu Watanabe
26a8be48bc network: introduce BPF to reject netlink messages about non-static neighbor
Fixes #21031.
2021-10-19 20:41:34 +09:00
Yu Watanabe
dc317a9aed sd-netlink: introduce sd_netlink_attach_filter() 2021-10-19 20:41:09 +09:00
4 changed files with 76 additions and 21 deletions

View File

@ -1023,3 +1023,18 @@ int sd_netlink_add_match(
return netlink_add_match_internal(rtnl, ret_slot, groups, n_groups, type, 0, callback, return netlink_add_match_internal(rtnl, ret_slot, groups, n_groups, type, 0, callback,
destroy_callback, userdata, description); destroy_callback, userdata, description);
} }
int sd_netlink_attach_filter(sd_netlink *nl, size_t len, struct sock_filter *filter) {
assert_return(nl, -EINVAL);
assert_return(len == 0 || filter, -EINVAL);
if (setsockopt(nl->fd, SOL_SOCKET,
len == 0 ? SO_DETACH_FILTER : SO_ATTACH_FILTER,
&(struct sock_fprog) {
.len = len,
.filter = filter,
}, sizeof(struct sock_fprog)) < 0)
return -errno;
return 0;
}

View File

@ -263,6 +263,40 @@ static int manager_connect_genl(Manager *m) {
return 0; return 0;
} }
static int manager_setup_rtnl_filter(Manager *manager) {
struct sock_filter filter[] = {
/* Check the packet length. */
BPF_STMT(BPF_LD + BPF_W + BPF_LEN, 0), /* A <- packet length */
BPF_JUMP(BPF_JMP + BPF_JGE + BPF_K, sizeof(struct nlmsghdr), 1, 0), /* A (packet length) >= sizeof(struct nlmsghdr) ? */
BPF_STMT(BPF_RET + BPF_K, 0), /* reject */
/* Always accept multipart message. */
BPF_STMT(BPF_LD + BPF_H + BPF_ABS, offsetof(struct nlmsghdr, nlmsg_flags)), /* A <- message flags */
BPF_JUMP(BPF_JMP + BPF_JSET + BPF_K, htobe16(NLM_F_MULTI), 0, 1), /* message flags has NLM_F_MULTI ? */
BPF_STMT(BPF_RET + BPF_K, UINT32_MAX), /* accept */
/* Accept all message types except for RTM_NEWNEIGH or RTM_DELNEIGH. */
BPF_STMT(BPF_LD + BPF_H + BPF_ABS, offsetof(struct nlmsghdr, nlmsg_type)), /* A <- message type */
BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, htobe16(RTM_NEWNEIGH), 2, 0), /* message type == RTM_NEWNEIGH ? */
BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, htobe16(RTM_DELNEIGH), 1, 0), /* message type == RTM_DELNEIGH ? */
BPF_STMT(BPF_RET + BPF_K, UINT32_MAX), /* accept */
/* Check the packet length. */
BPF_STMT(BPF_LD + BPF_W + BPF_LEN, 0), /* A <- packet length */
BPF_JUMP(BPF_JMP + BPF_JGE + BPF_K, sizeof(struct nlmsghdr) + sizeof(struct ndmsg), 1, 0),
/* packet length >= sizeof(struct nlmsghdr) + sizeof(struct ndmsg) ? */
BPF_STMT(BPF_RET + BPF_K, 0), /* reject */
/* Reject the message when the neighbor state does not have NUD_PERMANENT flag. */
BPF_STMT(BPF_LD + BPF_H + BPF_ABS, sizeof(struct nlmsghdr) + offsetof(struct ndmsg, ndm_state)),
/* A <- neighbor state */
BPF_JUMP(BPF_JMP + BPF_JSET + BPF_K, htobe16(NUD_PERMANENT), 1, 0), /* neighbor state has NUD_PERMANENT ? */
BPF_STMT(BPF_RET + BPF_K, 0), /* reject */
BPF_STMT(BPF_RET + BPF_K, UINT32_MAX), /* accept */
};
assert(manager);
assert(manager->rtnl);
return sd_netlink_attach_filter(manager->rtnl, ELEMENTSOF(filter), filter);
}
static int manager_connect_rtnl(Manager *m) { static int manager_connect_rtnl(Manager *m) {
int fd, r; int fd, r;
@ -337,7 +371,7 @@ static int manager_connect_rtnl(Manager *m) {
if (r < 0) if (r < 0)
return r; return r;
return 0; return manager_setup_rtnl_filter(m);
} }
static int manager_dirty_handler(sd_event_source *s, void *userdata) { static int manager_dirty_handler(sd_event_source *s, void *userdata) {

View File

@ -21,6 +21,7 @@
#include <inttypes.h> #include <inttypes.h>
#include <net/ethernet.h> #include <net/ethernet.h>
#include <netinet/in.h> #include <netinet/in.h>
#include <linux/filter.h>
#include <linux/neighbour.h> #include <linux/neighbour.h>
#include <linux/rtnetlink.h> #include <linux/rtnetlink.h>
@ -68,6 +69,7 @@ int sd_netlink_add_match(sd_netlink *nl, sd_netlink_slot **ret_slot, uint16_t ma
int sd_netlink_attach_event(sd_netlink *nl, sd_event *e, int64_t priority); int sd_netlink_attach_event(sd_netlink *nl, sd_event *e, int64_t priority);
int sd_netlink_detach_event(sd_netlink *nl); int sd_netlink_detach_event(sd_netlink *nl);
int sd_netlink_attach_filter(sd_netlink *nl, size_t len, struct sock_filter *filter);
/* message */ /* message */
int sd_netlink_message_append_string(sd_netlink_message *m, unsigned short type, const char *data); int sd_netlink_message_append_string(sd_netlink_message *m, unsigned short type, const char *data);

View File

@ -21,8 +21,9 @@ networkd_ci_path='/run/networkd-ci'
network_sysctl_ipv6_path='/proc/sys/net/ipv6/conf' network_sysctl_ipv6_path='/proc/sys/net/ipv6/conf'
network_sysctl_ipv4_path='/proc/sys/net/ipv4/conf' network_sysctl_ipv4_path='/proc/sys/net/ipv4/conf'
dnsmasq_pid_file='/run/networkd-ci/test-test-dnsmasq.pid' dnsmasq_pid_file='/run/networkd-ci/test-dnsmasq.pid'
dnsmasq_log_file='/run/networkd-ci/test-dnsmasq-log-file' dnsmasq_log_file='/run/networkd-ci/test-dnsmasq.log'
dnsmasq_lease_file='/run/networkd-ci/test-dnsmasq.lease'
systemd_lib_paths=['/usr/lib/systemd', '/lib/systemd'] systemd_lib_paths=['/usr/lib/systemd', '/lib/systemd']
which_paths=':'.join(systemd_lib_paths + os.getenv('PATH', os.defpath).lstrip(':').split(':')) which_paths=':'.join(systemd_lib_paths + os.getenv('PATH', os.defpath).lstrip(':').split(':'))
@ -474,10 +475,10 @@ def remove_networkd_conf_dropin(dropins):
os.remove(os.path.join(networkd_conf_dropin_path, dropin)) os.remove(os.path.join(networkd_conf_dropin_path, dropin))
def start_dnsmasq(additional_options='', ipv4_range='192.168.5.10,192.168.5.200', ipv6_range='2600::10,2600::20', lease_time='1h'): def start_dnsmasq(additional_options='', ipv4_range='192.168.5.10,192.168.5.200', ipv6_range='2600::10,2600::20', lease_time='1h'):
dnsmasq_command = f'dnsmasq -8 /var/run/networkd-ci/test-dnsmasq-log-file --log-queries=extra --log-dhcp --pid-file=/var/run/networkd-ci/test-test-dnsmasq.pid --conf-file=/dev/null --interface=veth-peer --enable-ra --dhcp-range={ipv6_range},{lease_time} --dhcp-range={ipv4_range},{lease_time} -R --dhcp-leasefile=/var/run/networkd-ci/lease --dhcp-option=26,1492 --dhcp-option=option:router,192.168.5.1 --port=0 ' + additional_options dnsmasq_command = f'dnsmasq -8 {dnsmasq_log_file} --log-queries=extra --log-dhcp --pid-file={dnsmasq_pid_file} --conf-file=/dev/null --interface=veth-peer --enable-ra --dhcp-range={ipv6_range},{lease_time} --dhcp-range={ipv4_range},{lease_time} -R --dhcp-leasefile={dnsmasq_lease_file} --dhcp-option=26,1492 --dhcp-option=option:router,192.168.5.1 --port=0 ' + additional_options
check_output(dnsmasq_command) check_output(dnsmasq_command)
def stop_dnsmasq(pid_file): def stop_by_pid_file(pid_file):
if os.path.exists(pid_file): if os.path.exists(pid_file):
with open(pid_file, 'r') as f: with open(pid_file, 'r') as f:
pid = f.read().rstrip(' \t\r\n\0') pid = f.read().rstrip(' \t\r\n\0')
@ -485,6 +486,9 @@ def stop_dnsmasq(pid_file):
os.remove(pid_file) os.remove(pid_file)
def stop_dnsmasq():
stop_by_pid_file(dnsmasq_pid_file)
def search_words_in_dnsmasq_log(words, show_all=False): def search_words_in_dnsmasq_log(words, show_all=False):
if os.path.exists(dnsmasq_log_file): if os.path.exists(dnsmasq_log_file):
with open (dnsmasq_log_file) as in_file: with open (dnsmasq_log_file) as in_file:
@ -498,11 +502,11 @@ def search_words_in_dnsmasq_log(words, show_all=False):
return True return True
return False return False
def remove_lease_file(): def remove_dnsmasq_lease_file():
if os.path.exists(os.path.join(networkd_ci_path, 'lease')): if os.path.exists(dnsmasq_lease_file):
os.remove(os.path.join(networkd_ci_path, 'lease')) os.remove(dnsmasq_lease_file)
def remove_log_file(): def remove_dnsmasq_log_file():
if os.path.exists(dnsmasq_log_file): if os.path.exists(dnsmasq_log_file):
os.remove(dnsmasq_log_file) os.remove(dnsmasq_log_file)
@ -4034,14 +4038,16 @@ class NetworkdDHCPClientTests(unittest.TestCase, Utilities):
'static.network'] 'static.network']
def setUp(self): def setUp(self):
stop_dnsmasq(dnsmasq_pid_file) stop_dnsmasq()
remove_dnsmasq_lease_file()
remove_dnsmasq_log_file()
remove_links(self.links) remove_links(self.links)
stop_networkd(show_logs=False) stop_networkd(show_logs=False)
def tearDown(self): def tearDown(self):
stop_dnsmasq(dnsmasq_pid_file) stop_dnsmasq()
remove_lease_file() remove_dnsmasq_lease_file()
remove_log_file() remove_dnsmasq_log_file()
remove_links(self.links) remove_links(self.links)
remove_unit_from_networkd_path(self.units) remove_unit_from_networkd_path(self.units)
stop_networkd(show_logs=True) stop_networkd(show_logs=True)
@ -4092,7 +4098,7 @@ class NetworkdDHCPClientTests(unittest.TestCase, Utilities):
self.assertRegex(output, r'192.168.5.6 proto dhcp scope link src 192.168.5.181 metric 1024') self.assertRegex(output, r'192.168.5.6 proto dhcp scope link src 192.168.5.181 metric 1024')
self.assertRegex(output, r'192.168.5.7 proto dhcp scope link src 192.168.5.181 metric 1024') self.assertRegex(output, r'192.168.5.7 proto dhcp scope link src 192.168.5.181 metric 1024')
stop_dnsmasq(dnsmasq_pid_file) stop_dnsmasq()
start_dnsmasq(additional_options='--dhcp-option=option:dns-server,192.168.5.1,192.168.5.7,192.168.5.8', lease_time='2m') start_dnsmasq(additional_options='--dhcp-option=option:dns-server,192.168.5.1,192.168.5.7,192.168.5.8', lease_time='2m')
# Sleep for 120 sec as the dnsmasq minimum lease time can only be set to 120 # Sleep for 120 sec as the dnsmasq minimum lease time can only be set to 120
@ -4330,7 +4336,7 @@ class NetworkdDHCPClientTests(unittest.TestCase, Utilities):
self.assertRegex(output, r'192.168.6.0/24 proto static') self.assertRegex(output, r'192.168.6.0/24 proto static')
self.assertRegex(output, r'192.168.7.0/24 proto static') self.assertRegex(output, r'192.168.7.0/24 proto static')
stop_dnsmasq(dnsmasq_pid_file) stop_dnsmasq()
start_dnsmasq(ipv4_range='192.168.5.210,192.168.5.220', lease_time='2m') start_dnsmasq(ipv4_range='192.168.5.210,192.168.5.220', lease_time='2m')
# Sleep for 120 sec as the dnsmasq minimum lease time can only be set to 120 # Sleep for 120 sec as the dnsmasq minimum lease time can only be set to 120
@ -4363,7 +4369,7 @@ class NetworkdDHCPClientTests(unittest.TestCase, Utilities):
self.assertRegex(output, r'2600::/64 proto ra metric 1024') self.assertRegex(output, r'2600::/64 proto ra metric 1024')
self.assertRegex(output, r'2600:0:0:1::/64 proto static metric 1024 pref medium') self.assertRegex(output, r'2600:0:0:1::/64 proto static metric 1024 pref medium')
stop_dnsmasq(dnsmasq_pid_file) stop_dnsmasq()
start_dnsmasq(ipv6_range='2600::30,2600::40', lease_time='2m') start_dnsmasq(ipv6_range='2600::30,2600::40', lease_time='2m')
# Sleep for 120 sec as the dnsmasq minimum lease time can only be set to 120 # Sleep for 120 sec as the dnsmasq minimum lease time can only be set to 120
@ -4393,7 +4399,7 @@ class NetworkdDHCPClientTests(unittest.TestCase, Utilities):
self.assertRegex(output, r'192.168.5.*') self.assertRegex(output, r'192.168.5.*')
# Stopping dnsmasq as networkd won't be allowed to renew the DHCP lease. # Stopping dnsmasq as networkd won't be allowed to renew the DHCP lease.
stop_dnsmasq(dnsmasq_pid_file) stop_dnsmasq()
# Sleep for 120 sec as the dnsmasq minimum lease time can only be set to 120 # Sleep for 120 sec as the dnsmasq minimum lease time can only be set to 120
print('Wait for the dynamic address to be expired') print('Wait for the dynamic address to be expired')
@ -4443,7 +4449,7 @@ class NetworkdDHCPClientTests(unittest.TestCase, Utilities):
print(output) print(output)
self.assertRegex(output, r'192.168.5.*') self.assertRegex(output, r'192.168.5.*')
stop_dnsmasq(dnsmasq_pid_file) stop_dnsmasq()
check_output('systemctl stop systemd-networkd.socket') check_output('systemctl stop systemd-networkd.socket')
check_output('systemctl stop systemd-networkd.service') check_output('systemctl stop systemd-networkd.service')
@ -4686,7 +4692,7 @@ class NetworkdDHCPClientTests(unittest.TestCase, Utilities):
self.assertRegex(output, f'default via 192.168.5.1 proto dhcp src {address1} metric 1024') self.assertRegex(output, f'default via 192.168.5.1 proto dhcp src {address1} metric 1024')
self.assertRegex(output, f'192.168.5.1 proto dhcp scope link src {address1} metric 1024') self.assertRegex(output, f'192.168.5.1 proto dhcp scope link src {address1} metric 1024')
stop_dnsmasq(dnsmasq_pid_file) stop_dnsmasq()
start_dnsmasq(ipv4_range='192.168.5.200,192.168.5.250', lease_time='2m') start_dnsmasq(ipv4_range='192.168.5.200,192.168.5.250', lease_time='2m')
print('Wait for the dynamic address to be expired') print('Wait for the dynamic address to be expired')
@ -4839,7 +4845,6 @@ class NetworkdIPv6PrefixTests(unittest.TestCase, Utilities):
stop_networkd(show_logs=False) stop_networkd(show_logs=False)
def tearDown(self): def tearDown(self):
remove_log_file()
remove_links(self.links) remove_links(self.links)
remove_unit_from_networkd_path(self.units) remove_unit_from_networkd_path(self.units)
stop_networkd(show_logs=True) stop_networkd(show_logs=True)
@ -4928,7 +4933,6 @@ class NetworkdMTUTests(unittest.TestCase, Utilities):
stop_networkd(show_logs=False) stop_networkd(show_logs=False)
def tearDown(self): def tearDown(self):
remove_log_file()
remove_links(self.links) remove_links(self.links)
remove_unit_from_networkd_path(self.units) remove_unit_from_networkd_path(self.units)
stop_networkd(show_logs=True) stop_networkd(show_logs=True)