1 /* $NetBSD: multilink.c,v 1.6 2025/01/08 19:59:39 christos Exp $ */ 2 3 /* 4 * multilink.c - support routines for multilink. 5 * 6 * Copyright (c) 2000-2024 Paul Mackerras. All rights reserved. 7 * 8 * Redistribution and use in source and binary forms, with or without 9 * modification, are permitted provided that the following conditions 10 * are met: 11 * 12 * 1. Redistributions of source code must retain the above copyright 13 * notice, this list of conditions and the following disclaimer. 14 * 15 * 2. Redistributions in binary form must reproduce the above copyright 16 * notice, this list of conditions and the following disclaimer in 17 * the documentation and/or other materials provided with the 18 * distribution. 19 * 20 * THE AUTHORS OF THIS SOFTWARE DISCLAIM ALL WARRANTIES WITH REGARD TO 21 * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY 22 * AND FITNESS, IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY 23 * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 24 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN 25 * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING 26 * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 27 */ 28 29 #include <sys/cdefs.h> 30 __RCSID("$NetBSD: multilink.c,v 1.6 2025/01/08 19:59:39 christos Exp $"); 31 32 33 #ifdef HAVE_CONFIG_H 34 #include "config.h" 35 #endif 36 37 #include <string.h> 38 #include <ctype.h> 39 #include <stdlib.h> 40 #include <netdb.h> 41 #include <errno.h> 42 #include <signal.h> 43 #include <netinet/in.h> 44 #include <unistd.h> 45 46 #include "pppd-private.h" 47 #include "fsm.h" 48 #include "lcp.h" 49 #include "tdb.h" 50 #include "multilink.h" 51 52 bool endpoint_specified; /* user gave explicit endpoint discriminator */ 53 char *bundle_id; /* identifier for our bundle */ 54 char *blinks_id; /* key for the list of links */ 55 bool doing_multilink; /* multilink was enabled and agreed to */ 56 bool multilink_master; /* we own the multilink bundle */ 57 58 extern TDB_CONTEXT *pppdb; 59 extern char db_key[]; 60 61 static void make_bundle_links(int append); 62 static void remove_bundle_link(void); 63 static void iterate_bundle_links(void (*func)(char *)); 64 65 static int get_default_epdisc(struct epdisc *); 66 static int parse_num(char *str, const char *key, int *valp); 67 static int owns_unit(TDB_DATA pid, int unit); 68 69 #define set_ip_epdisc(ep, addr) do { \ 70 ep->length = 4; \ 71 ep->value[0] = addr >> 24; \ 72 ep->value[1] = addr >> 16; \ 73 ep->value[2] = addr >> 8; \ 74 ep->value[3] = addr; \ 75 } while (0) 76 77 #define LOCAL_IP_ADDR(addr) \ 78 (((addr) & 0xff000000) == 0x0a000000 /* 10.x.x.x */ \ 79 || ((addr) & 0xfff00000) == 0xac100000 /* 172.16.x.x */ \ 80 || ((addr) & 0xffff0000) == 0xc0a80000) /* 192.168.x.x */ 81 82 #define process_exists(n) (kill((n), 0) == 0 || errno != ESRCH) 83 84 multilink_join_hook_fn *multilink_join_hook = NULL; 85 86 bool mp_master() 87 { 88 return multilink_master; 89 } 90 91 bool mp_on() 92 { 93 return doing_multilink; 94 } 95 96 void 97 mp_check_options(void) 98 { 99 lcp_options *wo = &lcp_wantoptions[0]; 100 lcp_options *ao = &lcp_allowoptions[0]; 101 102 doing_multilink = 0; 103 if (!multilink) 104 return; 105 /* if we're doing multilink, we have to negotiate MRRU */ 106 if (!wo->neg_mrru) { 107 /* mrru not specified, default to mru */ 108 wo->mrru = wo->mru; 109 wo->neg_mrru = 1; 110 } 111 ao->mrru = ao->mru; 112 ao->neg_mrru = 1; 113 114 if (!wo->neg_endpoint && !noendpoint) { 115 /* get a default endpoint value */ 116 wo->neg_endpoint = get_default_epdisc(&wo->endpoint); 117 } 118 } 119 120 /* 121 * Make a new bundle or join us to an existing bundle 122 * if we are doing multilink. 123 */ 124 int 125 mp_join_bundle(void) 126 { 127 lcp_options *go = &lcp_gotoptions[0]; 128 lcp_options *ho = &lcp_hisoptions[0]; 129 lcp_options *ao = &lcp_allowoptions[0]; 130 int unit, pppd_pid; 131 int l, mtu; 132 char *p; 133 TDB_DATA key, pid, rec; 134 135 if (doing_multilink) { 136 /* have previously joined a bundle */ 137 if (!go->neg_mrru || !ho->neg_mrru) { 138 notice("oops, didn't get multilink on renegotiation"); 139 lcp_close(0, "multilink required"); 140 return 0; 141 } 142 /* XXX should check the peer_authname and ho->endpoint 143 are the same as previously */ 144 return 0; 145 } 146 147 if (!go->neg_mrru || !ho->neg_mrru) { 148 /* not doing multilink */ 149 if (go->neg_mrru) 150 notice("oops, multilink negotiated only for receive"); 151 mtu = ho->neg_mru? ho->mru: PPP_MRU; 152 if (mtu > ao->mru) 153 mtu = ao->mru; 154 if (demand) { 155 /* already have a bundle */ 156 cfg_bundle(0, 0, 0, 0); 157 ppp_set_mtu(0, mtu); 158 return 0; 159 } 160 make_new_bundle(0, 0, 0, 0); 161 set_ifunit(1); 162 ppp_set_mtu(0, mtu); 163 return 0; 164 } 165 166 doing_multilink = 1; 167 168 /* 169 * Find the appropriate bundle or join a new one. 170 * First we make up a name for the bundle. 171 * The length estimate is worst-case assuming every 172 * character has to be quoted. 173 */ 174 l = 4 * strlen(peer_authname) + 10; 175 if (ho->neg_endpoint) 176 l += 3 * ho->endpoint.length + 8; 177 if (bundle_name) 178 l += 3 * strlen(bundle_name) + 2; 179 bundle_id = malloc(l); 180 if (bundle_id == 0) 181 novm("bundle identifier"); 182 183 p = bundle_id; 184 p += slprintf(p, l-1, "BUNDLE=\"%q\"", peer_authname); 185 if (ho->neg_endpoint || bundle_name) 186 *p++ = '/'; 187 if (ho->neg_endpoint) 188 p += slprintf(p, bundle_id+l-p, "%s", 189 epdisc_to_str(&ho->endpoint)); 190 if (bundle_name) 191 p += slprintf(p, bundle_id+l-p, "/%v", bundle_name); 192 193 /* Make the key for the list of links belonging to the bundle */ 194 l = p - bundle_id; 195 blinks_id = malloc(l + 7); 196 if (blinks_id == NULL) 197 novm("bundle links key"); 198 slprintf(blinks_id, l + 7, "BUNDLE_LINKS=%s", bundle_id + 7); 199 200 /* 201 * For demand mode, we only need to configure the bundle 202 * and attach the link. 203 */ 204 mtu = MIN(ho->mrru, ao->mru); 205 if (demand) { 206 cfg_bundle(go->mrru, ho->mrru, go->neg_ssnhf, ho->neg_ssnhf); 207 ppp_set_mtu(0, mtu); 208 ppp_script_setenv("BUNDLE", bundle_id + 7, 1); 209 return 0; 210 } 211 212 /* 213 * Check if the bundle ID is already in the database. 214 */ 215 unit = -1; 216 lock_db(); 217 key.dptr = bundle_id; 218 key.dsize = p - bundle_id; 219 pid = tdb_fetch(pppdb, key); 220 if (pid.dptr != NULL) { 221 /* bundle ID exists, see if the pppd record exists */ 222 rec = tdb_fetch(pppdb, pid); 223 if (rec.dptr != NULL && rec.dsize > 0) { 224 /* make sure the string is null-terminated */ 225 rec.dptr[rec.dsize-1] = 0; 226 /* parse the interface number */ 227 parse_num(rec.dptr, "UNIT=", &unit); 228 /* check the pid value */ 229 if (!parse_num(rec.dptr, "PPPD_PID=", &pppd_pid) 230 || !process_exists(pppd_pid) 231 || !owns_unit(pid, unit)) 232 unit = -1; 233 free(rec.dptr); 234 } 235 free(pid.dptr); 236 } 237 238 if (unit >= 0) { 239 /* attach to existing unit */ 240 if (bundle_attach(unit)) { 241 set_ifunit(0); 242 ppp_script_setenv("BUNDLE", bundle_id + 7, 0); 243 make_bundle_links(1); 244 unlock_db(); 245 info("Link attached to %s", ifname); 246 return 1; 247 } 248 /* attach failed because bundle doesn't exist */ 249 } 250 251 /* we have to make a new bundle */ 252 make_new_bundle(go->mrru, ho->mrru, go->neg_ssnhf, ho->neg_ssnhf); 253 set_ifunit(1); 254 ppp_set_mtu(0, mtu); 255 ppp_script_setenv("BUNDLE", bundle_id + 7, 1); 256 make_bundle_links(0); 257 unlock_db(); 258 info("New bundle %s created", ifname); 259 multilink_master = 1; 260 return 0; 261 } 262 263 void mp_exit_bundle(void) 264 { 265 lock_db(); 266 remove_bundle_link(); 267 unlock_db(); 268 } 269 270 static void sendhup(char *str) 271 { 272 int pid; 273 274 if (parse_num(str, "PPPD_PID=", &pid) && pid != getpid()) { 275 if (debug) 276 dbglog("sending SIGHUP to process %d", pid); 277 kill(pid, SIGHUP); 278 } 279 } 280 281 void mp_bundle_terminated(void) 282 { 283 TDB_DATA key; 284 285 bundle_terminating = 1; 286 upper_layers_down(0); 287 notice("Connection terminated."); 288 print_link_stats(); 289 if (!demand) { 290 remove_pidfiles(); 291 ppp_script_unsetenv("IFNAME"); 292 } 293 294 lock_db(); 295 destroy_bundle(); 296 iterate_bundle_links(sendhup); 297 key.dptr = blinks_id; 298 key.dsize = strlen(blinks_id); 299 tdb_delete(pppdb, key); 300 unlock_db(); 301 302 new_phase(PHASE_DEAD); 303 304 doing_multilink = 0; 305 multilink_master = 0; 306 } 307 308 static void make_bundle_links(int append) 309 { 310 TDB_DATA key, rec; 311 char *p; 312 char entry[32]; 313 int l; 314 315 key.dptr = blinks_id; 316 key.dsize = strlen(blinks_id); 317 slprintf(entry, sizeof(entry), "%s;", db_key); 318 p = entry; 319 if (append) { 320 rec = tdb_fetch(pppdb, key); 321 if (rec.dptr != NULL && rec.dsize > 0) { 322 rec.dptr[rec.dsize-1] = 0; 323 if (strstr(rec.dptr, db_key) != NULL) { 324 /* already in there? strange */ 325 warn("link entry already exists in tdb"); 326 return; 327 } 328 l = rec.dsize + strlen(entry); 329 p = malloc(l); 330 if (p == NULL) 331 novm("bundle link list"); 332 slprintf(p, l, "%s%s", rec.dptr, entry); 333 } else { 334 warn("bundle link list not found"); 335 } 336 if (rec.dptr != NULL) 337 free(rec.dptr); 338 } 339 rec.dptr = p; 340 rec.dsize = strlen(p) + 1; 341 if (tdb_store(pppdb, key, rec, TDB_REPLACE)) 342 error("couldn't %s bundle link list", 343 append? "update": "create"); 344 if (p != entry) 345 free(p); 346 } 347 348 static void remove_bundle_link(void) 349 { 350 TDB_DATA key, rec; 351 char entry[32]; 352 char *p, *q; 353 int l; 354 355 key.dptr = blinks_id; 356 key.dsize = strlen(blinks_id); 357 slprintf(entry, sizeof(entry), "%s;", db_key); 358 359 rec = tdb_fetch(pppdb, key); 360 if (rec.dptr == NULL || rec.dsize <= 0) { 361 if (rec.dptr != NULL) 362 free(rec.dptr); 363 return; 364 } 365 rec.dptr[rec.dsize-1] = 0; 366 p = strstr(rec.dptr, entry); 367 if (p != NULL) { 368 q = p + strlen(entry); 369 l = strlen(q) + 1; 370 memmove(p, q, l); 371 rec.dsize = p - rec.dptr + l; 372 if (tdb_store(pppdb, key, rec, TDB_REPLACE)) 373 error("couldn't update bundle link list (removal)"); 374 } 375 free(rec.dptr); 376 } 377 378 static void iterate_bundle_links(void (*func)(char *)) 379 { 380 TDB_DATA key, rec, pp; 381 char *p, *q; 382 383 key.dptr = blinks_id; 384 key.dsize = strlen(blinks_id); 385 rec = tdb_fetch(pppdb, key); 386 if (rec.dptr == NULL || rec.dsize <= 0) { 387 error("bundle link list not found (iterating list)"); 388 if (rec.dptr != NULL) 389 free(rec.dptr); 390 return; 391 } 392 p = rec.dptr; 393 p[rec.dsize-1] = 0; 394 while ((q = strchr(p, ';')) != NULL) { 395 *q = 0; 396 key.dptr = p; 397 key.dsize = q - p; 398 pp = tdb_fetch(pppdb, key); 399 if (pp.dptr != NULL && pp.dsize > 0) { 400 pp.dptr[pp.dsize-1] = 0; 401 func(pp.dptr); 402 } 403 if (pp.dptr != NULL) 404 free(pp.dptr); 405 p = q + 1; 406 } 407 free(rec.dptr); 408 } 409 410 static int 411 parse_num(char *str, const char *key, int *valp) 412 { 413 char *p, *endp; 414 int i; 415 416 p = strstr(str, key); 417 if (p != 0) { 418 p += strlen(key); 419 i = strtol(p, &endp, 10); 420 if (endp != p && (*endp == 0 || *endp == ';')) { 421 *valp = i; 422 return 1; 423 } 424 } 425 return 0; 426 } 427 428 /* 429 * Check whether the pppd identified by `key' still owns ppp unit `unit'. 430 */ 431 static int 432 owns_unit(TDB_DATA key, int unit) 433 { 434 char ifkey[32]; 435 TDB_DATA kd, vd; 436 int ret = 0; 437 438 slprintf(ifkey, sizeof(ifkey), "UNIT=%d", unit); 439 kd.dptr = ifkey; 440 kd.dsize = strlen(ifkey); 441 vd = tdb_fetch(pppdb, kd); 442 if (vd.dptr != NULL) { 443 ret = vd.dsize == key.dsize 444 && memcmp(vd.dptr, key.dptr, vd.dsize) == 0; 445 free(vd.dptr); 446 } 447 return ret; 448 } 449 450 static int 451 get_default_epdisc(struct epdisc *ep) 452 { 453 struct hostent *hp; 454 u_int32_t addr; 455 456 /* First try for an ethernet MAC address */ 457 if (get_first_ether_hwaddr(ep->value) >= 0) { 458 ep->class = EPD_MAC; 459 ep->length = 6; 460 return 1; 461 } 462 463 /* see if our hostname corresponds to a reasonable IP address */ 464 hp = gethostbyname(hostname); 465 if (hp != NULL) { 466 addr = *(u_int32_t *)hp->h_addr; 467 if (!ppp_bad_ip_addr(addr)) { 468 addr = ntohl(addr); 469 if (!LOCAL_IP_ADDR(addr)) { 470 ep->class = EPD_IP; 471 set_ip_epdisc(ep, addr); 472 return 1; 473 } 474 } 475 } 476 477 return 0; 478 } 479 480 /* 481 * epdisc_to_str - make a printable string from an endpoint discriminator. 482 */ 483 484 static char *endp_class_names[] = { 485 "null", "local", "IP", "MAC", "magic", "phone" 486 }; 487 488 char * 489 epdisc_to_str(struct epdisc *ep) 490 { 491 static char str[MAX_ENDP_LEN*3+8]; 492 u_char *p = ep->value; 493 int i, mask = 0; 494 char *q, c, c2; 495 496 if (ep->class == EPD_NULL && ep->length == 0) 497 return "null"; 498 if (ep->class == EPD_IP && ep->length == 4) { 499 u_int32_t addr; 500 501 GETLONG(addr, p); 502 slprintf(str, sizeof(str), "IP:%I", htonl(addr)); 503 return str; 504 } 505 506 c = ':'; 507 c2 = '.'; 508 if (ep->class == EPD_MAC && ep->length == 6) 509 c2 = ':'; 510 else if (ep->class == EPD_MAGIC && (ep->length % 4) == 0) 511 mask = 3; 512 q = str; 513 if (ep->class <= EPD_PHONENUM) 514 q += slprintf(q, sizeof(str)-1, "%s", 515 endp_class_names[ep->class]); 516 else 517 q += slprintf(q, sizeof(str)-1, "%d", ep->class); 518 c = ':'; 519 for (i = 0; i < ep->length && i < MAX_ENDP_LEN; ++i) { 520 if ((i & mask) == 0) { 521 *q++ = c; 522 c = c2; 523 } 524 q += slprintf(q, str + sizeof(str) - q, "%.2x", ep->value[i]); 525 } 526 return str; 527 } 528 529 static int hexc_val(int c) 530 { 531 if (c >= 'a') 532 return c - 'a' + 10; 533 if (c >= 'A') 534 return c - 'A' + 10; 535 return c - '0'; 536 } 537 538 int 539 str_to_epdisc(struct epdisc *ep, char *str) 540 { 541 int i, l; 542 char *p, *endp; 543 544 for (i = EPD_NULL; i <= EPD_PHONENUM; ++i) { 545 int sl = strlen(endp_class_names[i]); 546 if (strncasecmp(str, endp_class_names[i], sl) == 0) { 547 str += sl; 548 break; 549 } 550 } 551 if (i > EPD_PHONENUM) { 552 /* not a class name, try a decimal class number */ 553 i = strtol(str, &endp, 10); 554 if (endp == str) 555 return 0; /* can't parse class number */ 556 str = endp; 557 } 558 ep->class = i; 559 if (*str == 0) { 560 ep->length = 0; 561 return 1; 562 } 563 if (*str != ':' && *str != '.') 564 return 0; 565 ++str; 566 567 if (i == EPD_IP) { 568 u_int32_t addr; 569 i = parse_dotted_ip(str, &addr); 570 if (i == 0 || str[i] != 0) 571 return 0; 572 set_ip_epdisc(ep, addr); 573 return 1; 574 } 575 if (i == EPD_MAC && get_if_hwaddr(ep->value, str) >= 0) { 576 ep->length = 6; 577 return 1; 578 } 579 580 p = str; 581 for (l = 0; l < MAX_ENDP_LEN; ++l) { 582 if (*str == 0) 583 break; 584 if (p <= str) 585 for (p = str; isxdigit((unsigned char)*p); ++p) 586 ; 587 i = p - str; 588 if (i == 0) 589 return 0; 590 ep->value[l] = hexc_val(*str++); 591 if ((i & 1) == 0) 592 ep->value[l] = (ep->value[l] << 4) + hexc_val(*str++); 593 if (*str == ':' || *str == '.') 594 ++str; 595 } 596 if (*str != 0 || (ep->class == EPD_MAC && l != 6)) 597 return 0; 598 ep->length = l; 599 return 1; 600 } 601