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.3 2008/06/09 08:34:23 paulus Exp "; 18 19#define _GNU_SOURCE 1 20#include "pppoe.h" 21#include "pppd/pppd.h" 22 23#include <string.h> 24#include <errno.h> 25#include <stdlib.h> 26#include <syslog.h> /* for LOG_DEBUG */ 27 28#ifdef HAVE_UNISTD_H 29#include <unistd.h> 30#endif 31 32/********************************************************************** 33*%FUNCTION: parsePacket 34*%ARGUMENTS: 35* packet -- the PPPoE discovery packet to parse 36* func -- function called for each tag in the packet 37* extra -- an opaque data pointer supplied to parsing function 38*%RETURNS: 39* 0 if everything went well; -1 if there was an error 40*%DESCRIPTION: 41* Parses a PPPoE discovery packet, calling "func" for each tag in the packet. 42* "func" is passed the additional argument "extra". 43***********************************************************************/ 44int 45parsePacket(PPPoEPacket *packet, ParseFunc *func, void *extra) 46{ 47 UINT16_t len = ntohs(packet->length); 48 unsigned char *curTag; 49 UINT16_t tagType, tagLen; 50 51 if (PPPOE_VER(packet->vertype) != 1) { 52 error("Invalid PPPoE version (%d)", PPPOE_VER(packet->vertype)); 53 return -1; 54 } 55 if (PPPOE_TYPE(packet->vertype) != 1) { 56 error("Invalid PPPoE type (%d)", PPPOE_TYPE(packet->vertype)); 57 return -1; 58 } 59 60 /* Do some sanity checks on packet */ 61 if (len > ETH_JUMBO_LEN - PPPOE_OVERHEAD) { /* 6-byte overhead for PPPoE header */ 62 error("Invalid PPPoE packet length (%u)", len); 63 return -1; 64 } 65 66 /* Step through the tags */ 67 curTag = packet->payload; 68 while (curTag - packet->payload + TAG_HDR_SIZE <= len) { 69 /* Alignment is not guaranteed, so do this by hand... */ 70 tagType = (curTag[0] << 8) + curTag[1]; 71 tagLen = (curTag[2] << 8) + curTag[3]; 72 if (tagType == TAG_END_OF_LIST) { 73 return 0; 74 } 75 if ((curTag - packet->payload) + tagLen + TAG_HDR_SIZE > len) { 76 error("Invalid PPPoE tag length (%u)", tagLen); 77 return -1; 78 } 79 func(tagType, tagLen, curTag+TAG_HDR_SIZE, extra); 80 curTag = curTag + TAG_HDR_SIZE + tagLen; 81 } 82 return 0; 83} 84 85/*********************************************************************** 86*%FUNCTION: sendPADT 87*%ARGUMENTS: 88* conn -- PPPoE connection 89* msg -- if non-NULL, extra error message to include in PADT packet. 90*%RETURNS: 91* Nothing 92*%DESCRIPTION: 93* Sends a PADT packet 94***********************************************************************/ 95void 96sendPADT(PPPoEConnection *conn, char const *msg) 97{ 98 PPPoEPacket packet; 99 unsigned char *cursor = packet.payload; 100 101 UINT16_t plen = 0; 102 103 /* Do nothing if no session established yet */ 104 if (!conn->session) return; 105 106 /* Do nothing if no discovery socket */ 107 if (conn->discoverySocket < 0) return; 108 109 memcpy(packet.ethHdr.h_dest, conn->peerEth, ETH_ALEN); 110 memcpy(packet.ethHdr.h_source, conn->myEth, ETH_ALEN); 111 112 packet.ethHdr.h_proto = htons(Eth_PPPOE_Discovery); 113 packet.vertype = PPPOE_VER_TYPE(1, 1); 114 packet.code = CODE_PADT; 115 packet.session = conn->session; 116 117 /* Reset Session to zero so there is no possibility of 118 recursive calls to this function by any signal handler */ 119 conn->session = 0; 120 121 /* If we're using Host-Uniq, copy it over */ 122 if (conn->hostUniq.length) { 123 int len = ntohs(conn->hostUniq.length); 124 memcpy(cursor, &conn->hostUniq, len + TAG_HDR_SIZE); 125 cursor += len + TAG_HDR_SIZE; 126 plen += len + TAG_HDR_SIZE; 127 } 128 129 /* Copy error message */ 130 if (msg) { 131 PPPoETag err; 132 size_t elen = strlen(msg); 133 err.type = htons(TAG_GENERIC_ERROR); 134 err.length = htons(elen); 135 strcpy(err.payload, msg); 136 memcpy(cursor, &err, elen + TAG_HDR_SIZE); 137 cursor += elen + TAG_HDR_SIZE; 138 plen += elen + TAG_HDR_SIZE; 139 } 140 141 /* Copy cookie and relay-ID if needed */ 142 if (conn->cookie.type) { 143 CHECK_ROOM(cursor, packet.payload, 144 ntohs(conn->cookie.length) + TAG_HDR_SIZE); 145 memcpy(cursor, &conn->cookie, ntohs(conn->cookie.length) + TAG_HDR_SIZE); 146 cursor += ntohs(conn->cookie.length) + TAG_HDR_SIZE; 147 plen += ntohs(conn->cookie.length) + TAG_HDR_SIZE; 148 } 149 150 if (conn->relayId.type) { 151 CHECK_ROOM(cursor, packet.payload, 152 ntohs(conn->relayId.length) + TAG_HDR_SIZE); 153 memcpy(cursor, &conn->relayId, ntohs(conn->relayId.length) + TAG_HDR_SIZE); 154 cursor += ntohs(conn->relayId.length) + TAG_HDR_SIZE; 155 plen += ntohs(conn->relayId.length) + TAG_HDR_SIZE; 156 } 157 158 packet.length = htons(plen); 159 sendPacket(conn, conn->discoverySocket, &packet, (int) (plen + HDR_SIZE)); 160 info("Sent PADT"); 161} 162 163#define EH(x) (x)[0], (x)[1], (x)[2], (x)[3], (x)[4], (x)[5] 164 165/* Print out a PPPOE packet for debugging */ 166void pppoe_printpkt(PPPoEPacket *packet, 167 void (*printer)(void *, char *, ...), void *arg) 168{ 169 int len = ntohs(packet->length); 170 int i, tag, tlen, text; 171 172 switch (ntohs(packet->ethHdr.h_proto)) { 173 case ETH_PPPOE_DISCOVERY: 174 printer(arg, "PPPOE Discovery V%dT%d ", PPPOE_VER(packet->vertype), 175 PPPOE_TYPE(packet->vertype)); 176 switch (packet->code) { 177 case CODE_PADI: 178 printer(arg, "PADI"); 179 break; 180 case CODE_PADO: 181 printer(arg, "PADO"); 182 break; 183 case CODE_PADR: 184 printer(arg, "PADR"); 185 break; 186 case CODE_PADS: 187 printer(arg, "PADS"); 188 break; 189 case CODE_PADT: 190 printer(arg, "PADT"); 191 break; 192 default: 193 printer(arg, "unknown code %x", packet->code); 194 } 195 printer(arg, " session 0x%x length %d\n", ntohs(packet->session), len); 196 break; 197 case ETH_PPPOE_SESSION: 198 printer(arg, "PPPOE Session V%dT%d", PPPOE_VER(packet->vertype), 199 PPPOE_TYPE(packet->vertype)); 200 printer(arg, " code 0x%x session 0x%x length %d\n", packet->code, 201 ntohs(packet->session), len); 202 break; 203 default: 204 printer(arg, "Unknown ethernet frame with proto = 0x%x\n", 205 ntohs(packet->ethHdr.h_proto)); 206 } 207 208 printer(arg, " dst %02x:%02x:%02x:%02x:%02x:%02x ", EH(packet->ethHdr.h_dest)); 209 printer(arg, " src %02x:%02x:%02x:%02x:%02x:%02x\n", EH(packet->ethHdr.h_source)); 210 if (ntohs(packet->ethHdr.h_proto) != ETH_PPPOE_DISCOVERY) 211 return; 212 213 for (i = 0; i + TAG_HDR_SIZE <= len; i += tlen) { 214 tag = (packet->payload[i] << 8) + packet->payload[i+1]; 215 tlen = (packet->payload[i+2] << 8) + packet->payload[i+3]; 216 if (i + tlen + TAG_HDR_SIZE > len) 217 break; 218 text = 0; 219 i += TAG_HDR_SIZE; 220 printer(arg, " ["); 221 switch (tag) { 222 case TAG_END_OF_LIST: 223 printer(arg, "end-of-list"); 224 break; 225 case TAG_SERVICE_NAME: 226 printer(arg, "service-name"); 227 text = 1; 228 break; 229 case TAG_AC_NAME: 230 printer(arg, "AC-name"); 231 text = 1; 232 break; 233 case TAG_HOST_UNIQ: 234 printer(arg, "host-uniq"); 235 break; 236 case TAG_AC_COOKIE: 237 printer(arg, "AC-cookie"); 238 break; 239 case TAG_VENDOR_SPECIFIC: 240 printer(arg, "vendor-specific"); 241 break; 242 case TAG_RELAY_SESSION_ID: 243 printer(arg, "relay-session-id"); 244 break; 245 case TAG_PPP_MAX_PAYLOAD: 246 printer(arg, "PPP-max-payload"); 247 break; 248 case TAG_SERVICE_NAME_ERROR: 249 printer(arg, "service-name-error"); 250 text = 1; 251 break; 252 case TAG_AC_SYSTEM_ERROR: 253 printer(arg, "AC-system-error"); 254 text = 1; 255 break; 256 case TAG_GENERIC_ERROR: 257 printer(arg, "generic-error"); 258 text = 1; 259 break; 260 default: 261 printer(arg, "unknown tag 0x%x", tag); 262 } 263 if (tlen) { 264 if (text) 265 printer(arg, " %.*v", tlen, &packet->payload[i]); 266 else if (tlen <= 32) 267 printer(arg, " %.*B", tlen, &packet->payload[i]); 268 else 269 printer(arg, " %.32B... (length %d)", 270 &packet->payload[i], tlen); 271 } 272 printer(arg, "]"); 273 } 274 printer(arg, "\n"); 275} 276 277void pppoe_log_packet(const char *prefix, PPPoEPacket *packet) 278{ 279 init_pr_log(prefix, LOG_DEBUG); 280 pppoe_printpkt(packet, pr_log, NULL); 281 end_pr_log(); 282} 283