4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
22 * PPPoE Server-mode daemon option parsing.
24 * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
25 * Use is subject to license terms.
26 * Copyright (c) 2016 by Delphix. All rights reserved.
35 #include <sys/types.h>
43 #include <sys/socket.h>
45 #include <netinet/in.h>
46 #include <netinet/if_ether.h>
47 #include <net/sppptun.h>
52 #define MAX_KEYWORD 4096 /* Maximum token length */
53 #define MAX_NEST 32 /* Maximum ${$sub} nesting */
54 #define MAXARGS 256 /* Maximum number of pppd arguments */
57 * Client filter entry. These are linked in *reverse* order so that
58 * the DAG created by file inclusion nesting works as expected. Since
59 * the administrator who wrote the configuration expects "first
60 * match," this means that tests against the filter list must actually
64 struct filter_entry
*fe_prev
; /* Previous filter in list */
65 struct ether_addr fe_mac
; /* MAC address */
66 struct ether_addr fe_mask
; /* Mask for above address test */
67 uchar_t fe_isexcept
; /* invert sense; exclude matching clients */
68 uchar_t fe_prevcopy
; /* fe_prev points to copied list */
69 uchar_t fe_unused
[2]; /* padding */
73 * Note: I would like to make the strings and filters here const, but
74 * I can't because they have to be passed to free() during parsing. I
75 * could work around this with offsetof() or data copies, but it's not
78 struct service_entry
{
79 const char *se_name
; /* Name of service */
80 struct filter_entry
*se_flist
; /* Pointer to list of client filters */
81 uint_t se_flags
; /* SEF_* flags (below) */
82 int se_debug
; /* Debug level (0=nodebug) */
83 char *se_server
; /* Server (AC) name */
84 char *se_pppd
; /* Options for pppd */
85 char *se_path
; /* Path to pppd executable */
86 char *se_extra
; /* Extra options */
87 char *se_log
; /* Log file */
88 uid_t se_uid
; /* User ID */
89 gid_t se_gid
; /* Group ID */
92 #define SEF_WILD 0x00000001 /* Offer in wildcard reply */
93 #define SEF_NOWILD 0x00000002 /* Don't offer in wildcard */
94 #define SEF_CFLIST 0x00000004 /* se_flist copied from global */
95 #define SEF_CSERVER 0x00000008 /* se_server copied from global */
96 #define SEF_CPPPD 0x00000010 /* se_pppd copied from global */
97 #define SEF_CPATH 0x00000020 /* se_path copied from global */
98 #define SEF_CEXTRA 0x00000040 /* se_extra copied from global */
99 #define SEF_CLOG 0x00000080 /* se_log copied from global */
100 #define SEF_UIDSET 0x00000100 /* se_uid has been set */
101 #define SEF_GIDSET 0x00000200 /* se_gid has been set */
102 #define SEF_DEBUGCLR 0x00000400 /* do not add se_debug from global */
103 #define SEF_CDEV 0x00000800 /* copied devs (parse only) */
106 * One of these is allocated per lower-level stream (device) that is
107 * referenced by the configuration files. The queries are received
108 * per device, and this structure allows us to find all of the
109 * services that correspond to that device.
111 struct device_entry
{
113 const struct service_entry
**de_services
;
118 * This is the parsed configuration. While a new configuration is
119 * being read, this is kept around until the new configuration is
120 * ready, and then it is discarded in one operation. It has an array
121 * of device entries (as above) -- one per referenced lower stream --
122 * and a pointer to the allocated parser information. The latter is
123 * kept around because we reuse pointers rather than reallocating and
124 * copying the data. There are thus multiple aliases to the dynamic
125 * data, and the "owner" (for purposes of freeing the storage) is
126 * considered to be this 'junk' list.
128 struct option_state
{
129 const struct device_entry
*os_devices
;
131 struct per_file
*os_pfjunk
; /* Kept for deallocation */
132 char **os_evjunk
; /* ditto */
136 * This is the root pointer to the current parsed options.
137 * This cannot be const because it's passed to free() when reparsing
140 static struct option_state
*cur_options
;
142 /* Global settings for module-wide options. */
143 static struct service_entry glob_svc
;
146 * *******************************************************************
147 * Data structures generated during parsing.
150 /* List of device names attached to one service */
152 struct device_list
*dl_next
;
153 const char *dl_name
; /* Name of one device */
156 /* Entry for a single defined service. */
157 struct service_list
{
158 struct service_entry sl_entry
; /* Parsed service data */
159 struct service_list
*sl_next
; /* Next service entry */
160 struct parse_state
*sl_parse
; /* Back pointer to state */
161 struct device_list
*sl_dev
; /* List of devices */
162 int sl_serial
; /* Serial number (conflict resolve) */
164 #define SESERIAL(x) ((struct service_list *)&(x))->sl_serial
165 #define ISGLOBAL(x) ((x) == &(x)->sl_parse->ps_cfile->pf_global)
168 * Structure allocated for each file opened. File nesting is chained
169 * in reverse order so that global option scoping works as expected.
172 struct per_file
*pf_prev
; /* Back chain */
173 struct service_list pf_global
; /* Global (default) service context */
174 struct service_list
*pf_svc
; /* List of services */
175 struct service_list
*pf_svc_last
;
176 FILE *pf_input
; /* File for input */
177 const char *pf_name
; /* File name */
178 int pf_nsvc
; /* Count of services */
181 /* State of parser */
183 ksDefault
, ksService
, ksDevice
, ksClient
, ksClientE
, ksServer
,
184 ksPppd
, ksFile
, ksPath
, ksExtra
, ksLog
, ksUser
, ksGroup
188 * Global parser state. There is one of these structures, and it
189 * exists only while actively parsing configuration files.
192 enum key_state ps_state
; /* Parser state */
193 int ps_serial
; /* Service serial number */
194 struct per_file
*ps_files
; /* Parsed files */
195 struct per_file
*ps_cfile
; /* Current file */
196 struct service_list
*ps_csvc
; /* Current service */
197 struct device_list
*ps_star
; /* Wildcard device */
198 int ps_flags
; /* PSF_* below */
199 char **ps_evlist
; /* allocated environment variables */
200 int ps_evsize
; /* max length; for realloc */
203 #define PSF_PERDEV 0x0001 /* In a per-device file */
204 #define PSF_SETLEVEL 0x0002 /* Set log level along the way */
206 /* Should be in a library somewhere. */
208 strsave(const char *str
)
214 newstr
= (char *)malloc(strlen(str
) + 1);
216 (void) strcpy(newstr
, str
);
221 * Stop defining current service and revert to global definition.
222 * This resolves any implicit references to global options by copying
223 * ("inheriting") from the current global state.
226 close_service(struct service_list
*slp
)
228 struct parse_state
*psp
;
229 struct per_file
*cfile
;
230 struct service_entry
*sep
;
231 struct service_entry
*sedefp
;
232 struct filter_entry
*fep
;
236 cfile
= psp
->ps_cfile
;
238 /* If no current file, then nothing to close. */
242 sep
= &slp
->sl_entry
;
245 * Fix up filter pointers to make DAG. First, locate
246 * the end of the filter list.
248 if (sep
->se_flags
& SEF_CFLIST
) {
249 sep
->se_flist
= fep
= NULL
;
251 for (fep
= sep
->se_flist
; fep
!= NULL
; fep
= fep
->fe_prev
)
252 if (fep
->fe_prev
== NULL
|| fep
->fe_prevcopy
) {
257 if (slp
== &cfile
->pf_global
) {
259 * If we're in a global context, then we're about to
260 * open a new service, so it's time to fix up the
261 * filter list so that it's usable as a reference.
262 * Loop through files from which we were included, and
263 * link up filters. Note: closure may occur more than
266 /* We don't inherit from ourselves. */
267 cfile
= cfile
->pf_prev
;
268 while (cfile
!= NULL
) {
270 sep
->se_flist
= fep
=
271 cfile
->pf_global
.sl_entry
.se_flist
;
272 sep
->se_flags
|= SEF_CFLIST
;
273 } else if (fep
->fe_prev
== NULL
) {
275 cfile
->pf_global
.sl_entry
.se_flist
;
276 fep
->fe_prevcopy
= 1;
278 cfile
= cfile
->pf_prev
;
282 * Loop through default options in current and all
283 * enclosing include files. Inherit options.
285 logdbg("service %s ends", slp
->sl_entry
.se_name
);
286 while (cfile
!= NULL
) {
287 /* Inherit from global service options. */
288 if (slp
->sl_dev
== NULL
) {
289 slp
->sl_dev
= cfile
->pf_global
.sl_dev
;
290 sep
->se_flags
|= SEF_CDEV
;
292 sedefp
= &cfile
->pf_global
.sl_entry
;
294 sep
->se_flist
= fep
= sedefp
->se_flist
;
295 sep
->se_flags
|= SEF_CFLIST
;
296 } else if (fep
->fe_prev
== NULL
) {
297 fep
->fe_prev
= sedefp
->se_flist
;
298 fep
->fe_prevcopy
= 1;
300 if (sep
->se_server
== NULL
) {
301 sep
->se_server
= sedefp
->se_server
;
302 sep
->se_flags
|= SEF_CSERVER
;
304 if (sep
->se_pppd
== NULL
) {
305 sep
->se_pppd
= sedefp
->se_pppd
;
306 sep
->se_flags
|= SEF_CPPPD
;
308 if (sep
->se_path
== NULL
) {
309 sep
->se_path
= sedefp
->se_path
;
310 sep
->se_flags
|= SEF_CPATH
;
312 if (sep
->se_extra
== NULL
) {
313 sep
->se_extra
= sedefp
->se_extra
;
314 sep
->se_flags
|= SEF_CEXTRA
;
316 if (sep
->se_log
== NULL
) {
317 sep
->se_log
= sedefp
->se_log
;
318 sep
->se_flags
|= SEF_CLOG
;
320 if (!(sep
->se_flags
& SEF_UIDSET
) &&
321 (sedefp
->se_flags
& SEF_UIDSET
)) {
322 sep
->se_uid
= sedefp
->se_uid
;
323 sep
->se_flags
|= SEF_UIDSET
;
325 if (!(sep
->se_flags
& SEF_GIDSET
) &&
326 (sedefp
->se_flags
& SEF_GIDSET
)) {
327 sep
->se_gid
= sedefp
->se_gid
;
328 sep
->se_flags
|= SEF_GIDSET
;
330 if (!(sep
->se_flags
& (SEF_WILD
|SEF_NOWILD
)))
331 sep
->se_flags
|= sedefp
->se_flags
&
332 (SEF_WILD
|SEF_NOWILD
);
333 if (!(sep
->se_flags
& SEF_DEBUGCLR
)) {
334 sep
->se_debug
+= sedefp
->se_debug
;
335 sep
->se_flags
|= sedefp
->se_flags
&
338 cfile
= cfile
->pf_prev
;
341 /* Revert to global definitions. */
342 psp
->ps_csvc
= &psp
->ps_cfile
->pf_global
;
345 /* Discard a dynamic device list */
347 free_device_list(struct device_list
*dlp
)
349 struct device_list
*dln
;
351 while (dlp
!= NULL
) {
359 * Handle "service <name>" -- finish up previous service definition
360 * (if any) by copying from global state where necessary, and start
361 * defining new service.
364 set_service(struct service_list
*slp
, const char *str
)
366 struct parse_state
*psp
;
367 struct per_file
*cfile
;
369 /* Finish current service */
372 /* Start new service */
374 slp
= (struct service_list
*)calloc(sizeof (*slp
) + strlen(str
) + 1,
377 logerr("no memory for service \"%s\"", str
);
381 /* Add to end of list */
382 cfile
= psp
->ps_cfile
;
383 if (cfile
->pf_svc_last
== NULL
)
386 cfile
->pf_svc_last
->sl_next
= slp
;
387 cfile
->pf_svc_last
= slp
;
390 /* Fill in initial service entry */
391 slp
->sl_entry
.se_name
= (const char *)(slp
+1);
392 (void) strcpy((char *)(slp
+1), str
);
393 logdbg("service %s begins", slp
->sl_entry
.se_name
);
394 slp
->sl_serial
= psp
->ps_serial
++;
397 /* This is now the current service that we're defining. */
403 * Handle both "wildcard" and "nowildcard" options.
406 set_wildcard(struct service_list
*slp
, const char *str
)
408 /* Allow global context to switch back and forth without error. */
409 if (!ISGLOBAL(slp
) &&
410 (slp
->sl_entry
.se_flags
& (SEF_WILD
|SEF_NOWILD
))) {
411 logdbg("%s: extra \"%s\" ignored",
412 slp
->sl_parse
->ps_cfile
->pf_name
, str
);
415 slp
->sl_entry
.se_flags
=
416 (slp
->sl_entry
.se_flags
& ~(SEF_WILD
|SEF_NOWILD
)) |
417 (*str
== 'n' ? SEF_NOWILD
: SEF_WILD
);
422 * Handle "debug" option.
426 set_debug(struct service_list
*slp
, const char *str
)
428 slp
->sl_entry
.se_debug
++;
429 if (ISGLOBAL(slp
) && (slp
->sl_parse
->ps_flags
& PSF_SETLEVEL
)) {
430 log_level
= slp
->sl_entry
.se_debug
;
436 * Handle "nodebug" option.
440 set_nodebug(struct service_list
*slp
, const char *str
)
442 slp
->sl_entry
.se_flags
|= SEF_DEBUGCLR
;
443 slp
->sl_entry
.se_debug
= 0;
444 if (ISGLOBAL(slp
) && (slp
->sl_parse
->ps_flags
& PSF_SETLEVEL
)) {
445 log_level
= slp
->sl_entry
.se_debug
;
451 * Handle all plain string options; "server", "pppd", "path", "extra",
455 set_string(struct service_list
*slp
, const char *str
)
459 assert(!(slp
->sl_entry
.se_flags
&
460 (SEF_CSERVER
|SEF_CPPPD
|SEF_CPATH
|SEF_CEXTRA
|SEF_CLOG
)));
461 switch (slp
->sl_parse
->ps_state
) {
463 cpp
= &slp
->sl_entry
.se_server
;
466 cpp
= &slp
->sl_entry
.se_pppd
;
469 cpp
= &slp
->sl_entry
.se_path
;
472 cpp
= &slp
->sl_entry
.se_extra
;
475 cpp
= &slp
->sl_entry
.se_log
;
488 * Handle "file <name>" option. Close out current service (if any)
489 * and begin parsing from new file.
492 set_file(struct service_list
*slp
, const char *str
)
495 struct per_file
*pfp
;
496 struct parse_state
*psp
;
500 if ((fp
= fopen(str
, "r")) == NULL
) {
501 logwarn("%s: %s: %s", slp
->sl_parse
->ps_cfile
->pf_name
, str
,
505 pfp
= (struct per_file
*)calloc(sizeof (*pfp
) + strlen(str
) + 1, 1);
507 logerr("no memory for parsing file %s", str
);
511 logdbg("config file %s open", str
);
513 /* Fill in new file structure. */
514 pfp
->pf_name
= (const char *)(pfp
+1);
515 (void) strcpy((char *)(pfp
+1), str
);
518 pfp
->pf_prev
= psp
->ps_cfile
;
521 /* Start off in global context for this file. */
522 psp
->ps_csvc
= &pfp
->pf_global
;
523 pfp
->pf_global
.sl_parse
= psp
;
524 pfp
->pf_global
.sl_entry
.se_name
= "<global>";
529 * Handle "device <list>" option.
532 set_device(struct service_list
*slp
, const char *str
)
534 struct parse_state
*psp
= slp
->sl_parse
;
535 struct device_list
*dlp
;
536 struct device_list
*dln
;
537 struct device_list
**dlpp
;
541 /* Can't use this option in the per-device files. */
542 if (psp
->ps_flags
& PSF_PERDEV
) {
543 logerr("\"device %s\" ignored in %s", str
,
544 psp
->ps_cfile
->pf_name
);
548 if (strcmp(str
, "*") == 0 || strcmp(str
, "all") == 0) {
549 if (!(slp
->sl_entry
.se_flags
& SEF_CDEV
))
550 free_device_list(slp
->sl_dev
);
551 slp
->sl_dev
= psp
->ps_star
;
552 slp
->sl_entry
.se_flags
|= SEF_CDEV
;
556 while (isspace(*str
) || *str
== ',')
561 while (*str
!= '\0' && !isspace(*str
) && *str
!= ',')
564 if ((len
== 1 && *cp
== '*') ||
565 (len
== 3 && strncmp(cp
, "all", 3) == 0)) {
566 logerr("%s: cannot use %.*s in device list",
567 psp
->ps_cfile
->pf_name
, len
, cp
);
570 dln
= (struct device_list
*)malloc(sizeof (*dln
) +
573 logerr("no memory for device name");
576 dln
->dl_name
= (const char *)(dln
+ 1);
577 /* Cannot use strcpy because cp isn't terminated. */
578 (void) memcpy(dln
+ 1, cp
, len
);
579 ((char *)(dln
+ 1))[len
] = '\0';
580 logdbg("%s: device %s", psp
->ps_cfile
->pf_name
,
583 dlpp
= &dln
->dl_next
;
588 if (!(slp
->sl_entry
.se_flags
& SEF_CDEV
))
589 while (*dlpp
!= NULL
)
590 dlpp
= &(*dlpp
)->dl_next
;
592 slp
->sl_entry
.se_flags
&= ~SEF_CDEV
;
599 * Handle <list> portion of "client [except] <list>" option. Attach
600 * to list of filters in reverse order.
603 set_client(struct service_list
*slp
, const char *str
)
605 struct parse_state
*psp
= slp
->sl_parse
;
606 struct filter_entry
*fep
;
607 struct filter_entry
*fen
;
610 char hbuf
[MAXHOSTNAMELEN
];
611 struct ether_addr ea
;
612 struct ether_addr mask
;
617 fep
= slp
->sl_entry
.se_flist
;
619 while (isspace(*str
) || *str
== ',')
624 while (*str
!= '\0' && !isspace(*str
) && *str
!= ',')
627 (void) memcpy(hbuf
, cp
, len
);
629 mcp
= mask
.ether_addr_octet
;
630 mcp
[0] = mcp
[1] = mcp
[2] = mcp
[3] = mcp
[4] = mcp
[5] = 0xFF;
631 if (ether_hostton(hbuf
, &ea
) != 0) {
632 ucp
= ea
.ether_addr_octet
;
634 if (ucp
>= ea
.ether_addr_octet
+ sizeof (ea
))
642 *ucp
= hexdecode(*cp
++);
643 if (cp
< str
&& isxdigit(*cp
)) {
651 if (*cp
!= ':' || cp
+ 1 == str
)
657 logerr("%s: illegal Ethernet address %.*s",
658 psp
->ps_cfile
->pf_name
, len
, cp
);
662 fen
= (struct filter_entry
*)malloc(sizeof (*fen
));
664 logerr("unable to allocate memory for filter");
667 fen
->fe_isexcept
= psp
->ps_state
== ksClientE
;
668 fen
->fe_prevcopy
= 0;
669 (void) memcpy(&fen
->fe_mac
, &ea
, sizeof (fen
->fe_mac
));
670 (void) memcpy(&fen
->fe_mask
, &mask
, sizeof (fen
->fe_mask
));
674 slp
->sl_entry
.se_flist
= fep
;
679 * Handle "user <name>" option.
682 set_user(struct service_list
*slp
, const char *str
)
688 if ((pw
= getpwnam(str
)) == NULL
) {
689 uid
= (uid_t
)strtol(str
, &cp
, 0);
690 if (str
== cp
|| *cp
!= '\0') {
691 logerr("%s: bad user name \"%s\"",
692 slp
->sl_parse
->ps_cfile
->pf_name
, str
);
698 slp
->sl_entry
.se_uid
= uid
;
703 logdbg("%s: not root; ignoring attempt to set UID %d (%s)",
704 slp
->sl_parse
->ps_cfile
->pf_name
, uid
, str
);
707 slp
->sl_entry
.se_flags
|= SEF_UIDSET
;
712 * Handle "group <name>" option.
715 set_group(struct service_list
*slp
, const char *str
)
721 if ((gr
= getgrnam(str
)) == NULL
) {
722 gid
= (gid_t
)strtol(str
, &cp
, 0);
723 if (str
== cp
|| *cp
!= '\0') {
724 logerr("%s: bad group name \"%s\"",
725 slp
->sl_parse
->ps_cfile
->pf_name
, str
);
731 slp
->sl_entry
.se_gid
= gid
;
733 logdbg("%s: not root; ignoring attempt to set GID %d (%s)",
734 slp
->sl_parse
->ps_cfile
->pf_name
, gid
, str
);
737 slp
->sl_entry
.se_flags
|= SEF_GIDSET
;
742 * This state machine is used to parse the configuration files. The
743 * "kwe_in" is the state in which the keyword is recognized. The
744 * "kwe_out" is the state that the keyword produces.
747 const char *kwe_word
;
748 enum key_state kwe_in
;
749 enum key_state kwe_out
;
750 int (*kwe_func
)(struct service_list
*slp
, const char *str
);
753 static const struct kw_entry key_list
[] = {
754 { "service", ksDefault
, ksService
, NULL
},
755 { "device", ksDefault
, ksDevice
, NULL
},
756 { "client", ksDefault
, ksClient
, NULL
},
757 { "except", ksClient
, ksClientE
, NULL
},
758 { "wildcard", ksDefault
, ksDefault
, set_wildcard
},
759 { "nowildcard", ksDefault
, ksDefault
, set_wildcard
},
760 { "server", ksDefault
, ksServer
, NULL
},
761 { "pppd", ksDefault
, ksPppd
, NULL
},
762 { "debug", ksDefault
, ksDefault
, set_debug
},
763 { "nodebug", ksDefault
, ksDefault
, set_nodebug
},
764 { "file", ksDefault
, ksFile
, NULL
},
765 { "path", ksDefault
, ksPath
, NULL
},
766 { "extra", ksDefault
, ksExtra
, NULL
},
767 { "log", ksDefault
, ksLog
, NULL
},
768 { "user", ksDefault
, ksUser
, NULL
},
769 { "group", ksDefault
, ksGroup
, NULL
},
770 /* Wildcards only past this point. */
771 { "", ksService
, ksDefault
, set_service
},
772 { "", ksDevice
, ksDefault
, set_device
},
773 { "", ksClient
, ksDefault
, set_client
},
774 { "", ksClientE
, ksDefault
, set_client
},
775 { "", ksServer
, ksDefault
, set_string
},
776 { "", ksPppd
, ksDefault
, set_string
},
777 { "", ksFile
, ksDefault
, set_file
},
778 { "", ksPath
, ksDefault
, set_string
},
779 { "", ksExtra
, ksDefault
, set_string
},
780 { "", ksLog
, ksDefault
, set_string
},
781 { "", ksUser
, ksDefault
, set_user
},
782 { "", ksGroup
, ksDefault
, set_group
},
783 { NULL
, ksDefault
, ksDefault
, NULL
}
787 * Produce a string for the keyword that would have gotten us into the
791 after_key(enum key_state kstate
)
793 const struct kw_entry
*kep
;
795 for (kep
= key_list
; kep
->kwe_word
!= NULL
; kep
++)
796 if (kep
->kwe_out
== kstate
)
797 return (kep
->kwe_word
);
802 * Handle end-of-file processing -- close service, close file, revert
803 * to global context in previous include file nest level.
806 file_end(struct parse_state
*psp
)
808 struct per_file
*pfp
;
810 /* Must not be in the middle of parsing a multi-word sequence now. */
811 if (psp
->ps_state
!= ksDefault
) {
812 logerr("%s ends with \"%s\"", psp
->ps_cfile
->pf_name
,
813 after_key(psp
->ps_state
));
814 psp
->ps_state
= ksDefault
;
816 close_service(psp
->ps_csvc
);
817 if ((pfp
= psp
->ps_cfile
) != NULL
) {
818 /* Put this file on the list of finished files. */
819 psp
->ps_cfile
= pfp
->pf_prev
;
820 pfp
->pf_prev
= psp
->ps_files
;
822 if (pfp
->pf_input
!= NULL
) {
823 logdbg("file %s closed", pfp
->pf_name
);
824 (void) fclose(pfp
->pf_input
);
825 pfp
->pf_input
= NULL
;
828 /* Back up to previous file, if any, and set global context. */
829 if ((pfp
= psp
->ps_cfile
) != NULL
)
830 psp
->ps_csvc
= &pfp
->pf_global
;
835 * Dispatch a single keyword against the parser state machine or
836 * handle an environment variable assignment. The input is a string
837 * containing the single word to be dispatched.
840 dispatch_keyword(struct parse_state
*psp
, const char *keybuf
)
842 const struct kw_entry
*kep
;
850 for (kep
= key_list
; kep
->kwe_word
!= NULL
; kep
++) {
851 if (kep
->kwe_in
== psp
->ps_state
&&
852 (*kep
->kwe_word
== '\0' ||
853 strcasecmp(kep
->kwe_word
, keybuf
) == 0)) {
854 if (kep
->kwe_func
!= NULL
)
855 retv
= (*kep
->kwe_func
)(psp
->ps_csvc
, keybuf
);
856 psp
->ps_state
= kep
->kwe_out
;
860 if (strchr(keybuf
, '=') != NULL
) {
861 if ((cp
= strsave(keybuf
)) == NULL
) {
862 logerr("no memory to save %s", keybuf
);
865 len
= (strchr(cp
, '=') - cp
) + 1;
866 if ((evlist
= psp
->ps_evlist
) == NULL
) {
867 psp
->ps_evlist
= evlist
=
868 (char **)malloc(8 * sizeof (*evlist
));
869 if (evlist
== NULL
) {
870 logerr("no memory for evlist");
875 evlist
[0] = evlist
[1] = NULL
;
877 while ((env
= *evlist
) != NULL
) {
878 if (strncmp(cp
, env
, len
) == 0)
883 evlist
-psp
->ps_evlist
>= psp
->ps_evsize
-1) {
884 evlist
= (char **)realloc(psp
->ps_evlist
,
885 (psp
->ps_evsize
+ 8) * sizeof (*evlist
));
886 if (evlist
== NULL
) {
887 logerr("cannot realloc evlist to %d",
892 psp
->ps_evlist
= evlist
;
893 evlist
+= psp
->ps_evsize
- 1;
898 logdbg("setenv \"%s\"", cp
);
904 logerr("%s: unknown keyword '%s'", psp
->ps_cfile
->pf_name
, keybuf
);
909 * Modified version of standard getenv; looks in locally-stored
910 * environment first. This function exists because we need to be able
911 * to revert to the original environment during a reread (SIGHUP), and
912 * the putenv() function overwrites that environment.
915 my_getenv(struct parse_state
*psp
, char *estr
)
920 if (psp
!= NULL
&& (evlist
= psp
->ps_evlist
) != NULL
) {
922 while ((ent
= *evlist
++) != NULL
) {
923 if (strncmp(ent
, estr
, elen
) == 0 &&
925 return (ent
+ elen
+ 1);
928 return (getenv(estr
));
932 * Expand an environment variable at the end of current buffer and
933 * return pointer to next spot in buffer for character append. psp
934 * context may be null.
937 env_replace(struct parse_state
*psp
, char *keybuf
, char kwstate
)
942 if ((cp
= strrchr(keybuf
, kwstate
)) != NULL
) {
943 if ((cpe
= my_getenv(psp
, cp
+ 1)) != NULL
) {
945 (void) strncat(cp
, cpe
,
946 MAX_KEYWORD
- (cp
- keybuf
) - 1);
947 keybuf
[MAX_KEYWORD
- 1] = '\0';
950 logerr("unknown variable \"%s\"", cp
+ 1);
953 /* Should not occur. */
954 cp
= keybuf
+ strlen(keybuf
);
960 * Given a character-at-a-time input function, get a delimited keyword
961 * from the input. This function handles the usual escape sequences,
962 * quoting, commenting, and environment variable expansion.
964 * The standard wordexp(3C) function isn't used here because the POSIX
965 * definition is hard to use, and the Solaris implementation is
966 * resource-intensive and insecure. The "hard-to-use" part is that
967 * wordexp expands only variables from the environment, and can't
968 * handle an environment overlay. Instead, the caller must use the
969 * feeble putenv/getenv interface, and rewinding to the initial
970 * environment without leaking storage is hard. The Solaris
971 * implementation invokes an undocumented extensions via
972 * fork/exec("/bin/ksh -\005 %s") for every invocation, and gathers
973 * the expanded result with pipe. This makes it slow to execute and
974 * exposes the string being expanded to users with access to "ps -f."
976 * psp may be null; it's used only for environment variable expansion.
977 * Input "flag" is 1 to ignore EOL, '#', and '$'; 0 for normal file parsing.
980 * 0 - keyword parsed.
981 * 1 - end of file; no keyword.
982 * 2 - end of file after this keyword.
985 getkeyword(struct parse_state
*psp
, char *keybuf
, int keymax
,
986 int (*nextchr
)(void *), void *arg
, int flag
)
988 char varnest
[MAX_NEST
];
994 static const char escstr
[] = "a\ab\bf\fn\nr\r";
997 keymax
--; /* Account for trailing NUL byte */
1003 ichr
= (*nextchr
)(arg
);
1007 case '\\': /* Start of unquoted escape sequence */
1008 case '|': /* Start of escape sequence in double quotes */
1009 case '~': /* Start of escape sequence in single quotes */
1010 /* Convert the character if we can. */
1013 else if (isalpha(chr
) &&
1014 (cp
= strchr(escstr
, chr
)) != NULL
)
1016 /* Revert to previous state */
1029 case '"': /* In double-quote string */
1030 if (!flag
&& chr
== '$') {
1031 /* Handle variable expansion. */
1037 case '\'': /* In single-quote string */
1039 /* Handle start of escape sequence */
1040 kwstate
= kwstate
== '"' ? '|' : '~';
1044 if (chr
== kwstate
) {
1045 /* End of quoted string; revert to normal */
1050 case '$': /* Start of unquoted variable name */
1051 case '%': /* Start of variable name in quoted string */
1053 /* Variable name is bracketed. */
1055 kwstate
== '$' ? '{' : '[';
1058 *kbp
++ = kwstate
= kwstate
== '$' ? '+' : '*';
1060 case '+': /* Gathering unquoted variable name */
1061 case '*': /* Gathering variable name in quoted string */
1063 vnp
< varnest
+ sizeof (varnest
)) {
1069 if (!isalnum(chr
) && chr
!= '_' &&
1070 chr
!= '.' && chr
!= '-') {
1072 kbp
= env_replace(psp
, keybuf
, kwstate
);
1076 kwstate
= kwstate
== '+' ?
1078 /* Go reinterpret in new context */
1082 case '{': /* Gathering bracketed, unquoted var name */
1083 case '[': /* Gathering bracketed, quoted var name */
1086 kbp
= env_replace(psp
, keybuf
, kwstate
);
1087 kwstate
= kwstate
== '{' ? 'A' : '"';
1091 case '#': /* Comment before word state */
1092 case '@': /* Comment after word state */
1093 if (chr
== '\n' || chr
== '\r' || ichr
== EOF
) {
1094 /* At end of line, revert to previous state */
1095 kwstate
= kwstate
== '#' ? '\0' : ' ';
1101 case '\0': /* Initial state; no word seen yet. */
1102 if (ichr
== EOF
|| isspace(chr
)) {
1103 chr
= '\0'; /* Skip over leading spaces */
1108 chr
= '\0'; /* Skip over comments */
1111 /* Start of keyword seen. */
1114 default: /* Middle of keyword parsing. */
1117 if (isspace(chr
)) { /* Space terminates word */
1121 if (chr
== '"' || chr
== '\'' || chr
== '\\') {
1122 kwstate
= chr
; /* Begin quote or escape */
1126 if (flag
) /* Allow ignore; for string reparse */
1128 if (chr
== '#') { /* Comment terminates word */
1129 kwstate
= '@'; /* Must consume comment also */
1134 kwstate
= '$'; /* Begin variable expansion */
1140 * If we've reached a space at the end of the word,
1143 if (ichr
== EOF
|| kwstate
== ' ')
1146 * If there's a character to store and space
1147 * available, then add it to the string
1149 if (chr
!= '\0' && kbp
< keybuf
+ keymax
)
1156 return (kwstate
== '\0' ? 1 : 2);
1162 * Fetch words from current file until all files are closed. Handles
1166 parse_from_file(struct parse_state
*psp
)
1168 char keybuf
[MAX_KEYWORD
];
1171 while (psp
->ps_cfile
!= NULL
&& psp
->ps_cfile
->pf_input
!= NULL
) {
1172 retv
= getkeyword(psp
, keybuf
, sizeof (keybuf
),
1173 (int (*)(void *))fgetc
, (void *)psp
->ps_cfile
->pf_input
,
1177 (void) dispatch_keyword(psp
, keybuf
);
1185 * Open and parse named file. This is for the predefined
1186 * configuration files in /etc/ppp -- it's not an error if any of
1187 * these are missing.
1190 parse_file(struct parse_state
*psp
, const char *fname
)
1194 /* It's ok if any of these files are missing. */
1195 if (stat(fname
, &sb
) == -1 && errno
== ENOENT
)
1197 if (set_file(psp
->ps_csvc
, fname
) == 0)
1198 parse_from_file(psp
);
1202 * Dispatch keywords from command line. Handles any files included
1206 parse_arg_list(struct parse_state
*psp
, int argc
, char **argv
)
1208 /* The first argument (program name) can be null. */
1211 while (--argc
>= 0) {
1212 (void) dispatch_keyword(psp
, *++argv
);
1213 if (psp
->ps_cfile
->pf_input
!= NULL
)
1214 parse_from_file(psp
);
1218 /* Count length of dynamic device list */
1220 count_devs(struct device_list
*dlp
)
1225 for (; dlp
!= NULL
; dlp
= dlp
->dl_next
)
1230 /* Count number of devices named in entire file. */
1232 count_per_file(struct per_file
*pfp
)
1234 struct service_list
*slp
;
1237 for (; pfp
!= NULL
; pfp
= pfp
->pf_prev
) {
1238 ndevs
+= count_devs(pfp
->pf_global
.sl_dev
);
1239 for (slp
= pfp
->pf_svc
; slp
!= NULL
; slp
= slp
->sl_next
)
1240 if (!(slp
->sl_entry
.se_flags
& SEF_CDEV
))
1241 ndevs
+= count_devs(slp
->sl_dev
);
1246 /* Write device names into linear array. */
1247 static const char **
1248 devs_to_list(struct device_list
*dlp
, const char **dnames
)
1250 for (; dlp
!= NULL
; dlp
= dlp
->dl_next
)
1251 *dnames
++ = dlp
->dl_name
;
1255 /* Write all device names from file into a linear array. */
1256 static const char **
1257 per_file_to_list(struct per_file
*pfp
, const char **dnames
)
1259 struct service_list
*slp
;
1261 for (; pfp
!= NULL
; pfp
= pfp
->pf_prev
) {
1262 dnames
= devs_to_list(pfp
->pf_global
.sl_dev
, dnames
);
1263 for (slp
= pfp
->pf_svc
; slp
!= NULL
; slp
= slp
->sl_next
)
1264 if (!(slp
->sl_entry
.se_flags
& SEF_CDEV
))
1265 dnames
= devs_to_list(slp
->sl_dev
, dnames
);
1270 /* Compare device names; used with qsort */
1272 devcmp(const void *d1
, const void *d2
)
1274 return (strcmp(*(const char **)d1
, *(const char **)d2
));
1278 * Get sorted list of unique device names among all defined and
1279 * partially defined services in all files.
1281 static const char **
1282 get_unique_devs(struct parse_state
*psp
)
1285 const char **dnames
;
1290 * Count number of explicitly referenced devices among all
1291 * services (including duplicates).
1293 ndevs
= count_per_file(psp
->ps_files
);
1294 ndevs
+= count_per_file(psp
->ps_cfile
);
1299 /* Sort and trim out duplicate devices. */
1300 dnames
= (const char **)malloc((ndevs
+1) * sizeof (const char *));
1301 if (dnames
== NULL
) {
1302 logerr("unable to allocate space for %d devices", ndevs
+ 1);
1305 dnp
= per_file_to_list(psp
->ps_files
, dnames
);
1306 (void) per_file_to_list(psp
->ps_cfile
, dnp
);
1307 qsort(dnames
, ndevs
, sizeof (const char *), devcmp
);
1308 for (dnf
= (dnp
= dnames
) + 1; dnf
< dnames
+ndevs
; dnf
++)
1309 if (strcmp(*dnf
, *dnp
) != 0)
1313 /* Return array of pointers to names. */
1318 * Convert data structures created by parsing process into data
1319 * structures used by service dispatch. This gathers the unique
1320 * device (lower stream) names and attaches the services available on
1321 * each device to a list while triming duplicate services.
1323 static struct option_state
*
1324 organize_state(struct parse_state
*psp
)
1326 struct per_file
*pfp
;
1327 struct per_file
*pftopp
;
1328 struct service_list
*slp
;
1329 struct device_list
*dlp
;
1332 const char **dnames
;
1334 struct device_entry
*dep
;
1335 struct option_state
*osp
;
1336 struct service_entry
**sepp
;
1337 struct service_entry
**sebpp
;
1338 struct service_entry
**se2pp
;
1341 * Parsing is now done.
1343 close_service(psp
->ps_csvc
);
1344 psp
->ps_csvc
= NULL
;
1345 if ((pfp
= psp
->ps_cfile
) != NULL
) {
1346 pfp
->pf_prev
= psp
->ps_files
;
1347 psp
->ps_files
= pfp
;
1348 psp
->ps_cfile
= NULL
;
1351 /* Link the services from all files together for easy referencing. */
1352 pftopp
= psp
->ps_files
;
1353 for (pfp
= pftopp
->pf_prev
; pfp
!= NULL
; pfp
= pfp
->pf_prev
)
1354 if (pfp
->pf_svc
!= NULL
) {
1355 if (pftopp
->pf_svc_last
== NULL
)
1356 pftopp
->pf_svc
= pfp
->pf_svc
;
1358 pftopp
->pf_svc_last
->sl_next
= pfp
->pf_svc
;
1359 pftopp
->pf_svc_last
= pfp
->pf_svc_last
;
1360 pfp
->pf_svc
= pfp
->pf_svc_last
= NULL
;
1364 * Count up number of services per device, including
1365 * duplicates but not including defaults.
1368 for (slp
= psp
->ps_files
->pf_svc
; slp
!= NULL
; slp
= slp
->sl_next
)
1369 for (dlp
= slp
->sl_dev
; dlp
!= NULL
; dlp
= dlp
->dl_next
)
1373 * Get the unique devices referenced by all services.
1375 dnames
= get_unique_devs(psp
);
1376 if (dnames
== NULL
) {
1377 logdbg("no devices referenced by any service");
1381 for (dnp
= dnames
; *dnp
!= NULL
; dnp
++)
1385 * Allocate room for main structure, device records, and
1386 * per-device lists. Worst case is all devices having all
1387 * services; that's why we allocate for nsvcs * ndevs.
1389 osp
= (struct option_state
*)malloc(sizeof (*osp
) +
1390 ndevs
* sizeof (*dep
) + nsvcs
* ndevs
* sizeof (*sepp
));
1392 logerr("unable to allocate option state structure");
1397 /* We're going to succeed now, so steal these over. */
1398 osp
->os_devices
= dep
= (struct device_entry
*)(osp
+1);
1399 osp
->os_pfjunk
= psp
->ps_files
;
1400 psp
->ps_files
= NULL
;
1401 osp
->os_evjunk
= psp
->ps_evlist
;
1402 psp
->ps_evlist
= NULL
;
1404 /* Loop over devices, install services, remove duplicates. */
1405 sepp
= (struct service_entry
**)(dep
+ ndevs
);
1406 for (dnp
= dnames
; *dnp
!= NULL
; dnp
++) {
1407 dep
->de_name
= *dnp
;
1408 dep
->de_services
= (const struct service_entry
**)sepp
;
1410 for (slp
= osp
->os_pfjunk
->pf_svc
; slp
!= NULL
;
1412 for (dlp
= slp
->sl_dev
; dlp
!= NULL
;
1413 dlp
= dlp
->dl_next
) {
1414 if (dlp
->dl_name
== *dnp
||
1415 strcmp(dlp
->dl_name
, *dnp
) == 0) {
1416 for (se2pp
= sebpp
; se2pp
< sepp
;
1418 if ((*se2pp
)->se_name
==
1419 slp
->sl_entry
.se_name
||
1421 se_name
, slp
->sl_entry
.
1425 * We retain a service if it's
1426 * unique or if its serial
1427 * number (position in the
1428 * file) is greater than than
1432 *sepp
++ = &slp
->sl_entry
;
1433 else if (SESERIAL(**se2pp
) <
1434 SESERIAL(slp
->sl_entry
))
1435 *se2pp
= &slp
->sl_entry
;
1438 /* Count up the services on this device. */
1439 dep
->de_nservices
= (const struct service_entry
**)sepp
-
1441 /* Ignore devices having no services at all. */
1442 if (dep
->de_nservices
> 0)
1445 /* Count up the devices. */
1446 osp
->os_ndevices
= dep
- osp
->os_devices
;
1447 /* Free the list of device names */
1453 * Free storage unique to a given service. Pointers copied from other
1454 * services are ignored.
1457 free_service(struct service_list
*slp
)
1459 struct filter_entry
*fep
;
1460 struct filter_entry
*fen
;
1462 if (!(slp
->sl_entry
.se_flags
& SEF_CDEV
))
1463 free_device_list(slp
->sl_dev
);
1464 if (!(slp
->sl_entry
.se_flags
& SEF_CFLIST
)) {
1465 fep
= slp
->sl_entry
.se_flist
;
1466 while (fep
!= NULL
) {
1467 fen
= fep
->fe_prevcopy
? NULL
: fep
->fe_prev
;
1472 if (!(slp
->sl_entry
.se_flags
& SEF_CPPPD
) &&
1473 slp
->sl_entry
.se_pppd
!= NULL
)
1474 free(slp
->sl_entry
.se_pppd
);
1475 if (!(slp
->sl_entry
.se_flags
& SEF_CSERVER
) &&
1476 slp
->sl_entry
.se_server
!= NULL
)
1477 free(slp
->sl_entry
.se_server
);
1478 if (!(slp
->sl_entry
.se_flags
& SEF_CPATH
) &&
1479 slp
->sl_entry
.se_path
!= NULL
)
1480 free(slp
->sl_entry
.se_path
);
1481 if (!(slp
->sl_entry
.se_flags
& SEF_CEXTRA
) &&
1482 slp
->sl_entry
.se_extra
!= NULL
)
1483 free(slp
->sl_entry
.se_extra
);
1484 if (!(slp
->sl_entry
.se_flags
& SEF_CLOG
) &&
1485 slp
->sl_entry
.se_log
!= NULL
)
1486 free(slp
->sl_entry
.se_log
);
1490 * Free a linked list of services.
1493 free_service_list(struct service_list
*slp
)
1495 struct service_list
*sln
;
1497 while (slp
!= NULL
) {
1506 * Free a linked list of files and all services in those files.
1509 free_file_list(struct per_file
*pfp
)
1511 struct per_file
*pfn
;
1513 while (pfp
!= NULL
) {
1514 free_service(&pfp
->pf_global
);
1515 free_service_list(pfp
->pf_svc
);
1523 * Free an array of local environment variables.
1526 free_env_list(char **evlist
)
1531 if ((evp
= evlist
) != NULL
) {
1532 while ((env
= *evp
++) != NULL
)
1539 * Add a new device (lower stream) to the list for which we're the
1543 add_new_dev(int tunfd
, const char *dname
)
1545 union ppptun_name ptn
;
1547 (void) snprintf(ptn
.ptn_name
, sizeof (ptn
.ptn_name
), "%s:pppoed",
1549 if (strioctl(tunfd
, PPPTUN_SCTL
, &ptn
, sizeof (ptn
), 0) < 0) {
1550 logerr("PPPTUN_SCTL %s: %s", ptn
.ptn_name
, mystrerror(errno
));
1552 logdbg("added %s", ptn
.ptn_name
);
1557 * Remove an existing device (lower stream) from the list for which we
1558 * were the PPPoE server.
1561 rem_old_dev(int tunfd
, const char *dname
)
1563 union ppptun_name ptn
;
1565 (void) snprintf(ptn
.ptn_name
, sizeof (ptn
.ptn_name
), "%s:pppoed",
1567 if (strioctl(tunfd
, PPPTUN_DCTL
, &ptn
, sizeof (ptn
), 0) < 0) {
1568 logerr("PPPTUN_DCTL %s: %s", ptn
.ptn_name
, mystrerror(errno
));
1570 logdbg("removed %s", ptn
.ptn_name
);
1575 * Get a list of all of the devices currently plumbed for PPPoE. This
1576 * is used for supporting the "*" and "all" device aliases.
1579 get_device_list(struct parse_state
*psp
, int tunfd
)
1581 struct device_list
*dlp
;
1582 struct device_list
**dlpp
;
1583 struct device_list
*dlalt
;
1584 struct device_list
**dl2pp
;
1585 struct device_list
*dla
;
1587 union ppptun_name ptn
;
1590 /* First pass; just allocate space for all *:pppoe* devices */
1591 dlpp
= &psp
->ps_star
;
1593 for (i
= 0; ; i
++) {
1595 if (strioctl(tunfd
, PPPTUN_GNNAME
, &ptn
, sizeof (ptn
),
1596 sizeof (ptn
)) < 0) {
1597 logerr("PPPTUN_GNNAME %d: %s", i
, mystrerror(errno
));
1600 if (ptn
.ptn_name
[0] == '\0')
1602 if ((cp
= strchr(ptn
.ptn_name
, ':')) == NULL
||
1603 strncmp(cp
, ":pppoe", 6) != 0 ||
1604 (cp
[6] != '\0' && strcmp(cp
+6, "d") != 0))
1607 dlp
= (struct device_list
*)malloc(sizeof (*dlp
) +
1608 strlen(ptn
.ptn_name
) + 1);
1611 dlp
->dl_name
= (const char *)(dlp
+ 1);
1612 (void) strcpy((char *)(dlp
+ 1), ptn
.ptn_name
);
1613 if (cp
[6] == '\0') {
1615 dlpp
= &dlp
->dl_next
;
1618 dl2pp
= &dlp
->dl_next
;
1624 /* Second pass; eliminate improperly plumbed devices */
1625 for (dlpp
= &psp
->ps_star
; (dlp
= *dlpp
) != NULL
; ) {
1626 for (dla
= dlalt
; dla
!= NULL
; dla
= dla
->dl_next
)
1627 if (strcmp(dla
->dl_name
, dlp
->dl_name
) == 0)
1630 *dlpp
= dlp
->dl_next
;
1633 dlpp
= &dlp
->dl_next
;
1636 free_device_list(dlalt
);
1638 /* Add in "*" so we can always handle dynamic plumbing. */
1639 dlp
= (struct device_list
*)malloc(sizeof (*dlp
) + 2);
1641 dlp
->dl_name
= (const char *)(dlp
+ 1);
1642 (void) strcpy((char *)(dlp
+ 1), "*");
1643 dlp
->dl_next
= psp
->ps_star
;
1649 * Set logging subsystem back to configured global default values.
1652 global_logging(void)
1654 log_for_service(glob_svc
.se_log
, glob_svc
.se_debug
);
1658 * Handle SIGHUP -- reparse command line and all configuration files.
1659 * When reparsing is complete, free old parsed data and replace with
1663 parse_options(int tunfd
, int argc
, char **argv
)
1665 struct parse_state pstate
;
1666 struct per_file
*argpf
;
1667 struct option_state
*newopt
;
1668 const char **dnames
;
1670 const struct device_entry
*newdep
, *newmax
;
1671 const struct device_entry
*olddep
, *oldmax
;
1673 struct service_entry newglobsvc
, *mainsvc
;
1675 /* Note that all per_file structures must be freeable */
1676 argpf
= (struct per_file
*)calloc(sizeof (*argpf
), 1);
1677 if (argpf
== NULL
) {
1680 (void) memset(&pstate
, '\0', sizeof (pstate
));
1681 pstate
.ps_state
= ksDefault
;
1682 pstate
.ps_cfile
= argpf
;
1683 pstate
.ps_csvc
= &argpf
->pf_global
;
1684 argpf
->pf_global
.sl_parse
= &pstate
;
1685 argpf
->pf_name
= "command line";
1687 /* Default is 1 -- errors only */
1688 argpf
->pf_global
.sl_entry
.se_debug
++;
1689 argpf
->pf_global
.sl_entry
.se_name
= "<global>";
1691 /* Get list of all devices */
1692 get_device_list(&pstate
, tunfd
);
1694 /* Parse options from command line and main configuration file. */
1695 pstate
.ps_flags
|= PSF_SETLEVEL
;
1696 parse_arg_list(&pstate
, argc
, argv
);
1697 parse_file(&pstate
, "/etc/ppp/pppoe");
1698 pstate
.ps_flags
&= ~PSF_SETLEVEL
;
1701 * At this point, global options from the main configuration
1702 * file are pointed to by ps_files, and options from command
1703 * line are in argpf. We need to pull three special options
1704 * from these -- wildcard, debug, and log. Note that the main
1705 * options file overrides the command line. This is
1706 * intentional. The semantics are such that the system
1707 * behaves as though the main configuration file were
1708 * "included" from the command line, and thus options there
1709 * override the command line. This may seem odd, but at least
1710 * it's self-consistent.
1712 newglobsvc
= argpf
->pf_global
.sl_entry
;
1713 if (pstate
.ps_files
!= NULL
) {
1714 mainsvc
= &pstate
.ps_files
->pf_global
.sl_entry
;
1715 if (mainsvc
->se_log
!= NULL
)
1716 newglobsvc
.se_log
= mainsvc
->se_log
;
1717 if (mainsvc
->se_flags
& (SEF_WILD
|SEF_NOWILD
))
1718 newglobsvc
.se_flags
=
1719 (newglobsvc
.se_flags
& ~(SEF_WILD
|SEF_NOWILD
)) |
1720 (mainsvc
->se_flags
& (SEF_WILD
|SEF_NOWILD
));
1721 if (mainsvc
->se_flags
& SEF_DEBUGCLR
)
1722 newglobsvc
.se_debug
= 0;
1723 newglobsvc
.se_debug
+= mainsvc
->se_debug
;
1725 glob_svc
= newglobsvc
;
1728 /* Get the list of devices referenced by configuration above. */
1729 dnames
= get_unique_devs(&pstate
);
1730 if (dnames
!= NULL
) {
1731 /* Read per-device configuration files. */
1732 pstate
.ps_flags
|= PSF_PERDEV
;
1733 for (dnp
= dnames
; *dnp
!= NULL
; dnp
++)
1734 parse_file(&pstate
, *dnp
);
1735 pstate
.ps_flags
&= ~PSF_PERDEV
;
1741 * Convert parsed data structures into per-device structures.
1742 * (Invert the table.)
1744 newopt
= organize_state(&pstate
);
1746 /* If we're going to free the file name, then stop logging there. */
1747 if (newopt
== NULL
&& glob_svc
.se_log
!= NULL
) {
1748 glob_svc
.se_log
= NULL
;
1753 * Unless an error has occurred, these pointers are normally
1754 * all NULL. Nothing is freed until the file is re-read.
1756 free_file_list(pstate
.ps_files
);
1757 free_file_list(pstate
.ps_cfile
);
1758 free_device_list(pstate
.ps_star
);
1759 free_env_list(pstate
.ps_evlist
);
1762 * Match up entries on device list. Detach devices no longer
1763 * referenced. Attach ones now referenced. (The use of null
1764 * pointers here may look fishy, but it actually works.
1765 * NULL>=NULL is always true.)
1767 if (newopt
!= NULL
) {
1768 newdep
= newopt
->os_devices
;
1769 newmax
= newdep
+ newopt
->os_ndevices
;
1771 newdep
= newmax
= NULL
;
1773 if (cur_options
!= NULL
) {
1774 olddep
= cur_options
->os_devices
;
1775 oldmax
= olddep
+ cur_options
->os_ndevices
;
1777 olddep
= oldmax
= NULL
;
1779 while ((newdep
!= NULL
&& newdep
< newmax
) ||
1780 (olddep
!= NULL
&& olddep
< oldmax
)) {
1781 if (newdep
< newmax
) {
1782 if (olddep
>= oldmax
) {
1783 add_new_dev(tunfd
, newdep
->de_name
);
1786 cmpval
= strcmp(newdep
->de_name
,
1789 /* Brand new device seen. */
1790 add_new_dev(tunfd
, newdep
->de_name
);
1792 } else if (cmpval
== 0) {
1793 /* Existing device; skip it. */
1797 /* No else clause -- removal is below */
1800 if (olddep
< oldmax
) {
1801 if (newdep
>= newmax
) {
1802 rem_old_dev(tunfd
, olddep
->de_name
);
1805 cmpval
= strcmp(newdep
->de_name
,
1808 /* Old device is gone */
1809 rem_old_dev(tunfd
, olddep
->de_name
);
1811 } else if (cmpval
== 0) {
1812 /* Existing device; skip it. */
1816 /* No else clause -- insert handled above */
1821 /* Discard existing parsed data storage. */
1822 if (cur_options
!= NULL
) {
1823 free_file_list(cur_options
->os_pfjunk
);
1824 free_env_list(cur_options
->os_evjunk
);
1828 cur_options
= newopt
;
1832 * Check if configured filters permit requesting client to use a given
1833 * service. Note -- filters are stored in reverse order in order to
1834 * make file-inclusion work as expected. Thus, the "first match"
1835 * filter rule becomes "last match" here.
1838 allow_service(const struct service_entry
*sep
, const ppptun_atype
*pap
)
1840 const struct filter_entry
*fep
;
1841 const struct filter_entry
*lmatch
;
1842 boolean_t anynonexcept
= B_FALSE
;
1844 const uchar_t
*macp
;
1845 const uchar_t
*maskp
;
1849 for (fep
= sep
->se_flist
; fep
!= NULL
; fep
= fep
->fe_prev
) {
1850 anynonexcept
|= !fep
->fe_isexcept
;
1851 upt
= pap
->pta_pppoe
.ptma_mac
;
1852 macp
= fep
->fe_mac
.ether_addr_octet
;
1853 maskp
= fep
->fe_mask
.ether_addr_octet
;
1854 for (i
= sizeof (pap
->pta_pppoe
.ptma_mac
); i
> 0; i
--)
1855 if (((*macp
++ ^ *upt
++) & *maskp
++) != 0)
1861 if (lmatch
== NULL
) {
1863 * Assume reject by default if any positive-match
1864 * (non-except) filters are given. Otherwise, if
1865 * there are no positive-match filters, then
1866 * non-matching means accept by default.
1868 return (!anynonexcept
);
1870 return (!lmatch
->fe_isexcept
);
1874 * Locate available service(s) based on client request. Assumes that
1875 * outp points to a buffer of at least size PPPOE_MSGMAX. Creates a
1876 * PPPoE response message in outp. Returns count of matched services
1877 * and (through *srvp) a pointer to the last (or only) service. If
1878 * some error is found in the request, an error string is added and -1
1879 * is returned; the caller should just send the message without
1883 locate_service(poep_t
*poep
, int plen
, const char *iname
, ppptun_atype
*pap
,
1884 uint32_t *outp
, void **srvp
)
1887 const uint8_t *tagp
;
1892 const struct device_entry
*dep
, *depe
;
1893 const struct device_entry
*wdep
;
1894 const struct service_entry
**sepp
, **seppe
;
1895 const struct service_entry
*sep
;
1899 ispadi
= poep
->poep_code
== POECODE_PADI
;
1900 opoe
= poe_mkheader(outp
, ispadi
? POECODE_PADO
: POECODE_PADS
, 0);
1903 if (cur_options
== NULL
)
1906 /* Search for named device (lower stream) in tables. */
1907 dep
= cur_options
->os_devices
;
1908 depe
= dep
+ cur_options
->os_ndevices
;
1910 if ((cp
= strchr(iname
, ':')) != NULL
)
1913 tlen
= strlen(iname
);
1914 for (; dep
< depe
; dep
++)
1915 if (strncmp(iname
, dep
->de_name
, tlen
) == 0 &&
1916 dep
->de_name
[tlen
] == '\0')
1918 else if (dep
->de_name
[0] == '*' && dep
->de_name
[1] == '\0')
1923 * Return if interface not found. Zero-service case can't
1924 * occur, since devices with no services aren't included in
1925 * the list, but the code is just being safe here.
1927 if (dep
== NULL
|| dep
->de_services
== NULL
|| dep
->de_nservices
<= 0)
1931 * Loop over tags in client message and process them.
1932 * Services must be matched against our list. Host-Uniq and
1933 * Relay-Session-Id must be copied to the reply. All others
1934 * must be discarded.
1937 sepp
= dep
->de_services
;
1938 tagp
= (const uint8_t *)(poep
+ 1);
1939 while (poe_tagcheck(poep
, plen
, tagp
)) {
1940 ttyp
= POET_GET_TYPE(tagp
);
1941 if (ttyp
== POETT_END
)
1943 tlen
= POET_GET_LENG(tagp
);
1945 case POETT_SERVICE
: /* Service-Name */
1947 * Allow only one. (Note that this test works
1948 * because there's always at least one service
1949 * per device; otherwise, the device is
1950 * removed from the list.)
1952 if (sepp
!= dep
->de_services
) {
1954 (void) poe_add_str(opoe
, POETT_NAMERR
,
1955 "Too many Service-Name tags");
1959 seppe
= sepp
+ dep
->de_nservices
;
1962 * If config specifies "nowild" in a
1963 * global context, then we don't
1964 * respond to wildcard PADRs. The
1965 * client must know the exact service
1966 * name to get access.
1969 if (!ispadi
&& (glob_svc
.se_flags
& SEF_NOWILD
))
1971 while (sepp
< seppe
) {
1973 if (sep
->se_name
[0] == '\0' ||
1974 (sep
->se_flags
& SEF_NOWILD
) ||
1975 !allow_service(sep
, pap
))
1977 *srvp
= (void *)sep
;
1979 * RFC requires that PADO includes the
1980 * wildcard service request in response
1983 if (ispadi
&& nsvcs
== 0 &&
1984 !(glob_svc
.se_flags
& SEF_NOWILD
))
1985 (void) poe_tag_copy(opoe
, tagp
);
1987 (void) poe_add_str(opoe
, POETT_SERVICE
,
1989 /* If PADR, then one is enough */
1993 /* Just for generating error messages */
1995 (void) poe_tag_copy(opoe
, tagp
);
1998 * Clients's requested service must appear in
2001 (void) poe_tag_copy(opoe
, tagp
);
2003 /* Requested specific service; find it. */
2004 cp
= (char *)POET_DATA(tagp
);
2005 while (sepp
< seppe
) {
2007 if (strlen(sep
->se_name
) == tlen
&&
2008 strncasecmp(sep
->se_name
, cp
,
2010 if (allow_service(sep
, pap
)) {
2012 *srvp
= (void *)sep
;
2019 * Allow service definition to override
2020 * AC-Name (concentrator [server] name) field.
2022 if (*srvp
!= NULL
) {
2023 sep
= (const struct service_entry
*)*srvp
;
2024 log_for_service(sep
->se_log
, sep
->se_debug
);
2025 str
= "Solaris PPPoE";
2026 if (sep
->se_server
!= NULL
)
2027 str
= sep
->se_server
;
2028 (void) poe_add_str(opoe
, POETT_ACCESS
, str
);
2031 /* Ones we should discard */
2032 case POETT_ACCESS
: /* AC-Name */
2033 case POETT_COOKIE
: /* AC-Cookie */
2034 case POETT_NAMERR
: /* Service-Name-Error */
2035 case POETT_SYSERR
: /* AC-System-Error */
2036 case POETT_GENERR
: /* Generic-Error */
2037 case POETT_HURL
: /* Host-URL */
2038 case POETT_MOTM
: /* Message-Of-The-Minute */
2039 case POETT_RTEADD
: /* IP-Route-Add */
2040 case POETT_VENDOR
: /* Vendor-Specific */
2041 case POETT_MULTI
: /* Multicast-Capable */
2044 /* Ones we should copy */
2045 case POETT_UNIQ
: /* Host-Uniq */
2046 case POETT_RELAY
: /* Relay-Session-Id */
2047 (void) poe_tag_copy(opoe
, tagp
);
2050 tagp
= POET_NEXT(tagp
);
2056 * Like fgetc, but reads from a string.
2061 char **cpp
= (char **)arg
;
2068 * Given a service structure, launch pppd. Called by handle_input()
2069 * in pppoed.c if locate_service() [above] finds exactly one service
2073 launch_service(int tunfd
, poep_t
*poep
, void *srvp
, struct ppptun_control
*ptc
)
2075 const struct service_entry
*sep
= (const struct service_entry
*)srvp
;
2082 struct ppptun_peer ptp
;
2083 union ppptun_name ptn
;
2084 const char *args
[MAXARGS
];
2092 char keybuf
[MAX_KEYWORD
];
2094 assert(sep
!= NULL
);
2096 /* Get tunnel driver connection for new PPP session. */
2097 newtun
= open(tunnam
, O_RDWR
);
2101 /* Set this session up for standard PPP and client's address. */
2102 (void) memset(&ptp
, '\0', sizeof (ptp
));
2103 ptp
.ptp_style
= PTS_PPPOE
;
2104 ptp
.ptp_address
= ptc
->ptc_address
;
2105 if (strioctl(newtun
, PPPTUN_SPEER
, &ptp
, sizeof (ptp
), sizeof (ptp
)) <
2108 ptp
.ptp_rsessid
= ptp
.ptp_lsessid
;
2109 if (strioctl(newtun
, PPPTUN_SPEER
, &ptp
, sizeof (ptp
), sizeof (ptp
)) <
2113 /* Attach the requested lower stream. */
2114 cp
= strchr(ptc
->ptc_name
, ':');
2116 cp
= ptc
->ptc_name
+ strlen(ptc
->ptc_name
);
2117 (void) snprintf(ptn
.ptn_name
, sizeof (ptn
.ptn_name
), "%.*s:pppoe",
2118 cp
-ptc
->ptc_name
, ptc
->ptc_name
);
2119 if (strioctl(newtun
, PPPTUN_SDATA
, &ptn
, sizeof (ptn
), 0) < 0)
2121 (void) snprintf(ptn
.ptn_name
, sizeof (ptn
.ptn_name
), "%.*s:pppoed",
2122 cp
-ptc
->ptc_name
, ptc
->ptc_name
);
2123 if (strioctl(newtun
, PPPTUN_SCTL
, &ptn
, sizeof (ptn
), 0) < 0)
2127 if (pidv
== (pid_t
)-1)
2130 if (pidv
== (pid_t
)0) {
2132 * Use syslog only in order to avoid mixing log messages
2137 if ((path
= sep
->se_path
) == NULL
)
2138 path
= "/usr/bin/pppd";
2139 if ((extra
= sep
->se_extra
) == NULL
)
2140 extra
= "plugin pppoe.so directtty";
2141 if ((pppd
= sep
->se_pppd
) == NULL
)
2144 /* Concatenate these. */
2145 slen
= strlen(path
) + strlen(extra
) + strlen(pppd
) + 3;
2146 if ((sptr
= (char *)malloc(slen
)) == NULL
)
2148 (void) strcpy(sptr
, path
);
2149 (void) strcat(sptr
, " ");
2150 (void) strcat(sptr
, extra
);
2151 (void) strcat(sptr
, " ");
2152 (void) strcat(sptr
, pppd
);
2154 /* Parse out into arguments */
2157 while (cpp
< args
+ MAXARGS
- 1) {
2158 retv
= getkeyword(NULL
, keybuf
, sizeof (keybuf
), sgetc
,
2161 *cpp
++ = strsave(keybuf
);
2170 * Fix tunnel device on stdin/stdout and error file on
2173 if (newtun
!= 0 && dup2(newtun
, 0) < 0)
2175 if (newtun
!= 1 && dup2(newtun
, 1) < 0)
2178 (void) close(newtun
);
2180 (void) close(tunfd
);
2182 (void) open("/etc/ppp/pppoe-errors", O_WRONLY
| O_APPEND
|
2186 * Change GID first, for obvious reasons. Note that
2187 * we log any problems to syslog, not the errors file.
2188 * The errors file is intended for problems in the
2191 if ((sep
->se_flags
& SEF_GIDSET
) &&
2192 setgid(sep
->se_gid
) == -1) {
2193 cp
= mystrerror(errno
);
2195 logerr("setgid(%d): %s", sep
->se_gid
, cp
);
2198 if ((sep
->se_flags
& SEF_UIDSET
) &&
2199 setuid(sep
->se_uid
) == -1) {
2200 cp
= mystrerror(errno
);
2202 logerr("setuid(%d): %s", sep
->se_uid
, cp
);
2208 cp
= strrchr(args
[0], '/');
2209 if (cp
!= NULL
&& cp
[1] != '\0')
2212 (void) execv(path
, (char * const *)args
);
2216 * Exec failure; attempt to log the problem and send a
2217 * PADT to the client so that it knows the session
2221 cp
= mystrerror(errno
);
2223 logerr("\"%s\": %s", (sptr
== NULL
? path
: sptr
), cp
);
2225 poep
= poe_mkheader(pkt_output
, POECODE_PADT
, ptp
.ptp_lsessid
);
2226 poep
->poep_session_id
= htons(ptp
.ptp_lsessid
);
2227 (void) poe_add_str(poep
, POETT_SYSERR
, cp
);
2229 ctrl
.len
= sizeof (*ptc
);
2230 ctrl
.buf
= (caddr_t
)ptc
;
2231 data
.len
= poe_length(poep
) + sizeof (*poep
);
2232 data
.buf
= (caddr_t
)poep
;
2233 if (putmsg(newtun
, &ctrl
, &data
, 0) < 0) {
2234 logerr("putmsg %s: %s", ptc
->ptc_name
,
2240 (void) close(newtun
);
2242 /* Give session ID to client in reply. */
2243 poep
->poep_session_id
= htons(ptp
.ptp_lsessid
);
2247 /* Peer doesn't know session ID yet; hope for the best. */
2250 (void) close(newtun
);
2251 (void) poe_add_str(poep
, POETT_SYSERR
, mystrerror(retv
));
2256 * This is pretty awful -- it uses recursion to print a simple list.
2257 * It's just for debug, though, and does a reasonable job of printing
2258 * the filters in the right order.
2261 print_filter_list(FILE *fp
, struct filter_entry
*fep
)
2263 if (fep
->fe_prev
!= NULL
)
2264 print_filter_list(fp
, fep
->fe_prev
);
2265 (void) fprintf(fp
, "\t\t MAC %s", ehost2(&fep
->fe_mac
));
2266 (void) fprintf(fp
, ", mask %s%s\n", ehost2(&fep
->fe_mask
),
2267 (fep
->fe_isexcept
? ", except" : ""));
2271 * Write summary of parsed configuration data to given file.
2274 dump_configuration(FILE *fp
)
2276 const struct device_entry
*dep
;
2277 const struct service_entry
*sep
, **sepp
;
2278 struct per_file
*pfp
;
2281 (void) fprintf(fp
, "Will%s respond to wildcard queries.\n",
2282 (glob_svc
.se_flags
& SEF_NOWILD
) ? " not" : "");
2284 "Global debug level %d, log to %s; current level %d\n",
2286 ((glob_svc
.se_log
== NULL
|| *glob_svc
.se_log
== '\0') ?
2287 "syslog" : glob_svc
.se_log
),
2289 if (cur_options
== NULL
) {
2290 (void) fprintf(fp
, "No current configuration.\n");
2293 (void) fprintf(fp
, "Current configuration:\n");
2294 (void) fprintf(fp
, " %d device(s):\n", cur_options
->os_ndevices
);
2295 dep
= cur_options
->os_devices
;
2296 for (i
= 0; i
< cur_options
->os_ndevices
; i
++, dep
++) {
2297 (void) fprintf(fp
, "\t%s: %d service(s):\n",
2298 dep
->de_name
, dep
->de_nservices
);
2299 sepp
= dep
->de_services
;
2300 for (j
= 0; j
< dep
->de_nservices
; j
++, sepp
++) {
2302 (void) fprintf(fp
, "\t %s: debug level %d",
2303 sep
->se_name
, sep
->se_debug
);
2304 if (sep
->se_flags
& SEF_UIDSET
)
2305 (void) fprintf(fp
, ", UID %u", sep
->se_uid
);
2306 if (sep
->se_flags
& SEF_GIDSET
)
2307 (void) fprintf(fp
, ", GID %u", sep
->se_gid
);
2308 if (sep
->se_flags
& SEF_WILD
)
2309 (void) fprintf(fp
, ", wildcard");
2310 else if (sep
->se_flags
& SEF_NOWILD
)
2311 (void) fprintf(fp
, ", nowildcard");
2313 (void) fprintf(fp
, ", wildcard (default)");
2314 (void) putc('\n', fp
);
2315 if (sep
->se_server
!= NULL
)
2316 (void) fprintf(fp
, "\t\tserver \"%s\"\n",
2318 if (sep
->se_pppd
!= NULL
)
2319 (void) fprintf(fp
, "\t\tpppd \"%s\"\n",
2321 if (sep
->se_path
!= NULL
)
2322 (void) fprintf(fp
, "\t\tpath \"%s\"\n",
2324 if (sep
->se_extra
!= NULL
)
2325 (void) fprintf(fp
, "\t\textra \"%s\"\n",
2327 if (sep
->se_log
!= NULL
)
2328 (void) fprintf(fp
, "\t\tlog \"%s\"\n",
2330 if (sep
->se_flist
!= NULL
) {
2331 (void) fprintf(fp
, "\t\tfilter list:\n");
2332 print_filter_list(fp
, sep
->se_flist
);
2336 (void) fprintf(fp
, "\nConfiguration read from:\n");
2337 for (pfp
= cur_options
->os_pfjunk
; pfp
!= NULL
; pfp
= pfp
->pf_prev
) {
2338 (void) fprintf(fp
, " %s: %d service(s)\n", pfp
->pf_name
,