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