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 < 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->useHostUniq) { 123 PPPoETag hostUniq; 124 pid_t pid = getpid(); 125 hostUniq.type = htons(TAG_HOST_UNIQ); 126 hostUniq.length = htons(sizeof(pid)); 127 memcpy(hostUniq.payload, &pid, sizeof(pid)); 128 memcpy(cursor, &hostUniq, sizeof(pid) + TAG_HDR_SIZE); 129 cursor += sizeof(pid) + TAG_HDR_SIZE; 130 plen += sizeof(pid) + TAG_HDR_SIZE; 131 } 132 133 /* Copy error message */ 134 if (msg) { 135 PPPoETag err; 136 size_t elen = strlen(msg); 137 err.type = htons(TAG_GENERIC_ERROR); 138 err.length = htons(elen); 139 strcpy(err.payload, msg); 140 memcpy(cursor, &err, elen + TAG_HDR_SIZE); 141 cursor += elen + TAG_HDR_SIZE; 142 plen += elen + TAG_HDR_SIZE; 143 } 144 145 /* Copy cookie and relay-ID if needed */ 146 if (conn->cookie.type) { 147 CHECK_ROOM(cursor, packet.payload, 148 ntohs(conn->cookie.length) + TAG_HDR_SIZE); 149 memcpy(cursor, &conn->cookie, ntohs(conn->cookie.length) + TAG_HDR_SIZE); 150 cursor += ntohs(conn->cookie.length) + TAG_HDR_SIZE; 151 plen += ntohs(conn->cookie.length) + TAG_HDR_SIZE; 152 } 153 154 if (conn->relayId.type) { 155 CHECK_ROOM(cursor, packet.payload, 156 ntohs(conn->relayId.length) + TAG_HDR_SIZE); 157 memcpy(cursor, &conn->relayId, ntohs(conn->relayId.length) + TAG_HDR_SIZE); 158 cursor += ntohs(conn->relayId.length) + TAG_HDR_SIZE; 159 plen += ntohs(conn->relayId.length) + TAG_HDR_SIZE; 160 } 161 162 packet.length = htons(plen); 163 sendPacket(conn, conn->discoverySocket, &packet, (int) (plen + HDR_SIZE)); 164 info("Sent PADT"); 165 } 166 167 #define EH(x) (x)[0], (x)[1], (x)[2], (x)[3], (x)[4], (x)[5] 168 169 /* Print out a PPPOE packet for debugging */ 170 void pppoe_printpkt(PPPoEPacket *packet, 171 void (*printer)(void *, char *, ...), void *arg) 172 { 173 int len = ntohs(packet->length); 174 int i, tag, tlen, text; 175 176 switch (ntohs(packet->ethHdr.h_proto)) { 177 case ETH_PPPOE_DISCOVERY: 178 printer(arg, "PPPOE Discovery V%dT%d ", PPPOE_VER(packet->vertype), 179 PPPOE_TYPE(packet->vertype)); 180 switch (packet->code) { 181 case CODE_PADI: 182 printer(arg, "PADI"); 183 break; 184 case CODE_PADO: 185 printer(arg, "PADO"); 186 break; 187 case CODE_PADR: 188 printer(arg, "PADR"); 189 break; 190 case CODE_PADS: 191 printer(arg, "PADS"); 192 break; 193 case CODE_PADT: 194 printer(arg, "PADT"); 195 break; 196 default: 197 printer(arg, "unknown code %x", packet->code); 198 } 199 printer(arg, " session 0x%x length %d\n", ntohs(packet->session), len); 200 break; 201 case ETH_PPPOE_SESSION: 202 printer(arg, "PPPOE Session V%dT%d", PPPOE_VER(packet->vertype), 203 PPPOE_TYPE(packet->vertype)); 204 printer(arg, " code 0x%x session 0x%x length %d\n", packet->code, 205 ntohs(packet->session), len); 206 break; 207 default: 208 printer(arg, "Unknown ethernet frame with proto = 0x%x\n", 209 ntohs(packet->ethHdr.h_proto)); 210 } 211 212 printer(arg, " dst %02x:%02x:%02x:%02x:%02x:%02x ", EH(packet->ethHdr.h_dest)); 213 printer(arg, " src %02x:%02x:%02x:%02x:%02x:%02x\n", EH(packet->ethHdr.h_source)); 214 if (ntohs(packet->ethHdr.h_proto) != ETH_PPPOE_DISCOVERY) 215 return; 216 217 for (i = 0; i + TAG_HDR_SIZE <= len; i += tlen) { 218 tag = (packet->payload[i] << 8) + packet->payload[i+1]; 219 tlen = (packet->payload[i+2] << 8) + packet->payload[i+3]; 220 if (i + tlen + TAG_HDR_SIZE > len) 221 break; 222 text = 0; 223 i += TAG_HDR_SIZE; 224 printer(arg, " ["); 225 switch (tag) { 226 case TAG_END_OF_LIST: 227 printer(arg, "end-of-list"); 228 break; 229 case TAG_SERVICE_NAME: 230 printer(arg, "service-name"); 231 text = 1; 232 break; 233 case TAG_AC_NAME: 234 printer(arg, "AC-name"); 235 text = 1; 236 break; 237 case TAG_HOST_UNIQ: 238 printer(arg, "host-uniq"); 239 break; 240 case TAG_AC_COOKIE: 241 printer(arg, "AC-cookie"); 242 break; 243 case TAG_VENDOR_SPECIFIC: 244 printer(arg, "vendor-specific"); 245 break; 246 case TAG_RELAY_SESSION_ID: 247 printer(arg, "relay-session-id"); 248 break; 249 case TAG_PPP_MAX_PAYLOAD: 250 printer(arg, "PPP-max-payload"); 251 break; 252 case TAG_SERVICE_NAME_ERROR: 253 printer(arg, "service-name-error"); 254 text = 1; 255 break; 256 case TAG_AC_SYSTEM_ERROR: 257 printer(arg, "AC-system-error"); 258 text = 1; 259 break; 260 case TAG_GENERIC_ERROR: 261 printer(arg, "generic-error"); 262 text = 1; 263 break; 264 default: 265 printer(arg, "unknown tag 0x%x", tag); 266 } 267 if (tlen) { 268 if (text) 269 printer(arg, " %.*v", tlen, &packet->payload[i]); 270 else if (tlen <= 32) 271 printer(arg, " %.*B", tlen, &packet->payload[i]); 272 else 273 printer(arg, " %.32B... (length %d)", 274 &packet->payload[i], tlen); 275 } 276 printer(arg, "]"); 277 } 278 printer(arg, "\n"); 279 } 280 281 void pppoe_log_packet(const char *prefix, PPPoEPacket *packet) 282 { 283 init_pr_log(prefix, LOG_DEBUG); 284 pppoe_printpkt(packet, pr_log, NULL); 285 end_pr_log(); 286 } 287