1 /* $OpenBSD: config.c,v 1.63 2016/01/26 18:35:01 mmcc Exp $ */ 2 3 /* 4 * Copyright (c) 2003, 2004, 2005 Henning Brauer <henning@openbsd.org> 5 * 6 * Permission to use, copy, modify, and distribute this software for any 7 * purpose with or without fee is hereby granted, provided that the above 8 * copyright notice and this permission notice appear in all copies. 9 * 10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 15 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 16 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17 */ 18 19 #include <sys/types.h> 20 #include <sys/socket.h> 21 #include <sys/stat.h> 22 #include <sys/mman.h> 23 #include <sys/ioctl.h> 24 25 #include <netmpls/mpls.h> 26 27 #include <errno.h> 28 #include <ifaddrs.h> 29 #include <netdb.h> 30 #include <stdlib.h> 31 #include <string.h> 32 #include <unistd.h> 33 34 #include "bgpd.h" 35 #include "session.h" 36 37 u_int32_t get_bgpid(void); 38 int host_v4(const char *, struct bgpd_addr *, u_int8_t *); 39 int host_v6(const char *, struct bgpd_addr *); 40 void free_networks(struct network_head *); 41 void free_rdomains(struct rdomain_head *); 42 43 struct bgpd_config * 44 new_config(void) 45 { 46 struct bgpd_config *conf; 47 48 if ((conf = calloc(1, sizeof(struct bgpd_config))) == NULL) 49 fatal(NULL); 50 51 conf->min_holdtime = MIN_HOLDTIME; 52 conf->bgpid = get_bgpid(); 53 conf->fib_priority = RTP_BGP; 54 55 if ((conf->csock = strdup(SOCKET_NAME)) == NULL) 56 fatal(NULL); 57 58 if ((conf->filters = calloc(1, sizeof(struct filter_head))) == NULL) 59 fatal(NULL); 60 if ((conf->listen_addrs = calloc(1, sizeof(struct listen_addrs))) == 61 NULL) 62 fatal(NULL); 63 if ((conf->mrt = calloc(1, sizeof(struct mrt_head))) == NULL) 64 fatal(NULL); 65 66 /* init the various list for later */ 67 TAILQ_INIT(&conf->networks); 68 SIMPLEQ_INIT(&conf->rdomains); 69 70 TAILQ_INIT(conf->filters); 71 TAILQ_INIT(conf->listen_addrs); 72 LIST_INIT(conf->mrt); 73 74 return (conf); 75 } 76 77 void 78 free_networks(struct network_head *networks) 79 { 80 struct network *n; 81 82 while ((n = TAILQ_FIRST(networks)) != NULL) { 83 TAILQ_REMOVE(networks, n, entry); 84 filterset_free(&n->net.attrset); 85 free(n); 86 } 87 } 88 89 void 90 free_rdomains(struct rdomain_head *rdomains) 91 { 92 struct rdomain *rd; 93 94 while ((rd = SIMPLEQ_FIRST(rdomains)) != NULL) { 95 SIMPLEQ_REMOVE_HEAD(rdomains, entry); 96 filterset_free(&rd->export); 97 filterset_free(&rd->import); 98 free_networks(&rd->net_l); 99 free(rd); 100 } 101 } 102 103 void 104 free_config(struct bgpd_config *conf) 105 { 106 struct listen_addr *la; 107 struct mrt *m; 108 109 free_rdomains(&conf->rdomains); 110 free_networks(&conf->networks); 111 filterlist_free(conf->filters); 112 113 while ((la = TAILQ_FIRST(conf->listen_addrs)) != NULL) { 114 TAILQ_REMOVE(conf->listen_addrs, la, entry); 115 free(la); 116 } 117 free(conf->listen_addrs); 118 119 while ((m = LIST_FIRST(conf->mrt)) != NULL) { 120 LIST_REMOVE(m, entry); 121 free(m); 122 } 123 free(conf->mrt); 124 125 free(conf->csock); 126 free(conf->rcsock); 127 128 free(conf); 129 } 130 131 int 132 merge_config(struct bgpd_config *xconf, struct bgpd_config *conf, 133 struct peer *peer_l) 134 { 135 struct listen_addr *nla, *ola, *next; 136 struct network *n; 137 struct rdomain *rd; 138 139 /* 140 * merge the freshly parsed conf into the running xconf 141 */ 142 if (!conf->as) { 143 log_warnx("configuration error: AS not given"); 144 return (1); 145 } 146 147 if ((conf->flags & BGPD_FLAG_REFLECTOR) && conf->clusterid == 0) 148 conf->clusterid = conf->bgpid; 149 150 151 /* adjust FIB priority if changed */ 152 /* if xconf is uninitialized we get RTP_NONE */ 153 if (xconf->fib_priority != conf->fib_priority) { 154 kr_fib_decouple_all(xconf->fib_priority); 155 kr_fib_update_prio_all(conf->fib_priority); 156 kr_fib_couple_all(conf->fib_priority); 157 } 158 159 /* take over the easy config changes */ 160 xconf->flags = conf->flags; 161 xconf->log = conf->log; 162 xconf->bgpid = conf->bgpid; 163 xconf->clusterid = conf->clusterid; 164 xconf->as = conf->as; 165 xconf->short_as = conf->short_as; 166 xconf->holdtime = conf->holdtime; 167 xconf->min_holdtime = conf->min_holdtime; 168 xconf->connectretry = conf->connectretry; 169 xconf->fib_priority = conf->fib_priority; 170 171 /* clear old control sockets and use new */ 172 free(xconf->csock); 173 free(xconf->rcsock); 174 xconf->csock = conf->csock; 175 xconf->rcsock = conf->rcsock; 176 /* set old one to NULL so we don't double free */ 177 conf->csock = NULL; 178 conf->rcsock = NULL; 179 180 /* clear all current filters and take over the new ones */ 181 filterlist_free(xconf->filters); 182 xconf->filters = conf->filters; 183 conf->filters = NULL; 184 185 /* switch the network statements, but first remove the old ones */ 186 free_networks(&xconf->networks); 187 while ((n = TAILQ_FIRST(&conf->networks)) != NULL) { 188 TAILQ_REMOVE(&conf->networks, n, entry); 189 TAILQ_INSERT_TAIL(&xconf->networks, n, entry); 190 } 191 192 /* switch the rdomain configs, first remove the old ones */ 193 free_rdomains(&xconf->rdomains); 194 while ((rd = SIMPLEQ_FIRST(&conf->rdomains)) != NULL) { 195 SIMPLEQ_REMOVE_HEAD(&conf->rdomains, entry); 196 SIMPLEQ_INSERT_TAIL(&xconf->rdomains, rd, entry); 197 } 198 199 /* 200 * merge new listeners: 201 * -flag all existing ones as to be deleted 202 * -those that are in both new and old: flag to keep 203 * -new ones get inserted and flagged as to reinit 204 * -remove all that are still flagged for deletion 205 */ 206 207 TAILQ_FOREACH(nla, xconf->listen_addrs, entry) 208 nla->reconf = RECONF_DELETE; 209 210 /* no new listeners? preserve default ones */ 211 if (TAILQ_EMPTY(conf->listen_addrs)) 212 TAILQ_FOREACH(ola, xconf->listen_addrs, entry) 213 if (ola->flags & DEFAULT_LISTENER) 214 ola->reconf = RECONF_KEEP; 215 /* else loop over listeners and merge configs */ 216 for (nla = TAILQ_FIRST(conf->listen_addrs); nla != NULL; nla = next) { 217 next = TAILQ_NEXT(nla, entry); 218 219 TAILQ_FOREACH(ola, xconf->listen_addrs, entry) 220 if (!memcmp(&nla->sa, &ola->sa, sizeof(nla->sa))) 221 break; 222 223 if (ola == NULL) { 224 /* new listener, copy over */ 225 TAILQ_REMOVE(conf->listen_addrs, nla, entry); 226 TAILQ_INSERT_TAIL(xconf->listen_addrs, nla, entry); 227 nla->reconf = RECONF_REINIT; 228 } else /* exists, just flag */ 229 ola->reconf = RECONF_KEEP; 230 } 231 /* finally clean up the original list and remove all stale entires */ 232 for (nla = TAILQ_FIRST(xconf->listen_addrs); nla != NULL; nla = next) { 233 next = TAILQ_NEXT(nla, entry); 234 if (nla->reconf == RECONF_DELETE) { 235 TAILQ_REMOVE(xconf->listen_addrs, nla, entry); 236 free(nla); 237 } 238 } 239 240 /* conf is merged so free it */ 241 free_config(conf); 242 243 return (0); 244 } 245 246 u_int32_t 247 get_bgpid(void) 248 { 249 struct ifaddrs *ifap, *ifa; 250 u_int32_t ip = 0, cur, localnet; 251 252 localnet = htonl(INADDR_LOOPBACK & IN_CLASSA_NET); 253 254 if (getifaddrs(&ifap) == -1) 255 fatal("getifaddrs"); 256 257 for (ifa = ifap; ifa; ifa = ifa->ifa_next) { 258 if (ifa->ifa_addr->sa_family != AF_INET) 259 continue; 260 cur = ((struct sockaddr_in *)ifa->ifa_addr)->sin_addr.s_addr; 261 if ((cur & localnet) == localnet) /* skip 127/8 */ 262 continue; 263 if (ntohl(cur) > ntohl(ip)) 264 ip = cur; 265 } 266 freeifaddrs(ifap); 267 268 return (ip); 269 } 270 271 int 272 host(const char *s, struct bgpd_addr *h, u_int8_t *len) 273 { 274 int done = 0; 275 int mask; 276 char *p, *ps; 277 const char *errstr; 278 279 if ((p = strrchr(s, '/')) != NULL) { 280 mask = strtonum(p + 1, 0, 128, &errstr); 281 if (errstr) { 282 log_warnx("prefixlen is %s: %s", errstr, p + 1); 283 return (0); 284 } 285 if ((ps = malloc(strlen(s) - strlen(p) + 1)) == NULL) 286 fatal("host: malloc"); 287 strlcpy(ps, s, strlen(s) - strlen(p) + 1); 288 } else { 289 if ((ps = strdup(s)) == NULL) 290 fatal("host: strdup"); 291 mask = 128; 292 } 293 294 bzero(h, sizeof(struct bgpd_addr)); 295 296 /* IPv4 address? */ 297 if (!done) 298 done = host_v4(s, h, len); 299 300 /* IPv6 address? */ 301 if (!done) { 302 done = host_v6(ps, h); 303 *len = mask; 304 } 305 306 free(ps); 307 308 return (done); 309 } 310 311 int 312 host_v4(const char *s, struct bgpd_addr *h, u_int8_t *len) 313 { 314 struct in_addr ina; 315 int bits = 32; 316 317 bzero(&ina, sizeof(struct in_addr)); 318 if (strrchr(s, '/') != NULL) { 319 if ((bits = inet_net_pton(AF_INET, s, &ina, sizeof(ina))) == -1) 320 return (0); 321 } else { 322 if (inet_pton(AF_INET, s, &ina) != 1) 323 return (0); 324 } 325 326 h->aid = AID_INET; 327 h->v4.s_addr = ina.s_addr; 328 *len = bits; 329 330 return (1); 331 } 332 333 int 334 host_v6(const char *s, struct bgpd_addr *h) 335 { 336 struct addrinfo hints, *res; 337 338 bzero(&hints, sizeof(hints)); 339 hints.ai_family = AF_INET6; 340 hints.ai_socktype = SOCK_DGRAM; /*dummy*/ 341 hints.ai_flags = AI_NUMERICHOST; 342 if (getaddrinfo(s, "0", &hints, &res) == 0) { 343 sa2addr(res->ai_addr, h); 344 freeaddrinfo(res); 345 return (1); 346 } 347 348 return (0); 349 } 350 351 void 352 prepare_listeners(struct bgpd_config *conf) 353 { 354 struct listen_addr *la, *next; 355 int opt = 1; 356 357 if (TAILQ_EMPTY(conf->listen_addrs)) { 358 if ((la = calloc(1, sizeof(struct listen_addr))) == NULL) 359 fatal("setup_listeners calloc"); 360 la->fd = -1; 361 la->flags = DEFAULT_LISTENER; 362 la->reconf = RECONF_REINIT; 363 la->sa.ss_len = sizeof(struct sockaddr_in); 364 ((struct sockaddr_in *)&la->sa)->sin_family = AF_INET; 365 ((struct sockaddr_in *)&la->sa)->sin_addr.s_addr = 366 htonl(INADDR_ANY); 367 ((struct sockaddr_in *)&la->sa)->sin_port = htons(BGP_PORT); 368 TAILQ_INSERT_TAIL(conf->listen_addrs, la, entry); 369 370 if ((la = calloc(1, sizeof(struct listen_addr))) == NULL) 371 fatal("setup_listeners calloc"); 372 la->fd = -1; 373 la->flags = DEFAULT_LISTENER; 374 la->reconf = RECONF_REINIT; 375 la->sa.ss_len = sizeof(struct sockaddr_in6); 376 ((struct sockaddr_in6 *)&la->sa)->sin6_family = AF_INET6; 377 ((struct sockaddr_in6 *)&la->sa)->sin6_port = htons(BGP_PORT); 378 TAILQ_INSERT_TAIL(conf->listen_addrs, la, entry); 379 } 380 381 for (la = TAILQ_FIRST(conf->listen_addrs); la != NULL; la = next) { 382 next = TAILQ_NEXT(la, entry); 383 if (la->reconf != RECONF_REINIT) 384 continue; 385 386 if ((la->fd = socket(la->sa.ss_family, 387 SOCK_STREAM | SOCK_CLOEXEC | SOCK_NONBLOCK, 388 IPPROTO_TCP)) == -1) { 389 if (la->flags & DEFAULT_LISTENER && (errno == 390 EAFNOSUPPORT || errno == EPROTONOSUPPORT)) { 391 TAILQ_REMOVE(conf->listen_addrs, la, entry); 392 free(la); 393 continue; 394 } else 395 fatal("socket"); 396 } 397 398 opt = 1; 399 if (setsockopt(la->fd, SOL_SOCKET, SO_REUSEADDR, 400 &opt, sizeof(opt)) == -1) 401 fatal("setsockopt SO_REUSEADDR"); 402 403 if (bind(la->fd, (struct sockaddr *)&la->sa, la->sa.ss_len) == 404 -1) { 405 switch (la->sa.ss_family) { 406 case AF_INET: 407 log_warn("cannot bind to %s:%u", 408 log_sockaddr((struct sockaddr *)&la->sa), 409 ntohs(((struct sockaddr_in *) 410 &la->sa)->sin_port)); 411 break; 412 case AF_INET6: 413 log_warn("cannot bind to [%s]:%u", 414 log_sockaddr((struct sockaddr *)&la->sa), 415 ntohs(((struct sockaddr_in6 *) 416 &la->sa)->sin6_port)); 417 break; 418 default: 419 log_warn("cannot bind to %s", 420 log_sockaddr((struct sockaddr *)&la->sa)); 421 break; 422 } 423 close(la->fd); 424 TAILQ_REMOVE(conf->listen_addrs, la, entry); 425 free(la); 426 continue; 427 } 428 } 429 } 430 431 int 432 get_mpe_label(struct rdomain *r) 433 { 434 struct ifreq ifr; 435 struct shim_hdr shim; 436 int s; 437 438 s = socket(AF_INET, SOCK_DGRAM, 0); 439 if (s == -1) 440 return (-1); 441 442 bzero(&shim, sizeof(shim)); 443 bzero(&ifr, sizeof(ifr)); 444 strlcpy(ifr.ifr_name, r->ifmpe, sizeof(ifr.ifr_name)); 445 ifr.ifr_data = (caddr_t)&shim; 446 447 if (ioctl(s, SIOCGETLABEL, (caddr_t)&ifr) == -1) { 448 close(s); 449 return (-1); 450 } 451 close(s); 452 r->label = shim.shim_label; 453 return (0); 454 } 455