1 /* 2 * Copyright 2003-2006, Waldemar Kornewald <wkornew@gmx.net> 3 * Distributed under the terms of the MIT License. 4 */ 5 6 #include <KernelExport.h> 7 #include <driver_settings.h> 8 #include <core_funcs.h> 9 #include <net_module.h> 10 11 #include <ethernet_module.h> 12 13 #include <KPPPInterface.h> 14 #include <KPPPModule.h> 15 #include <KPPPUtils.h> 16 #include <LockerHelper.h> 17 18 #include "PPPoEDevice.h" 19 #include "DiscoveryPacket.h" 20 21 22 typedef struct pppoe_query { 23 ifnet *ethernetIfnet; 24 uint32 hostUniq; 25 thread_id receiver; 26 } pppoe_query; 27 28 #define PPPoE_MODULE_NAME NETWORK_MODULES_ROOT "ppp/pppoe" 29 30 struct core_module_info *core = NULL; 31 static struct ethernet_module_info *sEthernet; 32 static int32 sHostUniq = 0; 33 status_t std_ops(int32 op, ...); 34 35 static BLocker sLock("PPPoEList"); 36 static TemplateList<PPPoEDevice*> *sDevices; 37 static TemplateList<pppoe_query*> *sQueries; 38 39 40 static 41 void 42 SendQueryPacket(pppoe_query *query, DiscoveryPacket& discovery) 43 { 44 char data[PPPoE_QUERY_REPORT_SIZE]; 45 uint32 position = sizeof(uint32); 46 pppoe_tag *acName = discovery.TagWithType(AC_NAME); 47 48 if(acName) { 49 if(acName->length >= PPPoE_QUERY_REPORT_SIZE) 50 return; 51 52 memcpy(data + position, acName->data, acName->length); 53 position += acName->length; 54 } 55 56 data[position++] = 0; 57 58 pppoe_tag *tag; 59 for(int32 index = 0; index < discovery.CountTags(); index++) { 60 tag = discovery.TagAt(index); 61 if(tag && tag->type == SERVICE_NAME) { 62 if(position + tag->length >= PPPoE_QUERY_REPORT_SIZE) 63 return; 64 65 memcpy(data + position, tag->data, tag->length); 66 position += tag->length; 67 data[position++] = 0; 68 } 69 } 70 71 memcpy(data, &position, sizeof(uint32)); 72 // add the total length 73 74 send_data_with_timeout(query->receiver, PPPoE_QUERY_REPORT, data, 75 PPPoE_QUERY_REPORT_SIZE, 700000); 76 } 77 78 79 ifnet* 80 FindPPPoEInterface(const char *name) 81 { 82 if(!name) 83 return NULL; 84 85 ifnet *current = get_interfaces(); 86 for(; current; current = current->if_next) { 87 if(current->if_type == IFT_ETHER && current->if_name 88 && !strcmp(current->if_name, name)) 89 return current; 90 } 91 92 return NULL; 93 } 94 95 96 uint32 97 NewHostUniq() 98 { 99 return (uint32) atomic_add(&sHostUniq, 1); 100 } 101 102 103 void 104 add_device(PPPoEDevice *device) 105 { 106 TRACE("PPPoE: add_device()\n"); 107 108 LockerHelper locker(sLock); 109 if(!sDevices->HasItem(device)) 110 sDevices->AddItem(device); 111 } 112 113 114 void 115 remove_device(PPPoEDevice *device) 116 { 117 TRACE("PPPoE: remove_device()\n"); 118 119 LockerHelper locker(sLock); 120 sDevices->RemoveItem(device); 121 } 122 123 124 static 125 void 126 pppoe_input(struct mbuf *packet) 127 { 128 if(!packet) 129 return; 130 131 ifnet *sourceIfnet = packet->m_pkthdr.rcvif; 132 complete_pppoe_header *header = mtod(packet, complete_pppoe_header*); 133 PPPoEDevice *device; 134 pppoe_query *query; 135 136 LockerHelper locker(sLock); 137 138 if(header->ethernetHeader.ether_type == ETHERTYPE_PPPOEDISC 139 && header->pppoeHeader.length <= PPPoE_QUERY_REPORT_SIZE) { 140 for(int32 index = 0; index < sDevices->CountItems(); index++) { 141 query = sQueries->ItemAt(index); 142 143 if(query && query->ethernetIfnet == sourceIfnet) { 144 if(header->pppoeHeader.code == PADO) { 145 DiscoveryPacket discovery(packet, ETHER_HDR_LEN); 146 if(discovery.InitCheck() != B_OK) { 147 ERROR("PPPoE: received corrupted discovery packet!\n"); 148 m_freem(packet); 149 return; 150 } 151 152 pppoe_tag *hostTag = discovery.TagWithType(HOST_UNIQ); 153 if(hostTag && hostTag->length == 4 154 && *((uint32*)hostTag->data) == query->hostUniq) { 155 SendQueryPacket(query, discovery); 156 m_freem(packet); 157 return; 158 } 159 } 160 } 161 } 162 } 163 164 for(int32 index = 0; index < sDevices->CountItems(); index++) { 165 device = sDevices->ItemAt(index); 166 167 if(device && device->EthernetIfnet() == sourceIfnet) { 168 if(header->ethernetHeader.ether_type == ETHERTYPE_PPPOE 169 && header->pppoeHeader.sessionID == device->SessionID()) { 170 TRACE("PPPoE: session packet\n"); 171 device->Receive(packet); 172 return; 173 } else if(header->ethernetHeader.ether_type == ETHERTYPE_PPPOEDISC 174 && header->pppoeHeader.code != PADI 175 && header->pppoeHeader.code != PADR 176 && !device->IsDown()) { 177 TRACE("PPPoE: discovery packet\n"); 178 DiscoveryPacket discovery(packet, ETHER_HDR_LEN); 179 if(discovery.InitCheck() != B_OK) { 180 ERROR("PPPoE: received corrupted discovery packet!\n"); 181 m_freem(packet); 182 return; 183 } 184 185 pppoe_tag *tag = discovery.TagWithType(HOST_UNIQ); 186 if(header->pppoeHeader.code == PADT || (tag && tag->length == 4 187 && *((uint32*)tag->data) == device->HostUniq())) { 188 device->Receive(packet); 189 return; 190 } 191 } 192 } 193 } 194 195 ERROR("PPPoE: No device found for packet from: %s\n", sourceIfnet->if_name); 196 m_freem(packet); 197 } 198 199 200 static 201 bool 202 add_to(KPPPInterface& mainInterface, KPPPInterface *subInterface, 203 driver_parameter *settings, ppp_module_key_type type) 204 { 205 if(mainInterface.Mode() != PPP_CLIENT_MODE || type != PPP_DEVICE_KEY_TYPE) 206 return B_ERROR; 207 208 PPPoEDevice *device; 209 bool success; 210 if(subInterface) { 211 device = new PPPoEDevice(*subInterface, settings); 212 success = subInterface->SetDevice(device); 213 } else { 214 device = new PPPoEDevice(mainInterface, settings); 215 success = mainInterface.SetDevice(device); 216 } 217 218 TRACE("PPPoE: add_to(): %s\n", 219 success && device && device->InitCheck() == B_OK ? "OK" : "ERROR"); 220 221 return success && device && device->InitCheck() == B_OK; 222 } 223 224 225 static 226 status_t 227 control(uint32 op, void *data, size_t length) 228 { 229 switch(op) { 230 case PPPoE_GET_INTERFACES: { 231 int32 position = 0, count = 0; 232 char *names = (char*) data; 233 234 ifnet *current = get_interfaces(); 235 for(; current; current = current->if_next) { 236 if(current->if_type == IFT_ETHER && current->if_name) { 237 if(position + strlen(current->if_name) + 1 > length) 238 return B_NO_MEMORY; 239 240 strcpy(names + position, current->if_name); 241 position += strlen(current->if_name); 242 names[position++] = 0; 243 ++count; 244 } 245 } 246 247 return count; 248 } 249 250 case PPPoE_QUERY_SERVICES: { 251 // XXX: as all modules are loaded on-demand we must wait for the results 252 253 if(!data || length != sizeof(pppoe_query_request)) 254 return B_ERROR; 255 256 pppoe_query_request *request = (pppoe_query_request*) data; 257 258 pppoe_query query; 259 query.ethernetIfnet = FindPPPoEInterface(request->interfaceName); 260 if(!query.ethernetIfnet) 261 return B_BAD_VALUE; 262 263 query.hostUniq = NewHostUniq(); 264 query.receiver = request->receiver; 265 266 sLock.Lock(); 267 sQueries->AddItem(&query); 268 sLock.Unlock(); 269 270 snooze(2000000); 271 // wait two seconds for results 272 273 sLock.Lock(); 274 sQueries->RemoveItem(&query); 275 sLock.Unlock(); 276 } break; 277 278 default: 279 return B_ERROR; 280 } 281 282 return B_OK; 283 } 284 285 286 static ppp_module_info pppoe_module = { 287 { 288 PPPoE_MODULE_NAME, 289 0, 290 std_ops 291 }, 292 control, 293 add_to 294 }; 295 296 297 _EXPORT 298 status_t 299 std_ops(int32 op, ...) 300 { 301 switch(op) { 302 case B_MODULE_INIT: 303 if(get_module(NET_CORE_MODULE_NAME, (module_info**)&core) != B_OK) 304 return B_ERROR; 305 306 if(get_module(NET_ETHERNET_MODULE_NAME, 307 (module_info**) &sEthernet) != B_OK) { 308 put_module(NET_CORE_MODULE_NAME); 309 return B_ERROR; 310 } 311 312 set_max_linkhdr(2 + PPPoE_HEADER_SIZE + ETHER_HDR_LEN); 313 // 2 bytes for PPP header 314 sDevices = new TemplateList<PPPoEDevice*>; 315 sQueries = new TemplateList<pppoe_query*>; 316 sEthernet->set_pppoe_receiver(pppoe_input); 317 318 TRACE("PPPoE: Registered PPPoE receiver.\n"); 319 return B_OK; 320 321 case B_MODULE_UNINIT: 322 delete sDevices; 323 sEthernet->unset_pppoe_receiver(); 324 TRACE("PPPoE: Unregistered PPPoE receiver.\n"); 325 put_module(NET_CORE_MODULE_NAME); 326 put_module(NET_ETHERNET_MODULE_NAME); 327 break; 328 329 default: 330 return B_ERROR; 331 } 332 333 return B_OK; 334 } 335 336 337 _EXPORT 338 module_info *modules[] = { 339 (module_info*) &pppoe_module, 340 NULL 341 }; 342