xref: /netbsd-src/external/bsd/ppp/dist/pppd/multilink.c (revision 212397c69a103ae7e5eafa8731ddfae671d2dee7)
1 /*	$NetBSD: multilink.c,v 1.4 2014/10/25 21:11:37 christos Exp $	*/
2 
3 /*
4  * multilink.c - support routines for multilink.
5  *
6  * Copyright (c) 2000-2002 Paul Mackerras. All rights reserved.
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted provided that the following conditions
10  * are met:
11  *
12  * 1. Redistributions of source code must retain the above copyright
13  *    notice, this list of conditions and the following disclaimer.
14  *
15  * 2. The name(s) of the authors of this software must not be used to
16  *    endorse or promote products derived from this software without
17  *    prior written permission.
18  *
19  * 3. Redistributions of any form whatsoever must retain the following
20  *    acknowledgment:
21  *    "This product includes software developed by Paul Mackerras
22  *     <paulus@samba.org>".
23  *
24  * THE AUTHORS OF THIS SOFTWARE DISCLAIM ALL WARRANTIES WITH REGARD TO
25  * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
26  * AND FITNESS, IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY
27  * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
28  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
29  * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
30  * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
31  */
32 
33 #include <sys/cdefs.h>
34 __RCSID("$NetBSD: multilink.c,v 1.4 2014/10/25 21:11:37 christos Exp $");
35 
36 #include <string.h>
37 #include <ctype.h>
38 #include <stdlib.h>
39 #include <netdb.h>
40 #include <errno.h>
41 #include <signal.h>
42 #include <netinet/in.h>
43 #include <unistd.h>
44 
45 #include "pppd.h"
46 #include "fsm.h"
47 #include "lcp.h"
48 #include "tdb.h"
49 
50 bool endpoint_specified;	/* user gave explicit endpoint discriminator */
51 char *bundle_id;		/* identifier for our bundle */
52 char *blinks_id;		/* key for the list of links */
53 bool doing_multilink;		/* multilink was enabled and agreed to */
54 bool multilink_master;		/* we own the multilink bundle */
55 
56 extern TDB_CONTEXT *pppdb;
57 extern char db_key[];
58 
59 static void make_bundle_links __P((int append));
60 static void remove_bundle_link __P((void));
61 static void iterate_bundle_links __P((void (*func) __P((char *))));
62 
63 static int get_default_epdisc __P((struct epdisc *));
64 static int parse_num __P((char *str, const char *key, int *valp));
65 static int owns_unit __P((TDB_DATA pid, int unit));
66 
67 #define set_ip_epdisc(ep, addr) do {	\
68 	ep->length = 4;			\
69 	ep->value[0] = addr >> 24;	\
70 	ep->value[1] = addr >> 16;	\
71 	ep->value[2] = addr >> 8;	\
72 	ep->value[3] = addr;		\
73 } while (0)
74 
75 #define LOCAL_IP_ADDR(addr)						  \
76 	(((addr) & 0xff000000) == 0x0a000000		/* 10.x.x.x */	  \
77 	 || ((addr) & 0xfff00000) == 0xac100000		/* 172.16.x.x */  \
78 	 || ((addr) & 0xffff0000) == 0xc0a80000)	/* 192.168.x.x */
79 
80 #define process_exists(n)	(kill((n), 0) == 0 || errno != ESRCH)
81 
82 void
83 mp_check_options()
84 {
85 	lcp_options *wo = &lcp_wantoptions[0];
86 	lcp_options *ao = &lcp_allowoptions[0];
87 
88 	doing_multilink = 0;
89 	if (!multilink)
90 		return;
91 	/* if we're doing multilink, we have to negotiate MRRU */
92 	if (!wo->neg_mrru) {
93 		/* mrru not specified, default to mru */
94 		wo->mrru = wo->mru;
95 		wo->neg_mrru = 1;
96 	}
97 	ao->mrru = ao->mru;
98 	ao->neg_mrru = 1;
99 
100 	if (!wo->neg_endpoint && !noendpoint) {
101 		/* get a default endpoint value */
102 		wo->neg_endpoint = get_default_epdisc(&wo->endpoint);
103 	}
104 }
105 
106 /*
107  * Make a new bundle or join us to an existing bundle
108  * if we are doing multilink.
109  */
110 int
111 mp_join_bundle()
112 {
113 	lcp_options *go = &lcp_gotoptions[0];
114 	lcp_options *ho = &lcp_hisoptions[0];
115 	lcp_options *ao = &lcp_allowoptions[0];
116 	int unit, pppd_pid;
117 	int l, mtu;
118 	char *p;
119 	TDB_DATA key, pid, rec;
120 
121 	if (doing_multilink) {
122 		/* have previously joined a bundle */
123 		if (!go->neg_mrru || !ho->neg_mrru) {
124 			notice("oops, didn't get multilink on renegotiation");
125 			lcp_close(0, "multilink required");
126 			return 0;
127 		}
128 		/* XXX should check the peer_authname and ho->endpoint
129 		   are the same as previously */
130 		return 0;
131 	}
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 	doing_multilink = 1;
153 
154 	/*
155 	 * Find the appropriate bundle or join a new one.
156 	 * First we make up a name for the bundle.
157 	 * The length estimate is worst-case assuming every
158 	 * character has to be quoted.
159 	 */
160 	l = 4 * strlen(peer_authname) + 10;
161 	if (ho->neg_endpoint)
162 		l += 3 * ho->endpoint.length + 8;
163 	if (bundle_name)
164 		l += 3 * strlen(bundle_name) + 2;
165 	bundle_id = malloc(l);
166 	if (bundle_id == 0)
167 		novm("bundle identifier");
168 
169 	p = bundle_id;
170 	p += slprintf(p, l-1, "BUNDLE=\"%q\"", peer_authname);
171 	if (ho->neg_endpoint || bundle_name)
172 		*p++ = '/';
173 	if (ho->neg_endpoint)
174 		p += slprintf(p, bundle_id+l-p, "%s",
175 			      epdisc_to_str(&ho->endpoint));
176 	if (bundle_name)
177 		p += slprintf(p, bundle_id+l-p, "/%v", bundle_name);
178 
179 	/* Make the key for the list of links belonging to the bundle */
180 	l = p - bundle_id;
181 	blinks_id = malloc(l + 7);
182 	if (blinks_id == NULL)
183 		novm("bundle links key");
184 	slprintf(blinks_id, l + 7, "BUNDLE_LINKS=%s", bundle_id + 7);
185 
186 	/*
187 	 * For demand mode, we only need to configure the bundle
188 	 * and attach the link.
189 	 */
190 	mtu = MIN(ho->mrru, ao->mru);
191 	if (demand) {
192 		cfg_bundle(go->mrru, ho->mrru, go->neg_ssnhf, ho->neg_ssnhf);
193 		netif_set_mtu(0, mtu);
194 		script_setenv("BUNDLE", bundle_id + 7, 1);
195 		return 0;
196 	}
197 
198 	/*
199 	 * Check if the bundle ID is already in the database.
200 	 */
201 	unit = -1;
202 	lock_db();
203 	key.dptr = bundle_id;
204 	key.dsize = p - bundle_id;
205 	pid = tdb_fetch(pppdb, key);
206 	if (pid.dptr != NULL) {
207 		/* bundle ID exists, see if the pppd record exists */
208 		rec = tdb_fetch(pppdb, pid);
209 		if (rec.dptr != NULL && rec.dsize > 0) {
210 			/* make sure the string is null-terminated */
211 			rec.dptr[rec.dsize-1] = 0;
212 			/* parse the interface number */
213 			parse_num(rec.dptr, "IFNAME=ppp", &unit);
214 			/* check the pid value */
215 			if (!parse_num(rec.dptr, "PPPD_PID=", &pppd_pid)
216 			    || !process_exists(pppd_pid)
217 			    || !owns_unit(pid, unit))
218 				unit = -1;
219 			free(rec.dptr);
220 		}
221 		free(pid.dptr);
222 	}
223 
224 	if (unit >= 0) {
225 		/* attach to existing unit */
226 		if (bundle_attach(unit)) {
227 			set_ifunit(0);
228 			script_setenv("BUNDLE", bundle_id + 7, 0);
229 			make_bundle_links(1);
230 			unlock_db();
231 			info("Link attached to %s", ifname);
232 			return 1;
233 		}
234 		/* attach failed because bundle doesn't exist */
235 	}
236 
237 	/* we have to make a new bundle */
238 	make_new_bundle(go->mrru, ho->mrru, go->neg_ssnhf, ho->neg_ssnhf);
239 	set_ifunit(1);
240 	netif_set_mtu(0, mtu);
241 	script_setenv("BUNDLE", bundle_id + 7, 1);
242 	make_bundle_links(0);
243 	unlock_db();
244 	info("New bundle %s created", ifname);
245 	multilink_master = 1;
246 	return 0;
247 }
248 
249 void mp_exit_bundle()
250 {
251 	lock_db();
252 	remove_bundle_link();
253 	unlock_db();
254 }
255 
256 static void sendhup(char *str)
257 {
258 	int pid;
259 
260 	if (parse_num(str, "PPPD_PID=", &pid) && pid != getpid()) {
261 		if (debug)
262 			dbglog("sending SIGHUP to process %d", pid);
263 		kill(pid, SIGHUP);
264 	}
265 }
266 
267 void mp_bundle_terminated()
268 {
269 	TDB_DATA key;
270 
271 	bundle_terminating = 1;
272 	upper_layers_down(0);
273 	notice("Connection terminated.");
274 	print_link_stats();
275 	if (!demand) {
276 		remove_pidfiles();
277 		script_unsetenv("IFNAME");
278 	}
279 
280 	lock_db();
281 	destroy_bundle();
282 	iterate_bundle_links(sendhup);
283 	key.dptr = blinks_id;
284 	key.dsize = strlen(blinks_id);
285 	tdb_delete(pppdb, key);
286 	unlock_db();
287 
288 	new_phase(PHASE_DEAD);
289 
290 	doing_multilink = 0;
291 	multilink_master = 0;
292 }
293 
294 static void make_bundle_links(int append)
295 {
296 	TDB_DATA key, rec;
297 	char *p;
298 	char entry[32];
299 	int l;
300 
301 	key.dptr = blinks_id;
302 	key.dsize = strlen(blinks_id);
303 	slprintf(entry, sizeof(entry), "%s;", db_key);
304 	p = entry;
305 	if (append) {
306 		rec = tdb_fetch(pppdb, key);
307 		if (rec.dptr != NULL && rec.dsize > 0) {
308 			rec.dptr[rec.dsize-1] = 0;
309 			if (strstr(rec.dptr, db_key) != NULL) {
310 				/* already in there? strange */
311 				warn("link entry already exists in tdb");
312 				return;
313 			}
314 			l = rec.dsize + strlen(entry);
315 			p = malloc(l);
316 			if (p == NULL)
317 				novm("bundle link list");
318 			slprintf(p, l, "%s%s", rec.dptr, entry);
319 		} else {
320 			warn("bundle link list not found");
321 		}
322 		if (rec.dptr != NULL)
323 			free(rec.dptr);
324 	}
325 	rec.dptr = p;
326 	rec.dsize = strlen(p) + 1;
327 	if (tdb_store(pppdb, key, rec, TDB_REPLACE))
328 		error("couldn't %s bundle link list",
329 		      append? "update": "create");
330 	if (p != entry)
331 		free(p);
332 }
333 
334 static void remove_bundle_link()
335 {
336 	TDB_DATA key, rec;
337 	char entry[32];
338 	char *p, *q;
339 	int l;
340 
341 	key.dptr = blinks_id;
342 	key.dsize = strlen(blinks_id);
343 	slprintf(entry, sizeof(entry), "%s;", db_key);
344 
345 	rec = tdb_fetch(pppdb, key);
346 	if (rec.dptr == NULL || rec.dsize <= 0) {
347 		if (rec.dptr != NULL)
348 			free(rec.dptr);
349 		return;
350 	}
351 	rec.dptr[rec.dsize-1] = 0;
352 	p = strstr(rec.dptr, entry);
353 	if (p != NULL) {
354 		q = p + strlen(entry);
355 		l = strlen(q) + 1;
356 		memmove(p, q, l);
357 		rec.dsize = p - rec.dptr + l;
358 		if (tdb_store(pppdb, key, rec, TDB_REPLACE))
359 			error("couldn't update bundle link list (removal)");
360 	}
361 	free(rec.dptr);
362 }
363 
364 static void iterate_bundle_links(void (*func)(char *))
365 {
366 	TDB_DATA key, rec, pp;
367 	char *p, *q;
368 
369 	key.dptr = blinks_id;
370 	key.dsize = strlen(blinks_id);
371 	rec = tdb_fetch(pppdb, key);
372 	if (rec.dptr == NULL || rec.dsize <= 0) {
373 		error("bundle link list not found (iterating list)");
374 		if (rec.dptr != NULL)
375 			free(rec.dptr);
376 		return;
377 	}
378 	p = rec.dptr;
379 	p[rec.dsize-1] = 0;
380 	while ((q = strchr(p, ';')) != NULL) {
381 		*q = 0;
382 		key.dptr = p;
383 		key.dsize = q - p;
384 		pp = tdb_fetch(pppdb, key);
385 		if (pp.dptr != NULL && pp.dsize > 0) {
386 			pp.dptr[pp.dsize-1] = 0;
387 			func(pp.dptr);
388 		}
389 		if (pp.dptr != NULL)
390 			free(pp.dptr);
391 		p = q + 1;
392 	}
393 	free(rec.dptr);
394 }
395 
396 static int
397 parse_num(str, key, valp)
398      char *str;
399      const char *key;
400      int *valp;
401 {
402 	char *p, *endp;
403 	int i;
404 
405 	p = strstr(str, key);
406 	if (p != 0) {
407 		p += strlen(key);
408 		i = strtol(p, &endp, 10);
409 		if (endp != p && (*endp == 0 || *endp == ';')) {
410 			*valp = i;
411 			return 1;
412 		}
413 	}
414 	return 0;
415 }
416 
417 /*
418  * Check whether the pppd identified by `key' still owns ppp unit `unit'.
419  */
420 static int
421 owns_unit(key, unit)
422      TDB_DATA key;
423      int unit;
424 {
425 	char ifkey[32];
426 	TDB_DATA kd, vd;
427 	int ret = 0;
428 
429 	slprintf(ifkey, sizeof(ifkey), "IFNAME=ppp%d", unit);
430 	kd.dptr = ifkey;
431 	kd.dsize = strlen(ifkey);
432 	vd = tdb_fetch(pppdb, kd);
433 	if (vd.dptr != NULL) {
434 		ret = vd.dsize == key.dsize
435 			&& memcmp(vd.dptr, key.dptr, vd.dsize) == 0;
436 		free(vd.dptr);
437 	}
438 	return ret;
439 }
440 
441 static int
442 get_default_epdisc(ep)
443      struct epdisc *ep;
444 {
445 	char *p;
446 	struct hostent *hp;
447 	u_int32_t addr;
448 
449 	/* First try for an ethernet MAC address */
450 	p = get_first_ethernet();
451 	if (p != 0 && get_if_hwaddr(ep->value, p) >= 0) {
452 		ep->class = EPD_MAC;
453 		ep->length = 6;
454 		return 1;
455 	}
456 
457 	/* see if our hostname corresponds to a reasonable IP address */
458 	hp = gethostbyname(hostname);
459 	if (hp != NULL) {
460 		addr = *(u_int32_t *)hp->h_addr;
461 		if (!bad_ip_adrs(addr)) {
462 			addr = ntohl(addr);
463 			if (!LOCAL_IP_ADDR(addr)) {
464 				ep->class = EPD_IP;
465 				set_ip_epdisc(ep, addr);
466 				return 1;
467 			}
468 		}
469 	}
470 
471 	return 0;
472 }
473 
474 /*
475  * epdisc_to_str - make a printable string from an endpoint discriminator.
476  */
477 
478 static char *endp_class_names[] = {
479     "null", "local", "IP", "MAC", "magic", "phone"
480 };
481 
482 char *
483 epdisc_to_str(ep)
484      struct epdisc *ep;
485 {
486 	static char str[MAX_ENDP_LEN*3+8];
487 	u_char *p = ep->value;
488 	int i, mask = 0;
489 	char *q, c, c2;
490 
491 	if (ep->class == EPD_NULL && ep->length == 0)
492 		return "null";
493 	if (ep->class == EPD_IP && ep->length == 4) {
494 		u_int32_t addr;
495 
496 		GETLONG(addr, p);
497 		slprintf(str, sizeof(str), "IP:%I", htonl(addr));
498 		return str;
499 	}
500 
501 	c = ':';
502 	c2 = '.';
503 	if (ep->class == EPD_MAC && ep->length == 6)
504 		c2 = ':';
505 	else if (ep->class == EPD_MAGIC && (ep->length % 4) == 0)
506 		mask = 3;
507 	q = str;
508 	if (ep->class <= EPD_PHONENUM)
509 		q += slprintf(q, sizeof(str)-1, "%s",
510 			      endp_class_names[ep->class]);
511 	else
512 		q += slprintf(q, sizeof(str)-1, "%d", ep->class);
513 	c = ':';
514 	for (i = 0; i < ep->length && i < MAX_ENDP_LEN; ++i) {
515 		if ((i & mask) == 0) {
516 			*q++ = c;
517 			c = c2;
518 		}
519 		q += slprintf(q, str + sizeof(str) - q, "%.2x", ep->value[i]);
520 	}
521 	return str;
522 }
523 
524 static int hexc_val(int c)
525 {
526 	if (c >= 'a')
527 		return c - 'a' + 10;
528 	if (c >= 'A')
529 		return c - 'A' + 10;
530 	return c - '0';
531 }
532 
533 int
534 str_to_epdisc(ep, str)
535      struct epdisc *ep;
536      char *str;
537 {
538 	int i, l;
539 	char *p, *endp;
540 
541 	for (i = EPD_NULL; i <= EPD_PHONENUM; ++i) {
542 		int sl = strlen(endp_class_names[i]);
543 		if (strncasecmp(str, endp_class_names[i], sl) == 0) {
544 			str += sl;
545 			break;
546 		}
547 	}
548 	if (i > EPD_PHONENUM) {
549 		/* not a class name, try a decimal class number */
550 		i = strtol(str, &endp, 10);
551 		if (endp == str)
552 			return 0;	/* can't parse class number */
553 		str = endp;
554 	}
555 	ep->class = i;
556 	if (*str == 0) {
557 		ep->length = 0;
558 		return 1;
559 	}
560 	if (*str != ':' && *str != '.')
561 		return 0;
562 	++str;
563 
564 	if (i == EPD_IP) {
565 		u_int32_t addr;
566 		i = parse_dotted_ip(str, &addr);
567 		if (i == 0 || str[i] != 0)
568 			return 0;
569 		set_ip_epdisc(ep, addr);
570 		return 1;
571 	}
572 	if (i == EPD_MAC && get_if_hwaddr(ep->value, str) >= 0) {
573 		ep->length = 6;
574 		return 1;
575 	}
576 
577 	p = str;
578 	for (l = 0; l < MAX_ENDP_LEN; ++l) {
579 		if (*str == 0)
580 			break;
581 		if (p <= str)
582 			for (p = str; isxdigit((unsigned char)*p); ++p)
583 				;
584 		i = p - str;
585 		if (i == 0)
586 			return 0;
587 		ep->value[l] = hexc_val(*str++);
588 		if ((i & 1) == 0)
589 			ep->value[l] = (ep->value[l] << 4) + hexc_val(*str++);
590 		if (*str == ':' || *str == '.')
591 			++str;
592 	}
593 	if (*str != 0 || (ep->class == EPD_MAC && l != 6))
594 		return 0;
595 	ep->length = l;
596 	return 1;
597 }
598 
599