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