blob: d4fa1f025e4a3295207c49bad2153a9dd5343540 [file] [log] [blame]
//
// Copyright (C) 2016 The Android Open Source Project
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
#include "dhcp_client/socket_util.h"
#include <linux/filter.h>
#include <linux/if_packet.h>
#include <net/ethernet.h>
#include <net/if.h>
#include <net/if_arp.h>
#include <base/logging.h>
#include "dhcp_client/dhcp.h"
#include "shill/net/byte_string.h"
#include "shill/net/sockets.h"
namespace dhcp_client {
namespace {
// Socket filter for dhcp packet.
const sock_filter dhcp_bpf_filter[] = {
BPF_STMT(BPF_LD + BPF_B + BPF_ABS, 23 - ETH_HLEN),
BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, IPPROTO_UDP, 0, 6),
BPF_STMT(BPF_LD + BPF_H + BPF_ABS, 20 - ETH_HLEN),
BPF_JUMP(BPF_JMP + BPF_JSET + BPF_K, 0x1fff, 4, 0),
BPF_STMT(BPF_LDX + BPF_B + BPF_MSH, 14 - ETH_HLEN),
BPF_STMT(BPF_LD + BPF_H + BPF_IND, 16 - ETH_HLEN),
BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, DHCP::kDHCPClientPort, 0, 1),
BPF_STMT(BPF_RET + BPF_K, 0x0fffffff),
BPF_STMT(BPF_RET + BPF_K, 0),
};
const int dhcp_bpf_filter_len =
sizeof(dhcp_bpf_filter) / sizeof(dhcp_bpf_filter[0]);
} // namespace
SocketUtil::SocketUtil(const std::string& interface_name,
unsigned int interface_index)
: sockets_(new shill::Sockets()),
interface_name_(interface_name),
interface_index_(interface_index) {
}
SocketUtil::~SocketUtil() {}
bool SocketUtil::CreateUdpSocket(int* udp_socket) {
// Close previous Udp Socket.
if (*udp_socket != shill::Sockets::kInvalidFileDescriptor) {
sockets_->Close(*udp_socket);
*udp_socket = shill::Sockets::kInvalidFileDescriptor;
}
int fd = sockets_->Socket(PF_INET,
SOCK_DGRAM | SOCK_CLOEXEC | SOCK_NONBLOCK,
IPPROTO_IP);
if (fd == shill::Sockets::kInvalidFileDescriptor) {
PLOG(ERROR) << "Failed to create socket";
return false;
}
shill::ScopedSocketCloser socket_closer(sockets_, fd);
if (sockets_->ReuseAddress(fd) == -1) {
PLOG(ERROR) << "Failed to reuse socket address";
return false;
}
if (sockets_->BindToDevice(fd, interface_name_) < 0) {
PLOG(ERROR) << "Failed to bind socket to device";
return false;
}
struct sockaddr_in local;
memset(&local, 0, sizeof(local));
local.sin_family = PF_INET;
// We do not receive packet from this socket.
// At this time the ip address may not be setup by shill yet.
// It is a safe choice to use INADDR_ANY.
local.sin_addr.s_addr = htonl(INADDR_ANY);
local.sin_port = htons(DHCP::kDHCPServerPort);
if (sockets_->Bind(fd,
reinterpret_cast<struct sockaddr*>(&local),
sizeof(local)) < 0) {
PLOG(ERROR) << "Failed to bind to address";
return false;
}
*udp_socket = socket_closer.Release();
return true;
}
bool SocketUtil::CreateRawSocket(int* raw_socket) {
int fd = sockets_->Socket(PF_PACKET,
SOCK_DGRAM | SOCK_CLOEXEC | SOCK_NONBLOCK,
htons(ETHERTYPE_IP));
if (fd == shill::Sockets::kInvalidFileDescriptor) {
PLOG(ERROR) << "Failed to create socket";
return false;
}
shill::ScopedSocketCloser socket_closer(sockets_, fd);
// Apply the socket filter.
sock_fprog pf;
memset(&pf, 0, sizeof(pf));
pf.filter = const_cast<sock_filter*>(dhcp_bpf_filter);
pf.len = dhcp_bpf_filter_len;
if (sockets_->AttachFilter(fd, &pf) != 0) {
PLOG(ERROR) << "Failed to attach filter";
return false;
}
if (sockets_->ReuseAddress(fd) == -1) {
PLOG(ERROR) << "Failed to reuse socket address";
return false;
}
if (sockets_->BindToDevice(fd, interface_name_) < 0) {
PLOG(ERROR) << "Failed to bind socket to device";
return false;
}
struct sockaddr_ll local;
memset(&local, 0, sizeof(local));
local.sll_family = PF_PACKET;
local.sll_protocol = htons(ETHERTYPE_IP);
local.sll_ifindex = static_cast<int>(interface_index_);
if (sockets_->Bind(fd,
reinterpret_cast<struct sockaddr*>(&local),
sizeof(local)) < 0) {
PLOG(ERROR) << "Failed to bind to address";
return false;
}
*raw_socket = socket_closer.Release();
return true;
}
bool SocketUtil::SendBroadcastPacket(int raw_socket,
const shill::ByteString& packet) {
LOG(INFO) << __func__;
struct sockaddr_ll remote;
memset(&remote, 0, sizeof(remote));
remote.sll_family = AF_PACKET;
remote.sll_protocol = htons(ETHERTYPE_IP);
remote.sll_ifindex = interface_index_;
remote.sll_hatype = htons(ARPHRD_ETHER);
// Use broadcast hardware address.
remote.sll_halen = IFHWADDRLEN;
memset(remote.sll_addr, 0xff, IFHWADDRLEN);
size_t result = sockets_->SendTo(raw_socket,
packet.GetConstData(),
packet.GetLength(),
0,
reinterpret_cast<struct sockaddr *>(&remote),
sizeof(remote));
if (result != packet.GetLength()) {
PLOG(ERROR) << "Socket sento failed";
return false;
}
return true;
}
bool SocketUtil::SendUnicastPacket(int udp_socket,
const shill::ByteString& packet,
uint32_t server_ip) {
LOG(INFO) << __func__;
struct sockaddr_in remote;
memset(&remote, 0, sizeof(remote));
remote.sin_family = AF_INET;
remote.sin_port = htons(DHCP::kDHCPServerPort);
remote.sin_addr.s_addr = htonl(server_ip);
size_t result = sockets_->SendTo(udp_socket,
packet.GetConstData(),
packet.GetLength(),
0,
reinterpret_cast<struct sockaddr *>(&remote),
sizeof(remote));
if (result != packet.GetLength()) {
PLOG(ERROR) << "Socket sento failed";
return false;
}
return true;
}
} // namespace dhcp_client