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