?? if.c
字號:
/************************************************************************* 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.** LIC: GPL************************************************************************/static char const RCSID[] ="$Id: if.c,v 1.17 2005/08/08 03:51:35 dfs 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_tetherType(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.***********************************************************************/voidgetHWaddr(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.***********************************************************************/voidinitFilter(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.***********************************************************************/intopenInterface(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***********************************************************************/intopenInterface(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***********************************************************************/intsendPacket(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 && (errno != ENOBUFS)) { 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);
?? 快捷鍵說明
復制代碼
Ctrl + C
搜索代碼
Ctrl + F
全屏模式
F11
切換主題
Ctrl + Shift + D
顯示快捷鍵
?
增大字號
Ctrl + =
減小字號
Ctrl + -