// // 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 #include #include #include #include #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(&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(&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