Moved data structures to new collections subfolder
[strongswan.git] / src / dumm / iface.c
1 /*
2 * Copyright (C) 2008 Tobias Brunner
3 * Copyright (C) 2007 Martin Willi
4 * Hochschule fuer Technik Rapperswil
5 * Copyright (C) 2002 Jeff Dike
6 *
7 * Based on the "tunctl" utility from Jeff Dike.
8 *
9 * This program is free software; you can redistribute it and/or modify it
10 * under the terms of the GNU General Public License as published by the
11 * Free Software Foundation; either version 2 of the License, or (at your
12 * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
13 *
14 * This program is distributed in the hope that it will be useful, but
15 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
16 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
17 * for more details.
18 */
19
20 #include <sys/types.h>
21 #include <fcntl.h>
22 #include <unistd.h>
23 #include <stdio.h>
24 #include <net/if.h>
25 #include <sys/ioctl.h>
26 #include <linux/if_tun.h>
27
28 #include <debug.h>
29 #include <collections/linked_list.h>
30
31 #include "iface.h"
32
33 typedef struct private_iface_t private_iface_t;
34
35 struct private_iface_t {
36 /** public interface */
37 iface_t public;
38 /** device name in guest (eth0) */
39 char *guestif;
40 /** device name at host (tap0) */
41 char *hostif;
42 /** bridge this interface is attached to */
43 bridge_t *bridge;
44 /** guest this interface is attached to */
45 guest_t *guest;
46 /** mconsole for guest */
47 mconsole_t *mconsole;
48 };
49
50 /**
51 * bring an interface up or down (host side)
52 */
53 bool iface_control(char *name, bool up)
54 {
55 int s;
56 bool good = FALSE;
57 struct ifreq ifr;
58
59 memset(&ifr, 0, sizeof(struct ifreq));
60 strncpy(ifr.ifr_name, name, sizeof(ifr.ifr_name));
61
62 s = socket(AF_INET, SOCK_DGRAM, 0);
63 if (!s)
64 {
65 return FALSE;
66 }
67 if (ioctl(s, SIOCGIFFLAGS, &ifr) == 0)
68 {
69 if (up)
70 {
71 ifr.ifr_flags |= IFF_UP;
72 }
73 else
74 {
75 ifr.ifr_flags &= ~IFF_UP;
76 }
77 if (ioctl(s, SIOCSIFFLAGS, &ifr) == 0)
78 {
79 good = TRUE;
80 }
81 }
82 close(s);
83 return good;
84 }
85
86 METHOD(iface_t, get_guestif, char*,
87 private_iface_t *this)
88 {
89 return this->guestif;
90 }
91
92 METHOD(iface_t, get_hostif, char*,
93 private_iface_t *this)
94 {
95 return this->hostif;
96 }
97
98 METHOD(iface_t, add_address, bool,
99 private_iface_t *this, host_t *addr, int bits)
100 {
101 return (this->guest->exec(this->guest, NULL, NULL,
102 "exec ip addr add %H/%d dev %s", addr, bits, this->guestif) == 0);
103 }
104
105 /**
106 * compile a list of the addresses of an interface
107 */
108 static void compile_address_list(linked_list_t *list, char *address)
109 {
110 host_t *host = host_create_from_string(address, 0);
111 if (host)
112 {
113 list->insert_last(list, host);
114 }
115 }
116
117 /**
118 * delete the list of addresses
119 */
120 static void destroy_address_list(linked_list_t *list)
121 {
122 list->destroy_offset(list, offsetof(host_t, destroy));
123 }
124
125 METHOD(iface_t, create_address_enumerator, enumerator_t*,
126 private_iface_t *this)
127 {
128 linked_list_t *addresses = linked_list_create();
129 this->guest->exec_str(this->guest, (void(*)(void*,char*))compile_address_list,
130 TRUE, addresses,
131 "exec ip addr list dev %s scope global | "
132 "grep '^ \\+\\(inet6\\? \\)' | "
133 "awk -F '( +|/)' '{ print $3 }'", this->guestif);
134 return enumerator_create_cleaner(addresses->create_enumerator(addresses),
135 (void(*)(void*))destroy_address_list, addresses);
136 }
137
138 METHOD(iface_t, delete_address, bool,
139 private_iface_t *this, host_t *addr, int bits)
140 {
141 return (this->guest->exec(this->guest, NULL, NULL,
142 "exec ip addr del %H/%d dev %s", addr, bits, this->guestif) == 0);
143 }
144
145 METHOD(iface_t, set_bridge, void,
146 private_iface_t *this, bridge_t *bridge)
147 {
148 if (this->bridge == NULL && bridge)
149 {
150 this->guest->exec(this->guest, NULL, NULL,
151 "exec ip link set %s up", this->guestif);
152 }
153 else if (this->bridge && bridge == NULL)
154 {
155 this->guest->exec(this->guest, NULL, NULL,
156 "exec ip link set %s down", this->guestif);
157 }
158 this->bridge = bridge;
159 }
160
161 METHOD(iface_t, get_bridge, bridge_t*,
162 private_iface_t *this)
163 {
164 return this->bridge;
165 }
166
167 METHOD(iface_t, get_guest, guest_t*,
168 private_iface_t *this)
169 {
170 return this->guest;
171 }
172
173 /**
174 * destroy the tap device
175 */
176 static bool destroy_tap(private_iface_t *this)
177 {
178 struct ifreq ifr;
179 int tap;
180
181 if (!iface_control(this->hostif, FALSE))
182 {
183 DBG1(DBG_LIB, "bringing iface down failed: %m");
184 }
185 memset(&ifr, 0, sizeof(ifr));
186 ifr.ifr_flags = IFF_TAP | IFF_NO_PI;
187 strncpy(ifr.ifr_name, this->hostif, sizeof(ifr.ifr_name) - 1);
188
189 tap = open(TAP_DEVICE, O_RDWR);
190 if (tap < 0)
191 {
192 DBG1(DBG_LIB, "unable to open tap device %s: %m", TAP_DEVICE);
193 return FALSE;
194 }
195 if (ioctl(tap, TUNSETIFF, &ifr) < 0 ||
196 ioctl(tap, TUNSETPERSIST, 0) < 0)
197 {
198 DBG1(DBG_LIB, "removing %s failed: %m", this->hostif);
199 close(tap);
200 return FALSE;
201 }
202 close(tap);
203 return TRUE;
204 }
205
206 /**
207 * create the tap device
208 */
209 static char* create_tap(private_iface_t *this)
210 {
211 struct ifreq ifr;
212 int tap;
213
214 memset(&ifr, 0, sizeof(ifr));
215 ifr.ifr_flags = IFF_TAP | IFF_NO_PI;
216 snprintf(ifr.ifr_name, sizeof(ifr.ifr_name), "%s-%s",
217 this->guest->get_name(this->guest), this->guestif);
218
219 tap = open(TAP_DEVICE, O_RDWR);
220 if (tap < 0)
221 {
222 DBG1(DBG_LIB, "unable to open tap device %s: %m", TAP_DEVICE);
223 return NULL;
224 }
225 if (ioctl(tap, TUNSETIFF, &ifr) < 0 ||
226 ioctl(tap, TUNSETPERSIST, 1) < 0 ||
227 ioctl(tap, TUNSETOWNER, 0))
228 {
229 DBG1(DBG_LIB, "creating new tap device failed: %m");
230 close(tap);
231 return NULL;
232 }
233 close(tap);
234 return strdup(ifr.ifr_name);
235 }
236
237 METHOD(iface_t, destroy, void,
238 private_iface_t *this)
239 {
240 if (this->bridge)
241 {
242 this->bridge->disconnect_iface(this->bridge, &this->public);
243 }
244 /* TODO: iface mgmt is not blocking yet, so wait some ticks */
245 usleep(50000);
246 this->mconsole->del_iface(this->mconsole, this->guestif);
247 destroy_tap(this);
248 free(this->guestif);
249 free(this->hostif);
250 free(this);
251 }
252
253 /**
254 * create the iface instance
255 */
256 iface_t *iface_create(char *name, guest_t *guest, mconsole_t *mconsole)
257 {
258 private_iface_t *this;
259
260 INIT(this,
261 .public = {
262 .get_hostif = _get_hostif,
263 .get_guestif = _get_guestif,
264 .add_address = _add_address,
265 .create_address_enumerator = _create_address_enumerator,
266 .delete_address = _delete_address,
267 .set_bridge = _set_bridge,
268 .get_bridge = _get_bridge,
269 .get_guest = _get_guest,
270 .destroy = _destroy,
271 },
272 .mconsole = mconsole,
273 .guestif = strdup(name),
274 .guest = guest,
275 );
276 this->hostif = create_tap(this);
277 if (this->hostif == NULL)
278 {
279 destroy_tap(this);
280 free(this->guestif);
281 free(this);
282 return NULL;
283 }
284 if (!this->mconsole->add_iface(this->mconsole, this->guestif, this->hostif))
285 {
286 DBG1(DBG_LIB, "creating interface '%s' in guest failed", this->guestif);
287 destroy_tap(this);
288 free(this->guestif);
289 free(this->hostif);
290 free(this);
291 return NULL;
292 }
293 if (!iface_control(this->hostif, TRUE))
294 {
295 DBG1(DBG_LIB, "bringing iface '%s' up failed: %m", this->hostif);
296 }
297 return &this->public;
298 }
299