1 /*
2     device.c -- raw socket
3     Copyright (C) 2002-2005 Ivo Timmermans,
4                   2002-2014 Guus Sliepen <guus@tinc-vpn.org>
5 
6     This program is free software; you can redistribute it and/or modify
7     it under the terms of the GNU General Public License as published by
8     the Free Software Foundation; either version 2 of the License, or
9     (at your option) any later version.
10 
11     This program is distributed in the hope that it will be useful,
12     but WITHOUT ANY WARRANTY; without even the implied warranty of
13     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14     GNU General Public License for more details.
15 
16     You should have received a copy of the GNU General Public License along
17     with this program; if not, write to the Free Software Foundation, Inc.,
18     51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
19 */
20 
21 #include "system.h"
22 
23 #ifdef HAVE_NETPACKET_PACKET_H
24 #include <netpacket/packet.h>
25 #endif
26 
27 #include "conf.h"
28 #include "device.h"
29 #include "net.h"
30 #include "logger.h"
31 #include "utils.h"
32 #include "route.h"
33 #include "xalloc.h"
34 
35 #if defined(PF_PACKET) && defined(ETH_P_ALL) && defined(AF_PACKET) && defined(SIOCGIFINDEX)
36 static const char *device_info = "raw_socket";
37 
38 static uint64_t device_total_in = 0;
39 static uint64_t device_total_out = 0;
40 
setup_device(void)41 static bool setup_device(void) {
42 	struct ifreq ifr;
43 	struct sockaddr_ll sa;
44 
45 	if(!get_config_string(lookup_config(config_tree, "Interface"), &iface)) {
46 		iface = xstrdup("eth0");
47 	}
48 
49 	if(!get_config_string(lookup_config(config_tree, "Device"), &device)) {
50 		device = xstrdup(iface);
51 	}
52 
53 	if((device_fd = socket(PF_PACKET, SOCK_RAW, htons(ETH_P_ALL))) < 0) {
54 		logger(LOG_ERR, "Could not open %s: %s", device_info,
55 		       strerror(errno));
56 		return false;
57 	}
58 
59 #ifdef FD_CLOEXEC
60 	fcntl(device_fd, F_SETFD, FD_CLOEXEC);
61 #endif
62 
63 	memset(&ifr, 0, sizeof(ifr));
64 	strncpy(ifr.ifr_ifrn.ifrn_name, iface, IFNAMSIZ);
65 	ifr.ifr_ifrn.ifrn_name[IFNAMSIZ - 1] = 0;
66 
67 	if(ioctl(device_fd, SIOCGIFINDEX, &ifr)) {
68 		close(device_fd);
69 		logger(LOG_ERR, "Can't find interface %s: %s", ifr.ifr_ifrn.ifrn_name, strerror(errno));
70 		return false;
71 	}
72 
73 	memset(&sa, 0, sizeof(sa));
74 	sa.sll_family = AF_PACKET;
75 	sa.sll_protocol = htons(ETH_P_ALL);
76 	sa.sll_ifindex = ifr.ifr_ifindex;
77 
78 	if(bind(device_fd, (struct sockaddr *) &sa, (socklen_t) sizeof(sa))) {
79 		logger(LOG_ERR, "Could not bind %s to %s: %s", device, ifr.ifr_ifrn.ifrn_name, strerror(errno));
80 		return false;
81 	}
82 
83 	logger(LOG_INFO, "%s is a %s", device, device_info);
84 
85 	return true;
86 }
87 
close_device(void)88 static void close_device(void) {
89 	close(device_fd);
90 
91 	free(device);
92 	free(iface);
93 }
94 
read_packet(vpn_packet_t * packet)95 static bool read_packet(vpn_packet_t *packet) {
96 	int lenin;
97 
98 	if((lenin = read(device_fd, packet->data, MTU)) <= 0) {
99 		logger(LOG_ERR, "Error while reading from %s %s: %s", device_info,
100 		       device, strerror(errno));
101 		return false;
102 	}
103 
104 	packet->len = lenin;
105 
106 	device_total_in += packet->len;
107 
108 	ifdebug(TRAFFIC) logger(LOG_DEBUG, "Read packet of %d bytes from %s", packet->len,
109 	                        device_info);
110 
111 	return true;
112 }
113 
write_packet(vpn_packet_t * packet)114 static bool write_packet(vpn_packet_t *packet) {
115 	ifdebug(TRAFFIC) logger(LOG_DEBUG, "Writing packet of %d bytes to %s",
116 	                        packet->len, device_info);
117 
118 	if(write(device_fd, packet->data, packet->len) < 0) {
119 		logger(LOG_ERR, "Can't write to %s %s: %s", device_info, device,
120 		       strerror(errno));
121 		return false;
122 	}
123 
124 	device_total_out += packet->len;
125 
126 	return true;
127 }
128 
dump_device_stats(void)129 static void dump_device_stats(void) {
130 	logger(LOG_DEBUG, "Statistics for %s %s:", device_info, device);
131 	logger(LOG_DEBUG, " total bytes in:  %10"PRIu64, device_total_in);
132 	logger(LOG_DEBUG, " total bytes out: %10"PRIu64, device_total_out);
133 }
134 
135 const devops_t raw_socket_devops = {
136 	.setup = setup_device,
137 	.close = close_device,
138 	.read = read_packet,
139 	.write = write_packet,
140 	.dump_stats = dump_device_stats,
141 };
142 
143 #else
144 
not_supported(void)145 static bool not_supported(void) {
146 	logger(LOG_ERR, "Raw socket device not supported on this platform");
147 	return false;
148 }
149 
150 const devops_t raw_socket_devops = {
151 	.setup = not_supported,
152 	.close = NULL,
153 	.read = NULL,
154 	.write = NULL,
155 	.dump_stats = NULL,
156 };
157 #endif
158