1/* 2 * Copyright (c) 2003 Apple Computer, Inc. All rights reserved. 3 * 4 * @APPLE_LICENSE_HEADER_START@ 5 * 6 * This file contains Original Code and/or Modifications of Original Code 7 * as defined in and that are subject to the Apple Public Source License 8 * Version 2.0 (the 'License'). You may not use this file except in 9 * compliance with the License. Please obtain a copy of the License at 10 * http://www.opensource.apple.com/apsl/ and read it before using this 11 * file. 12 * 13 * The Original Code and all software distributed under the License are 14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER 15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, 16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, 17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. 18 * Please see the License for the specific language governing rights and 19 * limitations under the License. 20 * 21 * @APPLE_LICENSE_HEADER_END@ 22 */ 23/* 24 * multilink.c - support routines for multilink. 25 * 26 * Copyright (c) 2000-2002 Paul Mackerras. All rights reserved. 27 * 28 * Redistribution and use in source and binary forms, with or without 29 * modification, are permitted provided that the following conditions 30 * are met: 31 * 32 * 1. Redistributions of source code must retain the above copyright 33 * notice, this list of conditions and the following disclaimer. 34 * 35 * 2. Redistributions in binary form must reproduce the above copyright 36 * notice, this list of conditions and the following disclaimer in 37 * the documentation and/or other materials provided with the 38 * distribution. 39 * 40 * 3. The name(s) of the authors of this software must not be used to 41 * endorse or promote products derived from this software without 42 * prior written permission. 43 * 44 * 4. Redistributions of any form whatsoever must retain the following 45 * acknowledgment: 46 * "This product includes software developed by Paul Mackerras 47 * <paulus@samba.org>". 48 * 49 * THE AUTHORS OF THIS SOFTWARE DISCLAIM ALL WARRANTIES WITH REGARD TO 50 * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY 51 * AND FITNESS, IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY 52 * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 53 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN 54 * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING 55 * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 56 */ 57#include <string.h> 58#include <ctype.h> 59#include <stdlib.h> 60#include <netdb.h> 61#include <errno.h> 62#include <signal.h> 63#include <netinet/in.h> 64 65#include "pppd.h" 66#include "fsm.h" 67#include "lcp.h" 68#include "tdb.h" 69 70bool endpoint_specified; /* user gave explicit endpoint discriminator */ 71char *bundle_id; /* identifier for our bundle */ 72 73extern TDB_CONTEXT *pppdb; 74extern char db_key[]; 75 76static int get_default_epdisc __P((struct epdisc *)); 77static int parse_num __P((char *str, const char *key, int *valp)); 78static int owns_unit __P((TDB_DATA pid, int unit)); 79 80#define set_ip_epdisc(ep, addr) do { \ 81 ep->length = 4; \ 82 ep->value[0] = addr >> 24; \ 83 ep->value[1] = addr >> 16; \ 84 ep->value[2] = addr >> 8; \ 85 ep->value[3] = addr; \ 86} while (0) 87 88#define LOCAL_IP_ADDR(addr) \ 89 (((addr) & 0xff000000) == 0x0a000000 /* 10.x.x.x */ \ 90 || ((addr) & 0xfff00000) == 0xac100000 /* 172.16.x.x */ \ 91 || ((addr) & 0xffff0000) == 0xc0a80000) /* 192.168.x.x */ 92 93#define process_exists(n) (kill((n), 0) == 0 || errno != ESRCH) 94 95void 96mp_check_options() 97{ 98 lcp_options *wo = &lcp_wantoptions[0]; 99 lcp_options *ao = &lcp_allowoptions[0]; 100 101 if (!multilink) 102 return; 103 /* if we're doing multilink, we have to negotiate MRRU */ 104 if (!wo->neg_mrru) { 105 /* mrru not specified, default to mru */ 106 wo->mrru = wo->mru; 107 wo->neg_mrru = 1; 108 } 109 ao->mrru = ao->mru; 110 ao->neg_mrru = 1; 111 112 if (!wo->neg_endpoint && !noendpoint) { 113 /* get a default endpoint value */ 114 wo->neg_endpoint = get_default_epdisc(&wo->endpoint); 115 } 116} 117 118/* 119 * Make a new bundle or join us to an existing bundle 120 * if we are doing multilink. 121 */ 122int 123mp_join_bundle() 124{ 125 lcp_options *go = &lcp_gotoptions[0]; 126 lcp_options *ho = &lcp_hisoptions[0]; 127 lcp_options *ao = &lcp_allowoptions[0]; 128 int unit, pppd_pid; 129 int l, mtu; 130 char *p; 131 TDB_DATA key, pid, rec; 132 133 if (!go->neg_mrru || !ho->neg_mrru) { 134 /* not doing multilink */ 135 if (go->neg_mrru) 136 notice("oops, multilink negotiated only for receive"); 137 mtu = ho->neg_mru? ho->mru: PPP_MRU; 138 if (mtu > ao->mru) 139 mtu = ao->mru; 140 if (demand) { 141 /* already have a bundle */ 142 cfg_bundle(0, 0, 0, 0); 143 netif_set_mtu(0, mtu); 144 return 0; 145 } 146 make_new_bundle(0, 0, 0, 0); 147 set_ifunit(1); 148 netif_set_mtu(0, mtu); 149 return 0; 150 } 151 152 /* 153 * Find the appropriate bundle or join a new one. 154 * First we make up a name for the bundle. 155 * The length estimate is worst-case assuming every 156 * character has to be quoted. 157 */ 158 l = 4 * strlen(peer_authname) + 10; 159 if (ho->neg_endpoint) 160 l += 3 * ho->endpoint.length + 8; 161 if (bundle_name) 162 l += 3 * strlen(bundle_name) + 2; 163 bundle_id = malloc(l); 164 if (bundle_id == 0) 165 novm("bundle identifier"); 166 167 p = bundle_id; 168 p += slprintf(p, l-1, "BUNDLE=\"%q\"", peer_authname); 169 if (ho->neg_endpoint || bundle_name) 170 *p++ = '/'; 171 if (ho->neg_endpoint) 172 p += slprintf(p, bundle_id+l-p, "%s", 173 epdisc_to_str(&ho->endpoint)); 174 if (bundle_name) 175 p += slprintf(p, bundle_id+l-p, "/%v", bundle_name); 176 177 /* 178 * For demand mode, we only need to configure the bundle 179 * and attach the link. 180 */ 181 mtu = MIN(ho->mrru, ao->mru); 182 if (demand) { 183 cfg_bundle(go->mrru, ho->mrru, go->neg_ssnhf, ho->neg_ssnhf); 184 netif_set_mtu(0, mtu); 185 script_setenv("BUNDLE", bundle_id + 7, 1); 186 return 0; 187 } 188 189 /* 190 * Check if the bundle ID is already in the database. 191 */ 192 unit = -1; 193 tdb_writelock(pppdb); 194 key.dptr = bundle_id; 195 key.dsize = p - bundle_id; 196 pid = tdb_fetch(pppdb, key); 197 if (pid.dptr != NULL) { 198 /* bundle ID exists, see if the pppd record exists */ 199 rec = tdb_fetch(pppdb, pid); 200 if (rec.dptr != NULL) { 201 /* it is, parse the interface number */ 202 parse_num(rec.dptr, "IFNAME=ppp", &unit); 203 /* check the pid value */ 204 if (!parse_num(rec.dptr, "PPPD_PID=", &pppd_pid) 205 || !process_exists(pppd_pid) 206 || !owns_unit(pid, unit)) 207 unit = -1; 208 free(rec.dptr); 209 } 210 free(pid.dptr); 211 } 212 213 if (unit >= 0) { 214 /* attach to existing unit */ 215 if (bundle_attach(unit)) { 216 set_ifunit(0); 217 script_setenv("BUNDLE", bundle_id + 7, 0); 218 tdb_writeunlock(pppdb); 219 info("Link attached to %s", ifname); 220 return 1; 221 } 222 /* attach failed because bundle doesn't exist */ 223 } 224 225 /* we have to make a new bundle */ 226 make_new_bundle(go->mrru, ho->mrru, go->neg_ssnhf, ho->neg_ssnhf); 227 set_ifunit(1); 228 netif_set_mtu(0, mtu); 229 script_setenv("BUNDLE", bundle_id + 7, 1); 230 tdb_writeunlock(pppdb); 231 info("New bundle %s created", ifname); 232 return 0; 233} 234 235static int 236parse_num(str, key, valp) 237 char *str; 238 const char *key; 239 int *valp; 240{ 241 char *p, *endp; 242 int i; 243 244 p = strstr(str, key); 245 if (p != 0) { 246 p += strlen(key); 247 i = strtol(p, &endp, 10); 248 if (endp != p && (*endp == 0 || *endp == ';')) { 249 *valp = i; 250 return 1; 251 } 252 } 253 return 0; 254} 255 256/* 257 * Check whether the pppd identified by `key' still owns ppp unit `unit'. 258 */ 259static int 260owns_unit(key, unit) 261 TDB_DATA key; 262 int unit; 263{ 264 char ifkey[32]; 265 TDB_DATA kd, vd; 266 int ret = 0; 267 268 slprintf(ifkey, sizeof(ifkey), "IFNAME=ppp%d", unit); 269 kd.dptr = ifkey; 270 kd.dsize = strlen(ifkey); 271 vd = tdb_fetch(pppdb, kd); 272 if (vd.dptr != NULL) { 273 ret = vd.dsize == key.dsize 274 && memcmp(vd.dptr, key.dptr, vd.dsize) == 0; 275 free(vd.dptr); 276 } 277 return ret; 278} 279 280static int 281get_default_epdisc(ep) 282 struct epdisc *ep; 283{ 284 char *p; 285 struct hostent *hp; 286 u_int32_t addr; 287 288 /* First try for an ethernet MAC address */ 289 p = get_first_ethernet(); 290 if (p != 0 && get_if_hwaddr(ep->value, p) >= 0) { 291 ep->class = EPD_MAC; 292 ep->length = 6; 293 return 1; 294 } 295 296 /* see if our hostname corresponds to a reasonable IP address */ 297 hp = gethostbyname(hostname); 298 if (hp != NULL) { 299 addr = *(u_int32_t *)hp->h_addr; 300 if (!bad_ip_adrs(addr)) { 301 addr = ntohl(addr); 302 if (!LOCAL_IP_ADDR(addr)) { 303 ep->class = EPD_IP; 304 set_ip_epdisc(ep, addr); 305 return 1; 306 } 307 } 308 } 309 310 return 0; 311} 312 313/* 314 * epdisc_to_str - make a printable string from an endpoint discriminator. 315 */ 316 317static char *endp_class_names[] = { 318 "null", "local", "IP", "MAC", "magic", "phone" 319}; 320 321char * 322epdisc_to_str(ep) 323 struct epdisc *ep; 324{ 325 static char str[MAX_ENDP_LEN*3+8]; 326 u_char *p = ep->value; 327 int i, mask = 0; 328 char *q, c, c2; 329 330 if (ep->class == EPD_NULL && ep->length == 0) 331 return "null"; 332 if (ep->class == EPD_IP && ep->length == 4) { 333 u_int32_t addr; 334 335 GETLONG(addr, p); 336 slprintf(str, sizeof(str), "IP:%I", htonl(addr)); 337 return str; 338 } 339 340 c = ':'; 341 c2 = '.'; 342 if (ep->class == EPD_MAC && ep->length == 6) 343 c2 = ':'; 344 else if (ep->class == EPD_MAGIC && (ep->length % 4) == 0) 345 mask = 3; 346 q = str; 347 if (ep->class <= EPD_PHONENUM) 348 q += slprintf(q, sizeof(str)-1, "%s", 349 endp_class_names[ep->class]); 350 else 351 q += slprintf(q, sizeof(str)-1, "%d", ep->class); 352 c = ':'; 353 for (i = 0; i < ep->length && i < MAX_ENDP_LEN; ++i) { 354 if ((i & mask) == 0) { 355 *q++ = c; 356 c = c2; 357 } 358 q += slprintf(q, str + sizeof(str) - q, "%.2x", ep->value[i]); 359 } 360 return str; 361} 362 363static int hexc_val(int c) 364{ 365 if (c >= 'a') 366 return c - 'a' + 10; 367 if (c >= 'A') 368 return c - 'A' + 10; 369 return c - '0'; 370} 371 372int 373str_to_epdisc(ep, str) 374 struct epdisc *ep; 375 char *str; 376{ 377 int i, l; 378 char *p, *endp; 379 380 for (i = EPD_NULL; i <= EPD_PHONENUM; ++i) { 381 int sl = strlen(endp_class_names[i]); 382 if (strncasecmp(str, endp_class_names[i], sl) == 0) { 383 str += sl; 384 break; 385 } 386 } 387 if (i > EPD_PHONENUM) { 388 /* not a class name, try a decimal class number */ 389 i = strtol(str, &endp, 10); 390 if (endp == str) 391 return 0; /* can't parse class number */ 392 str = endp; 393 } 394 ep->class = i; 395 if (*str == 0) { 396 ep->length = 0; 397 return 1; 398 } 399 if (*str != ':' && *str != '.') 400 return 0; 401 ++str; 402 403 if (i == EPD_IP) { 404 u_int32_t addr; 405 i = parse_dotted_ip(str, &addr); 406 if (i == 0 || str[i] != 0) 407 return 0; 408 set_ip_epdisc(ep, addr); 409 return 1; 410 } 411 if (i == EPD_MAC && get_if_hwaddr(ep->value, str) >= 0) { 412 ep->length = 6; 413 return 1; 414 } 415 416 p = str; 417 for (l = 0; l < MAX_ENDP_LEN; ++l) { 418 if (*str == 0) 419 break; 420 if (p <= str) 421 for (p = str; isxdigit(*p); ++p) 422 ; 423 i = p - str; 424 if (i == 0) 425 return 0; 426 ep->value[l] = hexc_val(*str++); 427 if ((i & 1) == 0) 428 ep->value[l] = (ep->value[l] << 4) + hexc_val(*str++); 429 if (*str == ':' || *str == '.') 430 ++str; 431 } 432 if (*str != 0 || (ep->class == EPD_MAC && l != 6)) 433 return 0; 434 ep->length = l; 435 return 1; 436} 437 438