1 //----------------------------------------------------------------------- 2 // This software is part of the OpenBeOS distribution and is covered 3 // by the OpenBeOS license. 4 // 5 // Copyright (c) 2003-2004 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 19 #if DEBUG 20 static char sDigits[] = "0123456789ABCDEF"; 21 void 22 dump_packet(struct mbuf *packet) 23 { 24 if(!packet) 25 return; 26 27 uint8 *data = mtod(packet, uint8*); 28 uint8 buffer[33]; 29 uint8 bufferIndex = 0; 30 31 dprintf("Dumping packet;len=%ld;pkthdr.len=%d\n", packet->m_len, 32 packet->m_flags & M_PKTHDR ? packet->m_pkthdr.len : -1); 33 34 for(uint32 index = 0; index < packet->m_len; index++) { 35 buffer[bufferIndex++] = sDigits[data[index] >> 4]; 36 buffer[bufferIndex++] = sDigits[data[index] & 0x0F]; 37 if(bufferIndex == 32 || index == packet->m_len - 1) { 38 buffer[bufferIndex] = 0; 39 dprintf("%s\n", buffer); 40 bufferIndex = 0; 41 } 42 } 43 } 44 #endif 45 46 47 PPPoEDevice::PPPoEDevice(KPPPInterface& interface, driver_parameter *settings) 48 : KPPPDevice("PPPoE", PPPoE_HEADER_SIZE + ETHER_HDR_LEN, interface, settings), 49 fEthernetIfnet(NULL), 50 fSessionID(0), 51 fHostUniq(NewHostUniq()), 52 fACName(NULL), 53 fServiceName(NULL), 54 fAttempts(0), 55 fNextTimeout(0), 56 fState(INITIAL) 57 { 58 #if DEBUG 59 dprintf("PPPoEDevice: Constructor\n"); 60 if(!settings || !settings->parameters) 61 dprintf("PPPoEDevice::ctor: No settings!\n"); 62 #endif 63 64 interface.SetPFCOptions(PPP_ALLOW_PFC); 65 // we do not want to fail just because the other side requests PFC 66 67 memset(fPeer, 0xFF, sizeof(fPeer)); 68 SetMTU(1494); 69 // MTU size does not contain PPP header 70 71 // find ethernet device 72 const char *interfaceName = get_parameter_value(PPPoE_INTERFACE_KEY, settings); 73 if(!interfaceName) 74 return; 75 76 fACName = get_parameter_value(PPPoE_AC_NAME_KEY, settings); 77 fServiceName = get_parameter_value(PPPoE_SERVICE_NAME_KEY, settings); 78 79 fEthernetIfnet = FindPPPoEInterface(interfaceName); 80 81 #if DEBUG 82 if(!fEthernetIfnet) 83 dprintf("PPPoEDevice::ctor: could not find ethernet interface\n"); 84 #endif 85 86 add_device(this); 87 } 88 89 90 PPPoEDevice::~PPPoEDevice() 91 { 92 #if DEBUG 93 dprintf("PPPoEDevice: Destructor\n"); 94 #endif 95 96 remove_device(this); 97 } 98 99 100 status_t 101 PPPoEDevice::InitCheck() const 102 { 103 return EthernetIfnet() && EthernetIfnet()->output 104 && KPPPDevice::InitCheck() == B_OK ? B_OK : B_ERROR; 105 } 106 107 108 bool 109 PPPoEDevice::Up() 110 { 111 #if DEBUG 112 dprintf("PPPoEDevice: Up()\n"); 113 #endif 114 115 if(InitCheck() != B_OK) 116 return false; 117 118 LockerHelper locker(fLock); 119 120 if(IsUp()) 121 return true; 122 123 fState = INITIAL; 124 // reset state 125 126 if(fAttempts > PPPoE_MAX_ATTEMPTS) { 127 fAttempts = 0; 128 return false; 129 } 130 131 ++fAttempts; 132 // reset connection settings 133 memset(fPeer, 0xFF, sizeof(fPeer)); 134 135 // create PADI 136 DiscoveryPacket discovery(PADI); 137 if(ServiceName()) 138 discovery.AddTag(SERVICE_NAME, ServiceName(), strlen(ServiceName())); 139 else 140 discovery.AddTag(SERVICE_NAME, NULL, 0); 141 discovery.AddTag(HOST_UNIQ, &fHostUniq, sizeof(fHostUniq)); 142 discovery.AddTag(END_OF_LIST, NULL, 0); 143 144 // set up PPP header 145 struct mbuf *packet = discovery.ToMbuf(MTU()); 146 if(!packet) 147 return false; 148 149 // create destination 150 struct ether_header *ethernetHeader; 151 struct sockaddr destination; 152 memset(&destination, 0, sizeof(destination)); 153 destination.sa_family = AF_UNSPEC; 154 // raw packet with ethernet header 155 ethernetHeader = (struct ether_header*) destination.sa_data; 156 ethernetHeader->ether_type = ETHERTYPE_PPPOEDISC; 157 memcpy(ethernetHeader->ether_dhost, fPeer, sizeof(fPeer)); 158 159 // check if we are allowed to go up now (user intervention might disallow that) 160 if(fAttempts > 0 && !UpStarted()) { 161 fAttempts = 0; 162 DownEvent(); 163 return true; 164 // there was no error 165 } 166 167 fState = PADI_SENT; 168 // needed before sending, otherwise we might not get all packets 169 170 if(EthernetIfnet()->output(EthernetIfnet(), packet, &destination, NULL) != B_OK) { 171 fState = INITIAL; 172 fAttempts = 0; 173 dprintf("PPPoEDevice::Up(): EthernetIfnet()->output() failed!\n"); 174 return false; 175 } 176 177 fNextTimeout = system_time() + PPPoE_TIMEOUT; 178 179 return true; 180 } 181 182 183 bool 184 PPPoEDevice::Down() 185 { 186 #if DEBUG 187 dprintf("PPPoEDevice: Down()\n"); 188 #endif 189 190 if(InitCheck() != B_OK) 191 return false; 192 193 LockerHelper locker(fLock); 194 195 fState = INITIAL; 196 fAttempts = 0; 197 fNextTimeout = 0; 198 // disable timeouts 199 200 if(!IsUp()) { 201 DownEvent(); 202 return true; 203 } 204 205 DownStarted(); 206 // this tells StateMachine that DownEvent() does not mean we lost connection 207 208 // create PADT 209 DiscoveryPacket discovery(PADT, SessionID()); 210 discovery.AddTag(END_OF_LIST, NULL, 0); 211 212 struct mbuf *packet = discovery.ToMbuf(MTU()); 213 if(!packet) { 214 dprintf("PPPoEDevice::Down(): ToMbuf() failed; MTU=%ld\n", MTU()); 215 DownEvent(); 216 return false; 217 } 218 219 // create destination 220 struct ether_header *ethernetHeader; 221 struct sockaddr destination; 222 memset(&destination, 0, sizeof(destination)); 223 destination.sa_family = AF_UNSPEC; 224 // raw packet with ethernet header 225 ethernetHeader = (struct ether_header*) destination.sa_data; 226 ethernetHeader->ether_type = ETHERTYPE_PPPOEDISC; 227 memcpy(ethernetHeader->ether_dhost, fPeer, sizeof(fPeer)); 228 229 // reset connection settings 230 memset(fPeer, 0xFF, sizeof(fPeer)); 231 232 EthernetIfnet()->output(EthernetIfnet(), packet, &destination, NULL); 233 DownEvent(); 234 235 return true; 236 } 237 238 239 uint32 240 PPPoEDevice::InputTransferRate() const 241 { 242 return 10000000; 243 } 244 245 246 uint32 247 PPPoEDevice::OutputTransferRate() const 248 { 249 return 10000000; 250 } 251 252 253 uint32 254 PPPoEDevice::CountOutputBytes() const 255 { 256 // TODO: 257 // ?look through ethernet queue for outgoing pppoe packets coming from our device? 258 return 0; 259 } 260 261 262 status_t 263 PPPoEDevice::Send(struct mbuf *packet, uint16 protocolNumber = 0) 264 { 265 // Send() is only for PPP packets. PPPoE packets are sent directly to ethernet. 266 267 #if DEBUG 268 dprintf("PPPoEDevice: Send()\n"); 269 #endif 270 271 if(!packet) 272 return B_ERROR; 273 else if(InitCheck() != B_OK || protocolNumber != 0) { 274 dprintf("PPPoEDevice::Send(): InitCheck() != B_OK!\n"); 275 m_freem(packet); 276 return B_ERROR; 277 } 278 279 LockerHelper locker(fLock); 280 281 if(!IsUp()) { 282 dprintf("PPPoEDevice::Send(): no connection!\n"); 283 m_freem(packet); 284 return PPP_NO_CONNECTION; 285 } 286 287 uint16 length = packet->m_flags & M_PKTHDR ? packet->m_pkthdr.len : packet->m_len; 288 289 // encapsulate packet into pppoe header 290 M_PREPEND(packet, PPPoE_HEADER_SIZE); 291 pppoe_header *header = mtod(packet, pppoe_header*); 292 header->version = PPPoE_VERSION; 293 header->type = PPPoE_TYPE; 294 header->code = 0x00; 295 header->sessionID = SessionID(); 296 header->length = htons(length); 297 298 // create destination 299 struct ether_header *ethernetHeader; 300 struct sockaddr destination; 301 memset(&destination, 0, sizeof(destination)); 302 destination.sa_family = AF_UNSPEC; 303 // raw packet with ethernet header 304 ethernetHeader = (struct ether_header*) destination.sa_data; 305 ethernetHeader->ether_type = ETHERTYPE_PPPOE; 306 memcpy(ethernetHeader->ether_dhost, fPeer, sizeof(fPeer)); 307 308 locker.UnlockNow(); 309 310 if(!packet) 311 dprintf("PPPoEDevice::Send(): packet is NULL!\n"); 312 313 if(EthernetIfnet()->output(EthernetIfnet(), packet, &destination, NULL) != B_OK) { 314 dprintf("PPPoEDevice::Send(): EthernetIfnet()->output() failed!\n"); 315 DownEvent(); 316 // DownEvent() without DownStarted() indicates connection lost 317 return PPP_NO_CONNECTION; 318 } 319 320 return B_OK; 321 } 322 323 324 status_t 325 PPPoEDevice::Receive(struct mbuf *packet, uint16 protocolNumber = 0) 326 { 327 if(!packet) 328 return B_ERROR; 329 else if(InitCheck() != B_OK || IsDown()) { 330 m_freem(packet); 331 return B_ERROR; 332 } 333 334 complete_pppoe_header *completeHeader = mtod(packet, complete_pppoe_header*); 335 if(!completeHeader) { 336 m_freem(packet); 337 return B_ERROR; 338 } 339 340 uint8 ethernetSource[6]; 341 memcpy(ethernetSource, completeHeader->ethernetHeader.ether_shost, sizeof(fPeer)); 342 status_t result = B_OK; 343 344 if(completeHeader->ethernetHeader.ether_type == ETHERTYPE_PPPOE) { 345 m_adj(packet, ETHER_HDR_LEN); 346 pppoe_header *header = mtod(packet, pppoe_header*); 347 348 if(!IsUp() || header->version != PPPoE_VERSION || header->type != PPPoE_TYPE 349 || header->code != 0x0 || header->sessionID != SessionID()) { 350 m_freem(packet); 351 return B_ERROR; 352 } 353 354 m_adj(packet, PPPoE_HEADER_SIZE); 355 return Interface().ReceiveFromDevice(packet); 356 } else if(completeHeader->ethernetHeader.ether_type == ETHERTYPE_PPPOEDISC) { 357 m_adj(packet, ETHER_HDR_LEN); 358 pppoe_header *header = mtod(packet, pppoe_header*); 359 360 // we do not need to check HOST_UNIQ tag as this is done in pppoe.cpp 361 if(header->version != PPPoE_VERSION || header->type != PPPoE_TYPE) { 362 m_freem(packet); 363 return B_ERROR; 364 } 365 366 LockerHelper locker(fLock); 367 368 if(IsDown()) { 369 m_freem(packet); 370 return B_ERROR; 371 } 372 373 DiscoveryPacket discovery(packet); 374 switch(discovery.Code()) { 375 case PADO: { 376 if(fState != PADI_SENT) { 377 m_freem(packet); 378 return B_OK; 379 } 380 381 bool hasServiceName = false, hasACName = false; 382 pppoe_tag *tag; 383 DiscoveryPacket reply(PADR); 384 for(int32 index = 0; index < discovery.CountTags(); index++) { 385 tag = discovery.TagAt(index); 386 if(!tag) 387 continue; 388 389 switch(tag->type) { 390 case SERVICE_NAME: 391 if(!hasServiceName && (!ServiceName() 392 || (strlen(ServiceName()) == tag->length) 393 && !memcmp(tag->data, ServiceName(), 394 tag->length))) { 395 hasServiceName = true; 396 reply.AddTag(tag->type, tag->data, tag->length); 397 } 398 break; 399 400 case AC_NAME: 401 if(!hasACName && (!ACName() 402 || (strlen(ACName()) == tag->length) 403 && !memcmp(tag->data, ACName(), 404 tag->length))) { 405 hasACName = true; 406 reply.AddTag(tag->type, tag->data, tag->length); 407 } 408 break; 409 410 case AC_COOKIE: 411 case RELAY_SESSION_ID: 412 reply.AddTag(tag->type, tag->data, tag->length); 413 break; 414 415 case SERVICE_NAME_ERROR: 416 case AC_SYSTEM_ERROR: 417 case GENERIC_ERROR: 418 m_freem(packet); 419 return B_ERROR; 420 break; 421 422 default: 423 ; 424 } 425 } 426 427 if(!hasServiceName) { 428 m_freem(packet); 429 return B_ERROR; 430 } 431 432 reply.AddTag(HOST_UNIQ, &fHostUniq, sizeof(fHostUniq)); 433 reply.AddTag(END_OF_LIST, NULL, 0); 434 struct mbuf *replyPacket = reply.ToMbuf(MTU()); 435 if(!replyPacket) { 436 m_freem(packet); 437 return B_ERROR; 438 } 439 440 memcpy(fPeer, ethernetSource, sizeof(fPeer)); 441 442 // create destination 443 struct ether_header *ethernetHeader; 444 struct sockaddr destination; 445 memset(&destination, 0, sizeof(destination)); 446 destination.sa_family = AF_UNSPEC; 447 // raw packet with ethernet header 448 ethernetHeader = (struct ether_header*) destination.sa_data; 449 ethernetHeader->ether_type = ETHERTYPE_PPPOEDISC; 450 memcpy(ethernetHeader->ether_dhost, fPeer, sizeof(fPeer)); 451 452 fState = PADR_SENT; 453 454 if(EthernetIfnet()->output(EthernetIfnet(), replyPacket, &destination, 455 NULL) != B_OK) { 456 m_freem(packet); 457 return B_ERROR; 458 } 459 460 fNextTimeout = system_time() + PPPoE_TIMEOUT; 461 } break; 462 463 case PADS: 464 if(fState != PADR_SENT 465 || memcmp(ethernetSource, fPeer, sizeof(fPeer))) { 466 m_freem(packet); 467 return B_ERROR; 468 } 469 470 fSessionID = header->sessionID; 471 fState = OPENED; 472 fNextTimeout = 0; 473 UpEvent(); 474 break; 475 476 case PADT: 477 if(!IsUp() 478 || memcmp(ethernetSource, fPeer, sizeof(fPeer)) 479 || header->sessionID != SessionID()) { 480 m_freem(packet); 481 return B_ERROR; 482 } 483 484 fState = INITIAL; 485 fAttempts = 0; 486 fSessionID = 0; 487 fNextTimeout = 0; 488 DownEvent(); 489 break; 490 491 default: 492 m_freem(packet); 493 return B_ERROR; 494 } 495 } else 496 result = B_ERROR; 497 498 m_freem(packet); 499 return result; 500 } 501 502 503 void 504 PPPoEDevice::Pulse() 505 { 506 // We use Pulse() for timeout of connection establishment. 507 508 if(fNextTimeout == 0 || IsUp() || IsDown()) 509 return; 510 511 LockerHelper locker(fLock); 512 513 // check if timed out 514 if(system_time() >= fNextTimeout) { 515 if(!Up()) 516 UpFailedEvent(); 517 } 518 } 519