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