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