| /*********************************************************************** |
| * |
| * if.c |
| * |
| * Implementation of user-space PPPoE redirector for Linux. |
| * |
| * Functions for opening a raw socket and reading/writing raw Ethernet frames. |
| * |
| * Copyright (C) 2000 by Roaring Penguin Software Inc. |
| * |
| * This program may be distributed according to the terms of the GNU |
| * General Public License, version 2 or (at your option) any later version. |
| * |
| ***********************************************************************/ |
| |
| static char const RCSID[] = |
| "$Id: if.c,v 1.1 2001/12/14 02:55:20 mostrows Exp $"; |
| |
| #include "pppoe.h" |
| |
| #ifdef HAVE_UNISTD_H |
| #include <unistd.h> |
| #endif |
| |
| #ifdef HAVE_NETPACKET_PACKET_H |
| #include <netpacket/packet.h> |
| #elif defined(HAVE_LINUX_IF_PACKET_H) |
| #include <linux/if_packet.h> |
| #endif |
| |
| #ifdef HAVE_NET_ETHERNET_H |
| #include <net/ethernet.h> |
| #endif |
| |
| #ifdef HAVE_ASM_TYPES_H |
| #include <asm/types.h> |
| #endif |
| |
| #ifdef HAVE_SYS_IOCTL_H |
| #include <sys/ioctl.h> |
| #endif |
| |
| #ifdef HAVE_SYSLOG_H |
| #include <syslog.h> |
| #endif |
| |
| #include <errno.h> |
| #include <stdlib.h> |
| #include <string.h> |
| |
| #ifdef HAVE_NET_IF_ARP_H |
| #include <net/if_arp.h> |
| #endif |
| |
| #ifdef USE_DLPI |
| |
| #include <limits.h> |
| #include <fcntl.h> |
| #include <stdlib.h> |
| #include <sys/types.h> |
| #include <sys/time.h> |
| #include <sys/stream.h> |
| #include <sys/stropts.h> |
| #include <sys/dlpi.h> |
| #include <sys/bufmod.h> |
| #include <stdio.h> |
| #include <signal.h> |
| #include <stropts.h> |
| |
| /* function declarations */ |
| |
| void dlpromisconreq( int fd, u_long level); |
| void dlinforeq(int fd); |
| void dlunitdatareq(int fd, u_char *addrp, int addrlen, u_long minpri, u_long maxpri, u_char *datap, int datalen); |
| void dlinfoack(int fd, char *bufp); |
| void dlbindreq(int fd, u_long sap, u_long max_conind, u_long service_mode, u_long conn_mgmt, u_long xidtest); |
| void dlattachreq(int fd, u_long ppa); |
| void dlokack(int fd, char *bufp); |
| void dlbindack(int fd, char *bufp); |
| int strioctl(int fd, int cmd, int timout, int len, char *dp); |
| void strgetmsg(int fd, struct strbuf *ctlp, struct strbuf *datap, int *flagsp, char *caller); |
| void sigalrm(int sig); |
| void expecting(int prim, union DL_primitives *dlp); |
| char *dlprim(u_long prim); |
| |
| /* #define DL_DEBUG */ |
| |
| static int dl_abssaplen; |
| static int dl_saplen; |
| static int dl_addrlen; |
| |
| #endif |
| |
| #ifdef USE_BPF |
| #include <net/bpf.h> |
| #include <fcntl.h> |
| |
| unsigned char *bpfBuffer; /* Packet filter buffer */ |
| int bpfLength = 0; /* Packet filter buffer length */ |
| int bpfSize = 0; /* Number of unread bytes in buffer */ |
| int bpfOffset = 0; /* Current offset in bpfBuffer */ |
| #endif |
| |
| /* Initialize frame types to RFC 2516 values. Some broken peers apparently |
| use different frame types... sigh... */ |
| |
| UINT16_t Eth_PPPOE_Discovery = ETH_PPPOE_DISCOVERY; |
| UINT16_t Eth_PPPOE_Session = ETH_PPPOE_SESSION; |
| |
| /********************************************************************** |
| *%FUNCTION: etherType |
| *%ARGUMENTS: |
| * packet -- a received PPPoE packet |
| *%RETURNS: |
| * ethernet packet type (see /usr/include/net/ethertypes.h) |
| *%DESCRIPTION: |
| * Checks the ethernet packet header to determine its type. |
| * We should only be receveing DISCOVERY and SESSION types if the BPF |
| * is set up correctly. Logs an error if an unexpected type is received. |
| * Note that the ethernet type names come from "pppoe.h" and the packet |
| * packet structure names use the LINUX dialect to maintain consistency |
| * with the rest of this file. See the BSD section of "pppoe.h" for |
| * translations of the data structure names. |
| ***********************************************************************/ |
| UINT16_t |
| etherType(PPPoEPacket *packet) |
| { |
| UINT16_t type = (UINT16_t) ntohs(packet->ethHdr.h_proto); |
| if (type != Eth_PPPOE_Discovery && type != Eth_PPPOE_Session) { |
| syslog(LOG_ERR, "Invalid ether type 0x%x", type); |
| } |
| return type; |
| } |
| |
| #ifdef USE_BPF |
| /********************************************************************** |
| *%FUNCTION: getHWaddr |
| *%ARGUMENTS: |
| * ifname -- name of interface |
| * hwaddr -- buffer for ehthernet address |
| *%RETURNS: |
| * Nothing |
| *%DESCRIPTION: |
| * Locates the Ethernet hardware address for an interface. |
| ***********************************************************************/ |
| void |
| getHWaddr(int sock, char const *ifname, unsigned char *hwaddr) |
| { |
| char inbuf[8192]; |
| const struct sockaddr_dl *sdl; |
| struct ifconf ifc; |
| struct ifreq ifreq, *ifr; |
| int i; |
| int found = 0; |
| |
| ifc.ifc_len = sizeof(inbuf); |
| ifc.ifc_buf = inbuf; |
| if (ioctl(sock, SIOCGIFCONF, &ifc) < 0) { |
| fatalSys("SIOCGIFCONF"); |
| } |
| ifr = ifc.ifc_req; |
| ifreq.ifr_name[0] = '\0'; |
| for (i = 0; i < ifc.ifc_len; ) { |
| ifr = (struct ifreq *)((caddr_t)ifc.ifc_req + i); |
| i += sizeof(ifr->ifr_name) + |
| (ifr->ifr_addr.sa_len > sizeof(struct sockaddr) |
| ? ifr->ifr_addr.sa_len |
| : sizeof(struct sockaddr)); |
| if (ifr->ifr_addr.sa_family == AF_LINK) { |
| sdl = (const struct sockaddr_dl *) &ifr->ifr_addr; |
| if ((sdl->sdl_type == IFT_ETHER) && |
| (sdl->sdl_alen == ETH_ALEN) && |
| !strncmp(ifname, ifr->ifr_name, sizeof(ifr->ifr_name))) { |
| if (found) { |
| char buffer[256]; |
| sprintf(buffer, "interface %.16s has more than one ethernet address", ifname); |
| rp_fatal(buffer); |
| } else { |
| found = 1; |
| memcpy(hwaddr, LLADDR(sdl), ETH_ALEN); |
| } |
| } |
| } |
| } |
| if (!found) { |
| char buffer[256]; |
| sprintf(buffer, "interface %.16s has no ethernet address", ifname); |
| rp_fatal(buffer); |
| } |
| } |
| |
| /********************************************************************** |
| *%FUNCTION: initFilter |
| *%ARGUMENTS: |
| * fd -- file descriptor of BSD device |
| * type -- Ethernet frame type (0 for watch mode) |
| * hwaddr -- buffer with ehthernet address |
| *%RETURNS: |
| * Nothing |
| *%DESCRIPTION: |
| * Initializes the packet filter rules. |
| ***********************************************************************/ |
| void |
| initFilter(int fd, UINT16_t type, unsigned char *hwaddr) |
| { |
| /* Packet Filter Instructions: |
| * Note that the ethernet type names come from "pppoe.h" and are |
| * used here to maintain consistency with the rest of this file. */ |
| static struct bpf_insn bpfRun[] = { /* run PPPoE */ |
| BPF_STMT(BPF_LD+BPF_H+BPF_ABS, 12), /* ethernet type */ |
| BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, ETH_PPPOE_SESSION, 5, 0), |
| BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, ETH_PPPOE_DISCOVERY, 0, 9), |
| BPF_STMT(BPF_LD+BPF_W+BPF_ABS, 0), /* first word of dest. addr */ |
| #define PPPOE_BCAST_CMPW 4 /* offset of word compare */ |
| BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, 0, 0, 2), |
| BPF_STMT(BPF_LD+BPF_H+BPF_ABS, 4), /* next 1/2 word of dest. */ |
| #define PPPOE_BCAST_CMPH 6 /* offset of 1/2 word compare */ |
| BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, 0, 4, 0), |
| BPF_STMT(BPF_LD+BPF_W+BPF_ABS, 0), /* first word of dest. addr */ |
| #define PPPOE_FILTER_CMPW 8 /* offset of word compare */ |
| BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, 0, 0, 3), |
| BPF_STMT(BPF_LD+BPF_H+BPF_ABS, 4), /* next 1/2 word of dest. */ |
| #define PPPOE_FILTER_CMPH 10 /* offset of 1/rd compare */ |
| BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, 0, 0, 1), |
| BPF_STMT(BPF_RET+BPF_K, (u_int) -1), /* keep packet */ |
| BPF_STMT(BPF_RET+BPF_K, 0), /* drop packet */ |
| }; |
| |
| /* Fix the potentially varying parts */ |
| bpfRun[1].code = (u_short) BPF_JMP+BPF_JEQ+BPF_K; |
| bpfRun[1].jt = 5; |
| bpfRun[1].jf = 0; |
| bpfRun[1].k = Eth_PPPOE_Session; |
| |
| bpfRun[2].code = (u_short) BPF_JMP+BPF_JEQ+BPF_K; |
| bpfRun[2].jt = 0; |
| bpfRun[2].jf = 9; |
| bpfRun[2].k = Eth_PPPOE_Discovery; |
| |
| { |
| struct bpf_insn bpfInsn[sizeof(bpfRun) / sizeof(bpfRun[0])]; |
| struct bpf_program bpfProgram; |
| memcpy(bpfInsn, bpfRun, sizeof(bpfRun)); |
| bpfInsn[PPPOE_BCAST_CMPW].k = ((0xff << 24) | (0xff << 16) | |
| (0xff << 8) | 0xff); |
| bpfInsn[PPPOE_BCAST_CMPH].k = ((0xff << 8) | 0xff); |
| bpfInsn[PPPOE_FILTER_CMPW].k = ((hwaddr[0] << 24) | (hwaddr[1] << 16) | |
| (hwaddr[2] << 8) | hwaddr[3]); |
| bpfInsn[PPPOE_FILTER_CMPH].k = ((hwaddr[4] << 8) | hwaddr[5]); |
| bpfProgram.bf_len = (sizeof(bpfInsn) / sizeof(bpfInsn[0])); |
| bpfProgram.bf_insns = &bpfInsn[0]; |
| |
| /* Apply the filter */ |
| if (ioctl(fd, BIOCSETF, &bpfProgram) < 0) { |
| fatalSys("ioctl(BIOCSETF)"); |
| } |
| } |
| } |
| |
| /********************************************************************** |
| *%FUNCTION: openInterface |
| *%ARGUMENTS: |
| * ifname -- name of interface |
| * type -- Ethernet frame type (0 for any frame type) |
| * hwaddr -- if non-NULL, set to the hardware address |
| *%RETURNS: |
| * A file descriptor for talking with the Ethernet card. Exits on error. |
| * Note that the Linux version of this routine returns a socket instead. |
| *%DESCRIPTION: |
| * Opens a BPF on an interface for all PPPoE traffic (discovery and |
| * session). If 'type' is 0, uses promiscuous mode to watch any PPPoE |
| * traffic on this network. |
| ***********************************************************************/ |
| int |
| openInterface(char const *ifname, UINT16_t type, unsigned char *hwaddr) |
| { |
| static int fd = -1; |
| char bpfName[32]; |
| u_int optval; |
| struct bpf_version bpf_ver; |
| struct ifreq ifr; |
| int sock; |
| int i; |
| |
| /* BSD only opens one socket for both Discovery and Session packets */ |
| if (fd >= 0) { |
| return fd; |
| } |
| |
| /* Find a free BPF device */ |
| for (i = 0; i < 256; i++) { |
| sprintf(bpfName, "/dev/bpf%d", i); |
| if (((fd = open(bpfName, O_RDWR, 0)) >= 0) || |
| (errno != EBUSY)) { |
| break; |
| } |
| } |
| if (fd < 0) { |
| switch (errno) { |
| case EACCES: /* permission denied */ |
| { |
| char buffer[256]; |
| sprintf(buffer, "Cannot open %.32s -- pppoe must be run as root.", bpfName); |
| rp_fatal(buffer); |
| } |
| break; |
| case EBUSY: |
| case ENOENT: /* no such file */ |
| if (i == 0) { |
| rp_fatal("No /dev/bpf* devices (check your kernel configuration for BPF support)"); |
| } else { |
| rp_fatal("All /dev/bpf* devices are in use"); |
| } |
| break; |
| } |
| fatalSys(bpfName); |
| } |
| |
| if ((sock = socket(AF_LOCAL, SOCK_DGRAM, 0)) < 0) { |
| fatalSys("socket"); |
| } |
| |
| /* Check that the interface is up */ |
| strncpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name)); |
| if (ioctl(sock, SIOCGIFFLAGS, &ifr) < 0) { |
| fatalSys("ioctl(SIOCGIFFLAGS)"); |
| } |
| if ((ifr.ifr_flags & IFF_UP) == 0) { |
| char buffer[256]; |
| sprintf(buffer, "Interface %.16s is not up\n", ifname); |
| rp_fatal(buffer); |
| } |
| |
| /* Fill in hardware address and initialize the packet filter rules */ |
| if (hwaddr == NULL) { |
| rp_fatal("openInterface: no hwaddr arg."); |
| } |
| getHWaddr(sock, ifname, hwaddr); |
| initFilter(fd, type, hwaddr); |
| |
| /* Sanity check on MTU -- apparently does not work on OpenBSD */ |
| #if !defined(__OpenBSD__) |
| strncpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name)); |
| if (ioctl(sock, SIOCGIFMTU, &ifr) < 0) { |
| fatalSys("ioctl(SIOCGIFMTU)"); |
| } |
| if (ifr.ifr_mtu < ETH_DATA_LEN) { |
| char buffer[256]; |
| sprintf(buffer, "Interface %.16s has MTU of %d -- should be %d. You may have serious connection problems.", |
| ifname, ifr.ifr_mtu, ETH_DATA_LEN); |
| printErr(buffer); |
| } |
| #endif |
| |
| /* done with the socket */ |
| if (close(sock) < 0) { |
| fatalSys("close"); |
| } |
| |
| /* Check the BPF version number */ |
| if (ioctl(fd, BIOCVERSION, &bpf_ver) < 0) { |
| fatalSys("ioctl(BIOCVERSION)"); |
| } |
| if ((bpf_ver.bv_major != BPF_MAJOR_VERSION) || |
| (bpf_ver.bv_minor < BPF_MINOR_VERSION)) { |
| char buffer[256]; |
| sprintf(buffer, "Unsupported BPF version: %d.%d (kernel: %d.%d)", |
| BPF_MAJOR_VERSION, BPF_MINOR_VERSION, |
| bpf_ver.bv_major, bpf_ver.bv_minor); |
| rp_fatal(buffer); |
| } |
| |
| /* allocate a receive packet buffer */ |
| if (ioctl(fd, BIOCGBLEN, &bpfLength) < 0) { |
| fatalSys("ioctl(BIOCGBLEN)"); |
| } |
| if (!(bpfBuffer = (unsigned char *) malloc(bpfLength))) { |
| rp_fatal("malloc"); |
| } |
| |
| /* reads should return as soon as there is a packet available */ |
| optval = 1; |
| if (ioctl(fd, BIOCIMMEDIATE, &optval) < 0) { |
| fatalSys("ioctl(BIOCIMMEDIATE)"); |
| } |
| |
| /* Bind the interface to the filter */ |
| strncpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name)); |
| if (ioctl(fd, BIOCSETIF, &ifr) < 0) { |
| char buffer[256]; |
| sprintf(buffer, "ioctl(BIOCSETIF) can't select interface %.16s", |
| ifname); |
| rp_fatal(buffer); |
| } |
| |
| syslog(LOG_INFO, "Interface=%.16s HWaddr=%02X:%02X:%02X:%02X:%02X:%02X Device=%.32s Buffer size=%d", |
| ifname, |
| hwaddr[0], hwaddr[1], hwaddr[2], |
| hwaddr[3], hwaddr[4], hwaddr[5], |
| bpfName, bpfLength); |
| return fd; |
| } |
| |
| #endif /* USE_BPF */ |
| |
| #ifdef USE_LINUX_PACKET |
| /********************************************************************** |
| *%FUNCTION: openInterface |
| *%ARGUMENTS: |
| * ifname -- name of interface |
| * type -- Ethernet frame type |
| * hwaddr -- if non-NULL, set to the hardware address |
| *%RETURNS: |
| * A raw socket for talking to the Ethernet card. Exits on error. |
| *%DESCRIPTION: |
| * Opens a raw Ethernet socket |
| ***********************************************************************/ |
| int |
| openInterface(char const *ifname, UINT16_t type, unsigned char *hwaddr) |
| { |
| int optval=1; |
| int fd; |
| struct ifreq ifr; |
| int domain, stype; |
| |
| #ifdef HAVE_STRUCT_SOCKADDR_LL |
| struct sockaddr_ll sa; |
| #else |
| struct sockaddr sa; |
| #endif |
| |
| memset(&sa, 0, sizeof(sa)); |
| |
| #ifdef HAVE_STRUCT_SOCKADDR_LL |
| domain = PF_PACKET; |
| stype = SOCK_RAW; |
| #else |
| domain = PF_INET; |
| stype = SOCK_PACKET; |
| #endif |
| |
| if ((fd = socket(domain, stype, htons(type))) < 0) { |
| /* Give a more helpful message for the common error case */ |
| if (errno == EPERM) { |
| rp_fatal("Cannot create raw socket -- pppoe must be run as root."); |
| } |
| fatalSys("socket"); |
| } |
| |
| if (setsockopt(fd, SOL_SOCKET, SO_BROADCAST, &optval, sizeof(optval)) < 0) { |
| fatalSys("setsockopt"); |
| } |
| |
| /* Fill in hardware address */ |
| if (hwaddr) { |
| strncpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name)); |
| if (ioctl(fd, SIOCGIFHWADDR, &ifr) < 0) { |
| fatalSys("ioctl(SIOCGIFHWADDR)"); |
| } |
| memcpy(hwaddr, ifr.ifr_hwaddr.sa_data, ETH_ALEN); |
| #ifdef ARPHRD_ETHER |
| if (ifr.ifr_hwaddr.sa_family != ARPHRD_ETHER) { |
| char buffer[256]; |
| sprintf(buffer, "Interface %.16s is not Ethernet", ifname); |
| rp_fatal(buffer); |
| } |
| #endif |
| if (NOT_UNICAST(hwaddr)) { |
| char buffer[256]; |
| sprintf(buffer, |
| "Interface %.16s has broadcast/multicast MAC address??", |
| ifname); |
| rp_fatal(buffer); |
| } |
| } |
| |
| /* Sanity check on MTU */ |
| strncpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name)); |
| if (ioctl(fd, SIOCGIFMTU, &ifr) < 0) { |
| fatalSys("ioctl(SIOCGIFMTU)"); |
| } |
| if (ifr.ifr_mtu < ETH_DATA_LEN) { |
| char buffer[256]; |
| sprintf(buffer, "Interface %.16s has MTU of %d -- should be %d. You may have serious connection problems.", |
| ifname, ifr.ifr_mtu, ETH_DATA_LEN); |
| printErr(buffer); |
| } |
| |
| #ifdef HAVE_STRUCT_SOCKADDR_LL |
| /* Get interface index */ |
| sa.sll_family = AF_PACKET; |
| sa.sll_protocol = htons(type); |
| |
| strncpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name)); |
| if (ioctl(fd, SIOCGIFINDEX, &ifr) < 0) { |
| fatalSys("ioctl(SIOCFIGINDEX): Could not get interface index"); |
| } |
| sa.sll_ifindex = ifr.ifr_ifindex; |
| |
| #else |
| strcpy(sa.sa_data, ifname); |
| #endif |
| |
| /* We're only interested in packets on specified interface */ |
| if (bind(fd, (struct sockaddr *) &sa, sizeof(sa)) < 0) { |
| fatalSys("bind"); |
| } |
| |
| return fd; |
| } |
| |
| #endif /* USE_LINUX */ |
| |
| /*********************************************************************** |
| *%FUNCTION: sendPacket |
| *%ARGUMENTS: |
| * sock -- socket to send to |
| * pkt -- the packet to transmit |
| * size -- size of packet (in bytes) |
| *%RETURNS: |
| * 0 on success; -1 on failure |
| *%DESCRIPTION: |
| * Transmits a packet |
| ***********************************************************************/ |
| int |
| sendPacket(PPPoEConnection *conn, int sock, PPPoEPacket *pkt, int size) |
| { |
| #if defined(USE_BPF) |
| if (write(sock, pkt, size) < 0) { |
| sysErr("write (sendPacket)"); |
| return -1; |
| } |
| #elif defined(HAVE_STRUCT_SOCKADDR_LL) |
| if (send(sock, pkt, size, 0) < 0) { |
| sysErr("send (sendPacket)"); |
| return -1; |
| } |
| #else |
| #ifdef USE_DLPI |
| |
| #define ABS(x) ((x) < 0 ? -(x) : (x)) |
| |
| u_char addr[MAXDLADDR]; |
| u_char phys[MAXDLADDR]; |
| u_char sap[MAXDLADDR]; |
| u_char xmitbuf[MAXDLBUF]; |
| int data_size; |
| |
| short tmp_sap; |
| |
| tmp_sap = htons(pkt->ethHdr.h_proto); |
| data_size = size - sizeof(struct ethhdr); |
| |
| memcpy((char *)phys, (char *)pkt->ethHdr.h_dest, ETHERADDRL); |
| memcpy((char *)sap, (char *)&tmp_sap, sizeof(ushort_t)); |
| memcpy((char *)xmitbuf, (char *)pkt + sizeof(struct ethhdr), data_size); |
| |
| if (dl_saplen > 0) { /* order is sap+phys */ |
| (void) memcpy((char*)addr, (char*)&sap, dl_abssaplen); |
| (void) memcpy((char*)addr+dl_abssaplen, (char*)phys, ETHERADDRL); |
| } else { /* order is phys+sap */ |
| (void) memcpy((char*)addr, (char*)phys, ETHERADDRL); |
| (void) memcpy((char*)addr+ETHERADDRL, (char*)&sap, dl_abssaplen); |
| } |
| |
| #ifdef DL_DEBUG |
| printf("%02x:%02x:%02x:%02x:%02x:%02x %02x:%02x\n", |
| addr[0],addr[1],addr[2],addr[3],addr[4],addr[5], |
| addr[6],addr[7]); |
| #endif |
| |
| dlunitdatareq(sock, addr, dl_addrlen, 0, 0, xmitbuf, data_size); |
| |
| |
| |
| #else |
| struct sockaddr sa; |
| |
| if (!conn) { |
| rp_fatal("relay and server not supported on Linux 2.0 kernels"); |
| } |
| strcpy(sa.sa_data, conn->ifName); |
| if (sendto(sock, pkt, size, 0, &sa, sizeof(sa)) < 0) { |
| sysErr("sendto (sendPacket)"); |
| return -1; |
| } |
| #endif |
| #endif |
| return 0; |
| } |
| |
| #ifdef USE_BPF |
| /*********************************************************************** |
| *%FUNCTION: clearPacketHeader |
| *%ARGUMENTS: |
| * pkt -- packet that needs its head clearing |
| *%RETURNS: |
| * nothing |
| *%DESCRIPTION: |
| * Clears a PPPoE packet header after a truncated packet has been |
| * received. Insures that the packet will fail any integrity tests |
| * and will be discarded by upper level routines. Also resets the |
| * bpfSize and bpfOffset variables to force a new read on the next |
| * call to receivePacket(). |
| ***********************************************************************/ |
| void |
| clearPacketHeader(PPPoEPacket *pkt) |
| { |
| bpfSize = bpfOffset = 0; |
| memset(pkt, 0, HDR_SIZE); |
| } |
| #endif |
| |
| /*********************************************************************** |
| *%FUNCTION: receivePacket |
| *%ARGUMENTS: |
| * sock -- socket to read from |
| * pkt -- place to store the received packet |
| * size -- set to size of packet in bytes |
| *%RETURNS: |
| * >= 0 if all OK; < 0 if error |
| *%DESCRIPTION: |
| * Receives a packet |
| ***********************************************************************/ |
| int |
| receivePacket(int sock, PPPoEPacket *pkt, int *size) |
| { |
| #ifdef USE_BPF |
| struct bpf_hdr hdr; |
| int seglen, copylen; |
| |
| if (bpfSize <= 0) { |
| bpfOffset = 0; |
| if ((bpfSize = read(sock, bpfBuffer, bpfLength)) < 0) { |
| sysErr("read (receivePacket)"); |
| return -1; |
| } |
| } |
| if (bpfSize < sizeof(hdr)) { |
| syslog(LOG_ERR, "Truncated bpf packet header: len=%d", bpfSize); |
| clearPacketHeader(pkt); /* resets bpfSize and bpfOffset */ |
| return 0; |
| } |
| memcpy(&hdr, bpfBuffer + bpfOffset, sizeof(hdr)); |
| if (hdr.bh_caplen != hdr.bh_datalen) { |
| syslog(LOG_ERR, "Truncated bpf packet: caplen=%d, datalen=%d", |
| hdr.bh_caplen, hdr.bh_datalen); |
| clearPacketHeader(pkt); /* resets bpfSize and bpfOffset */ |
| return 0; |
| } |
| seglen = hdr.bh_hdrlen + hdr.bh_caplen; |
| if (seglen > bpfSize) { |
| syslog(LOG_ERR, "Truncated bpf packet: seglen=%d, bpfSize=%d", |
| seglen, bpfSize); |
| clearPacketHeader(pkt); /* resets bpfSize and bpfOffset */ |
| return 0; |
| } |
| seglen = BPF_WORDALIGN(seglen); |
| *size = copylen = ((hdr.bh_caplen < sizeof(PPPoEPacket)) ? |
| hdr.bh_caplen : sizeof(PPPoEPacket)); |
| memcpy(pkt, bpfBuffer + bpfOffset + hdr.bh_hdrlen, copylen); |
| if (seglen >= bpfSize) { |
| bpfSize = bpfOffset = 0; |
| } else { |
| bpfSize -= seglen; |
| bpfOffset += seglen; |
| } |
| #else |
| #ifdef USE_DLPI |
| struct strbuf data; |
| int flags = 0; |
| int retval; |
| |
| data.buf = (char *) pkt; |
| data.maxlen = MAXDLBUF; |
| data.len = 0; |
| |
| if ((retval = getmsg(sock, NULL, &data, &flags)) < 0) { |
| sysErr("read (receivePacket)"); |
| return -1; |
| } |
| |
| *size = data.len; |
| |
| #else |
| if ((*size = recv(sock, pkt, sizeof(PPPoEPacket), 0)) < 0) { |
| sysErr("recv (receivePacket)"); |
| return -1; |
| } |
| #endif |
| #endif |
| return 0; |
| } |
| |
| #ifdef USE_DLPI |
| /********************************************************************** |
| *%FUNCTION: openInterface |
| *%ARGUMENTS: |
| * ifname -- name of interface |
| * type -- Ethernet frame type |
| * hwaddr -- if non-NULL, set to the hardware address |
| *%RETURNS: |
| * A raw socket for talking to the Ethernet card. Exits on error. |
| *%DESCRIPTION: |
| * Opens a raw Ethernet socket |
| ***********************************************************************/ |
| int |
| openInterface(char const *ifname, UINT16_t type, unsigned char *hwaddr) |
| { |
| int fd; |
| long buf[MAXDLBUF]; |
| |
| union DL_primitives *dlp; |
| |
| char base_dev[PATH_MAX]; |
| int ppa; |
| |
| if(strlen(ifname) > PATH_MAX) { |
| rp_fatal("socket: string to long"); |
| } |
| |
| ppa = atoi(&ifname[strlen(ifname)-1]); |
| strncpy(base_dev, ifname, PATH_MAX); |
| base_dev[strlen(base_dev)-1] = '\0'; |
| |
| /* rearranged order of DLPI code - delphys 20010803 */ |
| dlp = (union DL_primitives*) buf; |
| |
| if (( fd = open(base_dev, O_RDWR)) < 0) { |
| /* Give a more helpful message for the common error case */ |
| if (errno == EPERM) { |
| rp_fatal("Cannot create raw socket -- pppoe must be run as root."); |
| } |
| fatalSys("socket"); |
| } |
| |
| /* rearranged order of DLPI code - delphys 20010803 */ |
| dlattachreq(fd, ppa); |
| dlokack(fd, (char *)buf); |
| |
| dlbindreq(fd, type, 0, DL_CLDLS, 0, 0); |
| dlbindack(fd, (char *)buf); |
| |
| dlinforeq(fd); |
| dlinfoack(fd, (char *)buf); |
| |
| dl_abssaplen = ABS(dlp->info_ack.dl_sap_length); |
| dl_saplen = dlp->info_ack.dl_sap_length; |
| if (ETHERADDRL != (dlp->info_ack.dl_addr_length - dl_abssaplen)) |
| fatalSys("invalid destination physical address length"); |
| dl_addrlen = dl_abssaplen + ETHERADDRL; |
| |
| /* ethernet address retrieved as part of DL_INFO_ACK - delphys 20010803 */ |
| memcpy(hwaddr, (u_char*)((char*)(dlp) + (int)(dlp->info_ack.dl_addr_offset)), ETHERADDRL); |
| |
| if ( strioctl(fd, DLIOCRAW, -1, 0, NULL) < 0 ) { |
| fatalSys("DLIOCRAW"); |
| } |
| |
| if (ioctl(fd, I_FLUSH, FLUSHR) < 0) fatalSys("I_FLUSH"); |
| |
| return fd; |
| } |
| |
| /* cloned from dlcommon.c */ |
| |
| void dlpromisconreq(int fd, u_long level) |
| { |
| dl_promiscon_req_t promiscon_req; |
| struct strbuf ctl; |
| int flags; |
| |
| promiscon_req.dl_primitive = DL_PROMISCON_REQ; |
| promiscon_req.dl_level = level; |
| |
| ctl.maxlen = 0; |
| ctl.len = sizeof (promiscon_req); |
| ctl.buf = (char *) &promiscon_req; |
| |
| flags = 0; |
| |
| if (putmsg(fd, &ctl, (struct strbuf*) NULL, flags) < 0) |
| fatalSys("dlpromiscon: putmsg"); |
| |
| } |
| |
| void dlinforeq(int fd) |
| { |
| dl_info_req_t info_req; |
| struct strbuf ctl; |
| int flags; |
| |
| info_req.dl_primitive = DL_INFO_REQ; |
| |
| ctl.maxlen = 0; |
| ctl.len = sizeof (info_req); |
| ctl.buf = (char *) &info_req; |
| |
| flags = RS_HIPRI; |
| |
| if (putmsg(fd, &ctl, (struct strbuf*) NULL, flags) < 0) |
| fatalSys("dlinforeq: putmsg"); |
| } |
| |
| void dlunitdatareq(int fd, u_char *addrp, int addrlen, u_long minpri, u_long maxpri, u_char *datap, int datalen) |
| { |
| long buf[MAXDLBUF]; |
| union DL_primitives *dlp; |
| struct strbuf data, ctl; |
| |
| dlp = (union DL_primitives*) buf; |
| |
| dlp->unitdata_req.dl_primitive = DL_UNITDATA_REQ; |
| dlp->unitdata_req.dl_dest_addr_length = addrlen; |
| dlp->unitdata_req.dl_dest_addr_offset = sizeof (dl_unitdata_req_t); |
| dlp->unitdata_req.dl_priority.dl_min = minpri; |
| dlp->unitdata_req.dl_priority.dl_max = maxpri; |
| |
| (void) memcpy(OFFADDR(dlp, sizeof (dl_unitdata_req_t)), addrp, addrlen); |
| |
| ctl.maxlen = 0; |
| ctl.len = sizeof (dl_unitdata_req_t) + addrlen; |
| ctl.buf = (char *) buf; |
| |
| data.maxlen = 0; |
| data.len = datalen; |
| data.buf = (char *) datap; |
| |
| if (putmsg(fd, &ctl, &data, 0) < 0) |
| fatalSys("dlunitdatareq: putmsg"); |
| } |
| |
| void dlinfoack(int fd, char *bufp) |
| { |
| union DL_primitives *dlp; |
| struct strbuf ctl; |
| int flags; |
| |
| ctl.maxlen = MAXDLBUF; |
| ctl.len = 0; |
| ctl.buf = bufp; |
| |
| strgetmsg(fd, &ctl, (struct strbuf*)NULL, &flags, "dlinfoack"); |
| |
| dlp = (union DL_primitives *) ctl.buf; |
| |
| expecting(DL_INFO_ACK, dlp); |
| |
| if (ctl.len < sizeof (dl_info_ack_t)) { |
| char buffer[256]; |
| sprintf(buffer, "dlinfoack: response ctl.len too short: %d", ctl.len); |
| rp_fatal(buffer); |
| } |
| |
| if (flags != RS_HIPRI) |
| rp_fatal("dlinfoack: DL_INFO_ACK was not M_PCPROTO"); |
| |
| if (ctl.len < sizeof (dl_info_ack_t)) { |
| char buffer[256]; |
| sprintf(buffer, "dlinfoack: short response ctl.len: %d", ctl.len); |
| rp_fatal(buffer); |
| } |
| } |
| |
| void dlbindreq(int fd, u_long sap, u_long max_conind, u_long service_mode, u_long conn_mgmt, u_long xidtest) |
| { |
| dl_bind_req_t bind_req; |
| struct strbuf ctl; |
| int flags; |
| |
| bind_req.dl_primitive = DL_BIND_REQ; |
| bind_req.dl_sap = sap; |
| bind_req.dl_max_conind = max_conind; |
| bind_req.dl_service_mode = service_mode; |
| bind_req.dl_conn_mgmt = conn_mgmt; |
| bind_req.dl_xidtest_flg = xidtest; |
| |
| ctl.maxlen = 0; |
| ctl.len = sizeof (bind_req); |
| ctl.buf = (char *) &bind_req; |
| |
| flags = 0; |
| |
| if (putmsg(fd, &ctl, (struct strbuf*) NULL, flags) < 0) |
| fatalSys("dlbindreq: putmsg"); |
| } |
| |
| void dlattachreq(int fd, u_long ppa) |
| { |
| dl_attach_req_t attach_req; |
| struct strbuf ctl; |
| int flags; |
| |
| attach_req.dl_primitive = DL_ATTACH_REQ; |
| attach_req.dl_ppa = ppa; |
| |
| ctl.maxlen = 0; |
| ctl.len = sizeof (attach_req); |
| ctl.buf = (char *) &attach_req; |
| |
| flags = 0; |
| |
| if (putmsg(fd, &ctl, (struct strbuf*) NULL, flags) < 0) |
| fatalSys("dlattachreq: putmsg"); |
| } |
| |
| void dlokack(int fd, char *bufp) |
| { |
| union DL_primitives *dlp; |
| struct strbuf ctl; |
| int flags; |
| |
| ctl.maxlen = MAXDLBUF; |
| ctl.len = 0; |
| ctl.buf = bufp; |
| |
| strgetmsg(fd, &ctl, (struct strbuf*)NULL, &flags, "dlokack"); |
| |
| dlp = (union DL_primitives *) ctl.buf; |
| |
| expecting(DL_OK_ACK, dlp); |
| |
| if (ctl.len < sizeof (dl_ok_ack_t)) { |
| char buffer[256]; |
| sprintf(buffer, "dlokack: response ctl.len too short: %d", ctl.len); |
| rp_fatal(buffer); |
| } |
| |
| if (flags != RS_HIPRI) |
| rp_fatal("dlokack: DL_OK_ACK was not M_PCPROTO"); |
| |
| if (ctl.len < sizeof (dl_ok_ack_t)) { |
| char buffer[256]; |
| sprintf(buffer, "dlokack: short response ctl.len: %d", ctl.len); |
| rp_fatal(buffer); |
| } |
| } |
| |
| void dlbindack(int fd, char *bufp) |
| { |
| union DL_primitives *dlp; |
| struct strbuf ctl; |
| int flags; |
| |
| ctl.maxlen = MAXDLBUF; |
| ctl.len = 0; |
| ctl.buf = bufp; |
| |
| strgetmsg(fd, &ctl, (struct strbuf*)NULL, &flags, "dlbindack"); |
| |
| dlp = (union DL_primitives *) ctl.buf; |
| |
| expecting(DL_BIND_ACK, dlp); |
| |
| if (flags != RS_HIPRI) |
| rp_fatal("dlbindack: DL_OK_ACK was not M_PCPROTO"); |
| |
| if (ctl.len < sizeof (dl_bind_ack_t)) { |
| char buffer[256]; |
| sprintf(buffer, "dlbindack: short response ctl.len: %d", ctl.len); |
| rp_fatal(buffer); |
| } |
| } |
| |
| int strioctl(int fd, int cmd, int timout, int len, char *dp) |
| { |
| struct strioctl sioc; |
| int rc; |
| |
| sioc.ic_cmd = cmd; |
| sioc.ic_timout = timout; |
| sioc.ic_len = len; |
| sioc.ic_dp = dp; |
| rc = ioctl(fd, I_STR, &sioc); |
| |
| if (rc < 0) |
| return (rc); |
| else |
| return (sioc.ic_len); |
| } |
| |
| void strgetmsg(int fd, struct strbuf *ctlp, struct strbuf *datap, int *flagsp, char *caller) |
| { |
| int rc; |
| static char errmsg[80]; |
| |
| /* |
| * Start timer. |
| */ |
| (void) signal(SIGALRM, sigalrm); |
| if (alarm(MAXWAIT) < 0) { |
| (void) sprintf(errmsg, "%s: alarm", caller); |
| fatalSys(errmsg); |
| } |
| |
| /* |
| * Set flags argument and issue getmsg(). |
| */ |
| *flagsp = 0; |
| if ((rc = getmsg(fd, ctlp, datap, flagsp)) < 0) { |
| (void) sprintf(errmsg, "%s: getmsg", caller); |
| fatalSys(errmsg); |
| } |
| |
| /* |
| * Stop timer. |
| */ |
| if (alarm(0) < 0) { |
| (void) sprintf(errmsg, "%s: alarm", caller); |
| fatalSys(errmsg); |
| } |
| |
| /* |
| * Check for MOREDATA and/or MORECTL. |
| */ |
| if ((rc & (MORECTL | MOREDATA)) == (MORECTL | MOREDATA)) { |
| char buffer[256]; |
| sprintf(buffer, "%s: MORECTL|MOREDATA", caller); |
| rp_fatal(buffer); |
| } |
| |
| if (rc & MORECTL) { |
| char buffer[256]; |
| sprintf(buffer, "%s: MORECTL", caller); |
| rp_fatal(buffer); |
| } |
| |
| if (rc & MOREDATA) { |
| char buffer[256]; |
| sprintf(buffer, "%s: MOREDATA", caller); |
| rp_fatal(buffer); |
| } |
| |
| /* |
| * Check for at least sizeof (long) control data portion. |
| */ |
| if (ctlp->len < sizeof (long)) { |
| char buffer[256]; |
| sprintf(buffer, "getmsg: control portion length < sizeof (long): %d", ctlp->len); |
| rp_fatal(buffer); |
| } |
| } |
| |
| void sigalrm(int sig) |
| { |
| (void) rp_fatal("sigalrm: TIMEOUT"); |
| } |
| |
| void expecting(int prim, union DL_primitives *dlp) |
| { |
| if (dlp->dl_primitive != (u_long)prim) { |
| char buffer[256]; |
| sprintf(buffer, "expected %s got %s", dlprim(prim), dlprim(dlp->dl_primitive)); |
| rp_fatal(buffer); |
| exit(1); |
| } |
| } |
| |
| char *dlprim(u_long prim) |
| { |
| static char primbuf[80]; |
| |
| switch ((int)prim) { |
| CASERET(DL_INFO_REQ); |
| CASERET(DL_INFO_ACK); |
| CASERET(DL_ATTACH_REQ); |
| CASERET(DL_DETACH_REQ); |
| CASERET(DL_BIND_REQ); |
| CASERET(DL_BIND_ACK); |
| CASERET(DL_UNBIND_REQ); |
| CASERET(DL_OK_ACK); |
| CASERET(DL_ERROR_ACK); |
| CASERET(DL_SUBS_BIND_REQ); |
| CASERET(DL_SUBS_BIND_ACK); |
| CASERET(DL_UNITDATA_REQ); |
| CASERET(DL_UNITDATA_IND); |
| CASERET(DL_UDERROR_IND); |
| CASERET(DL_UDQOS_REQ); |
| CASERET(DL_CONNECT_REQ); |
| CASERET(DL_CONNECT_IND); |
| CASERET(DL_CONNECT_RES); |
| CASERET(DL_CONNECT_CON); |
| CASERET(DL_TOKEN_REQ); |
| CASERET(DL_TOKEN_ACK); |
| CASERET(DL_DISCONNECT_REQ); |
| CASERET(DL_DISCONNECT_IND); |
| CASERET(DL_RESET_REQ); |
| CASERET(DL_RESET_IND); |
| CASERET(DL_RESET_RES); |
| CASERET(DL_RESET_CON); |
| default: |
| (void) sprintf(primbuf, "unknown primitive 0x%lx", prim); |
| return (primbuf); |
| } |
| } |
| |
| #endif /* USE_DLPI */ |