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