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