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 16 static 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 ***********************************************************************/ 44 int 45 parsePacket(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 ***********************************************************************/ 95 void 96 sendPADT(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 */ 166 void 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 277 void 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