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