1 /*
2 * multilink.c - support routines for multilink.
3 *
4 * Copyright (c) 2000-2001 by Sun Microsystems, Inc.
5 * All rights reserved.
6 *
7 * Copyright (c) 2000 Paul Mackerras.
8 * All rights reserved.
9 *
10 * Redistribution and use in source and binary forms are permitted
11 * provided that the above copyright notice and this paragraph are
12 * duplicated in all such forms. The name of the author may not be
13 * used to endorse or promote products derived from this software
14 * without specific prior written permission.
15 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
16 * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
17 * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
18 */
19
20 #pragma ident "%Z%%M% %I% %E% SMI"
21 #define RCSID "$Id: $"
22
23 #include <string.h>
24 #include <ctype.h>
25 #include <stdlib.h>
26 #include <netdb.h>
27 #include <errno.h>
28 #include <signal.h>
29 #include <netinet/in.h>
30
31 #include "pppd.h"
32 #include "fsm.h"
33 #include "lcp.h"
34
35 #ifdef HAVE_MULTILINK
36 #include "tdb.h"
37 #endif
38 #if !defined(lint) && !defined(_lint)
39 static const char rcsid[] = RCSID;
40 #endif
41
42 #define set_ip_epdisc(ep, addr) ( \
43 ep->length = 4, \
44 ep->value[0] = addr >> 24, \
45 ep->value[1] = addr >> 16, \
46 ep->value[2] = addr >> 8, \
47 ep->value[3] = addr \
48 )
49
50 #ifdef HAVE_MULTILINK
51 bool endpoint_specified; /* user gave explicit endpoint discriminator */
52 char *bundle_id; /* identifier for our bundle */
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 process_exists(n) (kill(0, (n)) == 0 || errno != ESRCH)
59
60 void
mp_check_options()61 mp_check_options()
62 {
63 lcp_options *wo = &lcp_wantoptions[0];
64 lcp_options *ao = &lcp_allowoptions[0];
65
66 if (!multilink)
67 return;
68 /* if we're doing multilink, we have to negotiate MRRU */
69 if (!wo->neg_mrru) {
70 /* mrru not specified, default to mru */
71 wo->mrru = wo->mru;
72 wo->neg_mrru = 1;
73 }
74 ao->mrru = ao->mru;
75 ao->neg_mrru = 1;
76
77 if (!wo->neg_endpoint && !noendpoint) {
78 /* get a default endpoint value */
79 wo->neg_endpoint = get_default_epdisc(&wo->endpoint);
80 if (wo->neg_endpoint)
81 dbglog("using default endpoint %s",
82 epdisc_to_str(&wo->endpoint));
83 }
84 }
85
86 /*
87 * Make a new bundle or join us to an existing bundle
88 * if we are doing multilink.
89 */
90 int
mp_join_bundle()91 mp_join_bundle()
92 {
93 lcp_options *go = &lcp_gotoptions[0];
94 lcp_options *ho = &lcp_hisoptions[0];
95 int unit, pppd_pid;
96 int l;
97 char *p;
98 TDB_DATA key, pid, rec;
99
100 if (!go->neg_mrru || !ho->neg_mrru) {
101 /* not doing multilink */
102 if (go->neg_mrru)
103 notice("oops, multilink negotiated only for receive");
104 multilink = 0;
105 if (demand) {
106 /* already have a bundle */
107 cfg_bundle(0, 0, 0, 0);
108 return 0;
109 }
110 make_new_bundle(0, 0, 0, 0);
111 set_ifunit(1);
112 return 0;
113 }
114
115 /*
116 * Find the appropriate bundle or join a new one.
117 * First we make up a name for the bundle.
118 * The length estimate is worst-case assuming every
119 * character has to be quoted.
120 *
121 * Note - RFC 1990 requires that an unnegotiated endpoint
122 * discriminator value be equivalent to negotiating with class
123 * zero. Do not test ho->neg_endpoint here.
124 */
125 l = 4 * strlen(peer_authname) + 10;
126 l += 3 * ho->endpoint.length + 8;
127 if (bundle_name)
128 l += 3 * strlen(bundle_name) + 2;
129 bundle_id = malloc(l);
130 if (bundle_id == NULL)
131 novm("bundle identifier");
132
133 p = bundle_id;
134 p += slprintf(p, l-1, "BUNDLE=\"%q\"", peer_authname);
135 *p++ = '/';
136 p += slprintf(p, bundle_id+l-p, "%s", epdisc_to_str(&ho->endpoint));
137 if (bundle_name)
138 p += slprintf(p, bundle_id+l-p, "/%v", bundle_name);
139 dbglog("bundle_id = %s", bundle_id+7);
140
141 /*
142 * For demand mode, we only need to configure the bundle
143 * and attach the link.
144 */
145 if (demand) {
146 cfg_bundle(go->mrru, ho->mrru, go->neg_ssnhf, ho->neg_ssnhf);
147 script_setenv("BUNDLE", bundle_id + 7, 1);
148 return 0;
149 }
150
151 /*
152 * Check if the bundle ID is already in the database.
153 */
154 unit = -1;
155 tdb_writelock(pppdb);
156 key.dptr = bundle_id;
157 key.dsize = p - bundle_id;
158 pid = tdb_fetch(pppdb, key);
159 if (pid.dptr != NULL) {
160 /* bundle ID exists, see if the pppd record exists */
161 rec = tdb_fetch(pppdb, pid);
162 if (rec.dptr != NULL) {
163 /* it does, parse the interface number */
164 parse_num(rec.dptr, "IFNAME=ppp", &unit);
165 /* check the pid value */
166 if (!parse_num(rec.dptr, "PPPD_PID=", &pppd_pid)
167 || !process_exists(pppd_pid)
168 || !owns_unit(pid, unit))
169 unit = -1;
170 free(rec.dptr);
171 }
172 free(pid.dptr);
173 }
174
175 if (unit >= 0) {
176 /* attach to existing unit */
177 if (bundle_attach(unit)) {
178 set_ifunit(0);
179 script_setenv("BUNDLE", bundle_id + 7, 0);
180 tdb_writeunlock(pppdb);
181 info("Link attached to %s", ifname);
182 return 1;
183 }
184 /* attach failed because bundle doesn't exist */
185 }
186
187 /* we have to make a new bundle */
188 make_new_bundle(go->mrru, ho->mrru, go->neg_ssnhf, ho->neg_ssnhf);
189 set_ifunit(1);
190 script_setenv("BUNDLE", bundle_id + 7, 1);
191 tdb_writeunlock(pppdb);
192 info("New bundle %s created", ifname);
193 return 0;
194 }
195
196 static int
parse_num(str,key,valp)197 parse_num(str, key, valp)
198 char *str;
199 const char *key;
200 int *valp;
201 {
202 char *p, *endp;
203 int i;
204
205 p = strstr(str, key);
206 if (p != 0) {
207 p += strlen(key);
208 i = strtol(p, &endp, 10);
209 if (endp != p && (*endp == 0 || *endp == ';')) {
210 *valp = i;
211 return 1;
212 }
213 }
214 return 0;
215 }
216
217 /*
218 * Check whether the pppd identified by `key' still owns ppp unit `unit'.
219 */
220 static int
owns_unit(key,unit)221 owns_unit(key, unit)
222 TDB_DATA key;
223 int unit;
224 {
225 char ifkey[32];
226 TDB_DATA kd, vd;
227 int ret = 0;
228
229 (void) slprintf(ifkey, sizeof(ifkey), "IFNAME=ppp%d", unit);
230 kd.dptr = ifkey;
231 kd.dsize = strlen(ifkey);
232 vd = tdb_fetch(pppdb, kd);
233 if (vd.dptr != NULL) {
234 ret = vd.dsize == key.dsize
235 && memcmp(vd.dptr, key.dptr, vd.dsize) == 0;
236 free(vd.dptr);
237 }
238 return ret;
239 }
240
241 static int
get_default_epdisc(ep)242 get_default_epdisc(ep)
243 struct epdisc *ep;
244 {
245 struct hostent *hp;
246 u_int32_t addr;
247
248 if (get_first_hwaddr(ep->value, sizeof(ep->value))) {
249 ep->class = EPD_MAC;
250 ep->length = 6;
251 return 1;
252 }
253
254 /* see if our hostname corresponds to a reasonable IP address */
255 hp = gethostbyname(hostname);
256 if (hp != NULL) {
257 addr = *(u_int32_t *)hp->h_addr;
258 if (!bad_ip_adrs(addr)) {
259 addr = ntohl(addr);
260 if (!LOCAL_IP_ADDR(addr)) {
261 ep->class = EPD_IP;
262 set_ip_epdisc(ep, addr);
263 return 1;
264 }
265 }
266 }
267
268 return 0;
269 }
270 #endif /* HAVE_MULTILINK */
271
272 /*
273 * epdisc_to_str - make a printable string from an endpoint discriminator.
274 */
275
276 static char *endp_class_names[] = {
277 "null", "local", "IP", "MAC", "magic", "phone"
278 };
279
280 char *
epdisc_to_str(ep)281 epdisc_to_str(ep)
282 struct epdisc *ep;
283 {
284 static char str[MAX_ENDP_LEN*3+8];
285 u_char *p = ep->value;
286 int i, mask = 0;
287 char *q, c, c2;
288
289 if (ep->class == EPD_NULL && ep->length == 0)
290 return "null";
291 if (ep->class == EPD_IP && ep->length == 4) {
292 u_int32_t addr;
293
294 GETLONG(addr, p);
295 (void) slprintf(str, sizeof(str), "IP:%I", htonl(addr));
296 return str;
297 }
298
299 c = ':';
300 c2 = '.';
301 if (ep->class == EPD_MAC && ep->length == 6)
302 c2 = ':';
303 else if (ep->class == EPD_MAGIC && (ep->length % 4) == 0)
304 mask = 3;
305 q = str;
306 if (ep->class <= EPD_PHONENUM)
307 q += slprintf(q, sizeof(str)-1, "%s",
308 endp_class_names[ep->class]);
309 else
310 q += slprintf(q, sizeof(str)-1, "%d", ep->class);
311 c = ':';
312 for (i = 0; i < ep->length && i < MAX_ENDP_LEN; ++i) {
313 if ((i & mask) == 0) {
314 *q++ = c;
315 c = c2;
316 }
317 q += slprintf(q, str + sizeof(str) - q, "%.2x", ep->value[i]);
318 }
319 return str;
320 }
321
hexc_val(int c)322 static int hexc_val(int c)
323 {
324 if (c >= 'a')
325 return c - 'a' + 10;
326 if (c >= 'A')
327 return c - 'A' + 10;
328 return c - '0';
329 }
330
331 int
str_to_epdisc(ep,str)332 str_to_epdisc(ep, str)
333 struct epdisc *ep;
334 char *str;
335 {
336 int i, l;
337 char *p, *endp;
338
339 for (i = EPD_NULL; i <= EPD_PHONENUM; ++i) {
340 int sl = strlen(endp_class_names[i]);
341 if (strncasecmp(str, endp_class_names[i], sl) == 0) {
342 str += sl;
343 break;
344 }
345 }
346 if (i > EPD_PHONENUM) {
347 /* not a class name, try a decimal class number */
348 i = strtol(str, &endp, 10);
349 if (endp == str) {
350 option_error("cannot parse endpoint class in \"%s\"",
351 str);
352 return 0; /* can't parse class number */
353 }
354 str = endp;
355 }
356 ep->class = i;
357 if (*str == 0) {
358 ep->length = 0;
359 return 1;
360 }
361 if (*str != ':' && *str != '.') {
362 option_error("invalid class/value separator '%c'", *str);
363 return 0;
364 }
365 ++str;
366
367 if (i == EPD_IP) {
368 u_int32_t addr;
369 i = parse_dotted_ip(str, &addr);
370 if (i == 0 || str[i] != 0)
371 return 0;
372 set_ip_epdisc(ep, addr);
373 dbglog("str_to_epdisc -> %s", epdisc_to_str(ep));
374 return 1;
375 }
376 if (i == EPD_MAC &&
377 get_if_hwaddr(ep->value, sizeof(ep->value), str) >= 0) {
378 ep->length = 6;
379 dbglog("str_to_epdisc -> %s", epdisc_to_str(ep));
380 return 1;
381 }
382
383 p = str;
384 for (l = 0; l < MAX_ENDP_LEN; ++l) {
385 if (*str == 0)
386 break;
387 if (p <= str)
388 for (p = str; isxdigit(*p); ++p)
389 ;
390 i = p - str;
391 if (i == 0) {
392 option_error("no valid hex digits in \"%s\"", str);
393 return 0;
394 }
395 ep->value[l] = hexc_val(*str++);
396 if ((i & 1) == 0)
397 ep->value[l] = (ep->value[l] << 4) + hexc_val(*str++);
398 if (*str == ':' || *str == '.')
399 ++str;
400 }
401 if (*str != 0) {
402 option_error("too many bytes in value; max is %d",
403 MAX_ENDP_LEN);
404 return 0;
405 }
406 if (ep->class == EPD_MAC && l != 6) {
407 option_error("bad endpoint; MAC address must have 6 bytes");
408 return 0;
409 }
410 ep->length = l;
411 dbglog("str_to_epdisc -> %s", epdisc_to_str(ep));
412 return 1;
413 }
414