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
16static char const RCSID[] =
17"$Id: if.c,v 1.1 2001/12/14 02:55:20 mostrows Exp $";
18
19#include "pppoe.h"
20
21#ifdef HAVE_UNISTD_H
22#include <unistd.h>
23#endif
24
25#ifdef HAVE_NETPACKET_PACKET_H
26#include <netpacket/packet.h>
27#elif defined(HAVE_LINUX_IF_PACKET_H)
28#include <linux/if_packet.h>
29#endif
30
31#ifdef HAVE_NET_ETHERNET_H
32#include <net/ethernet.h>
33#endif
34
35#ifdef HAVE_ASM_TYPES_H
36#include <asm/types.h>
37#endif
38
39#ifdef HAVE_SYS_IOCTL_H
40#include <sys/ioctl.h>
41#endif
42
43#ifdef HAVE_SYSLOG_H
44#include <syslog.h>
45#endif
46
47#include <errno.h>
48#include <stdlib.h>
49#include <string.h>
50
51#ifdef HAVE_NET_IF_ARP_H
52#include <net/if_arp.h>
53#endif
54
55#ifdef USE_DLPI
56
57#include <limits.h>
58#include <fcntl.h>
59#include <stdlib.h>
60#include <sys/types.h>
61#include <sys/time.h>
62#include <sys/stream.h>
63#include <sys/stropts.h>
64#include <sys/dlpi.h>
65#include <sys/bufmod.h>
66#include <stdio.h>
67#include <signal.h>
68#include <stropts.h>
69
70/* function declarations */
71
72void dlpromisconreq( int fd, u_long  level);
73void dlinforeq(int fd);
74void dlunitdatareq(int fd, u_char *addrp, int addrlen, u_long minpri, u_long maxpri, u_char *datap, int datalen);
75void dlinfoack(int fd, char *bufp);
76void dlbindreq(int fd, u_long sap, u_long max_conind, u_long service_mode, u_long conn_mgmt, u_long xidtest);
77void dlattachreq(int fd, u_long ppa);
78void dlokack(int fd, char *bufp);
79void dlbindack(int fd, char *bufp);
80int strioctl(int fd, int cmd, int timout, int len, char *dp);
81void strgetmsg(int fd, struct strbuf *ctlp, struct strbuf *datap, int *flagsp, char *caller);
82void sigalrm(int sig);
83void expecting(int prim, union DL_primitives *dlp);
84char *dlprim(u_long prim);
85
86/* #define DL_DEBUG */
87
88static	int     dl_abssaplen;
89static	int     dl_saplen;
90static	int 	dl_addrlen;
91
92#endif
93
94#ifdef USE_BPF
95#include <net/bpf.h>
96#include <fcntl.h>
97
98unsigned char *bpfBuffer;	/* Packet filter buffer */
99int bpfLength = 0;		/* Packet filter buffer length */
100int bpfSize = 0;		/* Number of unread bytes in buffer */
101int bpfOffset = 0;		/* Current offset in bpfBuffer */
102#endif
103
104/* Initialize frame types to RFC 2516 values.  Some broken peers apparently
105   use different frame types... sigh... */
106
107UINT16_t Eth_PPPOE_Discovery = ETH_PPPOE_DISCOVERY;
108UINT16_t Eth_PPPOE_Session   = ETH_PPPOE_SESSION;
109
110/**********************************************************************
111*%FUNCTION: etherType
112*%ARGUMENTS:
113* packet -- a received PPPoE packet
114*%RETURNS:
115* ethernet packet type (see /usr/include/net/ethertypes.h)
116*%DESCRIPTION:
117* Checks the ethernet packet header to determine its type.
118* We should only be receveing DISCOVERY and SESSION types if the BPF
119* is set up correctly.  Logs an error if an unexpected type is received.
120* Note that the ethernet type names come from "pppoe.h" and the packet
121* packet structure names use the LINUX dialect to maintain consistency
122* with the rest of this file.  See the BSD section of "pppoe.h" for
123* translations of the data structure names.
124***********************************************************************/
125UINT16_t
126etherType(PPPoEPacket *packet)
127{
128    UINT16_t type = (UINT16_t) ntohs(packet->ethHdr.h_proto);
129    if (type != Eth_PPPOE_Discovery && type != Eth_PPPOE_Session) {
130	syslog(LOG_ERR, "Invalid ether type 0x%x", type);
131    }
132    return type;
133}
134
135#ifdef USE_BPF
136/**********************************************************************
137*%FUNCTION: getHWaddr
138*%ARGUMENTS:
139* ifname -- name of interface
140* hwaddr -- buffer for ehthernet address
141*%RETURNS:
142* Nothing
143*%DESCRIPTION:
144* Locates the Ethernet hardware address for an interface.
145***********************************************************************/
146void
147getHWaddr(int sock, char const *ifname, unsigned char *hwaddr)
148{
149    char inbuf[8192];
150    const struct sockaddr_dl *sdl;
151    struct ifconf ifc;
152    struct ifreq ifreq, *ifr;
153    int i;
154    int found = 0;
155
156    ifc.ifc_len = sizeof(inbuf);
157    ifc.ifc_buf = inbuf;
158    if (ioctl(sock, SIOCGIFCONF, &ifc) < 0) {
159	fatalSys("SIOCGIFCONF");
160    }
161    ifr = ifc.ifc_req;
162    ifreq.ifr_name[0] = '\0';
163    for (i = 0; i < ifc.ifc_len; ) {
164	ifr = (struct ifreq *)((caddr_t)ifc.ifc_req + i);
165	i += sizeof(ifr->ifr_name) +
166		    (ifr->ifr_addr.sa_len > sizeof(struct sockaddr)
167		    ? ifr->ifr_addr.sa_len
168		    : sizeof(struct sockaddr));
169	if (ifr->ifr_addr.sa_family == AF_LINK) {
170	    sdl = (const struct sockaddr_dl *) &ifr->ifr_addr;
171	    if ((sdl->sdl_type == IFT_ETHER) &&
172	        (sdl->sdl_alen == ETH_ALEN) &&
173		!strncmp(ifname, ifr->ifr_name, sizeof(ifr->ifr_name))) {
174		if (found) {
175		    char buffer[256];
176		    sprintf(buffer, "interface %.16s has more than one ethernet address", ifname);
177		    rp_fatal(buffer);
178		} else {
179		    found = 1;
180	            memcpy(hwaddr, LLADDR(sdl), ETH_ALEN);
181		}
182	    }
183	}
184    }
185    if (!found) {
186	char buffer[256];
187        sprintf(buffer, "interface %.16s has no ethernet address", ifname);
188	rp_fatal(buffer);
189    }
190}
191
192/**********************************************************************
193*%FUNCTION: initFilter
194*%ARGUMENTS:
195* fd -- file descriptor of BSD device
196* type -- Ethernet frame type (0 for watch mode)
197* hwaddr -- buffer with ehthernet address
198*%RETURNS:
199* Nothing
200*%DESCRIPTION:
201* Initializes the packet filter rules.
202***********************************************************************/
203void
204initFilter(int fd, UINT16_t type, unsigned char *hwaddr)
205{
206    /* Packet Filter Instructions:
207     * Note that the ethernet type names come from "pppoe.h" and are
208     * used here to maintain consistency with the rest of this file. */
209    static struct bpf_insn bpfRun[] = {         /* run PPPoE */
210        BPF_STMT(BPF_LD+BPF_H+BPF_ABS, 12),     /* ethernet type */
211        BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, ETH_PPPOE_SESSION, 5, 0),
212        BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, ETH_PPPOE_DISCOVERY, 0, 9),
213        BPF_STMT(BPF_LD+BPF_W+BPF_ABS, 0),      /* first word of dest. addr */
214#define PPPOE_BCAST_CMPW 4                     /* offset of word compare */
215        BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, 0, 0, 2),
216        BPF_STMT(BPF_LD+BPF_H+BPF_ABS, 4),      /* next 1/2 word of dest. */
217#define PPPOE_BCAST_CMPH 6                     /* offset of 1/2 word compare */
218        BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, 0, 4, 0),
219        BPF_STMT(BPF_LD+BPF_W+BPF_ABS, 0),      /* first word of dest. addr */
220#define PPPOE_FILTER_CMPW 8                     /* offset of word compare */
221        BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, 0, 0, 3),
222        BPF_STMT(BPF_LD+BPF_H+BPF_ABS, 4),      /* next 1/2 word of dest. */
223#define PPPOE_FILTER_CMPH 10                    /* offset of 1/rd compare */
224        BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, 0, 0, 1),
225        BPF_STMT(BPF_RET+BPF_K, (u_int) -1),    /* keep packet */
226        BPF_STMT(BPF_RET+BPF_K, 0),             /* drop packet */
227    };
228
229    /* Fix the potentially varying parts */
230    bpfRun[1].code = (u_short) BPF_JMP+BPF_JEQ+BPF_K;
231    bpfRun[1].jt   = 5;
232    bpfRun[1].jf   = 0;
233    bpfRun[1].k    = Eth_PPPOE_Session;
234
235    bpfRun[2].code = (u_short) BPF_JMP+BPF_JEQ+BPF_K;
236    bpfRun[2].jt   = 0;
237    bpfRun[2].jf   = 9;
238    bpfRun[2].k    = Eth_PPPOE_Discovery;
239
240    {
241      struct bpf_insn bpfInsn[sizeof(bpfRun) / sizeof(bpfRun[0])];
242      struct bpf_program bpfProgram;
243      memcpy(bpfInsn, bpfRun, sizeof(bpfRun));
244      bpfInsn[PPPOE_BCAST_CMPW].k = ((0xff << 24) | (0xff << 16) |
245                                     (0xff << 8) | 0xff);
246      bpfInsn[PPPOE_BCAST_CMPH].k = ((0xff << 8) | 0xff);
247      bpfInsn[PPPOE_FILTER_CMPW].k = ((hwaddr[0] << 24) | (hwaddr[1] << 16) |
248				      (hwaddr[2] << 8) | hwaddr[3]);
249      bpfInsn[PPPOE_FILTER_CMPH].k = ((hwaddr[4] << 8) | hwaddr[5]);
250      bpfProgram.bf_len = (sizeof(bpfInsn) / sizeof(bpfInsn[0]));
251      bpfProgram.bf_insns = &bpfInsn[0];
252
253      /* Apply the filter */
254      if (ioctl(fd, BIOCSETF, &bpfProgram) < 0) {
255	fatalSys("ioctl(BIOCSETF)");
256      }
257    }
258}
259
260/**********************************************************************
261*%FUNCTION: openInterface
262*%ARGUMENTS:
263* ifname -- name of interface
264* type -- Ethernet frame type (0 for any frame type)
265* hwaddr -- if non-NULL, set to the hardware address
266*%RETURNS:
267* A file descriptor for talking with the Ethernet card.  Exits on error.
268* Note that the Linux version of this routine returns a socket instead.
269*%DESCRIPTION:
270* Opens a BPF on an interface for all PPPoE traffic (discovery and
271* session).  If 'type' is 0, uses promiscuous mode to watch any PPPoE
272* traffic on this network.
273***********************************************************************/
274int
275openInterface(char const *ifname, UINT16_t type, unsigned char *hwaddr)
276{
277    static int fd = -1;
278    char bpfName[32];
279    u_int optval;
280    struct bpf_version bpf_ver;
281    struct ifreq ifr;
282    int sock;
283    int i;
284
285    /* BSD only opens one socket for both Discovery and Session packets */
286    if (fd >= 0) {
287	return fd;
288    }
289
290    /* Find a free BPF device */
291    for (i = 0; i < 256; i++) {
292	sprintf(bpfName, "/dev/bpf%d", i);
293	if (((fd = open(bpfName, O_RDWR, 0)) >= 0) ||
294	    (errno != EBUSY)) {
295	    break;
296	}
297    }
298    if (fd < 0) {
299	switch (errno) {
300	case EACCES:		/* permission denied */
301	    {
302		char buffer[256];
303		sprintf(buffer, "Cannot open %.32s -- pppoe must be run as root.", bpfName);
304		rp_fatal(buffer);
305	    }
306	    break;
307	case EBUSY:
308	case ENOENT:		/* no such file */
309	    if (i == 0) {
310		rp_fatal("No /dev/bpf* devices (check your kernel configuration for BPF support)");
311	    } else {
312		rp_fatal("All /dev/bpf* devices are in use");
313	    }
314	    break;
315	}
316	fatalSys(bpfName);
317    }
318
319    if ((sock = socket(AF_LOCAL, SOCK_DGRAM, 0)) < 0) {
320	fatalSys("socket");
321    }
322
323    /* Check that the interface is up */
324    strncpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name));
325    if (ioctl(sock, SIOCGIFFLAGS, &ifr) < 0) {
326	fatalSys("ioctl(SIOCGIFFLAGS)");
327    }
328    if ((ifr.ifr_flags & IFF_UP) == 0) {
329	char buffer[256];
330	sprintf(buffer, "Interface %.16s is not up\n", ifname);
331	rp_fatal(buffer);
332    }
333
334    /* Fill in hardware address and initialize the packet filter rules */
335    if (hwaddr == NULL) {
336	rp_fatal("openInterface: no hwaddr arg.");
337    }
338    getHWaddr(sock, ifname, hwaddr);
339    initFilter(fd, type, hwaddr);
340
341    /* Sanity check on MTU -- apparently does not work on OpenBSD */
342#if !defined(__OpenBSD__)
343    strncpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name));
344    if (ioctl(sock, SIOCGIFMTU, &ifr) < 0) {
345	fatalSys("ioctl(SIOCGIFMTU)");
346    }
347    if (ifr.ifr_mtu < ETH_DATA_LEN) {
348	char buffer[256];
349	sprintf(buffer, "Interface %.16s has MTU of %d -- should be %d.  You may have serious connection problems.",
350		ifname, ifr.ifr_mtu, ETH_DATA_LEN);
351	printErr(buffer);
352    }
353#endif
354
355    /* done with the socket */
356    if (close(sock) < 0) {
357	fatalSys("close");
358    }
359
360    /* Check the BPF version number */
361    if (ioctl(fd, BIOCVERSION, &bpf_ver) < 0) {
362	fatalSys("ioctl(BIOCVERSION)");
363    }
364    if ((bpf_ver.bv_major != BPF_MAJOR_VERSION) ||
365        (bpf_ver.bv_minor < BPF_MINOR_VERSION)) {
366	char buffer[256];
367	sprintf(buffer, "Unsupported BPF version: %d.%d (kernel: %d.%d)",
368			BPF_MAJOR_VERSION, BPF_MINOR_VERSION,
369			bpf_ver.bv_major, bpf_ver.bv_minor);
370	rp_fatal(buffer);
371    }
372
373    /* allocate a receive packet buffer */
374    if (ioctl(fd, BIOCGBLEN, &bpfLength) < 0) {
375	fatalSys("ioctl(BIOCGBLEN)");
376    }
377    if (!(bpfBuffer = (unsigned char *) malloc(bpfLength))) {
378	rp_fatal("malloc");
379    }
380
381    /* reads should return as soon as there is a packet available */
382    optval = 1;
383    if (ioctl(fd, BIOCIMMEDIATE, &optval) < 0) {
384	fatalSys("ioctl(BIOCIMMEDIATE)");
385    }
386
387    /* Bind the interface to the filter */
388    strncpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name));
389    if (ioctl(fd, BIOCSETIF, &ifr) < 0) {
390	char buffer[256];
391	sprintf(buffer, "ioctl(BIOCSETIF) can't select interface %.16s",
392		ifname);
393	rp_fatal(buffer);
394    }
395
396    syslog(LOG_INFO, "Interface=%.16s HWaddr=%02X:%02X:%02X:%02X:%02X:%02X Device=%.32s Buffer size=%d",
397	   ifname,
398	   hwaddr[0], hwaddr[1], hwaddr[2],
399	   hwaddr[3], hwaddr[4], hwaddr[5],
400	   bpfName, bpfLength);
401    return fd;
402}
403
404#endif /* USE_BPF */
405
406#ifdef USE_LINUX_PACKET
407/**********************************************************************
408*%FUNCTION: openInterface
409*%ARGUMENTS:
410* ifname -- name of interface
411* type -- Ethernet frame type
412* hwaddr -- if non-NULL, set to the hardware address
413*%RETURNS:
414* A raw socket for talking to the Ethernet card.  Exits on error.
415*%DESCRIPTION:
416* Opens a raw Ethernet socket
417***********************************************************************/
418int
419openInterface(char const *ifname, UINT16_t type, unsigned char *hwaddr)
420{
421    int optval=1;
422    int fd;
423    struct ifreq ifr;
424    int domain, stype;
425
426#ifdef HAVE_STRUCT_SOCKADDR_LL
427    struct sockaddr_ll sa;
428#else
429    struct sockaddr sa;
430#endif
431
432    memset(&sa, 0, sizeof(sa));
433
434#ifdef HAVE_STRUCT_SOCKADDR_LL
435    domain = PF_PACKET;
436    stype = SOCK_RAW;
437#else
438    domain = PF_INET;
439    stype = SOCK_PACKET;
440#endif
441
442    if ((fd = socket(domain, stype, htons(type))) < 0) {
443	/* Give a more helpful message for the common error case */
444	if (errno == EPERM) {
445	    rp_fatal("Cannot create raw socket -- pppoe must be run as root.");
446	}
447	fatalSys("socket");
448    }
449
450    if (setsockopt(fd, SOL_SOCKET, SO_BROADCAST, &optval, sizeof(optval)) < 0) {
451	fatalSys("setsockopt");
452    }
453
454    /* Fill in hardware address */
455    if (hwaddr) {
456	strncpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name));
457	if (ioctl(fd, SIOCGIFHWADDR, &ifr) < 0) {
458	    fatalSys("ioctl(SIOCGIFHWADDR)");
459	}
460	memcpy(hwaddr, ifr.ifr_hwaddr.sa_data, ETH_ALEN);
461#ifdef ARPHRD_ETHER
462	if (ifr.ifr_hwaddr.sa_family != ARPHRD_ETHER) {
463	    char buffer[256];
464	    sprintf(buffer, "Interface %.16s is not Ethernet", ifname);
465	    rp_fatal(buffer);
466	}
467#endif
468	if (NOT_UNICAST(hwaddr)) {
469	    char buffer[256];
470	    sprintf(buffer,
471		    "Interface %.16s has broadcast/multicast MAC address??",
472		    ifname);
473	    rp_fatal(buffer);
474	}
475    }
476
477    /* Sanity check on MTU */
478    strncpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name));
479    if (ioctl(fd, SIOCGIFMTU, &ifr) < 0) {
480	fatalSys("ioctl(SIOCGIFMTU)");
481    }
482    if (ifr.ifr_mtu < ETH_DATA_LEN) {
483	char buffer[256];
484	sprintf(buffer, "Interface %.16s has MTU of %d -- should be %d.  You may have serious connection problems.",
485		ifname, ifr.ifr_mtu, ETH_DATA_LEN);
486	printErr(buffer);
487    }
488
489#ifdef HAVE_STRUCT_SOCKADDR_LL
490    /* Get interface index */
491    sa.sll_family = AF_PACKET;
492    sa.sll_protocol = htons(type);
493
494    strncpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name));
495    if (ioctl(fd, SIOCGIFINDEX, &ifr) < 0) {
496	fatalSys("ioctl(SIOCFIGINDEX): Could not get interface index");
497    }
498    sa.sll_ifindex = ifr.ifr_ifindex;
499
500#else
501    strcpy(sa.sa_data, ifname);
502#endif
503
504    /* We're only interested in packets on specified interface */
505    if (bind(fd, (struct sockaddr *) &sa, sizeof(sa)) < 0) {
506	fatalSys("bind");
507    }
508
509    return fd;
510}
511
512#endif /* USE_LINUX */
513
514/***********************************************************************
515*%FUNCTION: sendPacket
516*%ARGUMENTS:
517* sock -- socket to send to
518* pkt -- the packet to transmit
519* size -- size of packet (in bytes)
520*%RETURNS:
521* 0 on success; -1 on failure
522*%DESCRIPTION:
523* Transmits a packet
524***********************************************************************/
525int
526sendPacket(PPPoEConnection *conn, int sock, PPPoEPacket *pkt, int size)
527{
528#if defined(USE_BPF)
529    if (write(sock, pkt, size) < 0) {
530	sysErr("write (sendPacket)");
531	return -1;
532    }
533#elif defined(HAVE_STRUCT_SOCKADDR_LL)
534    if (send(sock, pkt, size, 0) < 0) {
535	sysErr("send (sendPacket)");
536	return -1;
537    }
538#else
539#ifdef USE_DLPI
540
541#define ABS(x)          ((x) < 0 ? -(x) : (x))
542
543	u_char  addr[MAXDLADDR];
544	u_char  phys[MAXDLADDR];
545	u_char  sap[MAXDLADDR];
546	u_char    xmitbuf[MAXDLBUF];
547	int	data_size;
548
549	short	tmp_sap;
550
551	tmp_sap = htons(pkt->ethHdr.h_proto);
552	data_size = size - sizeof(struct ethhdr);
553
554	memcpy((char *)phys, (char *)pkt->ethHdr.h_dest, ETHERADDRL);
555	memcpy((char *)sap,  (char *)&tmp_sap, sizeof(ushort_t));
556	memcpy((char *)xmitbuf, (char *)pkt + sizeof(struct ethhdr), data_size);
557
558	if (dl_saplen > 0) {  /* order is sap+phys */
559		(void) memcpy((char*)addr, (char*)&sap, dl_abssaplen);
560		(void) memcpy((char*)addr+dl_abssaplen, (char*)phys, ETHERADDRL);
561	} else {        /* order is phys+sap */
562		(void) memcpy((char*)addr, (char*)phys, ETHERADDRL);
563		(void) memcpy((char*)addr+ETHERADDRL, (char*)&sap, dl_abssaplen);
564	}
565
566#ifdef DL_DEBUG
567	printf("%02x:%02x:%02x:%02x:%02x:%02x %02x:%02x\n",
568		addr[0],addr[1],addr[2],addr[3],addr[4],addr[5],
569		addr[6],addr[7]);
570#endif
571
572	dlunitdatareq(sock, addr, dl_addrlen, 0, 0, xmitbuf, data_size);
573
574
575
576#else
577    struct sockaddr sa;
578
579    if (!conn) {
580	rp_fatal("relay and server not supported on Linux 2.0 kernels");
581    }
582    strcpy(sa.sa_data, conn->ifName);
583    if (sendto(sock, pkt, size, 0, &sa, sizeof(sa)) < 0) {
584	sysErr("sendto (sendPacket)");
585	return -1;
586    }
587#endif
588#endif
589    return 0;
590}
591
592#ifdef USE_BPF
593/***********************************************************************
594*%FUNCTION: clearPacketHeader
595*%ARGUMENTS:
596* pkt -- packet that needs its head clearing
597*%RETURNS:
598* nothing
599*%DESCRIPTION:
600* Clears a PPPoE packet header after a truncated packet has been
601* received.  Insures that the packet will fail any integrity tests
602* and will be discarded by upper level routines.  Also resets the
603* bpfSize and bpfOffset variables to force a new read on the next
604* call to receivePacket().
605***********************************************************************/
606void
607clearPacketHeader(PPPoEPacket *pkt)
608{
609    bpfSize = bpfOffset = 0;
610    memset(pkt, 0, HDR_SIZE);
611}
612#endif
613
614/***********************************************************************
615*%FUNCTION: receivePacket
616*%ARGUMENTS:
617* sock -- socket to read from
618* pkt -- place to store the received packet
619* size -- set to size of packet in bytes
620*%RETURNS:
621* >= 0 if all OK; < 0 if error
622*%DESCRIPTION:
623* Receives a packet
624***********************************************************************/
625int
626receivePacket(int sock, PPPoEPacket *pkt, int *size)
627{
628#ifdef USE_BPF
629    struct bpf_hdr hdr;
630    int seglen, copylen;
631
632    if (bpfSize <= 0) {
633	bpfOffset = 0;
634	if ((bpfSize = read(sock, bpfBuffer, bpfLength)) < 0) {
635	    sysErr("read (receivePacket)");
636	    return -1;
637	}
638    }
639    if (bpfSize < sizeof(hdr)) {
640	syslog(LOG_ERR, "Truncated bpf packet header: len=%d", bpfSize);
641	clearPacketHeader(pkt);		/* resets bpfSize and bpfOffset */
642	return 0;
643    }
644    memcpy(&hdr, bpfBuffer + bpfOffset, sizeof(hdr));
645    if (hdr.bh_caplen != hdr.bh_datalen) {
646	syslog(LOG_ERR, "Truncated bpf packet: caplen=%d, datalen=%d",
647	       hdr.bh_caplen, hdr.bh_datalen);
648	clearPacketHeader(pkt);		/* resets bpfSize and bpfOffset */
649	return 0;
650    }
651    seglen = hdr.bh_hdrlen + hdr.bh_caplen;
652    if (seglen > bpfSize) {
653	syslog(LOG_ERR, "Truncated bpf packet: seglen=%d, bpfSize=%d",
654	       seglen, bpfSize);
655	clearPacketHeader(pkt);		/* resets bpfSize and bpfOffset */
656	return 0;
657    }
658    seglen = BPF_WORDALIGN(seglen);
659    *size = copylen = ((hdr.bh_caplen < sizeof(PPPoEPacket)) ?
660			hdr.bh_caplen : sizeof(PPPoEPacket));
661    memcpy(pkt, bpfBuffer + bpfOffset + hdr.bh_hdrlen, copylen);
662    if (seglen >= bpfSize) {
663	bpfSize = bpfOffset = 0;
664    } else {
665	bpfSize -= seglen;
666	bpfOffset += seglen;
667    }
668#else
669#ifdef USE_DLPI
670	struct strbuf data;
671	int flags = 0;
672	int retval;
673
674	data.buf = (char *) pkt;
675	data.maxlen = MAXDLBUF;
676	data.len = 0;
677
678	if ((retval = getmsg(sock, NULL, &data, &flags)) < 0) {
679	    sysErr("read (receivePacket)");
680	    return -1;
681	}
682
683	*size = data.len;
684
685#else
686    if ((*size = recv(sock, pkt, sizeof(PPPoEPacket), 0)) < 0) {
687	sysErr("recv (receivePacket)");
688	return -1;
689    }
690#endif
691#endif
692    return 0;
693}
694
695#ifdef USE_DLPI
696/**********************************************************************
697*%FUNCTION: openInterface
698*%ARGUMENTS:
699* ifname -- name of interface
700* type -- Ethernet frame type
701* hwaddr -- if non-NULL, set to the hardware address
702*%RETURNS:
703* A raw socket for talking to the Ethernet card.  Exits on error.
704*%DESCRIPTION:
705* Opens a raw Ethernet socket
706***********************************************************************/
707int
708openInterface(char const *ifname, UINT16_t type, unsigned char *hwaddr)
709{
710    int fd;
711    long buf[MAXDLBUF];
712
713	union   DL_primitives   *dlp;
714
715    char base_dev[PATH_MAX];
716    int ppa;
717
718    if(strlen(ifname) > PATH_MAX) {
719	rp_fatal("socket: string to long");
720    }
721
722    ppa = atoi(&ifname[strlen(ifname)-1]);
723    strncpy(base_dev, ifname, PATH_MAX);
724    base_dev[strlen(base_dev)-1] = '\0';
725
726/* rearranged order of DLPI code - delphys 20010803 */
727    dlp = (union DL_primitives*) buf;
728
729    if (( fd = open(base_dev, O_RDWR)) < 0) {
730	/* Give a more helpful message for the common error case */
731	if (errno == EPERM) {
732	    rp_fatal("Cannot create raw socket -- pppoe must be run as root.");
733	}
734	fatalSys("socket");
735    }
736
737/* rearranged order of DLPI code - delphys 20010803 */
738    dlattachreq(fd, ppa);
739    dlokack(fd, (char *)buf);
740
741    dlbindreq(fd, type, 0, DL_CLDLS, 0, 0);
742    dlbindack(fd, (char *)buf);
743
744    dlinforeq(fd);
745    dlinfoack(fd, (char *)buf);
746
747    dl_abssaplen = ABS(dlp->info_ack.dl_sap_length);
748    dl_saplen = dlp->info_ack.dl_sap_length;
749    if (ETHERADDRL != (dlp->info_ack.dl_addr_length - dl_abssaplen))
750	fatalSys("invalid destination physical address length");
751    dl_addrlen = dl_abssaplen + ETHERADDRL;
752
753/* ethernet address retrieved as part of DL_INFO_ACK - delphys 20010803 */
754    memcpy(hwaddr, (u_char*)((char*)(dlp) + (int)(dlp->info_ack.dl_addr_offset)), ETHERADDRL);
755
756    if ( strioctl(fd, DLIOCRAW, -1, 0, NULL) < 0 ) {
757	fatalSys("DLIOCRAW");
758    }
759
760    if (ioctl(fd, I_FLUSH, FLUSHR) < 0) fatalSys("I_FLUSH");
761
762    return fd;
763}
764
765/* cloned from dlcommon.c */
766
767void dlpromisconreq(int fd, u_long level)
768{
769        dl_promiscon_req_t      promiscon_req;
770        struct  strbuf  ctl;
771        int     flags;
772
773        promiscon_req.dl_primitive = DL_PROMISCON_REQ;
774        promiscon_req.dl_level = level;
775
776        ctl.maxlen = 0;
777        ctl.len = sizeof (promiscon_req);
778        ctl.buf = (char *) &promiscon_req;
779
780        flags = 0;
781
782        if (putmsg(fd, &ctl, (struct strbuf*) NULL, flags) < 0)
783                fatalSys("dlpromiscon:  putmsg");
784
785}
786
787void dlinforeq(int fd)
788{
789        dl_info_req_t   info_req;
790        struct  strbuf  ctl;
791        int     flags;
792
793        info_req.dl_primitive = DL_INFO_REQ;
794
795        ctl.maxlen = 0;
796        ctl.len = sizeof (info_req);
797        ctl.buf = (char *) &info_req;
798
799        flags = RS_HIPRI;
800
801        if (putmsg(fd, &ctl, (struct strbuf*) NULL, flags) < 0)
802                fatalSys("dlinforeq:  putmsg");
803}
804
805void dlunitdatareq(int fd, u_char *addrp, int addrlen, u_long minpri, u_long maxpri, u_char *datap, int datalen)
806{
807        long    buf[MAXDLBUF];
808        union   DL_primitives   *dlp;
809        struct  strbuf  data, ctl;
810
811        dlp = (union DL_primitives*) buf;
812
813        dlp->unitdata_req.dl_primitive = DL_UNITDATA_REQ;
814        dlp->unitdata_req.dl_dest_addr_length = addrlen;
815        dlp->unitdata_req.dl_dest_addr_offset = sizeof (dl_unitdata_req_t);
816        dlp->unitdata_req.dl_priority.dl_min = minpri;
817        dlp->unitdata_req.dl_priority.dl_max = maxpri;
818
819        (void) memcpy(OFFADDR(dlp, sizeof (dl_unitdata_req_t)), addrp, addrlen);
820
821        ctl.maxlen = 0;
822        ctl.len = sizeof (dl_unitdata_req_t) + addrlen;
823        ctl.buf = (char *) buf;
824
825        data.maxlen = 0;
826        data.len = datalen;
827        data.buf = (char *) datap;
828
829        if (putmsg(fd, &ctl, &data, 0) < 0)
830                fatalSys("dlunitdatareq:  putmsg");
831}
832
833void dlinfoack(int fd, char *bufp)
834{
835        union   DL_primitives   *dlp;
836        struct  strbuf  ctl;
837        int     flags;
838
839        ctl.maxlen = MAXDLBUF;
840        ctl.len = 0;
841        ctl.buf = bufp;
842
843        strgetmsg(fd, &ctl, (struct strbuf*)NULL, &flags, "dlinfoack");
844
845        dlp = (union DL_primitives *) ctl.buf;
846
847        expecting(DL_INFO_ACK, dlp);
848
849        if (ctl.len < sizeof (dl_info_ack_t)) {
850		char buffer[256];
851		sprintf(buffer, "dlinfoack:  response ctl.len too short:  %d", ctl.len);
852                rp_fatal(buffer);
853	}
854
855        if (flags != RS_HIPRI)
856                rp_fatal("dlinfoack:  DL_INFO_ACK was not M_PCPROTO");
857
858        if (ctl.len < sizeof (dl_info_ack_t)) {
859		char buffer[256];
860		sprintf(buffer, "dlinfoack:  short response ctl.len:  %d", ctl.len);
861		rp_fatal(buffer);
862	}
863}
864
865void dlbindreq(int fd, u_long sap, u_long max_conind, u_long service_mode, u_long conn_mgmt, u_long xidtest)
866{
867        dl_bind_req_t   bind_req;
868        struct  strbuf  ctl;
869        int     flags;
870
871        bind_req.dl_primitive = DL_BIND_REQ;
872        bind_req.dl_sap = sap;
873        bind_req.dl_max_conind = max_conind;
874        bind_req.dl_service_mode = service_mode;
875        bind_req.dl_conn_mgmt = conn_mgmt;
876        bind_req.dl_xidtest_flg = xidtest;
877
878        ctl.maxlen = 0;
879        ctl.len = sizeof (bind_req);
880        ctl.buf = (char *) &bind_req;
881
882        flags = 0;
883
884        if (putmsg(fd, &ctl, (struct strbuf*) NULL, flags) < 0)
885                fatalSys("dlbindreq:  putmsg");
886}
887
888void dlattachreq(int fd, u_long ppa)
889{
890        dl_attach_req_t attach_req;
891        struct  strbuf  ctl;
892        int     flags;
893
894        attach_req.dl_primitive = DL_ATTACH_REQ;
895        attach_req.dl_ppa = ppa;
896
897        ctl.maxlen = 0;
898        ctl.len = sizeof (attach_req);
899        ctl.buf = (char *) &attach_req;
900
901        flags = 0;
902
903        if (putmsg(fd, &ctl, (struct strbuf*) NULL, flags) < 0)
904                fatalSys("dlattachreq:  putmsg");
905}
906
907void dlokack(int fd, char *bufp)
908{
909        union   DL_primitives   *dlp;
910        struct  strbuf  ctl;
911        int     flags;
912
913        ctl.maxlen = MAXDLBUF;
914        ctl.len = 0;
915        ctl.buf = bufp;
916
917        strgetmsg(fd, &ctl, (struct strbuf*)NULL, &flags, "dlokack");
918
919        dlp = (union DL_primitives *) ctl.buf;
920
921        expecting(DL_OK_ACK, dlp);
922
923        if (ctl.len < sizeof (dl_ok_ack_t)) {
924		char buffer[256];
925		sprintf(buffer, "dlokack:  response ctl.len too short:  %d", ctl.len);
926		rp_fatal(buffer);
927	}
928
929        if (flags != RS_HIPRI)
930                rp_fatal("dlokack:  DL_OK_ACK was not M_PCPROTO");
931
932        if (ctl.len < sizeof (dl_ok_ack_t)) {
933		char buffer[256];
934		sprintf(buffer, "dlokack:  short response ctl.len:  %d", ctl.len);
935		rp_fatal(buffer);
936	}
937}
938
939void dlbindack(int fd, char *bufp)
940{
941        union   DL_primitives   *dlp;
942        struct  strbuf  ctl;
943        int     flags;
944
945        ctl.maxlen = MAXDLBUF;
946        ctl.len = 0;
947        ctl.buf = bufp;
948
949        strgetmsg(fd, &ctl, (struct strbuf*)NULL, &flags, "dlbindack");
950
951        dlp = (union DL_primitives *) ctl.buf;
952
953        expecting(DL_BIND_ACK, dlp);
954
955        if (flags != RS_HIPRI)
956                rp_fatal("dlbindack:  DL_OK_ACK was not M_PCPROTO");
957
958        if (ctl.len < sizeof (dl_bind_ack_t)) {
959		char buffer[256];
960		sprintf(buffer, "dlbindack:  short response ctl.len:  %d", ctl.len);
961		rp_fatal(buffer);
962	}
963}
964
965int strioctl(int fd, int cmd, int timout, int len, char *dp)
966{
967        struct  strioctl        sioc;
968        int     rc;
969
970        sioc.ic_cmd = cmd;
971        sioc.ic_timout = timout;
972        sioc.ic_len = len;
973        sioc.ic_dp = dp;
974        rc = ioctl(fd, I_STR, &sioc);
975
976        if (rc < 0)
977                return (rc);
978        else
979                return (sioc.ic_len);
980}
981
982void strgetmsg(int fd, struct strbuf *ctlp, struct strbuf *datap, int *flagsp, char *caller)
983{
984        int     rc;
985        static  char    errmsg[80];
986
987        /*
988         * Start timer.
989         */
990        (void) signal(SIGALRM, sigalrm);
991        if (alarm(MAXWAIT) < 0) {
992                (void) sprintf(errmsg, "%s:  alarm", caller);
993                fatalSys(errmsg);
994        }
995
996        /*
997         * Set flags argument and issue getmsg().
998         */
999        *flagsp = 0;
1000        if ((rc = getmsg(fd, ctlp, datap, flagsp)) < 0) {
1001                (void) sprintf(errmsg, "%s:  getmsg", caller);
1002                fatalSys(errmsg);
1003        }
1004
1005        /*
1006         * Stop timer.
1007         */
1008        if (alarm(0) < 0) {
1009                (void) sprintf(errmsg, "%s:  alarm", caller);
1010                fatalSys(errmsg);
1011        }
1012
1013        /*
1014         * Check for MOREDATA and/or MORECTL.
1015         */
1016        if ((rc & (MORECTL | MOREDATA)) == (MORECTL | MOREDATA)) {
1017		char buffer[256];
1018		sprintf(buffer, "%s:  MORECTL|MOREDATA", caller);
1019		rp_fatal(buffer);
1020	}
1021
1022        if (rc & MORECTL) {
1023		char buffer[256];
1024		sprintf(buffer, "%s:  MORECTL", caller);
1025		rp_fatal(buffer);
1026	}
1027
1028        if (rc & MOREDATA) {
1029		char buffer[256];
1030		sprintf(buffer, "%s:  MOREDATA", caller);
1031		rp_fatal(buffer);
1032	}
1033
1034        /*
1035         * Check for at least sizeof (long) control data portion.
1036         */
1037        if (ctlp->len < sizeof (long)) {
1038		char buffer[256];
1039		sprintf(buffer, "getmsg:  control portion length < sizeof (long):  %d", ctlp->len);
1040		rp_fatal(buffer);
1041	}
1042}
1043
1044void sigalrm(int sig)
1045{
1046        (void) rp_fatal("sigalrm:  TIMEOUT");
1047}
1048
1049void expecting(int prim, union DL_primitives *dlp)
1050{
1051        if (dlp->dl_primitive != (u_long)prim) {
1052		char buffer[256];
1053		sprintf(buffer, "expected %s got %s", dlprim(prim), dlprim(dlp->dl_primitive));
1054		rp_fatal(buffer);
1055		exit(1);
1056	}
1057}
1058
1059char *dlprim(u_long prim)
1060{
1061        static  char    primbuf[80];
1062
1063        switch ((int)prim) {
1064                CASERET(DL_INFO_REQ);
1065                CASERET(DL_INFO_ACK);
1066                CASERET(DL_ATTACH_REQ);
1067                CASERET(DL_DETACH_REQ);
1068                CASERET(DL_BIND_REQ);
1069                CASERET(DL_BIND_ACK);
1070                CASERET(DL_UNBIND_REQ);
1071                CASERET(DL_OK_ACK);
1072                CASERET(DL_ERROR_ACK);
1073                CASERET(DL_SUBS_BIND_REQ);
1074                CASERET(DL_SUBS_BIND_ACK);
1075                CASERET(DL_UNITDATA_REQ);
1076                CASERET(DL_UNITDATA_IND);
1077                CASERET(DL_UDERROR_IND);
1078                CASERET(DL_UDQOS_REQ);
1079                CASERET(DL_CONNECT_REQ);
1080                CASERET(DL_CONNECT_IND);
1081                CASERET(DL_CONNECT_RES);
1082                CASERET(DL_CONNECT_CON);
1083                CASERET(DL_TOKEN_REQ);
1084                CASERET(DL_TOKEN_ACK);
1085                CASERET(DL_DISCONNECT_REQ);
1086                CASERET(DL_DISCONNECT_IND);
1087                CASERET(DL_RESET_REQ);
1088                CASERET(DL_RESET_IND);
1089                CASERET(DL_RESET_RES);
1090                CASERET(DL_RESET_CON);
1091                default:
1092                        (void) sprintf(primbuf, "unknown primitive 0x%lx", prim);
1093                        return (primbuf);
1094        }
1095}
1096
1097#endif /* USE_DLPI */
1098