144 lines
3.8 KiB
C++
144 lines
3.8 KiB
C++
//
|
|
// Copyright (C) 2013 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 "shill/icmp.h"
|
|
|
|
#include <netinet/ip_icmp.h>
|
|
|
|
#include "shill/logging.h"
|
|
#include "shill/net/ip_address.h"
|
|
#include "shill/net/sockets.h"
|
|
|
|
namespace shill {
|
|
|
|
const int Icmp::kIcmpEchoCode = 0; // value specified in RFC 792.
|
|
|
|
Icmp::Icmp()
|
|
: sockets_(new Sockets()),
|
|
socket_(-1) {}
|
|
|
|
Icmp::~Icmp() {}
|
|
|
|
bool Icmp::Start() {
|
|
int socket = sockets_->Socket(AF_INET, SOCK_RAW, IPPROTO_ICMP);
|
|
if (socket == -1) {
|
|
PLOG(ERROR) << "Could not create ICMP socket";
|
|
Stop();
|
|
return false;
|
|
}
|
|
socket_ = socket;
|
|
socket_closer_.reset(new ScopedSocketCloser(sockets_.get(), socket_));
|
|
|
|
if (sockets_->SetNonBlocking(socket_) != 0) {
|
|
PLOG(ERROR) << "Could not set socket to be non-blocking";
|
|
Stop();
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
void Icmp::Stop() {
|
|
socket_closer_.reset();
|
|
socket_ = -1;
|
|
}
|
|
|
|
bool Icmp::IsStarted() const {
|
|
return socket_closer_.get();
|
|
}
|
|
|
|
bool Icmp::TransmitEchoRequest(const IPAddress& destination, uint16_t id,
|
|
uint16_t seq_num) {
|
|
if (!IsStarted() && !Start()) {
|
|
return false;
|
|
}
|
|
|
|
if (!destination.IsValid()) {
|
|
LOG(ERROR) << "Destination address is not valid.";
|
|
return false;
|
|
}
|
|
|
|
if (destination.family() != IPAddress::kFamilyIPv4) {
|
|
NOTIMPLEMENTED() << "Only IPv4 destination addresses are implemented.";
|
|
return false;
|
|
}
|
|
|
|
struct icmphdr icmp_header;
|
|
memset(&icmp_header, 0, sizeof(icmp_header));
|
|
icmp_header.type = ICMP_ECHO;
|
|
icmp_header.code = kIcmpEchoCode;
|
|
icmp_header.un.echo.id = id;
|
|
icmp_header.un.echo.sequence = seq_num;
|
|
icmp_header.checksum = ComputeIcmpChecksum(icmp_header, sizeof(icmp_header));
|
|
|
|
struct sockaddr_in destination_address;
|
|
destination_address.sin_family = AF_INET;
|
|
CHECK_EQ(sizeof(destination_address.sin_addr.s_addr),
|
|
destination.GetLength());
|
|
memcpy(&destination_address.sin_addr.s_addr,
|
|
destination.address().GetConstData(),
|
|
sizeof(destination_address.sin_addr.s_addr));
|
|
|
|
int result = sockets_->SendTo(
|
|
socket_,
|
|
&icmp_header,
|
|
sizeof(icmp_header),
|
|
0,
|
|
reinterpret_cast<struct sockaddr*>(&destination_address),
|
|
sizeof(destination_address));
|
|
int expected_result = sizeof(icmp_header);
|
|
if (result != expected_result) {
|
|
if (result < 0) {
|
|
PLOG(ERROR) << "Socket sendto failed";
|
|
} else if (result < expected_result) {
|
|
LOG(ERROR) << "Socket sendto returned "
|
|
<< result
|
|
<< " which is less than the expected result "
|
|
<< expected_result;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
// static
|
|
uint16_t Icmp::ComputeIcmpChecksum(const struct icmphdr& hdr, size_t len) {
|
|
// Compute Internet Checksum for "len" bytes beginning at location "hdr".
|
|
// Adapted directly from the canonical implementation in RFC 1071 Section 4.1.
|
|
uint32_t sum = 0;
|
|
const uint16_t* addr = reinterpret_cast<const uint16_t*>(&hdr);
|
|
|
|
while (len > 1) {
|
|
sum += *addr;
|
|
++addr;
|
|
len -= sizeof(*addr);
|
|
}
|
|
|
|
// Add left-over byte, if any.
|
|
if (len > 0) {
|
|
sum += *reinterpret_cast<const uint8_t*>(addr);
|
|
}
|
|
|
|
// Fold 32-bit sum to 16 bits.
|
|
while (sum >> 16) {
|
|
sum = (sum & 0xffff) + (sum >> 16);
|
|
}
|
|
|
|
return static_cast<uint16_t>(~sum);
|
|
}
|
|
|
|
} // namespace shill
|