make starter behave more gracefully in the presence of non-fatal errors
[strongswan.git] / src / starter / interfaces.c
1 /* strongSwan IPsec interfaces management
2 * Copyright (C) 2001-2002 Mathieu Lafon - Arkoon Network Security
3 *
4 * This program is free software; you can redistribute it and/or modify it
5 * under the terms of the GNU General Public License as published by the
6 * Free Software Foundation; either version 2 of the License, or (at your
7 * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
8 *
9 * This program is distributed in the hope that it will be useful, but
10 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
11 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
12 * for more details.
13 *
14 * RCSID $Id: interfaces.c,v 1.15 2006/02/05 10:51:55 as Exp $
15 */
16
17 #include <sys/socket.h>
18 #include <sys/ioctl.h>
19 #include <linux/if.h>
20 #include <stdlib.h>
21 #include <string.h>
22 #include <unistd.h>
23 #include <errno.h>
24
25 #include <freeswan.h>
26 #include <ipsec_tunnel.h>
27
28 #include <constants.h>
29 #include <defs.h>
30 #include <log.h>
31
32 #include "interfaces.h"
33 #include "exec.h"
34 #include "files.h"
35
36 #define MIN(a,b) ( ((a)>(b)) ? (b) : (a) )
37
38 #define N_IPSEC_IF 4
39
40 struct st_ipsec_if {
41 char name[IFNAMSIZ];
42 char phys[IFNAMSIZ];
43 int up;
44 };
45
46 static struct st_ipsec_if _ipsec_if[N_IPSEC_IF];
47
48 static char *
49 _find_physical_iface(int sock, char *iface)
50 {
51 static char _if[IFNAMSIZ];
52 char *b;
53 struct ifreq req;
54 FILE *fd;
55 char line[BUF_LEN];
56
57 strncpy(req.ifr_name, iface, IFNAMSIZ);
58 if (ioctl(sock, SIOCGIFFLAGS, &req)==0)
59 {
60 if (req.ifr_flags & IFF_UP)
61 {
62 strncpy(_if, iface, IFNAMSIZ);
63 return _if;
64 }
65 }
66 else
67 {
68 /* If there is a file named /var/run/dynip/<iface>, look if we
69 * can get interface name from there (IP_PHYS)
70 */
71 b = (char *)alloc_bytes(strlen(DYNIP_DIR) + strlen(iface) + 10, "iface");
72 if (b)
73 {
74 sprintf(b, "%s/%s", DYNIP_DIR, iface);
75 fd = fopen(b, "r");
76 pfree(b);
77 if (fd)
78 {
79 memset(_if, 0, sizeof(_if));
80 memset(line, 0, sizeof(line));
81 while (fgets(line, sizeof(line), fd) != 0)
82 {
83 if ((strncmp(line,"IP_PHYS=\"", 9) == 0)
84 && (line[strlen(line) - 2] == '"')
85 && (line[strlen(line) - 1] == '\n'))
86 {
87 strncpy(_if, line + 9, MIN(strlen(line) - 11, IFNAMSIZ));
88 break;
89 }
90 else if ((strncmp(line,"IP_PHYS=", 8) == 0)
91 && (line[8] != '"')
92 && (line[strlen(line) - 1] == '\n'))
93 {
94 strncpy(_if, line + 8, MIN(strlen(line) - 9, IFNAMSIZ));
95 break;
96 }
97 }
98 fclose(fd);
99
100 if (*_if)
101 {
102 strncpy(req.ifr_name, _if, IFNAMSIZ);
103 if (ioctl(sock, SIOCGIFFLAGS, &req) == 0)
104 {
105 if (req.ifr_flags & IFF_UP)
106 return _if;
107 }
108 }
109 }
110 }
111 }
112 return NULL;
113 }
114
115 int
116 starter_iface_find(char *iface, int af, ip_address *dst, ip_address *nh)
117 {
118 char *phys;
119 struct ifreq req;
120 struct sockaddr_in *sa = (struct sockaddr_in *)(&req.ifr_addr);
121 int sock;
122
123 if (!iface)
124 return -1;
125
126 sock = socket(af, SOCK_DGRAM, 0);
127 if (sock < 0)
128 return -1;
129
130 phys = _find_physical_iface(sock, iface);
131 if (!phys)
132 goto failed;
133
134 strncpy(req.ifr_name, phys, IFNAMSIZ);
135 if (ioctl(sock, SIOCGIFFLAGS, &req)!=0)
136 goto failed;
137 if (!(req.ifr_flags & IFF_UP))
138 goto failed;
139
140 if ((req.ifr_flags & IFF_POINTOPOINT)
141 && nh
142 && ioctl(sock, SIOCGIFDSTADDR, &req) == 0)
143 {
144 if (sa->sin_family == af)
145 initaddr((const void *)&sa->sin_addr, sizeof(struct in_addr), af, nh);
146 }
147 if ((dst) && (ioctl(sock, SIOCGIFADDR, &req) == 0))
148 {
149 if (sa->sin_family == af)
150 initaddr((const void *)&sa->sin_addr, sizeof(struct in_addr), af, dst);
151 }
152 close(sock);
153 return 0;
154
155 failed:
156 close(sock);
157 return -1;
158 }
159
160 static int
161 valid_str(char *str, unsigned int *pn, char **pphys
162 , defaultroute_t *defaultroute)
163 {
164 if (streq(str, "%defaultroute"))
165 {
166 if (!defaultroute->defined)
167 {
168 return 0;
169 }
170 *pn = 0;
171 *pphys = defaultroute->iface;
172 }
173 else
174 {
175 if (strlen(str) < 8
176 || str[0] != 'i' || str[1] != 'p' || str[2] !='s' || str[3] != 'e'
177 || str[4] != 'c' || str[5] < '0' || str[5] > '9' || str[6] != '=')
178 {
179 return 0;
180 }
181 *pn = str[5] - '0';
182 *pphys = &(str[7]);
183 }
184 return 1;
185 }
186
187 static int
188 _iface_up (int sock, struct st_ipsec_if *iface, char *phys
189 , unsigned int mtu, bool nat_t)
190 {
191 struct ifreq req;
192 struct ipsectunnelconf *shc=(struct ipsectunnelconf *)&req.ifr_data;
193 short phys_flags;
194 int ret = 0;
195 /* sscholz@astaro.com: for network mask 32 bit
196 struct sockaddr_in *inp;
197 */
198
199 strncpy(req.ifr_name, phys, IFNAMSIZ);
200 if (ioctl(sock, SIOCGIFFLAGS, &req) !=0 )
201 return ret;
202 phys_flags = req.ifr_flags;
203
204 strncpy(req.ifr_name, iface->name, IFNAMSIZ);
205 if (ioctl(sock, SIOCGIFFLAGS, &req) != 0)
206 return ret;
207
208 if ((!(req.ifr_flags & IFF_UP)) || (!iface->up))
209 {
210 DBG(DBG_CONTROL,
211 DBG_log("attaching interface %s to %s", iface->name, phys)
212 )
213 ret = 1;
214 }
215
216 if ((*iface->phys) && (strcmp(iface->phys, phys) != 0 ))
217 {
218 /* tncfg --detach if phys has changed */
219 strncpy(req.ifr_name, iface->name, IFNAMSIZ);
220 ioctl(sock, IPSEC_DEL_DEV, &req);
221 ret = 1;
222 }
223
224 /* tncfg --attach */
225 strncpy(req.ifr_name, iface->name, IFNAMSIZ);
226 strncpy(shc->cf_name, phys, sizeof(shc->cf_name));
227 ioctl(sock, IPSEC_SET_DEV, &req);
228
229 /* set ipsec addr = phys addr */
230 strncpy(req.ifr_name, phys, IFNAMSIZ);
231 if (ioctl(sock, SIOCGIFADDR, &req) == 0)
232 {
233 strncpy(req.ifr_name, iface->name, IFNAMSIZ);
234 ioctl(sock, SIOCSIFADDR, &req);
235 }
236
237 /* set ipsec mask = phys mask */
238 strncpy(req.ifr_name, phys, IFNAMSIZ);
239 if (ioctl(sock, SIOCGIFNETMASK, &req) == 0)
240 {
241 strncpy(req.ifr_name, iface->name, IFNAMSIZ);
242 /* sscholz@astaro.com: changed netmask to 32 bit
243 * in order to prevent network routes from being created
244
245 inp = (struct sockaddr_in *)&req.ifr_addr;
246 inp->sin_addr.s_addr = 0xFFFFFFFFL;
247
248 */
249 ioctl(sock, SIOCSIFNETMASK, &req);
250 }
251
252 /* set other flags & addr */
253 strncpy(req.ifr_name, iface->name, IFNAMSIZ);
254 if (ioctl(sock, SIOCGIFFLAGS, &req)==0)
255 {
256 /* removed by sscholz@astaro.com (caused trouble with DSL/ppp0) */
257 /* if (phys_flags & IFF_POINTOPOINT)
258 {
259 req.ifr_flags |= IFF_POINTOPOINT;
260 req.ifr_flags &= ~IFF_BROADCAST;
261 ioctl(sock, SIOCSIFFLAGS, &req);
262 strncpy(req.ifr_name, phys, IFNAMSIZ);
263 if (ioctl(sock, SIOCGIFDSTADDR, &req) == 0)
264 {
265 strncpy(req.ifr_name, iface->name, IFNAMSIZ);
266 ioctl(sock, SIOCSIFDSTADDR, &req);
267 }
268 }
269 else
270 */
271 if (phys_flags & IFF_BROADCAST)
272 {
273 req.ifr_flags &= ~IFF_POINTOPOINT;
274 req.ifr_flags |= IFF_BROADCAST;
275 ioctl(sock, SIOCSIFFLAGS, &req);
276 strncpy(req.ifr_name, phys, IFNAMSIZ);
277 if (ioctl(sock, SIOCGIFBRDADDR, &req) == 0)
278 {
279 strncpy(req.ifr_name, iface->name, IFNAMSIZ);
280 ioctl(sock, SIOCSIFBRDADDR, &req);
281 }
282 }
283 else
284 {
285 req.ifr_flags &= ~IFF_POINTOPOINT;
286 req.ifr_flags &= ~IFF_BROADCAST;
287 ioctl(sock, SIOCSIFFLAGS, &req);
288 }
289 }
290
291 /*
292 * guess MTU = phys interface MTU - ESP Overhead
293 *
294 * ESP overhead : 10+16+7+2+12=57 -> 60 by security
295 * NAT-T overhead : 20
296 */
297 if (mtu == 0)
298 {
299 strncpy(req.ifr_name, phys, IFNAMSIZ);
300 ioctl(sock, SIOCGIFMTU, &req);
301 mtu = req.ifr_mtu - 60;
302 if (nat_t)
303 mtu -= 20;
304 }
305 /* set MTU */
306 if (mtu)
307 {
308 strncpy(req.ifr_name, iface->name, IFNAMSIZ);
309 req.ifr_mtu = mtu;
310 ioctl(sock, SIOCSIFMTU, &req);
311 }
312
313 /* ipsec interface UP */
314 strncpy(req.ifr_name, iface->name, IFNAMSIZ);
315 if (ioctl(sock, SIOCGIFFLAGS, &req) == 0)
316 {
317 req.ifr_flags |= IFF_UP;
318 ioctl(sock, SIOCSIFFLAGS, &req);
319 }
320
321 iface->up = 1;
322 strncpy(iface->phys, phys, IFNAMSIZ);
323 return ret;
324 }
325
326 static int
327 _iface_down(int sock, struct st_ipsec_if *iface)
328 {
329 struct ifreq req;
330 int ret = 0;
331
332 iface->up = 0;
333
334 strncpy(req.ifr_name, iface->name, IFNAMSIZ);
335 if (ioctl(sock, SIOCGIFFLAGS, &req)!=0)
336 return ret;
337
338 if (req.ifr_flags & IFF_UP)
339 {
340 DBG(DBG_CONTROL,
341 DBG_log("shutting down interface %s/%s", iface->name, iface->phys)
342 )
343 req.ifr_flags &= ~IFF_UP;
344 ioctl(sock, SIOCSIFFLAGS, &req);
345 ret = 1;
346 }
347
348 /* unset addr */
349 memset(&req.ifr_addr, 0, sizeof(req.ifr_addr));
350 req.ifr_addr.sa_family = AF_INET;
351 ioctl(sock, SIOCSIFADDR, &req);
352
353 /* tncfg --detach */
354 ioctl(sock, IPSEC_DEL_DEV, &req);
355
356 memset(iface->phys, 0, sizeof(iface->phys));
357
358 return ret;
359 }
360
361 void
362 starter_ifaces_init(void)
363 {
364 int i;
365
366 memset(_ipsec_if, 0, sizeof(_ipsec_if));
367 for (i = 0; i < N_IPSEC_IF; i++)
368 snprintf(_ipsec_if[i].name, IFNAMSIZ, "ipsec%d", i);
369 }
370
371 void
372 starter_ifaces_clear (void)
373 {
374 int sock;
375 unsigned int i;
376
377 sock = socket(AF_INET, SOCK_DGRAM, 0);
378 if (sock < 0)
379 return;
380
381 for (i = 0; i < N_IPSEC_IF; i++)
382 _iface_down (sock, &(_ipsec_if[i]));
383 }
384
385 int
386 starter_ifaces_load(char **ifaces, unsigned int omtu, bool nat_t
387 , defaultroute_t *defaultroute)
388 {
389 char *tmp_phys, *phys;
390 int n;
391 char **i;
392 int sock;
393 int j, found;
394 int ret = 0;
395 struct ifreq physreq, ipsecreq; // re-attach interface
396 struct sockaddr_in *inp1, *inp2; // re-attach interface
397
398 DBG(DBG_CONTROL,
399 DBG_log("starter_ifaces_load()")
400 )
401
402 sock = socket(AF_INET, SOCK_DGRAM, 0);
403 if (sock < 0)
404 return -1;
405
406 for (j = 0; j < N_IPSEC_IF; j++)
407 {
408 found = 0;
409
410 for (i = ifaces; i && *i; i++)
411 {
412 if (valid_str(*i, &n, &tmp_phys, defaultroute)
413 && tmp_phys
414 && n >= 0
415 && n < N_IPSEC_IF)
416 {
417 if (n==j)
418 {
419 if (found)
420 {
421 plog( "ignoring duplicate entry for interface ipsec%d", j);
422 }
423 else
424 {
425 found++;
426 phys = _find_physical_iface(sock, tmp_phys);
427
428 /* Re-attach ipsec interface if IP address changes
429 * sscholz@astaro.com
430 */
431 if (phys)
432 {
433 memset ((void*)&physreq, 0, sizeof(physreq));
434 memset ((void*)&ipsecreq, 0, sizeof(ipsecreq));
435 strncpy(physreq.ifr_name, phys, IFNAMSIZ);
436 sprintf(ipsecreq.ifr_name, "ipsec%d", j);
437 ioctl(sock, SIOCGIFADDR, &physreq);
438 ioctl(sock, SIOCGIFADDR, &ipsecreq);
439 inp1 = (struct sockaddr_in *)&physreq.ifr_addr;
440 inp2 = (struct sockaddr_in *)&ipsecreq.ifr_addr;
441 if (inp1->sin_addr.s_addr != inp2->sin_addr.s_addr)
442 {
443 plog("IP address of physical interface changed "
444 "-> reinit of ipsec interface");
445 _iface_down (sock, &(_ipsec_if[n]));
446 }
447 ret += _iface_up (sock, &(_ipsec_if[n]), phys, omtu, nat_t);
448 }
449 else
450 {
451 ret += _iface_down (sock, &(_ipsec_if[n]));
452 }
453 }
454 }
455 }
456 else if (j == 0)
457 {
458 /* Only log in the first loop */
459 plog("ignoring invalid interface '%s'", *i);
460 }
461 }
462 if (!found)
463 ret += _iface_down (sock, &(_ipsec_if[j]));
464 }
465
466 close(sock);
467 return ret; /* = number of changes - 'whack --listen' if > 0 */
468 }
469
470 /*
471 * initialize a defaultroute_t struct
472 */
473 static void
474 init_defaultroute(defaultroute_t *defaultroute)
475 {
476 memset(defaultroute, 0, sizeof(defaultroute_t));
477 }
478
479 /*
480 * discover the default route via /proc/net/route
481 */
482 void
483 get_defaultroute(defaultroute_t *defaultroute)
484 {
485 FILE *fd;
486 char line[BUF_LEN];
487 bool first = TRUE;
488
489 init_defaultroute(defaultroute);
490
491 fd = fopen("/proc/net/route", "r");
492
493 if (!fd)
494 {
495 plog("could not open 'proc/net/route'");
496 return;
497 }
498
499 while (fgets(line, sizeof(line), fd) != 0)
500 {
501 char iface[11];
502 char destination[9];
503 char gateway[11];
504 char flags[5];
505 char mask[9];
506
507 int refcnt;
508 int use;
509 int metric;
510 int items;
511
512 /* proc/net/route returns IP addresses in host order */
513 strcpy(gateway, "0h");
514
515 /* skip the header line */
516 if (first)
517 {
518 first = FALSE;
519 continue;
520 }
521
522 /* parsing a single line of proc/net/route */
523 items = sscanf(line, "%10s\t%8s\t%8s\t%5s\t%d\t%d\t%d\t%8s\t"
524 , iface, destination, gateway+2, flags, &refcnt, &use, &metric, mask);
525 if (items < 8)
526 {
527 plog("parsing error while scanning /proc/net/route");
528 continue;
529 }
530
531 /* check for defaultroute (destination 0.0.0.0 and mask 0.0.0.0) */
532 if (streq(destination, "00000000") && streq(mask, "00000000"))
533 {
534 if (defaultroute->defined)
535 {
536 plog("multiple default routes - cannot cope with %%defaultroute!!!");
537 defaultroute->defined = FALSE;
538 fclose(fd);
539 return;
540 }
541 ttoaddr(gateway, strlen(gateway), AF_INET, &defaultroute->nexthop);
542 strncpy(defaultroute->iface, iface, IFNAMSIZ);
543 defaultroute->defined = TRUE;
544 }
545 }
546 fclose(fd);
547
548 if (!defaultroute->defined)
549 {
550 plog("no default route - cannot cope with %%defaultroute!!!");
551 }
552 else
553 {
554 char addr_buf[20], nexthop_buf[20];
555 struct ifreq physreq;
556
557 int sock = socket(AF_INET, SOCK_DGRAM, 0);
558
559 /* determine IP address of iface */
560 if (sock < 0)
561 {
562 plog("could not open SOCK_DGRAM socket");
563 defaultroute->defined = FALSE;
564 return;
565 }
566 memset ((void*)&physreq, 0, sizeof(physreq));
567 strncpy(physreq.ifr_name, defaultroute->iface, IFNAMSIZ);
568 ioctl(sock, SIOCGIFADDR, &physreq);
569 close(sock);
570 defaultroute->addr.u.v4 = *((struct sockaddr_in *)&physreq.ifr_addr);
571
572 addrtot(&defaultroute->addr, 0, addr_buf, sizeof(addr_buf));
573 addrtot(&defaultroute->nexthop, 0, nexthop_buf, sizeof(nexthop_buf));
574
575 DBG(DBG_CONTROL,
576 DBG_log("Default route found: iface=%s, addr=%s, nexthop=%s"
577 , defaultroute->iface, addr_buf, nexthop_buf)
578 )
579
580 /* for backwards-compatibility with the awk shell scripts
581 * store the defaultroute in /var/run/ipsec.info
582 */
583 fd = fopen(INFO_FILE, "w");
584
585 if (fd)
586 {
587 fprintf(fd, "defaultroutephys=%s\n", defaultroute->iface );
588 fprintf(fd, "defaultroutevirt=ipsec0\n");
589 fprintf(fd, "defaultrouteaddr=%s\n", addr_buf);
590 fprintf(fd, "defaultroutenexthop=%s\n", nexthop_buf);
591 fclose(fd);
592 }
593 }
594 return;
595 }