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]
23 * Copyright 2010 Sun Microsystems, Inc. All rights reserved.
24 * Use is subject to license terms.
28 * pppoe.c - pppd plugin to handle PPPoE operation.
35 #include <sys/types.h>
38 #include <sys/stropts.h>
39 #include <netinet/in.h>
40 #include <net/pppio.h>
41 #include <net/sppptun.h>
42 #include <net/pppoe.h>
45 #include "pathnames.h"
47 /* Saved hook pointers */
48 static int (*old_check_options
)(uid_t uid
);
49 static int (*old_updown_script
)(const char ***argsp
);
50 static int (*old_sys_read_packet
)(int retv
, struct strbuf
*ctrl
,
51 struct strbuf
*data
, int flags
);
53 /* Room for 3 IPv4 addresses and metric */
54 #define RTE_MSG_LEN (3*16 + 10 + 1)
56 /* Environment string for routes */
57 #define RTE_STR "ROUTE_%d"
62 * wrapper for STREAMS I_STR ioctl.
65 strioctl(int fd
, int cmd
, void *ptr
, int ilen
, int olen
)
70 str
.ic_timout
= 0; /* Use default timer; 15 seconds */
74 if (ioctl(fd
, I_STR
, &str
) == -1) {
77 if (str
.ic_len
!= olen
) {
84 * If the user named the tunneling device, check that it is
85 * reasonable; otherwise check that standard input is the tunnel.
88 pppoe_check_options(uid_t uid
)
90 int tstfd
; /* fd for device being checked */
91 int err
; /* saved errno value */
92 int retv
; /* return value */
93 int intv
; /* integer return value (from ioctl) */
94 union ppptun_name ptn
;
96 if (devnam
[0] != '\0') {
98 * Open as real user so that modes on device can be
99 * used to limit access.
101 if (!devnam_info
.priv
)
103 tstfd
= open(devnam
, O_NONBLOCK
| O_RDWR
, 0);
105 if (!devnam_info
.priv
)
109 option_error("unable to open %s: %m", devnam
);
112 retv
= strioctl(tstfd
, PPPTUN_GDATA
, &ptn
, 0, sizeof (ptn
));
115 option_error("device %s is not a PPP tunneling device",
120 retv
= strioctl(0, PPPIO_GTYPE
, &intv
, 0, sizeof (intv
));
122 option_error("standard input is not a PPP device");
125 retv
= strioctl(0, PPPTUN_GDATA
, &ptn
, 0, sizeof (ptn
));
127 option_error("standard input is not a PPP tunnel");
130 if (strcmp(ptn
.ptn_name
+ strlen(ptn
.ptn_name
) - 6,
132 option_error("standard input not connected to PPPoE");
136 if (old_check_options
!= NULL
&&
137 old_check_options
!= pppoe_check_options
)
138 return ((*old_check_options
)(uid
));
143 * When we're about to call one of the up or down scripts, change the
144 * second argument to contain the interface name and selected PPPoE
148 pppoe_updown_script(const char ***argsp
)
152 if ((*argsp
)[2] == devnam
&&
153 (cp
= script_getenv("IF_AND_SERVICE")) != NULL
)
155 if (old_updown_script
!= NULL
&&
156 old_updown_script
!= pppoe_updown_script
)
157 return ((*old_updown_script
)(argsp
));
162 * Concatenate and save strings from command line into environment
166 cat_save_env(char **argv
, char idchar
, const char *envname
)
174 for (argp
= argv
; argp
[0] != NULL
; argp
+= 2)
175 if (*argp
[0] == idchar
)
176 totlen
+= strlen(argp
[1]) + 1;
177 if ((str
= malloc(totlen
+ 1)) == NULL
) {
178 error("cannot malloc PPPoE environment for %s", envname
);
182 for (argp
= argv
; argp
[0] != NULL
; argp
+= 2)
183 if (*argp
[0] == idchar
) {
184 (void) strcpy(cp
, argp
[1]);
189 script_setenv(envname
, str
, 0);
193 * Convert Message Of The Moment (MOTM) and Host Uniform Resource
194 * Locator (HURL) strings into environment variables and command-line
195 * arguments for script.
198 handle_motm_hurl(char **argv
, int argc
, const uint8_t *tagp
, int pktlen
)
205 /* Must have room for two strings and NULL terminator. */
208 while (pktlen
>= POET_HDRLEN
) {
209 ttype
= POET_GET_TYPE(tagp
);
210 if (ttype
== POETT_END
)
212 tlen
= POET_GET_LENG(tagp
);
213 if (tlen
> pktlen
- POET_HDRLEN
)
215 if (ttype
== POETT_HURL
|| ttype
== POETT_MOTM
) {
216 if ((str
= malloc(tlen
+ 1)) == NULL
) {
217 error("cannot malloc PPPoE message");
220 (void) memcpy(str
, POET_DATA(tagp
), tlen
);
223 pktlen
-= POET_HDRLEN
+ tlen
;
224 tagp
+= POET_HDRLEN
+ tlen
;
230 *argv
++ = ttype
== POETT_HURL
? "hurl" : "motm";
235 cat_save_env(oargv
, 'h', "HURL");
236 cat_save_env(oargv
, 'm', "MOTM");
240 * Convert IP Route Add structures into environment variables and
241 * command-line arguments for script.
244 handle_ip_route_add(char **argv
, int argc
, const uint8_t *tagp
, int pktlen
)
251 char envname
[sizeof (RTE_STR
) + 10];
255 /* Must have room for four strings and NULL terminator. */
258 while (pktlen
>= POET_HDRLEN
) {
259 ttype
= POET_GET_TYPE(tagp
);
260 if (ttype
== POETT_END
)
262 tlen
= POET_GET_LENG(tagp
);
263 if (tlen
> pktlen
- POET_HDRLEN
)
265 if (ttype
== POETT_RTEADD
&& tlen
>= sizeof (poer
) &&
266 (str
= malloc(RTE_MSG_LEN
)) == NULL
) {
267 error("cannot malloc PPPoE route");
270 pktlen
-= POET_HDRLEN
+ tlen
;
271 tagp
+= POET_HDRLEN
+ tlen
;
277 /* No alignment restrictions on source; copy to local. */
278 (void) memcpy(&poer
, POET_DATA(tagp
), sizeof (poer
));
279 (void) slprintf(str
, RTE_MSG_LEN
, "%I %I %I %d",
280 poer
.poer_dest_network
, poer
.poer_subnet_mask
,
281 poer
.poer_gateway
, (int)poer
.poer_metric
);
282 /* Save off the environment variable version of this. */
283 (void) slprintf(envname
, sizeof (envname
), RTE_STR
, ++idx
);
284 script_setenv(envname
, str
, 0);
285 *argv
++ = str
; /* Destination */
286 str
= strchr(str
, ' ');
288 *argv
++ = str
; /* Subnet mask */
289 str
= strchr(str
, ' ');
291 *argv
++ = str
; /* Gateway */
292 str
= strchr(str
, ' ');
294 *argv
++ = str
; /* Metric */
301 * If we get here, then the driver has already validated the sender,
302 * the PPPoE version, the message length, and session ID. The code
303 * number is known not to be zero.
306 handle_pppoe_input(const ppptun_atype
*pma
, struct strbuf
*ctrl
,
319 char envname
[sizeof (RTE_STR
) + 10];
324 * Warning: the data->buf pointer here is not necessarily properly
325 * aligned for access to the poep_session_id or poep_length members.
327 /* LINTED: alignment */
328 poep
= (const poep_t
*)data
->buf
;
329 tagp
= (const uint8_t *)poep
+ offsetof(poep_t
, poep_length
);
330 pktlen
= (tagp
[0] << 8) + tagp
[1];
331 tagp
= (const uint8_t *)(poep
+ 1);
332 switch (poep
->poep_code
) {
334 dbglog("received PPPoE PADT; connection has been closed");
335 /* LINTED: alignment */
336 plp
= (struct ppp_ls
*)ctrl
->buf
;
337 plp
->magic
= PPPLSMAGIC
;
338 plp
->ppp_message
= PPP_LINKSTAT_HANGUP
;
339 ctrl
->len
= sizeof (*plp
);
342 /* Active Discovery Message and Network extensions */
345 if (poep
->poep_code
== POECODE_PADM
) {
346 argv
[0] = _ROOT_PATH
"/etc/ppp/pppoe-msg";
348 handle_motm_hurl(argv
+ 4, Dim(argv
) - 4, tagp
, pktlen
);
350 argv
[0] = _ROOT_PATH
"/etc/ppp/pppoe-network";
352 handle_ip_route_add(argv
+ 4, Dim(argv
) - 4, tagp
,
356 /* Note: strdup doesn't handle NULL input. */
358 if ((cstr
= script_getenv("IF_AND_SERVICE")) == NULL
||
359 (str
= strdup(cstr
)) == NULL
) {
360 argv
[2] = argv
[3] = "";
362 if ((cp
= strrchr(str
, ':')) == NULL
)
363 cp
= str
+ strlen(str
);
369 rpid
= run_program(argv
[0], argv
, 0, NULL
, NULL
);
370 if (rpid
== (pid_t
)0)
371 dbglog("ignored PPPoE %s; no %s script", mname
,
373 else if (rpid
!= (pid_t
)-1)
374 dbglog("PPPoE %s: started PID %d", mname
, rpid
);
377 /* Free storage allocated by handle_{motm_hurl,ip_route_add} */
379 for (argp
= argv
+ 4; *argp
!= NULL
; ) {
380 if (poep
->poep_code
== POECODE_PADM
) {
386 (void) slprintf(envname
, sizeof (envname
),
388 script_unsetenv(envname
);
391 if (poep
->poep_code
== POECODE_PADM
) {
392 script_unsetenv("HURL");
393 script_unsetenv("MOTM");
398 warn("unexpected PPPoE code %d from %s", poep
->poep_code
,
399 ether_ntoa(&pma
->pta_pppoe
.ptma_mac_ether_addr
));
406 * Handle an action code passed up from the driver.
409 handle_action(struct ppptun_control
*ptc
, struct strbuf
*ctrl
,
412 switch (ptc
->ptc_action
) {
414 return (handle_pppoe_input(&ptc
->ptc_address
, ctrl
, data
));
417 warn("bad control message; session %u on %s", ptc
->ptc_rsessid
,
426 * sys-solaris has just read in a packet; grovel through it and see if
427 * it's something we need to handle ourselves.
430 pppoe_sys_read_packet(int retv
, struct strbuf
*ctrl
, struct strbuf
*data
,
433 struct ppptun_control
*ptc
;
435 if (retv
>= 0 && !(retv
& MORECTL
) && ctrl
->len
>= sizeof (uint32_t)) {
436 /* LINTED: alignment */
437 ptc
= (struct ppptun_control
*)ctrl
->buf
;
438 /* ptc_discrim is the first uint32_t of the structure. */
439 if (ptc
->ptc_discrim
== PPPOE_DISCRIM
) {
441 if (ctrl
->len
== sizeof (*ptc
))
442 retv
= handle_action(ptc
, ctrl
, data
);
448 /* Forward along to other plug-ins */
449 if (old_sys_read_packet
!= NULL
&&
450 old_sys_read_packet
!= pppoe_sys_read_packet
)
451 return ((*old_sys_read_packet
)(retv
, ctrl
, data
, flags
));
456 * Get an environment variable from the chat script.
459 saveenv(FILE *fd
, const char *envname
)
464 if (fgets(envstr
, sizeof (envstr
), fd
) == NULL
)
466 len
= strlen(envstr
);
469 envstr
[len
-1] = '\0';
470 script_setenv(envname
, envstr
, 0);
475 * Read environment variables exported by chat script.
478 pppoe_device_pipe(int pipefd
)
484 fd
= fdopen(pipefd
, "r");
486 fatal("unable to open environment file: %m");
487 (void) saveenv(fd
, "IF_AND_SERVICE");
488 (void) saveenv(fd
, "SERVICE_NAME");
489 (void) saveenv(fd
, "AC_NAME");
490 (void) saveenv(fd
, "AC_MAC");
491 (void) saveenv(fd
, "SESSION_ID");
493 (void) slprintf(envname
, sizeof (envname
),
494 "VENDOR_SPECIFIC_%d", i
);
495 if (saveenv(fd
, envname
) <= 0)
504 if (absmax_mtu
> 1492)
506 if (absmax_mru
> 1492)
508 old_check_options
= check_options_hook
;
509 check_options_hook
= pppoe_check_options
;
510 old_updown_script
= updown_script_hook
;
511 updown_script_hook
= pppoe_updown_script
;
512 old_sys_read_packet
= sys_read_packet_hook
;
513 sys_read_packet_hook
= pppoe_sys_read_packet
;
514 device_pipe_hook
= pppoe_device_pipe
;