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
135	                (maximum allowed 256 chars)");
136	    poe_die(-1);
137	}
138	ses->filt->stag = make_filter_tag(PTT_SRV_NAME,
139					  strlen(pppoe_srv_name),
140					  pppoe_srv_name);
141	if ( ses->filt->stag == NULL) {
142	    poe_error (ses,"failed to malloc for service name");
143	    poe_die(-1);
144	}
145    }
146
147    if (hostuniq) {
148	ses->filt->htag = make_filter_tag(PTT_HOST_UNIQ,
149					  strlen(hostuniq),
150					  hostuniq);
151	if ( ses->filt->htag == NULL) {
152	    poe_error (ses,"failed to malloc for Uniq Host Id ");
153	    poe_die(-1);
154	}
155    }
156
157    if (retries) {
158	ses->retries=retries;
159    }
160
161    memcpy( ses->name, devnam, IFNAMSIZ);
162    ses->opt_debug=1;
163    ses->fd = -1;
164}
165
166static void send_config_pppoe(int mtu,
167			      u_int32_t asyncmap,
168			      int pcomp,
169			      int accomp)
170{
171    int sock;
172    struct ifreq ifr;
173
174    if (mtu > PPPOE_MTU)
175	warn("Couldn't increase MTU to %d", mtu);
176    sock = socket(AF_INET, SOCK_DGRAM, 0);
177    if (sock < 0)
178	fatal("Couldn't create IP socket: %m");
179    strlcpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name));
180    ifr.ifr_mtu = mtu;
181    if (ioctl(sock, SIOCSIFMTU, (caddr_t) &ifr) < 0)
182	fatal("ioctl(SIOCSIFMTU): %m");
183    (void) close (sock);
184}
185
186
187static void recv_config_pppoe(int mru,
188			      u_int32_t asyncmap,
189			      int pcomp,
190			      int accomp)
191{
192    if (mru > PPPOE_MTU)
193	error("Couldn't increase MRU to %d", mru);
194}
195
196struct channel pppoe_channel;
197/* Check is cp is a valid ethernet device
198 * return either 1 if "cp" is a reasonable thing to name a device
199 * or die.
200 * Note that we don't actually open the device at this point
201 * We do need to fill in:
202 *   devnam: a string representation of the device
203 */
204
205int (*old_setdevname_hook)(const char* cp) = NULL;
206int setdevname_pppoe(const char *cp)
207{
208    int ret;
209    char dev[IFNAMSIZ+1];
210    int addr[ETH_ALEN];
211    int sid;
212    option_t *opt;
213
214    ret =sscanf(cp, FMTSTRING(IFNAMSIZ),addr, addr+1, addr+2,
215		addr+3, addr+4, addr+5,&sid,dev);
216    if( ret != 8 ){
217
218	ret = get_sockaddr_ll(cp,NULL);
219        if (ret < 0)
220	    fatal("PPPoE: Cannot create PF_PACKET socket for PPPoE discovery\n");
221	if (ret == 1)
222	    strncpy(devnam, cp, sizeof(devnam));
223    }else{
224	/* long form parsed */
225	ret = get_sockaddr_ll(dev,NULL);
226        if (ret < 0)
227	    fatal("PPPoE: Cannot create PF_PACKET socket for PPPoE discovery\n");
228
229	strncpy(devnam, cp, sizeof(devnam));
230	ret = 1;
231    }
232
233    info("PPPoE: Use %s for PPPoE discovery\n", devnam);
234
235    if( ret == 1 && the_channel != &pppoe_channel ){
236
237	the_channel = &pppoe_channel;
238
239	lcp_allowoptions[0].neg_accompression = 0;
240	lcp_wantoptions[0].neg_accompression = 0;
241
242	lcp_allowoptions[0].neg_asyncmap = 0;
243	lcp_wantoptions[0].neg_asyncmap = 0;
244
245	lcp_allowoptions[0].neg_pcompression = 0;
246	lcp_wantoptions[0].neg_pcompression = 0;
247
248	ipcp_allowoptions[0].neg_vj=0;
249	ipcp_wantoptions[0].neg_vj=0;
250
251	ipcp_allowoptions[0].default_route=1;
252	ipcp_wantoptions[0].default_route=1;
253
254	for (opt = ipcp_protent.options; opt->name != NULL; ++opt) {
255		if (!strncmp(opt->name, "usepeerdns", 10)) {
256			*(bool *)(opt->addr) = 1;
257			break;
258		}
259	}
260
261#ifdef CCP_SUPPORT
262	ccp_allowoptions[0].deflate = 0 ;
263	ccp_wantoptions[0].deflate = 0 ;
264
265	ccp_allowoptions[0].bsd_compress = 0;
266	ccp_wantoptions[0].bsd_compress = 0;
267#endif
268
269	init_device_pppoe();
270    }
271    return ret;
272}
273
274struct channel pppoe_channel = {
275    options: NULL,
276    process_extra_options: NULL,
277    check_options: NULL,
278    connect: &connect_pppoe_ses,
279    disconnect: &disconnect_pppoe_ses,
280    establish_ppp: &generic_establish_ppp,
281    disestablish_ppp: &generic_disestablish_ppp,
282    send_config: &send_config_pppoe,
283    recv_config: &recv_config_pppoe,
284    close: NULL,
285    cleanup: NULL
286};
287
288