blob: b59cd3c4767295a84a9d2420604614d0ec84e509
1 | /*********************************************************************** |
2 | * |
3 | * pppoe-server.c |
4 | * |
5 | * Implementation of a user-space PPPoE server |
6 | * |
7 | * Copyright (C) 2000 Roaring Penguin Software Inc. |
8 | * |
9 | * This program may be distributed according to the terms of the GNU |
10 | * General Public License, version 2 or (at your option) any later version. |
11 | * |
12 | * $Id$ |
13 | * |
14 | * LIC: GPL |
15 | * |
16 | ***********************************************************************/ |
17 | |
18 | static char const RCSID[] = |
19 | "$Id$"; |
20 | |
21 | #include "config.h" |
22 | |
23 | #if defined(HAVE_NETPACKET_PACKET_H) || defined(HAVE_LINUX_IF_PACKET_H) |
24 | #define _POSIX_SOURCE 1 /* For sigaction defines */ |
25 | #endif |
26 | |
27 | #define _BSD_SOURCE 1 /* for gethostname */ |
28 | |
29 | #include "pppoe-server.h" |
30 | #include "md5.h" |
31 | |
32 | #ifdef HAVE_SYSLOG_H |
33 | #include <syslog.h> |
34 | #endif |
35 | |
36 | #include <errno.h> |
37 | #include <string.h> |
38 | #include <stdlib.h> |
39 | #include <fcntl.h> |
40 | |
41 | #ifdef HAVE_UNISTD_H |
42 | #include <unistd.h> |
43 | #endif |
44 | |
45 | #ifdef HAVE_GETOPT_H |
46 | #include <getopt.h> |
47 | #endif |
48 | |
49 | #ifdef HAVE_SYS_WAIT_H |
50 | #include <sys/wait.h> |
51 | #endif |
52 | |
53 | #ifdef HAVE_SYS_TIME_H |
54 | #include <sys/time.h> |
55 | #endif |
56 | |
57 | #include <time.h> |
58 | |
59 | #include <signal.h> |
60 | |
61 | #ifdef HAVE_LICENSE |
62 | #include "license.h" |
63 | #include "licensed-only/servfuncs.h" |
64 | static struct License const *ServerLicense; |
65 | static struct License const *ClusterLicense; |
66 | #else |
67 | #define control_session_started(x) (void) 0 |
68 | #define control_session_terminated(x) (void) 0 |
69 | #define control_exit() (void) 0 |
70 | #define realpeerip peerip |
71 | #endif |
72 | |
73 | #ifdef HAVE_L2TP |
74 | extern PppoeSessionFunctionTable L2TPSessionFunctionTable; |
75 | extern void pppoe_to_l2tp_add_interface(EventSelector *es, |
76 | Interface *interface); |
77 | #endif |
78 | |
79 | static void InterfaceHandler(EventSelector *es, |
80 | int fd, unsigned int flags, void *data); |
81 | static void startPPPD(ClientSession *sess); |
82 | static void sendErrorPADS(int sock, unsigned char *source, unsigned char *dest, |
83 | int errorTag, char *errorMsg); |
84 | |
85 | #define CHECK_ROOM(cursor, start, len) \ |
86 | do {\ |
87 | if (((cursor)-(start))+(len) > MAX_PPPOE_PAYLOAD) { \ |
88 | syslog(LOG_ERR, "Would create too-long packet"); \ |
89 | return; \ |
90 | } \ |
91 | } while(0) |
92 | |
93 | static void PppoeStopSession(ClientSession *ses, char const *reason); |
94 | static int PppoeSessionIsActive(ClientSession *ses); |
95 | |
96 | /* Service-Names we advertise */ |
97 | #define MAX_SERVICE_NAMES 64 |
98 | static int NumServiceNames = 0; |
99 | static char const *ServiceNames[MAX_SERVICE_NAMES]; |
100 | |
101 | PppoeSessionFunctionTable DefaultSessionFunctionTable = { |
102 | PppoeStopSession, |
103 | PppoeSessionIsActive, |
104 | NULL |
105 | }; |
106 | |
107 | /* An array of client sessions */ |
108 | ClientSession *Sessions = NULL; |
109 | ClientSession *FreeSessions = NULL; |
110 | ClientSession *LastFreeSession = NULL; |
111 | ClientSession *BusySessions = NULL; |
112 | |
113 | /* Interfaces we're listening on */ |
114 | Interface interfaces[MAX_INTERFACES]; |
115 | int NumInterfaces = 0; |
116 | |
117 | /* The number of session slots */ |
118 | size_t NumSessionSlots; |
119 | |
120 | /* Maximum number of sessions per MAC address */ |
121 | int MaxSessionsPerMac; |
122 | |
123 | /* Number of active sessions */ |
124 | size_t NumActiveSessions = 0; |
125 | |
126 | /* Offset of first session */ |
127 | size_t SessOffset = 0; |
128 | |
129 | /* Event Selector */ |
130 | EventSelector *event_selector; |
131 | |
132 | /* Use Linux kernel-mode PPPoE? */ |
133 | static int UseLinuxKernelModePPPoE = 0; |
134 | |
135 | /* File with PPPD options */ |
136 | static char *pppoptfile = NULL; |
137 | |
138 | static int Debug = 0; |
139 | static int CheckPoolSyntax = 0; |
140 | |
141 | /* Synchronous mode */ |
142 | static int Synchronous = 0; |
143 | |
144 | /* Random seed for cookie generation */ |
145 | #define SEED_LEN 16 |
146 | #define MD5_LEN 16 |
147 | #define COOKIE_LEN (MD5_LEN + sizeof(pid_t)) /* Cookie is 16-byte MD5 + PID of server */ |
148 | |
149 | static unsigned char CookieSeed[SEED_LEN]; |
150 | |
151 | #define MAXLINE 512 |
152 | |
153 | /* Default interface if no -I option given */ |
154 | #define DEFAULT_IF "eth0" |
155 | |
156 | /* Access concentrator name */ |
157 | char *ACName = NULL; |
158 | |
159 | /* Options to pass to pppoe process */ |
160 | char PppoeOptions[SMALLBUF] = ""; |
161 | |
162 | /* Our local IP address */ |
163 | unsigned char LocalIP[IPV4ALEN] = {10, 0, 0, 1}; /* Counter optionally STARTS here */ |
164 | unsigned char RemoteIP[IPV4ALEN] = {10, 67, 15, 1}; /* Counter STARTS here */ |
165 | |
166 | /* Do we increment local IP for each connection? */ |
167 | int IncrLocalIP = 0; |
168 | |
169 | /* Do we randomize session numbers? */ |
170 | int RandomizeSessionNumbers = 0; |
171 | |
172 | /* Do we pass the "unit" option to pppd? (2.4 or greater) */ |
173 | int PassUnitOptionToPPPD = 0; |
174 | |
175 | static PPPoETag hostUniq; |
176 | static PPPoETag relayId; |
177 | static PPPoETag receivedCookie; |
178 | static PPPoETag requestedService; |
179 | |
180 | #define HOSTNAMELEN 256 |
181 | |
182 | static int |
183 | count_sessions_from_mac(unsigned char *eth) |
184 | { |
185 | int n=0; |
186 | ClientSession *s = BusySessions; |
187 | while(s) { |
188 | if (!memcmp(eth, s->eth, ETH_ALEN)) n++; |
189 | s = s->next; |
190 | } |
191 | return n; |
192 | } |
193 | |
194 | /********************************************************************** |
195 | *%FUNCTION: childHandler |
196 | *%ARGUMENTS: |
197 | * pid -- pid of child |
198 | * status -- exit status |
199 | * ses -- which session terminated |
200 | *%RETURNS: |
201 | * Nothing |
202 | *%DESCRIPTION: |
203 | * Called synchronously when a child dies. Remove from busy list. |
204 | ***********************************************************************/ |
205 | static void |
206 | childHandler(pid_t pid, int status, void *s) |
207 | { |
208 | ClientSession *session = s; |
209 | |
210 | /* Temporary structure for sending PADT's. */ |
211 | PPPoEConnection conn; |
212 | |
213 | #ifdef HAVE_L2TP |
214 | /* We're acting as LAC, so when child exits, become a PPPoE <-> L2TP |
215 | relay */ |
216 | if (session->flags & FLAG_ACT_AS_LAC) { |
217 | syslog(LOG_INFO, "Session %u for client " |
218 | "%02x:%02x:%02x:%02x:%02x:%02x handed off to LNS %s", |
219 | (unsigned int) ntohs(session->sess), |
220 | session->eth[0], session->eth[1], session->eth[2], |
221 | session->eth[3], session->eth[4], session->eth[5], |
222 | inet_ntoa(session->tunnel_endpoint.sin_addr)); |
223 | session->pid = 0; |
224 | session->funcs = &L2TPSessionFunctionTable; |
225 | return; |
226 | } |
227 | #endif |
228 | |
229 | memset(&conn, 0, sizeof(conn)); |
230 | conn.useHostUniq = 0; |
231 | |
232 | syslog(LOG_INFO, |
233 | "Session %u closed for client " |
234 | "%02x:%02x:%02x:%02x:%02x:%02x (%d.%d.%d.%d) on %s", |
235 | (unsigned int) ntohs(session->sess), |
236 | session->eth[0], session->eth[1], session->eth[2], |
237 | session->eth[3], session->eth[4], session->eth[5], |
238 | (int) session->realpeerip[0], (int) session->realpeerip[1], |
239 | (int) session->realpeerip[2], (int) session->realpeerip[3], |
240 | session->ethif->name); |
241 | memcpy(conn.myEth, session->ethif->mac, ETH_ALEN); |
242 | conn.discoverySocket = session->ethif->sock; |
243 | conn.session = session->sess; |
244 | memcpy(conn.peerEth, session->eth, ETH_ALEN); |
245 | if (!(session->flags & FLAG_SENT_PADT)) { |
246 | if (session->flags & FLAG_RECVD_PADT) { |
247 | sendPADT(&conn, "RP-PPPoE: Received PADT from peer"); |
248 | } else { |
249 | sendPADT(&conn, "RP-PPPoE: Child pppd process terminated"); |
250 | } |
251 | session->flags |= FLAG_SENT_PADT; |
252 | } |
253 | |
254 | session->serviceName = ""; |
255 | control_session_terminated(session); |
256 | if (pppoe_free_session(session) < 0) { |
257 | return; |
258 | } |
259 | |
260 | } |
261 | |
262 | /********************************************************************** |
263 | *%FUNCTION: incrementIPAddress (static) |
264 | *%ARGUMENTS: |
265 | * addr -- a 4-byte array representing IP address |
266 | *%RETURNS: |
267 | * Nothing |
268 | *%DESCRIPTION: |
269 | * Increments addr in-place |
270 | ***********************************************************************/ |
271 | static void |
272 | incrementIPAddress(unsigned char ip[IPV4ALEN]) |
273 | { |
274 | ip[3]++; |
275 | if (!ip[3]) { |
276 | ip[2]++; |
277 | if (!ip[2]) { |
278 | ip[1]++; |
279 | if (!ip[1]) { |
280 | ip[0]++; |
281 | } |
282 | } |
283 | } |
284 | } |
285 | |
286 | /********************************************************************** |
287 | *%FUNCTION: killAllSessions |
288 | *%ARGUMENTS: |
289 | * None |
290 | *%RETURNS: |
291 | * Nothing |
292 | *%DESCRIPTION: |
293 | * Kills all pppd processes (and hence all PPPoE sessions) |
294 | ***********************************************************************/ |
295 | void |
296 | killAllSessions(void) |
297 | { |
298 | ClientSession *sess = BusySessions; |
299 | while(sess) { |
300 | sess->funcs->stop(sess, "Shutting Down"); |
301 | sess = sess->next; |
302 | } |
303 | #ifdef HAVE_L2TP |
304 | pppoe_close_l2tp_tunnels(); |
305 | #endif |
306 | } |
307 | |
308 | /********************************************************************** |
309 | *%FUNCTION: parseAddressPool |
310 | *%ARGUMENTS: |
311 | * fname -- name of file containing IP address pool. |
312 | * install -- if true, install IP addresses in sessions. |
313 | *%RETURNS: |
314 | * Number of valid IP addresses found. |
315 | *%DESCRIPTION: |
316 | * Reads a list of IP addresses from a file. |
317 | ***********************************************************************/ |
318 | static int |
319 | parseAddressPool(char const *fname, int install) |
320 | { |
321 | FILE *fp = fopen(fname, "r"); |
322 | int numAddrs = 0; |
323 | unsigned int a, b, c, d; |
324 | unsigned int e, f, g, h; |
325 | char line[MAXLINE]; |
326 | |
327 | if (!fp) { |
328 | sysErr("Cannot open address pool file"); |
329 | exit(1); |
330 | } |
331 | |
332 | while (!feof(fp)) { |
333 | if (!fgets(line, MAXLINE, fp)) { |
334 | break; |
335 | } |
336 | if ((sscanf(line, "%u.%u.%u.%u:%u.%u.%u.%u", |
337 | &a, &b, &c, &d, &e, &f, &g, &h) == 8) && |
338 | a < 256 && b < 256 && c < 256 && d < 256 && |
339 | e < 256 && f < 256 && g < 256 && h < 256) { |
340 | |
341 | /* Both specified (local:remote) */ |
342 | if (install) { |
343 | Sessions[numAddrs].myip[0] = (unsigned char) a; |
344 | Sessions[numAddrs].myip[1] = (unsigned char) b; |
345 | Sessions[numAddrs].myip[2] = (unsigned char) c; |
346 | Sessions[numAddrs].myip[3] = (unsigned char) d; |
347 | Sessions[numAddrs].peerip[0] = (unsigned char) e; |
348 | Sessions[numAddrs].peerip[1] = (unsigned char) f; |
349 | Sessions[numAddrs].peerip[2] = (unsigned char) g; |
350 | Sessions[numAddrs].peerip[3] = (unsigned char) h; |
351 | #ifdef HAVE_LICENSE |
352 | memcpy(Sessions[numAddrs].realpeerip, |
353 | Sessions[numAddrs].peerip, IPV4ALEN); |
354 | #endif |
355 | } |
356 | numAddrs++; |
357 | } else if ((sscanf(line, "%u.%u.%u.%u-%u", &a, &b, &c, &d, &e) == 5) && |
358 | a < 256 && b < 256 && c < 256 && d < 256 && e < 256) { |
359 | /* Remote specied as a.b.c.d-e. Example: 1.2.3.4-8 yields: |
360 | 1.2.3.4, 1.2.3.5, 1.2.3.6, 1.2.3.7, 1.2.3.8 */ |
361 | /* Swap d and e so that e >= d */ |
362 | if (e < d) { |
363 | f = d; |
364 | d = e; |
365 | e = f; |
366 | } |
367 | if (install) { |
368 | while (d <= e) { |
369 | Sessions[numAddrs].peerip[0] = (unsigned char) a; |
370 | Sessions[numAddrs].peerip[1] = (unsigned char) b; |
371 | Sessions[numAddrs].peerip[2] = (unsigned char) c; |
372 | Sessions[numAddrs].peerip[3] = (unsigned char) d; |
373 | #ifdef HAVE_LICENSE |
374 | memcpy(Sessions[numAddrs].realpeerip, |
375 | Sessions[numAddrs].peerip, IPV4ALEN); |
376 | #endif |
377 | d++; |
378 | numAddrs++; |
379 | } |
380 | } else { |
381 | numAddrs += (e-d) + 1; |
382 | } |
383 | } else if ((sscanf(line, "%u.%u.%u.%u", &a, &b, &c, &d) == 4) && |
384 | a < 256 && b < 256 && c < 256 && d < 256) { |
385 | /* Only remote specified */ |
386 | if (install) { |
387 | Sessions[numAddrs].peerip[0] = (unsigned char) a; |
388 | Sessions[numAddrs].peerip[1] = (unsigned char) b; |
389 | Sessions[numAddrs].peerip[2] = (unsigned char) c; |
390 | Sessions[numAddrs].peerip[3] = (unsigned char) d; |
391 | #ifdef HAVE_LICENSE |
392 | memcpy(Sessions[numAddrs].realpeerip, |
393 | Sessions[numAddrs].peerip, IPV4ALEN); |
394 | #endif |
395 | } |
396 | numAddrs++; |
397 | } |
398 | } |
399 | fclose(fp); |
400 | if (!numAddrs) { |
401 | rp_fatal("No valid ip addresses found in pool file"); |
402 | } |
403 | return numAddrs; |
404 | } |
405 | |
406 | /********************************************************************** |
407 | *%FUNCTION: parsePADITags |
408 | *%ARGUMENTS: |
409 | * type -- tag type |
410 | * len -- tag length |
411 | * data -- tag data |
412 | * extra -- extra user data. |
413 | *%RETURNS: |
414 | * Nothing |
415 | *%DESCRIPTION: |
416 | * Picks interesting tags out of a PADI packet |
417 | ***********************************************************************/ |
418 | void |
419 | parsePADITags(UINT16_t type, UINT16_t len, unsigned char *data, |
420 | void *extra) |
421 | { |
422 | switch(type) { |
423 | case TAG_SERVICE_NAME: |
424 | /* Copy requested service name */ |
425 | requestedService.type = htons(type); |
426 | requestedService.length = htons(len); |
427 | memcpy(requestedService.payload, data, len); |
428 | break; |
429 | case TAG_RELAY_SESSION_ID: |
430 | relayId.type = htons(type); |
431 | relayId.length = htons(len); |
432 | memcpy(relayId.payload, data, len); |
433 | break; |
434 | case TAG_HOST_UNIQ: |
435 | hostUniq.type = htons(type); |
436 | hostUniq.length = htons(len); |
437 | memcpy(hostUniq.payload, data, len); |
438 | break; |
439 | } |
440 | } |
441 | |
442 | /********************************************************************** |
443 | *%FUNCTION: parsePADRTags |
444 | *%ARGUMENTS: |
445 | * type -- tag type |
446 | * len -- tag length |
447 | * data -- tag data |
448 | * extra -- extra user data. |
449 | *%RETURNS: |
450 | * Nothing |
451 | *%DESCRIPTION: |
452 | * Picks interesting tags out of a PADR packet |
453 | ***********************************************************************/ |
454 | void |
455 | parsePADRTags(UINT16_t type, UINT16_t len, unsigned char *data, |
456 | void *extra) |
457 | { |
458 | switch(type) { |
459 | case TAG_RELAY_SESSION_ID: |
460 | relayId.type = htons(type); |
461 | relayId.length = htons(len); |
462 | memcpy(relayId.payload, data, len); |
463 | break; |
464 | case TAG_HOST_UNIQ: |
465 | hostUniq.type = htons(type); |
466 | hostUniq.length = htons(len); |
467 | memcpy(hostUniq.payload, data, len); |
468 | break; |
469 | case TAG_AC_COOKIE: |
470 | receivedCookie.type = htons(type); |
471 | receivedCookie.length = htons(len); |
472 | memcpy(receivedCookie.payload, data, len); |
473 | break; |
474 | case TAG_SERVICE_NAME: |
475 | requestedService.type = htons(type); |
476 | requestedService.length = htons(len); |
477 | memcpy(requestedService.payload, data, len); |
478 | break; |
479 | } |
480 | } |
481 | |
482 | /********************************************************************** |
483 | *%FUNCTION: fatalSys |
484 | *%ARGUMENTS: |
485 | * str -- error message |
486 | *%RETURNS: |
487 | * Nothing |
488 | *%DESCRIPTION: |
489 | * Prints a message plus the errno value to stderr and syslog and exits. |
490 | ***********************************************************************/ |
491 | void |
492 | fatalSys(char const *str) |
493 | { |
494 | char buf[SMALLBUF]; |
495 | snprintf(buf, SMALLBUF, "%s: %s", str, strerror(errno)); |
496 | printErr(buf); |
497 | control_exit(); |
498 | exit(EXIT_FAILURE); |
499 | } |
500 | |
501 | /********************************************************************** |
502 | *%FUNCTION: sysErr |
503 | *%ARGUMENTS: |
504 | * str -- error message |
505 | *%RETURNS: |
506 | * Nothing |
507 | *%DESCRIPTION: |
508 | * Prints a message plus the errno value to syslog. |
509 | ***********************************************************************/ |
510 | void |
511 | sysErr(char const *str) |
512 | { |
513 | char buf[1024]; |
514 | sprintf(buf, "%.256s: %.256s", str, strerror(errno)); |
515 | printErr(buf); |
516 | } |
517 | |
518 | /********************************************************************** |
519 | *%FUNCTION: rp_fatal |
520 | *%ARGUMENTS: |
521 | * str -- error message |
522 | *%RETURNS: |
523 | * Nothing |
524 | *%DESCRIPTION: |
525 | * Prints a message to stderr and syslog and exits. |
526 | ***********************************************************************/ |
527 | void |
528 | rp_fatal(char const *str) |
529 | { |
530 | printErr(str); |
531 | control_exit(); |
532 | exit(EXIT_FAILURE); |
533 | } |
534 | |
535 | /********************************************************************** |
536 | *%FUNCTION: genCookie |
537 | *%ARGUMENTS: |
538 | * peerEthAddr -- peer Ethernet address (6 bytes) |
539 | * myEthAddr -- my Ethernet address (6 bytes) |
540 | * seed -- random cookie seed to make things tasty (16 bytes) |
541 | * cookie -- buffer which is filled with server PID and |
542 | * md5 sum of previous items |
543 | *%RETURNS: |
544 | * Nothing |
545 | *%DESCRIPTION: |
546 | * Forms the md5 sum of peer MAC address, our MAC address and seed, useful |
547 | * in a PPPoE Cookie tag. |
548 | ***********************************************************************/ |
549 | void |
550 | genCookie(unsigned char const *peerEthAddr, |
551 | unsigned char const *myEthAddr, |
552 | unsigned char const *seed, |
553 | unsigned char *cookie) |
554 | { |
555 | struct MD5Context ctx; |
556 | pid_t pid = getpid(); |
557 | |
558 | MD5Init(&ctx); |
559 | MD5Update(&ctx, peerEthAddr, ETH_ALEN); |
560 | MD5Update(&ctx, myEthAddr, ETH_ALEN); |
561 | MD5Update(&ctx, seed, SEED_LEN); |
562 | MD5Final(cookie, &ctx); |
563 | memcpy(cookie+MD5_LEN, &pid, sizeof(pid)); |
564 | } |
565 | |
566 | /********************************************************************** |
567 | *%FUNCTION: processPADI |
568 | *%ARGUMENTS: |
569 | * ethif -- Interface |
570 | * packet -- PPPoE PADI packet |
571 | * len -- length of received packet |
572 | *%RETURNS: |
573 | * Nothing |
574 | *%DESCRIPTION: |
575 | * Sends a PADO packet back to client |
576 | ***********************************************************************/ |
577 | void |
578 | processPADI(Interface *ethif, PPPoEPacket *packet, int len) |
579 | { |
580 | PPPoEPacket pado; |
581 | PPPoETag acname; |
582 | PPPoETag servname; |
583 | PPPoETag cookie; |
584 | size_t acname_len; |
585 | unsigned char *cursor = pado.payload; |
586 | UINT16_t plen; |
587 | |
588 | int sock = ethif->sock; |
589 | int i; |
590 | int ok = 0; |
591 | unsigned char *myAddr = ethif->mac; |
592 | |
593 | /* Ignore PADI's which don't come from a unicast address */ |
594 | if (NOT_UNICAST(packet->ethHdr.h_source)) { |
595 | syslog(LOG_ERR, "PADI packet from non-unicast source address"); |
596 | return; |
597 | } |
598 | |
599 | /* If number of sessions per MAC is limited, check here and don't |
600 | send PADO if already max number of sessions. */ |
601 | if (MaxSessionsPerMac) { |
602 | if (count_sessions_from_mac(packet->ethHdr.h_source) >= MaxSessionsPerMac) { |
603 | syslog(LOG_INFO, "PADI: Client %02x:%02x:%02x:%02x:%02x:%02x attempted to create more than %d session(s)", |
604 | packet->ethHdr.h_source[0], |
605 | packet->ethHdr.h_source[1], |
606 | packet->ethHdr.h_source[2], |
607 | packet->ethHdr.h_source[3], |
608 | packet->ethHdr.h_source[4], |
609 | packet->ethHdr.h_source[5], |
610 | MaxSessionsPerMac); |
611 | return; |
612 | } |
613 | } |
614 | |
615 | acname.type = htons(TAG_AC_NAME); |
616 | acname_len = strlen(ACName); |
617 | acname.length = htons(acname_len); |
618 | memcpy(acname.payload, ACName, acname_len); |
619 | |
620 | relayId.type = 0; |
621 | hostUniq.type = 0; |
622 | requestedService.type = 0; |
623 | parsePacket(packet, parsePADITags, NULL); |
624 | |
625 | /* If PADI specified non-default service name, and we do not offer |
626 | that service, DO NOT send PADO */ |
627 | if (requestedService.type) { |
628 | int slen = ntohs(requestedService.length); |
629 | if (slen) { |
630 | for (i=0; i<NumServiceNames; i++) { |
631 | if (slen == strlen(ServiceNames[i]) && |
632 | !memcmp(ServiceNames[i], &requestedService.payload, slen)) { |
633 | ok = 1; |
634 | break; |
635 | } |
636 | } |
637 | } else { |
638 | ok = 1; /* Default service requested */ |
639 | } |
640 | } else { |
641 | ok = 1; /* No Service-Name tag in PADI */ |
642 | } |
643 | |
644 | if (!ok) { |
645 | /* PADI asked for unsupported service */ |
646 | return; |
647 | } |
648 | |
649 | /* Generate a cookie */ |
650 | cookie.type = htons(TAG_AC_COOKIE); |
651 | cookie.length = htons(COOKIE_LEN); |
652 | genCookie(packet->ethHdr.h_source, myAddr, CookieSeed, cookie.payload); |
653 | |
654 | /* Construct a PADO packet */ |
655 | memcpy(pado.ethHdr.h_dest, packet->ethHdr.h_source, ETH_ALEN); |
656 | memcpy(pado.ethHdr.h_source, myAddr, ETH_ALEN); |
657 | pado.ethHdr.h_proto = htons(Eth_PPPOE_Discovery); |
658 | pado.ver = 1; |
659 | pado.type = 1; |
660 | pado.code = CODE_PADO; |
661 | pado.session = 0; |
662 | plen = TAG_HDR_SIZE + acname_len; |
663 | |
664 | CHECK_ROOM(cursor, pado.payload, acname_len+TAG_HDR_SIZE); |
665 | memcpy(cursor, &acname, acname_len + TAG_HDR_SIZE); |
666 | cursor += acname_len + TAG_HDR_SIZE; |
667 | |
668 | /* If no service-names specified on command-line, just send default |
669 | zero-length name. Otherwise, add all service-name tags */ |
670 | servname.type = htons(TAG_SERVICE_NAME); |
671 | if (!NumServiceNames) { |
672 | servname.length = 0; |
673 | CHECK_ROOM(cursor, pado.payload, TAG_HDR_SIZE); |
674 | memcpy(cursor, &servname, TAG_HDR_SIZE); |
675 | cursor += TAG_HDR_SIZE; |
676 | plen += TAG_HDR_SIZE; |
677 | } else { |
678 | for (i=0; i<NumServiceNames; i++) { |
679 | int slen = strlen(ServiceNames[i]); |
680 | servname.length = htons(slen); |
681 | CHECK_ROOM(cursor, pado.payload, TAG_HDR_SIZE+slen); |
682 | memcpy(cursor, &servname, TAG_HDR_SIZE); |
683 | memcpy(cursor+TAG_HDR_SIZE, ServiceNames[i], slen); |
684 | cursor += TAG_HDR_SIZE+slen; |
685 | plen += TAG_HDR_SIZE+slen; |
686 | } |
687 | } |
688 | |
689 | CHECK_ROOM(cursor, pado.payload, TAG_HDR_SIZE + COOKIE_LEN); |
690 | memcpy(cursor, &cookie, TAG_HDR_SIZE + COOKIE_LEN); |
691 | cursor += TAG_HDR_SIZE + COOKIE_LEN; |
692 | plen += TAG_HDR_SIZE + COOKIE_LEN; |
693 | |
694 | if (relayId.type) { |
695 | CHECK_ROOM(cursor, pado.payload, ntohs(relayId.length) + TAG_HDR_SIZE); |
696 | memcpy(cursor, &relayId, ntohs(relayId.length) + TAG_HDR_SIZE); |
697 | cursor += ntohs(relayId.length) + TAG_HDR_SIZE; |
698 | plen += ntohs(relayId.length) + TAG_HDR_SIZE; |
699 | } |
700 | if (hostUniq.type) { |
701 | CHECK_ROOM(cursor, pado.payload, ntohs(hostUniq.length)+TAG_HDR_SIZE); |
702 | memcpy(cursor, &hostUniq, ntohs(hostUniq.length) + TAG_HDR_SIZE); |
703 | cursor += ntohs(hostUniq.length) + TAG_HDR_SIZE; |
704 | plen += ntohs(hostUniq.length) + TAG_HDR_SIZE; |
705 | } |
706 | pado.length = htons(plen); |
707 | sendPacket(NULL, sock, &pado, (int) (plen + HDR_SIZE)); |
708 | } |
709 | |
710 | /********************************************************************** |
711 | *%FUNCTION: processPADT |
712 | *%ARGUMENTS: |
713 | * ethif -- interface |
714 | * packet -- PPPoE PADT packet |
715 | * len -- length of received packet |
716 | *%RETURNS: |
717 | * Nothing |
718 | *%DESCRIPTION: |
719 | * Kills session whose session-ID is in PADT packet. |
720 | ***********************************************************************/ |
721 | void |
722 | processPADT(Interface *ethif, PPPoEPacket *packet, int len) |
723 | { |
724 | size_t i; |
725 | |
726 | unsigned char *myAddr = ethif->mac; |
727 | |
728 | /* Ignore PADT's not directed at us */ |
729 | if (memcmp(packet->ethHdr.h_dest, myAddr, ETH_ALEN)) return; |
730 | |
731 | /* Get session's index */ |
732 | i = ntohs(packet->session) - 1 - SessOffset; |
733 | if (i >= NumSessionSlots) return; |
734 | if (Sessions[i].sess != packet->session) { |
735 | syslog(LOG_ERR, "Session index %u doesn't match session number %u", |
736 | (unsigned int) i, (unsigned int) ntohs(packet->session)); |
737 | return; |
738 | } |
739 | |
740 | |
741 | /* If source MAC does not match, do not kill session */ |
742 | if (memcmp(packet->ethHdr.h_source, Sessions[i].eth, ETH_ALEN)) { |
743 | syslog(LOG_WARNING, "PADT for session %u received from " |
744 | "%02X:%02X:%02X:%02X:%02X:%02X; should be from " |
745 | "%02X:%02X:%02X:%02X:%02X:%02X", |
746 | (unsigned int) ntohs(packet->session), |
747 | packet->ethHdr.h_source[0], |
748 | packet->ethHdr.h_source[1], |
749 | packet->ethHdr.h_source[2], |
750 | packet->ethHdr.h_source[3], |
751 | packet->ethHdr.h_source[4], |
752 | packet->ethHdr.h_source[5], |
753 | Sessions[i].eth[0], |
754 | Sessions[i].eth[1], |
755 | Sessions[i].eth[2], |
756 | Sessions[i].eth[3], |
757 | Sessions[i].eth[4], |
758 | Sessions[i].eth[5]); |
759 | return; |
760 | } |
761 | Sessions[i].flags |= FLAG_RECVD_PADT; |
762 | parsePacket(packet, parseLogErrs, NULL); |
763 | Sessions[i].funcs->stop(&Sessions[i], "Received PADT"); |
764 | } |
765 | |
766 | /********************************************************************** |
767 | *%FUNCTION: processPADR |
768 | *%ARGUMENTS: |
769 | * ethif -- Ethernet interface |
770 | * packet -- PPPoE PADR packet |
771 | * len -- length of received packet |
772 | *%RETURNS: |
773 | * Nothing |
774 | *%DESCRIPTION: |
775 | * Sends a PADS packet back to client and starts a PPP session if PADR |
776 | * packet is OK. |
777 | ***********************************************************************/ |
778 | void |
779 | processPADR(Interface *ethif, PPPoEPacket *packet, int len) |
780 | { |
781 | unsigned char cookieBuffer[COOKIE_LEN]; |
782 | ClientSession *cliSession; |
783 | pid_t child; |
784 | PPPoEPacket pads; |
785 | unsigned char *cursor = pads.payload; |
786 | UINT16_t plen; |
787 | int i; |
788 | int sock = ethif->sock; |
789 | unsigned char *myAddr = ethif->mac; |
790 | int slen = 0; |
791 | char const *serviceName = NULL; |
792 | |
793 | #ifdef HAVE_LICENSE |
794 | int freemem; |
795 | #endif |
796 | |
797 | /* Initialize some globals */ |
798 | relayId.type = 0; |
799 | hostUniq.type = 0; |
800 | receivedCookie.type = 0; |
801 | requestedService.type = 0; |
802 | |
803 | /* Ignore PADR's not directed at us */ |
804 | if (memcmp(packet->ethHdr.h_dest, myAddr, ETH_ALEN)) return; |
805 | |
806 | /* Ignore PADR's from non-unicast addresses */ |
807 | if (NOT_UNICAST(packet->ethHdr.h_source)) { |
808 | syslog(LOG_ERR, "PADR packet from non-unicast source address"); |
809 | return; |
810 | } |
811 | |
812 | /* If number of sessions per MAC is limited, check here and don't |
813 | send PADS if already max number of sessions. */ |
814 | if (MaxSessionsPerMac) { |
815 | if (count_sessions_from_mac(packet->ethHdr.h_source) >= MaxSessionsPerMac) { |
816 | syslog(LOG_INFO, "PADR: Client %02x:%02x:%02x:%02x:%02x:%02x attempted to create more than %d session(s)", |
817 | packet->ethHdr.h_source[0], |
818 | packet->ethHdr.h_source[1], |
819 | packet->ethHdr.h_source[2], |
820 | packet->ethHdr.h_source[3], |
821 | packet->ethHdr.h_source[4], |
822 | packet->ethHdr.h_source[5], |
823 | MaxSessionsPerMac); |
824 | return; |
825 | } |
826 | } |
827 | parsePacket(packet, parsePADRTags, NULL); |
828 | |
829 | /* Check that everything's cool */ |
830 | if (!receivedCookie.type) { |
831 | /* Drop it -- do not send error PADS */ |
832 | return; |
833 | } |
834 | |
835 | /* Is cookie kosher? */ |
836 | if (receivedCookie.length != htons(COOKIE_LEN)) { |
837 | /* Drop it -- do not send error PADS */ |
838 | return; |
839 | } |
840 | |
841 | genCookie(packet->ethHdr.h_source, myAddr, CookieSeed, cookieBuffer); |
842 | if (memcmp(receivedCookie.payload, cookieBuffer, COOKIE_LEN)) { |
843 | /* Drop it -- do not send error PADS */ |
844 | return; |
845 | } |
846 | |
847 | /* Check service name */ |
848 | if (!requestedService.type) { |
849 | syslog(LOG_ERR, "Received PADR packet with no SERVICE_NAME tag"); |
850 | sendErrorPADS(sock, myAddr, packet->ethHdr.h_source, |
851 | TAG_SERVICE_NAME_ERROR, "RP-PPPoE: Server: No service name tag"); |
852 | return; |
853 | } |
854 | |
855 | slen = ntohs(requestedService.length); |
856 | if (slen) { |
857 | /* Check supported services */ |
858 | for(i=0; i<NumServiceNames; i++) { |
859 | if (slen == strlen(ServiceNames[i]) && |
860 | !memcmp(ServiceNames[i], &requestedService.payload, slen)) { |
861 | serviceName = ServiceNames[i]; |
862 | break; |
863 | } |
864 | } |
865 | |
866 | if (!serviceName) { |
867 | syslog(LOG_ERR, "Received PADR packet asking for unsupported service %.*s", (int) ntohs(requestedService.length), requestedService.payload); |
868 | sendErrorPADS(sock, myAddr, packet->ethHdr.h_source, |
869 | TAG_SERVICE_NAME_ERROR, "RP-PPPoE: Server: Invalid service name tag"); |
870 | return; |
871 | } |
872 | } else { |
873 | serviceName = ""; |
874 | } |
875 | |
876 | |
877 | #ifdef HAVE_LICENSE |
878 | /* Are we licensed for this many sessions? */ |
879 | if (License_NumLicenses("PPPOE-SESSIONS") <= NumActiveSessions) { |
880 | syslog(LOG_ERR, "Insufficient session licenses (%02x:%02x:%02x:%02x:%02x:%02x)", |
881 | (unsigned int) packet->ethHdr.h_source[0], |
882 | (unsigned int) packet->ethHdr.h_source[1], |
883 | (unsigned int) packet->ethHdr.h_source[2], |
884 | (unsigned int) packet->ethHdr.h_source[3], |
885 | (unsigned int) packet->ethHdr.h_source[4], |
886 | (unsigned int) packet->ethHdr.h_source[5]); |
887 | sendErrorPADS(sock, myAddr, packet->ethHdr.h_source, |
888 | TAG_AC_SYSTEM_ERROR, "RP-PPPoE: Server: No session licenses available"); |
889 | return; |
890 | } |
891 | #endif |
892 | /* Enough free memory? */ |
893 | #ifdef HAVE_LICENSE |
894 | freemem = getFreeMem(); |
895 | if (freemem < MIN_FREE_MEMORY) { |
896 | syslog(LOG_WARNING, |
897 | "Insufficient free memory to create session: Want %d, have %d", |
898 | MIN_FREE_MEMORY, freemem); |
899 | sendErrorPADS(sock, myAddr, packet->ethHdr.h_source, |
900 | TAG_AC_SYSTEM_ERROR, "RP-PPPoE: Insufficient free RAM"); |
901 | return; |
902 | } |
903 | #endif |
904 | /* Looks cool... find a slot for the session */ |
905 | cliSession = pppoe_alloc_session(); |
906 | if (!cliSession) { |
907 | syslog(LOG_ERR, "No client slots available (%02x:%02x:%02x:%02x:%02x:%02x)", |
908 | (unsigned int) packet->ethHdr.h_source[0], |
909 | (unsigned int) packet->ethHdr.h_source[1], |
910 | (unsigned int) packet->ethHdr.h_source[2], |
911 | (unsigned int) packet->ethHdr.h_source[3], |
912 | (unsigned int) packet->ethHdr.h_source[4], |
913 | (unsigned int) packet->ethHdr.h_source[5]); |
914 | sendErrorPADS(sock, myAddr, packet->ethHdr.h_source, |
915 | TAG_AC_SYSTEM_ERROR, "RP-PPPoE: Server: No client slots available"); |
916 | return; |
917 | } |
918 | |
919 | /* Set up client session peer Ethernet address */ |
920 | memcpy(cliSession->eth, packet->ethHdr.h_source, ETH_ALEN); |
921 | cliSession->ethif = ethif; |
922 | cliSession->flags = 0; |
923 | cliSession->funcs = &DefaultSessionFunctionTable; |
924 | cliSession->startTime = time(NULL); |
925 | cliSession->serviceName = serviceName; |
926 | |
927 | /* Create child process, send PADS packet back */ |
928 | child = fork(); |
929 | if (child < 0) { |
930 | sendErrorPADS(sock, myAddr, packet->ethHdr.h_source, |
931 | TAG_AC_SYSTEM_ERROR, "RP-PPPoE: Server: Unable to start session process"); |
932 | pppoe_free_session(cliSession); |
933 | return; |
934 | } |
935 | if (child != 0) { |
936 | /* In the parent process. Mark pid in session slot */ |
937 | cliSession->pid = child; |
938 | Event_HandleChildExit(event_selector, child, |
939 | childHandler, cliSession); |
940 | control_session_started(cliSession); |
941 | return; |
942 | } |
943 | |
944 | /* In the child process. */ |
945 | |
946 | /* Close all file descriptors except for socket */ |
947 | closelog(); |
948 | for (i=0; i<CLOSEFD; i++) { |
949 | if (i != sock) { |
950 | close(i); |
951 | } |
952 | } |
953 | |
954 | openlog("pppoe-server", LOG_PID, LOG_DAEMON); |
955 | /* pppd has a nasty habit of killing all processes in its process group. |
956 | Start a new session to stop pppd from killing us! */ |
957 | setsid(); |
958 | |
959 | /* Send PADS and Start pppd */ |
960 | memcpy(pads.ethHdr.h_dest, packet->ethHdr.h_source, ETH_ALEN); |
961 | memcpy(pads.ethHdr.h_source, myAddr, ETH_ALEN); |
962 | pads.ethHdr.h_proto = htons(Eth_PPPOE_Discovery); |
963 | pads.ver = 1; |
964 | pads.type = 1; |
965 | pads.code = CODE_PADS; |
966 | |
967 | pads.session = cliSession->sess; |
968 | plen = 0; |
969 | |
970 | /* Copy requested service name tag back in. If requested-service name |
971 | length is zero, and we have non-zero services, use first service-name |
972 | as default */ |
973 | if (!slen && NumServiceNames) { |
974 | slen = strlen(ServiceNames[0]); |
975 | memcpy(&requestedService.payload, ServiceNames[0], slen); |
976 | requestedService.length = htons(slen); |
977 | } |
978 | memcpy(cursor, &requestedService, TAG_HDR_SIZE+slen); |
979 | cursor += TAG_HDR_SIZE+slen; |
980 | plen += TAG_HDR_SIZE+slen; |
981 | |
982 | if (relayId.type) { |
983 | memcpy(cursor, &relayId, ntohs(relayId.length) + TAG_HDR_SIZE); |
984 | cursor += ntohs(relayId.length) + TAG_HDR_SIZE; |
985 | plen += ntohs(relayId.length) + TAG_HDR_SIZE; |
986 | } |
987 | if (hostUniq.type) { |
988 | memcpy(cursor, &hostUniq, ntohs(hostUniq.length) + TAG_HDR_SIZE); |
989 | cursor += ntohs(hostUniq.length) + TAG_HDR_SIZE; |
990 | plen += ntohs(hostUniq.length) + TAG_HDR_SIZE; |
991 | } |
992 | pads.length = htons(plen); |
993 | sendPacket(NULL, sock, &pads, (int) (plen + HDR_SIZE)); |
994 | |
995 | /* Close sock; don't need it any more */ |
996 | close(sock); |
997 | |
998 | startPPPD(cliSession); |
999 | } |
1000 | |
1001 | /********************************************************************** |
1002 | *%FUNCTION: termHandler |
1003 | *%ARGUMENTS: |
1004 | * sig -- signal number |
1005 | *%RETURNS: |
1006 | * Nothing |
1007 | *%DESCRIPTION: |
1008 | * Called by SIGTERM or SIGINT. Causes all sessions to be killed! |
1009 | ***********************************************************************/ |
1010 | static void |
1011 | termHandler(int sig) |
1012 | { |
1013 | syslog(LOG_INFO, |
1014 | "Terminating on signal %d -- killing all PPPoE sessions", |
1015 | sig); |
1016 | killAllSessions(); |
1017 | control_exit(); |
1018 | exit(0); |
1019 | } |
1020 | |
1021 | /********************************************************************** |
1022 | *%FUNCTION: usage |
1023 | *%ARGUMENTS: |
1024 | * argv0 -- argv[0] from main |
1025 | *%RETURNS: |
1026 | * Nothing |
1027 | *%DESCRIPTION: |
1028 | * Prints usage instructions |
1029 | ***********************************************************************/ |
1030 | void |
1031 | usage(char const *argv0) |
1032 | { |
1033 | fprintf(stderr, "Usage: %s [options]\n", argv0); |
1034 | fprintf(stderr, "Options:\n"); |
1035 | #ifdef USE_BPF |
1036 | fprintf(stderr, " -I if_name -- Specify interface (REQUIRED)\n"); |
1037 | #else |
1038 | fprintf(stderr, " -I if_name -- Specify interface (default %s.)\n", |
1039 | DEFAULT_IF); |
1040 | #endif |
1041 | fprintf(stderr, " -T timeout -- Specify inactivity timeout in seconds.\n"); |
1042 | fprintf(stderr, " -C name -- Set access concentrator name.\n"); |
1043 | fprintf(stderr, " -m MSS -- Clamp incoming and outgoing MSS options.\n"); |
1044 | fprintf(stderr, " -L ip -- Set local IP address.\n"); |
1045 | fprintf(stderr, " -l -- Increment local IP address for each session.\n"); |
1046 | fprintf(stderr, " -R ip -- Set start address of remote IP pool.\n"); |
1047 | fprintf(stderr, " -S name -- Advertise specified service-name.\n"); |
1048 | fprintf(stderr, " -O fname -- Use PPPD options from specified file\n"); |
1049 | fprintf(stderr, " (default %s).\n", PPPOE_SERVER_OPTIONS); |
1050 | fprintf(stderr, " -p fname -- Optain IP address pool from specified file.\n"); |
1051 | fprintf(stderr, " -N num -- Allow 'num' concurrent sessions.\n"); |
1052 | fprintf(stderr, " -o offset -- Assign session numbers starting at offset+1.\n"); |
1053 | fprintf(stderr, " -f disc:sess -- Set Ethernet frame types (hex).\n"); |
1054 | fprintf(stderr, " -s -- Use synchronous PPP mode.\n"); |
1055 | #ifdef HAVE_LINUX_KERNEL_PPPOE |
1056 | fprintf(stderr, " -k -- Use kernel-mode PPPoE.\n"); |
1057 | #endif |
1058 | fprintf(stderr, " -u -- Pass 'unit' option to pppd.\n"); |
1059 | fprintf(stderr, " -r -- Randomize session numbers.\n"); |
1060 | fprintf(stderr, " -d -- Debug session creation.\n"); |
1061 | fprintf(stderr, " -x n -- Limit to 'n' sessions/MAC address.\n"); |
1062 | fprintf(stderr, " -P -- Check pool file for correctness and exit.\n"); |
1063 | #ifdef HAVE_LICENSE |
1064 | fprintf(stderr, " -c secret:if:port -- Enable clustering on interface 'if'.\n"); |
1065 | fprintf(stderr, " -1 -- Allow only one session per user.\n"); |
1066 | #endif |
1067 | |
1068 | fprintf(stderr, " -h -- Print usage information.\n\n"); |
1069 | fprintf(stderr, "PPPoE-Server Version %s, Copyright (C) 2001-2006 Roaring Penguin Software Inc.\n", VERSION); |
1070 | |
1071 | #ifndef HAVE_LICENSE |
1072 | fprintf(stderr, "PPPoE-Server comes with ABSOLUTELY NO WARRANTY.\n"); |
1073 | fprintf(stderr, "This is free software, and you are welcome to redistribute it\n"); |
1074 | fprintf(stderr, "under the terms of the GNU General Public License, version 2\n"); |
1075 | fprintf(stderr, "or (at your option) any later version.\n"); |
1076 | #endif |
1077 | fprintf(stderr, "http://www.roaringpenguin.com\n"); |
1078 | } |
1079 | |
1080 | /********************************************************************** |
1081 | *%FUNCTION: main |
1082 | *%ARGUMENTS: |
1083 | * argc, argv -- usual suspects |
1084 | *%RETURNS: |
1085 | * Exit status |
1086 | *%DESCRIPTION: |
1087 | * Main program of PPPoE server |
1088 | ***********************************************************************/ |
1089 | int |
1090 | main(int argc, char **argv) |
1091 | { |
1092 | |
1093 | FILE *fp; |
1094 | int i, j; |
1095 | int opt; |
1096 | int d[IPV4ALEN]; |
1097 | int beDaemon = 1; |
1098 | int found; |
1099 | unsigned int discoveryType, sessionType; |
1100 | char *addressPoolFname = NULL; |
1101 | #ifdef HAVE_LICENSE |
1102 | int use_clustering = 0; |
1103 | #endif |
1104 | |
1105 | #ifndef HAVE_LINUX_KERNEL_PPPOE |
1106 | char *options = "x:hI:C:L:R:T:m:FN:f:O:o:sp:lrudPc:S:1"; |
1107 | #else |
1108 | char *options = "x:hI:C:L:R:T:m:FN:f:O:o:skp:lrudPc:S:1"; |
1109 | #endif |
1110 | |
1111 | if (getuid() != geteuid() || |
1112 | getgid() != getegid()) { |
1113 | fprintf(stderr, "SECURITY WARNING: pppoe-server will NOT run suid or sgid. Fix your installation.\n"); |
1114 | exit(1); |
1115 | } |
1116 | |
1117 | memset(interfaces, 0, sizeof(interfaces)); |
1118 | |
1119 | /* Initialize syslog */ |
1120 | openlog("pppoe-server", LOG_PID, LOG_DAEMON); |
1121 | |
1122 | /* Default number of session slots */ |
1123 | NumSessionSlots = DEFAULT_MAX_SESSIONS; |
1124 | MaxSessionsPerMac = 0; /* No limit */ |
1125 | NumActiveSessions = 0; |
1126 | |
1127 | /* Parse command-line options */ |
1128 | while((opt = getopt(argc, argv, options)) != -1) { |
1129 | switch(opt) { |
1130 | case 'x': |
1131 | if (sscanf(optarg, "%d", &MaxSessionsPerMac) != 1) { |
1132 | usage(argv[0]); |
1133 | exit(EXIT_FAILURE); |
1134 | } |
1135 | if (MaxSessionsPerMac < 0) { |
1136 | MaxSessionsPerMac = 0; |
1137 | } |
1138 | break; |
1139 | |
1140 | #ifdef HAVE_LINUX_KERNEL_PPPOE |
1141 | case 'k': |
1142 | UseLinuxKernelModePPPoE = 1; |
1143 | break; |
1144 | #endif |
1145 | case 'S': |
1146 | if (NumServiceNames == MAX_SERVICE_NAMES) { |
1147 | fprintf(stderr, "Too many '-S' options (%d max)", |
1148 | MAX_SERVICE_NAMES); |
1149 | exit(1); |
1150 | } |
1151 | ServiceNames[NumServiceNames] = strdup(optarg); |
1152 | if (!ServiceNames[NumServiceNames]) { |
1153 | fprintf(stderr, "Out of memory"); |
1154 | exit(1); |
1155 | } |
1156 | NumServiceNames++; |
1157 | break; |
1158 | case 'c': |
1159 | #ifndef HAVE_LICENSE |
1160 | fprintf(stderr, "Clustering capability not available.\n"); |
1161 | exit(1); |
1162 | #else |
1163 | cluster_handle_option(optarg); |
1164 | use_clustering = 1; |
1165 | break; |
1166 | #endif |
1167 | |
1168 | case 'd': |
1169 | Debug = 1; |
1170 | break; |
1171 | case 'P': |
1172 | CheckPoolSyntax = 1; |
1173 | break; |
1174 | case 'u': |
1175 | PassUnitOptionToPPPD = 1; |
1176 | break; |
1177 | |
1178 | case 'r': |
1179 | RandomizeSessionNumbers = 1; |
1180 | break; |
1181 | |
1182 | case 'l': |
1183 | IncrLocalIP = 1; |
1184 | break; |
1185 | |
1186 | case 'p': |
1187 | SET_STRING(addressPoolFname, optarg); |
1188 | break; |
1189 | |
1190 | case 's': |
1191 | Synchronous = 1; |
1192 | /* Pass the Synchronous option on to pppoe */ |
1193 | snprintf(PppoeOptions + strlen(PppoeOptions), |
1194 | SMALLBUF-strlen(PppoeOptions), |
1195 | " -s"); |
1196 | break; |
1197 | |
1198 | case 'f': |
1199 | if (sscanf(optarg, "%x:%x", &discoveryType, &sessionType) != 2) { |
1200 | fprintf(stderr, "Illegal argument to -f: Should be disc:sess in hex\n"); |
1201 | exit(EXIT_FAILURE); |
1202 | } |
1203 | Eth_PPPOE_Discovery = (UINT16_t) discoveryType; |
1204 | Eth_PPPOE_Session = (UINT16_t) sessionType; |
1205 | /* This option gets passed to pppoe */ |
1206 | snprintf(PppoeOptions + strlen(PppoeOptions), |
1207 | SMALLBUF-strlen(PppoeOptions), |
1208 | " -%c %s", opt, optarg); |
1209 | break; |
1210 | |
1211 | case 'F': |
1212 | beDaemon = 0; |
1213 | break; |
1214 | |
1215 | case 'N': |
1216 | if (sscanf(optarg, "%d", &opt) != 1) { |
1217 | usage(argv[0]); |
1218 | exit(EXIT_FAILURE); |
1219 | } |
1220 | if (opt <= 0) { |
1221 | fprintf(stderr, "-N: Value must be positive\n"); |
1222 | exit(EXIT_FAILURE); |
1223 | } |
1224 | NumSessionSlots = opt; |
1225 | break; |
1226 | |
1227 | case 'O': |
1228 | SET_STRING(pppoptfile, optarg); |
1229 | break; |
1230 | |
1231 | case 'o': |
1232 | if (sscanf(optarg, "%d", &opt) != 1) { |
1233 | usage(argv[0]); |
1234 | exit(EXIT_FAILURE); |
1235 | } |
1236 | if (opt < 0) { |
1237 | fprintf(stderr, "-o: Value must be non-negative\n"); |
1238 | exit(EXIT_FAILURE); |
1239 | } |
1240 | SessOffset = (size_t) opt; |
1241 | break; |
1242 | |
1243 | case 'I': |
1244 | if (NumInterfaces >= MAX_INTERFACES) { |
1245 | fprintf(stderr, "Too many -I options (max %d)\n", |
1246 | MAX_INTERFACES); |
1247 | exit(EXIT_FAILURE); |
1248 | } |
1249 | found = 0; |
1250 | for (i=0; i<NumInterfaces; i++) { |
1251 | if (!strncmp(interfaces[i].name, optarg, IFNAMSIZ)) { |
1252 | found = 1; |
1253 | break; |
1254 | } |
1255 | } |
1256 | if (!found) { |
1257 | strncpy(interfaces[NumInterfaces].name, optarg, IFNAMSIZ); |
1258 | NumInterfaces++; |
1259 | } |
1260 | break; |
1261 | |
1262 | case 'C': |
1263 | SET_STRING(ACName, optarg); |
1264 | break; |
1265 | |
1266 | case 'L': |
1267 | case 'R': |
1268 | /* Get local/remote IP address */ |
1269 | if (sscanf(optarg, "%d.%d.%d.%d", &d[0], &d[1], &d[2], &d[3]) != 4) { |
1270 | usage(argv[0]); |
1271 | exit(EXIT_FAILURE); |
1272 | } |
1273 | for (i=0; i<IPV4ALEN; i++) { |
1274 | if (d[i] < 0 || d[i] > 255) { |
1275 | usage(argv[0]); |
1276 | exit(EXIT_FAILURE); |
1277 | } |
1278 | if (opt == 'L') { |
1279 | LocalIP[i] = (unsigned char) d[i]; |
1280 | } else { |
1281 | RemoteIP[i] = (unsigned char) d[i]; |
1282 | } |
1283 | } |
1284 | break; |
1285 | |
1286 | case 'T': |
1287 | case 'm': |
1288 | /* These just get passed to pppoe */ |
1289 | snprintf(PppoeOptions + strlen(PppoeOptions), |
1290 | SMALLBUF-strlen(PppoeOptions), |
1291 | " -%c %s", opt, optarg); |
1292 | break; |
1293 | |
1294 | case 'h': |
1295 | usage(argv[0]); |
1296 | exit(EXIT_SUCCESS); |
1297 | case '1': |
1298 | #ifdef HAVE_LICENSE |
1299 | MaxSessionsPerUser = 1; |
1300 | #else |
1301 | fprintf(stderr, "-1 option not valid.\n"); |
1302 | exit(1); |
1303 | #endif |
1304 | break; |
1305 | } |
1306 | } |
1307 | |
1308 | if (!pppoptfile) { |
1309 | pppoptfile = PPPOE_SERVER_OPTIONS; |
1310 | } |
1311 | |
1312 | #ifdef HAVE_LICENSE |
1313 | License_SetVersion(SERVPOET_VERSION); |
1314 | License_ReadBundleFile("/etc/rp/bundle.txt"); |
1315 | License_ReadFile("/etc/rp/license.txt"); |
1316 | ServerLicense = License_GetFeature("PPPOE-SERVER"); |
1317 | if (!ServerLicense) { |
1318 | fprintf(stderr, "License: GetFeature failed: %s\n", |
1319 | License_ErrorMessage()); |
1320 | exit(1); |
1321 | } |
1322 | #endif |
1323 | |
1324 | #ifdef USE_LINUX_PACKET |
1325 | #ifndef HAVE_STRUCT_SOCKADDR_LL |
1326 | fprintf(stderr, "The PPPoE server does not work on Linux 2.0 kernels.\n"); |
1327 | exit(EXIT_FAILURE); |
1328 | #endif |
1329 | #endif |
1330 | |
1331 | if (!NumInterfaces) { |
1332 | strcpy(interfaces[0].name, DEFAULT_IF); |
1333 | NumInterfaces = 1; |
1334 | } |
1335 | |
1336 | if (!ACName) { |
1337 | ACName = malloc(HOSTNAMELEN); |
1338 | if (gethostname(ACName, HOSTNAMELEN) < 0) { |
1339 | fatalSys("gethostname"); |
1340 | } |
1341 | } |
1342 | |
1343 | /* If address pool filename given, count number of addresses */ |
1344 | if (addressPoolFname) { |
1345 | NumSessionSlots = parseAddressPool(addressPoolFname, 0); |
1346 | if (CheckPoolSyntax) { |
1347 | printf("%lu\n", (unsigned long) NumSessionSlots); |
1348 | exit(0); |
1349 | } |
1350 | } |
1351 | |
1352 | /* Max 65534 - SessOffset sessions */ |
1353 | if (NumSessionSlots + SessOffset > 65534) { |
1354 | fprintf(stderr, "-N and -o options must add up to at most 65534\n"); |
1355 | exit(EXIT_FAILURE); |
1356 | } |
1357 | |
1358 | /* Allocate memory for sessions */ |
1359 | Sessions = calloc(NumSessionSlots, sizeof(ClientSession)); |
1360 | if (!Sessions) { |
1361 | rp_fatal("Cannot allocate memory for session slots"); |
1362 | } |
1363 | |
1364 | /* Fill in local addresses first (let pool file override later */ |
1365 | for (i=0; i<NumSessionSlots; i++) { |
1366 | memcpy(Sessions[i].myip, LocalIP, sizeof(LocalIP)); |
1367 | if (IncrLocalIP) { |
1368 | incrementIPAddress(LocalIP); |
1369 | } |
1370 | } |
1371 | |
1372 | /* Fill in remote IP addresses from pool (may also overwrite local ips) */ |
1373 | if (addressPoolFname) { |
1374 | (void) parseAddressPool(addressPoolFname, 1); |
1375 | } |
1376 | |
1377 | /* For testing -- generate sequential remote IP addresses */ |
1378 | for (i=0; i<NumSessionSlots; i++) { |
1379 | Sessions[i].pid = 0; |
1380 | Sessions[i].funcs = &DefaultSessionFunctionTable; |
1381 | Sessions[i].sess = htons(i+1+SessOffset); |
1382 | |
1383 | if (!addressPoolFname) { |
1384 | memcpy(Sessions[i].peerip, RemoteIP, sizeof(RemoteIP)); |
1385 | #ifdef HAVE_LICENSE |
1386 | memcpy(Sessions[i].realpeerip, RemoteIP, sizeof(RemoteIP)); |
1387 | #endif |
1388 | incrementIPAddress(RemoteIP); |
1389 | } |
1390 | } |
1391 | |
1392 | /* Initialize our random cookie. Try /dev/urandom; if that fails, |
1393 | use PID and rand() */ |
1394 | fp = fopen("/dev/urandom", "r"); |
1395 | if (fp) { |
1396 | unsigned int x; |
1397 | fread(&x, 1, sizeof(x), fp); |
1398 | srand(x); |
1399 | fread(&CookieSeed, 1, SEED_LEN, fp); |
1400 | fclose(fp); |
1401 | } else { |
1402 | srand((unsigned int) getpid() * (unsigned int) time(NULL)); |
1403 | CookieSeed[0] = getpid() & 0xFF; |
1404 | CookieSeed[1] = (getpid() >> 8) & 0xFF; |
1405 | for (i=2; i<SEED_LEN; i++) { |
1406 | CookieSeed[i] = (rand() >> (i % 9)) & 0xFF; |
1407 | } |
1408 | } |
1409 | |
1410 | if (RandomizeSessionNumbers) { |
1411 | int *permutation; |
1412 | int tmp; |
1413 | permutation = malloc(sizeof(int) * NumSessionSlots); |
1414 | if (!permutation) { |
1415 | fprintf(stderr, "Could not allocate memory to randomize session numbers\n"); |
1416 | exit(EXIT_FAILURE); |
1417 | } |
1418 | for (i=0; i<NumSessionSlots; i++) { |
1419 | permutation[i] = i; |
1420 | } |
1421 | for (i=0; i<NumSessionSlots-1; i++) { |
1422 | j = i + rand() % (NumSessionSlots - i); |
1423 | if (j != i) { |
1424 | tmp = permutation[j]; |
1425 | permutation[j] = permutation[i]; |
1426 | permutation[i] = tmp; |
1427 | } |
1428 | } |
1429 | /* Link sessions together */ |
1430 | FreeSessions = &Sessions[permutation[0]]; |
1431 | LastFreeSession = &Sessions[permutation[NumSessionSlots-1]]; |
1432 | for (i=0; i<NumSessionSlots-1; i++) { |
1433 | Sessions[permutation[i]].next = &Sessions[permutation[i+1]]; |
1434 | } |
1435 | Sessions[permutation[NumSessionSlots-1]].next = NULL; |
1436 | free(permutation); |
1437 | } else { |
1438 | /* Link sessions together */ |
1439 | FreeSessions = &Sessions[0]; |
1440 | LastFreeSession = &Sessions[NumSessionSlots - 1]; |
1441 | for (i=0; i<NumSessionSlots-1; i++) { |
1442 | Sessions[i].next = &Sessions[i+1]; |
1443 | } |
1444 | Sessions[NumSessionSlots-1].next = NULL; |
1445 | } |
1446 | |
1447 | if (Debug) { |
1448 | /* Dump session array and exit */ |
1449 | ClientSession *ses = FreeSessions; |
1450 | while(ses) { |
1451 | printf("Session %u local %d.%d.%d.%d remote %d.%d.%d.%d\n", |
1452 | (unsigned int) (ntohs(ses->sess)), |
1453 | ses->myip[0], ses->myip[1], |
1454 | ses->myip[2], ses->myip[3], |
1455 | ses->peerip[0], ses->peerip[1], |
1456 | ses->peerip[2], ses->peerip[3]); |
1457 | ses = ses->next; |
1458 | } |
1459 | exit(0); |
1460 | } |
1461 | |
1462 | /* Open all the interfaces */ |
1463 | for (i=0; i<NumInterfaces; i++) { |
1464 | interfaces[i].sock = openInterface(interfaces[i].name, Eth_PPPOE_Discovery, interfaces[i].mac); |
1465 | } |
1466 | |
1467 | /* Ignore SIGPIPE */ |
1468 | signal(SIGPIPE, SIG_IGN); |
1469 | |
1470 | /* Create event selector */ |
1471 | event_selector = Event_CreateSelector(); |
1472 | if (!event_selector) { |
1473 | rp_fatal("Could not create EventSelector -- probably out of memory"); |
1474 | } |
1475 | |
1476 | /* Set signal handlers for SIGTERM and SIGINT */ |
1477 | if (Event_HandleSignal(event_selector, SIGTERM, termHandler) < 0 || |
1478 | Event_HandleSignal(event_selector, SIGINT, termHandler) < 0) { |
1479 | fatalSys("Event_HandleSignal"); |
1480 | } |
1481 | |
1482 | /* Control channel */ |
1483 | #ifdef HAVE_LICENSE |
1484 | if (control_init(argc, argv, event_selector)) { |
1485 | rp_fatal("control_init failed"); |
1486 | } |
1487 | #endif |
1488 | |
1489 | /* Create event handler for each interface */ |
1490 | for (i = 0; i<NumInterfaces; i++) { |
1491 | interfaces[i].eh = Event_AddHandler(event_selector, |
1492 | interfaces[i].sock, |
1493 | EVENT_FLAG_READABLE, |
1494 | InterfaceHandler, |
1495 | &interfaces[i]); |
1496 | #ifdef HAVE_L2TP |
1497 | interfaces[i].session_sock = -1; |
1498 | #endif |
1499 | if (!interfaces[i].eh) { |
1500 | rp_fatal("Event_AddHandler failed"); |
1501 | } |
1502 | } |
1503 | |
1504 | #ifdef HAVE_LICENSE |
1505 | if (use_clustering) { |
1506 | ClusterLicense = License_GetFeature("PPPOE-CLUSTER"); |
1507 | if (!ClusterLicense) { |
1508 | fprintf(stderr, "License: GetFeature failed: %s\n", |
1509 | License_ErrorMessage()); |
1510 | exit(1); |
1511 | } |
1512 | if (!License_Expired(ClusterLicense)) { |
1513 | if (cluster_init(event_selector) < 0) { |
1514 | rp_fatal("cluster_init failed"); |
1515 | } |
1516 | } |
1517 | } |
1518 | #endif |
1519 | |
1520 | #ifdef HAVE_L2TP |
1521 | for (i=0; i<NumInterfaces; i++) { |
1522 | pppoe_to_l2tp_add_interface(event_selector, |
1523 | &interfaces[i]); |
1524 | } |
1525 | #endif |
1526 | |
1527 | /* Daemonize -- UNIX Network Programming, Vol. 1, Stevens */ |
1528 | if (beDaemon) { |
1529 | i = fork(); |
1530 | if (i < 0) { |
1531 | fatalSys("fork"); |
1532 | } else if (i != 0) { |
1533 | /* parent */ |
1534 | exit(EXIT_SUCCESS); |
1535 | } |
1536 | setsid(); |
1537 | signal(SIGHUP, SIG_IGN); |
1538 | i = fork(); |
1539 | if (i < 0) { |
1540 | fatalSys("fork"); |
1541 | } else if (i != 0) { |
1542 | exit(EXIT_SUCCESS); |
1543 | } |
1544 | |
1545 | chdir("/"); |
1546 | |
1547 | /* Point stdin/stdout/stderr to /dev/null */ |
1548 | for (i=0; i<3; i++) { |
1549 | close(i); |
1550 | } |
1551 | i = open("/dev/null", O_RDWR); |
1552 | if (i >= 0) { |
1553 | dup2(i, 0); |
1554 | dup2(i, 1); |
1555 | dup2(i, 2); |
1556 | if (i > 2) close(i); |
1557 | } |
1558 | } |
1559 | |
1560 | for(;;) { |
1561 | i = Event_HandleEvent(event_selector); |
1562 | if (i < 0) { |
1563 | fatalSys("Event_HandleEvent"); |
1564 | } |
1565 | |
1566 | #ifdef HAVE_LICENSE |
1567 | if (License_Expired(ServerLicense)) { |
1568 | syslog(LOG_INFO, "Server license has expired -- killing all PPPoE sessions"); |
1569 | killAllSessions(); |
1570 | control_exit(); |
1571 | exit(0); |
1572 | } |
1573 | #endif |
1574 | } |
1575 | return 0; |
1576 | } |
1577 | |
1578 | void |
1579 | serverProcessPacket(Interface *i) |
1580 | { |
1581 | int len; |
1582 | PPPoEPacket packet; |
1583 | int sock = i->sock; |
1584 | |
1585 | if (receivePacket(sock, &packet, &len) < 0) { |
1586 | return; |
1587 | } |
1588 | |
1589 | /* Check length */ |
1590 | if (ntohs(packet.length) + HDR_SIZE > len) { |
1591 | syslog(LOG_ERR, "Bogus PPPoE length field (%u)", |
1592 | (unsigned int) ntohs(packet.length)); |
1593 | return; |
1594 | } |
1595 | |
1596 | /* Sanity check on packet */ |
1597 | if (packet.ver != 1 || packet.type != 1) { |
1598 | /* Syslog an error */ |
1599 | return; |
1600 | } |
1601 | switch(packet.code) { |
1602 | case CODE_PADI: |
1603 | processPADI(i, &packet, len); |
1604 | break; |
1605 | case CODE_PADR: |
1606 | processPADR(i, &packet, len); |
1607 | break; |
1608 | case CODE_PADT: |
1609 | /* Kill the child */ |
1610 | processPADT(i, &packet, len); |
1611 | break; |
1612 | case CODE_SESS: |
1613 | /* Ignore SESS -- children will handle them */ |
1614 | break; |
1615 | case CODE_PADO: |
1616 | case CODE_PADS: |
1617 | /* Ignore PADO and PADS totally */ |
1618 | break; |
1619 | default: |
1620 | /* Syslog an error */ |
1621 | break; |
1622 | } |
1623 | } |
1624 | |
1625 | /********************************************************************** |
1626 | *%FUNCTION: sendErrorPADS |
1627 | *%ARGUMENTS: |
1628 | * sock -- socket to write to |
1629 | * source -- source Ethernet address |
1630 | * dest -- destination Ethernet address |
1631 | * errorTag -- error tag |
1632 | * errorMsg -- error message |
1633 | *%RETURNS: |
1634 | * Nothing |
1635 | *%DESCRIPTION: |
1636 | * Sends a PADS packet with an error message |
1637 | ***********************************************************************/ |
1638 | void |
1639 | sendErrorPADS(int sock, |
1640 | unsigned char *source, |
1641 | unsigned char *dest, |
1642 | int errorTag, |
1643 | char *errorMsg) |
1644 | { |
1645 | PPPoEPacket pads; |
1646 | unsigned char *cursor = pads.payload; |
1647 | UINT16_t plen; |
1648 | PPPoETag err; |
1649 | int elen = strlen(errorMsg); |
1650 | |
1651 | memcpy(pads.ethHdr.h_dest, dest, ETH_ALEN); |
1652 | memcpy(pads.ethHdr.h_source, source, ETH_ALEN); |
1653 | pads.ethHdr.h_proto = htons(Eth_PPPOE_Discovery); |
1654 | pads.ver = 1; |
1655 | pads.type = 1; |
1656 | pads.code = CODE_PADS; |
1657 | |
1658 | pads.session = htons(0); |
1659 | plen = 0; |
1660 | |
1661 | err.type = htons(errorTag); |
1662 | err.length = htons(elen); |
1663 | |
1664 | memcpy(err.payload, errorMsg, elen); |
1665 | memcpy(cursor, &err, TAG_HDR_SIZE+elen); |
1666 | cursor += TAG_HDR_SIZE + elen; |
1667 | plen += TAG_HDR_SIZE + elen; |
1668 | |
1669 | if (relayId.type) { |
1670 | memcpy(cursor, &relayId, ntohs(relayId.length) + TAG_HDR_SIZE); |
1671 | cursor += ntohs(relayId.length) + TAG_HDR_SIZE; |
1672 | plen += ntohs(relayId.length) + TAG_HDR_SIZE; |
1673 | } |
1674 | if (hostUniq.type) { |
1675 | memcpy(cursor, &hostUniq, ntohs(hostUniq.length) + TAG_HDR_SIZE); |
1676 | cursor += ntohs(hostUniq.length) + TAG_HDR_SIZE; |
1677 | plen += ntohs(hostUniq.length) + TAG_HDR_SIZE; |
1678 | } |
1679 | pads.length = htons(plen); |
1680 | sendPacket(NULL, sock, &pads, (int) (plen + HDR_SIZE)); |
1681 | } |
1682 | |
1683 | |
1684 | /********************************************************************** |
1685 | *%FUNCTION: startPPPDUserMode |
1686 | *%ARGUMENTS: |
1687 | * session -- client session record |
1688 | *%RETURNS: |
1689 | * Nothing |
1690 | *%DESCRIPTION: |
1691 | * Starts PPPD for user-mode PPPoE |
1692 | ***********************************************************************/ |
1693 | void |
1694 | startPPPDUserMode(ClientSession *session) |
1695 | { |
1696 | /* Leave some room */ |
1697 | char *argv[32]; |
1698 | |
1699 | char buffer[SMALLBUF]; |
1700 | |
1701 | int c = 0; |
1702 | |
1703 | argv[c++] = "pppd"; |
1704 | argv[c++] = "pty"; |
1705 | |
1706 | /* Let's hope service-name does not have ' in it... */ |
1707 | snprintf(buffer, SMALLBUF, "%s -n -I %s -e %u:%02x:%02x:%02x:%02x:%02x:%02x%s -S '%s'", |
1708 | PPPOE_PATH, session->ethif->name, |
1709 | (unsigned int) ntohs(session->sess), |
1710 | session->eth[0], session->eth[1], session->eth[2], |
1711 | session->eth[3], session->eth[4], session->eth[5], |
1712 | PppoeOptions, session->serviceName); |
1713 | argv[c++] = strdup(buffer); |
1714 | if (!argv[c-1]) { |
1715 | /* TODO: Send a PADT */ |
1716 | exit(EXIT_FAILURE); |
1717 | } |
1718 | |
1719 | argv[c++] = "file"; |
1720 | argv[c++] = pppoptfile; |
1721 | |
1722 | snprintf(buffer, SMALLBUF, "%d.%d.%d.%d:%d.%d.%d.%d", |
1723 | (int) session->myip[0], (int) session->myip[1], |
1724 | (int) session->myip[2], (int) session->myip[3], |
1725 | (int) session->peerip[0], (int) session->peerip[1], |
1726 | (int) session->peerip[2], (int) session->peerip[3]); |
1727 | syslog(LOG_INFO, |
1728 | "Session %u created for client %02x:%02x:%02x:%02x:%02x:%02x (%d.%d.%d.%d) on %s using Service-Name '%s'", |
1729 | (unsigned int) ntohs(session->sess), |
1730 | session->eth[0], session->eth[1], session->eth[2], |
1731 | session->eth[3], session->eth[4], session->eth[5], |
1732 | (int) session->peerip[0], (int) session->peerip[1], |
1733 | (int) session->peerip[2], (int) session->peerip[3], |
1734 | session->ethif->name, |
1735 | session->serviceName); |
1736 | argv[c++] = strdup(buffer); |
1737 | if (!argv[c-1]) { |
1738 | /* TODO: Send a PADT */ |
1739 | exit(EXIT_FAILURE); |
1740 | } |
1741 | argv[c++] = "nodetach"; |
1742 | argv[c++] = "noaccomp"; |
1743 | argv[c++] = "nobsdcomp"; |
1744 | argv[c++] = "nodeflate"; |
1745 | argv[c++] = "nopcomp"; |
1746 | argv[c++] = "novj"; |
1747 | argv[c++] = "novjccomp"; |
1748 | argv[c++] = "default-asyncmap"; |
1749 | if (Synchronous) { |
1750 | argv[c++] = "sync"; |
1751 | } |
1752 | if (PassUnitOptionToPPPD) { |
1753 | argv[c++] = "unit"; |
1754 | sprintf(buffer, "%u", (unsigned int) (ntohs(session->sess) - 1 - SessOffset)); |
1755 | argv[c++] = buffer; |
1756 | } |
1757 | argv[c++] = NULL; |
1758 | |
1759 | execv(PPPD_PATH, argv); |
1760 | exit(EXIT_FAILURE); |
1761 | } |
1762 | |
1763 | /********************************************************************** |
1764 | *%FUNCTION: startPPPDLinuxKernelMode |
1765 | *%ARGUMENTS: |
1766 | * session -- client session record |
1767 | *%RETURNS: |
1768 | * Nothing |
1769 | *%DESCRIPTION: |
1770 | * Starts PPPD for kernel-mode PPPoE on Linux |
1771 | ***********************************************************************/ |
1772 | void |
1773 | startPPPDLinuxKernelMode(ClientSession *session) |
1774 | { |
1775 | /* Leave some room */ |
1776 | char *argv[32]; |
1777 | |
1778 | int c = 0; |
1779 | |
1780 | char buffer[SMALLBUF]; |
1781 | |
1782 | argv[c++] = "pppd"; |
1783 | argv[c++] = "plugin"; |
1784 | argv[c++] = PLUGIN_PATH; |
1785 | |
1786 | /* Add "nic-" to interface name */ |
1787 | snprintf(buffer, SMALLBUF, "nic-%s", session->ethif->name); |
1788 | argv[c++] = strdup(buffer); |
1789 | if (!argv[c-1]) { |
1790 | exit(EXIT_FAILURE); |
1791 | } |
1792 | |
1793 | snprintf(buffer, SMALLBUF, "%u:%02x:%02x:%02x:%02x:%02x:%02x", |
1794 | (unsigned int) ntohs(session->sess), |
1795 | session->eth[0], session->eth[1], session->eth[2], |
1796 | session->eth[3], session->eth[4], session->eth[5]); |
1797 | argv[c++] = "rp_pppoe_sess"; |
1798 | argv[c++] = strdup(buffer); |
1799 | if (!argv[c-1]) { |
1800 | /* TODO: Send a PADT */ |
1801 | exit(EXIT_FAILURE); |
1802 | } |
1803 | argv[c++] = "rp_pppoe_service"; |
1804 | argv[c++] = (char *) session->serviceName; |
1805 | argv[c++] = "file"; |
1806 | argv[c++] = pppoptfile; |
1807 | |
1808 | snprintf(buffer, SMALLBUF, "%d.%d.%d.%d:%d.%d.%d.%d", |
1809 | (int) session->myip[0], (int) session->myip[1], |
1810 | (int) session->myip[2], (int) session->myip[3], |
1811 | (int) session->peerip[0], (int) session->peerip[1], |
1812 | (int) session->peerip[2], (int) session->peerip[3]); |
1813 | syslog(LOG_INFO, |
1814 | "Session %u created for client %02x:%02x:%02x:%02x:%02x:%02x (%d.%d.%d.%d) on %s using Service-Name '%s'", |
1815 | (unsigned int) ntohs(session->sess), |
1816 | session->eth[0], session->eth[1], session->eth[2], |
1817 | session->eth[3], session->eth[4], session->eth[5], |
1818 | (int) session->peerip[0], (int) session->peerip[1], |
1819 | (int) session->peerip[2], (int) session->peerip[3], |
1820 | session->ethif->name, |
1821 | session->serviceName); |
1822 | argv[c++] = strdup(buffer); |
1823 | if (!argv[c-1]) { |
1824 | /* TODO: Send a PADT */ |
1825 | exit(EXIT_FAILURE); |
1826 | } |
1827 | argv[c++] = "nodetach"; |
1828 | argv[c++] = "noaccomp"; |
1829 | argv[c++] = "nobsdcomp"; |
1830 | argv[c++] = "nodeflate"; |
1831 | argv[c++] = "nopcomp"; |
1832 | argv[c++] = "novj"; |
1833 | argv[c++] = "novjccomp"; |
1834 | argv[c++] = "default-asyncmap"; |
1835 | if (PassUnitOptionToPPPD) { |
1836 | argv[c++] = "unit"; |
1837 | sprintf(buffer, "%u", (unsigned int) (ntohs(session->sess) - 1 - SessOffset)); |
1838 | argv[c++] = buffer; |
1839 | } |
1840 | argv[c++] = NULL; |
1841 | execv(PPPD_PATH, argv); |
1842 | exit(EXIT_FAILURE); |
1843 | } |
1844 | |
1845 | /********************************************************************** |
1846 | *%FUNCTION: startPPPD |
1847 | *%ARGUMENTS: |
1848 | * session -- client session record |
1849 | *%RETURNS: |
1850 | * Nothing |
1851 | *%DESCRIPTION: |
1852 | * Starts PPPD |
1853 | ***********************************************************************/ |
1854 | void |
1855 | startPPPD(ClientSession *session) |
1856 | { |
1857 | if (UseLinuxKernelModePPPoE) startPPPDLinuxKernelMode(session); |
1858 | else startPPPDUserMode(session); |
1859 | } |
1860 | |
1861 | /********************************************************************** |
1862 | * %FUNCTION: InterfaceHandler |
1863 | * %ARGUMENTS: |
1864 | * es -- event selector (ignored) |
1865 | * fd -- file descriptor which is readable |
1866 | * flags -- ignored |
1867 | * data -- Pointer to the Interface structure |
1868 | * %RETURNS: |
1869 | * Nothing |
1870 | * %DESCRIPTION: |
1871 | * Handles a packet ready at an interface |
1872 | ***********************************************************************/ |
1873 | void |
1874 | InterfaceHandler(EventSelector *es, |
1875 | int fd, |
1876 | unsigned int flags, |
1877 | void *data) |
1878 | { |
1879 | serverProcessPacket((Interface *) data); |
1880 | } |
1881 | |
1882 | /********************************************************************** |
1883 | * %FUNCTION: PppoeStopSession |
1884 | * %ARGUMENTS: |
1885 | * ses -- the session |
1886 | * reason -- reason session is being stopped. |
1887 | * %RETURNS: |
1888 | * Nothing |
1889 | * %DESCRIPTION: |
1890 | * Kills pppd. |
1891 | ***********************************************************************/ |
1892 | static void |
1893 | PppoeStopSession(ClientSession *ses, |
1894 | char const *reason) |
1895 | { |
1896 | /* Temporary structure for sending PADT's. */ |
1897 | PPPoEConnection conn; |
1898 | |
1899 | memset(&conn, 0, sizeof(conn)); |
1900 | conn.useHostUniq = 0; |
1901 | |
1902 | memcpy(conn.myEth, ses->ethif->mac, ETH_ALEN); |
1903 | conn.discoverySocket = ses->ethif->sock; |
1904 | conn.session = ses->sess; |
1905 | memcpy(conn.peerEth, ses->eth, ETH_ALEN); |
1906 | sendPADT(&conn, reason); |
1907 | ses->flags |= FLAG_SENT_PADT; |
1908 | |
1909 | if (ses->pid) { |
1910 | kill(ses->pid, SIGTERM); |
1911 | } |
1912 | ses->funcs = &DefaultSessionFunctionTable; |
1913 | } |
1914 | |
1915 | /********************************************************************** |
1916 | * %FUNCTION: PppoeSessionIsActive |
1917 | * %ARGUMENTS: |
1918 | * ses -- the session |
1919 | * %RETURNS: |
1920 | * True if session is active, false if not. |
1921 | ***********************************************************************/ |
1922 | static int |
1923 | PppoeSessionIsActive(ClientSession *ses) |
1924 | { |
1925 | return (ses->pid != 0); |
1926 | } |
1927 | |
1928 | #ifdef HAVE_LICENSE |
1929 | /********************************************************************** |
1930 | * %FUNCTION: getFreeMem |
1931 | * %ARGUMENTS: |
1932 | * None |
1933 | * %RETURNS: |
1934 | * The amount of free RAM in kilobytes, or -1 if it could not be |
1935 | * determined |
1936 | * %DESCRIPTION: |
1937 | * Reads Linux-specific /proc/meminfo file and extracts free RAM |
1938 | ***********************************************************************/ |
1939 | int |
1940 | getFreeMem(void) |
1941 | { |
1942 | char buf[512]; |
1943 | int memfree=0, buffers=0, cached=0; |
1944 | FILE *fp = fopen("/proc/meminfo", "r"); |
1945 | if (!fp) return -1; |
1946 | |
1947 | while (fgets(buf, sizeof(buf), fp)) { |
1948 | if (!strncmp(buf, "MemFree:", 8)) { |
1949 | if (sscanf(buf, "MemFree: %d", &memfree) != 1) { |
1950 | fclose(fp); |
1951 | return -1; |
1952 | } |
1953 | } else if (!strncmp(buf, "Buffers:", 8)) { |
1954 | if (sscanf(buf, "Buffers: %d", &buffers) != 1) { |
1955 | fclose(fp); |
1956 | return -1; |
1957 | } |
1958 | } else if (!strncmp(buf, "Cached:", 7)) { |
1959 | if (sscanf(buf, "Cached: %d", &cached) != 1) { |
1960 | fclose(fp); |
1961 | return -1; |
1962 | } |
1963 | } |
1964 | } |
1965 | fclose(fp); |
1966 | /* return memfree + buffers + cached; */ |
1967 | return memfree; |
1968 | } |
1969 | #endif |
1970 | |
1971 | /********************************************************************** |
1972 | * %FUNCTION: pppoe_alloc_session |
1973 | * %ARGUMENTS: |
1974 | * None |
1975 | * %RETURNS: |
1976 | * NULL if no session is available, otherwise a ClientSession structure. |
1977 | * %DESCRIPTION: |
1978 | * Allocates a ClientSession structure and removes from free list, puts |
1979 | * on busy list |
1980 | ***********************************************************************/ |
1981 | ClientSession * |
1982 | pppoe_alloc_session(void) |
1983 | { |
1984 | ClientSession *ses = FreeSessions; |
1985 | if (!ses) return NULL; |
1986 | |
1987 | /* Remove from free sessions list */ |
1988 | if (ses == LastFreeSession) { |
1989 | LastFreeSession = NULL; |
1990 | } |
1991 | FreeSessions = ses->next; |
1992 | |
1993 | /* Put on busy sessions list */ |
1994 | ses->next = BusySessions; |
1995 | BusySessions = ses; |
1996 | |
1997 | /* Initialize fields to sane values */ |
1998 | ses->funcs = &DefaultSessionFunctionTable; |
1999 | ses->pid = 0; |
2000 | ses->ethif = NULL; |
2001 | memset(ses->eth, 0, ETH_ALEN); |
2002 | ses->flags = 0; |
2003 | ses->startTime = time(NULL); |
2004 | ses->serviceName = ""; |
2005 | #ifdef HAVE_LICENSE |
2006 | memset(ses->user, 0, MAX_USERNAME_LEN+1); |
2007 | memset(ses->realm, 0, MAX_USERNAME_LEN+1); |
2008 | memset(ses->realpeerip, 0, IPV4ALEN); |
2009 | #endif |
2010 | #ifdef HAVE_L2TP |
2011 | ses->l2tp_ses = NULL; |
2012 | #endif |
2013 | NumActiveSessions++; |
2014 | return ses; |
2015 | } |
2016 | |
2017 | /********************************************************************** |
2018 | * %FUNCTION: pppoe_free_session |
2019 | * %ARGUMENTS: |
2020 | * ses -- session to free |
2021 | * %RETURNS: |
2022 | * 0 if OK, -1 if error |
2023 | * %DESCRIPTION: |
2024 | * Places a ClientSession on the free list. |
2025 | ***********************************************************************/ |
2026 | int |
2027 | pppoe_free_session(ClientSession *ses) |
2028 | { |
2029 | ClientSession *cur, *prev; |
2030 | |
2031 | cur = BusySessions; |
2032 | prev = NULL; |
2033 | while (cur) { |
2034 | if (ses == cur) break; |
2035 | prev = cur; |
2036 | cur = cur->next; |
2037 | } |
2038 | |
2039 | if (!cur) { |
2040 | syslog(LOG_ERR, "pppoe_free_session: Could not find session %p on busy list", (void *) ses); |
2041 | return -1; |
2042 | } |
2043 | |
2044 | /* Remove from busy sessions list */ |
2045 | if (prev) { |
2046 | prev->next = ses->next; |
2047 | } else { |
2048 | BusySessions = ses->next; |
2049 | } |
2050 | |
2051 | /* Add to end of free sessions */ |
2052 | ses->next = NULL; |
2053 | if (LastFreeSession) { |
2054 | LastFreeSession->next = ses; |
2055 | LastFreeSession = ses; |
2056 | } else { |
2057 | FreeSessions = ses; |
2058 | LastFreeSession = ses; |
2059 | } |
2060 | |
2061 | /* Initialize fields to sane values */ |
2062 | ses->funcs = &DefaultSessionFunctionTable; |
2063 | ses->pid = 0; |
2064 | ses->flags = 0; |
2065 | #ifdef HAVE_L2TP |
2066 | ses->l2tp_ses = NULL; |
2067 | #endif |
2068 | NumActiveSessions--; |
2069 | return 0; |
2070 | } |
2071 | |
2072 | /********************************************************************** |
2073 | * %FUNCTION: sendHURLorMOTM |
2074 | * %ARGUMENTS: |
2075 | * conn -- PPPoE connection |
2076 | * url -- a URL, which *MUST* begin with "http://" or it won't be sent, or |
2077 | * a message. |
2078 | * tag -- one of TAG_HURL or TAG_MOTM |
2079 | * %RETURNS: |
2080 | * Nothing |
2081 | * %DESCRIPTION: |
2082 | * Sends a PADM packet contaning a HURL or MOTM tag to the victim...er, peer. |
2083 | ***********************************************************************/ |
2084 | void |
2085 | sendHURLorMOTM(PPPoEConnection *conn, char const *url, UINT16_t tag) |
2086 | { |
2087 | PPPoEPacket packet; |
2088 | PPPoETag hurl; |
2089 | size_t elen; |
2090 | unsigned char *cursor = packet.payload; |
2091 | UINT16_t plen = 0; |
2092 | |
2093 | if (!conn->session) return; |
2094 | if (conn->discoverySocket < 0) return; |
2095 | |
2096 | if (tag == TAG_HURL) { |
2097 | if (strncmp(url, "http://", 7)) { |
2098 | syslog(LOG_WARNING, "sendHURL(%s): URL must begin with http://", url); |
2099 | return; |
2100 | } |
2101 | } else { |
2102 | tag = TAG_MOTM; |
2103 | } |
2104 | |
2105 | memcpy(packet.ethHdr.h_dest, conn->peerEth, ETH_ALEN); |
2106 | memcpy(packet.ethHdr.h_source, conn->myEth, ETH_ALEN); |
2107 | |
2108 | packet.ethHdr.h_proto = htons(Eth_PPPOE_Discovery); |
2109 | packet.ver = 1; |
2110 | packet.type = 1; |
2111 | packet.code = CODE_PADM; |
2112 | packet.session = conn->session; |
2113 | |
2114 | elen = strlen(url); |
2115 | if (elen > 256) { |
2116 | syslog(LOG_WARNING, "MOTM or HURL too long: %d", (int) elen); |
2117 | return; |
2118 | } |
2119 | |
2120 | hurl.type = htons(tag); |
2121 | hurl.length = htons(elen); |
2122 | strcpy((char *) hurl.payload, url); |
2123 | memcpy(cursor, &hurl, elen + TAG_HDR_SIZE); |
2124 | cursor += elen + TAG_HDR_SIZE; |
2125 | plen += elen + TAG_HDR_SIZE; |
2126 | |
2127 | packet.length = htons(plen); |
2128 | |
2129 | sendPacket(conn, conn->discoverySocket, &packet, (int) (plen + HDR_SIZE)); |
2130 | #ifdef DEBUGGING_ENABLED |
2131 | if (conn->debugFile) { |
2132 | dumpPacket(conn->debugFile, &packet, "SENT"); |
2133 | fprintf(conn->debugFile, "\n"); |
2134 | fflush(conn->debugFile); |
2135 | } |
2136 | #endif |
2137 | } |
2138 |