Commit 71390391 authored by Marcin Siodelski's avatar Marcin Siodelski
Browse files

[3547] Updated BPF program to filter out the packets using dest IP address.

parent f8b8d39b
......@@ -56,18 +56,18 @@ const unsigned int BPF_LOCAL_LOOPBACK_HEADER_LEN = 4;
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").
BPF_STMT(BPF_LD + BPF_H + BPF_ABS, ETHERNET_PACKET_TYPE_OFFSET),
BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, ETHERTYPE_IP, 0, 8),
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),
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
......@@ -75,7 +75,22 @@ struct bpf_insn ethernet_ip_udp_filter [] = {
// the IP header, so the half-word at offset 20 (6 + size of
// Ethernet header) is loaded and an appropriate mask applied.
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),
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.
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.
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.
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
......@@ -91,7 +106,7 @@ struct bpf_insn ethernet_ip_udp_filter [] = {
// 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.
BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, DHCP4_SERVER_PORT, 0, 1),
......@@ -113,14 +128,14 @@ struct bpf_insn loopback_ip_udp_filter [] = {
// AF_INET. The default value used here (0xFFFFFFFF) must be overriden
// with htonl(AF_INET) from within the openSocket function.
BPF_STMT(BPF_LD + BPF_W + BPF_ABS, 0),
BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, 0xFFFFFFFF, 0, 8),
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.
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),
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
......@@ -129,7 +144,22 @@ struct bpf_insn loopback_ip_udp_filter [] = {
// pseudo header) is loaded and an appropriate mask applied.
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),
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.
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.
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.
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
......@@ -146,7 +176,7 @@ struct bpf_insn loopback_ip_udp_filter [] = {
// 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.
BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, DHCP4_SERVER_PORT, 0, 1),
......@@ -230,7 +260,7 @@ PktFilterBPF::openSocket(Iface& iface,
if ((ver.bv_major != BPF_MAJOR_VERSION) ||
(ver.bv_minor < BPF_MINOR_VERSION)) {
close(fallback);
close(sock);
close(sock);
isc_throw(SocketConfigError, "Invalid BPF version: "
<< ver.bv_major << "." << ver.bv_minor
<< " Expected at least version:"
......@@ -273,8 +303,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) {
......
......@@ -50,17 +50,18 @@ using namespace isc::dhcp;
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").
BPF_STMT(BPF_LD + BPF_H + BPF_ABS, ETHERNET_PACKET_TYPE_OFFSET),
BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, ETHERTYPE_IP, 0, 8),
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),
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, 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
......@@ -68,7 +69,22 @@ struct sock_filter dhcp_sock_filter [] = {
// the IP header, so the half-word at offset 20 (6 + size of
// Ethernet header) is loaded and an appropriate mask applied.
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),
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.
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.
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.
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
......@@ -82,9 +98,9 @@ struct sock_filter dhcp_sock_filter [] = {
// of the UDP destination port (2) within the UDP header.
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.
BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, DHCP4_SERVER_PORT, 0, 1),
......@@ -129,8 +145,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.
prog.bf_insns[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;
......
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