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