]> git.ozlabs.org Git - ppp.git/blob - pppd/multilink.c
Remove references to the old CHAP code.
[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. Redistributions in binary form must reproduce the above copyright
14  *    notice, this list of conditions and the following disclaimer in
15  *    the documentation and/or other materials provided with the
16  *    distribution.
17  *
18  * 3. The name(s) of the authors of this software must not be used to
19  *    endorse or promote products derived from this software without
20  *    prior written permission.
21  *
22  * 4. Redistributions of any form whatsoever must retain the following
23  *    acknowledgment:
24  *    "This product includes software developed by Paul Mackerras
25  *     <paulus@samba.org>".
26  *
27  * THE AUTHORS OF THIS SOFTWARE DISCLAIM ALL WARRANTIES WITH REGARD TO
28  * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
29  * AND FITNESS, IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY
30  * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
31  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
32  * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
33  * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
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
43 #include "pppd.h"
44 #include "fsm.h"
45 #include "lcp.h"
46 #include "tdb.h"
47
48 bool endpoint_specified;        /* user gave explicit endpoint discriminator */
49 char *bundle_id;                /* identifier for our bundle */
50
51 extern TDB_CONTEXT *pppdb;
52 extern char db_key[];
53
54 static int get_default_epdisc __P((struct epdisc *));
55 static int parse_num __P((char *str, const char *key, int *valp));
56 static int owns_unit __P((TDB_DATA pid, int unit));
57
58 #define set_ip_epdisc(ep, addr) do {    \
59         ep->length = 4;                 \
60         ep->value[0] = addr >> 24;      \
61         ep->value[1] = addr >> 16;      \
62         ep->value[2] = addr >> 8;       \
63         ep->value[3] = addr;            \
64 } while (0)
65
66 #define LOCAL_IP_ADDR(addr)                                               \
67         (((addr) & 0xff000000) == 0x0a000000            /* 10.x.x.x */    \
68          || ((addr) & 0xfff00000) == 0xac100000         /* 172.16.x.x */  \
69          || ((addr) & 0xffff0000) == 0xc0a80000)        /* 192.168.x.x */
70
71 #define process_exists(n)       (kill((n), 0) == 0 || errno != ESRCH)
72
73 void
74 mp_check_options()
75 {
76         lcp_options *wo = &lcp_wantoptions[0];
77         lcp_options *ao = &lcp_allowoptions[0];
78
79         if (!multilink)
80                 return;
81         /* if we're doing multilink, we have to negotiate MRRU */
82         if (!wo->neg_mrru) {
83                 /* mrru not specified, default to mru */
84                 wo->mrru = wo->mru;
85                 wo->neg_mrru = 1;
86         }
87         ao->mrru = ao->mru;
88         ao->neg_mrru = 1;
89
90         if (!wo->neg_endpoint && !noendpoint) {
91                 /* get a default endpoint value */
92                 wo->neg_endpoint = get_default_epdisc(&wo->endpoint);
93         }
94 }
95
96 /*
97  * Make a new bundle or join us to an existing bundle
98  * if we are doing multilink.
99  */
100 int
101 mp_join_bundle()
102 {
103         lcp_options *go = &lcp_gotoptions[0];
104         lcp_options *ho = &lcp_hisoptions[0];
105         lcp_options *ao = &lcp_allowoptions[0];
106         int unit, pppd_pid;
107         int l, mtu;
108         char *p;
109         TDB_DATA key, pid, rec;
110
111         if (!go->neg_mrru || !ho->neg_mrru) {
112                 /* not doing multilink */
113                 if (go->neg_mrru)
114                         notice("oops, multilink negotiated only for receive");
115                 mtu = ho->neg_mru? ho->mru: PPP_MRU;
116                 if (mtu > ao->mru)
117                         mtu = ao->mru;
118                 if (demand) {
119                         /* already have a bundle */
120                         cfg_bundle(0, 0, 0, 0);
121                         netif_set_mtu(0, mtu);
122                         return 0;
123                 }
124                 make_new_bundle(0, 0, 0, 0);
125                 set_ifunit(1);
126                 netif_set_mtu(0, mtu);
127                 return 0;
128         }
129
130         /*
131          * Find the appropriate bundle or join a new one.
132          * First we make up a name for the bundle.
133          * The length estimate is worst-case assuming every
134          * character has to be quoted.
135          */
136         l = 4 * strlen(peer_authname) + 10;
137         if (ho->neg_endpoint)
138                 l += 3 * ho->endpoint.length + 8;
139         if (bundle_name)
140                 l += 3 * strlen(bundle_name) + 2;
141         bundle_id = malloc(l);
142         if (bundle_id == 0)
143                 novm("bundle identifier");
144
145         p = bundle_id;
146         p += slprintf(p, l-1, "BUNDLE=\"%q\"", peer_authname);
147         if (ho->neg_endpoint || bundle_name)
148                 *p++ = '/';
149         if (ho->neg_endpoint)
150                 p += slprintf(p, bundle_id+l-p, "%s",
151                               epdisc_to_str(&ho->endpoint));
152         if (bundle_name)
153                 p += slprintf(p, bundle_id+l-p, "/%v", bundle_name);
154
155         /*
156          * For demand mode, we only need to configure the bundle
157          * and attach the link.
158          */
159         mtu = MIN(ho->mrru, ao->mru);
160         if (demand) {
161                 cfg_bundle(go->mrru, ho->mrru, go->neg_ssnhf, ho->neg_ssnhf);
162                 netif_set_mtu(0, mtu);
163                 script_setenv("BUNDLE", bundle_id + 7, 1);
164                 return 0;
165         }
166
167         /*
168          * Check if the bundle ID is already in the database.
169          */
170         unit = -1;
171         tdb_writelock(pppdb);
172         key.dptr = bundle_id;
173         key.dsize = p - bundle_id;
174         pid = tdb_fetch(pppdb, key);
175         if (pid.dptr != NULL) {
176                 /* bundle ID exists, see if the pppd record exists */
177                 rec = tdb_fetch(pppdb, pid);
178                 if (rec.dptr != NULL) {
179                         /* it is, parse the interface number */
180                         parse_num(rec.dptr, "IFNAME=ppp", &unit);
181                         /* check the pid value */
182                         if (!parse_num(rec.dptr, "PPPD_PID=", &pppd_pid)
183                             || !process_exists(pppd_pid)
184                             || !owns_unit(pid, unit))
185                                 unit = -1;
186                         free(rec.dptr);
187                 }
188                 free(pid.dptr);
189         }
190
191         if (unit >= 0) {
192                 /* attach to existing unit */
193                 if (bundle_attach(unit)) {
194                         set_ifunit(0);
195                         script_setenv("BUNDLE", bundle_id + 7, 0);
196                         tdb_writeunlock(pppdb);
197                         info("Link attached to %s", ifname);
198                         return 1;
199                 }
200                 /* attach failed because bundle doesn't exist */
201         }
202
203         /* we have to make a new bundle */
204         make_new_bundle(go->mrru, ho->mrru, go->neg_ssnhf, ho->neg_ssnhf);
205         set_ifunit(1);
206         netif_set_mtu(0, mtu);
207         script_setenv("BUNDLE", bundle_id + 7, 1);
208         tdb_writeunlock(pppdb);
209         info("New bundle %s created", ifname);
210         return 0;
211 }
212
213 static int
214 parse_num(str, key, valp)
215      char *str;
216      const char *key;
217      int *valp;
218 {
219         char *p, *endp;
220         int i;
221
222         p = strstr(str, key);
223         if (p != 0) {
224                 p += strlen(key);
225                 i = strtol(p, &endp, 10);
226                 if (endp != p && (*endp == 0 || *endp == ';')) {
227                         *valp = i;
228                         return 1;
229                 }
230         }
231         return 0;
232 }
233
234 /*
235  * Check whether the pppd identified by `key' still owns ppp unit `unit'.
236  */
237 static int
238 owns_unit(key, unit)
239      TDB_DATA key;
240      int unit;
241 {
242         char ifkey[32];
243         TDB_DATA kd, vd;
244         int ret = 0;
245
246         slprintf(ifkey, sizeof(ifkey), "IFNAME=ppp%d", unit);
247         kd.dptr = ifkey;
248         kd.dsize = strlen(ifkey);
249         vd = tdb_fetch(pppdb, kd);
250         if (vd.dptr != NULL) {
251                 ret = vd.dsize == key.dsize
252                         && memcmp(vd.dptr, key.dptr, vd.dsize) == 0;
253                 free(vd.dptr);
254         }
255         return ret;
256 }
257
258 static int
259 get_default_epdisc(ep)
260      struct epdisc *ep;
261 {
262         char *p;
263         struct hostent *hp;
264         u_int32_t addr;
265
266         /* First try for an ethernet MAC address */
267         p = get_first_ethernet();
268         if (p != 0 && get_if_hwaddr(ep->value, p) >= 0) {
269                 ep->class = EPD_MAC;
270                 ep->length = 6;
271                 return 1;
272         }
273
274         /* see if our hostname corresponds to a reasonable IP address */
275         hp = gethostbyname(hostname);
276         if (hp != NULL) {
277                 addr = *(u_int32_t *)hp->h_addr;
278                 if (!bad_ip_adrs(addr)) {
279                         addr = ntohl(addr);
280                         if (!LOCAL_IP_ADDR(addr)) {
281                                 ep->class = EPD_IP;
282                                 set_ip_epdisc(ep, addr);
283                                 return 1;
284                         }
285                 }
286         }
287
288         return 0;
289 }
290
291 /*
292  * epdisc_to_str - make a printable string from an endpoint discriminator.
293  */
294
295 static char *endp_class_names[] = {
296     "null", "local", "IP", "MAC", "magic", "phone"
297 };
298
299 char *
300 epdisc_to_str(ep)
301      struct epdisc *ep;
302 {
303         static char str[MAX_ENDP_LEN*3+8];
304         u_char *p = ep->value;
305         int i, mask = 0;
306         char *q, c, c2;
307
308         if (ep->class == EPD_NULL && ep->length == 0)
309                 return "null";
310         if (ep->class == EPD_IP && ep->length == 4) {
311                 u_int32_t addr;
312
313                 GETLONG(addr, p);
314                 slprintf(str, sizeof(str), "IP:%I", htonl(addr));
315                 return str;
316         }
317
318         c = ':';
319         c2 = '.';
320         if (ep->class == EPD_MAC && ep->length == 6)
321                 c2 = ':';
322         else if (ep->class == EPD_MAGIC && (ep->length % 4) == 0)
323                 mask = 3;
324         q = str;
325         if (ep->class <= EPD_PHONENUM)
326                 q += slprintf(q, sizeof(str)-1, "%s",
327                               endp_class_names[ep->class]);
328         else
329                 q += slprintf(q, sizeof(str)-1, "%d", ep->class);
330         c = ':';
331         for (i = 0; i < ep->length && i < MAX_ENDP_LEN; ++i) {
332                 if ((i & mask) == 0) {
333                         *q++ = c;
334                         c = c2;
335                 }
336                 q += slprintf(q, str + sizeof(str) - q, "%.2x", ep->value[i]);
337         }
338         return str;
339 }
340
341 static int hexc_val(int c)
342 {
343         if (c >= 'a')
344                 return c - 'a' + 10;
345         if (c >= 'A')
346                 return c - 'A' + 10;
347         return c - '0';
348 }
349
350 int
351 str_to_epdisc(ep, str)
352      struct epdisc *ep;
353      char *str;
354 {
355         int i, l;
356         char *p, *endp;
357
358         for (i = EPD_NULL; i <= EPD_PHONENUM; ++i) {
359                 int sl = strlen(endp_class_names[i]);
360                 if (strncasecmp(str, endp_class_names[i], sl) == 0) {
361                         str += sl;
362                         break;
363                 }
364         }
365         if (i > EPD_PHONENUM) {
366                 /* not a class name, try a decimal class number */
367                 i = strtol(str, &endp, 10);
368                 if (endp == str)
369                         return 0;       /* can't parse class number */
370                 str = endp;
371         }
372         ep->class = i;
373         if (*str == 0) {
374                 ep->length = 0;
375                 return 1;
376         }
377         if (*str != ':' && *str != '.')
378                 return 0;
379         ++str;
380
381         if (i == EPD_IP) {
382                 u_int32_t addr;
383                 i = parse_dotted_ip(str, &addr);
384                 if (i == 0 || str[i] != 0)
385                         return 0;
386                 set_ip_epdisc(ep, addr);
387                 return 1;
388         }
389         if (i == EPD_MAC && get_if_hwaddr(ep->value, str) >= 0) {
390                 ep->length = 6;
391                 return 1;
392         }
393
394         p = str;
395         for (l = 0; l < MAX_ENDP_LEN; ++l) {
396                 if (*str == 0)
397                         break;
398                 if (p <= str)
399                         for (p = str; isxdigit(*p); ++p)
400                                 ;
401                 i = p - str;
402                 if (i == 0)
403                         return 0;
404                 ep->value[l] = hexc_val(*str++);
405                 if ((i & 1) == 0)
406                         ep->value[l] = (ep->value[l] << 4) + hexc_val(*str++);
407                 if (*str == ':' || *str == '.')
408                         ++str;
409         }
410         if (*str != 0 || (ep->class == EPD_MAC && l != 6))
411                 return 0;
412         ep->length = l;
413         return 1;
414 }
415