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