xref: /netbsd/external/bsd/ppp/dist/pppd/multilink.c (revision 3dce80e5)
1 /*	$NetBSD: multilink.c,v 1.5 2021/01/09 16:39:28 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.5 2021/01/09 16:39:28 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(int append);
60 static void remove_bundle_link(void);
61 static void iterate_bundle_links(void (*func)(char *));
62 
63 static int get_default_epdisc(struct epdisc *);
64 static int parse_num(char *str, const char *key, int *valp);
65 static int owns_unit(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
mp_check_options(void)83 mp_check_options(void)
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
mp_join_bundle(void)111 mp_join_bundle(void)
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, "UNIT=", &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 
mp_exit_bundle(void)249 void mp_exit_bundle(void)
250 {
251 	lock_db();
252 	remove_bundle_link();
253 	unlock_db();
254 }
255 
sendhup(char * str)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 
mp_bundle_terminated(void)267 void mp_bundle_terminated(void)
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 
make_bundle_links(int append)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 
remove_bundle_link(void)334 static void remove_bundle_link(void)
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 
iterate_bundle_links(void (* func)(char *))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
parse_num(char * str,const char * key,int * valp)397 parse_num(char *str, const char *key, int *valp)
398 {
399 	char *p, *endp;
400 	int i;
401 
402 	p = strstr(str, key);
403 	if (p != 0) {
404 		p += strlen(key);
405 		i = strtol(p, &endp, 10);
406 		if (endp != p && (*endp == 0 || *endp == ';')) {
407 			*valp = i;
408 			return 1;
409 		}
410 	}
411 	return 0;
412 }
413 
414 /*
415  * Check whether the pppd identified by `key' still owns ppp unit `unit'.
416  */
417 static int
owns_unit(TDB_DATA key,int unit)418 owns_unit(TDB_DATA key, int unit)
419 {
420 	char ifkey[32];
421 	TDB_DATA kd, vd;
422 	int ret = 0;
423 
424 	slprintf(ifkey, sizeof(ifkey), "UNIT=%d", unit);
425 	kd.dptr = ifkey;
426 	kd.dsize = strlen(ifkey);
427 	vd = tdb_fetch(pppdb, kd);
428 	if (vd.dptr != NULL) {
429 		ret = vd.dsize == key.dsize
430 			&& memcmp(vd.dptr, key.dptr, vd.dsize) == 0;
431 		free(vd.dptr);
432 	}
433 	return ret;
434 }
435 
436 static int
get_default_epdisc(struct epdisc * ep)437 get_default_epdisc(struct epdisc *ep)
438 {
439 	struct hostent *hp;
440 	u_int32_t addr;
441 
442 	/* First try for an ethernet MAC address */
443 	if (get_first_ether_hwaddr(ep->value) >= 0) {
444 		ep->class = EPD_MAC;
445 		ep->length = 6;
446 		return 1;
447 	}
448 
449 	/* see if our hostname corresponds to a reasonable IP address */
450 	hp = gethostbyname(hostname);
451 	if (hp != NULL) {
452 		addr = *(u_int32_t *)hp->h_addr;
453 		if (!bad_ip_adrs(addr)) {
454 			addr = ntohl(addr);
455 			if (!LOCAL_IP_ADDR(addr)) {
456 				ep->class = EPD_IP;
457 				set_ip_epdisc(ep, addr);
458 				return 1;
459 			}
460 		}
461 	}
462 
463 	return 0;
464 }
465 
466 /*
467  * epdisc_to_str - make a printable string from an endpoint discriminator.
468  */
469 
470 static char *endp_class_names[] = {
471     "null", "local", "IP", "MAC", "magic", "phone"
472 };
473 
474 char *
epdisc_to_str(struct epdisc * ep)475 epdisc_to_str(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(struct epdisc * ep,char * str)525 str_to_epdisc(struct epdisc *ep, char *str)
526 {
527 	int i, l;
528 	char *p, *endp;
529 
530 	for (i = EPD_NULL; i <= EPD_PHONENUM; ++i) {
531 		int sl = strlen(endp_class_names[i]);
532 		if (strncasecmp(str, endp_class_names[i], sl) == 0) {
533 			str += sl;
534 			break;
535 		}
536 	}
537 	if (i > EPD_PHONENUM) {
538 		/* not a class name, try a decimal class number */
539 		i = strtol(str, &endp, 10);
540 		if (endp == str)
541 			return 0;	/* can't parse class number */
542 		str = endp;
543 	}
544 	ep->class = i;
545 	if (*str == 0) {
546 		ep->length = 0;
547 		return 1;
548 	}
549 	if (*str != ':' && *str != '.')
550 		return 0;
551 	++str;
552 
553 	if (i == EPD_IP) {
554 		u_int32_t addr;
555 		i = parse_dotted_ip(str, &addr);
556 		if (i == 0 || str[i] != 0)
557 			return 0;
558 		set_ip_epdisc(ep, addr);
559 		return 1;
560 	}
561 	if (i == EPD_MAC && get_if_hwaddr(ep->value, str) >= 0) {
562 		ep->length = 6;
563 		return 1;
564 	}
565 
566 	p = str;
567 	for (l = 0; l < MAX_ENDP_LEN; ++l) {
568 		if (*str == 0)
569 			break;
570 		if (p <= str)
571 			for (p = str; isxdigit((unsigned char)*p); ++p)
572 				;
573 		i = p - str;
574 		if (i == 0)
575 			return 0;
576 		ep->value[l] = hexc_val(*str++);
577 		if ((i & 1) == 0)
578 			ep->value[l] = (ep->value[l] << 4) + hexc_val(*str++);
579 		if (*str == ':' || *str == '.')
580 			++str;
581 	}
582 	if (*str != 0 || (ep->class == EPD_MAC && l != 6))
583 		return 0;
584 	ep->length = l;
585 	return 1;
586 }
587