2 * multilink.c - support routines for multilink.
4 * Copyright (c) 2000-2002 Paul Mackerras. All rights reserved.
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
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.
17 * 3. Redistributions of any form whatsoever must retain the following
19 * "This product includes software developed by Paul Mackerras
20 * <paulus@samba.org>".
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.
36 #include <netinet/in.h>
44 bool endpoint_specified
; /* user gave explicit endpoint discriminator */
45 char *bundle_id
; /* identifier for our bundle */
46 char *blinks_id
; /* key for the list of links */
47 bool doing_multilink
; /* multilink was enabled and agreed to */
48 bool multilink_master
; /* we own the multilink bundle */
50 extern TDB_CONTEXT
*pppdb
;
53 static void make_bundle_links
__P((int append
));
54 static void remove_bundle_link
__P((void));
55 static void iterate_bundle_links
__P((void (*func
) __P((char *))));
57 static int get_default_epdisc
__P((struct epdisc
*));
58 static int parse_num
__P((char *str
, const char *key
, int *valp
));
59 static int owns_unit
__P((TDB_DATA pid
, int unit
));
61 #define set_ip_epdisc(ep, addr) do { \
63 ep->value[0] = addr >> 24; \
64 ep->value[1] = addr >> 16; \
65 ep->value[2] = addr >> 8; \
66 ep->value[3] = addr; \
69 #define LOCAL_IP_ADDR(addr) \
70 (((addr) & 0xff000000) == 0x0a000000 /* 10.x.x.x */ \
71 || ((addr) & 0xfff00000) == 0xac100000 /* 172.16.x.x */ \
72 || ((addr) & 0xffff0000) == 0xc0a80000) /* 192.168.x.x */
74 #define process_exists(n) (kill((n), 0) == 0 || errno != ESRCH)
79 lcp_options
*wo
= &lcp_wantoptions
[0];
80 lcp_options
*ao
= &lcp_allowoptions
[0];
85 /* if we're doing multilink, we have to negotiate MRRU */
87 /* mrru not specified, default to mru */
94 if (!wo
->neg_endpoint
&& !noendpoint
) {
95 /* get a default endpoint value */
96 wo
->neg_endpoint
= get_default_epdisc(&wo
->endpoint
);
101 * Make a new bundle or join us to an existing bundle
102 * if we are doing multilink.
107 lcp_options
*go
= &lcp_gotoptions
[0];
108 lcp_options
*ho
= &lcp_hisoptions
[0];
109 lcp_options
*ao
= &lcp_allowoptions
[0];
113 TDB_DATA key
, pid
, rec
;
115 if (doing_multilink
) {
116 /* have previously joined a bundle */
117 if (!go
->neg_mrru
|| !ho
->neg_mrru
) {
118 notice("oops, didn't get multilink on renegotiation");
119 lcp_close(0, "multilink required");
122 /* XXX should check the peer_authname and ho->endpoint
123 are the same as previously */
127 if (!go
->neg_mrru
|| !ho
->neg_mrru
) {
128 /* not doing multilink */
130 notice("oops, multilink negotiated only for receive");
131 mtu
= ho
->neg_mru
? ho
->mru
: PPP_MRU
;
135 /* already have a bundle */
136 cfg_bundle(0, 0, 0, 0);
137 netif_set_mtu(0, mtu
);
140 make_new_bundle(0, 0, 0, 0);
142 netif_set_mtu(0, mtu
);
149 * Find the appropriate bundle or join a new one.
150 * First we make up a name for the bundle.
151 * The length estimate is worst-case assuming every
152 * character has to be quoted.
154 l
= 4 * strlen(peer_authname
) + 10;
155 if (ho
->neg_endpoint
)
156 l
+= 3 * ho
->endpoint
.length
+ 8;
158 l
+= 3 * strlen(bundle_name
) + 2;
159 bundle_id
= malloc(l
);
161 novm("bundle identifier");
164 p
+= slprintf(p
, l
-1, "BUNDLE=\"%q\"", peer_authname
);
165 if (ho
->neg_endpoint
|| bundle_name
)
167 if (ho
->neg_endpoint
)
168 p
+= slprintf(p
, bundle_id
+l
-p
, "%s",
169 epdisc_to_str(&ho
->endpoint
));
171 p
+= slprintf(p
, bundle_id
+l
-p
, "/%v", bundle_name
);
173 /* Make the key for the list of links belonging to the bundle */
175 blinks_id
= malloc(l
+ 7);
176 if (blinks_id
== NULL
)
177 novm("bundle links key");
178 slprintf(blinks_id
, l
+ 7, "BUNDLE_LINKS=%s", bundle_id
+ 7);
181 * For demand mode, we only need to configure the bundle
182 * and attach the link.
184 mtu
= MIN(ho
->mrru
, ao
->mru
);
186 cfg_bundle(go
->mrru
, ho
->mrru
, go
->neg_ssnhf
, ho
->neg_ssnhf
);
187 netif_set_mtu(0, mtu
);
188 script_setenv("BUNDLE", bundle_id
+ 7, 1);
193 * Check if the bundle ID is already in the database.
197 key
.dptr
= bundle_id
;
198 key
.dsize
= p
- bundle_id
;
199 pid
= tdb_fetch(pppdb
, key
);
200 if (pid
.dptr
!= NULL
) {
201 /* bundle ID exists, see if the pppd record exists */
202 rec
= tdb_fetch(pppdb
, pid
);
203 if (rec
.dptr
!= NULL
&& rec
.dsize
> 0) {
204 /* make sure the string is null-terminated */
205 rec
.dptr
[rec
.dsize
-1] = 0;
206 /* parse the interface number */
207 parse_num(rec
.dptr
, "IFNAME=ppp", &unit
);
208 /* check the pid value */
209 if (!parse_num(rec
.dptr
, "PPPD_PID=", &pppd_pid
)
210 || !process_exists(pppd_pid
)
211 || !owns_unit(pid
, unit
))
219 /* attach to existing unit */
220 if (bundle_attach(unit
)) {
222 script_setenv("BUNDLE", bundle_id
+ 7, 0);
223 make_bundle_links(1);
225 info("Link attached to %s", ifname
);
228 /* attach failed because bundle doesn't exist */
231 /* we have to make a new bundle */
232 make_new_bundle(go
->mrru
, ho
->mrru
, go
->neg_ssnhf
, ho
->neg_ssnhf
);
234 netif_set_mtu(0, mtu
);
235 script_setenv("BUNDLE", bundle_id
+ 7, 1);
236 make_bundle_links(0);
238 info("New bundle %s created", ifname
);
239 multilink_master
= 1;
243 void mp_exit_bundle()
246 remove_bundle_link();
250 static void sendhup(char *str
)
254 if (parse_num(str
, "PPPD_PID=", &pid
) && pid
!= getpid()) {
256 dbglog("sending SIGHUP to process %d", pid
);
261 void mp_bundle_terminated()
265 bundle_terminating
= 1;
266 upper_layers_down(0);
267 notice("Connection terminated.");
271 script_unsetenv("IFNAME");
276 iterate_bundle_links(sendhup
);
277 key
.dptr
= blinks_id
;
278 key
.dsize
= strlen(blinks_id
);
279 tdb_delete(pppdb
, key
);
282 new_phase(PHASE_DEAD
);
285 multilink_master
= 0;
288 static void make_bundle_links(int append
)
295 key
.dptr
= blinks_id
;
296 key
.dsize
= strlen(blinks_id
);
297 slprintf(entry
, sizeof(entry
), "%s;", db_key
);
300 rec
= tdb_fetch(pppdb
, key
);
301 if (rec
.dptr
!= NULL
&& rec
.dsize
> 0) {
302 rec
.dptr
[rec
.dsize
-1] = 0;
303 if (strstr(rec
.dptr
, db_key
) != NULL
) {
304 /* already in there? strange */
305 warn("link entry already exists in tdb");
308 l
= rec
.dsize
+ strlen(entry
);
311 novm("bundle link list");
312 slprintf(p
, l
, "%s%s", rec
.dptr
, entry
);
314 warn("bundle link list not found");
316 if (rec
.dptr
!= NULL
)
320 rec
.dsize
= strlen(p
) + 1;
321 if (tdb_store(pppdb
, key
, rec
, TDB_REPLACE
))
322 error("couldn't %s bundle link list",
323 append
? "update": "create");
328 static void remove_bundle_link()
335 key
.dptr
= blinks_id
;
336 key
.dsize
= strlen(blinks_id
);
337 slprintf(entry
, sizeof(entry
), "%s;", db_key
);
339 rec
= tdb_fetch(pppdb
, key
);
340 if (rec
.dptr
== NULL
|| rec
.dsize
<= 0) {
341 if (rec
.dptr
!= NULL
)
345 rec
.dptr
[rec
.dsize
-1] = 0;
346 p
= strstr(rec
.dptr
, entry
);
348 q
= p
+ strlen(entry
);
351 rec
.dsize
= p
- rec
.dptr
+ l
;
352 if (tdb_store(pppdb
, key
, rec
, TDB_REPLACE
))
353 error("couldn't update bundle link list (removal)");
358 static void iterate_bundle_links(void (*func
)(char *))
360 TDB_DATA key
, rec
, pp
;
363 key
.dptr
= blinks_id
;
364 key
.dsize
= strlen(blinks_id
);
365 rec
= tdb_fetch(pppdb
, key
);
366 if (rec
.dptr
== NULL
|| rec
.dsize
<= 0) {
367 error("bundle link list not found (iterating list)");
368 if (rec
.dptr
!= NULL
)
374 while ((q
= strchr(p
, ';')) != NULL
) {
378 pp
= tdb_fetch(pppdb
, key
);
379 if (pp
.dptr
!= NULL
&& pp
.dsize
> 0) {
380 pp
.dptr
[pp
.dsize
-1] = 0;
391 parse_num(str
, key
, valp
)
399 p
= strstr(str
, key
);
402 i
= strtol(p
, &endp
, 10);
403 if (endp
!= p
&& (*endp
== 0 || *endp
== ';')) {
412 * Check whether the pppd identified by `key' still owns ppp unit `unit'.
423 slprintf(ifkey
, sizeof(ifkey
), "IFNAME=ppp%d", unit
);
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;
436 get_default_epdisc(ep
)
443 /* First try for an ethernet MAC address */
444 p
= get_first_ethernet();
445 if (p
!= 0 && get_if_hwaddr(ep
->value
, p
) >= 0) {
451 /* see if our hostname corresponds to a reasonable IP address */
452 hp
= gethostbyname(hostname
);
454 addr
= *(u_int32_t
*)hp
->h_addr
;
455 if (!bad_ip_adrs(addr
)) {
457 if (!LOCAL_IP_ADDR(addr
)) {
459 set_ip_epdisc(ep
, addr
);
469 * epdisc_to_str - make a printable string from an endpoint discriminator.
472 static char *endp_class_names
[] = {
473 "null", "local", "IP", "MAC", "magic", "phone"
480 static char str
[MAX_ENDP_LEN
*3+8];
481 u_char
*p
= ep
->value
;
485 if (ep
->class == EPD_NULL
&& ep
->length
== 0)
487 if (ep
->class == EPD_IP
&& ep
->length
== 4) {
491 slprintf(str
, sizeof(str
), "IP:%I", htonl(addr
));
497 if (ep
->class == EPD_MAC
&& ep
->length
== 6)
499 else if (ep
->class == EPD_MAGIC
&& (ep
->length
% 4) == 0)
502 if (ep
->class <= EPD_PHONENUM
)
503 q
+= slprintf(q
, sizeof(str
)-1, "%s",
504 endp_class_names
[ep
->class]);
506 q
+= slprintf(q
, sizeof(str
)-1, "%d", ep
->class);
508 for (i
= 0; i
< ep
->length
&& i
< MAX_ENDP_LEN
; ++i
) {
509 if ((i
& mask
) == 0) {
513 q
+= slprintf(q
, str
+ sizeof(str
) - q
, "%.2x", ep
->value
[i
]);
518 static int hexc_val(int c
)
528 str_to_epdisc(ep
, str
)
535 for (i
= EPD_NULL
; i
<= EPD_PHONENUM
; ++i
) {
536 int sl
= strlen(endp_class_names
[i
]);
537 if (strncasecmp(str
, endp_class_names
[i
], sl
) == 0) {
542 if (i
> EPD_PHONENUM
) {
543 /* not a class name, try a decimal class number */
544 i
= strtol(str
, &endp
, 10);
546 return 0; /* can't parse class number */
554 if (*str
!= ':' && *str
!= '.')
560 i
= parse_dotted_ip(str
, &addr
);
561 if (i
== 0 || str
[i
] != 0)
563 set_ip_epdisc(ep
, addr
);
566 if (i
== EPD_MAC
&& get_if_hwaddr(ep
->value
, str
) >= 0) {
572 for (l
= 0; l
< MAX_ENDP_LEN
; ++l
) {
576 for (p
= str
; isxdigit(*p
); ++p
)
581 ep
->value
[l
] = hexc_val(*str
++);
583 ep
->value
[l
] = (ep
->value
[l
] << 4) + hexc_val(*str
++);
584 if (*str
== ':' || *str
== '.')
587 if (*str
!= 0 || (ep
->class == EPD_MAC
&& l
!= 6))