1/*********************************************************************** 2* 3* common.c 4* 5* Implementation of user-space PPPoE redirector for Linux. 6* 7* Common functions used by PPPoE client and server 8* 9* Copyright (C) 2000 by Roaring Penguin Software Inc. 10* 11* This program may be distributed according to the terms of the GNU 12* General Public License, version 2 or (at your option) any later version. 13* 14* LIC: GPL 15* 16***********************************************************************/ 17 18static char const RCSID[] = 19"$Id: common.c,v 1.1.1.1 2008/10/15 03:30:51 james26_jang Exp $"; 20/* For vsnprintf prototype */ 21#define _ISOC99_SOURCE 1 22 23#include "pppoe.h" 24 25 26#ifdef HAVE_SYSLOG_H 27#include <syslog.h> 28#endif 29 30#include <string.h> 31#include <errno.h> 32#include <stdlib.h> 33#include <stdarg.h> 34 35#ifdef HAVE_UNISTD_H 36#include <unistd.h> 37#endif 38 39/********************************************************************** 40*%FUNCTION: parsePacket 41*%ARGUMENTS: 42* packet -- the PPPoE discovery packet to parse 43* func -- function called for each tag in the packet 44* extra -- an opaque data pointer supplied to parsing function 45*%RETURNS: 46* 0 if everything went well; -1 if there was an error 47*%DESCRIPTION: 48* Parses a PPPoE discovery packet, calling "func" for each tag in the packet. 49* "func" is passed the additional argument "extra". 50***********************************************************************/ 51int 52parsePacket(PPPoEPacket *packet, ParseFunc *func, void *extra) 53{ 54 UINT16_t len = ntohs(packet->length); 55 unsigned char *curTag; 56 UINT16_t tagType, tagLen; 57 58 if (packet->ver != 1) { 59 syslog(LOG_ERR, "Invalid PPPoE version (%d)", (int) packet->ver); 60 return -1; 61 } 62 if (packet->type != 1) { 63 syslog(LOG_ERR, "Invalid PPPoE type (%d)", (int) packet->type); 64 return -1; 65 } 66 67 /* Do some sanity checks on packet */ 68 if (len > ETH_DATA_LEN - 6) { /* 6-byte overhead for PPPoE header */ 69 syslog(LOG_ERR, "Invalid PPPoE packet length (%u)", len); 70 return -1; 71 } 72 73 /* Step through the tags */ 74 curTag = packet->payload; 75 while(curTag - packet->payload < len) { 76 /* Alignment is not guaranteed, so do this by hand... */ 77 tagType = (((UINT16_t) curTag[0]) << 8) + 78 (UINT16_t) curTag[1]; 79 tagLen = (((UINT16_t) curTag[2]) << 8) + 80 (UINT16_t) curTag[3]; 81 if (tagType == TAG_END_OF_LIST) { 82 return 0; 83 } 84 if ((curTag - packet->payload) + tagLen + TAG_HDR_SIZE > len) { 85 syslog(LOG_ERR, "Invalid PPPoE tag length (%u)", tagLen); 86 return -1; 87 } 88 func(tagType, tagLen, curTag+TAG_HDR_SIZE, extra); 89 curTag = curTag + TAG_HDR_SIZE + tagLen; 90 } 91 return 0; 92} 93 94/********************************************************************** 95*%FUNCTION: findTag 96*%ARGUMENTS: 97* packet -- the PPPoE discovery packet to parse 98* type -- the type of the tag to look for 99* tag -- will be filled in with tag contents 100*%RETURNS: 101* A pointer to the tag if one of the specified type is found; NULL 102* otherwise. 103*%DESCRIPTION: 104* Looks for a specific tag type. 105***********************************************************************/ 106unsigned char * 107findTag(PPPoEPacket *packet, UINT16_t type, PPPoETag *tag) 108{ 109 UINT16_t len = ntohs(packet->length); 110 unsigned char *curTag; 111 UINT16_t tagType, tagLen; 112 113 if (packet->ver != 1) { 114 syslog(LOG_ERR, "Invalid PPPoE version (%d)", (int) packet->ver); 115 return NULL; 116 } 117 if (packet->type != 1) { 118 syslog(LOG_ERR, "Invalid PPPoE type (%d)", (int) packet->type); 119 return NULL; 120 } 121 122 /* Do some sanity checks on packet */ 123 if (len > ETH_DATA_LEN - 6) { /* 6-byte overhead for PPPoE header */ 124 syslog(LOG_ERR, "Invalid PPPoE packet length (%u)", len); 125 return NULL; 126 } 127 128 /* Step through the tags */ 129 curTag = packet->payload; 130 while(curTag - packet->payload < len) { 131 /* Alignment is not guaranteed, so do this by hand... */ 132 tagType = (((UINT16_t) curTag[0]) << 8) + 133 (UINT16_t) curTag[1]; 134 tagLen = (((UINT16_t) curTag[2]) << 8) + 135 (UINT16_t) curTag[3]; 136 if (tagType == TAG_END_OF_LIST) { 137 return NULL; 138 } 139 if ((curTag - packet->payload) + tagLen + TAG_HDR_SIZE > len) { 140 syslog(LOG_ERR, "Invalid PPPoE tag length (%u)", tagLen); 141 return NULL; 142 } 143 if (tagType == type) { 144 memcpy(tag, curTag, tagLen + TAG_HDR_SIZE); 145 return curTag; 146 } 147 curTag = curTag + TAG_HDR_SIZE + tagLen; 148 } 149 return NULL; 150} 151 152/********************************************************************** 153*%FUNCTION: printErr 154*%ARGUMENTS: 155* str -- error message 156*%RETURNS: 157* Nothing 158*%DESCRIPTION: 159* Prints a message to stderr and syslog. 160***********************************************************************/ 161void 162printErr(char const *str) 163{ 164 fprintf(stderr, "pppoe: %s\n", str); 165 syslog(LOG_ERR, "%s", str); 166} 167 168 169/********************************************************************** 170*%FUNCTION: strDup 171*%ARGUMENTS: 172* str -- string to copy 173*%RETURNS: 174* A malloc'd copy of str. Exits if malloc fails. 175***********************************************************************/ 176char * 177strDup(char const *str) 178{ 179 char *copy = malloc(strlen(str)+1); 180 if (!copy) { 181 rp_fatal("strdup failed"); 182 } 183 strcpy(copy, str); 184 return copy; 185} 186 187/********************************************************************** 188*%FUNCTION: computeTCPChecksum 189*%ARGUMENTS: 190* ipHdr -- pointer to IP header 191* tcpHdr -- pointer to TCP header 192*%RETURNS: 193* The computed TCP checksum 194***********************************************************************/ 195UINT16_t 196computeTCPChecksum(unsigned char *ipHdr, unsigned char *tcpHdr) 197{ 198 UINT32_t sum = 0; 199 UINT16_t count = ipHdr[2] * 256 + ipHdr[3]; 200 unsigned char *addr = tcpHdr; 201 unsigned char pseudoHeader[12]; 202 203 /* Count number of bytes in TCP header and data */ 204 count -= (ipHdr[0] & 0x0F) * 4; 205 206 memcpy(pseudoHeader, ipHdr+12, 8); 207 pseudoHeader[8] = 0; 208 pseudoHeader[9] = ipHdr[9]; 209 pseudoHeader[10] = (count >> 8) & 0xFF; 210 pseudoHeader[11] = (count & 0xFF); 211 212 /* Checksum the pseudo-header */ 213 sum += * (UINT16_t *) pseudoHeader; 214 sum += * ((UINT16_t *) (pseudoHeader+2)); 215 sum += * ((UINT16_t *) (pseudoHeader+4)); 216 sum += * ((UINT16_t *) (pseudoHeader+6)); 217 sum += * ((UINT16_t *) (pseudoHeader+8)); 218 sum += * ((UINT16_t *) (pseudoHeader+10)); 219 220 /* Checksum the TCP header and data */ 221 while (count > 1) { 222 sum += * (UINT16_t *) addr; 223 addr += 2; 224 count -= 2; 225 } 226 if (count > 0) { 227 sum += *addr; 228 } 229 230 while(sum >> 16) { 231 sum = (sum & 0xffff) + (sum >> 16); 232 } 233 return (UINT16_t) (~sum & 0xFFFF); 234} 235 236/********************************************************************** 237*%FUNCTION: clampMSS 238*%ARGUMENTS: 239* packet -- PPPoE session packet 240* dir -- either "incoming" or "outgoing" 241* clampMss -- clamp value 242*%RETURNS: 243* Nothing 244*%DESCRIPTION: 245* Clamps MSS option if TCP SYN flag is set. 246***********************************************************************/ 247void 248clampMSS(PPPoEPacket *packet, char const *dir, int clampMss) 249{ 250 unsigned char *tcpHdr; 251 unsigned char *ipHdr; 252 unsigned char *opt; 253 unsigned char *endHdr; 254 unsigned char *mssopt = NULL; 255 UINT16_t csum; 256 257 int len, minlen; 258 259 /* check PPP protocol type */ 260 if (packet->payload[0] & 0x01) { 261 /* 8 bit protocol type */ 262 263 /* Is it IPv4? */ 264 if (packet->payload[0] != 0x21) { 265 /* Nope, ignore it */ 266 return; 267 } 268 269 ipHdr = packet->payload + 1; 270 minlen = 41; 271 } else { 272 /* 16 bit protocol type */ 273 274 /* Is it IPv4? */ 275 if (packet->payload[0] != 0x00 || 276 packet->payload[1] != 0x21) { 277 /* Nope, ignore it */ 278 return; 279 } 280 281 ipHdr = packet->payload + 2; 282 minlen = 42; 283 } 284 285 /* Is it too short? */ 286 len = (int) ntohs(packet->length); 287 if (len < minlen) { 288 /* 20 byte IP header; 20 byte TCP header; at least 1 or 2 byte PPP protocol */ 289 return; 290 } 291 292 /* Verify once more that it's IPv4 */ 293 if ((ipHdr[0] & 0xF0) != 0x40) { 294 return; 295 } 296 297 /* Is it a fragment that's not at the beginning of the packet? */ 298 if ((ipHdr[6] & 0x1F) || ipHdr[7]) { 299 /* Yup, don't touch! */ 300 return; 301 } 302 /* Is it TCP? */ 303 if (ipHdr[9] != 0x06) { 304 return; 305 } 306 307 /* Get start of TCP header */ 308 tcpHdr = ipHdr + (ipHdr[0] & 0x0F) * 4; 309 310 /* Is SYN set? */ 311 if (!(tcpHdr[13] & 0x02)) { 312 return; 313 } 314 315 /* Compute and verify TCP checksum -- do not touch a packet with a bad 316 checksum */ 317 csum = computeTCPChecksum(ipHdr, tcpHdr); 318 if (csum) { 319 syslog(LOG_ERR, "Bad TCP checksum %x", (unsigned int) csum); 320 321 /* Upper layers will drop it */ 322 return; 323 } 324 325 /* Look for existing MSS option */ 326 endHdr = tcpHdr + ((tcpHdr[12] & 0xF0) >> 2); 327 opt = tcpHdr + 20; 328 while (opt < endHdr) { 329 if (!*opt) break; /* End of options */ 330 switch(*opt) { 331 case 1: 332 opt++; 333 break; 334 335 case 2: 336 if (opt[1] != 4) { 337 /* Something fishy about MSS option length. */ 338 syslog(LOG_ERR, 339 "Bogus length for MSS option (%u) from %u.%u.%u.%u", 340 (unsigned int) opt[1], 341 (unsigned int) ipHdr[12], 342 (unsigned int) ipHdr[13], 343 (unsigned int) ipHdr[14], 344 (unsigned int) ipHdr[15]); 345 return; 346 } 347 mssopt = opt; 348 break; 349 default: 350 if (opt[1] < 2) { 351 /* Someone's trying to attack us? */ 352 syslog(LOG_ERR, 353 "Bogus TCP option length (%u) from %u.%u.%u.%u", 354 (unsigned int) opt[1], 355 (unsigned int) ipHdr[12], 356 (unsigned int) ipHdr[13], 357 (unsigned int) ipHdr[14], 358 (unsigned int) ipHdr[15]); 359 return; 360 } 361 opt += (opt[1]); 362 break; 363 } 364 /* Found existing MSS option? */ 365 if (mssopt) break; 366 } 367 368 /* If MSS exists and it's low enough, do nothing */ 369 if (mssopt) { 370 unsigned mss = mssopt[2] * 256 + mssopt[3]; 371 if (mss <= clampMss) { 372 return; 373 } 374 375 mssopt[2] = (((unsigned) clampMss) >> 8) & 0xFF; 376 mssopt[3] = ((unsigned) clampMss) & 0xFF; 377 } else { 378 /* No MSS option. Don't add one; we'll have to use 536. */ 379 return; 380 } 381 382 /* Recompute TCP checksum */ 383 tcpHdr[16] = 0; 384 tcpHdr[17] = 0; 385 csum = computeTCPChecksum(ipHdr, tcpHdr); 386 (* (UINT16_t *) (tcpHdr+16)) = csum; 387} 388 389/*********************************************************************** 390*%FUNCTION: sendPADT 391*%ARGUMENTS: 392* conn -- PPPoE connection 393* msg -- if non-NULL, extra error message to include in PADT packet. 394*%RETURNS: 395* Nothing 396*%DESCRIPTION: 397* Sends a PADT packet 398***********************************************************************/ 399void 400sendPADT(PPPoEConnection *conn, char const *msg) 401{ 402 PPPoEPacket packet; 403 unsigned char *cursor = packet.payload; 404 405 UINT16_t plen = 0; 406 407 /* Do nothing if no session established yet */ 408 if (!conn->session) return; 409 410 /* Do nothing if no discovery socket */ 411 if (conn->discoverySocket < 0) return; 412 413 memcpy(packet.ethHdr.h_dest, conn->peerEth, ETH_ALEN); 414 memcpy(packet.ethHdr.h_source, conn->myEth, ETH_ALEN); 415 416 packet.ethHdr.h_proto = htons(Eth_PPPOE_Discovery); 417 packet.ver = 1; 418 packet.type = 1; 419 packet.code = CODE_PADT; 420 packet.session = conn->session; 421 422 /* Reset Session to zero so there is no possibility of 423 recursive calls to this function by any signal handler */ 424 conn->session = 0; 425 426 /* If we're using Host-Uniq, copy it over */ 427 if (conn->useHostUniq) { 428 PPPoETag hostUniq; 429 pid_t pid = getpid(); 430 hostUniq.type = htons(TAG_HOST_UNIQ); 431 hostUniq.length = htons(sizeof(pid)); 432 memcpy(hostUniq.payload, &pid, sizeof(pid)); 433 memcpy(cursor, &hostUniq, sizeof(pid) + TAG_HDR_SIZE); 434 cursor += sizeof(pid) + TAG_HDR_SIZE; 435 plen += sizeof(pid) + TAG_HDR_SIZE; 436 } 437 438 /* Copy error message */ 439 if (msg) { 440 PPPoETag err; 441 size_t elen = strlen(msg); 442 err.type = htons(TAG_GENERIC_ERROR); 443 err.length = htons(elen); 444 strcpy(err.payload, msg); 445 memcpy(cursor, &err, elen + TAG_HDR_SIZE); 446 cursor += elen + TAG_HDR_SIZE; 447 plen += elen + TAG_HDR_SIZE; 448 } 449 450 /* Copy cookie and relay-ID if needed */ 451 if (conn->cookie.type) { 452 CHECK_ROOM(cursor, packet.payload, 453 ntohs(conn->cookie.length) + TAG_HDR_SIZE); 454 memcpy(cursor, &conn->cookie, ntohs(conn->cookie.length) + TAG_HDR_SIZE); 455 cursor += ntohs(conn->cookie.length) + TAG_HDR_SIZE; 456 plen += ntohs(conn->cookie.length) + TAG_HDR_SIZE; 457 } 458 459 if (conn->relayId.type) { 460 CHECK_ROOM(cursor, packet.payload, 461 ntohs(conn->relayId.length) + TAG_HDR_SIZE); 462 memcpy(cursor, &conn->relayId, ntohs(conn->relayId.length) + TAG_HDR_SIZE); 463 cursor += ntohs(conn->relayId.length) + TAG_HDR_SIZE; 464 plen += ntohs(conn->relayId.length) + TAG_HDR_SIZE; 465 } 466 467 packet.length = htons(plen); 468 sendPacket(conn, conn->discoverySocket, &packet, (int) (plen + HDR_SIZE)); 469 if (conn->debugFile) { 470 dumpPacket(conn->debugFile, &packet, "SENT"); 471 fprintf(conn->debugFile, "\n"); 472 fflush(conn->debugFile); 473 } 474 syslog(LOG_INFO,"Sent PADT"); 475} 476 477/*********************************************************************** 478*%FUNCTION: sendPADTf 479*%ARGUMENTS: 480* conn -- PPPoE connection 481* msg -- printf-style format string 482* args -- arguments for msg 483*%RETURNS: 484* Nothing 485*%DESCRIPTION: 486* Sends a PADT packet with a formatted message 487***********************************************************************/ 488void 489sendPADTf(PPPoEConnection *conn, char const *fmt, ...) 490{ 491 char msg[512]; 492 va_list ap; 493 494 va_start(ap, fmt); 495 vsnprintf(msg, sizeof(msg), fmt, ap); 496 va_end(ap); 497 msg[511] = 0; 498 499 sendPADT(conn, msg); 500} 501 502/********************************************************************** 503*%FUNCTION: parseLogErrs 504*%ARGUMENTS: 505* type -- tag type 506* len -- tag length 507* data -- tag data 508* extra -- extra user data 509*%RETURNS: 510* Nothing 511*%DESCRIPTION: 512* Picks error tags out of a packet and logs them. 513***********************************************************************/ 514void 515parseLogErrs(UINT16_t type, UINT16_t len, unsigned char *data, 516 void *extra) 517{ 518 switch(type) { 519 case TAG_SERVICE_NAME_ERROR: 520 syslog(LOG_ERR, "PADT: Service-Name-Error: %.*s", (int) len, data); 521 fprintf(stderr, "PADT: Service-Name-Error: %.*s\n", (int) len, data); 522 break; 523 case TAG_AC_SYSTEM_ERROR: 524 syslog(LOG_ERR, "PADT: System-Error: %.*s", (int) len, data); 525 fprintf(stderr, "PADT: System-Error: %.*s\n", (int) len, data); 526 break; 527 case TAG_GENERIC_ERROR: 528 syslog(LOG_ERR, "PADT: Generic-Error: %.*s", (int) len, data); 529 fprintf(stderr, "PADT: Generic-Error: %.*s\n", (int) len, data); 530 break; 531 } 532} 533