1 1.1 christos /*********************************************************************** 2 1.1 christos * 3 1.1 christos * common.c 4 1.1 christos * 5 1.1 christos * Implementation of user-space PPPoE redirector for Linux. 6 1.1 christos * 7 1.1 christos * Common functions used by PPPoE client and server 8 1.1 christos * 9 1.1 christos * Copyright (C) 2000 by Roaring Penguin Software Inc. 10 1.1 christos * 11 1.1 christos * This program may be distributed according to the terms of the GNU 12 1.1 christos * General Public License, version 2 or (at your option) any later version. 13 1.1 christos * 14 1.1 christos ***********************************************************************/ 15 1.1 christos 16 1.1.1.2 christos #ifdef HAVE_CONFIG_H 17 1.1.1.2 christos #include "config.h" 18 1.1.1.2 christos #endif 19 1.1 christos 20 1.1 christos #define _GNU_SOURCE 1 21 1.1 christos #include "pppoe.h" 22 1.1.1.2 christos #include <pppd/pppd.h> 23 1.1 christos 24 1.1 christos #include <string.h> 25 1.1 christos #include <errno.h> 26 1.1 christos #include <stdlib.h> 27 1.1 christos #include <syslog.h> /* for LOG_DEBUG */ 28 1.1.1.2 christos #include <ctype.h> 29 1.1 christos 30 1.1 christos #ifdef HAVE_UNISTD_H 31 1.1 christos #include <unistd.h> 32 1.1 christos #endif 33 1.1 christos 34 1.1 christos /********************************************************************** 35 1.1 christos *%FUNCTION: parsePacket 36 1.1 christos *%ARGUMENTS: 37 1.1 christos * packet -- the PPPoE discovery packet to parse 38 1.1 christos * func -- function called for each tag in the packet 39 1.1 christos * extra -- an opaque data pointer supplied to parsing function 40 1.1 christos *%RETURNS: 41 1.1 christos * 0 if everything went well; -1 if there was an error 42 1.1 christos *%DESCRIPTION: 43 1.1 christos * Parses a PPPoE discovery packet, calling "func" for each tag in the packet. 44 1.1 christos * "func" is passed the additional argument "extra". 45 1.1 christos ***********************************************************************/ 46 1.1 christos int 47 1.1 christos parsePacket(PPPoEPacket *packet, ParseFunc *func, void *extra) 48 1.1 christos { 49 1.1 christos UINT16_t len = ntohs(packet->length); 50 1.1 christos unsigned char *curTag; 51 1.1 christos UINT16_t tagType, tagLen; 52 1.1 christos 53 1.1 christos if (PPPOE_VER(packet->vertype) != 1) { 54 1.1 christos error("Invalid PPPoE version (%d)", PPPOE_VER(packet->vertype)); 55 1.1 christos return -1; 56 1.1 christos } 57 1.1 christos if (PPPOE_TYPE(packet->vertype) != 1) { 58 1.1 christos error("Invalid PPPoE type (%d)", PPPOE_TYPE(packet->vertype)); 59 1.1 christos return -1; 60 1.1 christos } 61 1.1 christos 62 1.1 christos /* Do some sanity checks on packet */ 63 1.1 christos if (len > ETH_JUMBO_LEN - PPPOE_OVERHEAD) { /* 6-byte overhead for PPPoE header */ 64 1.1 christos error("Invalid PPPoE packet length (%u)", len); 65 1.1 christos return -1; 66 1.1 christos } 67 1.1 christos 68 1.1 christos /* Step through the tags */ 69 1.1 christos curTag = packet->payload; 70 1.1 christos while (curTag - packet->payload + TAG_HDR_SIZE <= len) { 71 1.1 christos /* Alignment is not guaranteed, so do this by hand... */ 72 1.1 christos tagType = (curTag[0] << 8) + curTag[1]; 73 1.1 christos tagLen = (curTag[2] << 8) + curTag[3]; 74 1.1 christos if (tagType == TAG_END_OF_LIST) { 75 1.1 christos return 0; 76 1.1 christos } 77 1.1 christos if ((curTag - packet->payload) + tagLen + TAG_HDR_SIZE > len) { 78 1.1 christos error("Invalid PPPoE tag length (%u)", tagLen); 79 1.1 christos return -1; 80 1.1 christos } 81 1.1 christos func(tagType, tagLen, curTag+TAG_HDR_SIZE, extra); 82 1.1 christos curTag = curTag + TAG_HDR_SIZE + tagLen; 83 1.1 christos } 84 1.1 christos return 0; 85 1.1 christos } 86 1.1 christos 87 1.1 christos /*********************************************************************** 88 1.1 christos *%FUNCTION: sendPADT 89 1.1 christos *%ARGUMENTS: 90 1.1 christos * conn -- PPPoE connection 91 1.1 christos * msg -- if non-NULL, extra error message to include in PADT packet. 92 1.1 christos *%RETURNS: 93 1.1 christos * Nothing 94 1.1 christos *%DESCRIPTION: 95 1.1 christos * Sends a PADT packet 96 1.1 christos ***********************************************************************/ 97 1.1 christos void 98 1.1 christos sendPADT(PPPoEConnection *conn, char const *msg) 99 1.1 christos { 100 1.1 christos PPPoEPacket packet; 101 1.1 christos unsigned char *cursor = packet.payload; 102 1.1 christos 103 1.1 christos UINT16_t plen = 0; 104 1.1 christos 105 1.1 christos /* Do nothing if no session established yet */ 106 1.1 christos if (!conn->session) return; 107 1.1 christos 108 1.1 christos /* Do nothing if no discovery socket */ 109 1.1 christos if (conn->discoverySocket < 0) return; 110 1.1 christos 111 1.1 christos memcpy(packet.ethHdr.h_dest, conn->peerEth, ETH_ALEN); 112 1.1 christos memcpy(packet.ethHdr.h_source, conn->myEth, ETH_ALEN); 113 1.1 christos 114 1.1 christos packet.ethHdr.h_proto = htons(Eth_PPPOE_Discovery); 115 1.1 christos packet.vertype = PPPOE_VER_TYPE(1, 1); 116 1.1 christos packet.code = CODE_PADT; 117 1.1 christos packet.session = conn->session; 118 1.1 christos 119 1.1 christos /* Reset Session to zero so there is no possibility of 120 1.1 christos recursive calls to this function by any signal handler */ 121 1.1 christos conn->session = 0; 122 1.1 christos 123 1.1 christos /* If we're using Host-Uniq, copy it over */ 124 1.1 christos if (conn->hostUniq.length) { 125 1.1 christos int len = ntohs(conn->hostUniq.length); 126 1.1 christos memcpy(cursor, &conn->hostUniq, len + TAG_HDR_SIZE); 127 1.1 christos cursor += len + TAG_HDR_SIZE; 128 1.1 christos plen += len + TAG_HDR_SIZE; 129 1.1 christos } 130 1.1 christos 131 1.1 christos /* Copy error message */ 132 1.1 christos if (msg) { 133 1.1 christos PPPoETag err; 134 1.1 christos size_t elen = strlen(msg); 135 1.1 christos err.type = htons(TAG_GENERIC_ERROR); 136 1.1 christos err.length = htons(elen); 137 1.1.1.2 christos strcpy((char*) err.payload, msg); 138 1.1 christos memcpy(cursor, &err, elen + TAG_HDR_SIZE); 139 1.1 christos cursor += elen + TAG_HDR_SIZE; 140 1.1 christos plen += elen + TAG_HDR_SIZE; 141 1.1 christos } 142 1.1 christos 143 1.1 christos /* Copy cookie and relay-ID if needed */ 144 1.1 christos if (conn->cookie.type) { 145 1.1 christos CHECK_ROOM(cursor, packet.payload, 146 1.1 christos ntohs(conn->cookie.length) + TAG_HDR_SIZE); 147 1.1 christos memcpy(cursor, &conn->cookie, ntohs(conn->cookie.length) + TAG_HDR_SIZE); 148 1.1 christos cursor += ntohs(conn->cookie.length) + TAG_HDR_SIZE; 149 1.1 christos plen += ntohs(conn->cookie.length) + TAG_HDR_SIZE; 150 1.1 christos } 151 1.1 christos 152 1.1 christos if (conn->relayId.type) { 153 1.1 christos CHECK_ROOM(cursor, packet.payload, 154 1.1 christos ntohs(conn->relayId.length) + TAG_HDR_SIZE); 155 1.1 christos memcpy(cursor, &conn->relayId, ntohs(conn->relayId.length) + TAG_HDR_SIZE); 156 1.1 christos cursor += ntohs(conn->relayId.length) + TAG_HDR_SIZE; 157 1.1 christos plen += ntohs(conn->relayId.length) + TAG_HDR_SIZE; 158 1.1 christos } 159 1.1 christos 160 1.1 christos packet.length = htons(plen); 161 1.1 christos sendPacket(conn, conn->discoverySocket, &packet, (int) (plen + HDR_SIZE)); 162 1.1 christos info("Sent PADT"); 163 1.1 christos } 164 1.1 christos 165 1.1.1.2 christos static void 166 1.1.1.2 christos pppoe_printpkt_hex(void (*printer)(void *, char *, ...), void *arg, unsigned char const *buf, int len) 167 1.1.1.2 christos { 168 1.1.1.2 christos int i; 169 1.1.1.2 christos int base; 170 1.1.1.2 christos 171 1.1.1.2 christos /* do NOT dump PAP packets */ 172 1.1.1.2 christos if (len >= 2 && buf[0] == 0xC0 && buf[1] == 0x23) { 173 1.1.1.2 christos printer(arg, "(PAP Authentication Frame -- Contents not dumped)\n"); 174 1.1.1.2 christos return; 175 1.1.1.2 christos } 176 1.1.1.2 christos 177 1.1.1.2 christos for (base=0; base<len; base += 16) { 178 1.1.1.2 christos for (i=base; i<base+16; i++) { 179 1.1.1.2 christos if (i < len) { 180 1.1.1.2 christos printer(arg, "%02x ", (unsigned) buf[i]); 181 1.1.1.2 christos } else { 182 1.1.1.2 christos printer(arg, " "); 183 1.1.1.2 christos } 184 1.1.1.2 christos } 185 1.1.1.2 christos printer(arg, " "); 186 1.1.1.2 christos for (i=base; i<base+16; i++) { 187 1.1.1.2 christos if (i < len) { 188 1.1.1.2 christos if (isprint(buf[i])) { 189 1.1.1.2 christos printer(arg, "%c", buf[i]); 190 1.1.1.2 christos } else { 191 1.1.1.2 christos printer(arg, "."); 192 1.1.1.2 christos } 193 1.1.1.2 christos } else { 194 1.1.1.2 christos break; 195 1.1.1.2 christos } 196 1.1.1.2 christos } 197 1.1.1.2 christos printer(arg, "\n"); 198 1.1.1.2 christos } 199 1.1.1.2 christos } 200 1.1.1.2 christos 201 1.1 christos #define EH(x) (x)[0], (x)[1], (x)[2], (x)[3], (x)[4], (x)[5] 202 1.1 christos 203 1.1 christos /* Print out a PPPOE packet for debugging */ 204 1.1 christos void pppoe_printpkt(PPPoEPacket *packet, 205 1.1 christos void (*printer)(void *, char *, ...), void *arg) 206 1.1 christos { 207 1.1 christos int len = ntohs(packet->length); 208 1.1.1.2 christos int i, j, tag, tlen, text; 209 1.1 christos 210 1.1 christos switch (ntohs(packet->ethHdr.h_proto)) { 211 1.1 christos case ETH_PPPOE_DISCOVERY: 212 1.1 christos printer(arg, "PPPOE Discovery V%dT%d ", PPPOE_VER(packet->vertype), 213 1.1 christos PPPOE_TYPE(packet->vertype)); 214 1.1 christos switch (packet->code) { 215 1.1 christos case CODE_PADI: 216 1.1 christos printer(arg, "PADI"); 217 1.1 christos break; 218 1.1 christos case CODE_PADO: 219 1.1 christos printer(arg, "PADO"); 220 1.1 christos break; 221 1.1 christos case CODE_PADR: 222 1.1 christos printer(arg, "PADR"); 223 1.1 christos break; 224 1.1 christos case CODE_PADS: 225 1.1 christos printer(arg, "PADS"); 226 1.1 christos break; 227 1.1 christos case CODE_PADT: 228 1.1 christos printer(arg, "PADT"); 229 1.1 christos break; 230 1.1 christos default: 231 1.1 christos printer(arg, "unknown code %x", packet->code); 232 1.1 christos } 233 1.1 christos printer(arg, " session 0x%x length %d\n", ntohs(packet->session), len); 234 1.1 christos break; 235 1.1 christos case ETH_PPPOE_SESSION: 236 1.1 christos printer(arg, "PPPOE Session V%dT%d", PPPOE_VER(packet->vertype), 237 1.1 christos PPPOE_TYPE(packet->vertype)); 238 1.1 christos printer(arg, " code 0x%x session 0x%x length %d\n", packet->code, 239 1.1 christos ntohs(packet->session), len); 240 1.1 christos break; 241 1.1 christos default: 242 1.1 christos printer(arg, "Unknown ethernet frame with proto = 0x%x\n", 243 1.1 christos ntohs(packet->ethHdr.h_proto)); 244 1.1 christos } 245 1.1 christos 246 1.1 christos printer(arg, " dst %02x:%02x:%02x:%02x:%02x:%02x ", EH(packet->ethHdr.h_dest)); 247 1.1 christos printer(arg, " src %02x:%02x:%02x:%02x:%02x:%02x\n", EH(packet->ethHdr.h_source)); 248 1.1.1.2 christos if (pppoe_verbose >= 2) 249 1.1.1.2 christos pppoe_printpkt_hex(printer, arg, packet->payload, ntohs(packet->length)); 250 1.1 christos if (ntohs(packet->ethHdr.h_proto) != ETH_PPPOE_DISCOVERY) 251 1.1 christos return; 252 1.1 christos 253 1.1 christos for (i = 0; i + TAG_HDR_SIZE <= len; i += tlen) { 254 1.1 christos tag = (packet->payload[i] << 8) + packet->payload[i+1]; 255 1.1 christos tlen = (packet->payload[i+2] << 8) + packet->payload[i+3]; 256 1.1 christos if (i + tlen + TAG_HDR_SIZE > len) 257 1.1 christos break; 258 1.1 christos text = 0; 259 1.1 christos i += TAG_HDR_SIZE; 260 1.1 christos printer(arg, " ["); 261 1.1 christos switch (tag) { 262 1.1 christos case TAG_END_OF_LIST: 263 1.1 christos printer(arg, "end-of-list"); 264 1.1 christos break; 265 1.1 christos case TAG_SERVICE_NAME: 266 1.1 christos printer(arg, "service-name"); 267 1.1 christos text = 1; 268 1.1 christos break; 269 1.1 christos case TAG_AC_NAME: 270 1.1 christos printer(arg, "AC-name"); 271 1.1 christos text = 1; 272 1.1 christos break; 273 1.1 christos case TAG_HOST_UNIQ: 274 1.1 christos printer(arg, "host-uniq"); 275 1.1 christos break; 276 1.1 christos case TAG_AC_COOKIE: 277 1.1 christos printer(arg, "AC-cookie"); 278 1.1 christos break; 279 1.1 christos case TAG_VENDOR_SPECIFIC: 280 1.1 christos printer(arg, "vendor-specific"); 281 1.1 christos break; 282 1.1 christos case TAG_RELAY_SESSION_ID: 283 1.1 christos printer(arg, "relay-session-id"); 284 1.1 christos break; 285 1.1 christos case TAG_PPP_MAX_PAYLOAD: 286 1.1 christos printer(arg, "PPP-max-payload"); 287 1.1 christos break; 288 1.1 christos case TAG_SERVICE_NAME_ERROR: 289 1.1 christos printer(arg, "service-name-error"); 290 1.1 christos text = 1; 291 1.1 christos break; 292 1.1 christos case TAG_AC_SYSTEM_ERROR: 293 1.1 christos printer(arg, "AC-system-error"); 294 1.1 christos text = 1; 295 1.1 christos break; 296 1.1 christos case TAG_GENERIC_ERROR: 297 1.1 christos printer(arg, "generic-error"); 298 1.1 christos text = 1; 299 1.1 christos break; 300 1.1 christos default: 301 1.1 christos printer(arg, "unknown tag 0x%x", tag); 302 1.1 christos } 303 1.1 christos if (tlen) { 304 1.1.1.2 christos /* If it is supposed to be text, make sure it's all printing chars */ 305 1.1.1.2 christos if (text) { 306 1.1.1.2 christos for (j = 0; j < tlen; ++j) { 307 1.1.1.2 christos if (!isprint(packet->payload[i+j])) { 308 1.1.1.2 christos text = 0; 309 1.1.1.2 christos break; 310 1.1.1.2 christos } 311 1.1.1.2 christos } 312 1.1.1.2 christos } 313 1.1 christos if (text) 314 1.1.1.2 christos printer(arg, " %.*s", tlen, &packet->payload[i]); 315 1.1.1.2 christos else { 316 1.1.1.2 christos for (j = 0; j < tlen && j < 32; j++) 317 1.1.1.2 christos printer(arg, " %02x", (unsigned) *(&packet->payload[i]+j)); 318 1.1.1.2 christos if (j < tlen) 319 1.1.1.2 christos printer(arg, "... (length %d)", tlen); 320 1.1.1.2 christos } 321 1.1 christos } 322 1.1 christos printer(arg, "]"); 323 1.1 christos } 324 1.1 christos printer(arg, "\n"); 325 1.1 christos } 326 1.1 christos 327 1.1 christos void pppoe_log_packet(const char *prefix, PPPoEPacket *packet) 328 1.1 christos { 329 1.1 christos init_pr_log(prefix, LOG_DEBUG); 330 1.1 christos pppoe_printpkt(packet, pr_log, NULL); 331 1.1 christos end_pr_log(); 332 1.1 christos } 333