summaryrefslogtreecommitdiffstats
Commit message (Collapse)AuthorAgeFilesLines
* handle bad PO-: comments at the lexical level instead of doing it in the grammarPascal Rigaux2004-04-284-10/+26
| | | | | to have a better error message
* add a testPascal Rigaux2004-04-281-2/+6
|
* fix typoPascal Rigaux2004-04-281-1/+1
|
* add a test for open F, ...Pascal Rigaux2004-04-281-0/+2
|
* - deprecate "open F, ..." prefering "open(my $F, ...)"Pascal Rigaux2004-04-281-1/+14
| | | | | - fix an internal_error
* test with local fake_packagesPascal Rigaux2004-04-281-0/+1
|
* Clean up pod2html temp filesRafael Garcia-Suarez2004-04-281-0/+1
|
* handle: -e "foo" && -f _Pascal Rigaux2004-04-281-0/+1
|
* allowPascal Rigaux2004-04-281-1/+1
| | | | | | | | | my ($a, $b,); since it can be useful in: my ($a, # dqsdsq $b, # qdfqsdf ) = @_;
* - do check_simple_pattern() directly in from_PATTERN()Pascal Rigaux2004-04-282-10/+12
| | | | | - don't die, log instead for some regexp expressions
* no more internal_error "from_qw_raw" (which doesn't tell much about where it ↵Pascal Rigaux2004-04-281-6/+39
| | | | goes wrong)
* handle "use Foo X.X" (where X.X is a version number requested)Pascal Rigaux2004-04-091-1/+5
|
* generate perl_checker.htmlPascal Rigaux2004-04-071-1/+1
|
* cleanup perl_checker.htmlPascal Rigaux2004-04-071-1/+1
|
* add perl_checker.htmlPascal Rigaux2004-04-053-1/+162
|
* use local perl_checkerPascal Rigaux2004-04-051-1/+1
|
* provide Gnome2::Vte fake package too (thus enabling to kill one warning inThierry Vignaud2004-04-051-0/+72
| | | | | drakconsole)
* provide Gnome2 fake package tooThierry Vignaud2004-04-051-0/+641
|
* resync with Glib/Gtk2 1.040Thierry Vignaud2004-04-052-154/+772
|
* *** empty log message ***Pascal Rigaux2004-04-011-1/+2
|
* fix warning (would also need fixing detecting of boolean context vs scalarPascal Rigaux2004-03-311-1/+1
| | | | | context)
* add tests for perl_checkerPascal Rigaux2004-03-3113-0/+380
|
* in "$a ? $a : xxx", "xxx" can need short circuitPascal Rigaux2004-03-311-1/+1
| | | | | (even if i don't think any value which is_a_scalar can need_short_circuit)
* - turn some errors to warningsPascal Rigaux2004-03-311-4/+4
| | | | | - add function "-c"
* update from current patched xs sourcesThierry Vignaud2004-02-122-162/+87
|
* replace the information "a variable is accessed" with the more precisePascal Rigaux2004-01-224-31/+41
| | | | | | Access_none | Access_write_only | Access_various so that we can say either "variable unused" or "variable assigned but not read"
* handle "format" perl instructionPascal Rigaux2004-01-223-22/+42
|
* when debugging default is ../t.plPascal Rigaux2004-01-221-1/+1
|
* use a float to save the mtime since int overflows.Pascal Rigaux2004-01-136-10/+15
| | | | | this fixes the cache being used partially
* filter perl keywordThierry Vignaud2004-01-132-4/+4
|
* add exceptions for some std perl keywords used in glib/gtk2 bindingThierry Vignaud2004-01-131-1/+1
|
* - simplify using operator "..."Pascal Rigaux2004-01-131-7/+2
| | | | | - fix line number
* update to latest Glib and Gtk2 bindingsThierry Vignaud2004-01-122-125/+404
|
* ignore pod while parsing xs filesThierry Vignaud2004-01-121-1/+7
|
* perl_checker: entries in generated pot file are sorted by filesPascal Rigaux2004-01-094-15/+21
|
* now allow ../../../xxx instead of ../../xxxPascal Rigaux2004-01-091-2/+5
| | | | | (to allow move/move.pm to be relative)
* always print "can't find package" warning, even with restrict-to-filesPascal Rigaux2004-01-093-1/+3
|
* Failure must be always printedPascal Rigaux2004-01-091-1/+1
|
* add sqrt, atan2, cos, sinPascal Rigaux2004-01-061-3/+3
|
* fix reported position of variables in stringsPascal Rigaux2004-01-051-0/+2
|
* warn when a PO: comment is not followed by N(...)Pascal Rigaux2004-01-051-0/+1
|
* handle \\$aPascal Rigaux2004-01-051-2/+2
|
* fix checking dropped value in some casesPascal Rigaux2004-01-051-1/+2
|
* use hash_ref() to check priority of the argumentsPascal Rigaux2004-01-053-23/+28
|
* check unused file-global my variablesPascal Rigaux2003-12-181-1/+2
|
* disallowPascal Rigaux2003-12-165-25/+51
| | | | | | | - $a = (1, 2) - my $a = (1, 2) - my ($a, $b) = (1, 2, 3)
* check the number of parameters given to N()Pascal Rigaux2003-12-162-11/+20
|
* nice error message instead of syntax error in: map { $_ } /re/ ? 1 : 2Pascal Rigaux2003-12-161-0/+1
|
* - drop internally operator "qw" by rewriting qw(...) into a list of stringsPascal Rigaux2003-12-163-5/+3
| | | | | | - as a side effect "foo" . qw(bar boo) is now detected - allow pop(@l) (with explicit parentheses)
* - pop behaves just like shiftPascal Rigaux2003-12-162-3/+10
| | | | | - check that the parameter to pop & shift is an array, and not more parameters are given
2' href='#n782'>782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 968 969 970 971 972 973 974 975 976 977 978 979 980 981 982 983 984 985 986 987 988 989 990 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 1052 1053 1054 1055 1056 1057 1058 1059 1060 1061 1062 1063 1064 1065 1066 1067 1068 1069 1070 1071 1072 1073 1074 1075 1076 1077 1078 1079 1080 1081 1082 1083 1084 1085 1086 1087 1088 1089 1090 1091 1092
/***********************************************************************
*
* 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$";

#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'; 

    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");
    }

	dlinforeq(fd);
	dlinfoack(fd, (char *)buf);

	dlp = (union DL_primitives*) 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;

	dlattachreq(fd, ppa); 
	dlokack(fd, (char *)buf);

	dlbindreq(fd, type, 0, DL_CLDLS, 0, 0);
	dlbindack(fd, (char *)buf);

	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); 
	}
}