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 /*! \class KPPPLCP 9 \brief The LCP protocol. 10 11 Every PPP interface \e must have an LCP protocol. It is used for establishing 12 and terminating connections. \n 13 When establishing a connecition the LCP protocol determines connection-specific 14 settings like the packet MRU. These settings are handled by the KPPPOptionHandler 15 class. Additional LCP codes like the PPP Multilink-Protocol uses them should 16 be implemented through the KPPPLCPExtension class. \n 17 */ 18 19 #include <KPPPInterface.h> 20 #include <KPPPDevice.h> 21 #include <KPPPLCPExtension.h> 22 #include <KPPPOptionHandler.h> 23 24 #include <LockerHelper.h> 25 26 #include <netinet/in.h> 27 #include <core_funcs.h> 28 #include <sys/socket.h> 29 30 31 //! Creates a new LCP protocol for the given interface. 32 KPPPLCP::KPPPLCP(KPPPInterface& interface) 33 : KPPPProtocol("LCP", PPP_ESTABLISHMENT_PHASE, PPP_LCP_PROTOCOL, 34 PPP_PROTOCOL_LEVEL, AF_UNSPEC, 0, interface, NULL, PPP_ALWAYS_ALLOWED), 35 fStateMachine(interface.StateMachine()), 36 fTarget(NULL) 37 { 38 SetUpRequested(false); 39 // the state machine does everything for us 40 } 41 42 43 //! Deletes all added option handlers and LCP extensions. 44 KPPPLCP::~KPPPLCP() 45 { 46 while(CountOptionHandlers()) 47 delete OptionHandlerAt(0); 48 while(CountLCPExtensions()) 49 delete LCPExtensionAt(0); 50 } 51 52 53 /*! \brief Adds a new option handler. 54 55 NOTE: You can only add option handlers in \c PPP_DOWN_PHASE. \n 56 There may only be one handler per option type! 57 */ 58 bool 59 KPPPLCP::AddOptionHandler(KPPPOptionHandler *optionHandler) 60 { 61 if(!optionHandler || &optionHandler->Interface() != &Interface()) 62 return false; 63 64 LockerHelper locker(StateMachine().fLock); 65 66 if(Interface().Phase() != PPP_DOWN_PHASE 67 || OptionHandlerFor(optionHandler->Type())) 68 return false; 69 // a running connection may not change and there may only be 70 // one handler per option type 71 72 return fOptionHandlers.AddItem(optionHandler); 73 } 74 75 76 /*! \brief Removes an option handler, but does not delete it. 77 78 NOTE: You can only remove option handlers in \c PPP_DOWN_PHASE. 79 */ 80 bool 81 KPPPLCP::RemoveOptionHandler(KPPPOptionHandler *optionHandler) 82 { 83 LockerHelper locker(StateMachine().fLock); 84 85 if(Interface().Phase() != PPP_DOWN_PHASE) 86 return false; 87 // a running connection may not change 88 89 return fOptionHandlers.RemoveItem(optionHandler); 90 } 91 92 93 //! Returns the option handler at the given \a index. 94 KPPPOptionHandler* 95 KPPPLCP::OptionHandlerAt(int32 index) const 96 { 97 KPPPOptionHandler *optionHandler = fOptionHandlers.ItemAt(index); 98 99 if(optionHandler == fOptionHandlers.GetDefaultItem()) 100 return NULL; 101 102 return optionHandler; 103 } 104 105 106 //! Returns the option handler that can handle options of a given \a type. 107 KPPPOptionHandler* 108 KPPPLCP::OptionHandlerFor(uint8 type, int32 *start = NULL) const 109 { 110 // The iteration style in this method is strange C/C++. 111 // Explanation: I use this style because it makes extending all XXXFor 112 // methods simpler as that they look very similar, now. 113 114 int32 index = start ? *start : 0; 115 116 if(index < 0) 117 return NULL; 118 119 KPPPOptionHandler *current = OptionHandlerAt(index); 120 121 for(; current; current = OptionHandlerAt(++index)) { 122 if(current->Type() == type) { 123 if(start) 124 *start = index; 125 return current; 126 } 127 } 128 129 return NULL; 130 } 131 132 133 /*! \brief Adds a new LCP extension. 134 135 NOTE: You can only add LCP extensions in \c PPP_DOWN_PHASE. 136 */ 137 bool 138 KPPPLCP::AddLCPExtension(KPPPLCPExtension *lcpExtension) 139 { 140 if(!lcpExtension || &lcpExtension->Interface() != &Interface()) 141 return false; 142 143 LockerHelper locker(StateMachine().fLock); 144 145 if(Interface().Phase() != PPP_DOWN_PHASE) 146 return false; 147 // a running connection may not change 148 149 return fLCPExtensions.AddItem(lcpExtension); 150 } 151 152 153 /*! \brief Removes an LCP extension, but does not delete it. 154 155 NOTE: You can only remove LCP extensions in \c PPP_DOWN_PHASE. 156 */ 157 bool 158 KPPPLCP::RemoveLCPExtension(KPPPLCPExtension *lcpExtension) 159 { 160 LockerHelper locker(StateMachine().fLock); 161 162 if(Interface().Phase() != PPP_DOWN_PHASE) 163 return false; 164 // a running connection may not change 165 166 return fLCPExtensions.RemoveItem(lcpExtension); 167 } 168 169 170 //! Returns the LCP extension at the given \a index. 171 KPPPLCPExtension* 172 KPPPLCP::LCPExtensionAt(int32 index) const 173 { 174 KPPPLCPExtension *lcpExtension = fLCPExtensions.ItemAt(index); 175 176 if(lcpExtension == fLCPExtensions.GetDefaultItem()) 177 return NULL; 178 179 return lcpExtension; 180 } 181 182 183 //! Returns the LCP extension that can handle LCP packets of a given \a code. 184 KPPPLCPExtension* 185 KPPPLCP::LCPExtensionFor(uint8 code, int32 *start = NULL) const 186 { 187 // The iteration style in this method is strange C/C++. 188 // Explanation: I use this style because it makes extending all XXXFor 189 // methods simpler as that they look very similar, now. 190 191 int32 index = start ? *start : 0; 192 193 if(index < 0) 194 return NULL; 195 196 KPPPLCPExtension *current = LCPExtensionAt(index); 197 198 for(; current; current = LCPExtensionAt(++index)) { 199 if(current->Code() == code) { 200 if(start) 201 *start = index; 202 return current; 203 } 204 } 205 206 return NULL; 207 } 208 209 210 //! Returns the interface, target, and device overhead. 211 uint32 212 KPPPLCP::AdditionalOverhead() const 213 { 214 uint32 overhead = Interface().Overhead(); 215 216 if(Target()) 217 overhead += Target()->Overhead(); 218 219 if(Interface().Device()) 220 overhead += Interface().Device()->Overhead(); 221 222 return overhead; 223 } 224 225 226 //! Calls \c ProfileChanged() for each option handler and LCP extension. 227 void 228 KPPPLCP::ProfileChanged() 229 { 230 KPPPLCPExtension *extension; 231 KPPPOptionHandler *handler; 232 233 for(int32 index = 0; index < CountLCPExtensions(); index++) { 234 extension = LCPExtensionAt(index); 235 if(extension) 236 extension->ProfileChanged(); 237 } 238 239 for(int32 index = 0; index < CountOptionHandlers(); index++) { 240 handler = OptionHandlerAt(index); 241 if(handler) 242 handler->ProfileChanged(); 243 } 244 } 245 246 247 //! Always returns \c true. 248 bool 249 KPPPLCP::Up() 250 { 251 return true; 252 } 253 254 255 //! Always returns \c true. 256 bool 257 KPPPLCP::Down() 258 { 259 return true; 260 } 261 262 263 //! Sends a packet to the target (if there is one) or to the interface. 264 status_t 265 KPPPLCP::Send(struct mbuf *packet, uint16 protocolNumber = PPP_LCP_PROTOCOL) 266 { 267 if(Target()) 268 return Target()->Send(packet, PPP_LCP_PROTOCOL); 269 else 270 return Interface().Send(packet, PPP_LCP_PROTOCOL); 271 } 272 273 274 //! Decodes the LCP packet and passes it to the KPPPStateMachine or an LCP extension. 275 status_t 276 KPPPLCP::Receive(struct mbuf *packet, uint16 protocolNumber) 277 { 278 if(!packet) 279 return B_ERROR; 280 281 if(protocolNumber != PPP_LCP_PROTOCOL) { 282 dprintf("KPPPLCP::Receive(): wrong protocol number!\n"); 283 return PPP_UNHANDLED; 284 } 285 286 ppp_lcp_packet *data = mtod(packet, ppp_lcp_packet*); 287 288 // remove padding 289 int32 length = packet->m_len; 290 if(packet->m_flags & M_PKTHDR) 291 length = packet->m_pkthdr.len; 292 length -= ntohs(data->length); 293 if(length) 294 m_adj(packet, -length); 295 296 struct mbuf *copy = m_gethdr(MT_DATA); 297 if(copy) { 298 copy->m_data += AdditionalOverhead(); 299 copy->m_pkthdr.len = copy->m_len = packet->m_len; 300 memcpy(copy->m_data, packet->m_data, copy->m_len); 301 } 302 303 if(ntohs(data->length) < 4) 304 return B_ERROR; 305 306 bool handled = true; 307 308 switch(data->code) { 309 case PPP_CONFIGURE_REQUEST: 310 StateMachine().RCREvent(packet); 311 break; 312 313 case PPP_CONFIGURE_ACK: 314 StateMachine().RCAEvent(packet); 315 break; 316 317 case PPP_CONFIGURE_NAK: 318 case PPP_CONFIGURE_REJECT: 319 StateMachine().RCNEvent(packet); 320 break; 321 322 case PPP_TERMINATE_REQUEST: 323 StateMachine().RTREvent(packet); 324 break; 325 326 case PPP_TERMINATE_ACK: 327 StateMachine().RTAEvent(packet); 328 break; 329 330 case PPP_CODE_REJECT: 331 StateMachine().RXJEvent(packet); 332 break; 333 334 case PPP_PROTOCOL_REJECT: 335 StateMachine().RXJEvent(packet); 336 break; 337 338 case PPP_ECHO_REQUEST: 339 case PPP_ECHO_REPLY: 340 case PPP_DISCARD_REQUEST: 341 StateMachine().RXREvent(packet); 342 break; 343 344 default: 345 m_freem(packet); 346 handled = false; 347 } 348 349 packet = copy; 350 351 if(!packet) 352 return handled ? B_OK : B_ERROR; 353 354 status_t result = B_OK; 355 356 // Try to find LCP extensions that can handle this code. 357 // We must duplicate the packet in order to ask all handlers. 358 int32 index = 0; 359 KPPPLCPExtension *lcpExtension = LCPExtensionFor(data->code, &index); 360 for(; lcpExtension; lcpExtension = LCPExtensionFor(data->code, &(++index))) { 361 if(!lcpExtension->IsEnabled()) 362 continue; 363 364 result = lcpExtension->Receive(packet, data->code); 365 366 // check return value and return it on error 367 if(result == B_OK) 368 handled = true; 369 else if(result != PPP_UNHANDLED) { 370 m_freem(packet); 371 return result; 372 } 373 } 374 375 if(!handled) { 376 StateMachine().RUCEvent(packet, PPP_LCP_PROTOCOL, PPP_CODE_REJECT); 377 return PPP_REJECTED; 378 } 379 380 m_freem(packet); 381 382 return result; 383 } 384 385 386 //! Calls \c Pulse() for each LCP extension. 387 void 388 KPPPLCP::Pulse() 389 { 390 StateMachine().TimerEvent(); 391 392 for(int32 index = 0; index < CountLCPExtensions(); index++) 393 LCPExtensionAt(index)->Pulse(); 394 } 395