Home | History | Annotate | Download | only in shill
      1 //
      2 // Copyright (C) 2013 The Android Open Source Project
      3 //
      4 // Licensed under the Apache License, Version 2.0 (the "License");
      5 // you may not use this file except in compliance with the License.
      6 // You may obtain a copy of the License at
      7 //
      8 //      http://www.apache.org/licenses/LICENSE-2.0
      9 //
     10 // Unless required by applicable law or agreed to in writing, software
     11 // distributed under the License is distributed on an "AS IS" BASIS,
     12 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13 // See the License for the specific language governing permissions and
     14 // limitations under the License.
     15 //
     16 
     17 #include "shill/eap_listener.h"
     18 
     19 #include <linux/if_ether.h>
     20 #include <linux/if_packet.h>
     21 #include <netinet/in.h>
     22 
     23 #include <base/bind.h>
     24 #include <base/compiler_specific.h>
     25 
     26 #include "shill/eap_protocol.h"
     27 #include "shill/event_dispatcher.h"
     28 #include "shill/logging.h"
     29 #include "shill/net/sockets.h"
     30 
     31 namespace shill {
     32 
     33 const size_t EapListener::kMaxEapPacketLength =
     34     sizeof(eap_protocol::Ieee8021xHdr) + sizeof(eap_protocol::EapHeader);
     35 
     36 EapListener::EapListener(EventDispatcher* event_dispatcher,
     37                          int interface_index)
     38     : dispatcher_(event_dispatcher),
     39       interface_index_(interface_index),
     40       sockets_(new Sockets()),
     41       socket_(-1) {}
     42 
     43 EapListener::~EapListener() {}
     44 
     45 bool EapListener::Start() {
     46   if (!CreateSocket()) {
     47     LOG(ERROR) << "Could not open EAP listener socket.";
     48     Stop();
     49     return false;
     50   }
     51 
     52   receive_request_handler_.reset(
     53     dispatcher_->CreateReadyHandler(
     54         socket_,
     55         IOHandler::kModeInput,
     56         base::Bind(&EapListener::ReceiveRequest, base::Unretained(this))));
     57 
     58   return true;
     59 }
     60 
     61 void EapListener::Stop() {
     62   receive_request_handler_.reset();
     63   socket_closer_.reset();
     64   socket_ = -1;
     65 }
     66 
     67 
     68 bool EapListener::CreateSocket() {
     69   int socket = sockets_->Socket(PF_PACKET, SOCK_DGRAM, htons(ETH_P_PAE));
     70   if (socket == -1) {
     71     PLOG(ERROR) << "Could not create EAP listener socket";
     72     return false;
     73   }
     74   socket_ = socket;
     75   socket_closer_.reset(new ScopedSocketCloser(sockets_.get(), socket_));
     76 
     77   if (sockets_->SetNonBlocking(socket_) != 0) {
     78     PLOG(ERROR) << "Could not set socket to be non-blocking";
     79     return false;
     80   }
     81 
     82   sockaddr_ll socket_address;
     83   memset(&socket_address, 0, sizeof(socket_address));
     84   socket_address.sll_family = AF_PACKET;
     85   socket_address.sll_protocol = htons(ETH_P_PAE);
     86   socket_address.sll_ifindex = interface_index_;
     87 
     88   if (sockets_->Bind(socket_,
     89                      reinterpret_cast<struct sockaddr*>(&socket_address),
     90                      sizeof(socket_address)) != 0) {
     91     PLOG(ERROR) << "Could not bind socket to interface";
     92     return false;
     93   }
     94 
     95   return true;
     96 }
     97 
     98 void EapListener::ReceiveRequest(int fd) {
     99   struct ALIGNAS(1) {
    100     eap_protocol::Ieee8021xHdr onex_header;
    101     eap_protocol::EapHeader eap_header;
    102   } payload;
    103   sockaddr_ll remote_address;
    104   memset(&remote_address, 0, sizeof(remote_address));
    105   socklen_t socklen = sizeof(remote_address);
    106   int result = sockets_->RecvFrom(
    107       socket_, &payload, sizeof(payload), 0,
    108       reinterpret_cast<struct sockaddr*>(&remote_address),
    109       &socklen);
    110   if (result < 0) {
    111     PLOG(ERROR) << "Socket recvfrom failed";
    112     Stop();
    113     return;
    114   }
    115 
    116   if (result != sizeof(payload)) {
    117     LOG(INFO) << "Short EAP packet received";
    118     return;
    119   }
    120 
    121   if (payload.onex_header.version < eap_protocol::kIeee8021xEapolVersion1 ||
    122       payload.onex_header.type != eap_protocol::kIIeee8021xTypeEapPacket ||
    123       payload.eap_header.code != eap_protocol::kEapCodeRequest) {
    124     LOG(INFO) << "Packet is not a valid EAP request";
    125     return;
    126   }
    127 
    128   request_received_callback_.Run();
    129 }
    130 
    131 }  // namespace shill
    132