• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
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
mp_check_options()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
mp_join_bundle()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 
mp_exit_bundle()243 void mp_exit_bundle()
244 {
245 	lock_db();
246 	remove_bundle_link();
247 	unlock_db();
248 }
249 
sendhup(char * str)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 
mp_bundle_terminated()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 
make_bundle_links(int append)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 
remove_bundle_link()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 
iterate_bundle_links(void (* func)(char *))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
parse_num(str,key,valp)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
owns_unit(key,unit)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
get_default_epdisc(ep)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 *
epdisc_to_str(ep)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 
hexc_val(int c)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
str_to_epdisc(ep,str)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