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 
EapListener(EventDispatcher * event_dispatcher,int interface_index)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 
~EapListener()43 EapListener::~EapListener() {}
44 
Start()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 
Stop()61 void EapListener::Stop() {
62   receive_request_handler_.reset();
63   socket_closer_.reset();
64   socket_ = -1;
65 }
66 
67 
CreateSocket()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 
ReceiveRequest(int fd)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