]>
Commit | Line | Data |
---|---|---|
5734509c PJ |
1 | /* |
2 | * This file is free software: you may copy, redistribute and/or modify it | |
3 | * under the terms of the GNU General Public License as published by the | |
4 | * Free Software Foundation, either version 2 of the License, or (at your | |
5 | * option) any later version. | |
6 | * | |
7 | * This file is distributed in the hope that it will be useful, but | |
8 | * WITHOUT ANY WARRANTY; without even the implied warranty of | |
9 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
10 | * General Public License for more details. | |
11 | * | |
12 | * You should have received a copy of the GNU General Public License | |
13 | * along with this program. If not, see <http://www.gnu.org/licenses/>. | |
14 | * | |
15 | * This file incorporates work covered by the following copyright and | |
16 | * permission notice: | |
17 | * | |
18 | ||
19 | Copyright 2011 by Matthieu Boutier and Juliusz Chroboczek | |
20 | ||
21 | Permission is hereby granted, free of charge, to any person obtaining a copy | |
22 | of this software and associated documentation files (the "Software"), to deal | |
23 | in the Software without restriction, including without limitation the rights | |
24 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | |
25 | copies of the Software, and to permit persons to whom the Software is | |
26 | furnished to do so, subject to the following conditions: | |
27 | ||
28 | The above copyright notice and this permission notice shall be included in | |
29 | all copies or substantial portions of the Software. | |
30 | ||
31 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |
32 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |
33 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | |
34 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |
35 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |
36 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | |
37 | THE SOFTWARE. | |
38 | */ | |
39 | ||
40 | /* include zebra library */ | |
41 | #include <zebra.h> | |
42 | #include "getopt.h" | |
43 | #include "if.h" | |
44 | #include "log.h" | |
45 | #include "thread.h" | |
46 | #include "privs.h" | |
47 | #include "sigevent.h" | |
48 | #include "version.h" | |
49 | #include "command.h" | |
50 | #include "vty.h" | |
51 | #include "memory.h" | |
52 | ||
53 | #include "babel_main.h" | |
54 | #include "babeld.h" | |
55 | #include "util.h" | |
56 | #include "kernel.h" | |
57 | #include "babel_interface.h" | |
58 | #include "neighbour.h" | |
59 | #include "route.h" | |
60 | #include "xroute.h" | |
61 | #include "message.h" | |
62 | #include "resend.h" | |
63 | #include "babel_zebra.h" | |
64 | ||
65 | ||
66 | static void babel_init (int argc, char **argv); | |
5734509c PJ |
67 | static char *babel_get_progname(char *argv_0); |
68 | static void babel_fail(void); | |
69 | static void babel_init_random(void); | |
70 | static void babel_replace_by_null(int fd); | |
71 | static void babel_load_state_file(void); | |
72 | static void babel_init_signals(void); | |
73 | static void babel_exit_properly(void); | |
74 | static void babel_save_state_file(void); | |
75 | ||
76 | ||
77 | struct thread_master *master; /* quagga's threads handler */ | |
78 | struct timeval babel_now; /* current time */ | |
79 | ||
80 | unsigned char myid[8]; /* unique id (mac address of an interface) */ | |
81 | int debug = BABEL_DEBUG_COMMON; | |
82 | ||
83 | time_t reboot_time; | |
84 | int idle_time = 320; | |
85 | int link_detect = 0; | |
86 | int wireless_hello_interval = -1; | |
87 | int wired_hello_interval = -1; | |
88 | int idle_hello_interval = -1; | |
87c271c6 | 89 | static const char *pidfile = PATH_BABELD_PID; |
5734509c PJ |
90 | |
91 | const unsigned char zeroes[16] = {0}; | |
92 | const unsigned char ones[16] = | |
93 | {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, | |
94 | 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}; | |
95 | ||
87c271c6 | 96 | static const char *state_file = "/var/lib/babeld/babel-state"; |
5734509c PJ |
97 | |
98 | unsigned char protocol_group[16]; /* babel's link-local multicast address */ | |
99 | int protocol_port; /* babel's port */ | |
100 | int protocol_socket = -1; /* socket: communicate with others babeld */ | |
101 | ||
102 | static char babel_config_default[] = SYSCONFDIR BABEL_DEFAULT_CONFIG; | |
103 | static char *babel_config_file = NULL; | |
104 | static char *babel_vty_addr = NULL; | |
105 | static int babel_vty_port = BABEL_VTY_PORT; | |
106 | ||
107 | /* Babeld options. */ | |
108 | struct option longopts[] = | |
109 | { | |
110 | { "daemon", no_argument, NULL, 'd'}, | |
111 | { "config_file", required_argument, NULL, 'f'}, | |
112 | { "pid_file", required_argument, NULL, 'i'}, | |
8f3607f8 | 113 | { "socket", required_argument, NULL, 'z'}, |
5734509c PJ |
114 | { "help", no_argument, NULL, 'h'}, |
115 | { "vty_addr", required_argument, NULL, 'A'}, | |
116 | { "vty_port", required_argument, NULL, 'P'}, | |
117 | { "user", required_argument, NULL, 'u'}, | |
118 | { "group", required_argument, NULL, 'g'}, | |
119 | { "version", no_argument, NULL, 'v'}, | |
120 | { 0 } | |
121 | }; | |
122 | ||
123 | /* babeld privileges */ | |
124 | static zebra_capabilities_t _caps_p [] = | |
125 | { | |
126 | ZCAP_NET_RAW, | |
127 | ZCAP_BIND | |
128 | }; | |
129 | static struct zebra_privs_t babeld_privs = | |
130 | { | |
131 | #if defined(QUAGGA_USER) | |
132 | .user = QUAGGA_USER, | |
133 | #endif | |
134 | #if defined QUAGGA_GROUP | |
135 | .group = QUAGGA_GROUP, | |
136 | #endif | |
137 | #ifdef VTY_GROUP | |
138 | .vty_group = VTY_GROUP, | |
139 | #endif | |
140 | .caps_p = _caps_p, | |
141 | .cap_num_p = 2, | |
142 | .cap_num_i = 0 | |
143 | }; | |
144 | ||
145 | ||
146 | int | |
147 | main(int argc, char **argv) | |
148 | { | |
149 | struct thread thread; | |
150 | /* and print banner too */ | |
151 | babel_init(argc, argv); | |
152 | while (thread_fetch (master, &thread)) { | |
153 | thread_call (&thread); | |
154 | } | |
155 | return 0; | |
156 | } | |
157 | ||
446d73b7 DO |
158 | static void |
159 | babel_usage (char *progname, int status) | |
160 | { | |
161 | if (status != 0) | |
162 | fprintf (stderr, "Try `%s --help' for more information.\n", progname); | |
163 | else | |
164 | { | |
165 | printf ("Usage : %s [OPTION...]\n\ | |
166 | Daemon which manages Babel routing protocol.\n\n\ | |
167 | -d, --daemon Runs in daemon mode\n\ | |
168 | -f, --config_file Set configuration file name\n\ | |
169 | -i, --pid_file Set process identifier file name\n\ | |
8f3607f8 | 170 | -z, --socket Set path of zebra socket\n\ |
446d73b7 DO |
171 | -A, --vty_addr Set vty's bind address\n\ |
172 | -P, --vty_port Set vty's port number\n\ | |
173 | -u, --user User to run as\n\ | |
174 | -g, --group Group to run as\n\ | |
175 | -v, --version Print program version\n\ | |
176 | -h, --help Display this help and exit\n\ | |
177 | \n\ | |
178 | Report bugs to %s\n", progname, ZEBRA_BUG_ADDRESS); | |
179 | } | |
180 | exit (status); | |
181 | } | |
182 | ||
5734509c PJ |
183 | /* make initialisations witch don't need infos about kernel(interfaces, etc.) */ |
184 | static void | |
185 | babel_init(int argc, char **argv) | |
186 | { | |
187 | int rc, opt; | |
188 | int do_daemonise = 0; | |
189 | char *progname = NULL; | |
5734509c PJ |
190 | |
191 | /* Set umask before anything for security */ | |
192 | umask (0027); | |
193 | progname = babel_get_progname(argv[0]); | |
194 | ||
195 | /* set default log (lib/log.h) */ | |
196 | zlog_default = openzlog(progname, ZLOG_BABEL, | |
197 | LOG_CONS|LOG_NDELAY|LOG_PID, LOG_DAEMON); | |
198 | /* set log destination as stdout until the config file is read */ | |
199 | zlog_set_level(NULL, ZLOG_DEST_STDOUT, LOG_WARNING); | |
200 | ||
201 | babel_init_random(); | |
202 | ||
203 | /* set the Babel's default link-local multicast address and Babel's port */ | |
204 | parse_address("ff02:0:0:0:0:0:1:6", protocol_group, NULL); | |
205 | protocol_port = 6696; | |
206 | ||
207 | /* get options */ | |
208 | while(1) { | |
8f3607f8 | 209 | opt = getopt_long(argc, argv, "df:i:z:hA:P:u:g:v", longopts, 0); |
5734509c PJ |
210 | if(opt < 0) |
211 | break; | |
212 | ||
213 | switch(opt) { | |
214 | case 0: | |
215 | break; | |
216 | case 'd': | |
217 | do_daemonise = -1; | |
218 | break; | |
219 | case 'f': | |
220 | babel_config_file = optarg; | |
221 | break; | |
222 | case 'i': | |
223 | pidfile = optarg; | |
224 | break; | |
8f3607f8 DO |
225 | case 'z': |
226 | zclient_serv_path_set (optarg); | |
227 | break; | |
5734509c PJ |
228 | case 'A': |
229 | babel_vty_addr = optarg; | |
230 | break; | |
231 | case 'P': | |
232 | babel_vty_port = atoi (optarg); | |
233 | if (babel_vty_port < 0 || babel_vty_port > 0xffff | |
234 | || (babel_vty_port == 0 && optarg[0] != '0'/*atoi error*/)) | |
235 | babel_vty_port = BABEL_VTY_PORT; | |
236 | break; | |
237 | case 'u': | |
238 | babeld_privs.user = optarg; | |
239 | break; | |
240 | case 'g': | |
241 | babeld_privs.group = optarg; | |
242 | break; | |
243 | case 'v': | |
244 | print_version (progname); | |
245 | exit (0); | |
246 | break; | |
247 | case 'h': | |
446d73b7 | 248 | babel_usage (progname, 0); |
5734509c PJ |
249 | break; |
250 | default: | |
446d73b7 | 251 | babel_usage (progname, 1); |
5734509c PJ |
252 | break; |
253 | } | |
254 | } | |
255 | ||
256 | /* create the threads handler */ | |
257 | master = thread_master_create (); | |
258 | ||
259 | /* Library inits. */ | |
260 | zprivs_init (&babeld_privs); | |
261 | babel_init_signals(); | |
262 | cmd_init (1); | |
263 | vty_init (master); | |
264 | memory_init (); | |
265 | ||
266 | /* babeld inits (default options) */ | |
267 | /* set default interval's values */ | |
268 | if(wireless_hello_interval <= 0) | |
269 | wireless_hello_interval = 4000; | |
270 | wireless_hello_interval = MAX(wireless_hello_interval, 5); | |
271 | ||
272 | if(wired_hello_interval <= 0) | |
273 | wired_hello_interval = 4000; | |
274 | wired_hello_interval = MAX(wired_hello_interval, 5); | |
275 | ||
276 | /* an assertion */ | |
277 | if(parasitic && allow_duplicates >= 0) { | |
278 | /* Too difficult to get right. */ | |
279 | zlog_err("Sorry, -P and -A are incompatible."); | |
280 | exit(1); | |
281 | } | |
282 | ||
283 | babel_replace_by_null(STDIN_FILENO); | |
284 | ||
285 | if (do_daemonise && daemonise() < 0) { | |
286 | zlog_err("daemonise: %s", safe_strerror(errno)); | |
287 | exit (1); | |
288 | } | |
289 | ||
290 | /* write pid file */ | |
291 | if (pid_output(pidfile) < 0) { | |
292 | zlog_err("error while writing pidfile"); | |
293 | exit (1); | |
294 | }; | |
295 | ||
296 | /* init some quagga's dependencies, and babeld's commands */ | |
297 | babeld_quagga_init(); | |
298 | /* init zebra client's structure and it's commands */ | |
299 | /* this replace kernel_setup && kernel_setup_socket */ | |
300 | babelz_zebra_init (); | |
301 | ||
302 | /* Sort all installed commands. */ | |
303 | sort_node (); | |
304 | ||
305 | /* Get zebra configuration file. */ | |
306 | zlog_set_level (NULL, ZLOG_DEST_STDOUT, ZLOG_DISABLED); | |
307 | vty_read_config (babel_config_file, babel_config_default); | |
308 | ||
309 | myseqno = (random() & 0xFFFF); | |
310 | babel_load_state_file(); | |
311 | ||
312 | /* Create VTY socket */ | |
313 | vty_serv_sock (babel_vty_addr, babel_vty_port, BABEL_VTYSH_PATH); | |
314 | ||
315 | /* init buffer */ | |
316 | rc = resize_receive_buffer(1500); | |
317 | if(rc < 0) | |
318 | babel_fail(); | |
319 | ||
320 | schedule_neighbours_check(5000, 1); | |
321 | ||
322 | zlog_notice ("BABELd %s starting: vty@%d", BABEL_VERSION, babel_vty_port); | |
323 | } | |
324 | ||
325 | /* return the progname (without path, example: "./x/progname" --> "progname") */ | |
326 | static char * | |
327 | babel_get_progname(char *argv_0) { | |
328 | char *p = strrchr (argv_0, '/'); | |
329 | return (p ? ++p : argv_0); | |
330 | } | |
331 | ||
5734509c PJ |
332 | static void |
333 | babel_fail(void) | |
334 | { | |
335 | exit(1); | |
336 | } | |
337 | ||
338 | /* initialize random value, and set 'babel_now' by the way. */ | |
339 | static void | |
340 | babel_init_random(void) | |
341 | { | |
342 | gettime(&babel_now); | |
343 | int rc; | |
344 | unsigned int seed; | |
345 | ||
346 | rc = read_random_bytes(&seed, sizeof(seed)); | |
347 | if(rc < 0) { | |
348 | zlog_err("read(random): %s", safe_strerror(errno)); | |
349 | seed = 42; | |
350 | } | |
351 | ||
352 | seed ^= (babel_now.tv_sec ^ babel_now.tv_usec); | |
353 | srandom(seed); | |
354 | } | |
355 | ||
356 | /* | |
357 | close fd, and replace it by "/dev/null" | |
358 | exit if error | |
359 | */ | |
360 | static void | |
361 | babel_replace_by_null(int fd) | |
362 | { | |
363 | int fd_null; | |
364 | int rc; | |
365 | ||
366 | fd_null = open("/dev/null", O_RDONLY); | |
367 | if(fd_null < 0) { | |
368 | zlog_err("open(null): %s", safe_strerror(errno)); | |
369 | exit(1); | |
370 | } | |
371 | ||
372 | rc = dup2(fd_null, fd); | |
373 | if(rc < 0) { | |
374 | zlog_err("dup2(null, 0): %s", safe_strerror(errno)); | |
375 | exit(1); | |
376 | } | |
377 | ||
378 | close(fd_null); | |
379 | } | |
380 | ||
381 | /* | |
382 | Load the state file: check last babeld's running state, usefull in case of | |
383 | "/etc/init.d/babeld restart" | |
384 | */ | |
385 | static void | |
386 | babel_load_state_file(void) | |
387 | { | |
388 | reboot_time = babel_now.tv_sec; | |
389 | int fd; | |
390 | int rc; | |
391 | ||
392 | fd = open(state_file, O_RDONLY); | |
393 | if(fd < 0 && errno != ENOENT) | |
394 | zlog_err("open(babel-state: %s)", safe_strerror(errno)); | |
395 | rc = unlink(state_file); | |
396 | if(fd >= 0 && rc < 0) { | |
397 | zlog_err("unlink(babel-state): %s", safe_strerror(errno)); | |
398 | /* If we couldn't unlink it, it's probably stale. */ | |
399 | close(fd); | |
400 | fd = -1; | |
401 | } | |
402 | if(fd >= 0) { | |
403 | char buf[100]; | |
404 | char buf2[100]; | |
405 | int s; | |
406 | long t; | |
407 | rc = read(fd, buf, 99); | |
408 | if(rc < 0) { | |
409 | zlog_err("read(babel-state): %s", safe_strerror(errno)); | |
410 | } else { | |
411 | buf[rc] = '\0'; | |
412 | rc = sscanf(buf, "%99s %d %ld\n", buf2, &s, &t); | |
413 | if(rc == 3 && s >= 0 && s <= 0xFFFF) { | |
414 | unsigned char sid[8]; | |
415 | rc = parse_eui64(buf2, sid); | |
416 | if(rc < 0) { | |
417 | zlog_err("Couldn't parse babel-state."); | |
418 | } else { | |
419 | struct timeval realnow; | |
420 | debugf(BABEL_DEBUG_COMMON, | |
421 | "Got %s %d %ld from babel-state.", | |
422 | format_eui64(sid), s, t); | |
423 | gettimeofday(&realnow, NULL); | |
424 | if(memcmp(sid, myid, 8) == 0) | |
425 | myseqno = seqno_plus(s, 1); | |
426 | else | |
427 | zlog_err("ID mismatch in babel-state."); | |
428 | /* Convert realtime into monotonic time. */ | |
429 | if(t >= 1176800000L && t <= realnow.tv_sec) | |
430 | reboot_time = babel_now.tv_sec - (realnow.tv_sec - t); | |
431 | } | |
432 | } else { | |
433 | zlog_err("Couldn't parse babel-state."); | |
434 | } | |
435 | } | |
436 | close(fd); | |
437 | fd = -1; | |
438 | } | |
439 | } | |
440 | ||
441 | static void | |
442 | babel_sigexit(void) | |
443 | { | |
444 | zlog_notice("Terminating on signal"); | |
445 | ||
446 | babel_exit_properly(); | |
447 | } | |
448 | ||
449 | static void | |
450 | babel_sigusr1 (void) | |
451 | { | |
452 | zlog_rotate (NULL); | |
453 | } | |
454 | ||
455 | static void | |
456 | babel_init_signals(void) | |
457 | { | |
458 | static struct quagga_signal_t babel_signals[] = | |
459 | { | |
460 | { | |
461 | .signal = SIGUSR1, | |
462 | .handler = &babel_sigusr1, | |
463 | }, | |
464 | { | |
465 | .signal = SIGINT, | |
466 | .handler = &babel_sigexit, | |
467 | }, | |
468 | { | |
469 | .signal = SIGTERM, | |
470 | .handler = &babel_sigexit, | |
471 | }, | |
472 | }; | |
473 | ||
474 | signal_init (master, Q_SIGC(babel_signals), babel_signals); | |
475 | } | |
476 | ||
477 | static void | |
478 | babel_exit_properly(void) | |
479 | { | |
480 | debugf(BABEL_DEBUG_COMMON, "Exiting..."); | |
481 | usleep(roughly(10000)); | |
482 | gettime(&babel_now); | |
483 | ||
484 | /* Uninstall and flush all routes. */ | |
485 | debugf(BABEL_DEBUG_COMMON, "Uninstall routes."); | |
486 | babel_uninstall_all_routes(); | |
487 | babel_interface_close_all(); | |
488 | babel_zebra_close_connexion(); | |
489 | babel_save_state_file(); | |
490 | debugf(BABEL_DEBUG_COMMON, "Remove pid file."); | |
491 | if(pidfile) | |
492 | unlink(pidfile); | |
493 | debugf(BABEL_DEBUG_COMMON, "Done."); | |
494 | ||
495 | exit(0); | |
496 | } | |
497 | ||
498 | static void | |
499 | babel_save_state_file(void) | |
500 | { | |
501 | int fd; | |
502 | int rc; | |
503 | ||
504 | debugf(BABEL_DEBUG_COMMON, "Save state file."); | |
505 | fd = open(state_file, O_WRONLY | O_TRUNC | O_CREAT, 0644); | |
506 | if(fd < 0) { | |
507 | zlog_err("creat(babel-state): %s", safe_strerror(errno)); | |
508 | unlink(state_file); | |
509 | } else { | |
510 | struct timeval realnow; | |
511 | char buf[100]; | |
512 | gettimeofday(&realnow, NULL); | |
513 | rc = snprintf(buf, 100, "%s %d %ld\n", | |
514 | format_eui64(myid), (int)myseqno, | |
515 | (long)realnow.tv_sec); | |
516 | if(rc < 0 || rc >= 100) { | |
517 | zlog_err("write(babel-state): overflow."); | |
518 | unlink(state_file); | |
519 | } else { | |
520 | rc = write(fd, buf, rc); | |
521 | if(rc < 0) { | |
522 | zlog_err("write(babel-state): %s", safe_strerror(errno)); | |
523 | unlink(state_file); | |
524 | } | |
525 | fsync(fd); | |
526 | } | |
527 | close(fd); | |
528 | } | |
529 | } |