1 /*********************************************************************** 2 * 3 * if.c 4 * 5 * Implementation of user-space PPPoE redirector for Linux. 6 * 7 * Functions for opening a raw socket and reading/writing raw Ethernet frames. 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: if.c,v 1.2 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 #ifdef HAVE_UNISTD_H 24 #include <unistd.h> 25 #endif 26 27 #ifdef HAVE_NETPACKET_PACKET_H 28 #include <netpacket/packet.h> 29 #elif defined(HAVE_LINUX_IF_PACKET_H) 30 #include <linux/if_packet.h> 31 #endif 32 33 #ifdef HAVE_ASM_TYPES_H 34 #include <asm/types.h> 35 #endif 36 37 #ifdef HAVE_SYS_IOCTL_H 38 #include <sys/ioctl.h> 39 #endif 40 41 #include <errno.h> 42 #include <stdlib.h> 43 #include <string.h> 44 45 #ifdef HAVE_NET_IF_ARP_H 46 #include <net/if_arp.h> 47 #endif 48 49 /* Initialize frame types to RFC 2516 values. Some broken peers apparently 50 use different frame types... sigh... */ 51 52 UINT16_t Eth_PPPOE_Discovery = ETH_PPPOE_DISCOVERY; 53 UINT16_t Eth_PPPOE_Session = ETH_PPPOE_SESSION; 54 55 /********************************************************************** 56 *%FUNCTION: etherType 57 *%ARGUMENTS: 58 * packet -- a received PPPoE packet 59 *%RETURNS: 60 * ethernet packet type (see /usr/include/net/ethertypes.h) 61 *%DESCRIPTION: 62 * Checks the ethernet packet header to determine its type. 63 * We should only be receveing DISCOVERY and SESSION types if the BPF 64 * is set up correctly. Logs an error if an unexpected type is received. 65 * Note that the ethernet type names come from "pppoe.h" and the packet 66 * packet structure names use the LINUX dialect to maintain consistency 67 * with the rest of this file. See the BSD section of "pppoe.h" for 68 * translations of the data structure names. 69 ***********************************************************************/ 70 UINT16_t 71 etherType(PPPoEPacket *packet) 72 { 73 UINT16_t type = (UINT16_t) ntohs(packet->ethHdr.h_proto); 74 if (type != Eth_PPPOE_Discovery && type != Eth_PPPOE_Session) { 75 error("Invalid ether type 0x%x", type); 76 } 77 return type; 78 } 79 80 /********************************************************************** 81 *%FUNCTION: openInterface 82 *%ARGUMENTS: 83 * ifname -- name of interface 84 * type -- Ethernet frame type 85 * hwaddr -- if non-NULL, set to the hardware address 86 *%RETURNS: 87 * A raw socket for talking to the Ethernet card. Exits on error. 88 *%DESCRIPTION: 89 * Opens a raw Ethernet socket 90 ***********************************************************************/ 91 int 92 openInterface(char const *ifname, UINT16_t type, unsigned char *hwaddr) 93 { 94 int optval=1; 95 int fd; 96 struct ifreq ifr; 97 int domain, stype; 98 99 #ifdef HAVE_STRUCT_SOCKADDR_LL 100 struct sockaddr_ll sa; 101 #else 102 struct sockaddr sa; 103 #endif 104 105 memset(&sa, 0, sizeof(sa)); 106 107 #ifdef HAVE_STRUCT_SOCKADDR_LL 108 domain = PF_PACKET; 109 stype = SOCK_RAW; 110 #else 111 domain = PF_INET; 112 stype = SOCK_PACKET; 113 #endif 114 115 if ((fd = socket(domain, stype, htons(type))) < 0) { 116 /* Give a more helpful message for the common error case */ 117 if (errno == EPERM) { 118 fatal("Cannot create raw socket -- pppoe must be run as root."); 119 } 120 error("Can't open socket for pppoe: %m"); 121 return -1; 122 } 123 124 if (setsockopt(fd, SOL_SOCKET, SO_BROADCAST, &optval, sizeof(optval)) < 0) { 125 error("Can't set socket options for pppoe: %m"); 126 close(fd); 127 return -1; 128 } 129 130 /* Fill in hardware address */ 131 if (hwaddr) { 132 strlcpy(ifr.ifr_name, ifname, IFNAMSIZ); 133 if (ioctl(fd, SIOCGIFHWADDR, &ifr) < 0) { 134 error("Can't get hardware address for %s: %m", ifname); 135 close(fd); 136 return -1; 137 } 138 memcpy(hwaddr, ifr.ifr_hwaddr.sa_data, ETH_ALEN); 139 #ifdef ARPHRD_ETHER 140 if (ifr.ifr_hwaddr.sa_family != ARPHRD_ETHER) { 141 warn("Interface %.16s is not Ethernet", ifname); 142 } 143 #endif 144 if (NOT_UNICAST(hwaddr)) { 145 fatal("Can't use interface %.16s: it has broadcast/multicast MAC address", 146 ifname); 147 } 148 } 149 150 /* Sanity check on MTU */ 151 strlcpy(ifr.ifr_name, ifname, IFNAMSIZ); 152 if (ioctl(fd, SIOCGIFMTU, &ifr) < 0) { 153 error("Can't get MTU for %s: %m", ifname); 154 } else if (ifr.ifr_mtu < ETH_DATA_LEN) { 155 error("Interface %.16s has MTU of %d -- should be at least %d.", 156 ifname, ifr.ifr_mtu, ETH_DATA_LEN); 157 error("This may cause serious connection problems."); 158 } 159 160 #ifdef HAVE_STRUCT_SOCKADDR_LL 161 /* Get interface index */ 162 sa.sll_family = AF_PACKET; 163 sa.sll_protocol = htons(type); 164 165 strlcpy(ifr.ifr_name, ifname, IFNAMSIZ); 166 if (ioctl(fd, SIOCGIFINDEX, &ifr) < 0) { 167 error("Could not get interface index for %s: %m", ifname); 168 close(fd); 169 return -1; 170 } 171 sa.sll_ifindex = ifr.ifr_ifindex; 172 173 #else 174 strcpy(sa.sa_data, ifname); 175 #endif 176 177 /* We're only interested in packets on specified interface */ 178 if (bind(fd, (struct sockaddr *) &sa, sizeof(sa)) < 0) { 179 error("Failed to bind to interface %s: %m", ifname); 180 close(fd); 181 return -1; 182 } 183 184 return fd; 185 } 186 187 188 /*********************************************************************** 189 *%FUNCTION: sendPacket 190 *%ARGUMENTS: 191 * sock -- socket to send to 192 * pkt -- the packet to transmit 193 * size -- size of packet (in bytes) 194 *%RETURNS: 195 * 0 on success; -1 on failure 196 *%DESCRIPTION: 197 * Transmits a packet 198 ***********************************************************************/ 199 int 200 sendPacket(PPPoEConnection *conn, int sock, PPPoEPacket *pkt, int size) 201 { 202 int err; 203 204 if (debug) 205 pppoe_log_packet("Send ", pkt); 206 #if defined(HAVE_STRUCT_SOCKADDR_LL) 207 err = send(sock, pkt, size, 0); 208 #else 209 struct sockaddr sa; 210 211 strcpy(sa.sa_data, conn->ifName); 212 err = sendto(sock, pkt, size, 0, &sa, sizeof(sa)); 213 #endif 214 if (err < 0) { 215 error("error sending pppoe packet: %m"); 216 return -1; 217 } 218 return 0; 219 } 220 221 /*********************************************************************** 222 *%FUNCTION: receivePacket 223 *%ARGUMENTS: 224 * sock -- socket to read from 225 * pkt -- place to store the received packet 226 * size -- set to size of packet in bytes 227 *%RETURNS: 228 * >= 0 if all OK; < 0 if error 229 *%DESCRIPTION: 230 * Receives a packet 231 ***********************************************************************/ 232 int 233 receivePacket(int sock, PPPoEPacket *pkt, int *size) 234 { 235 if ((*size = recv(sock, pkt, sizeof(PPPoEPacket), 0)) < 0) { 236 error("error receiving pppoe packet: %m"); 237 return -1; 238 } 239 if (debug) 240 pppoe_log_packet("Recv ", pkt); 241 return 0; 242 } 243