Commit 803f1f0f authored by Marcin Siodelski's avatar Marcin Siodelski
Browse files

[master] Merge branch 'trac3547'

parents 35be7340 eeddb1b5
// Copyright (C) 2014 Internet Systems Consortium, Inc. ("ISC")
// Copyright (C) 2014-2015 Internet Systems Consortium, Inc. ("ISC")
//
// Permission to use, copy, modify, and/or distribute this software for any
// purpose with or without fee is hereby granted, provided that the above
......@@ -50,36 +50,64 @@ const unsigned int BPF_LOCAL_LOOPBACK_HEADER_LEN = 4;
/// 2 bytes UDP destination port
/// 4 bytes Rest of UDP header
///
/// Each instruction is preceded with the comment giving the instruction
/// number within a BPF program, in the following format: #123.
///
/// @todo We may want to extend the filter to receive packets sent
/// to the particular IP address assigned to the interface or
/// broadcast address.
struct bpf_insn ethernet_ip_udp_filter [] = {
// Make sure this is an IP packet: check the half-word (two bytes)
// at offset 12 in the packet (the Ethernet packet type). If it
// is, advance to the next instruction. If not, advance 8
// is, advance to the next instruction. If not, advance 11
// instructions (which takes execution to the last instruction in
// the sequence: "drop it").
// #0
BPF_STMT(BPF_LD + BPF_H + BPF_ABS, ETHERNET_PACKET_TYPE_OFFSET),
BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, ETHERTYPE_IP, 0, 8),
// #1
BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, ETHERTYPE_IP, 0, 11),
// Make sure it's a UDP packet. The IP protocol is at offset
// 9 in the IP header so, adding the Ethernet packet header size
// of 14 bytes gives an absolute byte offset in the packet of 23.
// #2
BPF_STMT(BPF_LD + BPF_B + BPF_ABS,
ETHERNET_HEADER_LEN + IP_PROTO_TYPE_OFFSET),
BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, IPPROTO_UDP, 0, 6),
// #3
BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, IPPROTO_UDP, 0, 9),
// Make sure this isn't a fragment by checking that the fragment
// offset field in the IP header is zero. This field is the
// least-significant 13 bits in the bytes at offsets 6 and 7 in
// the IP header, so the half-word at offset 20 (6 + size of
// Ethernet header) is loaded and an appropriate mask applied.
// #4
BPF_STMT(BPF_LD + BPF_H + BPF_ABS, ETHERNET_HEADER_LEN + IP_FLAGS_OFFSET),
BPF_JUMP(BPF_JMP + BPF_JSET + BPF_K, 0x1fff, 4, 0),
// #5
BPF_JUMP(BPF_JMP + BPF_JSET + BPF_K, 0x1fff, 7, 0),
// Check the packet's destination address. The program will only
// allow the packets sent to the broadcast address or unicast
// to the specific address on the interface. By default, this
// address is set to 0 and must be set to the specific value
// when the raw socket is created and the program is attached
// to it. The caller must assign the address to the
// prog.bf_insns[8].k in the network byte order.
// #6
BPF_STMT(BPF_LD + BPF_W + BPF_ABS,
ETHERNET_HEADER_LEN + IP_DEST_ADDR_OFFSET),
// If this is a broadcast address, skip the next check.
// #7
BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, 0xffffffff, 1, 0),
// If this is not broadcast address, compare it with the unicast
// address specified for the interface.
// #8
BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, 0x00000000, 0, 4),
// Get the IP header length. This is achieved by the following
// (special) instruction that, given the offset of the start
// of the IP header (offset 14) loads the IP header length.
// #9
BPF_STMT(BPF_LDX + BPF_B + BPF_MSH, ETHERNET_HEADER_LEN),
// Make sure it's to the right port. The following instruction
......@@ -87,18 +115,22 @@ struct bpf_insn ethernet_ip_udp_filter [] = {
// offset to locate the correct byte. The given offset of 16
// comprises the length of the Ethernet header (14) plus the offset
// of the UDP destination port (2) within the UDP header.
// #10
BPF_STMT(BPF_LD + BPF_H + BPF_IND, ETHERNET_HEADER_LEN + UDP_DEST_PORT),
// The following instruction tests against the default DHCP server port,
// but the action port is actually set in PktFilterBPF::openSocket().
// N.B. The code in that method assumes that this instruction is at
// offset 8 in the program. If this is changed, openSocket() must be
// offset 11 in the program. If this is changed, openSocket() must be
// updated.
// #11
BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, DHCP4_SERVER_PORT, 0, 1),
// If we passed all the tests, ask for the whole packet.
// #12
BPF_STMT(BPF_RET + BPF_K, (u_int)-1),
// Otherwise, drop it.
// #13
BPF_STMT(BPF_RET + BPF_K, 0),
};
......@@ -107,33 +139,61 @@ struct bpf_insn ethernet_ip_udp_filter [] = {
/// contain the regular link-layer header, but rather a 4-byte long pseudo
/// header containing the address family. The reminder of the packet contains
/// IP header, UDP header and a DHCP message.
///
/// Each instruction is preceded with the comment giving the instruction
/// number within a BPF program, in the following format: #123.
struct bpf_insn loopback_ip_udp_filter [] = {
// Make sure this is an IP packet. The pseudo header comprises a 4-byte
// long value identifying the address family, which should be set to
// AF_INET. The default value used here (0xFFFFFFFF) must be overriden
// with htonl(AF_INET) from within the openSocket function.
// #0
BPF_STMT(BPF_LD + BPF_W + BPF_ABS, 0),
BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, 0xFFFFFFFF, 0, 8),
// #1
BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, 0xFFFFFFFF, 0, 11),
// Make sure it's a UDP packet. The IP protocol is at offset
// 9 in the IP header so, adding the pseudo header size 4 bytes
// gives an absolute byte offset in the packet of 13.
// #2
BPF_STMT(BPF_LD + BPF_B + BPF_ABS,
BPF_LOCAL_LOOPBACK_HEADER_LEN + IP_PROTO_TYPE_OFFSET),
BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, IPPROTO_UDP, 0, 6),
// #3
BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, IPPROTO_UDP, 0, 9),
// Make sure this isn't a fragment by checking that the fragment
// offset field in the IP header is zero. This field is the
// least-significant 13 bits in the bytes at offsets 6 and 7 in
// the IP header, so the half-word at offset 10 (6 + size of
// pseudo header) is loaded and an appropriate mask applied.
// #4
BPF_STMT(BPF_LD + BPF_H + BPF_ABS,
BPF_LOCAL_LOOPBACK_HEADER_LEN + IP_FLAGS_OFFSET),
BPF_JUMP(BPF_JMP + BPF_JSET + BPF_K, 0x1fff, 4, 0),
// #5
BPF_JUMP(BPF_JMP + BPF_JSET + BPF_K, 0x1fff, 7, 0),
// Check the packet's destination address. The program will only
// allow the packets sent to the broadcast address or unicast
// to the specific address on the interface. By default, this
// address is set to 0 and must be set to the specific value
// when the raw socket is created and the program is attached
// to it. The caller must assign the address to the
// prog.bf_insns[8].k in the network byte order.
// #6
BPF_STMT(BPF_LD + BPF_W + BPF_ABS,
BPF_LOCAL_LOOPBACK_HEADER_LEN + IP_DEST_ADDR_OFFSET),
// If this is a broadcast address, skip the next check.
// #7
BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, 0xffffffff, 1, 0),
// If this is not broadcast address, compare it with the unicast
// address specified for the interface.
// #8
BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, 0x00000000, 0, 4),
// Get the IP header length. This is achieved by the following
// (special) instruction that, given the offset of the start
// of the IP header (offset 4) loads the IP header length.
// #9
BPF_STMT(BPF_LDX + BPF_B + BPF_MSH, BPF_LOCAL_LOOPBACK_HEADER_LEN),
// Make sure it's to the right port. The following instruction
......@@ -141,19 +201,23 @@ struct bpf_insn loopback_ip_udp_filter [] = {
// offset to locate the correct byte. The given offset of 6
// comprises the length of the pseudo header (4) plus the offset
// of the UDP destination port (2) within the UDP header.
// #10
BPF_STMT(BPF_LD + BPF_H + BPF_IND,
BPF_LOCAL_LOOPBACK_HEADER_LEN + UDP_DEST_PORT),
// The following instruction tests against the default DHCP server port,
// but the action port is actually set in PktFilterBPF::openSocket().
// N.B. The code in that method assumes that this instruction is at
// offset 8 in the program. If this is changed, openSocket() must be
// offset 11 in the program. If this is changed, openSocket() must be
// updated.
// #11
BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, DHCP4_SERVER_PORT, 0, 1),
// If we passed all the tests, ask for the whole packet.
// #12
BPF_STMT(BPF_RET + BPF_K, (u_int)-1),
// Otherwise, drop it.
// #13
BPF_STMT(BPF_RET + BPF_K, 0),
};
......@@ -273,8 +337,13 @@ PktFilterBPF::openSocket(Iface& iface,
prog.bf_len = sizeof(ethernet_ip_udp_filter) / sizeof(struct bpf_insn);
}
// Configure the BPF program to receive unicast packets sent to the
// specified address. The program will also allow packets sent to the
// 255.255.255.255 broadcast address.
prog.bf_insns[8].k = static_cast<uint32_t>(addr);
// Configure the BPF program to receive packets on the specified port.
prog.bf_insns[8].k = port;
prog.bf_insns[11].k = port;
// Actually set the filter program for the device.
if (ioctl(sock, BIOCSETF, &prog) < 0) {
......
// Copyright (C) 2013-2014 Internet Systems Consortium, Inc. ("ISC")
// Copyright (C) 2013-2015 Internet Systems Consortium, Inc. ("ISC")
//
// Permission to use, copy, modify, and/or distribute this software for any
// purpose with or without fee is hereby granted, provided that the above
......@@ -44,35 +44,64 @@ using namespace isc::dhcp;
/// 2 bytes UDP destination port
/// 4 bytes Rest of UDP header
///
/// Each instruction is preceded with the comments giving the instruction
/// number within a BPF program, in the following format: #123.
///
/// @todo We may want to extend the filter to receive packets sent
/// to the particular IP address assigned to the interface or
/// broadcast address.
struct sock_filter dhcp_sock_filter [] = {
// Make sure this is an IP packet: check the half-word (two bytes)
// at offset 12 in the packet (the Ethernet packet type). If it
// is, advance to the next instruction. If not, advance 8
// is, advance to the next instruction. If not, advance 11
// instructions (which takes execution to the last instruction in
// the sequence: "drop it").
// #0
BPF_STMT(BPF_LD + BPF_H + BPF_ABS, ETHERNET_PACKET_TYPE_OFFSET),
BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, ETHERTYPE_IP, 0, 8),
// #1
BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, ETHERTYPE_IP, 0, 11),
// Make sure it's a UDP packet. The IP protocol is at offset
// 9 in the IP header so, adding the Ethernet packet header size
// of 14 bytes gives an absolute byte offset in the packet of 23.
BPF_STMT(BPF_LD + BPF_B + BPF_ABS, ETHERNET_HEADER_LEN + IP_PROTO_TYPE_OFFSET),
BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, IPPROTO_UDP, 0, 6),
// #2
BPF_STMT(BPF_LD + BPF_B + BPF_ABS,
ETHERNET_HEADER_LEN + IP_PROTO_TYPE_OFFSET),
// #3
BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, IPPROTO_UDP, 0, 9),
// Make sure this isn't a fragment by checking that the fragment
// offset field in the IP header is zero. This field is the
// least-significant 13 bits in the bytes at offsets 6 and 7 in
// the IP header, so the half-word at offset 20 (6 + size of
// Ethernet header) is loaded and an appropriate mask applied.
// #4
BPF_STMT(BPF_LD + BPF_H + BPF_ABS, ETHERNET_HEADER_LEN + IP_FLAGS_OFFSET),
BPF_JUMP(BPF_JMP + BPF_JSET + BPF_K, 0x1fff, 4, 0),
// #5
BPF_JUMP(BPF_JMP + BPF_JSET + BPF_K, 0x1fff, 7, 0),
// Check the packet's destination address. The program will only
// allow the packets sent to the broadcast address or unicast
// to the specific address on the interface. By default, this
// address is set to 0 and must be set to the specific value
// when the raw socket is created and the program is attached
// to it. The caller must assign the address to the
// prog.bf_insns[8].k in the network byte order.
// #6
BPF_STMT(BPF_LD + BPF_W + BPF_ABS,
ETHERNET_HEADER_LEN + IP_DEST_ADDR_OFFSET),
// If this is a broadcast address, skip the next check.
// #7
BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, 0xffffffff, 1, 0),
// If this is not broadcast address, compare it with the unicast
// address specified for the interface.
// #8
BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, 0x00000000, 0, 4),
// Get the IP header length. This is achieved by the following
// (special) instruction that, given the offset of the start
// of the IP header (offset 14) loads the IP header length.
// #9
BPF_STMT(BPF_LDX + BPF_B + BPF_MSH, ETHERNET_HEADER_LEN),
// Make sure it's to the right port. The following instruction
......@@ -80,18 +109,22 @@ struct sock_filter dhcp_sock_filter [] = {
// offset to locate the correct byte. The given offset of 16
// comprises the length of the Ethernet header (14) plus the offset
// of the UDP destination port (2) within the UDP header.
// #10
BPF_STMT(BPF_LD + BPF_H + BPF_IND, ETHERNET_HEADER_LEN + UDP_DEST_PORT),
// The following instruction tests against the default DHCP server port,
// but the action port is actually set in PktFilterLPF::openSocket().
// but the action port is actually set in PktFilterBPF::openSocket().
// N.B. The code in that method assumes that this instruction is at
// offset 8 in the program. If this is changed, openSocket() must be
// offset 11 in the program. If this is changed, openSocket() must be
// updated.
// #11
BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, DHCP4_SERVER_PORT, 0, 1),
// If we passed all the tests, ask for the whole packet.
// #12
BPF_STMT(BPF_RET + BPF_K, (u_int)-1),
// Otherwise, drop it.
// #13
BPF_STMT(BPF_RET + BPF_K, 0),
};
......@@ -129,8 +162,14 @@ PktFilterLPF::openSocket(Iface& iface,
filter_program.filter = dhcp_sock_filter;
filter_program.len = sizeof(dhcp_sock_filter) / sizeof(struct sock_filter);
// Configure the filter program to receive unicast packets sent to the
// specified address. The program will also allow packets sent to the
// 255.255.255.255 broadcast address.
dhcp_sock_filter[8].k = static_cast<uint32_t>(addr);
// Override the default port value.
dhcp_sock_filter[8].k = port;
dhcp_sock_filter[11].k = port;
// Apply the filter.
if (setsockopt(sock, SOL_SOCKET, SO_ATTACH_FILTER, &filter_program,
sizeof(filter_program)) < 0) {
......
......@@ -56,6 +56,8 @@ static const size_t IP_FLAGS_OFFSET = 6;
static const size_t IP_PROTO_TYPE_OFFSET = 9;
/// Offset of source address in the IPv4 header.
static const size_t IP_SRC_ADDR_OFFSET = 12;
/// Offset of destination address in the IPv4 header.
static const size_t IP_DEST_ADDR_OFFSET = 16;
/// UDP header length.
static const size_t UDP_HEADER_LEN = 8;
......
// Copyright (C) 2014 Internet Systems Consortium, Inc. ("ISC")
// Copyright (C) 2014, 2015 Internet Systems Consortium, Inc. ("ISC")
//
// Permission to use, copy, modify, and/or distribute this software for any
// purpose with or without fee is hereby granted, provided that the above
......@@ -202,4 +202,41 @@ TEST_F(PktFilterBPFTest, DISABLED_receive) {
testRcvdMessage(rcvd_pkt);
}
// This test verifies that if the packet is received over the raw
// socket and its destination address doesn't match the address
// to which the socket is "bound", the packet is dropped.
TEST_F(PktFilterBPFTest, DISABLED_filterOutUnicast) {
// Packet will be received over loopback interface.
Iface iface(ifname_, ifindex_);
iface.flag_loopback_ = true;
IOAddress addr("127.0.0.1");
// Create an instance of the class which we are testing.
PktFilterBPF pkt_filter;
// Open socket. We don't check that the socket has appropriate
// options and family set because we have checked that in the
// openSocket test already.
sock_info_ = pkt_filter.openSocket(iface, addr, PORT, false, false);
ASSERT_GE(sock_info_.sockfd_, 0);
// The message sent to the local loopback interface will have an
// invalid (non-existing) destination address. The socket should
// drop this packet.
sendMessage(IOAddress("128.0.0.1"));
// Perform select on the socket to make sure that the packet has
// been dropped.
fd_set readfds;
FD_ZERO(&readfds);
FD_SET(sock_info_.sockfd_, &readfds);
struct timeval timeout;
timeout.tv_sec = 1;
timeout.tv_usec = 0;
int result = select(sock_info_.sockfd_ + 1, &readfds, NULL, NULL, &timeout);
ASSERT_LE(result, 0);
}
} // anonymous namespace
// Copyright (C) 2013 Internet Systems Consortium, Inc. ("ISC")
// Copyright (C) 2013, 2015 Internet Systems Consortium, Inc. ("ISC")
//
// Permission to use, copy, modify, and/or distribute this software for any
// purpose with or without fee is hereby granted, provided that the above
......@@ -189,4 +189,41 @@ TEST_F(PktFilterLPFTest, DISABLED_receive) {
testRcvdMessage(rcvd_pkt);
}
// This test verifies that if the packet is received over the raw
// socket and its destination address doesn't match the address
// to which the socket is "bound", the packet is dropped.
TEST_F(PktFilterLPFTest, DISABLED_filterOutUnicast) {
// Packet will be received over loopback interface.
Iface iface(ifname_, ifindex_);
iface.flag_loopback_ = true;
IOAddress addr("127.0.0.1");
// Create an instance of the class which we are testing.
PktFilterLPF pkt_filter;
// Open socket. We don't check that the socket has appropriate
// options and family set because we have checked that in the
// openSocket test already.
sock_info_ = pkt_filter.openSocket(iface, addr, PORT, false, false);
ASSERT_GE(sock_info_.sockfd_, 0);
// The message sent to the local loopback interface will have an
// invalid (non-existing) destination address. The socket should
// drop this packet.
sendMessage(IOAddress("128.0.0.1"));
// Perform select on the socket to make sure that the packet has
// been dropped.
fd_set readfds;
FD_ZERO(&readfds);
FD_SET(sock_info_.sockfd_, &readfds);
struct timeval timeout;
timeout.tv_sec = 1;
timeout.tv_usec = 0;
int result = select(sock_info_.sockfd_ + 1, &readfds, NULL, NULL, &timeout);
ASSERT_LE(result, 0);
}
} // anonymous namespace
// Copyright (C) 2013-2014 Internet Systems Consortium, Inc. ("ISC")
// Copyright (C) 2013-2015 Internet Systems Consortium, Inc. ("ISC")
//
// Permission to use, copy, modify, and/or distribute this software for any
// purpose with or without fee is hereby granted, provided that the above
......@@ -91,7 +91,7 @@ PktFilterTest::loInit() {
}
void
PktFilterTest::sendMessage() {
PktFilterTest::sendMessage(const IOAddress& dest) {
// Packet will be sent over loopback interface.
Iface iface(ifname_, ifindex_);
......@@ -112,6 +112,7 @@ PktFilterTest::sendMessage() {
memset(&dest_addr4, 0, sizeof(sockaddr));
dest_addr4.sin_family = AF_INET;
dest_addr4.sin_port = htons(port_);
dest_addr4.sin_addr.s_addr = htonl(dest);
ASSERT_EQ(sendto(send_msg_sock_, test_message_->getBuffer().getData(),
test_message_->getBuffer().getLength(), 0,
reinterpret_cast<struct sockaddr*>(&dest_addr4),
......
// Copyright (C) 2013-2014 Internet Systems Consortium, Inc. ("ISC")
// Copyright (C) 2013-2015 Internet Systems Consortium, Inc. ("ISC")
//
// Permission to use, copy, modify, and/or distribute this software for any
// purpose with or without fee is hereby granted, provided that the above
......@@ -67,7 +67,10 @@ public:
/// is closed automatically in the destructor. If the function succeeds to
/// send a DHCPv4 message, the socket is closed so as the function can be
/// called again within the same test.
void sendMessage();
///
/// @param dest Destination address for the packet.
void sendMessage(const asiolink::IOAddress& dest =
asiolink::IOAddress("127.0.0.1"));
/// @brief Test that the datagram socket is opened correctly.
///
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment