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.1.1.1 2008/10/15 03:31:00 james26_jang 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(struct 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: printErr 90*%ARGUMENTS: 91* str -- error message 92*%RETURNS: 93* Nothing 94*%DESCRIPTION: 95* Prints a message to stderr and syslog. 96***********************************************************************/ 97void 98printErr(char const *str) 99{ 100 fprintf(stderr, "pppoe: %s\n", str); 101 syslog(LOG_ERR, "%s", str); 102} 103 104 105/********************************************************************** 106*%FUNCTION: strDup 107*%ARGUMENTS: 108* str -- string to copy 109*%RETURNS: 110* A malloc'd copy of str. Exits if malloc fails. 111***********************************************************************/ 112char * 113strDup(char const *str) 114{ 115 char *copy = malloc(strlen(str)+1); 116 if (!copy) { 117 fatal("strdup failed"); 118 } 119 strcpy(copy, str); 120 return copy; 121} 122 123/********************************************************************** 124*%FUNCTION: computeTCPChecksum 125*%ARGUMENTS: 126* ipHdr -- pointer to IP header 127* tcpHdr -- pointer to TCP header 128*%RETURNS: 129* The computed TCP checksum 130***********************************************************************/ 131UINT16_t 132computeTCPChecksum(unsigned char *ipHdr, unsigned char *tcpHdr) 133{ 134 UINT32_t sum = 0; 135 UINT16_t count = ipHdr[2] * 256 + ipHdr[3]; 136 unsigned char *addr = tcpHdr; 137 unsigned char pseudoHeader[12]; 138 139 /* Count number of bytes in TCP header and data */ 140 count -= (ipHdr[0] & 0x0F) * 4; 141 142 memcpy(pseudoHeader, ipHdr+12, 8); 143 pseudoHeader[8] = 0; 144 pseudoHeader[9] = ipHdr[9]; 145 pseudoHeader[10] = (count >> 8) & 0xFF; 146 pseudoHeader[11] = (count & 0xFF); 147 148 /* Checksum the pseudo-header */ 149 sum += * (UINT16_t *) pseudoHeader; 150 sum += * ((UINT16_t *) (pseudoHeader+2)); 151 sum += * ((UINT16_t *) (pseudoHeader+4)); 152 sum += * ((UINT16_t *) (pseudoHeader+6)); 153 sum += * ((UINT16_t *) (pseudoHeader+8)); 154 sum += * ((UINT16_t *) (pseudoHeader+10)); 155 156 /* Checksum the TCP header and data */ 157 while (count > 1) { 158 sum += * (UINT16_t *) addr; 159 addr += 2; 160 count -= 2; 161 } 162 if (count > 0) { 163 sum += *addr; 164 } 165 166 while(sum >> 16) { 167 sum = (sum & 0xffff) + (sum >> 16); 168 } 169 return (UINT16_t) (~sum & 0xFFFF); 170} 171 172/********************************************************************** 173*%FUNCTION: clampMSS 174*%ARGUMENTS: 175* packet -- PPPoE session packet 176* dir -- either "incoming" or "outgoing" 177* clampMss -- clamp value 178*%RETURNS: 179* Nothing 180*%DESCRIPTION: 181* Clamps MSS option if TCP SYN flag is set. 182***********************************************************************/ 183void 184clampMSS(struct PPPoEPacket *packet, char const *dir, int clampMss) 185{ 186 unsigned char *tcpHdr; 187 unsigned char *ipHdr; 188 unsigned char *opt; 189 unsigned char *endHdr; 190 unsigned char *mssopt = NULL; 191 UINT16_t csum; 192 193 int len; 194 195 /* Is it IPv4? */ 196 if (packet->payload[0] != 0x00 || 197 packet->payload[1] != 0x21) { 198 /* Nope, ignore it */ 199 return; 200 } 201 202 ipHdr = packet->payload + 2; 203 204 /* Is it too short? */ 205 len = (int) ntohs(packet->length); 206 if (len < 42) { 207 /* 20 byte IP header; 20 byte TCP header; 2 byte PPP protocol */ 208 return; 209 } 210 211 /* Verify once more that it's IPv4 */ 212 if ((ipHdr[0] & 0xF0) != 0x40) { 213 return; 214 } 215 216 /* Is it a fragment that's not at the beginning of the packet? */ 217 if ((ipHdr[6] & 0x1F) || ipHdr[7]) { 218 /* Yup, don't touch! */ 219 return; 220 } 221 /* Is it TCP? */ 222 if (ipHdr[9] != 0x06) { 223 return; 224 } 225 226 /* Get start of TCP header */ 227 tcpHdr = ipHdr + (ipHdr[0] & 0x0F) * 4; 228 229 /* Is SYN set? */ 230 if (!(tcpHdr[13] & 0x02)) { 231 return; 232 } 233 234 /* Compute and verify TCP checksum -- do not touch a packet with a bad 235 checksum */ 236 csum = computeTCPChecksum(ipHdr, tcpHdr); 237 if (csum) { 238 syslog(LOG_ERR, "Bad TCP checksum %x", (unsigned int) csum); 239 240 /* Upper layers will drop it */ 241 return; 242 } 243 244 /* Look for existing MSS option */ 245 endHdr = tcpHdr + ((tcpHdr[12] & 0xF0) >> 2); 246 opt = tcpHdr + 20; 247 while (opt < endHdr) { 248 if (!*opt) break; /* End of options */ 249 switch(*opt) { 250 case 1: 251 opt++; 252 break; 253 254 case 2: 255 if (opt[1] != 4) { 256 /* Something fishy about MSS option length. */ 257 syslog(LOG_ERR, 258 "Bogus length for MSS option (%u) from %u.%u.%u.%u", 259 (unsigned int) opt[1], 260 (unsigned int) ipHdr[12], 261 (unsigned int) ipHdr[13], 262 (unsigned int) ipHdr[14], 263 (unsigned int) ipHdr[15]); 264 return; 265 } 266 mssopt = opt; 267 break; 268 default: 269 if (opt[1] < 2) { 270 /* Someone's trying to attack us? */ 271 syslog(LOG_ERR, 272 "Bogus TCP option length (%u) from %u.%u.%u.%u", 273 (unsigned int) opt[1], 274 (unsigned int) ipHdr[12], 275 (unsigned int) ipHdr[13], 276 (unsigned int) ipHdr[14], 277 (unsigned int) ipHdr[15]); 278 return; 279 } 280 opt += (opt[1]); 281 break; 282 } 283 /* Found existing MSS option? */ 284 if (mssopt) break; 285 } 286 287 /* If MSS exists and it's low enough, do nothing */ 288 if (mssopt) { 289 unsigned mss = mssopt[2] * 256 + mssopt[3]; 290 if (mss <= clampMss) { 291 return; 292 } 293 294 mssopt[2] = (((unsigned) clampMss) >> 8) & 0xFF; 295 mssopt[3] = ((unsigned) clampMss) & 0xFF; 296 } else { 297 /* No MSS option. Don't add one; we'll have to use 536. */ 298 return; 299 } 300 301 /* Recompute TCP checksum */ 302 tcpHdr[16] = 0; 303 tcpHdr[17] = 0; 304 csum = computeTCPChecksum(ipHdr, tcpHdr); 305 (* (UINT16_t *) (tcpHdr+16)) = csum; 306} 307