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