blob: f99b8ea4870fbb2e1499c89785d16dc4595180a5 [file] [log] [blame]
Paul Stewart1c9a5662013-02-12 09:41:33 -08001// Copyright (c) 2013 The Chromium OS Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "shill/shims/netfilter_queue_processor.h"
6
7#include <arpa/inet.h>
8#include <errno.h>
9#include <net/if.h>
10#include <netinet/in.h>
11#include <libnetfilter_queue/libnetfilter_queue.h>
12#include <linux/ip.h>
13#include <linux/netfilter.h> /* for NF_ACCEPT */
14#include <linux/types.h>
15#include <linux/udp.h>
16#include <string.h>
17#include <sys/ioctl.h>
18#include <unistd.h>
19
20#include <deque>
21
22#include <base/logging.h>
23
24using std::deque;
25
26namespace shill {
27
28namespace shims {
29
30// static
31int NetfilterQueueProcessor::kBufferSize = 4096;
32int NetfilterQueueProcessor::kExpirationIntervalSeconds = 5;
33int NetfilterQueueProcessor::kIPHeaderLengthUnitBytes = 4;
34int NetfilterQueueProcessor::kMaxIPHeaderLength = 16; // ihl is a 4-bit field.
35size_t NetfilterQueueProcessor::kMaxListenerEntries = 32;
36int NetfilterQueueProcessor::kPayloadCopySize = 0xffff;
37
38NetfilterQueueProcessor::Packet::Packet()
39 : packet_id_(0),
40 in_device_(0),
41 out_device_(0),
42 is_udp_(false),
43 source_ip_(INADDR_ANY),
44 destination_ip_(INADDR_ANY),
45 source_port_(0),
46 destination_port_(0) {}
47
48NetfilterQueueProcessor::Packet::~Packet() {}
49
50bool NetfilterQueueProcessor::Packet::ParseNetfilterData(
51 struct nfq_data *netfilter_data) {
52 struct nfqnl_msg_packet_hdr *packet_header =
53 nfq_get_msg_packet_hdr(netfilter_data);
54 if (!packet_header) {
55 return false;
56 }
57 packet_id_ = ntohl(packet_header->packet_id);
58 in_device_ = nfq_get_indev(netfilter_data);
59 out_device_ = nfq_get_outdev(netfilter_data);
60
61 unsigned char *payload;
62 int payload_len = nfq_get_payload(netfilter_data, &payload);
63 if (payload_len >= 0) {
64 is_udp_ = ParsePayloadUDPData(payload, payload_len);
65 }
66
67 return true;
68}
69
70bool NetfilterQueueProcessor::Packet::ParsePayloadUDPData(
71 const unsigned char *payload, size_t payload_len) {
72 struct iphdr ip;
73
74 if (payload_len <= sizeof(ip)) {
75 return false;
76 }
77
78 memcpy(&ip, payload, sizeof(ip));
79
80 size_t iphdr_len = ip.ihl * kIPHeaderLengthUnitBytes;
81 if (iphdr_len < sizeof(ip) ||
82 ip.version != IPVERSION ||
83 ip.protocol != IPPROTO_UDP) {
84 return false;
85 }
86
87 struct udphdr udp;
88 if (payload_len < iphdr_len + sizeof(udp)) {
89 return false;
90 }
91
92 memcpy(&udp, payload + iphdr_len, sizeof(udp));
93
94 source_ip_ = ntohl(ip.saddr);
95 destination_ip_ = ntohl(ip.daddr);
96 source_port_ = ntohs(udp.source);
97 destination_port_ = ntohs(udp.dest);
98
99 return true;
100}
101
102void NetfilterQueueProcessor::Packet::SetValues(int in_device,
103 int out_device,
104 bool is_udp,
105 uint32_t packet_id,
106 uint32_t source_ip,
107 uint32_t destination_ip,
108 uint16_t source_port,
109 uint16_t destination_port) {
110 in_device_ = in_device;
111 out_device_ = out_device;
112 is_udp_ = is_udp;
113 packet_id_ = packet_id;
114 source_ip_ = source_ip;
115 destination_ip_ = destination_ip;
116 source_port_ = source_port;
117 destination_port_ = destination_port;
118}
119
120NetfilterQueueProcessor::NetfilterQueueProcessor(
121 int input_queue, int output_queue)
122 : input_queue_(input_queue),
123 output_queue_(output_queue),
124 nfq_handle_(NULL),
125 input_queue_handle_(NULL),
126 output_queue_handle_(NULL) {
127 VLOG(2) << "Created netfilter queue processor.";
128}
129
130NetfilterQueueProcessor::~NetfilterQueueProcessor() {
131 Stop();
132}
133
134void NetfilterQueueProcessor::Run() {
135 LOG(INFO) << "Netfilter queue processor running.";
136 CHECK(nfq_handle_);
137
138 int file_handle = nfq_fd(nfq_handle_);
139 char buffer[kBufferSize] __attribute__ ((aligned));
140
141 for (;;) {
142 int receive_count = recv(file_handle, buffer, sizeof(buffer), 0);
143 if (receive_count <= 0) {
144 if (receive_count < 0 && errno == ENOBUFS) {
145 LOG(WARNING) << "Packets dropped in the queue.";
146 continue;
147 }
148 LOG(ERROR) << "Receive failed; exiting";
149 break;
150 }
151
152 nfq_handle_packet(nfq_handle_, buffer, receive_count);
153 }
154}
155
156bool NetfilterQueueProcessor::Start() {
157 VLOG(2) << "Netfilter queue processor starting.";
158 if (!nfq_handle_) {
159 nfq_handle_ = nfq_open();
160 if (!nfq_handle_) {
161 LOG(ERROR) << "nfq_open() returned an error";
162 return false;
163 }
164 }
165
166 if (nfq_unbind_pf(nfq_handle_, AF_INET) < 0) {
167 LOG(ERROR) << "nfq_unbind_pf() returned an error";
168 return false;
169 }
170
171 if (nfq_bind_pf(nfq_handle_, AF_INET) < 0) {
172 LOG(ERROR) << "nfq_bind_pf() returned an error";
173 return false;
174 }
175
176 input_queue_handle_ = nfq_create_queue(
177 nfq_handle_, input_queue_,
178 &NetfilterQueueProcessor::InputQueueCallback, this);
179 if (!input_queue_handle_) {
180 LOG(ERROR) << "nfq_create_queue() failed for input queue" << input_queue_;
181 return false;
182 }
183
184 if (nfq_set_mode(input_queue_handle_, NFQNL_COPY_PACKET,
185 kPayloadCopySize) < 0) {
186 LOG(ERROR) << "nfq_set_mode() failed: can't set input queue packet_copy.";
187 return false;
188 }
189
190 output_queue_handle_ = nfq_create_queue(
191 nfq_handle_, output_queue_,
192 &NetfilterQueueProcessor::OutputQueueCallback, this);
193 if (!output_queue_handle_) {
194 LOG(ERROR) << "nfq_create_queue() failed for output queue" << output_queue_;
195 return false;
196 }
197
198 if (nfq_set_mode(output_queue_handle_, NFQNL_COPY_PACKET,
199 kPayloadCopySize) < 0) {
200 LOG(ERROR) << "nfq_set_mode() failed: can't set output queue packet_copy.";
201 return false;
202 }
203
204 return true;
205}
206
207void NetfilterQueueProcessor::Stop() {
208 if (input_queue_handle_) {
209 nfq_destroy_queue(input_queue_handle_);
210 input_queue_handle_ = NULL;
211 }
212
213 if (output_queue_handle_) {
214 nfq_destroy_queue(output_queue_handle_);
215 output_queue_handle_ = NULL;
216 }
217
218 if (nfq_handle_) {
219 nfq_close(nfq_handle_);
220 nfq_handle_ = NULL;
221 }
222}
223
224// static
225int NetfilterQueueProcessor::InputQueueCallback(
226 struct nfq_q_handle *queue_handle,
227 struct nfgenmsg *generic_message,
228 struct nfq_data *netfilter_data,
229 void *private_data) {
230 Packet packet;
231 if (!packet.ParseNetfilterData(netfilter_data)) {
232 LOG(FATAL) << "Unable to parse netfilter data.";
233 }
234
235 NetfilterQueueProcessor *processor =
236 reinterpret_cast<NetfilterQueueProcessor *>(private_data);
237 uint32_t verdict;
238 time_t now = time(NULL);
239 if (processor->IsIncomingPacketAllowed(packet, now)) {
240 verdict = NF_ACCEPT;
241 } else {
242 verdict = NF_DROP;
243 }
244 return nfq_set_verdict(queue_handle, packet.packet_id(), verdict, 0, NULL);
245}
246
247// static
248int NetfilterQueueProcessor::OutputQueueCallback(
249 struct nfq_q_handle *queue_handle,
250 struct nfgenmsg *generic_message,
251 struct nfq_data *netfilter_data,
252 void *private_data) {
253 Packet packet;
254 if (!packet.ParseNetfilterData(netfilter_data)) {
255 LOG(FATAL) << "Unable to get parse netfilter data.";
256 }
257
258 NetfilterQueueProcessor *processor =
259 reinterpret_cast<NetfilterQueueProcessor *>(private_data);
260 time_t now = time(NULL);
261 processor->LogOutgoingPacket(packet, now);
262 return nfq_set_verdict(queue_handle, packet.packet_id(), NF_ACCEPT, 0, NULL);
263}
264
265// static
266uint32_t NetfilterQueueProcessor::GetNetmaskForDevice(int device_index) {
267 char ifname[IFNAMSIZ];
268 if (if_indextoname(device_index, ifname) != ifname) {
269 return INADDR_NONE;
270 }
271
272 int socket_fd = socket(AF_INET, SOCK_DGRAM, 0);
273 if (socket_fd < 0) {
274 return INADDR_NONE;
275 }
276
277 struct ifreq ifr;
278 memset(&ifr, 0, sizeof(ifr));
279 strcpy(ifr.ifr_name, ifname);
280 if (ioctl(socket_fd, SIOCGIFNETMASK, &ifr) != 0) {
281 return INADDR_NONE;
282 }
283
284 struct sockaddr_in *netmask_addr =
285 reinterpret_cast<struct sockaddr_in *>(&ifr.ifr_netmask);
286 return ntohl(netmask_addr->sin_addr.s_addr);
287}
288
289void NetfilterQueueProcessor::ExpireListeners(time_t now) {
290 time_t expiration_threshold = now - kExpirationIntervalSeconds;
291 VLOG(2) << __func__ << " entered.";
292 while (!listeners_.empty()) {
293 const ListenerEntryPtr &last_listener = listeners_.back();
294 if (last_listener->last_transmission >= expiration_threshold &&
295 listeners_.size() <= kMaxListenerEntries) {
296 break;
297 }
298 listeners_.pop_back();
299 }
300}
301
302deque<NetfilterQueueProcessor::ListenerEntryPtr>::iterator
303 NetfilterQueueProcessor::FindListener(uint16_t port,
304 int device_index,
305 uint32_t address) {
306 deque<ListenerEntryPtr>::iterator it;
307 for (it = listeners_.begin(); it != listeners_.end(); ++it) {
308 if ((*it)->port == port &&
309 (*it)->device_index == device_index &&
310 (*it)->address == address) {
311 break;
312 }
313 }
314 return it;
315}
316
317bool NetfilterQueueProcessor::IsIncomingPacketAllowed(
318 const Packet &packet, time_t now) {
319 VLOG(2) << __func__ << " entered.";
320 if (!packet.is_udp()) {
321 VLOG(2) << "Incoming packet is not udp.";
322 return false;
323 }
324
325 if (IN_MULTICAST(packet.destination_ip())) {
326 VLOG(2) << "Incoming packet is multicast.";
327 return false;
328 }
329
330 ExpireListeners(now);
331
332 uint16_t port = packet.destination_port();
333 uint32_t address = packet.destination_ip();
334 int device_index = packet.in_device();
335
336 std::deque<ListenerEntryPtr>::iterator entry_ptr =
337 FindListener(port, device_index, address);
338 if (entry_ptr == listeners_.end()) {
339 VLOG(2) << "Incoming does not match any listener.";
340 return false;
341 }
342
343 uint32_t netmask = (*entry_ptr)->netmask;
344 if ((packet.source_ip() & netmask) != (address & netmask)) {
345 VLOG(2) << "Incoming packet is from a non-local address.";
346 return false;
347 }
348
349 VLOG(3) << "Accepting packet.";
350 return true;
351}
352
353void NetfilterQueueProcessor::LogOutgoingPacket(
354 const Packet &packet, time_t now) {
355 VLOG(2) << __func__ << " entered.";
356 if (!packet.is_udp()) {
357 VLOG(2) << "Outgoing packet is not udp.";
358 return;
359 }
360 if (!IN_MULTICAST(packet.destination_ip())) {
361 VLOG(2) << "Outgoing packet is not multicast.";
362 return;
363 }
364 int device_index = packet.out_device();
365 if (device_index == 0) {
366 VLOG(2) << "Outgoing packet is not assgned a vald device.";
367 return;
368 }
369 uint16_t port = packet.source_port();
370 uint32_t address = packet.source_ip();
371 deque<ListenerEntryPtr>::iterator entry_it =
372 FindListener(port, device_index, address);
373 if (entry_it != listeners_.end()) {
374 if (entry_it != listeners_.begin()) {
375 // Make this the newest entry.
376 ListenerEntryPtr entry_ptr = *entry_it;
377 listeners_.erase(entry_it);
378 listeners_.push_front(entry_ptr);
379 entry_it = listeners_.begin();
380 }
381 (*entry_it)->last_transmission = now;
382 } else {
383 uint32_t netmask = GetNetmaskForDevice(device_index);
384 ListenerEntryPtr entry_ptr(
385 new ListenerEntry(now, port, device_index, address, netmask));
386 listeners_.push_front(entry_ptr);
387 }
388
389 // Perform expiration at the end, so that we don't end up expiring something
390 // just to resurrect it again.
391 ExpireListeners(now);
392}
393
394} // namespace shims
395
396} // namespace shill
397