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