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