blob: 64bed4d325d43fbf2738fc9ffefed2f7c30bcc82 [file] [log] [blame]
//
// 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/eap_listener.h"
#include <linux/if_ether.h>
#include <linux/if_packet.h>
#include <netinet/in.h>
#include <base/bind.h>
#include <base/compiler_specific.h>
#include "shill/eap_protocol.h"
#include "shill/event_dispatcher.h"
#include "shill/logging.h"
#include "shill/net/sockets.h"
namespace shill {
const size_t EapListener::kMaxEapPacketLength =
sizeof(eap_protocol::Ieee8021xHdr) + sizeof(eap_protocol::EapHeader);
EapListener::EapListener(EventDispatcher* event_dispatcher,
int interface_index)
: dispatcher_(event_dispatcher),
interface_index_(interface_index),
sockets_(new Sockets()),
socket_(-1) {}
EapListener::~EapListener() {}
bool EapListener::Start() {
if (!CreateSocket()) {
LOG(ERROR) << "Could not open EAP listener socket.";
Stop();
return false;
}
receive_request_handler_.reset(
dispatcher_->CreateReadyHandler(
socket_,
IOHandler::kModeInput,
base::Bind(&EapListener::ReceiveRequest, base::Unretained(this))));
return true;
}
void EapListener::Stop() {
receive_request_handler_.reset();
socket_closer_.reset();
socket_ = -1;
}
bool EapListener::CreateSocket() {
int socket = sockets_->Socket(PF_PACKET, SOCK_DGRAM, htons(ETH_P_PAE));
if (socket == -1) {
PLOG(ERROR) << "Could not create EAP listener socket";
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";
return false;
}
sockaddr_ll socket_address;
memset(&socket_address, 0, sizeof(socket_address));
socket_address.sll_family = AF_PACKET;
socket_address.sll_protocol = htons(ETH_P_PAE);
socket_address.sll_ifindex = interface_index_;
if (sockets_->Bind(socket_,
reinterpret_cast<struct sockaddr*>(&socket_address),
sizeof(socket_address)) != 0) {
PLOG(ERROR) << "Could not bind socket to interface";
return false;
}
return true;
}
void EapListener::ReceiveRequest(int fd) {
struct ALIGNAS(1) {
eap_protocol::Ieee8021xHdr onex_header;
eap_protocol::EapHeader eap_header;
} payload;
sockaddr_ll remote_address;
memset(&remote_address, 0, sizeof(remote_address));
socklen_t socklen = sizeof(remote_address);
int result = sockets_->RecvFrom(
socket_, &payload, sizeof(payload), 0,
reinterpret_cast<struct sockaddr*>(&remote_address),
&socklen);
if (result < 0) {
PLOG(ERROR) << "Socket recvfrom failed";
Stop();
return;
}
if (result != sizeof(payload)) {
LOG(INFO) << "Short EAP packet received";
return;
}
if (payload.onex_header.version < eap_protocol::kIeee8021xEapolVersion1 ||
payload.onex_header.type != eap_protocol::kIIeee8021xTypeEapPacket ||
payload.eap_header.code != eap_protocol::kEapCodeRequest) {
LOG(INFO) << "Packet is not a valid EAP request";
return;
}
request_received_callback_.Run();
}
} // namespace shill