1 /* 2 * Copyright 2003-2004, Waldemar Kornewald <Waldemar.Kornewald@web.de> 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 sDevices->AddItem(device); 110 } 111 112 113 void 114 remove_device(PPPoEDevice *device) 115 { 116 TRACE("PPPoE: remove_device()\n"); 117 118 LockerHelper locker(sLock); 119 sDevices->RemoveItem(device); 120 } 121 122 123 static 124 void 125 pppoe_input(struct mbuf *packet) 126 { 127 if(!packet) 128 return; 129 130 ifnet *sourceIfnet = packet->m_pkthdr.rcvif; 131 complete_pppoe_header *header = mtod(packet, complete_pppoe_header*); 132 PPPoEDevice *device; 133 pppoe_query *query; 134 135 LockerHelper locker(sLock); 136 137 if(header->ethernetHeader.ether_type == ETHERTYPE_PPPOEDISC 138 && header->pppoeHeader.length <= PPPoE_QUERY_REPORT_SIZE) { 139 for(int32 index = 0; index < sDevices->CountItems(); index++) { 140 query = sQueries->ItemAt(index); 141 142 if(query && query->ethernetIfnet == sourceIfnet) { 143 if(header->pppoeHeader.code == PADO) { 144 DiscoveryPacket discovery(packet, ETHER_HDR_LEN); 145 if(discovery.InitCheck() != B_OK) { 146 ERROR("PPPoE: received corrupted discovery packet!\n"); 147 m_freem(packet); 148 return; 149 } 150 151 pppoe_tag *hostTag = discovery.TagWithType(HOST_UNIQ); 152 if(hostTag && hostTag->length == 4 153 && *((uint32*)hostTag->data) == query->hostUniq) { 154 SendQueryPacket(query, discovery); 155 m_freem(packet); 156 return; 157 } 158 } 159 } 160 } 161 } 162 163 for(int32 index = 0; index < sDevices->CountItems(); index++) { 164 device = sDevices->ItemAt(index); 165 166 if(device && device->EthernetIfnet() == sourceIfnet) { 167 if(header->ethernetHeader.ether_type == ETHERTYPE_PPPOE 168 && header->pppoeHeader.sessionID == device->SessionID()) { 169 TRACE("PPPoE: session packet\n"); 170 device->Receive(packet); 171 return; 172 } else if(header->ethernetHeader.ether_type == ETHERTYPE_PPPOEDISC 173 && header->pppoeHeader.code != PADI 174 && header->pppoeHeader.code != PADR 175 && !device->IsDown()) { 176 TRACE("PPPoE: discovery packet\n"); 177 DiscoveryPacket discovery(packet, ETHER_HDR_LEN); 178 if(discovery.InitCheck() != B_OK) { 179 ERROR("PPPoE: received corrupted discovery packet!\n"); 180 m_freem(packet); 181 return; 182 } 183 184 pppoe_tag *tag = discovery.TagWithType(HOST_UNIQ); 185 if(header->pppoeHeader.code == PADT || (tag && tag->length == 4 186 && *((uint32*)tag->data) == device->HostUniq())) { 187 device->Receive(packet); 188 return; 189 } 190 } 191 } 192 } 193 194 ERROR("PPPoE: No device found for packet from: %s\n", sourceIfnet->if_name); 195 m_freem(packet); 196 } 197 198 199 static 200 bool 201 add_to(KPPPInterface& mainInterface, KPPPInterface *subInterface, 202 driver_parameter *settings, ppp_module_key_type type) 203 { 204 if(mainInterface.Mode() != PPP_CLIENT_MODE || type != PPP_DEVICE_KEY_TYPE) 205 return B_ERROR; 206 207 PPPoEDevice *device; 208 bool success; 209 if(subInterface) { 210 device = new PPPoEDevice(*subInterface, settings); 211 success = subInterface->SetDevice(device); 212 } else { 213 device = new PPPoEDevice(mainInterface, settings); 214 success = mainInterface.SetDevice(device); 215 } 216 217 TRACE("PPPoE: add_to(): %s\n", 218 success && device && device->InitCheck() == B_OK ? "OK" : "ERROR"); 219 220 return success && device && device->InitCheck() == B_OK; 221 } 222 223 224 static 225 status_t 226 control(uint32 op, void *data, size_t length) 227 { 228 switch(op) { 229 case PPPoE_GET_INTERFACES: { 230 int32 position = 0, count = 0; 231 char *names = (char*) data; 232 233 ifnet *current = get_interfaces(); 234 for(; current; current = current->if_next) { 235 if(current->if_type == IFT_ETHER && current->if_name) { 236 if(position + strlen(current->if_name) + 1 > length) 237 return B_NO_MEMORY; 238 239 strcpy(names + position, current->if_name); 240 position += strlen(current->if_name); 241 names[position++] = 0; 242 ++count; 243 } 244 } 245 246 return count; 247 } 248 249 case PPPoE_QUERY_SERVICES: { 250 // XXX: as all modules are loaded on-demand we must wait for the results 251 252 if(!data || length != sizeof(pppoe_query_request)) 253 return B_ERROR; 254 255 pppoe_query_request *request = (pppoe_query_request*) data; 256 257 pppoe_query query; 258 query.ethernetIfnet = FindPPPoEInterface(request->interfaceName); 259 if(!query.ethernetIfnet) 260 return B_BAD_VALUE; 261 262 query.hostUniq = NewHostUniq(); 263 query.receiver = request->receiver; 264 265 sLock.Lock(); 266 sQueries->AddItem(&query); 267 sLock.Unlock(); 268 269 snooze(2000000); 270 // wait two seconds for results 271 272 sLock.Lock(); 273 sQueries->RemoveItem(&query); 274 sLock.Unlock(); 275 } break; 276 277 default: 278 return B_ERROR; 279 } 280 281 return B_OK; 282 } 283 284 285 static ppp_module_info pppoe_module = { 286 { 287 PPPoE_MODULE_NAME, 288 0, 289 std_ops 290 }, 291 control, 292 add_to 293 }; 294 295 296 _EXPORT 297 status_t 298 std_ops(int32 op, ...) 299 { 300 switch(op) { 301 case B_MODULE_INIT: 302 if(get_module(NET_CORE_MODULE_NAME, (module_info**)&core) != B_OK) 303 return B_ERROR; 304 305 if(get_module(NET_ETHERNET_MODULE_NAME, 306 (module_info**) &sEthernet) != B_OK) { 307 put_module(NET_CORE_MODULE_NAME); 308 return B_ERROR; 309 } 310 311 set_max_linkhdr(2 + PPPoE_HEADER_SIZE + ETHER_HDR_LEN); 312 // 2 bytes for PPP header 313 sDevices = new TemplateList<PPPoEDevice*>; 314 sQueries = new TemplateList<pppoe_query*>; 315 sEthernet->set_pppoe_receiver(pppoe_input); 316 317 TRACE("PPPoE: Registered PPPoE receiver.\n"); 318 return B_OK; 319 320 case B_MODULE_UNINIT: 321 delete sDevices; 322 sEthernet->unset_pppoe_receiver(); 323 TRACE("PPPoE: Unregistered PPPoE receiver.\n"); 324 put_module(NET_CORE_MODULE_NAME); 325 put_module(NET_ETHERNET_MODULE_NAME); 326 break; 327 328 default: 329 return B_ERROR; 330 } 331 332 return B_OK; 333 } 334 335 336 _EXPORT 337 module_info *modules[] = { 338 (module_info*) &pppoe_module, 339 NULL 340 }; 341