1 //---------------------------------------------------------------------- 2 // This software is part of the OpenBeOS distribution and is covered 3 // by the OpenBeOS license. 4 // 5 // Copyright (c) 2003 Waldemar Kornewald, Waldemar.Kornewald@web.de 6 //--------------------------------------------------------------------- 7 8 #include "PPPoEDevice.h" 9 #include "DiscoveryPacket.h" 10 11 #include <core_funcs.h> 12 #include <cstdlib> 13 14 // from libkernelppp 15 #include <settings_tools.h> 16 #include <LockerHelper.h> 17 18 #ifdef _KERNEL_MODE 19 #define spawn_thread spawn_kernel_thread 20 #define printf dprintf 21 #else 22 #include <cstdio> 23 #endif 24 25 26 PPPoEDevice::PPPoEDevice(PPPInterface& interface, driver_parameter *settings) 27 : PPPDevice("PPPoE", interface, settings), 28 fEthernetIfnet(NULL), 29 fSessionID(0), 30 fHostUniq(NewHostUniq()), 31 fACName(NULL), 32 fServiceName(NULL), 33 fNextTimeout(0), 34 fState(INITIAL) 35 { 36 #if DEBUG 37 printf("PPPoEDevice: Constructor\n"); 38 if(!settings || !settings->parameters) 39 printf("PPPoEDevice::ctor: No settings!\n"); 40 #endif 41 42 memset(fPeer, 0xFF, sizeof(fPeer)); 43 SetMTU(1494); 44 // MTU size does not contain PPP header 45 46 // find ethernet device 47 const char *interfaceName = get_parameter_value(PPPoE_INTERFACE_KEY, settings); 48 if(!interfaceName) 49 return; 50 #if DEBUG 51 printf("PPPoEDevice::ctor: interfaceName: %s\n", interfaceName); 52 #endif 53 54 ifnet *current = get_interfaces(); 55 for(; current; current = current->if_next) { 56 if(current->if_type == IFT_ETHER && current->if_name 57 && !strcmp(current->if_name, interfaceName)) { 58 #if DEBUG 59 printf("PPPoEDevice::ctor: found ethernet interface\n"); 60 #endif 61 fEthernetIfnet = current; 62 break; 63 } 64 } 65 66 #if DEBUG 67 if(!fEthernetIfnet) 68 printf("PPPoEDevice::ctor: could not find ethernet interface\n"); 69 #endif 70 } 71 72 73 PPPoEDevice::~PPPoEDevice() 74 { 75 #if DEBUG 76 printf("PPPoEDevice: Destructor\n"); 77 #endif 78 79 free(fACName); 80 free(fServiceName); 81 } 82 83 84 status_t 85 PPPoEDevice::InitCheck() const 86 { 87 return EthernetIfnet() && EthernetIfnet()->output 88 && PPPDevice::InitCheck() == B_OK ? B_OK : B_ERROR; 89 } 90 91 92 bool 93 PPPoEDevice::Up() 94 { 95 #if DEBUG 96 printf("PPPoEDevice: Up()\n"); 97 #endif 98 99 if(InitCheck() != B_OK) 100 return false; 101 102 LockerHelper locker(fLock); 103 104 if(IsUp()) 105 return true; 106 107 // reset connection settings 108 memset(fPeer, 0xFF, sizeof(fPeer)); 109 110 // create PADI 111 DiscoveryPacket discovery(PADI); 112 if(fServiceName) 113 discovery.AddTag(SERVICE_NAME, strlen(fServiceName), fServiceName); 114 else 115 discovery.AddTag(SERVICE_NAME, 0, NULL); 116 discovery.AddTag(HOST_UNIQ, sizeof(uint32), &fHostUniq); 117 discovery.AddTag(END_OF_LIST, 0, NULL); 118 119 // set up PPP header 120 struct mbuf *packet = discovery.ToMbuf(); 121 if(!packet) 122 return false; 123 124 // create destination 125 struct sockaddr destination; 126 memset(&destination, 0, sizeof(destination)); 127 destination.sa_family = AF_UNSPEC; 128 // raw packet with ethernet header 129 memcpy(destination.sa_data, fPeer, sizeof(fPeer)); 130 131 if(EthernetIfnet()->output(EthernetIfnet(), packet, &destination, NULL) != B_OK) 132 return false; 133 134 // check if we are allowed to go up now (user intervention might disallow that) 135 if(!UpStarted()) { 136 fState = INITIAL; 137 // reset state 138 DownEvent(); 139 return true; 140 } 141 142 fState = PADI_SENT; 143 144 fNextTimeout = system_time() + PPPoE_TIMEOUT; 145 146 return true; 147 } 148 149 150 bool 151 PPPoEDevice::Down() 152 { 153 #if DEBUG 154 printf("PPPoEDevice: Down()\n"); 155 #endif 156 157 if(InitCheck() != B_OK) 158 return false; 159 160 LockerHelper locker(fLock); 161 162 fNextTimeout = 0; 163 // disable timeouts 164 165 DownStarted(); 166 // this tells StateMachine that DownEvent() does not mean we lost connection 167 168 if(!IsUp()) { 169 DownEvent(); 170 return true; 171 } 172 173 // create PADT 174 DiscoveryPacket discovery(PADT, SessionID()); 175 discovery.AddTag(END_OF_LIST, 0, NULL); 176 177 struct mbuf *packet = discovery.ToMbuf(); 178 if(!packet) { 179 DownEvent(); 180 return false; 181 } 182 183 // create destination 184 struct sockaddr destination; 185 memset(&destination, 0, sizeof(destination)); 186 destination.sa_family = AF_UNSPEC; 187 // raw packet with ethernet header 188 memcpy(destination.sa_data, fPeer, sizeof(fPeer)); 189 190 // reset connection settings 191 memset(fPeer, 0xFF, sizeof(fPeer)); 192 193 EthernetIfnet()->output(EthernetIfnet(), packet, &destination, NULL); 194 DownEvent(); 195 196 return false; 197 } 198 199 200 uint32 201 PPPoEDevice::InputTransferRate() const 202 { 203 return 10000000; 204 } 205 206 207 uint32 208 PPPoEDevice::OutputTransferRate() const 209 { 210 return 10000000; 211 } 212 213 214 uint32 215 PPPoEDevice::CountOutputBytes() const 216 { 217 // TODO: 218 // ?look through ethernet queue for outgoing pppoe packets coming from our device? 219 return 0; 220 } 221 222 223 status_t 224 PPPoEDevice::Send(struct mbuf *packet, uint16 protocolNumber = 0) 225 { 226 #if DEBUG 227 printf("PPPoEDevice: Send()\n"); 228 #endif 229 230 if(InitCheck() != B_OK || protocolNumber != 0) { 231 m_freem(packet); 232 return B_ERROR; 233 } else if(!packet) 234 return B_ERROR; 235 236 LockerHelper locker(fLock); 237 238 if(!IsUp()) 239 return PPP_NO_CONNECTION; 240 241 uint16 length = packet->m_flags & M_PKTHDR ? packet->m_pkthdr.len : packet->m_len; 242 243 // encapsulate packet into pppoe header 244 M_PREPEND(packet, PPPoE_HEADER_SIZE); 245 pppoe_header *header = mtod(packet, pppoe_header*); 246 header->ethernetHeader.ether_type = ETHERTYPE_PPPOE; 247 header->version = PPPoE_VERSION; 248 header->type = PPPoE_TYPE; 249 header->code = 0x00; 250 header->sessionID = SessionID(); 251 header->length = htons(length); 252 253 // create destination 254 struct sockaddr destination; 255 memset(&destination, 0, sizeof(destination)); 256 destination.sa_family = AF_UNSPEC; 257 // raw packet with ethernet header 258 memcpy(destination.sa_data, fPeer, sizeof(fPeer)); 259 260 locker.UnlockNow(); 261 262 if(EthernetIfnet()->output(EthernetIfnet(), packet, &destination, NULL) != B_OK) { 263 printf("PPPoEDevice::Send(): EthernetIfnet()->output() failed!\n"); 264 DownEvent(); 265 // DownEvent() without DownStarted() indicates connection lost 266 return PPP_NO_CONNECTION; 267 } 268 269 return B_OK; 270 } 271 272 273 status_t 274 PPPoEDevice::Receive(struct mbuf *packet, uint16 protocolNumber = 0) 275 { 276 #if DEBUG 277 printf("PPPoEDevice: Receive()\n"); 278 #endif 279 280 if(InitCheck() != B_OK || IsDown()) { 281 m_freem(packet); 282 return B_ERROR; 283 } else if(!packet) 284 return B_ERROR; 285 286 pppoe_header *header = mtod(packet, pppoe_header*); 287 if(!header) { 288 m_freem(packet); 289 return B_ERROR; 290 } 291 292 status_t result = B_OK; 293 294 if(header->ethernetHeader.ether_type == ETHERTYPE_PPPOE) { 295 if(!IsUp() || header->version != PPPoE_VERSION || header->type != PPPoE_TYPE 296 || header->code != 0x0 || header->sessionID != SessionID()) { 297 m_freem(packet); 298 return B_ERROR; 299 } 300 301 m_adj(packet, PPPoE_HEADER_SIZE); 302 return Interface().ReceiveFromDevice(packet); 303 } else if(header->ethernetHeader.ether_type == ETHERTYPE_PPPOEDISC) { 304 // we do not need to check HOST_UNIQ tag as this is done in pppoe.cpp 305 if(header->version != PPPoE_VERSION || header->type != PPPoE_TYPE) { 306 m_freem(packet); 307 return B_ERROR; 308 } 309 310 LockerHelper locker(fLock); 311 312 if(IsDown()) { 313 m_freem(packet); 314 return B_ERROR; 315 } 316 317 DiscoveryPacket discovery(packet); 318 switch(discovery.Code()) { 319 case PADO: { 320 if(fState != PADI_SENT) { 321 m_freem(packet); 322 return B_OK; 323 } 324 325 bool hasServiceName = false; 326 pppoe_tag *tag; 327 DiscoveryPacket reply(PADR); 328 for(int32 index = 0; index < discovery.CountTags(); index++) { 329 tag = discovery.TagAt(index); 330 switch(tag->type) { 331 case SERVICE_NAME: 332 if(!hasServiceName && (!fServiceName 333 || !memcmp(tag->data, fServiceName, tag->length))) { 334 hasServiceName = true; 335 reply.AddTag(tag->type, tag->length, tag->data); 336 } 337 break; 338 339 case AC_COOKIE: 340 case RELAY_SESSION_ID: 341 reply.AddTag(tag->type, tag->length, tag->data); 342 break; 343 344 case SERVICE_NAME_ERROR: 345 case AC_SYSTEM_ERROR: 346 case GENERIC_ERROR: 347 m_freem(packet); 348 return B_ERROR; 349 break; 350 351 default: 352 ; 353 } 354 } 355 356 if(!hasServiceName) { 357 m_freem(packet); 358 return B_ERROR; 359 } 360 361 reply.AddTag(END_OF_LIST, 0, NULL); 362 struct mbuf *replyPacket = reply.ToMbuf(); 363 if(!replyPacket) { 364 m_freem(packet); 365 return B_ERROR; 366 } 367 368 memcpy(fPeer, header->ethernetHeader.ether_shost, sizeof(fPeer)); 369 370 // create destination 371 struct sockaddr destination; 372 memset(&destination, 0, sizeof(destination)); 373 destination.sa_family = AF_UNSPEC; 374 // raw packet with ethernet header 375 memcpy(destination.sa_data, fPeer, sizeof(fPeer)); 376 377 if(EthernetIfnet()->output(EthernetIfnet(), replyPacket, &destination, 378 NULL) != B_OK) { 379 m_freem(packet); 380 return B_ERROR; 381 } 382 383 fState = PADR_SENT; 384 fNextTimeout = system_time() + PPPoE_TIMEOUT; 385 } break; 386 387 case PADS: 388 if(fState != PADR_SENT 389 || memcmp(header->ethernetHeader.ether_shost, fPeer, 390 sizeof(fPeer))) { 391 m_freem(packet); 392 return B_ERROR; 393 } 394 395 fSessionID = header->sessionID; 396 fState = OPENED; 397 fNextTimeout = 0; 398 UpEvent(); 399 break; 400 401 case PADT: 402 if(!IsUp() 403 || memcmp(header->ethernetHeader.ether_shost, fPeer, 404 sizeof(fPeer)) 405 || header->sessionID != SessionID()) { 406 m_freem(packet); 407 return B_ERROR; 408 } 409 410 fState = INITIAL; 411 fSessionID = 0; 412 fNextTimeout = 0; 413 DownEvent(); 414 break; 415 416 default: 417 m_freem(packet); 418 return B_ERROR; 419 } 420 } else 421 result = B_ERROR; 422 423 m_freem(packet); 424 return result; 425 } 426 427 428 void 429 PPPoEDevice::Pulse() 430 { 431 // We use Pulse() for timeout of connection establishment. 432 433 if(fNextTimeout == 0 || IsUp() || IsDown()) 434 return; 435 436 LockerHelper locker(fLock); 437 438 // check if timed out 439 if(system_time() >= fNextTimeout) 440 Up(); 441 } 442