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