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