1/* pppoe.c - pppd plugin to implement PPPoE protocol.
2 *
3 * Copyright 2000 Michal Ostrowski <mostrows@styx.uwaterloo.ca>,
4 *		  Jamal Hadi Salim <hadi@cyberus.ca>
5 * Borrows heavily from the PPPoATM plugin by Mitchell Blank Jr.,
6 * which is based in part on work from Jens Axboe and Paul Mackerras.
7 *
8 *  This program is free software; you can redistribute it and/or
9 *  modify it under the terms of the GNU General Public License
10 *  as published by the Free Software Foundation; either version
11 *  2 of the License, or (at your option) any later version.
12 */
13
14#include <string.h>
15#include <sys/ioctl.h>
16#include <sys/types.h>
17#include <sys/socket.h>
18#include <unistd.h>
19#include <errno.h>
20#include <sys/stat.h>
21#include <pppoe.h>
22#if _linux_
23#include <linux/ppp_defs.h>
24#include <linux/if_pppox.h>
25#include <linux/if_ppp.h>
26#else
27#error this module meant for use with linux only at this time
28#endif
29
30
31#include <pppd.h>
32#include <fsm.h>
33#include <lcp.h>
34#include <ipcp.h>
35#include <ccp.h>
36#include <pathnames.h>
37
38const char pppd_version[] = VERSION;
39
40#define _PATH_ETHOPT         _ROOT_PATH "/etc/ppp/options."
41
42#define PPPOE_MTU	1492
43extern int kill_link;
44
45bool	pppoe_server=0;
46char	*pppoe_srv_name=NULL;
47char	*pppoe_ac_name=NULL;
48char    *hostuniq = NULL;
49int     retries = 0;
50
51int setdevname_pppoe(const char *cp);
52
53struct session *ses = NULL;
54static int connect_pppoe_ses(void)
55{
56    int err=-1;
57
58    client_init_ses(ses,devnam);
59
60    strlcpy(ppp_devnam, devnam, sizeof(ppp_devnam));
61
62    err= session_connect ( ses );
63
64    if(err < 0){
65	poe_fatal(ses,"Failed to negotiate PPPoE connection: %d %m",errno,errno);
66    }
67
68
69    poe_info(ses,"Connecting PPPoE socket: %E %04x %s %p",
70	     ses->sp.sa_addr.pppoe.remote,
71	     ses->sp.sa_addr.pppoe.sid,
72	     ses->sp.sa_addr.pppoe.dev,ses);
73
74    err = connect(ses->fd, (struct sockaddr*)&ses->sp,
75		  sizeof(struct sockaddr_pppox));
76
77
78    if( err < 0 ){
79	poe_fatal(ses,"Failed to connect PPPoE socket: %d %m",errno,errno);
80	return err;
81    }
82    /* Once the logging is fixed, print a message here indicating
83       connection parameters */
84
85    return ses->fd;
86}
87
88static void disconnect_pppoe_ses(void)
89{
90    int ret;
91    warn("Doing disconnect");
92    session_disconnect(ses);
93    ses->sp.sa_addr.pppoe.sid = 0;
94    ret = connect(ses->fd, (struct sockaddr*)&ses->sp,
95	    sizeof(struct sockaddr_pppox));
96}
97
98static void init_device_pppoe(void)
99{
100    struct filter *filt;
101    ses=(void *)malloc(sizeof(struct session));
102    if(!ses){
103	fatal("No memory for new PPPoE session");
104    }
105    memset(ses,0,sizeof(struct session));
106
107    if ((ses->filt=malloc(sizeof(struct filter))) == NULL) {
108	poe_error (ses,"failed to malloc for Filter ");
109	poe_die (-1);
110    }
111
112    filt=ses->filt;  /* makes the code more readable */
113    memset(filt,0,sizeof(struct filter));
114
115    if (pppoe_ac_name !=NULL) {
116	if (strlen (pppoe_ac_name) > 255) {
117	    poe_error (ses," AC name too long (maximum allowed 256 chars)");
118	    poe_die(-1);
119	}
120	ses->filt->ntag = make_filter_tag(PTT_AC_NAME,
121					  strlen(pppoe_ac_name),
122					  pppoe_ac_name);
123
124	if ( ses->filt->ntag== NULL) {
125	    poe_error (ses,"failed to malloc for AC name");
126	    poe_die(-1);
127	}
128
129    }
130
131
132    if (pppoe_srv_name !=NULL) {
133	if (strlen (pppoe_srv_name) > 255) {
134	    poe_error (ses," Service name too long\n  (maximum allowed 256 chars)");
135	    poe_die(-1);
136	}
137	ses->filt->stag = make_filter_tag(PTT_SRV_NAME,
138					  strlen(pppoe_srv_name),
139					  pppoe_srv_name);
140	if ( ses->filt->stag == NULL) {
141	    poe_error (ses,"failed to malloc for service name");
142	    poe_die(-1);
143	}
144    }
145
146    if (hostuniq) {
147	ses->filt->htag = make_filter_tag(PTT_HOST_UNIQ,
148					  strlen(hostuniq),
149					  hostuniq);
150	if ( ses->filt->htag == NULL) {
151	    poe_error (ses,"failed to malloc for Uniq Host Id ");
152	    poe_die(-1);
153	}
154    }
155
156    if (retries) {
157	ses->retries=retries;
158    }
159
160    memcpy( ses->name, devnam, IFNAMSIZ);
161    ses->opt_debug=1;
162    ses->fd = -1;
163}
164
165static void send_config_pppoe(int mtu,
166			      u_int32_t asyncmap,
167			      int pcomp,
168			      int accomp)
169{
170    int sock;
171    struct ifreq ifr;
172
173    if (mtu > PPPOE_MTU)
174	warn("Couldn't increase MTU to %d", mtu);
175    sock = socket(AF_INET, SOCK_DGRAM, 0);
176    if (sock < 0)
177	fatal("Couldn't create IP socket: %m");
178    strlcpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name));
179    ifr.ifr_mtu = mtu;
180    if (ioctl(sock, SIOCSIFMTU, (caddr_t) &ifr) < 0)
181	fatal("ioctl(SIOCSIFMTU): %m");
182    (void) close (sock);
183}
184
185
186static void recv_config_pppoe(int mru,
187			      u_int32_t asyncmap,
188			      int pcomp,
189			      int accomp)
190{
191    if (mru > PPPOE_MTU)
192	error("Couldn't increase MRU to %d", mru);
193}
194
195struct channel pppoe_channel;
196/* Check is cp is a valid ethernet device
197 * return either 1 if "cp" is a reasonable thing to name a device
198 * or die.
199 * Note that we don't actually open the device at this point
200 * We do need to fill in:
201 *   devnam: a string representation of the device
202 */
203
204int (*old_setdevname_hook)(const char* cp) = NULL;
205int setdevname_pppoe(const char *cp)
206{
207    int ret;
208    char dev[IFNAMSIZ+1];
209    int addr[ETH_ALEN];
210    int sid;
211    option_t *opt;
212
213    ret =sscanf(cp, FMTSTRING(IFNAMSIZ),addr, addr+1, addr+2,
214		addr+3, addr+4, addr+5,&sid,dev);
215    if( ret != 8 ){
216
217	ret = get_sockaddr_ll(cp,NULL);
218        if (ret < 0)
219	    fatal("PPPoE: Cannot create PF_PACKET socket for PPPoE discovery\n");
220	if (ret == 1)
221	    strncpy(devnam, cp, sizeof(devnam));
222    }else{
223	/* long form parsed */
224	ret = get_sockaddr_ll(dev,NULL);
225        if (ret < 0)
226	    fatal("PPPoE: Cannot create PF_PACKET socket for PPPoE discovery\n");
227
228	strncpy(devnam, cp, sizeof(devnam));
229	ret = 1;
230    }
231
232    info("PPPoE: Use %s for PPPoE discovery\n", devnam);
233
234    if( ret == 1 && the_channel != &pppoe_channel ){
235
236	the_channel = &pppoe_channel;
237
238	lcp_allowoptions[0].neg_accompression = 0;
239	lcp_wantoptions[0].neg_accompression = 0;
240
241	lcp_allowoptions[0].neg_asyncmap = 0;
242	lcp_wantoptions[0].neg_asyncmap = 0;
243
244	lcp_allowoptions[0].neg_pcompression = 0;
245	lcp_wantoptions[0].neg_pcompression = 0;
246
247	ipcp_allowoptions[0].neg_vj=0;
248	ipcp_wantoptions[0].neg_vj=0;
249
250	ipcp_allowoptions[0].default_route=1;
251	ipcp_wantoptions[0].default_route=1;
252
253	for (opt = ipcp_protent.options; opt->name != NULL; ++opt) {
254		if (!strncmp(opt->name, "usepeerdns", 10)) {
255			*(bool *)(opt->addr) = 1;
256			break;
257		}
258	}
259
260#ifdef CCP_SUPPORT
261	ccp_allowoptions[0].deflate = 0 ;
262	ccp_wantoptions[0].deflate = 0 ;
263
264	ccp_allowoptions[0].bsd_compress = 0;
265	ccp_wantoptions[0].bsd_compress = 0;
266#endif
267
268	init_device_pppoe();
269    }
270    return ret;
271}
272
273struct channel pppoe_channel = {
274    options: NULL,
275    process_extra_options: NULL,
276    check_options: NULL,
277    connect: &connect_pppoe_ses,
278    disconnect: &disconnect_pppoe_ses,
279    establish_ppp: &generic_establish_ppp,
280    disestablish_ppp: &generic_disestablish_ppp,
281    send_config: &send_config_pppoe,
282    recv_config: &recv_config_pppoe,
283    close: NULL,
284    cleanup: NULL
285};
286