| // |
| // 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 |