Home | History | Annotate | Download | only in pppd
      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 (at) 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 
     44 bool endpoint_specified;	/* user gave explicit endpoint discriminator */
     45 char *bundle_id;		/* identifier for our bundle */
     46 char *blinks_id;		/* key for the list of links */
     47 bool doing_multilink;		/* multilink was enabled and agreed to */
     48 bool multilink_master;		/* we own the multilink bundle */
     49 
     50 extern TDB_CONTEXT *pppdb;
     51 extern char db_key[];
     52 
     53 static void make_bundle_links __P((int append));
     54 static void remove_bundle_link __P((void));
     55 static void iterate_bundle_links __P((void (*func) __P((char *))));
     56 
     57 static int get_default_epdisc __P((struct epdisc *));
     58 static int parse_num __P((char *str, const char *key, int *valp));
     59 static 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 
     76 void
     77 mp_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  */
    104 int
    105 mp_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 
    243 void mp_exit_bundle()
    244 {
    245 	lock_db();
    246 	remove_bundle_link();
    247 	unlock_db();
    248 }
    249 
    250 static 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 
    261 void 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 
    282 new_phase(PHASE_DEAD);
    283 }
    284 
    285 static 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 
    325 static 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 
    355 static 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 
    387 static int
    388 parse_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  */
    411 static int
    412 owns_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 
    432 static int
    433 get_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 
    469 static char *endp_class_names[] = {
    470     "null", "local", "IP", "MAC", "magic", "phone"
    471 };
    472 
    473 char *
    474 epdisc_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 
    515 static 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 
    524 int
    525 str_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